259 lines
7.2 KiB
C
259 lines
7.2 KiB
C
#include "impl_lvgl.h"
|
|
|
|
#include "esp_log.h"
|
|
#include "impl_btn.h"
|
|
#include "impl_epd.h"
|
|
#include "task_config.h"
|
|
|
|
#define LOG_TAG "IMPL_LVGL"
|
|
|
|
#define EPD_DISPLAY_PIXEL_COUNT 400 * 300
|
|
#define EPD_DISPLAY_FRAME_SIZE (EPD_DISPLAY_PIXEL_COUNT * 2 / 8)
|
|
|
|
#define EPD_DISPLAY_GS 0
|
|
#define EPD_DISPLAY_MAX_PARTIAL 64 /* !! Must be even !! */
|
|
|
|
static impl_epd_handle_t s_epd_impl;
|
|
|
|
static epd_gdew042t2_t s_gd_epd = {
|
|
.cb =
|
|
{
|
|
.reset_cb = impl_epd_reset,
|
|
.write_command_cb = impl_epd_write_command,
|
|
.write_data_cb = impl_epd_write_data,
|
|
.poll_busy_cb = impl_epd_poll_busy,
|
|
.delay_cb = impl_epd_delay,
|
|
},
|
|
#if EPD_DISPLAY_GS
|
|
.mode = EPD_GDEW042T2_MODE_GS,
|
|
#else
|
|
.mode = EPD_GDEW042T2_MODE_BW,
|
|
#endif
|
|
.user_data = &s_epd_impl,
|
|
};
|
|
|
|
#if !EPD_DISPLAY_GS
|
|
static uint8_t s_epd_partial_counter = 0;
|
|
#endif
|
|
|
|
static uint32_t s_last_key = 0;
|
|
|
|
static lv_disp_draw_buf_t s_disp_buf;
|
|
static lv_color_t *s_disp_store;
|
|
static lv_disp_drv_t s_disp_drv;
|
|
static lv_indev_drv_t s_indev_drv;
|
|
|
|
static TaskHandle_t s_lv_tick_handle;
|
|
static TaskHandle_t s_lv_task_handle;
|
|
static SemaphoreHandle_t s_lv_semphr_handle;
|
|
|
|
static void impl_lvgl_epd_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) {
|
|
uint32_t byte_index = (buf_w * y + x) / 8;
|
|
uint8_t bit_index = 7 - (x % 8);
|
|
|
|
uint8_t brightness = lv_color_brightness(color);
|
|
|
|
#if EPD_DISPLAY_GS
|
|
if (brightness < 64) {
|
|
buf[byte_index] &= ~(1 << bit_index);
|
|
buf[15000 + byte_index] &= ~(1 << bit_index);
|
|
} else if (brightness < 128) {
|
|
buf[byte_index] &= ~(1 << bit_index);
|
|
buf[15000 + byte_index] |= (1 << bit_index);
|
|
} else if (brightness < 192) {
|
|
buf[byte_index] |= (1 << bit_index);
|
|
buf[15000 + byte_index] &= ~(1 << bit_index);
|
|
} else {
|
|
buf[byte_index] |= (1 << bit_index);
|
|
buf[15000 + byte_index] |= (1 << bit_index);
|
|
}
|
|
#else
|
|
uint8_t current_frame = 0;
|
|
if ((s_epd_partial_counter % 2) == 1) {
|
|
current_frame = 1;
|
|
}
|
|
|
|
if (brightness > 128) {
|
|
buf[current_frame * 15000 + byte_index] |= (1 << bit_index);
|
|
} else {
|
|
buf[current_frame * 15000 + byte_index] &= ~(1 << bit_index);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void impl_lvgl_epd_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) {
|
|
epd_coord_t coord = {
|
|
.x_start = 0,
|
|
.x_end = 399,
|
|
.y_start = 0,
|
|
.y_end = 299,
|
|
};
|
|
|
|
#if EPD_DISPLAY_GS
|
|
epd_gdew042t2_upload(&s_gd_epd, &coord, (uint8_t *)color_p, (uint8_t *)&color_p[15000]);
|
|
#else
|
|
if (s_epd_partial_counter == 0) {
|
|
s_epd_partial_counter = EPD_DISPLAY_MAX_PARTIAL - 1;
|
|
|
|
s_gd_epd.mode = EPD_GDEW042T2_MODE_BW;
|
|
|
|
// Flush to full
|
|
epd_gdew042t2_upload(&s_gd_epd, &coord, (uint8_t *)color_p, (uint8_t *)color_p);
|
|
} else {
|
|
s_gd_epd.mode = EPD_GDEW042T2_MODE_BW_PART;
|
|
|
|
// Even: buffer 0, odd: buffer 1
|
|
if ((s_epd_partial_counter % 2) == 1) {
|
|
epd_gdew042t2_upload(&s_gd_epd, &coord, (uint8_t *)color_p, (uint8_t *)&color_p[15000]);
|
|
} else {
|
|
epd_gdew042t2_upload(&s_gd_epd, &coord, (uint8_t *)&color_p[15000], (uint8_t *)color_p);
|
|
}
|
|
s_epd_partial_counter--;
|
|
}
|
|
#endif
|
|
|
|
ESP_LOGD(LOG_TAG, "Memory buffer flushed to EPD.");
|
|
|
|
lv_disp_flush_ready(disp_drv);
|
|
}
|
|
|
|
static void impl_lvgl_epd_rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) {
|
|
area->x1 = 0;
|
|
area->x2 = 399;
|
|
}
|
|
|
|
static void impl_lvgl_button_read(lv_indev_drv_t *drv, lv_indev_data_t *data) {
|
|
uint8_t btn = 0U;
|
|
impl_btn_read(&btn);
|
|
|
|
if (btn != 0) {
|
|
switch (btn) {
|
|
case 0x02:
|
|
data->key = LV_KEY_ENTER;
|
|
break;
|
|
case 0x04:
|
|
data->key = LV_KEY_NEXT;
|
|
break;
|
|
case 0x08:
|
|
data->key = LV_KEY_PREV;
|
|
break;
|
|
}
|
|
|
|
data->state = LV_INDEV_STATE_PRESSED;
|
|
s_last_key = data->key;
|
|
}
|
|
|
|
else {
|
|
data->key = s_last_key;
|
|
data->state = LV_INDEV_STATE_RELEASED;
|
|
s_last_key = 0U;
|
|
}
|
|
}
|
|
|
|
static void impl_lvgl_log_cb(const char *buf) {
|
|
// Log to console.
|
|
ESP_LOGI("LVGL", "%s", buf);
|
|
}
|
|
|
|
void impl_lvgl_timer_task(void *pvParameters) {
|
|
for (;;) {
|
|
impl_lvgl_lock();
|
|
lv_timer_handler();
|
|
impl_lvgl_unlock();
|
|
vTaskDelay(pdMS_TO_TICKS(100));
|
|
}
|
|
}
|
|
|
|
void impl_lvgl_tick_task(void *pvParameters) {
|
|
for (;;) {
|
|
impl_lvgl_lock();
|
|
lv_tick_inc(50);
|
|
impl_lvgl_unlock();
|
|
vTaskDelay(pdMS_TO_TICKS(50));
|
|
}
|
|
}
|
|
|
|
esp_err_t impl_lvgl_lock(void) {
|
|
if (xSemaphoreTake(s_lv_semphr_handle, portMAX_DELAY) != pdTRUE) {
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t impl_lvgl_unlock(void) {
|
|
xSemaphoreGive(s_lv_semphr_handle);
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t impl_lvgl_init(void) {
|
|
ESP_ERROR_CHECK(impl_epd_init(&s_epd_impl));
|
|
|
|
lv_init();
|
|
|
|
lv_log_register_print_cb(impl_lvgl_log_cb);
|
|
|
|
s_disp_store = malloc(EPD_DISPLAY_FRAME_SIZE);
|
|
if (s_disp_store == NULL) return ESP_FAIL;
|
|
|
|
lv_disp_draw_buf_init(&s_disp_buf, s_disp_store, NULL, EPD_DISPLAY_PIXEL_COUNT);
|
|
lv_disp_drv_init(&s_disp_drv);
|
|
|
|
s_disp_drv.set_px_cb = impl_lvgl_epd_set_px_cb;
|
|
s_disp_drv.flush_cb = impl_lvgl_epd_flush_cb;
|
|
s_disp_drv.rounder_cb = impl_lvgl_epd_rounder_cb;
|
|
s_disp_drv.hor_res = 400;
|
|
s_disp_drv.ver_res = 300;
|
|
s_disp_drv.draw_buf = &s_disp_buf;
|
|
s_disp_drv.full_refresh = 1;
|
|
|
|
lv_disp_t *disp = lv_disp_drv_register(&s_disp_drv);
|
|
if (disp == NULL) {
|
|
ESP_LOGE(LOG_TAG, "LVGL display driver register failed.");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
impl_btn_init();
|
|
|
|
lv_indev_drv_init(&s_indev_drv);
|
|
s_indev_drv.type = LV_INDEV_TYPE_KEYPAD;
|
|
s_indev_drv.read_cb = impl_lvgl_button_read;
|
|
|
|
lv_indev_t *indev = lv_indev_drv_register(&s_indev_drv);
|
|
if (indev == NULL) {
|
|
ESP_LOGE(LOG_TAG, "LVGL indev register failed.");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
lv_group_t *indev_group = lv_group_create();
|
|
lv_group_set_default(indev_group);
|
|
lv_indev_set_group(indev, indev_group);
|
|
|
|
lv_theme_t *default_theme = lv_theme_mono_init(disp, false, &lv_font_unscii_16);
|
|
lv_disp_set_theme(disp, default_theme);
|
|
|
|
s_lv_semphr_handle = xSemaphoreCreateMutex();
|
|
if (s_lv_semphr_handle == NULL) {
|
|
ESP_LOGE(LOG_TAG, "LVGL semaphore creation failed.");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
xSemaphoreGive(s_lv_semphr_handle);
|
|
|
|
if (xTaskCreate(impl_lvgl_tick_task, "IMPL_LVT", TASK_IMPL_LVT_STACK_SIZE, NULL, TASK_IMPL_LVT_PRIORITY,
|
|
&s_lv_tick_handle) != pdPASS) {
|
|
ESP_LOGE(LOG_TAG, "LVGL tick task creation failed.");
|
|
return ESP_FAIL;
|
|
}
|
|
if (xTaskCreate(impl_lvgl_timer_task, "IMPL_LVE", TASK_IMPL_LVE_STACK_SIZE, NULL, TASK_IMPL_LVE_PRIORITY,
|
|
&s_lv_task_handle) != pdPASS) {
|
|
ESP_LOGE(LOG_TAG, "LVGL timer handler task creation failed.");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
ESP_LOGI(LOG_TAG, "LVGL initialized.");
|
|
|
|
return ESP_OK;
|
|
} |