dql2016 发表于 2017-4-15 19:47

单片机软件设计1-使用SPI+DMA驱动WS2812B型LED

<div class='showpostmsg'> 本帖最后由 dql2016 于 2017-4-15 19:57 编辑

最近太忙了,实在事不好意思,今天抽空分享一下如何使用STM32CubeMX快速配置SensorTile的SPI接口,用来驱动WS2812B型全彩LED。参考了网友zoomdy的帖子http://www.stmcu.org/module/forum/thread-610279-1-1.html,在此对zoomdy无私的分享精神表示感谢。





















下面事它的时序图,十分特殊,相当于是用不同脉宽的PWM信号来表示它的一个位,虽然灯珠电气参数要求的电压比SenSorTile的3.3V要大一点点,但实测还是能完全驱动的,只需三根线(电源需用5V,地线,信号线)即可驱动,理论上可“无限”级联,实际上级联过多时,信号会产生畸变,且功率还是非常大的,每颗灯珠最多60mA电流,因此要选用合适的电源才行。
使用SensorTile的SPI来模拟它的时序,SPI配置为8位数据模式,假设其传输速率为b Mbit/s,即b/8 MB/s=b/8 Byte/us,传输一个字节需要8/b us,用1字节的SPI数据来表示WS2812B的一位数据。假设SPI发送的1字节数据中连续“1”的个数为n,按照时序要求,对于1码:那么n需要满足如下关系:n/8 * 8/b~(0.85us+/-150ns);对于0码:那么n需要满足如下关系:n/8 * 8/b~(0.4us+/-150ns);考虑到信号的上升沿等因素,SPI发送的1字节数据其最高位均取“0”。这里我配置SPI速率为1Mbit/s时,SPI发送的1字节数据0x70(0111 0000)表示WS2812B的0码,SPI数据0x7C(0111 1100)表示WS2812B的1码。一颗WS2812B需要24位数据(按照绿-红-蓝顺序,高位在前)。
STM32CubeMX对SPI的关键配置如下:






如下图Adafruit_NeoPixel.c文件,先编写一个发送8个位的函数,使用了DMA:

然后是发送24个位的:



我在底层驱动的基础上移植了Arduino平台的Adafruit_NeoPixel库函数,提供丰富的应用函数,如彩虹、呼吸等效果。
这是Adafruit_NeoPixel.h文件内容:

使用时只需配置好SPI后把下面2个文件添加进工程即可:

在main函数调用初始化后,即可愉快的玩耍了:


效果图见此帖:https://bbs.eeworld.com.cn/thread-525615-1-1.html

附上工程文件:




</div><script>                                        var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;"   style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
                                       
                                        if(parseInt(discuz_uid)==0){
                                                                                                (function($){
                                                        var postHeight = getTextHeight(400);
                                                        $(".showpostmsg").html($(".showpostmsg").html());
                                                        $(".showpostmsg").after(loginstr);
                                                        $(".showpostmsg").css({height:postHeight,overflow:"hidden"});
                                                })(jQuery);
                                        }                </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script>

rainbow0wjy 发表于 2017-6-7 14:14

谢谢分享!!!

bbg321 发表于 2018-1-8 20:38

好想法,学习一下

yyq006 发表于 2018-1-29 16:47


谢谢分享!!!正想找这个。

704308532 发表于 2018-6-24 14:18

谢谢楼主分享

xd785 发表于 2018-8-14 17:37

谢谢楼主分享

unsem1 发表于 2018-8-15 23:05

借鉴下

qq236106303 发表于 2018-9-17 21:03

谢谢啦, 移植看下效果

PCB制作 发表于 2018-9-18 14:51

谢谢分享

F思念 发表于 2018-10-29 10:15

来学习下这个

sbn2000 发表于 2018-11-20 11:09

学习学习,谢谢分享

小灰兔 发表于 2018-11-20 21:28

SPI传输每传输8位中间有间歇哈?这个弄?

scccdl 发表于 2018-12-19 11:47

感谢分享

风狼 发表于 2019-1-9 14:55

哇感谢大佬!一只萌新路过

风狼 发表于 2019-1-10 16:55

昨天看了大佬的教程,今天把东西弄出来了,过来补充一些。首先一开始不太理解大佬说的0码跟1码,后来抓了波形才懂,其实1码最左边的0,作为是上一个数据的补充,0码同理。大佬这一招很妙!另外大佬spi的速率应该是不对的吧?1Mbit/s的话8位数据的时间就是8us远超一个ws2812数据码的周期。所以一开始移植的时候(一开始为了节约时间没有仔细研究大佬说的话)死活调不通,后来25us发现spi速率配错了。emmm我自己配的速率8位数据1.25us直接跟他对应起来,每一位就是的时间1.25/8,速率就是8/1.25=6.4M。为了这6.4M我还专门去调了stm32f4的总线时钟频率,结果当然是降频了,感觉有点得不偿失。总之还是为了提醒大家学习要多专研啊!也给大家提提醒,顺便谢谢大佬的指教了!

littleshrimp 发表于 2019-1-10 19:37

风狼 发表于 2019-1-10 16:55
昨天看了大佬的教程,今天把东西弄出来了,过来补充一些。首先一开始不太理解大佬说的0码跟1码,后来抓了波 ...

楼主的这种方式有些浪费内存资源
使用SPI最节省内存的方法是用3bit来实现ws2812的一位
SPI时钟在2.5Mbps左右时可以用100=0,110=1
这样ws2812b的24位数据可以使用9个字节实现
具体可以看一下我的贴子

风狼 发表于 2019-1-11 16:26

littleshrimp 发表于 2019-1-10 19:37
楼主的这种方式有些浪费内存资源
使用SPI最节省内存的方法是用3bit来实现ws2812的一位
SPI时钟在2.5Mbp ...

嗯嗯确实,谢谢指教。

阿仪 发表于 2019-1-15 15:41

好东西啊,经典

阿仪 发表于 2019-1-15 15:45

这个历程比较巧妙的用多位SPI的数据来达到高电平长短的标识,来实现WS2812的时序要求,最有意思的是能够移植arduino的库,我用过那个库确实很炫,厉害厉害,哈哈哈哈:):):)

zhuyebb 发表于 2019-1-15 17:42

页: [1] 2
查看完整版本: 单片机软件设计1-使用SPI+DMA驱动WS2812B型LED