当前位置: 首页 > news >正文

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

目录

  • ESP-ADF外设子系统深度解析:esp_peripherals组件架构与核心设计(显示输出类外设之LCD)
    • 简介
    • 模块概述
      • 功能定义
      • 架构位置
      • 核心特性
    • LCD外设分析
      • LCD外设概述
        • LCD外设层次架构图
      • LCD外设API和数据结构
        • 外设层API
        • 公共API
        • 内部数据结构
      • LCD外设配置选项
      • LCD外设初始化流程
        • 外设层初始化过程(periph_lcd.c)
        • LCD IO总线设置过程(_setup_lcd函数)
        • LCD面板初始化过程(_lcd_init函数)
        • LCD默认复位函数(_lcd_rest_default函数)
        • LCD外设完整初始化时序图
      • LCD外设销毁流程
        • LCD外设销毁时序图
        • 销毁流程注意事项
      • LCD外设事件处理
        • LCD外设事件处理特点
        • LCD外设与应用程序交互流程
        • 典型应用代码示例

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

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

LCD外设分析

LCD外设概述

LCD外设基于ESP-IDF的LCD驱动框架实现,支持多种LCD控制器和接口类型,主要用于显示文本、图形和用户界面。LCD外设通过SPI或I2C接口与LCD控制器通信,提供了显示初始化、显示控制和显示内容更新等功能。

LCD外设实现仅在ESP-IDF 4.4.0及以上版本可用,它主要依赖于ESP-IDF的esp_lcd组件,该组件提供了统一的LCD面板驱动接口。

LCD外设实现主要包含一个层次:

  1. 外设层:负责将LCD集成到ESP-ADF外设系统中,处理初始化、配置和生命周期管理。
    • 头文件:components/esp_peripherals/include/periph_lcd.h
    • 实现文件:components/esp_peripherals/periph_lcd.c
LCD外设层次架构图
ESP-IDF LCD驱动
外设层 periph_lcd.c
调用
调用
设置
设置
设置
调用
调用
调用
调用
调用
调用
调用
调用
调用
调用
调用
LCD面板初始化
esp_lcd_panel_init
颜色反转设置
esp_lcd_panel_invert_color
显示区域设置
esp_lcd_panel_set_gap
XY坐标交换
esp_lcd_panel_swap_xy
镜像设置
esp_lcd_panel_mirror
显示开关控制
esp_lcd_panel_disp_on_off
_lcd_init
periph_lcd_init
_lcd_run
_lcd_destroy
_setup_lcd
ESP-ADF应用
esp_periph_create
new_panel_io
new_lcd_panel
esp_lcd_panel_del
esp_lcd_panel_io_del

LCD外设API和数据结构

外设层API

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

公共API
// LCD外设初始化函数
esp_periph_handle_t periph_lcd_init(periph_lcd_cfg_t *config);// 获取LCD面板句柄
esp_lcd_panel_handle_t periph_lcd_get_panel_handle(esp_periph_handle_t handle);// LCD面板IO总线获取回调函数类型
typedef esp_err_t (*get_lcd_io_bus)(void *bus, esp_lcd_panel_io_spi_config_t *io_config, esp_lcd_panel_io_handle_t *out_panel_io);// LCD面板获取回调函数类型
typedef esp_err_t (*get_lcd_panel)(const esp_lcd_panel_io_handle_t panel_io, const esp_lcd_panel_dev_config_t *panel_dev_config,esp_lcd_panel_handle_t *ret_panel);// LCD供应商初始化回调函数类型
typedef esp_err_t (*lcd_vender_init_func)(const esp_lcd_panel_io_handle_t panel_io);// LCD复位回调函数类型
typedef esp_err_t (*perph_lcd_rest)(esp_periph_handle_t self, void *ctx);// LCD配置结构体
typedef struct {void                                *io_bus;               // IO总线句柄get_lcd_io_bus                      new_panel_io;          // 面板IO创建函数esp_lcd_panel_io_spi_config_t       *lcd_io_cfg;           // LCD IO配置get_lcd_panel                       new_lcd_panel;         // 面板创建函数lcd_vender_init_func                vendor_init;           // 供应商初始化函数esp_lcd_panel_dev_config_t          *lcd_dev_cfg;          // LCD设备配置perph_lcd_rest                      rest_cb;               // 复位回调函数void                                *rest_cb_ctx;          // 复位回调上下文bool                                lcd_swap_xy;           // 是否交换XY坐标bool                                lcd_mirror_x;          // 是否X轴镜像bool                                lcd_mirror_y;          // 是否Y轴镜像bool                                lcd_color_invert;      // 是否颜色反转
} periph_lcd_cfg_t;
内部数据结构
// LCD外设内部结构体 (定义在periph_lcd.c中)
typedef struct periph_lcd {void                                *io_bus;               // IO总线句柄get_lcd_io_bus                      new_panel_io;          // 面板IO创建函数esp_lcd_panel_io_spi_config_t       lcd_io_cfg;            // LCD IO配置get_lcd_panel                       new_lcd_panel;         // 面板创建函数esp_lcd_panel_dev_config_t          lcd_dev_cfg;           // LCD设备配置esp_lcd_panel_io_handle_t           lcd_io_handle;         // LCD IO句柄esp_lcd_panel_handle_t              lcd_panel_handle;      // LCD面板句柄perph_lcd_rest                      rest_cb;               // 复位回调函数lcd_vender_init_func                vendor_init;           // 供应商初始化函数void                                *rest_cb_ctx;          // 复位回调上下文bool                                lcd_swap_xy;           // 是否交换XY坐标bool                                lcd_mirror_x;          // 是否X轴镜像bool                                lcd_mirror_y;          // 是否Y轴镜像bool                                lcd_color_invert;      // 是否颜色反转
} periph_lcd_t;

