硬汉嵌入式论坛

 找回密码
 立即注册
查看: 4366|回复: 8
收起左侧

[有问必答] 使用串口bsp的疑惑

[复制链接]

4

主题

18

回帖

30

积分

新手上路

积分
30
发表于 2020-9-17 10:55:10 | 显示全部楼层 |阅读模式
请教大佬们,我在使用串口bsp的时候,如果直接获取一个字符,然后发送该字符,相当于做一个回显的的测试,发现会存在一些问题。大概代码如下:

printf("Do you like www.armbbs.cn?");
printf("Yes I do.");
while(1)
{
        if( comGetChar(COM2, &ch) )
        {
                comSendChar(COM2, ch);
        }

}

然后我就通过串口调试助手给板子发送数据,当我发送速度快一点的时候,发现回显不正常了,大概情况如下,看样子是FIFO的读写指针的问题。
问题我也在排查,如果我自己能解决到时候把方法贴上来,嘻嘻。



回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117586
QQ
发表于 2020-9-17 11:26:24 | 显示全部楼层
注意:

V5,V6,V7板子串口FIFO驱动中的串口2未开启发送,因为这个接口用于GPS,仅需接收,移植要注意
https://forum.anfulai.cn/forum.php?m ... id=97592&fromuid=58
(出处: 硬汉嵌入式论坛)

如果需要参考,此贴即可
发一个bsp_uart_fifo.C文件配置串口1,2,3,4,5,6可以同时使用的例子,方便大家对比参考
https://forum.anfulai.cn/forum.p ... 2149&fromuid=58
(出处: 硬汉嵌入式论坛)


回复

使用道具 举报

4

主题

18

回帖

30

积分

新手上路

积分
30
 楼主| 发表于 2020-9-19 15:51:59 | 显示全部楼层
eric2013 发表于 2020-9-17 11:26
注意:

V5,V6,V7板子串口FIFO驱动中的串口2未开启发送,因为这个接口用于GPS,仅需接收,移植要注意

多谢大佬回复,驱动已经移植OK了的,经过排查发现当一次发送的数据超出串口的接收缓冲区长度的时候,会出现FIFO读写位置不一样的BUG。检查代码发现串口接收的时候rx_count有一点问题,改过来以后就正常了,我将问题复现过程和解决办法贴出来,方便以后有需要的小伙伴:

驱动文件名:bsp_uart_fifo.c   V1.8版本

串口2的参数:115200,发送缓冲区大小1024字节,接收缓冲区大小10字节(特意设置为10字节方便快速复现问题)


实验步骤:
(1)串口初始化,使能发送和接收。
(2)调用以下代码,进行串口回显测试:
  1. int main(void)
  2. {
  3.         uint8_t data;
  4.         uint32_t i = 0;
  5.         bsp_init();                                /* 驱动初始化 */
  6.        
  7.         while(1)
  8.         {
  9.                 if( comGetChar(COM2, &data) ) /* 串口缓冲区没有数据,直接返回 */
  10.                 {
  11.                         /* 写一个字节到USART2 */
  12.                         USART2->DR = data;
  13.                        
  14.                         /* 等待发送结束 */
  15.                         while((USART2->SR & USART_SR_TC) == 0)
  16.                         {}
  17.                                
  18.                         printf("123456789:%d\r\n", i++);
  19.                 }
  20.         }
  21. }       
复制代码
(3)然后就使用串口调试助手,一次发送12字节的数据到串口2,请看下图

(4)然后检查g_tUart2结构体,发现此时接收FIFO的读写位置已经不一样了

(5)然后检查代码,发现rx_count的计算不太对,一旦出现丢包的情况,rx_count就不会进行计算,因此导致读写位置的差异。

(6)最后改动一下rx_count的位置,到这段代码的外面,让数据出现丢失的时候,也计算rx_count。

(7)然后再次测试,就发现正常了。



