123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597 |
- /*
- * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
- *
- * SPDX-License-Identifier: Apache-2.0
- */
- #include "freertos/FreeRTOS.h"
- #include "freertos/semphr.h"
- #include "freertos/task.h"
- #include "esp_lcd_panel_ops.h"
- #include "esp_lcd_panel_rgb.h"
- #include "esp_lcd_touch.h"
- #include "esp_timer.h"
- #include "esp_log.h"
- #include "lvgl.h"
- #include "lvgl_port.h"
- #include "app/include/beep.h"
- static const char *TAG = "lv_port";
- static SemaphoreHandle_t lvgl_mux; // LVGL 互斥锁
- static TaskHandle_t lvgl_task_handle = NULL;
- static bool isPressed = false;
- static bool isReleased = false;
- // #define EXAMPLE_LVGL_PORT_ROTATION_180 1
- #define EXAMPLE_LVGL_PORT_ROTATION_DEGREE 180
- #if EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0
- static void *get_next_frame_buffer(esp_lcd_panel_handle_t panel_handle)
- {
- static void *next_fb = NULL;
- static void *fb[2] = {NULL};
- if (next_fb == NULL)
- {
- ESP_ERROR_CHECK(esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 2, &fb[0], &fb[1])); // 获取双缓冲区
- next_fb = fb[1];
- }
- else
- {
- next_fb = (next_fb == fb[0]) ? fb[1] : fb[0]; // 切换缓冲区
- }
- return next_fb;
- }
- IRAM_ATTR static void rotate_copy_pixel(const uint16_t *from, uint16_t *to, uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end, uint16_t w, uint16_t h, uint16_t rotation)
- {
- int from_index = 0;
- int to_index = 0;
- int to_index_const = 0;
- switch (rotation)
- {
- case 90:
- to_index_const = (w - x_start - 1) * h;
- for (int from_y = y_start; from_y < y_end + 1; from_y++)
- {
- from_index = from_y * w + x_start;
- to_index = to_index_const + from_y;
- for (int from_x = x_start; from_x < x_end + 1; from_x++)
- {
- *(to + to_index) = *(from + from_index);
- from_index += 1;
- to_index -= h;
- }
- }
- break;
- case 180:
- to_index_const = h * w - x_start - 1;
- for (int from_y = y_start; from_y < y_end + 1; from_y++)
- {
- from_index = from_y * w + x_start;
- to_index = to_index_const - from_y * w;
- for (int from_x = x_start; from_x < x_end + 1; from_x++)
- {
- *(to + to_index) = *(from + from_index);
- from_index += 1;
- to_index -= 1;
- }
- }
- break;
- case 270:
- to_index_const = (x_start + 1) * h - 1;
- for (int from_y = y_start; from_y < y_end + 1; from_y++)
- {
- from_index = from_y * w + x_start;
- to_index = to_index_const - from_y;
- for (int from_x = x_start; from_x < x_end + 1; from_x++)
- {
- *(to + to_index) = *(from + from_index);
- from_index += 1;
- to_index += h;
- }
- }
- break;
- default:
- break;
- }
- }
- #endif /* EXAMPLE_LVGL_PORT_ROTATION_DEGREE */
- #if LVGL_PORT_AVOID_TEAR_ENABLE
- #if LVGL_PORT_DIRECT_MODE
- #if EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0
- typedef struct
- {
- uint16_t inv_p;
- uint8_t inv_area_joined[LV_INV_BUF_SIZE];
- lv_area_t inv_areas[LV_INV_BUF_SIZE];
- } lv_port_dirty_area_t;
- typedef enum
- {
- FLUSH_STATUS_PART,
- FLUSH_STATUS_FULL
- } lv_port_flush_status_t;
- typedef enum
- {
- FLUSH_PROBE_PART_COPY,
- FLUSH_PROBE_SKIP_COPY,
- FLUSH_PROBE_FULL_COPY,
- } lv_port_flush_probe_t;
- static lv_port_dirty_area_t dirty_area;
- static void flush_dirty_save(lv_port_dirty_area_t *dirty_area)
- {
- lv_disp_t *disp = _lv_refr_get_disp_refreshing(); // 获取正在刷新的显示设备
- dirty_area->inv_p = disp->inv_p; // 保存无效区域的数量
- for (int i = 0; i < disp->inv_p; i++)
- {
- dirty_area->inv_area_joined[i] = disp->inv_area_joined[i]; // 保存无效区域的合并状态
- dirty_area->inv_areas[i] = disp->inv_areas[i]; // 保存无效区域的信息
- }
- }
- /**
- * @brief 探测无效区域以进行复制
- *
- * @note 此函数用于避免撕裂效果,仅在 LVGL 直接模式下有效。
- *
- */
- static lv_port_flush_probe_t flush_copy_probe(lv_disp_drv_t *drv)
- {
- static lv_port_flush_status_t prev_status = FLUSH_STATUS_PART;
- lv_port_flush_status_t cur_status;
- lv_port_flush_probe_t probe_result;
- lv_disp_t *disp_refr = _lv_refr_get_disp_refreshing(); // 获取正在刷新的显示设备
- uint32_t flush_ver = 0;
- uint32_t flush_hor = 0;
- for (int i = 0; i < disp_refr->inv_p; i++)
- {
- if (disp_refr->inv_area_joined[i] == 0)
- {
- flush_ver = (disp_refr->inv_areas[i].y2 + 1 - disp_refr->inv_areas[i].y1); // 计算垂直方向的刷新长度
- flush_hor = (disp_refr->inv_areas[i].x2 + 1 - disp_refr->inv_areas[i].x1); // 计算水平方向的刷新长度
- break;
- }
- }
- /* 检查当前是否为全屏刷新 */
- cur_status = ((flush_ver == drv->ver_res) && (flush_hor == drv->hor_res)) ? (FLUSH_STATUS_FULL) : (FLUSH_STATUS_PART);
- if (prev_status == FLUSH_STATUS_FULL)
- {
- if ((cur_status == FLUSH_STATUS_PART))
- {
- probe_result = FLUSH_PROBE_FULL_COPY; // 上次是全屏刷新,本次是部分刷新,需要复制整个屏幕
- }
- else
- {
- probe_result = FLUSH_PROBE_SKIP_COPY; // 上次和本次都是全屏刷新,跳过复制
- }
- }
- else
- {
- probe_result = FLUSH_PROBE_PART_COPY; // 上次是部分刷新,本次也是部分刷新,只复制部分区域
- }
- prev_status = cur_status;
- return probe_result;
- }
- static inline void *flush_get_next_buf(void *panel_handle)
- {
- return get_next_frame_buffer(panel_handle); // 获取下一个帧缓冲区
- }
- /**
- * @brief 复制无效区域
- *
- * @note 此函数用于避免撕裂效果,仅在 LVGL 直接模式下有效。
- *
- */
- static void flush_dirty_copy(void *dst, void *src, lv_port_dirty_area_t *dirty_area)
- {
- lv_coord_t x_start, x_end, y_start, y_end;
- for (int i = 0; i < dirty_area->inv_p; i++)
- {
- /* 刷新未合并的区域 */
- if (dirty_area->inv_area_joined[i] == 0)
- {
- x_start = dirty_area->inv_areas[i].x1;
- x_end = dirty_area->inv_areas[i].x2;
- y_start = dirty_area->inv_areas[i].y1;
- y_end = dirty_area->inv_areas[i].y2;
- rotate_copy_pixel(src, dst, x_start, y_start, x_end, y_end, LV_HOR_RES, LV_VER_RES, EXAMPLE_LVGL_PORT_ROTATION_DEGREE);
- }
- }
- }
- static void flush_callback(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;
- const int offsetx1 = area->x1;
- const int offsetx2 = area->x2;
- const int offsety1 = area->y1;
- const int offsety2 = area->y2;
- void *next_fb = NULL;
- lv_port_flush_probe_t probe_result = FLUSH_PROBE_PART_COPY;
- lv_disp_t *disp = lv_disp_get_default();
- /* 最后一个区域刷新后的操作 */
- if (lv_disp_flush_is_last(drv))
- {
- /* 检查是否触发了 `full_refresh` 标志 */
- if (drv->full_refresh)
- {
- /* 重置标志 */
- drv->full_refresh = 0;
- // 将整个屏幕的 LVGL 缓冲区数据旋转并复制到下一个帧缓冲区
- next_fb = flush_get_next_buf(panel_handle);
- rotate_copy_pixel((uint16_t *)color_map, next_fb, offsetx1, offsety1, offsetx2, offsety2, LV_HOR_RES, LV_VER_RES, EXAMPLE_LVGL_PORT_ROTATION_DEGREE);
- /* 将当前 RGB 帧缓冲区切换到 `next_fb` */
- esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, next_fb);
- /* 等待当前帧缓冲区传输完成 */
- ulTaskNotifyValueClear(NULL, ULONG_MAX);
- ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
- /* 异步更新另一个帧缓冲区的无效区域 */
- flush_dirty_copy(flush_get_next_buf(panel_handle), color_map, &dirty_area);
- flush_get_next_buf(panel_handle);
- }
- else
- {
- /* 探测当前无效区域的复制方法 */
- probe_result = flush_copy_probe(drv);
- if (probe_result == FLUSH_PROBE_FULL_COPY)
- {
- /* 保存当前无效区域以供下一个帧缓冲区使用 */
- flush_dirty_save(&dirty_area);
- /* 设置 LVGL 全刷新标志并提前设置刷新准备好 */
- drv->full_refresh = 1;
- disp->rendering_in_progress = false;
- lv_disp_flush_ready(drv);
- /* 强制刷新整个屏幕,将递归调用 `flush_callback` */
- lv_refr_now(_lv_refr_get_disp_refreshing());
- }
- else
- {
- /* 更新当前无效区域以供下一个帧缓冲区使用 */
- next_fb = flush_get_next_buf(panel_handle);
- flush_dirty_save(&dirty_area);
- flush_dirty_copy(next_fb, color_map, &dirty_area);
- /* 将当前 RGB 帧缓冲区切换到 `next_fb` */
- esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, next_fb);
- /* 等待当前帧缓冲区传输完成 */
- ulTaskNotifyValueClear(NULL, ULONG_MAX);
- ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
- if (probe_result == FLUSH_PROBE_PART_COPY)
- {
- /* 异步更新另一个帧缓冲区的无效区域 */
- flush_dirty_save(&dirty_area);
- flush_dirty_copy(flush_get_next_buf(panel_handle), color_map, &dirty_area);
- flush_get_next_buf(panel_handle);
- }
- }
- }
- }
- lv_disp_flush_ready(drv);
- }
- #endif
- #elif LVGL_PORT_FULL_REFRESH && LVGL_PORT_LCD_RGB_BUFFER_NUMS == 3
- #if EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 0
- static void *lvgl_port_rgb_last_buf = NULL;
- static void *lvgl_port_rgb_next_buf = NULL;
- static void *lvgl_port_flush_next_buf = NULL;
- #endif
- void flush_callback(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;
- const int offsetx1 = area->x1;
- const int offsetx2 = area->x2;
- const int offsety1 = area->y1;
- const int offsety2 = area->y2;
- #if EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0
- void *next_fb = get_next_frame_buffer(panel_handle);
- /* 将当前 LVGL 缓冲区中的无效区域旋转并复制到下一个 RGB 帧缓冲区 */
- rotate_copy_pixel((uint16_t *)color_map, next_fb, offsetx1, offsety1, offsetx2, offsety2, LV_HOR_RES, LV_VER_RES, EXAMPLE_LVGL_PORT_ROTATION_DEGREE);
- /* 将当前 RGB 帧缓冲区切换到 `next_fb` */
- esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, next_fb);
- #else
- drv->draw_buf->buf1 = color_map;
- drv->draw_buf->buf2 = lvgl_port_flush_next_buf;
- lvgl_port_flush_next_buf = color_map;
- /* 将当前 RGB 帧缓冲区切换到 `color_map` */
- esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
- lvgl_port_rgb_next_buf = color_map;
- #endif
- lv_disp_flush_ready(drv);
- }
- #endif
- #else
- static void flush_callback(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map)
- {
- esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)lv_display_get_user_data(disp);
- // static uint8_t rotated_buf[LVGL_PORT_H_RES * LVGL_PORT_V_RES];
- //
- // lv_display_rotation_t rotation = lv_display_get_rotation(disp);
- // lv_area_t rotated_area;
- // if(rotation != LV_DISPLAY_ROTATION_0) {
- // lv_color_format_t cf = lv_display_get_color_format(disp);
- // /*Calculate the position of the rotated area*/
- // rotated_area = *area;
- // lv_display_rotate_area(disp, &rotated_area);
- // /*Calculate the source stride (bytes in a line) from the width of the area*/
- // uint32_t src_stride = lv_draw_buf_width_to_stride(lv_area_get_width(area), cf);
- // /*Calculate the stride of the destination (rotated) area too*/
- // uint32_t dest_stride = lv_draw_buf_width_to_stride(lv_area_get_width(&rotated_area), cf);
- // /*Have a buffer to store the rotated area and perform the rotation*/
- //
- // int32_t src_w = lv_area_get_width(area);
- // int32_t src_h = lv_area_get_height(area);
- // lv_draw_sw_rotate(px_map, rotated_buf, src_w, src_h, src_stride, dest_stride, rotation, cf);
- // /*Use the rotated area and rotated buffer from now on*/
- // area = &rotated_area;
- // px_map = rotated_buf;
- // }
- const int offsetx1 = area->x1;
- const int offsetx2 = area->x2;
- const int offsety1 = area->y1;
- const int offsety2 = area->y2;
- /* 将颜色映射中的数据直接复制到 RGB 帧缓冲区 */
- esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, px_map);
- lv_display_flush_ready(disp);
- }
- #endif /* LVGL_PORT_AVOID_TEAR_ENABLE */
- static lv_display_t *display_init(esp_lcd_panel_handle_t panel_handle)
- {
- assert(panel_handle);
- ESP_LOGD(TAG, "为 LVGL 缓冲区分配内存");
- // 分配 LVGL 使用的绘图缓冲区
- void *buf1 = NULL;
- void *buf2 = NULL;
- int buffer_size = 0;
- #if LVGL_PORT_AVOID_TEAR_ENABLE
- // 为了避免撕裂效果,我们应该至少使用两个帧缓冲区:一个用于 LVGL 渲染,另一个用于 RGB 输出
- buffer_size = LVGL_PORT_H_RES * LVGL_PORT_V_RES;
- #if (LVGL_PORT_LCD_RGB_BUFFER_NUMS == 3) && (EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 0) && LVGL_PORT_FULL_REFRESH
- // 使用三个缓冲区和全刷新,我们总是有一个缓冲区可用于渲染,消除了等待 RGB 同步信号的需要
- ESP_ERROR_CHECK(esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 3, &lvgl_port_rgb_last_buf, &buf1, &buf2));
- lvgl_port_rgb_next_buf = lvgl_port_rgb_last_buf;
- lvgl_port_flush_next_buf = buf2;
- #elif (LVGL_PORT_LCD_RGB_BUFFER_NUMS == 3) && (EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0)
- // 这里我们使用三个帧缓冲区,一个用于 LVGL 渲染,另外两个用于 RGB 驱动(其中一个用于旋转)
- void *fbs[3];
- ESP_ERROR_CHECK(esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 3, &fbs[0], &fbs[1], &fbs[2]));
- buf1 = fbs[2];
- #else
- ESP_ERROR_CHECK(esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 2, &buf1, &buf2));
- #endif
- #else
- buffer_size = LVGL_PORT_H_RES * LVGL_PORT_BUFFER_HEIGHT;
- buf1 = heap_caps_malloc(buffer_size * sizeof(lv_color_t), MALLOC_CAP_SPIRAM);
- assert(buf1);
- ESP_LOGI(TAG, "LVGL 缓冲区大小: %dKB", buffer_size * sizeof(lv_color_t) / 1024);
- #endif
- // 创建显示设备
- lv_display_t *disp = lv_display_create(LVGL_PORT_H_RES, LVGL_PORT_V_RES);
- if (!disp)
- {
- ESP_LOGE(TAG, "创建显示设备失败");
- return NULL;
- }
- // 设置显示缓冲区
- lv_display_set_buffers(disp, buf1, buf2, buffer_size * sizeof(lv_color_t), LV_DISPLAY_RENDER_MODE_PARTIAL);
- // 设置显示回调
- lv_display_set_flush_cb(disp, flush_callback);
- // 设置用户数据
- lv_display_set_user_data(disp, panel_handle);
- #if LVGL_PORT_FULL_REFRESH
- lv_display_set_render_mode(disp, LV_DISPLAY_RENDER_MODE_FULL);
- #elif LVGL_PORT_DIRECT_MODE
- lv_display_set_render_mode(disp, LV_DISPLAY_RENDER_MODE_DIRECT);
- #endif
- return disp;
- }
- static void touchpad_read(lv_indev_t *indev, lv_indev_data_t *data)
- {
- esp_lcd_touch_handle_t tp = (esp_lcd_touch_handle_t)lv_indev_get_user_data(indev);
- assert(tp);
- uint16_t touchpad_x;
- uint16_t touchpad_y;
- uint8_t touchpad_cnt = 0;
- esp_lcd_touch_read_data(tp);
- bool touchpad_pressed = esp_lcd_touch_get_coordinates(tp, &touchpad_x, &touchpad_y, NULL, &touchpad_cnt, 1);
- if (touchpad_pressed && touchpad_cnt > 0)
- {
- data->point.x = touchpad_x;
- data->point.y = touchpad_y;
- data->state = LV_INDEV_STATE_PRESSED;
- if(!isReleased && !isPressed)
- {
- beep();
- }
- }
- else
- {
- data->state = LV_INDEV_STATE_RELEASED;
- beep_stop();
- }
- }
- static lv_indev_t *indev_init(esp_lcd_touch_handle_t tp)
- {
- assert(tp);
- // 创建输入设备
- lv_indev_t *indev = lv_indev_create();
- if (!indev)
- {
- ESP_LOGE(TAG, "创建输入设备失败");
- return NULL;
- }
- // 设置输入设备属性
- lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
- lv_indev_set_read_cb(indev, touchpad_read);
- lv_indev_set_user_data(indev, tp);
- return indev;
- }
- static void tick_increment(void *arg)
- {
- /* 告诉 LVGL 已经过去了多少毫秒 */
- lv_tick_inc(LVGL_PORT_TICK_PERIOD_MS);
- }
- static esp_err_t tick_init(void)
- {
- // LVGL 的 Tick 接口(使用 esp_timer 生成 2ms 周期事件)
- const esp_timer_create_args_t lvgl_tick_timer_args = {
- .callback = &tick_increment,
- .name = "LVGL tick"};
- esp_timer_handle_t lvgl_tick_timer = NULL;
- ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
- return esp_timer_start_periodic(lvgl_tick_timer, LVGL_PORT_TICK_PERIOD_MS * 1000);
- }
- static void lvgl_port_task(void *arg)
- {
- ESP_LOGD(TAG, "启动 LVGL 任务");
- uint32_t task_delay_ms = LVGL_PORT_TASK_MAX_DELAY_MS;
- while (1)
- {
- // 使用 FreeRTOS 的任务切换机制,提高效率
- if (lvgl_port_lock(0))
- {
- task_delay_ms = lv_timer_handler();
- lvgl_port_unlock();
- }
- if (task_delay_ms > LVGL_PORT_TASK_MAX_DELAY_MS)
- {
- task_delay_ms = LVGL_PORT_TASK_MAX_DELAY_MS;
- }
- else if (task_delay_ms < LVGL_PORT_TASK_MIN_DELAY_MS)
- {
- task_delay_ms = LVGL_PORT_TASK_MIN_DELAY_MS;
- }
- // 使用 vTaskDelay 函数,而不是直接使用延时函数,提高效率
- vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
- }
- }
- esp_err_t lvgl_port_init(esp_lcd_panel_handle_t lcd_handle, esp_lcd_touch_handle_t tp_handle)
- {
- lv_init();
- ESP_ERROR_CHECK(tick_init());
- lv_disp_t *disp = display_init(lcd_handle);
- assert(disp);
- if (tp_handle)
- {
- lv_indev_t *indev = indev_init(tp_handle);
- assert(indev);
- #if EXAMPLE_LVGL_PORT_ROTATION_90
- esp_lcd_touch_set_swap_xy(tp_handle, true);
- esp_lcd_touch_set_mirror_y(tp_handle, true);
- #elif EXAMPLE_LVGL_PORT_ROTATION_180
- esp_lcd_touch_set_mirror_x(tp_handle, true);
- esp_lcd_touch_set_mirror_y(tp_handle, true);
- #elif EXAMPLE_LVGL_PORT_ROTATION_270
- esp_lcd_touch_set_swap_xy(tp_handle, true);
- esp_lcd_touch_set_mirror_x(tp_handle, true);
- #endif
- }
- lvgl_mux = xSemaphoreCreateRecursiveMutex();
- assert(lvgl_mux);
- ESP_LOGI(TAG, "创建 LVGL 任务");
- BaseType_t core_id = (LVGL_PORT_TASK_CORE < 0) ? tskNO_AFFINITY : LVGL_PORT_TASK_CORE;
- BaseType_t ret = xTaskCreatePinnedToCore(lvgl_port_task, "lvgl", LVGL_PORT_TASK_STACK_SIZE, NULL,
- LVGL_PORT_TASK_PRIORITY, &lvgl_task_handle, core_id);
- if (ret != pdPASS)
- {
- ESP_LOGE(TAG, "创建 LVGL 任务失败");
- return ESP_FAIL;
- }
- return ESP_OK;
- }
- bool lvgl_port_lock(int timeout_ms)
- {
- assert(lvgl_mux && "必须先调用 lvgl_port_init");
- const TickType_t timeout_ticks = (timeout_ms < 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
- return xSemaphoreTakeRecursive(lvgl_mux, timeout_ticks) == pdTRUE;
- }
- void lvgl_port_unlock(void)
- {
- assert(lvgl_mux && "必须先调用 lvgl_port_init");
- xSemaphoreGiveRecursive(lvgl_mux);
- }
- bool lvgl_port_notify_rgb_vsync(void)
- {
- BaseType_t need_yield = pdFALSE;
- #if LVGL_PORT_FULL_REFRESH && (LVGL_PORT_LCD_RGB_BUFFER_NUMS == 3) && (EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 0)
- if (lvgl_port_rgb_next_buf != lvgl_port_rgb_last_buf)
- {
- lvgl_port_flush_next_buf = lvgl_port_rgb_last_buf;
- lvgl_port_rgb_last_buf = lvgl_port_rgb_next_buf;
- }
- #elif LVGL_PORT_AVOID_TEAR_ENABLE
- // 通知当前 RGB 帧缓冲区已传输完毕
- xTaskNotifyFromISR(lvgl_task_handle, ULONG_MAX, eNoAction, &need_yield);
- #endif
- return (need_yield == pdTRUE);
- }
|