diff --git a/CMakeLists.txt b/CMakeLists.txt index 60d01e2..3a424f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,7 +92,11 @@ pico_enable_stdio_usb(basic1 1) # Add the standard library to the build target_link_libraries(basic1 - pico_stdlib) + pico_stdlib + hardware_dma + hardware_spi + hardware_pwm +) # Add the standard include files to the build target_include_directories(basic1 PRIVATE diff --git a/display/low_level_display_st7796.cpp b/display/low_level_display_st7796.cpp index 2e170a5..9799b11 100644 --- a/display/low_level_display_st7796.cpp +++ b/display/low_level_display_st7796.cpp @@ -61,7 +61,11 @@ void LowLevelDisplayST7796::draw_buffer(const uint8_t* bit_buffer) { } // Draw entire buffer at once st7796_set_cursor(0, 0); - st7796_write(rgb_buffer, width * height); + // Use raw write for speed. + // Since we only use 0x0000 (Black) and 0xFFFF (White), endianness doesn't matter. + // 0x0000 -> 0x00, 0x00 (LE) -> Display sees 0x00, 0x00 (0x0000 correct) + // 0xFFFF -> 0xFF, 0xFF (LE) -> Display sees 0xFF, 0xFF (0xFFFF correct) + st7796_write_raw((const uint8_t*)rgb_buffer, width * height * 2); } void LowLevelDisplayST7796::refresh() { diff --git a/lib/st7796/st7796.c b/lib/st7796/st7796.c index 13c052e..5303e06 100644 --- a/lib/st7796/st7796.c +++ b/lib/st7796/st7796.c @@ -58,6 +58,7 @@ #include "hardware/gpio.h" #include "hardware/spi.h" +#include "hardware/dma.h" #include "hardware/pwm.h" #include "pico/binary_info.h" #include "pico/stdlib.h" @@ -123,6 +124,10 @@ static uint8_t current_brightness = 100; // Current brightness level (0-100) static uint pwm_slice; // PWM slice number for backlight control static uint pwm_channel; // PWM channel number for backlight control +// DMA Channel for fast transfers +static int dma_tx_channel = -1; +static dma_channel_config dma_tx_config; + /** * @brief Activate chip select (pull CS LOW) * @@ -337,6 +342,12 @@ void st7796_init(const struct st7796_config *c, uint16_t w, uint16_t h) { // only use a portion of it. Adjust these if your display is misaligned. x_offset = 0; y_offset = 0; + + // Initialize DMA for SPI + dma_tx_channel = dma_claim_unused_channel(true); + dma_tx_config = dma_channel_get_default_config(dma_tx_channel); + channel_config_set_transfer_data_size(&dma_tx_config, DMA_SIZE_8); // 8-bit transfers + channel_config_set_dreq(&dma_tx_config, spi_get_dreq(config->spi, true)); // Sync with SPI TX // Initialize SPI at maximum stable speed for ST7796 // Datasheet says max 15.15 MHz, but modern displays work much faster @@ -551,6 +562,39 @@ void st7796_write(const uint16_t *data, size_t len) { cs_deselect(); } +/** + * @brief Write raw buffer data directly to display + */ +void st7796_write_raw(const uint8_t *data, size_t len) { + dc_data(); + cs_select(); + + // DMA Implementation (Blocking) + // 1. Configure DMA channel to write from buffer to SPI TX register + dma_channel_configure( + dma_tx_channel, + &dma_tx_config, + &spi_get_hw(config->spi)->dr, // Write to SPI TX register + data, // Read from buffer + len, // Element count (bytes) + true // Start immediately + ); + + // 2. Wait for DMA to finish + dma_channel_wait_for_finish_blocking(dma_tx_channel); + + // 3. Wait for SPI Transfer to complete (DMA only fills the FIFO) + // We need to wait for the FIFO to drain and the bus to be idle before raising CS + while (spi_is_busy(config->spi)) { + // tight_loop_contents() is empty on some platforms, + // using a simple volatile read ensures the compiler doesn't optimize this away + // efficiently. + __asm volatile ("nop"); + } + + cs_deselect(); +} + /** * @brief Draw single pixel at specific coordinates * diff --git a/lib/st7796/st7796.h b/lib/st7796/st7796.h index 6dd286c..1a9aad5 100644 --- a/lib/st7796/st7796.h +++ b/lib/st7796/st7796.h @@ -233,6 +233,21 @@ void st7796_set_cursor(uint16_t x, uint16_t y); */ void st7796_write(const uint16_t *data, size_t len); +/** + * @brief Write raw buffer data directly to display + * + * Writes a buffer of bytes directly to the display without any + * conversion or byte swapping. This function assume the data is + * already in the correct format (Big-Endian RGB565) for the display. + * + * This is significantly faster for large block transfers than + * st7796_write() as it uses a single SPI transaction. + * + * @param data Pointer to raw byte buffer + * @param len Number of bytes to send + */ +void st7796_write_raw(const uint8_t *data, size_t len); + /** * @brief Draw a single pixel at specified coordinates *