STM32 TIMER+DMA输出PWM异常案例的问题解析

描述

案例1:STM32U575的TIMER+GPDMA输出PWM异常

有人使用STM32U575的TIMER加上DMA做PWM输出。具体就是利用某TIMER的一个通道的比较事件触发DMA,通过DMA修改CCR值来实现指定占空比的PWM输出。

对于很多STM32用户来说,这个应用算是比较常见的做法了。可当他使用CubeMx完成配置,生成工程添加相应用户代码后,发现输出跟预期不一致。

而当他使用STM32F1或STM32G0系列来实现时又没有任何问题。其实,定时器基本配置都差不多,都是调用ST提供的HAL库函数HAL_TIM_PWM_Start_DMA()。该函数的原型就是下面样子:

定时器

鉴于该用户的反馈,我找了STM32H563的开发板,也来做些验证测试。使用TIM1,快速对其做配置,开启通道1比较事件的DMA请求及PWM输出,让DMA动态修改CCR1来改变PWM输出占空比。使用CubeMx进行配置:

定时器

定时器

创建工程后,稍微添加点用户代码后即可进行测试。下面数组里有10个数据,对应10个不同占空比的脉冲。

定时器

我期望输出下面的波形:

定时器

当我基于上面配置及用户代码运行程序后,发现输出却是这样的:

定时器

显然跟我的预期相差甚远。那是怎么回事呢?

我也顺便找了块STM32G4的开发板,快速地验证了该功能,输出跟预期完全吻合。那STM32U5有什么特别的地方呢?后来经过快速浏览手册和阅读库函数,终于还是发现了问题原因所在。

不管我们使用哪个STM32系列,实现上述功能,使用HAL库的话,调用的库函数都是一样的。都是前面提到过的HAL_TIM_PWM_Start_DMA(),【注:最后都会调用HAL_DMA_Start_IT】,在这个函数里有个Length变量。该变量在STM32U5系列的HAL库里的约定含义跟其它系列,比如F4/G4/G0等的不太一样。

在STM32U5系列库函数里,该Length变量表示的是一轮【块】传输过程中DMA从源搬到目的的数据所对应的字节数;【下面截图来自STM32U5系列HAL库,注释中特地强调以字节为单位】

定时器

而在F4/G4/G0这些系列的库函数里,该变量表示的则是DMA从源搬到目的的数据个数【见下面函数注释】;

定时器

当然,我们也可以从STM32参考手册里的寄存器定义看出差别来。在STM32U5系列GPDMA里描述DMA传输长度的寄存器是GPDMA_CxBR1,其中字段BNDT[15:0]表述块传输过程中从源传输到目的的数据字节数。

定时器

而在STM32G4/F4系列里,描述DMA传输长度的寄存器是DMA_CNDTRx。

定时器

明确表示该长度为DMA传输的数据个数,一轮【块】最多传输64K个数据。

介绍到这里,我们就基本明白了,为什么同样的操作及函数,STM32F4/G4/G0/F1系列表现正常,而STM32U5表现异常了。回到开头,现在源数据宽度为16位,希望一轮传输10个数据,那对应的传输字节数就是10*2。当我把上面的那个DAM启动函数里的Length调整20时输出也的确正常了。

也就是说,对于STM32U5系列,这个Length应设置成传输的数据个数乘以源数据宽度即可。当然,别的系列是否也有类似问题不好说,具体应用时我们稍微留意下。总之,遇到问题时不能完全死守经验,必要的阅读手册和阅读函数也是必要的。

案例2:基于TIMER Burst DMA实现PWM输出异常

有人使用STM32G4系列芯片开发产品,用到基于STM32定时器的DMA BURST传输。他使用定时器TIM1的更新事件同时修改其3个输出通道的PWM占空比并保持同步输出。如下图所示:

定时器

可是,他在调试过程中发现个非常奇怪的现象。感觉只要接上STLINK调试器,输出就不正常。主要体现就是TIM1的3路PWM输出不再保持同步,如下图所示的情形:

定时器

结合他的反馈和本人经验,我觉得问题可能不是出在连接调试器上,而很可能与打开PC端IDE的寄存器窗口有关。经了解,他当时使用的IDE是ARM MDK,调试过程中也的确一直打开了相关TIMER的寄存器观察串口。

定时器

当他尝试把TIMER1的寄存器视窗关闭后,重新运行代码,3路输出就恢复同步输出了,完全符合预期。看来,输出正常与否跟接不接STLINK调试器是没有关系的,根本原因是打开了TIMER的寄存器视窗。

那问题来了,TIMER的寄存器观察窗口被打开后怎么就影响到PWM输出呢?

这里可以有个初步判断,因为芯片的调试组件不时地访问TIMER的某些寄存器而影响到PWM输出了。TIMER的寄存器这么多,具体是哪个或哪几个寄存器因为调试组件的访问而被影响其内容,进而影响到PWM输出呢?

快速浏览一遍TIM1的寄存器,并没有立即发现或锁定哪个寄存器因被读取后会明显影响当前定时器的正常运行的。后来,他干脆就将那些TIMER寄存器一个个地试,看看到底哪个寄存器因为中途被读取后会影响到当前PWM输出。功夫不负有心人,最后发现跟寄存器TIMx_DMAR有关!

刚才前面说过了,他现在是使用STM32定时器的DMA BURST传输。该类传输涉及到2个专门的寄存器,分别是TIMx_DCR 和 TIMx_DMAR。

定时器

其中,DCR寄存器配置BURST长度和每次发起Burst传输时参与传输的定时器的第一个寄存器位置。DMAR寄存器专门用来提供DMA访问定时器寄存器时的地址,我们可以将DMAR寄存器看成一个地址指针,DMA访问DMAR寄存器时所获得的外设地址【即定时器寄存器地址】由算式(TIMx_CR1地址)+(DBA+DMA 索引号)x 4来决定。

定时器

其中,DBA、DBL在DCR寄存器中配置好了的,是固定值。DMA索引号在0~DBL之间依次动态变化。具体到本案例应用,DBL等于2。

为什么当开启寄存器观察窗口后,DMAR寄存器被调试组件访问后会影响到此处Burst传输呢?关于这点,我是这样理解的。本来DMAR寄存器是专门给DMA访问来实现定时器寄存器与内存间的Burst传输的。在这个过程中,DMA Index基于DMA访问自动调整从而实现Burst传输。如果说,在这个过程中调试组件也参与进来对DMAR寄存器进行访问,这时可能导致DMA Index变更的混乱,从而导致对定时器寄存器访问序列的混乱,最后导致3路PWM输出的混乱。


审核编辑:刘清

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分