T4 FlexIO - Looking back at my T4 beta testing library FlexIO_t4

Sorry, sometimes this stuff can be a pain in the ...

That is why most of my flexIO system code includes debug stuff, like:
Code:
		// Lets print out some of the settings and the like to get idea of state
		#ifdef DEBUG_FlexSerial
		Serial.printf("CCM_CDCDR: %x\n", CCM_CDCDR);
		Serial.printf("VERID:%x PARAM:%x CTRL:%x PIN: %x\n", p->VERID, p->PARAM, p->CTRL, p->PIN);
		Serial.printf("SHIFTSTAT:%x SHIFTERR=%x TIMSTAT=%x\n", p->SHIFTSTAT, p->SHIFTERR, p->TIMSTAT);
		Serial.printf("SHIFTSIEN:%x SHIFTEIEN=%x TIMIEN=%x\n", p->SHIFTSIEN, p->SHIFTEIEN, p->TIMIEN);
		Serial.printf("SHIFTSDEN:%x SHIFTSTATE=%x\n", p->SHIFTSDEN, p->SHIFTSTATE);
		Serial.printf("SHIFTCTL:%x %x %x %x\n", p->SHIFTCTL[0], p->SHIFTCTL[1], p->SHIFTCTL[2], p->SHIFTCTL[3]);
		Serial.printf("SHIFTCFG:%x %x %x %x\n", p->SHIFTCFG[0], p->SHIFTCFG[1], p->SHIFTCFG[2], p->SHIFTCFG[3]);
		Serial.printf("TIMCTL:%x %x %x %x\n", p->TIMCTL[0], p->TIMCTL[1], p->TIMCTL[2], p->TIMCTL[3]);
		Serial.printf("TIMCFG:%x %x %x %x\n", p->TIMCFG[0], p->TIMCFG[1], p->TIMCFG[2], p->TIMCFG[3]);
		Serial.printf("TIMCMP:%x %x %x %x\n", p->TIMCMP[0], p->TIMCMP[1], p->TIMCMP[2], p->TIMCMP[3]);
So that I can go through and look at the actual state of each of the registers.

It looked like you setup the interrupt vector and enabled it... So something should be called

But also part of the flexio_t4 code is a function addIOHandlerCallback.... which sets up the ISR with it's own IRQ handler where you could setup callbacks for different timers...

Beyond that I am not sure.
 
But also part of the flexio_t4 code is a function addIOHandlerCallback.... which sets up the ISR with it's own IRQ handler where you could setup callbacks for different timers...
I was hoping to try this, but couldn't find a code example of it.
My interpretation was I setup my own callback function, and pass it in addIOHandlerCallback() - it gets called based on whichever interrupts I enabled on the relevant instance (timer interrupt, shifter interrupt or error interrup).
But talking another look, Im not sure that's the way to do it.
 
@KurtE been trying to register a callback with addIOHandlerCallback but to no avail.
I created a callback handler:
Code:
FlexIOHandlerCallback *cb;

I pass cb to addIOHandlerCallback
Code:
addIOHandlerCallback(cb);

I have my own callback function,
Code:
bool myCB(){
return true;
}

But how to I assign myCB to the callback handler cb?
 
@Rezo - I've made some good progress which might be useful to you. I successfully got the interrupt method working. Functional proof of concept here:
https://github.com/easone/FlexIO_interrupt

The ISR needs to clear the interrupt signal - this is done by writing to the TIMSTAT register in case of a timer status interrupt, or writing data to the shifter buffers in case of a shifter status interrupt. Although, one subtlety is that the shifter status interrupt also triggers after a transfer is complete, so you have to disable the interrupt signal in the SHIFTSIEN register once the transfer is done... It's not sufficient to write to the SHIFTSTAT register to clear that interrupt, because clearing the shifter status flag also triggers FlexIO to shift more data.
 
Im just making sure i get the flex io right.

I needed another spi bus on a project, all dedicated buses are already in use, so i looked into flexIO and i have some pins on the MicroMod teensy available (44, 46, 48, 73) which is part of flexIO "2" which should be fully featured with dma support?

So im hoping this config is possible: pin 44 mosi, pin 46 miso, pin 48 clk, pin 73 cs.

