Refactoring through example: the core template

This commit is contained in:
Stanislav N Mikhailov
2026-02-22 17:55:34 +03:00
parent 88d8ab4c57
commit d8f7bf241f
2 changed files with 407 additions and 0 deletions
+229
View File
@@ -0,0 +1,229 @@
#include "display.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#ifndef DISPLAY_MAX_BUFFERS
#define DISPLAY_MAX_BUFFERS 2
#endif
typedef struct
{
uint16_t width;
uint16_t height;
uint8_t buffer_count;
display_mode_t mode;
volatile bool dma_busy;
volatile bool frame_done_pending;
display_frame_done_cb_t frame_done_cb;
uint16_t* buffers[DISPLAY_MAX_BUFFERS];
uint8_t draw_index;
uint8_t scanout_index;
} display_context_t;
static display_context_t ctx;
/* ============================================================
=== Hardware abstraction (to be implemented later)
============================================================ */
static void hw_init(uint16_t width, uint16_t height)
{
/* TODO:
- init SPI
- init DMA
- init Core1 if needed
- register DMA IRQ
*/
}
static void hw_start_dma(uint16_t* buffer, size_t pixel_count)
{
/* TODO:
- assert dma not active at HW level
- assert CS low
- configure DMA transfer
- start transfer
*/
(void)buffer;
(void)pixel_count;
}
static void hw_raise_cs(void)
{
/* TODO: raise chip select */
}
/* ============================================================
=== DMA IRQ handler (must be wired to real IRQ later)
============================================================ */
void display_dma_irq_handler(void)
{
/* TODO: clear DMA interrupt flag */
hw_raise_cs();
ctx.dma_busy = false;
ctx.frame_done_pending = true;
}
/* ============================================================
=== Public API
============================================================ */
void display_init(const display_config_t* cfg)
{
assert(cfg != NULL);
assert(cfg->buffer_count >= 1);
assert(cfg->buffer_count <= DISPLAY_MAX_BUFFERS);
memset(&ctx, 0, sizeof(ctx));
ctx.width = cfg->width;
ctx.height = cfg->height;
ctx.buffer_count = cfg->buffer_count;
ctx.mode = cfg->mode;
ctx.frame_done_cb = cfg->frame_done_cb;
size_t pixels = (size_t)ctx.width * ctx.height;
size_t bytes = pixels * sizeof(uint16_t);
for (uint8_t i = 0; i < ctx.buffer_count; i++)
{
ctx.buffers[i] = malloc(bytes);
assert(ctx.buffers[i] != NULL);
memset(ctx.buffers[i], 0, bytes);
}
ctx.draw_index = 0;
ctx.scanout_index = 0;
ctx.dma_busy = false;
ctx.frame_done_pending = false;
hw_init(ctx.width, ctx.height);
}
/* ------------------------------------------------------------ */
uint16_t* display_get_draw_buffer(void)
{
if (ctx.mode == DISPLAY_MODE_SAFE &&
ctx.buffer_count == 1)
{
while (ctx.dma_busy)
{
/* busy wait */
}
}
return ctx.buffers[ctx.draw_index];
}
/* ------------------------------------------------------------ */
uint16_t* display_get_scanout_buffer(void)
{
return ctx.buffers[ctx.scanout_index];
}
/* ------------------------------------------------------------ */
bool display_swap_buffers(void)
{
if (ctx.mode != DISPLAY_MODE_RAW)
return false;
if (ctx.buffer_count < 2)
return false;
if (ctx.dma_busy)
return false;
uint8_t tmp = ctx.draw_index;
ctx.draw_index = ctx.scanout_index;
ctx.scanout_index = tmp;
return true;
}
/* ------------------------------------------------------------ */
bool display_submit(void)
{
if (ctx.dma_busy)
return false;
if (ctx.mode == DISPLAY_MODE_SAFE &&
ctx.buffer_count == 2)
{
uint8_t tmp = ctx.draw_index;
ctx.draw_index = ctx.scanout_index;
ctx.scanout_index = tmp;
}
size_t pixels = (size_t)ctx.width * ctx.height;
ctx.dma_busy = true;
hw_start_dma(
ctx.buffers[ctx.scanout_index],
pixels
);
return true;
}
/* ------------------------------------------------------------ */
bool display_ready(void)
{
return !ctx.dma_busy;
}
/* ------------------------------------------------------------ */
void display_wait(void)
{
while (ctx.dma_busy)
{
/* busy wait */
}
}
/* ------------------------------------------------------------ */
void display_poll(void)
{
if (ctx.frame_done_pending)
{
ctx.frame_done_pending = false;
if (ctx.frame_done_cb)
{
ctx.frame_done_cb();
}
}
}
+178
View File
@@ -0,0 +1,178 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/* ============================================================
=== Compile-time configuration
============================================================ */
/*
* Максимальное количество буферов,
* доступное в данной сборке.
*
* RP2040: обычно 1
* RP2350: можно 2
*/
#ifndef DISPLAY_MAX_BUFFERS
#define DISPLAY_MAX_BUFFERS 2
#endif
/* ============================================================
=== Modes
============================================================ */
typedef enum
{
/*
* SAFE:
* - swap управляется engine
* - нет tearing при 2 буферах
* - возможны блокировки
*/
DISPLAY_MODE_SAFE = 0,
/*
* RAW:
* - swap управляется пользователем
* - engine не блокирует
* - tearing возможен
*/
DISPLAY_MODE_RAW
} display_mode_t;
/* ============================================================
=== Callback
============================================================ */
typedef void (*display_frame_done_cb_t)(void);
/* ============================================================
=== Configuration
============================================================ */
typedef struct
{
uint16_t width;
uint16_t height;
/*
* 1 или 2.
* Должно быть <= DISPLAY_MAX_BUFFERS.
*/
uint8_t buffer_count;
display_mode_t mode;
/*
* Вызывается после завершения DMA
* через display_poll().
* Может быть NULL.
*/
display_frame_done_cb_t frame_done_cb;
} display_config_t;
/* ============================================================
=== Lifecycle
============================================================ */
/*
* Инициализация display engine.
* Выделяет буферы и инициализирует HW.
*/
void display_init(const display_config_t* cfg);
/* ============================================================
=== Buffer access
============================================================ */
/*
* Буфер для рисования.
*
* SAFE + 1 buffer:
* может блокировать пока DMA активен.
*
* RAW:
* никогда не блокирует.
*/
uint16_t* display_get_draw_buffer(void);
/*
* Буфер, который сейчас передаётся DMA.
*/
uint16_t* display_get_scanout_buffer(void);
/* ============================================================
=== Buffer control (RAW mode only)
============================================================ */
/*
* Явная смена ролей draw/scanout.
*
* Возвращает false если:
* - не RAW режим
* - меньше 2 буферов
* - DMA активен
*/
bool display_swap_buffers(void);
/* ============================================================
=== Frame control
============================================================ */
/*
* Запуск передачи текущего scanout буфера.
*
* Возвращает false если DMA активен.
*/
bool display_submit(void);
/*
* true если DMA не активен.
*/
bool display_ready(void);
/*
* Блокирующее ожидание завершения DMA.
*/
void display_wait(void);
/*
* Обработка отложенного события завершения кадра.
* Должна вызываться из основного цикла.
*/
void display_poll(void);
/* ============================================================
=== IRQ hook
============================================================ */
/*
* Должен вызываться из реального DMA IRQ handler.
*/
void display_dma_irq_handler(void);
#ifdef __cplusplus
}
#endif