Bootstrap

手把手教你玩转WS2812B灯

手把手教你玩转WS2812B灯

这是一起关于WS2812B灯带的驱动教学,带你玩转WS2812B灯带,下面我们先来看看效果吧

效果展示

WS2812B测试

WS2812B简介

在这里插入图片描述

WS2812B是一种数字可编程LED灯条,也被称为NeoPixel。它由RGB(红、绿、蓝)三种颜色的LED组成,并集成了控制电路和信号处理功能。每个WS2812B LED都有一个唯一的地址,并可以通过单个数据线进行串联连接。

WS2812B具有以下特点:

  • 高度可编程性:每个LED可以独立地设置颜色和亮度,因此可以实现各种动态效果和彩色变化。
  • 简单的控制接口:WS2812B使用单个数据线进行通信,通过发送特定的序列来控制每个LED的颜色和亮度。
  • 低功耗:WS2812B采用高效的LED驱动技术,具有较低的功耗,适合电池供电的应用。
  • 灵活的应用:WS2812B可广泛应用于室内装饰、灯光艺术、电子产品原型设计等领域,具有丰富的创意空间和应用场景。
  • 需要注意的是,WS2812B是商业产品,如果你有任何具体的问题或需求,请提供更多细节,我将尽力为您提供帮助。

具体参数如下(了解5V供电即可):

在这里插入图片描述

引脚图:

在这里插入图片描述

  • 当然,在使用的过程中,我们往往会使用不止一个LED灯,会将多个灯级联成灯带或者灯板,我们这次使用的就是灯板。

具体级联方式如下:

在这里插入图片描述

  • 其中DIN作为数据的输入端,每个LED需要24位数据控制,多出的数据会通过DOUT引脚传递给下一个LED,以此类推,当WS2812接收到280us以上的低电平时,数据被写入LED,灯的颜色改变。
    在这里插入图片描述

以下是数据的表示方法:

在这里插入图片描述

通常使用PWM波的方式驱动WS2812B,PWM的信号频率为800KHz,即一个数据表示的周期为1.25us

  • 数据0由一个TOH和TOL表示
  • 数据1由一个T1H和T1L表示
  • 0码和1码的差别就是在一个周期内高电平持续时间不同,我们粗略的认为0码高电平时间为周期的1/3,1码高电平时间为周期的2/3
  • 这样的话,只需要设置PWM信号频率为800KHz,当占空比为33%时,数据为0;当占空比为66%时,数据为1.
  • RSSET时间为280us以上的低电平,但我在其他的资料里面看到好像不需要那么久,24us即可,这个我们这里不管,当作24us即可。

在这里插入图片描述

在有多个led级联的情况下,先发送第一个led的数据,后第二个、第三个、以此类推。

CubeMX配置

其他部分的配置,我们这里不做介绍,重点展示tim1的PWM输出配置

在这里插入图片描述

使用定时器内部时钟,通道1PWM输出模式

在这里插入图片描述

设置时钟不分频,自动重装载值为125,则PWM波频率 = 100MHz / 125 = 800KHz,刚好符合WS2812B的数据写入频率。

在这里插入图片描述

开启定时器DMA请求,数据传输方向为内存到外设,内存地址递增模式。字长为32位。

代码实现

  1. RGB.c
#include "RGB.h"
#include "tim.h"
#include "stdlib.h"

/*Some Static Colors------------------------------*/
const RGB_Color_TypeDef RED      = {255,0,0};   //红色
const RGB_Color_TypeDef GREEN    = {0,255,0};   //绿色
const RGB_Color_TypeDef BLUE     = {0,0,255};   //深蓝色
const RGB_Color_TypeDef SKY      = {0,255,255};  //天蓝色
const RGB_Color_TypeDef MAGENTA  = {255,0,220};  //粉色
const RGB_Color_TypeDef YELLOW   = {128,216,0};  //黄色
const RGB_Color_TypeDef OEANGE   = {127,106,0};  //橘色
const RGB_Color_TypeDef BLACK    = {0,0,0};    //无颜色
const RGB_Color_TypeDef WHITE    = {255,255,255}; //白色

