最近一直在研究STM32,上一篇文章是:STM32G0 的IIC 接口做为slave从机应用程序示例,这篇尝试使用STM32的定时器输出PWM,驱动基于WS2812的RGB灯带。无意中发现了PWM输出时,第一个周期异常的问题。后面网上搜了一下,发现确实有这个现象,结合实际使用情况,通过调整输出方式,解决了这个问题。
接下来说一下详细的过程。
一、STM32基础配置
这里使用STM32G030F6单片机做演示,打开SWD调试接口,时钟使用芯片内部的高速RC,配置为64MHZ.


基操,不解释。
二、定时器TIM3配置
这里配置定时器TIM3的通道2为PWM输出模式,具体配置参数如下:

时钟源选择内部时钟;
通道2配置为PWM输出模式;
Prescale,即分频,这里是0,也就是不分频,64MHZ;
Period这里是80-1,因为驱动WS2812的PWM通常为800KHZ,64MHZ/800KHZ=80,所以周期这里是80-1.
Pulse这里写26,即占空比为26/80,32.5%。
三、定时器TIM3的DMA配置
因为WS2812的一个灯的颜色由24个PWM信号控制,不同的占空比对应不同的颜色,所以这里使用DMA的方式快速更新PWM的占空比。配置如下:

这里注意几个地方:
- 1、方向,内存到外设。
- 2、优先级,这里建议选中级,以前选低级好像是有些异常,但是不确定。所以保险起见,选中级。
- 3、模式,我选的正常,循环也可以,看自己需求。正常的话,它只执行一次,然后就保持最后输出的那个占空比不变了;循环的话,顾名思义,不解释。
- 4、数据宽度,选字节,可以省空间。注意事项,外设那里不能选字节,工作异常。
四、中断代码生成
中断这里,DMA的中断相关的代码,生成一下,比较省心。

五、生成工程
指定路径,写好工程名称,点击生成即可。

六、添加部分代码
定义一个数组,初始化里面的值都是54,然后PWM输出:
/* USER CODE BEGIN 2 */
for(i = 0 ; i < 384; i++)
{
pwm_buf[i] = 54;
}
HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_2, (uint32_t *)pwm_buf, 384);
/* USER CODE END 2 */
很简单吧!
编译、烧录、上示波器,可以看到波形很好:

但是,如果用示波器的单次触发功能,看它输出的第一个脉冲:

你会发现第一个脉冲的周期或占空比完全不对,根本不是你配置的那样。对占空比要求不高的环境,这样没事。但是WS2812对每个周期都很敏感,这就不行了。
七、解决问题
针对这个问题,我上网搜了一下。很多人反映说是ARR初始值的问题,我尝试改为0,但是没什么效果。
后来想到一个地方,STM32CUBEMX里面配置PWM占空比的时候,写的是26。实际用的时候,又改成了54,可能和这里有关系。于是我在STM32CUBEMX里面把PWM的占空比改为0,重新生成代码试一下:

编译、烧录、上电,捕捉!

可以看到输出波形恢复正常!
根本原因我并不清楚,因为我只是使用者,而且HAL对芯片的配置包装的太深了,我懒得看。但是通过这个现象可以基本猜出来,和初始的PWM占空比有关。
为了避开这个问题,初始占空比设为0,就相当于刚开始是低电平。后面DMA的数据进来了,才开始出有效数据。
大致就是这么个思路,我是单片机爱好者-MCU起航,打完收工!