Skip to content
Snippets Groups Projects
Commit 4b525b17 authored by q3k's avatar q3k
Browse files

components/st3m: init with crtc

This implements the 'crtc' part of the graphics stack, ie. the task
responsible for blitting out a set of framebuffers over SPI as fast as
possible.

Currently this is still single-buffered, as the micropython API needs to
be changed to support injected, per-frame ctx instances.
parent 642d0cf7
No related branches found
No related tags found
No related merge requests found
...@@ -13,5 +13,6 @@ idf_component_register( ...@@ -13,5 +13,6 @@ idf_component_register(
include include
REQUIRES REQUIRES
flow3r_bsp flow3r_bsp
st3m
espressif__led_strip espressif__led_strip
) )
...@@ -7,65 +7,92 @@ ...@@ -7,65 +7,92 @@
#include "esp_log.h" #include "esp_log.h"
#include "esp_system.h" #include "esp_system.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "../../usermodule/uctx/uctx/ctx.h" #include "../../usermodule/uctx/uctx/ctx.h"
#include "flow3r_bsp.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() { typedef struct {
the_ctx = ctx_new_for_framebuffer( Ctx *ctx;
ctx_framebuffer, 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_WIDTH,
FLOW3R_BSP_DISPLAY_HEIGHT, FLOW3R_BSP_DISPLAY_HEIGHT,
FLOW3R_BSP_DISPLAY_WIDTH * 2, FLOW3R_BSP_DISPLAY_WIDTH * 2,
CTX_FORMAT_RGB565_BYTESWAPPED CTX_FORMAT_RGB565_BYTESWAPPED
); );
assert(ctx != NULL);
int32_t offset_x = FLOW3R_BSP_DISPLAY_WIDTH / 2; int32_t offset_x = FLOW3R_BSP_DISPLAY_WIDTH / 2;
int32_t offset_y = FLOW3R_BSP_DISPLAY_HEIGHT / 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 // 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) { static ctx_target_t targets[ST3M_GFX_NBUFFERS];
ctx_rgb(the_ctx, 0.157, 0.129, 0.167);
ctx_rectangle(the_ctx, -120, -120, 240, 240);
ctx_fill(the_ctx);
ctx_move_to(the_ctx, 0, 0); static void display_loading_splash(ctx_target_t *target) {
ctx_rgb(the_ctx, 0.9, 0.9, 0.9); ctx_rgb(target->ctx, 0.157, 0.129, 0.167);
ctx_text_align(the_ctx, CTX_TEXT_ALIGN_CENTER); ctx_rectangle(target->ctx, -120, -120, 240, 240);
ctx_text_baseline(the_ctx, CTX_TEXT_BASELINE_ALPHABETIC); ctx_fill(target->ctx);
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() { void display_init() {
flow3r_bsp_display_init(); // HACK: needed until we have new async gfx api.
display_ctx_init(); assert(ST3M_GFX_NBUFFERS == 1);
display_loading_splash();
// Delay turning on backlight, otherwise we get a flash of some old st3m_gfx_init();
// buffer..? Is the display RAMWR not synchronous..? for (int i = 0; i < ST3M_GFX_NBUFFERS; i++) {
vTaskDelay(100 / portTICK_PERIOD_MS); targets[i].ctx = NULL;
flow3r_bsp_display_set_backlight(100); 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; initialized = true;
} }
void display_update(){ void display_update(){
if (!initialized) { if (!initialized) {
return; 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) { void display_set_backlight(uint8_t percent) {
...@@ -79,5 +106,5 @@ Ctx *display_global_ctx(void) { ...@@ -79,5 +106,5 @@ Ctx *display_global_ctx(void) {
if (!initialized) { if (!initialized) {
return NULL; return NULL;
} }
return the_ctx; return sync_target->ctx;
} }
\ No newline at end of file
idf_component_register( idf_component_register(
SRCS SRCS
st3m_gfx.c
INCLUDE_DIRS INCLUDE_DIRS
. .
REQUIRES REQUIRES
......
#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
#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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment