基于F407ZGT6的WS2812B彩灯驱动

硬件连接

WS2812B是一款全彩LED控制IC,单总线控制,可以级联。

基于F407ZGT6的WS2812B彩灯驱动
单个灯珠连接将DIN脚连接至某一个IO口并接上电源即可
多个灯珠串联将第一个灯珠的DOUT脚连接至下一个灯珠的DIN脚

驱动原理

1.时序图

基于F407ZGT6的WS2812B彩灯驱动
如图,由高低电平持续的不同时间代表0或者1,用一段长时间拉低表示reset,下图为对应的不同时长。
基于F407ZGT6的WS2812B彩灯驱动

2.数据传输方式

基于F407ZGT6的WS2812B彩灯驱动
如图,MCU将串联的GRB码传输进第一个灯珠,每个灯接收的是24bit数据(GRB颜色值),D1灯在收到24bit数据后,会把数据保存,如果还收到数据,会通过DO脚传给D2。

重点:高低电平的时间一定要控制在规格书的要求范围内

驱动代码

代码部分参考_祥子@这位老哥的文章

WS2812.h

typedef struct color{				//结构体定义
  
  uint8_t R;
  uint8_t G;
  uint8_t B;
  
}Color_TypeDef;

#define LED_NUM 12									//定义灯珠数量,便于修改
#define WS2812B_High()		HAL_GPIO_WritePin(RGB_GPIO_Port,RGB_Pin,GPIO_PIN_SET);		//定义拉高拉低
#define WS2812B_Low()	  	HAL_GPIO_WritePin(RGB_GPIO_Port,RGB_Pin,GPIO_PIN_RESET);

/*******************************************函数*********************************************/
void delay_320ns(void);
void delay_1000ns(void);
void MY_delay_us(uint16_t num);

void WS2812B_Reset(void);
void WS2812B_WriteByte(uint8_t dat);
void WS2812B_WriteColor(Color_TypeDef *pColor);

void Copy_Color(Color_TypeDef *pDst,Color_TypeDef *pScr);
void WS2812B_Refresh(void);
void WS2812B_Fillcolor(uint16_t start,uint16_t end,Color_TypeDef *pColor);
void WS2812B_Move(uint8_t dir);

WS2812.c

1.先写出发送0码1码的函数,使用了__nop();函数,一个__nop()对应一个机器周期,具体时长需要测量

void delay_320ns(void)    //T0H & T1L		延时320ns
{
  uint8_t i;
  for(i=0;i<5;i++)
  {
  __nop();
  }
}

void delay_1000ns(void) //T0L & T1H			//延时1000ns
{
  uint8_t i;
  for(i=0;i<25;i++)
  {
  __nop();
  }
}

void MY_delay_us(uint16_t num)			//us级延时
{
  uint8_t i;
  while(num)
  {
    for(i=0;i<25;i++)
    {
    __nop();
    }
    num--;
  }
}

测量延时时长的方法&函数
在main函数中调用这个函数,然后开启debug,轮流测试两个函数延时时长,不断更改循环次数直到时长满足时序要求。

void WS2812B_Test2(void)
{
	WS2812B_Hi();
	delay_320ns();
	//delay_1000ns();
	WS2812B_Low();
}

debug测试方法
在测试函数前后加上断点放在while内,进入debug后看右下角
基于F407ZGT6的WS2812B彩灯驱动
如果是t0就右击选择t2或者t1。
在测试函数前的断点停下时再右键把数值清零,到结束的断点停下时显示的值就是延时函数实际的时间

2.写数据发送函数

void WS2812B_Reset(void)					//reset函数
{
  WS2812B_Low();
  int i=60;
	while(i--)
	{
		delay_1000ns();
	}
}

