RTT串口V1版本的使用分析及问题排查指南(三)

RT-Thread操作系统导致高速数据丢失 RT-Thread操作系统在STM32F407上运行,捕获、多串口等高速数据丢失情况分析 阅读详情

本文由RT-Thread论坛用户123原创发布,原文:https://club.rt-thread.org/ask/article/2904.html

应用层的串口问题

应用层使用串口时,需要先指定以何种方式(轮询、中断或者DMA)去打开串口,然后再进行串口的操作。这部分相关的内容在第一章已经说过,这里主要用来总结串口使用时遇到的一些问题。

串口注册时候的默认规则慢慢道来:

串口注册的默认规则

我们都知道,串口设备需要先注册,才能通过rt_device_open API 去访问,那么串口注册时候的默认规则是什么呢?看串口驱动的注册代码如下所示:

int rt_hw_usart_init(void)
{
    rt_size_t obj_num = sizeof(uart_obj) / sizeof(struct stm32_uart);
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
    rt_err_t result = 0;

    stm32_uart_get_dma_config();                                     /* (1)*/

    for (int i = 0; i < obj_num; i++)
    {
        /* init UART object */
        uart_obj[i].config = &uart_config[i];
        uart_obj[i].serial.ops    = &stm32_uart_ops;
        uart_obj[i].serial.config = config;                          /* (2) */

        /* register UART device */
        result = rt_hw_serial_register(&uart_obj[i].serial, uart_obj[i].config->name,
                                       RT_DEVICE_FLAG_RDWR
                                       | RT_DEVICE_FLAG_INT_RX
                                       | RT_DEVICE_FLAG_INT_TX
                                       | uart_obj[i].uart_dma_flag
                                       , NULL);                     /* (3) */
        RT_ASSERT(result == RT_EOK);
    }

    return result;
}

第(1)点:获取串口对应的dma配置,当配置串口支持dma发送或者dma接收时,在这个函数里面将会记录串口的模式配置,在后边初始化串口设备的时候,再对串口进行DMA配置。

第(2)点:配置默认的参数信息,例如波特率,停止位,奇偶校验等,这里选择的是统一配置默认的参数RT_SERIAL_CONFIG_DEFAULT,该参数在serial.h文件中定义,默认配置如下,在这里,其中有一个参数RT_SERIAL_RB_BUFSZ,这个参数的意思是设置串口的接收缓冲区大小,它的默认值是64字节,需要注意的是,串口参数里面没有发送缓冲区的设置。

/* Default config for serial_configure structure */
#define RT_SERIAL_CONFIG_DEFAULT           \
{                                          \
    BAUD_RATE_115200, /* 115200 bits/s */  \
    DATA_BITS_8,      /* 8 databits */     \
    STOP_BITS_1,      /* 1 stopbit */      \
    PARITY_NONE,      /* No parity  */     \
    BIT_ORDER_LSB,    /* LSB first sent */ \
    NRZ_NORMAL,       /* Normal mode */    \
    RT_SERIAL_RB_BUFSZ, /* Buffer size */  \
    0                                      \
}

第(3)点:注册串口设备,这里主要关注的是FLAG标志,可以看到串口注册的时候,默认加上了中断发送和中断接收的支持,又由于串口隐性支持轮询发送和轮询接收,因此只需要配置DMA相关即可,也就是uart_obj[i].uart_dma_flag。

总结

串口注册时候的默认规则:

1. 串口参数配置按照RT_SERIAL_CONFIG_DEFAULT ,其中串口接收缓冲区大小由RT_SERIAL_RB_BUFSZ决定,默认大小为64字节,串口没有发送缓冲区大小的设置。

2. 串口默认支持中断和轮询的收发模式。

串口打开的默认规则:

串口打开的时候使用rt_device_open(),其中参数oflags支持下列取值 (可以采用或的方式支持多种取值):

#define RT_DEVICE_FLAG_STREAM       0x040     /* 流模式      */
/* 接收模式参数 */
#define RT_DEVICE_FLAG_INT_RX       0x100     /* 中断接收模式 */
#define RT_DEVICE_FLAG_DMA_RX       0x200     /* DMA 接收模式 */
/* 发送模式参数 */
#define RT_DEVICE_FLAG_INT_TX       0x400     /* 中断发送模式 */
#define RT_DEVICE_FLAG_DMA_TX       0x800     /* DMA 发送模式 */

