社区导航

 
查看: 983|回复: 15

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

[复制链接]

1151

TA的帖子

2

TA的资源

版主

Rank: 6Rank: 6

发表于 2018-5-7 00:06:10 | 显示全部楼层 |阅读模式


接上节我们介绍了一种STM32控制WS2812最常用,
也是最用一想到的方法,就是用PWM来模式控制信号,
并通过DMA传输数组数据,来保证信号传输的连续性。
这篇我们来介绍一个不常用的方法——用SPI单总线控制

估计好多不熟悉STM32的小伙伴都没通过这种通信方式吧,
其实我也不熟,只是知道这种模式从未用过,今天也算学习了。
下面是STM32F1的Reference Manual里给的一段介绍,了解一下。

无标题.png

其实说白了,SPI不是有一个MOSI引脚吗,
我们就是用MO功能,通过发送一个字节或半字的数据,
来模拟我们的LED控制信号0或1,看下图。
标题.png

是不是有恍然大悟的感觉,本来就很简单嘛。
可仔细看来这是不是有点得不偿失啊,
用16位数据去模拟1位数据,是不是感觉太浪费资源了!!!
还在我们的SPI通信速率足够高,完全可以应付的来。





评分

1

查看全部评分



回复

使用道具 举报

1151

TA的帖子

2

TA的资源

版主

Rank: 6Rank: 6

 楼主| 发表于 2018-5-7 00:06:25 | 显示全部楼层
本帖最后由 通宵敲代码 于 2018-5-7 00:28 编辑


原理介绍完了,我们就直接来看程序了

时钟就不用说了,还是能跑多快就跑多块咯,
重点来看看我们SPI的单总线模式是如何配置的,
当然第一个要配置的自然是我们的信号输出引脚,
SIM32F1系列SPI1的MOSI引脚是PA7,
TIM截图20180507001120.png

注意要配置成复用输出模式,最大驱动频率50MHz,
接下来就是我们的主角SPI1的配置了。

TIM截图20180507000646.png

前两句就可以看到,我们将SPI配置成了主模式下的单线发送模式,
采用了16微数据帧格式,时钟引脚悬空未用,片选引脚未用。
然后就是我们的DMA数据传输的配置,
通过我们的Reference Manual可以查到SPI的TX使用的DMA1_CH3通道。
TIM截图20180507001316.png

接下来就是我们有关LED通信数据的处理了,这才是最有意思的部分。
首先是RGB数据的分解,在C语言里并没有定义24位主句格式,
只有32位可用,所以我们将RGB数组定义32位格式,如下是我定义的一个数组。
TIM截图20180507001715.png

然后我们要将每个数组元素中的RGB三色分解出来,函数如下
注意一下数据的先后顺序,我们定义的是RGB,而发送需要的是GRB。
TIM截图20180507001849.png

然后我们要将分解出来的RGB信号的每一位进行解析,
转化成我们即将使用SPI发送出去的16位帧信号,
注意,此处帧的格式是根据LED所需的信号0和1定义好的,
TIM截图20180507002010.png

然后我们要将转化出来的16位帧数据一次存放打我们给SPI准备的数组中,
也就是我们通过DMA功能给SPI的TX引脚传送数据的数组,注意对应哦。

TIM截图20180507002017.png
看看是不是跟DMA初始化的时候配置的一样呢。
TIM截图20180507002120.png

接下来就是如何把已经转化好的16位帧数据发送出去了。
具体函数如下,记得要使用DMA功能传送数据哦。
TIM截图20180507002628.png

点评

謝謝分享。  详情 回复 发表于 2018-5-7 07:52


回复

使用道具 举报

1151

TA的帖子

2

TA的资源

版主

Rank: 6Rank: 6

 楼主| 发表于 2018-5-7 00:06:37 | 显示全部楼层
本帖最后由 通宵敲代码 于 2018-5-7 00:33 编辑


我们在主函数中,通过数据发送函数,将RGB信号传送到LED,
同时,我们简单通过一个循环移位函数,实现了LED的三色流水灯。

TIM截图20180507002848.png

TIM截图20180507002832.png



附上程序
STM32F1_DMA_SPI_16B_WS2813E_2018_05_06-Bingo.zip (6.94 MB, 下载次数: 96)


回复

使用道具 举报

363

TA的帖子

0

TA的资源

纯净的硅(初级)

Rank: 4

发表于 2018-5-7 07:52:32 | 显示全部楼层
通宵敲代码 发表于 2018-5-7 00:06
原理介绍完了,我们就直接来看程序了

时钟就不用说了,还是能跑多快就跑多块咯,
重点来看看我们SPI ...

謝謝分享。


回复

使用道具 举报

280

TA的帖子

0

TA的资源

一粒金砂(高级)

Rank: 3Rank: 3

发表于 2018-5-7 08:53:27 | 显示全部楼层
能共享一下这个程序的代码吗  谢谢啊!

点评

附件里有,可以下载  详情 回复 发表于 2018-5-7 08:55


回复

使用道具 举报

1151

TA的帖子

2

TA的资源

版主

Rank: 6Rank: 6

 楼主| 发表于 2018-5-7 08:55:32 | 显示全部楼层
bioger 发表于 2018-5-7 08:53
能共享一下这个程序的代码吗  谢谢啊!

附件里有,可以下载

点评

谢谢 看到了  详情 回复 发表于 2018-5-7 08:56


回复

使用道具 举报

280

TA的帖子

0

TA的资源

一粒金砂(高级)

Rank: 3Rank: 3

发表于 2018-5-7 08:56:35 | 显示全部楼层
通宵敲代码 发表于 2018-5-7 08:55
附件里有,可以下载

