ESP-IDF学习笔记-并口LCD
ESP32S3中提供了一堆外设,其中就包括LCD外设。该外设可以直接驱动屏幕,支持SPI,I2C,8080,RGB接口的屏幕。这与STM32不同(需要基于FSMC或者SPI写驱动),硬件完成了大部分操作。这里记录使用8080并口屏的方法,以及使用触摸作为输入。
一,配置步骤概览
以8080总线驱动的HX8369A屏幕为例,ESP32中提供的API结构大概如下:
初始化总线
1 | esp_lcd_new_i80_bus(); |
使用该函数定义时钟源,D/C引脚,WR引脚,总线宽度(8/16),数据引脚,单次最大传输数据量,DMA对齐参数。是对仅对8080总线的定义。
定义一个i80panel的IO
1 | esp_lcd_new_panel_io_i80(); |
在LCD驱动中,使用panel处理一个需要驱动的屏幕,需要我们去设置这个panel的io口。这个IO口是包含i80在内的片选等一个屏幕的io的合集。同时还定义了io口电平的含义(命令数据是高电平还是低电平等),定义了屏幕的数据时钟速度,是否交换颜色数据字节,刷新完成回调(用于GUI),命令长度和参数长度等定义。
创建一个panel
1 | esp_lcd_new_panel_st7789() |
在这一步创建一个面板句柄,绑定了特定的屏幕驱动芯片,设置reset脚,色域深度等。
初始化屏幕
1 | esp_lcd_panel_reset(panel_handle); |
这一步将初始化屏幕。
设置其他参数
设置屏幕间隙,是否交换x/y轴等。
二,配置步骤
这里使用HX8369A驱动的480*800屏幕,利用8线8080数据线驱动的屏幕。
初始化总线
1 | esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lcd_i80_bus_handle_t *ret_bus) |
该函数创建了一个i80句柄(ret_bus)。输入参数:
esp_lcd_i80_bus_config_t
1 | typedef struct { |
其中:
clk_src
时钟源选择。一般选择
LCD_CLK_SRC_DEFAULT
(LCD_CLK_SRC_PLL160M
)。还可以选择LCD_CLK_SRC_PLL240M
,LCD_CLK_SRC_XTAL
。max_transfer_bytes
决定了内部DMA的传输长度,一般为行的整数倍:
EXAMPLE_LCD_H_RES * 100 * sizeof(uint16_t)
psram_trans_align
PRAM中使用的数据对齐,支持16,32,64。
Supported alignment: 16, 32, 64. A higher alignment can enables higher burst transfer size, thus a higher i80 bus throughput.
sram_trans_align
一般为4
这个DMA对齐我还没搞明白,但应该是和STM32DMA设置中的字半字之类有关吧。
使用例:
1 | esp_lcd_i80_bus_handle_t i80_bus = NULL; |
创建IO设备句柄
1 | esp_err_t esp_lcd_new_panel_io_i80(esp_lcd_i80_bus_handle_t bus, const esp_lcd_panel_io_i80_config_t *io_config, esp_lcd_panel_io_handle_t *ret_io) |
在i80总线的基础上创建IO句柄。
参数:
bus
esp_lcd_new_i80_bus()
创建的句柄ret_io
创建出来的句柄
io_config
1 | typedef struct { |
其中回调函数的模板:
1 | typedef bool (*esp_lcd_panel_io_color_trans_done_cb_t)(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx); |
例子:
1 |
|
创建panel句柄
创建完IO句柄后,驱动知道了怎么发数据,但是还不知道发什么数据/命令。因此,需要一层驱动层。
使用如下命令创建面板。
1 | esp_err_t esp_lcd_new_panel_st7789(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel) |
创建了一个基于st7789的面板句柄。
io
上面创建的io句柄
ret_panel
创建出来的句柄
panel_dev_config
设置结构体
1 |
|
其中:
rgb_endian
:LCD_RGB_ENDIAN_RGB
/LCD_RGB_ENDIAN_BGR
例子:
1 | esp_lcd_panel_handle_t panel_handle = NULL; |
然而,这是ST7789的驱动,我这个sb开发板是hx8369A驱动的,这个在官方的库中没支持。(虽然官方也只支持了ST7789,nt35510,ssd1306),在esp的组件库中也没有。但是在例程中给的库lvgl_esp32_drivers
中有这个芯片,在该库中实现了一个hx8396a的panel生成,我这个开发板的店家也是直接用的那里面的驱动,然后好像改成了8080发送数据。然后我问店家怎么移植,他直接说用这个,然后说他们不教这个???
关于如果需要其他IC驱动的芯片(冷门的导致ESP官方没有支持的),需要自己添加驱动层代码,我会在另一篇文章细说。
调用函数初始化
以下函数都实际在上面的panel中实现的。
1 | esp_err_t esp_lcd_panel_reset(esp_lcd_panel_handle_t panel); |
重置屏幕,该函数需要在使用esp_lcd_panel_init()
前被调用。
1 | esp_err_t esp_lcd_panel_init(esp_lcd_panel_handle_t panel); |
初始化屏幕。
1 | esp_err_t esp_lcd_panel_mirror(esp_lcd_panel_handle_t panel, bool mirror_x, bool mirror_y); |
在某个轴镜像屏幕。与esp_lcd_panel_swap_xy()
协同使用。
1 | esp_err_t esp_lcd_panel_swap_xy(esp_lcd_panel_handle_t panel, bool swap_axes); |
交换x/y轴。与esp_lcd_panel_mirror()
协同使用。
1 | esp_err_t esp_lcd_panel_set_gap(esp_lcd_panel_handle_t panel, int x_gap, int y_gap) |
设置x/y上的间隙(到边框的距离)。间隙是指液晶面板的左/顶部两侧分别与实际显示的第一行/列之间的空间(像素)。
当定位或对准比LCD小的框架时,设置间隙是很有用的。
1 | esp_err_t esp_lcd_panel_invert_color(esp_lcd_panel_handle_t panel, bool invert_color_data) |
颜色反转。
1 | esp_err_t esp_lcd_panel_disp_on_off(esp_lcd_panel_handle_t panel, bool on_off) |
打开或者关闭显示。
例子:
1 |
|
三,使用屏幕
ESP库提供了最基础的画点函数。
1 | esp_err_t esp_lcd_panel_draw_bitmap(esp_lcd_panel_handle_t panel, int x_start, int y_start, int x_end, int y_end, const void *color_data) |
一个窗口中绘制像素点。
Tips:start点被包含而end点没有被包含。
当初始化化完IO句柄后,其实已经可以实现发送和接收数据来驱动屏幕了,后面不过是封装了特定的驱动。
1 | esp_err_t esp_lcd_panel_io_rx_param(esp_lcd_panel_io_handle_t io, int lcd_cmd, void *param, size_t param_size) |
发送命令并接收参数。
1 | esp_err_t esp_lcd_panel_io_tx_param(esp_lcd_panel_io_handle_t io, int lcd_cmd, const void *param, size_t param_size) |
发送命令和参数。
1 | esp_err_t esp_lcd_panel_io_tx_color(esp_lcd_panel_io_handle_t io, int lcd_cmd, const void *color, size_t color_size) |
发送颜色数据。
这个函数将要发送的数据加入到后台队列,由DMA+中断发送。
由于有缓存时间存在,因此需要回调处理数据的生命周期。
四,SDKconfig中的设置
在LCD and Touch Panel--->LCD Peripheral Configuration
中,需要注意这样一个设置LCD panel io foramt buffer size
。该参数的值与io层单次最大发送数据量param_size
有关,如果该值设置过小,会导致发送时报错。
在本人移植驱动过程中,由于没有设置该值,导致在初始化时在下面函数中报错
1 | esp_lcd_panel_io_tx_param(io, 0x2D, cmd_192,192); |
192超过了默认值大小,导致报错。