rt-thread icon indicating copy to clipboard operation
rt-thread copied to clipboard

[drivers][serial_v2]允许阻塞接收超过rx缓冲区大小的数据、增加超时时间、flush、获取缓冲区数据长度命令、数据溢出逻…

Open Ryan-CW-Code opened this issue 1 year ago • 6 comments

…辑修复、稳定性细节优化、添加更多serial_v2测试用例

当前只在STM32平台测试过,别的平台只是将rt_ringbuffer_putchar替换为rt_ringbuffer_putchar_force理论上不会出问题,可以再检查一下

拉取/合并请求描述:(PR description)

[

为什么提交这份PR (why to submit this PR)

优化serial_v2驱动

你的解决方案是什么 (what is your solution)

请提供验证的bsp和config (provide the config and bsp)

  • BSP: STM32F407-lckfb-skystar进行utest、echo等测试 STM32F401RCT6进行at_socket测试
  • .config:
  • action:

]

当前拉取/合并请求的状态 Intent for your PR

必须选择一项 Choose one (Mandatory):

  • [ ] 本拉取/合并请求是一个草稿版本 This PR is for a code-review and is intended to get feedback
  • [x] 本拉取/合并请求是一个成熟版本 This PR is mature, and ready to be integrated into the repo

代码质量 Code Quality:

我在这个拉取/合并请求中已经考虑了 As part of this pull request, I've considered the following:

  • [x] 已经仔细查看过代码改动的对比 Already check the difference between PR and old code
  • [x] 代码风格正确,包括缩进空格,命名及其他风格 Style guide is adhered to, including spacing, naming and other styles
  • [x] 没有垃圾代码,代码尽量精简,不包含#if 0代码,不包含已经被注释了的代码 All redundant code is removed and cleaned up
  • [x] 所有变更均有原因及合理的,并且不会影响到其他软件组件代码或BSP All modifications are justified and not affect other components or BSP
  • [x] 对难懂代码均提供对应的注释 I've commented appropriately where code is tricky
  • [x] 代码是高质量的 Code in this PR is of high quality
  • [x] 已经使用formatting 等源码格式化工具确保格式符合RT-Thread代码规范 This PR complies with RT-Thread code specification

Ryan-CW-Code avatar Sep 04 '24 02:09 Ryan-CW-Code

CLA assistant check
All committers have signed the CLA.

CLAassistant avatar Sep 04 '24 02:09 CLAassistant

@Ryan-CW-Code 感谢PR,但CI的代码格式化检查没有通过,麻烦使用工具:https://github.com/mysterywolf/formatting 格式化下再提交下~

Rbb666 avatar Sep 04 '24 03:09 Rbb666

utest和at_socket都使用dma和int方式分别进行测试。 echo测试代码如下,之前跑了两天数据没有丢包

大家可以再测试测试,看看有什么问题是没发现的

#include <board.h>
#include <rtthread.h>
#include <rtdevice.h>

#define echo_test_buffer_size (1024)

// 测试环境
// 串口2、3开启dma, 5、6不开启
// 缓冲区大小都为 128 字节

// echo test
// 3tx - 6rx
// 3rx - 6tx

// self test
// 2tx - 2rx
// 5tx - 5rx

static rt_device_t u2serial;
static rt_device_t u3serial;
static rt_device_t u5serial;
static rt_device_t u6serial;

static rt_uint32_t u2rx_length = 0;
static rt_uint32_t u2tx_length = 0;

static rt_uint32_t u3rx_length = 0;
static rt_uint32_t u3tx_length = 0;

static rt_uint32_t u5rx_length = 0;
static rt_uint32_t u5tx_length = 0;

static rt_uint32_t u6rx_length = 0;
static rt_uint32_t u6tx_length = 0;

