FluxDC/components/FluxDisplayPort/FluxDisplayPort.c
2025-04-28 17:49:47 +08:00

393 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @file FluxDisplayPort.c
* @brief LCD及触摸屏控制函数实现
*
* 用于实现LCD控制IO及初始化函数
*
* @author wang xiang en
* @date 2025-04-18
* @version 版本号
* @copyright 版权声明((C)2025, YUWELL MEDTECH Co.ltd
*/
#include "FluxDisplayPort.h"
/* 用于初始化配置的全局变量 */
static esp_lcd_touch_handle_t tp = NULL;
static SemaphoreHandle_t lvgl_mux = NULL;
static const char *TAG = "LCD_Init";
/* 用于初始化配置的全局变量 */
static lv_disp_draw_buf_t disp_buf; // Contains internal graphic buffer(s) called draw buffer(s)
static lv_disp_drv_t disp_drv; // Contains callback functions
/**
* @brief 通知LVGL库屏幕已经更新
*
* 屏幕刷新时LVGL库不会自动通知屏幕已经更新需要手动调用此函数通知LVGL库屏幕已经更新。
*
* @param[in] panel_io lcd控制面板
* @param[in] edata 事件码
* @param[in] user_ctx 用户数据
*
* @return 成功返回0失败返回错误码
*/
static bool example_notify_lvgl_flush_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
{
lv_disp_drv_t *disp_driver = (lv_disp_drv_t *)user_ctx;
lv_disp_flush_ready(disp_driver);
return false;
}
/**
* @brief 刷新屏幕
*
* 使用lcd驱动进行屏幕内容刷新
*
* @param[in] drv lcd控制驱动
* @param[in] area 绘图区域
* @param[in] color_map 色谱
*
*/
static void example_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
{
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;
int offsetx1 = area->x1;
int offsetx2 = area->x2;
int offsety1 = area->y1;
int offsety2 = area->y2;
// Copy a buffer's content to a specific area of the display
esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
}
/**
* @brief 触摸屏控制块
*
* 通知LCD内核触摸屏的点击事件
*
* @param[in] drv 控制驱动
* @param[out] data 点击区域
*
*/
static void example_lvgl_touch_cb(lv_indev_drv_t *drv, lv_indev_data_t *data)
{
uint16_t touchpad_x[1] = {0};
uint16_t touchpad_y[1] = {0};
uint8_t touchpad_cnt = 0;
/* Read touch controller data */
esp_lcd_touch_read_data(drv->user_data);
/* Get coordinates */
bool touchpad_pressed = esp_lcd_touch_get_coordinates(drv->user_data, touchpad_x, touchpad_y, NULL, &touchpad_cnt, 1);
if (touchpad_pressed && touchpad_cnt > 0)
{
data->point.x = touchpad_x[0];
data->point.y = touchpad_y[0];
data->state = LV_INDEV_STATE_PRESSED;
}
else
{
data->state = LV_INDEV_STATE_RELEASED;
}
}
/**
* @brief lvgl时钟加一
*
* 时钟计数增加驱动lvgl时钟
*
* @param[in] arg not used
*
*/
static void example_increase_lvgl_tick(void *arg)
{
/* Tell LVGL how many milliseconds have elapsed */
lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
}
/**
* @brief lvgl锁
*
* 加锁以保护临界资源
*
* @param[in] timeout_ms 锁住LVGL锁时间
* @return true lvgl锁成功
*/
bool example_lvgl_lock(int timeout_ms)
{
// Convert timeout in milliseconds to FreeRTOS ticks
// If `timeout_ms` is set to -1, the program will block until the condition is met
const TickType_t timeout_ticks = (timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
return xSemaphoreTakeRecursive(lvgl_mux, timeout_ticks) == pdTRUE;
}
/**
* @brief lvgl解锁
*
* 解锁更新界面内容
*/
void example_lvgl_unlock(void)
{
xSemaphoreGiveRecursive(lvgl_mux);
}
/**
* @brief lvgl主任务
*
* 启动后开始进行lvgl线程运行
*
* @param[in] arg not used
*/
static void example_lvgl_port_task(void *arg)
{
ESP_LOGI(TAG, "Starting LVGL task");
uint32_t task_delay_ms = EXAMPLE_LVGL_TASK_MAX_DELAY_MS;
while (1) {
// Lock the mutex due to the LVGL APIs are not thread-safe
if (example_lvgl_lock(-1)) {
task_delay_ms = lv_timer_handler();
// Release the mutex
example_lvgl_unlock();
}
if (task_delay_ms > EXAMPLE_LVGL_TASK_MAX_DELAY_MS) {
task_delay_ms = EXAMPLE_LVGL_TASK_MAX_DELAY_MS;
} else if (task_delay_ms < EXAMPLE_LVGL_TASK_MIN_DELAY_MS) {
task_delay_ms = EXAMPLE_LVGL_TASK_MIN_DELAY_MS;
}
vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
}
}
/**
* @brief i80总线初始化
*
* 根据IO Num初始化总线
*
* @param[in] io_handle 控制句柄
* @param[in] user_ctx 用户数据
*/
static void example_init_i80_bus(esp_lcd_panel_io_handle_t *io_handle, void *user_ctx)
{
ESP_LOGI(TAG, "Initialize Intel 8080 bus");
/* 创建i80总线控制句柄 */
esp_lcd_i80_bus_handle_t i80_bus = NULL;
esp_lcd_i80_bus_config_t bus_config = {
.clk_src = LCD_CLK_SRC_DEFAULT,
.dc_gpio_num = EXAMPLE_PIN_NUM_DC,
.wr_gpio_num = EXAMPLE_PIN_NUM_PCLK,
.data_gpio_nums = {
EXAMPLE_PIN_NUM_DATA0,
EXAMPLE_PIN_NUM_DATA1,
EXAMPLE_PIN_NUM_DATA2,
EXAMPLE_PIN_NUM_DATA3,
EXAMPLE_PIN_NUM_DATA4,
EXAMPLE_PIN_NUM_DATA5,
EXAMPLE_PIN_NUM_DATA6,
EXAMPLE_PIN_NUM_DATA7,
},
.bus_width = CONFIG_EXAMPLE_LCD_I80_BUS_WIDTH,
.max_transfer_bytes = EXAMPLE_LCD_H_RES * 100 * sizeof(uint16_t),
.dma_burst_size = EXAMPLE_DMA_BURST_SIZE,
};
/* 根据配置创建 i80总线控制句柄 */
ESP_ERROR_CHECK(esp_lcd_new_i80_bus(&bus_config, &i80_bus));
/* 配置i80总线IO引脚 */
esp_lcd_panel_io_i80_config_t io_config = {
.cs_gpio_num = EXAMPLE_PIN_NUM_CS,
.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
.trans_queue_depth = 10,
.dc_levels = {
.dc_idle_level = 0,
.dc_cmd_level = 0,
.dc_dummy_level = 0,
.dc_data_level = 1,
},
.flags = {
.swap_color_bytes = !LV_COLOR_16_SWAP, // Swap can be done in LvGL (default) or DMA
},
.on_color_trans_done = example_notify_lvgl_flush_ready,
.user_ctx = user_ctx,
.lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
.lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
};
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i80(i80_bus, &io_config, io_handle));
}
/**
* @brief LCD控制句柄
*
* 添加LCD控制句柄
*
* @param[in] io_handle io句柄
* @param[out] panel lcd句柄
*/
static void example_init_lcd_panel(esp_lcd_panel_io_handle_t io_handle, esp_lcd_panel_handle_t *panel)
{
esp_lcd_panel_handle_t panel_handle = NULL;
ESP_LOGI(TAG, "Install LCD driver of st7789");
esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = EXAMPLE_PIN_NUM_RST,
.rgb_ele_order = COLOR_RGB_ELEMENT_ORDER_BGR,
.bits_per_pixel = 16,
};
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle));
esp_lcd_panel_reset(panel_handle);
esp_lcd_panel_init(panel_handle);
// Set inversion, x/y coordinate order, x/y mirror according to your LCD module spec
// The gap is LCD panel specific, even panels with the same driver IC, can have different gap value
esp_lcd_panel_invert_color(panel_handle, false);
esp_lcd_panel_set_gap(panel_handle, 0, 0);
esp_lcd_panel_mirror(panel_handle, true, false);
*panel = panel_handle;
}
/**
* @brief 触摸屏驱动初始化
*
*/
static void example_init_touch_panel(void)
{
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
ESP_LOGI(TAG, "Initialize I2C");
const i2c_config_t i2c_conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = EXAMPLE_I2C_SDA,
.scl_io_num = EXAMPLE_I2C_SCL,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 400000,
};
/* Initialize I2C */
ESP_ERROR_CHECK(i2c_param_config(EXAMPLE_I2C_NUM, &i2c_conf));
ESP_ERROR_CHECK(i2c_driver_install(EXAMPLE_I2C_NUM, i2c_conf.mode, 0, 0, 0));
esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_FT5x06_CONFIG();
ESP_LOGI(TAG, "Initialize touch IO (I2C)");
/* Touch IO handle */
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)EXAMPLE_I2C_NUM, &tp_io_config, &tp_io_handle));
esp_lcd_touch_config_t tp_cfg = {
.x_max = EXAMPLE_LCD_V_RES,
.y_max = EXAMPLE_LCD_H_RES,
.rst_gpio_num = -1,
.int_gpio_num = -1,
.flags = {
.swap_xy = 0,
.mirror_x = 0,
.mirror_y = 0,
},
};
ESP_LOGI(TAG, "Initialize touch controller FT5X06");
ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_ft5x06(tp_io_handle, &tp_cfg, &tp));
}
/**
* @brief 最后总的触摸屏及LCD初始化函数
*
*/
void initialize_display_and_touch(void)
{
/* 确定背光是否可用 */
#if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
ESP_LOGI(TAG, "Turn off LCD backlight");
gpio_config_t bk_gpio_config = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT
};
ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
/* 关闭LCD背光 */
gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL);
#endif // EXAMPLE_PIN_NUM_BK_LIGHT >= 0
/* 初始化i80总线 */
esp_lcd_panel_io_handle_t io_handle = NULL;
example_init_i80_bus(&io_handle, &disp_drv);
/* 初始化LCD控制panel */
esp_lcd_panel_handle_t panel_handle = NULL;
example_init_lcd_panel(io_handle, &panel_handle);
// Stub: User can flush pre-defined pattern to the screen before we turn on the screen or backlight
/* Since everything is initialized, let's display some content */
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
/* Turn on the backlight! */
#if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
ESP_LOGI(TAG, "Turn on LCD backlight");
//等待所有初始完毕后再开灯
//gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);
#endif // EXAMPLE_PIN_NUM_BK_LIGHT >= 0
/* 初始化触摸屏 */
example_init_touch_panel();
ESP_LOGI(TAG, "Initialize LVGL library");
lv_init();
// Allocate draw buffers used by LVGL
// It's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized
uint32_t draw_buf_alloc_caps = 0;
#if CONFIG_EXAMPLE_LCD_I80_COLOR_IN_PSRAM
draw_buf_alloc_caps |= MALLOC_CAP_SPIRAM;
#endif
lv_color_t *buf1 = esp_lcd_i80_alloc_draw_buffer(io_handle, EXAMPLE_LCD_H_RES * 100 * sizeof(lv_color_t), draw_buf_alloc_caps);
lv_color_t *buf2 = esp_lcd_i80_alloc_draw_buffer(io_handle, EXAMPLE_LCD_H_RES * 100 * sizeof(lv_color_t), draw_buf_alloc_caps);
assert(buf1);
assert(buf2);
ESP_LOGI(TAG, "buf1@%p, buf2@%p", buf1, buf2);
// Initialize LVGL draw buffers
lv_disp_draw_buf_init(&disp_buf, buf1, buf2, EXAMPLE_LCD_H_RES * 100);
ESP_LOGI(TAG, "Register display driver to LVGL");
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = EXAMPLE_LCD_H_RES;
disp_drv.ver_res = EXAMPLE_LCD_V_RES;
disp_drv.flush_cb = example_lvgl_flush_cb;
disp_drv.draw_buf = &disp_buf;
disp_drv.user_data = panel_handle;
lv_disp_t *disp = lv_disp_drv_register(&disp_drv);
ESP_LOGI(TAG, "Install LVGL tick timer");
// Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
const esp_timer_create_args_t lvgl_tick_timer_args = {
.callback = &example_increase_lvgl_tick,
.name = "lvgl_tick"
};
esp_timer_handle_t lvgl_tick_timer = NULL;
ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000));
/* Install touch screen driver */
static lv_indev_drv_t indev_drv; // Input device driver (Touch)
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.disp = disp;
indev_drv.read_cb = example_lvgl_touch_cb;
indev_drv.user_data = tp;
lv_indev_drv_register(&indev_drv);
lvgl_mux = xSemaphoreCreateRecursiveMutex();
assert(lvgl_mux);
ESP_LOGI(TAG, "Create LVGL task");
xTaskCreate(example_lvgl_port_task, "LVGL", EXAMPLE_LVGL_TASK_STACK_SIZE, NULL, EXAMPLE_LVGL_TASK_PRIORITY, NULL);
ESP_LOGI(TAG, "Display LVGL animation");
}