WS2812驱动

这里只是把数据手册中使用到的重要部分摘取出来,完整版请看ws2812,这只是这个项目中的一个驱动代码,后续继续更新项目代码。项目实现功能请先看,智能门锁专栏 / 智能门锁功能实现这篇文章,里面介绍了实现的功能,后续将更新详细项目介绍

一. 简介

  • 每个像素点的三基色颜色可实现256级亮度显示,完成16777216种颜色的全真色彩显示。

  • 串行级联接口,能通过一根信号线完成数据的接收与解码。

  • 数据协议采用单线归零码的通讯方式,像素点在上电复位以后,DIN端接受从控制器传输过来的数据,首先

    送过来的24bit数据被第一个像素点提取后,送到像素点内部的数据锁存器,剩余的数据经过内部整形处理电路整

    形放大后通过DO端口开始转发输出给下一个级联的像素点,每经过一个像素点的传输,信号减少24bit;像素点

    采用自动整形转发技术,使得该像素点的级联个数不受信号传送的限制,仅受限信号传输速度要求;

  • 引脚说明

    序号 符号 管脚名 功能描述
    1 DO 数据输出 控制数据信号输出
    2 GND 地 信号接地和电源接地
    3 DI 数据输入 控制数据信号输入
    4 VDD 电源 供电管脚

二. 关键参数

  • 时序格式

    时序表名称
    Timeline name
    最小值
    Min.
    典型值
    Typical value
    最大值
    Max.
    单位
    Unit
    T/码元周期 0.89 --- --- μs
    TOH/0码, 高电平时间 0.2 0.295 0.35 μs
    TOL/0码, 低电平时间 0.55 0.595 1.2 μs
    T1H/1码, 高电平时间 0.55 0.595 1.2 μs
    T1L/1码, 低电平时间 0.2 0.295 0.35 μs
    TRST/Reset码, 低电平时间 80 --- --- μs

    注意 1. 写程序时,码元周期最低要求为 0.9us;

    2. 0 码、1 码的高电平时间需按照上表的规定范围,0 码、1 码的低电平时间要求小于 15us;

  • 时序波形图

    image-20250815103625604

  • 数据传输方式

image-20250815103656044

  • esp32可以使用RMT或SPI控制时序波形。后面使用RMT控制

三. 代码

这里用两种方法实现了WS2812的时序,一种是用官方组件库,另一种是调用库函数实现。因为正好也是学习一下esp32,所以就把两种方法都写了一下。官方组件库其实也是调用的库函数,只是把它又封装一层,省去自己写驱动的过程,直接使用封装好的函数即可。

组件库地址espressif/led_strip • v3.0.1~1 • ESP Component Registry

image-20250815145020473

把这段代码复制到vscode的esp-idf插件的终端,回车,就会自动下载组件。

image-20250815145148964

硬件抽象层

HAL_rmt.c

#include "HAL_rmt.h"

#if LED_STRIP_INIT

/* 全部led灯带句柄 */
static led_strip_handle_t g_xled_strip_handler;

/**
 * @brief 初始化rmt
 *
 * @param 无
 *
 * @return 无
 */
void rmt_Init(void)
{
    /* 配置WS2812灯带 */
    led_strip_config_t led_strip_config = 
    {
        .color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB,/* 颜色格式RGB    */
        .flags.invert_out = false,                                  /* 输出不反转     */
        .led_model = LED_MODEL_WS2812,                              /* 灯的类型WS2812 */
        .max_leds = RMT_LED_NUM,                                    /* 最大灯珠数量12 */
        .strip_gpio_num = RMT_DATA_GPIO,                            /* gpio引脚       */
    };
    /* 配置 rmt 通道 */
    led_strip_rmt_config_t led_strip_rmt_config = 
    {
        .clk_src = RMT_CLK_SRC_DEFAULT,                             /* 默认时钟源APB  */
        .flags.with_dma = false,                                    /* 不使用DMA      */
        .mem_block_symbols = 64,                                    /* 最大RMT符号数  */
        .resolution_hz = RMT_TX_CHANNEL_RESOLUTION,                 /* 分辨率10MHz    */
    };
    led_strip_new_rmt_device(&led_strip_config,
                             &led_strip_rmt_config,
                             &g_xled_strip_handler);                /* 初始化rmt通道  */
}