static void echo_test_u3_thread_entry(void *parameter)
{
    char *uart_name = "uart3";

    rt_err_t result;
    static char rx_buffer[echo_test_buffer_size + 1];

    char str[] = "hello RT-Thread!";

    /* 查找串口设备 */
    u3serial = rt_device_find(uart_name);
    if (!u3serial)
    {
        rt_kprintf("find %s failed!\n", uart_name);
        return;
    }

    rt_device_open(u3serial, RT_DEVICE_FLAG_RX_BLOCKING | RT_DEVICE_FLAG_TX_BLOCKING);

    rt_ssize_t buf_datalen = 0;
    while (1)
    {
        rt_device_control(u3serial, RT_SERIAL_CTRL_GET_UNREAD_BYTES_COUNT, (void *)&buf_datalen);
        int32_t recbLen = rt_device_read(u3serial, 0, rx_buffer, buf_datalen > 0 ? buf_datalen : 1);
        if (recbLen > 0)
        {
            u3rx_length += recbLen;

            u3tx_length += rt_device_write(u3serial, 0, rx_buffer, recbLen);
        }
    }
}

static void echo_test_u6_thread_entry(void *parameter)
{
    static char rx_buffer[echo_test_buffer_size + 1];
    rt_ssize_t buf_datalen = 0;
    while (1)
    {
        rt_device_control(u6serial, RT_SERIAL_CTRL_GET_UNREAD_BYTES_COUNT, (void *)&buf_datalen);
        int32_t recbLen = rt_device_read(u6serial, 0, rx_buffer, buf_datalen > 0 ? buf_datalen : 1);
        if (recbLen > 0)
        {
            u6rx_length += recbLen;
        }
    }
}

static void echo_test(void *parameter)
{
    static char tx_buffer[echo_test_buffer_size];

    for (uint32_t i = 0; i < sizeof(tx_buffer); i++)
        tx_buffer[i] = i;

    char *uart_name = "uart6";
    u6serial = rt_device_find(uart_name);
    if (!u6serial)
    {
        rt_kprintf("find %s failed!\n", uart_name);
        return;
    }

    rt_device_open(u6serial, RT_DEVICE_FLAG_RX_BLOCKING | RT_DEVICE_FLAG_TX_BLOCKING);

    rt_thread_startup(rt_thread_create("serial3", echo_test_u3_thread_entry, RT_NULL, 2048, 7, 5));
    rt_thread_startup(rt_thread_create("serial6", echo_test_u6_thread_entry, RT_NULL, 2048, 7, 5));

    uint32_t count = 0;
    while (1)
    {
        // 不定长发送数据
        for (uint32_t i = sizeof(tx_buffer) / 10; i < sizeof(tx_buffer); i++)
        {
            count++;

            u6tx_length += rt_device_write(u6serial, 0, tx_buffer, i);
            rt_thread_mdelay(15);

            if (count % 100 == 0)
            {
                rt_kprintf("echo, uart3: tx: %ld, rx: %ld\r\n", u3tx_length, u3rx_length);
                rt_kprintf("echo, uart6: tx: %ld, rx: %ld\r\n", u6tx_length, u6rx_length);
                if (u3tx_length != u3rx_length || u6tx_length != u6rx_length || u3tx_length != u6tx_length)
                {
                    rt_kprintf("echo test error!!!\r\n");
                    return;
                }
            }
        }
    }
}

static void self_tx_rx_u2_thread_entry(void *parameter)
{
    static char rx_buffer[1024 + 1];

    while (1)
    {
        /* 从串口读取数据 */
        u2rx_length += rt_device_read(u2serial, 0, rx_buffer, 1000);
    }
}

static void self_tx_rx_u5_thread_entry(void *parameter)
{
    static char rx_buffer[1024 + 1];

    while (1)
    {
        /* 从串口读取数据 */
        u5rx_length += rt_device_read(u5serial, 0, rx_buffer, 500);
    }
}

