WS2812B灯带

 开发WS2812B灯带笔记


目录

什么是WS2812B灯

WS2812B灯带连接原理图

WS2812B灯带的数据和数据传输是啥样的?

补充:什么是RGB?

那么WS2812B灯的24bit数据如何构造?

WS2812B灯的逻辑“1”和逻辑“0”

代码部分

实现ns级别延时


什么是WS2812B灯

WS2812B是一种智能控制LED光源,将控制电路和RGB芯片集成在5050个元件包中。其内部包括智能数字端口数据锁存器和信号重塑放大驱动电路。还包括精密内部振荡器和电压可编程恒流控制部分,有效地确保像素点光色高度一致。俗称IC灯。

WS2812B灯带连接原理图

WS2812B灯带_第1张图片

主要通过一条数据线来控制每个灯的颜色 ,每个灯珠控制着下个灯珠。

WS2812B灯带的数据和数据传输是啥样的?

WS2812B灯带_第2张图片

数据传输协议采用单一的NZR通信模式。在像素通电复位后,DIN端口从控制器接收数据,第一个像素收集初始24位数据,然后发送到内部数据锁存器,其他数据通过内部信号重塑放大电路的重塑,通过DO端口发送到下一个级联像素。在对每个像素进行传输后,该信号减少24位。像素采用自动整形传输技术,使像素级联数不限制信号的传输,只依赖于信号传输的速度。

通俗点讲,就是每个灯接受24位的数据为每个灯的颜色像素(RGB888)。比如,你发送了3组24位的数据,第一个灯珠接收了第一组24位数据后,会把后面的两组数据发给第二个灯珠,第二个灯珠接受了第二组24位数据后,会把第三组数据发给第三个灯珠,以此类推。

灯珠接受的24位数据按图里来看,是8位绿色,8位红色,8位蓝色组成。

补充:什么是RGB?

RGB颜色空间以R(Red:红)、G(Green:绿)、B(Blue:蓝)三种基本色为基础,进行不同程度的叠加,产生丰富而广泛的颜色,所以俗称三基色模式。

RGB空间是生活中最常用的一个颜色显示模型,电视机、电脑的CRT显示器等大部分都是采用这种模型。自然界中的任何一种颜色都可以由红、绿、蓝三种色光混合而成,现实生活中人们见到的颜色大多是混合而成的色彩。

肉眼可以识别世界上的所有颜色,而RGB几乎可以组合成世界上所有的颜色!

以我们所用的RGB24为例,RGB24的每个元素在计算机内存中占用1个字节,1个字节等于8个bit位,所以RGB每个元素的取值范围为:0~255。

那么三色组合起来256x256x256=16777216(一千六百七十七万七千二百一十六)种颜色。

那么WS2812B灯的24bit数据如何构造?


既然是24bit数据代表三种颜色,我们就要首先知道一个bit的意义是什么,我们传统意义上来说1个bit代表一个数据位,但是对于数据位bit的理解好像就是“1”或者“0”在数电里我们很容易把高低电平跟逻辑1和逻辑0对应起来,但是表示灯珠的逻辑电平不是简单的高低电平。在数值上0xFFFFFF就是24bit的1,0x000000就是24bit的0.这里有8个bit代表颜色G分量,G7G6G5G4G3G2G1G0,有8个bit代表R分量R7R6R5R4R3R2R1R0,有8个bit代表B分量B7B6B5B4B3B2B1B0,当不同分量组合时候就会有不同的数据产生,这个数据背后其实是逻辑电平。

但是我们IC灯的逻辑“1”并不是简简单单的高电平,IC灯的逻辑“0”也不是简简单单的低电平。

 

WS2812B灯的逻辑“1”和逻辑“0”

WS2812B灯带_第3张图片

 从上图可知,逻辑“1”和逻辑“0”并不是普通的高电平,低电平。而是由高低电平不同比例组成的。

逻辑“0”:由小部分的高电平和大部分的低电平组成 

逻辑“1”:由大部分的高电平和小部分的低电平组成

下面讲讲这大部分和小部分的精确取值:WS2812B灯带_第4张图片

 逻辑“0”:由220ns-380ns的高电平和580ns-1us的低电平组成

逻辑“1”:由由220ns-380ns的低电平和580ns-1us的高电平组成

代码部分

到这我们差不多就知道了WS2812B灯的原理和时序了,下面讲讲编程的部分。

实现ns级别延时

  1. 硬件延时NOP实现
  2. 滴答定时器中断实现
  3. 普通定时器实现

首先因为对时序要求比较严格,所以需要实现ns级别的延时。

我用的板子是gd32f450,系统频率是200mhz。

1/200Mhz = 0.005 ms = 5 ns

所以一个nop的延时是5ns。

