ESP-ADF外设子系统深度解析:esp_peripherals组件架构与核心设计(显示输出类外设之WS2812)

ESP-ADF外设子系统深度解析:esp_peripherals组件架构与核心设计(显示输出类外设之WS2812)

版本信息: ESP-ADF v2.7-65-gcf908721

简介

本文档详细分析ESP-ADF中的显示/输出类外设实现机制,包括LCD、LED、WS2812、IS31FL3216和AW2013等外设的设计模式、接口规范、初始化流程和事件处理机制。ESP-ADF显示/输出类外设基于统一的外设框架设计,通过事件驱动模型实现显示和指示功能,为音频应用提供了丰富的视觉反馈能力和用户界面支持。

模块概述

功能定义

ESP-ADF显示/输出类外设主要负责提供视觉反馈和用户界面显示功能,将应用程序的状态和数据以可视化方式呈现给用户。主要功能包括:

  • 状态指示(LED指示灯、状态灯等)
  • 用户界面显示(LCD屏幕显示文本、图形等)
  • 视觉效果(WS2812彩色灯带、IS31FL3216和AW2013 LED矩阵等)
  • 音频可视化(音频频谱显示、节奏灯光效果等)

架构位置

显示/输出类外设是ESP-ADF外设子系统的重要组成部分,位于硬件驱动层和应用层之间:

应用程序
ESP外设子系统
显示/输出类外设
LCD外设
LED外设
WS2812外设
IS31FL3216外设
AW2013外设
SPI/I2C驱动
GPIO驱动
RMT驱动
I2C驱动
I2C驱动

核心特性

  • 多种显示设备支持:支持LCD、LED、WS2812、IS31FL3216和AW2013等多种显示和指示设备
  • 统一控制接口:所有显示/输出外设使用统一的初始化和控制接口
  • 丰富的显示效果:支持开关控制、亮度调节、颜色变化、动画效果等多种显示功能
  • 与音频处理集成:可与音频处理模块协同工作,实现音频可视化效果
  • 低功耗设计:支持设备休眠和唤醒管理,优化功耗表现
  • 事件驱动模型:通过事件机制实现显示状态变化的通知和处理

WS2812外设

WS2812外设概述

WS2812是一种集成了控制电路与RGB发光电路于一体的智能外部控制LED光源。在ESP-ADF框架中,WS2812外设提供了对WS2812 LED灯带的控制功能。

与其他外设不同,WS2812外设只有一个实现层次,即外设驱动层,所有功能都集成在一个文件中:

  • 头文件:components/esp_peripherals/include/periph_ws2812.h
  • 实现文件:components/esp_peripherals/periph_ws2812.c

该外设驱动同时负责ESP-ADF外设系统集成、RMT通道配置、数据传输和LED控制等全部功能。
需要注意的是,WS2812外设依赖于ESP-IDF的RMT模块来生成精确的时序信号并进行数据发送。

WS2812外设层次架构图
外设驱动 periph_ws2812.c
调用
调用
设置
设置
设置
调用
返回
调用
配置
注册
调用
调用
启动
调用
控制
_ws2812_init
periph_ws2812_init
_ws2812_run
_ws2812_destroy
ws2812_init_rmt_channel
ESP_OK
ws2812_set_colors
RMT通道设置
RMT中断
ws2812_data_copy
rmt_write_items
ESP-ADF应用
esp_periph_create
定时器
ws2812_timer_handler
LED状态变化
功能和特点
  1. 集成RGB控制:WS2812集成了红、绿、蓝三色LED和控制电路,可以实现1670万色的全彩显示
  2. 级联控制:支持多个WS2812 LED级联,通过单一GPIO引脚控制整条灯带
  3. 多种显示模式:支持单色显示、闪烁效果和渐变效果
  4. 精确时序控制:使用RMT(Remote Control Transceiver)模块生成精确的时序信号
  5. 低功耗:支持PWM调光,可以实现不同亮度的控制
通信协议和时序要求

WS2812采用单线通信协议,通过精确的时序控制来传输数据:

  1. 数据格式:每个LED需要24位数据(RGB各8位)

  2. 时序要求

    • 位"0"由350ns高电平+900ns低电平表示
    • 位"1"由900ns高电平+350ns低电平表示
    • 复位码由至少50μs的低电平表示
  3. 数据传输顺序

    • 颜色顺序为GRB(绿、红、蓝)
    • 数据从高位到低位传输
    • 数据按LED的级联顺序传输