static void self_tx_rx_test(void *parameter)
{
    static char tx_buffer[1024 + 1];
    for (uint32_t i = 0; i < sizeof(tx_buffer); i++)
        tx_buffer[i] = i;

    char *uart_name = "uart2";
    u2serial = rt_device_find(uart_name);
    if (!u2serial)
    {
        rt_kprintf("find %s failed!\n", uart_name);
        return;
    }
    rt_device_open(u2serial, RT_DEVICE_FLAG_RX_BLOCKING | RT_DEVICE_FLAG_TX_BLOCKING);

    char *uart5_name = "uart5";
    u5serial = rt_device_find(uart5_name);
    if (!u5serial)
    {
        rt_kprintf("find %s failed!\n", uart5_name);
        return;
    }
    rt_device_open(u5serial, RT_DEVICE_FLAG_RX_BLOCKING | RT_DEVICE_FLAG_TX_BLOCKING);

    rt_thread_startup(rt_thread_create("serial2", self_tx_rx_u2_thread_entry, RT_NULL, 2048, 6, 5));
    rt_thread_startup(rt_thread_create("serial5", self_tx_rx_u5_thread_entry, RT_NULL, 2048, 6, 5));

    for (uint32_t i = 0;; i++)
    {

        u2tx_length += rt_device_write(u2serial, 0, tx_buffer, 1000);
        u5tx_length += rt_device_write(u5serial, 0, tx_buffer, 500);
        rt_thread_mdelay(10);

        if (i % 100 == 0)
        {
            rt_kprintf("self_test, uart2: tx: %ld, rx: %ld\r\n", u2tx_length, u2rx_length);
            rt_kprintf("self_test, uart5: tx: %ld, rx: %ld\r\n", u5tx_length, u5rx_length);
            if (u2tx_length != u2rx_length || u5tx_length != u5rx_length)
            {
                rt_kprintf("self test error!!!\r\n");
                return;
            }
        }
    }
}

static int uart_test(void)
{
    rt_thread_startup(rt_thread_create("echo_test", echo_test, RT_NULL, 2048, 10, 5));
    rt_thread_startup(rt_thread_create("self_test", self_tx_rx_test, RT_NULL, 2048, 10, 5));

    return 0;
}

MSH_CMD_EXPORT_ALIAS(uart_test, uart_test, );

Ryan-CW-Code avatar Sep 04 '24 04:09 Ryan-CW-Code

at_client我们配合at_socket用了快一个月。 at_server我们没有产品在使用,用命令行简单的测试了一下

Ryan-CW-Code avatar Sep 10 '24 03:09 Ryan-CW-Code

utest和at_socket都使用dma和int方式分别进行测试。 echo测试代码如下,之前跑了两天数据没有丢包

大家可以再测试测试,看看有什么问题是没发现的

#include <board.h>
#include <rtthread.h>
#include <rtdevice.h>

#define echo_test_buffer_size (1024)

// 测试环境
// 串口2、3开启dma, 5、6不开启
// 缓冲区大小都为 128 字节

// echo test
// 3tx - 6rx
// 3rx - 6tx

// self test
// 2tx - 2rx
// 5tx - 5rx

static rt_device_t u2serial;
static rt_device_t u3serial;
static rt_device_t u5serial;
static rt_device_t u6serial;

static rt_uint32_t u2rx_length = 0;
static rt_uint32_t u2tx_length = 0;

static rt_uint32_t u3rx_length = 0;
static rt_uint32_t u3tx_length = 0;

static rt_uint32_t u5rx_length = 0;
static rt_uint32_t u5tx_length = 0;

static rt_uint32_t u6rx_length = 0;
static rt_uint32_t u6tx_length = 0;

static void echo_test_u3_thread_entry(void *parameter)
{
    char *uart_name = "uart3";

    rt_err_t result;
    static char rx_buffer[echo_test_buffer_size + 1];

    char str[] = "hello RT-Thread!";

    /* 查找串口设备 */
    u3serial = rt_device_find(uart_name);
    if (!u3serial)
    {
        rt_kprintf("find %s failed!\n", uart_name);
        return;
    }

    rt_device_open(u3serial, RT_DEVICE_FLAG_RX_BLOCKING | RT_DEVICE_FLAG_TX_BLOCKING);

    rt_ssize_t buf_datalen = 0;
    while (1)
    {
        rt_device_control(u3serial, RT_SERIAL_CTRL_GET_RX_DATA_LEN, (void *)&buf_datalen);
        int32_t recbLen = rt_device_read(u3serial, 0, rx_buffer, buf_datalen > 0 ? buf_datalen : 1);
        if (recbLen > 0)
        {
            u3rx_length += recbLen;

            u3tx_length += rt_device_write(u3serial, 0, rx_buffer, recbLen);
        }
    }
}

