Hi,
I'm using a K22 as MCU for my project. I used Processor Expert 10.4.2 to set up the configuration. I've managed to use the SPI peripheral with the DMA support.
Basically, I configured the SPI component from the PE with the interrupt service/event flag disabled, and added two different DMA channel for RX/TX.
Briefly, the DMA channels in PE are configured as follows:
TX channel
- hardware request flag enabled (SPI0_Transmit_DMA_Request);
- channel select fixed (#0);
- source transaction 0 / transaction size 32bit / address adjustment 4 / address modulo disabled
- destination start address 0x4002C034 / transaction size 32bit / address adjustment 0 / address modulo disabled
- transactions count 1 / request count 1 / bandwidth control no stalls / disable peripheral request flag enabled (after transfer complete)
/* Channel configuration structure */
DMA_PE_TChnInit const SPI_TX_DMA_PE_ChInit = {
/* Logical channel number */
DMA_PE_STATIC_CHANNEL_0, /* Phy channel: DMA_Channel0 */
{ /* TCD initial settings */
DMA_SADDR_SADDR(0x00), /* TCD_SADDR register initial value */
DMA_SOFF_SOFF(0x04), /* TCD_SOFF register initial value */
( DMA_ATTR_SMOD(0x00) |
DMA_ATTR_SSIZE(0x02) |
DMA_ATTR_DMOD(0x00) |
DMA_ATTR_DSIZE(0x02)
), /* TCD_ATTR register initial value */
DMA_NBYTES_MLNO_NBYTES(0x04), /* TCD_NBYTES_MLNO register initial value */
DMA_SLAST_SLAST(0x00), /* TCD_SLAST register initial value */
DMA_DADDR_DADDR(0x4002C034), /* TCD_DADDR register initial value */
DMA_DOFF_DOFF(0x00), /* TCD_DOFF register initial value */
(DMA_CITER_ELINKYES_LINKCH(0x00) | DMA_CITER_ELINKYES_CITER(0x01)), /* TCD_CITER_ELINKYES register initial value */
DMA_DLAST_SGA_DLASTSGA(0x00), /* TCD_DLASTSGA register initial value */
( DMA_CSR_BWC(0x02) |
DMA_CSR_MAJORLINKCH(0x00) |
DMA_CSR_DREQ_MASK |
DMA_CSR_INTMAJOR_MASK
), /* TCD_CSR register initial value */
(DMA_BITER_ELINKYES_LINKCH(0x00) | DMA_BITER_ELINKYES_BITER(0x01)) /* TCD_BITER_ELINKYES register initial value */
},
/* DMAMUX initial settings */
(DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(0x0F)), /* DMA multiplexor configuration register initial value 'SPI0_Transmit_DMA_Request' */
{ /* Events initial setting */
&SPI_TX_DMA_PE_OnComplete /* OnComplete callback address */
},
FALSE /* Channel state variable initial value */
};
RX channel
- hardware request flag enabled (SPI0_Receive_DMA_Request);
- channel select fixed (#1);
- source transaction 0x4002C038 / transaction size 32bit / address adjustment 0 / address modulo disabled
- destination start address 0/ transaction size 32bit / address adjustment 4 / address modulo disabled
- transactions count 1 / request count 1 / bandwidth control no stalls / disable peripheral request flag enabled (after transfer complete)
/* Channel configuration structure */
DMA_PE_TChnInit const SPI_RX_DMA_PE_ChInit = {
/* Logical channel number */
DMA_PE_STATIC_CHANNEL_1, /* Phy channel: DMA_Channel1 */
{ /* TCD initial settings */
DMA_SADDR_SADDR(0x4002C038), /* TCD_SADDR register initial value */
DMA_SOFF_SOFF(0x00), /* TCD_SOFF register initial value */
( DMA_ATTR_SMOD(0x00) |
DMA_ATTR_SSIZE(0x02) |
DMA_ATTR_DMOD(0x00) |
DMA_ATTR_DSIZE(0x02)
), /* TCD_ATTR register initial value */
DMA_NBYTES_MLNO_NBYTES(0x04), /* TCD_NBYTES_MLNO register initial value */
DMA_SLAST_SLAST(0x00), /* TCD_SLAST register initial value */
DMA_DADDR_DADDR(0x00), /* TCD_DADDR register initial value */
DMA_DOFF_DOFF(0x04), /* TCD_DOFF register initial value */
(DMA_CITER_ELINKYES_LINKCH(0x00) | DMA_CITER_ELINKYES_CITER(0x01)), /* TCD_CITER_ELINKYES register initial value */
DMA_DLAST_SGA_DLASTSGA(0x00), /* TCD_DLASTSGA register initial value */
( DMA_CSR_BWC(0x00) |
DMA_CSR_MAJORLINKCH(0x00) |
DMA_CSR_DREQ_MASK |
DMA_CSR_INTMAJOR_MASK
), /* TCD_CSR register initial value */
(DMA_BITER_ELINKYES_LINKCH(0x00) | DMA_BITER_ELINKYES_BITER(0x01)) /* TCD_BITER_ELINKYES register initial value */
},
/* DMAMUX initial settings */
(DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(0x0E)), /* DMA multiplexor configuration register initial value 'SPI0_Receive_DMA_Request' */
{ /* Events initial setting */
&SPI_RX_DMA_PE_OnComplete /* OnComplete callback address */
},
FALSE /* Channel state variable initial value */
};
In my SPI driver I use a single buffer for RX/TX.
static int32 i32usgBuffer[1024];
These are the init - read - write functions of my SPI driver (SWIxxx functions are wrappers for FreeRTOS).
INIT
int8s spi_init (const void* vpParameters)
{
int8s i8slRes = SPI_ERR_INIT;
/* Call PE SPI peripheral initialization function. */
if (SPI_PE_Init (NULL) &&
SPI_TX_DMA_PE_Init (NULL) &&
SPI_RX_DMA_PE_Init (NULL) &&
!SPI_TX_DMA_PE_Enable (SPI_TX_DMA_PE_DeviceData) &&
!SPI_RX_DMA_PE_Enable (SPI_RX_DMA_PE_DeviceData) &&
!SPI_PE_Enable (SPI_PE_DeviceData))
{
SPI_PDD_EnableDmasInterrupts (SPI0_BASE_PTR, SPI_PDD_TX_FIFO_FILL_DMA | SPI_PDD_RX_FIFO_DRAIN_DMA);
/* No error occurred: initialize peripheral mutex then. */
if (!tsMutex)
{
tsMutex = SWIMutexCreate ();
}
/* Initialize RX semaphore (max count 1, initial count 0). */
if (!tsRxSemaphore)
{
tsRxSemaphore = SWISemaphoreCreate (1, 0);
}
/* Initialize TX semaphore (max count 1, initial count 1). */
if (!tsTxSemaphore)
{
tsTxSemaphore = SWISemaphoreCreate (1, 1);
}
i8slRes = SPI_OK;
}
/* Driver initialization succeed. */
return i8slRes;
}
READ
int16s spi_read (const void *vpParameters, void *vpBuffer, int16u i16upSize)
{
int16u i16ulI;
typeSPIChipSelect tlChipSelect;
int8s i8ulRes = SPI_OK;
if (!tsMutex)
{
return SPI_ERR_INIT;
}
if ((vpParameters != NULL) && (vpBuffer != NULL))
{
if (i16upSize)
{
if (i16upSize > SPI_BUFFER_SIZE)
{
/* Trying to send more bytes than local buffer size. */
return SPI_ERR_SIZE_OVERFLOW;
}
/*
* Take the peripheral mutex. No checks are performed on return types
* since it blocks indefinitely.
*/
SWIMutexTake (tsMutex, SWI_BLOCK_INDEFINITELY);
/* Wait indefinitely till the the transmitter is ready to be used. */
SWISemaphoreGet (tsTxSemaphore, SWI_BLOCK_INDEFINITELY);
/* Retrieve chip select to read. */
tlChipSelect = *((typeSPIChipSelect*) vpParameters);
/* Configure SPI transfers (32-bit write access at SPI_PUSHR). */
for (i16ulI = 0; i16ulI < (i16upSize - 1); i16ulI++)
{
i32usgBuffer[i16ulI] = ((int8u*) vpBuffer)[i16ulI] | 0x80000000U | CSToMask[tlChipSelect];
}
/* De-assert chip select when sending the last byte */
i32usgBuffer[i16ulI] = (((int8u*) vpBuffer)[i16ulI] | CSToMask[tlChipSelect]) & 0x7FFFFFFFU;
/* Configure the transfer. */
if (SPI_PE_SelectConfiguration (SPI_PE_DeviceData, tlChipSelect, i8usgConfigLookupTab[tlChipSelect]) ||
SPI_TX_DMA_PE_SetSourceAddress (SPI_TX_DMA_PE_DeviceData, i32usgBuffer) ||
SPI_TX_DMA_PE_SetRequestCount (SPI_TX_DMA_PE_DeviceData, i16upSize) ||
SPI_TX_DMA_PE_EnableRequest (SPI_TX_DMA_PE_DeviceData) ||
SPI_RX_DMA_PE_SetDestinationAddress (SPI_RX_DMA_PE_DeviceData, i32usgBuffer) ||
SPI_RX_DMA_PE_SetRequestCount (SPI_RX_DMA_PE_DeviceData, i16upSize) ||
SPI_RX_DMA_PE_EnableRequest (SPI_RX_DMA_PE_DeviceData))
{
/* Set error flag (any value). */
i8ulRes = 1;
}
else
{
/* Enable RX and TX interrupts! */
SPI_PDD_ClearRxFIFO (SPI0_BASE_PTR);
SPI_PDD_ClearInterruptFlags (SPI0_BASE_PTR, SPI_PDD_RX_FIFO_DRAIN_INT_DMA);
SPI_PDD_EnableDmasInterrupts (SPI0_BASE_PTR, SPI_PDD_RX_FIFO_DRAIN_INT_DMA);
SPI_PDD_ClearTxFIFO (SPI0_BASE_PTR);
SPI_PDD_ClearInterruptFlags (SPI0_BASE_PTR, SPI_PDD_TX_FIFO_FILL_INT_DMA);
SPI_PDD_EnableDmasInterrupts (SPI0_BASE_PTR, SPI_PDD_TX_FIFO_FILL_INT_DMA);
/*
* Take RX semaphore (wait till ISR generate a 'give semaphore'
* event, reporting that the block has been received).
*/
SWISemaphoreGet (tsRxSemaphore, SWI_BLOCK_INDEFINITELY);
}
/* Check if SPI_PE driver failed. */
if (i8ulRes)
{
/* Return 0 bytes read. */
i16upSize = 0;
}
else
{
/* Copy from local buffer (32-bit) to destination buffer (8-bit). */
for (i16ulI = 0; i16ulI < i16upSize; i16ulI++)
{
((int8u*) vpBuffer)[i16ulI] = (int8u) i32usgBuffer[i16ulI];
}
}
/* Release peripheral mutex. */
SWIMutexGive (tsMutex);
}
/* Return number of bytes read. */
return i16upSize;
}
/* NULL pointer passed as parameters. */
return SPI_ERR_BAD_PARAMETERS;
}
WRITE
int16s spi_write (const void *vpParameters, const void *vpBuffer, int16u i16upSize)
{
int16u i16ulI;
typeSPIChipSelect tlChipSelect;
int8s i8ulRes = SPI_OK;
if (!tsMutex)
{
return SPI_ERR_INIT;
}
if ((vpParameters != NULL) && (vpBuffer != NULL))
{
if (i16upSize)
{
if (i16upSize > SPI_BUFFER_SIZE)
{
/* Trying to send more bytes than local buffer size. */
return SPI_ERR_SIZE_OVERFLOW;
}
/*
* Take the peripheral mutex. No checks are performed on return types
* since it blocks indefinitely.
*/
SWIMutexTake (tsMutex, SWI_BLOCK_INDEFINITELY);
/* Wait indefinitely till the the transmitter is ready to be used. */
SWISemaphoreGet (tsTxSemaphore, SWI_BLOCK_INDEFINITELY);
/* Retrieve chip select. */
tlChipSelect = *((typeSPIChipSelect*) vpParameters);
/* Configure SPI transfers (32-bit write access at SPI_PUSHR). */
for (i16ulI = 0; i16ulI < (i16upSize - 1); i16ulI++)
{
i32usgBuffer[i16ulI] = ((int8u*) vpBuffer)[i16ulI] | 0x80000000U | CSToMask[tlChipSelect];
}
/* De-assert chip select when sending the last byte */
i32usgBuffer[i16ulI] = (((int8u*) vpBuffer)[i16ulI] | CSToMask[tlChipSelect]) & 0x7FFFFFFFU;
/* Configure the transfer. */
if (SPI_PE_SelectConfiguration (SPI_PE_DeviceData, tlChipSelect, i8usgConfigLookupTab[tlChipSelect]) ||
SPI_TX_DMA_PE_SetSourceAddress (SPI_TX_DMA_PE_DeviceData, i32usgBuffer) ||
SPI_TX_DMA_PE_SetRequestCount (SPI_TX_DMA_PE_DeviceData, i16upSize) ||
SPI_TX_DMA_PE_EnableRequest (SPI_TX_DMA_PE_DeviceData))
{
/* Set error flag (any value). */
i8ulRes = 1;
}
else
{
/* Enable TX! */
SPI_PDD_ClearTxFIFO (SPI0_BASE_PTR);
SPI_PDD_ClearInterruptFlags (SPI0_BASE_PTR, SPI_PDD_TX_FIFO_FILL_INT_DMA);
SPI_PDD_EnableDmasInterrupts (SPI0_BASE_PTR, SPI_PDD_TX_FIFO_FILL_INT_DMA);
}
/* Check if SPI_PE driver failed. */
if (i8ulRes)
{
/* Return 0 bytes written. */
i16upSize = 0;
}
/* Release peripheral mutex. */
SWIMutexGive (tsMutex);
}
/* Return number of bytes written. */
return i16upSize;
}
/* NULL pointer passed as parameters. */
return SPI_ERR_BAD_PARAMETERS;
}
These are my "on complete" callbacks
void SPI_TX_DMA_PE_OnComplete(void* UserDataPtr)
{
/* Disable TX. */
SPI_PDD_DisableDmasInterrupts (SPI0_BASE_PTR, SPI_PDD_TX_FIFO_FILL_INT_DMA);
/* Release TX semaphore: peripheral is ready for new operations). */
SWISemaphorePutFromISR (tsTxSemaphore);
}
void SPI_RX_DMA_PE_OnComplete(void *UserDataPtr)
{
/* Disable RX. */
SPI_PDD_DisableDmasInterrupts (SPI0_BASE_PTR, SPI_PDD_RX_FIFO_DRAIN_INT_DMA);
/* Release RX semaphore (block received, task can then continue). */
SWISemaphorePutFromISR (tsRxSemaphore);
}
The driver seems to works fine, except for the fact that in particular conditions, alternating write/read calls, SPI SCK is clocked with the chip select de-asserted as you can see in the following image.

Am I missing something (wrong DMA/SPI configuration)? Using the interrupt version w/o the DMA all is working w/o problems.
Alessandro