#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 #define LVGL_CACHE_DEPTH 40 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, .delay_cb = epd_impl_delay, }, .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 * LVGL_CACHE_DEPTH]; __attribute((section(".lvgl_buffer"))) static lv_color_t s_lvgl_buf_2[LVGL_RES_HOR * LVGL_CACHE_DEPTH]; 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) { LV_UNUSED(drv); uint8_t flags = 0; if (mode == LV_FS_MODE_WR) flags = FA_WRITE | FA_OPEN_ALWAYS; else if (mode == LV_FS_MODE_RD) flags = FA_READ; else if (mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) flags = FA_READ | FA_WRITE | FA_OPEN_ALWAYS; FIL *f = lv_mem_alloc(sizeof(FIL)); if (f == NULL) return NULL; TCHAR *ff_path = lv_mem_alloc(sizeof(TCHAR) * 256); if (ff_path == NULL) { lv_mem_free(f); return NULL; } snprintf(ff_path, 256, LVGL_FS_BASE "%s", path); FRESULT res = f_open(f, ff_path, flags); lv_mem_free(ff_path); if (res == FR_OK) { return f; } else { lv_mem_free(f); return NULL; } } static lv_fs_res_t lvgl_fs_close_cb(lv_fs_drv_t *drv, void *file_p) { LV_UNUSED(drv); f_close(file_p); lv_mem_free(file_p); 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) { LV_UNUSED(drv); FRESULT res = f_read(file_p, buf, btr, (UINT *)br); if (res == FR_OK) return LV_FS_RES_OK; else return LV_FS_RES_UNKNOWN; } static lv_fs_res_t lvgl_fs_write_cb(lv_fs_drv_t *drv, void *file_p, const void *buf, uint32_t btw, uint32_t *bw) { LV_UNUSED(drv); FRESULT res = f_write(file_p, buf, btw, (UINT *)bw); if (res == FR_OK) return LV_FS_RES_OK; else return LV_FS_RES_UNKNOWN; } 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) { LV_UNUSED(drv); switch (whence) { case LV_FS_SEEK_SET: f_lseek(file_p, pos); break; case LV_FS_SEEK_CUR: f_lseek(file_p, f_tell((FIL *)file_p) + pos); break; case LV_FS_SEEK_END: f_lseek(file_p, f_size((FIL *)file_p) + pos); break; default: break; } return LV_FS_RES_OK; } static lv_fs_res_t lvgl_fs_tell_cb(lv_fs_drv_t *drv, void *file_p, uint32_t *pos_p) { LV_UNUSED(drv); *pos_p = f_tell((FIL *)file_p); return LV_FS_RES_OK; } static void *lvgl_fs_dir_open_cb(lv_fs_drv_t *drv, const char *path) { LV_UNUSED(drv); DIR *d = lv_mem_alloc(sizeof(DIR)); if (d == NULL) return NULL; TCHAR *ff_path = lv_mem_alloc(sizeof(TCHAR) * 256); if (ff_path == NULL) { lv_mem_free(d); return NULL; } snprintf(ff_path, 256, LVGL_FS_BASE "%s", path); FRESULT res = f_opendir(d, ff_path); lv_mem_free(ff_path); if (res != FR_OK) { lv_mem_free(d); d = NULL; } return d; } static lv_fs_res_t lvgl_fs_dir_read_cb(lv_fs_drv_t *drv, void *dir_p, char *fn) { LV_UNUSED(drv); FRESULT res; FILINFO fno; fn[0] = '\0'; do { res = f_readdir(dir_p, &fno); if (res != FR_OK) return LV_FS_RES_UNKNOWN; if (fno.fattrib & AM_DIR) { fn[0] = '/'; strcpy(&fn[1], fno.fname); } else strcpy(fn, fno.fname); } while (strcmp(fn, "/.") == 0 || strcmp(fn, "/..") == 0); return LV_FS_RES_OK; } static lv_fs_res_t lvgl_fs_dir_close_cb(lv_fs_drv_t *drv, void *dir_p) { LV_UNUSED(drv); f_closedir(dir_p); lv_mem_free(dir_p); 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 * LVGL_CACHE_DEPTH); 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.write_cb = lvgl_fs_write_cb; s_lvgl_fs_drv.seek_cb = lvgl_fs_seek_cb; s_lvgl_fs_drv.tell_cb = lvgl_fs_tell_cb; s_lvgl_fs_drv.dir_open_cb = lvgl_fs_dir_open_cb; s_lvgl_fs_drv.dir_read_cb = lvgl_fs_dir_read_cb; s_lvgl_fs_drv.dir_close_cb = lvgl_fs_dir_close_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, 13, 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)); } }