Microchip® Advanced Software Framework

Quick Start Guide for Using DMA with SERCOM USART

The supported board list:

  • SAM D21 Xplained Pro
  • SAM R21 Xplained Pro
  • SAM D11 Xplained Pro
  • SAM DA1 Xplained Pro
  • SAM HA1G16A Xplained Pro
  • SAM L21 Xplained Pro
  • SAM L22 Xplained Pro
  • SAM C21 Xplained Pro

This quick start will receive eight bytes of data from the PC terminal and transmit back the string to the terminal through DMA. In this use case the USART will be configured with the following settings:

  • Asynchronous mode
  • 9600 Baudrate
  • 8-bits, No Parity and one Stop Bit
  • TX and RX enabled and connected to the Xplained Pro Embedded Debugger virtual COM port

Setup

Prerequisites

There are no special setup requirements for this use-case.

Code

Add to the main application source file, outside of any functions:

struct usart_module usart_instance;
struct dma_resource usart_dma_resource_rx;
struct dma_resource usart_dma_resource_tx;
#define BUFFER_LEN 8
static uint16_t string[BUFFER_LEN];
DmacDescriptor example_descriptor_rx SECTION_DMAC_DESCRIPTOR;
DmacDescriptor example_descriptor_tx SECTION_DMAC_DESCRIPTOR;

Copy-paste the following setup code to your user application:

static void transfer_done_rx(struct dma_resource* const resource )
{
dma_start_transfer_job(&usart_dma_resource_tx);
}
static void transfer_done_tx(struct dma_resource* const resource )
{
dma_start_transfer_job(&usart_dma_resource_rx);
}
static void configure_dma_resource_rx(struct dma_resource *resource)
{
struct dma_resource_config config;
#if(SAMR30E)
config.peripheral_trigger = CDC_SERCOM_DMAC_ID_RX;
#else
config.peripheral_trigger = EDBG_CDC_SERCOM_DMAC_ID_RX;
#endif
config.trigger_action = DMA_TRIGGER_ACTION_BEAT;
dma_allocate(resource, &config);
}
static void setup_transfer_descriptor_rx(DmacDescriptor *descriptor)
{
struct dma_descriptor_config descriptor_config;
descriptor_config.beat_size = DMA_BEAT_SIZE_HWORD;
descriptor_config.src_increment_enable = false;
descriptor_config.block_transfer_count = BUFFER_LEN;
descriptor_config.destination_address =
(uint32_t)string + sizeof(string);
descriptor_config.source_address =
(uint32_t)(&usart_instance.hw->USART.DATA.reg);
dma_descriptor_create(descriptor, &descriptor_config);
}
static void configure_dma_resource_tx(struct dma_resource *resource)
{
struct dma_resource_config config;
#if(SAMR30E)
config.peripheral_trigger = CDC_SERCOM_DMAC_ID_TX;
#else
config.peripheral_trigger = EDBG_CDC_SERCOM_DMAC_ID_TX;
#endif
config.trigger_action = DMA_TRIGGER_ACTION_BEAT;
dma_allocate(resource, &config);
}
static void setup_transfer_descriptor_tx(DmacDescriptor *descriptor)
{
struct dma_descriptor_config descriptor_config;
descriptor_config.beat_size = DMA_BEAT_SIZE_HWORD;
descriptor_config.dst_increment_enable = false;
descriptor_config.block_transfer_count = BUFFER_LEN;
descriptor_config.source_address = (uint32_t)string + sizeof(string);
descriptor_config.destination_address =
(uint32_t)(&usart_instance.hw->USART.DATA.reg);
dma_descriptor_create(descriptor, &descriptor_config);
}
static void configure_usart(void)
{
struct usart_config config_usart;
usart_get_config_defaults(&config_usart);
#if(SAMR30E)
{
config_usart.baudrate = 9600;
config_usart.mux_setting = CDC_SERCOM_MUX_SETTING;
config_usart.pinmux_pad0 = CDC_SERCOM_PINMUX_PAD0;
config_usart.pinmux_pad1 = CDC_SERCOM_PINMUX_PAD1;
config_usart.pinmux_pad2 = CDC_SERCOM_PINMUX_PAD2;
config_usart.pinmux_pad3 = CDC_SERCOM_PINMUX_PAD3;
CDC_MODULE, &config_usart) != STATUS_OK) {
}
}
#else
{
config_usart.baudrate = 9600;
config_usart.mux_setting = EDBG_CDC_SERCOM_MUX_SETTING;
config_usart.pinmux_pad0 = EDBG_CDC_SERCOM_PINMUX_PAD0;
config_usart.pinmux_pad1 = EDBG_CDC_SERCOM_PINMUX_PAD1;
config_usart.pinmux_pad2 = EDBG_CDC_SERCOM_PINMUX_PAD2;
config_usart.pinmux_pad3 = EDBG_CDC_SERCOM_PINMUX_PAD3;
EDBG_CDC_MODULE, &config_usart) != STATUS_OK) {
}
}
#endif
}