/**
 * @brief rmt 发送数据
 *
 * @param rmt_tx_buff: 发送缓冲区
 * @param rmt_tx_buff_len: 发送缓冲区大小
 *
 * @return 无
 */
void rmt_send_data(uint8_t *rmt_tx_buff,uint8_t rmt_tx_buff_len)
{
    uint8_t i = 0;
    for(i = 0; i < rmt_tx_buff_len / 3; i++)
    {
        /* 设置灯带颜色 */
        led_strip_set_pixel(g_xled_strip_handler, 
                            i,
                            rmt_tx_buff[i * 3], 
                            rmt_tx_buff[i * 3 + 1], 
                            rmt_tx_buff[i * 3 + 2]);
    }
    /* 刷新缓存 */
    led_strip_refresh(g_xled_strip_handler);
}

#elif RMT_INIT

/* RMT 通道句柄  */
static rmt_channel_handle_t g_xrmt_channel_handler;
/* RMT 编码器句柄*/
static rmt_encoder_handle_t g_xrmt_encoder_handler;

/**
 * @brief 初始化rmt
 *
 * @param 无
 *
 * @return 无
 */
void rmt_Init(void)
{
    /* 创建RMT通道        */
    rmt_tx_channel_config_t rmt_tx_channel_config = 
    {
        .clk_src = RMT_CLK_SRC_DEFAULT,                          /* 使用默认时钟源  */
        .gpio_num = RMT_DATA_GPIO,                               /* GPIO引脚       */ 
        .mem_block_symbols = 64,                                 /* RMT符号最大数量 */ 
        .resolution_hz = RMT_TX_CHANNEL_RESOLUTION,              /* 分辨率10MHz     */
        .trans_queue_depth = 6,                                  /* 队列深度        */
    };
    rmt_new_tx_channel(&rmt_tx_channel_config,
                       &g_xrmt_channel_handler);

    /* 创建 rmt字节编码器 */
    rmt_bytes_encoder_config_t rmt_byter_encoder_config = 
    {
        .bit0 =                                                       /* 发送 0     */
        {
            .level0 = 1,                                              /* 首先高电平 */
            .duration0 = 0.3 * RMT_TX_CHANNEL_RESOLUTION / 1000000,   /* 时间 0.3us */
            .level1 = 0,                                              /* 再低电平  */
            .duration1 = 0.9 * RMT_TX_CHANNEL_RESOLUTION / 1000000,   /* 时间 0.9us */
        },
        .bit1 = 
        {
            .level0 = 1,                                              /* 首先高电平 */
            .duration0 = 0.9 * RMT_TX_CHANNEL_RESOLUTION / 1000000,   /* 时间 0.9us */
            .level1 = 0,                                              /* 再低电平  */
            .duration1 = 0.3 * RMT_TX_CHANNEL_RESOLUTION / 1000000,   /* 时间 0.3us */
        },
        .flags.msb_first = 1,                                         /* 高位先行   */
    };
    rmt_new_bytes_encoder(&rmt_byter_encoder_config,
                          &g_xrmt_encoder_handler);

    /* 使能RMT */
    rmt_enable(g_xrmt_channel_handler);
}

/**
 * @brief rmt 发送数据 
 *
 * @param rmt_tx_buff: 发送缓冲区
 * @param rmt_tx_buff_len: 发送缓冲区大小
 *
 * @return 无
 */
void rmt_send_data(uint8_t *rmt_tx_buff,uint8_t rmt_tx_buff_len)
{
    rmt_transmit_config_t rmt_transmit_config = 
    {
        .loop_count = false,                                        /* 不使用循环发送   */
        .flags = 
        {
            .eot_level = 0,                                         /* 结束低电平       */
            .queue_nonblocking = 1,                                 /* 队列满返回不阻塞 */
        }
    };

    rmt_transmit(g_xrmt_channel_handler,
                 g_xrmt_encoder_handler,
                 rmt_tx_buff,
                 rmt_tx_buff_len,
                 &rmt_transmit_config);                             /* 发送数据         */
    
    rmt_tx_wait_all_done(g_xrmt_channel_handler,
                         portMAX_DELAY);                            /* 等待发送完成      */
}

#endif

HAL_rmt.h

#ifndef HAL_RMT_H
#define HAL_RMT_H

#define RMT_INIT                                                                 0
#define LED_STRIP_INIT                                                           1

#include <stdio.h>
#include <string.h>

