ESP8266开发学习笔记_ws2812三原色灯
ESP8266 开发学习笔记_By_GYC 【spi方式驱动 ws2812 三原色灯(稳定优化)】
本章介绍ESP8266 IDF 框架下 如何使用 骚操作 的使用SPI总线,发送更高精度的脉冲信号,ws2812作为控制芯片三色灯的使用方法,实现三原色显示灯带。在研究过程中,发现ESP8266的引脚响应速度有些慢,输出2.5us才能够翻转一次,而ws2812的控制电平分辨率要求在百纳秒级,所以需要其他方法来输出控制信号才能保证灯光稳定。本次选用SPI信号输出口,使灯光达到了稳定。
一、驱动ws2812遇到的问题
在淘宝上偶然看见有只需要一个引脚就能高速的控制三原色全彩LED灯,这让我很感兴趣,就买下来回来尝试,结果到手当天就遇到了很严重的问题,根据手册的说明写了一下简单的驱动程序,灯亮是能亮但是只有一个颜色,没有办法像网上说的那样能够自由的调节颜色,搞得我很是崩溃,还以为自己的编程水平出了问题,明明代码逻辑已经没有什么问题了,却还是不能正常显示,我就喊朋友来帮忙驱动一下,他用stm32的开发板,十几分钟就从网上扣下源码给我驱动了,代码逻辑和我的相差无几。
那么确定不是代码的问题了,就要找找其他的问题,比如我正在研究的单片机ESP8266。放到示波器上显示GPIO引脚的输出电平可以发现,引脚的实际输出速度并不像程序设计的那样,实际操作时ESP8266的管脚每2.5us(0.4MHz)才能够进行一次有效的翻转,而ws2812的控制电平要求精度在百ns级别,普通的GPIO管脚并不能达到这样的速度,而stm32的引脚翻转速度远大于ESP8266的,其I/O口驱动电路的响应速度有2M、10M、50M可选,轻松就能达到百纳秒的精度。所以stm32能够轻松的驱动ws2812而ESP8266只能通过骚操作来实现。
二、可能的方案
1、特殊GPIO
一般如stm32主频比较高的单片机,可以直接通过驱动GPIO引脚,控制引脚的翻转,实现对ws2812的控制。虽然ESP8266的GPIO翻转速度无法达到期望的速度,但是根据网上其他人的分享,发现ESP8266的GPIO0的翻转速度和响应速度都比片上其他的GPIO快,可以作为ws2812的驱动引脚。经过测试,发现配合寄存器操作的GPIO0确实能够驱动ws2812、并且能够显示色彩进行调节。不过这种方法稳定性较低,不知道是我使用的芯片问题还是普遍存在,用GPIO0驱动的ws2812灯圈(8个)不稳定,偶尔就会一个灯珠颜色错误。这让我很是难受。
2、使用pwm驱动
PWM,周期设置为3MHz,发送0就把占空比设置为33%,发送1就把占空比设置为66%。也是一种很有创意的驱动方式。可惜的是ESP8266的PWM功能是通过定时器用GPIO翻转模拟的,它的PWM 周期范围是:1000us (1KHz) ~ 10000us (100Hz),达不到要求。
3、使用SPI方案(本次使用)
可以注意到,将SPI的时钟调整为8MHz,发送一字节是1us,一个比特是0.125us,给ws2812发送逻辑0即可以通过SPI总线发送11000000b来实现(0.25us高电平,0.75us低电平),发送逻辑1即可以通过SPI总线发送11111100b来实现(0.75us高电平,0.25低电平)。通过这种方式驱动的灯光稳定可靠。能够保证灯光不会出现闪烁或者某个灯珠颜色跳变的情况。本次要介绍的ws2812驱动就是使用这种控制方式来实现的。
TODO: 低电平时间0.25us是硬件规定的低电平最小时间,如果能增大一点会更稳定,可以把控制引脚高低的数据改成数据流,比如
TL:1110000000b (0.375us[0.4-0.025] 高电平,0.875us[0.85+0.025] 低电平) 9位 共1.125us
TH:1111110000b (0.75us [0.8-0.05] 高电平,0.5us [0.45+0.05] 低电平)9位 共1.125us
高低电平的数据组合组成一长串的 spi 数据,可以使控制更加稳定。
三、具体实现
首先根据ESP8266 的资源信息确认需要用到的引脚。
根据上图所示,ESP8266在nodemcu上的SPI引脚是D5-D8、我们可以通过初始化控制禁用CS和MISO使能,只使用MOSI作为WS2812的输出引脚。设置SPI的时钟频率(SPI clock frequency)为8MHz,使一个字节周期为1.25us。
spi引脚初始化函数如下。
1 | void ws2812_spi_mode_init(void) //must use the ESP8266 GPIO13 as the hspi pin to drive WS2812B RGB LED!!! |
需要注意的是,这里虽然没有用到,但是你需要设置spi的事件回调函数,即使他是空的
1 | static void IRAM_ATTR spi_event_callback(int event, void *arg) |
准备工作做好之后,我们就要编写数据发送函数了。网上其他的例程里面常常把数据发送函数分为位发送、字节发送、像素点发送三层,层层调用,这种逻辑非常的便于阅读。但是在引脚响应速度并不那么快的单片机上,这种结构并不能保证时序的稳定性,因此,此处我直接略去了前两个过程,直接提供了一个像素数据发送的函数。避免函数切换、SPI重新启动引起的时序不稳定问题。
这个函数在结构上还有待优化,待我闲下来的时候再重构一下,先提供一个能够使用的版本,也希望有高手能够分享这个程序的简化版本。
1 | void WS2812BSend_24bit(uint8_t R, uint8_t G, uint8_t B) |
比较麻烦的是,我这里每次传输了192(24*8)bit,由于这是32位的单片机,他是以32bit为单位进行传输的,而且每次都是从低位开始传输。由于ESP8266是小端字节序(与我们的阅读习惯不一致),所以在设置传输的时候需要将数据反一下,保证数据输出的顺序是我们想要的顺序。
以unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况
|内存地址 |小端模式存放内容 |大端模式存放内容|
|:——-: |:——-: |:——-:|
|0x4000 |0x78| |0x12|
|0x4001 |0x56| |0x34|
|0x4002 |0x34| |0x56|
|0x4003 |0x12| |0x78|
有了如上函数,我们就可以轻松的点亮ws2812三原色灯珠了。
ws2812具体的协议可以参考技术规格书(https://wenku.baidu.com/view/25f176db482fb4daa48d4ba1.html?rec_flag=default&sxts=1561280682919),
使用到的主要内容如下:
需要注意的是 要保证电源稳定,因为电源问题我遇到了 意外的灯光闪烁、多个灯一起点亮时产生颜色偏差 的问题,更换了供电线和使用5V给模块供电之后,颜色显示完全稳定和正常了。保证硬件良好是软件调试好软件的关键。
四、测试程序
1 |
|
效果
五、还没结束
目前这个项目还未完全完成,还存在一些优化空间,希望大家能够多多和我交流,写出更好的程序。O(∩_∩)O哈哈~
在此特别感谢“半颗心脏”大佬对我项目的关注,互相学习啦。
我的源文件和头文件已经上传至我的github上(https://github.com/gengyuchao),欢迎大家关注我的博客和github呀。
- Post Title: ESP8266开发学习笔记_ws2812三原色灯
- Post Author: Geng Yuchao
- Post Link: https://github.com/gengyuchao/gengyuchao.github.io/2020/08/09/ESP8266学习笔记/ESP8266开发学习笔记_ws2812三原色灯/
- Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.