当前位置: 首页 > news >正文

MCU单片机驱动WS2812,点亮RGB灯带各种效果

目录

一、WS2812驱动信号说明

1、手册上的信号说明

2、实际信号工作方式推测

二、程序调试

1、接线说明

2、编写最基本的代码,点亮第一个灯

3、接下来,点亮8个灯

4、接下来,按数组中的定义的颜色点亮灯

5、最后,我们来做渐变流彩灯效果

6、总结

注意事项


注意:本文转载至简书,感谢大佬帮助过我,再此进行转载记录供更多人学习!

作者:szryg
链接:https://www.jianshu.com/p/0bc6981d949f
来源:简书

一、WS2812驱动信号说明

1、手册上的信号说明

但实际测试时,发现即使使用不符合这个表要求的时序也是可以正常驱动WS2812的,如下图就是一个能正常驱动WS2812信号中断其中一个bit,这个信号发送的是0,高电平宽度是180ns,并且他与下一个bit的低电平时长差不多接近2us了。

                                                 两个bit数据之间的低电平间隔时间

请看下图,这是RGB每个字节之间的间断时间,可以发现时间已经超过5us了,但此时WS2812任然能够正确解析信号,信号已经完全超出手册定义的范围,为什么WS2812还能正常工作?

2、实际信号工作方式推测

我们推测组成数据信号的关键不是PWM的占空比,而是PWM高电平脉冲的宽度。

这是另一份WS2812灯珠的数据手册,这个手册换了一种描述信号的方法,从图可以看出,只要高电平时间在220~380ns之间就认为是0码580ns~1us之间则认为是1码,这里我们猜测380ns580ns480ns,就是WS2812判断0 1码的标准。

换句话说,只要高电平时间<480ns,WS2812就会读到0码;高电平脉冲>480ns,2812就会读到一个1码,而低电平时长对01码的识别没有太大影响(当然低电平时间不能超过Reset码的时间),这样看来,我们只要把握好每个信号的高电平宽度即可,这个要求对于现在的1T内核的8051MCU还是可以做到的。

二、程序调试

这边使用的是51单片机,主频24MHZ

1、接线说明

2、编写最基本的代码,点亮第一个灯

