Add RP2040 8 bit parallel support with DMA
The RP2040 can now be used with 8 bit parallel TFT interface displays. DMA is also supported for both SPI and 8 bit parallel displays.
This commit is contained in:
1 parent
20ad7e2cc1
commit
eee56b2cec
18 files changed
+857
-206
No files matched your search
@@ -646,6 +646,38 @@ void TFT_eSPI::pushPixelsDMA(uint16_t* image, uint32_t len)
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: pushImageDMA
|
||||
** Description: Push image to a window (w*h must be less than 65536)
|
||||
***************************************************************************************/
|
||||
// Fixed const data assumed, will NOT clip or swap bytes
|
||||
void TFT_eSPI::pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t const* image)
|
||||
{
|
||||
if ((w == 0) || (h == 0) || (!DMA_Enabled)) return;
|
||||
|
||||
uint32_t len = w*h;
|
||||
|
||||
dmaWait();
|
||||
|
||||
setAddrWindow(x, y, w, h);
|
||||
|
||||
esp_err_t ret;
|
||||
static spi_transaction_t trans;
|
||||
|
||||
memset(&trans, 0, sizeof(spi_transaction_t));
|
||||
|
||||
trans.user = (void *)1;
|
||||
trans.tx_buffer = image; //Data pointer
|
||||
trans.length = len * 16; //Data length, in bits
|
||||
trans.flags = 0; //SPI_TRANS_USE_TXDATA flag
|
||||
|
||||
ret = spi_device_queue_trans(dmaHAL, &trans, portMAX_DELAY);
|
||||
assert(ret == ESP_OK);
|
||||
|
||||
spiBusyCheck++;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: pushImageDMA
|
||||
** Description: Push image to a window (w*h must be less than 65536)
|
||||
|
||||
+166
-24
@@ -6,7 +6,8 @@
|
||||
// Global variables
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if !defined (TFT_PARALLEL_8_BIT)
|
||||
#if !defined (TFT_PARALLEL_8_BIT) // SPI
|
||||
|
||||
// Select the SPI port and board package to use
|
||||
#ifdef ARDUINO_ARCH_MBED
|
||||
// Arduino RP2040 board package
|
||||
@@ -16,6 +17,26 @@
|
||||
//SPIClass& spi = SPI; // will use board package default pins
|
||||
SPIClassRP2040 spi = SPIClassRP2040(SPI_X, TFT_MISO, -1, TFT_SCLK, TFT_MOSI);
|
||||
#endif
|
||||
|
||||
#else // 8 bit parallel
|
||||
|
||||
#include "pio_8bit_parallel.pio.h"
|
||||
|
||||
// Board package specific differences
|
||||
#ifdef ARDUINO_ARCH_MBED
|
||||
// Not supported at the moment
|
||||
#error The Arduino RP2040 MBED board package is not supported. Use the community package by Earle Philhower.
|
||||
#endif
|
||||
|
||||
// Community RP2040 board package by Earle Philhower
|
||||
PIO pio = pio0; // Code will try both pio's to find a free SM
|
||||
int8_t pio_sm = 0; // pioinit will claim a free one
|
||||
|
||||
// Updated later with the loading offset of the PIO program.
|
||||
uint32_t program_offset = 0;
|
||||
uint32_t pull_stall_mask = 0;
|
||||
uint32_t pio_instr_jmp8 = 0;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef RP2040_DMA
|
||||
@@ -69,10 +90,57 @@ void TFT_eSPI::end_SDA_Read(void)
|
||||
#endif // #if defined (TFT_SDA_READ)
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
#if defined (TFT_PARALLEL_8_BIT)
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
void pioinit(uint8_t tft_d0_pin, uint8_t tft_wr_pin, uint16_t clock_div, uint16_t fract_div) {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
#if defined (TFT_PARALLEL_8_BIT) // Code for generic (i.e. any) processor
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Find a free SM on one of the PIO's
|
||||
pio = pio0;
|
||||
pio_sm = pio_claim_unused_sm(pio, false); // false means don't panic
|
||||
// Try pio1 if SM not found
|
||||
if (pio_sm < 0) {
|
||||
pio = pio1;
|
||||
pio_sm = pio_claim_unused_sm(pio, true); // panic this time if no SM is free
|
||||
}
|
||||
|
||||
// Load the PIO program
|
||||
program_offset = pio_add_program(pio, &tft_io_program);
|
||||
|
||||
// Associate pins with the PIO
|
||||
pio_gpio_init(pio, tft_wr_pin);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
pio_gpio_init(pio, tft_d0_pin + i);
|
||||
}
|
||||
|
||||
// Configure the pins to be outputs
|
||||
pio_sm_set_consecutive_pindirs(pio, pio_sm, tft_wr_pin, 1, true);
|
||||
pio_sm_set_consecutive_pindirs(pio, pio_sm, tft_d0_pin, 8, true);
|
||||
|
||||
// Configure the state machine
|
||||
pio_sm_config c = tft_io_program_get_default_config(program_offset);
|
||||
// Define the single side-set pin
|
||||
sm_config_set_sideset_pins(&c, tft_wr_pin);
|
||||
// Define the 8 consecutive pins that are used for data output
|
||||
sm_config_set_out_pins(&c, tft_d0_pin, 8);
|
||||
// Set clock divider and fractional divider
|
||||
sm_config_set_clkdiv_int_frac(&c, clock_div, fract_div);
|
||||
// Make a single 8 words FIFO from the 4 words TX and RX FIFOs
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
|
||||
// The OSR register shifts to the right, sm designed to send MS byte of a colour first
|
||||
sm_config_set_out_shift(&c, true, false, 0);
|
||||
// Now load the configuration
|
||||
pio_sm_init(pio, pio_sm, program_offset + tft_io_offset_start_8, &c);
|
||||
|
||||
// Start the state machine.
|
||||
pio_sm_set_enabled(pio, pio_sm, true);
|
||||
|
||||
// Create the pull stall bit mask
|
||||
pull_stall_mask = 1u << (PIO_FDEBUG_TXSTALL_LSB + pio_sm);
|
||||
|
||||
// Create the assembler instruction for the jump to byte send routine
|
||||
pio_instr_jmp8 = pio_encode_jmp(program_offset + tft_io_offset_start_8);
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: pushBlock - for generic processor and parallel display
|
||||
@@ -80,25 +148,71 @@ void TFT_eSPI::end_SDA_Read(void)
|
||||
***************************************************************************************/
|
||||
void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){
|
||||
|
||||
while (len>1) {tft_Write_32D(color); len-=2;}
|
||||
if (len) {tft_Write_16(color);}
|
||||
while (len > 4) {
|
||||
// 5 seems to be the optimum for maximum transfer rate
|
||||
WAIT_FOR_FIFO_FREE(5);
|
||||
TX_FIFO = color;
|
||||
TX_FIFO = color;
|
||||
TX_FIFO = color;
|
||||
TX_FIFO = color;
|
||||
TX_FIFO = color;
|
||||
|
||||
len -= 5;
|
||||
}
|
||||
|
||||
if (len) {
|
||||
// There could be a maximum of 4 words left to send
|
||||
WAIT_FOR_FIFO_FREE(4);
|
||||
while (len--) TX_FIFO = color;
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: pushPixels - for gereric processor and parallel display
|
||||
** Function name: pushPixels - for generic processor and parallel display
|
||||
** Description: Write a sequence of pixels
|
||||
***************************************************************************************/
|
||||
void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){
|
||||
|
||||
uint16_t *data = (uint16_t*)data_in;
|
||||
if(_swapBytes) {
|
||||
while (len>1) {tft_Write_16(*data); data++; tft_Write_16(*data); data++; len -=2;}
|
||||
if (len) {tft_Write_16(*data);}
|
||||
return;
|
||||
}
|
||||
const uint16_t *data = (uint16_t*)data_in;
|
||||
|
||||
while (len>1) {tft_Write_16S(*data); data++; tft_Write_16S(*data); data++; len -=2;}
|
||||
if (len) {tft_Write_16S(*data);}
|
||||
// PIO sends MS byte first, so bytes are already swapped on transmit
|
||||
if(_swapBytes) {
|
||||
while (len > 4) {
|
||||
WAIT_FOR_FIFO_FREE(5);
|
||||
TX_FIFO = data[0];
|
||||
TX_FIFO = data[1];
|
||||
TX_FIFO = data[2];
|
||||
TX_FIFO = data[3];
|
||||
TX_FIFO = data[4];
|
||||
data += 5;
|
||||
len -= 5;
|
||||
}
|
||||
|
||||
if (len) {
|
||||
WAIT_FOR_FIFO_FREE(4);
|
||||
while(len--) TX_FIFO = *data++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
while (len > 4) {
|
||||
WAIT_FOR_FIFO_FREE(5);
|
||||
TX_FIFO = data[0] << 8 | data[0] >> 8;
|
||||
TX_FIFO = data[1] << 8 | data[1] >> 8;
|
||||
TX_FIFO = data[2] << 8 | data[2] >> 8;
|
||||
TX_FIFO = data[3] << 8 | data[3] >> 8;
|
||||
TX_FIFO = data[4] << 8 | data[4] >> 8;
|
||||
data += 5;
|
||||
len -= 5;
|
||||
}
|
||||
|
||||
if (len) {
|
||||
WAIT_FOR_FIFO_FREE(4);
|
||||
while(len--) {
|
||||
TX_FIFO = *data << 8 | *data >> 8;
|
||||
data++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
@@ -107,6 +221,10 @@ void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){
|
||||
***************************************************************************************/
|
||||
void TFT_eSPI::busDir(uint32_t mask, uint8_t mode)
|
||||
{
|
||||
// Avoid warnings
|
||||
mask = mask;
|
||||
mode = mode;
|
||||
/*
|
||||
// mask is unused for generic processor
|
||||
// Arduino native functions suited well to a generic driver
|
||||
pinMode(TFT_D0, mode);
|
||||
@@ -117,7 +235,7 @@ void TFT_eSPI::busDir(uint32_t mask, uint8_t mode)
|
||||
pinMode(TFT_D5, mode);
|
||||
pinMode(TFT_D6, mode);
|
||||
pinMode(TFT_D7, mode);
|
||||
return;
|
||||
*/
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
@@ -126,17 +244,20 @@ void TFT_eSPI::busDir(uint32_t mask, uint8_t mode)
|
||||
***************************************************************************************/
|
||||
void TFT_eSPI::gpioMode(uint8_t gpio, uint8_t mode)
|
||||
{
|
||||
// No fast port based generic approach available
|
||||
// Avoid warnings
|
||||
gpio = gpio;
|
||||
mode = mode;
|
||||
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: read byte - supports class functions
|
||||
** Description: Read a byte - parallel bus only
|
||||
** Description: Read a byte - parallel bus only - not supported yet
|
||||
***************************************************************************************/
|
||||
uint8_t TFT_eSPI::readByte(void)
|
||||
{
|
||||
uint8_t b = 0;
|
||||
|
||||
/*
|
||||
busDir(0, INPUT);
|
||||
digitalWrite(TFT_RD, LOW);
|
||||
|
||||
@@ -151,7 +272,7 @@ uint8_t TFT_eSPI::readByte(void)
|
||||
|
||||
digitalWrite(TFT_RD, HIGH);
|
||||
busDir(0, OUTPUT);
|
||||
|
||||
*/
|
||||
return b;
|
||||
}
|
||||
|
||||
@@ -286,11 +407,10 @@ void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
#if defined (RP2040_DMA) && !defined (TFT_PARALLEL_8_BIT) // DMA FUNCTIONS
|
||||
#ifdef RP2040_DMA // DMA functions for 16 bit SPI and 8 bit parallel displays
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
Minimal function set to support DMA:
|
||||
These are created in header file:
|
||||
uint32_t dma_tx_channel;
|
||||
dma_channel_config dma_tx_config;
|
||||
*/
|
||||
@@ -303,8 +423,13 @@ bool TFT_eSPI::dmaBusy(void) {
|
||||
if (!DMA_Enabled) return false;
|
||||
|
||||
if (dma_channel_is_busy(dma_tx_channel)) return true;
|
||||
|
||||
#if !defined (TFT_PARALLEL_8_BIT)
|
||||
// For SPI must also wait for FIFO to flush and reset format
|
||||
while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {};
|
||||
spi_set_format(SPI_X, 16, (spi_cpol_t)0, (spi_cpha_t)0, SPI_MSB_FIRST);
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -315,8 +440,12 @@ bool TFT_eSPI::dmaBusy(void) {
|
||||
void TFT_eSPI::dmaWait(void)
|
||||
{
|
||||
while (dma_channel_is_busy(dma_tx_channel));
|
||||
|
||||
#if !defined (TFT_PARALLEL_8_BIT)
|
||||
// For SPI must also wait for FIFO to flush and reset format
|
||||
while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {};
|
||||
spi_set_format(SPI_X, 16, (spi_cpol_t)0, (spi_cpha_t)0, SPI_MSB_FIRST);
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
@@ -330,7 +459,12 @@ void TFT_eSPI::pushPixelsDMA(uint16_t* image, uint32_t len)
|
||||
dmaWait();
|
||||
|
||||
channel_config_set_bswap(&dma_tx_config, !_swapBytes);
|
||||
|
||||
#if !defined (TFT_PARALLEL_8_BIT)
|
||||
dma_channel_configure(dma_tx_channel, &dma_tx_config, &spi_get_hw(SPI_X)->dr, (uint16_t*)image, len, true);
|
||||
#else
|
||||
dma_channel_configure(dma_tx_channel, &dma_tx_config, &pio->txf[pio_sm], (uint16_t*)image, len, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
@@ -378,8 +512,12 @@ void TFT_eSPI::pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t
|
||||
setAddrWindow(x, y, dw, dh);
|
||||
|
||||
channel_config_set_bswap(&dma_tx_config, !_swapBytes);
|
||||
dma_channel_configure(dma_tx_channel, &dma_tx_config, &spi_get_hw(SPI_X)->dr, (uint16_t*)buffer, len, true);
|
||||
|
||||
#if !defined (TFT_PARALLEL_8_BIT)
|
||||
dma_channel_configure(dma_tx_channel, &dma_tx_config, &spi_get_hw(SPI_X)->dr, (uint16_t*)buffer, len, true);
|
||||
#else
|
||||
dma_channel_configure(dma_tx_channel, &dma_tx_config, &pio->txf[pio_sm], (uint16_t*)buffer, len, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
@@ -396,7 +534,11 @@ bool TFT_eSPI::initDMA(bool ctrl_cs)
|
||||
dma_tx_config = dma_channel_get_default_config(dma_tx_channel);
|
||||
|
||||
channel_config_set_transfer_data_size(&dma_tx_config, DMA_SIZE_16);
|
||||
#if !defined (TFT_PARALLEL_8_BIT)
|
||||
channel_config_set_dreq(&dma_tx_config, spi_get_index(SPI_X) ? DREQ_SPI1_TX : DREQ_SPI0_TX);
|
||||
#else
|
||||
channel_config_set_dreq(&dma_tx_config, pio_get_dreq(pio, pio_sm, true));
|
||||
#endif
|
||||
|
||||
DMA_Enabled = true;
|
||||
return true;
|
||||
|
||||
+227
-127
@@ -10,6 +10,7 @@
|
||||
|
||||
// Required for both the official and community board packages
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/pio.h"
|
||||
|
||||
// Processor ID reported by getSetup()
|
||||
#define PROCESSOR_ID 0x2040
|
||||
@@ -17,23 +18,30 @@
|
||||
// Include processor specific header
|
||||
// None
|
||||
|
||||
// Use SPI0 as default if not defined
|
||||
#ifndef TFT_SPI_PORT
|
||||
#define TFT_SPI_PORT 0
|
||||
#endif
|
||||
|
||||
#if (TFT_SPI_PORT == 0)
|
||||
#define SPI_X spi0
|
||||
#if !defined (TFT_PARALLEL_8_BIT) // SPI
|
||||
// Use SPI0 as default if not defined
|
||||
#ifndef TFT_SPI_PORT
|
||||
#define TFT_SPI_PORT 0
|
||||
#endif
|
||||
|
||||
#if (TFT_SPI_PORT == 0)
|
||||
#define SPI_X spi0
|
||||
#else
|
||||
#define SPI_X spi1
|
||||
#endif
|
||||
|
||||
// Processor specific code used by SPI bus transaction begin/end_tft_write functions
|
||||
#define SET_BUS_WRITE_MODE spi_set_format(SPI_X, 8, (spi_cpol_t)0, (spi_cpha_t)0, SPI_MSB_FIRST)
|
||||
#define SET_BUS_READ_MODE // spi_set_format(SPI_X, 8, (spi_cpol_t)0, (spi_cpha_t)0, SPI_MSB_FIRST)
|
||||
#else
|
||||
#define SPI_X spi1
|
||||
// Processor specific code used by SPI bus transaction begin/end_tft_write functions
|
||||
#define SET_BUS_WRITE_MODE
|
||||
#define SET_BUS_READ_MODE
|
||||
#endif
|
||||
|
||||
// Processor specific code used by SPI bus transaction begin/end_tft_write functions
|
||||
#define SET_BUS_WRITE_MODE spi_set_format(SPI_X, 8, (spi_cpol_t)0, (spi_cpha_t)0, SPI_MSB_FIRST)
|
||||
#define SET_BUS_READ_MODE // spi_set_format(SPI_X, 8, (spi_cpol_t)0, (spi_cpha_t)0, SPI_MSB_FIRST)
|
||||
|
||||
// Code to check if SPI or DMA is busy, used by SPI bus transaction startWrite and/or endWrite functions
|
||||
#if !defined(TFT_PARALLEL_8_BIT) && !defined(SPI_18BIT_DRIVER)
|
||||
#if !defined(SPI_18BIT_DRIVER)
|
||||
#define RP2040_DMA
|
||||
// Code to check if DMA is busy, used by SPI DMA + transaction + endWrite functions
|
||||
#define DMA_BUSY_CHECK dmaWait()
|
||||
@@ -41,18 +49,40 @@
|
||||
#define DMA_BUSY_CHECK
|
||||
#endif
|
||||
|
||||
// Wait for tx to end, flush rx FIFO, clear rx overrun
|
||||
#define SPI_BUSY_CHECK while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; \
|
||||
while (spi_is_readable(SPI_X)) (void)spi_get_hw(SPI_X)->dr; \
|
||||
spi_get_hw(SPI_X)->icr = SPI_SSPICR_RORIC_BITS
|
||||
#if !defined (TFT_PARALLEL_8_BIT) // SPI
|
||||
// Initialise processor specific SPI functions, used by init()
|
||||
#define INIT_TFT_DATA_BUS // Not used
|
||||
|
||||
// Wait for tx to end, flush rx FIFO, clear rx overrun
|
||||
#define SPI_BUSY_CHECK while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; \
|
||||
while (spi_is_readable(SPI_X)) (void)spi_get_hw(SPI_X)->dr; \
|
||||
spi_get_hw(SPI_X)->icr = SPI_SSPICR_RORIC_BITS
|
||||
|
||||
// To be safe, SUPPORT_TRANSACTIONS is assumed mandatory
|
||||
#if !defined (SUPPORT_TRANSACTIONS)
|
||||
#define SUPPORT_TRANSACTIONS
|
||||
#endif
|
||||
#else
|
||||
// Initialise TFT data bus
|
||||
#define INIT_TFT_DATA_BUS
|
||||
|
||||
#define SPI_BUSY_CHECK
|
||||
|
||||
// ILI9481 needs a slower cycle time
|
||||
// Byte rate = (CPU clock/(4 * divider))
|
||||
#ifdef ILI9481_DRIVER
|
||||
#define DIV_UNITS 1
|
||||
#define DIV_FRACT 160
|
||||
#else
|
||||
#define DIV_UNITS 1
|
||||
#define DIV_FRACT 0
|
||||
#endif
|
||||
|
||||
// Set the state machine clock divider (from integer and fractional parts - 16:8)
|
||||
#define CONSTRUCTOR_INIT_TFT_DATA_BUS pioinit(TFT_D0, TFT_WR, DIV_UNITS, DIV_FRACT);
|
||||
|
||||
// To be safe, SUPPORT_TRANSACTIONS is assumed mandatory
|
||||
#if !defined (SUPPORT_TRANSACTIONS)
|
||||
#define SUPPORT_TRANSACTIONS
|
||||
#endif
|
||||
|
||||
// Initialise processor specific SPI functions, used by init()
|
||||
#define INIT_TFT_DATA_BUS // Not used
|
||||
|
||||
// If smooth fonts are enabled the filing system may need to be loaded
|
||||
#ifdef SMOOTH_FONT
|
||||
@@ -68,14 +98,23 @@
|
||||
#define DC_C // No macro allocated so it generates no code
|
||||
#define DC_D // No macro allocated so it generates no code
|
||||
#else
|
||||
//#define DC_C sio_hw->gpio_clr = (1ul << TFT_DC)
|
||||
//#define DC_D sio_hw->gpio_set = (1ul << TFT_DC)
|
||||
#if defined (RPI_DISPLAY_TYPE)
|
||||
#define DC_C digitalWrite(TFT_DC, LOW);
|
||||
#define DC_D digitalWrite(TFT_DC, HIGH);
|
||||
#if !defined (TFT_PARALLEL_8_BIT) // SPI
|
||||
//#define DC_C sio_hw->gpio_clr = (1ul << TFT_DC)
|
||||
//#define DC_D sio_hw->gpio_set = (1ul << TFT_DC)
|
||||
#if defined (RPI_DISPLAY_TYPE)
|
||||
#define DC_C digitalWrite(TFT_DC, LOW);
|
||||
#define DC_D digitalWrite(TFT_DC, HIGH);
|
||||
#else
|
||||
#define DC_C sio_hw->gpio_clr = (1ul << TFT_DC)
|
||||
#define DC_D sio_hw->gpio_set = (1ul << TFT_DC)
|
||||
#endif
|
||||
#else
|
||||
#define DC_C sio_hw->gpio_clr = (1ul << TFT_DC)
|
||||
#define DC_D sio_hw->gpio_set = (1ul << TFT_DC)
|
||||
// Must wait for data to flush through before changing DC line
|
||||
#define DC_C WAIT_FOR_STALL; \
|
||||
sio_hw->gpio_clr = (1ul << TFT_DC)
|
||||
|
||||
// Flush has happened before this and mode changed back to 16 bit
|
||||
#define DC_D sio_hw->gpio_set = (1ul << TFT_DC)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -98,6 +137,7 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Make sure TFT_RD is defined if not used to avoid an error message
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// At the moment read is not supported for parallel mode, tie TFT signal high
|
||||
#ifndef TFT_RD
|
||||
#define TFT_RD -1
|
||||
#endif
|
||||
@@ -105,20 +145,30 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Define the WR (TFT Write) pin drive code
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef TFT_WR
|
||||
#define WR_L digitalWrite(TFT_WR, LOW)
|
||||
#define WR_H digitalWrite(TFT_WR, HIGH)
|
||||
#if !defined (TFT_PARALLEL_8_BIT) // SPI
|
||||
#ifdef TFT_WR
|
||||
#define WR_L digitalWrite(TFT_WR, LOW)
|
||||
#define WR_H digitalWrite(TFT_WR, HIGH)
|
||||
#endif
|
||||
#else
|
||||
// The PIO manages the write line
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Define the touch screen chip select pin drive code
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined TOUCH_CS || (TOUCH_CS < 0)
|
||||
#define T_CS_L // No macro allocated so it generates no code
|
||||
#define T_CS_H // No macro allocated so it generates no code
|
||||
#if !defined (TFT_PARALLEL_8_BIT) // SPI
|
||||
#if !defined TOUCH_CS || (TOUCH_CS < 0)
|
||||
#define T_CS_L // No macro allocated so it generates no code
|
||||
#define T_CS_H // No macro allocated so it generates no code
|
||||
#else
|
||||
#define T_CS_L digitalWrite(TOUCH_CS, LOW)
|
||||
#define T_CS_H digitalWrite(TOUCH_CS, HIGH)
|
||||
#endif
|
||||
#else
|
||||
#define T_CS_L digitalWrite(TOUCH_CS, LOW)
|
||||
#define T_CS_H digitalWrite(TOUCH_CS, HIGH)
|
||||
#ifdef TOUCH_CS
|
||||
#error Touch screen not supported in parallel mode, use a separate library.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -131,113 +181,163 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Macros to write commands/pixel colour data to a SPI ILI948x TFT
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
#if defined (SPI_18BIT_DRIVER) // SPI 18 bit colour
|
||||
#if !defined (TFT_PARALLEL_8_BIT) // SPI
|
||||
|
||||
// Write 8 bits to TFT
|
||||
#define tft_Write_8(C) spi_get_hw(SPI_X)->dr = (uint32_t)(C); \
|
||||
while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; \
|
||||
#if defined (SPI_18BIT_DRIVER) // SPI 18 bit colour
|
||||
|
||||
//#define tft_Write_8(C) spi.transfer(C);
|
||||
#define tft_Write_8N(B) while (!spi_is_writable(SPI_X)){}; \
|
||||
spi_get_hw(SPI_X)->dr = (uint8_t)(B)
|
||||
|
||||
// Convert 16 bit colour to 18 bit and write in 3 bytes
|
||||
#define tft_Write_16(C) tft_Write_8N(((C) & 0xF800)>>8); \
|
||||
tft_Write_8N(((C) & 0x07E0)>>3); \
|
||||
tft_Write_8N(((C) & 0x001F)<<3)
|
||||
|
||||
// Convert 16 bit colour to 18 bit and write in 3 bytes
|
||||
#define tft_Write_16N(C) tft_Write_8N(((C) & 0xF800)>>8); \
|
||||
tft_Write_8N(((C) & 0x07E0)>>3); \
|
||||
tft_Write_8N(((C) & 0x001F)<<3)
|
||||
|
||||
// Convert swapped byte 16 bit colour to 18 bit and write in 3 bytes
|
||||
#define tft_Write_16S(C) tft_Write_8N((C) & 0xF8); \
|
||||
tft_Write_8N(((C) & 0xE000)>>11 | ((C) & 0x07)<<5); \
|
||||
tft_Write_8N(((C) & 0x1F00)>>5)
|
||||
// Write 32 bits to TFT
|
||||
#define tft_Write_32(C) tft_Write_8N(C>>24); \
|
||||
tft_Write_8N(C>>16); \
|
||||
tft_Write_8N(C>>8); \
|
||||
tft_Write_8N(C)
|
||||
|
||||
// Write two address coordinates
|
||||
#define tft_Write_32C(C,D) tft_Write_8N(C>>8); \
|
||||
tft_Write_8N(C); \
|
||||
tft_Write_8N(D>>8); \
|
||||
tft_Write_8N(D)
|
||||
|
||||
// Write same value twice
|
||||
#define tft_Write_32D(C) tft_Write_8N(C>>8); \
|
||||
tft_Write_8N(C); \
|
||||
tft_Write_8N(C>>8); \
|
||||
tft_Write_8N(C)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Macros to write commands/pixel colour data to other displays
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
#else
|
||||
#if defined (RPI_DISPLAY_TYPE) // RPi TFT type always needs 16 bit transfers
|
||||
#define tft_Write_8(C) spi.transfer(C); spi.transfer(C)
|
||||
#define tft_Write_16(C) spi.transfer((uint8_t)((C)>>8));spi.transfer((uint8_t)((C)>>0))
|
||||
#define tft_Write_16N(C) spi.transfer((uint8_t)((C)>>8));spi.transfer((uint8_t)((C)>>0))
|
||||
#define tft_Write_16S(C) spi.transfer((uint8_t)((C)>>0));spi.transfer((uint8_t)((C)>>8))
|
||||
|
||||
#define tft_Write_32(C) \
|
||||
tft_Write_16((uint16_t) ((C)>>16)); \
|
||||
tft_Write_16((uint16_t) ((C)>>0))
|
||||
|
||||
#define tft_Write_32C(C,D) \
|
||||
spi.transfer(0); spi.transfer((C)>>8); \
|
||||
spi.transfer(0); spi.transfer((C)>>0); \
|
||||
spi.transfer(0); spi.transfer((D)>>8); \
|
||||
spi.transfer(0); spi.transfer((D)>>0)
|
||||
|
||||
#define tft_Write_32D(C) \
|
||||
spi.transfer(0); spi.transfer((C)>>8); \
|
||||
spi.transfer(0); spi.transfer((C)>>0); \
|
||||
spi.transfer(0); spi.transfer((C)>>8); \
|
||||
spi.transfer(0); spi.transfer((C)>>0)
|
||||
|
||||
#else
|
||||
|
||||
// This swaps to 8 bit mode, then back to 16 bit mode
|
||||
#define tft_Write_8(C) while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; \
|
||||
spi_set_format(SPI_X, 8, (spi_cpol_t)0, (spi_cpha_t)0, SPI_MSB_FIRST); \
|
||||
spi_get_hw(SPI_X)->dr = (uint32_t)(C); \
|
||||
// Write 8 bits to TFT
|
||||
#define tft_Write_8(C) spi_get_hw(SPI_X)->dr = (uint32_t)(C); \
|
||||
while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; \
|
||||
spi_set_format(SPI_X, 16, (spi_cpol_t)0, (spi_cpha_t)0, SPI_MSB_FIRST)
|
||||
|
||||
// Note: the following macros do not wait for the end of transmission
|
||||
//#define tft_Write_8(C) spi.transfer(C);
|
||||
#define tft_Write_8N(B) while (!spi_is_writable(SPI_X)){}; \
|
||||
spi_get_hw(SPI_X)->dr = (uint8_t)(B)
|
||||
|
||||
#define tft_Write_16(C) while (!spi_is_writable(SPI_X)){}; spi_get_hw(SPI_X)->dr = (uint32_t)(C)
|
||||
// Convert 16 bit colour to 18 bit and write in 3 bytes
|
||||
#define tft_Write_16(C) tft_Write_8N(((C) & 0xF800)>>8); \
|
||||
tft_Write_8N(((C) & 0x07E0)>>3); \
|
||||
tft_Write_8N(((C) & 0x001F)<<3)
|
||||
|
||||
#define tft_Write_16N(C) while (!spi_is_writable(SPI_X)){}; spi_get_hw(SPI_X)->dr = (uint32_t)(C)
|
||||
// Convert 16 bit colour to 18 bit and write in 3 bytes
|
||||
#define tft_Write_16N(C) tft_Write_8N(((C) & 0xF800)>>8); \
|
||||
tft_Write_8N(((C) & 0x07E0)>>3); \
|
||||
tft_Write_8N(((C) & 0x001F)<<3)
|
||||
|
||||
#define tft_Write_16S(C) while (!spi_is_writable(SPI_X)){}; spi_get_hw(SPI_X)->dr = (uint32_t)(C)<<8 | (C)>>8
|
||||
// Convert swapped byte 16 bit colour to 18 bit and write in 3 bytes
|
||||
#define tft_Write_16S(C) tft_Write_8N((C) & 0xF8); \
|
||||
tft_Write_8N(((C) & 0xE000)>>11 | ((C) & 0x07)<<5); \
|
||||
tft_Write_8N(((C) & 0x1F00)>>5)
|
||||
// Write 32 bits to TFT
|
||||
#define tft_Write_32(C) tft_Write_8N(C>>24); \
|
||||
tft_Write_8N(C>>16); \
|
||||
tft_Write_8N(C>>8); \
|
||||
tft_Write_8N(C)
|
||||
|
||||
#define tft_Write_32(C) spi_get_hw(SPI_X)->dr = (uint32_t)((C)>>16); spi_get_hw(SPI_X)->dr = (uint32_t)(C)
|
||||
// Write two address coordinates
|
||||
#define tft_Write_32C(C,D) tft_Write_8N(C>>8); \
|
||||
tft_Write_8N(C); \
|
||||
tft_Write_8N(D>>8); \
|
||||
tft_Write_8N(D)
|
||||
|
||||
#define tft_Write_32C(C,D) spi_get_hw(SPI_X)->dr = (uint32_t)(C); spi_get_hw(SPI_X)->dr = (uint32_t)(D)
|
||||
// Write same value twice
|
||||
#define tft_Write_32D(C) tft_Write_8N(C>>8); \
|
||||
tft_Write_8N(C); \
|
||||
tft_Write_8N(C>>8); \
|
||||
tft_Write_8N(C)
|
||||
|
||||
#define tft_Write_32D(C) spi_get_hw(SPI_X)->dr = (uint32_t)(C); spi_get_hw(SPI_X)->dr = (uint32_t)(C)
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Macros to write commands/pixel colour data to other displays
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
#else
|
||||
#if defined (RPI_DISPLAY_TYPE) // RPi TFT type always needs 16 bit transfers
|
||||
#define tft_Write_8(C) spi.transfer(C); spi.transfer(C)
|
||||
#define tft_Write_16(C) spi.transfer((uint8_t)((C)>>8));spi.transfer((uint8_t)((C)>>0))
|
||||
#define tft_Write_16N(C) spi.transfer((uint8_t)((C)>>8));spi.transfer((uint8_t)((C)>>0))
|
||||
#define tft_Write_16S(C) spi.transfer((uint8_t)((C)>>0));spi.transfer((uint8_t)((C)>>8))
|
||||
|
||||
#define tft_Write_32(C) \
|
||||
tft_Write_16((uint16_t) ((C)>>16)); \
|
||||
tft_Write_16((uint16_t) ((C)>>0))
|
||||
|
||||
#define tft_Write_32C(C,D) \
|
||||
spi.transfer(0); spi.transfer((C)>>8); \
|
||||
spi.transfer(0); spi.transfer((C)>>0); \
|
||||
spi.transfer(0); spi.transfer((D)>>8); \
|
||||
spi.transfer(0); spi.transfer((D)>>0)
|
||||
|
||||
#define tft_Write_32D(C) \
|
||||
spi.transfer(0); spi.transfer((C)>>8); \
|
||||
spi.transfer(0); spi.transfer((C)>>0); \
|
||||
spi.transfer(0); spi.transfer((C)>>8); \
|
||||
spi.transfer(0); spi.transfer((C)>>0)
|
||||
|
||||
#else
|
||||
|
||||
// This swaps to 8 bit mode, then back to 16 bit mode
|
||||
#define tft_Write_8(C) while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; \
|
||||
spi_set_format(SPI_X, 8, (spi_cpol_t)0, (spi_cpha_t)0, SPI_MSB_FIRST); \
|
||||
spi_get_hw(SPI_X)->dr = (uint32_t)(C); \
|
||||
while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; \
|
||||
spi_set_format(SPI_X, 16, (spi_cpol_t)0, (spi_cpha_t)0, SPI_MSB_FIRST)
|
||||
|
||||
// Note: the following macros do not wait for the end of transmission
|
||||
|
||||
#define tft_Write_16(C) while (!spi_is_writable(SPI_X)){}; spi_get_hw(SPI_X)->dr = (uint32_t)(C)
|
||||
|
||||
#define tft_Write_16N(C) while (!spi_is_writable(SPI_X)){}; spi_get_hw(SPI_X)->dr = (uint32_t)(C)
|
||||
|
||||
#define tft_Write_16S(C) while (!spi_is_writable(SPI_X)){}; spi_get_hw(SPI_X)->dr = (uint32_t)(C)<<8 | (C)>>8
|
||||
|
||||
#define tft_Write_32(C) spi_get_hw(SPI_X)->dr = (uint32_t)((C)>>16); spi_get_hw(SPI_X)->dr = (uint32_t)(C)
|
||||
|
||||
#define tft_Write_32C(C,D) spi_get_hw(SPI_X)->dr = (uint32_t)(C); spi_get_hw(SPI_X)->dr = (uint32_t)(D)
|
||||
|
||||
#define tft_Write_32D(C) spi_get_hw(SPI_X)->dr = (uint32_t)(C); spi_get_hw(SPI_X)->dr = (uint32_t)(C)
|
||||
|
||||
#endif // RPI_DISPLAY_TYPE
|
||||
#endif
|
||||
|
||||
#else // Parallel 8 bit
|
||||
|
||||
// On it's own this does not appear to reliably detect a stall, found that we must wait
|
||||
// for FIFO to empty before clearing the stall flag, and then wait for it to be set
|
||||
#define WAIT_FOR_STALL pio->fdebug = pull_stall_mask; while (!(pio->fdebug & pull_stall_mask))
|
||||
|
||||
// Wait until at least "S" locations free
|
||||
#define WAIT_FOR_FIFO_FREE(S) while (((pio->flevel >> (pio_sm * 8)) & 0x000F) > (8-S)){}
|
||||
|
||||
// Wait until at least 5 locations free
|
||||
#define WAIT_FOR_FIFO_5_FREE while ((pio->flevel) & (0x000c << (pio_sm * 8))){}
|
||||
|
||||
// Wait until at least 1 location free
|
||||
#define WAIT_FOR_FIFO_1_FREE while ((pio->flevel) & (0x0008 << (pio_sm * 8))){}
|
||||
|
||||
// Wait for FIFO to empty (use before swapping to 8 bits)
|
||||
#define WAIT_FOR_FIFO_EMPTY while(!(pio->fstat & (1u << (PIO_FSTAT_TXEMPTY_LSB + pio_sm))))
|
||||
|
||||
// The write register of the TX FIFO.
|
||||
#define TX_FIFO pio->txf[pio_sm]
|
||||
|
||||
// Temporary - to be deleted
|
||||
#define dir_mask 0
|
||||
|
||||
|
||||
// This writes 8 bits, then switches back to 16 bit mode automatically
|
||||
// Have already waited for pio stalled (last data write complete) when DC switched to command mode
|
||||
// The wait for stall allows DC to be changed immediately afterwards
|
||||
#define tft_Write_8(C) pio->sm[pio_sm].instr = pio_instr_jmp8; \
|
||||
TX_FIFO = C; \
|
||||
WAIT_FOR_STALL; \
|
||||
|
||||
// Note: the following macros do not wait for the end of transmission
|
||||
|
||||
#define tft_Write_16(C) WAIT_FOR_FIFO_FREE(1); TX_FIFO = (C)
|
||||
|
||||
#define tft_Write_16N(C) WAIT_FOR_FIFO_FREE(1); TX_FIFO = (C)
|
||||
|
||||
#define tft_Write_16S(C) WAIT_FOR_FIFO_FREE(1); TX_FIFO = ((C)<<8) | ((C)>>8)
|
||||
|
||||
#define tft_Write_32(C) WAIT_FOR_FIFO_FREE(2); TX_FIFO = (C)>>16; TX_FIFO = (C)
|
||||
|
||||
#define tft_Write_32C(C,D) WAIT_FOR_FIFO_FREE(2); TX_FIFO = (C); TX_FIFO = (D)
|
||||
|
||||
#define tft_Write_32D(C) WAIT_FOR_FIFO_FREE(2); TX_FIFO = (C); TX_FIFO = (C)
|
||||
|
||||
#endif // RPI_DISPLAY_TYPE
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Macros to read from display using SPI or software SPI
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
#if defined (TFT_SDA_READ)
|
||||
// Use a bit banged function call for STM32 and bi-directional SDA pin
|
||||
#define TFT_eSPI_ENABLE_8_BIT_READ // Enable tft_Read_8(void);
|
||||
#define SCLK_L digitalWrite(TFT_SCLK, LOW)
|
||||
#define SCLK_H digitalWrite(TFT_SCLK, LOW)
|
||||
#else
|
||||
// Use a SPI read transfer
|
||||
#define tft_Read_8() spi.transfer(0)
|
||||
#if !defined (TFT_PARALLEL_8_BIT) // SPI
|
||||
#if defined (TFT_SDA_READ)
|
||||
// Use a bit banged function call for STM32 and bi-directional SDA pin
|
||||
#define TFT_eSPI_ENABLE_8_BIT_READ // Enable tft_Read_8(void);
|
||||
#define SCLK_L digitalWrite(TFT_SCLK, LOW)
|
||||
#define SCLK_H digitalWrite(TFT_SCLK, LOW)
|
||||
#else
|
||||
// Use a SPI read transfer
|
||||
#define tft_Read_8() spi.transfer(0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Temporary to keep the "Arduino Mbed OS RP2040 Boards" support package happy
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
// Raspberry Pi Pico PIO program to output data to a TFT
|
||||
// controller via a 8 bit 8080 style data path.
|
||||
|
||||
// Original sourced from:
|
||||
// https://github.com/zapta/pio_tft
|
||||
|
||||
// Side set: 1 output pin, TFT_WR. Active low.
|
||||
// Data set: 8 consecutive output pins, TFT_D0 .. TFT_D7
|
||||
|
||||
.program tft_io
|
||||
.side_set 1 opt ; The TFT_WR output.
|
||||
|
||||
// The C++ code switches between the 8 bits and 16 bits loops
|
||||
// by waiting for the SM to be idle and setting its PC.
|
||||
//
|
||||
public start_16:
|
||||
// Fetch into OSR the next 32 bit value from the TX FIFO.
|
||||
// This is a blocking operation. Sets WR high.
|
||||
pull side 1
|
||||
// Shift the OSR reg right by 8 bits, loading the low 8 bits
|
||||
// of reg x with the shifted data.
|
||||
out x, 8
|
||||
// Write the first byte (MSB) and sets WR low. This also
|
||||
// shift OSR by 8 bits which we don't care about.
|
||||
out pins, 8 side 0 [1]
|
||||
// Set TFT_WR back high.
|
||||
nop side 1
|
||||
// Move the LSB byte back to the OSR.
|
||||
mov osr, x
|
||||
// Output the second byte and set TFT_WRITE low.
|
||||
out pins, 8 side 0
|
||||
jmp start_16
|
||||
|
||||
// 8 bit transfer
|
||||
public start_8:
|
||||
// Fetch into OSR the next 32 bit value from the TX FIFO.
|
||||
// This is a blocking operation. Sets WR high.
|
||||
pull side 1
|
||||
// Write the first byte (LSB) and sets WR low. This also
|
||||
// shifts the OSR right by 8 bits.
|
||||
out pins, 8 side 0 [1]
|
||||
@@ -0,0 +1,44 @@
|
||||
// -------------------------------------------------- //
|
||||
// This file is autogenerated by pioasm; do not edit! //
|
||||
// -------------------------------------------------- //
|
||||
|
||||
#pragma once
|
||||
|
||||
// ------ //
|
||||
// tft_io //
|
||||
// ------ //
|
||||
|
||||
#define tft_io_wrap_target 0
|
||||
#define tft_io_wrap 8
|
||||
|
||||
#define tft_io_offset_start_16 0u
|
||||
#define tft_io_offset_start_8 7u
|
||||
|
||||
static const uint16_t tft_io_program_instructions[] = {
|
||||
// .wrap_target
|
||||
0x98a0, // 0: pull block side 1
|
||||
0x6028, // 1: out x, 8
|
||||
0x7108, // 2: out pins, 8 side 0 [1]
|
||||
0xb842, // 3: nop side 1
|
||||
0xa0e1, // 4: mov osr, x
|
||||
0x7008, // 5: out pins, 8 side 0
|
||||
0x0000, // 6: jmp 0
|
||||
0x98a0, // 7: pull block side 1
|
||||
0x7108, // 8: out pins, 8 side 0 [1]
|
||||
// .wrap
|
||||
};
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
static const struct pio_program tft_io_program = {
|
||||
.instructions = tft_io_program_instructions,
|
||||
.length = 9,
|
||||
.origin = -1,
|
||||
};
|
||||
|
||||
static inline pio_sm_config tft_io_program_get_default_config(uint offset) {
|
||||
pio_sm_config c = pio_get_default_sm_config();
|
||||
sm_config_set_wrap(&c, offset + tft_io_wrap_target, offset + tft_io_wrap);
|
||||
sm_config_set_sideset(&c, 2, true, false);
|
||||
return c;
|
||||
}
|
||||
#endif
|
||||
Reference in new issue
Block a user