WS2812外设API和数据结构

外设层API

源文件components/esp_peripherals/include/periph_ws2812.hcomponents/esp_peripherals/periph_ws2812.c

公共API
// 初始化WS2812外设
esp_periph_handle_t periph_ws2812_init(periph_ws2812_cfg_t *config);

// 控制WS2812外设
esp_err_t periph_ws2812_control(esp_periph_handle_t periph, periph_ws2812_ctrl_cfg_t *control_cfg, void *ctx);

// 停止WS2812外设
esp_err_t periph_ws2812_stop(esp_periph_handle_t periph);
配置结构体
  1. 初始化配置结构体
typedef struct {
    int      gpio_num;     // WS2812连接的GPIO引脚号
    int      led_num;      // WS2812 LED的数量
} periph_ws2812_cfg_t;
  1. 控制配置结构体
typedef struct periph_ws2812_ctrl_cfg {
    periph_rgb_value         color;          // RGB颜色值
    uint32_t                 time_on_ms;     // 点亮时间(毫秒),建议最小100ms
    uint32_t                 time_off_ms;    // 熄灭时间(毫秒),建议最小100ms
    uint32_t                 loop;           // 循环次数
    periph_ws2812_mode_t     mode;           // 模式(单色、闪烁或渐变)
} periph_ws2812_ctrl_cfg_t;
  1. 工作模式枚举
typedef enum {
    PERIPH_WS2812_BLINK,    // 闪烁模式
    PERIPH_WS2812_FADE,     // 渐变模式
    PERIPH_WS2812_ONE,      // 单色模式
} periph_ws2812_mode_t;
  1. RGB颜色值
typedef uint32_t periph_rgb_value;

// 颜色值辅助宏
#define make_rgb_value(x, y, z)  (((int)(z) << 16) + ((int)(y) << 8 )+ (x))

// 预定义颜色
#define LED2812_COLOR_BLACK   make_rgb_value(0,   0,  0)
#define LED2812_COLOR_BLUE    make_rgb_value(0,   0,   255)
#define LED2812_COLOR_GREEN   make_rgb_value(0,   255, 0)
#define LED2812_COLOR_CYAN    make_rgb_value(0,   255, 255)
#define LED2812_COLOR_RED     make_rgb_value(255, 0,   0)
#define LED2812_COLOR_PURPLE  make_rgb_value(255, 0,   255)
#define LED2812_COLOR_YELLOW  make_rgb_value(255, 255, 0)
#define LED2812_COLOR_WHITE   make_rgb_value(255, 255, 255)
#define LED2812_COLOR_ORANGE  make_rgb_value(255, 165, 0)
内部数据结构

WS2812外设内部使用以下数据结构:

  1. RGB值联合体
typedef union {
    struct __attribute__ ((packed)) {
        uint8_t r, g, b;
    };
    uint32_t num;
} rgb_value;
  1. WS2812状态结构体
typedef struct {
    periph_rgb_value         color;          // 颜色值
    periph_ws2812_mode_t     mode;           // 工作模式
    uint32_t                 time_on_ms;     // 点亮时间
    uint32_t                 time_off_ms;    // 熄灭时间
    long long                tick;           // 时间戳
    uint32_t                 loop;           // 循环次数
    bool                     is_on;          // 是否点亮
    bool                     is_set;         // 是否已设置
} periph_ws2812_state_t;
  1. WS2812处理结构体
typedef struct {
    uint32_t                 pos;            // 当前位置
    uint32_t                 half;           // 半值
    uint8_t                  *buffer;        // 数据缓冲区
} periph_ws2812_process_t;
  1. WS2812主结构体
typedef struct periph_ws2812 {
    periph_rgb_value          *color;        // 颜色数组
    uint32_t                  led_num;       // LED数量
    TimerHandle_t             timer;         // 定时器句柄
    xSemaphoreHandle          sem;           // 信号量
    intr_handle_t             rmt_intr_handle; // RMT中断句柄
    periph_ws2812_state_t     *state;        // 状态数组
    periph_ws2812_process_t   process;       // 处理结构体
} periph_ws2812_t;

WS2812外设配置选项

  • gpio_num: 指定WS2812连接的GPIO引脚号
  • led_num: 指定WS2812 LED的数量
控制选项
  • color: RGB颜色值,可以使用预定义颜色或通过make_rgb_value宏创建
  • time_on_ms: 点亮时间(毫秒),建议最小设置为100ms
  • time_off_ms: 熄灭时间(毫秒),建议最小设置为100ms
  • loop: 循环次数,设置为0表示不执行任何操作
  • mode: 工作模式,包括单色模式、闪烁模式和渐变模式