other then that, are there any downsides to using the flexIO for spi rather then the dedicated spi?
 
Last edited:
I was trying to use FlexSerial to replicate a UART for MIDI Output. Unfortunately, I'm hitting some roadblocks. I'm not able to manipulate the baud rate for the FlexIO pin. begin(31250) should work, but the pin is still sending the MIDI message at a baud rate of 115200.
FlexSerial.jpg
Photo above comparing the FlexIO pin (yellow) vs a Serial TX pin running at 31250baud (blue) and both writing the same bytes. I've looked at the code in FlexSerial.cpp and compared it to the IMXRT manual chapter 50 and the flexio configuration code looks fine. Adjusting the FLEXIO1_CLOCK value somewhat changes the speed but I am not able to determine a relationship between that and the baud rate.

Any idea where I should be looking at?
 
Ah yes, double. Getting close but something still isnt quite right.

I tried to half the FlexIO clock speed by using the divider
Code:
  CCM_CS1CDR &= ~( CCM_CS1CDR_FLEXIO2_CLK_PRED( 7 ) );
  CCM_CS1CDR |= CCM_CS1CDR_FLEXIO2_CLK_PRED( 0b11 );
And its close to 31250 now, but still not quite. More like 34500.
Untitled 2.jpg
Blue is MIDI TX from Serial1 running at 31250.
Yellow is from the FlexIO pin.

EDIT** Resolved! I just had to divide the clockrate (FLEXIO1_CLOCK) in FlexSerial.cpp as well.
 
@Rezo - I've made some good progress which might be useful to you. I successfully got the interrupt method working. Functional proof of concept here:
https://github.com/easone/FlexIO_interrupt

The ISR needs to clear the interrupt signal - this is done by writing to the TIMSTAT register in case of a timer status interrupt, or writing data to the shifter buffers in case of a shifter status interrupt. Although, one subtlety is that the shifter status interrupt also triggers after a transfer is complete, so you have to disable the interrupt signal in the SHIFTSIEN register once the transfer is done... It's not sufficient to write to the SHIFTSTAT register to clear that interrupt, because clearing the shifter status flag also triggers FlexIO to shift more data.

I haven't played around with FlexIO for a few months now, but decided to try port my MicroMod display driver to the T4.1 using FlexIO3 and interrupts.
While I can get single beat transfers working without interrupts, I can't seem to get the who IRQ thing to work.

After the first burst of data out, the interrupt is triggered from SHIFTER_IRQ, at that point it should reload the shifter buffers with new data and continue transferring the data unit completed, but for some reason it's not. The teensy doesn't reboot or anything, it just gets stuck somewhere waiting for something.
The only major difference between the code example and my code (which I will publish soon) is that I am using FlexIO3 and not FlexIO2.
Besides removing FLEXIO_CTRL_FASTACC from the CTRL register, I don't recall any other difference between the two in term of basic functionality? Is my memory correct?
 
I got it working mostly.
Had to flip the bytes on the half words for 16bit color transfers.
But the disappointing part is:
A fully blocking transfer takes 9ms while an interrupt based transfer takes 95ms!!

I’ll keep playing with it, but I don’t have my hopes up :rolleyes:
 
When I was experimenting with this I had weird issues with FlexIO3. The same exact code works on FlexIO2 but doesn't work or works inconsistently on FlexIO3. I didn't figure out exactly what the difference was. I do know that FlexIO3 is incompatible with DMA - it does not have any working external triggers, so it cannot be triggered by DMA transfers - so you have to load the buffers with polling or interrupts. But I seem to remember having mysterious issues with interrupts and FlexIO3 like you are mentioning.
 
I think that makes sense - to use FASTACC the FlexIO base clock has to be at least two times the bus clock. But although FlexIO2 and FlexIO3 share the same base clock, FlexIO2 is connected to the (relatively) slow IP bus while FlexIO3 is connected to the faster AHB bus.
 
I just realized I had an 80ms delay in my custom callback that is triggered once all data is pushed out.
So it seem that full polling vs interrupt take the exact same time, down to the microsecond (15362uS/ 15ms) at 20Mhz bus speed.
Now my last challenge is to understand why the image comes though a bit fuzzy and offset to the right when I use the polling method.
IMG_0797.jpg
Top half is interrupt transfer, offset and fuzzy. Bottom half is polling and is crisp and clear.