LCD外设配置选项

  • io_bus: IO总线句柄,可以是SPI或I2C总线
  • new_panel_io: 面板IO创建函数,用于创建LCD IO句柄
  • lcd_io_cfg: LCD IO配置,包括SPI时钟、命令/数据引脚等
  • new_lcd_panel: 面板创建函数,用于创建LCD面板句柄
  • vendor_init: 供应商初始化函数,用于执行特定LCD控制器的初始化序列
  • lcd_dev_cfg: LCD设备配置,包括分辨率、复位引脚等
  • rest_cb: 复位回调函数,用于执行LCD复位操作
  • rest_cb_ctx: 复位回调上下文
  • lcd_swap_xy: 是否交换XY坐标
  • lcd_mirror_x: 是否X轴镜像
  • lcd_mirror_y: 是否Y轴镜像
  • lcd_color_invert: 是否颜色反转

LCD外设初始化流程

LCD外设的初始化流程主要在外设层完成,涉及到ESP-IDF的LCD驱动接口。下面详细介绍初始化过程。

外设层初始化过程(periph_lcd.c)

外设层初始化主要通过periph_lcd_init函数(位于periph_lcd.c)完成,主要包括以下步骤:

  1. 创建LCD外设结构体:分配periph_lcd_t结构体内存
  2. 复制配置参数:从传入的配置结构体复制参数到内部结构体
  3. 创建外设句柄:调用esp_periph_create函数创建外设句柄
  4. 设置回调函数:设置复位回调函数和外设生命周期函数
  5. 设置LCD IO总线:调用_setup_lcd函数设置LCD IO总线
  6. 初始化LCD面板:调用_lcd_init函数初始化LCD面板
// 文件:components/esp_peripherals/periph_lcd.c
esp_periph_handle_t periph_lcd_init(periph_lcd_cfg_t *config)
{// 1. 创建LCD外设结构体periph_lcd_t *periph_lcd = audio_calloc(1, sizeof(periph_lcd_t));AUDIO_MEM_CHECK(TAG, periph_lcd, return NULL);// 2. 复制配置参数periph_lcd->io_bus = config->io_bus;memcpy(&periph_lcd->lcd_io_cfg, config->lcd_io_cfg, sizeof(esp_lcd_panel_io_spi_config_t));memcpy(&periph_lcd->lcd_dev_cfg, config->lcd_dev_cfg, sizeof(esp_lcd_panel_dev_config_t));periph_lcd->new_panel_io = config->new_panel_io;periph_lcd->new_lcd_panel = config->new_lcd_panel;periph_lcd->lcd_swap_xy = config->lcd_swap_xy;periph_lcd->lcd_mirror_x = config->lcd_mirror_x;periph_lcd->lcd_mirror_y = config->lcd_mirror_y;periph_lcd->lcd_color_invert = config->lcd_color_invert;periph_lcd->rest_cb = config->rest_cb;periph_lcd->vendor_init = config->vendor_init;// 3. 创建外设句柄esp_periph_handle_t periph = esp_periph_create(PERIPH_ID_LCD, "periph_lcd");AUDIO_MEM_CHECK(TAG, periph, {audio_free(periph_lcd);return NULL;});// 4. 设置回调函数if (periph_lcd->rest_cb == NULL) {periph_lcd->rest_cb = _lcd_rest_default;}periph_lcd->rest_cb_ctx = config->rest_cb_ctx;esp_periph_set_data(periph, periph_lcd);esp_periph_set_function(periph, NULL, _lcd_run, _lcd_destroy);// 5. 设置LCD IO总线_setup_lcd(periph);// 6. 初始化LCD面板_lcd_init(periph);return periph;
}
LCD IO总线设置过程(_setup_lcd函数)