WS2812外设初始化流程

WS2812外设的初始化涉及到ESP-ADF外设框架和RMT驱动的配置。下面详细分析初始化过程中的调用链和执行流程。

外设初始化入口函数(periph_ws2812.c)

WS2812外设初始化的入口是periph_ws2812_init函数(位于periph_ws2812.c),该函数完成以下任务:

  1. 创建外设句柄:调用esp_periph_create函数创建外设句柄
  2. 分配内部数据结构:分配periph_ws2812_t结构体内存
  3. 初始化参数:创建信号量,分配颜色和状态数组
  4. 配置RMT通道:调用ws2812_init_rmt_channel配置RMT通道
  5. 注册回调函数:设置初始化、运行和销毁回调函数
// 文件:components/esp_peripherals/periph_ws2812.c
esp_periph_handle_t periph_ws2812_init(periph_ws2812_cfg_t *config)
{
    // 1. 创建外设句柄
    esp_periph_handle_t periph = esp_periph_create(PERIPH_ID_WS2812, "periph_ws2812");
    AUDIO_MEM_CHECK(TAG, periph, return NULL);
    
    // 2. 分配内部数据结构
    periph_ws2812_t *periph_ws2812 = audio_calloc(1, sizeof(periph_ws2812_t));
    AUDIO_MEM_CHECK(TAG, periph_ws2812, {
        esp_periph_destroy(periph);
        return NULL;
    });
    
    // 3. 初始化参数
    periph_ws2812->led_num = config->led_num;
    periph_ws2812->sem = xSemaphoreCreateBinary();
    AUDIO_MEM_CHECK(TAG, periph_ws2812->sem, {
        audio_free(periph_ws2812);
        esp_periph_destroy(periph);
        return NULL;
    });
    
    // 分配颜色数组
    periph_ws2812->color = audio_malloc(sizeof(periph_rgb_value) * periph_ws2812->led_num);
    AUDIO_MEM_CHECK(TAG, periph_ws2812->color, {
        vSemaphoreDelete(periph_ws2812->sem);
        audio_free(periph_ws2812);
        esp_periph_destroy(periph);
        return NULL;
    });
    
    // 初始化颜色为黑色
    for (int i = 0; i < periph_ws2812->led_num; i++) {
        periph_ws2812->color[i] = LED2812_COLOR_BLACK;
    }
    
    // 分配状态数组
    periph_ws2812->state = audio_malloc(sizeof(periph_ws2812_state_t) * (periph_ws2812->led_num));
    AUDIO_MEM_CHECK(TAG, periph_ws2812->state, {
        audio_free(periph_ws2812->color);
        vSemaphoreDelete(periph_ws2812->sem);
        audio_free(periph_ws2812);
        esp_periph_destroy(periph);
        return NULL;
    });
    
    // 4. 配置RMT通道
    esp_err_t ret = ws2812_init_rmt_channel(RMTCHANNEL, (gpio_num_t)config->gpio_num);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "ws2812 init rmt channel failed");
        audio_free(periph_ws2812->state);
        audio_free(periph_ws2812->color);
        vSemaphoreDelete(periph_ws2812->sem);
        audio_free(periph_ws2812);
        esp_periph_destroy(periph);
        return NULL;
    }
    
    // 设置外设数据
    esp_periph_set_data(periph, periph_ws2812);
    
    // 注册发送完成回调
    rmt_register_tx_end_callback(rmt_handle_tx_end, (void *)periph_ws2812);
    
    // 5. 注册回调函数
    esp_periph_set_function(periph, _ws2812_init, _ws2812_run, _ws2812_destroy);
    
    // 设置初始颜色
    ws2812_set_colors(periph_ws2812);
    
    return periph;
}

当外设被添加到外设集合并启动时,会调用_ws2812_init函数(位于periph_ws2812.c),该函数很简单,只返回ESP_OK,因为大部分初始化工作已经在periph_ws2812_init中完成:

// 文件:components/esp_peripherals/periph_ws2812.c
static esp_err_t _ws2812_init(esp_periph_handle_t self)
{
    return ESP_OK;
}
RMT通道初始化过程

WS2812外设初始化过程中的一个关键步骤是配置RMT通道。这是通过ws2812_init_rmt_channel函数(位于periph_ws2812.c)完成的:

