2022-01-18 12:42:57 +00:00
# include "RA8875_t4.h"
# include "RA8875_registers.h"
2022-01-19 23:40:10 +00:00
// Discussion about DMA channels: http://forum.pjrc.com/threads/25778-Could-there-be-something-like-an-ISR-template-function/page3
2022-01-20 23:19:11 +00:00
// Thread discussing the way to get started: https://forum.pjrc.com/threads/63353-Teensy-4-1-How-to-start-using-DMA
// Thread of someone writing an LCD interface: https://forum.pjrc.com/threads/67247-Teensy-4-0-DMA-SPI
2022-01-19 23:40:10 +00:00
// And these libraries use it:
// https://github.com/PaulStoffregen/Audio
// https://github.com/PaulStoffregen/OctoWS2811
// https://github.com/pedvide/ADC
// https://github.com/duff2013/SerialEvent
// https://github.com/pixelmatix/SmartMatrix
// https://github.com/crteensy/DmaSpi <-- DmaSpi has adopted this scheme
2022-01-20 12:46:10 +00:00
//
/* The ST7735 and ILI9341 have both been modified in the Teensyduino
* distribution to use DMA + SPI the way that I want to do this here .
* The major differences here :
* - Both the 7735 and 9341 use 16 - bit transfers . Since this has a
* substantially larger framebuffer that doesn ' t fit in the DMAMEM
* region , that won ' t work - - so I ' m using 8 - bpp mode .
* - Both the ILI9341 and ST7735 have a D / C pin to tell the display
* if the data being sent is a command or is data . The 8875 does
* not have a D / C pin .
* - Both the 7735 and 9341 drivers have a lot of # ifdef overhead
* to support multiple different Teensys . I ' m targeting the 4.1
* so I ' m not including any of that abstraction .
* - The RA8875 driver supports multiple different pixel - size
* displays ( and vendors ' interfaces ) . Aiie only supports 800 x480
* using the Adafruit RA8875 display module .
2022-01-20 23:19:11 +00:00
* - Each dmasettings looks like it can address 32767 bytes of data ,
* so while the 320 * 240 16 - bit structure needs 3 structs ( well ,
* 2.34 structs ) the 800 * 480 8 - bit structure needs 12 structs .
*
2022-01-20 12:46:10 +00:00
*
* The initialization sets us to 8 bpp color mode , so that 800 * 480 fits
* within 512 k of RAM ( 800 * 480 bytes = 375 k ; if this were 16 bpp , then
* it would overflow by 238 k . ) This is important because , if I
* understand it rightly , only one of the 512 k banks on the Teensy is
* coupled to DMA . ( The other bank of memory is tightly coupled to the
* processor for faster access . ) It ' s possible to further reduce our
* memory footprint if necessary by just caching the Apple ' s display
* size of 560 x240 ( reducing it to about 132 k ) and initializing the
* display with a " display window " for that area of the screen ,
* turning off DMA and drawing outside the window when necessary ( to
* update a drive indicator , debug message , or the UI outline itself ) .
*
* The core drawing operation uses the RA8875 ' s MRWC command to send
* data in a transaction , driven by the DMA layer . It will do this :
* start
* RA8875_CMDWRITE
* RA8875_MRWC
* end
* start
* RA8875_DATAWRITE
* < all the data >
* end
*
* It ' s unclear to me whether or not we ' ll have to end and restart
* after every frame , but I ' ll find out as it ' s built . . .
*
* The one abstraction I did leave in here is which SPI pins are being
* used . This still supports moving to any of the three SPI busses .
*
* WIP : using the LED and Serial to debug why interrupts haven ' t been
* firing the way I expected . I think it may be because I didn ' t have a
* transaction running .
* . . . no , that didn ' t seem to solve the problem .
*
* I don ' t know if the way I ' m maybeUpdateTCR - ing with
* LPSPI_TCR_PCS ( 3 ) is correct or not - - I haven ' t fully comprehended
* what the code in 9341 / 7735 are doing there , because it ' s combined
2022-01-20 23:19:11 +00:00
* with the D / C pin functionality . But LPSPI_TCR_PCS refers to
* the SPI peripheral chip select : 00 = LPSPI_PCS [ 0 ] ; 01 = 1 ; etc .
* From what I see in the original maybeUpdateTCR method , it ' s using
* LPSPI_TCR_PCS ( 3 ) as a CARRIER of the expected state of the _dc
* pin . It is masked back out before setting TCR . So I think the
* right thing to do here is completely remove all of the LPSPI_TCR_PCS
* bits . . . . but it still isn ' t getting to an interrupt state .
2022-01-20 12:46:10 +00:00
*
* Some pages I ' ve been reading :
* https : //forum.pjrc.com/threads/57280-RA8875-from-Buydisplay/page9?highlight=lpspi_tcr_pcs (pg9)
* https : //www.displayfuture.com/Display/datasheet/controller/ST7735.pdf
* https : //github.com/ElectroTechnique/TSynth-for-Teensy3.6/blob/master/ST7735_t3.cpp
* https : //github-wiki-see.page/m/TeensyUser/doc/wiki/Memory-Mapping
*/
2022-01-19 23:40:10 +00:00
// at 8bpp, each pixel is 1 byte
2022-01-20 23:19:11 +00:00
# define COUNT_PIXELS_WRITE (RA8875_WIDTH * RA8875_HEIGHT)
2022-01-19 23:40:10 +00:00
2022-01-20 23:19:11 +00:00
# define TCR_MASK ( LPSPI_TCR_FRAMESZ(31) | LPSPI_TCR_CONT | LPSPI_TCR_RXMSK )
2022-01-20 13:19:16 +00:00
# define _565toR(c) ( ((c) & 0xF800) >> 8 )
# define _565toG(c) ( ((c) & 0x07E0) >> 3 )
# define _565toB(c) ( ((c) & 0x001F) << 3 )
2022-01-20 23:19:11 +00:00
// 3 of these, one for each of the 3 busses, so that 3 separate
// displays could be driven. FIXME: I don't really need all 3 in this
// application, so this can be pared down.
2022-01-19 23:40:10 +00:00
static RA8875_t4 * _dmaActiveDisplay [ 3 ] ;
2022-01-18 12:42:57 +00:00
RA8875_t4 : : RA8875_t4 ( const uint8_t cs_pin , const uint8_t rst_pin , const uint8_t mosi_pin , const uint8_t sck_pin , const uint8_t miso_pin )
{
_mosi = mosi_pin ;
_miso = miso_pin ;
_cs = cs_pin ;
_rst = rst_pin ;
_sck = sck_pin ;
_pspi = NULL ;
2022-01-19 23:40:10 +00:00
_pfbtft = NULL ;
_dma_state = 0 ;
_frame_complete_callback = NULL ;
_dma_frame_count = 0 ;
_dmaActiveDisplay [ 0 ] = _dmaActiveDisplay [ 1 ] = _dmaActiveDisplay [ 2 ] = NULL ;
2022-01-18 12:42:57 +00:00
}
RA8875_t4 : : ~ RA8875_t4 ( )
{
}
void RA8875_t4 : : begin ( uint32_t spi_clock , uint32_t spi_clock_read )
{
_spi_clock = spi_clock ;
_spi_clock_read = spi_clock_read ;
_clock = 4000000UL ; // start at low speed
// figure out which SPI bus we're using
if ( SPI . pinIsMOSI ( _mosi ) & & ( ( _miso = = 0xff ) | | SPI . pinIsMISO ( _miso ) ) & & SPI . pinIsSCK ( _sck ) ) {
_pspi = & SPI ;
_spi_num = 0 ;
_pimxrt_spi = & IMXRT_LPSPI4_S ;
} else if ( SPI1 . pinIsMOSI ( _mosi ) & & ( ( _miso = = 0xff ) | | SPI1 . pinIsMISO ( _miso ) ) & & SPI1 . pinIsSCK ( _sck ) ) {
_pspi = & SPI1 ;
_spi_num = 1 ;
_pimxrt_spi = & IMXRT_LPSPI3_S ;
} else if ( SPI2 . pinIsMOSI ( _mosi ) & & ( ( _miso = = 0xff ) | | SPI2 . pinIsMISO ( _miso ) ) & & SPI2 . pinIsSCK ( _sck ) ) {
_pspi = & SPI2 ;
_spi_num = 2 ;
_pimxrt_spi = & IMXRT_LPSPI1_S ;
} else {
Serial . println ( " Pins given are not valid SPI bus pins " ) ;
return ;
}
_pspi - > setMOSI ( _mosi ) ;
_pspi - > setSCK ( _sck ) ;
if ( _miso ! = 0xff ) _pspi - > setMISO ( _miso ) ;
uint32_t * pa = ( uint32_t * ) _pspi ;
_spi_hardware = ( SPIClass : : SPI_Hardware_t * ) pa [ 1 ] ;
_pspi - > begin ( ) ;
pinMode ( _cs , OUTPUT ) ;
2022-01-18 22:13:12 +00:00
digitalWriteFast ( _cs , HIGH ) ;
2022-01-18 12:42:57 +00:00
2022-01-19 23:40:10 +00:00
_spi_tcr_current = _pimxrt_spi - > TCR ; // get the current TCR value
2022-01-18 12:42:57 +00:00
2022-01-20 23:19:11 +00:00
maybeUpdateTCR ( LPSPI_TCR_FRAMESZ ( 7 ) ) ;
2022-01-21 20:37:06 +00:00
2022-01-18 12:42:57 +00:00
_initializeTFT ( ) ;
}
void RA8875_t4 : : _initializeTFT ( )
{
2022-01-18 22:13:12 +00:00
// toggle RST low to reset
2022-01-18 12:42:57 +00:00
if ( _rst < 255 ) {
pinMode ( _rst , OUTPUT ) ;
2022-01-20 23:19:11 +00:00
digitalWriteFast ( _rst , HIGH ) ;
2022-01-18 12:42:57 +00:00
delay ( 10 ) ;
2022-01-20 23:19:11 +00:00
digitalWriteFast ( _rst , LOW ) ;
2022-01-18 12:42:57 +00:00
delay ( 220 ) ;
2022-01-20 23:19:11 +00:00
digitalWriteFast ( _rst , HIGH ) ;
2022-01-18 12:42:57 +00:00
delay ( 300 ) ;
} else {
// Try a soft reset
writeCommand ( RA8875_PWRR ) ;
_writeData ( RA8875_PWRR_SOFTRESET ) ;
delay ( 20 ) ;
_writeData ( RA8875_PWRR_NORMAL ) ;
delay ( 200 ) ;
}
2022-01-21 20:37:06 +00:00
2022-01-18 12:42:57 +00:00
// Set the sysclock
_writeRegister ( RA8875_PLLC1 , 0x07 ) ; // same as default value: %0000 0111 == pre /=1; input /=7
delay ( 1 ) ;
_writeRegister ( RA8875_PLLC1 + 1 , 0x03 ) ; // same as default value: %0000 0011 == output /8
delay ( 1 ) ;
_writeRegister ( RA8875_PCSR , 0x81 ) ; // pixel clock setting register: %1000 0001 == PDAT at PCLK falling edge; PCLK period is 2* system clock period
delay ( 1 ) ;
// colorspace
2022-01-18 22:34:50 +00:00
_writeRegister ( RA8875_SYSR , 0x00 ) ; // 8-bit (0x0C == 16-bit)
2022-01-18 12:42:57 +00:00
_writeRegister ( RA8875_HDWR , 0x63 ) ; // LCD horizontal display width == (v+1)*8
_writeRegister ( RA8875_HNDFTR , 0x00 ) ; // Horizontal non-display period fine tuning
_writeRegister ( RA8875_HNDR , 0x03 ) ; // LCD Horizontal non-display period register; period (in pixels) = (v+1)*8 + HNDFTR+2 == 32
_writeRegister ( RA8875_HSTR , 0x03 ) ; // HSYNC start position register; start position (in pixels) = (v+1)*8 == 24
_writeRegister ( RA8875_HPWR , 0x0B ) ; // HSYNC pulse width register; %0000 1011 == HSYNC low active, hsync pulse width = ((v&32)+1)*8 = 88
_writeRegister ( RA8875_VDHR0 , 0xDF ) ; // LCD Vertical display height register 0 (low byte of height, where height = this value + 1)
_writeRegister ( RA8875_VDHR0 + 1 , 0x01 ) ; // LCD Vertical display height register 1 (high byte of height)
_writeRegister ( RA8875_VNDR0 , 0x1F ) ; // LCD vertical non-display period register 0 (low byte of non-display period in lines, where period=(VNDR+1) == 32)
_writeRegister ( RA8875_VNDR0 + 1 , 0x00 ) ; // LCD vertical non-display period register 1 (high byte of non-display period in lines)
_writeRegister ( RA8875_VSTR0 , 0x16 ) ; // VSYNC start position register 0; low byte, where start pos(line) = (VSTR+1) == 23
_writeRegister ( RA8875_VSTR0 + 1 , 0x00 ) ; // VSYNC start position register 1; high byte
_writeRegister ( RA8875_VPWR , 0x01 ) ; // VSYNC pulse width register; %0000 0001 == low active, pulse width (in lines) = (v&0x7F)+1 == 2
// Set the entire screen as the active window
_writeRegister ( RA8875_HSAW0 , 0x00 ) ; // horizontal start point of active window
_writeRegister ( RA8875_HSAW0 + 1 , 0x00 ) ;
_writeRegister ( RA8875_HEAW0 , ( RA8875_WIDTH - 1 ) & 0xFF ) ;
_writeRegister ( RA8875_HEAW0 + 1 , ( RA8875_WIDTH - 1 ) > > 8 ) ; // horizontal end point of active window
_writeRegister ( RA8875_VSAW0 , 0x00 ) ; // vertical start point of active window
_writeRegister ( RA8875_VSAW0 + 1 , 0x00 ) ;
_writeRegister ( RA8875_VEAW0 , ( RA8875_HEIGHT - 1 ) & 0xFF ) ; // vertical end point of active window
_writeRegister ( RA8875_VEAW0 + 1 , ( RA8875_HEIGHT - 1 ) > > 8 ) ;
2022-01-21 20:37:06 +00:00
delay ( 100 ) ;
2022-01-18 22:13:12 +00:00
// clear memory
uint8_t temp ;
temp = _readRegister ( RA8875_MCLR ) ;
temp | = ( 1 < < 7 ) ;
_writeData ( temp ) ;
_waitBusy ( 0x80 ) ;
delay ( 1 ) ;
2022-01-21 20:37:06 +00:00
// Update the sysclock. 300MHz Fpll; 150MHz sysclk; 18.75 MHz pixel
// clock. That's the fastest that seems to reliably work for me. It
// also allows a SPI bus transfer at just under 80MHz (literally
// 79.999... there must be a constant somewhere that's breaking the
// SPI module at 80, or a Teensy 4.1 bus limit, or ... ?)
_writeRegister ( RA8875_PLLC1 , 0x0E ) ;
delay ( 1 ) ;
_writeRegister ( RA8875_PLLC1 + 1 , 0x01 ) ;
delay ( 1 ) ;
_writeRegister ( RA8875_PCSR , 0x82 ) ;
delay ( 1 ) ;
_clock = _spi_clock ; // speed up to full speed now
delay ( 10 ) ;
2022-01-18 22:13:12 +00:00
// turn on the display
_writeRegister ( RA8875_PWRR , RA8875_PWRR_NORMAL | RA8875_PWRR_DISPON ) ;
delay ( 1 ) ;
fillWindow ( ) ; // defaults to black
2022-01-18 22:18:12 +00:00
// turn on backlight
2022-01-21 13:03:40 +00:00
_writeRegister ( RA8875_P1CR , ( RA8875_PxCR_ENABLE | ( RA8875_PWM_CLK_DIV2048 & 0xF ) ) ) ;
2022-01-18 22:18:12 +00:00
_writeRegister ( RA8875_P1DCR , 255 ) ; // brightness
2022-01-18 22:13:12 +00:00
// set graphics mode & default memory write order/behavior
_writeRegister ( RA8875_MWCR0 , 0x00 ) ;
// *** rotation?
// Not sure we have to do this a second time, but set active window...
_writeRegister ( RA8875_HSAW0 , 0x00 ) ; // horizontal start point of active window
_writeRegister ( RA8875_HSAW0 + 1 , 0x00 ) ;
_writeRegister ( RA8875_HEAW0 , ( RA8875_WIDTH - 1 ) & 0xFF ) ;
_writeRegister ( RA8875_HEAW0 + 1 , ( RA8875_WIDTH - 1 ) > > 8 ) ; // horizontal end point of active window
_writeRegister ( RA8875_VSAW0 , 0x00 ) ; // vertical start point of active window
_writeRegister ( RA8875_VSAW0 + 1 , 0x00 ) ;
_writeRegister ( RA8875_VEAW0 , ( RA8875_HEIGHT - 1 ) & 0xFF ) ; // vertical end point of active window
_writeRegister ( RA8875_VEAW0 + 1 , ( RA8875_HEIGHT - 1 ) > > 8 ) ;
2022-01-18 12:42:57 +00:00
2022-01-18 22:13:12 +00:00
// set foreground color
_writeRegister ( RA8875_FGCR0 , 0 ) ;
_writeRegister ( RA8875_FGCR0 + 1 , 0 ) ;
_writeRegister ( RA8875_FGCR0 + 2 , 0 ) ;
// set background color
_writeRegister ( RA8875_BGCR0 , 0 ) ;
_writeRegister ( RA8875_BGCR0 + 1 , 0 ) ;
_writeRegister ( RA8875_BGCR0 + 2 , 0 ) ;
// _writeRegister(RA8875_FNCR1, 0); // probably not necessary since we're not using built-in fonts
// setCursor(0,0);
_writeRegister ( RA8875_GPIOX , true ) ; // turn on backlight
2022-01-18 12:42:57 +00:00
}
2022-01-19 23:40:10 +00:00
void RA8875_t4 : : setFrameBuffer ( uint8_t * frame_buffer )
2022-01-18 12:42:57 +00:00
{
2022-01-19 23:40:10 +00:00
_pfbtft = frame_buffer ;
_dma_state & = ~ RA8875_DMA_INIT ;
2022-01-18 12:42:57 +00:00
}
bool RA8875_t4 : : asyncUpdateActive ( )
{
2022-01-21 20:37:06 +00:00
return ( _dma_state & RA8875_DMA_ACTIVE ) ;
2022-01-18 12:42:57 +00:00
}
2022-01-19 23:40:10 +00:00
void RA8875_t4 : : initDMASettings ( )
{
if ( _dma_state & RA8875_DMA_INIT )
return ;
uint8_t dmaTXevent = _spi_hardware - > tx_dma_channel ;
2022-01-20 23:19:11 +00:00
// Each DMA structure can only track 32767 words written (where a
// word here is 8 bits). So we need 12 of these to cover the whole
// set of 800*480 display data.
uint16_t pixelsWrittenPerDMAreq = COUNT_PIXELS_WRITE / 12 ;
2022-01-19 23:40:10 +00:00
if ( _dma_state & RA8875_DMA_EVER_INIT ) {
2022-01-20 23:19:11 +00:00
for ( int i = 0 ; i < 12 ; i + + ) {
_dmasettings [ i ] . sourceBuffer ( & _pfbtft [ pixelsWrittenPerDMAreq * i ] , pixelsWrittenPerDMAreq ) ;
}
2022-01-19 23:40:10 +00:00
} else {
2022-01-20 23:19:11 +00:00
for ( int i = 0 ; i < 12 ; i + + ) {
_dmasettings [ i ] . sourceBuffer ( & _pfbtft [ pixelsWrittenPerDMAreq * i ] , pixelsWrittenPerDMAreq ) ;
_dmasettings [ i ] . destination ( _pimxrt_spi - > TDR ) ; // DMA sends data to LPSPI's transmit data register
_dmasettings [ i ] . TCD - > ATTR_DST = 0 ; // 8-bit destination size (%000)
_dmasettings [ i ] . replaceSettingsOnCompletion ( _dmasettings [ ( i + 1 ) % 12 ] ) ;
}
// "half done" for 12 is at the end of index 5, so we don't have to set up interruptAtHalf()
// but we do have to change the way we deal with sub-frame counting. If we need it. I don't
// think we do, so I'm leaving this here as a comment for now...
// _dmasettings[5].interruptAtCompletion();
_dmasettings [ 11 ] . interruptAtCompletion ( ) ;
2022-01-19 23:40:10 +00:00
_dmatx = _dmasettings [ 0 ] ;
_dmatx . begin ( true ) ;
_dmatx . triggerAtHardwareEvent ( dmaTXevent ) ;
if ( _spi_num = = 0 ) _dmatx . attachInterrupt ( dmaInterrupt ) ;
else if ( _spi_num = = 1 ) _dmatx . attachInterrupt ( dmaInterrupt1 ) ;
else _dmatx . attachInterrupt ( dmaInterrupt2 ) ;
_dma_state = RA8875_DMA_INIT | RA8875_DMA_EVER_INIT ;
}
}
2022-01-20 23:19:11 +00:00
bool RA8875_t4 : : updateScreenAsync ( bool update_cont )
{
2022-01-19 23:40:10 +00:00
if ( ! _pfbtft ) return false ;
initDMASettings ( ) ;
2022-01-20 23:19:11 +00:00
if ( _dma_state & RA8875_DMA_ACTIVE ) {
2022-01-19 23:40:10 +00:00
return false ;
2022-01-20 23:19:11 +00:00
}
2022-01-19 23:40:10 +00:00
// Half of main ram has a 32k cache. This tells it to flush the cache if necessary.
if ( ( uint32_t ) _pfbtft > = 0x20200000u ) arm_dcache_flush ( _pfbtft , RA8875_WIDTH * RA8875_HEIGHT ) ;
2022-01-20 12:46:10 +00:00
_dmasettings [ 2 ] . TCD - > CSR & = ~ ( DMA_TCD_CSR_DREQ ) ; // DMA_TCDn_CSR[3] -- If this flag is set, the eDMA hardware automatically clears the corresponding ERQ bit when the current major iteration count reaches zero.
2022-01-19 23:40:10 +00:00
// Don't need to reset the window b/c we never change it; but set the X/Y cursor back to the origin
_writeRegister ( RA8875_CURV0 , 0 ) ;
_writeRegister ( RA8875_CURV0 + 1 , 0 ) ;
_writeRegister ( RA8875_CURH0 , 0 ) ;
_writeRegister ( RA8875_CURH0 + 1 , 0 ) ;
2022-01-20 12:46:10 +00:00
// Start it sending data
2022-01-19 23:40:10 +00:00
writeCommand ( RA8875_MRWC ) ;
2022-01-20 12:46:10 +00:00
_startSend ( ) ;
_pspi - > transfer ( RA8875_DATAWRITE ) ;
2022-01-19 23:40:10 +00:00
_spi_fcr_save = _pimxrt_spi - > FCR ; // FIFO Control Register
_pimxrt_spi - > FCR = 0 ; // turn off FIFO watermarks
// Set transmit command register: disable RX ("mask out RX"), enable
// TX from FIFO (b/c it's not masked out), and 8-bit data transfers
// (7+1).
2022-01-20 23:19:11 +00:00
maybeUpdateTCR ( LPSPI_TCR_FRAMESZ ( 7 ) | LPSPI_TCR_RXMSK /*| LPSPI_TCR_CONT*/ ) ;
2022-01-19 23:40:10 +00:00
// Set up the DMA Enable Register to enable transmit DMA (and not receive DMA)
_pimxrt_spi - > DER = LPSPI_DER_TDDE ;
// Clear the status register %0011 1111 0000 0000 == set DMF, REF,
// TEF, TCF, FCF, WCF; clear MBF, RDF, TDF. MBF: busy flag; DMF:
// data match; REF: rec error flag; TEF: xmit error flag; TCF:
// xmit complete flag; FCF: frame complete flag; WCF: word
// complete flag; RDF: rec data flag; TDF: xmit data flag
2022-01-20 23:19:11 +00:00
_pimxrt_spi - > SR = 0x3f00 ; // Why setting these bits? Should this be '&=' ?
2022-01-19 23:40:10 +00:00
_dmatx . triggerAtHardwareEvent ( _spi_hardware - > tx_dma_channel ) ;
_dmatx = _dmasettings [ 0 ] ;
2022-01-20 23:19:11 +00:00
2022-01-19 23:40:10 +00:00
_dma_frame_count = 0 ;
_dmaActiveDisplay [ _spi_num ] = this ;
2022-01-20 23:19:11 +00:00
_dmatx . begin ( false ) ;
_dmatx . enable ( ) ;
2022-01-19 23:40:10 +00:00
if ( update_cont ) {
_dma_state | = RA8875_DMA_CONT ;
} else {
2022-01-20 23:19:11 +00:00
_dmasettings [ 11 ] . disableOnCompletion ( ) ;
2022-01-19 23:40:10 +00:00
_dma_state & = ~ RA8875_DMA_CONT ;
}
_dma_state | = RA8875_DMA_ACTIVE ;
2022-01-20 23:19:11 +00:00
2022-01-19 23:40:10 +00:00
return true ;
2022-01-18 12:42:57 +00:00
}
void RA8875_t4 : : fillWindow ( uint16_t color )
{
2022-01-20 23:23:31 +00:00
// FIXME: reduce color & fill appropriately
memset ( _pfbtft , RA8875_WIDTH * RA8875_HEIGHT , 0 ) ;
2022-01-18 12:42:57 +00:00
}
2022-01-18 22:34:50 +00:00
// *** Remove this and convert to native 8-bit? Or make it inline?
uint8_t _color16To8bpp ( uint16_t color ) {
return ( ( color & 0xe000 ) > > 8 ) | ( ( color & 0x700 ) > > 6 ) | ( ( color & 0x18 ) > > 3 ) ;
}
2022-01-18 12:42:57 +00:00
void RA8875_t4 : : drawPixel ( int16_t x , int16_t y , uint16_t color )
{
// FIXME: bounds checking
2022-01-20 23:23:31 +00:00
_pfbtft [ y * RA8875_WIDTH + x ] = _color16To8bpp ( color ) ;
2022-01-18 12:42:57 +00:00
}
uint32_t RA8875_t4 : : frameCount ( )
{
2022-01-20 23:19:11 +00:00
return _dma_frame_count ;
2022-01-18 12:42:57 +00:00
}
void RA8875_t4 : : writeCommand ( const uint8_t d )
{
_startSend ( ) ;
_pspi - > transfer ( RA8875_CMDWRITE ) ;
_pspi - > transfer ( d ) ;
_endSend ( ) ;
}
void RA8875_t4 : : writeData16 ( uint16_t data )
{
_startSend ( ) ;
_pspi - > transfer ( RA8875_DATAWRITE ) ;
_pspi - > transfer16 ( data ) ;
_endSend ( ) ;
}
void RA8875_t4 : : _writeData ( uint8_t data )
{
_startSend ( ) ;
_pspi - > transfer ( RA8875_DATAWRITE ) ;
_pspi - > transfer ( data ) ;
_endSend ( ) ;
}
uint8_t RA8875_t4 : : _readData ( bool stat )
{
2022-01-19 23:40:10 +00:00
// FIXME do we need to slow down for reads?
2022-01-18 12:42:57 +00:00
_startSend ( ) ;
_pspi - > transfer ( stat ? RA8875_CMDREAD : RA8875_DATAREAD ) ;
2022-01-19 23:40:10 +00:00
uint8_t x = _pspi - > transfer ( 0x00 ) ;
2022-01-18 12:42:57 +00:00
_endSend ( ) ;
2022-01-19 23:40:10 +00:00
return x ;
2022-01-18 12:42:57 +00:00
}
void RA8875_t4 : : _writeRegister ( const uint8_t reg , uint8_t val )
{
writeCommand ( reg ) ;
_writeData ( val ) ;
}
uint8_t RA8875_t4 : : _readRegister ( const uint8_t reg )
{
writeCommand ( reg ) ;
return _readData ( false ) ;
}
boolean RA8875_t4 : : _waitPoll ( uint8_t regname , uint8_t waitflag , uint8_t timeout )
{
uint8_t temp ;
unsigned long start_time = millis ( ) ;
while ( 1 ) {
temp = _readRegister ( regname ) ;
if ( ! ( temp & waitflag ) ) {
return true ;
}
if ( ( millis ( ) - start_time ) > timeout ) {
// timeout
return false ;
}
}
/* NOTREACHED */
}
2022-01-18 22:13:12 +00:00
void RA8875_t4 : : _waitBusy ( uint8_t res )
{
uint8_t temp ;
unsigned long start = millis ( ) ; //M.Sandercock
do {
if ( res = = 0x01 ) writeCommand ( RA8875_DMACR ) ; //dma
temp = _readData ( true ) ;
if ( ( millis ( ) - start ) > 10 ) return ;
} while ( ( temp & res ) = = res ) ;
}
2022-01-19 23:40:10 +00:00
void RA8875_t4 : : maybeUpdateTCR ( uint32_t requested_tcr_state )
{
if ( ( _spi_tcr_current & TCR_MASK ) ! = requested_tcr_state ) {
// PCS is the peripheral chip select used for the transfer
_spi_tcr_current = ( _spi_tcr_current & ~ TCR_MASK ) | requested_tcr_state ;
// only output when Transfer queue is empty.
while ( ( _pimxrt_spi - > FSR & 0x1f ) ) ;
_pimxrt_spi - > TCR = _spi_tcr_current ; // update the TCR
}
}
void RA8875_t4 : : dmaInterrupt ( void ) {
if ( _dmaActiveDisplay [ 0 ] ) {
_dmaActiveDisplay [ 0 ] - > process_dma_interrupt ( ) ;
}
}
void RA8875_t4 : : dmaInterrupt1 ( void ) {
2022-01-20 12:46:10 +00:00
// FIXME this isn't being called - the LED doesn't go off.
// I'm using single (not continuous) async writes, so this *should* go off; and then call the interrupt; and then clear the state, so I get a second dash printed to the serial console, showing that it's doing another update. But instead I get one dash and then the sound buffer OVERRRUN messages b/c the main thread isn't running. It's got to be the DMA code that's hanging it somewhere.
2022-01-19 23:40:10 +00:00
if ( _dmaActiveDisplay [ 1 ] ) {
_dmaActiveDisplay [ 1 ] - > process_dma_interrupt ( ) ;
}
}
void RA8875_t4 : : dmaInterrupt2 ( void ) {
if ( _dmaActiveDisplay [ 2 ] ) {
_dmaActiveDisplay [ 2 ] - > process_dma_interrupt ( ) ;
}
}
void RA8875_t4 : : process_dma_interrupt ( void ) {
_dmatx . clearInterrupt ( ) ;
2022-01-20 23:19:11 +00:00
_dma_frame_count + + ;
if ( ( _dma_state & RA8875_DMA_CONT ) = = 0 ) {
// Single refresh, or the user canceled, so release the CS pin
while ( _pimxrt_spi - > FSR & 0x1f ) ; // wait until transfer is done
while ( _pimxrt_spi - > SR & LPSPI_SR_MBF ) ; // ... and the module is not busy
_dmatx . clearComplete ( ) ;
_pimxrt_spi - > FCR = LPSPI_FCR_TXWATER ( 15 ) ;
_pimxrt_spi - > DER = 0 ; // turn off tx and rx DMA
_pimxrt_spi - > CR = LPSPI_CR_MEN | LPSPI_CR_RRF | LPSPI_CR_RTF ; //RRF: reset receive FIFO; RTF: reset transmit FIFO; MEN: enable module
_pimxrt_spi - > SR = 0x3f00 ; // Why setting these bits? Should this be '&=' ?
// DMF: data match flag set
// REF: receive error flag set
// TEF: transmit error flag set
// TCF: transfer complete flag set
// FCF: frame complete flag set
// WCF: word complete flag set
maybeUpdateTCR ( LPSPI_TCR_FRAMESZ ( 7 ) ) ;
// *** the other modules send a NOP here, don't know why
_endSend ( ) ;
_dma_state & = ~ RA8875_DMA_ACTIVE ;
_dmaActiveDisplay [ _spi_num ] = 0 ;
} else {
2022-01-20 12:46:10 +00:00
if ( _frame_complete_callback )
( * _frame_complete_callback ) ( ) ;
2022-01-20 23:19:11 +00:00
else {
// Try to flush memory
if ( ( uint32_t ) _pfbtft > = 0x20200000u )
arm_dcache_flush ( _pfbtft , RA8875_WIDTH * RA8875_HEIGHT ) ;
2022-01-19 23:40:10 +00:00
}
}
2022-01-20 23:19:11 +00:00
// make sure the code is synchronized - memory access must be
// complete before we continue
asm ( " dsb " ) ;
2022-01-19 23:40:10 +00:00
}
2022-01-20 12:46:10 +00:00
// Other possible methods, that I don't think we'll need:
// void RA8875_t4::setFrameCompleteCB(void (*pcb)(), bool fCallAlsoHalfDone)