generated from Embedded_Projects/Landzo_K60Z_LwIP
272 lines
7.1 KiB
C
272 lines
7.1 KiB
C
#include <stdio.h>
|
|
|
|
/* 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));
|
|
}
|
|
}
|