314 lines
10 KiB
C
314 lines
10 KiB
C
#include "lcd/lcd.h"
|
|
|
|
#include "driver/gpio.h"
|
|
#include "driver/i2c_master.h"
|
|
#include "driver/ledc.h"
|
|
#include "esp_check.h"
|
|
#include "esp_idf_version.h"
|
|
#include "esp_lcd_panel_io.h"
|
|
#include "esp_lcd_panel_ops.h"
|
|
#include "esp_lcd_panel_rgb.h"
|
|
#include "esp_lcd_touch_gt911.h"
|
|
#include "esp_log.h"
|
|
#include "esp_lvgl_port.h"
|
|
|
|
#define LCD_DATA_WIDTH (16)
|
|
#define LCD_RGB_BUFFER_NUMS (2)
|
|
#define LCD_DRAW_BUFF_HEIGHT (100)
|
|
#define LCD_LVGL_DIRECT_MODE (0)
|
|
#define LCD_LVGL_AVOID_TEAR (0)
|
|
#define LCD_RGB_BOUNCE_BUFFER_MODE (1)
|
|
#define LCD_RGB_BOUNCE_BUFFER_HEIGHT (10)
|
|
#define LCD_LVGL_ROTATION (LV_DISPLAY_ROTATION_90)
|
|
|
|
#define LCD_GPIO_VSYNC (GPIO_NUM_41)
|
|
#define LCD_GPIO_HSYNC (GPIO_NUM_39)
|
|
#define LCD_GPIO_DE (GPIO_NUM_40)
|
|
#define LCD_GPIO_PCLK (GPIO_NUM_0)
|
|
#define LCD_GPIO_DISP (GPIO_NUM_NC)
|
|
#define LCD_GPIO_BACKLIGHT (GPIO_NUM_2)
|
|
#define LCD_BACKLIGHT_ON_LEVEL (1)
|
|
#define LCD_BACKLIGHT_LEDC_MODE (LEDC_LOW_SPEED_MODE)
|
|
#define LCD_BACKLIGHT_LEDC_TIMER (LEDC_TIMER_0)
|
|
#define LCD_BACKLIGHT_LEDC_CHANNEL (LEDC_CHANNEL_0)
|
|
#define LCD_BACKLIGHT_LEDC_RESOLUTION (LEDC_TIMER_8_BIT)
|
|
#define LCD_BACKLIGHT_LEDC_DUTY_MAX ((1 << 8) - 1)
|
|
#define LCD_BACKLIGHT_LEDC_FREQ_HZ (5000)
|
|
#define LCD_BACKLIGHT_FADE_MS (150)
|
|
#define LCD_GPIO_DATA0 (GPIO_NUM_8)
|
|
#define LCD_GPIO_DATA1 (GPIO_NUM_3)
|
|
#define LCD_GPIO_DATA2 (GPIO_NUM_46)
|
|
#define LCD_GPIO_DATA3 (GPIO_NUM_9)
|
|
#define LCD_GPIO_DATA4 (GPIO_NUM_1)
|
|
#define LCD_GPIO_DATA5 (GPIO_NUM_5)
|
|
#define LCD_GPIO_DATA6 (GPIO_NUM_6)
|
|
#define LCD_GPIO_DATA7 (GPIO_NUM_7)
|
|
#define LCD_GPIO_DATA8 (GPIO_NUM_15)
|
|
#define LCD_GPIO_DATA9 (GPIO_NUM_16)
|
|
#define LCD_GPIO_DATA10 (GPIO_NUM_4)
|
|
#define LCD_GPIO_DATA11 (GPIO_NUM_45)
|
|
#define LCD_GPIO_DATA12 (GPIO_NUM_48)
|
|
#define LCD_GPIO_DATA13 (GPIO_NUM_47)
|
|
#define LCD_GPIO_DATA14 (GPIO_NUM_21)
|
|
#define LCD_GPIO_DATA15 (GPIO_NUM_14)
|
|
|
|
#define TOUCH_I2C_NUM (1)
|
|
#define TOUCH_I2C_SCL (GPIO_NUM_20)
|
|
#define TOUCH_I2C_SDA (GPIO_NUM_19)
|
|
#define TOUCH_RST (GPIO_NUM_NC)
|
|
#define TOUCH_INT (GPIO_NUM_NC)
|
|
|
|
#define LCD_PANEL_RGB_TIMING() \
|
|
{ \
|
|
.pclk_hz = 12 * 1000 * 1000, \
|
|
.h_res = LCD_H_RES, \
|
|
.v_res = LCD_V_RES, \
|
|
.hsync_pulse_width = 4, \
|
|
.hsync_front_porch = 8, \
|
|
.hsync_back_porch = 43, \
|
|
.vsync_pulse_width = 4, \
|
|
.vsync_front_porch = 8, \
|
|
.vsync_back_porch = 12, \
|
|
.flags.pclk_active_neg = true, \
|
|
}
|
|
|
|
static const char *TAG = "lcd";
|
|
|
|
static esp_lcd_panel_handle_t lcd_panel;
|
|
static esp_lcd_touch_handle_t touch_handle;
|
|
static lv_display_t *lvgl_display;
|
|
static lv_indev_t *lvgl_touch_indev;
|
|
|
|
static esp_err_t init_backlight(void){
|
|
const ledc_timer_config_t timer_config = {
|
|
.speed_mode = LCD_BACKLIGHT_LEDC_MODE,
|
|
.timer_num = LCD_BACKLIGHT_LEDC_TIMER,
|
|
.duty_resolution = LCD_BACKLIGHT_LEDC_RESOLUTION,
|
|
.freq_hz = LCD_BACKLIGHT_LEDC_FREQ_HZ,
|
|
.clk_cfg = LEDC_AUTO_CLK,
|
|
};
|
|
ESP_RETURN_ON_ERROR(ledc_timer_config(&timer_config), TAG, "Backlight LEDC timer config failed");
|
|
|
|
const ledc_channel_config_t channel_config = {
|
|
.gpio_num = LCD_GPIO_BACKLIGHT,
|
|
.speed_mode = LCD_BACKLIGHT_LEDC_MODE,
|
|
.channel = LCD_BACKLIGHT_LEDC_CHANNEL,
|
|
.timer_sel = LCD_BACKLIGHT_LEDC_TIMER,
|
|
.intr_type = LEDC_INTR_DISABLE,
|
|
.duty = (LCD_BACKLIGHT_ON_LEVEL != 0) ? LCD_BACKLIGHT_LEDC_DUTY_MAX : 0,
|
|
.hpoint = 0,
|
|
};
|
|
ESP_RETURN_ON_ERROR(ledc_channel_config(&channel_config), TAG, "Backlight LEDC channel config failed");
|
|
ESP_RETURN_ON_ERROR(ledc_fade_func_install(0), TAG, "Backlight LEDC fade install failed");
|
|
ESP_LOGI(TAG, "Backlight PWM enabled on GPIO%d", LCD_GPIO_BACKLIGHT);
|
|
return ESP_OK;
|
|
}
|
|
|
|
static esp_err_t probe_gt911_address(i2c_master_bus_handle_t i2c_handle, uint32_t *dev_addr){
|
|
const uint8_t addresses[] = {
|
|
ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS,
|
|
ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP,
|
|
};
|
|
|
|
for(size_t address_index = 0; address_index < sizeof(addresses) / sizeof(addresses[0]); address_index++){
|
|
const uint8_t address = addresses[address_index];
|
|
const esp_err_t probe_ret = i2c_master_probe(i2c_handle, address, 100);
|
|
if(probe_ret == ESP_OK){
|
|
*dev_addr = address;
|
|
ESP_LOGI(TAG, "GT911 found at I2C address 0x%02x", address);
|
|
return ESP_OK;
|
|
}
|
|
|
|
ESP_LOGW(TAG, "GT911 not found at I2C address 0x%02x: %s", address, esp_err_to_name(probe_ret));
|
|
}
|
|
|
|
return ESP_ERR_NOT_FOUND;
|
|
}
|
|
|
|
static esp_err_t init_rgb_panel(void){
|
|
ESP_LOGI(TAG, "Initialize RGB panel");
|
|
|
|
const esp_lcd_rgb_panel_config_t panel_config = {
|
|
.clk_src = LCD_CLK_SRC_PLL160M,
|
|
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0)
|
|
.psram_trans_align = 64,
|
|
#else
|
|
.dma_burst_size = 64,
|
|
#endif
|
|
.data_width = LCD_DATA_WIDTH,
|
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
|
|
.in_color_format = LCD_COLOR_FMT_RGB565,
|
|
#else
|
|
.bits_per_pixel = 16,
|
|
#endif
|
|
.de_gpio_num = LCD_GPIO_DE,
|
|
.pclk_gpio_num = LCD_GPIO_PCLK,
|
|
.vsync_gpio_num = LCD_GPIO_VSYNC,
|
|
.hsync_gpio_num = LCD_GPIO_HSYNC,
|
|
.disp_gpio_num = LCD_GPIO_DISP,
|
|
.data_gpio_nums = {
|
|
LCD_GPIO_DATA0,
|
|
LCD_GPIO_DATA1,
|
|
LCD_GPIO_DATA2,
|
|
LCD_GPIO_DATA3,
|
|
LCD_GPIO_DATA4,
|
|
LCD_GPIO_DATA5,
|
|
LCD_GPIO_DATA6,
|
|
LCD_GPIO_DATA7,
|
|
LCD_GPIO_DATA8,
|
|
LCD_GPIO_DATA9,
|
|
LCD_GPIO_DATA10,
|
|
LCD_GPIO_DATA11,
|
|
LCD_GPIO_DATA12,
|
|
LCD_GPIO_DATA13,
|
|
LCD_GPIO_DATA14,
|
|
LCD_GPIO_DATA15,
|
|
},
|
|
.timings = LCD_PANEL_RGB_TIMING(),
|
|
.flags.fb_in_psram = true,
|
|
.num_fbs = LCD_RGB_BUFFER_NUMS,
|
|
#if LCD_RGB_BOUNCE_BUFFER_MODE
|
|
.bounce_buffer_size_px = LCD_H_RES * LCD_RGB_BOUNCE_BUFFER_HEIGHT,
|
|
#endif
|
|
};
|
|
|
|
ESP_RETURN_ON_ERROR(esp_lcd_new_rgb_panel(&panel_config, &lcd_panel), TAG, "RGB panel create failed");
|
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_init(lcd_panel), TAG, "LCD panel init failed");
|
|
return init_backlight();
|
|
}
|
|
|
|
static esp_err_t init_touch(void){
|
|
ESP_LOGI(TAG, "Initialize GT911 touch");
|
|
|
|
i2c_master_bus_handle_t i2c_handle = NULL;
|
|
const i2c_master_bus_config_t i2c_config = {
|
|
.i2c_port = TOUCH_I2C_NUM,
|
|
.sda_io_num = TOUCH_I2C_SDA,
|
|
.scl_io_num = TOUCH_I2C_SCL,
|
|
.clk_source = I2C_CLK_SRC_DEFAULT,
|
|
.glitch_ignore_cnt = 7,
|
|
.flags = {
|
|
.enable_internal_pullup = true,
|
|
},
|
|
};
|
|
ESP_RETURN_ON_ERROR(i2c_new_master_bus(&i2c_config, &i2c_handle), TAG, "I2C bus init failed");
|
|
|
|
esp_lcd_panel_io_i2c_config_t touch_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
|
|
ESP_RETURN_ON_ERROR(probe_gt911_address(i2c_handle, &touch_io_config.dev_addr), TAG, "GT911 I2C probe failed");
|
|
|
|
esp_lcd_touch_io_gt911_config_t gt911_config = {
|
|
.dev_addr = touch_io_config.dev_addr,
|
|
};
|
|
|
|
const esp_lcd_touch_config_t touch_config = {
|
|
.x_max = LCD_H_RES,
|
|
.y_max = LCD_V_RES,
|
|
.rst_gpio_num = TOUCH_RST,
|
|
.int_gpio_num = TOUCH_INT,
|
|
.levels = {
|
|
.reset = 0,
|
|
.interrupt = 0,
|
|
},
|
|
.flags = {
|
|
.swap_xy = false,
|
|
.mirror_x = false,
|
|
.mirror_y = false,
|
|
},
|
|
.driver_data = >911_config,
|
|
};
|
|
|
|
esp_lcd_panel_io_handle_t touch_io_handle = NULL;
|
|
ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c(i2c_handle, &touch_io_config, &touch_io_handle), TAG, "Touch IO init failed");
|
|
ESP_RETURN_ON_ERROR(esp_lcd_touch_new_i2c_gt911(touch_io_handle, &touch_config, &touch_handle), TAG, "GT911 driver init failed");
|
|
ESP_LOGI(TAG, "GT911 touch ready");
|
|
return ESP_OK;
|
|
}
|
|
|
|
static esp_err_t init_lvgl(void){
|
|
ESP_LOGI(TAG, "Initialize LVGL port");
|
|
lvgl_port_cfg_t lvgl_config = ESP_LVGL_PORT_INIT_CONFIG();
|
|
lvgl_config.task_stack = 16 * 1024;
|
|
ESP_RETURN_ON_ERROR(lvgl_port_init(&lvgl_config), TAG, "LVGL port init failed");
|
|
|
|
uint32_t buffer_size = LCD_H_RES * LCD_DRAW_BUFF_HEIGHT;
|
|
#if LCD_LVGL_DIRECT_MODE
|
|
buffer_size = LCD_H_RES * LCD_V_RES;
|
|
#endif
|
|
|
|
const lvgl_port_display_cfg_t display_config = {
|
|
.panel_handle = lcd_panel,
|
|
.buffer_size = buffer_size,
|
|
.double_buffer = false,
|
|
.hres = LCD_H_RES,
|
|
.vres = LCD_V_RES,
|
|
.monochrome = false,
|
|
#if LVGL_VERSION_MAJOR >= 9
|
|
.color_format = LV_COLOR_FORMAT_RGB565,
|
|
#endif
|
|
.rotation = {
|
|
.swap_xy = false,
|
|
.mirror_x = false,
|
|
.mirror_y = false,
|
|
},
|
|
.flags = {
|
|
.buff_dma = false,
|
|
.buff_spiram = false,
|
|
.sw_rotate = true,
|
|
#if LVGL_VERSION_MAJOR >= 9
|
|
.swap_bytes = false,
|
|
#endif
|
|
#if LCD_LVGL_DIRECT_MODE
|
|
.direct_mode = true,
|
|
#endif
|
|
},
|
|
};
|
|
|
|
const lvgl_port_display_rgb_cfg_t rgb_config = {
|
|
.flags = {
|
|
.bb_mode = true,
|
|
#if LCD_LVGL_AVOID_TEAR
|
|
.avoid_tearing = true,
|
|
#endif
|
|
},
|
|
};
|
|
|
|
ESP_LOGI(TAG, "Add LVGL RGB display");
|
|
lvgl_display = lvgl_port_add_disp_rgb(&display_config, &rgb_config);
|
|
ESP_RETURN_ON_FALSE(lvgl_display, ESP_FAIL, TAG, "LVGL display add failed");
|
|
lv_display_set_rotation(lvgl_display, LCD_LVGL_ROTATION);
|
|
ESP_LOGI(TAG, "LVGL display rotation set to %d", LCD_LVGL_ROTATION);
|
|
|
|
if(touch_handle){
|
|
const lvgl_port_touch_cfg_t touch_config = {
|
|
.disp = lvgl_display,
|
|
.handle = touch_handle,
|
|
};
|
|
lvgl_touch_indev = lvgl_port_add_touch(&touch_config);
|
|
ESP_RETURN_ON_FALSE(lvgl_touch_indev, ESP_FAIL, TAG, "LVGL touch add failed");
|
|
ESP_LOGI(TAG, "LVGL touch input ready");
|
|
}
|
|
else ESP_LOGW(TAG, "LVGL touch input disabled");
|
|
|
|
ESP_LOGI(TAG, "LVGL ready");
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
void lcd_backlight_set(uint8_t level){
|
|
const uint32_t duty = (LCD_BACKLIGHT_ON_LEVEL != 0) ? level : (LCD_BACKLIGHT_LEDC_DUTY_MAX - level);
|
|
esp_err_t err = ledc_set_fade_with_time(LCD_BACKLIGHT_LEDC_MODE, LCD_BACKLIGHT_LEDC_CHANNEL, duty, LCD_BACKLIGHT_FADE_MS);
|
|
if(err == ESP_OK) err = ledc_fade_start(LCD_BACKLIGHT_LEDC_MODE, LCD_BACKLIGHT_LEDC_CHANNEL, LEDC_FADE_WAIT_DONE);
|
|
if(err != ESP_OK) ESP_LOGW(TAG, "Backlight set failed: %s", esp_err_to_name(err));
|
|
}
|
|
|
|
esp_err_t lcd_init(void){
|
|
ESP_RETURN_ON_ERROR(init_rgb_panel(), TAG, "LCD panel init failed");
|
|
const esp_err_t touch_ret = init_touch();
|
|
if(touch_ret != ESP_OK) ESP_LOGW(TAG, "Continuing without touch: %s", esp_err_to_name(touch_ret));
|
|
return init_lvgl();
|
|
}
|
|
|
|
lv_display_t *lcd_get_display(void){
|
|
return lvgl_display;
|
|
}
|