现在有人谈到这样一个需求,他使用STM32F429芯片做开发,其中用到TIM2做3路PWM输出。另外有个上位机跟STM32的UART接口相连,上位机可能不定期地需要通过UART接口给STM32发送新的占空比参数,而且每次都发送3个输出通道的比较参数。如何快捷地实现这个功能呢?要求收到数据后尽快修改3个比较通道的参数。
STM32F429的TIM2是32位定时器,3个通道的CCR寄存器也是32位的。那么上位机每次发3个通道的参数对应12字节的数据。
显然,整体上功能不算复杂。我们可以先通过UART收到12字节数据,稍加整理后手动将数据逐字写到3个CCR寄存器来改变PWM输出。说实在的,这个方案的实现过程还是很清晰的,相信也是很多人的选择。或许有人觉得操作起来稍显琐碎,有没有比这个操作更方便快捷的呢?
既然这样,我们可以调整下做法。因为STM32的高级定时器和大部分通用定时器都支持基于定时器事件的DMABurst传输。即先通过UART收到12字节数据,稍加整理后借助于定时器事件的DMA Burst 传输 一次性地将3个字【12字节】的数据传输到CCR1CCR2CCR3三个寄存器,从而实现3个通道PWM的占空比的调整。实际上就是将上面第一种方案的手动逐个修改变为DMA批量修改。
毫无疑问,这个第2种方案也是可行、挺方便的。但使用该方案就得研究下基于定时器事件的DMABurst传输的内容,数据的修改需借助定时器事件。当然,所用定时器还得支持TIMERBurst DMA传输功能。说实在的,数据修改是变快捷了,但貌似难度提升了、代码方面相比第一种方案变得复杂了。【为何说貌似,毕竟难易因人而异。】那有没有一种更为通用的常规做法来实现上述功能呢?
我们不妨看看第3种方案。因为这里所选芯片STM32F429的DMA是带4字FIFO的,我们可以通过UART按字节接收数据,然后经过FIFO打包成字,再通过DMA直接将数据送到3个输出通道的CCR寄存器。这样操作的话就非常简单快捷,DMA的传输不涉及定时器事件,也不再要求定时器支持基于定时器事件的DMA Burst传输功能,自然也就无须使用者花精力研究这块内容。相比前两种方案,代码方面也更为简单。
那么,我这里就专门选择第3种方案,来简单演示下它的实现过程。
下面我用UART自发自收来模拟来自上位机的修改CCR参数的数据信号。
TIM2使用3个通道做PWM输出,参考配置如下:
我这里选用USART1外设,开启其TX/RX事件的DMA传输功能。参考配置如下:
我用32位TxData【3】数组存放待传输的3个CCR值。
uint32_t TxData[]={2000,4000,8000};//3个初始值我随便定的。
使用CubeMx配置完成后,添加相关用户代码。【基于HAL库组织代码】
上面三行代码的功能一目了然,即开启TIM2定时器3个通道的PWM输出功能。
下面循环体代码,就是修改CCR的值、通过UART 发、收,利用DMA修改3个通道的值,进而调整PWM输出占空比。这样循环进行,我们通过示波器查看输出结果。
稍作编译调试,即可通过示波器可以看到预期的结果。下面三副截图代表不同时刻三个通道的输出情况。
从上面截图不难看出,3个输出通道的PWM占空比在动态改变。当然,我们也可以通过IDE的寄存器观察窗口查看CCR值的改变,如下图所示:
上面重点针对第3种方案的实现过程做了简单的演示。基本配置、实现代码都是3种方案中最简捷的,也是较为常规的通用做法。其中的关键配置和实现代码我都清晰地贴出来了,以便有需要的人士参考。其实,只要我们对STM32的DMA功能把握得比较好,吃透原理、把握细节,实现起来也不算难。
最后顺便用一个与本文内容相关的小细节提醒来结尾。关于STM32的定时器的各个寄存器在地址空间中的地址安排,其中TIMx_CCR1,TIMx_CCR2, TIMx_CCR3,TIMx_CCR4这几个寄存器的地址总是依次顺序摆放的,了解这点对阅读上面个别函数调用有帮助。