深入剖析RT-Thread串口驱动:基于STM32H750的FinSH Shell全链路Trace分析与实战解密(上)

发布于:2025-08-05 ⋅ 阅读:(12) ⋅ 点赞:(0)

0. 概述

这是cherryusb代码trace分析系列文章之七。

RT-Thread串口驱动框架与FinSH Shell运行机制深度解析:针对STM32H750 ART-PI平台,本文独辟蹊径采用创新的代码trace分析方法,破解庞大串口框架下的复杂运行逻辑。通过精确trace日志与drv_usart_v2.c、dev_serial_v2.c、kservice.c等核心驱动源码的深度结合,系统揭示了从rt_hw_usart_init串口硬件初始化、rt_console_set_device控制台设备配置、rt_kprintf调试输出机制到FinSH Shell交互的全链路执行流程。

文章特别抓取了系统启动过程中7次rt_kprintf调用的轮询发送机制细节,深入剖析UART4中断处理与FinSH Shell的完整交互过程,包括rt_ringbuffer缓冲区管理、rt_completion同步机制、NVIC中断优先级配置等关键技术点。通过trace分析,完整呈现了FinSH Shell与串口通信的底层细节,精确捕获了所有关键中断处理流程和时序关系。

本文为工程师和嵌入式爱好者提供了宝贵的技术参考,帮助他们深入理解RT-Thread底层串口通信机制,并能够针对性修改代码以适应板载串口、USB CDC_ACM等不同接口需求。文档以实际trace数据为基础,结合精确的函数调用链分析,为RT-Thread串口驱动框架的定制化开发和问题定位提供了强有力的技术支撑。

1. 运行的输入与输出

  • 我们的处理方法是, 首先在如下与串口驱动和finsh有关的c源文件上, 每个函数开头和关键节点处, 用my_printf和my_printf2进行trace record. 这些文件包括:
    drv_usart_v2.c
    dev_serial_v2.c
    kservice.c
    ringbuffer.c
    device.c
    shell.c
  • STM32H750 ART-PI平台的RT-Thread配置为最精简模式, 只保留串口, 其它都不开启
  • 烧录板子后, 正常输出开机rt-thread的log, 然后运行一个命令"list device\r\n". 输出结果如下:
[0m[D/drv.sdram] sdram init success, mapped at 0xC0000000, size is 33554432 bytes, data width is 16[0m

 \ | /
- RT -     Thread Operating System
 / | \     5.2.0 build Aug  3 2025 14:32:02
 2006 - 2024 Copyright by RT-Thread team
msh >list device
device           type         ref count
-------- -------------------- ----------
uart4    Character Device     2       
pin      Pin Device           0       
msh >
msh >
  • 过10秒后, trace record记录完毕, 开始打印trace到的log. 这个case的完整log文件有1723行. 我们以附录的方式提供.

2. 运行过程流程图

在这里插入图片描述

2.1 rt_hw_usart_init的执行

  • rt_hw_usart_init是drv_common.c中rt_hw_board_init函数中实现的
/**
 * This function will initial STM32 board.
 */
rt_weak void rt_hw_board_init(void)
{
#ifdef BSP_SCB_ENABLE_I_CACHE
    /* Enable I-Cache---------------------------------------------------------*/
    SCB_EnableICache();
#endif

#ifdef BSP_SCB_ENABLE_D_CACHE
    /* Enable D-Cache---------------------------------------------------------*/
    SCB_EnableDCache();
#endif

    /* HAL_Init() function is called at the beginning of the program */
    HAL_Init();

    /* System clock initialization */
    SystemClock_Config();

#if defined(RT_USING_HEAP)
    /* Heap initialization */
    rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END);
#endif

#ifdef RT_USING_PIN
    rt_hw_pin_init();
#endif

#ifdef RT_USING_SERIAL
    rt_hw_usart_init();
#endif

#if defined(RT_USING_CONSOLE) && defined(RT_USING_DEVICE)
    /* Set the shell console output device */
    rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
#endif

#if defined(RT_USING_CONSOLE) && defined(RT_USING_NANO)
    extern void rt_hw_console_init(void);
    rt_hw_console_init();
#endif

#ifdef RT_USING_COMPONENTS_INIT
    /* Board underlying hardware initialization */
    rt_components_board_init();
#endif
}
  • 经过对trace log分析的解读, rt_hw_usart_init的函数调用链如下所示
/*来自drv_common.c中的rt_hw_board_init*/
/*
rt_hw_usart_init
    |-> stm32_uart_get_config
    |-> rt_hw_serial_register-> rt_device_register-> rt_device_find
*/
  • trace到的运行log如下
drv_usart_v2.c    :1380| rt_hw_usart_init                      
drv_usart_v2.c    :1384| rt_hw_usart_init                                                go=> stm32_uart_get_config
drv_usart_v2.c    :972 | stm32_uart_get_config                 
drv_usart_v2.c    :1392| rt_hw_usart_init                                                go=> rt_hw_serial_register
dev_serial_v2.c   :1543| rt_hw_serial_register                 
dev_serial_v2.c   :1567| rt_hw_serial_register                                           go=> rt_device_register
device.c          :72  | rt_device_register                    
device.c          :73  | rt_device_register                                              uart4
device.c          :74  | rt_device_register                                3(0x00000003) <= flags
device.c          :78  | rt_device_register                                              go=> rt_device_find
device.c          :130 | rt_device_find                        
device.c          :131 | rt_device_find                                                  uart4

2.2 rt_console_set_device的执行(为了实现rt_kprintf)

  • rt_console_set_device是drv_common.c中rt_hw_board_init函数中实现的, 上面已经列出来了. 这个函数主要是为了实现rt_kprintf, 在系统调度没有启动之前就能运行, 打印系统初始化过程日志.
  • 经过对trace log分析的解读, rt_console_set_device的函数调用链如下所示
/*来自drv_common.c中的rt_hw_board_init*/
/*
rt_console_set_device
    |-> rt_device_find
    |-> rt_device_open
        |-> device_init->rt_serial_init->stm32_configure->HAL_UART_Init
        |-> device_open->rt_serial_open->rt_serial_rx_enable
                                            |-> rt_ringbuffer_init
                                            |-> stm32_control-> stm32_control-> NVIC_EnableIRQ
                                        |->rt_serial_tx_enable
                                            |-> stm32_control
                                            |-> rt_ringbuffer_init
                                            |-> rt_completion_init
*/
  • trace到的运行log如下
kservice.c        :192 | rt_console_set_device                 
kservice.c        :194 | rt_console_set_device                                           go=> rt_device_find
device.c          :130 | rt_device_find                        
device.c          :131 | rt_device_find                                                  uart4
kservice.c        :207 | rt_console_set_device                                           go=> rt_device_open
device.c          :235 | rt_device_open                        
device.c          :236 | rt_device_open                                                  uart4
/*rt_console_set_device用0x43打开uart4*/
/*oflag=0x43=> RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_STREAM*/
device.c          :237 | rt_device_open                                   67(0x00000043) <= oflag
device.c          :249 | rt_device_open                                                  go=> device_init
dev_serial_v2.c   :979 | rt_serial_init                        
drv_usart_v2.c    :120 | stm32_configure                       
drv_usart_v2.c    :197 | stm32_configure                                                 go=> HAL_UART_Init
device.c          :276 | rt_device_open                                                  go=> device_open
dev_serial_v2.c   :1006| rt_serial_open                        
dev_serial_v2.c   :1007| rt_serial_open                                   67(0x00000043) <= oflag
dev_serial_v2.c   :1043| rt_serial_open                                                  go=> rt_serial_rx_enable
dev_serial_v2.c   :800 | rt_serial_rx_enable                   
dev_serial_v2.c   :801 | rt_serial_rx_enable                            8192(0x00002000) <= rx_oflag
dev_serial_v2.c   :833 | rt_serial_rx_enable                                             go=> rt_ringbuffer_init
ringbuffer.c      :43  | rt_ringbuffer_init                    
ringbuffer.c      :44  | rt_ringbuffer_init                              256(0x00000100) <= size
dev_serial_v2.c   :846 | rt_serial_rx_enable                                             go=> serial->ops->control
drv_usart_v2.c    :208 | stm32_control                         
drv_usart_v2.c    :209 | stm32_control                                     3(0x00000003) <= cmd/*CONFIG*/
drv_usart_v2.c    :213 | stm32_control                                  8192(0x00002000) <= ctrl_arg/*RT_DEVICE_FLAG_RX_NON_BLOCKING*/
drv_usart_v2.c    :232 | stm32_control                                   256(0x00000100) <= ctrl_arg/*RT_DEVICE_FLAG_INT_RX*/
drv_usart_v2.c    :293 | stm32_control                                                   cmd: RT_DEVICE_CTRL_CONFIG
drv_usart_v2.c    :304 | stm32_control                                                   go=> stm32_control
drv_usart_v2.c    :208 | stm32_control                         
drv_usart_v2.c    :209 | stm32_control                                     6(0x00000006) <= cmd/*SET_INT*/
drv_usart_v2.c    :213 | stm32_control                                   256(0x00000100) <= ctrl_arg
drv_usart_v2.c    :232 | stm32_control                                   256(0x00000100) <= ctrl_arg
drv_usart_v2.c    :280 | stm32_control                                                   cmd: RT_DEVICE_CTRL_SET_INT
drv_usart_v2.c    :281 | stm32_control                                                   go=> HAL_NVIC_SetPriority
drv_usart_v2.c    :283 | stm32_control                                                   go=> NVIC_EnableIRQ
dev_serial_v2.c   :1051| rt_serial_open                                                  go=> rt_serial_tx_enable
dev_serial_v2.c   :672 | rt_serial_tx_enable                   
dev_serial_v2.c   :673 | rt_serial_tx_enable                           16384(0x00004000) <= tx_oflag
dev_serial_v2.c   :707 | rt_serial_tx_enable                                             go=> serial->ops->control
drv_usart_v2.c    :208 | stm32_control                         
drv_usart_v2.c    :209 | stm32_control                                    32(0x00000020) <= cmd/*RT_DEVICE_CHECK_OPTMODE*/
drv_usart_v2.c    :213 | stm32_control                                 16384(0x00004000) <= ctrl_arg/*RT_DEVICE_FLAG_TX_BLOCKING*/
drv_usart_v2.c    :232 | stm32_control                                  1024(0x00000400) <= ctrl_arg/*RT_DEVICE_FLAG_INT_TX*/
drv_usart_v2.c    :310 | stm32_control                                                   cmd: RT_DEVICE_CHECK_OPTMODE
dev_serial_v2.c   :718 | rt_serial_tx_enable                                             go=> rt_ringbuffer_init
ringbuffer.c      :43  | rt_ringbuffer_init                    
ringbuffer.c      :44  | rt_ringbuffer_init                              256(0x00000100) <= size
dev_serial_v2.c   :755 | rt_serial_tx_enable                                             go=> rt_completion_init
/*初始化完毕*/

2.3 rt_kprintf的执行

  • 经过对trace log分析的解读, rt_kprintf的函数调用链如下所示. 可见rt_kprintf是通过_serial_poll_tx方式轮询发送的.
/*
rt_kprintf (kservice.c)
    |-> _kputs (kservice.c)
        |-> rt_console_get_device (kservice.c)
        |-> rt_device_write (device.c)
            |-> device_write (dev_serial_v2.c)
                |-> _serial_fifo_tx_blocking_buf (dev_serial_v2.c)
                    |-> _serial_poll_tx (dev_serial_v2.c)
                        |-> stm32_putc (drv_usart_v2.c) [多次调用]
*/
  • 系统启动初始化过程中, trace到了7次rt_kprintf打印过程, 每次打印的数据量不同. 完整的运行log如下
/*rt_kprintf正式被调用了*/
kservice.c        :372 | rt_kprintf                            
kservice.c        :391 | rt_kprintf                                                      go=> _kputs
kservice.c        :321 | _kputs                                
kservice.c        :323 | _kputs                                                          go=> rt_console_get_device
kservice.c        :337 | _kputs                                                          go=> rt_device_write
device.c          :407 | rt_device_write                       
device.c          :408 | rt_device_write                                                 uart4
device.c          :409 | rt_device_write                                  18(0x00000012) <= size
device.c          :423 | rt_device_write                                                 go=> device_write
dev_serial_v2.c   :536 | _serial_fifo_tx_blocking_buf          
dev_serial_v2.c   :537 | _serial_fifo_tx_blocking_buf                     18(0x00000012) <= size
dev_serial_v2.c   :555 | _serial_fifo_tx_blocking_buf                                    go=> _serial_poll_tx
dev_serial_v2.c   :369 | _serial_poll_tx                       
dev_serial_v2.c   :370 | _serial_poll_tx                                  18(0x00000012) <= size
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
dev_serial_v2.c   :397 | _serial_poll_tx                                  18(0x00000012) <= putc_size - size

/*第2次调用rt_kprintf*/
kservice.c        :372 | rt_kprintf                            
kservice.c        :391 | rt_kprintf                                                      go=> _kputs
kservice.c        :321 | _kputs                                
kservice.c        :323 | _kputs                                                          go=> rt_console_get_device
kservice.c        :337 | _kputs                                                          go=> rt_device_write
device.c          :407 | rt_device_write                       
device.c          :408 | rt_device_write                                                 uart4
device.c          :409 | rt_device_write                                  82(0x00000052) <= size
device.c          :423 | rt_device_write                                                 go=> device_write
dev_serial_v2.c   :536 | _serial_fifo_tx_blocking_buf          
dev_serial_v2.c   :537 | _serial_fifo_tx_blocking_buf                     82(0x00000052) <= size
dev_serial_v2.c   :555 | _serial_fifo_tx_blocking_buf                                    go=> _serial_poll_tx
dev_serial_v2.c   :369 | _serial_poll_tx                       
dev_serial_v2.c   :370 | _serial_poll_tx                                  82(0x00000052) <= size
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
dev_serial_v2.c   :397 | _serial_poll_tx                                  82(0x00000052) <= putc_size - size

/*第3次调用rt_kprintf*/
kservice.c        :372 | rt_kprintf                            
kservice.c        :391 | rt_kprintf                                                      go=> _kputs
kservice.c        :321 | _kputs                                
kservice.c        :323 | _kputs                                                          go=> rt_console_get_device
kservice.c        :337 | _kputs                                                          go=> rt_device_write
device.c          :407 | rt_device_write                       
device.c          :408 | rt_device_write                                                 uart4
device.c          :409 | rt_device_write                                   5(0x00000005) <= size
device.c          :423 | rt_device_write                                                 go=> device_write
dev_serial_v2.c   :536 | _serial_fifo_tx_blocking_buf          
dev_serial_v2.c   :537 | _serial_fifo_tx_blocking_buf                      5(0x00000005) <= size
dev_serial_v2.c   :555 | _serial_fifo_tx_blocking_buf                                    go=> _serial_poll_tx
dev_serial_v2.c   :369 | _serial_poll_tx                       
dev_serial_v2.c   :370 | _serial_poll_tx                                   5(0x00000005) <= size
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
dev_serial_v2.c   :397 | _serial_poll_tx                                   5(0x00000005) <= putc_size - size

/*第4次调用rt_kprintf*/
kservice.c        :372 | rt_kprintf                            
kservice.c        :391 | rt_kprintf                                                      go=> _kputs
kservice.c        :321 | _kputs                                
kservice.c        :323 | _kputs                                                          go=> rt_console_get_device
kservice.c        :337 | _kputs                                                          go=> rt_device_write
device.c          :407 | rt_device_write                       
device.c          :408 | rt_device_write                                                 uart4
device.c          :409 | rt_device_write                                   8(0x00000008) <= size
device.c          :423 | rt_device_write                                                 go=> device_write
dev_serial_v2.c   :536 | _serial_fifo_tx_blocking_buf          
dev_serial_v2.c   :537 | _serial_fifo_tx_blocking_buf                      8(0x00000008) <= size
dev_serial_v2.c   :555 | _serial_fifo_tx_blocking_buf                                    go=> _serial_poll_tx
dev_serial_v2.c   :369 | _serial_poll_tx                       
dev_serial_v2.c   :370 | _serial_poll_tx                                   8(0x00000008) <= size
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
dev_serial_v2.c   :397 | _serial_poll_tx                                   8(0x00000008) <= putc_size - size

/*第5次调用rt_kprintf*/
kservice.c        :372 | rt_kprintf                            
kservice.c        :391 | rt_kprintf                                                      go=> _kputs
kservice.c        :321 | _kputs                                
kservice.c        :323 | _kputs                                                          go=> rt_console_get_device
kservice.c        :337 | _kputs                                                          go=> rt_device_write
device.c          :407 | rt_device_write                       
device.c          :408 | rt_device_write                                                 uart4
device.c          :409 | rt_device_write                                  35(0x00000023) <= size
device.c          :423 | rt_device_write                                                 go=> device_write
dev_serial_v2.c   :536 | _serial_fifo_tx_blocking_buf          
dev_serial_v2.c   :537 | _serial_fifo_tx_blocking_buf                     35(0x00000023) <= size
dev_serial_v2.c   :555 | _serial_fifo_tx_blocking_buf                                    go=> _serial_poll_tx
dev_serial_v2.c   :369 | _serial_poll_tx                       
dev_serial_v2.c   :370 | _serial_poll_tx                                  35(0x00000023) <= size
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
dev_serial_v2.c   :397 | _serial_poll_tx                                  35(0x00000023) <= putc_size - size

/*第6次调用rt_kprintf*/
kservice.c        :372 | rt_kprintf                            
kservice.c        :391 | rt_kprintf                                                      go=> _kputs
kservice.c        :321 | _kputs                                
kservice.c        :323 | _kputs                                                          go=> rt_console_get_device
kservice.c        :337 | _kputs                                                          go=> rt_device_write
device.c          :407 | rt_device_write                       
device.c          :408 | rt_device_write                                                 uart4
device.c          :409 | rt_device_write                                  44(0x0000002c) <= size
device.c          :423 | rt_device_write                                                 go=> device_write
dev_serial_v2.c   :536 | _serial_fifo_tx_blocking_buf          
dev_serial_v2.c   :537 | _serial_fifo_tx_blocking_buf                     44(0x0000002c) <= size
dev_serial_v2.c   :555 | _serial_fifo_tx_blocking_buf                                    go=> _serial_poll_tx
dev_serial_v2.c   :369 | _serial_poll_tx                       
dev_serial_v2.c   :370 | _serial_poll_tx                                  44(0x0000002c) <= size
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
dev_serial_v2.c   :397 | _serial_poll_tx                                  44(0x0000002c) <= putc_size - size

/*第7次调用rt_kprintf*/
kservice.c        :372 | rt_kprintf                            
kservice.c        :391 | rt_kprintf                                                      go=> _kputs
kservice.c        :321 | _kputs                                
kservice.c        :323 | _kputs                                                          go=> rt_console_get_device
kservice.c        :337 | _kputs                                                          go=> rt_device_write
device.c          :407 | rt_device_write                       
device.c          :408 | rt_device_write                                                 uart4
device.c          :409 | rt_device_write                                  41(0x00000029) <= size
device.c          :423 | rt_device_write                                                 go=> device_write
dev_serial_v2.c   :536 | _serial_fifo_tx_blocking_buf          
dev_serial_v2.c   :537 | _serial_fifo_tx_blocking_buf                     41(0x00000029) <= size
dev_serial_v2.c   :555 | _serial_fifo_tx_blocking_buf                                    go=> _serial_poll_tx
dev_serial_v2.c   :369 | _serial_poll_tx                       
dev_serial_v2.c   :370 | _serial_poll_tx                                  41(0x00000029) <= size
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
dev_serial_v2.c   :397 | _serial_poll_tx                                  41(0x00000029) <= putc_size - size

2.4 finsh_system_init与finsh_thread_entry的执行(创建一个finsh shell单独的线程, 处理shell的收发)

  • finsh_system_init是在shell.c中实现的, 它主要是创建了一个finsh线程, 并在finsh线程中用finsh_set_device设置了device, 配置了不同的oflag属性. finsh_set_device相比rt_console_set_device, 多了一个RT_DEVICE_FLAG_INT_RX属性, 以满足中断接收的要求.
  • 经过对trace log分析的解读, finsh_system_init的函数调用链如下所示
/*finsh_system_init是用INIT_APP_EXPORT初始化的*/
/*
finsh_system_init (shell.c)
    |-> rt_thread_create (shell.c)
    |-> rt_sem_init (shell.c)
    |-> finsh_set_prompt_mode (shell.c)
    |-> finsh_thread_entry (shell.c)
        |-> rt_console_get_device (shell.c)
        |-> finsh_set_device (shell.c)
            |-> rt_device_find (device.c) [uart4]
            |-> rt_device_open (device.c) [uart4, oflag=0x143]
                |-> rt_serial_open (dev_serial_v2.c)
            |-> rt_device_set_rx_indicate (device.c) [uart4]
*/
  • trace到的运行log如下
shell.c           :799 | finsh_system_init                     
shell.c           :848 | finsh_system_init                                               go=> rt_thread_create
shell.c           :862 | finsh_system_init                                               go=> rt_sem_init
shell.c           :864 | finsh_system_init                                               go=> finsh_set_prompt_mode
shell.c           :157 | finsh_set_prompt_mode                 
shell.c           :503 | finsh_thread_entry                    
shell.c           :519 | finsh_thread_entry                                              go=> rt_console_get_device
shell.c           :523 | finsh_thread_entry                                              go=> finsh_set_device
shell.c           :233 | finsh_set_device                      
shell.c           :237 | finsh_set_device                                                go=> rt_device_find
device.c          :130 | rt_device_find                        
device.c          :131 | rt_device_find                                                  uart4
shell.c           :248 | finsh_set_device                                                go=> rt_device_open
device.c          :235 | rt_device_open                        
device.c          :236 | rt_device_open                                                  uart4
/*finsh_set_device用0x143打开uart4, 相比rt_console_set_device多了一个RT_DEVICE_FLAG_INT_RX*/
/*oflag=0x143=> RT_DEVICE_OFLAG_RDWR|RT_DEVICE_FLAG_INT_RX|RT_DEVICE_FLAG_STREAM*/
device.c          :237 | rt_device_open                                  323(0x00000143) <= oflag
device.c          :276 | rt_device_open                                                  go=> device_open
dev_serial_v2.c   :1006| rt_serial_open                        
dev_serial_v2.c   :1007| rt_serial_open                                  323(0x00000143) <= oflag
shell.c           :266 | finsh_set_device                                                go=> rt_device_set_rx_indicate
device.c          :479 | rt_device_set_rx_indicate             
device.c          :480 | rt_device_set_rx_indicate                                       uart4
shell.c           :542 | finsh_thread_entry                                              go=> rt_kprintf
/*执行rt_kprintf(FINSH_PROMPT), 因为#define FINSH_PROMPT  finsh_get_prompt(), 所以先执行finsh_get_prompt*/
shell.c           :99  | finsh_get_prompt 

/*第8次调用rt_kprintf, 打印rt_kprintf(FINSH_PROMPT)*/
kservice.c        :372 | rt_kprintf                            
kservice.c        :391 | rt_kprintf                                                      go=> _kputs
kservice.c        :321 | _kputs                                
kservice.c        :323 | _kputs                                                          go=> rt_console_get_device
kservice.c        :337 | _kputs                                                          go=> rt_device_write
device.c          :407 | rt_device_write                       
device.c          :408 | rt_device_write                                                 uart4
device.c          :409 | rt_device_write                                   5(0x00000005) <= size
device.c          :423 | rt_device_write                                                 go=> device_write
dev_serial_v2.c   :536 | _serial_fifo_tx_blocking_buf          
dev_serial_v2.c   :537 | _serial_fifo_tx_blocking_buf                      5(0x00000005) <= size
dev_serial_v2.c   :555 | _serial_fifo_tx_blocking_buf                                    go=> _serial_poll_tx
dev_serial_v2.c   :369 | _serial_poll_tx                       
dev_serial_v2.c   :370 | _serial_poll_tx                                   5(0x00000005) <= size
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
dev_serial_v2.c   :397 | _serial_poll_tx                                   5(0x00000005) <= putc_size - size

2.5 finsh_thread_entry的执行过程

  • 线程循环体的执行逻辑就是finsh_getchar-> process(ch), 这里proces()过程细节就非常多了, 包括回显, push历史, 命令执行等
static void finsh_thread_entry(void *parameter)
{
    rt_device_t console = rt_console_get_device();
    finsh_set_device(console->parent.name);

    while (1)
    {
        ch = (int)finsh_getchar();
        if (ch < 0)
        {
            continue;
        }

        process(ch);

    }
}

2.6 finsh_getchar的执行(console如何获取一个char?)

  • 经过对trace log分析的解读, finsh_getchar的函数调用链如下所示. 在finsh_getchar底层调用了rt_ringbuffer_get, 如果ringbuffer为空, 那么就会调用rt_sem_take等待.
/*读取第1个char*/
/*
finsh_getchar (shell.c)
    |-> rt_device_read (device.c)
        |-> device_read (dev_serial_v2.c)
            |-> _serial_fifo_rx (dev_serial_v2.c)
                |-> rt_ringbuffer_get (ringbuffer.c)
                    |-> rt_ringbuffer_data_len (ringbuffer.c)
    |-> rt_sem_take (when buffer empty)
    |-> ......
    |-> rt_device_read (after wakeup)
        |-> device_read (dev_serial_v2.c)
            |-> _serial_fifo_rx (dev_serial_v2.c)
                |-> rt_ringbuffer_get (ringbuffer.c)
                    |-> rt_ringbuffer_data_len (ringbuffer.c)
*/
  • trace到的finsh_getchar获取一个char的完整运行log如下. 启动读后, 因为ringbuffer此时为空, 因此会调用rt_sem_take睡眠. 直到串口发生rx中断, 接收到了char, 并通过rt_ringbuffer_putchar存入ringbuff, 然后调用rt_hw_serial_isr发送信号量, finsh_getchar被唤醒继续执行, 成功读取到一个char.
/*进入finsh_thread_entry循环体执行*/
/*读取第1个char*/
shell.c           :547 | finsh_thread_entry                                              go=> finsh_getchar
shell.c           :164 | finsh_getchar                         
shell.c           :187 | finsh_getchar                                                   go=> rt_device_read
device.c          :360 | rt_device_read                        
device.c          :361 | rt_device_read                                                  uart4
device.c          :362 | rt_device_read                                    1(0x00000001) <= size
device.c          :376 | rt_device_read                                                  go=> device_read
dev_serial_v2.c   :415 | _serial_fifo_rx                       
dev_serial_v2.c   :416 | _serial_fifo_rx                                   1(0x00000001) <= size
dev_serial_v2.c   :460 | _serial_fifo_rx                                                 go=> rt_ringbuffer_get
ringbuffer.c      :189 | rt_ringbuffer_get                     
ringbuffer.c      :394 | rt_ringbuffer_data_len                
ringbuffer.c      :21  | rt_ringbuffer_status                  
ringbuffer.c      :398 | rt_ringbuffer_data_len                            0(0x00000000) <= data_len
dev_serial_v2.c   :465 | _serial_fifo_rx                                   0(0x00000000) <= recv_len
shell.c           :190 | finsh_getchar                                                   go=> rt_sem_take...
/*进入睡眠状态*/

/*中断唤醒, 串口收到数据了*/
/*基本操作就是先用rt_ringbuffer_putchar存入ringbuff, 然后调用rt_hw_serial_isr发送信号量*/
drv_usart_v2.c    :703 | UART4_IRQHandler                      
drv_usart_v2.c    :707 | UART4_IRQHandler                                                go=> uart_isr
drv_usart_v2.c    :475 | uart_isr                              
drv_usart_v2.c    :488 | uart_isr                                                        go=> rt_ringbuffer_putchar
drv_usart_v2.c    :346 | stm32_uart_get_mask                   
ringbuffer.c      :285 | rt_ringbuffer_putchar                 
ringbuffer.c      :394 | rt_ringbuffer_data_len                
ringbuffer.c      :21  | rt_ringbuffer_status                  
ringbuffer.c      :398 | rt_ringbuffer_data_len                            0(0x00000000) <= data_len
drv_usart_v2.c    :491 | uart_isr                                                        go=> rt_hw_serial_isr
/*调用rt_hw_serial_isr, 最终结果是发送rt_sem_release*/
dev_serial_v2.c   :1584| rt_hw_serial_isr                      
dev_serial_v2.c   :1585| rt_hw_serial_isr                                  1(0x00000001) <= event
dev_serial_v2.c   :1613| rt_hw_serial_isr                                                go=> rt_ringbuffer_data_len
ringbuffer.c      :394 | rt_ringbuffer_data_len                
ringbuffer.c      :21  | rt_ringbuffer_status                  
ringbuffer.c      :410 | rt_ringbuffer_data_len                            1(0x00000001) <= data_len
dev_serial_v2.c   :1628| rt_hw_serial_isr                                                go=> serial->parent.rx_indicate
shell.c           :214 | finsh_rx_ind                          
shell.c           :218 | finsh_rx_ind                                                    go=> rt_sem_release

/*finsh_getchar被唤醒*/
shell.c           :192 | finsh_getchar                                                   wakeup from rt_sem_take...
shell.c           :201 | finsh_getchar                                                   go=> rt_device_read
device.c          :360 | rt_device_read                        
device.c          :361 | rt_device_read                                                  uart4
device.c          :362 | rt_device_read                                    1(0x00000001) <= size
device.c          :376 | rt_device_read                                                  go=> device_read
dev_serial_v2.c   :415 | _serial_fifo_rx                       
dev_serial_v2.c   :416 | _serial_fifo_rx                                   1(0x00000001) <= size
dev_serial_v2.c   :460 | _serial_fifo_rx                                                 go=> rt_ringbuffer_get
ringbuffer.c      :189 | rt_ringbuffer_get                     
ringbuffer.c      :394 | rt_ringbuffer_data_len                
ringbuffer.c      :21  | rt_ringbuffer_status                  
ringbuffer.c      :410 | rt_ringbuffer_data_len                            1(0x00000001) <= data_len
/*_serial_fifo_rx函数返回1, 终于读到了数据*/
dev_serial_v2.c   :465 | _serial_fifo_rx                                   1(0x00000001) <= recv_len

2.7 UART4_IRQHandler的执行(如何获取一个char? 这个char如何存放?)

  • 串口接收数据是通过中断来实现的. 我们在调用finsh_set_device的时候, 其中包含RT_DEVICE_FLAG_INT_RX属性, 以满足中断接收的要求.
  • 经过对trace log分析的解读, UART4_IRQHandler的函数调用链如下所示. 基本操作就是先用rt_ringbuffer_putchar存入ringbuff, 然后调用rt_hw_serial_isr发送信号量.
/*中断唤醒, 串口收到数据了*/
/*基本操作就是先用rt_ringbuffer_putchar存入ringbuff, 然后调用rt_hw_serial_isr发送信号量*/
/*
UART4_IRQHandler (drv_usart_v2.c)
    |-> uart_isr (drv_usart_v2.c)
        |-> rt_ringbuffer_putchar (ringbuffer.c)
            |-> rt_ringbuffer_data_len (ringbuffer.c)
                |-> rt_ringbuffer_status (ringbuffer.c)
        |-> rt_hw_serial_isr (dev_serial_v2.c)
            |-> rt_ringbuffer_data_len (ringbuffer.c)
                |-> rt_ringbuffer_status (ringbuffer.c)
            |-> serial->parent.rx_indicate (shell.c)
                |-> rt_sem_release
*/
  • trace到的运行log如下
/*中断唤醒, 串口收到数据了*/
/*基本操作就是先用rt_ringbuffer_putchar存入ringbuff, 然后调用rt_hw_serial_isr发送信号量*/
drv_usart_v2.c    :703 | UART4_IRQHandler                      
drv_usart_v2.c    :707 | UART4_IRQHandler                                                go=> uart_isr
drv_usart_v2.c    :475 | uart_isr                              
drv_usart_v2.c    :488 | uart_isr                                                        go=> rt_ringbuffer_putchar
drv_usart_v2.c    :346 | stm32_uart_get_mask                   
ringbuffer.c      :285 | rt_ringbuffer_putchar                 
ringbuffer.c      :394 | rt_ringbuffer_data_len                
ringbuffer.c      :21  | rt_ringbuffer_status                  
ringbuffer.c      :398 | rt_ringbuffer_data_len                            0(0x00000000) <= data_len
drv_usart_v2.c    :491 | uart_isr                                                        go=> rt_hw_serial_isr
/*调用rt_hw_serial_isr, 最终结果是发送rt_sem_release*/
dev_serial_v2.c   :1584| rt_hw_serial_isr                      
dev_serial_v2.c   :1585| rt_hw_serial_isr                                  1(0x00000001) <= event
dev_serial_v2.c   :1613| rt_hw_serial_isr                                                go=> rt_ringbuffer_data_len
ringbuffer.c      :394 | rt_ringbuffer_data_len                
ringbuffer.c      :21  | rt_ringbuffer_status                  
ringbuffer.c      :410 | rt_ringbuffer_data_len                            1(0x00000001) <= data_len
dev_serial_v2.c   :1628| rt_hw_serial_isr                                                go=> serial->parent.rx_indicate
shell.c           :214 | finsh_rx_ind                          
shell.c           :218 | finsh_rx_ind                                                    go=> rt_sem_release

2.8 finish如何识别命令并执行?

  • 在finsh_thread_entry循环体中, 通过finsh_getchar成功获得char之后, 就交给process()处理.
  • 在process()中有如下这样的一段, 它是以判断当前字符是’\r’或者’\n’来作为命令接收完毕的判据.
  • 这意味着, 如果我们输入的命令是"list device\r\n", 当收到"list device\r"时, 就认为收到一个完整命令, 然后运行. 毫无疑问, 这会发生一次成功的命令执行.
  • 接着继续接收到一个’\n’, 满足条件, 也认为是收到一个完整命令, 然后运行. 毫无疑问, 这是一次无效的命令执行, 因为命令为空.
        /* handle end of line, break */
        if (ch == '\r' || ch == '\n')
        {
#ifdef FINSH_USING_HISTORY
            shell_push_history(shell);
#endif
            if (shell->echo_mode)
                rt_kprintf("\n");
            msh_exec(shell->line, shell->line_position);

            rt_kprintf(FINSH_PROMPT);
            rt_memset(shell->line, 0, sizeof(shell->line));
            shell->line_curpos = shell->line_position = 0;
            continue;
        }
  • trace到的运行log如下
.....
.....

/*finsh_thread_entry循环体继续执行*/
/*读取第11个char*/
shell.c           :547 | finsh_thread_entry                                              go=> finsh_getchar
shell.c           :164 | finsh_getchar                         
shell.c           :187 | finsh_getchar                                                   go=> rt_device_read
device.c          :360 | rt_device_read                        
device.c          :361 | rt_device_read                                                  uart4
device.c          :362 | rt_device_read                                    1(0x00000001) <= size
device.c          :376 | rt_device_read                                                  go=> device_read
dev_serial_v2.c   :415 | _serial_fifo_rx                       
dev_serial_v2.c   :416 | _serial_fifo_rx                                   1(0x00000001) <= size
dev_serial_v2.c   :460 | _serial_fifo_rx                                                 go=> rt_ringbuffer_get
ringbuffer.c      :189 | rt_ringbuffer_get                     
ringbuffer.c      :394 | rt_ringbuffer_data_len                
ringbuffer.c      :21  | rt_ringbuffer_status                  
ringbuffer.c      :410 | rt_ringbuffer_data_len                            3(0x00000003) <= data_len
dev_serial_v2.c   :465 | _serial_fifo_rx                                   1(0x00000001) <= recv_len
shell.c           :740 | finsh_thread_entry                                              go=> rt_kprintf
kservice.c        :372 | rt_kprintf                            
kservice.c        :391 | rt_kprintf                                                      go=> _kputs
kservice.c        :321 | _kputs                                
kservice.c        :323 | _kputs                                                          go=> rt_console_get_device
kservice.c        :337 | _kputs                                                          go=> rt_device_write
device.c          :407 | rt_device_write                       
device.c          :408 | rt_device_write                                                 uart4
device.c          :409 | rt_device_write                                   1(0x00000001) <= size
device.c          :423 | rt_device_write                                                 go=> device_write
dev_serial_v2.c   :536 | _serial_fifo_tx_blocking_buf          
dev_serial_v2.c   :537 | _serial_fifo_tx_blocking_buf                      1(0x00000001) <= size
dev_serial_v2.c   :555 | _serial_fifo_tx_blocking_buf                                    go=> _serial_poll_tx
dev_serial_v2.c   :369 | _serial_poll_tx                       
dev_serial_v2.c   :370 | _serial_poll_tx                                   1(0x00000001) <= size
drv_usart_v2.c    :332 | stm32_putc                            
dev_serial_v2.c   :397 | _serial_poll_tx                                   1(0x00000001) <= putc_size - size

/*finsh_thread_entry循环体继续执行*/
/*读取第12个char*/
shell.c           :547 | finsh_thread_entry                                              go=> finsh_getchar
shell.c           :164 | finsh_getchar                         
shell.c           :187 | finsh_getchar                                                   go=> rt_device_read
device.c          :360 | rt_device_read                        
device.c          :361 | rt_device_read                                                  uart4
device.c          :362 | rt_device_read                                    1(0x00000001) <= size
device.c          :376 | rt_device_read                                                  go=> device_read
dev_serial_v2.c   :415 | _serial_fifo_rx                       
dev_serial_v2.c   :416 | _serial_fifo_rx                                   1(0x00000001) <= size
dev_serial_v2.c   :460 | _serial_fifo_rx                                                 go=> rt_ringbuffer_get
ringbuffer.c      :189 | rt_ringbuffer_get                     
ringbuffer.c      :394 | rt_ringbuffer_data_len                
ringbuffer.c      :21  | rt_ringbuffer_status                  
ringbuffer.c      :410 | rt_ringbuffer_data_len                            2(0x00000002) <= data_len
dev_serial_v2.c   :465 | _serial_fifo_rx                                   1(0x00000001) <= recv_len
/*到此, 接收到了list device\r, 一共12个char. 因为收到了 '\r'或'\n', 认为命令接收完毕*/
/*执行shell_push_history, 将命令压入历史堆栈*/
shell.c           :702 | finsh_thread_entry                                              go=> shell_push_history
shell.c           :443 | shell_push_history                    
shell.c           :706 | finsh_thread_entry                                              go=> rt_kprintf
kservice.c        :372 | rt_kprintf                            
kservice.c        :391 | rt_kprintf                                                      go=> _kputs
kservice.c        :321 | _kputs                                
kservice.c        :323 | _kputs                                                          go=> rt_console_get_device
kservice.c        :337 | _kputs                                                          go=> rt_device_write
device.c          :407 | rt_device_write                       
device.c          :408 | rt_device_write                                                 uart4
device.c          :409 | rt_device_write                                   1(0x00000001) <= size
device.c          :423 | rt_device_write                                                 go=> device_write
dev_serial_v2.c   :536 | _serial_fifo_tx_blocking_buf          
dev_serial_v2.c   :537 | _serial_fifo_tx_blocking_buf                      1(0x00000001) <= size
dev_serial_v2.c   :555 | _serial_fifo_tx_blocking_buf                                    go=> _serial_poll_tx
dev_serial_v2.c   :369 | _serial_poll_tx                       
dev_serial_v2.c   :370 | _serial_poll_tx                                   1(0x00000001) <= size
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
dev_serial_v2.c   :397 | _serial_poll_tx                                   1(0x00000001) <= putc_size - size

/*命令接收完毕, 开始执行命令msh_exec*/
shell.c           :707 | finsh_thread_entry                                              go=> msh_exec

/*msh_exec命令执行过程, 我们的命令是list device, 因此开始产生一些输出*/
kservice.c        :372 | rt_kprintf                            
kservice.c        :391 | rt_kprintf                                                      go=> _kputs
kservice.c        :321 | _kputs                                
kservice.c        :323 | _kputs                                                          go=> rt_console_get_device
kservice.c        :337 | _kputs                                                          go=> rt_device_write
device.c          :407 | rt_device_write                       
device.c          :408 | rt_device_write                                                 uart4
device.c          :409 | rt_device_write                                  40(0x00000028) <= size
device.c          :423 | rt_device_write                                                 go=> device_write
dev_serial_v2.c   :536 | _serial_fifo_tx_blocking_buf          
dev_serial_v2.c   :537 | _serial_fifo_tx_blocking_buf                     40(0x00000028) <= size
dev_serial_v2.c   :555 | _serial_fifo_tx_blocking_buf                                    go=> _serial_poll_tx
dev_serial_v2.c   :369 | _serial_poll_tx                       
dev_serial_v2.c   :370 | _serial_poll_tx                                  40(0x00000028) <= size
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2

.....
.....

drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
dev_serial_v2.c   :397 | _serial_poll_tx                                  39(0x00000027) <= putc_size - size
shell.c           :710 | finsh_thread_entry                                              go=> rt_kprintf
/*msh_exe执行完毕, 最后执行rt_kprintf(FINSH_PROMPT)*/
shell.c           :99  | finsh_get_prompt                      
kservice.c        :372 | rt_kprintf                            
kservice.c        :391 | rt_kprintf                                                      go=> _kputs
kservice.c        :321 | _kputs                                
kservice.c        :323 | _kputs                                                          go=> rt_console_get_device
kservice.c        :337 | _kputs                                                          go=> rt_device_write
device.c          :407 | rt_device_write                       
device.c          :408 | rt_device_write                                                 uart4
device.c          :409 | rt_device_write                                   5(0x00000005) <= size
device.c          :423 | rt_device_write                                                 go=> device_write
dev_serial_v2.c   :536 | _serial_fifo_tx_blocking_buf          
dev_serial_v2.c   :537 | _serial_fifo_tx_blocking_buf                      5(0x00000005) <= size
dev_serial_v2.c   :555 | _serial_fifo_tx_blocking_buf                                    go=> _serial_poll_tx
dev_serial_v2.c   :369 | _serial_poll_tx                       
dev_serial_v2.c   :370 | _serial_poll_tx                                   5(0x00000005) <= size
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
dev_serial_v2.c   :397 | _serial_poll_tx                                   5(0x00000005) <= putc_size - size

/*finsh_thread_entry循环体继续执行*/
/*读取第13个char, 此时读到的是'\n', 再次进入这一段逻辑(这一遍实际是无效的).*/
shell.c           :547 | finsh_thread_entry                                              go=> finsh_getchar
shell.c           :164 | finsh_getchar                         
shell.c           :187 | finsh_getchar                                                   go=> rt_device_read
device.c          :360 | rt_device_read                        
device.c          :361 | rt_device_read                                                  uart4
device.c          :362 | rt_device_read                                    1(0x00000001) <= size
device.c          :376 | rt_device_read                                                  go=> device_read
dev_serial_v2.c   :415 | _serial_fifo_rx                       
dev_serial_v2.c   :416 | _serial_fifo_rx                                   1(0x00000001) <= size
dev_serial_v2.c   :460 | _serial_fifo_rx                                                 go=> rt_ringbuffer_get
ringbuffer.c      :189 | rt_ringbuffer_get                     
ringbuffer.c      :394 | rt_ringbuffer_data_len                
ringbuffer.c      :21  | rt_ringbuffer_status                  
ringbuffer.c      :410 | rt_ringbuffer_data_len                            1(0x00000001) <= data_len
dev_serial_v2.c   :465 | _serial_fifo_rx                                   1(0x00000001) <= recv_len
/*于是再次触发shell_push_history*/
shell.c           :702 | finsh_thread_entry                                              go=> shell_push_history
shell.c           :443 | shell_push_history
/*回显'\n'*/                    
shell.c           :706 | finsh_thread_entry                                              go=> rt_kprintf
kservice.c        :372 | rt_kprintf                            
kservice.c        :391 | rt_kprintf                                                      go=> _kputs
kservice.c        :321 | _kputs                                
kservice.c        :323 | _kputs                                                          go=> rt_console_get_device
kservice.c        :337 | _kputs                                                          go=> rt_device_write
device.c          :407 | rt_device_write                       
device.c          :408 | rt_device_write                                                 uart4
device.c          :409 | rt_device_write                                   1(0x00000001) <= size
device.c          :423 | rt_device_write                                                 go=> device_write
dev_serial_v2.c   :536 | _serial_fifo_tx_blocking_buf          
dev_serial_v2.c   :537 | _serial_fifo_tx_blocking_buf                      1(0x00000001) <= size
dev_serial_v2.c   :555 | _serial_fifo_tx_blocking_buf                                    go=> _serial_poll_tx
dev_serial_v2.c   :369 | _serial_poll_tx                       
dev_serial_v2.c   :370 | _serial_poll_tx                                   1(0x00000001) <= size
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
dev_serial_v2.c   :397 | _serial_poll_tx                                   1(0x00000001) <= putc_size - size
/*再次触发msh_exec, 实际上没有有效的命令*/
shell.c           :707 | finsh_thread_entry                                              go=> msh_exec
shell.c           :710 | finsh_thread_entry                                              go=> rt_kprintf
/*再次执行rt_kprintf(FINSH_PROMPT)*/
shell.c           :99  | finsh_get_prompt                      
kservice.c        :372 | rt_kprintf                            
kservice.c        :391 | rt_kprintf                                                      go=> _kputs
kservice.c        :321 | _kputs                                
kservice.c        :323 | _kputs                                                          go=> rt_console_get_device
kservice.c        :337 | _kputs                                                          go=> rt_device_write
device.c          :407 | rt_device_write                       
device.c          :408 | rt_device_write                                                 uart4
device.c          :409 | rt_device_write                                   5(0x00000005) <= size
device.c          :423 | rt_device_write                                                 go=> device_write
dev_serial_v2.c   :536 | _serial_fifo_tx_blocking_buf          
dev_serial_v2.c   :537 | _serial_fifo_tx_blocking_buf                      5(0x00000005) <= size
dev_serial_v2.c   :555 | _serial_fifo_tx_blocking_buf                                    go=> _serial_poll_tx
dev_serial_v2.c   :369 | _serial_poll_tx                       
dev_serial_v2.c   :370 | _serial_poll_tx                                   5(0x00000005) <= size
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
drv_usart_v2.c    :332 | stm32_putc                            
dev_serial_v2.c   :397 | _serial_poll_tx                                   5(0x00000005) <= putc_size - size

/*finsh_thread_entry循环体继续执行*/
shell.c           :547 | finsh_thread_entry                                              go=> finsh_getchar
shell.c           :164 | finsh_getchar                         
shell.c           :187 | finsh_getchar                                                   go=> rt_device_read
device.c          :360 | rt_device_read                        
device.c          :361 | rt_device_read                                                  uart4
device.c          :362 | rt_device_read                                    1(0x00000001) <= size
device.c          :376 | rt_device_read                                                  go=> device_read
dev_serial_v2.c   :415 | _serial_fifo_rx                       
dev_serial_v2.c   :416 | _serial_fifo_rx                                   1(0x00000001) <= size
dev_serial_v2.c   :460 | _serial_fifo_rx                                                 go=> rt_ringbuffer_get
ringbuffer.c      :189 | rt_ringbuffer_get                     
ringbuffer.c      :394 | rt_ringbuffer_data_len                
ringbuffer.c      :21  | rt_ringbuffer_status                  
ringbuffer.c      :398 | rt_ringbuffer_data_len                            0(0x00000000) <= data_len
dev_serial_v2.c   :465 | _serial_fifo_rx                                   0(0x00000000) <= recv_len
shell.c           :190 | finsh_getchar                                                   go=> rt_sem_take...

2.9 存在的问题: finsh假唤醒问题?

通过对trace log的仔细分析,我们发现,当finsh执行完一条命令后,后面每次finsh_getchar()运行,因为没有获得数据(rt_ringbuffer_data_len返回0),从而执行rt_sem_take,但马上就被唤醒了。通过查代码,找不到任何唤醒源。感觉是假唤醒。具体的log如下面所示:

......
......

/*finsh_thread_entry循环体继续执行*/
/*读取第14个char*/
shell.c           :547 | finsh_thread_entry                                              go=> finsh_getchar
shell.c           :164 | finsh_getchar                         
shell.c           :187 | finsh_getchar                                                   go=> rt_device_read
device.c          :360 | rt_device_read                        
device.c          :361 | rt_device_read                                                  uart4
device.c          :362 | rt_device_read                                    1(0x00000001) <= size
device.c          :376 | rt_device_read                                                  go=> device_read
dev_serial_v2.c   :415 | _serial_fifo_rx                       
dev_serial_v2.c   :416 | _serial_fifo_rx                                   1(0x00000001) <= size
dev_serial_v2.c   :460 | _serial_fifo_rx                                                 go=> rt_ringbuffer_get
ringbuffer.c      :189 | rt_ringbuffer_get                     
ringbuffer.c      :394 | rt_ringbuffer_data_len                
ringbuffer.c      :21  | rt_ringbuffer_status                  
ringbuffer.c      :398 | rt_ringbuffer_data_len                            0(0x00000000) <= data_len
dev_serial_v2.c   :465 | _serial_fifo_rx                                   0(0x00000000) <= recv_len
shell.c           :190 | finsh_getchar                                                   go=> rt_sem_take...
/*这里, 都没有recv到数据, 为什么会wakeup?*/
shell.c           :192 | finsh_getchar                                                   wakeup from rt_sem_take...
shell.c           :201 | finsh_getchar                                                   go=> rt_device_read
device.c          :360 | rt_device_read                        
device.c          :361 | rt_device_read                                                  uart4
device.c          :362 | rt_device_read                                    1(0x00000001) <= size
device.c          :376 | rt_device_read                                                  go=> device_read
dev_serial_v2.c   :415 | _serial_fifo_rx                       
dev_serial_v2.c   :416 | _serial_fifo_rx                                   1(0x00000001) <= size
dev_serial_v2.c   :460 | _serial_fifo_rx                                                 go=> rt_ringbuffer_get
ringbuffer.c      :189 | rt_ringbuffer_get                     
ringbuffer.c      :394 | rt_ringbuffer_data_len                
ringbuffer.c      :21  | rt_ringbuffer_status                  
ringbuffer.c      :398 | rt_ringbuffer_data_len                            0(0x00000000) <= data_len
dev_serial_v2.c   :465 | _serial_fifo_rx                                   0(0x00000000) <= recv_len
shell.c           :190 | finsh_getchar                                                   go=> rt_sem_take...
/*这里, 都没有recv到数据, 为什么会wakeup?*/
shell.c           :192 | finsh_getchar                                                   wakeup from rt_sem_take...
shell.c           :201 | finsh_getchar                                                   go=> rt_device_read
device.c          :360 | rt_device_read                        
device.c          :361 | rt_device_read                                                  uart4
device.c          :362 | rt_device_read                                    1(0x00000001) <= size
device.c          :376 | rt_device_read                                                  go=> device_read
dev_serial_v2.c   :415 | _serial_fifo_rx                       
dev_serial_v2.c   :416 | _serial_fifo_rx                                   1(0x00000001) <= size
dev_serial_v2.c   :460 | _serial_fifo_rx                                                 go=> rt_ringbuffer_get
ringbuffer.c      :189 | rt_ringbuffer_get                     
ringbuffer.c      :394 | rt_ringbuffer_data_len                
ringbuffer.c      :21  | rt_ringbuffer_status                  
ringbuffer.c      :398 | rt_ringbuffer_data_len                            0(0x00000000) <= data_len
dev_serial_v2.c   :465 | _serial_fifo_rx                                   0(0x00000000) <= recv_len
shell.c           :190 | finsh_getchar                                                   go=> rt_sem_take...
/*这里, 都没有recv到数据, 为什么会wakeup?*/
shell.c           :192 | finsh_getchar                                                   wakeup from rt_sem_take...
shell.c           :201 | finsh_getchar                                                   go=> rt_device_read
device.c          :360 | rt_device_read                        
device.c          :361 | rt_device_read                                                  uart4
device.c          :362 | rt_device_read                                    1(0x00000001) <= size
device.c          :376 | rt_device_read                                                  go=> device_read
dev_serial_v2.c   :415 | _serial_fifo_rx                       
dev_serial_v2.c   :416 | _serial_fifo_rx                                   1(0x00000001) <= size
dev_serial_v2.c   :460 | _serial_fifo_rx                                                 go=> rt_ringbuffer_get
ringbuffer.c      :189 | rt_ringbuffer_get                     
ringbuffer.c      :394 | rt_ringbuffer_data_len                
ringbuffer.c      :21  | rt_ringbuffer_status                  
ringbuffer.c      :398 | rt_ringbuffer_data_len                            0(0x00000000) <= data_len
dev_serial_v2.c   :465 | _serial_fifo_rx                                   0(0x00000000) <= recv_len
shell.c           :190 | finsh_getchar                                                   go=> rt_sem_take...
/*这里, 都没有recv到数据, 为什么会wakeup?*/
shell.c           :192 | finsh_getchar                                                   wakeup from rt_sem_take...
shell.c           :201 | finsh_getchar                                                   go=> rt_device_read
device.c          :360 | rt_device_read                        
device.c          :361 | rt_device_read                                                  uart4
device.c          :362 | rt_device_read                                    1(0x00000001) <= size
device.c          :376 | rt_device_read                                                  go=> device_read
dev_serial_v2.c   :415 | _serial_fifo_rx                       
......
......

3. 总结与反思

  • 假设我们要让自己的某个接口(比如usb cdc_acm) 作为rt-thread的console, 如何修改代码实现呢?
  • 我们的想法是尽量模仿uart串口的行为, 只要把底层的收发char的功能用我们自己的接口来替换. 来看看stm32_uart定义了那些操作函数吧.
  • 首先我们观察stm32_uart_ops, 在rt_hw_usart_init中需要调用rt_hw_serial_register注册串口设备. 一共有5个操作函数.
static const struct rt_uart_ops stm32_uart_ops =
{
    .configure = stm32_configure,
    .control = stm32_control,
    .putc = stm32_putc,
    .getc = stm32_getc,
    .transmit = stm32_transmit
};
  • 从rt_console_set_device函数调用链可以看到, stm32_configure和stm32_control被调用了, 因此必须要有. 但如果我们只需要固定一种配置即可, 那么这2个函数无用, 可以是空函数, 直接返回RET_OK即可.
  • 从rt_console_set_device函数调用链可以看到, rx和tx的rt_ringbuffer_init都已经定义好了, 不用用户定义. 直接用即可.
  • 从rt_kprintf函数调用链可以看到, 底层只需要实现stm32_putc即可.
  • finish_getchar是如何获得char的呢? 从finish_getchar函数调用关系可见, finsh_getchar就是通过rt_ringbuffer_get从ringbuffer中读取的. 因此我们需要负责往ringbuffer中填充数据.
  • 从UART4_IRQHandler接收的处理过程可以看到, 基本操作就是把读取到的ch先用rt_ringbuffer_putchar存入ringbuff, 然后调用rt_hw_serial_isr发送信号量. 因此, 我们只需要负责把接收到的数据用rt_ringbuffer_putchar存入ringbuff, 然后调用rt_hw_serial_isr发送信号即可. 比如对于usb cdc_acm, 我们可以usbd_cdc_acm_bulk_out函数中执行这2个操作. 因此stm32_getc不是必须的.
  • 对于rt-thread console和finsh设备, stm32_transmit用不到. 不用定义.
  • 总结以上, 假设要用usb cdc_acm作为rt-thread的console和finsh设备, 代码框架大致如下
void usbd_cdc_acm_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
    struct rt_serial_rx_fifo *rx_fifo;
    rx_fifo = (struct rt_serial_rx_fifo *)_serial_cdc_acm.serial_rx;
    RT_ASSERT(rx_fifo != RT_NULL);

    for(int i=0; i < nbytes; i++){
        rt_ringbuffer_putchar(&(rx_fifo->rb), read_buffer[i]);
        rt_hw_serial_isr(&_serial_cdc_acm, RT_SERIAL_EVENT_RX_IND);      
    }

    // 继续接收数据
    usbd_ep_start_read(busid, ep, &read_buffer[0], usbd_get_ep_mps(busid, ep));    
}