_setup_lcd函数负责设置LCD IO总线和创建LCD面板,主要包括以下步骤:

  1. 获取LCD外设数据:从外设句柄获取LCD外设数据
  2. 创建LCD IO句柄:调用new_panel_io回调函数创建LCD IO句柄
  3. 创建LCD面板句柄:调用new_lcd_panel回调函数创建LCD面板句柄
// 文件:components/esp_peripherals/periph_lcd.c
esp_err_t _setup_lcd(esp_periph_handle_t self)
{// 1. 获取LCD外设数据periph_lcd_t *periph_lcd = esp_periph_get_data(self);// 2. 创建LCD IO句柄ESP_ERROR_CHECK(periph_lcd->new_panel_io(periph_lcd->io_bus,&periph_lcd->lcd_io_cfg, &periph_lcd->lcd_io_handle));// 3. 创建LCD面板句柄ESP_ERROR_CHECK(periph_lcd->new_lcd_panel(periph_lcd->lcd_io_handle, &periph_lcd->lcd_dev_cfg, &periph_lcd->lcd_panel_handle));return ESP_OK;
}
LCD面板初始化过程(_lcd_init函数)

_lcd_init函数负责初始化LCD面板和设置显示参数,主要包括以下步骤:

  1. 获取LCD外设数据:从外设句柄获取LCD外设数据
  2. 执行LCD复位:调用复位回调函数执行LCD复位
  3. 初始化LCD面板:调用esp_lcd_panel_init函数初始化LCD面板
  4. 执行供应商特定初始化:如果提供了供应商初始化函数,则调用该函数
  5. 配置LCD显示参数:设置颜色反转、显示区域、坐标交换和镜像等参数
  6. 打开显示:调用esp_lcd_panel_disp_on_off函数打开显示
// 文件:components/esp_peripherals/periph_lcd.c
static esp_err_t _lcd_init(esp_periph_handle_t self)
{// 1. 获取LCD外设数据periph_lcd_t *periph_lcd = esp_periph_get_data(self);// 2. 执行LCD复位if (periph_lcd->rest_cb) {periph_lcd->rest_cb(self, periph_lcd->rest_cb_ctx);}// 3. 初始化LCD面板ESP_ERROR_CHECK(esp_lcd_panel_init(periph_lcd->lcd_panel_handle));// 4. 执行供应商特定初始化if (periph_lcd->vendor_init) {ESP_ERROR_CHECK(periph_lcd->vendor_init(periph_lcd->lcd_io_handle));}// 5. 配置LCD显示参数ESP_ERROR_CHECK(esp_lcd_panel_invert_color(periph_lcd->lcd_panel_handle, periph_lcd->lcd_color_invert));ESP_ERROR_CHECK(esp_lcd_panel_set_gap(periph_lcd->lcd_panel_handle, 0, 0));ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(periph_lcd->lcd_panel_handle, periph_lcd->lcd_swap_xy));ESP_ERROR_CHECK(esp_lcd_panel_mirror(periph_lcd->lcd_panel_handle, periph_lcd->lcd_mirror_x, periph_lcd->lcd_mirror_y));// 6. 打开显示ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(periph_lcd->lcd_panel_handle, true));return ESP_OK;
}
LCD默认复位函数(_lcd_rest_default函数)

如果用户没有提供复位回调函数,则使用默认的复位函数:

// 文件:components/esp_peripherals/periph_lcd.c
esp_err_t _lcd_rest_default(esp_periph_handle_t self, void *ctx)
{periph_lcd_t *periph_lcd = esp_periph_get_data(self);ESP_ERROR_CHECK(esp_lcd_panel_reset(periph_lcd->lcd_panel_handle));return ESP_OK;
}
LCD外设完整初始化时序图

下图展示了LCD外设从应用程序调用到ESP-IDF LCD驱动完成初始化的完整流程:

应用程序 periph_lcd_init (periph_lcd.c) esp_periph库 _setup_lcd (periph_lcd.c) _lcd_init (periph_lcd.c) ESP-IDF LCD API periph_lcd_init(config) 分配 periph_lcd_t 结构体 复制配置参数 esp_periph_create(PERIPH_ID_LCD, "periph_lcd") periph 设置复位回调函数 esp_periph_set_data(periph, periph_lcd) esp_periph_set_function(periph, NULL, _lcd_run, _lcd_destroy) _setup_lcd(periph) esp_periph_get_data(periph) periph_lcd new_panel_io(io_bus, lcd_io_cfg, &lcd_io_handle) ESP_OK new_lcd_panel(lcd_io_handle, lcd_dev_cfg, &lcd_panel_handle) ESP_OK ESP_OK _lcd_init(periph) esp_periph_get_data(periph) periph_lcd rest_cb(self, rest_cb_ctx) esp_lcd_panel_reset(lcd_panel_handle) ESP_OK esp_lcd_panel_init(lcd_panel_handle) ESP_OK vendor_init(lcd_io_handle) ESP_OK opt [如果提供了供应商初始化函数] esp_lcd_panel_invert_color(lcd_panel_handle, lcd_color_invert) ESP_OK esp_lcd_panel_set_gap(lcd_panel_handle, 0, 0) ESP_OK esp_lcd_panel_swap_xy(lcd_panel_handle, lcd_swap_xy) ESP_OK esp_lcd_panel_mirror(lcd_panel_handle, lcd_mirror_x, lcd_mirror_y) ESP_OK esp_lcd_panel_disp_on_off(lcd_panel_handle, true) ESP_OK ESP_OK periph 应用程序 periph_lcd_init (periph_lcd.c) esp_periph库 _setup_lcd (periph_lcd.c) _lcd_init (periph_lcd.c) ESP-IDF LCD API

通过上述初始化流程,LCD外设完成了从创建外设句柄到初始化LCD面板的全过程,使应用程序能够通过获取LCD面板句柄来控制LCD显示。

LCD外设销毁流程

LCD外设的销毁流程相对简单,主要通过_lcd_destroy函数(位于periph_lcd.c)完成,主要包括以下步骤:

  1. 获取LCD外设数据:从外设句柄获取LCD外设数据
  2. 删除LCD面板:调用esp_lcd_panel_del函数删除LCD面板
  3. 删除LCD IO:调用esp_lcd_panel_io_del函数删除LCD IO
// 文件:components/esp_peripherals/periph_lcd.c
static esp_err_t _lcd_destroy(esp_periph_handle_t self)
{// 1. 获取LCD外设数据periph_lcd_t *periph_lcd = esp_periph_get_data(self);// 2. 删除LCD面板esp_lcd_panel_del(periph_lcd->lcd_panel_handle);// 3. 删除LCD IOesp_lcd_panel_io_del(periph_lcd->lcd_io_handle);// 注意:这里没有释放periph_lcd结构体内存,因为这个操作由esp_periph库负责return ESP_OK;
}
LCD外设销毁时序图

下图展示了LCD外设销毁的完整流程:

应用程序 esp_periph库 _lcd_destroy ESP-IDF LCD API esp_periph_destroy(periph) _lcd_destroy(self) esp_periph_get_data(self) periph_lcd esp_lcd_panel_del(lcd_panel_handle) ESP_OK esp_lcd_panel_io_del(lcd_io_handle) ESP_OK ESP_OK 释放periph_lcd结构体内存 ESP_OK 应用程序 esp_periph库 _lcd_destroy ESP-IDF LCD API
销毁流程注意事项
  1. 内存管理_lcd_destroy函数不负责释放periph_lcd结构体内存,这个操作由esp_periph库负责。这是因为该结构体是通过esp_periph_set_data函数设置给外设句柄的,由外设框架统一管理。

  2. 资源释放顺序:先删除LCD面板,再删除LCD IO,这个顺序确保了资源的正确释放,避免了悬挂指针和内存泄漏问题。

  3. 错误处理:销毁函数没有进行错误检查,这是因为销毁操作通常是在应用程序退出或者资源清理阶段执行的,即使出现错误也不会影响程序的正常退出。

通过上述销毁流程,LCD外设能够正确释放所有分配的资源,确保应用程序不会出现资源泄漏问题。

LCD外设事件处理

LCD外设的事件处理机制相对简单,主要通过_lcd_run函数(位于periph_lcd.c)实现,该函数在外设框架接收到事件时被调用。

// 文件:components/esp_peripherals/periph_lcd.c
static esp_err_t _lcd_run(esp_periph_handle_t self, audio_event_iface_msg_t *msg)
{return ESP_OK;
}

