(ESP-IDF)LVGL 对接 显示
驱动屏幕
此处使用 ESP-IDF 提供的 GC9A01 驱动,已经实现了底层
我仅做了:
1、初始数
2、驱动亮度显示
3、需要注意:这里的写入屏幕完成回调要调用 lv_disp_flush_ready(lv_display);
#include <driver/ledc.h>
#include "dev_gc9a01.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "esp_lcd_panel_io_interface.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_ops.h"
#include "unity_test_runner.h"
#include "esp_lcd_gc9a01.h"
#include "lvgl.h"
#include "lvgl_port.h"
static const char *TAG = "gc9a01_app";
esp_lcd_panel_handle_t panel_handle = NULL;
esp_lcd_panel_io_handle_t io_handle = NULL;
static bool lcd_flush_done_cb(esp_lcd_panel_io_handle_t io,
esp_lcd_panel_io_event_data_t *edata,
void *user_ctx) {
lv_disp_flush_ready(lv_display);
return false;
}
// 初始化GC9A01显示屏
esp_err_t dev_gc9a01_init(void) {
// 1. 初始化SPI总线
const spi_bus_config_t buscfg = GC9A01_PANEL_BUS_SPI_CONFIG(
PIN_NUM_LCD_PCLK,
PIN_NUM_LCD_DATA0,
LCD_H_RES * LCD_V_RES * LCD_BIT_PER_PIXEL / 8
);
esp_err_t ret = spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "spi_bus_initialize err %s", esp_err_to_name(ret));
return ret;
}
// 2. 安装面板IO
const esp_lcd_panel_io_spi_config_t io_config = GC9A01_PANEL_IO_SPI_CONFIG(
PIN_NUM_LCD_CS,
PIN_NUM_LCD_DC,
lcd_flush_done_cb,
NULL
);
ret = esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t) LCD_HOST, &io_config, &io_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "esp_lcd_new_panel_io_spi err %s", esp_err_to_name(ret));
spi_bus_free(LCD_HOST);
return ret;
}
// 3. 安装GC9A01面板驱动
const esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = PIN_NUM_LCD_RST,
.bits_per_pixel = LCD_BIT_PER_PIXEL,
.rgb_endian = LCD_RGB_ENDIAN_BGR,
};
ret = esp_lcd_new_panel_gc9a01(io_handle, &panel_config, &panel_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "esp_lcd_new_panel_gc9a01 err %s", esp_err_to_name(ret));
esp_lcd_panel_io_del(io_handle);
spi_bus_free(LCD_HOST);
return ret;
}
// 4. 初始化面板
esp_lcd_panel_reset(panel_handle);
esp_lcd_panel_init(panel_handle);
esp_lcd_panel_invert_color(panel_handle, true);
esp_lcd_panel_mirror(panel_handle, true, false);
esp_lcd_panel_disp_on_off(panel_handle, true);
ESP_LOGI(TAG, "init_gc9a01_display success");
// 5. 调节亮度
lcd_backlight_init();
lcd_backlight_brightness_set(50);
return ESP_OK;
}
static uint16_t color_data[LCD_H_RES * LCD_V_RES] = {0};
void clear_lcd_display(void) {
esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, LCD_H_RES, LCD_V_RES, color_data);
}
void clear_lcd_display_white() {
for (int i = 0; i < LCD_H_RES * LCD_V_RES; i++) {
color_data[i] = 0xFFFF;
}
clear_lcd_display();
}
void test_lcd_display() {
clear_lcd_display_white();
vTaskDelay(pdMS_TO_TICKS(1000));
for (int i = 0; i < LCD_H_RES * LCD_V_RES; i++) {
color_data[i] = 0xF800;
}
esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, LCD_H_RES / 2, LCD_V_RES / 2, color_data);
}
void lcd_backlight_init() {
const ledc_channel_config_t LCD_backlight_channel = {
.gpio_num = PIN_NUM_LCD_BL,
.speed_mode = LEDC_LOW_SPEED_MODE,
.channel = LEDC_CHANNEL_0,
.intr_type = LEDC_INTR_DISABLE,
.timer_sel = 0,
.duty = 0,
.hpoint = 0,
.flags.output_invert = true
};
const ledc_timer_config_t LCD_backlight_timer = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.duty_resolution = LEDC_TIMER_10_BIT,
.timer_num = 0,
.freq_hz = 5000,
.clk_cfg = LEDC_AUTO_CLK
};
ESP_ERROR_CHECK(ledc_timer_config(&LCD_backlight_timer));
ESP_ERROR_CHECK(ledc_channel_config(&LCD_backlight_channel));
}
void lcd_backlight_brightness_set(uint8_t brightness_percent) {
if (brightness_percent > 100) {
brightness_percent = 100;
}
if (brightness_percent == 0) {
brightness_percent = 1;
}
brightness_percent = 100 - brightness_percent;
uint32_t duty_cycle = (1023 * brightness_percent) / 100;
ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, duty_cycle));
ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0));
}
对接LVGL
1、放入屏幕的初始数
2、在最新版的 LVGL 中,需要改变 RGB565 中的大小端需要在disp_flush中交换。之后调用底层写入esp_lcd_panel_draw_bitmap。
#include <esp_lcd_panel_ops.h>
#include "lvgl_port.h"
#include "lvgl.h"
#include "iot_knob.h"
#include "module_button.h"
#include "dev_gc9a01.h"
#include "dev_knob.h"
#define BYTE_PER_PIXEL (LV_COLOR_FORMAT_GET_SIZE(LV_COLOR_FORMAT_RGB565_SWAPPED))
static void disp_init(void) {
dev_gc9a01_init();
}
static void disp_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map);
lv_display_t *lv_display;
void lv_port_disp_init(void) {
disp_init();
lv_display = lv_display_create(LCD_H_RES, LCD_V_RES);
lv_display_set_flush_cb(lv_display, disp_flush);
LV_ATTRIBUTE_MEM_ALIGN
static uint8_t buf_1_1[LCD_H_RES * LCD_V_RES * BYTE_PER_PIXEL];
lv_display_set_buffers(lv_display, buf_1_1, NULL, sizeof(buf_1_1), LV_DISPLAY_RENDER_MODE_PARTIAL);
}
static void disp_flush(lv_disp_t *disp_drv, const lv_area_t *area, uint8_t *px_map) {
lv_draw_sw_rgb565_swap(px_map, (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1));
esp_lcd_panel_draw_bitmap(panel_handle, area->x1, area->y1, area->x2 + 1, area->y2 + 1, px_map);
}
LVGL任务
#include <lvsc_main.h>
#include "lvgl_task.h"
#include "lvgl.h"
#include "lvgl_port.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "widgets/lv_example_widgets.h"
#include "benchmark/lv_demo_benchmark.h"
void lvgl_tick(void *pvParameters) {
for (;;) {
lv_tick_inc(5);
vTaskDelay(pdMS_TO_TICKS(5));
}
}
void lvgl_task(void *pvParameters) {
lv_init();
lv_port_disp_init();
//lv_port_knob_init();
// lv_demo_benchmark();
lvsc_main_init();
for (;;) {
lv_task_handler();
vTaskDelay(pdMS_TO_TICKS(5));
}
}
void lvgl_task_init(void) {
xTaskCreate(lvgl_tick, "lvgl_tick", 1024 * 2, NULL, 10, NULL);
xTaskCreate(lvgl_task, "lvgl_task", 1024 * 6, NULL, 10, NULL);
}
License:
CC BY 4.0