Library can be found here if anyone is interested in having a go at it
https://github.com/david-res/ILI948x_t41_p
 
A bit of reading in the Reference manual and some chatGPT and I’ve got the interrupt method pushing out the data in the desired order:
Code:
  for (int i = 0; i < SHIFTNUM; i++) {
                uint32_t data = *readPtr++;
                p->SHIFTBUFBYS[i] = ((data >> 16) & 0xFFFF) | ((data << 16) & 0xFFFF0000);
        }
 
A bit of reading in the Reference manual and some chatGPT and I’ve got the interrupt method pushing out the data in the desired order:
Code:
  for (int i = 0; i < SHIFTNUM; i++) {
                uint32_t data = *readPtr++;
                p->SHIFTBUFBYS[i] = ((data >> 16) & 0xFFFF) | ((data << 16) & 0xFFFF0000);
        }

Interesting, is this some FlexIO3 weirdness or is it the same 16-bit word swap issue that we had issues with in September 2021 in this thread?
 
Is the same issue we had back in Sept 21..
Back then you whipped out some dark magic and got it running by loading the data in from end to start and swapping half words with the DMA engine.

Basically when you fill the shifter buffer with 32 bits it will start at the last 8 bits (bit 26), and decrement by 8 bits at a time until bit 7 for the last byte of data to be sent out.
So it gets sent out in reverse from how it came in.. god knows why it was designed like this
 
It's not FlexIO doing anything wrong, it outputs the bits from the shift buffer starting with the lowest through to the highest. The problem is the ILI9488 expects the upper byte (of each 16 bits) to be received first:
Screenshot 2023-04-22 045059.png
 
It's not FlexIO doing anything wrong, it outputs the bits from the shift buffer starting with the lowest through to the highest. The problem is the ILI9488 expects the upper byte (of each 16 bits) to be received first:
View attachment 30962

Interesting observation! I've seen that image tens of times yet never actually payed attention to what was in it..
I guess there's no way on the ILI9488 to overcome this based on the brief search I did in the 9488's datasheet
 
I'm now trying to implement the read function into both of my libraries but I'm not having any success..

Here is my read command function; the first part where it write the command works, as this was taken from a working write data function, the reading part doesn't work.
Code:
FASTRUN uint8_t ILI948x_t4_mm::readCommand(uint32_t const cmd){
  while(WR_DMATransferDone == false)
  {
    //Wait for any DMA transfers to complete
  }

    FlexIO_Config_SnglBeat();
    DCLow();

    /* Write command index */
    p->SHIFTBUF[0] = cmd;

    /*Wait for transfer to be completed */
    while(0 == (p->SHIFTSTAT & (1 << 0)))
    {
    }
    while(0 == (p->TIMSTAT & (1 << 0)))
            {  
            }

    /* De-assert RS pin */
    microSecondDelay();
    DCHigh();

    FlexIO_Config_SnglBeat_Read();

    uint32_t dummy;
    uint32_t data = 0;

    while (0 == (p->SHIFTSTAT & (1 << 0)))
        {
        }
    dummy = p->SHIFTBUF[0];

    while (0 == (p->SHIFTSTAT & (1 << 0)))
        {
        }
    data = p->SHIFTBUF[0];

    microSecondDelay();
    Serial.printf("Dummy 0x%x, data 0x%x\n", dummy, data);
    
    
    //Set FlexIO back to Write mode
    FlexIO_Config_SnglBeat();
  return data;
 

};

