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
|
# 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
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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)
|
||||||
*
|
*
|
||||||
@@ -338,6 +343,12 @@ void st7796_init(const struct st7796_config *c, uint16_t w, uint16_t h) {
|
|||||||
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
|
||||||
// Successfully tested at 80 MHz, now trying 100 MHz
|
// Successfully tested at 80 MHz, now trying 100 MHz
|
||||||
@@ -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
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user