ESP 芯片可以产生市面上常见 LCD 所需的各种时序,例如 SPI LCD、I2C LCD、并行 LCD(Intel 8080)、RGB/SRGB LCD、MIPI DSI LCD 等。esp_lcd esp_lcd 提供了一个抽象的驱动框架以统一的方式支持它们。
本次以立创实战派开发板为例,屏幕为SPI接口,使用ST7789驱动
LCD 通常由两个主要平面组成:
控制平面 :此平面允许我们读取和写入 LCD 设备控制器的内部寄存器。主机通常使用此平面执行诸如初始化 LCD 电源和执行伽马校准等任务。
数据平面 :数据平面负责将像素数据传输到 LCD 设备
1.头文件
#include "hal/lcd_types.h"
#include "esp_lcd_types.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_panel_vendor.h"
2.常用初始化案例
首先是打开屏幕背光,在这里我们可以使用LEDC功能输出pwm控制屏幕背光,并设置背光亮度百分比
//初始化LEDC 控制屏幕背光
esp_err_t bsp_display_brightness_init(void)
{
//准备并应用led PWM定时器配置
ledc_timer_config_t ledc_timer = {
.speed_mode = LEDC_LOW_SPEED_MODE, //速度模式(值必须为 LEDC_LOW_SPEED_MODE)
.duty_resolution = LEDC_TIMER_10_BIT, //PWM 占空比分辨率
.timer_num = 1, //定时器索引
.freq_hz = 5000, // PWM 信号频率(Hz)
.clk_cfg = LEDC_AUTO_CLK //时钟源
};
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
// 准备并应用LEDC PWM通道配置
ledc_channel_config_t ledc_channel = {
.speed_mode = LEDC_LOW_SPEED_MODE, //高速模式(仅在esp32上存在)或低速模式
.channel = LCD_LEDC_CH, //LEDC通道
.timer_sel = 1, //选择通道的定时器源
.intr_type = LEDC_INTR_DISABLE, //配置中断
.gpio_num = BSP_LCD_BACKLIGHT, //LEDC输出的GPIO
.duty = 0, // LEDC通道占空比
.hpoint = 0, //LEDC通道hpoint值
.flags.output_invert = 1 //启用(1)或禁用(0)gpio输出反相
};
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
return ESP_OK;
}
//设置屏幕背光亮度 参数为百分比
esp_err_t bsp_display_brightness_set(int brightness_percent)
{
if (brightness_percent > 100) {
brightness_percent = 100;
} else if (brightness_percent < 0) {
brightness_percent = 0;
}
ESP_LOGI(TAG, "Setting LCD backlight: %d%%", brightness_percent);
//将百分比转换为10位PWM占空比值(LEDC分辨率设为10位,所以100%对应1023)
uint32_t duty_cycle = (1023 * brightness_percent) / 100;
ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH, duty_cycle));
ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH));
return ESP_OK;
}
//背光熄灭
esp_err_t bsp_display_backlight_off(void)
{
return bsp_display_brightness_set(0);
}
//背光最亮
esp_err_t bsp_display_backlight_on(void)
{
return bsp_display_brightness_set(100);
}
接下来就是初始化屏幕
首先初始化控制时序(SPI),接着LCD控制器的IO,LCD面板初始化(ST7789驱动芯片),最后配置面板和错误处理
//定义lcd面板
static esp_lcd_panel_handle_t panel_handle = NULL;
esp_lcd_panel_io_handle_t io_handle = NULL;
esp_err_t bsp_display_new(void)
{
esp_err_t ret = ESP_OK;
// 背光初始化
ESP_RETURN_ON_ERROR(bsp_display_brightness_init(), TAG, "Brightness init failed");
// 初始化SPI总线
ESP_LOGD(TAG, "Initialize SPI bus");
const spi_bus_config_t buscfg = {
.sclk_io_num = BSP_LCD_SPI_CLK, //clk
.mosi_io_num = BSP_LCD_SPI_MOSI, //mosi
.miso_io_num = GPIO_NUM_NC, //miso,这里指向空,没使用
.quadwp_io_num = GPIO_NUM_NC,
.quadhd_io_num = GPIO_NUM_NC,
.max_transfer_sz = BSP_LCD_H_RES * BSP_LCD_V_RES * sizeof(uint16_t), //最大传输大小(以字节为单位)
};
ESP_RETURN_ON_ERROR(spi_bus_initialize(BSP_LCD_SPI_NUM, &buscfg, SPI_DMA_CH_AUTO), TAG, "SPI init failed");
// 液晶屏控制IO初始化
ESP_LOGD(TAG, "Install panel IO");
const esp_lcd_panel_io_spi_config_t io_config = {
.dc_gpio_num = BSP_LCD_DC, //设置 DC 信号线的 gpio 编号(某些 LCD 称之为 RS 线)
.cs_gpio_num = BSP_LCD_SPI_CS, //设置 CS 信号线的 gpio 编号
.pclk_hz = BSP_LCD_PIXEL_CLOCK_HZ, //设置像素时钟的频率(以 Hz 为单位)
.lcd_cmd_bits = LCD_CMD_BITS, //设置 LCD 控制器芯片识别的命令的位宽。这是芯片特定的
.lcd_param_bits = LCD_PARAM_BITS, //设置 LCD 控制器芯片识别参数的位宽。这是芯片特定的
.spi_mode = 2, //设置 SPI 模式
.trans_queue_depth = 10, //设置 SPI 事务队列的深度
};
//初始化LCD控制器的SPI接口
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)BSP_LCD_SPI_NUM, &io_config, &io_handle), err, TAG, "New panel IO failed");
// 初始化液晶屏驱动芯片ST7789
ESP_LOGD(TAG, "Install LCD driver");
const esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = BSP_LCD_RST, //复位引脚
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, //RGB 子像素排列顺序
.bits_per_pixel = BSP_LCD_BITS_PER_PIXEL, //每个像素的位数(通常为 16,即 RGB565)
};
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle), err, TAG, "New panel failed");
//配置LCD面板
esp_lcd_panel_reset(panel_handle); // 液晶屏复位
lcd_cs(0); // 拉低CS引脚
esp_lcd_panel_init(panel_handle); // 初始化配置寄存器
esp_lcd_panel_invert_color(panel_handle, true); // 颜色反转
esp_lcd_panel_swap_xy(panel_handle, true); // 显示翻转
esp_lcd_panel_mirror(panel_handle, true, false); // 镜像
return ret;
//错误处理
err:
if (panel_handle) {
esp_lcd_panel_del(panel_handle); //释放ST7789驱动芯片占用的资源
}
if (io_handle) {
esp_lcd_panel_io_del(io_handle); //关闭SPI通信接口,释放DMA通道和GPIO引脚
}
spi_bus_free(BSP_LCD_SPI_NUM); //解除SPI总线占用
return ret;
}
3.案例
结合上述初始化,下面显示函数直接照搬即可
// LCD显示初始化
esp_err_t bsp_lcd_init(void)
{
esp_err_t ret = ESP_OK;
ret = bsp_display_new(); // 液晶屏驱动初始化
lcd_set_color(0x0000); // 设置整屏背景黑色
ret = esp_lcd_panel_disp_on_off(panel_handle, true); // 打开液晶屏显示
ret = bsp_display_backlight_on(); // 打开背光显示
return ret;
}
// 显示图片
void lcd_draw_pictrue(int x_start, int y_start, int x_end, int y_end, const unsigned char *gImage)
{
// 分配内存 分配了需要的字节大小 且指定在外部SPIRAM中分配
size_t pixels_byte_size = (x_end - x_start)*(y_end - y_start) * 2;
uint16_t *pixels = (uint16_t *)heap_caps_malloc(pixels_byte_size, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
if (NULL == pixels)
{
ESP_LOGE(TAG, "Memory for bitmap is not enough");
return;
}
memcpy(pixels, gImage, pixels_byte_size); // 把图片数据拷贝到内存
esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_end, y_end, (uint16_t *)pixels); // 显示整张图片数据
heap_caps_free(pixels); // 释放内存
}
// 设置液晶屏颜色
void lcd_set_color(uint16_t color)
{
// 分配内存 这里分配了液晶屏一行数据需要的大小
uint16_t *buffer = (uint16_t *)heap_caps_malloc(BSP_LCD_H_RES * sizeof(uint16_t), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
if (NULL == buffer)
{
ESP_LOGE(TAG, "Memory for bitmap is not enough");
}
else
{
for (size_t i = 0; i < BSP_LCD_H_RES; i++) // 给缓存中放入颜色数据
{
buffer[i] = color;
}
for (int y = 0; y < 240; y++) // 显示整屏颜色
{
esp_lcd_panel_draw_bitmap(panel_handle, 0, y, 320, y+1, buffer);
}
free(buffer); // 释放内存
}
}