#include "freertos/FreeRTOS.h"
#include "driver/gpio.h"

#if LED_STRIP_INIT

#include "../../managed_components/espressif__led_strip/include/led_strip.h"

#elif RMT_INIT

#include "driver/rmt_tx.h"
#include "driver/rmt_encoder.h"
#include "esp_err.h"

#endif


#define RMT_TX_CHANNEL_RESOLUTION  10000000 /* 发送通道分辨率 10Mhz 0.1us */

/* rmt GPIO 引脚 */
#define RMT_DATA_GPIO                                           GPIO_NUM_6
/* 灯珠数量 */ 
#define RMT_LED_NUM                                                     12

/**
 * @brief 初始化rmt
 *
 * @param 无
 *
 * @return 无
 */
void rmt_Init(void);

/**
 * @brief rmt 发送数据 
 *
 * @param rmt_tx_buff: 发送缓冲区
 * @param rmt_tx_buff_len: 发送缓冲区大小
 *
 * @return 无
 */
void rmt_send_data(uint8_t *rmt_tx_buff,uint8_t rmt_tx_buff_len);

#endif

驱动层

Dri_WS2812.c

#include "Dri_WS2812.h"

/* led灯颜色 */
uint8_t color_red[3]   = {255, 0  , 0  };
uint8_t color_green[3] = {0  , 255, 0  };
uint8_t color_bule[3]  = {0  , 0  , 255};
uint8_t color_white[3] = {255, 255, 255};
uint8_t color_black[3] = {0  , 0  , 0  };

/* RMT 发送缓冲区 */
static uint8_t g_rmt_tx_buff[36];

/**
 * @brief 初始化WS2812
 *
 * @param 无
 *
 * @return 无
 */
void WS2812_Init(void)
{
    WS2812_set_led_strip_allcolor(color_black);
}

/**
 * @brief 设置WS2812灯带颜色
 *
 * @param led_num: 灯珠号
 * @param color_data: 颜色
 *
 * @return 无
 */
void WS2812_set_led_strip_color(uint8_t led_num, uint8_t color_data[])
{
    memset(&g_rmt_tx_buff[led_num * 3], 0, 3);            /* 清空对应数组数据 */
    memcpy(&g_rmt_tx_buff[led_num * 3], color_data, 3);   /* 设置灯珠颜色     */
    
    /* rmt 发送数据 */
    rmt_send_data(g_rmt_tx_buff,sizeof(g_rmt_tx_buff));
}

/**
 * @brief 设置WS2812灯带全部颜色
 *
 * @param color_data: 颜色
 *
 * @return 无
 */
void WS2812_set_led_strip_allcolor(uint8_t color_data[])
{
    uint8_t i = 0;

    memset(g_rmt_tx_buff,0,sizeof(g_rmt_tx_buff));       /* 清空数组         */

    for(i = 0; i < WS2812_LED_NUM; i++)
    {
        memcpy(&g_rmt_tx_buff[i * 3], color_data, 3);    /* 设置灯珠颜色     */
    }

    /* rmt 发送数据 */
    rmt_send_data(g_rmt_tx_buff,sizeof(g_rmt_tx_buff));
}


Dri_WS2812.h

#ifndef HAL_WS2812_H
#define HAL_WS2812_H

#include <stdio.h>
#include <string.h>

#include "freertos/FreeRTOS.h"

#include "HAL_rmt.h"
#include "Dri_SC12B.h"

/* 灯珠数量 */ 
#define WS2812_LED_NUM                    12

/* led灯颜色 */
extern uint8_t color_red[3];
extern uint8_t color_green[3];
extern uint8_t color_bule[3];
extern uint8_t color_white[3];
extern uint8_t color_black[3];

/**
 * @brief 初始化WS2812
 *
 * @param 无
 *
 * @return 无
 */
void WS2812_Init(void);

/**
 * @brief 设置WS2812灯带颜色
 *
 * @param led_num: 灯珠号
 * @param color_data: 颜色
 *
 * @return 无
 */
void WS2812_set_led_strip_color(uint8_t led_num,uint8_t color_data[]);

/**
 * @brief 设置WS2812灯带全部颜色
 *
 * @param color_data: 颜色
 *
 * @return 无
 */
void WS2812_set_led_strip_allcolor(uint8_t color_data[]);

#endif

posted @ 2025-08-15 10:52  2666666  阅读(416)  评论(0)    收藏  举报