here is where I setup FlexIO for reading:
Code:
FASTRUN void ILI948x_t4_mm::FlexIO_Config_SnglBeat_Read()
{
  
    p->CTRL &= ~FLEXIO_CTRL_FLEXEN;
    p->CTRL |= FLEXIO_CTRL_SWRST;
    p->CTRL &= ~FLEXIO_CTRL_SWRST;

    gpioRead();

    /* Configure the shifters */
    p->SHIFTCFG[0] = 
        FLEXIO_SHIFTCFG_INSRC                                                     /* Pin input */
       | FLEXIO_SHIFTCFG_SSTOP(0)                                               /* Shifter stop bit disabled */
       | FLEXIO_SHIFTCFG_SSTART(0)                                             /* Shifter start bit disabled and loading data on enabled */
       | FLEXIO_SHIFTCFG_PWIDTH(7);                                            /* Bus width */
     
    p->SHIFTCTL[0] = 
        FLEXIO_SHIFTCTL_TIMSEL(0)                                              /* Shifter's assigned timer index */
      | FLEXIO_SHIFTCTL_TIMPOL*(0)                                             /* Shift on posedge of shift clock */
      | FLEXIO_SHIFTCTL_PINCFG(3)                                              /* Shifter's pin configured as output */
      | FLEXIO_SHIFTCTL_PINSEL(4)                                              /* Shifter's pin start index */
      | FLEXIO_SHIFTCTL_PINPOL*(0)                                             /* Shifter's pin active high */
      | FLEXIO_SHIFTCTL_SMOD(1);                                               /* Shifter mode as recieve */

    /* Configure the timer for shift clock */
    p->TIMCMP[0] = 
        (((1 * 2) - 1) << 8)                                                   /* TIMCMP[15:8] = number of beats x 2 – 1 */
      | ((_buad_div/2) - 1);                                                    /* TIMCMP[7:0] = baud rate divider / 2 – 1 */
    
    p->TIMCFG[0] = 
        FLEXIO_TIMCFG_TIMOUT(0)                                                /* Timer output logic one when enabled and not affected by reset */
      | FLEXIO_TIMCFG_TIMDEC(0)                                                /* Timer decrement on FlexIO clock, shift clock equals timer output */
      | FLEXIO_TIMCFG_TIMRST(0)                                                /* Timer never reset */
      | FLEXIO_TIMCFG_TIMDIS(2)                                                /* Timer disabled on timer compare */
      | FLEXIO_TIMCFG_TIMENA(2)                                                /* Timer enabled on trigger high */
      | FLEXIO_TIMCFG_TSTOP(0)                                                 /* Timer stop bit disabled */
      | FLEXIO_TIMCFG_TSTART*(0);                                              /* Timer start bit disabled */
    
    p->TIMCTL[0] = 
        FLEXIO_TIMCTL_TRGSEL((((0) << 2) | 1))                                 /* Timer trigger selected as shifter's status flag */
      | FLEXIO_TIMCTL_TRGPOL*(1)                                               /* Timer trigger polarity as active low */
      | FLEXIO_TIMCTL_TRGSRC*(1)                                               /* Timer trigger source as internal */
      | FLEXIO_TIMCTL_PINCFG(3)                                                /* Timer' pin configured as output */
      | FLEXIO_TIMCTL_PINSEL(1)                                                /* Timer' pin index: RD pin */
      | FLEXIO_TIMCTL_PINPOL*(1)                                               /* Timer' pin active low */
      | FLEXIO_TIMCTL_TIMOD(1);                                                /* Timer mode as dual 8-bit counters baud/bit */

    /* Enable FlexIO */
   p->CTRL |= FLEXIO_CTRL_FLEXEN;      

}

And here are two functions that switch the RD/WR pins between FlexIO and GPIO:
Code:
FASTRUN void ILI948x_t4_mm::gpioWrite(){
  pFlex->setIOPinToFlexMode(10);
  pinMode(12, OUTPUT);
  digitalWriteFast(12, HIGH);
}

FASTRUN void ILI948x_t4_mm::gpioRead(){
  pFlex->setIOPinToFlexMode(12);
  pinMode(10, OUTPUT);
  digitalWriteFast(10, HIGH);
}


Is there anything I am missing in the FlexIO config?
 
Only Shifters 3 and 7 can do parallel reads from pins (PWIDTH > 0). Similar to how only shifters 0 and 4 can do parallel writes.
You also need to set the pins as inputs rather than outputs (PINCFG).
 
Only Shifters 3 and 7 can do parallel reads from pins (PWIDTH > 0). Similar to how only shifters 0 and 4 can do parallel writes.
You also need to set the pins as inputs rather than outputs (PINCFG).

