WS2812灯珠的STM32驱动方式(三)——DMA+SPI
<div class='showpostmsg'>接上节我们介绍了一种STM32控制WS2812最常用,
也是最用一想到的方法,就是用PWM来模式控制信号,
并通过DMA传输数组数据,来保证信号传输的连续性。
这篇我们来介绍一个不常用的方法——用SPI单总线控制
估计好多不熟悉STM32的小伙伴都没通过这种通信方式吧,
其实我也不熟,只是知道这种模式从未用过,今天也算学习了。
下面是STM32F1的Reference Manual里给的一段介绍,了解一下。
其实说白了,SPI不是有一个MOSI引脚吗,
我们就是用MO功能,通过发送一个字节或半字的数据,
来模拟我们的LED控制信号0或1,看下图。
是不是有恍然大悟的感觉,本来就很简单嘛。
可仔细看来这是不是有点得不偿失啊,
用16位数据去模拟1位数据,是不是感觉太浪费资源了!!!
还在我们的SPI通信速率足够高,完全可以应付的来。
</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> 本帖最后由 通宵敲代码 于 2018-5-7 00:28 编辑
原理介绍完了,我们就直接来看程序了
时钟就不用说了,还是能跑多快就跑多块咯,
重点来看看我们SPI的单总线模式是如何配置的,
当然第一个要配置的自然是我们的信号输出引脚,
SIM32F1系列SPI1的MOSI引脚是PA7,
注意要配置成复用输出模式,最大驱动频率50MHz,
接下来就是我们的主角SPI1的配置了。
前两句就可以看到,我们将SPI配置成了主模式下的单线发送模式,
采用了16微数据帧格式,时钟引脚悬空未用,片选引脚未用。
然后就是我们的DMA数据传输的配置,
通过我们的Reference Manual可以查到SPI的TX使用的DMA1_CH3通道。
接下来就是我们有关LED通信数据的处理了,这才是最有意思的部分。
首先是RGB数据的分解,在C语言里并没有定义24位主句格式,
只有32位可用,所以我们将RGB数组定义32位格式,如下是我定义的一个数组。
然后我们要将每个数组元素中的RGB三色分解出来,函数如下
注意一下数据的先后顺序,我们定义的是RGB,而发送需要的是GRB。
然后我们要将分解出来的RGB信号的每一位进行解析,
转化成我们即将使用SPI发送出去的16位帧信号,
注意,此处帧的格式是根据LED所需的信号0和1定义好的,
然后我们要将转化出来的16位帧数据一次存放打我们给SPI准备的数组中,
也就是我们通过DMA功能给SPI的TX引脚传送数据的数组,注意对应哦。
看看是不是跟DMA初始化的时候配置的一样呢。
接下来就是如何把已经转化好的16位帧数据发送出去了。
具体函数如下,记得要使用DMA功能传送数据哦。
本帖最后由 通宵敲代码 于 2018-5-7 00:33 编辑
我们在主函数中,通过数据发送函数,将RGB信号传送到LED,
同时,我们简单通过一个循环移位函数,实现了LED的三色流水灯。
http://v.youku.com/v_show/id_XMzU4ODcyODg0OA==.html?spm=a2h3j.8428770.3416059.1
附上程序
通宵敲代码 发表于 2018-5-7 00:06
原理介绍完了,我们就直接来看程序了
时钟就不用说了,还是能跑多快就跑多块咯,
重点来看看我们SPI ...
{:1_103:}謝謝分享。 能共享一下这个程序的代码吗谢谢啊! bioger 发表于 2018-5-7 08:53
能共享一下这个程序的代码吗谢谢啊!
附件里有,可以下载:titter: 通宵敲代码 发表于 2018-5-7 08:55
附件里有,可以下载
谢谢 看到了 详细,很好的教程!!! for (i = 0; i < 20; i++)
{
LED_SPI_WriteByte(0x00);
}
请问这句话是什么用意呢? bioger 发表于 2018-5-8 10:30
for (i = 0; i < 20; i++)
{
LED_SPI_WriteByte(0x00);
}
请问这句 ...
用来填充数组的空闲数据,
防止发出去一堆乱数据。
:Laugh::Laugh:
通宵敲代码 发表于 2018-5-9 09:56
用来填充数组的空闲数据,
防止发出去一堆乱数据。
那从数量上计算也是884,不是数组定义时候的1024了,填充20是什么道理呢? bioger 发表于 2018-5-9 10:17
那从数量上计算也是884,不是数组定义时候的1024了,填充20是什么道理呢?
不必纠结细节,程序能用就行
(之前调程序加的冗余代码,完全可以删掉)
:Laugh::Laugh::Laugh::Laugh::Laugh: 通宵敲代码 发表于 2018-5-9 10:26
不必纠结细节,程序能用就行
(之前调程序加的冗余代码,完全可以删掉)
:L ...
好吧 本帖最后由 jishuaihu 于 2018-5-10 11:00 编辑
我也这么用过,也用过DMA+PWM的方式驱动。缺点你上面也说了。用8个字节的数据去模拟一个字节,太浪费空间了。
我打算改成另外一种方式去做,直接用PWM控制,在每个周期内根据需要发送的数据的bit去调节PWM的占空。这种每个周期调节占空比的方法在其他应用上也挺常用的,比如数字电源。这样只需要处理好两个索引,即LED的编号和每个字节中的bit位。比起上述方法应该能省不少内存。
不过最近没时间弄,也不着急用,就一直搁下了。
有兴趣的可以试一下
刚才看到楼主在以前的帖子里介绍过我上面说的方法了。楼主提到要不断地改变定时器CCR的数值,觉得这个很麻烦,可能影响信号的稳定性。
这一点楼主可以放心,稳定性没有问题,数字电源就是这么用的,我以前的项目中用PWM控制采样频率,也是根据捕获的频率实时改变采样频率的。在定时器的溢出中断里去改变下一个周期的占空比,消耗的CPU资源也不是很多,当然可能比DMA方式多一些,但是省了不少内存,可以去控制更多是LED。
jishuaihu 发表于 2018-5-10 10:48
我也这么用过,也用过DMA+PWM的方式驱动。缺点你上面也说了。用8个字节的数据去模拟一个字节,太浪费空间了 ...
嗯,用外设实现信号输出,
相比直接用I/O口能节省很多资源,
再加上DMA直接做好数据映射,
基本可以解放MCU了。
:Laugh: 感谢分享{:1_103:} 好,尝试尝试 {:1_103:}谢谢分享!!! 版主,附件下载不了了