1.2.5 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