串口数据接收和发送数据的模式分为 3 种:中断模式、轮询模式、DMA 模式。在使用的时候,这 3 种模式只能若串口的打开参数 oflags 没有指定使用中断模式或者 DMA 模式,则默认使用轮询模式。

总结

串口打开的默认规则:

若串口的打开参数 oflags 没有指定使用中断模式或者 DMA 模式,则默认使用轮询模式。

串口读写的默认规则:
先说串口的读数据

我们一般会选择串口中断接收或者是串口DMA接收(好像没有轮询死等串口数据的场景吧,这里不再讨论这个轮询读的方式),这里比较统一,无论是哪种方式,都是非阻塞的接收模式。即,我们可能会通过一个信号量或者消息队列,当接收到数据的时候,就唤醒当前线程进行数据的读取。这里我们参照串口的文档中心的例子:中断接收 和 DMA接收,这两个demo,一个是使用信号量,一个是使用消息队列,来完成串口数据的读取的。

因此可以统一地说,串口的读数据接口,是按照非阻塞的方式进行接收的,当读取不到数据的时候,会将当前线程挂起,以免浪费CPU资源。

再说串口的写数据

串口的写数据会选择三种模式,轮询、中断、DMA。

串口的的轮询写数据这个模式,是我们用的比较多的一个场景,比如上一章节说的FinSH的输出,或者说是rt_kprintf的输出。因此显而易见的,轮询模式的写数据接口是阻塞的方式进行的。

重点来了:串口的中断模式,这个模式其实是有一些问题的,我们理想中的中断发送,其实是先打开发送的中断使能,然后在中断服务函数内把数据发送出去,等数据发送空中断后再发送下一个字节的数据,依次单字节循环发送即将数据发送完成。串口V1的中断发送是怎么实现的呢?我们来看一下代码:

rt_inline int _serial_int_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length)
{
    int size;
    size = length;
    while (length)
    {
        /*
         * to be polite with serial console add a line feed
         * to the carriage return character
         */
        if (*data == '\n' && (serial->parent.open_flag & RT_DEVICE_FLAG_STREAM))
        {
            if (serial->ops->putc(serial, '\r') == -1)
            {
                rt_completion_wait(&(tx->completion), RT_WAITING_FOREVER);
                continue;
            }
        }
        if (serial->ops->putc(serial, *(char*)data) == -1)
        {
            rt_completion_wait(&(tx->completion), RT_WAITING_FOREVER);
            continue;
        }
        data ++; length --;
    }

    return size - length;
}

比较清晰的看到,中断发送最终是调用的putc接口,而serial->ops->putc其实代表的就是stm32_putc:

static int stm32_putc(struct rt_serial_device *serial, char c)
{
    struct stm32_uart *uart;
    RT_ASSERT(serial != RT_NULL);

    uart = rt_container_of(serial, struct stm32_uart, serial);
    UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_TC);
#if defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32WL) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32F0) \
    || defined(SOC_SERIES_STM32L0) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32H7) \
    || defined(SOC_SERIES_STM32G4) || defined(SOC_SERIES_STM32MP1) || defined(SOC_SERIES_STM32WB)
    uart->handle.Instance->TDR = c;
#else
    uart->handle.Instance->DR = c;
#endif
    while (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_TC) == RESET);
    return 1;
}

这个函数里面,是相当于将数据发送给了TDR数据发送寄存器,然后通过while(1)死等的方式,直到数据的发送完成。因此串口中断发送模式,其实就是和串口轮询发送模式是一样的,并没有用到中断发送的功能。

最后说串口DMA发送模式。由上面讲解我们知道,串口发送轮询模式和中断模式,其实都是阻塞发送模式,这个DMA发送模式则不是这样的。结合第一章节DMA发送的讲解,我们知道DMA发送最终调用的是stm32_dna_transmit,我们看函数代码:

static rt_size_t stm32_dma_transmit(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size, int direction)
{
    if (RT_SERIAL_DMA_TX == direction)
    {
        if (HAL_UART_Transmit_DMA(&uart->handle, buf, size) == HAL_OK)
        {
            return size;
        }
    }
    return 0;
}

这里是调用了HAL_UART_Transmit_DMA函数,有兴趣的可以再跟踪一下这个函数,我这里直接说结论了,这个函数是将buf数据直接传递给数据发送端,然后就直接返回了,也就是说,当函数返回的时候,其实数据并没有发送完成,这就意味着,串口DMA发送模式,其实是一个非阻塞发送模式。

那么会存在哪些问题呢?