// 文件:components/esp_peripherals/periph_ws2812.c
static esp_err_t ws2812_init_rmt_channel(int rmt_channel, int gpio_num)
{
    // 配置RMT发送通道结构体
    rmt_config_t rmt_tx;
    rmt_tx.channel = rmt_channel;      // 设置RMT通道号
    rmt_tx.gpio_num = gpio_num;        // 设置GPIO引脚号
    rmt_tx.mem_block_num = 1;          // 使用1个内存块
    rmt_tx.clk_div = DIVIDER;          // 设置时钟分频器(DIVIDER=4)
    rmt_tx.tx_config.loop_en = false;  // 禁用循环发送模式
    rmt_tx.tx_config.carrier_level = 1; // 载波电平
    rmt_tx.tx_config.carrier_en = 0;    // 禁用载波
    rmt_tx.tx_config.idle_level = 0;    // 空闲时为低电平
    rmt_tx.tx_config.idle_output_en = true; // 启用空闲输出
    rmt_tx.rmt_mode = RMT_MODE_TX;     // 设置为发送模式
    
    // 应用RMT配置
    rmt_config(&rmt_tx);
    
    // 安装RMT驱动程序(不使用接收缓冲区和中断)
    rmt_driver_install(rmt_tx.channel, 0, 0);

    // 设置发送阈值中断(当发送的项目数达到MAX_PULSES时触发中断)
    rmt_set_tx_thr_intr_en(RMTCHANNEL, true, MAX_PULSES);
    
    // 设置内存块数量为1
    rmt_set_mem_block_num(RMTCHANNEL, 1);
    
    // 禁用内存掉电
    rmt_set_mem_pd(RMTCHANNEL, false);
    
    // 禁用循环模式
    rmt_set_tx_loop_mode(RMTCHANNEL, false);
    
    // 根据ESP-IDF版本设置时钟源
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
    rmt_set_source_clk(RMTCHANNEL, RMT_BASECLK_DEFAULT); // ESP-IDF 5.0及以上版本
#else
    rmt_set_source_clk(RMTCHANNEL, RMT_BASECLK_APB);     // 较早版本
#endif

    // 在较早的ESP-IDF版本中启用特定中断掩码
#if (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 3, 0))
    rmt_set_intr_enable_mask(BIT(0) | BIT(24));
#endif
    return ESP_OK;
}
颜色设置过程

初始化完成后,还会调用ws2812_set_colors函数(位于periph_ws2812.c)设置初始颜色(全黑):

// 文件:components/esp_peripherals/periph_ws2812.c
static esp_err_t ws2812_set_colors(periph_ws2812_t *ws)
{
    // 检查ws指针是否有效
    AUDIO_NULL_CHECK(TAG, ws, return ESP_FAIL);

    // 为RGB数据分配内存缓冲区(每个LED需要3个字节:G、R、B)
    ws->process.buffer = audio_malloc(ws->led_num * 3 * sizeof(uint8_t));
    AUDIO_NULL_CHECK(TAG, ws->process.buffer, return ESP_FAIL);

    // 将每个LED的RGB值转换为GRB格式(WS2812要求的顺序)
    for (int i = 0; i < ws->led_num; i++) {
        rgb_value rgb = {
            .num = ws->color[i]  // 从颜色数组获取RGB值
        };
        ws->process.buffer[0 + i * 3] = rgb.g;  // 绿色字节
        ws->process.buffer[1 + i * 3] = rgb.r;  // 红色字节
        ws->process.buffer[2 + i * 3] = rgb.b;  // 蓝色字节
    }
    
    // 重置处理位置和半标志
    ws->process.pos = 0;
    ws->process.half = 0;

    // 将颜色数据转换为RMT项目并发送
    ws2812_data_copy(ws);
    
    // 等待发送完成(通过信号量同步)
    xSemaphoreTake(ws->sem, portMAX_DELAY);
    
    // 释放临时缓冲区
    if (ws->process.buffer) {
        audio_free(ws->process.buffer);
        ws->process.buffer = NULL;
    }

    return ESP_OK;
}

ws2812_data_copy函数负责将颜色数据转换为RMT项目格式并发送:

