lvgl_port.c 20 KB

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