Hi
Below is the code to do this in the uTasker project:
#define SPI_DMA_LENGTH 11
#define SPI_DMA_CHANNEL 7
// PIT 0 interrupt callback
//
static __callback_interrupt void _startTx(void)
{
static unsigned long ulSPI_TxBuffer[SPI_DMA_LENGTH] = {0}; // tx buffer
unsigned char ucByteValue = (unsigned char)ulSPI_TxBuffer[0];
WRITE_ONE_TO_CLEAR(SPI1_SR, (SPI_SR_EOQF)); // clear the end of queue flag
while (i < (SPI_DMA_LENGTH - 1)) { // prepare a tx data pattern
ulSPI_TxBuffer[i++] = (SPI_PUSHR_CONT | SPI_PUSHR_CTAS_CTAR0 | SPI_PUSHR_PCS0 | ++ucByteValue);
}
ulSPI_TxBuffer[i] = (SPI_PUSHR_EOQ | SPI_PUSHR_CTAS_CTAR0 | SPI_PUSHR_PCS0 | ++ucByteValue); // negate CS line after final bytes is sent
// Configure Tx DMA (buffer to SPI, single buffer with no interrupt on termination)
//
fnConfigDMA_buffer(SPI_DMA_CHANNEL, DMAMUX0_CHCFG_SOURCE_SPI1_TX, sizeof(ulSPI_TxBuffer), &ulSPI_TxBuffer[0], (void *)SPI1_PUSHR_ADDR, (DMA_LONG_WORDS | DMA_DIRECTION_OUTPUT | DMA_SINGLE_CYCLE), 0, PRIORITY_DMA10);
fnDMA_BufferReset(SPI_DMA_CHANNEL, DMA_BUFFER_START); // start the transfer
}
// PIT and SPI configuration
//
static void fnConfigurePIT_SPI(void)
{
PIT_SETUP pit_setup; // PIT interrupt configuration parameters
// Configure SPI
//
POWER_UP_ATOMIC(6, SPI1);
_CONFIG_PERIPHERAL(D, 4, (PD_4_SPI1_PCS0 | PORT_SRE_FAST | PORT_DSE_HIGH));
_CONFIG_PERIPHERAL(D, 5, (PD_5_SPI1_SCK | PORT_SRE_FAST | PORT_DSE_HIGH));
_CONFIG_PERIPHERAL(D, 7, (PD_7_SPI1_SIN | PORT_SRE_FAST | PORT_DSE_HIGH));
_CONFIG_PERIPHERAL(D, 6, (PD_6_SPI1_SOUT));
SPI1_MCR = (SPI_MCR_MSTR | SPI_MCR_DCONF_SPI | SPI_MCR_CLR_RXF | SPI_MCR_CLR_TXF | SPI_MCR_PCSIS_CS0 | SPI_MCR_PCSIS_CS1 | SPI_MCR_PCSIS_CS2 | SPI_MCR_PCSIS_CS3 | SPI_MCR_PCSIS_CS4 | SPI_MCR_PCSIS_CS5);
SPI1_CTAR0 = (SPI_CTAR_DBR | SPI_CTAR_FMSZ_8 | SPI_CTAR_PDT_7 | SPI_CTAR_BR_2);
SPI1_RSER = (SPI_SRER_TFFF_DIRS | SPI_SRER_TFFF_RE); // enable DMA request when tx fifo not full
pit_setup.int_type = PIT_INTERRUPT;
pit_setup.mode = PIT_PERIODIC; // PIT periodic interrupt
pit_setup.ucPIT = 0; // use PIT0
pit_setup.int_handler = _startTx; // PIT interrupt callback
pit_setup.int_priority = PIT0_INTERRUPT_PRIORITY;
pit_setup.count_delay = PIT_FREERUN_FREQ(64000); // 64kHz interrupt rate
fnConfigureInterrupt((void *)&pit_setup); // configure and start the PIT
}
This is the output showing 11 bytes sent 64000 times a second:
mjbcswitzerland_0-1618283085029.png
Closer look:
mjbcswitzerland_1-1618283153756.png
Some notes:
It is not absolutely necessary to use a DMA completion interrupt since the PIT interrupt can simply configure everything needed each time it starts the transfer.
It IS IMPORTANT to clear the EOQF flag in the status register otherwise it will not transmit a second time.
There is no need to enable and disable the SPI since if the DMA is not active it won't send anything anyway.
Although only a detail it is not recommended to clear flags with
SPI0_SR |= SPI_SR_EOQF_MASK | SPI_SR_TCF_MASK | SPI_SR_RFDF_MASK | SPI_SR_TFFF_MASK | SPI_SR_RFDF_MASK
but correctly they should be cleared with (not ORing with what is read)
SPI0_SR = SPI_SR_EOQF_MASK | SPI_SR_TCF_MASK | SPI_SR_RFDF_MASK | SPI_SR_TFFF_MASK | SPI_SR_RFDF_MASK
Although the DMA API you are using needs a lot of careful setup (and error prone due to this) I didn't actually spot anything that was incorrectly configured. Therefore check that the EOQF clear is not somehow being optimised away and try not disabling/enabling the SPI between transfers.
Regards
Mark
[uTasker project developer for Kinetis and i.MX RT]
Contact me by personal message or on the uTasker web site to discuss professional training, solutions to problems or rapid product development requirements
For professionals searching for faster, problem-free Kinetis and i.MX RT 10xx developments the uTasker project holds the key: https://www.utasker.com/iMX/RT1064.html