一、WS2812介绍
WS2812是一种广泛使用的地址可控制的RGB LED模块,其内置驱动电路和控制芯片,允许用户通过单线信号控制多个LED的颜色和亮度。以下是WS2812模块的一些主要特点和应用:
主要特点
单线控制: WS2812模块通过单一数据线进行控制,简化了连接线的复杂度。
地址可控: 每个WS2812 LED都有独立的地址,这意味着你可以分别控制每个LED的颜色和亮度,适合制作动态效果。
多彩显示: 支持全彩显示,通常可以混合红色、绿色和蓝色三基色,产生多种颜色。
链式连接: 支持多颗WS2812 LED串联,可以方便地扩展LED的数量,只需将下一颗LED的数据线连接到前一颗LED上。
高亮度和高密度: WS2812 LED具有较高的亮度,可以在较大面积上提供均匀的光效,适合室内外装饰和显示。
控制精度: 通常提供256级亮度(8位)和1670万种颜色选择,确保丰富的视觉效果。
主控制板上的WS2812部分如下图:
主控制板上WS2812模块的接线图如下:
可以看到一共有八个模块级联在一起,并且由PA12控制。
通信原理
WS2812的数据协议采用单线归零码的通讯方式,支持串行级联接口,能通过一根信号线完成数据的接收与解码。每个灯就是一个像素点,每个像素点的三基色颜色可实现256级亮度显示,完成16777216种颜色的全真色彩显示。
像素点在上电复位以后,DIN端接受从控制器传输过来的数据,首先送过来的24bit数据被第一个像素点提取后,送到像素点内部的数据锁存器,剩余的数据经过内部整形处理电路整 形放大后通过DO端口开始转发输出给下一个级联的像素点,每经过一个像素点的传输,信号减少24bit。像素点采用自动整形转发技术,使得该像素点的级联个数不受信号传送的限制,仅受限信号传输速度要求。
控制方式
因为使用的是单总线,一根线完成一个灯要显示的24位颜色数据,是通过高低电平的时间长度来确定发送的是什么数据。24位的数据结构见下图。
其中G代表三色中的绿色,R代表三色中的红色,B表示三色中的蓝色。例如想要只显示红色则发送 0X00FF00即可。
控制时序
发送24位颜色数据,是通过高低电平的时间长度来确定发送的是0还是1。
发送一位数据0,需要总线拉高T0H的时间再拉低T0L的时间,WS2812才会自动识别该数据是0。
发送一位数据1,需要总线拉高T1H的时间再拉低T1L的时间,WS2812才会自动识别该数据是1。
二、驱动代码编写
WS2812的驱动方法有很多种,定时器+DMA,SPI+DMA,软件模拟等,我们这里选择软件模拟时序的方式来驱动。
首先初始化GPIO口,将PA12口配置为推挽输出。注意CW32F030和CW32L012的时钟配置区别。
void WS2812_Init(void){__RCC_GPIOA_CLK_ENABLE();//开启时钟GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.IT=GPIO_IT_NONE;GPIO_InitStruct.Mode=GPIO_MODE_OUTPUT_PP;//推挽输出GPIO_InitStruct.Pins=GPIO_PIN_12;GPIO_InitStruct.Speed=GPIO_SPEED_HIGH;GPIO_Init(CW_GPIOA, &GPIO_InitStruct);RGB_Color_Init();//初始化所有灯}
随后根据时序的高低电平要求,先编写一个ns延时,由于当前使用主频为64mhz,可以算出一个机器指令所需时间大概为1/64=15ns,然后加上GPIO电平翻转时间即可。
/**************************400ns延时**************************/void delay_400ns(void)//409{__nop();__nop();__nop();__nop();__nop();//__nop();__nop();}/**************************800ns延时**************************/void delay_800ns(void)//800{__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();// __nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();// __nop();__nop();}
然后根据时序所需的高低电平时间编写0,1指令
/**************************发送1**************************/void RGB_Send1(void){RGB_H;delay_800ns();RGB_L;delay_400ns();}/**************************发送0**************************/void RGB_Send0(void){RGB_H;delay_400ns();RGB_L;delay_800ns();}
然后就可以向芯片发送指令了,一个颜色分为RGB三个色域,一个色域为8位,所以我们需要八位数据一起发送,编写如下代码:
/**************************发送颜色数据,高位先行**************************/void RGB_send_Data(uint8_t Data){uint8_t i;for(i=0;i<8;i++){if(Data & 0x80){RGB_Send1();}else{RGB_Send0();}Data<<=1;}}
这样就可以向芯片发送指定的颜色数据啦,最后在封装一下,我们想要的要求是指定某个灯亮某种颜色,或者指定某个灯熄灭,不能影响到其他灯,所以我们定义一个数组来装下所有灯的颜色数据,到时候只需要改变数组中固定位置的值就可以直接改变颜色了,代码如下:
/**************************显示颜色n:第几个灯R:红色G:绿色B:蓝色**************************/void Send_RGB(uint8_t n,uint8_t R,uint8_t G,uint8_t B){uint8_t i;RGB_Data[3*n-3]=G;RGB_Data[3*n-2]=R;RGB_Data[3*n-1]=B;for(i=0;i<24;i++){RGB_send_Data(RGB_Data[i]);}RGB_Reset();}
最后在main函数编写如下代码(OLED显示代码不在此处列出)
int main(void){OLED_Init();//初始化OLED_ShowString(1,1,"Hello");//OLED显示字符串WS2812_Init();while(1){Send_RGB(1,255,0,0);//255,0,0对应红色Delay_ms(500);Send_RGB(1,0,255,0);//255,0,0对应绿色Delay_ms(500);Send_RGB(1,0,0,255);//255,0,0对应蓝色Delay_ms(500);}}
三、工作现象
在烧写代码之后,我们可以观察到以下现象,OLED第一行第一列显示HELLO,第一个RGB灯每隔500ms转换一次颜色,在红绿蓝三色之间反复切换。
扫码加入QQ群3群| 610403240
248