谢谢 看到了


回复

使用道具 举报

251

TA的帖子

0

TA的资源

一粒金砂(中级)

Rank: 2

发表于 2018-5-7 09:00:30 | 显示全部楼层
详细,很好的教程!!!


回复

使用道具 举报

280

TA的帖子

0

TA的资源

一粒金砂(高级)

Rank: 3Rank: 3

发表于 2018-5-8 10:30:59 | 显示全部楼层
for (i = 0; i < 20; i++)   
        {
            LED_SPI_WriteByte(0x00);
        }

请问这句话是什么用意呢?

点评

用来填充数组的空闲数据, 防止发出去一堆乱数据。  详情 回复 发表于 2018-5-9 09:56


回复

使用道具 举报

1151

TA的帖子

2

TA的资源

版主

Rank: 6Rank: 6

 楼主| 发表于 2018-5-9 09:56:07 | 显示全部楼层
bioger 发表于 2018-5-8 10:30
for (i = 0; i < 20; i++)   
        {
            LED_SPI_WriteByte(0x00);
        }

请问这句 ...



用来填充数组的空闲数据,
防止发出去一堆乱数据。



点评

那从数量上计算也是884,不是数组定义时候的1024了,填充20是什么道理呢?  详情 回复 发表于 2018-5-9 10:17


回复

使用道具 举报

280

TA的帖子

0

TA的资源

一粒金砂(高级)

Rank: 3Rank: 3

发表于 2018-5-9 10:17:07 | 显示全部楼层
通宵敲代码 发表于 2018-5-9 09:56
用来填充数组的空闲数据,
防止发出去一堆乱数据。

那从数量上计算也是884,不是数组定义时候的1024了,填充20是什么道理呢?

点评

不必纠结细节,程序能用就行 (之前调程序加的冗余代码,完全可以删掉)  详情 回复 发表于 2018-5-9 10:26


回复

使用道具 举报

1151

TA的帖子

2

TA的资源

版主

Rank: 6Rank: 6

 楼主| 发表于 2018-5-9 10:26:05 | 显示全部楼层
bioger 发表于 2018-5-9 10:17
那从数量上计算也是884,不是数组定义时候的1024了,填充20是什么道理呢?

不必纠结细节,程序能用就行
(之前调程序加的冗余代码,完全可以删掉)

点评

好吧  详情 回复 发表于 2018-5-9 13:09


回复

使用道具 举报

280

TA的帖子

0

TA的资源

一粒金砂(高级)

Rank: 3Rank: 3

发表于 2018-5-9 13:09:25 | 显示全部楼层
通宵敲代码 发表于 2018-5-9 10:26
不必纠结细节,程序能用就行
(之前调程序加的冗余代码,完全可以删掉)
:L ...

好吧


回复

使用道具 举报

3551

TA的帖子

6

TA的资源

裸片初长成(初级)

Rank: 10Rank: 10Rank: 10

荣誉会员勋章

发表于 2018-5-10 10:48:45 | 显示全部楼层
本帖最后由 jishuaihu 于 2018-5-10 11:00 编辑

我也这么用过,也用过DMA+PWM的方式驱动。缺点你上面也说了。用8个字节的数据去模拟一个字节,太浪费空间了。
我打算改成另外一种方式去做,直接用PWM控制,在每个周期内根据需要发送的数据的bit去调节PWM的占空。这种每个周期调节占空比的方法在其他应用上也挺常用的,比如数字电源。这样只需要处理好两个索引,即LED的编号和每个字节中的bit位。比起上述方法应该能省不少内存。
不过最近没时间弄,也不着急用,就一直搁下了。
有兴趣的可以试一下


刚才看到楼主在以前的帖子里介绍过我上面说的方法了。楼主提到要不断地改变定时器CCR的数值,觉得这个很麻烦,可能影响信号的稳定性。
这一点楼主可以放心,稳定性没有问题,数字电源就是这么用的,我以前的项目中用PWM控制采样频率,也是根据捕获的频率实时改变采样频率的。在定时器的溢出中断里去改变下一个周期的占空比,消耗的CPU资源也不是很多,当然可能比DMA方式多一些,但是省了不少内存,可以去控制更多是LED。

点评

嗯,用外设实现信号输出, 相比直接用I/O口能节省很多资源, 再加上DMA直接做好数据映射, 基本可以解放MCU了。  详情 回复 发表于 2018-5-11 09:03


回复

使用道具 举报

1151

TA的帖子

2

TA的资源

版主

Rank: 6Rank: 6

 楼主| 发表于 2018-5-11 09:03:50 | 显示全部楼层
jishuaihu 发表于 2018-5-10 10:48
我也这么用过,也用过DMA+PWM的方式驱动。缺点你上面也说了。用8个字节的数据去模拟一个字节,太浪费空间了 ...

嗯,用外设实现信号输出,
相比直接用I/O口能节省很多资源,
再加上DMA直接做好数据映射,
基本可以解放MCU了。


回复

使用道具 举报

1

TA的帖子

0

TA的资源

一粒金砂(初级)

Rank: 1

发表于 2018-5-11 21:07:53 | 显示全部楼层
感谢分享


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

  • 论坛活动 E手掌握

    扫码关注
    EEWORLD 官方微信

  • EE福利  唾手可得

    扫码关注
    EE福利 唾手可得

小黑屋|手机版|Archiver|电子工程世界 ( 京ICP证 060456

GMT+8, 2018-8-15 12:51 , Processed in 0.478378 second(s), 18 queries , Gzip On, Redis On.

快速回复 返回顶部 返回列表