lvgl_port.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. /*
  2. * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include "freertos/FreeRTOS.h"
  7. #include "freertos/semphr.h"
  8. #include "freertos/task.h"
  9. #include "esp_lcd_panel_ops.h"
  10. #include "esp_lcd_panel_rgb.h"
  11. #include "esp_lcd_touch.h"
  12. #include "esp_timer.h"
  13. #include "esp_log.h"
  14. #include "lvgl.h"
  15. #include "lvgl_port.h"
  16. #include "app/include/beep.h"
  17. static const char *TAG = "lv_port";
  18. static SemaphoreHandle_t lvgl_mux; // LVGL 互斥锁
  19. static TaskHandle_t lvgl_task_handle = NULL;
  20. static bool isPressed = false;
  21. static bool isReleased = false;
  22. // #define EXAMPLE_LVGL_PORT_ROTATION_180 1
  23. #define EXAMPLE_LVGL_PORT_ROTATION_DEGREE 180
  24. #if EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0
  25. static void *get_next_frame_buffer(esp_lcd_panel_handle_t panel_handle)
  26. {
  27. static void *next_fb = NULL;
  28. static void *fb[2] = {NULL};
  29. if (next_fb == NULL)
  30. {
  31. ESP_ERROR_CHECK(esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 2, &fb[0], &fb[1])); // 获取双缓冲区
  32. next_fb = fb[1];
  33. }
  34. else
  35. {
  36. next_fb = (next_fb == fb[0]) ? fb[1] : fb[0]; // 切换缓冲区
  37. }
  38. return next_fb;
  39. }
  40. 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)
  41. {
  42. int from_index = 0;
  43. int to_index = 0;
  44. int to_index_const = 0;
  45. switch (rotation)
  46. {
  47. case 90:
  48. to_index_const = (w - x_start - 1) * h;
  49. for (int from_y = y_start; from_y < y_end + 1; from_y++)
  50. {
  51. from_index = from_y * w + x_start;
  52. to_index = to_index_const + from_y;
  53. for (int from_x = x_start; from_x < x_end + 1; from_x++)
  54. {
  55. *(to + to_index) = *(from + from_index);
  56. from_index += 1;
  57. to_index -= h;
  58. }
  59. }
  60. break;
  61. case 180:
  62. to_index_const = h * w - x_start - 1;
  63. for (int from_y = y_start; from_y < y_end + 1; from_y++)
  64. {
  65. from_index = from_y * w + x_start;
  66. to_index = to_index_const - from_y * w;
  67. for (int from_x = x_start; from_x < x_end + 1; from_x++)
  68. {
  69. *(to + to_index) = *(from + from_index);
  70. from_index += 1;
  71. to_index -= 1;
  72. }
  73. }
  74. break;
  75. case 270:
  76. to_index_const = (x_start + 1) * h - 1;
  77. for (int from_y = y_start; from_y < y_end + 1; from_y++)
  78. {
  79. from_index = from_y * w + x_start;
  80. to_index = to_index_const - from_y;
  81. for (int from_x = x_start; from_x < x_end + 1; from_x++)
  82. {
  83. *(to + to_index) = *(from + from_index);
  84. from_index += 1;
  85. to_index += h;
  86. }
  87. }
  88. break;
  89. default:
  90. break;
  91. }
  92. }
  93. #endif /* EXAMPLE_LVGL_PORT_ROTATION_DEGREE */
  94. #if LVGL_PORT_AVOID_TEAR_ENABLE
  95. #if LVGL_PORT_DIRECT_MODE
  96. #if EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0
  97. typedef struct
  98. {
  99. uint16_t inv_p;
  100. uint8_t inv_area_joined[LV_INV_BUF_SIZE];
  101. lv_area_t inv_areas[LV_INV_BUF_SIZE];
  102. } lv_port_dirty_area_t;
  103. typedef enum
  104. {
  105. FLUSH_STATUS_PART,
  106. FLUSH_STATUS_FULL
  107. } lv_port_flush_status_t;
  108. typedef enum
  109. {
  110. FLUSH_PROBE_PART_COPY,
  111. FLUSH_PROBE_SKIP_COPY,
  112. FLUSH_PROBE_FULL_COPY,
  113. } lv_port_flush_probe_t;
  114. static lv_port_dirty_area_t dirty_area;
  115. static void flush_dirty_save(lv_port_dirty_area_t *dirty_area)
  116. {
  117. lv_disp_t *disp = _lv_refr_get_disp_refreshing(); // 获取正在刷新的显示设备
  118. dirty_area->inv_p = disp->inv_p; // 保存无效区域的数量
  119. for (int i = 0; i < disp->inv_p; i++)
  120. {
  121. dirty_area->inv_area_joined[i] = disp->inv_area_joined[i]; // 保存无效区域的合并状态
  122. dirty_area->inv_areas[i] = disp->inv_areas[i]; // 保存无效区域的信息
  123. }
  124. }
  125. /**
  126. * @brief 探测无效区域以进行复制
  127. *
  128. * @note 此函数用于避免撕裂效果,仅在 LVGL 直接模式下有效。
  129. *
  130. */
  131. static lv_port_flush_probe_t flush_copy_probe(lv_disp_drv_t *drv)
  132. {
  133. static lv_port_flush_status_t prev_status = FLUSH_STATUS_PART;
  134. lv_port_flush_status_t cur_status;
  135. lv_port_flush_probe_t probe_result;
  136. lv_disp_t *disp_refr = _lv_refr_get_disp_refreshing(); // 获取正在刷新的显示设备
  137. uint32_t flush_ver = 0;
  138. uint32_t flush_hor = 0;
  139. for (int i = 0; i < disp_refr->inv_p; i++)
  140. {
  141. if (disp_refr->inv_area_joined[i] == 0)
  142. {
  143. flush_ver = (disp_refr->inv_areas[i].y2 + 1 - disp_refr->inv_areas[i].y1); // 计算垂直方向的刷新长度
  144. flush_hor = (disp_refr->inv_areas[i].x2 + 1 - disp_refr->inv_areas[i].x1); // 计算水平方向的刷新长度
  145. break;
  146. }
  147. }
  148. /* 检查当前是否为全屏刷新 */
  149. cur_status = ((flush_ver == drv->ver_res) && (flush_hor == drv->hor_res)) ? (FLUSH_STATUS_FULL) : (FLUSH_STATUS_PART);
  150. if (prev_status == FLUSH_STATUS_FULL)
  151. {
  152. if ((cur_status == FLUSH_STATUS_PART))
  153. {
  154. probe_result = FLUSH_PROBE_FULL_COPY; // 上次是全屏刷新,本次是部分刷新,需要复制整个屏幕
  155. }
  156. else
  157. {
  158. probe_result = FLUSH_PROBE_SKIP_COPY; // 上次和本次都是全屏刷新,跳过复制
  159. }
  160. }
  161. else
  162. {
  163. probe_result = FLUSH_PROBE_PART_COPY; // 上次是部分刷新,本次也是部分刷新,只复制部分区域
  164. }
  165. prev_status = cur_status;
  166. return probe_result;
  167. }
  168. static inline void *flush_get_next_buf(void *panel_handle)
  169. {
  170. return get_next_frame_buffer(panel_handle); // 获取下一个帧缓冲区
  171. }
  172. /**
  173. * @brief 复制无效区域
  174. *
  175. * @note 此函数用于避免撕裂效果,仅在 LVGL 直接模式下有效。
  176. *
  177. */
  178. static void flush_dirty_copy(void *dst, void *src, lv_port_dirty_area_t *dirty_area)
  179. {
  180. lv_coord_t x_start, x_end, y_start, y_end;
  181. for (int i = 0; i < dirty_area->inv_p; i++)
  182. {
  183. /* 刷新未合并的区域 */
  184. if (dirty_area->inv_area_joined[i] == 0)
  185. {
  186. x_start = dirty_area->inv_areas[i].x1;
  187. x_end = dirty_area->inv_areas[i].x2;
  188. y_start = dirty_area->inv_areas[i].y1;
  189. y_end = dirty_area->inv_areas[i].y2;
  190. rotate_copy_pixel(src, dst, x_start, y_start, x_end, y_end, LV_HOR_RES, LV_VER_RES, EXAMPLE_LVGL_PORT_ROTATION_DEGREE);
  191. }
  192. }
  193. }
  194. static void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
  195. {
  196. esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)drv->user_data;
  197. const int offsetx1 = area->x1;
  198. const int offsetx2 = area->x2;
  199. const int offsety1 = area->y1;
  200. const int offsety2 = area->y2;
  201. void *next_fb = NULL;
  202. lv_port_flush_probe_t probe_result = FLUSH_PROBE_PART_COPY;
  203. lv_disp_t *disp = lv_disp_get_default();
  204. /* 最后一个区域刷新后的操作 */
  205. if (lv_disp_flush_is_last(drv))
  206. {
  207. /* 检查是否触发了 `full_refresh` 标志 */
  208. if (drv->full_refresh)
  209. {
  210. /* 重置标志 */
  211. drv->full_refresh = 0;
  212. // 将整个屏幕的 LVGL 缓冲区数据旋转并复制到下一个帧缓冲区
  213. next_fb = flush_get_next_buf(panel_handle);
  214. rotate_copy_pixel((uint16_t *)color_map, next_fb, offsetx1, offsety1, offsetx2, offsety2, LV_HOR_RES, LV_VER_RES, EXAMPLE_LVGL_PORT_ROTATION_DEGREE);
  215. /* 将当前 RGB 帧缓冲区切换到 `next_fb` */
  216. esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, next_fb);
  217. /* 等待当前帧缓冲区传输完成 */
  218. ulTaskNotifyValueClear(NULL, ULONG_MAX);
  219. ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
  220. /* 异步更新另一个帧缓冲区的无效区域 */
  221. flush_dirty_copy(flush_get_next_buf(panel_handle), color_map, &dirty_area);
  222. flush_get_next_buf(panel_handle);
  223. }
  224. else
  225. {
  226. /* 探测当前无效区域的复制方法 */
  227. probe_result = flush_copy_probe(drv);
  228. if (probe_result == FLUSH_PROBE_FULL_COPY)
  229. {
  230. /* 保存当前无效区域以供下一个帧缓冲区使用 */
  231. flush_dirty_save(&dirty_area);
  232. /* 设置 LVGL 全刷新标志并提前设置刷新准备好 */
  233. drv->full_refresh = 1;
  234. disp->rendering_in_progress = false;
  235. lv_disp_flush_ready(drv);
  236. /* 强制刷新整个屏幕,将递归调用 `flush_callback` */
  237. lv_refr_now(_lv_refr_get_disp_refreshing());
  238. }
  239. else
  240. {
  241. /* 更新当前无效区域以供下一个帧缓冲区使用 */
  242. next_fb = flush_get_next_buf(panel_handle);
  243. flush_dirty_save(&dirty_area);
  244. flush_dirty_copy(next_fb, color_map, &dirty_area);
  245. /* 将当前 RGB 帧缓冲区切换到 `next_fb` */
  246. esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, next_fb);
  247. /* 等待当前帧缓冲区传输完成 */
  248. ulTaskNotifyValueClear(NULL, ULONG_MAX);
  249. ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
  250. if (probe_result == FLUSH_PROBE_PART_COPY)
  251. {
  252. /* 异步更新另一个帧缓冲区的无效区域 */
  253. flush_dirty_save(&dirty_area);
  254. flush_dirty_copy(flush_get_next_buf(panel_handle), color_map, &dirty_area);
  255. flush_get_next_buf(panel_handle);
  256. }
  257. }
  258. }
  259. }
  260. lv_disp_flush_ready(drv);
  261. }
  262. #endif
  263. #elif LVGL_PORT_FULL_REFRESH && LVGL_PORT_LCD_RGB_BUFFER_NUMS == 3
  264. #if EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 0
  265. static void *lvgl_port_rgb_last_buf = NULL;
  266. static void *lvgl_port_rgb_next_buf = NULL;
  267. static void *lvgl_port_flush_next_buf = NULL;
  268. #endif
  269. void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
  270. {
  271. esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)drv->user_data;
  272. const int offsetx1 = area->x1;
  273. const int offsetx2 = area->x2;
  274. const int offsety1 = area->y1;
  275. const int offsety2 = area->y2;
  276. #if EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0
  277. void *next_fb = get_next_frame_buffer(panel_handle);
  278. /* 将当前 LVGL 缓冲区中的无效区域旋转并复制到下一个 RGB 帧缓冲区 */
  279. rotate_copy_pixel((uint16_t *)color_map, next_fb, offsetx1, offsety1, offsetx2, offsety2, LV_HOR_RES, LV_VER_RES, EXAMPLE_LVGL_PORT_ROTATION_DEGREE);
  280. /* 将当前 RGB 帧缓冲区切换到 `next_fb` */
  281. esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, next_fb);
  282. #else
  283. drv->draw_buf->buf1 = color_map;
  284. drv->draw_buf->buf2 = lvgl_port_flush_next_buf;
  285. lvgl_port_flush_next_buf = color_map;
  286. /* 将当前 RGB 帧缓冲区切换到 `color_map` */
  287. esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
  288. lvgl_port_rgb_next_buf = color_map;
  289. #endif
  290. lv_disp_flush_ready(drv);
  291. }
  292. #endif
  293. #else
  294. static void flush_callback(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map)
  295. {
  296. esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)lv_display_get_user_data(disp);
  297. // static uint8_t rotated_buf[LVGL_PORT_H_RES * LVGL_PORT_V_RES];
  298. //
  299. // lv_display_rotation_t rotation = lv_display_get_rotation(disp);
  300. // lv_area_t rotated_area;
  301. // if(rotation != LV_DISPLAY_ROTATION_0) {
  302. // lv_color_format_t cf = lv_display_get_color_format(disp);
  303. // /*Calculate the position of the rotated area*/
  304. // rotated_area = *area;
  305. // lv_display_rotate_area(disp, &rotated_area);
  306. // /*Calculate the source stride (bytes in a line) from the width of the area*/
  307. // uint32_t src_stride = lv_draw_buf_width_to_stride(lv_area_get_width(area), cf);
  308. // /*Calculate the stride of the destination (rotated) area too*/
  309. // uint32_t dest_stride = lv_draw_buf_width_to_stride(lv_area_get_width(&rotated_area), cf);
  310. // /*Have a buffer to store the rotated area and perform the rotation*/
  311. //
  312. // int32_t src_w = lv_area_get_width(area);
  313. // int32_t src_h = lv_area_get_height(area);
  314. // lv_draw_sw_rotate(px_map, rotated_buf, src_w, src_h, src_stride, dest_stride, rotation, cf);
  315. // /*Use the rotated area and rotated buffer from now on*/
  316. // area = &rotated_area;
  317. // px_map = rotated_buf;
  318. // }
  319. const int offsetx1 = area->x1;
  320. const int offsetx2 = area->x2;
  321. const int offsety1 = area->y1;
  322. const int offsety2 = area->y2;
  323. /* 将颜色映射中的数据直接复制到 RGB 帧缓冲区 */
  324. esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, px_map);
  325. lv_display_flush_ready(disp);
  326. }
  327. #endif /* LVGL_PORT_AVOID_TEAR_ENABLE */
  328. static lv_display_t *display_init(esp_lcd_panel_handle_t panel_handle)
  329. {
  330. assert(panel_handle);
  331. ESP_LOGD(TAG, "为 LVGL 缓冲区分配内存");
  332. // 分配 LVGL 使用的绘图缓冲区
  333. void *buf1 = NULL;
  334. void *buf2 = NULL;
  335. int buffer_size = 0;
  336. #if LVGL_PORT_AVOID_TEAR_ENABLE
  337. // 为了避免撕裂效果,我们应该至少使用两个帧缓冲区:一个用于 LVGL 渲染,另一个用于 RGB 输出
  338. buffer_size = LVGL_PORT_H_RES * LVGL_PORT_V_RES;
  339. #if (LVGL_PORT_LCD_RGB_BUFFER_NUMS == 3) && (EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 0) && LVGL_PORT_FULL_REFRESH
  340. // 使用三个缓冲区和全刷新,我们总是有一个缓冲区可用于渲染,消除了等待 RGB 同步信号的需要
  341. ESP_ERROR_CHECK(esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 3, &lvgl_port_rgb_last_buf, &buf1, &buf2));
  342. lvgl_port_rgb_next_buf = lvgl_port_rgb_last_buf;
  343. lvgl_port_flush_next_buf = buf2;
  344. #elif (LVGL_PORT_LCD_RGB_BUFFER_NUMS == 3) && (EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0)
  345. // 这里我们使用三个帧缓冲区,一个用于 LVGL 渲染,另外两个用于 RGB 驱动(其中一个用于旋转)
  346. void *fbs[3];
  347. ESP_ERROR_CHECK(esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 3, &fbs[0], &fbs[1], &fbs[2]));
  348. buf1 = fbs[2];
  349. #else
  350. ESP_ERROR_CHECK(esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 2, &buf1, &buf2));
  351. #endif
  352. #else
  353. buffer_size = LVGL_PORT_H_RES * LVGL_PORT_BUFFER_HEIGHT;
  354. buf1 = heap_caps_malloc(buffer_size * sizeof(lv_color_t), MALLOC_CAP_SPIRAM);
  355. assert(buf1);
  356. ESP_LOGI(TAG, "LVGL 缓冲区大小: %dKB", buffer_size * sizeof(lv_color_t) / 1024);
  357. #endif
  358. // 创建显示设备
  359. lv_display_t *disp = lv_display_create(LVGL_PORT_H_RES, LVGL_PORT_V_RES);
  360. if (!disp)
  361. {
  362. ESP_LOGE(TAG, "创建显示设备失败");
  363. return NULL;
  364. }
  365. // 设置显示缓冲区
  366. lv_display_set_buffers(disp, buf1, buf2, buffer_size * sizeof(lv_color_t), LV_DISPLAY_RENDER_MODE_PARTIAL);
  367. // 设置显示回调
  368. lv_display_set_flush_cb(disp, flush_callback);
  369. // 设置用户数据
  370. lv_display_set_user_data(disp, panel_handle);
  371. #if LVGL_PORT_FULL_REFRESH
  372. lv_display_set_render_mode(disp, LV_DISPLAY_RENDER_MODE_FULL);
  373. #elif LVGL_PORT_DIRECT_MODE
  374. lv_display_set_render_mode(disp, LV_DISPLAY_RENDER_MODE_DIRECT);
  375. #endif
  376. return disp;
  377. }
  378. static void touchpad_read(lv_indev_t *indev, lv_indev_data_t *data)
  379. {
  380. esp_lcd_touch_handle_t tp = (esp_lcd_touch_handle_t)lv_indev_get_user_data(indev);
  381. assert(tp);
  382. uint16_t touchpad_x;
  383. uint16_t touchpad_y;
  384. uint8_t touchpad_cnt = 0;
  385. esp_lcd_touch_read_data(tp);
  386. bool touchpad_pressed = esp_lcd_touch_get_coordinates(tp, &touchpad_x, &touchpad_y, NULL, &touchpad_cnt, 1);
  387. if (touchpad_pressed && touchpad_cnt > 0)
  388. {
  389. data->point.x = touchpad_x;
  390. data->point.y = touchpad_y;
  391. data->state = LV_INDEV_STATE_PRESSED;
  392. if(!isReleased && !isPressed)
  393. {
  394. beep();
  395. isPressed = true;
  396. }
  397. }
  398. else
  399. {
  400. if(isPressed) {
  401. isReleased = false;
  402. isPressed = false;
  403. }
  404. beep_stop();
  405. data->state = LV_INDEV_STATE_RELEASED;
  406. }
  407. }
  408. static lv_indev_t *indev_init(esp_lcd_touch_handle_t tp)
  409. {
  410. assert(tp);
  411. // 创建输入设备
  412. lv_indev_t *indev = lv_indev_create();
  413. if (!indev)
  414. {
  415. ESP_LOGE(TAG, "创建输入设备失败");
  416. return NULL;
  417. }
  418. // 设置输入设备属性
  419. lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
  420. lv_indev_set_read_cb(indev, touchpad_read);
  421. lv_indev_set_user_data(indev, tp);
  422. return indev;
  423. }
  424. static void tick_increment(void *arg)
  425. {
  426. /* 告诉 LVGL 已经过去了多少毫秒 */
  427. lv_tick_inc(LVGL_PORT_TICK_PERIOD_MS);
  428. }
  429. static esp_err_t tick_init(void)
  430. {
  431. // LVGL 的 Tick 接口(使用 esp_timer 生成 2ms 周期事件)
  432. const esp_timer_create_args_t lvgl_tick_timer_args = {
  433. .callback = &tick_increment,
  434. .name = "LVGL tick"};
  435. esp_timer_handle_t lvgl_tick_timer = NULL;
  436. ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
  437. return esp_timer_start_periodic(lvgl_tick_timer, LVGL_PORT_TICK_PERIOD_MS * 1000);
  438. }
  439. static void lvgl_port_task(void *arg)
  440. {
  441. ESP_LOGD(TAG, "启动 LVGL 任务");
  442. uint32_t task_delay_ms = LVGL_PORT_TASK_MAX_DELAY_MS;
  443. while (1)
  444. {
  445. // 使用 FreeRTOS 的任务切换机制,提高效率
  446. if (lvgl_port_lock(0))
  447. {
  448. task_delay_ms = lv_timer_handler();
  449. lvgl_port_unlock();
  450. }
  451. if (task_delay_ms > LVGL_PORT_TASK_MAX_DELAY_MS)
  452. {
  453. task_delay_ms = LVGL_PORT_TASK_MAX_DELAY_MS;
  454. }
  455. else if (task_delay_ms < LVGL_PORT_TASK_MIN_DELAY_MS)
  456. {
  457. task_delay_ms = LVGL_PORT_TASK_MIN_DELAY_MS;
  458. }
  459. // 使用 vTaskDelay 函数,而不是直接使用延时函数,提高效率
  460. vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
  461. }
  462. }
  463. esp_err_t lvgl_port_init(esp_lcd_panel_handle_t lcd_handle, esp_lcd_touch_handle_t tp_handle)
  464. {
  465. lv_init();
  466. ESP_ERROR_CHECK(tick_init());
  467. lv_disp_t *disp = display_init(lcd_handle);
  468. assert(disp);
  469. if (tp_handle)
  470. {
  471. lv_indev_t *indev = indev_init(tp_handle);
  472. assert(indev);
  473. #if EXAMPLE_LVGL_PORT_ROTATION_90
  474. esp_lcd_touch_set_swap_xy(tp_handle, true);
  475. esp_lcd_touch_set_mirror_y(tp_handle, true);
  476. #elif EXAMPLE_LVGL_PORT_ROTATION_180
  477. esp_lcd_touch_set_mirror_x(tp_handle, true);
  478. esp_lcd_touch_set_mirror_y(tp_handle, true);
  479. #elif EXAMPLE_LVGL_PORT_ROTATION_270
  480. esp_lcd_touch_set_swap_xy(tp_handle, true);
  481. esp_lcd_touch_set_mirror_x(tp_handle, true);
  482. #endif
  483. }
  484. lvgl_mux = xSemaphoreCreateRecursiveMutex();
  485. assert(lvgl_mux);
  486. ESP_LOGI(TAG, "创建 LVGL 任务");
  487. BaseType_t core_id = (LVGL_PORT_TASK_CORE < 0) ? tskNO_AFFINITY : LVGL_PORT_TASK_CORE;
  488. BaseType_t ret = xTaskCreatePinnedToCore(lvgl_port_task, "lvgl", LVGL_PORT_TASK_STACK_SIZE, NULL,
  489. LVGL_PORT_TASK_PRIORITY, &lvgl_task_handle, core_id);
  490. if (ret != pdPASS)
  491. {
  492. ESP_LOGE(TAG, "创建 LVGL 任务失败");
  493. return ESP_FAIL;
  494. }
  495. return ESP_OK;
  496. }
  497. bool lvgl_port_lock(int timeout_ms)
  498. {
  499. assert(lvgl_mux && "必须先调用 lvgl_port_init");
  500. const TickType_t timeout_ticks = (timeout_ms < 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
  501. return xSemaphoreTakeRecursive(lvgl_mux, timeout_ticks) == pdTRUE;
  502. }
  503. void lvgl_port_unlock(void)
  504. {
  505. assert(lvgl_mux && "必须先调用 lvgl_port_init");
  506. xSemaphoreGiveRecursive(lvgl_mux);
  507. }
  508. bool lvgl_port_notify_rgb_vsync(void)
  509. {
  510. BaseType_t need_yield = pdFALSE;
  511. #if LVGL_PORT_FULL_REFRESH && (LVGL_PORT_LCD_RGB_BUFFER_NUMS == 3) && (EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 0)
  512. if (lvgl_port_rgb_next_buf != lvgl_port_rgb_last_buf)
  513. {
  514. lvgl_port_flush_next_buf = lvgl_port_rgb_last_buf;
  515. lvgl_port_rgb_last_buf = lvgl_port_rgb_next_buf;
  516. }
  517. #elif LVGL_PORT_AVOID_TEAR_ENABLE
  518. // 通知当前 RGB 帧缓冲区已传输完毕
  519. xTaskNotifyFromISR(lvgl_task_handle, ULONG_MAX, eNoAction, &need_yield);
  520. #endif
  521. return (need_yield == pdTRUE);
  522. }