目录
- ESP-ADF外设子系统深度解析:esp_peripherals组件架构与核心设计(显示输出类外设之IS31FL3216)
- 简介
- 模块概述
- 功能定义
- 架构位置
- 核心特性
- IS31FL3216外设分析
- IS31FL3216外设概述
- IS31FL3216外设层次架构图
- IS31FL3216外设API和数据结构
- 外设层API
- 公共API
- 内部数据结构
- 底层驱动API
- 公开API (IS31FL3216.h)
- IS31FL3216外设工作模式
- IS31FL3216外设配置选项
- 外设层配置选项
- 底层驱动配置选项
- IS31FL3216外设初始化分析
- 初始化流程概述
- 外设层初始化过程(periph_is31fl3216.c)
- 辅助函数详解
- is31_leds_duty
- is31fl3216_write_reg
- is31fl3216_ch_disable
- is31fl3216_ch_duty_set
- is31fl3218S_channel_duty_by_bits
- is31fl3216_update_reg
- 底层驱动初始化过程(IS31FL3216.c)
- 辅助函数详解
- is31fl3216_power
- is31fl3216_cur_mode_set
- is31fl3216_cur_value_set
- 运行任务创建与状态管理
- 辅助函数
- is31_leds_ctrl
- is31fl3216_ch_enable
- 状态切换处理
- 辅助函数
- is31fl3216_work_mode_set
- is31fl3216_sample_rate_set
- is31fl3216_frame_value_set
- is31fl3216_first_frame_set
- IS31FL3216外设完整初始化时序图
- 总结
- IS31FL3216外设销毁流程分析
- 销毁流程概述
- 外设层销毁过程(periph_is31fl3216.c)
- 底层驱动销毁过程(IS31FL3216.c)
- 完整销毁时序图
- 总结
- IS31FL3216外设事件处理机制
- 事件类型与消息结构
- 事件发送机制
- 事件处理流程
- 状态机实现
- 状态控制与参数配置接口
- 状态控制接口
- 参数配置接口
- 事件处理时序图
- 总结
- 典型示例
- 常用参数和配置
- LED通道模式(用于`periph_is31fl3216_set_blink_pattern`)
- LED工作状态(用于`periph_is31fl3216_set_state`)
- 移位模式(用于`periph_is31fl3216_set_shift_mode`)
- 常见应用场景
ESP-ADF外设子系统深度解析:esp_peripherals组件架构与核心设计(显示输出类外设之IS31FL3216)
版本信息: 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等多种显示和指示设备
- 统一控制接口:所有显示/输出外设使用统一的初始化和控制接口
- 丰富的显示效果:支持开关控制、亮度调节、颜色变化、动画效果等多种显示功能
- 与音频处理集成:可与音频处理模块协同工作,实现音频可视化效果
- 低功耗设计:支持设备休眠和唤醒管理,优化功耗表现
- 事件驱动模型:通过事件机制实现显示状态变化的通知和处理
IS31FL3216外设分析
IS31FL3216外设概述
IS31FL3216 是一款 16 通道 LED 驱动芯片,通过 I2C 总线控制,支持 PWM 调光、自动帧播放和音频调制等多种工作模式。在 ESP-ADF 框架中,IS31FL3216 外设被封装为三个层次:
-
外设层:负责将 IS31FL3216 集成到 ESP-ADF 外设系统中,处理事件分发和生命周期管理。
- 头文件:
components/esp_peripherals/include/periph_is31fl3216.h
- 实现文件:
components/esp_peripherals/periph_is31fl3216.c
- 头文件:
-
底层驱动层:提供底层 IS31FL3216 驱动,负责 I2C 通信、寄存器配置和 LED 控制。
- 头文件:
components/esp_peripherals/lib/IS31FL3216/IS31FL3216.h
- 实现文件:
components/esp_peripherals/lib/IS31FL3216/IS31FL3216.c
- 头文件:
-
总线层:底层驱动通过 I2C 总线层与硬件通信,提供统一的总线访问接口。
- 头文件:
components/esp_peripherals/driver/i2c_bus/i2c_bus.h
- 实现文件:
components/esp_peripherals/driver/i2c_bus/i2c_bus.c
(ESP-IDF 5.3.0- 实现) - 实现文件:
components/esp_peripherals/driver/i2c_bus/i2c_bus_v2.c
(ESP-IDF 5.3.0+ 实现)
- 头文件:
IS31FL3216 芯片具有以下主要特性:
- 16 通道 LED 驱动,每通道最大 60mA 驱动电流
- 8 位 PWM 调光控制(256 级亮度)
- 多种工作模式:PWM 控制、自动帧播放、音频调制
- 支持音频信号调制 LED 亮度,内置 AGC 和增益控制
- 级联模式支持多芯片扩展
- 通过 I2C 总线控制(400kHz)
IS31FL3216外设层次架构图
IS31FL3216外设API和数据结构
外设层API
源文件:components/esp_peripherals/include/periph_is31fl3216.h
和components/esp_peripherals/periph_is31fl3216.c
公共API
// IS31FL3216通道数量常量
#define IS31FL3216_CH_NUM 16 // 应小于等于16
#define BLUE_LED_MAX_NUM 12// IS31FL3216状态枚举
typedef enum {IS31FL3216_STATE_UNKNOWN, // 未知状态IS31FL3216_STATE_OFF, // 关闭状态IS31FL3216_STATE_ON, // 开启状态IS31FL3216_STATE_FLASH, // 闪烁状态IS31FL3216_STATE_BY_AUDIO, // 音频调制状态IS31FL3216_STATE_SHIFT, // 移位状态
} periph_is31fl3216_state_t;// 移位模式枚举
typedef enum {PERIPH_IS31_SHIFT_MODE_UNKNOWN,PERIPH_IS31_SHIFT_MODE_ACC, // 累积模式PERIPH_IS31_SHIFT_MODE_SINGLE, // 单通道模式
} periph_is31_shift_mode_t;// IS31FL3216配置结构体
typedef struct {uint32_t duty[IS31FL3216_CH_NUM]; // 各通道占空比数组uint16_t is31fl3216_pattern; // 当前启用的通道periph_is31fl3216_state_t state; // 所有通道的状态
} periph_is31fl3216_cfg_t;// 初始化IS31FL3216外设
esp_periph_handle_t periph_is31fl3216_init(periph_is31fl3216_cfg_t *is31fl3216_config);// 设置所有通道的状态
esp_err_t periph_is31fl3216_set_state(esp_periph_handle_t periph, periph_is31fl3216_state_t state);// 设置闪烁模式的通道模式
esp_err_t periph_is31fl3216_set_blink_pattern(esp_periph_handle_t periph, uint16_t blink_pattern);// 设置指定通道的占空比
esp_err_t periph_is31fl3216_set_duty(esp_periph_handle_t periph, uint8_t index, uint8_t value);// 设置闪烁模式的步进值
esp_err_t periph_is31fl3216_set_duty_step(esp_periph_handle_t periph, uint8_t step);// 设置时间间隔
esp_err_t periph_is31fl3216_set_interval(esp_periph_handle_t periph, uint16_t interval_ms);// 设置移位模式
esp_err_t periph_is31fl3216_set_shift_mode(esp_periph_handle_t periph, periph_is31_shift_mode_t mode);// 设置点亮的LED数量
esp_err_t periph_is31fl3216_set_light_on_num(esp_periph_handle_t periph, uint16_t light_on_num, uint16_t max_light_num);// 设置动作时间
esp_err_t periph_is31fl3216_set_act_time(esp_periph_handle_t periph, uint16_t act_ms);
内部数据结构
// 重要宏定义
#define IS31FL3216_TASK_STACK_SIZE (2048 + 1024) // IS31FL3216任务栈大小
#define IS31FL3216_TASK_PRIORITY 3 // IS31FL3216任务优先级
#define ONE_FRAME_BYTE_SIZE 18 // 帧大小
#define DEFAULT_FLASH_STEP 2 // 默认闪烁步进
#define DESTROY_BIT BIT0 // 销毁位// 命令类型枚举
typedef enum {PERIPH_IS31_CMD_CHG_STATE, // 改变状态命令PERIPH_IS31_CMD_QUIT, // 退出命令
} periph_is31_cmd_t;// IS31FL3216参数结构体
typedef struct {uint16_t max_light_num; // 最大灯数uint16_t light_num; // 工作灯数uint16_t light_mask; // 灯位掩码int interval_time; // 间隔工作时间uint16_t act_time; // 动作时间uint8_t duty_step; // 占空比步进periph_is31_shift_mode_t shift_mode; // 移位模式
} periph_is31_arg_t;// IS31FL3216外设内部结构体
typedef struct {periph_is31_arg_t *arg; // IS31FL3216参数uint8_t duty[IS31FL3216_CH_NUM]; // 各通道占空比is31fl3216_handle_t handle; // 底层驱动句柄periph_is31fl3216_state_t cur_state; // 当前状态QueueHandle_t evt; // 事件队列EventGroupHandle_t g_event_bit; // 事件位组
} periph_is31fl3216_t;// IS31FL3216消息结构体
typedef struct {periph_is31_cmd_t type; // 命令类型uint32_t data; // 命令数据
} periph_is31_msg_t;// 音频帧数据
static const uint8_t light_audio_frames[8][ONE_FRAME_BYTE_SIZE] = {{0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},{0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},{0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},{0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff},{0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xff, 0xff},{0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xff, 0xff, 0xff},{0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xff, 0xff, 0xff},{0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xff, 0xFF, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
};// 验证宏
#define VALIDATE_IS31FL3216(periph, ret) if (!(periph && esp_periph_get_id(periph) == PERIPH_ID_IS31FL3216)) { \ESP_LOGE(TAG, "Invalid is31fl3216 periph, at line %d", __LINE__);\return ret;\
}
底层驱动API
源文件:components/esp_peripherals/lib/IS31FL3216/IS31FL3216.h
和components/esp_peripherals/lib/IS31FL3216/IS31FL3216.c
公开API (IS31FL3216.h)
// 宏定义
#define IS31FL3216_CH_NUM_MAX 16 // 最大通道数
#define IS31FL3216_DUTY_MAX 255 // 最大占空比// 电源模式枚举
typedef enum {IS31FL3216_PWR_NORMAL = 0, // 正常工作模式IS31FL3216_PWR_SHUTDOWN, // 软件关断模式IS31FL3216_PWR_MAX,
} is31fl3216_pwr_t;// 工作模式枚举
typedef enum {IS31FL3216_MODE_PWM = 0, // PWM控制模式IS31FL3216_MODE_AUTO_FRAME, // 自动帧播放模式IS31FL3216_MODE_FRAME, // 音频帧模式IS31FL3216_MODE_MAX,
} is31fl3216_work_mode_t;// 电流模式枚举
typedef enum {IS31FL3216_CUR_MODE_REXT = 0, // 输出电流由寄存器设置IS31FL3216_CUR_MODE_AUDIO, // 输出电流由音频信号调制IS31FL3216_CUR_MODE_MAX,
} is31fl3216_cur_mode_t;// 电流值枚举
typedef enum {IS31FL3216_CUR_1_00 = 0, // 输出电流选择IS31FL3216_CUR_0_75,IS31FL3216_CUR_0_50,IS31FL3216_CUR_0_25,IS31FL3216_CUR_2_00,IS31FL3216_CUR_1_75,IS31FL3216_CUR_1_50,IS31FL3216_CUR_1_25,IS31FL3216_CUR_MAX,
} is31fl3216_cur_value_t;// 级联模式枚举
typedef enum {IS31FL3216_CASCADE_MASTER = 0, // 主机模式IS31FL3216_CASCADE_SLAVE, // 从机模式
} is31fl3216_cascade_mode_t;// 音频增益枚举
typedef enum {IS31FL3216_AGS_0DB = 0, // 音频增益选择IS31FL3216_AGS_3DB,IS31FL3216_AGS_6DB,IS31FL3216_AGS_9DB,IS31FL3216_AGS_12DB,IS31FL3216_AGS_15DB,IS31FL3216_AGS_18DB,IS31FL3216_AGS_21DB,IS31FL3216_AGS_MAX,
} is31fl3216_ags_value_t;// 帧延时时间枚举
typedef enum {IS31FL3216_TIME_32MS = 0, // 帧延时时间IS31FL3216_TIME_64MS,IS31FL3216_TIME_128MS,IS31FL3216_TIME_256MS,IS31FL3216_TIME_512MS,IS31FL3216_TIME_1024MS,IS31FL3216_TIME_2048MS,IS31FL3216_TIME_4096MS,IS31FL3216_TIME_MAX,
} is31fl3216_delay_time_t;// 寄存器地址枚举
typedef enum {IS31FL3216_REG_CONFIG = 0x00, // 配置寄存器IS31FL3216_REG_LED_CTRL_H = 0x01, // LED控制寄存器 OUT9-OUT16 使能位IS31FL3216_REG_LED_CTRL_L = 0x02, // LED控制寄存器 OUT1-OUT8 使能位IS31FL3216_REG_LED_EFFECT = 0x03, // 设置输出电流和音频增益IS31FL3216_REG_CH_CONFIG = 0x04, // 设置OUT9~OUT16的工作模式IS31FL3216_REG_GPIO_CONFIG = 0x05, // 设置OUT9~OUT16作为GPIO端口的工作模式IS31FL3216_REG_OUTPUT = 0x06, // 设置OUT9~OUT16作为输出端口的逻辑电平IS31FL3216_REG_INPUT_CTRL = 0x07, // 设置OUT9~OUT16的中断功能IS31FL3216_REG_STATE = 0x08, // 存储OUT9~OUT16作为输入端口的状态IS31FL3216_REG_ADC_RATE = 0x09, // 设置输入信号的ADC采样率IS31FL3216_REG_PWM_16 = 0x10, // 设置PWM占空比数据IS31FL3216_REG_PWM_15,IS31FL3216_REG_PWM_14,IS31FL3216_REG_PWM_13,IS31FL3216_REG_PWM_12,IS31FL3216_REG_PWM_11,IS31FL3216_REG_PWM_10,IS31FL3216_REG_PWM_09,IS31FL3216_REG_PWM_08,IS31FL3216_REG_PWM_07,IS31FL3216_REG_PWM_06,IS31FL3216_REG_PWM_05,IS31FL3216_REG_PWM_04,IS31FL3216_REG_PWM_03,IS31FL3216_REG_PWM_02,IS31FL3216_REG_PWM_01,IS31FL3216_REG_FRAME1_CTRL = 0x20, // 存储8帧数据IS31FL3216_REG_FRAME1_PWM = 0x22,IS31FL3216_REG_FRAME2_CTRL = 0x32,IS31FL3216_REG_FRAME2_PWM = 0x34,IS31FL3216_REG_FRAME3_CTRL = 0x44,IS31FL3216_REG_FRAME3_PWM = 0x46,IS31FL3216_REG_FRAME4_CTRL = 0x56,IS31FL3216_REG_FRAME4_PWM = 0x58,IS31FL3216_REG_FRAME5_CTRL = 0x68,IS31FL3216_REG_FRAME5_PWM = 0x6A,IS31FL3216_REG_FRAME6_CTRL = 0x7A,IS31FL3216_REG_FRAME6_PWM = 0x7C,IS31FL3216_REG_FRAME7_CTRL = 0x8C,IS31FL3216_REG_FRAME7_PWM = 0x8E,IS31FL3216_REG_FRAME8_CTRL = 0x9E,IS31FL3216_REG_FRAME8_PWM = 0xA0,IS31FL3216_REG_UPDATE = 0xB0, // 加载PWM寄存器数据IS31FL3216_REG_FRAME_DELAY = 0xB6, // 设置每帧之间的延迟时间IS31FL3216_REG_FRAME_START = 0xB7, // 设置自动帧播放模式的起始帧IS31FL3216_REG_MAX,
} is31fl3216_reg_t;// 通道枚举
typedef enum {IS31FL3216_CH_1 = 0x0001, // 通道位掩码IS31FL3216_CH_2 = 0x0002,IS31FL3216_CH_3 = 0x0004,IS31FL3216_CH_4 = 0x0008,IS31FL3216_CH_5 = 0x0010,IS31FL3216_CH_6 = 0x0020,IS31FL3216_CH_7 = 0x0040,IS31FL3216_CH_8 = 0x0080,IS31FL3216_CH_9 = 0x0100,IS31FL3216_CH_10 = 0x0200,IS31FL3216_CH_11 = 0x0400,IS31FL3216_CH_12 = 0x0800,IS31FL3216_CH_13 = 0x1000,IS31FL3216_CH_14 = 0x2000,IS31FL3216_CH_15 = 0x4000,IS31FL3216_CH_16 = 0x8000,IS31FL3216_CH_ALL = 0xFFFF,
} is31_pwm_channel_t;// 驱动句柄
typedef void *is31fl3216_handle_t;// 设置电源模式
esp_err_t is31fl3216_power(is31fl3216_handle_t handle, is31fl3216_pwr_t mode);// 设置通道占空比
esp_err_t is31fl3216_ch_duty_set(is31fl3216_handle_t handle, is31_pwm_channel_t ch_bits, uint8_t duty);// 设置工作模式
esp_err_t is31fl3216_work_mode_set(is31fl3216_handle_t handle, is31fl3216_work_mode_t mode);// 启用通道
esp_err_t is31fl3216_ch_enable(is31fl3216_handle_t handle, is31_pwm_channel_t ch_bits);// 禁用通道
esp_err_t is31fl3216_ch_disable(is31fl3216_handle_t handle, is31_pwm_channel_t ch_bits);// 设置电流模式
esp_err_t is31fl3216_cur_mode_set(is31fl3216_handle_t handle, is31fl3216_cur_mode_t mode);// 设置电流值
esp_err_t is31fl3216_cur_value_set(is31fl3216_handle_t handle, is31fl3216_cur_value_t value);// 设置音频增益
esp_err_t is31fl3216_ags_value_set(is31fl3216_handle_t handle, is31fl3216_ags_value_t value);// 配置自动增益控制
esp_err_t is31fl3216_agc_cfg(is31fl3216_handle_t handle, uint32_t en);// 设置级联模式
esp_err_t is31fl3216_cascade_mode_set(is31fl3216_handle_t handle, is31fl3216_cascade_mode_t mode);// 更新寄存器
esp_err_t is31fl3216_update_reg(is31fl3216_handle_t handle);// 设置采样率
esp_err_t is31fl3216_sample_rate_set(is31fl3216_handle_t handle, uint32_t value);// 设置帧时间
esp_err_t is31fl3216_frame_time_set(is31fl3216_handle_t handle, is31fl3216_delay_time_t time);// 设置起始帧
esp_err_t is31fl3216_first_frame_set(is31fl3216_handle_t handle, uint32_t frame);// 设置帧数据
esp_err_t is31fl3216_frame_value_set(is31fl3216_handle_t handle, uint32_t num, uint8_t *data, uint32_t len);// 复位芯片
esp_err_t is31fl3216_reset(is31fl3216_handle_t handle);// 初始化驱动
is31fl3216_handle_t is31fl3216_init(void);// 释放驱动
esp_err_t is31fl3216_deinit(is31fl3216_handle_t handle);
IS31FL3216外设工作模式
IS31FL3216 支持多种工作模式,可以通过外设层的 periph_is31fl3216_set_state
函数进行设置:
-
常亮模式 (IS31FL3216_STATE_ON):
- 所有配置的 LED 通道以固定亮度常亮
- 亮度通过
periph_is31fl3216_set_duty
设置 - 底层驱动使用 PWM 模式
-
闪烁模式 (IS31FL3216_STATE_FLASH):
- LED 通道按照设定的间隔和步进值闪烁
- 间隔通过
periph_is31fl3216_set_interval
设置 - 步进值通过
periph_is31fl3216_set_duty_step
设置 - 闪烁模式通过
periph_is31fl3216_set_blink_pattern
设置
-
音频调制模式 (IS31FL3216_STATE_BY_AUDIO):
- LED 亮度随音频信号变化
- 底层使用 IS31FL3216 的音频帧模式
- 预设了 8 个音频响应帧
- 内置 AGC 和增益控制
-
移位模式 (IS31FL3216_STATE_SHIFT):
- LED 按照设定的模式和速度移位点亮
- 支持累积模式和单通道模式
- 通过
periph_is31fl3216_set_shift_mode
设置模式 - 通过
periph_is31fl3216_set_light_on_num
设置点亮数量 - 通过
periph_is31fl3216_set_interval
设置移位速度
-
关闭模式 (IS31FL3216_STATE_OFF):
- 关闭所有 LED 通道
- 底层驱动使用
is31fl3216_ch_disable
禁用所有通道
IS31FL3216外设配置选项
外设层配置选项
- duty[IS31FL3216_CH_NUM]: 各通道的初始占空比,范围为0-255
- is31fl3216_pattern: 指定要使用的LED通道模式,使用位标识表示,如IS31FL3216_CH_ALL表示所有通道
- state: 初始工作状态,可以是常亮、闪烁、音频调制、移位或关闭模式
底层驱动配置选项
- 电源模式: 可以设置为正常工作模式或软件关闭模式
- 工作模式: 可以设置为 PWM 控制模式、自动帧播放模式或音频帧模式
- 电流模式: 可以设置为寄存器控制或音频信号调制
- 电流值: 设置输出电流大小,范围从 0.25 倍到 2.00 倍
- 音频增益: 设置音频信号的增益,范围从 0dB 到 21dB
- 自动增益控制: 可以启用或禁用自动增益控制
- 级联模式: 可以设置为主机模式或从机模式
- 帧时间: 设置自动帧播放模式下的帧间隔,范围从 32ms 到 4096ms
注意:外设层的 API 已经封装了底层驱动的复杂性,应用程序只需要使用外设层的 API 即可实现大部分功能。如果需要更精细的控制,可以直接使用底层驱动的 API。
IS31FL3216外设初始化分析
初始化流程概述
IS31FL3216外设的初始化流程涉及两个层次:外设层(Peripheral Layer)和底层驱动(Driver Layer)。外设层负责创建外设句柄、分配内存、设置配置参数和注册回调函数;底层驱动层负责配置I2C总线、设置设备地址、初始化寄存器和配置工作模式。
外设层初始化过程(periph_is31fl3216.c)
外设层初始化主要通过periph_is31fl3216_init
函数(位于periph_is31fl3216.c
)完成,主要包括以下步骤:
- 创建外设句柄:调用
esp_periph_create
函数创建外设句柄 - 分配内部数据结构:分配
periph_is31fl3216_t
结构体内存 - 创建事件组和队列:用于任务间通信和同步
- 设置配置参数:设置默认参数和LED通道占空比
- 初始化底层驱动:调用
is31fl3216_init
函数初始化底层驱动 - 注册回调函数:设置初始化和销毁回调函数
// 文件:components/esp_peripherals/periph_is31fl3216.c
esp_periph_handle_t periph_is31fl3216_init(periph_is31fl3216_cfg_t *is31fl3216_config)
{// 1. 创建外设句柄esp_periph_handle_t periph = esp_periph_create(PERIPH_ID_IS31FL3216, "periph_is31fl3216");AUDIO_MEM_CHECK(TAG, periph, return NULL);// 2. 分配内部数据结构periph_is31fl3216_t *is31fl3216 = audio_calloc(1, sizeof(periph_is31fl3216_t));AUDIO_MEM_CHECK(TAG, is31fl3216, {audio_free(periph);return NULL;});// 3. 创建事件组和队列is31fl3216->g_event_bit = xEventGroupCreate();AUDIO_NULL_CHECK(TAG, is31fl3216->g_event_bit, {audio_free(periph);audio_free(is31fl3216);});is31fl3216->evt = xQueueCreate(2, sizeof(periph_is31_msg_t));AUDIO_MEM_CHECK(TAG, is31fl3216->evt, {audio_free(periph);vEventGroupDelete(is31fl3216->g_event_bit);audio_free(is31fl3216);return NULL;});// 分配参数结构体内存is31fl3216->arg = audio_calloc(1, sizeof(periph_is31_arg_t));AUDIO_MEM_CHECK(TAG, is31fl3216->arg, {vQueueDelete(is31fl3216->evt);vEventGroupDelete(is31fl3216->g_event_bit);audio_free(periph);audio_free(is31fl3216);return NULL;});// 4. 设置配置参数is31fl3216->arg->max_light_num = IS31FL3216_CH_NUM;is31fl3216->arg->light_num = 0;is31fl3216->arg->light_mask = 0;is31fl3216->arg->interval_time = 1000;is31fl3216->arg->act_time = 0;is31fl3216->arg->duty_step = DEFAULT_FLASH_STEP;is31fl3216->arg->shift_mode = PERIPH_IS31_SHIFT_MODE_ACC;// 设置每个通道的占空比for (int i = 0; i < IS31FL3216_CH_NUM; i++) {is31fl3216->duty[i] = is31fl3216_config->duty[i];}// 5. 初始化底层驱动is31fl3216->handle = is31fl3216_init();AUDIO_MEM_CHECK(TAG, is31fl3216, {audio_free(is31fl3216->arg);vQueueDelete(is31fl3216->evt);audio_free(periph);vEventGroupDelete(is31fl3216->g_event_bit);audio_free(is31fl3216);return NULL;});// 6. 注册回调函数esp_periph_set_data(periph, is31fl3216);esp_periph_set_function(periph, _is31fl3216_init, NULL, _is31fl3216_destroy);return periph;
}
当外设被添加到外设集合并启动时,会调用_is31fl3216_init
函数(位于periph_is31fl3216.c
),该函数负责禁用所有LED通道、设置占空比为0,并创建运行任务:
// 文件:components/esp_peripherals/periph_is31fl3216.c
static esp_err_t _is31fl3216_init(esp_periph_handle_t self)
{// 获取IS31FL3216外设数据periph_is31fl3216_t *is31fl3216 = esp_periph_get_data(self);esp_err_t ret = ESP_OK;// 禁用所有LED通道is31fl3216_ch_disable(is31fl3216->handle, IS31FL3216_CH_ALL);// 设置所有通道占空比为0is31_leds_duty(is31fl3216->handle, 0, IS31FL3216_CH_ALL);// 创建运行任务xTaskCreate(is31fl3216_run_task, "is31fl3216_run_task", IS31FL3216_TASK_STACK_SIZE, (void *)self, IS31FL3216_TASK_PRIORITY, NULL);if (ret) {ESP_LOGE(TAG, "Failed to initialize is31fl3216");return ESP_FAIL;}return ESP_OK;
}
辅助函数详解
is31_leds_duty
// 文件:components/esp_peripherals/periph_is31fl3216.c
static esp_err_t is31_leds_duty(is31fl3216_handle_t *handle, int duty, uint16_t mask)
{esp_err_t ret = ESP_OK;// 遍历所有LED通道(16个)for (int i = 0; i < IS31FL3216_CH_NUM; i++) {// 检查当前通道是否在掩码中被选中if (mask & (1UL << i))// 设置选中通道的占空比ret |= is31fl3216_ch_duty_set(handle, 1UL << i, duty);}return ret;
}
该函数用于设置多个LED通道的亮度值。通过掩码(mask)参数选择要设置的通道,并将这些通道的占空比(duty)设置为相同的值。在初始化过程中,该函数用于将所有LED通道的亮度设置为0,即关闭所有LED。
is31fl3216_write_reg
// 文件:components/esp_peripherals/lib/IS31FL3216/IS31FL3216.c
static esp_err_t is31fl3216_write_reg(is31fl3216_handle_t handle, is31fl3216_reg_t regAddr, uint8_t *data, uint8_t data_num)
{// 参数检查IS31_PARAM_CHECK(NULL != data);// 获取设备结构体is31fl3216_dev_t *dev = (is31fl3216_dev_t *) handle;// 通过I2C总线写入寄存器// IS31FL3216_ADDRESS: 设备地址(0xE8)// IS31FL3216_WRITE_BIT: 写操作位(0)// regAddr: 目标寄存器地址// data: 要写入的数据// data_num: 数据字节数esp_err_t ret = i2c_bus_write_bytes(dev->bus, IS31FL3216_ADDRESS | IS31FL3216_WRITE_BIT, (uint8_t *)®Addr, 1, data, data_num);return ret;
}
该函数是底层I2C通信的封装,用于向IS31FL3216芯片的指定寄存器写入数据。所有的配置操作最终都调用此函数来实现与IS31FL3216芯片的通信。
is31fl3216_ch_disable
// 文件:components/esp_peripherals/lib/IS31FL3216/IS31FL3216.c
esp_err_t is31fl3216_ch_disable(is31fl3216_handle_t handle, is31_pwm_channel_t ch_bits)
{// 初始化返回值esp_err_t ret = ESP_OK;// 参数检查IS31_PARAM_CHECK(NULL != handle);// 获取当前的LED控制寄存器值// 将高字节寄存器值左移8位,然后与低字节寄存器值合并uint16_t value = ((uint16_t)Is31Value[IS31FL3216_REG_LED_CTRL_H]) << 8;value |= Is31Value[IS31FL3216_REG_LED_CTRL_L];// 遍历所有通道,将需要禁用的通道对应的位置0for (int i = 0; i < IS31FL3216_CH_NUM_MAX; ++i) {if ((ch_bits >> i) & 0x01) {// 将对应位清零,表示禁用该通道value = value & (~(1 << i));}}// 将新的值分别存入高低字节寄存器Is31Value[IS31FL3216_REG_LED_CTRL_H] = value >> 8;Is31Value[IS31FL3216_REG_LED_CTRL_L] = value;// 写入寄存器,从高字节开始写入2个字节ret = is31fl3216_write_reg(handle, IS31FL3216_REG_LED_CTRL_H, &Is31Value[IS31FL3216_REG_LED_CTRL_H], 2);return ret;
}
该函数用于禁用指定的LED通道。通过位操作将LED控制寄存器中对应的位置0,从而关闭指定的LED通道。该函数与is31fl3216_ch_enable
函数功能相反,在需要关闭某些LED通道时使用。
is31fl3216_ch_duty_set
// 文件:components/esp_peripherals/lib/IS31FL3216/IS31FL3216.c
esp_err_t is31fl3216_ch_duty_set(is31fl3216_handle_t handle, is31_pwm_channel_t ch_bits, uint8_t duty)
{// 初始化返回值esp_err_t ret = ESP_OK;// 参数检查IS31_PARAM_CHECK(NULL != handle);// 调用辅助函数设置指定通道的PWM占空比ret = is31fl3218S_channel_duty_by_bits(handle, ch_bits, duty);if (ret != ESP_OK) {IS31_CHECK_I2C_RES(ret);return ret;}// 更新寄存器,使设置生效ret = is31fl3216_update_reg(handle);if (ret != ESP_OK) {IS31_CHECK_I2C_RES(ret);return ret;}return ESP_OK;
}
该函数用于设置指定通道的PWM占空比,从而控制LED的亮度。函数先调用is31fl3218S_channel_duty_by_bits
设置具体的PWM值,然后调用is31fl3216_update_reg
使设置生效。该函数是LED亮度控制的核心函数之一。
is31fl3218S_channel_duty_by_bits
// 文件:components/esp_peripherals/lib/IS31FL3216/IS31FL3216.c
static esp_err_t is31fl3218S_channel_duty_by_bits(is31fl3216_handle_t handle, uint32_t by_bits, uint8_t duty)
{// 遍历所有可能的LED通道for (int i = 0; i < IS31FL3216_CH_NUM_MAX; i++) {// 检查当前通道是否在指定的位掩码中if ((by_bits >> i) & 0x1) {// 写入PWM寄存器,设置占空比// IS31FL3216_REG_PWM_16: PWM寄存器起始地址// IS31FL3216_CH_NUM_MAX - i - 1: 计算具体的寄存器偏移(通道号反向排列)// duty: 要设置的PWM占空比值esp_err_t ret = is31fl3216_write_reg(handle, IS31FL3216_REG_PWM_16 + (IS31FL3216_CH_NUM_MAX - i - 1), &duty, 1);if (ret == ESP_OK) {//成功则继续} else {// 失败则检查错误并返回IS31_CHECK_I2C_RES(ret);return ret;}}}return ESP_OK;
}
该函数是is31fl3216_ch_duty_set
的内部辅助函数,用于设置多个通道的PWM占空比值。它通过位操作遍历by_bits
参数中指定的所有通道,并将对应的PWM寄存器设置为指定的duty
值。注意到IS31FL3216的寄存器映射中,通道号与寄存器偏移是反向排列的。
is31fl3216_update_reg
// 文件:components/esp_peripherals/lib/IS31FL3216/IS31FL3216.c
esp_err_t is31fl3216_update_reg(is31fl3216_handle_t handle)
{// 参数检查IS31_PARAM_CHECK(NULL != handle);// 准备写入的数据(0值)uint8_t m = 0;// 写入更新寄存器,触发更新操作return is31fl3216_write_reg(handle, IS31FL3216_REG_UPDATE, &m, 1);
}
该函数用于触发IS31FL3216芯片的寄存器更新操作。根据IS31FL3216的数据手册,当写入PWM寄存器或LED控制寄存器后,需要写入更新寄存器(IS31FL3216_REG_UPDATE)来使设置生效。这是一种原子操作机制,确保所有设置同时生效,避免中间状态导致的闪烁或异常。
底层驱动初始化过程(IS31FL3216.c)
底层驱动初始化通过is31fl3216_init
函数(位于IS31FL3216.c
)完成,主要包括以下步骤:
- 配置I2C总线:设置I2C主机模式、SDA/SCL引脚、上拉电阻和时钟频率
- 分配驱动结构体:分配
is31fl3216_dev_t
结构体内存 - 创建I2C总线:调用
i2c_bus_create
函数创建I2C总线 - 设置设备地址:设置IS31FL3216的I2C地址(0xE8)
- 配置工作模式:设置正常工作模式、电流模式和电流值
// 文件:components/esp_peripherals/lib/IS31FL3216/IS31FL3216.c
is31fl3216_handle_t is31fl3216_init(void)
{// 1. 配置I2C总线i2c_config_t conf = {0};conf.mode = I2C_MODE_MASTER;conf.sda_io_num = I2C_MASTER_SDA_IO; // SDA引脚 = 18conf.sda_pullup_en = GPIO_PULLUP_ENABLE;conf.scl_io_num = I2C_MASTER_SCL_IO; // SCL引脚 = 23conf.scl_pullup_en = GPIO_PULLUP_ENABLE;conf.master.clk_speed = I2C_MASTER_FREQ_HZ; // 时钟频率 = 100kHz// 2. 分配驱动结构体is31fl3216_dev_t *led = (is31fl3216_dev_t *) audio_calloc(1, sizeof(is31fl3216_dev_t));// 3. 创建I2C总线led->bus = i2c_bus_create(I2C_MASTER_NUM, &conf);// 4. 设置设备地址led->addr = IS31FL3216_ADDRESS; // I2C地址 = 0xE8// 5. 配置工作模式IS31_ERROR_CHECK(ESP_OK == is31fl3216_power(led, IS31FL3216_PWR_NORMAL)); // 设置正常工作模式IS31_ERROR_CHECK(ESP_OK == is31fl3216_cur_mode_set(led, IS31FL3216_CUR_MODE_REXT)); // 设置电流模式为外部电阻模式IS31_ERROR_CHECK(ESP_OK == is31fl3216_cur_value_set(led, IS31FL3216_CUR_0_75)); // 设置电流值为最大值的75%return (is31fl3216_handle_t) led;
}
辅助函数详解
is31fl3216_power
// 文件:components/esp_peripherals/lib/IS31FL3216/IS31FL3216.c
esp_err_t is31fl3216_power(is31fl3216_handle_t handle, is31fl3216_pwr_t mode)
{// 参数检查IS31_PARAM_CHECK(NULL != handle);// 根据模式设置配置寄存器的第7位if (IS31FL3216_PWR_SHUTDOWN == mode) {// 关断模式:第7位置1Is31Value[IS31FL3216_REG_CONFIG] = (Is31Value[IS31FL3216_REG_CONFIG] & (~(1 << 7))) | (1 << 7);} else if (IS31FL3216_PWR_NORMAL == mode) {// 正常模式:第7位置0Is31Value[IS31FL3216_REG_CONFIG] = (Is31Value[IS31FL3216_REG_CONFIG] & (~(1 << 7)));} else {return ESP_FAIL;}// 写入寄存器esp_err_t ret = is31fl3216_write_reg(handle, IS31FL3216_REG_CONFIG, (uint8_t *) &Is31Value[IS31FL3216_REG_CONFIG], 1);return ret;
}
该函数通过修改配置寄存器(IS31FL3216_REG_CONFIG)的第7位来控制芯片的工作状态。在初始化时,调用此函数并设置为IS31FL3216_PWR_NORMAL
,使芯片进入正常工作模式,以便驱动LED。
is31fl3216_cur_mode_set
// 文件:components/esp_peripherals/lib/IS31FL3216/IS31FL3216.c
esp_err_t is31fl3216_cur_mode_set(is31fl3216_handle_t handle, is31fl3216_cur_mode_t mode)
{// 参数检查IS31_PARAM_CHECK(NULL != handle);// 根据模式设置配置寄存器的第4位if (IS31FL3216_CUR_MODE_REXT == mode) {// 外部电阻模式:第4位置0Is31Value[IS31FL3216_REG_CONFIG] = (Is31Value[IS31FL3216_REG_CONFIG] & (~(1 << 4)));} else if (IS31FL3216_CUR_MODE_AUDIO == mode) {// 音频调制模式:第4位置1Is31Value[IS31FL3216_REG_CONFIG] = (Is31Value[IS31FL3216_REG_CONFIG] & (~(1 << 4))) | (1 << 4);} else {return ESP_FAIL;}// 写入寄存器esp_err_t ret = is31fl3216_write_reg(handle, IS31FL3216_REG_CONFIG, (uint8_t *) &Is31Value[IS31FL3216_REG_CONFIG], 1);return ret;
}
该函数通过修改配置寄存器(IS31FL3216_REG_CONFIG)的第4位来控制输出电流的来源。在初始化时,调用此函数并设置为IS31FL3216_CUR_MODE_REXT
,选择使用外部电阻来设置电流值,而非使用音频信号调制。
is31fl3216_cur_value_set
// 文件:components/esp_peripherals/lib/IS31FL3216/IS31FL3216.c
esp_err_t is31fl3216_cur_value_set(is31fl3216_handle_t handle, is31fl3216_cur_value_t value)
{// 参数检查IS31_PARAM_CHECK(NULL != handle);// 设置LED效果寄存器的第4-6位为指定的电流值Is31Value[IS31FL3216_REG_LED_EFFECT] = (Is31Value[IS31FL3216_REG_LED_EFFECT] & (~(7 << 4))) | value << 4;// 写入寄存器esp_err_t ret = is31fl3216_write_reg(handle, IS31FL3216_REG_LED_EFFECT, &Is31Value[IS31FL3216_REG_LED_EFFECT], 1);return ret;
}
该函数通过修改LED效果寄存器(IS31FL3216_REG_LED_EFFECT)的第4-6位来设置输出电流的大小。在初始化时,调用此函数并设置为IS31FL3216_CUR_0_75
,表示输出电流为最大值的75%,这个值能够提供足够的LED亮度,同时避免过大电流导致的发热和功耗问题。
运行任务创建与状态管理
在_is31fl3216_init
函数中,会创建一个名为is31fl3216_run_task
的任务,该任务负责处理IS31FL3216的各种工作模式和状态转换:
// 文件:components/esp_peripherals/periph_is31fl3216.c
static void is31fl3216_run_task(void *Para)
{esp_periph_handle_t periph = (esp_periph_handle_t) Para;periph_is31fl3216_t *is31 = esp_periph_get_data(periph);periph_is31_arg_t is31_arg = {.max_light_num = IS31FL3216_CH_NUM,.light_num = 1,.light_mask = 1,.interval_time = 1000,.act_time = 0,.duty_step = DEFAULT_FLASH_STEP,.shift_mode = 0,};periph_is31_msg_t msg = {0};int wait_time_ms = portMAX_DELAY;bool task_run = true;// 清除销毁位xEventGroupClearBits(is31->g_event_bit, DESTROY_BIT);// 初始化变量int cur_duty = 0;int sig = 2;int cur_bits_mask = 0;int i = 0;uint16_t act_times = 0;// 任务主循环while (task_run) {// 从队列接收消息if (xQueueReceive(is31->evt, &msg, (wait_time_ms / portTICK_PERIOD_MS))) {ESP_LOGD(TAG, "cmd:%d, data:%"PRIu32, msg.type, msg.data);// 处理消息switch (msg.type) {case PERIPH_IS31_CMD_CHG_STATE:// 处理状态变更命令memcpy(&is31_arg, is31->arg, sizeof(periph_is31_arg_t));wait_time_ms = is31->arg->interval_time;memset(is31->arg, 0, sizeof(periph_is31_arg_t));is31->arg->interval_time = portMAX_DELAY;is31->arg->max_light_num = IS31FL3216_CH_NUM;is31->arg->duty_step = DEFAULT_FLASH_STEP;is31_change_state(is31, msg.data, &is31_arg);// 设置闪烁步长和动作次数if (IS31FL3216_STATE_FLASH == msg.data) {sig = is31_arg.duty_step;}if (is31_arg.act_time && wait_time_ms) {act_times = is31_arg.act_time / wait_time_ms;} else {act_times = 0;}break;case PERIPH_IS31_CMD_QUIT:// 处理退出命令task_run = false;if (is31->g_event_bit) {xEventGroupSetBits(is31->g_event_bit, DESTROY_BIT);}break;default:break;}if (task_run == false) {ESP_LOGW(TAG, "Quit is31fl3216 task ...");break;}}// 根据当前状态执行相应操作switch (is31->cur_state) {case IS31FL3216_STATE_FLASH:// 闪烁模式处理is31_leds_duty(is31->handle, cur_duty, is31_arg.light_mask);is31_leds_ctrl(is31->handle, is31_arg.light_mask);cur_duty += sig;if (cur_duty > IS31FL3216_DUTY_MAX) {cur_duty = IS31FL3216_DUTY_MAX;sig = -(is31_arg.duty_step);}if (cur_duty < 0) {cur_duty = 0;sig = (is31_arg.duty_step);}// 检查动作时间if (is31_arg.act_time == 0) {act_times = 0;break;}act_times--;if (act_times == 0) {wait_time_ms = portMAX_DELAY;is31->cur_state = IS31FL3216_STATE_UNKNOWN;is31_leds_ctrl(is31->handle, 0);}break;case IS31FL3216_STATE_SHIFT:// 移位模式处理if (is31_arg.shift_mode == PERIPH_IS31_SHIFT_MODE_SINGLE) {// 单一模式:固定数量的LED依次移动cur_bits_mask = ((1UL << is31_arg.light_num) - 1) << (i++);if (i == (is31_arg.max_light_num - is31_arg.light_num + 1)) {i = 0;}} else if (is31_arg.shift_mode == PERIPH_IS31_SHIFT_MODE_ACC) {// 累积模式:LED数量逐渐增加,然后重置cur_bits_mask = (1UL << (is31_arg.light_num * ((i++) + 1))) - 1;if ((cur_bits_mask >> is31_arg.max_light_num) & 0x01) {cur_bits_mask = 0;i = 0;}}// 设置LED亮度和控制状态is31_leds_duty(is31->handle, IS31FL3216_DUTY_MAX, cur_bits_mask);is31_leds_ctrl(is31->handle, cur_bits_mask);// 检查动作时间if (is31_arg.act_time == 0) {act_times = 0;break;}act_times--;if (act_times == 0) {wait_time_ms = portMAX_DELAY;is31->cur_state = IS31FL3216_STATE_UNKNOWN;is31_leds_ctrl(is31->handle, 0);}break;default:break;}}// 删除任务vTaskDelete(NULL);
}
is31fl3216_run_task
函数是IS31FL3216外设的核心任务,负责处理不同工作模式下的LED控制逻辑。下面是该函数的时序图,展示了其工作流程和状态转换逻辑:
该时序图展示了is31fl3216_run_task
函数的主要工作流程:
-
初始化阶段:设置初始参数和状态
-
消息处理循环:
- 从队列中接收命令消息
- 处理状态变更命令(PERIPH_IS31_CMD_CHG_STATE)
- 处理退出命令(PERIPH_IS31_CMD_QUIT)
-
状态处理:
- 闪烁模式(IS31FL3216_STATE_FLASH):通过动态调整PWM占空比实现LED亮度的渐变
- 移位模式(IS31FL3216_STATE_SHIFT):实现LED的移位效果,支持单一模式和累积模式
-
超时处理:当动作时间到期时,关闭LED并切换到未知状态
通过这种设计,IS31FL3216外设能够实现多种复杂的LED灯光效果,如呼吸灯、跑马灯等。
辅助函数
is31_leds_ctrl
// 文件:components/esp_peripherals/periph_is31fl3216.c
static esp_err_t is31_leds_ctrl(is31fl3216_handle_t *handle, uint16_t mask)
{// 初始化返回值esp_err_t ret = ESP_OK;// 遍历所有LED通道for (int i = 0; i < IS31FL3216_CH_NUM; i++) {// 检查当前通道是否在掩码中被选中if (mask & (1UL << i)) {// 如果被选中,启用该通道ret |= is31fl3216_ch_enable(handle, 1UL << i);} else {// 如果未被选中,禁用该通道ret |= is31fl3216_ch_disable(handle, 1UL << i);}}return ret;
}
该函数用于控制多个LED通道的开关状态。通过掩码(mask)参数选择要启用的通道,对于掩码中置1的位启用相应的LED通道,对于置0的位禁用相应的LED通道。在闪烁和移位模式中,该函数用于控制哪些LED处于激活状态。
is31fl3216_ch_enable
// 文件:components/esp_peripherals/lib/IS31FL3216/IS31FL3216.c
esp_err_t is31fl3216_ch_enable(is31fl3216_handle_t handle, is31_pwm_channel_t ch_bits)
{// 初始化返回值esp_err_t ret = ESP_OK;// 参数检查IS31_PARAM_CHECK(NULL != handle);// 初始化值变量uint16_t value = 0;// 遍历所有通道,生成控制值for (int i = 0; i < IS31FL3216_CH_NUM_MAX; ++i) {// 检查当前通道是否在指定的位掩码中if ((ch_bits >> i) & 0x01) {// 将对应位置1,表示启用该通道value |= (1 << i);}}// 更新LED控制寄存器值// 高字节寄存器保存高位通道状态Is31Value[IS31FL3216_REG_LED_CTRL_H] |= value >> 8;// 低字节寄存器保存低位通道状态Is31Value[IS31FL3216_REG_LED_CTRL_L] |= value;// 写入寄存器,从高字节开始写入2个字节ret = is31fl3216_write_reg(handle, IS31FL3216_REG_LED_CTRL_H, &Is31Value[IS31FL3216_REG_LED_CTRL_H], 2);return ret;
}
该函数用于启用指定的LED通道。与is31fl3216_ch_disable
函数功能相反,该函数通过位操作将LED控制寄存器中对应的位置1,从而打开指定的LED通道。注意该函数使用按位或操作,保留原有的其他通道状态。
状态切换处理
当应用程序调用periph_is31fl3216_set_state
函数改变IS31FL3216的工作状态时,会向任务队列发送状态变更命令,然后由is31_change_state
函数处理状态切换:
// 文件:components/esp_peripherals/periph_is31fl3216.c
static esp_err_t is31_change_state(periph_is31fl3216_t *is31, int state, periph_is31_arg_t *arg)
{esp_err_t ret = ESP_OK;switch (state) {case IS31FL3216_STATE_OFF:// 关闭模式:禁用所有LED通道ret |= is31fl3216_ch_disable(is31->handle, arg->light_mask);arg->interval_time = portMAX_DELAY;is31->cur_state = IS31FL3216_STATE_OFF;break;case IS31FL3216_STATE_ON:// 常亮模式:如果之前是音频模式,需要切换到PWM模式if (is31->cur_state == IS31FL3216_STATE_BY_AUDIO) {ret |= is31fl3216_work_mode_set(is31->handle, IS31FL3216_MODE_PWM);is31_leds_duty(is31->handle, IS31FL3216_DUTY_MAX, arg->light_mask);}// 启用指定的LED通道is31_leds_ctrl(is31->handle, arg->light_mask);arg->interval_time = portMAX_DELAY;is31->cur_state = IS31FL3216_STATE_ON;break;case IS31FL3216_STATE_FLASH:// 闪烁模式:如果之前是音频模式,需要切换到PWM模式if (is31->cur_state == IS31FL3216_STATE_BY_AUDIO) {ret |= is31fl3216_work_mode_set(is31->handle, IS31FL3216_MODE_PWM);}is31->cur_state = IS31FL3216_STATE_FLASH;break;case IS31FL3216_STATE_SHIFT:// 移位模式:如果之前是音频模式,需要切换到PWM模式if (is31->cur_state == IS31FL3216_STATE_BY_AUDIO) {ret |= is31fl3216_work_mode_set(is31->handle, IS31FL3216_MODE_PWM);}is31->cur_state = IS31FL3216_STATE_SHIFT;break;case IS31FL3216_STATE_BY_AUDIO:// 音频响应模式:重置芯片,设置帧模式is31fl3216_reset(is31->handle);is31fl3216_work_mode_set(is31->handle, IS31FL3216_MODE_FRAME);is31fl3216_sample_rate_set(is31->handle, 0xB4); // 设置ADC采样率is31fl3216_frame_value_set(is31->handle, 1, (uint8_t *)&light_audio_frames, sizeof(light_audio_frames));is31fl3216_first_frame_set(is31->handle, 0);is31->cur_state = IS31FL3216_STATE_BY_AUDIO;arg->interval_time = portMAX_DELAY;break;default:ESP_LOGE(TAG, "State %d is not supported", state);break;}return ret;
}
该状态图展示了IS31FL3216外设的五种主要工作状态及其之间的转换逻辑:
-
关闭模式(IS31FL3216_STATE_OFF):禁用指定的LED通道,设置无限等待时间
-
常亮模式(IS31FL3216_STATE_ON):启用指定的LED通道,并设置为最大亮度
-
闪烁模式(IS31FL3216_STATE_FLASH):设置闪烁模式,在运行任务中实现LED亮度的渐变
-
移位模式(IS31FL3216_STATE_SHIFT):设置移位模式,在运行任务中实现LED的移位效果
-
音频响应模式(IS31FL3216_STATE_BY_AUDIO):设置芯片为帧模式,使其能够响应音频信号
值得注意的是,当从音频响应模式切换到其他模式时,需要先将芯片切换回 PWM 模式。这是因为音频响应模式使用的是帧模式,而其他模式都基于 PWM 模式实现。
辅助函数
is31fl3216_work_mode_set
// 文件:components/esp_peripherals/lib/IS31FL3216/IS31FL3216.c
esp_err_t is31fl3216_work_mode_set(is31fl3216_handle_t handle, is31fl3216_work_mode_t mode)
{// 参数检查IS31_PARAM_CHECK(NULL != handle);// 根据模式设置配置寄存器的第5-6位if (IS31FL3216_MODE_PWM == mode) {// PWM模式:第5-6位置0Is31Value[IS31FL3216_REG_CONFIG] = (Is31Value[IS31FL3216_REG_CONFIG] & (~(3 << 5)));} else if (IS31FL3216_MODE_AUTO_FRAME == mode) {// 自动帧模式:第5位置1,第6位置0Is31Value[IS31FL3216_REG_CONFIG] = (Is31Value[IS31FL3216_REG_CONFIG] & (~(3 << 5))) | (1 << 5);} else if (IS31FL3216_MODE_FRAME == mode) {// 帧模式:第5位置0,第6位置1Is31Value[IS31FL3216_REG_CONFIG] = (Is31Value[IS31FL3216_REG_CONFIG] & (~(3 << 5))) | (2 << 5);} else {return ESP_FAIL;}// 写入寄存器esp_err_t ret = is31fl3216_write_reg(handle, IS31FL3216_REG_CONFIG, (uint8_t *) &Is31Value[IS31FL3216_REG_CONFIG], 1);return ret;
}
该函数用于设置IS31FL3216芯片的工作模式。IS31FL3216支持三种工作模式:
- PWM模式:直接控制每个LED的PWM占空比,用于常亮、闪烁和移位模式
- 自动帧模式:自动播放预定义的帧序列
- 帧模式:响应音频信号,用于音频响应模式
is31fl3216_sample_rate_set
// 文件:components/esp_peripherals/lib/IS31FL3216/IS31FL3216.c
esp_err_t is31fl3216_sample_rate_set(is31fl3216_handle_t handle, uint32_t value)
{// 参数检查IS31_PARAM_CHECK(NULL != handle);// 将值转换为字节uint8_t dat = value;// 写入ADC采样率寄存器esp_err_t ret = is31fl3216_write_reg(handle, IS31FL3216_REG_ADC_RATE, &dat, 1);return ret;
}
该函数用于设置IS31FL3216芯片的ADC采样率。在音频响应模式下,芯片需要采样外部音频信号,该函数设置采样的速率。在状态切换中,音频模式使用的值为0xB4,这是一个适合大多数音频应用的采样率。
is31fl3216_frame_value_set
// 文件:components/esp_peripherals/lib/IS31FL3216/IS31FL3216.c
esp_err_t is31fl3216_frame_value_set(is31fl3216_handle_t handle, uint32_t num, uint8_t *data, uint32_t len)
{// 参数检查IS31_PARAM_CHECK(NULL != handle);IS31_PARAM_CHECK(NULL != data);// 计算帧控制寄存器的起始地址// IS31FL3216_REG_FRAME1_CTRL: 第一帧的起始地址// (num - 1) * 18: 每个帧占据18个字节,计算偏移uint8_t startAddr = IS31FL3216_REG_FRAME1_CTRL + (num - 1) * 18;// 写入帧数据esp_err_t ret = is31fl3216_write_reg(handle, startAddr, data, len);return ret;
}
该函数用于设置IS31FL3216芯片的帧数据。在帧模式或自动帧模式下,芯片可以存储多个帧,每个帧定义了16个LED通道的状态。在音频响应模式下,该函数用于设置音频响应的帧数据,定义了不同音频强度下的LED显示效果。
is31fl3216_first_frame_set
// 文件:components/esp_peripherals/lib/IS31FL3216/IS31FL3216.c
esp_err_t is31fl3216_first_frame_set(is31fl3216_handle_t handle, uint32_t frame)
{// 参数检查IS31_PARAM_CHECK(NULL != handle);// 将帧编号左移5位,存入到寄存器的第5-7位uint8_t dat = frame << 5;// 写入帧起始寄存器esp_err_t ret = is31fl3216_write_reg(handle, IS31FL3216_REG_FRAME_START, &dat, 1);return ret;
}
该函数用于设置IS31FL3216芯片的起始帧。在帧模式或自动帧模式下,芯片需要知道从哪一帧开始播放。在音频响应模式下,该函数将起始帧设置为0,表示从第一帧开始响应音频信号。
IS31FL3216外设完整初始化时序图
下图展示了IS31FL3216外设从应用程序调用到底层驱动完成初始化的完整流程:
这个时序图详细展示了IS31FL3216外设的完整生命周期,包括以下几个主要阶段:
-
外设初始化阶段:应用程序调用
periph_is31fl3216_init
创建外设句柄、分配内存、创建同步对象并初始化底层驱动 -
底层驱动初始化:配置I2C总线、设置芯片工作模式和电流参数
-
外设启动阶段:当外设被添加到外设集合并启动时,调用
_is31fl3216_init
函数初始化LED状态并创建运行任务 -
任务运行阶段:
is31fl3216_run_task
初始化参数并进入主循环,等待队列消息 -
状态切换阶段:应用程序调用
periph_is31fl3216_set_state
发送状态变更命令,任务根据不同的状态执行相应的操作
这个完整的时序图清晰地展示了各个组件之间的交互和数据流向,有助于理解IS31FL3216外设的工作原理和实现机制。
总结
IS31FL3216外设的初始化过程涉及多个层次的组件交互,包括外设层、底层驱动、I2C总线和任务管理。初始化完成后,IS31FL3216外设可以通过状态切换实现不同的LED效果,如常亮、闪烁、移位和音频响应等。整个初始化流程设计合理,各组件职责明确,便于维护和扩展。
IS31FL3216外设销毁流程分析
销毁流程概述
IS31FL3216外设的销毁流程是初始化流程的逆过程,主要涉及资源释放、任务终止和内存回收。销毁过程同样分为外设层(Peripheral Layer)和底层驱动(Driver Layer)两个层次,确保所有在初始化时分配的资源都被正确释放,防止内存泄漏和资源占用。
外设层销毁过程(periph_is31fl3216.c)
外设层销毁主要通过_is31fl3216_destroy
函数(位于periph_is31fl3216.c
)完成,该函数在外设被移除时由ESP-ADF框架调用。主要步骤包括:
- 验证参数:验证外设句柄的有效性
- 获取外设数据:从外设句柄中获取IS31FL3216外设数据
- 发送退出命令:向任务发送退出命令,通知任务终止运行
- 等待任务退出:等待任务设置销毁位,确认任务已安全退出,并删除事件组
- 禁用LED通道:禁用所有LED通道,确保LED关闭
- 释放底层驱动:调用
is31fl3216_deinit
函数释放底层驱动资源 - 释放内存和同步对象:释放事件组、消息队列和分配的内存
// 文件:components/esp_peripherals/periph_is31fl3216.c
static esp_err_t _is31fl3216_destroy(esp_periph_handle_t self)
{// 1. 验证参数VALIDATE_IS31FL3216(self, ESP_FAIL);// 2. 获取外设数据periph_is31fl3216_t *is31fl3216 = esp_periph_get_data(self);// 3. 发送退出命令is31_evt_send(is31fl3216->evt, PERIPH_IS31_CMD_QUIT, 0, 0);// 4. 等待任务退出并删除事件组if (is31fl3216->g_event_bit) {xEventGroupWaitBits(is31fl3216->g_event_bit, DESTROY_BIT, pdTRUE, pdFALSE, portMAX_DELAY);vEventGroupDelete(is31fl3216->g_event_bit);is31fl3216->g_event_bit = NULL;}// 5. 禁用所有LED通道并释放底层驱动esp_err_t ret = ESP_OK;ret |= is31fl3216_ch_disable(is31fl3216->handle, IS31FL3216_CH_ALL);ret |= is31fl3216_deinit(is31fl3216->handle);// 6. 释放内存和同步对象audio_free(is31fl3216->arg);vQueueDelete(is31fl3216->evt);audio_free(is31fl3216);// 7. 检查错误并返回结果if (ret) {ESP_LOGE(TAG, "Error occurred when stopping the is31fl3216");return ESP_FAIL;}return ESP_OK;
}// 发送事件消息
static void is31_evt_send(void *que, periph_is31_cmd_t type, uint32_t data, int dir)
{periph_is31_msg_t evt = {0};evt.type = type;evt.data = data;if (dir) {xQueueSendToFront(que, &evt, 0) ;} else {xQueueSend(que, &evt, 0);}
}
底层驱动销毁过程(IS31FL3216.c)
底层驱动销毁通过is31fl3216_deinit
函数(位于IS31FL3216.c
)完成,主要包括以下步骤:
- 释放I2C总线:释放I2C总线资源并将总线指针设置为NULL
- 释放驱动结构体:释放分配的驱动结构体内存
// 文件:components/esp_peripherals/lib/IS31FL3216/IS31FL3216.c
esp_err_t is31fl3216_deinit(is31fl3216_handle_t handle)
{// 获取设备结构体is31fl3216_dev_t *dev = (is31fl3216_dev_t *) handle;// 1. 释放I2C总线if (dev->bus) {i2c_bus_delete(dev->bus);dev->bus = NULL;}// 2. 释放驱动结构体audio_free(dev);return ESP_OK;
}
完整销毁时序图
下图展示了IS31FL3216外设从应用程序调用到底层驱动完成销毁的完整流程:
这个时序图详细展示了IS31FL3216外设的完整销毁流程,包括以下几个主要阶段:
-
外设销毁阶段:应用程序调用
esp_periph_remove
函数移除外设,ESP-ADF框架调用注册的销毁回调函数。 -
任务终止阶段:销毁函数向任务发送退出命令,任务接收命令后设置退出标志并安全退出。
-
同步与事件组释放阶段:销毁函数等待任务设置销毁位,确保任务已完全退出,并删除事件组。
-
底层驱动销毁:调用
is31fl3216_deinit
函数释放I2C总线和驱动结构体。 -
资源释放阶段:释放事件组、消息队列和分配的内存,完成所有资源的回收。
通过这种设计,IS31FL3216外设能够安全、有序地释放所有资源,避免内存泄漏和资源占用问题。
总结
IS31FL3216外设的销毁流程是一个有序、安全的资源释放过程,涉及多个层次的组件交互,包括外设层、底层驱动、I2C总线和任务管理。通过发送退出命令、等待任务退出、释放底层驱动和释放内存等步骤,确保所有资源都被正确回收,避免内存泄漏和资源占用问题。整个销毁流程与初始化流程形成了完整的对应关系,体现了良好的软件设计原则。
IS31FL3216外设事件处理机制
事件类型与消息结构
IS31FL3216外设的事件处理机制基于消息队列和状态机实现,通过事件驱动方式控制LED灯的不同工作模式和状态切换。事件类型主要包括:
typedef enum {PERIPH_IS31_CMD_CHG_STATE, // 状态切换命令PERIPH_IS31_CMD_QUIT, // 退出命令
} periph_is31_cmd_t;
事件消息结构定义如下:
typedef struct {periph_is31_cmd_t type; // 事件类型uint32_t data; // 事件数据
} periph_is31_msg_t;
事件发送机制
IS31FL3216外设通过is31_evt_send
函数发送事件消息,该函数将事件消息放入消息队列,供任务处理:
static void is31_evt_send(void *que, periph_is31_cmd_t type, uint32_t data, int dir)
{periph_is31_msg_t evt = {0};evt.type = type;evt.data = data;if (dir) {xQueueSendToFront(que, &evt, 0) ; // 高优先级消息,放入队列前端} else {xQueueSend(que, &evt, 0); // 普通消息,放入队列尾部}
}
应用程序通过调用以下API函数间接发送事件:
- 状态切换事件:通过
periph_is31fl3216_set_state
函数发送状态切换事件 - 参数设置事件:通过各种参数设置函数修改参数,在下一次状态切换时生效
事件处理流程
IS31FL3216外设的事件处理在is31fl3216_run_task
任务中进行,主要处理流程如下:
- 事件接收:任务通过
xQueueReceive
函数从消息队列接收事件消息 - 事件解析:根据事件类型执行相应的处理逻辑
- 状态切换:对于状态切换事件,调用
is31_change_state
函数切换到新状态 - 状态处理:根据当前状态执行相应的LED控制逻辑
// 事件接收与处理示例代码片段
if (xQueueReceive(is31->evt, &msg, (wait_time_ms / portTICK_PERIOD_MS))) {switch (msg.type) {case PERIPH_IS31_CMD_CHG_STATE:memcpy(&is31_arg, is31->arg, sizeof(periph_is31_arg_t));wait_time_ms = is31->arg->interval_time;// 重置参数并切换状态memset(is31->arg, 0, sizeof(periph_is31_arg_t));is31->arg->interval_time = portMAX_DELAY;is31->arg->max_light_num = IS31FL3216_CH_NUM;is31->arg->duty_step = DEFAULT_FLASH_STEP;is31_change_state(is31, msg.data, &is31_arg);break;case PERIPH_IS31_CMD_QUIT:task_run = false;if (is31->g_event_bit) {xEventGroupSetBits(is31->g_event_bit, DESTROY_BIT);}break;default:break;}
}
状态机实现
IS31FL3216外设通过状态机管理不同的工作模式,主要状态包括:
- IS31FL3216_STATE_OFF:关闭状态,禁用所有LED通道
- IS31FL3216_STATE_ON:常亮状态,启用指定的LED通道
- IS31FL3216_STATE_FLASH:闪烁状态,LED亮度周期性变化
- IS31FL3216_STATE_SHIFT:移位状态,LED通道按照指定模式移位
- IS31FL3216_STATE_BY_AUDIO:音频响应状态,LED亮度随音频信号变化
状态切换通过is31_change_state
函数实现:
static esp_err_t is31_change_state(periph_is31fl3216_t *is31, int state, periph_is31_arg_t *arg)
{esp_err_t ret = ESP_OK;switch (state) {case IS31FL3216_STATE_OFF:ret |= is31fl3216_ch_disable(is31->handle, arg->light_mask);arg->interval_time = portMAX_DELAY;is31->cur_state = IS31FL3216_STATE_OFF;break;case IS31FL3216_STATE_ON:if (is31->cur_state == IS31FL3216_STATE_BY_AUDIO) {ret |= is31fl3216_work_mode_set(is31->handle, IS31FL3216_MODE_PWM);is31_leds_duty(is31->handle, IS31FL3216_DUTY_MAX, arg->light_mask);}is31_leds_ctrl(is31->handle, arg->light_mask);arg->interval_time = portMAX_DELAY;is31->cur_state = IS31FL3216_STATE_ON;break;// 其他状态处理...}return ret;
}
状态控制与参数配置接口
状态控制接口
IS31FL3216外设的核心控制接口是periph_is31fl3216_set_state
函数,用于切换LED的工作状态:
esp_err_t periph_is31fl3216_set_state(esp_periph_handle_t periph, periph_is31fl3216_state_t state)
{periph_is31fl3216_t *is31fl3216 = esp_periph_get_data(periph);is31_evt_send(is31fl3216->evt, PERIPH_IS31_CMD_CHG_STATE, state, 0);return ESP_OK;
}
这个函数的实现非常简洁,它通过以下步骤实现状态切换:
- 从外设句柄中获取IS31FL3216外设数据
- 调用
is31_evt_send
函数发送PERIPH_IS31_CMD_CHG_STATE
事件,并将目标状态作为事件数据 - 事件消息被放入消息队列,等待
is31fl3216_run_task
任务处理
这种设计实现了控制逻辑与实际执行的解耦,应用程序只需要发送状态切换请求,而不需要关心具体的实现细节。
参数配置接口
IS31FL3216外设提供了一系列参数配置接口,用于设置LED的工作参数:
-
periph_is31fl3216_set_blink_pattern:设置LED通道掩码,指定哪些通道参与工作
esp_err_t periph_is31fl3216_set_blink_pattern(esp_periph_handle_t periph, uint16_t blink_pattern) {periph_is31fl3216_t *is31fl3216 = esp_periph_get_data(periph);is31fl3216->arg->light_mask = blink_pattern;return ESP_OK; }
-
periph_is31fl3216_set_duty_step:设置闪烁模式下的亮度变化步长
-
periph_is31fl3216_set_interval:设置状态更新的时间间隔
-
periph_is31fl3216_set_shift_mode:设置移位模式(单通道移位或累积移位)
-
periph_is31fl3216_set_light_on_num:设置移位模式下点亮的LED数量
-
periph_is31fl3216_set_act_time:设置动作持续时间
事件处理时序图
总结
IS31FL3216外设的事件处理机制基于消息队列和状态机实现,通过事件驱动方式控制LED灯的不同工作模式和状态切换。应用程序通过调用API函数发送事件,任务接收事件并执行相应的处理逻辑。状态机管理不同的工作模式,实现LED灯的常亮、闪烁、移位和音频响应等效果。参数配置接口提供了灵活的LED控制能力,满足不同应用场景的需求。
典型示例
#include "esp_log.h"
#include "esp_peripherals.h"
#include "periph_is31fl3216.h"#define TAG "IS31FL3216_DEMO"void is31fl3216_demo(void)
{// 1. 初始化外设管理器esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();esp_periph_set_handle_t set = esp_periph_set_init(&periph_cfg);// 2. 配置IS31FL3216参数periph_is31fl3216_cfg_t is31_cfg = {.is31fl3216_pattern = 0xFFFF, // 启用所有LED.state = IS31FL3216_STATE_OFF // 初始状态为关闭};// 3. 设置所有LED通道亮度为最大for (int i = 0; i < IS31FL3216_CH_NUM; i++) {is31_cfg.duty[i] = 255;}// 4. 初始化并启动IS31FL3216外设esp_periph_handle_t is31_handle = periph_is31fl3216_init(&is31_cfg);esp_periph_start(set, is31_handle);// 5. 设置常亮模式(所有LED常亮)periph_is31fl3216_set_blink_pattern(is31_handle, 0xFFFF);periph_is31fl3216_set_state(is31_handle, IS31FL3216_STATE_ON);vTaskDelay(3000 / portTICK_PERIOD_MS); // 持续3秒// 6. 设置闪烁模式(所有LED闪烁)periph_is31fl3216_set_blink_pattern(is31_handle, 0xFFFF);periph_is31fl3216_set_interval(is31_handle, 100); // 100ms间隔periph_is31fl3216_set_duty_step(is31_handle, 5); // 亮度变化步长periph_is31fl3216_set_state(is31_handle, IS31FL3216_STATE_FLASH);vTaskDelay(3000 / portTICK_PERIOD_MS); // 持续3秒// 7. 设置移位模式(跑马灯效果)periph_is31fl3216_set_shift_mode(is31_handle, PERIPH_IS31_SHIFT_MODE_SINGLE);periph_is31fl3216_set_light_on_num(is31_handle, 1, IS31FL3216_CH_NUM);periph_is31fl3216_set_interval(is31_handle, 200); // 200ms间隔periph_is31fl3216_set_state(is31_handle, IS31FL3216_STATE_SHIFT);vTaskDelay(3000 / portTICK_PERIOD_MS); // 持续3秒// 8. 设置音频响应模式(LED亮度随音频变化)periph_is31fl3216_set_state(is31_handle, IS31FL3216_STATE_BY_AUDIO);// 9. 清理资源(实际应用中在退出前调用)// esp_periph_set_stop_all(set);// esp_periph_set_destroy(set);
}
常用参数和配置
LED通道模式(用于periph_is31fl3216_set_blink_pattern
)
#define LED_PATTERN_ALL 0xFFFF // 所有LED
#define LED_PATTERN_EVEN 0xAAAA // 偶数LED
#define LED_PATTERN_ODD 0x5555 // 奇数LED
LED工作状态(用于periph_is31fl3216_set_state
)
typedef enum {IS31FL3216_STATE_OFF = 0, // LED关闭IS31FL3216_STATE_ON, // LED常亮IS31FL3216_STATE_FLASH, // LED闪烁IS31FL3216_STATE_SHIFT, // LED移位(跑马灯)IS31FL3216_STATE_BY_AUDIO, // LED随音频变化IS31FL3216_STATE_UNKNOWN // 未知状态
} periph_is31fl3216_state_t;
移位模式(用于periph_is31fl3216_set_shift_mode
)
typedef enum {PERIPH_IS31_SHIFT_MODE_SINGLE = 0, // 单个LED移位PERIPH_IS31_SHIFT_MODE_ACC, // 累积LED移位(逐渐增加)
} periph_is31_shift_mode_t;
常见应用场景
- 音频可视化:在音频播放器中实现音频频谱显示
- 状态指示:用不同的LED模式指示设备的不同工作状态
- 用户界面反馈:为用户操作提供可视化反馈
- 装饰照明:实现各种装饰性照明效果
注意:使用音频响应模式时,需确保音频输入正确连接到IS31FL3216芯片的音频输入引脚。