与按键外设不同,LCD外设的_lcd_run函数实现非常简单,仅返回ESP_OK,不处理任何事件。这是因为LCD外设主要是一个输出设备,不会主动产生事件,而是被动接收应用程序的控制命令进行显示更新。

LCD外设事件处理特点
  1. 被动设备:LCD外设是一个典型的被动设备,不会主动产生事件,而是由应用程序控制其显示内容和状态。

  2. 无事件类型定义:与按键外设不同,LCD外设没有定义特定的事件类型,因为它不需要向应用程序报告状态变化。

  3. 控制方式:应用程序通过以下方式控制LCD显示:

    • 初始化LCD外设:periph_lcd_init
    • 获取LCD面板句柄:periph_lcd_get_panel_handle
    • 使用ESP-IDF的LCD面板API进行显示控制:esp_lcd_panel_draw_bitmap
LCD外设与应用程序交互流程
典型应用代码示例
#include "esp_peripherals.h"
#include "periph_lcd.h"
#include "esp_lcd_panel_ops.h"void app_lcd_demo(void)
{// 1. 初始化esp_periph库esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();esp_periph_set_handle_t set = esp_periph_set_init(&periph_cfg);// 2. 初始化LCD外设periph_lcd_cfg_t lcd_cfg = {// 填写LCD配置参数};esp_periph_handle_t lcd_periph = periph_lcd_init(&lcd_cfg);esp_periph_start(set, lcd_periph);// 3. 获取LCD面板句柄esp_lcd_panel_handle_t panel = periph_lcd_get_panel_handle(lcd_periph);// 4. 使用ESP-IDF LCD API绘制内容uint16_t color_data[320 * 240] = {0}; // 示例:320x240分辨率esp_lcd_panel_draw_bitmap(panel, 0, 0, 320, 240, color_data);// ... 其他应用逻辑// 5. 销毁LCD外设esp_periph_stop(set, lcd_periph);esp_periph_destroy(set);
}

下图展示了LCD外设与应用程序的交互流程:

应用程序 esp_periph库 LCD外设 ESP-IDF LCD API esp_periph_init() periph_lcd_init(config) periph句柄 esp_periph_start(periph) periph_lcd_get_panel_handle(periph) panel句柄 esp_lcd_panel_draw_bitmap(panel, ...) 更新显示内容 esp_periph_destroy(periph) _lcd_destroy(self) 释放资源 应用程序 esp_periph库 LCD外设 ESP-IDF LCD API

通过上述交互流程,应用程序可以控制LCD显示内容,而无需关心事件处理机制。这种设计简化了LCD外设的使用,使开发者可以专注于显示内容的设计和实现。

相关文章:

  • 深入理解前端安全:CSRF与XSS攻击详解
  • 在小米AX6000中通过米家控制tailscale
  • 使用ref和refs获取DOM元素和组件方法,使用$nextTick解决Vue异步更新的逻辑下无法获取DOM的问题
  • 力扣面试150题-- 存在重复元素 II和最长连续序列
  • 系统架构设计师:计算机组成与体系结构(如CPU、存储系统、I/O系统)案例分析与简答题、详细解析与评分要点
  • 中间件--ClickHouse-12--案例-1-日志分析和监控
  • 如何0基础学stm32?
  • redis和lua为什么能实现事务
  • 智能语音备忘录:SpeechRecognition与gTTS的奇妙融合
  • 在swiftui项目中使用WKWebView加载自定义脚本文件
  • WebSocket介绍
  • 深入剖析JavaScript内存泄漏:识别、定位与实战解决
  • LeetCode 解题思路 42(Hot 100)
  • JMeter实现UI自动化测试的完整方案
  • stack,queue和priority_queue
  • Java @Serial 注解深度解析
  • MATLAB 控制系统设计与仿真 - 39
  • JDBC 数据库连接全解析:从驱动配置到工具类封装
  • 《JVM考古现场(二十三):归零者·重启奇点的终极奥义》
  • 若依(笔记)
  • 民建吉林省委提案:当前生育政策集中鼓励多孩生育,应该转变思路
  • 上海开展数据产品知识产权登记存证试点,243个产品许可收益超20亿元
  • 广发基金刘格崧一季报:首次买入广东宏大、分众传媒,减仓亿纬锂能
  • 比起追逐爆款,动画行业更需要打开思路“重塑肉身”
  • 大国重器飞天背后,有一位上海航天的“老法师”
  • 今年1-3月全国吸收外资2692.3亿元人民币