CI60%}~H_(M98S[(9OUBGEJ.png
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117586
QQ
发表于 2020-9-19 16:01:37 | 显示全部楼层
tanzhuolin 发表于 2020-9-19 15:51
多谢大佬回复,驱动已经移植OK了的,经过排查发现当一次发送的数据超出串口的接收缓冲区长度的时候,会出 ...

这不是bug,这是环形FIFO,FIFO满了要覆盖的,所以你还要及时提取。
回复

使用道具 举报

4

主题

18

回帖

30

积分

新手上路

积分
30
 楼主| 发表于 2020-9-19 16:26:16 | 显示全部楼层
发现直接截图贴上来的图片看不到,好尴尬吖,我重新梳理一下吧。
驱动:bsp_uart_fifo.c V1.8版本

问题点:当串口一次接收的数据长度超出接收缓冲区大小的时候,会出现接收缓冲区的读写指针位置不一样的情况。

问题复现步骤:

(1)为了方便问题复现,先将接收缓冲区的大小改为10字节。
  1. #if UART2_FIFO_EN == 1
  2.         #define UART2_BAUD                        115200
  3.         #define UART2_TX_BUF_SIZE        1*1024
  4.         #define UART2_RX_BUF_SIZE        1*10
  5. #endif
复制代码

(2)使用以下代码进行测试。
  1. int main(void)
  2. {
  3.         uint8_t data;
  4.         uint32_t i = 0;
  5.         bsp_init();                                /* 驱动初始化 */
  6.        
  7.         while(1)
  8.         {
  9.                 if( comGetChar(COM2, &data) ) /* 串口缓冲区没有数据,直接返回 */
  10.                 {
  11.                         /* 写一个字节到USART2 */
  12.                         USART2->DR = data;
  13.                        
  14.                         /* 等待发送结束 */
  15.                         while((USART2->SR & USART_SR_TC) == 0)
  16.                         {}
  17.                                
  18.                         printf("123456789:%d\r\n", i++);
  19.                 }
  20.         }
  21. }       
复制代码
(3)使用串口调试助手一次发送12字节的数据,请看下图。
1.png
(4)此时发现接收的数据已经不对了,然后去检查串口那个结构体的内容,发现接收缓冲区的读写位置也不对了,请看下图:
2.png


解决方法:
(1)经过排查后,发现rx_count的计算不太正确,这是导致读写位置不一样的原因,当出接收的数据量超出接收缓冲区大小的时候,rx_count没有将这部分的数据计算进去,请看下图。
3.png
(2)解决的方法就是当FIFO丢失数据的时候,不要写入FIFO,丢了就让它丢了吧,至少不会引起错误,请看下图。
4.png
(3)再次测试,发现就不会出现这种问题了。

5.png
6.png
回复

使用道具 举报

4

主题

18

回帖

30

积分

新手上路

积分
30
 楼主| 发表于 2020-9-19 16:29:26 | 显示全部楼层
eric2013 发表于 2020-9-19 16:01
这不是bug,这是环形FIFO,FIFO满了要覆盖的,所以你还要及时提取。

这个算是BUG吧,因为出现问题以后,回显的数据不对了,例如我发送的是1,但是返回的数据不是1
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117586
QQ
发表于 2020-9-19 16:45:42 | 显示全部楼层
tanzhuolin 发表于 2020-9-19 16:29
这个算是BUG吧,因为出现问题以后,回显的数据不对了,例如我发送的是1,但是返回的数据不是1

这么处理就复杂了,其实最好的还是现在这样,什么都不要管,及时接受处理。

像OS里面,大部分的处理都是队列满了之后,阻塞等待,当然也可以选择覆盖。
回复

使用道具 举报

4

主题

18

回帖

30

积分

新手上路

积分
30
 楼主| 发表于 2020-9-19 17:16:25 | 显示全部楼层
eric2013 发表于 2020-9-19 16:45
这么处理就复杂了,其实最好的还是现在这样,什么都不要管,及时接受处理。

像OS里面,大部分的处理都 ...

哎呀,多谢大佬提醒,确实这么处理不好,把FIFO的优势都去掉了。但是如果什么都不做还是会存在问题的,还是刚刚说的那样,当出现问题以后,再次发送数据做回显的实验,当我发送1的时候,回显的数据不是1,而是缓冲区中另外一个位置的数据,这不太合理,因此rx_cout还是需要将溢出的数据也计算进去,这样getChar的时候才会正常。

请看我以下的对比实验:
rx_count在溢出的时候不进行计算的情况,也就是1.8版本的原始代码:
(1)先发送123456789012,此时返回
1123456789:0
2123456789:1
3123456789:2
4123456789:3
5123456789:4
6123456789:5
7123456789:6
8123456789:7
9123456789:8
0123456789:9
1123456789:10


(2)然后我再次发送1,但是实际返回的不是1,而是2,这应该是有问题的。
2123456789:11

而在数据溢出的时候,rx_count也计算进去,就不会出现这个问题。
  1.   uint8_t ch;

  2.                 ch = READ_REG(_pUart->uart->DR);
  3.                 _pUart->pRxBuf[_pUart->usRxWrite] = ch;
  4.                 if (++_pUart->usRxWrite >= _pUart->usRxBufSize)
  5.                 {
  6.                         _pUart->usRxWrite = 0;
  7.                 }
  8.                 if (_pUart->usRxCount < _pUart->usRxBufSize)//0 1 2 3 4 5 6 7 8 9 10
  9.                 {
  10.                         //_pUart->usRxCount++;
  11.                 }
  12.                 else
  13.                 {
  14.                         static uint32_t rx_fifo_lost = 0;
  15.                         rx_fifo_lost++;
  16.                 }
  17.                 _pUart->usRxCount++;//rx_count也需要计算溢出的部分,这样getchar才会正确
  18.                 /* 回调函数,通知应用程序收到新数据,一般是发送1个消息或者设置一个标记 */
  19.                 //if (_pUart->usRxWrite == _pUart->usRxRead)
  20.                 //if (_pUart->usRxCount == 1)
  21.                 {
  22.                         if (_pUart->ReciveNew)
  23.                         {
  24.                                 _pUart->ReciveNew(ch); /* 比如,交给MODBUS解码程序处理字节流 */
  25.                         }
  26.                 }
复制代码
这个时候,即使溢出了,也不会出现读写位置不一样的情况。

但是这么处理,也还是可能出问题,就是当rx_count满了16bit的时候溢出变为0,这时候就还是会出问题,不过这有65535个字节,当出现这么大的丢失数据还没有取出的时候,说明应用代码也有问题,使用的时候注意应该问题不大。但是rx_count那里应该还是要改的,不知道我的理解对不对。



回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117586
QQ
发表于 2020-9-20 09:49:13 | 显示全部楼层
tanzhuolin 发表于 2020-9-19 17:16
哎呀,多谢大佬提醒,确实这么处理不好,把FIFO的优势都去掉了。但是如果什么都不做还是会存在问题的,还 ...

使用我么这个方案要做的就是避免溢出,FIFO开的稍大点,及时处理这才是正确的做法。

去后续补救溢出后的处理都不是明智的,就像大部分OS那样,超出队列大小,就禁止接收,等待有空间可用。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-8-16 15:20 , Processed in 0.171979 second(s), 27 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表