diff --git a/src/core/display.c b/src/core/display.c index 645bc7f..ed57d98 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -2,6 +2,10 @@ #include #include #include +#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 */ + /* активное ожидание */ } } @@ -226,4 +390,4 @@ void display_poll(void) ctx.frame_done_cb(); } } -} \ No newline at end of file +}