Optimize ST7796 driver: Use bulk SPI transfers and blocking DMA for higher framerate

This commit is contained in:
Adolfo Reyna
2026-02-11 11:40:26 -05:00
parent 47fc02f05c
commit b59d716965
4 changed files with 69 additions and 2 deletions
+5 -1
View File
@@ -92,7 +92,11 @@ pico_enable_stdio_usb(basic1 1)
# Add the standard library to the build # Add the standard library to the build
target_link_libraries(basic1 target_link_libraries(basic1
pico_stdlib) pico_stdlib
hardware_dma
hardware_spi
hardware_pwm
)
# Add the standard include files to the build # Add the standard include files to the build
target_include_directories(basic1 PRIVATE target_include_directories(basic1 PRIVATE
+5 -1
View File
@@ -61,7 +61,11 @@ void LowLevelDisplayST7796::draw_buffer(const uint8_t* bit_buffer) {
} }
// Draw entire buffer at once // Draw entire buffer at once
st7796_set_cursor(0, 0); 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() { void LowLevelDisplayST7796::refresh() {
+44
View File
@@ -58,6 +58,7 @@
#include "hardware/gpio.h" #include "hardware/gpio.h"
#include "hardware/spi.h" #include "hardware/spi.h"
#include "hardware/dma.h"
#include "hardware/pwm.h" #include "hardware/pwm.h"
#include "pico/binary_info.h" #include "pico/binary_info.h"
#include "pico/stdlib.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_slice; // PWM slice number for backlight control
static uint pwm_channel; // PWM channel 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) * @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. // only use a portion of it. Adjust these if your display is misaligned.
x_offset = 0; x_offset = 0;
y_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 // Initialize SPI at maximum stable speed for ST7796
// Datasheet says max 15.15 MHz, but modern displays work much faster // 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(); 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 * @brief Draw single pixel at specific coordinates
* *
+15
View File
@@ -233,6 +233,21 @@ void st7796_set_cursor(uint16_t x, uint16_t y);
*/ */
void st7796_write(const uint16_t *data, size_t len); 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 * @brief Draw a single pixel at specified coordinates
* *