void usbd_cdc_acm_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
    // USB_LOG_RAW("actual in len:%d\r\n", nbytes);

    if ((nbytes % usbd_get_ep_mps(busid, ep)) == 0 && nbytes) {
        /* send zlp */
        usbd_ep_start_write(busid, CDC_IN_EP, NULL, 0);
    } else {
        ep_tx_busy_flag = false;
    }
}


static rt_err_t _cdc_acm_cfg(struct rt_serial_device *serial, struct serial_configure *cfg)
{
    return RT_EOK;
}

static rt_err_t _cdc_acm_ctrl(struct rt_serial_device *serial, int cmd, void *arg)
{
    return RT_EOK;
}
static int _cdc_acm_putc(struct rt_serial_device *serial, char c)
{
    ep_tx_busy_flag = true;
    rt_memcpy(write_buffer, &c, 1);  /*必须借助于write_buffer, 因为它是nonchache的。否则输出乱码。*/
    usbd_ep_start_write(0, CDC_IN_EP, write_buffer, 1);
    while (ep_tx_busy_flag) {}

    return RT_EOK;
}

static int _cdc_acm_getc(struct rt_serial_device *serial)
{
    /*这个函数不是必须的, 因为阅读源码发现, finsh_getchar-->rt_device_read-->_serial_fifo_rx-->rt_ringbuffer_get
    直接从ringbuffer get的, 不需要这个函数*/

    char ch=-1;
    return ch;
}