static void echo_test_u6_thread_entry(void *parameter)
{
    static char rx_buffer[echo_test_buffer_size + 1];
    rt_ssize_t buf_datalen = 0;
    while (1)
    {
        rt_device_control(u6serial, RT_SERIAL_CTRL_GET_RX_DATA_LEN, (void *)&buf_datalen);
        int32_t recbLen = rt_device_read(u6serial, 0, rx_buffer, buf_datalen > 0 ? buf_datalen : 1);
        if (recbLen > 0)
        {
            u6rx_length += recbLen;
        }
    }
}

static void echo_test(void *parameter)
{
    static char tx_buffer[echo_test_buffer_size];

    for (uint32_t i = 0; i < sizeof(tx_buffer); i++)
        tx_buffer[i] = i;

    char *uart_name = "uart6";
    u6serial = rt_device_find(uart_name);
    if (!u6serial)
    {
        rt_kprintf("find %s failed!\n", uart_name);
        return;
    }

    rt_device_open(u6serial, RT_DEVICE_FLAG_RX_BLOCKING | RT_DEVICE_FLAG_TX_BLOCKING);

    rt_thread_startup(rt_thread_create("serial3", echo_test_u3_thread_entry, RT_NULL, 2048, 7, 5));
    rt_thread_startup(rt_thread_create("serial6", echo_test_u6_thread_entry, RT_NULL, 2048, 7, 5));

    uint32_t count = 0;
    while (1)
    {
        // 不定长发送数据
        for (uint32_t i = sizeof(tx_buffer) / 10; i < sizeof(tx_buffer); i++)
        {
            count++;

            u6tx_length += rt_device_write(u6serial, 0, tx_buffer, i);
            rt_thread_mdelay(15);

            if (count % 100 == 0)
            {
                rt_kprintf("echo, uart3: tx: %ld, rx: %ld\r\n", u3tx_length, u3rx_length);
                rt_kprintf("echo, uart6: tx: %ld, rx: %ld\r\n", u6tx_length, u6rx_length);
                if (u3tx_length != u3rx_length || u6tx_length != u6rx_length || u3tx_length != u6tx_length)
                {
                    rt_kprintf("echo test error!!!\r\n");
                    return;
                }
            }
        }
    }
}

static void self_tx_rx_u2_thread_entry(void *parameter)
{
    static char rx_buffer[1024 + 1];

    while (1)
    {
        /* 从串口读取数据 */
        u2rx_length += rt_device_read(u2serial, 0, rx_buffer, 1000);
    }
}

static void self_tx_rx_u5_thread_entry(void *parameter)
{
    static char rx_buffer[1024 + 1];

    while (1)
    {
        /* 从串口读取数据 */
        u5rx_length += rt_device_read(u5serial, 0, rx_buffer, 500);
    }
}

static void self_tx_rx_test(void *parameter)
{
    static char tx_buffer[1024 + 1];
    for (uint32_t i = 0; i < sizeof(tx_buffer); i++)
        tx_buffer[i] = i;

    char *uart_name = "uart2";
    u2serial = rt_device_find(uart_name);
    if (!u2serial)
    {
        rt_kprintf("find %s failed!\n", uart_name);
        return;
    }
    rt_device_open(u2serial, RT_DEVICE_FLAG_RX_BLOCKING | RT_DEVICE_FLAG_TX_BLOCKING);

    char *uart5_name = "uart5";
    u5serial = rt_device_find(uart5_name);
    if (!u5serial)
    {
        rt_kprintf("find %s failed!\n", uart5_name);
        return;
    }
    rt_device_open(u5serial, RT_DEVICE_FLAG_RX_BLOCKING | RT_DEVICE_FLAG_TX_BLOCKING);

    rt_thread_startup(rt_thread_create("serial2", self_tx_rx_u2_thread_entry, RT_NULL, 2048, 6, 5));
    rt_thread_startup(rt_thread_create("serial5", self_tx_rx_u5_thread_entry, RT_NULL, 2048, 6, 5));

    for (uint32_t i = 0;; i++)
    {

        u2tx_length += rt_device_write(u2serial, 0, tx_buffer, 1000);
        u5tx_length += rt_device_write(u5serial, 0, tx_buffer, 500);
        rt_thread_mdelay(10);

        if (i % 100 == 0)
        {
            rt_kprintf("self_test, uart2: tx: %ld, rx: %ld\r\n", u2tx_length, u2rx_length);
            rt_kprintf("self_test, uart5: tx: %ld, rx: %ld\r\n", u5tx_length, u5rx_length);
            if (u2tx_length != u2rx_length || u5tx_length != u5rx_length)
            {
                rt_kprintf("self test error!!!\r\n");
                return;
            }
        }
    }
}

