#include "f_lcd8048.h" #include "f_panelConfig.h" #include #include #include "esp_log.h" #include "esp_check.h" #include "driver/gpio.h" #include "esp_lcd_panel_ops.h" #include "esp_lcd_panel_rgb.h" #include "freertos/semphr.h" #include "freertos/task.h" static const char *TAG = "f_lcd8048"; static esp_lcd_panel_handle_t s_panel = NULL; static uint16_t *s_framebuffer[2] = {NULL, NULL}; static SemaphoreHandle_t s_frame_done_sem = NULL; static volatile bool s_flush_pending = false; static volatile uint32_t s_vsync_sequence = 0; /* * O VSYNC marca a fronteira entre dois quadros. Depois desse evento o * framebuffer anterior pode voltar para o LVGL sem ser alterado durante a * varredura ativa do painel. */ static bool IRAM_ATTR rgb_vsync_cb( esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx) { (void)panel; (void)edata; (void)user_ctx; /* * O contador permite distinguir um VSYNC ocorrido durante draw_bitmap() * daquele que realmente aconteceu depois que o novo framebuffer foi * selecionado. */ s_vsync_sequence++; if (!s_flush_pending || s_frame_done_sem == NULL) { return false; } BaseType_t high_task_woken = pdFALSE; xSemaphoreGiveFromISR(s_frame_done_sem, &high_task_woken); return high_task_woken == pdTRUE; } static inline uint16_t rgb565(uint8_t r, uint8_t g, uint8_t b) { return (uint16_t)(((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3)); } static void backlight_init(void) { const gpio_config_t io_conf = { .pin_bit_mask = 1ULL << LCD_BKLT_GPIO, .mode = GPIO_MODE_OUTPUT, .pull_up_en = GPIO_PULLUP_DISABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, .intr_type = GPIO_INTR_DISABLE }; ESP_ERROR_CHECK(gpio_config(&io_conf)); gpio_set_level(LCD_BKLT_GPIO, 0); } static void backlight_on(void) { gpio_set_level(LCD_BKLT_GPIO, 1); } static void fill_framebuffer(uint16_t *framebuffer, uint16_t color) { if (framebuffer == NULL) { return; } const size_t total_pixels = LCD_H_RES * LCD_V_RES; for (size_t i = 0; i < total_pixels; i++) { framebuffer[i] = color; } } static void fill_screen(uint16_t color) { fill_framebuffer(s_framebuffer[0], color); fill_framebuffer(s_framebuffer[1], color); } static void draw_test_pattern_on(uint16_t *framebuffer) { if (framebuffer == NULL) { return; } const uint16_t colors[] = { rgb565(255, 255, 255), rgb565(255, 255, 0), rgb565( 0, 255, 255), rgb565( 0, 255, 0), rgb565(255, 0, 255), rgb565(255, 0, 0), rgb565( 0, 0, 255), rgb565( 0, 0, 0) }; const int bar_count = sizeof(colors) / sizeof(colors[0]); const int bar_width = LCD_H_RES / bar_count; for (int y = 0; y < LCD_V_RES; y++) { for (int x = 0; x < LCD_H_RES; x++) { int idx = x / bar_width; if (idx >= bar_count) { idx = bar_count - 1; } framebuffer[y * LCD_H_RES + x] = colors[idx]; } } for (int x = 0; x < LCD_H_RES; x++) { framebuffer[x] = rgb565(255, 255, 255); framebuffer[(LCD_V_RES - 1) * LCD_H_RES + x] = rgb565(255, 255, 255); } for (int y = 0; y < LCD_V_RES; y++) { framebuffer[y * LCD_H_RES] = rgb565(255, 255, 255); framebuffer[y * LCD_H_RES + (LCD_H_RES - 1)] = rgb565(255, 255, 255); } } static void draw_test_pattern(void) { draw_test_pattern_on(s_framebuffer[0]); draw_test_pattern_on(s_framebuffer[1]); } esp_err_t f_setup_lcd8048(void) { if (s_panel != NULL) { ESP_LOGW(TAG, "Display already initialized."); return ESP_OK; } backlight_init(); esp_lcd_rgb_panel_config_t panel_config = f_panelConfig_get(); ESP_RETURN_ON_ERROR( esp_lcd_new_rgb_panel(&panel_config, &s_panel), TAG, "esp_lcd_new_rgb_panel falhou" ); s_frame_done_sem = xSemaphoreCreateBinary(); ESP_RETURN_ON_FALSE( s_frame_done_sem != NULL, ESP_ERR_NO_MEM, TAG, "Falha ao criar semaforo de sincronismo do LCD" ); const esp_lcd_rgb_panel_event_callbacks_t callbacks = { .on_vsync = rgb_vsync_cb, }; ESP_RETURN_ON_ERROR( esp_lcd_rgb_panel_register_event_callbacks(s_panel, &callbacks, NULL), TAG, "esp_lcd_rgb_panel_register_event_callbacks falhou" ); ESP_RETURN_ON_ERROR( esp_lcd_panel_reset(s_panel), TAG, "esp_lcd_panel_reset falhou" ); ESP_RETURN_ON_ERROR( esp_lcd_panel_init(s_panel), TAG, "esp_lcd_panel_init falhou" ); void *fb0 = NULL; void *fb1 = NULL; ESP_RETURN_ON_ERROR( esp_lcd_rgb_panel_get_frame_buffer( s_panel, 2, &fb0, &fb1 ), TAG, "esp_lcd_rgb_panel_get_frame_buffer falhou" ); s_framebuffer[0] = (uint16_t *)fb0; s_framebuffer[1] = (uint16_t *)fb1; ESP_RETURN_ON_FALSE( s_framebuffer[0] != NULL && s_framebuffer[1] != NULL, ESP_ERR_INVALID_STATE, TAG, "Um ou mais framebuffers retornaram NULL" ); fill_screen(rgb565(0, 0, 0)); /* Para teste sem LVGL, descomente a linha abaixo. */ // draw_test_pattern(); backlight_on(); ESP_LOGI( TAG, "Display 4.3 initialized successfully (%dx%d).", LCD_H_RES, LCD_V_RES ); ESP_LOGI( TAG, "Framebuffers: FB0=%p FB1=%p", s_framebuffer[0], s_framebuffer[1] ); return ESP_OK; } void f_display43_prepare_flush_sync(void) { if (s_frame_done_sem == NULL) { return; } /* Descarta uma notificacao antiga e passa a observar os proximos VSYNCs. */ xSemaphoreTake(s_frame_done_sem, 0); s_flush_pending = true; } void f_display43_cancel_flush_sync(void) { s_flush_pending = false; if (s_frame_done_sem != NULL) { xSemaphoreTake(s_frame_done_sem, 0); } } bool f_display43_wait_flush_release(TickType_t timeout_ticks) { if (s_frame_done_sem == NULL) { return false; } /* * A captura acontece somente depois de draw_bitmap(). Uma notificacao que * tenha chegado durante o desenho pode acordar esta tarefa, mas sera * ignorada enquanto o contador ainda representar o mesmo VSYNC. */ const uint32_t sequence_after_draw = s_vsync_sequence; const TickType_t wait_start = xTaskGetTickCount(); bool released = false; while (s_vsync_sequence == sequence_after_draw) { TickType_t remaining_ticks = timeout_ticks; if (timeout_ticks != portMAX_DELAY) { const TickType_t elapsed_ticks = xTaskGetTickCount() - wait_start; if (elapsed_ticks >= timeout_ticks) { break; } remaining_ticks = timeout_ticks - elapsed_ticks; } if (xSemaphoreTake(s_frame_done_sem, remaining_ticks) != pdTRUE && s_vsync_sequence == sequence_after_draw) { break; } } released = s_vsync_sequence != sequence_after_draw; s_flush_pending = false; xSemaphoreTake(s_frame_done_sem, 0); return released; } esp_lcd_panel_handle_t f_display43_get_panel(void) { return s_panel; } uint16_t *f_display43_get_framebuffer(void) { return s_framebuffer[0]; } uint16_t *f_display43_get_framebuffer2(void) { return s_framebuffer[1]; }