Files
esp-idf/docs/zh_CN/api-reference/peripherals/lcd/dsi_lcd.rst
2026-08-13 14:29:43 +08:00

173 lines
10 KiB
ReStructuredText
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

MIPI DSI 接口的 LCD
===================
:link_to_translation:`en:[English]`
#. 创建 DSI 总线,并对 D-PHY 进行初始化设置。
.. code-block:: c
esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL;
esp_lcd_dsi_bus_config_t bus_config = {
.bus_id = 0, // 从 0 开始编号,指定要使用的 DSI 主机
.num_data_lanes = 2, // 要使用的数据通道数,不能超过芯片支持的数量
.lane_bit_rate_mbps = EXAMPLE_MIPI_DSI_LANE_BITRATE_MBPS, // 数据通道的比特率 (Mbps)
};
ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus));
#. 从 DSI 总线派生 DBI 接口。DBI 接口主要用作 esp_lcd 组件中的控制 IO 层。使用该接口可读写 LCD 设备内部的配置寄存器。在此步骤中,需要提供以下信息:
- :cpp:member:`esp_lcd_dbi_io_config_t::virtual_channel` 设置虚拟通道号。虚拟通道是一种逻辑通道,用于从不同来源多路复用数据。如果只连接了一个 LCD则将此值设置为 ``0``
- :cpp:member:`esp_lcd_dbi_io_config_t::lcd_cmd_bits`:cpp:member:`esp_lcd_dbi_io_config_t::lcd_param_bits` 分别设置 LCD 控制器芯片可识别的命令及参数的位宽。不同芯片对位宽要求不同,请提前参阅 LCD 规格书。
.. code-block:: c
esp_lcd_panel_io_handle_t mipi_dbi_io = NULL;
esp_lcd_dbi_io_config_t dbi_config = {
.virtual_channel = 0,
.lcd_cmd_bits = 8, // 依据 LCD 规格书说明来设置
.lcd_param_bits = 8, // 依据 LCD 规格书说明来设置
};
ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io));
#. 安装 LCD 控制器驱动程序。LCD 控制器驱动程序负责向 LCD 控制器芯片发送命令和参数。在此步骤中,需要指定上一步骤中分配到的 MIPI DBI IO 设备句柄以及一些面板特定配置:
- :cpp:member:`esp_lcd_panel_dev_config_t::bits_per_pixel` 设置每个像素的位宽。LCD 驱动程序使用此值计算要发送到 LCD 控制器芯片的字节数。
- :cpp:member:`esp_lcd_panel_dev_config_t::reset_gpio_num` 设置复位管脚的 GPIO 编号。如果 LCD 控制器芯片没有硬件复位管脚,则将此设置为 ``-1``
- :cpp:member:`esp_lcd_panel_dev_config_t::rgb_ele_order` 设置像素数据的 RGB 元素顺序,可以是 **RGB****BGR**
.. code-block:: c
esp_lcd_panel_handle_t ili9881c_ctrl_panel = NULL;
esp_lcd_panel_dev_config_t lcd_dev_config = {
.bits_per_pixel = 24, // MIPI LCD 上通常每像素使用 24 位(即 RGB888
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
.reset_gpio_num = EXAMPLE_PIN_NUM_LCD_RST,
};
ESP_ERROR_CHECK(esp_lcd_new_panel_ili9881c(mipi_dbi_io, &lcd_dev_config, &ili9881c_ctrl_panel));
#. 使用上一步骤中返回的 LCD 控制面板,在基本初始化完成后可以重置 LCD 设备。之后,可以打开显示器。
.. code-block:: c
ESP_ERROR_CHECK(esp_lcd_panel_reset(ili9881c_ctrl_panel));
ESP_ERROR_CHECK(esp_lcd_panel_init(ili9881c_ctrl_panel));
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(ili9881c_ctrl_panel, true));
#. 此时仍然无法使用控制面板向 MIPI LCD 发送像素数据,因为 MIPI LCD 具有高分辨率,而 LCD 控制器中没有 GRAM。因此需要维护 LCD 帧 buffer并通过 MIPI DSI DPI 接口将其刷新到 LCD 屏幕上。分配 DPI 数据面板需要许多基本参数,例如 DPI 时钟频率、像素格式、视频时序等。
- :cpp:member:`esp_lcd_dpi_panel_config_t::virtual_channel` 设置虚拟通道号。与 DBI 接口类似DPI 接口也需要设置虚拟通道。如果只连接了一个 LCD则将此值设置为 ``0``
- :cpp:member:`esp_lcd_dpi_panel_config_t::dpi_clk_src` 设置 DPI 接口的时钟源。可用的时钟源列表见 :cpp:type:`mipi_dsi_dpi_clock_source_t`
- :cpp:member:`esp_lcd_dpi_panel_config_t::dpi_clock_freq_mhz` 设置 DPI 时钟频率 (MHz)。像素时钟频率越高,刷新率越高,但如果 DMA 带宽不足或 LCD 控制器芯片不支持高像素时钟频率,则可能会导致闪烁。
- :cpp:member:`esp_lcd_dpi_panel_config_t::in_color_format` 设置输入的像素数据的格式。可用的像素格式见 :cpp:type:`lcd_color_format_t`。MIPI LCD 通常使用 **RGB888** 来获得最佳色彩深度。
- :cpp:member:`esp_lcd_dpi_panel_config_t::video_timing` 设置 LCD 面板的特定时序参数。包括 LCD 分辨率和消隐间隔在内的必要参数列表见 :cpp:type:`esp_lcd_video_timing_t`,请依据 LCD 技术规格书填写参数。
.. code-block:: c
esp_lcd_panel_handle_t mipi_dpi_panel = NULL;
esp_lcd_dpi_panel_config_t dpi_config = {
.virtual_channel = 0,
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT,
.dpi_clock_freq_mhz = 1 * 1000,
.in_color_format = LCD_COLOR_FMT_RGB888,
.video_timing = {
.h_size = EXAMPLE_MIPI_DSI_LCD_H_RES,
.v_size = EXAMPLE_MIPI_DSI_LCD_V_RES,
.hsync_back_porch = EXAMPLE_MIPI_DSI_LCD_HBP,
.hsync_pulse_width = EXAMPLE_MIPI_DSI_LCD_HSYNC,
.hsync_front_porch = EXAMPLE_MIPI_DSI_LCD_HFP,
.vsync_back_porch = EXAMPLE_MIPI_DSI_LCD_VBP,
.vsync_pulse_width = EXAMPLE_MIPI_DSI_LCD_VSYNC,
.vsync_front_porch = EXAMPLE_MIPI_DSI_LCD_VFP,
},
};
ESP_ERROR_CHECK(esp_lcd_new_panel_dpi(mipi_dsi_bus, &dpi_config, &mipi_dpi_panel));
ESP_ERROR_CHECK(esp_lcd_panel_init(mipi_dpi_panel));
#. 配置绘制位图钩子函数(可选)
若想使用 DMA2D 加速位图的复制,驱动程序内部已实现基于 DMA2D 的位图复制钩子函数,用户只需调用 :func:`esp_lcd_dpi_panel_enable_dma2d` 即可。
.. code-block:: c
ESP_ERROR_CHECK(esp_lcd_dpi_panel_enable_dma2d(mipi_dpi_panel));
.. note::
由于硬件限制,如果启用了外部存储加密,则 DMA2D 只能访问地址和长度都对齐到 {IDF_TARGET_SOC_MEMSPI_ENCRYPTION_ALIGNMENT} 字节的地址空间。你需要确保你的绘制 buffer 的地址和长度都对齐到 {IDF_TARGET_SOC_MEMSPI_ENCRYPTION_ALIGNMENT} 字节。 :example:`peripherals/lcd/mipi_dsi` 演示了如何使用 LVGL 控制重新绘制区域来确保对齐。
若需更高级的应用,用户可为绘制位图添加自定义钩子,例如通过 PPA 实现旋转、缩放等操作。
.. code-block:: c
esp_lcd_panel_hooks_t hooks = {
.draw_bitmap_hook = custom_draw_bitmap_hook,
};
ESP_ERROR_CHECK(esp_lcd_dpi_panel_register_hooks(mipi_dpi_panel, &hooks, &user_ctx));
如果自定义钩子是异步的——例如钩子函数启动 PPA 后立即返回,真正的像素处理仍由硬件在后台执行——则必须在硬件操作真正完成后再调用 :cpp:member:`esp_lcd_draw_bitmap_hook_data_t::on_hook_end`。该回调由 DPI 面板驱动实现并填入 ``hook_data``,用户无需自行编写,只需在异步操作完成时调用它,以通知驱动结束本次绘制事务;若已注册颜色传输完成回调,也会在此时被调用。
面板驱动本身不会等待上一笔绘制结束,同步由自定义钩子自行负责。最简单的做法是串行执行(上一笔完成前不启动下一笔),如下面示例所示。若希望利用 PPA 等加速器的事务队列并发提交多笔绘制,则需要为每笔事务单独保存 ``hook_data``、保证源 buffer 在硬件完成前有效,并处理好目标区域重叠等问题。
下面是一个异步自定义钩子的示意代码:钩子启动 PPA 后立即返回,并在 PPA 完成回调中调用 ``on_hook_end``。示例采用串行方式,用信号量保证同一时间只有一笔绘制在进行:
.. code-block:: c
typedef struct {
esp_lcd_panel_handle_t panel;
esp_lcd_draw_bitmap_hook_data_t hook_data;
SemaphoreHandle_t draw_sem;
// ... 其他字段,例如 ppa_client_handle_t
} draw_bitmap_hook_ctx_t;
static bool ppa_trans_done_callback(ppa_client_handle_t ppa_client, ppa_event_data_t *edata, void *user_ctx)
{
draw_bitmap_hook_ctx_t *ctx = (draw_bitmap_hook_ctx_t *)user_ctx;
bool need_yield = false;
// on_hook_end 由 DPI 面板驱动提供,操作完成后直接调用即可
if (ctx->hook_data.on_hook_end) {
if (ctx->hook_data.on_hook_end(ctx->panel)) {
need_yield = true;
}
}
BaseType_t task_woken = pdFALSE;
xSemaphoreGiveFromISR(ctx->draw_sem, &task_woken);
if (task_woken == pdTRUE) {
need_yield = true;
}
return need_yield;
}
static esp_err_t custom_draw_bitmap_hook(esp_lcd_panel_handle_t panel,
const esp_lcd_draw_bitmap_hook_data_t *hook_data,
void *user_ctx)
{
draw_bitmap_hook_ctx_t *ctx = (draw_bitmap_hook_ctx_t *)user_ctx;
// 最简单的同步方式:等待上一笔完成后再启动本笔
xSemaphoreTake(ctx->draw_sem, portMAX_DELAY);
// 保存 hook_data供完成回调稍后调用 on_hook_end
ctx->hook_data = *hook_data;
// 启动异步 PPA 传输后立即返回
// ppa_do_scale_rotate_mirror(...);
return ESP_OK;
}
关于 MIPI DPHY 的供电
---------------------
{IDF_TARGET_NAME} 中的 MIPI DPHY 需要独立的 2.5V 电源供电,请查阅原理图,确保在使用 MIPI DSI 驱动之前,已将其供电引脚(名字可能是 ``VDD_MIPI_DPHY``)连接至 2.5V 电源。
.. only:: SOC_GP_LDO_SUPPORTED
在 {IDF_TARGET_NAME} 中, MIPI DPHY 可以使用内部的可调 LDO 供电。请将 LDO 通道的输出引脚连接至 MIPI DPHY 的供电引脚。然后在初始化 DSI 驱动之前,使用 :doc:`/api-reference/peripherals/ldo_regulator` 中提供的 API 配置 LDO 输出 2.5V 电压。
API 参考
--------
.. include-build-file:: inc/esp_lcd_mipi_dsi.inc