
Originally Posted by
Jean-Marc
Is it possible to combine 4 shift registers to store 16 pixels in one go instead of 4?
How do I do this?
I haven't been able to test the following for VGA, but it is nearly the same as the configuration I am using for the SmartMatrix driver. Please forgive any errors!
First, you need to configure all 4 shift buffers. Set SHIFTCFG[INSRC] = 1 for each shifter to use the next shifter's output as input. Only shifter 0 can drive parallel pins, so the pins need to be disabled for shifters 1-3.
Code:
uint32_t timerSelect, timerPolarity, pinConfig, pinSelect, pinPolarity, shifterMode, parallelWidth, inputSource, stopBit, startBit;
uint32_t triggerSelect, triggerPolarity, triggerSource, timerMode, timerOutput, timerDecrement, timerReset, timerDisable, timerEnable;
// Shifter 0 registers
parallelWidth = FLEXIO_SHIFTCFG_PWIDTH(7); // 8-bit parallel shift width
pinSelect = FLEXIO_SHIFTCTL_PINSEL(0); // Select pins FXIO_D0 through FXIO_D7
inputSource = FLEXIO_SHIFTCFG_INSRC*(1); // Input source from next shifter
stopBit = FLEXIO_SHIFTCFG_SSTOP(0); // Stop bit disabled
startBit = FLEXIO_SHIFTCFG_SSTART(0); // Start bit disabled, transmitter loads data on enable
timerSelect = FLEXIO_SHIFTCTL_TIMSEL(0); // Use timer 0
timerPolarity = FLEXIO_SHIFTCTL_TIMPOL*(1); // Shift on negedge of clock
pinConfig = FLEXIO_SHIFTCTL_PINCFG(3); // Shifter pin output
pinPolarity = FLEXIO_SHIFTCTL_PINPOL*(0); // Shifter pin active high polarity
shifterMode = FLEXIO_SHIFTCTL_SMOD(2); // Shifter transmit mode
FLEXIO2_SHIFTCFG0 = parallelWidth | inputSource | stopBit | startBit;
FLEXIO2_SHIFTCTL0 = timerSelect | timerPolarity | pinConfig | pinSelect | pinPolarity | shifterMode;
// Shifter 1-3 registers are identical except with pin output disabled
parallelWidth = FLEXIO_SHIFTCFG_PWIDTH(7); // 8-bit parallel shift width
inputSource = FLEXIO_SHIFTCFG_INSRC*(1); // Input source from next shifter
stopBit = FLEXIO_SHIFTCFG_SSTOP(0); // Stop bit disabled
startBit = FLEXIO_SHIFTCFG_SSTART(0); // Start bit disabled, transmitter loads data on enable
timerSelect = FLEXIO_SHIFTCTL_TIMSEL(0); // Use timer 0
timerPolarity = FLEXIO_SHIFTCTL_TIMPOL*(1); // Shift on negedge of clock
pinConfig = FLEXIO_SHIFTCTL_PINCFG(0); // Shifter pin output disabled
shifterMode = FLEXIO_SHIFTCTL_SMOD(2); // Shifter transmit mode
FLEXIO2_SHIFTCFG1 = parallelWidth | inputSource | stopBit | startBit;
FLEXIO2_SHIFTCTL1 = timerSelect | timerPolarity | pinConfig | shifterMode;
FLEXIO2_SHIFTCFG2 = parallelWidth | inputSource | stopBit | startBit;
FLEXIO2_SHIFTCTL2 = timerSelect | timerPolarity | pinConfig | shifterMode;
FLEXIO2_SHIFTCFG3 = parallelWidth | inputSource | stopBit | startBit;
FLEXIO2_SHIFTCTL3 = timerSelect | timerPolarity | pinConfig | shifterMode;
The FlexIO timer and the DMA request should be configured to trigger on Shifter 3 instead of Shifter 0 (because shift buffer 3 will be loaded last), and the SHIFTS_PER_TRANSFER is 16 instead of 4:
Code:
// Timer 0 registers
timerOutput = FLEXIO_TIMCFG_TIMOUT(1); // Timer output is logic zero when enabled and is not affected by the Timer reset
timerDecrement = FLEXIO_TIMCFG_TIMDEC(0); // Timer decrements on FlexIO clock, shift clock equals timer output
timerReset = FLEXIO_TIMCFG_TIMRST(0); // Timer never reset
timerDisable = FLEXIO_TIMCFG_TIMDIS(2); // Timer disabled on Timer compare
timerEnable = FLEXIO_TIMCFG_TIMENA(2); // Timer enabled on Trigger assert
stopBit = FLEXIO_TIMCFG_TSTOP(0); // Stop bit disabled
startBit = FLEXIO_TIMCFG_TSTART*(0); // Start bit disabled
triggerSelect = FLEXIO_TIMCTL_TRGSEL(1+4*(3)); // Trigger select Shifter 3 status flag
triggerPolarity = FLEXIO_TIMCTL_TRGPOL*(1); // Trigger active low
triggerSource = FLEXIO_TIMCTL_TRGSRC*(1); // Internal trigger selected
pinConfig = FLEXIO_TIMCTL_PINCFG(0); // Timer pin output disabled
timerMode = FLEXIO_TIMCTL_TIMOD(1); // Dual 8-bit counters baud mode
#define SHIFTS_PER_TRANSFER 16 // Shift out 8 bits 16 times with every transfer = four 32-bit words = contents of Shifters 0-3
FLEXIO2_TIMCFG0 = timerOutput | timerDecrement | timerReset | timerDisable | timerEnable | stopBit | startBit;
FLEXIO2_TIMCTL0 = triggerSelect | triggerPolarity | triggerSource | pinConfig | timerMode;
FLEXIO2_TIMCMP0 = ((SHIFTS_PER_TRANSFER*2-1)<<8) | ((flexio_clock_div/2-1)<<0);
FLEXIO2_SHIFTSDEN |= (1<<3);
To configure the DMA transfer to fill up all 4 buffers in a single burst each time it is triggered, you can use a minor loop offset to reset the destination address after each burst. Conveniently, the FlexIO buffer registers are adjacent in memory space. The number of pixels in each line needs to be a multiple of 16.
Code:
unsigned int minorLoopBytes, minorLoopIterations, majorLoopBytes, majorLoopIterations;
int destinationAddressOffset, destinationAddressLastOffset, sourceAddressOffset, sourceAddressLastOffset, minorLoopOffset;
volatile uint32_t *destinationAddress, *sourceAddress;
DMA_CR |= DMA_CR_EMLM; // Enable minor loop mapping so that we can have a minor loop offset
minorLoopIterations = 4; // transfer 4 words with each DMA trigger into 4 FlexIO buffers
minorLoopBytes = minorLoopIterations * sizeof(uint32_t);
#define BYTES_PER_PIXEL 1
majorLoopBytes = maxpixperline * BYTES_PER_PIXEL; // This must be evenly divisible by 16
majorLoopIterations = majorLoopBytes / minorLoopBytes;
sourceAddress = (uint32_t*) & gfxbuffer[0];
sourceAddressOffset = sizeof(uint32_t);
sourceAddressLastOffset = - majorLoopBytes; // at completion of major loop, reset source address
destinationAddress = &FLEXIO2_SHIFTBUF0;
destinationAddressOffset = sizeof(uint32_t);
minorLoopOffset = -minorLoopIterations * destinationAddressOffset; // reset destination address at end of each minor loop...
destinationAddressLastOffset = minorLoopOffset; // ...and at end of major loop
flexio2DMA.TCD->SADDR = sourceAddress;
flexio2DMA.TCD->SOFF = sourceAddressOffset;
flexio2DMA.TCD->SLAST = sourceAddressLastOffset;
flexio2DMA.TCD->ATTR_SRC = DMA_TCD_ATTR_SIZE_32BIT;
flexio2DMA.TCD->DADDR = destinationAddress;
flexio2DMA.TCD->DOFF = destinationAddressOffset;
flexio2DMA.TCD->ATTR_DST = DMA_TCD_ATTR_SIZE_32BIT;
flexio2DMA.TCD->DLASTSGA = destinationAddressLastOffset;
flexio2DMA.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE | DMA_TCD_NBYTES_MLOFFYES_MLOFF(minorLoopOffset) | DMA_TCD_NBYTES_MLOFFYES_NBYTES(minorLoopBytes);
flexio2DMA.TCD->BITER = majorLoopIterations;
flexio2DMA.TCD->CITER = majorLoopIterations;
flexio2DMA.disableOnCompletion(); // disable on completion or else it will be triggered by FlexIO continuously
By the way, I would configure both FlexIO1 and FlexIO2 to use FLEXIO_SHIFTCFG_PWIDTH(7) and FLEXIO_SHIFTCTL_PINSEL(0). This will select signals FXIO_D0 through FXIO_D7 to be output on both FlexIOs, but since the Teensy 4.1 only has pins 0 through 3 on FlexIO2 and pins 4 through 7 on FlexIO1, the extra signals will be discarded. And this should enable you to use FLEXIO2_SHIFTBUF0 and FLEXIO1_SHIFTBUF0 as your destination registers - no need to use the nibble byte swapped register FLEXIO1_SHIFTBUFNBS0 or to use an unaligned copy.
Hope that helps...