// 文件:components/esp_peripherals/periph_ws2812.c
static esp_err_t ws2812_data_copy(periph_ws2812_t *ws)
{
    unsigned int i, j, len, bit;

    // 计算需要处理的总字节数(每个LED 3个字节:G、R、B)
    len = ws->led_num * 3;  
    
    // 为RMT项目分配内存(每个字节8位,每位对应一个RMT项目)
    rmt_item32_t *rmt_data = malloc(sizeof(rmt_item32_t) * len * 8);

    // 将每个字节转换为8个RMT项目
    for (i = 0; i < len; i++) {
        bit = ws->process.buffer[i];  // 获取当前字节
        for (j = 0; j < 8; j++, bit <<= 1) {  // 处理字节的每一位
            if ((bit >> 7) & 0x01) {  // 检查最高位是否为1
                // 位"1"的脉冲:较长高电平(900ns),较短低电平(350ns)
                rmt_data[j + i * 8].val = PULSE_BIT1;
            } else {
                // 位"0"的脉冲:较短高电平(350ns),较长低电平(900ns)
                rmt_data[j + i * 8].val = PULSE_BIT0;
            }
        }
        // 在最后一个字节的最后一位后添加复位码(至少50μs低电平)
        if (i + ws->process.pos == ws->led_num * 3 - 1) {
            rmt_data[7 + i * 8].duration1 = PULSE_TRS;
        }
    }
    
    // 通过RMT发送所有项目(阻塞直到所有项目加入队列)
    rmt_write_items(RMTCHANNEL, rmt_data, len * 8, portMAX_DELAY);
    
    // 释放临时RMT数据缓冲区
    if (rmt_data) {
        free(rmt_data);
        rmt_data = NULL;
    }
    return ESP_OK;
}
发送完成回调处理

当RMT发送完成时,会调用注册的回调函数rmt_handle_tx_end

// 文件:components/esp_peripherals/periph_ws2812.c
static void rmt_handle_tx_end(rmt_channel_t channel, void *arg)
{
    portBASE_TYPE taskAwoken = 0;  // 任务唤醒标志
    periph_ws2812_t *ws = (periph_ws2812_t *)(arg);  // 获取WS2812结构体
    
    // 发送信号通知主任务数据发送已完成
    xSemaphoreGiveFromISR(ws->sem, &taskAwoken);
}
WS2812外设完整初始化时序图

下图展示了WS2812外设从应用程序调用到RMT驱动配置完成的完整流程:

应用程序 periph_ws2812_init (periph_ws2812.c) esp_periph库 ws2812_init_rmt_channel (periph_ws2812.c) RMT驱动 ws2812_set_colors (periph_ws2812.c) ws2812_data_copy (periph_ws2812.c) periph_ws2812_init(config) esp_periph_create(PERIPH_ID_WS2812, "periph_ws2812") periph 分配periph_ws2812_t结构体 创建信号量 xSemaphoreCreateBinary() 分配颜色数组和状态数组 初始化颜色为黑色 ws2812_init_rmt_channel(RMTCHANNEL, gpio_num) rmt_config(rmt_tx) rmt_driver_install(channel, 0, 0) rmt_set_tx_thr_intr_en(RMTCHANNEL, true, MAX_PULSES) rmt_set_mem_block_num(RMTCHANNEL, 1) rmt_set_mem_pd(RMTCHANNEL, false) rmt_set_tx_loop_mode(RMTCHANNEL, false) rmt_set_source_clk(RMTCHANNEL, RMT_BASECLK_APB) ESP_OK esp_periph_set_data(periph, periph_ws2812) rmt_register_tx_end_callback(rmt_handle_tx_end, periph_ws2812) esp_periph_set_function(periph, _ws2812_init, _ws2812_run, _ws2812_destroy) ws2812_set_colors(periph_ws2812) 分配缓冲区 转换RGB顺序为GRB ws2812_data_copy(ws) rmt_write_items(RMTCHANNEL, rmt_data, len * 8, portMAX_DELAY) ESP_OK RMT发送完成时触发回调 rmt_handle_tx_end(channel, periph_ws2812) xSemaphoreGiveFromISR(ws->>sem, &taskAwoken) 返回 xSemaphoreTake(ws->>sem, portMAX_DELAY) 释放缓冲区 ESP_OK periph 当外设被添加到外设集合并启动时 _ws2812_init(self) ESP_OK 应用程序 periph_ws2812_init (periph_ws2812.c) esp_periph库 ws2812_init_rmt_channel (periph_ws2812.c) RMT驱动 ws2812_set_colors (periph_ws2812.c) ws2812_data_copy (periph_ws2812.c)

WS2812外设销毁流程

WS2812外设的销毁流程主要在外设层(periph_ws2812.c)完成,负责熄灭LED、释放内存资源和卸载RMT驱动。具体步骤如下:

