I'm trying to trigger a SPI communication using a timer on the NXP LPC1788 microcontroller. In the features of the DMA user manual it's written that the controller can use a timer for triggering a DMA burst. But I can't find a hint where the trigger source is configured in the DMA controller.
I plan to send a high frequent SPI command and read the result SPI with the same frequency using the DMA Controller. The transfer should be triggered by a timer. Is this possible?
Of cause I can implement the transfer in a timer ISR. But the controller is already busy doing other more important things.
Edit:
Today I had time to retry this again. I Use timer 3 match 0 to count with 1 kHz. Then I configured DMA stream 3 with source peripheral type TIM3M0 and destination connection type SSP0 TX. The timer triggers the DMA transfer once but then the channel is disabled.
So the configuration seams to be correct as the data gets transferred once as expected. So I tried to configure circular mode for DMA. I found the linked list configuration within the DMA controller what seams perfect. I added one element link to itself to close the loop. Now If I run this example the timer triggers the DMA channel again and the communication starts in an endless loop running as fast as possible. That's also not what I wanted to archive. Why is it not waiting for the trigger condition of the source peripheral. At the moment I think this is not possible with this microcontroller.
I add my current register configuration here:
Timer configuration
DMA source peripheral selection
DMA stream 3 configuration
Edit 2:
I also think that there is a small chance that this is possible. You have to use 3 channels.
- Channel 4 is a memory to memory transfer. It gets triggered by timer 3 match 0. The task is to reconfigure and enable channel 3 and channel 5.
- Channel 3 is used for Memory to SPI Peripheral transfer
- Channel 5 is also a memory to memory transfer used to reconfigure Channel 4 finally. This one closes the loop
I added the code I'm testing at the moment. The chain runs once as expected. But reenabling the chain is not working yet. I added three test elements to the chains to see if the get executed. Do you see any obvious error? The problem is you have to reconfigure the entire DMA channel with all registers after channel transfer termination.
GPDMA_LLI_Type linkedListEntry[15];
uint32_t specialControlSPI = 0x0;
uint32_t specialControlEnable = 0x0;
uint32_t specialConfigSPI = 0x0;
uint32_t specialConfigEnable = 0x0;
uint32_t specialConfigReenab = 0x0;
uint32_t specialCotrolReenab = 0x0;
uint32_t linkedListEnab = 0x0;
uint32_t linkedListReenab = 0x0;
uint32_t myTest = 0x0;
uint32_t myTest2 = 0x0;
uint32_t srcDataRegisterSPI = 0x0;
uint32_t srcDataRegisterEnab= 0x0;
uint32_t srcDataRegisterReenab = 0x0;
uint32_t destDataRegisterEnab= 0x0;
uint32_t destDataRegisterReenab = 0x0;
const uint32_t pattern1 = 0xAABBCCDD;
const uint32_t pattern2 = 0xEEFF0088;
#define MEMORY_TRANSFER_BURST (GPDMA_BSIZE_1)
#define MEMORY_TRANSFER_WIDTH (GPDMA_WIDTH_WORD)
void configureDMA_ForSPI_ADC_Tx(DMA dmaTx, void* data, uint32_t size)
{
//Disable all required channels
GPDMA_ChannelCmd(3, DISABLE);
GPDMA_ChannelCmd(4, DISABLE);
GPDMA_ChannelCmd(5, DISABLE);
//Form spcecial control for "enable" channel
uint32_t specialControl = GPDMA_DMACCxControl_TransferSize(4) \
| GPDMA_DMACCxControl_SBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_DBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_SWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_DWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_SI
| GPDMA_DMACCxControl_DI;
uint32_t j = 0;
//Configure "enable" channel to be triggered by timer 3 match 0 resulting in a memory transfer
GPDMA_Channel_CFG_Type conf;
conf.ChannelNum = 4;
conf.DstConn = 0;
conf.SrcConn = GPDMA_CONN_MAT3_0;
conf.TransferType = GPDMA_TRANSFER_M2M_CTRL_DMA;
conf.SrcMemAddr = (uint32_t)&specialControlSPI;
conf.TransferSize = 4;
conf.DstMemAddr = (uint32_t)&LPC_GPDMACH3->CControl;
conf.DMALLI = (uint32_t)&linkedListEntry[j],
GPDMA_Setup(&conf, &specialControl);
//Prepare linked list to enable "enable" channel again
linkedListEntry[j].Control = GPDMA_DMACCxControl_TransferSize(4) \
| GPDMA_DMACCxControl_SBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_DBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_SWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_DWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_SI
| GPDMA_DMACCxControl_DI;
linkedListEntry[j].DstAddr = (uint32_t)&LPC_GPDMACH3->CSrcAddr;
linkedListEntry[j].SrcAddr = (uint32_t)&srcDataRegisterSPI;
linkedListEntry[j].NextLLI = (uint32_t)&linkedListEntry[j+1];
j++;
linkedListEntry[j].Control = GPDMA_DMACCxControl_TransferSize(4) \
| GPDMA_DMACCxControl_SBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_DBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_SWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_DWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_SI
| GPDMA_DMACCxControl_DI;
linkedListEntry[j].DstAddr = (uint32_t)&LPC_GPDMACH3->CConfig; ///Enable SPI channel
linkedListEntry[j].SrcAddr = (uint32_t)&specialConfigSPI;
linkedListEntry[j].NextLLI = (uint32_t)&linkedListEntry[j+1];
j++;
linkedListEntry[j].Control = GPDMA_DMACCxControl_TransferSize(4) \
| GPDMA_DMACCxControl_SBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_DBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_SWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_DWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_SI
| GPDMA_DMACCxControl_DI;
linkedListEntry[j].DstAddr = (uint32_t)&LPC_GPDMACH5->CControl;
linkedListEntry[j].SrcAddr = (uint32_t)&specialCotrolReenab;
linkedListEntry[j].NextLLI = (uint32_t)&linkedListEntry[j+1];
j++;
linkedListEntry[j].Control = GPDMA_DMACCxControl_TransferSize(4) \
| GPDMA_DMACCxControl_SBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_DBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_SWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_DWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_SI
| GPDMA_DMACCxControl_DI;
linkedListEntry[j].DstAddr = (uint32_t)&LPC_GPDMACH5->CLLI;
linkedListEntry[j].SrcAddr = (uint32_t)&linkedListReenab;
linkedListEntry[j].NextLLI = (uint32_t)&linkedListEntry[j+1];
j++;
linkedListEntry[j].Control = GPDMA_DMACCxControl_TransferSize(4) \
| GPDMA_DMACCxControl_SBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_DBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_SWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_DWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_SI
| GPDMA_DMACCxControl_DI;
linkedListEntry[j].DstAddr = (uint32_t)&LPC_GPDMACH5->CDestAddr;
linkedListEntry[j].SrcAddr = (uint32_t)&destDataRegisterReenab;
linkedListEntry[j].NextLLI = (uint32_t)&linkedListEntry[j+1];
j++;
linkedListEntry[j].Control = GPDMA_DMACCxControl_TransferSize(4) \
| GPDMA_DMACCxControl_SBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_DBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_SWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_DWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_SI
| GPDMA_DMACCxControl_DI;
linkedListEntry[j].DstAddr = (uint32_t)&LPC_GPDMACH5->CSrcAddr;
linkedListEntry[j].SrcAddr = (uint32_t)&srcDataRegisterReenab;
linkedListEntry[j].NextLLI = (uint32_t)&linkedListEntry[j+1];
j++;
linkedListEntry[j].Control = GPDMA_DMACCxControl_TransferSize(4) \
| GPDMA_DMACCxControl_SBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_DBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_SWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_DWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_SI
| GPDMA_DMACCxControl_DI;
linkedListEntry[j].DstAddr = (uint32_t)&LPC_GPDMACH5->CConfig; ///Enable "Reenable" channel
linkedListEntry[j].SrcAddr = (uint32_t)&specialConfigReenab;
linkedListEntry[j].NextLLI = (uint32_t)&linkedListEntry[j+1];
j++;
linkedListEntry[j].Control = GPDMA_DMACCxControl_TransferSize(4) \
| GPDMA_DMACCxControl_SBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_DBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_SWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_DWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_SI
| GPDMA_DMACCxControl_DI;
linkedListEntry[j].DstAddr = (uint32_t)&LPC_GPDMACH7->CControl;
linkedListEntry[j].SrcAddr = (uint32_t)&pattern1;
linkedListEntry[j].NextLLI = (uint32_t)&linkedListEntry[j+1];
j++;
linkedListEntry[j].Control = GPDMA_DMACCxControl_TransferSize(4)
| GPDMA_DMACCxControl_SBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_DBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_SWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_DWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_SI
| GPDMA_DMACCxControl_DI;
linkedListEntry[j].DstAddr = (uint32_t)&myTest;
linkedListEntry[j].SrcAddr = (uint32_t)&pattern1;
linkedListEntry[j].NextLLI = 0;
j++;
//------------------------------------------------------------------------------------------------------------------
//Form special control for "SPI" channel
specialControl = GPDMA_DMACCxControl_TransferSize(size) \
| GPDMA_DMACCxControl_SBSize((uint32_t)GPDMA_BSIZE_1)
| GPDMA_DMACCxControl_DBSize((uint32_t)GPDMA_BSIZE_1)
| GPDMA_DMACCxControl_SWidth((uint32_t)GPDMA_WIDTH_HALFWORD)
| GPDMA_DMACCxControl_DWidth((uint32_t)GPDMA_WIDTH_HALFWORD)
| GPDMA_DMACCxControl_SI;
//Configure "SPI" channel to be triggered by software resulting in a peripheral transfer and a memory transfer in linked list
conf.ChannelNum = 3;
conf.DMALLI = 0,
conf.DstConn = GPDMA_CONN_SSP0_Tx;
conf.SrcConn = 0;
conf.TransferType = GPDMA_TRANSFER_M2P_CTRL_DMA;
conf.SrcMemAddr = (uint32_t)data;
conf.TransferSize = size;
conf.DstMemAddr = 0;
GPDMA_Setup(&conf, &specialControl);
//------------------------------------------------------------------------------------------------------------------
//Form special control for "reenab" channel
specialControl = GPDMA_DMACCxControl_TransferSize(size)
| GPDMA_DMACCxControl_SBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_DBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_SWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_DWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_SI
| GPDMA_DMACCxControl_DI;
//Configure "SPI" channel to be triggered by software resulting in a peripheral transfer and a memory transfer in linked list
conf.ChannelNum = 5;
conf.DstConn = 0;
conf.SrcConn = 0;
conf.TransferType = GPDMA_TRANSFER_M2M_CTRL_DMA;
conf.TransferSize = 4;
conf.SrcMemAddr = (uint32_t)&specialControlEnable;
conf.DstMemAddr = (uint32_t)&LPC_GPDMACH4->CControl;
conf.DMALLI = (uint32_t)&linkedListEntry[j],
GPDMA_Setup(&conf, &specialControl);
linkedListEntry[j].Control = GPDMA_DMACCxControl_TransferSize(4)
| GPDMA_DMACCxControl_SBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_DBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_SWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_DWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_SI
| GPDMA_DMACCxControl_DI;
linkedListEntry[j].DstAddr = (uint32_t)&LPC_GPDMACH4->CLLI;
linkedListEntry[j].SrcAddr = (uint32_t)&linkedListEnab;
linkedListEntry[j].NextLLI = (uint32_t)&linkedListEntry[j+1];
j++;
linkedListEntry[j].Control = GPDMA_DMACCxControl_TransferSize(4)
| GPDMA_DMACCxControl_SBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_DBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_SWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_DWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_SI
| GPDMA_DMACCxControl_DI;
linkedListEntry[j].DstAddr = (uint32_t)&LPC_GPDMACH4->CDestAddr;
linkedListEntry[j].SrcAddr = (uint32_t)&destDataRegisterEnab;
linkedListEntry[j].NextLLI = (uint32_t)&linkedListEntry[j+1];
j++;
linkedListEntry[j].Control = GPDMA_DMACCxControl_TransferSize(4)
| GPDMA_DMACCxControl_SBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_DBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_SWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_DWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_SI
| GPDMA_DMACCxControl_DI;
linkedListEntry[j].DstAddr = (uint32_t)&LPC_GPDMACH4->CSrcAddr;
linkedListEntry[j].SrcAddr = (uint32_t)&srcDataRegisterEnab;
linkedListEntry[j].NextLLI = (uint32_t)&linkedListEntry[j+1];
j++;
//Prepare linked list to enable "enable" channel again
linkedListEntry[j].Control = GPDMA_DMACCxControl_TransferSize(4)
| GPDMA_DMACCxControl_SBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_DBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_SWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_DWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_SI
| GPDMA_DMACCxControl_DI;
linkedListEntry[j].DstAddr = (uint32_t)&LPC_GPDMACH4->CConfig;
linkedListEntry[j].SrcAddr = (uint32_t)&specialConfigEnable; ///Enable trigger channel again
linkedListEntry[j].NextLLI = (uint32_t)&linkedListEntry[j+1];
j++;
linkedListEntry[j].Control = GPDMA_DMACCxControl_TransferSize(4)
| GPDMA_DMACCxControl_SBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_DBSize((uint32_t)MEMORY_TRANSFER_BURST)
| GPDMA_DMACCxControl_SWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_DWidth((uint32_t)MEMORY_TRANSFER_WIDTH)
| GPDMA_DMACCxControl_SI
| GPDMA_DMACCxControl_DI;
linkedListEntry[j].DstAddr = (uint32_t)&myTest2;
linkedListEntry[j].SrcAddr = (uint32_t)&pattern2;
linkedListEntry[j].NextLLI = 0;
j++;
//Store the configuration registers for DMA operation
specialControlSPI = LPC_GPDMACH3->CControl | GPDMA_DMACCxControl_TransferSize(size);
specialControlEnable = LPC_GPDMACH4->CControl | GPDMA_DMACCxControl_TransferSize(4);
specialCotrolReenab = LPC_GPDMACH5->CControl | GPDMA_DMACCxControl_TransferSize(4);
srcDataRegisterSPI = LPC_GPDMACH3->CSrcAddr;
srcDataRegisterEnab = LPC_GPDMACH4->CSrcAddr;
srcDataRegisterReenab = LPC_GPDMACH5->CSrcAddr;
destDataRegisterEnab = LPC_GPDMACH4->CDestAddr;
destDataRegisterReenab = LPC_GPDMACH5->CDestAddr;
specialConfigSPI = (0x1) | LPC_GPDMACH3->CConfig;
specialConfigEnable = (0x1) | LPC_GPDMACH4->CConfig;
specialConfigReenab = (0x1) | LPC_GPDMACH5->CConfig;
linkedListReenab = LPC_GPDMACH5->CLLI;
linkedListEnab = LPC_GPDMACH4->CLLI;
//Enable the channel "enable" channel. Hopfully will be triggered by timer match soon. This should enable SPI transfer
GPDMA_ChannelCmd(4, ENABLE);
}