display: move ST7789 window setup and DMA start into hw_start_dma

This commit is contained in:
Stanislav N Mikhailov
2026-02-23 13:31:45 +03:00
parent 8b7b91eaad
commit bfb7c3cbd5
+185 -21
View File
@@ -2,6 +2,10 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "hardware/dma.h"
#include "hardware/irq.h"
#ifndef DISPLAY_MAX_BUFFERS
@@ -31,48 +35,208 @@ typedef struct
static display_context_t ctx;
static int dma_chan = -1;
#ifndef SPI_PORT
#define SPI_PORT spi0
#endif
#ifndef PIN_MOSI
#define PIN_MOSI 19
#endif
#ifndef PIN_SCK
#define PIN_SCK 18
#endif
#ifndef PIN_CS
#define PIN_CS 17
#endif
#ifndef PIN_DC
#define PIN_DC 22
#endif
#ifndef PIN_RST
#define PIN_RST 13
#endif
#ifndef PIN_BL
#define PIN_BL 12
#endif
static inline void st7789_send_command(uint8_t cmd)
{
/* DC=0: передаём байт команды контроллеру дисплея */
gpio_put(PIN_DC, 0);
gpio_put(PIN_CS, 0);
spi_write_blocking(SPI_PORT, &cmd, 1);
gpio_put(PIN_CS, 1);
}
static inline void st7789_send_data_bytes(const uint8_t* data, size_t len)
{
/* DC=1: передаём полезные данные команды */
gpio_put(PIN_DC, 1);
gpio_put(PIN_CS, 0);
spi_write_blocking(SPI_PORT, data, len);
gpio_put(PIN_CS, 1);
}
static inline void st7789_send_data_u8(uint8_t data)
{
st7789_send_data_bytes(&data, 1);
}
static void display_dma_irq_trampoline(void)
{
display_dma_irq_handler();
}
/* ============================================================
=== 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
*/
/* Максимально быстрый SPI для вывода кадров на ST7789 */
spi_init(SPI_PORT, 1000 * 100 * 625); /* 62.5 MHz */
spi_set_format(
SPI_PORT,
8,
SPI_CPOL_0,
SPI_CPHA_0,
SPI_MSB_FIRST
);
/* Линии данных SPI */
gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI);
gpio_set_function(PIN_SCK, GPIO_FUNC_SPI);
/* Управляющие пины дисплея */
gpio_init(PIN_CS);
gpio_init(PIN_DC);
gpio_init(PIN_RST);
gpio_init(PIN_BL);
gpio_set_dir(PIN_CS, GPIO_OUT);
gpio_set_dir(PIN_DC, GPIO_OUT);
gpio_set_dir(PIN_RST, GPIO_OUT);
gpio_set_dir(PIN_BL, GPIO_OUT);
gpio_put(PIN_CS, 1);
gpio_put(PIN_DC, 1);
gpio_put(PIN_BL, 0);
/* Аппаратный reset дисплея */
gpio_put(PIN_RST, 0);
sleep_ms(50);
gpio_put(PIN_RST, 1);
sleep_ms(50);
st7789_send_command(0x01); /* SWRESET: программный сброс */
sleep_ms(150);
st7789_send_command(0x11); /* SLPOUT: выход из sleep mode */
sleep_ms(150);
st7789_send_command(0x36); /* MADCTL: ориентация/порядок осей и RGB/BGR */
st7789_send_data_u8(0b10100000); /* Параметр из рабочей версии Thread.c */
st7789_send_command(0x3A); /* COLMOD: формат пикселя */
st7789_send_data_u8(0x55); /* 16 бит на пиксель (RGB565) */
{
uint16_t x_end = (width > 0) ? (uint16_t)(width - 1u) : 0u;
uint16_t y_end = (height > 0) ? (uint16_t)(height - 1u) : 0u;
uint8_t window[4];
/* Окно вывода по X: [0 .. width-1] */
st7789_send_command(0x2A); /* CASET: Column Address Set */
window[0] = 0x00;
window[1] = 0x00;
window[2] = (uint8_t)(x_end >> 8);
window[3] = (uint8_t)x_end;
st7789_send_data_bytes(window, sizeof(window));
/* Окно вывода по Y: [0 .. height-1] */
st7789_send_command(0x2B); /* RASET: Row Address Set */
window[2] = (uint8_t)(y_end >> 8);
window[3] = (uint8_t)y_end;
st7789_send_data_bytes(window, sizeof(window));
}
/* DMA -> SPI TX, чтобы выгружать кадр без участия CPU */
dma_chan = dma_claim_unused_channel(true);
dma_channel_config c = dma_channel_get_default_config((uint)dma_chan);
channel_config_set_transfer_data_size(&c, DMA_SIZE_8); /* SPI в режиме 8 бит */
channel_config_set_read_increment(&c, true); /* читать массив буфера */
channel_config_set_write_increment(&c, false); /* писать в один регистр SPI DR */
channel_config_set_dreq(&c, spi_get_dreq(SPI_PORT, true)); /* Тактирование от готовности SPI TX */
dma_channel_configure(
(uint)dma_chan,
&c,
&spi_get_hw(SPI_PORT)->dr, /* куда пишем: SPI data register */
NULL,
0,
false
);
/* Прерывание по окончанию DMA-передачи кадра */
dma_channel_set_irq0_enabled((uint)dma_chan, true);
irq_set_exclusive_handler(DMA_IRQ_0, display_dma_irq_trampoline);
irq_set_enabled(DMA_IRQ_0, true);
st7789_send_command(0x21); /* INVON: включить инверсию (как в старом коде) */
st7789_send_command(0x29); /* DISPON: включить дисплей */
gpio_put(PIN_BL, 1);
}
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
*/
uint16_t x_end = (ctx.width > 0) ? (uint16_t)(ctx.width - 1u) : 0u;
uint16_t y_end = (ctx.height > 0) ? (uint16_t)(ctx.height - 1u) : 0u;
uint8_t window[4];
(void)buffer;
(void)pixel_count;
st7789_send_command(0x2A); /* CASET: Column Address Set */
window[0] = 0x00;
window[1] = 0x00;
window[2] = (uint8_t)(x_end >> 8);
window[3] = (uint8_t)x_end;
st7789_send_data_bytes(window, sizeof(window));
st7789_send_command(0x2B); /* RASET: Row Address Set */
window[2] = (uint8_t)(y_end >> 8);
window[3] = (uint8_t)y_end;
st7789_send_data_bytes(window, sizeof(window));
st7789_send_command(0x2C); /* RAMWR: Memory Write */
/* DC=1 и CS=0 перед непрерывной DMA-передачей буфера */
gpio_put(PIN_DC, 1);
gpio_put(PIN_CS, 0);
dma_channel_set_read_addr((uint)dma_chan, buffer, false);
dma_channel_set_trans_count((uint)dma_chan, pixel_count * sizeof(uint16_t), true);
}
static void hw_raise_cs(void)
{
/* TODO: raise chip select */
gpio_put(PIN_CS, 1);
}
/* ============================================================
=== DMA IRQ handler (must be wired to real IRQ later)
=== Обработчик DMA IRQ (позже подключить к реальному IRQ)
============================================================ */
void display_dma_irq_handler(void)
{
/* TODO: clear DMA interrupt flag */
if (dma_chan >= 0)
{
/* Сбрасываем флаг IRQ у текущего DMA-канала */
dma_hw->ints0 = 1u << (uint)dma_chan;
}
hw_raise_cs();
@@ -82,7 +246,7 @@ void display_dma_irq_handler(void)
/* ============================================================
=== Public API
=== Публичный API
============================================================ */
void display_init(const display_config_t* cfg)
@@ -129,7 +293,7 @@ uint16_t* display_get_draw_buffer(void)
{
while (ctx.dma_busy)
{
/* busy wait */
/* активное ожидание */
}
}
@@ -208,7 +372,7 @@ void display_wait(void)
{
while (ctx.dma_busy)
{
/* busy wait */
/* активное ожидание */
}
}