Hi
There is code in the open source uTasker project.
This is the extract from the developer's version (more advanced and supported). Eg. for any UART (channel is 0..5)
fnConfigDMA_buffer(UART_DMA_RX_CHANNEL[channel], (DMAMUX_CHCFG_SOURCE_UART0_RX + (2 * channel)), buffer_length, uart_data_reg, (void *)buffer_address, (DMA_DIRECTION_INPUT | DMA_BYTES), 0, 0);
so for SPI slave uart_data_reg is replaced by the SPI rx reg address and DMAMUX_CHCFG_SOURCE_UART0_RX by DMAMUX_CHCFG_SOURCE_SPI0_RX.If the SPI is not in 8 bit mode DMA_HALF_WORDS or DMA_LONG_WORDS is defined to suit (instead of DMA_BYTES).
fnConfigDMA_buffer() works for all DMA controller types (compatible on all K or KL parts with DMA) and free-running mode is set when no full-buffer interrupt or half-buffer-interupt is defined (last parameter 0).
Complete routine follows so you can pick out the bits of interest.
Note that the uTasker project also simulates the DMA operation in Visual Studio so that it can be completely verified (as well a Kinetis peripherals, interrupts etc.) so will typically allow developments (debugging, testing, validating) in a fraction of the time using traditional techniques.
Regards
Mark
extern void fnConfigDMA_buffer(unsigned char ucDMA_channel, unsigned short usDmaTriggerSource, unsigned long ulBufLength, void *ptrBufSource, void *ptrBufDest, unsigned long ulRules, void (*int_handler)(void), int int_priority)
{
unsigned char ucSize = (unsigned char)(ulRules & 0x07);
#if defined KINETIS_KL && !defined DEVICE_WITH_eDMA
KINETIS_DMA *ptrDMA = (KINETIS_DMA *)DMA_BLOCK;
#if defined _WINDOWS
if (ucDMA_channel >= DMA_CHANNEL_COUNT) {
_EXCEPTION("Error - peripheral DMA is specifying a non-existent channel!!");
}
#endif
ptrDMA += ucDMA_channel;
ptrDMA->DMA_DSR_BCR = DMA_DSR_BCR_DONE;
switch (ucSize) {
default:
case 1:
ucSize = 1;
ptrDMA->DMA_DCR = (DMA_DCR_DSIZE_8 | DMA_DCR_SSIZE_8 | DMA_DCR_DMOD_OFF | DMA_DCR_SMOD_OFF);
break;
case 2:
ptrDMA->DMA_DCR = (DMA_DCR_DSIZE_16 | DMA_DCR_SSIZE_16 | DMA_DCR_DMOD_OFF | DMA_DCR_SMOD_OFF);
break;
case 4:
ptrDMA->DMA_DCR = (DMA_DCR_DSIZE_32 | DMA_DCR_SSIZE_32 | DMA_DCR_DMOD_OFF | DMA_DCR_SMOD_OFF);
break;
}
if ((ulRules & DMA_DIRECTION_OUTPUT) != 0) {
ptrDMA->DMA_DCR |= (DMA_DCR_SINC);
ucDirectionOutput[ucDMA_channel] = DMA_TRANSFER_OUTPUT;
ptrStart[ucDMA_channel] = ptrBufSource;
}
else {
ptrDMA->DMA_DCR |= (DMA_DCR_DINC);
ucDirectionOutput[ucDMA_channel] = DMA_TRANSFER_INPUT;
ptrStart[ucDMA_channel] = ptrBufDest;
}
if ((DMA_FIXED_ADDRESSES & ulRules) != 0) {
ptrDMA->DMA_DCR &= ~(DMA_DCR_DINC | DMA_DCR_SINC);
}
ptrDMA->DMA_SAR = (unsigned long)ptrBufSource;
ptrDMA->DMA_DAR = (unsigned long)ptrBufDest;
if ((ulRules & DMA_AUTOREPEAT) != 0) {
ulRepeatLength[ucDMA_channel] = ulBufLength;
if ((ulRules & DMA_HALF_BUFFER_INTERRUPT) != 0) {
#if defined _WINDOWS
if ((ulBufLength%(ucSize * 2)) != 0) {
_EXCEPTION("Buffer length is expected to be divisible by twice the transfer size");
}
#endif
ulBufLength /= 2;
ucDirectionOutput[ucDMA_channel] |= DMA_TRANSFER_HALF_BUFFER;
}
}
else {
ulRepeatLength[ucDMA_channel] = 0;
}
_DMA_handler[ucDMA_channel] = int_handler;
if ((int_handler != 0) || ((ulRules & DMA_AUTOREPEAT) != 0)) {
ptrDMA->DMA_DSR_BCR = ulBufLength;
fnEnterInterrupt((irq_DMA0_ID + ucDMA_channel), int_priority, (void (*)(void))_DMA_Interrupt[ucDMA_channel]);
ptrDMA->DMA_DCR |= (DMA_DCR_EINT | DMA_DCR_D_REQ);
}
else {
ptrDMA->DMA_DSR_BCR = 0xffff0;
if ((DMA_NO_MODULO & ulRules) == 0) {
unsigned long ulMod = DMA_DCR_SMOD_256K;
#if defined _WINDOWS
if ((ulBufLength != 16) && (ulBufLength != 32) && (ulBufLength != 64) && (ulBufLength != 128) && (ulBufLength != 256) && (ulBufLength != 512) && (ulBufLength != 1024) && (ulBufLength != (2 * 1024)) && (ulBufLength != (4 * 1024)) && (ulBufLength != (8 * 1024)) && (ulBufLength != (16 * 1024)) && (ulBufLength != (32 * 1024)) && (ulBufLength != (64 * 1024)) && (ulBufLength != (128 * 1024)) && (ulBufLength != (256 * 1024))) {
_EXCEPTION("Invalid circular buffer size!!");
}
if ((ulRules & DMA_FIXED_ADDRESSES) == 0) {
if ((ulRules & DMA_DIRECTION_OUTPUT) != 0) {
if ((unsigned long)ptrBufSource & (ulBufLength - 1)) {
_EXCEPTION("Circular source buffer not-aligned!!");
}
}
else {
if ((unsigned long)ptrBufDest & (ulBufLength - 1)) {
_EXCEPTION("Circular destination buffer not-aligned!!");
}
}
}
#endif
while (ulBufLength < (256 * 1024)) {
ulBufLength *= 2;
ulMod -= DMA_DCR_SMOD_16;
}
if ((ulRules & DMA_DIRECTION_OUTPUT) == 0) {
ulMod >>= 4;
}
ptrDMA->DMA_DCR |= ulMod;
}
}
POWER_UP_ATOMIC(6, DMAMUX0);
*(unsigned char *)(DMAMUX0_BLOCK + ucDMA_channel) = (unsigned char)(usDmaTriggerSource | DMAMUX_CHCFG_ENBL);
ptrDMA->DMA_DCR |= (DMA_DCR_CS | DMA_DCR_EADREQ);
#else
KINETIS_DMA_TDC *ptrDMA_TCD = (KINETIS_DMA_TDC *)eDMA_DESCRIPTORS;
ptrDMA_TCD += ucDMA_channel;
if ((DMA_FIXED_ADDRESSES & ulRules) != 0) {
ptrDMA_TCD->DMA_TCD_SOFF = 0;
ptrDMA_TCD->DMA_TCD_DOFF = 0;
ptrDMA_TCD->DMA_TCD_SLAST = 0;
ptrDMA_TCD->DMA_TCD_DLASTSGA = 0;
}
else {
if ((ulRules & DMA_DIRECTION_OUTPUT) != 0) {
ptrDMA_TCD->DMA_TCD_SOFF = ucSize;
ptrDMA_TCD->DMA_TCD_DOFF = 0;
ptrDMA_TCD->DMA_TCD_DLASTSGA = 0;
ptrDMA_TCD->DMA_TCD_SLAST = (-(signed long)(ulBufLength));
}
else {
ptrDMA_TCD->DMA_TCD_SOFF = 0;
ptrDMA_TCD->DMA_TCD_DOFF = ucSize;
ptrDMA_TCD->DMA_TCD_DLASTSGA = (-(signed long)(ulBufLength));
ptrDMA_TCD->DMA_TCD_SLAST = 0;
}
}
switch (ucSize) {
default:
case 1:
ucSize = 1;
ptrDMA_TCD->DMA_TCD_ATTR = (DMA_TCD_ATTR_DSIZE_8 | DMA_TCD_ATTR_SSIZE_8);
break;
case 2:
ptrDMA_TCD->DMA_TCD_ATTR = (DMA_TCD_ATTR_DSIZE_16 | DMA_TCD_ATTR_SSIZE_16);
break;
case 4:
ptrDMA_TCD->DMA_TCD_ATTR = (DMA_TCD_ATTR_DSIZE_32 | DMA_TCD_ATTR_SSIZE_32);
break;
}
ptrDMA_TCD->DMA_TCD_SADDR = (unsigned long)ptrBufSource;
ptrDMA_TCD->DMA_TCD_NBYTES_ML = ucSize;
_DMA_handler[ucDMA_channel] = int_handler;
if (int_handler != 0) {
if ((ulRules & DMA_HALF_BUFFER_INTERRUPT) != 0) {
ptrDMA_TCD->DMA_TCD_CSR = (DMA_TCD_CSR_INTMAJOR | DMA_TCD_CSR_INTHALF);
}
else {
ptrDMA_TCD->DMA_TCD_CSR = (DMA_TCD_CSR_INTMAJOR);
}
#if defined eDMA_SHARES_INTERRUPTS
fnEnterInterrupt((irq_DMA0_0_4_ID + (ucDMA_channel%_DMA_CHANNEL_COUNT)), int_priority, (void(*)(void))_DMA_Interrupt[ucDMA_channel%_DMA_CHANNEL_COUNT]);
#else
fnEnterInterrupt((irq_DMA0_ID + ucDMA_channel), int_priority, (void(*)(void))_DMA_Interrupt[ucDMA_channel]);
#endif
}
else {
ptrDMA_TCD->DMA_TCD_CSR = 0;
}
if ((ulRules & DMA_SINGLE_CYCLE) != 0) {
ptrDMA_TCD->DMA_TCD_CSR |= DMA_TCD_CSR_DREQ;
}
ptrDMA_TCD->DMA_TCD_DADDR = (unsigned long)ptrBufDest;
if ((ulRules & DMA_SW_TRIGGER) != 0) {
ptrDMA_TCD->DMA_TCD_CITER_ELINK = 1;
ptrDMA_TCD->DMA_TCD_NBYTES_ML = ulBufLength;
if ((ulRules & DMA_INITIATE_TRANSFER) != 0) {
ptrDMA_TCD->DMA_TCD_CSR = DMA_TCD_CSR_START;
if ((ulRules & DMA_WAIT_TERMINATION) != 0) {
while ((ptrDMA_TCD->DMA_TCD_CSR & DMA_TCD_CSR_DONE) == 0) { fnSimulateDMA(ucDMA_channel); }
}
}
return;
}
ptrDMA_TCD->DMA_TCD_BITER_ELINK = ptrDMA_TCD->DMA_TCD_CITER_ELINK = (signed short)(ulBufLength/ucSize);
POWER_UP_ATOMIC(6, DMAMUX0);
#if defined TRGMUX_AVAILABLE
if ((usDmaTriggerSource & DMAMUX_CHCFG_TRIG) != 0) {
#if defined _WINDOWS
if ((usDmaTriggerSource >> 8) >= 4) {
_EXCEPTION("Invalid LPIT periodic trigger source!");
}
#endif
TRGMUX_DMAMUX0 = (TRGMUX_SEL_LPIT0_CHANNEL_0 + (usDmaTriggerSource >> 8));
usDmaTriggerSource = DMAMUX0_DMA0_CHCFG_SOURCE_PIT0;
}
#endif
*(unsigned char *)(DMAMUX0_BLOCK + ucDMA_channel) = (unsigned char)(usDmaTriggerSource | DMAMUX_CHCFG_ENBL);
#endif
#if defined _WINDOWS
#if defined DMA_MEMCPY_CHANNEL
if (DMA_MEMCPY_CHANNEL == ucDMA_channel) {
_EXCEPTION("Warning - peripheral DMA is using the channel reserved for DMA based uMemcpy()!!");
}
#endif
#if defined DMA_MEMCPY_CHANNEL_ALT
if (DMA_MEMCPY_CHANNEL_ALT == ucDMA_channel) {
_EXCEPTION("Warning - peripheral DMA is using the alternative channel reserved for DMA based uMemcpy()!!");
}
#endif
if (DMAMUX0_DMA0_CHCFG_SOURCE_PIT0 == usDmaTriggerSource) {
if (ucDMA_channel != 0) {
#if defined LPITS_AVAILABLE
_EXCEPTION("LPIT triggers only operate on DMA channel 0!!");
#else
_EXCEPTION("PIT0 trigger only operates on DMA channel 0!!");
#endif
}
#if defined ERRATA_ID_5746
if ((ptrDMA->DMA_DCR & DMA_DCR_CS) != 0) {
_EXCEPTION("PIT0 trigger generates two data transfers when in cycle-steal mode!!");
}
#endif
}
#if defined DMAMUX0_DMA0_CHCFG_SOURCE_PIT1
else if (DMAMUX0_DMA0_CHCFG_SOURCE_PIT1 == usDmaTriggerSource) {
if (ucDMA_channel != 1) {
_EXCEPTION("PIT1 trigger only operates on DMA channel 1!!");
}
#if defined ERRATA_ID_5746
if ((ptrDMA->DMA_DCR & DMA_DCR_CS) != 0) {
_EXCEPTION("PIT1 trigger generates two data transfers when in cycle-steal mode!!");
}
#endif
}
else if (DMAMUX0_DMA0_CHCFG_SOURCE_PIT2 == usDmaTriggerSource) {
if (ucDMA_channel != 2) {
_EXCEPTION("PIT2 trigger only operates on DMA channel 2!!");
}
}
else if (DMAMUX0_DMA0_CHCFG_SOURCE_PIT3 == usDmaTriggerSource) {
if (ucDMA_channel != 3) {
_EXCEPTION("PIT3 trigger only operates on DMA channel 3!!");
}
}
#endif
#endif
}