WS2812B智能外控集成LED灯驱动编程
WS2812B是一个集控制电路与发光电路于一体的智能外控LED光源,同时也是一款高性能的LED驱动器,具有高效率、低功耗、稳定性能等特点,广泛应用于各种照明和显示设备。主要应用领域有:消费性电子产品领域、LED灯饰亮化领域、 电脑及周边设备\游戏设备\各种电器设备领域。本文采用ESP-IDF v5.1对ESP32S3进行WS2812B驱动控制。
1、WS2812B应用场景
WS2812B是一个集控制电路与发光电路于一体的智能外控LED光源,同时也是一款高性能的LED驱动器,具有高效率、低功耗、稳定性能等特点,广泛应用于各种照明和显示设备。例如下面列举出来的是一些可能的、常见的应用场景:
①、LED灯带和LED模块:用来制作全彩色LED灯带和LED模块,实现各种炫彩的灯光效果,如游戏键盘。
②、室内外照明:因其高效率、低功耗和稳定性能,非常适合用于室内外照明设备,如路灯、景观灯、室内照明灯具等。
③、商业照明和广告牌:全彩色显示和易于控制的特性使其成为商业照明和广告牌的理想选择。
④、艺术装置和舞台表演:通过编程创造出的各种独特的灯光效果,为舞台表演增添更多的视觉冲击力。
⑤、智能家居控制系统:与智能家居控制系统集成,实现灯光的智能控制。
WS2812B循环闪烁控制效果图
2、WS2812B驱动控制原理
①、模块接线说明
LED具有低电压驱动、环保节能、亮度高、散射角度大、一致性好超、低功率及超长寿命等优点。将控制电路集成于LED上面,电路变得更加简单,体积小,安装更加简便。
如下图所示,WS2812B灯珠模块一共有4根管脚:
VDD:电源供电管脚
VSS:信号接地和电源接地引脚
DOUT:控制数据信号输出引脚
DIN:控制数据信号输入引脚
WS2812B单灯珠模块示意图
在实际的项目工程开发中,往往并不是一个WS2812B灯珠进行控制,因此需要掌握了解多灯珠控制接线的方法。
如下图所示可知,如要实现多灯珠控制,需要采用串联的方式将各个独立的灯珠模块进行联通。即上一个灯珠的DOUT端引脚接入下一个灯珠的DIN引脚。最后一个灯珠的DOUT引脚不接,而第一个灯珠的DIN引脚接到控制器的数据输出控制端。
WS2812B多灯珠串联接线示意图
②、通信编码数据解析
WS2812B的数据协议采用单线归零码的通讯方式,像素点在上电复位以后,DIN端接受从控制器传输过来的数据,首先送过来的24bit数据被第一个像素点提取后,送到像素点内部的数据锁存器,剩余的数据经过内部整形处理电路整形放大后通过DO端口开始转发输出给下一个级联的像素点,每经过一个像素点的传输,信号减少24bit。像素点采用自动整形转发技术,使得该像素点的级联个数不受信号传送的限制,仅受限信号传输速度要求。
WS2812B的一个数据帧结构为24bit,即3个字节,其中 0~7 bit为蓝色像素字节,8~15bit为红色像素字节,16~23bit为绿色像素字节。
数据发送时,从高位到低位传输到WS2812B灯珠模块中。通信数据是MSB的格式,高位先发原则。
如果要使WS2812B显示如下图所示的颜色数据,那么需要将发送的数据按照进行组合。
//高位先发原则,按照GRB的顺序组合发送数据
//在程序中可以采用如下两种常见的数据定义形式
//方式1
unsigned char color[3] = {155, 23, 186}; //GRB
//方式2
typedef struct ws2812b{
unsigned char green; //G
unsigned char red; //R
unsigned char blue; //B
}ws2812b_t;
ws2812b_t data = {0};
data.green = 155;
data.red = 23;
data.blue = 186;
从Windows的画图软件中随意选择一种颜色,然后查看其RGB颜色编码方式,后续的程序代码中,可以直接使用这些像素数据。
如下图所示为多颗WS2812B串联灯珠通信的抽象数据传输图,可见数据全部从D1号灯珠的DIN引脚输入,然后第一颗灯珠只提取第一个24位(3个字节)的数据,其余数据全部通过D1号灯珠的DO引脚输出到下一颗灯珠,每颗灯珠都只提取接收到的前3个字节数据后,发送到下一个灯珠,以此类推。
如下图所示位WS2812B官方手册对于0码和1码的说明
0码:高电平时间较短(约350ns),低电平时间较长(约850ns)
1码:高电平时间较长(约850ns),低电平时间较短(约350ns)
从时序图可以看到,WS2812B通信的0码和1码采用周期相同,占空比不同的方波来表示的。
3、WS2812B通信驱动编程
①、通信编程思路
(1)、GPIO引脚初始化
(2)、实现ws2812b的数据电平0
(3)、实现ws2812b的数据电平1
(4)、实现ws2812b的复位信号
(5)、实现写单字节数据(8位)
(6)、实现写3个字节数据(24位RGB)
上面的步骤(2)、(3)、(4)中,需要严格控制延时的时间!!!
上面部分的知识体系是通用的,适用于WS2812B在各种平台上进行开发。当前手上只有一块ESP32S3,所以下面的程序代码是使用这块开发板进行开发适配的,如果需要在其它平台(GD32、STM32、CH32、CW32或嵌入式Linux等)上实现,那么可以根据上面的驱动编程思路进行简单的编写即可实现。
根据开发板WS2812B模块部分的原理图可知,WS2812B的数据输入DIN引脚为GPIO48。这里的GPIO输出引脚选择配置,需要根据各自的开发板实际情况进行设置。
下面所示的代码使用的是ESP-IDF v5.1框架平台开发的。
②、main.c
#include <stdio.h>
#include <unistd.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "driver/rmt_tx.h"
#include "ws2812b.h"
int app_main(void)
{
printf("ws2812B RMT Display starting...\n");
ws2812b_rmt_init();
ws2812b_t color = {0};
while(1)
{
color.green = 255;
color.red = 25;
color.blue = 250;
ws2812b_rmt_show_color(color);
vTaskDelay(500/portTICK_PERIOD_MS);
color.green = 24;
color.red = 255;
color.blue = 234;
ws2812b_rmt_show_color(color);
vTaskDelay(500/portTICK_PERIOD_MS);
color.green = 212;
color.red = 255;
color.blue = 42;
ws2812b_rmt_show_color(color);
vTaskDelay(500/portTICK_PERIOD_MS);
}
return 0;
}
③、ws2812b.h
#ifndef __WS2812B_H
#define __WS2812B_H
#include "stdio.h"
#include "unistd.h"
#include "driver/gpio.h"
#include "driver/rmt_tx.h"
typedef struct ws2812b{
unsigned char green; //G
unsigned char red; //R
unsigned char blue; //B
}ws2812b_t;
extern rmt_channel_handle_t ws2812b_tx;
extern rmt_tx_channel_config_t ws2812b_tx_config;
extern rmt_encoder_handle_t ws2812b_encode;
extern rmt_bytes_encoder_config_t ws2812b_byte_encode;
extern rmt_transmit_config_t trans_config;
int ws2812b_rmt_init(void);
void ws2812b_rmt_show_color(ws2812b_t color);
#endif
④、ws2812b.c
#include "ws2812b.h"
rmt_channel_handle_t ws2812b_tx = NULL;
rmt_tx_channel_config_t ws2812b_tx_config = {0};
rmt_encoder_handle_t ws2812b_encode = NULL;
rmt_bytes_encoder_config_t ws2812b_byte_encode = {0};
rmt_transmit_config_t trans_config = {0};
/**
* @brief WS2812B RMT控制初始化
* @param None
* @retval None
*/
int ws2812b_rmt_init(void)
{
gpio_config_t gpio48;
gpio48.intr_type = GPIO_INTR_DISABLE;
gpio48.mode = GPIO_MODE_OUTPUT;
gpio48.pin_bit_mask = (1ULL << GPIO_NUM_48); // 设置 GPIO48
gpio48.pull_down_en = GPIO_PULLDOWN_ENABLE;
gpio48.pull_up_en = GPIO_PULLUP_DISABLE;
if(gpio_config(&gpio48) != ESP_OK)
{
printf("配置GPIO引脚失败\n");
return -1;
}
gpio_set_level(GPIO_NUM_48, 0);
ws2812b_tx_config.clk_src = RMT_CLK_SRC_DEFAULT;
ws2812b_tx_config.gpio_num = GPIO_NUM_48;
ws2812b_tx_config.mem_block_symbols = 64;
ws2812b_tx_config.resolution_hz = 10 * 1000 * 1000;
ws2812b_tx_config.trans_queue_depth = 4;
ws2812b_tx_config.flags.invert_out = false;
ws2812b_tx_config.flags.with_dma = false;
ESP_ERROR_CHECK(rmt_new_tx_channel(&ws2812b_tx_config, &ws2812b_tx));
//rmt_encoder_handle_t ws2812b_encode = NULL;
ws2812b_byte_encode.bit0.level0 = 1;
ws2812b_byte_encode.bit0.duration0 = 3; //0.3us
ws2812b_byte_encode.bit0.level1 = 0;
ws2812b_byte_encode.bit0.duration1 = 9; //0.9us
ws2812b_byte_encode.bit1.level0 = 1;
ws2812b_byte_encode.bit1.duration0 = 9; //0.9us
ws2812b_byte_encode.bit1.level1 = 0;
ws2812b_byte_encode.bit1.duration1 = 3; //0.3us
ws2812b_byte_encode.flags.msb_first = 1; //大端先发
rmt_new_bytes_encoder(&ws2812b_byte_encode, &ws2812b_encode);
ESP_ERROR_CHECK(rmt_enable(ws2812b_tx));
trans_config.loop_count = 0; //不循环发送
ws2812b_t color;
color.green = 155;
color.red = 23;
color.blue = 186;
rmt_transmit(ws2812b_tx, ws2812b_encode, &color, sizeof(color), &trans_config);
rmt_tx_wait_all_done(ws2812b_tx, 0xFF);
return 0;
}
/**
* @brief WS2812B RMT控制显示颜色
* @param color :颜色RGB数据
* @retval None
*/
void ws2812b_rmt_show_color(ws2812b_t color)
{
extern rmt_channel_handle_t ws2812b_tx;
extern rmt_encoder_handle_t ws2812b_encode;
extern rmt_transmit_config_t trans_config;
rmt_transmit(ws2812b_tx, ws2812b_encode, &color, sizeof(color), &trans_config);
rmt_tx_wait_all_done(ws2812b_tx, 0xFF);
gpio_set_level(GPIO_NUM_48, 0);
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)