Add to user application initialization (typically the start of main()):

configure_dma_resource_rx(&usart_dma_resource_rx);
configure_dma_resource_tx(&usart_dma_resource_tx);
setup_transfer_descriptor_rx(&example_descriptor_rx);
setup_transfer_descriptor_tx(&example_descriptor_tx);
dma_add_descriptor(&usart_dma_resource_rx, &example_descriptor_rx);
dma_add_descriptor(&usart_dma_resource_tx, &example_descriptor_tx);
dma_register_callback(&usart_dma_resource_rx, transfer_done_rx,
dma_register_callback(&usart_dma_resource_tx, transfer_done_tx,
dma_enable_callback(&usart_dma_resource_rx,
dma_enable_callback(&usart_dma_resource_tx,

Workflow

Create variables

  1. Create a module software instance structure for the USART module to store the USART driver state while it is in use.
    struct usart_module usart_instance;
    Note
    This should never go out of scope as long as the module is in use. In most cases, this should be global.
  2. Create module software instance structures for DMA resources to store the DMA resource state while it is in use.
    struct dma_resource usart_dma_resource_rx;
    struct dma_resource usart_dma_resource_tx;
    Note
    This should never go out of scope as long as the module is in use. In most cases, this should be global.
  3. Create a buffer to store the data to be transferred /received.
    #define BUFFER_LEN 8
    static uint16_t string[BUFFER_LEN];
  4. Create DMA transfer descriptors for RX/TX.
    DmacDescriptor example_descriptor_rx SECTION_DMAC_DESCRIPTOR;
    DmacDescriptor example_descriptor_tx SECTION_DMAC_DESCRIPTOR;

Configure the USART

  1. Create a USART module configuration struct, which can be filled out to adjust the configuration of a physical USART peripheral.
    struct usart_config config_usart;
  2. Initialize the USART configuration struct with the module's default values.
    usart_get_config_defaults(&config_usart);
    Note
    This should always be performed before using the configuration struct to ensure that all values are initialized to known default settings.
  3. Alter the USART settings to configure the physical pinout, baudrate, and other relevant parameters.
    CDC_MODULE, &config_usart) != STATUS_OK) {
    }
    }
    #else
    {
    config_usart.baudrate = 9600;
    config_usart.mux_setting = EDBG_CDC_SERCOM_MUX_SETTING;
    config_usart.pinmux_pad0 = EDBG_CDC_SERCOM_PINMUX_PAD0;
    config_usart.pinmux_pad1 = EDBG_CDC_SERCOM_PINMUX_PAD1;
    config_usart.pinmux_pad2 = EDBG_CDC_SERCOM_PINMUX_PAD2;
    config_usart.pinmux_pad3 = EDBG_CDC_SERCOM_PINMUX_PAD3;
  4. Configure the USART module with the desired settings, retrying while the driver is busy until the configuration is stressfully set.
    CDC_MODULE, &config_usart) != STATUS_OK) {
    }
  5. Enable the USART module.

