打印
[综合信息]

DMA 导致 SPI 异常停止的原因分析、DMA 配置的那些坑

[复制链接]
3748|19
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2021-12-1 08:50 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
缘起

  在最近的项目测试中发现,SPI 通信总是莫名其妙的失败,查看寄存器发现 SPI 已经被停止了。根据手册,SPI 在异常情况下会被强制停止(SPI 的使能为被清零),而根据波形显示通信过程没有问题。下图是我实际中的 DMA 及 SPI 使用情况:

  • SPI1 仅使用发送功能,SPI2 仅使用接收功能,两者均使用 DMA。由于 SPI 没有仅发送模式,因此 SPI2 必须要配置一个 TX,否则导致 SPI 报错(实际并不配置 SPI 的发送引脚)。
  • 在初始化时,先初始化了 SPI2(含 DMA),然后再初始化的 SPI1(含DMA),这里是关键。
  • 在初始化之后,SPI 立刻开始工作(有数据通信)。其中,SPI2 大约以 8MHz 的速率不停的接收数据,SPI1 则以 1MHz 的速率在需要的时候才会发送数据。
  • 问题是在以上流程中,在配置 SPI2 时,有概率导致 SPI2 异常停止!



问题分析

  最初怀疑是 SPI 的问题,因为在初始实现这部分功能时就遇到了很多坑,我也写过博文华大 MCU 之五 SPI 从机 DMA 模式 配置(不能正常接收问题处理) 来专门记录遇到的问题。但是再一次整理了一下 SPI 的配置流程,并没有发现啥问题。在测试中发现,将 SPI2 使用的 DMA 停止,则不会出现问题,于是开始调查 DMA 的问题。



使用特权

评论回复
沙发
tpgf|  楼主 | 2021-12-1 08:51 | 只看该作者
DMA 的坑
  最开始将问题重点放到了 DMA 的配置上,怀疑是不是 DMA 部分配置项不正确。但是,读数情况下 SPI 是工作正常的,如果是配置不对,应该是一个必出现的问题。转而又开始怀疑是不是配置时序(寄存器的配置的先后顺序)不对,反复查看代码也没有发现问题。正在一筹莫展之时,有同事提出了手册中 DMA 章节给出的一个注意事项:


  其实,最开始实现 SPI 驱动时就关注过该注意事项,只是开始的理解是,这条注意事项是针对同一通道而言的,同一通道在工作时不能再次配置上面说的这些寄存器,不同 DMA 通道之间应该互不影响。然而实际情况是,只要 DMA 有任意一个通道在传输数据,其他所有通道都不可以配置!

  这就导致了一个很大的问题,外设在工作中不可避免的要重新配置 DMA。例如,SPI 的 DMA 发送,由于发送数据长度是动态变化的,必须每次重新进行配置,则根据上面这一条,必须把该 DMA 下的其他通道(例如上面我的 SPI1 使用的通道)也都全部停止,这样就可能导致其他外设丢失数据。

 最麻烦的是,这个问题没有找到任何其他的解决方法,只能是停止该 DMA 下的所有通道,进而来配置自己需要的那个通道参数。起初在寻找解决方法的时候,也考虑是不是可以用下图所示的寄存器位来进行一下判断,而是实际是下面的位根本没有任何用处。

还是以我上面的使用示例来说,SPI2 作为从机接收,由于使用了 DMA,MCU 并不能确定数据何时到来。即使 MCU 检测 DMAACT 未动作,可能在实际配置通道时,DMA 又变为了动作状态。


使用特权

评论回复
板凳
tpgf|  楼主 | 2021-12-1 08:52 | 只看该作者
配置

  根据手册,使用 DMA 时需要先写寄存器将 DMA 控制器使能,使能方法是写 DMA 使能寄存器 DMA_EN.EN 位。 如下图所示:

我试了一下,不先使能貌似也没法发现啥问题啊!不知道为啥!?


无法获取当前传出数据长度

  先来说一下需求:在串口驱动中,串口的接收使用 DMA 来实现,DMA 配置为循环模式,在指定缓冲区中循环存放收到的数据,通过读写指针来标记数据的读和写位置。

  然而,驱动库中 DMA 接口并没有能获取当前 DMA 传输了多少字节的接口!!!无奈只能选择修改驱动库,添加一些指定的接口,如下图所示:


 后来,为了不修改驱动库,我直接把接口放到了自己的驱动里面,然后使用寄存器直接读取:M4DMA1->MONRPT0_f.DRPT,这样的话,需要注意与通道号的对应关系,如 MONRPT0 即通道 0。