外设层销毁过程(periph_ws2812.c)

外设层销毁通过_ws2812_destroy函数(位于periph_ws2812.c)完成,主要包括以下步骤:

  1. 获取外设数据结构:获取WS2812外设的内部数据结构
  2. 熄灭所有LED:将所有LED颜色设置为黑色,确保销毁时灯带关闭
  3. 释放颜色数组和状态数组:释放动态分配的内存资源
  4. 删除信号量:释放同步信号量
  5. 卸载RMT驱动:释放RMT硬件资源
  6. 释放主结构体:释放WS2812外设的主结构体内存
// 文件:components/esp_peripherals/periph_ws2812.c
static esp_err_t _ws2812_destroy(esp_periph_handle_t periph)
{
    // 1. 获取外设数据结构
    periph_ws2812_t *periph_ws2812 = esp_periph_get_data(periph);
    AUDIO_NULL_CHECK(TAG, periph_ws2812, return ESP_FAIL);

    if (periph_ws2812) {
        // 2. 熄灭所有LED
        periph_ws2812_state_t *st = periph_ws2812->state;
        for (int i = 0; i < periph_ws2812->led_num; i++) {
            st[i].color = LED2812_COLOR_BLACK;
            st[i].is_on = true;
            st[i].mode = PERIPH_WS2812_ONE;
        }
        ws2812_set_colors(periph_ws2812);

        // 3. 释放颜色数组
        if (periph_ws2812->color) {
            audio_free(periph_ws2812->color);
            periph_ws2812->color = NULL;
        }

        // 3. 释放状态数组
        if (periph_ws2812->state) {
            audio_free(periph_ws2812->state);
            periph_ws2812->state = NULL;
        }

        // 4. 删除信号量
        if (periph_ws2812->sem) {
            vSemaphoreDelete(periph_ws2812->sem);
            periph_ws2812->sem = NULL;
        }

        // 5. 卸载RMT驱动
        rmt_driver_uninstall(RMTCHANNEL);

        // 6. 释放主结构体
        audio_free(periph_ws2812);
        esp_periph_set_data(periph, NULL);
    }
    return ESP_OK;
}
WS2812外设完整销毁时序图

下图展示了WS2812外设从应用程序调用到资源完全释放的完整流程:

应用程序 esp_periph库 _ws2812_destroy (periph_ws2812.c) esp_periph_destroy(periph) 当外设被销毁时,调用_ws2812_destroy _ws2812_destroy(periph) esp_periph_get_data(periph) periph_ws2812 熄灭所有LED(ws2812_set_colors) 释放颜色数组 释放状态数组 删除信号量 卸载RMT驱动 释放主结构体 ESP_OK 应用程序 esp_periph库 _ws2812_destroy (periph_ws2812.c)

事件处理

WS2812外设的事件处理流程主要涉及外设层的控制接口和定时处理。核心函数包括_ws2812_runperiph_ws2812_controlws2812_timer_handler,具体实现如下:

外设层事件处理(_ws2812_run)

_ws2812_run为外设主事件处理回调,当前实现如下:

// 文件:components/esp_peripherals/periph_ws2812.c
// 外设主事件处理回调,目前未做任何事件分发或处理,仅返回ESP_OK。
static esp_err_t _ws2812_run(esp_periph_handle_t periph, audio_event_iface_msg_t *msg)
{
    return ESP_OK;
}

该函数当前未做任何事件分发或处理,仅返回ESP_OK


控制接口(periph_ws2812_control)
// 文件:components/esp_peripherals/periph_ws2812.c
// 控制接口,根据配置参数设置各LED的颜色、模式、时序等,并启动定时器。
esp_err_t periph_ws2812_control(esp_periph_handle_t periph, periph_ws2812_ctrl_cfg_t *control_cfg, void *ctx)
{
    periph_ws2812_t *periph_ws2812 = esp_periph_get_data(periph);

    AUDIO_NULL_CHECK(TAG, periph_ws2812, return ESP_FAIL); // 检查外设结构体有效性
    AUDIO_NULL_CHECK(TAG, control_cfg, return ESP_FAIL);   // 检查控制参数有效性

    for (int i = 0; i < periph_ws2812->led_num; i++) {
        // 设置每个LED的目标颜色
        periph_ws2812->state[i].color = control_cfg[i].color;
        periph_ws2812->color[i] = control_cfg[i].color;
        // 设置每个LED的亮灭时长
        periph_ws2812->state[i].time_on_ms = control_cfg[i].time_on_ms;
        periph_ws2812->state[i].time_off_ms = control_cfg[i].time_off_ms;
        // 初始化定时基准点
        periph_ws2812->state[i].tick = audio_sys_get_time_ms();
        // 设置循环次数、初始状态、控制标志和模式
        periph_ws2812->state[i].loop = control_cfg[i].loop;
        periph_ws2812->state[i].is_on = true;
        periph_ws2812->state[i].is_set = true;
        periph_ws2812->state[i].mode = control_cfg[i].mode;
    }

    // 启动定时器,定期调用ws2812_timer_handler实现动态效果
    esp_periph_start_timer(periph, INTERVAL_TIME_MS / portTICK_RATE_MS, ws2812_timer_handler);

    return ESP_OK;
}

