Optimize ST7796 driver: Use bulk SPI transfers and blocking DMA for higher framerate
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user