#include "ch554.h"
#include "Debug.h"
#include "GPIO.h"sbit outPin = P1^3;//功能:产生一个低电平复位信号给WS2812
void WS2812_Reset(void)
{int i = 200;while(i--)outPin = 0;     //这里具体时间没有测试,大于100us应该就可以,请自行调试
}//功能:给WS2812发送一个字节数据
void WS2812_WriteByte(unsigned char dat)
{char i = 8;while(i--){//产生脉冲时序部分,具体执行的代码需要根据您使用的单片机而定//我这里使用的CH552T@24MHz,执行一次SETB或CLRB指令大概耗时80ns,我按照这个特性,写出了下面的时序用于产生0码和1码//输出在低电平期间,则进行其他处理,如循环,数据移位等if(dat & 0x80){//Write Bit 1outPin = 1;   //这里多次调用SETB指令来达到延迟的目的,产生一个大概800ns的高电平脉冲outPin = 1;outPin = 1;outPin = 1;outPin = 1;outPin = 1;outPin = 1;outPin = 0;}else{//Write Bit 0outPin = 1;   //这里产生一个大概180ns的高电平脉冲outPin = 1;outPin = 1;outPin = 0;}dat <<= 1;}
}void main(void)
{CfgFsys();            //这是官方提供库"Debug.C"中的函数,用于初始化时钟信号,请将"Debug.h"中的"FREQ_SYS = 12000000"改为"FREQ_SYS = 24000000", 使用24M主频Port1Cfg(1, 3);       //设置P1.3口为推挽输出WS2812_Reset();WS2812_WriteByte(0xff);   //GreenWS2812_WriteByte(0xff);   //RedWS2812_WriteByte(0xff);   //Bluewhile(1);return;
}

载程序,复位运行,您就可以看到第一颗WS2812被点亮了,发出耀眼的白光。

3、接下来,点亮8个灯

修改写main()中的代码,让3个颜色的数据重复传输8次,您可以看到,所有灯都被点亮了。

void main(void)
{char i;CfgFsys();            //这是官方提供库"Debug.C"中的函数,用于初始化时钟信号,请将"Debug.h"中的"FREQ_SYS = 12000000"改为"FREQ_SYS = 24000000", 使用24M主频Port1Cfg(1, 3);       //设置P1.3口为推挽输出WS2812_Reset();for(i=0; i<8; i++){WS2812_WriteByte(0xff);   //GreenWS2812_WriteByte(0xff);   //RedWS2812_WriteByte(0xff);   //Blue}while(1);return;
}

4、接下来,按数组中的定义的颜色点亮灯

#include "ch554.h"
#include "Debug.h"
#include "GPIO.h"sbit outPin = P1^3;//定义一个结构体,用于表示一个灯的颜色数据
typedef struct{unsigned char red;unsigned char green;unsigned char blue;
}WS2812_Color_t;//功能:产生一个低电平复位信号给WS2812
void WS2812_Reset(void)
{int i = 200;while(i--)outPin = 0;     //这里具体时间没有测试,大于100us应该就可以,请自行调试
}//功能:给WS2812发送一个字节数据
void WS2812_WriteByte(unsigned char dat)
{char i = 8;while(i--){//产生脉冲时序部分,具体执行的代码需要根据您使用的单片机而定//我这里使用的CH552T@24MHz,执行一次SETB或CLRB指令大概耗时80ns,我按照这个特性,写出了下面的时序用于产生0码和1码//输出在低电平期间,则进行其他处理,如循环,数据移位等if(dat & 0x80){//Write Bit 1outPin = 1;   //这里多次调用SETB指令来达到延迟的目的,产生一个大概800ns的高电平脉冲outPin = 1;outPin = 1;outPin = 1;outPin = 1;outPin = 1;outPin = 1;outPin = 0;}else{//Write Bit 0outPin = 1;   //这里产生一个大概180ns的高电平脉冲outPin = 1;outPin = 1;outPin = 0;}dat <<= 1;}
}//功能:按照颜色数据,传输一个灯的颜色数据
//参数:*pColor    请输入存放颜色数据的地址(灯颜色数据地址)
void WS2812_WriteColor(WS2812_Color_t *pColor)
{WS2812_WriteByte(pColor[0].green);WS2812_WriteByte(pColor[0].red);WS2812_WriteByte(pColor[0].blue);
}//功能:按照颜色数据,一次性传输多个灯的颜色数据
//参数:*pColor    请输入存放颜色数据的地址(灯颜色数据地址)
//      num        颜色数据长度,例如要点亮1个灯,输入1
void WS2812_WriteColors(WS2812_Color_t *pColor, int num)
{while(num--)WS2812_WriteColor(pColor++);
}void main(void)
{//定义每个灯的颜色值,存储在Flash中WS2812_Color_t code testColorTable[8] = {{255,   0,   0},{  0, 255,   0},{  0,   0, 255},{255, 255,   0},{  0, 255, 255},{255,   0, 255},{128, 255, 255},{255, 128, 255},};CfgFsys();            //这是官方提供库"Debug.C"中的函数,用于初始化时钟信号,请将"Debug.h"中的"FREQ_SYS = 12000000"改为"FREQ_SYS = 24000000", 使用24M主频Port1Cfg(1, 3);       //设置P1.3口为推挽输出WS2812_Reset();WS2812_WriteColors(testColorTable, 8);    //写出数据,点亮8个灯while(1);return;
}

5、最后,我们来做渐变流彩灯效果

在这个版本里,增加了一个函数用于生成连续的色彩变化,当8个灯的颜色相距一段距离,然后依次点亮,行程了流彩灯的效果

#include "ch554.h"
#include "Debug.h"
#include "GPIO.h"sbit outPin = P1^3;//定义一个结构体,用于表示一个灯的颜色数据
typedef struct{unsigned char red;unsigned char green;unsigned char blue;
}WS2812_Color_t;//功能:产生一个低电平复位信号给WS2812
void WS2812_Reset(void)
{int i = 200;while(i--)outPin = 0;     //这里具体时间没有测试,大于100us应该就可以,请自行调试
}//功能:给WS2812发送一个字节数据
void WS2812_WriteByte(unsigned char dat)
{char i = 8;while(i--){//产生脉冲时序部分,具体执行的代码需要根据您使用的单片机而定//我这里使用的CH552T@24MHz,执行一次SETB或CLRB指令大概耗时80ns,我按照这个特性,写出了下面的时序用于产生0码和1码//输出在低电平期间,则进行其他处理,如循环,数据移位等if(dat & 0x80){//Write Bit 1outPin = 1;   //这里多次调用SETB指令来达到延迟的目的,产生一个大概800ns的高电平脉冲outPin = 1;outPin = 1;outPin = 1;outPin = 1;outPin = 1;outPin = 1;outPin = 0;}else{//Write Bit 0outPin = 1;   //这里产生一个大概180ns的高电平脉冲outPin = 1;outPin = 1;outPin = 0;}dat <<= 1;}
}//功能:按照颜色数据,传输一个灯的颜色数据
void WS2812_WriteColor(WS2812_Color_t *pColor)
{WS2812_WriteByte(pColor[0].green);WS2812_WriteByte(pColor[0].red);WS2812_WriteByte(pColor[0].blue);
}//功能:按照颜色数据,一次性传输多个灯的颜色数据
//参数:*pColor    请输入存放颜色数据的地址(灯颜色数据地址)
//      num        颜色数据长度,例如要点亮1个灯,输入1
void WS2812_WriteColors(WS2812_Color_t *pColor, int num)
{while(num--)WS2812_WriteColor(pColor++);
}//根据输入index, 取一个颜色,
//参数:*pColor    请输入存放颜色数据的地址(灯颜色数据地址)
//      cnt        颜色索引,输入不同值会得到不同的颜色输出,输入范围:0~767
void getColors(WS2812_Color_t *pColor, unsigned int index)
{//这段代码其实是根据输入数值生成颜色,2个颜色之间此消彼长地变化if(index <= 255){pColor[0].red = index;pColor[0].green = 0;pColor[0].blue = 255-index;}else if(index <= 511){pColor[0].red = 511 - index;pColor[0].green = index - 256;pColor[0].blue = 0;}else if(index <= 767){pColor[0].red = 0;pColor[0].green = 767 - index;pColor[0].blue = index - 512;}else{//不处理}
}//根据输入数值更新8个灯的颜色数据
//参数:*pColor    请输入存放颜色数据的地址(灯颜色数据地址)
//      cnt        颜色索引,输入不同值会得到不同的颜色输出,输入范围:0~767
void ColorsUpdata_8Led(WS2812_Color_t *pColor, unsigned int inIndex)
{char i;if(inIndex > 767)inIndex = 767;for(i=0; i<8; i++){int index;//这段代码中的数字(60),决定了每个灯之间的颜色差异(颜色渐变的缓和度,可自行测试, 如果设置为0,则失去流水灯效果)index  = inIndex + (60) * i;if(index >= 768)index -= 768;getColors(pColor + i, index);}
}void main(void)
{WS2812_Color_t xdata ColorTable[8]; //用于存储8个灯的颜色数据CfgFsys();            //这是官方提供库"Debug.C"中的函数,用于初始化时钟信号,请将"Debug.h"中的"FREQ_SYS = 12000000"改为"FREQ_SYS = 24000000", 使用24M主频Port1Cfg(1, 3);       //设置P1.3口为推挽输出while(1){static unsigned int colorIndex = 0;       ColorsUpdata_8Led(ColorTable, colorIndex);    //根据Index,获取当前的颜色数据WS2812_Reset();WS2812_WriteColors(ColorTable, 8);            //写出8个灯的数据//更新颜色IndexcolorIndex++;if(colorIndex >= 768)colorIndex = 0;mDelaymS(5);  //延迟5ms,更改这个数可以加快(减小数值)或减慢(加大数值)颜色变化的速度}return;
}

6、总结

注意事项

在本程序里,WS2812相关函数发送数据中不可被打断,否则可能导致数据传输失败,所以如果您有使用中断,请在发送数据期间暂时关闭中断。

将驱动的要点总结一下:

  • 低电平大于100us,总线复位(不同灯珠这个时间可能不同)
  • 高电平脉冲时间大于480ns为1码,实际发送750ns
  • 高电平脉冲时间小于480ns为0码,实际发送250ns
  • 低电平时间可以延长,不会导致通讯失败,但不超过总线复位时间(这个是猜测没有验证,但拖长几个微秒是已确定没有问题的)




 

http://www.dtcms.com/a/574434.html

相关文章:

  • 公司的服务器能建设网站吗网站如何做移动适配
  • 嘉兴做网站优化多少钱网站搜索引擎友好性
  • 正规网站建设公司哪家好wordpress js被挂木马
  • 贵州省建设厅网站查合肥网站排名优化公司
  • 什么是接口测试?为什么要做接口测试?
  • 淘宝网站制作教程北京网站建设东轩seo
  • 无锡公司建立网站佛山企业用seo策略
  • 24.系统日志查看方法与实战
  • WordPress软件连接不了网站宁波建设监理协会网站
  • 网站网络安全怎么做如何提高网站百度权重
  • Openmetadata数据质量管理-新增自定义测试类型
  • 49-基于ZigBee的室内甲醛监测系统设计与实现
  • 一文读懂稳态太阳光模拟器
  • 云南建站推广南通网站seo服务
  • 网站配色绿色微商商城系统开发
  • 仓颉编程语言基础集合类型详解:HashSet深度解析
  • 无代码网站开发网页怎么生成长图
  • 户县网站建设福建省网站建设绩效排名
  • 基于MQTT和Sparkplug B的UNS系统的元数据管理
  • Origin将Y偏移图升级为3D瀑布图
  • 职业学院网站建设方案做网站怎么搭建环境
  • 网站副标题wordpresswordpress做个米表
  • 开淘宝店怎么做充值网站杭州巴顿品牌设计
  • 北京市城乡建设协会官方网站开发网站如何选需要
  • VASP 教程:使用 VASP 进行机器学习力场训练
  • 受限长度路径搜索算法
  • H265 vs AV1 vs H266 rdoq对比
  • 在Linux服务器上安装CVAT (Docker 28.5.1)
  • 四川学校网站建设农业公司网站建设
  • 网站建设报价购物凡科建站提示网站建设中