1.9.4 Direct Memory Access Controller (DMA)
The DMA Controller (DMAC) can transfer data between memories and peripherals, and thus off-load these tasks from the CPU. It enables high data transfer rates with minimum CPU intervention, and frees up CPU time. With access to all peripherals, the DMAC can handle automatic transfer of data between communication modules. DMAC has several DMA channels and each channel is fully programmable and provides both peripheral or memory-to-memory transfers.
Using The Library
DMA Memory to Peripheral and Peripheral to Memory transfer:
\#define MINIMUM_DMA_BUFFER_SIZE (32) \#define NUM_BYTES_TO_READ (10) \#define LED_ON LED0_Clear \#define LED_OFF LED0_Set static __attribute__ ((aligned (32))) char startMessage[192] = "**** DMAC USART echo demo ****\\r\\n\\ \**** Type a buffer of 10 characters and observe it echo back using DMA ****\\r\\n\\ \**** LED toggles each time the buffer is echoed ****\\r\\n"; static __attribute__ ((aligned (32))) char receiveBuffer[MINIMUM_DMA_BUFFER_SIZE] = {0}; static __attribute__ ((aligned (32))) char echoBuffer[MINIMUM_DMA_BUFFER_SIZE] = {0}; static volatile bool writeComplete = false; static volatile bool readComplete = false; static void TransmitCompleteCallback(DMA_TRANSFER_EVENT event, uintptr_t contextHandle) { if (event & DMA_TRANSFER_EVENT_BLOCK_TRANSFER_COMPLETE) { writeComplete = true; } } static void ReceiveCompleteCallback(DMA_TRANSFER_EVENT event, uintptr_t contextHandle) { if (event & DMA_TRANSFER_EVENT_BLOCK_TRANSFER_COMPLETE) { readComplete = true; } } int main ( void ) { /* Initialize all modules */ SYS_Initialize ( NULL ); DMA_ChannelCallbackRegister(DMA_CHANNEL_0, TransmitCompleteCallback, 0); DMA_ChannelCallbackRegister(DMA_CHANNEL_1, ReceiveCompleteCallback, 0); /* Clean cache before submitting DMA request, to move data from data cache to main memory */ DCACHE_CLEAN_BY_ADDR((uint32_t *)startMessage, strlen(startMessage)); DMA_ChannelTransfer(DMA_CHANNEL_0, startMessage, (const void *)&SERCOM5_REGS-\>USART_INT.SERCOM_DATA, strlen(startMessage)); while ( true ) { if(readComplete == true) { /* Echo back received buffer and Toggle LED */ readComplete = false; memcpy(echoBuffer, receiveBuffer, NUM_BYTES_TO_READ); echoBuffer[NUM_BYTES_TO_READ] = '\\r'; echoBuffer[NUM_BYTES_TO_READ + 1] ='\\n'; /* Clean cache before submitting DMA request, to move data from data cache to main memory */ DCACHE_CLEAN_BY_ADDR((uint32_t *)echoBuffer, (NUM_BYTES_TO_READ + 2)); DMA_ChannelTransfer(DMA_CHANNEL_0, echoBuffer, (const void *)&SERCOM5_REGS-\>USART_INT.SERCOM_DATA, (NUM_BYTES_TO_READ + 2)); LED0_Toggle(); } else if(writeComplete == true) { /* Now, submit DMA request to read user data */ writeComplete = false; /* Invalidate cache lines before submitting DMA read request */ DCACHE_INVALIDATE_BY_ADDR((uint32_t *)receiveBuffer, NUM_BYTES_TO_READ); DMA_ChannelTransfer(DMA_CHANNEL_1, (const void *)&SERCOM5_REGS-\>USART_INT.SERCOM_DATA, receiveBuffer, NUM_BYTES_TO_READ); } else { /* Repeat the loop */ ; } } /* Execution should not come here during normal operation */ \return ( EXIT_FAILURE ); }
DMA Memory to Memory transfer:
\#define LED_On LED0_Clear \#define LED_Off LED0_Set \#define TRANSFER_SIZE 1024 __attribute__ ((aligned (32))) uint8_t srcBuffer[TRANSFER_SIZE] = {0}; __attribute__ ((aligned (32))) uint8_t dstBuffer[TRANSFER_SIZE] = {0}; volatile bool dmaXferDone = false; volatile bool dmaXferError = false; void DMA_EventHandler(DMA_TRANSFER_EVENT status, uintptr_t context) { if(status & DMA_TRANSFER_EVENT_BLOCK_TRANSFER_COMPLETE) { dmaXferDone = true; } else if (status & DMA_TRANSFER_EVENT_ERROR) { dmaXferError = true; } } int main ( void ) { uint32_t i = 0; /* Initialize all modules */ SYS_Initialize ( NULL ); LED_Off(); /* Build the srcBuffer */ for (i = 0; i \< TRANSFER_SIZE; i++) { srcBuffer[i] = i; } printf("\\n\\r-------------------------------------------------------------"); printf("\\n\\r\\t\\t DMA Memory Transfer DEMO\\t\\t"); printf("\\n\\r-------------------------------------------------------------"); /* Register a callback with DMA PLIB to get transfer complete and error events. */ DMA_ChannelCallbackRegister(DMA_CHANNEL_0, DMA_EventHandler, 0); /* Clean cache lines having source buffer before submitting a transfer \* request to DMA to load the latest data in the cache to the actual \* memory */ DCACHE_CLEAN_BY_ADDR((uint32_t *)srcBuffer, TRANSFER_SIZE); /* Invalidate cache lines before submitting DMA request */ DCACHE_INVALIDATE_BY_ADDR((uint32_t *)dstBuffer, TRANSFER_SIZE); DMA_ChannelTransfer(DMA_CHANNEL_0, srcBuffer, dstBuffer, TRANSFER_SIZE); while (dmaXferDone == false && dmaXferError == false); if(dmaXferDone == true) { dmaXferDone = false; if(memcmp(srcBuffer, dstBuffer, TRANSFER_SIZE) == 0) { /* Successfully transferred the data using DMAC */ printf("\\n\\r DMA Memory Transfer Successful with Data Match\\n\\r"); LED_On(); } else { /* Data transfers done, but data mismatch occurred */ printf("\\n\\r DMA Memory Transfer Successful with Data Mismatch !!!\\n\\r"); LED_Off(); } } else if(dmaXferError == true) { /* Error occurred during the transfers */ dmaXferError = false; printf("\\n\\r DMA Memory Transfer Error !!!\\n\\r"); LED_Off(); } else { /* Nothing to do, loop */ ; } while (1) { } /* Execution should not come here during normal operation */ \return ( EXIT_FAILURE ); }
DMA Linked List transfer:
\#define SRC_BUFF1_SIZE 32 \#define SRC_BUFF2_SIZE 128 \#define SRC_BUFF3_SIZE 256 \#define DST_BUFF_SIZE (SRC_BUFF1_SIZE + SRC_BUFF2_SIZE + SRC_BUFF3_SIZE) static DMA_DESCRIPTOR_REGS dmaDescRegs[3] = {0}; static volatile bool dmaLLXferDone = false; static volatile bool dmaXferError = false; static __attribute__ ((aligned (32))) uint8_t srcBuffer1[SRC_BUFF1_SIZE]; static __attribute__ ((aligned (32))) uint8_t srcBuffer2[SRC_BUFF2_SIZE]; static __attribute__ ((aligned (32))) uint8_t srcBuffer3[SRC_BUFF3_SIZE]; static __attribute__ ((aligned (32))) uint8_t dstBuffer[DST_BUFF_SIZE]; void DMA_EventHandler(DMA_TRANSFER_EVENT status, uintptr_t context) { if(status & DMA_TRANSFER_EVENT_LINKED_LIST_TRANSFER_COMPLETE) { dmaLLXferDone = true; } else if (status & DMA_TRANSFER_EVENT_ERROR) { dmaXferError = true; } } void DMA_LinkedListDescSetup(void) { dmaDescRegs[0].DMA_BDNXT = (uint32_t)&dmaDescRegs[1]; dmaDescRegs[0].DMA_BDCFG.SSA = 1; dmaDescRegs[0].DMA_BDCFG.DSA = 1; dmaDescRegs[0].DMA_BDCFG.XSIZ = 1; dmaDescRegs[0].DMA_BDCFG.ENABLE = 1; dmaDescRegs[0].DMA_BDCFG.LLEN = 1; dmaDescRegs[0].DMA_BDCFG.SWFRC = 1; dmaDescRegs[0].DMA_BDSSA = (uint32_t)srcBuffer1; dmaDescRegs[0].DMA_BDDSA = (uint32_t)&dstBuffer[0]; dmaDescRegs[0].DMA_BDXSIZ.CSZ = 1; dmaDescRegs[0].DMA_BDXSIZ.BLKSZ = SRC_BUFF1_SIZE; /*------------------------------------------------------*/ dmaDescRegs[1].DMA_BDNXT = (uint32_t)&dmaDescRegs[2]; dmaDescRegs[1].DMA_BDCFG.SSA = 1; dmaDescRegs[1].DMA_BDCFG.DSA = 1; dmaDescRegs[1].DMA_BDCFG.XSIZ = 1; dmaDescRegs[1].DMA_BDCFG.ENABLE = 1; dmaDescRegs[1].DMA_BDCFG.LLEN = 1; dmaDescRegs[1].DMA_BDCFG.SWFRC = 1; dmaDescRegs[1].DMA_BDSSA = (uint32_t)srcBuffer2; dmaDescRegs[1].DMA_BDDSA = (uint32_t)&dstBuffer[SRC_BUFF1_SIZE]; dmaDescRegs[1].DMA_BDXSIZ.CSZ = 1; dmaDescRegs[1].DMA_BDXSIZ.BLKSZ = SRC_BUFF2_SIZE; /*------------------------------------------------------*/ dmaDescRegs[2].DMA_BDNXT = 0xFFFFFFFF; dmaDescRegs[2].DMA_BDCFG.SSA = 1; dmaDescRegs[2].DMA_BDCFG.DSA = 1; dmaDescRegs[2].DMA_BDCFG.XSIZ = 1; dmaDescRegs[2].DMA_BDCFG.ENABLE = 1; dmaDescRegs[2].DMA_BDCFG.LLEN = 0; dmaDescRegs[2].DMA_BDCFG.SWFRC = 1; dmaDescRegs[2].DMA_BDSSA = (uint32_t)srcBuffer3; dmaDescRegs[2].DMA_BDDSA = (uint32_t)&dstBuffer[SRC_BUFF1_SIZE + SRC_BUFF2_SIZE]; dmaDescRegs[2].DMA_BDXSIZ.CSZ = 1; dmaDescRegs[2].DMA_BDXSIZ.BLKSZ = SRC_BUFF3_SIZE; } void initSourceBuffers(void) { uint32_t i; for (i = 0; i \< SRC_BUFF1_SIZE; i++ ) { srcBuffer1[i] = i; } for (i = 0; i \< SRC_BUFF2_SIZE; i++ ) { srcBuffer2[i] = i; } for (i = 0; i \< SRC_BUFF3_SIZE; i++ ) { srcBuffer3[i] = i; } } int main ( void ) { /* Initialize all modules */ SYS_Initialize ( NULL ); printf("\\n\\r-------------------------------------------------------------"); printf("\\n\\r\\t DMA Memory Transfer Using Linked Lists \\t\\t"); printf("\\n\\r-------------------------------------------------------------"); DMA_ChannelCallbackRegister(DMA_CHANNEL_0, DMA_EventHandler, 0); initSourceBuffers(); /* Clean cache lines to push data into the main memory */ DCACHE_CLEAN_BY_ADDR((uint32_t *)srcBuffer1, SRC_BUFF1_SIZE); DCACHE_CLEAN_BY_ADDR((uint32_t *)srcBuffer2, SRC_BUFF2_SIZE); DCACHE_CLEAN_BY_ADDR((uint32_t *)srcBuffer3, SRC_BUFF3_SIZE); /* Invalidate cache lines before submitting DMA request */ DCACHE_INVALIDATE_BY_ADDR((uint32_t *)dstBuffer, DST_BUFF_SIZE); DMA_LinkedListDescSetup(); DMA_ChannelLinkedListTransfer(DMA_CHANNEL_0, dmaDescRegs); while (dmaLLXferDone == false && dmaXferError == false); if (dmaLLXferDone == true) { if ( (memcmp(srcBuffer1, &dstBuffer[0], SRC_BUFF1_SIZE) == 0) && (memcmp(srcBuffer2, &dstBuffer[SRC_BUFF1_SIZE], SRC_BUFF2_SIZE) == 0) && (memcmp(srcBuffer3, &dstBuffer[SRC_BUFF1_SIZE + SRC_BUFF2_SIZE], SRC_BUFF3_SIZE) == 0) ) { printf("\\n\\r DMA linked list transfer successful with data match\\r\\n"); } else { printf("\\n\\r DMA linked list error - data mismatch\\r\\n"); } } else { printf("\\n\\r DMA linked list transfer error\\r\\n"); } while ( true ) { } /* Execution should not come here during normal operation */ \return ( EXIT_FAILURE ); }
DMA Abort on Pattern Match:
\#define LED_ON LED0_Clear \#define LED_OFF LED0_Set \#define USART_RECEIVE_CHANNEL DMA_CHANNEL_0 \#define USART_TRANSMIT_CHANNEL DMA_CHANNEL_1 \#define MINIMUM_DMA_BUFFER_SIZE (32) \#define NUM_BYTES_TO_READ (20) static __attribute__ ((aligned (32))) char startMessage[192] = "*** DMA USART echo demo ***\\r\\n\\ \*** Press Enter key to terminate the DMA transfer ***\\r\\n\\ \*** Maximum number of characters entered must be less than 20 ***\\r\\n"; static __attribute__ ((aligned (32))) char receiveBuffer[MINIMUM_DMA_BUFFER_SIZE] = {0}; static __attribute__ ((aligned (32))) char echoBuffer[MINIMUM_DMA_BUFFER_SIZE] = {0}; volatile bool writeComplete = false; volatile bool readComplete = false; const uint8_t new_line_ch = 0x0A; static void TransmitCompleteCallback(DMA_TRANSFER_EVENT event, uintptr_t contextHandle) { if (event & DMA_TRANSFER_EVENT_BLOCK_TRANSFER_COMPLETE) { writeComplete = true; } } static void ReceiveCompleteCallback(DMA_TRANSFER_EVENT event, uintptr_t contextHandle) { if (event & DMA_TRANSFER_EVENT_BLOCK_TRANSFER_COMPLETE) { readComplete = true; } } int main ( void ) { /* Initialize all modules */ SYS_Initialize ( NULL ); /* Clean cache before submitting DMA request, to move data from data cache to main memory */ DCACHE_CLEAN_BY_ADDR((uint32_t *)startMessage, strlen(startMessage)); /* Transmit the startMessage before turning on the pattern match */ DMA_ChannelTransfer(USART_TRANSMIT_CHANNEL, (const void *)startMessage, (const void *)&SERCOM5_REGS-\>USART_INT.SERCOM_DATA, sizeof(startMessage)); while (DMA_ChannelIsBusy(USART_TRANSMIT_CHANNEL)); DMA_ChannelCallbackRegister(USART_TRANSMIT_CHANNEL, TransmitCompleteCallback,0); DMA_ChannelCallbackRegister(USART_RECEIVE_CHANNEL, ReceiveCompleteCallback,0); /* data reception from user will stop as soon as "Enter" key is pressed (pattern is matched) */ DMA_ChannelPatternMatchSetup(USART_RECEIVE_CHANNEL, DMA_PATTERN_MATCH_LEN_1BYTE, (uint16_t)(0x0D)); DMA_ChannelPatternMatchSetup(USART_TRANSMIT_CHANNEL, DMA_PATTERN_MATCH_LEN_1BYTE, (uint16_t)(0x0D)); /* Submit buffer to read user data */ DMA_ChannelTransfer(USART_RECEIVE_CHANNEL, (const void *)&SERCOM5_REGS-\>USART_INT.SERCOM_DATA, (const void *)receiveBuffer, NUM_BYTES_TO_READ); while ( true ) { if(readComplete == true) { /* Echo back received buffer and Toggle LED */ readComplete = false; memcpy(echoBuffer, receiveBuffer, NUM_BYTES_TO_READ); /* Clean cache before submitting DMA request, to move data from data cache to main memory */ DCACHE_CLEAN_BY_ADDR((uint32_t *)echoBuffer, NUM_BYTES_TO_READ); /* Since pattern matching is enabled on DMA USART TX channel, this should terminate transfer when 0x0D is found */ DMA_ChannelTransfer(USART_TRANSMIT_CHANNEL, (const void *)echoBuffer, (const void *)&SERCOM5_REGS-\>USART_INT.SERCOM_DATA, NUM_BYTES_TO_READ); LED0_Toggle(); } else if(writeComplete == true) { writeComplete = false; /* Ensure to add new line character on the console before receiving the next data from user */ DMA_ChannelTransfer(USART_TRANSMIT_CHANNEL, (const void *)&new_line_ch, (const void *)&SERCOM5_REGS-\>USART_INT.SERCOM_DATA, 1); while(writeComplete == false); writeComplete = false; /* Invalidate cache lines before submitting DMA read request */ DCACHE_INVALIDATE_BY_ADDR((uint32_t *)receiveBuffer, NUM_BYTES_TO_READ); /* Submit buffer to read user data */ DMA_ChannelTransfer(USART_RECEIVE_CHANNEL, (const void *)&SERCOM5_REGS-\>USART_INT.SERCOM_DATA, (const void *)receiveBuffer, NUM_BYTES_TO_READ); } else { /* Repeat the loop */ ; } } /* Execution should not come here during normal operation */ \return ( EXIT_FAILURE ); }
Library Interface
Direct Memory Access Controller peripheral library provides the following interfaces:
Functions
Name | Description |
---|---|
DMA_Initialize | Initializes the DMA controller of the device |
DMA_ChannelCallbackRegister | This function allows a DMA PLIB client to set an event handler |
DMA_ChannelTransfer | Schedules a DMA transfer on the specified DMA channel |
DMA_ChannelIsBusy | The function returns the busy status of the channel |
DMA_ChannelDisable | The function disables the specified DMA channel |
DMA_ChannelEnable | The function enables the specified DMA channel |
DMA_ChannelGetTransferredCount | Returns transfer count of the ongoing DMA transfer |
DMA_ChannelInterruptEnable | This API is used to enable DMA channel interrupt |
DMA_ChannelInterruptDisable | This API is used to disable DMA channel interrupt |
DMA_ChannelInterruptFlagsGet | This API is used to get the DMA channel interrupt flags |
DMA_ChannelPatternMatchSetup | This API is used to setup DMA pattern matching |
DMA_ChannelPatternMatchEnable | Enable DMA channel pattern matching |
DMA_ChannelPatternMatchDisable | Disable DMA channel pattern matching |
DMA_ChannelPatternIgnoreByteEnable | Enable DMA channel pattern ignore byte |
DMA_ChannelPatternIgnoreByteDisable | Disable DMA channel pattern ignore byte |
DMA_ChannelPatternIgnoreValue | Configures the byte to be ignored for pattern matching when it is enabled |
DMA_ChannelSettingsGet | Returns the current channel settings for the specified DMA Channel |
DMA_ChannelSettingsSet | Changes the current transfer settings of the specified DMA channel |
DMA_ChannelLinkedListTransfer | The function submits a list of DMA transfers |
Data types and constants
Name | Type | Description |
---|---|---|
DMA_CHANNEL | Enum | Lists the set of channels available for data transfer using DMA |
DMA_CHANNEL_CONFIG | Typedef | Defines the data type for DMA channel configuration |
DMA_TRANSFER_EVENT | Typedef and Macros | Defines the data type and macros for DMA_TRANSFER_EVENT |
DMA_INT | Typedef and Macros | Defines the data type and macros for DMA_INT |
DMA_PATTERN_MATCH_LEN | Enum | Enumeration for DMA pattern match length type |
DMA_DESCRIPTOR_REGS | Struct | DMA linked list descriptor |
DMA_CHANNEL_CALLBACK | Typedef | Pointer to a DMA Transfer Event handler function |