[原创] WS2812灯珠的STM32驱动方式(二)——DMA+PWM

通宵敲代码   2018-5-6 22:48 楼主
上节我们重点介绍了一下WS2812B, 这种内部集成驱动芯片的RGB灯珠, 并通过对驱动信号通信速率的详细计算, 重点说明了STM32等一众单片机通过I/O翻转方式, 控制这种灯珠的困难之处,这篇我们就介绍一下, 如何用最常见的方式来实现STM32对WS2812的控制。 常用STM32的都知道,STM32有8个定时器, 其中TIM1跟TIM8是高级定时器,TIM2345是通用定时器, TIM67是基本定时器,当然还有个滴答定时器, 其中TIM123458都可以独立控制输出PWM波形, TIM1跟TIM8还可以输出两组互补PWM波形, 这个功能在电机驱动中十分常用。 TIM截图20180506224832.png STM32定时器的PWM输出功能, 是通过CNT与CCR寄存器的数值比, 直接控制对应引脚输出高低电平的, 也就是说只要我们愿意,可以用定时器直接输出36MHz的方波, 当然波形可能就不那么好看了, 不过用来驱动我们800kbps的WS2812,可以说是绰绰有余了, TIM截图20180506225124.png 但是还有一个问题,通常情况下, 我们的WS2812不会仅仅是一个或者几个LED串联, 如果串联的LED较多的话,又会面临驱动信号的稳定性问题, 毕竟我们要不断地改变定时器CCR的数值, 来控制对应的I/O发送信号0还是信号1, 而项目中我们的单片机又不可能只是用来控制LED, 这时候我们的DMA功能就派上用场了。 这也就是我们今天要介绍的最容易想到的一种方法DMA+PWM, TIM截图20180506225220.png 本帖最后由 通宵敲代码 于 2018-5-6 23:34 编辑

回复评论 (75)

上面介绍完原理,下面就是如何实现功能了, 我们直接来看程序,声明一下我们是用原子的例程移植的。 这里我们依然使用是STM32的HAL库, 没办法,用熟了,顺手,而且各种以前的资源不能浪费了不是。 首先是时钟配置,这个没什么好说的,能跑多快就多快吧。 就跟没人会希望自己媳妇丑一样。 TIM截图20180506225817.png (哦,不好意思这段是之前用寄存器写的,拿过来用了,见谅) 下面是今天的主角,DMA+PWM的配置方式。 TIM截图20180506230104.png 先配置下GPIO吧,我们使用的是TIM2的CH1通道, 对用的PWM输出引脚是PA0,注意要用复用输出功能哦。 下面是TIM2+CH1通道的配置,注意要配置成800KHz的频率, 内核时钟72M / 90 = 800KHz,应该不难理解吧。 然后是CH1输出通道的,配置成PWM1输出模式即可, 注意电平的极性,不放心的可以用示波器抓抓波形。 TIM截图20180506230113.png 然后就是我们的DMA得配置了,要设置好数组地址跟寄存器地址 然后就是DMA传输的字节长度,传输模式等等了 TIM截图20180506230138.png 有关寄存器地址大家应该会找吧,不会的看下边, 先从Datasheet找到我们的存储器映像图表,找到TIM寄存器租的起始地址, TIM截图20180506231215.png 可以看到TIM2寄存器组的起始地址是0x40000000, 然后找到Reference Manual里,我们需要使用的TIM2_CCR1寄存器, TIM截图20180506231626.png 可以看到CCR1的偏移地址是0x34,所以我们需要的寄存器地址就是, 0x40000000 + 0x34 = 0x40000034,不懂得童鞋回去好好啃啃计算机原理吧, 需要的外设资源配置好了,接下来就是万事俱备只欠东风了, 下面就是我们有关LLED控制信号的处理函数了, look,不多解释。 TIM截图20180506232012.png 上图最后面那个bug,是之前原子的程序就带着的, 家里没有示波器目前没做验证,有空会控死抓抓波形看看。 程序能用,也就没深究下去。 注意一下,到这可没玩呢,说好了要用DMA方式传输数据的, 下面才是数据传输的主要内容呢。 TIM截图20180506232023.png 本帖最后由 通宵敲代码 于 2018-5-6 23:28 编辑
  • TIM截图20180506230138.png