第一呢,是模式不够统一,其他两个模式都是阻塞发送,这里突然变成了非阻塞发送,这样就会使得用户编写的上层应用在模式改变的情况下,无法做到行为一致。

第二呢,是数据紊乱,当应用程序模式为轮询时正常工作,然后切换成DMA模式后出现数据紊乱的情况。这部分内容我在第一章DMA发送部分也提到过的,

dma.png

当时说的是,”该缓冲区内容被意外修改了“,为什么会被意外修改呢,就是因为发送接口返回了,而数据并没有发送完成。由于发送接口传递的是缓冲区指针,因此改变数据的内容时,DMA发送的地址并不会变,这样的话就相当于直接修改了DMA发送的数据内容,导致数据发送数据错误、丢包等问题。

总结

串口的读数据接口:是按照非阻塞的方式进行接收的。

串口的写数据接口:中断模式实质上是轮询模式的一种方式,并未充分发挥中断的优势;中断和轮询都是阻塞发送模式,而DMA模式是非阻塞发送模式。如果使用不当,将会使得发送数据容易错误、丢包等问题。

其他

最后再说一下其他方面的问题:

  1. 用户使用串口的时候,有时候开发板并未做到抗干扰的保护措施,因此一定要注意,需要将串口引脚设置为上拉模式。有时候的错误干扰将会导致串口中断行为被破坏,导致串口数据无法正常工作。为了验证这种情况,也可以通过串口的错误标志来判断是否有这样的情况产生。下面给出一段测试代码,感兴趣的可以测试下,当串口为浮空时和串口为上拉时候的抗干扰能力。这段测试代码可以直接放到drv_usart.c的uart_isr中:相关ISSUE

    static void uart_isr(struct rt_serial_device *serial)
    {
        struct stm32_uart *uart;
    
        RT_ASSERT(serial != RT_NULL);
        uart = rt_container_of(serial, struct stm32_uart, serial);
        if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_ORE) != RESET)
        {
            LOG_E("(%s) serial device Overrun error!", serial->parent.parent.name);
            __HAL_UART_CLEAR_OREFLAG(&uart->handle);
        }
        if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_NE) != RESET)
        {
            LOG_E("(%s) serial device Noise error!", serial->parent.parent.name);
            __HAL_UART_CLEAR_NEFLAG(&uart->handle);
        }
        if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_FE) != RESET)
        {
            LOG_E("(%s) serial device Framing error!", serial->parent.parent.name);
            __HAL_UART_CLEAR_FEFLAG(&uart->handle);
        }
        if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_PE) != RESET)
        {
            LOG_E("(%s) serial device Parity error!", serial->parent.parent.name);
            __HAL_UART_CLEAR_PEFLAG(&uart->handle);
        }
    
        if ((__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_RXNE) != RESET) &&
                (__HAL_UART_GET_IT_SOURCE(&(uart->handle), UART_IT_RXNE) != RESET))
        {
    ... ...
    
    
  2. 既然串口V1版本有这些问题,那么怎么修改呢?是的,既然命名为串口V1,那肯定是有串口V2啦,串口V2解决了上述从一些问题,使得用户使用上更加明确(串口V2版本是本人写的)。当前关于串口V2版本的介绍比较少,只有文档中心的[UART 设备 v2 版本],也是本人写的使用教程。后续计划是,整理总结更多的串口V2的文档资料,包括串口V2的原理分析、串口V2版本与V1版本对比、以及串口V2版本适配指南等文档,方便大家一起适配共同使用,结合大家的力量,发挥开源的精神。

更多文章:
RTT串口V1版本的使用分析及问题排查指南(一)
RTT串口V1版本的使用分析及问题排查指南(二)
RTT串口V1版本的使用分析及问题排查指南(三)

RT-Thread uart2串口dma idle接收不断帧 STM32F407 RT-Thread uart2串口dma idle接收不断帧 阅读详情

相关推荐

DAPLink V0.0.20上位机实战:手把手教你配置RTT Viewer调试STM32(附常见问题解决)

本文详细介绍了如何使用DAPLink V0.0.20上位机配置RTT Viewer调试STM32。通过手把手实战教学,涵盖环境搭建、RTT核心原理、多开发环境(Keil/IAR/STM32CubeIDE)下的代码集成与配置,并重点解析了利用ELF文件提升连接可靠性的方法。文章还提供了连接失败、无输出等常见问题的排查思路与解决方案,帮助开发者高效利用这一低成本、高速的调试工具。

apple5的专栏 788

RT-Thread 串口应用指南

本应用笔记描述了如何使用 RT-Thread 的串口设备,包括串口配置、设备操作接口的应用。并给出了在正点原子 STM32F4 探索 者开发板上验证的代码示例。

WCH CH32V303RCT6 SDI Printf虚拟串口:原理剖析与RTT式调试实战

本文深入剖析了WCH CH32V303RCT6的SDI Printf虚拟串口功能,详细介绍了其原理、配置步骤及与SEGGER RTT的对比。通过实战案例展示了如何在资源受限的项目中高效使用这一调试工具,并提供了性能优化和常见问题解决方案。

weixin_33721427的博客 829

RT-Thread系统 STM32 DMA驱动接收数据包被自动拆包的解决办法,实现不定长数据包完整接收

前言 使用串口解析协议数据,肯定会使用DMA的空闲中断接收不定长数据,不需要在串行数据中解析协议头,rtthread官方提供的串口驱动支持的DMA空闲中断的接收,但是串口rx_fifo不管多大会存在溢出,由于官方库在数据溢出时,向中间件层推送了数据,导致应用层接收的数据会被拆包,影响协议的解析。 开启DMA功能 1.使用DMA功能首先需要在RT-Thread Settings中打开DMA驱动支持,并根据接收的数据包最大长度适当增加缓冲区大小,如果超出缓冲区的数据将会覆盖起始数据。 2.在board.h中打

songxunwen的博客 1773

JLink RTT 完整教程

JLink RTT是一种高效的嵌入式调试技术,通过RAM缓冲区实现MCU与主机的双向高速通信。相比传统串口调试,RTT不占用硬件资源、传输速度快(可达1Mbps以上)、不影响程序实时性,且支持多通道日志分类。本文详细介绍了RTT的核心优势、适用场景及完整实现步骤,包括源码移植、API调用和日志查看方法,并提供了printf重定向、多通道使用等进阶技巧。该技术兼容各类ARM内核芯片,只需J-Link仿真器即可快速部署,能显著提升嵌入式开发调试效率。

z521524的博客 808

告别传统串口调试:利用JLink RTT实现高效日志输出的完整指南

本文详细介绍了如何利用JLink RTT技术替代传统串口调试,实现高效日志输出。通过零硬件成本的RTT技术,开发者可以快速定位问题,提升调试效率。文章包含环境搭建、工程移植、高级应用技巧及常见问题解决方案,特别适合嵌入式开发人员参考。

weixin_29168393的博客 249

RT-Thread Studio 串口终端没有输出

RT-Thread Studio 串行终端没有输出

weixin_38743772的博客 2188

告别串口!用STM32CubeMX+J-Link RTT三分钟搭建极速调试环境(Keil版)

本文详细介绍了如何使用STM32CubeMX和J-Link RTT技术快速搭建高效调试环境,替代传统串口调试。通过性能对比和实战步骤,展示了RTT在速度、资源占用和多任务支持方面的显著优势,帮助开发者提升嵌入式调试效率。

weixin_42567497的博客 364

RTT串口V1版本的使用分析及问题排查指南(一)

本文由RT-Thread论坛用户123原创发布:https://club.rt-thread.org/ask/article/2894.html RTT串口V1版本的使用分析及问题排查指南(一) 简述 无论是刚接触 RT-Thread 的新手,还是经验老道的大牛们,他们使用 RT-Thread 的时候,使用最频繁最广泛的外设,想必也非串口设备莫属。 回想大家在移植一个新的BSP或者芯片时,如何验证是否移植成功呢?是的,msh控制台窗口走一波 RTT 的 logo 信息,输出成功了就基本代表移植成功了。如下所

rtthreadiotos的博客 1423

JLink V9虚拟串口功能全攻略:告别USB转TTL,一根线搞定SWD调试和串口通信

本文详细介绍了JLink V9虚拟串口功能的全面应用指南,通过单线实现SWD调试和串口通信的双重功能,显著提升嵌入式开发效率。内容涵盖硬件连接、驱动配置、跨平台适配及高级优化技巧,特别适合需要高稳定性的工业场景。

spice的博客 788

STM32CUBEIDE调试神器:用J-Link RTT替代串口打印,效率翻倍(附完整配置流程)

本文详细介绍了如何在STM32CUBEIDE中使用J-Link RTT技术替代传统UART串口打印,实现调试效率的显著提升。通过对比RTT与UART的性能差异,解析RTT技术原理,并提供完整的配置流程和代码实现,帮助嵌入式开发者快速掌握这一高效调试工具。文章还包含性能优化建议和常见问题解决方案,特别适合实时控制系统和高频数据采集场景。

weixin_33712987的博客 435

RTT串口v1使用分析以及问题排查指南 (二)

本文由RT-Thread论坛账号123 原创发布:https://club.rt-thread.org/ask/article/2898.html RTT串口V1版本的使用分析及问题排查指南(二) 串口相关问题解析 承接RTT串口V1版本的使用分析及问题排查指南(一)文章。 结合串口使用过程的反馈信息,本章节将结合 FinSH组件的串口相关问题 和 应用层上使用串口设备的相关问题 这两个方面进行分析。这一章节探讨FinSH组件上的串口问题。 由于串口外设所涉及的方面过于广泛,分析时不可能涵盖全部应用场景(

rtthreadiotos的博客 1268

SEGGER JLink隐藏功能全解锁:RTT比UART快3倍的调试方案(V6.44b实测)

本文深度解析SEGGER J-Link的RTT(Real Time Transfer)调试方案,通过实测数据对比,揭示其相比传统UART在速度上的巨大优势(实测快3倍以上)及低CPU占用特性。文章详细探讨了V6.44b等版本的实际配置差异、大数据量场景下的缓冲区优化策略,并介绍了RTT在交互式命令控制、系统监控等高阶调试场景中的应用,为嵌入式工程师提供了一套高效、现代的调试方法学。

xray4的博客 893

工程调试利器:RTT(Real-Time Transfer)保姆级使用教程

摘要: RTT(Real-Time Transfer)是SEGGER推出的高速嵌入式调试技术,利用调试通道实现MB/s级数据传输,无需占用串口引脚。本教程详解RTT优势、文件结构及集成方法:1)硬件需J-Link调试器和支持RTT的MCU;2)软件需安装V864版J-Link驱动包;3)工程集成时添加SEGGER_RTT核心文件,配置缓冲区大小。RTT支持多通道双向通信,适用于实时日志、性能分析等场景,显著提升调试效率。注意根据工具链选择正确的系统调用文件,并合理优化缓冲区配置。