void WS2812B_WriteByte(uint8_t dat)				//写一个字节
{
	uint8_t i;
	for (i=0;i<8;i++)
	{
		//先发送高位
		if (dat & 0x80) //1				//数据 &0x80 ,若高位为1则结果为1,否则为0
		{
			WS2812B_High();
			delay_1000ns(); //T1H
			WS2812B_Low();	
			delay_320ns(); //T1L
		}
		else	//0
		{
			WS2812B_High();
			delay_320ns(); //T0H
			WS2812B_Low();	
			delay_1000ns(); //T0L
		}
		dat<<=1;
	}
}

void WS2812B_WriteColor(Color_TypeDef *pColor)		//发送颜色,通过分别发送三个颜色的字节完成
{
    WS2812B_WriteByte(pColor->G);
	WS2812B_WriteByte(pColor->R);
	WS2812B_WriteByte(pColor->B);
} 

void WS2812B_Refresh(void)				//把最新的结构体数据刷新到灯上显示
{
  uint8_t i;
  
  for(i=0;i<LED_NUM;i++)
  {
    WS2812B_WriteColor(&WS2812B_Buf[i]);
  }
}

3.灯珠颜色处理
(实现跑马灯/彩虹渐变)

//1.复制颜色
void Copy_Color(Color_TypeDef *pDst,Color_TypeDef *pScr)		//把后一个灯的颜色给前一个灯珠
{
  pDst->R = pScr->R;
	pDst->G = pScr->G;
	pDst->B = pScr->B;
}

//2.填充颜色(用于给一长串灯珠同一个颜色值)
void WS2812B_Fillcolor(uint16_t start,uint16_t end,Color_TypeDef *pColor)		//始于,终于,颜色值
{
  if(start>end)
  {
    uint16_t temp;
    temp=start;
    start=end;
    end=temp;
  }
  
  if (start>=LED_NUM)return;
  if(end>=LED_NUM)end=LED_NUM-1;
  
  while(start<=end)
  {
    Copy_Color(&WS2812B_Buf[start],pColor);
    start++;
  }
}

//3.移动灯珠颜色
void WS2812B_Move(uint8_t dir)		//dir为移动的个数,越大移动越多
{
  Color_TypeDef temp;
  uint8_t i;
  
  if(dir)
  {
    Copy_Color(&temp,&WS2812B_Buf[LED_NUM-1]);
    
    i = LED_NUM-1;
    while(i)
    {
      Copy_Color(&WS2812B_Buf[i],&WS2812B_Buf[i-1]);
      i--;
    }
    Copy_Color(&WS2812B_Buf[0],&temp);
  }
  else
  { 
    Copy_Color(&temp,&WS2812B_Buf[0]);
    
    i = 0;
    while(i<(LED_NUM-1))
    {
      Copy_Color(&WS2812B_Buf[i],&WS2812B_Buf[i+1]);
      i++;
    }
    Copy_Color(&WS2812B_Buf[LED_NUM-1],&temp);
  }
}

main.c

定义

Color_TypeDef WS2812B_Buf[LED_NUM];

初始化

  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();

  uint8_t i;
  for(i=0;i<8;i++)								//给前12个灯珠不同的GRB值,这里给的是R-G-B渐变
  {
    WS2812B_Buf[i].R=63-7*i;
    WS2812B_Buf[i].G=7*i;
    WS2812B_Buf[i].B=0;
    HAL_Delay(1);
  }
    for(i=8;i<12;i++)
  {
    
    WS2812B_Buf[i].R=0;
    WS2812B_Buf[i].G=127-7*i;
    WS2812B_Buf[i].B=7*i-56;
    HAL_Delay(1);
  }
  
  WS2812B_Refresh();	//由于上一步只更新了颜色值,并没有显示,所以加入refresh刷新显示

循环

while(1)
{
	HAL_Delay(40);			//延时用于调整速度
    WS2812B_Move(5);		//move函数控制移动个数
    WS2812B_Refresh();		//刷新显示
}

效果展示

初始化完的灯效 (加上一块均光板或者喷过漆的亚克力板可以让光更均匀)
(随便捡了一块废板喷的漆)
基于F407ZGT6的WS2812B彩灯驱动

上一篇:初次stack-overflow 提交答案


下一篇:protues仿真器51单片机定时器内部定时方式