STMCU小助手
发布时间:2025-10-24 13:51
|
这里分享三个STM32应用中遇到的小问题,一起看看。 一、多个SPI的DMA传输为何仅1个有效? 有人使用STM32F407配置了3个SPI并开启DMA来实现通信。他在调试中发现,虽然配置了3个SPI以及各自DMA功能,但每次只能启动1个SPI的DMA收发功能。总是先使能哪个SPI的DMA功能,该SPI就工作,其它则不工作。也就是说,3个SPI及相应的DMA都能工作,就是不能同时工作。 客户使用Cubemx进行配置,基于LL库组织代码。下面截图是该用户提供的部分配置代码【SPI2和SPI3的初始化配置】:
如果SPI2的DMA使能代码在前SPI3在后,SPI2工作正常,SPI3则不工作。
如果SPI3的DMA使能代码在前,SPI3工作正常,SPI2就不工作。
但是,如果只配置任意1个SPI的DMA传输,都工作正常。 这样看来,他的SPI及DMA配置应该没啥问题。那问题在哪呢? 我看他的初始代码里有开启各DMA传输完成中断,怀疑是不是每次一使能某个SPI的DMA后,就触发了DMA传输完成中断。如果用户没有在DMA传输完成中断程序里做标志的清零,就有可能来不及继续运行后续的代码,即后续其它SPI的DMA传输的使能代码。 我拿STM32F407开发板,使用SPI1和SPI2进行测试验证。使用CubeMx进行配置并基于LL库组织代码。SP1和SP2的配置基本一样,都开启了各自的收发的DMA传输。
创建工程后,添加相应用户代码,进行测试。
刚开始测试时,我每次让SPI1/SPI2收发8个数据,循环收发。结果发现,不论我先使能谁的DMA传输,都没有问题。
然后,我将DMA传输中断服务程序屏蔽掉,再行测试。不论我先使能谁的DMA传输,仍然都没有问题。显然,此时因为DMA传输中断服务程序被屏蔽,该中断会被没完没了的触发。 可能是传输数据较多的原因,在上一个DMA传输完成中断来临之前,不至于没有时间和机会来运行后续代码。于是我不断减少每轮DMA传输的个数,当减到1个时,果真出现了用户反馈的情形,即先使能哪个SPI的DMA传输,谁就工作正常,另外的就不工作了。 下图就是DMA传输个数为1,先使能SPI1的DMA的运行情况。
下图就是DMA传输个数为1,先使能SPI2的DMA的运行情况。
看到这里,看官应该明白怎么回事了。该问题来自于论坛,该用户估计是新手,可能他根本就没有准备相应的中断服务程序,刚好测试时又只收发1个数据,导致一使能某个DMA就立即触发中断,由于中断标志没能及时清零,导致CPU高速循环进出中断而不能运行其它代码了。【注:如果他使用cubemx创建基于HAL库的代码,就不会发生这个问题,除非他在cubeMx特意关闭DMA中断。因为HAL库里对各种中断最基本的标志清零管理都帮用户做了。用户往往只需补充些回调函数的代码。】 该问题虽然比较极端,但因类似响应时间、代码运行时序、中断管理方面问题我们在开发时还得多加留意。 二、多行UART的DMA输出代码怎么只有后面输出行才有效? 经常有STM32用户在做产品开发时遇到这种情况,就是基于DMA方式做UART输出时,连续几行的发送输出,只能看到最后一行的输出结果。 下图就是一种应用情景,本来希望相继输出2行不同内容,结果只输出后面代码的相应内容。
本来是希望输出电压信息和温度信息,结果只看到温度信息。 对于初次使用DMA的人来说,很容易发生类似问题。其实,这里的DMA发送API函数只是完成相关配置及使能操作,并不等于DMA的传输完成,这也是非阻塞式启动代码的特点。具体到这里,刚完成前面配置马上又来改变配置了,最后就变成只有最后那行启动代码真正生效了。 这个地方被调用的外设是同一个UART,即使使用DMA模式,也得等前次执行完再来执行后面的。我们可以查询外设状态量、或者基于DMA产生的完成事件作为结束标标,或者简单粗暴点来个合适的阻塞式延时。要不就别使用DMA或中断方式,而使用阻塞式的查询操作。我这里为了演示,就简单点,直接加点延时。[注:其实ST官方库代码里的外设状态量就有避免出现类似问题的作用。但有时我们的代码又未必完全按照库的逻辑走,会手动调整状态量,或者说对库里那些状态量的功能也未必很清楚。]
类似问题,感觉遇到的人较多。之前聊过这个话题,这里再次提醒下。 三、如何避免UART基于DMA方式接收时出现数据错位? 有人在使用STM32G4系列和STM32H5系列的UART,并开启DMA方式的接收,DMA工作在Normal模式。下图是UART接收出错的情形。
如上图所示,在基于UART DMA方式做定长接收时,由于实际发送的数据个数因为各种因素可能大于接收定长,出现接收数据错位的情形,而且总是第一个数据是错的,导致整串数据错位无法使用。【注:这里都是基于HAL库函数和CubeMx的配置而言的。】 要解决这个问题的方法不是唯一的,具体跟你是否开启UART出错监视、是否使能UART中断等。这里有个简单稍显粗暴、适用性较强的可靠方法,就是每次启动UART的DMA接收之前将UART初始化代码再运行一次。 比方像下面这样:
上面的RxCplt标志和DlyCnt变量在DMA接收完成中断里被赋值,DlyCnt延时一段时间确保每次通信结束后再开启下次接收。 这个做法我在STM32G4和STM32H5的芯片上都测试过,可靠有效。至于其它办法需结合具体芯片和特定配置来看,在此不做延伸。 OK,今天的STM32应用分享就到这里,供君参考。 |
经验分享 | STM32 DMAMUX应用示例
经验分享 | STM32C0 HAL 库的 SPI 驱动导致的 Hardfault 问题分析
经验分享 | STM32双定时器+ADC+DMA实战案例
经验分享 | STM32U5系列TIMER+DMA+DAC应用演示
经验分享 | 基于STM32H7 UART 空闲事件及DMA传输示例
经验分享 | SPI传输时为何丢失2个数据?
经验分享 | 关于STM32 DMA传输的两个问题释疑
经验分享 | STM32G4 UART+TIMER+DMA应用示例
经验分享 | STM32 TIMER+DMA输出PWM异常二案例
经验分享 | STM32H7 EXTI + SPI +DMA 双缓冲应用演示
微信公众号
手机版