It's fundamental things like these that I forgot over the past two years since originally writing the library with @easone.

Alight, so I change FlexIO config to use shifter 3, and set the pins as inputs. Also adjusted FLEXIO_TIMCTL_TRGSEL to point to shifter 3.
I lowered the baud rate too as I know reading from the ILI9488 does not work at the same speed as writing.
Code:
p->SHIFTCFG[3] = 
        FLEXIO_SHIFTCFG_INSRC                                                  /* Shifter input */
       | FLEXIO_SHIFTCFG_SSTOP(0)                                              /* Shifter stop bit disabled */
       | FLEXIO_SHIFTCFG_SSTART(0)                                             /* Shifter start bit disabled and loading data on enabled */
       | FLEXIO_SHIFTCFG_PWIDTH(7);                                            /* Bus width */
     
    p->SHIFTCTL[3] = 
        FLEXIO_SHIFTCTL_TIMSEL(0)                                              /* Shifter's assigned timer index */
      | FLEXIO_SHIFTCTL_TIMPOL*(0)                                             /* Shift on posedge of shift clock */
      | FLEXIO_SHIFTCTL_PINCFG(0)                                              /* Shifter's pin configured as input */
      | FLEXIO_SHIFTCTL_PINSEL(4)                                              /* Shifter's pin start index */
      | FLEXIO_SHIFTCTL_PINPOL*(0)                                             /* Shifter's pin active high */
      | FLEXIO_SHIFTCTL_SMOD(1);                                               /* Shifter mode as recieve */

    /* Configure the timer for shift clock */
    p->TIMCMP[0] = 
        (((1 * 2) - 1) << 8)                                                   /* TIMCMP[15:8] = number of beats x 2 – 1 */
      | (((30)/2) - 1);                                                        /* TIMCMP[7:0] = baud rate divider / 2 – 1 ::: 30 = 8Mhz with current controller speed */
    
    p->TIMCFG[0] = 
        FLEXIO_TIMCFG_TIMOUT(0)                                                /* Timer output logic one when enabled and not affected by reset */
      | FLEXIO_TIMCFG_TIMDEC(0)                                                /* Timer decrement on FlexIO clock, shift clock equals timer output */
      | FLEXIO_TIMCFG_TIMRST(0)                                                /* Timer never reset */
      | FLEXIO_TIMCFG_TIMDIS(2)                                                /* Timer disabled on timer compare */
      | FLEXIO_TIMCFG_TIMENA(2)                                                /* Timer enabled on trigger high */
      | FLEXIO_TIMCFG_TSTOP(0)                                                 /* Timer stop bit disabled */
      | FLEXIO_TIMCFG_TSTART*(0);                                              /* Timer start bit disabled */
    
    p->TIMCTL[0] = 
        FLEXIO_TIMCTL_TRGSEL((((3) << 2) | 1))                                 /* Timer trigger selected as shifter's status flag */
      | FLEXIO_TIMCTL_TRGPOL*(1)                                               /* Timer trigger polarity as active low */
      | FLEXIO_TIMCTL_TRGSRC*(1)                                               /* Timer trigger source as internal */
      | FLEXIO_TIMCTL_PINCFG(3)                                                /* Timer' pin configured as output */
      | FLEXIO_TIMCTL_PINSEL(1)                                                /* Timer' pin index: WR pin */
      | FLEXIO_TIMCTL_PINPOL*(1)                                               /* Timer' pin active low */
      | FLEXIO_TIMCTL_TIMOD(1);                                                /* Timer mode as dual 8-bit counters baud/bit */

But still getting nothing..
Is the way I read data from the shifters correct as well?
Code:
FlexIO_Config_SnglBeat_Read();

    uint32_t dummy;
    uint32_t data = 0;

    while (0 == (p->SHIFTSTAT & (1 << 3)))
        {
        }
    dummy = p->SHIFTBUF[3];

    while (0 == (p->SHIFTSTAT & (1 << 3)))
        {
        }
    data = p->SHIFTBUF[3];
 
You want to get rid of FLEXIO_SHIFTCFG_INSRC, because that makes the shifter pull data from shiftbuf4 instead of the pins.
 
Back
Top