static struct rt_uart_ops _cdc_acm_ops =
{
    .configure= _cdc_acm_cfg,
    .control = _cdc_acm_ctrl,
    .putc = _cdc_acm_putc,
    .getc = _cdc_acm_getc,
#ifdef RT_USING_SERIAL_V2
    .transmit = NULL
#endif    
};


int rt_hw_cdc_acm_init(void)
{
    cdc_acm_init(0, HPM_USB0_BASE);

    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
    _serial_cdc_acm.ops = &_cdc_acm_ops;
    _serial_cdc_acm.config = config;
    m_cdc_acm_cfg.serial = &_serial_cdc_acm;
    rt_hw_serial_register(&_serial_cdc_acm, "cdcRtt", \
                          RT_DEVICE_FLAG_RDWR | RT_SERIAL_EVENT_RX_IND,  &m_cdc_acm_cfg);

    return 0;
}

int rtt_hw_cdc_console_init(void)
{
    rt_hw_cdc_acm_init();
    rt_console_set_device("cdcRtt");
    return 0;
}
INIT_APP_EXPORT(rtt_hw_cdc_console_init);

4. 附录: 完整版的trace log

详见
《深入剖析RT-Thread串口驱动:基于STM32H750的FinSH Shell全链路Trace分析与实战解密(下)》


网站公告

今日签到

点亮在社区的每一天
去签到