static int uart_test(void)
{
    rt_thread_startup(rt_thread_create("echo_test", echo_test, RT_NULL, 2048, 10, 5));
    rt_thread_startup(rt_thread_create("self_test", self_tx_rx_test, RT_NULL, 2048, 10, 5));

    return 0;
}

MSH_CMD_EXPORT_ALIAS(uart_test, uart_test, );

已在瑞萨RA8开发板测试,没问题

Rbb666 avatar Sep 10 '24 05:09 Rbb666

基于最新提交gd32使能中断后测试结果: image

Rbb666 avatar Sep 12 '24 02:09 Rbb666

dfs1.0里面这个函数是不是应该把红框部分放到上面,文件有ioctl函数的情况下依然会进不去文件本身的ioctl。 导致现在串口posix的非阻塞和阻塞实现只能在发送函数里面实现,理想情况下是用户调用fcntl时就切换阻塞和非阻塞模式。 image

Ryan-CW-Code avatar Oct 29 '24 08:10 Ryan-CW-Code

#8854 #8352 #8143 #3086

Ryan-CW-Code avatar Oct 29 '24 08:10 Ryan-CW-Code

现在驱动中关于ringbuff使用了大量的关中断,看了下ringbuff源码。 如果采用写满拒绝新数据策略,应该能实现单生产者和单消费者,这样能让ringbuff在无锁模式下正常运行,高速通讯丢包会小很多。 RTT官方目前也没看到关于这方面规范的说明,可以在这里讨论下怎么选择。

或许也可以把写满拒绝新数据策略覆盖旧数据策略选择权交给用户,这样的话要改的东西要多一点

Ryan-CW-Code avatar Oct 29 '24 11:10 Ryan-CW-Code

现在驱动中关于ringbuff使用了大量的关中断,看了下ringbuff源码。 如果采用写满拒绝新数据策略,应该能实现单生产者和单消费者,这样能让ringbuff在无锁模式下正常运行,高速通讯丢包会小很多。 RTT官方目前也没看到关于这方面规范的说明,可以在这里讨论下怎么选择。

或许也可以把写满拒绝新数据策略覆盖旧数据策略选择权交给用户,这样的话要改的东西要多一点

这块内容要不下次在社区例会讨论下?

Rbb666 avatar Nov 04 '24 02:11 Rbb666

现在驱动中关于ringbuff使用了大量的关中断,看了下ringbuff源码。 如果采用写满拒绝新数据策略,应该能实现单生产者和单消费者,这样能让ringbuff在无锁模式下正常运行,高速通讯丢包会小很多。 RTT官方目前也没看到关于这方面规范的说明,可以在这里讨论下怎么选择。 或许也可以把写满拒绝新数据策略覆盖旧数据策略选择权交给用户,这样的话要改的东西要多一点

这块内容要不下次在社区例会讨论下?

现在驱动中关于ringbuff使用了大量的关中断,看了下ringbuff源码。 如果采用写满拒绝新数据策略,应该能实现单生产者和单消费者,这样能让ringbuff在无锁模式下正常运行,高速通讯丢包会小很多。 RTT官方目前也没看到关于这方面规范的说明,可以在这里讨论下怎么选择。 或许也可以把写满拒绝新数据策略覆盖旧数据策略选择权交给用户,这样的话要改的东西要多一点

这块内容要不下次在社区例会讨论下?