曾记否@ 5277

STM32F4开发实战:用Segger Embedded Studio的RTT功能替代串口打印,调试效率翻倍(附CubeMX配置)

本文详细介绍了如何在STM32F4开发中使用Segger Embedded Studio的RTT功能替代传统串口打印,显著提升调试效率。通过STM32CubeMX配置和Segger Embedded Studio的工程设置,开发者可以轻松实现高速日志输出、终端输入和实时变量监控,无需额外硬件支持。文章还提供了高级调试技巧和常见问题解决方案,帮助开发者充分利用RTT的强大功能。

weixin_30698297的博客 670

H7-TOOL全功能RTT Viewer实战:如何用SWD两线替代串口调试(含代码移植指南)

本文详细介绍了H7-TOOL全功能RTT Viewer如何通过SWD两线替代传统串口调试,提供完整的解决方案。文章涵盖RTT技术原理、代码移植指南及多端协同调试技巧,特别适合嵌入式开发者在引脚资源受限场景下的高效调试。H7-TOOL的RTT Viewer功能显著提升调试效率,释放串口资源。

weixin_29166013的博客 181

告别串口占用!用JLink RTT Viewer调试NRF52832蓝牙项目(附完整SDK配置流程)

本文详细介绍了如何使用JLink RTT Viewer调试NRF52832蓝牙项目,避免串口占用问题。通过完整的SDK配置流程和实战技巧,开发者可以高效实现RTT打印调试,提升开发效率。文章还对比了RTT与串口打印的优势,并提供了常见问题解决方案。

weixin_42570484的博客 149

RT-Thread STM UART配置后,串口RX接收到无效数据问题解决方法

在使用RT-Thread操作系统配置STM32的串口通信时,如果串口RX引脚悬空,可能会导致接收到无效数据的问题。当使用RT-Thread操作系统配置STM32的串口通信时,如果串口的接收引脚(RX)没有连接任何外部设备,或者连接的设备未发送有效数据时,串口接收到的数据可能会是无效的。这是因为悬空的RX引脚会受到噪声干扰,导致接收到错误的数据。在使用RT-Thread操作系统的STM32单片机中配置串口通信时,有时会遇到串口接收(RX)引脚悬空导致接收到无效数据的问题。

QoyOle的博客 981
上一篇: RTT串口v1使用分析以及问题排查指南 (二)
下一篇: 串口框架V1和V2版本对比
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值