//将好看的颜色封装成数组,便于集中管理和访问
RGB_Color_TypeDef table[16] = 
{
   {254,67,101}, 
   {76,0,10},
   {249,15,173},
   {128,0,32},
   {158,46,36},
   {184,206,142},
   {227,23,13},
   {178,34,34},
   {255,99,71},
   {99,38,18},
   {255,97,0},
   {21,161,201},
   {56,94,15},
   {50,205,50},
   {160,32,240},
    {218,60,90}
};
 //这些是好看的颜色
const RGB_Color_TypeDef color1 = {254,67,101};
//const RGB_Color_TypeDef color2 = {76,0,10};
//const RGB_Color_TypeDef color3 = {249,15,173};
//const RGB_Color_TypeDef color4 = {128,0,32};
//const RGB_Color_TypeDef color5 = {158,46,36};
//const RGB_Color_TypeDef color6 = {184,206,142};
//const RGB_Color_TypeDef color7 = {227,23,13};
//const RGB_Color_TypeDef color8 = {178,34,34};
//const RGB_Color_TypeDef color9 = {255,99,71};
//const RGB_Color_TypeDef color10 ={99,38,18};
//const RGB_Color_TypeDef color11= {255,97,0};
//const RGB_Color_TypeDef color12= {21,161,201};
//const RGB_Color_TypeDef color13= {56,94,15};
//const RGB_Color_TypeDef color14= {50,205,50};
//const RGB_Color_TypeDef color15= {160,32,240};
//const RGB_Color_TypeDef color16= {218,60,90};

 
/*二维数组存放最终PWM输出数组,每一行24个数据代表一个LED,最后一行24个0用于复位*/
uint32_t Pixel_Buf[Pixel_NUM+1][24];       

/*
功能:最后一行装在24个0,输出24个周期占空比为0的PWM波,作为最后reset延时,这里总时长为24*1.25=37.5us > 24us(要求大于24us)
//如果出现无法复位的情况,只需要在增加数组Pixel_Buf[Pixel_NUM+1][24]的行数,并改写Reset_Load即可,这里不做演示了,
*/
static void Reset_Load(void)
{
	uint8_t i;
	for(i=0;i<24;i++)
	{
		Pixel_Buf[Pixel_NUM][i] = 0;
	}
}
 
/*
功能:发送数组Pixel_Buf[Pixel_NUM+1][24]内的数据,发送的数据被存储到定时器1通道1的CCR寄存器,用于控制PWM占空比
参数:(&htim1)定时器1,(TIM_CHANNEL_1)通道1,((uint32_t *)Pixel_Buf)待发送数组,
			(Pixel_NUM+1)*24)发送个数,数组行列相乘
*/
static  void RGB_SendArray(void)
{
	HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1, (uint32_t *)Pixel_Buf,(Pixel_NUM+1)*24);
}

/*
功能:设定单个RGB LED的颜色,把结构体中RGB的24BIT转换为0码和1码
参数:LedId为LED序号,Color:定义的颜色结构体
*/
 //刷新WS2812B灯板显示函数
static void RGB_Flush(void)
{
    Reset_Load();     //复位
    RGB_SendArray();  //发送数据
    
	  
}