可以呀,你们来主导就行,驱动规范这方面你们来主导最好。 目前是两种策略都实现了,DMA也狠狠心把乒乓缓冲加上了(原本不想加,修改的的东西太多太麻烦了)。

Ryan-CW-Code avatar Nov 04 '24 02:11 Ryan-CW-Code

现在驱动中关于ringbuff使用了大量的关中断,看了下ringbuff源码。 如果采用写满拒绝新数据策略,应该能实现单生产者和单消费者,这样能让ringbuff在无锁模式下正常运行,高速通讯丢包会小很多。 RTT官方目前也没看到关于这方面规范的说明,可以在这里讨论下怎么选择。 或许也可以把写满拒绝新数据策略覆盖旧数据策略选择权交给用户,这样的话要改的东西要多一点

这块内容要不下次在社区例会讨论下?

现在驱动中关于ringbuff使用了大量的关中断,看了下ringbuff源码。 如果采用写满拒绝新数据策略,应该能实现单生产者和单消费者,这样能让ringbuff在无锁模式下正常运行,高速通讯丢包会小很多。 RTT官方目前也没看到关于这方面规范的说明,可以在这里讨论下怎么选择。 或许也可以把写满拒绝新数据策略覆盖旧数据策略选择权交给用户,这样的话要改的东西要多一点

这块内容要不下次在社区例会讨论下?

可以呀,你们来主导就行,驱动规范这方面你们来主导最好。 目前是两种策略都实现了,DMA也狠狠心把乒乓缓冲加上了(原本不想加,修改的的东西太多太麻烦了)。

留个联系方式?下次开会拉上你~

Rbb666 avatar Nov 04 '24 11:11 Rbb666

留个联系方式?下次开会拉上你~ 发您b站私信了,微信号不方便放到公开区域

Ryan-CW-Code avatar Nov 05 '24 02:11 Ryan-CW-Code

这份修改思路合理.

cc-caixf avatar Jan 22 '25 01:01 cc-caixf

非常详实、也实用的PR,希望能尽快合并,赶上下一次发版

mr-cn avatar Mar 17 '25 05:03 mr-cn

非常详实、也实用的PR,希望能尽快合并,赶上下一次发版

这份PR您有在平台测试过吗?

Rbb666 avatar Mar 17 '25 05:03 Rbb666

非常详实、也实用的PR,希望能尽快合并,赶上下一次发版

这份PR您有在平台测试过吗?

暂时没有,我有STM32G4平台,需要帮助测试吗?有哪些部分可以帮上忙的?

mr-cn avatar Mar 17 '25 05:03 mr-cn

非常详实、也实用的PR,希望能尽快合并,赶上下一次发版

这份PR您有在平台测试过吗?

暂时没有,我有STM32G4平台,需要帮助测试吗?有哪些部分可以帮上忙的?

好呀,您可以列一下可以测试的硬件平台,我认为可以把作者的测试用例跑过就算通过了

Rbb666 avatar Mar 24 '25 12:03 Rbb666

惭愧,这段时间太忙了,很多事情缠身实在没有精力。 RTT更新了很多,这个PR也有很多冲突需要进行解决。 有愿意继续完善的欢迎贡献和提意见

立个flag,五一前或五一假期完善此PR

Ryan-CW-Code avatar Mar 24 '25 12:03 Ryan-CW-Code

惭愧,这段时间太忙了,很多事情缠身实在没有精力。 RTT更新了很多,这个PR也有很多冲突需要进行解决。 有愿意继续完善的欢迎贡献和提意见

立个flag,五一前或五一假期完善此PR

理解,有RTT需要协助帮忙的可以提出来,我们可以一起推进

Rbb666 avatar Mar 24 '25 12:03 Rbb666

这个例程能跑过的v2驱动大致就能使用了。 utest能跑过很多隐藏问题应该都可以发现

#include <board.h>
#include <rtthread.h>
#include <rtdevice.h>

#define echo_test_buffer_size (1024)
// 测试环境
// 串口2、3开启dma, 5、6不开启
// 缓冲区大小都为 128 字节

// echo test
// 3tx - 6rx
// 3rx - 6tx

// self test
// 2tx - 2rx
// 5tx - 5rx

