目录
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外设子系统的重要组成部分,位于硬件驱动层和应用层之间:
核心特性
- 多种显示设备支持:支持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外设层次架构图
功能和特点
- 集成RGB控制:WS2812集成了红、绿、蓝三色LED和控制电路,可以实现1670万色的全彩显示
- 级联控制:支持多个WS2812 LED级联,通过单一GPIO引脚控制整条灯带
- 多种显示模式:支持单色显示、闪烁效果和渐变效果
- 精确时序控制:使用RMT(Remote Control Transceiver)模块生成精确的时序信号
- 低功耗:支持PWM调光,可以实现不同亮度的控制
通信协议和时序要求
WS2812采用单线通信协议,通过精确的时序控制来传输数据:
-
数据格式:每个LED需要24位数据(RGB各8位)
-
时序要求:
- 位"0"由350ns高电平+900ns低电平表示
- 位"1"由900ns高电平+350ns低电平表示
- 复位码由至少50μs的低电平表示
-
数据传输顺序:
- 颜色顺序为GRB(绿、红、蓝)
- 数据从高位到低位传输
- 数据按LED的级联顺序传输
WS2812外设API和数据结构
外设层API
源文件:components/esp_peripherals/include/periph_ws2812.h
和components/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);
配置结构体
- 初始化配置结构体:
typedef struct {
int gpio_num; // WS2812连接的GPIO引脚号
int led_num; // WS2812 LED的数量
} periph_ws2812_cfg_t;
- 控制配置结构体:
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;
- 工作模式枚举:
typedef enum {
PERIPH_WS2812_BLINK, // 闪烁模式
PERIPH_WS2812_FADE, // 渐变模式
PERIPH_WS2812_ONE, // 单色模式
} periph_ws2812_mode_t;
- 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外设内部使用以下数据结构:
- RGB值联合体:
typedef union {
struct __attribute__ ((packed)) {
uint8_t r, g, b;
};
uint32_t num;
} rgb_value;
- 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;
- WS2812处理结构体:
typedef struct {
uint32_t pos; // 当前位置
uint32_t half; // 半值
uint8_t *buffer; // 数据缓冲区
} periph_ws2812_process_t;
- 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
),该函数完成以下任务:
- 创建外设句柄:调用
esp_periph_create
函数创建外设句柄 - 分配内部数据结构:分配
periph_ws2812_t
结构体内存 - 初始化参数:创建信号量,分配颜色和状态数组
- 配置RMT通道:调用
ws2812_init_rmt_channel
配置RMT通道 - 注册回调函数:设置初始化、运行和销毁回调函数
// 文件: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驱动配置完成的完整流程:
WS2812外设销毁流程
WS2812外设的销毁流程主要在外设层(periph_ws2812.c)完成,负责熄灭LED、释放内存资源和卸载RMT驱动。具体步骤如下:
外设层销毁过程(periph_ws2812.c)
外设层销毁通过_ws2812_destroy
函数(位于periph_ws2812.c
)完成,主要包括以下步骤:
- 获取外设数据结构:获取WS2812外设的内部数据结构
- 熄灭所有LED:将所有LED颜色设置为黑色,确保销毁时灯带关闭
- 释放颜色数组和状态数组:释放动态分配的内存资源
- 删除信号量:释放同步信号量
- 卸载RMT驱动:释放RMT硬件资源
- 释放主结构体:释放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外设从应用程序调用到资源完全释放的完整流程:
事件处理
WS2812外设的事件处理流程主要涉及外设层的控制接口和定时处理。核心函数包括_ws2812_run
、periph_ws2812_control
和ws2812_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;
}
}
}
时序图
外设停止接口(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);
}