定时处理(ws2812_timer_handler)
// 文件:components/esp_peripherals/periph_ws2812.c
// 定时器回调,根据每个LED的模式和状态动态更新颜色,实现单色/闪烁/渐变等效果。
static void ws2812_timer_handler(TimerHandle_t tmr)
{
    esp_periph_handle_t periph = (esp_periph_handle_t)pvTimerGetTimerID(tmr);
    periph_ws2812_t *periph_ws2812 = esp_periph_get_data(periph);
    periph_ws2812_state_t *st = periph_ws2812->state;
    for ( int i = 0; i < periph_ws2812->led_num; i++ ) {
        switch (st[i].mode) {
            case PERIPH_WS2812_ONE:
                // 单色模式:只亮一次
                if (st[i].is_on) {
                    periph_ws2812->color[i] = st[i].color;
                    ws2812_set_colors(periph_ws2812);
                    st[i].is_on = false;
                    st[i].loop = 0;
                }
                break;
            case PERIPH_WS2812_BLINK:
                // 闪烁模式:按设定亮灭时长循环切换
                if (st[i].is_set == false) {
                    continue;
                }
                if (st[i].loop == 0) {
                    periph_ws2812->color[i] = LED2812_COLOR_BLACK;
                    ws2812_set_colors(periph_ws2812);
                    st[i].is_set = false;
                }
                // 亮灭切换
                if (st[i].is_on && audio_sys_get_time_ms() - st[i].tick > st[i].time_off_ms) {
                    if (st[i].loop > 0) {
                        st[i].loop--;
                    } else {
                        continue;
                    }
                    st[i].is_on = false;
                    st[i].tick = audio_sys_get_time_ms();
                    periph_ws2812->color[i] = st[i].color;
                    ws2812_set_colors(periph_ws2812);
                } else if (!st[i].is_on && audio_sys_get_time_ms() - st[i].tick > st[i].time_on_ms) {
                    st[i].is_on = true;
                    st[i].tick = audio_sys_get_time_ms();
                    periph_ws2812->color[i] = LED2812_COLOR_BLACK;
                    ws2812_set_colors(periph_ws2812);
                }
                break;
            case PERIPH_WS2812_FADE:
                // 渐变模式:分步递减/递增RGB分量,实现渐变
                if (st[i].is_set == false) {
                    continue;
                }
                if (st[i].loop == 0) {
                    periph_ws2812->color[i] = LED2812_COLOR_BLACK;
                    ws2812_set_colors(periph_ws2812);
                    st[i].is_set = false;
                    continue;
                }
                // 递减RGB分量,逐步熄灭
                if (st[i].is_on && (audio_sys_get_time_ms() - st[i].tick > ((st[i].time_on_ms / FADE_STEP)))) {
                    st[i].tick = audio_sys_get_time_ms();
                    rgb_value rgb = {
                        .num = st[i].color
                    };
                    rgb_value rgb1 = {
                        .num =  periph_ws2812->color[i]
                    };
                    rgb1.r -= (uint8_t)rgb.r / FADE_STEP;
                    rgb1.g -= (uint8_t)rgb.g / FADE_STEP;
                    rgb1.b -= (uint8_t)rgb.b / FADE_STEP;
                    ws2812_set_colors(periph_ws2812);
                    periph_ws2812->color[i] = rgb1.num;
                    if ((rgb1.r <= (uint8_t)rgb.r / FADE_STEP)
                        && (rgb1.g <= (uint8_t)rgb.g / FADE_STEP)
                        && (rgb1.b <= (uint8_t)rgb.b / FADE_STEP)) {
                        st[i].is_on = false;
                        st[i].loop--;
                    }
                } else if ((st[i].is_on == false) && (audio_sys_get_time_ms() - st[i].tick > ((st[i].time_off_ms / FADE_STEP)))) {
                    // 递增RGB分量,逐步点亮
                    st[i].tick = audio_sys_get_time_ms();
                    rgb_value rgb = {
                        .num = st[i].color
                    };
                    rgb_value rgb1 = {
                        .num =  periph_ws2812->color[i]
                    };
                    rgb1.r += (uint8_t)rgb.r / FADE_STEP;
                    rgb1.g += (uint8_t)rgb.g / FADE_STEP;
                    rgb1.b += (uint8_t)rgb.b / FADE_STEP;
                    ws2812_set_colors(periph_ws2812);
                    periph_ws2812->color[i] = rgb1.num;
                    if ((((uint8_t)rgb.r - rgb1.r) <= (uint8_t)rgb.r / FADE_STEP)
                        && (((uint8_t)rgb.g - rgb1.g) <= (uint8_t)rgb.g / FADE_STEP)
                        && (((uint8_t)rgb.b - rgb1.b) <= (uint8_t)rgb.b / FADE_STEP))  {
                        st[i].is_on = true;
                    }
                }
                break;
            default:
                ESP_LOGW(TAG, "The ws2812 mode[%d] is invalid", st[i].mode);
                break;
        }
    }
}

