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:
Bodmer
2022-01-02 01:08:22 +00:00
parent 681eb9dfec
commit 8a2398451b
17 changed files with 718 additions and 322 deletions
+1 -1
View File
@@ -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
View File
@@ -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
+40 -24
View File
@@ -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);
+1 -1
View File
@@ -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
+85 -24
View File
@@ -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
+44 -14
View File
@@ -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,
};
+91
View File
@@ -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
+73
View File
@@ -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