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:
Bodmer committed 2021-12-18 17:06:16 +00:00
1 parent 20ad7e2cc1
commit eee56b2cec
18 files changed
+857 -206

No files matched your search

+32
View File
@@ -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
View File
@@ -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
View File
@@ -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
////////////////////////////////////////////////////////////////////////////////////////
+41
View File
@@ -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]
+44
View File
@@ -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