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

Posted RT-Thread物联网操作系统

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RTT串口v1使用分析以及问题排查指南 相关的知识,希望对你有一定的参考价值。

本文由RT-Thread论坛账号123 原创发布:https://club.rt-thread.org/ask/article/2898.html

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

串口相关问题解析

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

结合串口使用过程的反馈信息,本章节将结合 FinSH组件的串口相关问题应用层上使用串口设备的相关问题 这两个方面进行分析。这一章节探讨FinSH组件上的串口问题。

由于串口外设所涉及的方面过于广泛,分析时不可能涵盖全部应用场景(以上两个方面应该能涵盖了串口使用过程中的七八成的应用场景),但是,结合本章的分析问题的方法,相信其他方面的问题也将会迎刃而解。

FinSH 组件上的串口问题

说起FinSH组件,肯定是离不开串口的,FinSH组件的底层数据流默认由串口外设提供(当然也可以选择网络、USB、蓝牙等方式,本节只讨论和串口相关的问题)。

RT-Thread的FinSH组件,提供了一套供用户在命令行调用的操作接口,主要用于调试或查看系统信息,类似于Linux下的Terminal。其执行序列如下图所示(抄袭的文档中心的序列图)

image.png

结合上图,可以粗略的讲, FinSH就有点类似于在RT-Thread系统中创建了一个串口线程,它用来接收(监听)用户数据,并对数据进行解析,然后执行结果,并把需要显示的结果通过串口发送给用户。再简言之,就是串口外设的收发任务。

那么就简化成了三点:1.FinSH 线程是如何启动并工作的? 2. FinSH 是如何接收到数据的? 3. FinSH 是如何显示执行结果给用户的?

FinSH 工作流程分析

image.png

在图中最关键的就是(1)、(2)和(3)这三个关键语句。

(1)语句是设置FinSH线程的shell设备,在这里这个shell设备就是串口设备,查看它的函数原形如下:

/* finsh_set_device 的关键代码 */
void finsh_set_device(const char *device_name)
{
    dev = rt_device_find(device_name);
    ... ...
    /* open this device and set the new device in finsh shell */
    if (rt_device_open(dev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX | \\
                       RT_DEVICE_FLAG_STREAM) == RT_EOK)
    {
        rt_device_set_rx_indicate(dev, finsh_rx_ind);
    }
    ... ...
}

可以看到,该设备的打开模式是RT_DEVICE_FLAG_INT_RX,对于发送并未指明是中断RT_DEVICE_FLAG_INT_TX或是RT_DEVICE_FLAG_DMA_TX,那就代表设备发送是轮询模式。也就是说,FinSH的接收是接收中断模式,FinSH的发送是轮询模式。这个很重要,一定要记住。

(2)语句是FinSH的接收函数,结合最下边finsh_getchar()函数实体,我们可以清晰的知道,原来FinSH的数据接收是靠信号量传递的:当执行 finsh_getchar() 的时候,会去通过调用 rt_device_read()一个字节的数据,这个时候只要返回值不为1(返回值为1则代表有数据被读出),则将通过信号量的将该线程永久挂起 ,直到接收到FinSH数据为止。而如果有数据接收到的话,那么将通过FinSH的数据接收回调 finsh_rx_ind()来释放一个信号量,这样 FinSH 线程就会开始执行,开始读取一个字节数据。

/* FinSH的接收回调很简单,就是触发回调后释放一个信号量就结束 */
static rt_err_t finsh_rx_ind(rt_device_t dev, rt_size_t size)
{
    RT_ASSERT(shell != RT_NULL);

    /* release semaphore to let finsh thread rx data */
    rt_sem_release(&shell->rx_sem);

    return RT_EOK;
}

(3)语句是发送语句。Finsh线程执行(2)语句之后便拿到了一个字节的数据,然后经过数据解析,就会把这一个字节数据回显到终端上,也就是把数据通过 rt_kprintf发送到终端上。rt_kprintf底层是如何发送串口数据的,这里由于篇幅有限,不再细说,直接说结论就是,rt_kprintf底层对接的是串口的轮询发送模式。

FinSH总结

由上文可以得出一些结论:

  1. FinSH 用串口外设作数据流时,其发送模式为轮询模式,接收模式为中断模式。

  2. 接收靠的是信号量做数据通信,且每次进接收中断时,只能接收单字节数据。

  3. 接收端当没有接收到数据时,FinSH将通过信号量挂起自身,因此属于非阻塞接收模式。

  4. 发送端 (也包括rt_kprintf)为轮询发送,因此属于阻塞发送模式。(这里延伸一下,rt_kprintf能在中断环境下使用么?答案是可以的,因为该函数没有影响中断环境状态的变化,亦没有等待信号量或者挂起线程等操作。不过还是尽量少在中断环境下打印数据,因为中断执行时间要尽可能短,加入打印将会使得中断执行时间超过预期,从而出现不安全的意外风险)

