STC15F104W驱动WS2812
提示:学习目标:了解WS2812的驱动原理,写出驱动代码。
最近开了个专栏,专门用来学习ESP32S3的,当然,代码也适用于ESP32板子,改下引脚号就可以的。感兴趣的可以看看。
https://blog.csdn.net/weixin_38476200/article/details/129828874
内容:
提示:这里可以添加要学的内容
例如:
1、 连接WS2812时序;
2、 基于STC15F104W写出驱动程序。
3、 软件调试计算程序的机器周期。
4、 测试程序,并根据示波器得到的实际机器周期时间重新调整驱动程序
正文:
1,WS2812时序
WS2812是一个集控制电路与发光电路于一体的智能外控LED光源,每个ws2812均含有4个引脚,引脚功能如下图:
WS2812可采用级联的方式,将上一个WS2812的DOUT引脚连接到下一个WS2812的DIN引脚,即可实现一个引脚控制多个WS2812。一般自己设计电路时还要在电源输入处添加一个0.1uF的小电容进行滤波。注意,虽然理论上可以连接足够多的ws2812,但是使用时要注意驱动电压和驱动电流是否足够驱动这些彩灯。
由于只有一个引脚控制ws2812,所以我们只能通过控制引脚输出高、低电平的时间,来让WS2812知道我们想让他显示哪一个灯,显示什么颜色。
想要让1个ws2812显示我们想要的颜色,我们需要给它发送颜色数据。每一个ws2812的颜色数据都是24位:8位绿色+8位红色+8位蓝色。
如果我们想控制1个ws2812,我们发送24位颜色数据;如果我们想控制2个ws2812,我们需要连续发送48位颜色数据;如果我们想控制n个ws2812,我们需要连续发送n*24位颜色数据。
那么ws2812如何知道我们发送的每一个位的数据是“1”还是“0”呢?
WS2812手册中对于数据有明确的时间定义:
可以看出,当我们想要发送一个位的数据时,如果这个位是“1”,我们就控制单片机的引脚输出高电平0.85us,然后控制引脚输出低电平0.40us;如果这个位是“0”,则控制引脚输出高电平0.40us,然后控制引脚输出低电平0.85us。
而如果我们想控制1个灯显示颜色,我们需要发送24个位的颜色数据给ws2812。如果我们想控制n个灯显示颜色,我们从第1个灯的颜色数据开始发送,直至发送完n*24个数据。最后我们需要控制单片机引脚输出低电平超过50us,让彩灯显示颜色。
2,写出驱动程序
注意,STC15F104W只有8个引脚,我们一般采用内部晶振电路。下面的代码都是基于12MHz的晶振频率写的。WS2812对于单片机的引脚时序要求比较高。我们一般调用intrins.h这个头文件的机器周期函数,nop()函数执行一次占用一个机器周期(此处时钟周期和机器周期相等),所以每条机器周期时间为1/12MHz=83.3us。(代码中很多处使用宏定义,宏定义在预编译阶段程序就完成代码替代工作,不会影响程序的执行时间,且修改方便)
下面是h文件代码:
#ifndef __WS2812_H
#define __WS2812_H
//头文件区
#include <STC15F2K60S2.H>
#include <intrins.h>
//用户修改参数区
//#define WS2812_FREQUENCY
#define RGB_PIN P33 //控制彩灯引脚(需要配置为强推挽输出)
#define WS2812_MAX 25 //彩灯最大个数
#define WS2812_NUMBERS 8 //彩灯个数
#define RED 0xff0000 //红色
#define GREEN 0x00ff00 //绿色
#define BLUE 0x0000ff //蓝色
#define BLACK 0x000000 //熄灭
#define WHITE 0xffffff //白色
#define RGB_PIN_H() RGB_PIN = 1
#define RGB_PIN_L() RGB_PIN = 0
#define delay1NOP() _nop_();
#define delay1NOP() _nop_();
#define delay2NOP() delay1NOP(); _nop_();
#define delay3NOP() delay2NOP();_nop_();
#define delay5NOP() delay3NOP();delay2NOP();
#define delay7NOP() delay5NOP();delay2NOP();
void Ws2812b_WriteByte(unsigned char byte);//发送一个字节数据(@12.000MHz,理论每个机器周期83ns,测试约为76ns)
void setLedCount(unsigned char count);//设置彩灯数目,范围0-25.
unsigned char getLedCount();//彩灯数目查询函数
void rgb_SetColor(unsigned char LedId, unsigned long color);//设置彩灯颜色
void rgb_SetRGB(unsigned char LedId, unsigned long red, unsigned long green