使用特权

评论回复
地板
tpgf|  楼主 | 2021-12-1 08:53 | 只看该作者
中断的使用

  在华大的 DMA 中,中断默认都是开启的,这点在配置 DMA 的时候需要特殊注意。我们需要使用 DMA 的屏蔽中断寄存器来屏蔽不使用的中断。如下图示:


在实际写代码时,需要调用 en_result_t DMA_DisableIrq(M4_DMA_TypeDef* pstcDmaReg, uint8_t u8Ch, en_dma_irq_sel_t enIrqSel); 来关闭不需要的中中断。例如,DMA 的块传输完成中断 和 传输完成中断 通常不会一起使用!这点对于用惯了 ST MCU 的人来说需要特殊注意!



结论

在使用中,发现华大 MCU 的外设配置中,有不少的坑,尤其是对于我这种用惯了 STM32 的人来说。例如之前说的 SPI、中断、这里说的 DMA 等等。

千万不要完全相信华大 MCU 驱动库中给出的示例,里面是丢三落四各种错误!

在用户手册中一些比较重要的点,手册往往是一笔带过,在我们实际使用中容易被忽略,而往往忽略的就是问题的关键。而且对于异常情况,手册中没有很详细的描述,导致真正出现问题时,从手册中不能很快分析出原因。

对于某些外设,有很多用来观察外设工作的寄存器(ST 没有这部分寄存器),我们在实际使用中,往往需要等待这些寄存器中的某些位被置位之后才可以继续操作,否则会出现各种错误



使用特权

评论回复
5
华大单片机| | 2021-12-1 09:32 | 只看该作者
您好,毕竟国产要跟国外来区别。华大的DMA确实是有些不同,如果可以最好有工程师来协助处理。

使用特权

评论回复
6
sadicy| | 2021-12-1 13:47 | 只看该作者
用惯了一种片子,换其他的话,总会不由自主的对比
然后觉得现在用的为什么和之前不一样
就好像用惯了安捷伦的万用表示波器,在用其他的,界面都别扭一样~~~

不过,现在国产芯片确实好很多了,发展也快了~~一些方面,优势也有了

使用特权

评论回复
7
caigang13| | 2021-12-3 08:35 | 只看该作者
谢谢楼主分享避坑经验

使用特权

评论回复
8
两只袜子| | 2021-12-6 09:29 | 只看该作者
学习下楼主分享的闭坑经验

使用特权

评论回复
9
andygirl| | 2021-12-8 17:03 | 只看该作者
学习一下,DMA不是 很熟

使用特权

评论回复
10
duo点| | 2021-12-9 16:18 | 只看该作者
学习学习

使用特权

评论回复
11
xing650721| | 2021-12-10 00:25 | 只看该作者
DMA还是很有用处的

使用特权

评论回复
12
daichaodai| | 2021-12-10 07:53 | 只看该作者
谢谢分享经验

使用特权

评论回复
13
麻花油条| | 2021-12-13 14:37 | 只看该作者
学习学习

使用特权

评论回复
14
alxd| | 2021-12-20 14:59 | 只看该作者
哈哈,挺好,感谢楼主分享

使用特权

评论回复
15
Allison8859| | 2021-12-20 15:01 | 只看该作者
能早点看到就好了能少走不少弯路

使用特权

评论回复
16
guijial511| | 2021-12-21 08:10 | 只看该作者
哈哈,楼主是踩了多少坑啊?

使用特权

评论回复
17
littlelida| | 2022-1-18 15:14 | 只看该作者
确实有一些点很简单的带过了,但是感觉总体来说,还可以,
华大甚至其他一些国产品牌的片子,还是有很多不错的。
其实ST也会有一些坑,只是踩的人多了,人家一点点的改了而已。

感谢楼主分享采坑经验 ,分享还是很好的

使用特权

评论回复
18
热任务人| | 2024-4-3 11:15 | 只看该作者
那需要同时对同一个dma不同通道进行设置,这怎么解决

使用特权

评论回复
19
xdvca| | 2024-7-31 22:34 | 只看该作者
要生成 38.4kHz 的红外信号,你可以使用定时器模块来产生 PWM 信号。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1897

主题

15568

帖子

11

粉丝