其他问题探讨

结合FinSH的一些特性,以及日常遇到的问题,总结了一下几个频发的点以供参考:

  1. FinSH 打开后接收不到数据:

    由于FinSH是中断接收模式,既然接收不到数据,那就直接定位以下几个点:

     1. `finsh_thread_entry` 线程是否初始化成功并在`finsh_getchar()`处等待?
     
     2. 输入任意字符时,`finsh_getchar()`能否立刻返回数据?
     
     3. 输入任意字符时,`finsh_rx_ind()`接收中断回调是否得到执行?
     
     4. 输入任意字符时,对应的串口是否能进入串口中断,并查看串口对应的  接收数据寄存器(RDR)  是否有值(该值就是你输入的字符对应的ASCII码值)?
     
     5. 如果连 接收数据寄存器都没有数据,那么排查串口配置的原因(包括时钟、引脚、中断等配置),或者是硬件的问题
    
  2. 硬件复位后系统无法输出:

    其实这个问题和FinSH无关,但是很多人觉得这个是控制台无输出,应该是FinSH相关的问题 ,因此我干脆把这个问题放在这里。

    这个问题和 问题1 很像,但是本质区别还是有的,就是复现的场景是复位后出现问题,那么基本可以定位到是和复位有关的。经验告诉我们,一些开发板由于考虑一键下载的功能,因此在开发板的串口上集成了一键下载电路,导致系统复位时直接改变了BOOT模式,从而进入串口ISP下载模式。这里给出解决办法的参考链接:硬件复位程序无法正常运行

  3. FinSH输出乱码:

    出现这样的问题,要么是电脑终端控制台软件的问题,要么是波特率等配置不对,要么就是硬件没有接地。

  4. 默认FinSH输出正常,切换其他串口后就无法工作:

    这个问题按照问题1去排查就行,大概率是第5点,配置有问题,串口输出重定向错误等等。

  5. 打开FinSH后系统就hardfault了:

    这种问题其实没有特别的方法,就按照普通的hardfault去查就行了。也可以参照这个链接Cortex-M内核硬件故障问题的分析方法

个人调试总结

本人在调试finsh的时候,通常是采用比较直接的手段,首先关注FinSH线程是否启动,如果是正常的话就直接拉出串口的发送数据寄存器TDR和接收数据寄存器RDR(根据MCU的版本和型号不同,这两个寄存器有的统一叫做DR数据寄存器,不过功能是一样的)。个人觉得这样的方式是最有效率的,下面以STM32F4为例,以MDK调试环境进行说明。

如何判定串口FinSH接收是正常的?

上文已经总结过,FinSH的接收是中断接收,那么很简单就在串口接收中断的位置打个断点,然后键盘输入一个字符看中断是否触发,并判断接收数据寄存器是否是输入的字符对应的ASCII码值。如图所示:

image.png

左边方框断点位置就是串口接收中断触发的位置,右边DR寄存器就是串口接收到的字符存放的位置,此时让程序全速运行。当不输入字符时,这个断点处的代码是不会执行到的,现在敲击键盘一个数字 “1”,如果是正常的情况的话,将会得到如下图所示的效果:

image.png

输入数字 “1” 后,可以看到程序执行到断点的位置了,此时的DR数据寄存器也获得了数字 “1”对应的ASCII码值,这就代表FinSH接收数据是正常的。

如何判定串口FinSH发送是正常的?
按照接收的测试方法类推,发送数据时,是使用的轮询发送模式,那么直接在stm32_getc()函数位置设置断点,如下图所示:

image.png

你可以利用rt_kprintf,或者是FinSH的回显功能,最终都会执行到此处代码段,然后查看DR寄存器数据是否是正确的即可。

image.png

应用层的串口问题

未完待续,见下一章节。。。

以上是关于RTT串口v1使用分析以及问题排查指南 的主要内容,如果未能解决你的问题,请参考以下文章

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

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

RTT-Studio使用CubeMx开发串口报错

SEGGER调试利器RTT,替代串口,高速数据上传

SEGGER调试利器RTT,替代串口,高速数据上传

使用RTT代替UART,把你的JLink变成串口调试助手~