lvgl_port.c 19 KB

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