时序图
应用层 WS2812外设 定时器回调 periph_ws2812_control(cfg) 配置LED参数并启动定时器 定时到达自动回调 读取LED状态与模式 动态更新颜色/亮灭/渐变 ws2812_set_colors loop [每次定时周期] 应用层 WS2812外设 定时器回调
外设停止接口(periph_ws2812_stop)
// 文件:components/esp_peripherals/periph_ws2812.c
// 外设停止接口,将所有LED熄灭并重置状态为单次模式。
esp_err_t periph_ws2812_stop(esp_periph_handle_t periph)
{
    periph_ws2812_t *periph_ws2812 = esp_periph_get_data(periph);

    AUDIO_NULL_CHECK(TAG, periph_ws2812, return ESP_FAIL);

    periph_ws2812_state_t *st = periph_ws2812->state;
    for (int i = 0; i < periph_ws2812->led_num; i++) {
        st[i].color = LED2812_COLOR_BLACK;        // 设置为黑色,熄灭LED
        st[i].is_on = true;                      // 状态重置为点亮
        st[i].mode = PERIPH_WS2812_ONE;          // 模式重置为单次
    }
    ws2812_set_colors(periph_ws2812);            // 刷新LED显示
    return ESP_OK;
}

功能说明:

  • 该接口用于“停止”WS2812外设,典型应用如外设关闭、系统待机等场景。
  • 其实现逻辑为:遍历所有LED,将颜色设置为黑色(熄灭),状态和模式重置为初始单次点亮模式,然后调用ws2812_set_colors立即刷新显示,确保所有LED被关闭。
  • 该函数不会释放任何内存资源,仅做LED状态的逻辑重置和熄灭。

典型使用示例

以下代码演示了WS2812外设的基本初始化、设置LED为红色并闪烁、以及停止(熄灭)灯带的完整流程:

#include "esp_peripherals.h"
#include "periph_ws2812.h"

void app_ws2812_example(void)
{
    // 1. 配置并初始化WS2812外设
    periph_ws2812_cfg_t ws2812_cfg = {
        .gpio_num = 18,
        .led_num = 4, // 假设灯带有4颗LED
    };
    esp_periph_handle_t ws2812_periph = periph_ws2812_init(&ws2812_cfg);

    // 2. 设置所有LED为红色并闪烁
    periph_ws2812_ctrl_cfg_t ctrl_cfg[4];
    for (int i = 0; i < 4; i++) {
        ctrl_cfg[i].color = 0x00FF0000; // 红色
        ctrl_cfg[i].mode = PERIPH_WS2812_BLINK;
        ctrl_cfg[i].time_on_ms = 300;   // 亮300ms
        ctrl_cfg[i].time_off_ms = 300;  // 灭300ms
        ctrl_cfg[i].loop = 10;          // 闪烁10次
    }
    periph_ws2812_control(ws2812_periph, ctrl_cfg, NULL);

    // ...此时灯带开始闪烁...

    // 3. 停止并熄灭所有LED
    periph_ws2812_stop(ws2812_periph);

    // 4. 销毁外设(如需要释放资源)
    esp_periph_destroy(ws2812_periph);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

omnibots

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值