#include "impl_lvgl.h" #include "esp_log.h" #include "impl_epd.h" #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 6 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 = EPD_DISPLAY_MAX_PARTIAL - 1; #endif static lv_disp_draw_buf_t s_disp_buf; static lv_color_t s_disp_store[EPD_DISPLAY_FRAME_SIZE]; static lv_disp_drv_t s_disp_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 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_log_cb(const char *buf) { 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_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; printf("Buffer start: %p\r\n", s_disp_store); lv_disp_t *disp = lv_disp_drv_register(&s_disp_drv); if (disp == NULL) { return ESP_FAIL; } s_lv_semphr_handle = xSemaphoreCreateBinary(); if (s_lv_semphr_handle == NULL) { return ESP_FAIL; } xSemaphoreGive(s_lv_semphr_handle); xTaskCreate(impl_lvgl_tick_task, "LV_TICK", 1024, NULL, 5, &s_lv_tick_handle); xTaskCreate(impl_lvgl_timer_task, "LV_TMR", 4096, NULL, 4, &s_lv_task_handle); // TODO: Launch LVGL tasks below. return ESP_OK; }