Configure DMA

  1. Create a callback function of receiver done.
    static void transfer_done_rx(struct dma_resource* const resource )
    {
    dma_start_transfer_job(&usart_dma_resource_tx);
    }
  2. Create a callback function of transmission done.
    static void transfer_done_tx(struct dma_resource* const resource )
    {
    dma_start_transfer_job(&usart_dma_resource_rx);
    }
  3. Create a DMA resource configuration structure, which can be filled out to adjust the configuration of a single DMA transfer.
    struct dma_resource_config config;
  4. Initialize the DMA resource configuration struct with the module's default values.
    Note
    This should always be performed before using the configuration struct to ensure that all values are initialized to known default settings.
  5. Set extra configurations for the DMA resource. It is using peripheral trigger. SERCOM TX empty trigger causes a beat transfer in this example.
    #if(SAMR30E)
    config.peripheral_trigger = CDC_SERCOM_DMAC_ID_RX;
    #else
    config.peripheral_trigger = EDBG_CDC_SERCOM_DMAC_ID_RX;
    #endif
    config.trigger_action = DMA_TRIGGER_ACTION_BEAT;
  6. Allocate a DMA resource with the configurations.
    dma_allocate(resource, &config);
  7. Create a DMA transfer descriptor configuration structure, which can be filled out to adjust the configuration of a single DMA transfer.
    struct dma_descriptor_config descriptor_config;
  8. Initialize the DMA transfer descriptor configuration struct with the module's default values.
    Note
    This should always be performed before using the configuration struct to ensure that all values are initialized to known default settings.
  9. Set the specific parameters for a DMA transfer with transfer size, source address, and destination address.
    descriptor_config.beat_size = DMA_BEAT_SIZE_HWORD;
    descriptor_config.src_increment_enable = false;
    descriptor_config.block_transfer_count = BUFFER_LEN;
    descriptor_config.destination_address =
    (uint32_t)string + sizeof(string);
    descriptor_config.source_address =
    (uint32_t)(&usart_instance.hw->USART.DATA.reg);
  10. Create the DMA transfer descriptor.
    dma_descriptor_create(descriptor, &descriptor_config);
  11. Create a DMA resource configuration structure for TX, which can be filled out to adjust the configuration of a single DMA transfer.
    struct dma_resource_config config;
  12. Initialize the DMA resource configuration struct with the module's default values.
    Note
    This should always be performed before using the configuration struct to ensure that all values are initialized to known default settings.
  13. Set extra configurations for the DMA resource. It is using peripheral trigger. SERCOM RX Ready trigger causes a beat transfer in this example.
    #if(SAMR30E)
    config.peripheral_trigger = CDC_SERCOM_DMAC_ID_TX;
    #else
    config.peripheral_trigger = EDBG_CDC_SERCOM_DMAC_ID_TX;
    #endif
    config.trigger_action = DMA_TRIGGER_ACTION_BEAT;
  14. Allocate a DMA resource with the configurations.
    dma_allocate(resource, &config);
  15. Create a DMA transfer descriptor configuration structure, which can be filled out to adjust the configuration of a single DMA transfer.
    struct dma_descriptor_config descriptor_config;
  16. Initialize the DMA transfer descriptor configuration struct with the module's default values.
    Note
    This should always be performed before using the configuration struct to ensure that all values are initialized to known default settings.
  17. Set the specific parameters for a DMA transfer with transfer size, source address, and destination address.
    descriptor_config.beat_size = DMA_BEAT_SIZE_HWORD;
    descriptor_config.dst_increment_enable = false;
    descriptor_config.block_transfer_count = BUFFER_LEN;
    descriptor_config.source_address = (uint32_t)string + sizeof(string);
    descriptor_config.destination_address =
    (uint32_t)(&usart_instance.hw->USART.DATA.reg);
  18. Create the DMA transfer descriptor.
    dma_descriptor_create(descriptor, &descriptor_config);

Use Case

Code

Copy-paste the following code to your user application:

dma_start_transfer_job(&usart_dma_resource_rx);
while (true) {
}

Workflow

  1. Wait for receiving data.
    dma_start_transfer_job(&usart_dma_resource_rx);
  2. Enter endless loop.
    while (true) {
    }