#define DELAY_5ns	__NOP() /* 一个nup为5ns		1/200Mhz = 0.005 ms = 5 ns*/
#define DELAY_50ns  DELAY_5ns;DELAY_5ns;DELAY_5ns;DELAY_5ns;DELAY_5ns;DELAY_5ns;DELAY_5ns;DELAY_5ns;DELAY_5ns;DELAY_5ns
#define DELAY_250ns	DELAY_50ns;DELAY_50ns;DELAY_50ns;DELAY_50ns;DELAY_50ns
#define DELAY_300ns	DELAY_50ns;DELAY_50ns;DELAY_50ns;DELAY_50ns;DELAY_50ns;DELAY_50ns
#define DELAY_600ns	DELAY_300ns;DELAY_300ns

编写逻辑“0”和逻辑“1”的函数

void Bit_0(void)
{
	rt_pin_write(IC_LED_GREEN_REG,1);
	DELAY_300ns;
	rt_pin_write(IC_LED_GREEN_REG,0);
	DELAY_600ns;
}
void Bit_1(void)
{
	rt_pin_write(IC_LED_GREEN_REG,1);
	DELAY_600ns;
	rt_pin_write(IC_LED_GREEN_REG,0);
	DELAY_300ns;
}

最简单的驱动函数

void RGB_Set(void)
{
	for(uint8_t i = 0;i<8;i++)
	{
		Bit_1();
	}
	for(uint8_t i = 0;i<8;i++)
	{
		Bit_0();
	}
	for(uint8_t i = 0;i<8;i++)
	{
		Bit_0();
	}
}

这样就生成了24bit的数据,111111110000000000000000,绿灯

但是这样的操作方法显然不适合在项目里使用,所以我们要写一些函数来设置

比如我们一般是按RGB值来设定灯的颜色,我们输入RGB三颜色的值就可以。但是这样的话又会又一个问题,我们输入的是10进制的值,0-255。但是我们所需要的却是需要把这个值换成2进制,因此,需要根据这些来编写代码

首先需要控制函数,以后项目需要控制IC灯只需要调用这个函数输入相对应的RGB值就行了,

int ic_led_ctrl(int r_val, int g_val, int b_val) 
{
	decimal_to_binary(r_val,g_val,b_val);
	
	return 0;
}

接着我们要实现把10进制的RGB值转化为2进制的RGB值

/* 连接数组 */
int connection_arr(int *r,int *g,int *b)
{
    int i = 1;
    int *rgb = rgb_arr;
	while (i <= 8)
	{
		*rgb = *g;
		rgb++;
		g++;
		i++;
	}

    while (i <= 16)
	{
		*rgb = *r;
		rgb++;
		r++;
		i++;
	}

    while (i <= 24)
	{
		*rgb = *b;
		rgb++;
		b++;
		i++;
	}
	
	return 0;
}

/* 反转数组 */
int reverse(int *x) {
    int* p, temp, * i, * j;
	int	m = (8 - 1) / 2;
	
    i = x;    		/* 数组首元素地址 */
    j = x + 8 - 1;	/* 数组末尾元素地址 */
    p = x + m;		/* 中间元素地址 */
    
    for (i = x; i <= p; ++i, --j) {
        temp = *i;
        *i = *j;
        *j = temp;
    }
	
    return 0;
}
/* 十进制转换成二进制 */
static void decimal_to_binary(int r_num,int g_num, int b_num)
{

        int r_arr[8] = {0}; /* 用一个长度为32的数组接收最后的二进制数,里面的0是为数组设置初始值 */
        int g_arr[8] = {0};
        int b_arr[8] = {0};

        int index = 0;/* 用来定义数组的下标 */
        
		r_arr[index] = r_num % 2; /* 先获取输入数字的第一个二进制位 */
        g_arr[index] = g_num % 2;
        b_arr[index] = b_num % 2;
        index++; /* 下标下移一位 */
        
        while (1) {/* 使用while循环来获取剩下的二进制位 */
			/*十进制转换为二进制思想:
			 1.对十进制数进行除以2的操作
             2.对所获得的商进行取余的操作,所获得的数就是二进制每一位
             3.当num<2时,即num除以二不会再有大于1的数字,num%2必为0时
             此时说明num已经完全转换位二进制数,则跳出循环*/
			r_num = r_num / 2;
			g_num = g_num / 2;
            b_num = b_num / 2;
			
            r_arr[index] = r_num % 2;
            g_arr[index] = g_num % 2;
            b_arr[index] = b_num % 2;
            index++;
			
            if (g_num < 2 && r_num <2 && b_num < 2){
                break;
            }			
        }		
		reverse(r_arr);
		reverse(g_arr);
		reverse(b_arr);
		connection_arr(r_arr,g_arr,b_arr);
}

十进制转换二进制代码详解,请点击观看

当做完上面这些,我们只需要在主函数中写入RGB的值就能实现灯带的点亮了。

void RGB_Set(void)
{
	int i = 0;
	
	for(i = 0; i < 24; ++i) {
		if(rgb_arr[i] == 1)
		{
			Bit_1();
		} else {
			Bit_0();
		}
	}
	
}

你可能感兴趣的:(stm32,c语言,单片机,mcu)