avatar

松果工作室

欢迎光临

  • 首页
  • ESP
  • LVGL
  • CubeMX
  • freeRTOS
  • 快速笔记
  • 个人收藏
  • 我的服务
  • 所见所闻
Home (ESP-IDF)LVGL 对接 显示
文章

(ESP-IDF)LVGL 对接 显示

Posted 2024-04-3 Updated 2026-02- 18
By YCP
15~19 min read

驱动屏幕

此处使用 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);
}
LVGL ESP
License:  CC BY 4.0
Share

Further Reading

OLDER

(ESP-IDF)LVGL 对接 编码器

NEWER

(Linux)算丰学院

Recently Updated

  • [CubeMX] SPI
  • [CubeMX] 串口 DMA
  • [CubeMX] 基础工程配置
  • (LVGL)接口预览 样式
  • (LVGL)案例预览 页面滚动吸附

Trending Tags

LVGL WCH Linux Elec ThatProject freeRTOS STM ESP Flutter Others

Contents

©2026 松果工作室. Some rights reserved.

Using the Halo theme Chirpy