forked from Bodmer/TFT_eSPI
RP2040: add SPI PIO interface option, enhance 8 bit parallel PIO
The RP2040 processors can now drive 8 bit parallel and SPI displays using the PIO hardware. The PIO offloads the processor by providing: 1. PIO managed setWindow sequence 2. PIO managed block and screen fill
This commit is contained in:
@@ -322,7 +322,7 @@ SPI3_HOST = 2
|
||||
|
||||
// Create a bit set lookup table for data bus - wastes 1kbyte of RAM but speeds things up dramatically
|
||||
// can then use e.g. GPIO.out_w1ts = set_mask(0xFF); to set data bus to 0xFF
|
||||
#define CONSTRUCTOR_INIT_TFT_DATA_BUS \
|
||||
#define PARALLEL_INIT_TFT_DATA_BUS \
|
||||
for (int32_t c = 0; c<256; c++) \
|
||||
{ \
|
||||
xset_mask[c] = 0; \
|
||||
|
||||
+124
-27
@@ -6,7 +6,7 @@
|
||||
// Global variables
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if !defined (TFT_PARALLEL_8_BIT) // SPI
|
||||
#if !defined (RP2040_PIO_INTERFACE) // SPI
|
||||
|
||||
// Select the SPI port and board package to use
|
||||
#ifdef ARDUINO_ARCH_MBED
|
||||
@@ -18,9 +18,13 @@
|
||||
SPIClassRP2040 spi = SPIClassRP2040(SPI_X, TFT_MISO, -1, TFT_SCLK, TFT_MOSI);
|
||||
#endif
|
||||
|
||||
#else // 8 bit parallel
|
||||
#else // PIO interface used (8 bit parallel or SPI)
|
||||
|
||||
#include "pio_8bit_parallel.pio.h"
|
||||
#ifdef RP2040_PIO_SPI
|
||||
#include "pio_SPI.pio.h"
|
||||
#else
|
||||
#include "pio_8bit_parallel.pio.h"
|
||||
#endif
|
||||
|
||||
// Board package specific differences
|
||||
#ifdef ARDUINO_ARCH_MBED
|
||||
@@ -31,11 +35,20 @@
|
||||
// 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;
|
||||
|
||||
// SM stalled mask
|
||||
uint32_t pull_stall_mask = 0;
|
||||
|
||||
// SM jump instructions to change SM behaviour
|
||||
uint32_t pio_instr_jmp8 = 0;
|
||||
uint32_t pio_instr_fill = 0;
|
||||
uint32_t pio_instr_addr = 0;
|
||||
|
||||
// SM "set" instructions to control DC control signal
|
||||
uint32_t pio_instr_set_dc = 0;
|
||||
uint32_t pio_instr_clr_dc = 0;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -45,7 +58,7 @@
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
#if defined (TFT_SDA_READ) && !defined (TFT_PARALLEL_8_BIT)
|
||||
#if defined (TFT_SDA_READ) && !defined (RP2040_PIO_INTERFACE)
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/***************************************************************************************
|
||||
@@ -91,9 +104,10 @@ void TFT_eSPI::end_SDA_Read(void)
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
#if defined (TFT_PARALLEL_8_BIT)
|
||||
#if defined (RP2040_PIO_INTERFACE)
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
void pioinit(uint8_t tft_d0_pin, uint8_t tft_wr_pin, uint16_t clock_div, uint16_t fract_div) {
|
||||
#ifdef RP2040_PIO_SPI
|
||||
void pioinit(uint32_t clock_freq) {
|
||||
|
||||
// Find a free SM on one of the PIO's
|
||||
pio = pio0;
|
||||
@@ -108,29 +122,32 @@ void pioinit(uint8_t tft_d0_pin, uint8_t tft_wr_pin, uint16_t clock_div, uint16_
|
||||
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);
|
||||
}
|
||||
pio_gpio_init(pio, TFT_DC);
|
||||
pio_gpio_init(pio, TFT_SCLK);
|
||||
pio_gpio_init(pio, TFT_MOSI);
|
||||
|
||||
// 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);
|
||||
pio_sm_set_consecutive_pindirs(pio, pio_sm, TFT_DC, 1, true);
|
||||
pio_sm_set_consecutive_pindirs(pio, pio_sm, TFT_SCLK, 1, true);
|
||||
pio_sm_set_consecutive_pindirs(pio, pio_sm, TFT_MOSI, 1, true);
|
||||
|
||||
// Configure the state machine
|
||||
pio_sm_config c = tft_io_program_get_default_config(program_offset);
|
||||
|
||||
sm_config_set_set_pins(&c, TFT_DC, 1);
|
||||
// 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);
|
||||
sm_config_set_sideset_pins(&c, TFT_SCLK);
|
||||
// Define the pin used for data output
|
||||
sm_config_set_out_pins(&c, TFT_MOSI, 1);
|
||||
// Set clock divider, frequency is set up to 2% faster than specified, or next division down
|
||||
uint16_t clock_div = 0.98 + clock_get_hz(clk_sys) / (clock_freq * 2.0); // 2 cycles per bit
|
||||
sm_config_set_clkdiv(&c, clock_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);
|
||||
// The OSR register shifts to the left, sm designed to send MS byte of a colour first, autopull off
|
||||
sm_config_set_out_shift(&c, false, false, 0);
|
||||
// Now load the configuration
|
||||
pio_sm_init(pio, pio_sm, program_offset + tft_io_offset_start_8, &c);
|
||||
pio_sm_init(pio, pio_sm, program_offset + tft_io_offset_start_16, &c);
|
||||
|
||||
// Start the state machine.
|
||||
pio_sm_set_enabled(pio, pio_sm, true);
|
||||
@@ -140,12 +157,91 @@ void pioinit(uint8_t tft_d0_pin, uint8_t tft_wr_pin, uint16_t clock_div, uint16_
|
||||
|
||||
// Create the assembler instruction for the jump to byte send routine
|
||||
pio_instr_jmp8 = pio_encode_jmp(program_offset + tft_io_offset_start_8);
|
||||
pio_instr_fill = pio_encode_jmp(program_offset + tft_io_offset_block_fill);
|
||||
pio_instr_addr = pio_encode_jmp(program_offset + tft_io_offset_set_addr_window);
|
||||
|
||||
pio_instr_set_dc = pio_encode_set((pio_src_dest)0, 1);
|
||||
pio_instr_clr_dc = pio_encode_set((pio_src_dest)0, 0);
|
||||
}
|
||||
#else
|
||||
void pioinit(uint16_t clock_div, uint16_t fract_div) {
|
||||
|
||||
// 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_DC);
|
||||
pio_gpio_init(pio, TFT_WR);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
pio_gpio_init(pio, TFT_D0 + i);
|
||||
}
|
||||
|
||||
// Configure the pins to be outputs
|
||||
pio_sm_set_consecutive_pindirs(pio, pio_sm, TFT_DC, 1, true);
|
||||
pio_sm_set_consecutive_pindirs(pio, pio_sm, TFT_WR, 1, true);
|
||||
pio_sm_set_consecutive_pindirs(pio, pio_sm, TFT_D0, 8, true);
|
||||
|
||||
// Configure the state machine
|
||||
pio_sm_config c = tft_io_program_get_default_config(program_offset);
|
||||
// Define the set pin
|
||||
sm_config_set_set_pins(&c, TFT_DC, 1);
|
||||
// Define the single side-set pin
|
||||
sm_config_set_sideset_pins(&c, TFT_WR);
|
||||
// Define the 8 consecutive pins that are used for data output
|
||||
sm_config_set_out_pins(&c, TFT_D0, 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 left, sm designed to send MS byte of a colour first
|
||||
sm_config_set_out_shift(&c, false, false, 0);
|
||||
// Now load the configuration
|
||||
pio_sm_init(pio, pio_sm, program_offset + tft_io_offset_start_16, &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);
|
||||
//pio_instr_jmp32 = pio_encode_jmp(program_offset + tft_io_offset_start_32);
|
||||
pio_instr_fill = pio_encode_jmp(program_offset + tft_io_offset_block_fill);
|
||||
pio_instr_addr = pio_encode_jmp(program_offset + tft_io_offset_set_addr_window);
|
||||
|
||||
pio_instr_set_dc = pio_encode_set((pio_src_dest)0, 1);
|
||||
pio_instr_clr_dc = pio_encode_set((pio_src_dest)0, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: pushBlock - for generic processor and parallel display
|
||||
** Description: Write a block of pixels of the same colour
|
||||
***************************************************************************************/
|
||||
#ifdef RP2040_PIO_PUSHBLOCK
|
||||
// PIO handles pixel block fill writes
|
||||
void TFT_eSPI::pushBlock(uint16_t color, uint32_t len)
|
||||
{
|
||||
if (len) {
|
||||
WAIT_FOR_STALL;
|
||||
pio->sm[pio_sm].instr = pio_instr_fill;
|
||||
|
||||
TX_FIFO = color;
|
||||
TX_FIFO = --len; // Decrement first as PIO sends n+1
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){
|
||||
|
||||
while (len > 4) {
|
||||
@@ -166,6 +262,7 @@ void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){
|
||||
while (len--) TX_FIFO = color;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: pushPixels - for generic processor and parallel display
|
||||
@@ -424,7 +521,7 @@ bool TFT_eSPI::dmaBusy(void) {
|
||||
|
||||
if (dma_channel_is_busy(dma_tx_channel)) return true;
|
||||
|
||||
#if !defined (TFT_PARALLEL_8_BIT)
|
||||
#if !defined (RP2040_PIO_INTERFACE)
|
||||
// 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);
|
||||
@@ -441,7 +538,7 @@ void TFT_eSPI::dmaWait(void)
|
||||
{
|
||||
while (dma_channel_is_busy(dma_tx_channel));
|
||||
|
||||
#if !defined (TFT_PARALLEL_8_BIT)
|
||||
#if !defined (RP2040_PIO_INTERFACE)
|
||||
// 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);
|
||||
@@ -460,7 +557,7 @@ void TFT_eSPI::pushPixelsDMA(uint16_t* image, uint32_t len)
|
||||
|
||||
channel_config_set_bswap(&dma_tx_config, !_swapBytes);
|
||||
|
||||
#if !defined (TFT_PARALLEL_8_BIT)
|
||||
#if !defined (RP2040_PIO_INTERFACE)
|
||||
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);
|
||||
@@ -513,7 +610,7 @@ void TFT_eSPI::pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t
|
||||
|
||||
channel_config_set_bswap(&dma_tx_config, !_swapBytes);
|
||||
|
||||
#if !defined (TFT_PARALLEL_8_BIT)
|
||||
#if !defined (RP2040_PIO_INTERFACE)
|
||||
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);
|
||||
@@ -534,8 +631,8 @@ 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);
|
||||
#if !defined (RP2040_PIO_INTERFACE)
|
||||
channel_config_set_dreq(&dma_tx_config, spi_get_dreq(SPI_X, true));
|
||||
#else
|
||||
channel_config_set_dreq(&dma_tx_config, pio_get_dreq(pio, pio_sm, true));
|
||||
#endif
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
// Required for both the official and community board packages
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/pio.h"
|
||||
#include "hardware/clocks.h"
|
||||
|
||||
// Processor ID reported by getSetup()
|
||||
#define PROCESSOR_ID 0x2040
|
||||
@@ -18,8 +19,12 @@
|
||||
// Include processor specific header
|
||||
// None
|
||||
|
||||
#if defined (TFT_PARALLEL_8_BIT) || defined (RP2040_PIO_SPI)
|
||||
#define RP2040_PIO_INTERFACE
|
||||
#define RP2040_PIO_PUSHBLOCK
|
||||
#endif
|
||||
|
||||
#if !defined (TFT_PARALLEL_8_BIT) // SPI
|
||||
#if !defined (RP2040_PIO_INTERFACE)// SPI
|
||||
// Use SPI0 as default if not defined
|
||||
#ifndef TFT_SPI_PORT
|
||||
#define TFT_SPI_PORT 0
|
||||
@@ -49,7 +54,7 @@
|
||||
#define DMA_BUSY_CHECK
|
||||
#endif
|
||||
|
||||
#if !defined (TFT_PARALLEL_8_BIT) // SPI
|
||||
#if !defined (RP2040_PIO_INTERFACE) // SPI
|
||||
// Initialise processor specific SPI functions, used by init()
|
||||
#define INIT_TFT_DATA_BUS // Not used
|
||||
|
||||
@@ -63,10 +68,6 @@
|
||||
#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))
|
||||
@@ -78,8 +79,17 @@
|
||||
#define DIV_FRACT 0
|
||||
#endif
|
||||
|
||||
// Initialise TFT data bus
|
||||
#if defined (TFT_PARALLEL_8_BIT)
|
||||
#define INIT_TFT_DATA_BUS pioinit(DIV_UNITS, DIV_FRACT);
|
||||
#elif defined (RP2040_PIO_SPI)
|
||||
#define INIT_TFT_DATA_BUS pioinit(SPI_FREQUENCY);
|
||||
#endif
|
||||
|
||||
#define SPI_BUSY_CHECK
|
||||
|
||||
// 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);
|
||||
#define PARALLEL_INIT_TFT_DATA_BUS // Not used
|
||||
|
||||
#endif
|
||||
|
||||
@@ -98,7 +108,7 @@
|
||||
#define DC_C // No macro allocated so it generates no code
|
||||
#define DC_D // No macro allocated so it generates no code
|
||||
#else
|
||||
#if !defined (TFT_PARALLEL_8_BIT) // SPI
|
||||
#if !defined (RP2040_PIO_INTERFACE)// 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)
|
||||
@@ -109,12 +119,13 @@
|
||||
#define DC_D sio_hw->gpio_set = (1ul << TFT_DC)
|
||||
#endif
|
||||
#else
|
||||
// PIO takes control of 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)
|
||||
pio->sm[pio_sm].instr = pio_instr_clr_dc
|
||||
|
||||
// Flush has happened before this and mode changed back to 16 bit
|
||||
#define DC_D sio_hw->gpio_set = (1ul << TFT_DC)
|
||||
#define DC_D pio->sm[pio_sm].instr = pio_instr_set_dc
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -125,12 +136,17 @@
|
||||
#define CS_L // No macro allocated so it generates no code
|
||||
#define CS_H // No macro allocated so it generates no code
|
||||
#else
|
||||
#if defined (RPI_DISPLAY_TYPE)
|
||||
#define CS_L digitalWrite(TFT_CS, LOW);
|
||||
#define CS_H digitalWrite(TFT_CS, HIGH);
|
||||
#else
|
||||
#if !defined (RP2040_PIO_INTERFACE) // SPI
|
||||
#if defined (RPI_DISPLAY_TYPE)
|
||||
#define CS_L digitalWrite(TFT_CS, LOW);
|
||||
#define CS_H digitalWrite(TFT_CS, HIGH);
|
||||
#else
|
||||
#define CS_L sio_hw->gpio_clr = (1ul << TFT_CS)
|
||||
#define CS_H sio_hw->gpio_set = (1ul << TFT_CS)
|
||||
#endif
|
||||
#else // PIO interface display
|
||||
#define CS_L sio_hw->gpio_clr = (1ul << TFT_CS)
|
||||
#define CS_H sio_hw->gpio_set = (1ul << TFT_CS)
|
||||
#define CS_H WAIT_FOR_STALL; sio_hw->gpio_set = (1ul << TFT_CS)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -157,7 +173,7 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Define the touch screen chip select pin drive code
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined (TFT_PARALLEL_8_BIT) // SPI
|
||||
#if !defined (RP2040_PIO_INTERFACE)// 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
|
||||
@@ -181,7 +197,7 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Macros to write commands/pixel colour data to a SPI ILI948x TFT
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined (TFT_PARALLEL_8_BIT) // SPI
|
||||
#if !defined (RP2040_PIO_INTERFACE) // SPI
|
||||
|
||||
#if defined (SPI_18BIT_DRIVER) // SPI 18 bit colour
|
||||
|
||||
@@ -277,10 +293,10 @@
|
||||
#endif // RPI_DISPLAY_TYPE
|
||||
#endif
|
||||
|
||||
#else // Parallel 8 bit
|
||||
#else // Parallel 8 bit or PIO SPI
|
||||
|
||||
// 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
|
||||
// Wait for the PIO to stall (SM pull request finds no data in TX FIFO)
|
||||
// This is used to detect when the SM is idle and hence ready for a jump instruction
|
||||
#define WAIT_FOR_STALL pio->fdebug = pull_stall_mask; while (!(pio->fdebug & pull_stall_mask))
|
||||
|
||||
// Wait until at least "S" locations free
|
||||
@@ -306,8 +322,8 @@
|
||||
// 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; \
|
||||
TX_FIFO = (C); \
|
||||
WAIT_FOR_STALL
|
||||
|
||||
// Note: the following macros do not wait for the end of transmission
|
||||
|
||||
@@ -317,7 +333,7 @@
|
||||
|
||||
#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_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)
|
||||
|
||||
@@ -327,7 +343,7 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Macros to read from display using SPI or software SPI
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined (TFT_PARALLEL_8_BIT) // SPI
|
||||
#if !defined (RP2040_PIO_INTERFACE)// 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);
|
||||
|
||||
@@ -308,7 +308,7 @@
|
||||
// Mask for the 8 data bits to set pin directions (not used)
|
||||
#define dir_mask 0
|
||||
|
||||
#define CONSTRUCTOR_INIT_TFT_DATA_BUS // None
|
||||
#define PARALLEL_INIT_TFT_DATA_BUS // None
|
||||
|
||||
#define INIT_TFT_DATA_BUS // Setup built into TFT_eSPI.cpp
|
||||
|
||||
|
||||
@@ -10,32 +10,93 @@
|
||||
.program tft_io
|
||||
.side_set 1 opt ; The TFT_WR output.
|
||||
|
||||
// The C++ code switches between the 8 bits and 16 bits loops
|
||||
// The C++ code switches between the different SM routines
|
||||
// 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
|
||||
// The default SM routine is a 16 bit transfer
|
||||
|
||||
// 8 bit transfer
|
||||
public start_32:
|
||||
// Fetch the next 32 bit value from the TX FIFO and set TFT_WR high.
|
||||
pull side 1
|
||||
// Output byte, TFT_WR low.
|
||||
out pins, 8 side 0 [1]
|
||||
// Loop until 4 bytes sent, TFT_WR high.
|
||||
jmp !osre, send_xy side 1 [1]
|
||||
//Jump back to 16 bit
|
||||
jmp start_16
|
||||
|
||||
// Do a block fill of N+1 pixels.
|
||||
public block_fill:
|
||||
// Fetch colour value.
|
||||
pull side 1
|
||||
// Move colour to x.
|
||||
mov x, osr
|
||||
// Fetch pixel count N (sends N+1 pixels).
|
||||
pull
|
||||
// Move pixel count to y.
|
||||
mov y, osr
|
||||
next:
|
||||
// Copy colour value into osr, colour in LS 16 bits.
|
||||
mov osr, x side 1
|
||||
// Output colour 8 MS bits, unwanted top 16 bits shifted through.
|
||||
out pins, 24 side 0 [1]
|
||||
// Write first colour byte.
|
||||
nop side 1 [1]
|
||||
// Write second colour byte.
|
||||
out pins, 8 side 0 [1]
|
||||
// Decrement pixel count and loop.
|
||||
jmp y--, next side 1
|
||||
|
||||
.wrap_target
|
||||
// Transmit a 16 bit value (LS 16 bits of 32 bits).
|
||||
public start_16:
|
||||
// Fetch the next 32 bit value from the TX FIFO and set TFT_WR high.
|
||||
pull side 1
|
||||
// Write the first byte (MSB) and set WR low. This also
|
||||
// shifts the unused top 16 bits through.
|
||||
out pins, 24 side 0 [1]
|
||||
// Set WR high and delay to next byte.
|
||||
nop side 1 [1]
|
||||
// Output the second byte and set TFT_WRITE low.
|
||||
out pins, 8 side 0 [1]
|
||||
// Set WR high and jump back to start.
|
||||
jmp start_16 side 1
|
||||
|
||||
// Transmit an 8 bit value (LS 8 bits of 32 bits).
|
||||
public start_8:
|
||||
// Fetch into OSR the next 32 bit value from the TX FIFO.
|
||||
// This is a blocking operation. Sets WR high.
|
||||
// Fetch the next 32 bit value from the TX FIFO and set TFT_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]
|
||||
// shifts the unused top 24 bits through.
|
||||
out pins, 32 side 0 [1]
|
||||
// Jump to start
|
||||
jmp start_16 side 1
|
||||
|
||||
// Transmit a set window command sequence.
|
||||
public set_addr_window:
|
||||
// Loop count in x (to send caset, paset and ramwr commands).
|
||||
set x, 2 side 1
|
||||
pull_cmd:
|
||||
// Set TFT_DC low.
|
||||
set pins, 0
|
||||
// Fetch caset, paset or ramwr.
|
||||
pull
|
||||
// Output LS byte (caset, paset or ramwr), discarding top 24 bits, set TFT_WR low.
|
||||
out pins, 32 side 0
|
||||
// Jump to end if 3rd cmd byte ramwr sent (x == 0)
|
||||
jmp !x, end_set_addr
|
||||
// pull next start and end coordinates, TFT_WR high.
|
||||
pull side 1
|
||||
// Set TFT_DC high.
|
||||
set pins, 1
|
||||
send_xy:
|
||||
// Output byte, TFT_WR low.
|
||||
out pins, 8 side 0 [1]
|
||||
// Loop until 4 bytes sent, TFT_WR high.
|
||||
jmp !osre, send_xy side 1 [1]
|
||||
end_set_addr:
|
||||
// Loop back for next command and write last command.
|
||||
jmp x--, pull_cmd side 1
|
||||
// Set DC high.
|
||||
set pins, 1
|
||||
// Auto-wrap back to start_16.
|
||||
.wrap
|
||||
@@ -4,34 +4,64 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
#include "hardware/pio.h"
|
||||
#endif
|
||||
|
||||
// ------ //
|
||||
// tft_io //
|
||||
// ------ //
|
||||
|
||||
#define tft_io_wrap_target 0
|
||||
#define tft_io_wrap 8
|
||||
#define tft_io_wrap_target 13
|
||||
#define tft_io_wrap 31
|
||||
|
||||
#define tft_io_offset_start_16 0u
|
||||
#define tft_io_offset_start_8 7u
|
||||
#define tft_io_offset_start_32 0u
|
||||
#define tft_io_offset_block_fill 4u
|
||||
#define tft_io_offset_start_16 13u
|
||||
#define tft_io_offset_start_8 18u
|
||||
#define tft_io_offset_set_addr_window 21u
|
||||
|
||||
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]
|
||||
0x7108, // 1: out pins, 8 side 0 [1]
|
||||
0x19fc, // 2: jmp !osre, 28 side 1 [1]
|
||||
0x000d, // 3: jmp 13
|
||||
0x98a0, // 4: pull block side 1
|
||||
0xa027, // 5: mov x, osr
|
||||
0x80a0, // 6: pull block
|
||||
0xa047, // 7: mov y, osr
|
||||
0xb8e1, // 8: mov osr, x side 1
|
||||
0x7118, // 9: out pins, 24 side 0 [1]
|
||||
0xb942, // 10: nop side 1 [1]
|
||||
0x7108, // 11: out pins, 8 side 0 [1]
|
||||
0x1888, // 12: jmp y--, 8 side 1
|
||||
// .wrap_target
|
||||
0x98a0, // 13: pull block side 1
|
||||
0x7118, // 14: out pins, 24 side 0 [1]
|
||||
0xb942, // 15: nop side 1 [1]
|
||||
0x7108, // 16: out pins, 8 side 0 [1]
|
||||
0x180d, // 17: jmp 13 side 1
|
||||
0x98a0, // 18: pull block side 1
|
||||
0x7100, // 19: out pins, 32 side 0 [1]
|
||||
0x180d, // 20: jmp 13 side 1
|
||||
0xf822, // 21: set x, 2 side 1
|
||||
0xe000, // 22: set pins, 0
|
||||
0x80a0, // 23: pull block
|
||||
0x7000, // 24: out pins, 32 side 0
|
||||
0x003e, // 25: jmp !x, 30
|
||||
0x98a0, // 26: pull block side 1
|
||||
0xe001, // 27: set pins, 1
|
||||
0x7108, // 28: out pins, 8 side 0 [1]
|
||||
0x19fc, // 29: jmp !osre, 28 side 1 [1]
|
||||
0x1856, // 30: jmp x--, 22 side 1
|
||||
0xe001, // 31: set pins, 1
|
||||
// .wrap
|
||||
};
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
static const struct pio_program tft_io_program = {
|
||||
.instructions = tft_io_program_instructions,
|
||||
.length = 9,
|
||||
.length = 32,
|
||||
.origin = -1,
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
// Raspberry Pi Pico PIO program to output data to a TFT
|
||||
// controller via a SPI output data path.
|
||||
|
||||
//"Set" set: 1 output pin, TFT_DC
|
||||
// Side set: 1 output pin, TFT_SCLK
|
||||
// Data set: 1 output pin, TFT_MOSI
|
||||
|
||||
.program tft_io
|
||||
.side_set 1 opt ; The TFT_SCLK 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.
|
||||
//
|
||||
|
||||
// 8 bit transfer
|
||||
public start_8:
|
||||
// Pull the next 32 bit value from the TX FIFO.
|
||||
// Lose the top 24 bits
|
||||
pull side 0
|
||||
out null, 24
|
||||
spi_out_8:
|
||||
// Output the next 8 bits
|
||||
out pins, 1 side 0
|
||||
// Set TFT_SCLK high and jump for next bit
|
||||
jmp !osre, spi_out_8 side 1
|
||||
// Return to start
|
||||
jmp start_16 side 0
|
||||
|
||||
public set_addr_window:
|
||||
// Loop count in x for caset, paset and ramwr
|
||||
set x, 2 side 0
|
||||
pull_cmd:
|
||||
// Set DC low
|
||||
set pins, 0
|
||||
// Fetch and output LS byte (caset, paset or ramwr), discarding top 24 bits, set WR low
|
||||
pull side 0
|
||||
out null, 24
|
||||
next_cmd_bit:
|
||||
out pins, 1 side 0
|
||||
jmp !osre, next_cmd_bit side 1
|
||||
// Set DC high
|
||||
set pins, 1 side 0
|
||||
// Finish if 3rd cmd byte ramwr sent (x == 0)
|
||||
jmp !x, start_16
|
||||
pull
|
||||
next_xy:
|
||||
// send 32 bit start and end coordinates
|
||||
out pins, 1 side 0
|
||||
jmp !osre, next_xy side 1
|
||||
// Loop back for next command
|
||||
jmp x--, pull_cmd side 0
|
||||
// End
|
||||
jmp start_16
|
||||
|
||||
public block_fill:
|
||||
// Fetch colour value
|
||||
pull side 0
|
||||
// Move colour to x
|
||||
mov x, osr
|
||||
// Fetch pixel count
|
||||
pull
|
||||
// Move pixel count to y
|
||||
mov y, osr
|
||||
next_16:
|
||||
// Copy colour value back into osr
|
||||
mov osr, x side 0
|
||||
// Lose the top 16 bits, send 1st bit
|
||||
out pins, 17 side 0
|
||||
nop side 1
|
||||
next_bit:
|
||||
// Output next 15 colour bits
|
||||
out pins, 1 side 0
|
||||
// Set TFT_SCLK high and jump for next bit
|
||||
jmp !osre, next_bit side 1
|
||||
// Decrement count and loop
|
||||
jmp y--, next_16 side 0
|
||||
// Now drop back to 16 bit output
|
||||
|
||||
.wrap_target
|
||||
public start_16:
|
||||
// Pull the next 32 bit value from the TX FIFO.
|
||||
// Write the top 16 bits
|
||||
pull side 0
|
||||
out null, 16
|
||||
spi_out_16:
|
||||
// Output the next 16 bits
|
||||
out pins, 1 side 0
|
||||
// Set TFT_SCLK high and jump for next bit
|
||||
jmp !osre, spi_out_16 side 1
|
||||
// Return to start
|
||||
.wrap
|
||||
@@ -0,0 +1,73 @@
|
||||
// -------------------------------------------------- //
|
||||
// This file is autogenerated by pioasm; do not edit! //
|
||||
// -------------------------------------------------- //
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
#include "hardware/pio.h"
|
||||
#endif
|
||||
|
||||
// ------ //
|
||||
// tft_io //
|
||||
// ------ //
|
||||
|
||||
#define tft_io_wrap_target 28
|
||||
#define tft_io_wrap 31
|
||||
|
||||
#define tft_io_offset_start_8 0u
|
||||
#define tft_io_offset_set_addr_window 5u
|
||||
#define tft_io_offset_block_fill 18u
|
||||
#define tft_io_offset_start_16 28u
|
||||
|
||||
static const uint16_t tft_io_program_instructions[] = {
|
||||
0x90a0, // 0: pull block side 0
|
||||
0x6078, // 1: out null, 24
|
||||
0x7001, // 2: out pins, 1 side 0
|
||||
0x18e2, // 3: jmp !osre, 2 side 1
|
||||
0x101c, // 4: jmp 28 side 0
|
||||
0xf022, // 5: set x, 2 side 0
|
||||
0xe000, // 6: set pins, 0
|
||||
0x90a0, // 7: pull block side 0
|
||||
0x6078, // 8: out null, 24
|
||||
0x7001, // 9: out pins, 1 side 0
|
||||
0x18e9, // 10: jmp !osre, 9 side 1
|
||||
0xf001, // 11: set pins, 1 side 0
|
||||
0x003c, // 12: jmp !x, 28
|
||||
0x80a0, // 13: pull block
|
||||
0x7001, // 14: out pins, 1 side 0
|
||||
0x18ee, // 15: jmp !osre, 14 side 1
|
||||
0x1046, // 16: jmp x--, 6 side 0
|
||||
0x001c, // 17: jmp 28
|
||||
0x90a0, // 18: pull block side 0
|
||||
0xa027, // 19: mov x, osr
|
||||
0x80a0, // 20: pull block
|
||||
0xa047, // 21: mov y, osr
|
||||
0xb0e1, // 22: mov osr, x side 0
|
||||
0x7011, // 23: out pins, 17 side 0
|
||||
0xb842, // 24: nop side 1
|
||||
0x7001, // 25: out pins, 1 side 0
|
||||
0x18f9, // 26: jmp !osre, 25 side 1
|
||||
0x1096, // 27: jmp y--, 22 side 0
|
||||
// .wrap_target
|
||||
0x90a0, // 28: pull block side 0
|
||||
0x6070, // 29: out null, 16
|
||||
0x7001, // 30: out pins, 1 side 0
|
||||
0x18fe, // 31: jmp !osre, 30 side 1
|
||||
// .wrap
|
||||
};
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
static const struct pio_program tft_io_program = {
|
||||
.instructions = tft_io_program_instructions,
|
||||
.length = 32,
|
||||
.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