#include /* LVGL */ #include "lvgl.h" /* FreeRTOS */ #include "FreeRTOS.h" #include "event_groups.h" #include "semphr.h" #include "task.h" /* FatFS */ #include "ff.h" /* LCD */ #include "epd-spi/panel/lcd_generic_ssd1289.h" #include "lcd_impl.h" #define LVGL_FS_BASE "0:/LV_ROOT/" #define LVGL_RES_HOR 240 #define LVGL_RES_VER 320 SemaphoreHandle_t g_lvgl_semphr; EventGroupHandle_t g_lvgl_event_group; static lv_disp_draw_buf_t s_lvgl_disp_buf; static lv_disp_drv_t s_lvgl_disp_drv; static lv_fs_drv_t s_lvgl_fs_drv; static lcd_impl_t s_lcd_impl; static lcd_generic_ssd1289_t s_lcd = { .cb = { .reset_cb = epd_impl_reset, .write_command_cb = epd_impl_write_command, .write_data_cb = epd_impl_write_data, }, .dir = LCD_GENERIC_SSD1289_DIR_VERTICAL, .mode = LCD_GENERIC_SSD1289_MODE_XBRG8888, .user_data = &s_lcd_impl, }; __attribute((section(".lvgl_buffer"))) static lv_color_t s_lvgl_buf_1[LVGL_RES_HOR * 20]; __attribute((section(".lvgl_buffer"))) static lv_color_t s_lvgl_buf_2[LVGL_RES_HOR * 20]; void lvgl_task(void *pvParameters); static void lvgl_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) { lcd_generic_ssd1289_t *lcd = disp_drv->user_data; epd_coord_t coord = { .x_start = area->x1, .x_end = area->x2, .y_start = area->y1, .y_end = area->y2, }; lcd_generic_ssd1289_upload(lcd, &coord, (uint8_t *)color_p); lv_disp_flush_ready(disp_drv); } static void lvgl_set_px_cb(lv_disp_drv_t *disp_drv, uint8_t *buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa) { /* 4 bytes per pixel. */ uint32_t *px = (uint32_t *)&buf[(y * buf_w + x) * 4]; /* Set color */ *px = color.ch.green & 0xFFU; *px |= (color.ch.red & 0xFFU) << 8U; *px |= (color.ch.blue & 0xFFU) << 16U; } static void *lvgl_fs_open_cb(lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode) { FIL *fp = pvPortMalloc(sizeof(FIL)); if (fp == NULL) { return NULL; } char *ff_path = pvPortMalloc(255); if (ff_path == NULL) { vPortFree(fp); return NULL; } snprintf(ff_path, 255, LVGL_FS_BASE "%s", path); FRESULT res = f_open(fp, ff_path, FA_READ); if (res != FR_OK) { vPortFree(ff_path); vPortFree(fp); return NULL; } vPortFree(ff_path); return fp; } static lv_fs_res_t lvgl_fs_close_cb(lv_fs_drv_t *drv, void *file_p) { FIL *fp = file_p; FRESULT res = f_close(fp); vPortFree(fp); if (res != FR_OK) { return LV_FS_RES_FS_ERR; } return LV_FS_RES_OK; } static lv_fs_res_t lvgl_fs_read_cb(lv_fs_drv_t *drv, void *file_p, void *buf, uint32_t btr, uint32_t *br) { FIL *fp = file_p; FRESULT res = f_read(fp, buf, btr, (unsigned int *)br); if (res != FR_OK) { return LV_FS_RES_FS_ERR; } return LV_FS_RES_OK; } static lv_fs_res_t lvgl_fs_seek_cb(lv_fs_drv_t *drv, void *file_p, uint32_t pos, lv_fs_whence_t whence) { FRESULT res; FIL *fp = file_p; uint32_t size = f_size(fp); if (whence != LV_FS_SEEK_CUR) { res = f_rewind(fp); if (res != FR_OK) { return LV_FS_RES_FS_ERR; } } if (whence == LV_FS_SEEK_END) { pos = (size - 1) - pos; } res = f_lseek(fp, pos); if (res != FR_OK) { return LV_FS_RES_FS_ERR; } return LV_FS_RES_OK; } int lvgl_setup(void) { lv_init(); lv_disp_draw_buf_init(&s_lvgl_disp_buf, s_lvgl_buf_1, s_lvgl_buf_2, LVGL_RES_HOR * 10); epd_impl_init(&s_lcd_impl); lcd_generic_ssd1289_init(&s_lcd); lv_disp_drv_init(&s_lvgl_disp_drv); s_lvgl_disp_drv.draw_buf = &s_lvgl_disp_buf; s_lvgl_disp_drv.user_data = &s_lcd; s_lvgl_disp_drv.flush_cb = lvgl_flush_cb; s_lvgl_disp_drv.set_px_cb = lvgl_set_px_cb; s_lvgl_disp_drv.hor_res = LVGL_RES_HOR; s_lvgl_disp_drv.ver_res = LVGL_RES_VER; lv_disp_drv_register(&s_lvgl_disp_drv); lv_fs_drv_init(&s_lvgl_fs_drv); s_lvgl_fs_drv.letter = 'A'; s_lvgl_fs_drv.open_cb = lvgl_fs_open_cb; s_lvgl_fs_drv.close_cb = lvgl_fs_close_cb; s_lvgl_fs_drv.read_cb = lvgl_fs_read_cb; s_lvgl_fs_drv.seek_cb = lvgl_fs_seek_cb; lv_fs_drv_register(&s_lvgl_fs_drv); g_lvgl_semphr = xSemaphoreCreateBinary(); if (g_lvgl_semphr == NULL) { return -1; } xSemaphoreGive(g_lvgl_semphr); if (xTaskCreate(lvgl_task, "LVTASK", 2048, NULL, 32, NULL) != pdPASS) { return -3; } return 0; } uint32_t lvgl_millis(void) { return xTaskGetTickCount(); } void lvgl_task(void *pvParameters) { for (;;) { if (xSemaphoreTake(g_lvgl_semphr, pdMS_TO_TICKS(150)) == pdTRUE) { lv_timer_handler(); xSemaphoreGive(g_lvgl_semphr); } vTaskDelay(pdMS_TO_TICKS(15)); } }