mirror of
https://github.com/stasenso/rp_pico_display_engine.git
synced 2026-06-26 21:32:41 +03:00
Refactor display stack into driver and transport layers
This commit is contained in:
@@ -8,10 +8,11 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE BOOL "Export compile_commands.json" F
|
||||
|
||||
pico_sdk_init()
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
src/main.c
|
||||
add_library(display_engine STATIC
|
||||
../../src/Font/font_data.c
|
||||
../../src/core/display.c
|
||||
../../src/core/display_transport.c
|
||||
../../src/core/display_driver.c
|
||||
../../src/render/context.c
|
||||
../../src/render/line.c
|
||||
../../src/render/grid.c
|
||||
@@ -19,12 +20,12 @@ add_executable(${PROJECT_NAME}
|
||||
../../src/render/bezier.c
|
||||
)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
target_include_directories(display_engine PUBLIC
|
||||
../../include
|
||||
../../include/display
|
||||
)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
target_link_libraries(display_engine PUBLIC
|
||||
pico_stdlib
|
||||
hardware_spi
|
||||
hardware_dma
|
||||
@@ -40,16 +41,26 @@ set(DISPLAY_PIN_CS 13)
|
||||
set(DISPLAY_PIN_DC 12)
|
||||
set(DISPLAY_PIN_RST 11)
|
||||
set(DISPLAY_PIN_BL 10)
|
||||
set(DISPLAY_TYPE DISPLAY_TYPE_ST7789)
|
||||
|
||||
# Pass board pin mapping into display.c without editing engine sources.
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE
|
||||
SPI_PORT=${DISPLAY_SPI_PORT}
|
||||
PIN_MOSI=${DISPLAY_PIN_MOSI}
|
||||
PIN_SCK=${DISPLAY_PIN_SCK}
|
||||
PIN_CS=${DISPLAY_PIN_CS}
|
||||
PIN_DC=${DISPLAY_PIN_DC}
|
||||
PIN_RST=${DISPLAY_PIN_RST}
|
||||
PIN_BL=${DISPLAY_PIN_BL}
|
||||
# Pass display mapping into display backend without editing engine sources.
|
||||
target_compile_definitions(display_engine PUBLIC
|
||||
DISPLAY_TYPE=${DISPLAY_TYPE}
|
||||
DISPLAY_SPI_PORT=${DISPLAY_SPI_PORT}
|
||||
DISPLAY_PIN_MOSI=${DISPLAY_PIN_MOSI}
|
||||
DISPLAY_PIN_SCK=${DISPLAY_PIN_SCK}
|
||||
DISPLAY_PIN_CS=${DISPLAY_PIN_CS}
|
||||
DISPLAY_PIN_DC=${DISPLAY_PIN_DC}
|
||||
DISPLAY_PIN_RST=${DISPLAY_PIN_RST}
|
||||
DISPLAY_PIN_BL=${DISPLAY_PIN_BL}
|
||||
)
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
src/main.c
|
||||
)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
display_engine
|
||||
)
|
||||
|
||||
pico_add_extra_outputs(${PROJECT_NAME})
|
||||
|
||||
+6
-244
@@ -1,11 +1,9 @@
|
||||
#include "display.h"
|
||||
#include "display_transport.h"
|
||||
#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
|
||||
@@ -35,80 +33,6 @@ 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
|
||||
|
||||
/*
|
||||
* Назначение:
|
||||
* Отправляет дисплею служебную команду (например, сброс или смену режима).
|
||||
*
|
||||
* Параметры:
|
||||
* cmd (uint8_t) - код команды ST7789, диапазон 0..255.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Назначение:
|
||||
* Отправляет дисплею набор данных, относящихся к последней команде.
|
||||
*
|
||||
* Параметры:
|
||||
* data (const uint8_t*) - указатель на буфер данных; должен быть валиден при len > 0.
|
||||
* len (size_t) - количество передаваемых байтов, диапазон 0..SIZE_MAX.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Назначение:
|
||||
* Отправляет дисплею один байт данных.
|
||||
*
|
||||
* Параметры:
|
||||
* data (uint8_t) - байт данных, диапазон 0..255.
|
||||
*/
|
||||
static inline void st7789_send_data_u8(uint8_t data)
|
||||
{
|
||||
st7789_send_data_bytes(&data, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Назначение:
|
||||
@@ -120,157 +44,6 @@ static void display_dma_irq_trampoline(void)
|
||||
}
|
||||
|
||||
|
||||
/* ============================================================
|
||||
=== Абстракция оборудования (реализовать позже)
|
||||
============================================================ */
|
||||
|
||||
/*
|
||||
* Назначение:
|
||||
* Полностью подготавливает железо для работы экрана: линии, SPI, контроллер и DMA.
|
||||
*
|
||||
* Параметры:
|
||||
* width (uint16_t) - ширина кадра в пикселях, диапазон 0..65535.
|
||||
* height (uint16_t) - высота кадра в пикселях, диапазон 0..65535.
|
||||
*/
|
||||
static void hw_init(uint16_t width, uint16_t height)
|
||||
{
|
||||
/* Максимально быстрый 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Назначение:
|
||||
* Готовит экран к приёму кадра и запускает быструю отправку пикселей через DMA.
|
||||
*
|
||||
* Параметры:
|
||||
* buffer (uint16_t*) - указатель на буфер пикселей RGB565; должен быть валиден.
|
||||
* pixel_count (size_t) - число пикселей для отправки, диапазон 0..SIZE_MAX.
|
||||
*/
|
||||
static void hw_start_dma(uint16_t* buffer, size_t pixel_count)
|
||||
{
|
||||
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];
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Назначение:
|
||||
* Завершает текущую передачу в экран, поднимая линию выбора устройства (CS).
|
||||
*/
|
||||
static void hw_raise_cs(void)
|
||||
{
|
||||
gpio_put(PIN_CS, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Назначение:
|
||||
* Пытается запустить отложенный submit, если DMA уже освободился.
|
||||
@@ -292,7 +65,7 @@ static void display_kick_pending_if_possible(void)
|
||||
|
||||
size_t pixels = (size_t)ctx.width * ctx.height;
|
||||
ctx.dma_busy = true;
|
||||
hw_start_dma(ctx.buffers[ctx.scanout_index], pixels);
|
||||
display_transport_start_frame(ctx.width, ctx.height, ctx.buffers[ctx.scanout_index], pixels);
|
||||
}
|
||||
|
||||
|
||||
@@ -307,15 +80,7 @@ static void display_kick_pending_if_possible(void)
|
||||
*/
|
||||
void display_dma_irq_handler(void)
|
||||
{
|
||||
/* Проверяем, что DMA-канал действительно был выделен. */
|
||||
if (dma_chan >= 0)
|
||||
{
|
||||
/* Сбрасываем флаг IRQ у текущего DMA-канала */
|
||||
dma_hw->ints0 = 1u << (uint)dma_chan;
|
||||
}
|
||||
|
||||
/* Поднимаем CS, завершая SPI-транзакцию кадра. */
|
||||
hw_raise_cs();
|
||||
display_transport_complete_irq();
|
||||
|
||||
/* Отмечаем, что DMA завершил передачу и шина свободна. */
|
||||
ctx.dma_busy = false;
|
||||
@@ -382,8 +147,8 @@ void display_init(const display_config_t* cfg)
|
||||
ctx.dma_busy = false;
|
||||
ctx.paint_in_progress = false;
|
||||
|
||||
/* Инициализируем аппаратный слой дисплея. */
|
||||
hw_init(ctx.width, ctx.height);
|
||||
/* Инициализируем транспортный слой дисплея и backend контроллера. */
|
||||
display_transport_init(ctx.width, ctx.height, display_dma_irq_trampoline);
|
||||
}
|
||||
|
||||
|
||||
@@ -577,10 +342,7 @@ bool display_submit(void)
|
||||
ctx.dma_busy = true;
|
||||
|
||||
/* Запускаем передачу scanout-буфера в дисплей. */
|
||||
hw_start_dma(
|
||||
ctx.buffers[ctx.scanout_index],
|
||||
pixels
|
||||
);
|
||||
display_transport_start_frame(ctx.width, ctx.height, ctx.buffers[ctx.scanout_index], pixels);
|
||||
|
||||
/* Запуск принят. */
|
||||
return true;
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
#include "display_driver.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/spi.h"
|
||||
|
||||
#if DISPLAY_TYPE == DISPLAY_TYPE_ST7789
|
||||
|
||||
static inline void display_send_command(uint8_t cmd)
|
||||
{
|
||||
gpio_put(DISPLAY_PIN_DC, 0);
|
||||
gpio_put(DISPLAY_PIN_CS, 0);
|
||||
spi_write_blocking(DISPLAY_SPI_PORT, &cmd, 1);
|
||||
gpio_put(DISPLAY_PIN_CS, 1);
|
||||
}
|
||||
|
||||
static inline void display_send_data_bytes(const uint8_t* data, size_t len)
|
||||
{
|
||||
gpio_put(DISPLAY_PIN_DC, 1);
|
||||
gpio_put(DISPLAY_PIN_CS, 0);
|
||||
spi_write_blocking(DISPLAY_SPI_PORT, data, len);
|
||||
gpio_put(DISPLAY_PIN_CS, 1);
|
||||
}
|
||||
|
||||
static inline void display_send_data_u8(uint8_t data)
|
||||
{
|
||||
display_send_data_bytes(&data, 1);
|
||||
}
|
||||
|
||||
static inline void st7789_set_window(uint16_t width, uint16_t height)
|
||||
{
|
||||
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];
|
||||
|
||||
display_send_command(0x2A); /* CASET */
|
||||
window[0] = 0x00;
|
||||
window[1] = 0x00;
|
||||
window[2] = (uint8_t)(x_end >> 8);
|
||||
window[3] = (uint8_t)x_end;
|
||||
display_send_data_bytes(window, sizeof(window));
|
||||
|
||||
display_send_command(0x2B); /* RASET */
|
||||
window[2] = (uint8_t)(y_end >> 8);
|
||||
window[3] = (uint8_t)y_end;
|
||||
display_send_data_bytes(window, sizeof(window));
|
||||
}
|
||||
|
||||
void display_driver_panel_init(uint16_t width, uint16_t height)
|
||||
{
|
||||
gpio_set_function(DISPLAY_PIN_MOSI, GPIO_FUNC_SPI);
|
||||
gpio_set_function(DISPLAY_PIN_SCK, GPIO_FUNC_SPI);
|
||||
|
||||
gpio_init(DISPLAY_PIN_CS);
|
||||
gpio_init(DISPLAY_PIN_DC);
|
||||
gpio_init(DISPLAY_PIN_RST);
|
||||
gpio_init(DISPLAY_PIN_BL);
|
||||
gpio_set_dir(DISPLAY_PIN_CS, GPIO_OUT);
|
||||
gpio_set_dir(DISPLAY_PIN_DC, GPIO_OUT);
|
||||
gpio_set_dir(DISPLAY_PIN_RST, GPIO_OUT);
|
||||
gpio_set_dir(DISPLAY_PIN_BL, GPIO_OUT);
|
||||
|
||||
gpio_put(DISPLAY_PIN_CS, 1);
|
||||
gpio_put(DISPLAY_PIN_DC, 1);
|
||||
gpio_put(DISPLAY_PIN_BL, 0);
|
||||
|
||||
gpio_put(DISPLAY_PIN_RST, 0);
|
||||
sleep_ms(50);
|
||||
gpio_put(DISPLAY_PIN_RST, 1);
|
||||
sleep_ms(50);
|
||||
|
||||
display_send_command(0x01); /* SWRESET */
|
||||
sleep_ms(150);
|
||||
display_send_command(0x11); /* SLPOUT */
|
||||
sleep_ms(150);
|
||||
|
||||
display_send_command(0x36); /* MADCTL */
|
||||
display_send_data_u8(0b10100000);
|
||||
|
||||
display_send_command(0x3A); /* COLMOD */
|
||||
display_send_data_u8(0x55); /* RGB565 */
|
||||
|
||||
st7789_set_window(width, height);
|
||||
|
||||
display_send_command(0x21); /* INVON */
|
||||
display_send_command(0x29); /* DISPON */
|
||||
gpio_put(DISPLAY_PIN_BL, 1);
|
||||
}
|
||||
|
||||
void display_driver_begin_frame_transfer(uint16_t width, uint16_t height)
|
||||
{
|
||||
st7789_set_window(width, height);
|
||||
display_send_command(0x2C); /* RAMWR */
|
||||
|
||||
gpio_put(DISPLAY_PIN_DC, 1);
|
||||
gpio_put(DISPLAY_PIN_CS, 0);
|
||||
}
|
||||
|
||||
void display_driver_end_frame_transfer(void)
|
||||
{
|
||||
gpio_put(DISPLAY_PIN_CS, 1);
|
||||
}
|
||||
|
||||
#else
|
||||
#error "Unsupported DISPLAY_TYPE. Add implementation in src/core/display_driver.c."
|
||||
#endif
|
||||
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Display type identifiers used by DISPLAY_TYPE. */
|
||||
#define DISPLAY_TYPE_ST7789 1
|
||||
|
||||
#ifndef DISPLAY_TYPE
|
||||
#define DISPLAY_TYPE DISPLAY_TYPE_ST7789
|
||||
#endif
|
||||
|
||||
/* Backward compatibility with old compile definitions from CMake. */
|
||||
#if !defined(DISPLAY_SPI_PORT) && defined(SPI_PORT)
|
||||
#define DISPLAY_SPI_PORT SPI_PORT
|
||||
#endif
|
||||
#ifndef DISPLAY_SPI_PORT
|
||||
#define DISPLAY_SPI_PORT spi0
|
||||
#endif
|
||||
|
||||
#if !defined(DISPLAY_PIN_MOSI) && defined(PIN_MOSI)
|
||||
#define DISPLAY_PIN_MOSI PIN_MOSI
|
||||
#endif
|
||||
#ifndef DISPLAY_PIN_MOSI
|
||||
#define DISPLAY_PIN_MOSI 19
|
||||
#endif
|
||||
|
||||
#if !defined(DISPLAY_PIN_SCK) && defined(PIN_SCK)
|
||||
#define DISPLAY_PIN_SCK PIN_SCK
|
||||
#endif
|
||||
#ifndef DISPLAY_PIN_SCK
|
||||
#define DISPLAY_PIN_SCK 18
|
||||
#endif
|
||||
|
||||
#if !defined(DISPLAY_PIN_CS) && defined(PIN_CS)
|
||||
#define DISPLAY_PIN_CS PIN_CS
|
||||
#endif
|
||||
#ifndef DISPLAY_PIN_CS
|
||||
#define DISPLAY_PIN_CS 17
|
||||
#endif
|
||||
|
||||
#if !defined(DISPLAY_PIN_DC) && defined(PIN_DC)
|
||||
#define DISPLAY_PIN_DC PIN_DC
|
||||
#endif
|
||||
#ifndef DISPLAY_PIN_DC
|
||||
#define DISPLAY_PIN_DC 22
|
||||
#endif
|
||||
|
||||
#if !defined(DISPLAY_PIN_RST) && defined(PIN_RST)
|
||||
#define DISPLAY_PIN_RST PIN_RST
|
||||
#endif
|
||||
#ifndef DISPLAY_PIN_RST
|
||||
#define DISPLAY_PIN_RST 13
|
||||
#endif
|
||||
|
||||
#if !defined(DISPLAY_PIN_BL) && defined(PIN_BL)
|
||||
#define DISPLAY_PIN_BL PIN_BL
|
||||
#endif
|
||||
#ifndef DISPLAY_PIN_BL
|
||||
#define DISPLAY_PIN_BL 12
|
||||
#endif
|
||||
|
||||
/* Display controller abstraction, selected by DISPLAY_TYPE define. */
|
||||
void display_driver_panel_init(uint16_t width, uint16_t height);
|
||||
void display_driver_begin_frame_transfer(uint16_t width, uint16_t height);
|
||||
void display_driver_end_frame_transfer(void);
|
||||
@@ -0,0 +1,59 @@
|
||||
#include "display_transport.h"
|
||||
#include "display_driver.h"
|
||||
|
||||
#include "hardware/spi.h"
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/irq.h"
|
||||
|
||||
static int dma_chan = -1;
|
||||
|
||||
void display_transport_init(uint16_t width, uint16_t height, irq_handler_t dma_irq_handler)
|
||||
{
|
||||
spi_init(DISPLAY_SPI_PORT, 1000 * 100 * 625); /* 62.5 MHz */
|
||||
spi_set_format(
|
||||
DISPLAY_SPI_PORT,
|
||||
8,
|
||||
SPI_CPOL_0,
|
||||
SPI_CPHA_0,
|
||||
SPI_MSB_FIRST
|
||||
);
|
||||
|
||||
display_driver_panel_init(width, height);
|
||||
|
||||
dma_chan = dma_claim_unused_channel(true);
|
||||
dma_channel_config cfg = dma_channel_get_default_config((uint)dma_chan);
|
||||
channel_config_set_transfer_data_size(&cfg, DMA_SIZE_8);
|
||||
channel_config_set_read_increment(&cfg, true);
|
||||
channel_config_set_write_increment(&cfg, false);
|
||||
channel_config_set_dreq(&cfg, spi_get_dreq(DISPLAY_SPI_PORT, true));
|
||||
|
||||
dma_channel_configure(
|
||||
(uint)dma_chan,
|
||||
&cfg,
|
||||
&spi_get_hw(DISPLAY_SPI_PORT)->dr,
|
||||
NULL,
|
||||
0,
|
||||
false
|
||||
);
|
||||
|
||||
dma_channel_set_irq0_enabled((uint)dma_chan, true);
|
||||
irq_set_exclusive_handler(DMA_IRQ_0, dma_irq_handler);
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
}
|
||||
|
||||
void display_transport_start_frame(uint16_t width, uint16_t height, uint16_t* buffer, size_t pixel_count)
|
||||
{
|
||||
display_driver_begin_frame_transfer(width, height);
|
||||
dma_channel_set_read_addr((uint)dma_chan, buffer, false);
|
||||
dma_channel_set_trans_count((uint)dma_chan, pixel_count * sizeof(uint16_t), true);
|
||||
}
|
||||
|
||||
void display_transport_complete_irq(void)
|
||||
{
|
||||
if (dma_chan >= 0)
|
||||
{
|
||||
dma_hw->ints0 = 1u << (uint)dma_chan;
|
||||
}
|
||||
|
||||
display_driver_end_frame_transfer();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "hardware/irq.h"
|
||||
|
||||
/* Initializes transport and panel backend, and registers DMA IRQ callback. */
|
||||
void display_transport_init(uint16_t width, uint16_t height, irq_handler_t dma_irq_handler);
|
||||
|
||||
/* Starts a single frame transfer via panel backend + DMA. */
|
||||
void display_transport_start_frame(uint16_t width, uint16_t height, uint16_t* buffer, size_t pixel_count);
|
||||
|
||||
/* Acknowledges DMA IRQ and finalizes frame SPI transaction. */
|
||||
void display_transport_complete_irq(void);
|
||||
Reference in New Issue
Block a user