static rt_device_t u2serial;
static rt_device_t u3serial;
static rt_device_t u5serial;
static rt_device_t u6serial;

static rt_uint32_t u2rx_length = 0;
static rt_uint32_t u2tx_length = 0;

static rt_uint32_t u3rx_length = 0;
static rt_uint32_t u3tx_length = 0;

static rt_uint32_t u5rx_length = 0;
static rt_uint32_t u5tx_length = 0;

static rt_uint32_t u6rx_length = 0;
static rt_uint32_t u6tx_length = 0;

static void echo_test_u3_thread_entry(void *parameter)
{
    char *uart_name = "uart3";
    static char rx_buffer[echo_test_buffer_size + 1];

    /* 查找串口设备 */
    u3serial = rt_device_find(uart_name);
    if (!u3serial)
    {
        rt_kprintf("find %s failed!\n", uart_name);
        return;
    }

    rt_device_open(u3serial, RT_DEVICE_FLAG_RX_BLOCKING | RT_DEVICE_FLAG_TX_BLOCKING);

    rt_ssize_t buf_datalen = 0;
    while (1)
    {
        rt_device_control(u3serial, RT_SERIAL_CTRL_GET_UNREAD_BYTES_COUNT, (void *)&buf_datalen);
        int32_t recbLen = rt_device_read(u3serial, 0, rx_buffer, buf_datalen > 0 ? buf_datalen : 1);
        if (recbLen > 0)
        {
            u3rx_length += recbLen;
            u3tx_length += rt_device_write(u3serial, 0, rx_buffer, recbLen);
        }
    }
}

static void echo_test_u6_thread_entry(void *parameter)
{
    static char rx_buffer[echo_test_buffer_size + 1];
    rt_ssize_t buf_datalen = 0;
    while (1)
    {
        rt_device_control(u6serial, RT_SERIAL_CTRL_GET_UNREAD_BYTES_COUNT, (void *)&buf_datalen);
        int32_t recbLen = rt_device_read(u6serial, 0, rx_buffer, buf_datalen > 0 ? buf_datalen : 1);
        if (recbLen > 0)
        {
            u6rx_length += recbLen;
        }
    }
}

static void echo_test(void *parameter)
{
    static char tx_buffer[echo_test_buffer_size];

    for (uint32_t i = 0; i < sizeof(tx_buffer); i++)
        tx_buffer[i] = i;

    char *uart_name = "uart6";
    u6serial = rt_device_find(uart_name);
    if (!u6serial)
    {
        rt_kprintf("find %s failed!\n", uart_name);
        return;
    }

    rt_device_open(u6serial, RT_DEVICE_FLAG_RX_BLOCKING | RT_DEVICE_FLAG_TX_BLOCKING);

    rt_thread_startup(rt_thread_create("serial3", echo_test_u3_thread_entry, RT_NULL, 2048, 7, 5));
    rt_thread_startup(rt_thread_create("serial6", echo_test_u6_thread_entry, RT_NULL, 2048, 7, 5));

    uint32_t count = 0;
    uint32_t sendTotalCount = 0;
    while (1)
    {
        count++;

        // 不定长发送数据
        uint32_t sendCount = rand() % echo_test_buffer_size;
        u6tx_length += rt_device_write(u6serial, 0, tx_buffer, sendCount);
        sendTotalCount += sendCount;

        // 等待交叉发送完毕
        rt_thread_mdelay(15);

        if (count % 100 == 0)
        {
            srand(rt_tick_get());
            rt_kprintf("echo, uart3: tx: %ld, rx: %ld\r\n", u3tx_length, u3rx_length);
            rt_kprintf("echo, uart6: tx: %ld, rx: %ld\r\n", u6tx_length, u6rx_length);
            if (u3tx_length != u3rx_length || u6tx_length != u6rx_length || u3tx_length != u6tx_length)
            {
                rt_kprintf("echo test error!!!\r\n");
                return;
            }

            // 前面已经判断过4个值是否相等了
            if (u3tx_length != sendTotalCount)
            {
                rt_kprintf("发送数据不对等,echo test error!!!\r\n");
                return;
            }
        }
    }
}

