author:Niki
一、项目介绍
本项目基于GD32F303开发板对WS2811/2812灯带进行控制,WS2811/2812是一种可编程的LED灯,具有RGB显示效果,可显示的颜色数量为2^24。
我手上的2812与2811灯带的区别主要就是在于2812灯带使用5V供电,2811使用12V供电,2812一个完整控制数据控制一个灯,2811一个完整控制数据控制三个灯,在控制原理上没有任何区别。后文就以2812为名了。

WS2812灯带
二、灯带驱动原理介绍
WS2812采用单线归零码的通讯方式,即利用高低电平的持续时间来确定0和1。这种通信方式优点是只需要一根通信线,缺点是对通信的时序要求较高。
官方规定的通信时间为:
| TH0 | TL0 |
TH1 | TL1 | RES |
|
0码高电平时间 |
0码低电平时间 |
1码高电平时间 |
1码低电平时间 |
帧间隔,低电平时间 |
| 220~350ns | 580~1000ns | 580~1000ns | 580~1000ns | 280us以上 |
不过经过实际测试,其实灯带对于低电平的要求没有那么高,哪怕是在一个220ns的高电平后面加上一个5us的低电平依旧是可以识别成为0码的(没有什么特殊原因的话不建议哈)。
驱动灯带是需要24位码作为一个单位来作为一颗灯(其实是一个2812芯片的信号,2811将信号转换成PWM输出到灯上),RGB灯其实就是红绿蓝三个灯伪装成一个灯,然后就能组合出很多的颜色,输入的24位码的第一个八位是作为蓝灯的输入信号,第二个八位是作为红灯的输入信号,第三个八位是作为绿灯的输入信号。在数据信号发送的时候,数据信号是不会生效的,只有在发送了RES信号之后才会使之前发送的数据信号生效。
那么驱动一个灯带需要做什么就基本清楚了,如果想要驱动一个灯亮,比如让亮白光(0xffffff),那就只需要先输入24个1码之后再输入一个RES信号就可以实现点亮一个白灯,想点亮十个白灯就先输入240个1码之后再发送RES信号就好了。在发完RES信号之后会开始接受下一次的数据,下一次的数据就又是从第一个灯的数据开始发送。
在这边说一下,2812芯片连接方式是第一个芯片连着单片机的输出端,后面的连着前一个芯片的输出端,当芯片接受到一个完整的24位码之后就会将后面接收到的数据通过输出端输出,后面连接着的芯片就会接收到数据了。

三、代码
前面鬼扯那么多,终于到了最期待的环节了,下面废话不多说,直接展示:
灯带的c文件如下:
#include "dev_led.h"
unsigned long WsDat[nWs];//灯带的数据,nWs是灯的数量
/***************************
引脚初始化函数,使用的GPIOB9作为数据输出引脚
****************************/
void WS_Init()
{
rcu_periph_clock_enable(RCU_GPIOB);
gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_bit_set(GPIOB,GPIO_PIN_9);
}
/***************************
延时函数,注意此函数使用的是120MHz的时钟频率
****************************/
void delay1us()
{
unsigned char i;
for(i=0; i<15; i++);
}
void delay08us()
{
unsigned char i;
for(i=0; i<12; i++);
}
void delay03us()
{
unsigned char i;
for(i=0; i<5; i++);
}
void delay_us(int num)
{
for(int i=0;i<num;i++)
{
unsigned char j;
for(j=0; j<15; j++);
}
}
/***************************
数据发送函数及复位函数
****************************/
void TX0()
{
gpio_bit_set(GPIOB,GPIO_PIN_9);
delay03us();
gpio_bit_reset(GPIOB,GPIO_PIN_9);
delay1us();
}
void TX1()
{
gpio_bit_set(GPIOB,GPIO_PIN_9);
delay08us();
gpio_bit_reset(GPIOB,GPIO_PIN_9);
delay08us();
}
void WS_Reset()
{
gpio_bit_reset(GPIOB,GPIO_PIN_9);
delay_us(300);
}
/***************************
单灯数据传输函数
****************************/
void WS_Set1(unsigned long dat)
{
unsigned char i;
for(i=0; i<24; i++)
{
if(0x800000 == (dat & 0x800000))
TX1();
else
TX0();
dat<<=1;
}
}
/***************************
全部灯带数据传输函数
****************************/
void WS_SetAll()
{
unsigned char j;
for(j=0; j<nWs; j++)
{
WS_Set1(WsDat[j]); // j / 0
}
WS_Reset();
}
/***************************
流水灯程序
****************************/
uint32_t Water_Color(unsigned long color0, unsigned long color1)
{
unsigned char Red0, Green0, Blue0;
unsigned char Red1, Green1, Blue1;
int RedMinus, GreenMinus, BlueMinus;
unsigned char NStep;
float RedStep, GreenStep, BlueStep;
unsigned long color;
unsigned char i;
Red0 = color0>>8;
Green0 = color0>>16;
Blue0 = color0;
Red1 = color1>>8;
Green1 = color1>>16;
Blue1 = color1;
RedMinus = Red1 - Red0;
GreenMinus = Green1 - Green0;
BlueMinus = Blue1 - Blue0;
NStep = ( abs0(RedMinus) > abs0(GreenMinus) ) ? abs0(RedMinus):abs0(GreenMinus);
NStep = ( NStep > abs0(BlueMinus) ) ? NStep:abs0(BlueMinus);
NStep =30;
RedStep = (float)RedMinus / NStep;
GreenStep = (float)GreenMinus / NStep;
BlueStep = (float)BlueMinus / NStep;
for(i=0; i<NStep; i++)
{
Red1 = Red0 + (int)(RedStep * i);
Green1 = Green0 + (int)(GreenStep * i);
Blue1 = Blue0 + (int)(BlueStep * i);
color = Green1<<16 | Red1<<8 | Blue1;
if(i>0)
for(int j=1;j<=i%10;j++)
{
WsDat[j]=WsDat[j-1];
}
WsDat[0] = color;
WS_SetAll();
vTaskDelay(100);
}
return color;
}
灯带的h文件如下:
#ifndef __DEV_LED_H
#define __DEV_LED_H
#include "main.h"
#define White 0xFFFFFF
#define Black 0x000000
#define Red 0x00ff00
#define Green 0x0000ff
#define Blue 0xff0000
#define nWs 10 //灯带个数
extern unsigned long WsDat[];
extern void WS_Reset(void);
void delay_us(int num);
extern void WS_Init(void);
extern void WS_SetAll(void);
extern uint32_t ColorToColor(unsigned long color0, unsigned long color1);
extern uint32_t Water_Color(unsigned long color0, unsigned long color1);
#endif
mian程序如下:
#include "dev_led.h"
int mian()
{
SystemInit();
systick_config();
WS_Init();
whlie(1)
{
Water_Color(0x00ff00,0xff0000);
}
return 0;
}
以上程序即可以实现控制灯带流水灯循环点亮了,但是GPIO的点亮方式对于单片机的时钟信号要求较高,对于不同时钟信号需要调节TX1和TX0的延时函数。
3万+

被折叠的 条评论
为什么被折叠?



