良许Linux教程网 干货合集 串口 DMA 发送竟然用局部变量作缓存?

串口 DMA 发送竟然用局部变量作缓存?

之前,有个同事使用了串口查询的方式发送数据,让我非常不满。明明有DMA资源可用,他竟然不使用。作为一个对性能要求很高的人来说,我无法接受这种情况。所以我当时告诉他,有时间的话一定要改过来。

然而,几个月过去了他才有时间来处理这个问题。不过可惜的是,他在处理过程中还是遇到了问题。别无选择,只能找我来解决了。

具体的现象是这样的:使用查询方式时,一切都正常,从机能够正常接收数据。但是一旦切换成DMA方式,从机就无法正确解析数据,从机缓存的数据也不正确。

当时我也很困惑,不知道他是做了什么操作导致出现这种问题。因为他调用DMA发送函数的代码都是我很久之前写的,而且经过了很多次测试,不应该出现问题。

所以,在完全没有头绪的情况下,我决定同时调试两颗单片机。

是的,你没有看错,我使用了两个调试器同时对两个单片机进行调试。

非常简单,只需要在下面的图中进行选择即可。我估计很多经验丰富的人都不知道这个功能。(两个不同的工程)

image-20230913001244726
image-20230913001244726

这样一台电脑就可以同时调试主机和从机了。

当时首先查看了 DMA 外设和 UART 寄存器情况,发现并没有问题(毕竟如果这个错了,再怎么查应用代码也是没用的,排查问题要讲究先后顺序)。

又在线查了一会发现,如果我在 DMA 发送函数后打上断点,从机是可以正常解析的,这一点又让我疑惑了,所以为了防止从机代码可能有问题,直接让同事用一个串口模块接收数据。

串口助手显示,发送的字节数是正确的,但是只有帧头几个字节对的,其它字节全是错的。

一看到这里,鱼鹰大概就知道了,大概率是 DMA 发送缓冲区被篡改了,一查函数调用,瞬间就明白了是怎么回事,函数调用大概如下(细节没有展示):

void dma_send(DMA_Channel_TypeDef *DMAx, void *buff, uint8_t size)
{
  DMAx->CNDTR = size;
  DMAx->CMAR  = buff;
}

void send_data()
{
  uint8_t buff[8];
  
  buff[0] = 1;
  buff[1] = 2;
     ……
  dma_send(dma1, buff, 8); 
}

竟然用局部数组变量作为 DMA 发送的缓冲区,鱼鹰也是醉了。

那么为什么查询方式下,这样的代码不会出错,DMA 方式就错了?

要解答这样的问题,基础必须扎实:

1、DMA 传输原理

2、局部变量存放位置与特性。

只要知道这两点,这个问题就很容易避免。

但事实上,很多人只会大概用,根本没有真正理解。

DMA 串口传输凭什么说效率高?是因为它让串口速率传输更快了吗?一个字节传输本来要 1 ms,用 DMA 只要 0.5 ms?估计很多人都是这么理解的吧。

事实上,DMA 并没有加快传输速率,只不过是把传输的任务转交给专业的而言《数据传输还用 CPU?不如交给 DMA 吧!》,而 CPU 就可以专心干剩下的事情。

注意这里的转交一词,CPU 把必要的传输信息(比如传输地址、大小等)告诉 DMA 后,一般会启动 DMA,之后立刻运行后面的代码,****但此时 DMA 缓存地址里面的数据并没全部发送出去,如果这个缓存用的是局部变量,离开这个函数后,局部变量被回收,并会继续给其他函数使用,此时这个缓存的数据就被篡改了,这样 DMA 发送出去的数据当然不正确。

所以,从这个角度来说,DMA 并没有加快串口本身的传输速度,只是解放了 CPU 资源而已。但是 CPU 被解放了, DMA 所使用的 缓存 资源可不能也随之解放呀,只能等发送完毕后才能释放。所以最简单的方法是在 缓存 前面加一个 static 。

那么为什么查询不会出问题呢?查询是把所有缓存中的数据发送出去后,才会离开当前函数,这样局部变量始终存在,也就不会有问题了,不像 DMA 一样扔到缓存里面就溜之大吉,局部变量也随之溜了。

但是,还有一种特殊的局部变量,也能达到全局的效果。这就是操作系统中的主函数的局部变量。

void  task()
{
  uint32_t buff[32]; // 局部变量,但效果和全局变量差不多。
  while(1)
  {
  
  }
}

因为任务一般会被死循环包含,永不退出(前提条件),所以这里的局部变量也就不会被释放掉,所以有些情况下,为了更好的使用资源,会采用这种方式。

好了,今天的分享到此结束,有收获的话,记得三连支持一波呦!

以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !

137e00002230ad9f26e78-265x300
本文由 良许Linux教程网 发布,可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
良许

作者: 良许

良许,世界500强企业Linux开发工程师,公众号【良许Linux】的作者,全网拥有超30W粉丝。个人标签:创业者,CSDN学院讲师,副业达人,流量玩家,摄影爱好者。
上一篇
下一篇

发表评论

联系我们

联系我们

公众号:良许Linux

在线咨询: QQ交谈

邮箱: yychuyu@163.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部