static void self_tx_rx_u2_thread_entry(void *parameter)
{
    static char rx_buffer[1024 + 1];

    while (1)
    {
        u2rx_length += rt_device_read(u2serial, 0, rx_buffer, 1000);
    }
}

static void self_tx_rx_u5_thread_entry(void *parameter)
{
    static char rx_buffer[1024 + 1];

    while (1)
    {
        u5rx_length += rt_device_read(u5serial, 0, rx_buffer, 500);
    }
}

/**
 * @brief 测试思路,创建u2和u5线程接收自己的数据并记录接收长度,在当前线程u2和u5发送数据,等待数据发送完毕,判断发送数据长度和接收数据长度是否一致
 *
 * @param parameter
 */
static void self_tx_rx_test(void *parameter)
{
    static char tx_buffer[1024 + 1];
    for (uint32_t i = 0; i < sizeof(tx_buffer); i++)
        tx_buffer[i] = i;

    char *uart_name = "uart2";
    u2serial = rt_device_find(uart_name);
    if (!u2serial)
    {
        rt_kprintf("find %s failed!\n", uart_name);
        return;
    }
    rt_device_open(u2serial, RT_DEVICE_FLAG_RX_BLOCKING | RT_DEVICE_FLAG_TX_BLOCKING);

    char *uart5_name = "uart5";
    u5serial = rt_device_find(uart5_name);
    if (!u5serial)
    {
        rt_kprintf("find %s failed!\n", uart5_name);
        return;
    }
    rt_device_open(u5serial, RT_DEVICE_FLAG_RX_BLOCKING | RT_DEVICE_FLAG_TX_BLOCKING);

    rt_thread_startup(rt_thread_create("serial2", self_tx_rx_u2_thread_entry, RT_NULL, 2048, 6, 5));
    rt_thread_startup(rt_thread_create("serial5", self_tx_rx_u5_thread_entry, RT_NULL, 2048, 6, 5));

    for (uint32_t i = 0;; i++)
    {
        u2tx_length += rt_device_write(u2serial, 0, tx_buffer, 1000);
        u5tx_length += rt_device_write(u5serial, 0, tx_buffer, 500);

        rt_device_control(u2serial, RT_SERIAL_CTRL_TX_FLUSH, RT_NULL);
        rt_device_control(u5serial, RT_SERIAL_CTRL_TX_FLUSH, RT_NULL);

        // rt_thread_mdelay(10);

        if (i % 100 == 0)
        {
            rt_kprintf("self_test, uart2: tx: %ld, rx: %ld\r\n", u2tx_length, u2rx_length);
            rt_kprintf("self_test, uart5: tx: %ld, rx: %ld\r\n", u5tx_length, u5rx_length);
            if (u2tx_length != u2rx_length || u5tx_length != u5rx_length)
            {
                rt_kprintf("self test error!!!\r\n");
                return;
            }

            if (u5rx_length * 2 != u2rx_length)
            {
                rt_kprintf("数据不等长 self test error!!!\r\n");
                return;
            }
        }
    }
}

//
// uart_blocking_rx
// uart_blocking_tx
// testcases.drivers.uart_flush_rx
// testcases.drivers.uart_flush_txb
// testcases.drivers.uart_flush_txnb
// testcases.drivers.uart_get_unread_bytes_count
// uart_nonblocking_rx
// uart_nonblocking_tx
// testcases.drivers.uart_overflow_rxb_txb
// testcases.drivers.uart_rxb_txb
// testcases.drivers.uart_rxb_txnb
// testcases.drivers.uart_rxnb_txb
// testcases.drivers.uart_rxnb_txnb
// testcases.drivers.uart_timeout_rxb_txb

static int uart_test(void)
{
    rt_thread_startup(rt_thread_create("echo_test", echo_test, RT_NULL, 2048, 10, 5));
    rt_thread_startup(rt_thread_create("self_test", self_tx_rx_test, RT_NULL, 2048, 10, 5));
    return 0;
}

MSH_CMD_EXPORT_ALIAS(uart_test, uart_test, );

Ryan-CW-Code avatar Apr 18 '25 03:04 Ryan-CW-Code