diff --git a/components/badge23/CMakeLists.txt b/components/badge23/CMakeLists.txt index 2fa07d9f4d6a057db613d19141233fb1da58a8e9..90a2355b754ba8851d2b898152d338b63283a3cb 100644 --- a/components/badge23/CMakeLists.txt +++ b/components/badge23/CMakeLists.txt @@ -13,5 +13,6 @@ idf_component_register( include REQUIRES flow3r_bsp + st3m espressif__led_strip ) diff --git a/components/badge23/display.c b/components/badge23/display.c index 92d234956b730875a4a8dac5691f80ea51d5b608..3a8629f0969506b7dd85ebd4f6c93a0c447e011f 100644 --- a/components/badge23/display.c +++ b/components/badge23/display.c @@ -7,65 +7,92 @@ #include "esp_log.h" #include "esp_system.h" #include "freertos/FreeRTOS.h" -#include "freertos/task.h" #include "../../usermodule/uctx/uctx/ctx.h" #include "flow3r_bsp.h" +#include "st3m_gfx.h" -static Ctx *the_ctx = NULL; -static volatile bool initialized = false; -static DMA_ATTR uint16_t ctx_framebuffer[FLOW3R_BSP_DISPLAY_WIDTH * FLOW3R_BSP_DISPLAY_HEIGHT]; -void display_ctx_init() { - the_ctx = ctx_new_for_framebuffer( - ctx_framebuffer, +typedef struct { + Ctx *ctx; + st3m_framebuffer_desc_t *desc; +} ctx_target_t; + +static void new_ctx_target(ctx_target_t *target, st3m_framebuffer_desc_t *desc) { + Ctx *ctx = ctx_new_for_framebuffer( + desc->buffer, FLOW3R_BSP_DISPLAY_WIDTH, FLOW3R_BSP_DISPLAY_HEIGHT, FLOW3R_BSP_DISPLAY_WIDTH * 2, CTX_FORMAT_RGB565_BYTESWAPPED ); + assert(ctx != NULL); int32_t offset_x = FLOW3R_BSP_DISPLAY_WIDTH / 2; int32_t offset_y = FLOW3R_BSP_DISPLAY_HEIGHT / 2; // rotate by 180 deg and translate x and y by 120 px to have (0,0) at the center of the screen - ctx_apply_transform(the_ctx,-1,0,offset_x,0,-1,offset_y,0,0,1); + ctx_apply_transform(ctx,-1,0,offset_x,0,-1,offset_y,0,0,1); + + target->ctx = ctx; + target->desc = desc; } -static void display_loading_splash(void) { - ctx_rgb(the_ctx, 0.157, 0.129, 0.167); - ctx_rectangle(the_ctx, -120, -120, 240, 240); - ctx_fill(the_ctx); +static ctx_target_t targets[ST3M_GFX_NBUFFERS]; + +static void display_loading_splash(ctx_target_t *target) { + ctx_rgb(target->ctx, 0.157, 0.129, 0.167); + ctx_rectangle(target->ctx, -120, -120, 240, 240); + ctx_fill(target->ctx); - ctx_move_to(the_ctx, 0, 0); - ctx_rgb(the_ctx, 0.9, 0.9, 0.9); - ctx_text_align(the_ctx, CTX_TEXT_ALIGN_CENTER); - ctx_text_baseline(the_ctx, CTX_TEXT_BASELINE_ALPHABETIC); - ctx_text(the_ctx, "Loading..."); + ctx_move_to(target->ctx, 0, 0); + ctx_rgb(target->ctx, 0.9, 0.9, 0.9); + ctx_text_align(target->ctx, CTX_TEXT_ALIGN_CENTER); + ctx_text_baseline(target->ctx, CTX_TEXT_BASELINE_ALPHABETIC); + ctx_text(target->ctx, "Loading..."); +} - flow3r_bsp_display_send_fb(ctx_framebuffer); +static ctx_target_t *next_target(void) { + st3m_framebuffer_desc_t *desc = st3m_gfx_framebuffer_get(portMAX_DELAY); + if (targets[desc->num].ctx == NULL) { + new_ctx_target(&targets[desc->num], desc); + } + + return &targets[desc->num]; } +static bool initialized = false; +static ctx_target_t *sync_target = NULL; + void display_init() { - flow3r_bsp_display_init(); - display_ctx_init(); - display_loading_splash(); - // Delay turning on backlight, otherwise we get a flash of some old - // buffer..? Is the display RAMWR not synchronous..? - vTaskDelay(100 / portTICK_PERIOD_MS); - flow3r_bsp_display_set_backlight(100); + // HACK: needed until we have new async gfx api. + assert(ST3M_GFX_NBUFFERS == 1); + + st3m_gfx_init(); + for (int i = 0; i < ST3M_GFX_NBUFFERS; i++) { + targets[i].ctx = NULL; + targets[i].desc = NULL; + } - memset(ctx_framebuffer, 0, FLOW3R_BSP_DISPLAY_WIDTH * FLOW3R_BSP_DISPLAY_HEIGHT * 2); + ctx_target_t *tgt = next_target(); + display_loading_splash(tgt); + st3m_gfx_framebuffer_queue(tgt->desc); + sync_target = next_target(); + flow3r_bsp_display_set_backlight(100); initialized = true; } + void display_update(){ if (!initialized) { return; } - flow3r_bsp_display_send_fb(ctx_framebuffer); + + st3m_gfx_framebuffer_queue(sync_target->desc); + ctx_target_t *tgt = next_target(); + assert(tgt == sync_target); } void display_set_backlight(uint8_t percent) { @@ -79,5 +106,5 @@ Ctx *display_global_ctx(void) { if (!initialized) { return NULL; } - return the_ctx; + return sync_target->ctx; } \ No newline at end of file diff --git a/components/st3m/CMakeLists.txt b/components/st3m/CMakeLists.txt index 29dde06d87a05743d6223e465947118c811b76d3..7196e6632afe7bed752925c7b8348f807d8f2c00 100644 --- a/components/st3m/CMakeLists.txt +++ b/components/st3m/CMakeLists.txt @@ -1,5 +1,6 @@ idf_component_register( SRCS + st3m_gfx.c INCLUDE_DIRS . REQUIRES diff --git a/components/st3m/st3m_gfx.c b/components/st3m/st3m_gfx.c new file mode 100644 index 0000000000000000000000000000000000000000..8aa4ae0e665eddeb6bedd1fa71c626027f312b5d --- /dev/null +++ b/components/st3m/st3m_gfx.c @@ -0,0 +1,77 @@ +#include "st3m_gfx.h" + +#include <string.h> + +#include "esp_system.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" + +#include "flow3r_bsp.h" + +// Actual framebuffers, efficiently accessible via DMA. +static DMA_ATTR uint16_t framebuffers[ST3M_GFX_NBUFFERS][FLOW3R_BSP_DISPLAY_WIDTH * FLOW3R_BSP_DISPLAY_HEIGHT]; + +static st3m_framebuffer_desc_t framebuffer_descs[ST3M_GFX_NBUFFERS]; + +// Queue of free framebuffer descriptors, written into by crtc once rendered, +// read from by rasterizer when new frame starts. +static QueueHandle_t framebuffer_freeq = NULL; + +// Queue of framebuffer descriptors to blit out. +static QueueHandle_t framebuffer_blitq = NULL; + +static void st3m_gfx_crtc_task(void *_arg) { + (void)_arg; + + while (true) { + int descno; + xQueueReceive(framebuffer_blitq, &descno, portMAX_DELAY); + + flow3r_bsp_display_send_fb(framebuffer_descs[descno].buffer); + + xQueueSend(framebuffer_freeq, &descno, portMAX_DELAY); + } +} + +void st3m_gfx_init(void) { + // Make sure we're not being re-initialized. + assert(framebuffer_freeq == NULL); + + flow3r_bsp_display_init(); + + // Create framebuffer queues. + framebuffer_freeq = xQueueCreate(2, sizeof(int)); + assert(framebuffer_freeq != NULL); + framebuffer_blitq = xQueueCreate(2, sizeof(int)); + assert(framebuffer_blitq != NULL); + + // Zero out framebuffers and set up descriptors. + for (int i = 0; i < ST3M_GFX_NBUFFERS; i++) { + memset(&framebuffers[i], 0, FLOW3R_BSP_DISPLAY_WIDTH * FLOW3R_BSP_DISPLAY_HEIGHT * 2); + st3m_framebuffer_desc_t *desc = &framebuffer_descs[i]; + desc->num = i; + desc->buffer = framebuffers[i]; + + // Push descriptor to freeq. + BaseType_t res = xQueueSend(framebuffer_freeq, &i, 0); + assert(res == pdTRUE); + } + + // Start crtc. + BaseType_t res = xTaskCreate(st3m_gfx_crtc_task, "crtc", 2048, NULL, configMAX_PRIORITIES - 2, NULL); + assert(res == pdPASS); +} + +st3m_framebuffer_desc_t *st3m_gfx_framebuffer_get(TickType_t ticks_to_wait) { + int descno; + BaseType_t res = xQueueReceive(framebuffer_freeq, &descno, ticks_to_wait); + if (res != pdTRUE) { + return NULL; + } + return &framebuffer_descs[descno]; +} + +void st3m_gfx_framebuffer_queue(st3m_framebuffer_desc_t *desc) { + xQueueSend(framebuffer_blitq, &desc->num, portMAX_DELAY); +} \ No newline at end of file diff --git a/components/st3m/st3m_gfx.h b/components/st3m/st3m_gfx.h new file mode 100644 index 0000000000000000000000000000000000000000..55849fd682ae98170eff8788051e94060868aecc --- /dev/null +++ b/components/st3m/st3m_gfx.h @@ -0,0 +1,19 @@ +#pragma once + +#include "freertos/FreeRTOS.h" + +// Each buffer takes ~116kB SRAM. While one framebuffer is being blitted, the +// other one is being written to by the rasterizer. +#define ST3M_GFX_NBUFFERS 1 + +// A framebuffer descriptor, pointing at a framebuffer. +typedef struct { + int num; + uint16_t *buffer; +} st3m_framebuffer_desc_t; + +void st3m_gfx_init(void); + +st3m_framebuffer_desc_t *st3m_gfx_framebuffer_get(TickType_t ticks_to_wait); + +void st3m_gfx_framebuffer_queue(st3m_framebuffer_desc_t *desc); \ No newline at end of file