点赞 (1) 2018-5-6 22:49
定义了三个数组,主函数里写个三色循环呼吸灯看看效果。 TIM截图20180506232627.png TIM截图20180506232619.png 附上相关程序 这个是用TIM2_CH1实现三色呼吸灯的。
STM32F1_DMA_PWM_WS2813E_2018_05_06-Bingo.zip (3.28 MB)
(下载次数: 3446, 2018-5-6 23:32 上传)
这个是用TIM2_CH123实现三路LED控制的。
STM32F1_DMA_PWM_WS2813E_2018_05_06-TIM2_CH123.zip (3.29 MB)
(下载次数: 2235, 2018-5-6 23:32 上传)
这个是用TIM2345_CH1实现四路LED控制的。
STM32F1_DMA_PWM_WS2813E_2018_05_06-TIM2345.zip (3.29 MB)
(下载次数: 2140, 2018-5-6 23:32 上传)
本帖最后由 通宵敲代码 于 2018-5-6 23:34 编辑
点赞  2018-5-6 22:49
引用: 通宵敲代码 发表于 2018-5-6 22:49
上面介绍完原理,下面就是如何实现功能了,
我们直接来看程序,声明一下我们是用原子的例程移植的。
这 ...

謝謝分享。
点赞  2018-5-7 07:51
引用: 通宵敲代码 发表于 2018-5-6 22:49
定义了三个数组,主函数里写个三色循环呼吸灯看看效果。






附上相关程序
这个是用TIM2_CH1实 ...

谢谢分享
点赞  2018-5-7 08:31


在开启DMA数据传输,定时器开启之前,
增加一条计数寄存器清空指令,
防止寄存器非零造成第一次脉冲信号不准确。

同时在DMA数据传输完成后,应先关闭DMA通道,再关闭定时器,
防止DMA传输完成,但PWM最后一次信号未发送完影响最后一个灯的显示。


TIM截图20180507111733.png

TIM截图20180507111130.png



点赞  2018-5-7 11:28
楼主,为什么我的PA0没有输出呢?用万用表也测不出来,我按照你的代码,原封不动,可就是没有输出,还请大佬指导一波
点赞  2018-5-19 21:33
还有,LED_Init();这个来初始化LED貌似没什么用吧,程序里好像没用上吧,输出是PA0吧?
点赞  2018-5-19 21:37
我仿真出来PA0一直是低电平
点赞  2018-5-20 12:33
引用: eleven@ 发表于 2018-5-19 21:37
还有,LED_Init();这个来初始化LED貌似没什么用吧,程序里好像没用上吧,输出是PA0吧?

嗯,这个确实没用,以前的函数留下的。
输出引脚确实是PA0,程序都是验证过的,你好好检查一下硬件。
点赞  2018-5-25 17:12
楼主你的硬件不是级联?程序上看起来是每一个都用一个DMA的通道?
点赞  2018-7-20 10:01
优酷视频打不开啊,能直接发个图片我看下或者其他视频,看下显示效果,不知道我这边显示的效果对不对。
点赞  2018-7-29 16:08
引用: DSCX05 发表于 2018-7-20 10:01
楼主你的硬件不是级联?程序上看起来是每一个都用一个DMA的通道?

做的四路独立控制,
需要级联你直接延长灯带就行了,
我试过288颗灯级联的,毫无压力,
官方给的数据可以级联1000颗灯
点赞  2018-7-30 08:58
引用: mochou678 发表于 2018-7-29 16:08
优酷视频打不开啊,能直接发个图片我看下或者其他视频,看下显示效果,不知道我这边显示的效果对不对。

百度一搜很多这个灯珠的视频,
我这边公司的网络也上不了优酷,
我记不清这个是呼吸灯还是流水灯了,
你看一下,只要颜色渐变流畅就没问题。
点赞  2018-7-30 09:01
点赞  2018-7-30 09:09
怎么输出来的脉冲没有24个?
点赞  2018-8-24 16:10
为什么使用TIM1_CH1定时器就不行呢,灯可以点亮,颜色不受控
点赞  2018-10-30 15:57
引用: lvfangfang000 发表于 2018-10-30 15:57
为什么使用TIM1_CH1定时器就不行呢,灯可以点亮,颜色不受控

你解决问题了吗?我也像你一样,现在不知道怎么搞
点赞  2018-11-1 09:43
有一点我不明白的是,为什么提到用hal库,但是大体上看起来还是标准库的代码风格?新手,求指教
点赞  2018-12-25 17:25
引用: soqy 发表于 2018-8-24 16:10
怎么输出来的脉冲没有24个?

我也是这样,只有8个,请问你解决了吗,还是说要使用三路LED的代码
点赞  2018-12-25 18:08
1234下一页
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复