void RGB_SetOne_Color(uint8_t LedId,RGB_Color_TypeDef Color)
{
	uint8_t i; 
	if(LedId > Pixel_NUM)return; //avoid overflow 防止写入ID大于LED总数
	//这里是对 Pixel_Buf[LedId][i]写入一个周期内高电平的持续时间(或者说时PWM的占空比寄存器CCR1),
	for(i=0;i<8;i++) Pixel_Buf[LedId][i]   = ( ((Color.G/5) & (1 << (7 -i)))? (CODE_1):CODE_0 );//数组某一行0~7转化存放G
	for(i=8;i<16;i++) Pixel_Buf[LedId][i]  = ( ((Color.R/5) & (1 << (15-i)))? (CODE_1):CODE_0 );//数组某一行8~15转化存放R
	for(i=16;i<24;i++) Pixel_Buf[LedId][i] = ( ((Color.B/5) & (1 << (23-i)))? (CODE_1):CODE_0 );//数组某一行16~23转化存放B
}

//调用RGB_SetOne_Color函数,完成对多个LED的颜色设置。
void RGB_SetMore_Color(uint8_t head, uint8_t heal,RGB_Color_TypeDef color)
{
    uint8_t i=0;
    for(i=head;i<=heal;i++)
    {
        RGB_SetOne_Color(i,color) ;
    }
}
 

 
//用来显示单个颜色的函数,只能从第一个开始显示,不好用
//void RGB_RED(uint16_t Pixel_Len)
//{
//	uint16_t i;
//	for(i=0;i<Pixel_Len;i++)//给对应个数LED写入红色
//	{
//		RGB_SetOne_Color(i,RED);
//	}
//}
// 


//灯管实现函数(完成本期效果的实现)
void RGB_Show_64(void)   
{
    RGB_SetMore_Color(0,63,BLACK);  //清空所有的LED数据
    RGB_SetMore_Color(0,rand()%8,table[rand()%16]);  //第一行随机个灯亮随机颜色
    RGB_SetMore_Color(8,rand()%8+8,table[rand()%16]);  //第二行。。。。以此类推
    RGB_SetMore_Color(16,rand()%8+16,table[rand()%16]);
    RGB_SetMore_Color(24,rand()%8+24,table[rand()%16]);
    RGB_SetMore_Color(32,rand()%8+32,table[rand()%16]);
    RGB_SetMore_Color(40,rand()%8+40,table[rand()%16]);
    RGB_SetMore_Color(48,rand()%8+48,table[rand()%16]);
    RGB_SetMore_Color(56,rand()%8+56,table[rand()%16]);
    RGB_Flush();      //刷新WS2812B的显示
}



  1. RGB.h
#ifndef __RGB_H__
#define __RGB_H__
 
#include "main.h"
 
//0码和1码的定义,设置的时CCR寄存器的值
//由于使用的思PWM输出模式1,计数值<CCR时,输出有效电平-高电平(CubeMX配置默认有效电平为高电平)
#define CODE_1       (84)       //1码定时器计数次数,控制占空比为84/125 = 66%
#define CODE_0       (42)       //0码定时器计数次数,控制占空比为42/125 = 33%
 
//单个LED的颜色控制结构体
typedef struct
{
	uint8_t R;
	uint8_t G;
	uint8_t B;
}RGB_Color_TypeDef;
 
#define Pixel_NUM 64  //LED数量宏定义,我们灯板上有64个,

static void Reset_Load(void); //该函数用于将数组最后24个数据变为0,代表RESET_code

    //发送最终数组
static void RGB_SendArray(void); 

static void RGB_Flush(void);  //刷新RGB显示

 
void RGB_SetOne_Color(uint8_t LedId,RGB_Color_TypeDef Color);//给一个LED装载24个颜色数据码(0码和1码)
     

//void RGB_RED(uint16_t Pixel_Len);  //显示红灯

//控制多个LED显示相同的颜色
void RGB_SetMore_Color(uint8_t head, uint8_t heal,RGB_Color_TypeDef color);


void RGB_Show_64(void); //RGB写入函数
 
#endif


  1. main.c
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "tim.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "RGB.h"				//包含RGB.h头文件
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_TIM1_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
       RGB_Show_64();				//调用RGB灯板显示函数
      HAL_Delay(200);				//延时200ms,
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

以上就是本期的所有内容。

;