一文速通WS2811/2812灯带 驱动方式GD32+GPIO

author:Niki

一、项目介绍

本项目基于GD32F303开发板对WS2811/2812灯带进行控制,WS2811/2812是一种可编程的LED灯,具有RGB显示效果,可显示的颜色数量为2^24。

我手上的2812与2811灯带的区别主要就是在于2812灯带使用5V供电,2811使用12V供电,2812一个完整控制数据控制一个灯,2811一个完整控制数据控制三个灯,在控制原理上没有任何区别。后文就以2812为名了。

83f9e6a3f3524f83aa411ee810fc4f88.png

WS2812灯带

二、灯带驱动原理介绍

WS2812采用单线归零码的通讯方式,即利用高低电平的持续时间来确定0和1。这种通信方式优点是只需要一根通信线,缺点是对通信的时序要求较高。

官方规定的通信时间为:

TH0TL0

TH1

TL1RES

0码高电平时间

0码低电平时间

1码高电平时间

1码低电平时间

帧间隔,低电平时间

220~350ns580~1000ns580~1000ns580~1000ns280us以上

不过经过实际测试,其实灯带对于低电平的要求没有那么高,哪怕是在一个220ns的高电平后面加上一个5us的低电平依旧是可以识别成为0码的(没有什么特殊原因的话不建议哈)。

驱动灯带是需要24位码作为一个单位来作为一颗灯(其实是一个2812芯片的信号,2811将信号转换成PWM输出到灯上),RGB灯其实就是红绿蓝三个灯伪装成一个灯,然后就能组合出很多的颜色,输入的24位码的第一个八位是作为蓝灯的输入信号,第二个八位是作为红灯的输入信号,第三个八位是作为绿灯的输入信号。在数据信号发送的时候,数据信号是不会生效的,只有在发送了RES信号之后才会使之前发送的数据信号生效。

那么驱动一个灯带需要做什么就基本清楚了,如果想要驱动一个灯亮,比如让亮白光(0xffffff),那就只需要先输入24个1码之后再输入一个RES信号就可以实现点亮一个白灯,想点亮十个白灯就先输入240个1码之后再发送RES信号就好了。在发完RES信号之后会开始接受下一次的数据,下一次的数据就又是从第一个灯的数据开始发送。

在这边说一下,2812芯片连接方式是第一个芯片连着单片机的输出端,后面的连着前一个芯片的输出端,当芯片接受到一个完整的24位码之后就会将后面接收到的数据通过输出端输出,后面连接着的芯片就会接收到数据了。

26cddbf61cbe4f38a485557a946a01d5.png

三、代码

前面鬼扯那么多,终于到了最期待的环节了,下面废话不多说,直接展示:

灯带的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的延时函数。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值