Microchip® Advanced Software Framework

Quick Start Guide for Using DMA with TC

The supported kit list:

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

In this use case, the TC will be used to generate a PWM signal. Here the pulse width is set to one quarter of the period. Once the counter value matches the values in the Compare/Capture Value register, an event will be tiggered for a DMA memory to memory transfer. The TC module will be set up as follows:

  • GCLK generator 0 (GCLK main) clock source
  • 16-bit resolution on the counter
  • No prescaler
  • Normal PWM wave generation
  • GCLK reload action
  • Don't run in standby
  • No inversion of waveform output
  • No capture enabled
  • Count upward
  • Don't perform one-shot operations
  • No event input enabled
  • No event action
  • No event generation enabled
  • Counter starts on 0
  • Capture compare channel 0 set to 0xFFFF/4

The DMA module is configured for:

  • Move data from memory to memory
  • Using peripheral trigger of TC6 Match/Compare 0
  • Using DMA priority level 0

Quick Start

Prerequisites

There are no prerequisites for this use case.

Code

Add to the main application source file, before any functions, according to the kit used:

  • SAM D21 Xplained Pro.
    #define PWM_MODULE EXT1_PWM_MODULE
    #define PWM_OUT_PIN EXT1_PWM_0_PIN
    #define PWM_OUT_MUX EXT1_PWM_0_MUX
    #define M2M_DMAC_TRIGGER_ID TC6_DMAC_ID_MC_0
  • SAM R21 Xplained Pro.
    #define PWM_MODULE EXT1_PWM_MODULE
    #define PWM_OUT_PIN EXT1_PWM_0_PIN
    #define PWM_OUT_MUX EXT1_PWM_0_MUX
    #define M2M_DMAC_TRIGGER_ID TC3_DMAC_ID_MC_0
  • SAM D11 Xplained Pro.
    #define PWM_MODULE EXT1_PWM_MODULE
    #define PWM_OUT_PIN EXT1_PWM_0_PIN
    #define PWM_OUT_MUX EXT1_PWM_0_MUX
    #define M2M_DMAC_TRIGGER_ID TC1_DMAC_ID_MC_0
  • SAM L21 Xplained Pro.
    #define PWM_MODULE EXT2_PWM_MODULE
    #define PWM_OUT_PIN EXT2_PWM_0_PIN
    #define PWM_OUT_MUX EXT2_PWM_0_MUX
    #define M2M_DMAC_TRIGGER_ID TC0_DMAC_ID_MC_0
  • SAM L22 Xplained Pro.
    #define PWM_MODULE EXT1_PWM_MODULE
    #define PWM_OUT_PIN EXT1_PWM_0_PIN
    #define PWM_OUT_MUX EXT1_PWM_0_MUX
    #define M2M_DMAC_TRIGGER_ID TC0_DMAC_ID_MC_0
  • SAM DA1 Xplained Pro.
    #define PWM_MODULE EXT1_PWM_MODULE
    #define PWM_OUT_PIN EXT1_PWM_0_PIN
    #define PWM_OUT_MUX EXT1_PWM_0_MUX
    #define M2M_DMAC_TRIGGER_ID TC6_DMAC_ID_MC_0
  • SAM HA1G16A Xplained Pro.
    #define PWM_MODULE EXT1_PWM_MODULE
    #define PWM_OUT_PIN EXT1_PWM_0_PIN
    #define PWM_OUT_MUX EXT1_PWM_0_MUX
    #define M2M_DMAC_TRIGGER_ID TC6_DMAC_ID_MC_0
  • SAM C21 Xplained Pro.
    #define PWM_MODULE EXT1_PWM_MODULE
    #define PWM_OUT_PIN EXT1_PWM_0_PIN
    #define PWM_OUT_MUX EXT1_PWM_0_MUX
    #define M2M_DMAC_TRIGGER_ID TC0_DMAC_ID_MC_0
    Add to the main application source file, outside of any functions:
    struct tc_module tc_instance;
    struct dma_resource example_resource;
    #define TRANSFER_SIZE (16)
    #define TRANSFER_COUNTER (32)
    static uint8_t source_memory[TRANSFER_SIZE*TRANSFER_COUNTER];
    static uint8_t destination_memory[TRANSFER_SIZE*TRANSFER_COUNTER];
    static volatile bool transfer_is_done = false;
    DmacDescriptor example_descriptor SECTION_DMAC_DESCRIPTOR;
    Copy-paste the following setup code to your user application:
    #define TRANSFER_SIZE (16)
    #define TRANSFER_COUNTER (32)
    static uint8_t source_memory[TRANSFER_SIZE*TRANSFER_COUNTER];
    static uint8_t destination_memory[TRANSFER_SIZE*TRANSFER_COUNTER];
    static volatile bool transfer_is_done = false;
    DmacDescriptor example_descriptor SECTION_DMAC_DESCRIPTOR;
    void configure_tc(void)
    {
    struct tc_config config_tc;
    config_tc.counter_size = TC_COUNTER_SIZE_16BIT;
    config_tc.wave_generation = TC_WAVE_GENERATION_NORMAL_PWM;
    config_tc.counter_16_bit.compare_capture_channel[0] = (0xFFFF / 4);
    config_tc.pwm_channel[0].enabled = true;
    config_tc.pwm_channel[0].pin_out = PWM_OUT_PIN;
    config_tc.pwm_channel[0].pin_mux = PWM_OUT_MUX;
    tc_init(&tc_instance, PWM_MODULE, &config_tc);
    tc_enable(&tc_instance);
    }
    void transfer_done(struct dma_resource* const resource )
    {
    UNUSED(resource);
    transfer_is_done = true;
    }
    void configure_dma_resource(struct dma_resource *resource)
    {
    struct dma_resource_config config;
    config.peripheral_trigger = M2M_DMAC_TRIGGER_ID;
    dma_allocate(resource, &config);
    }
    void setup_dma_descriptor(DmacDescriptor *descriptor)
    {
    struct dma_descriptor_config descriptor_config;
    descriptor_config.block_transfer_count = TRANSFER_SIZE;
    descriptor_config.source_address = (uint32_t)source_memory + TRANSFER_SIZE;
    descriptor_config.destination_address =
    (uint32_t)destination_memory + TRANSFER_SIZE;
    dma_descriptor_create(descriptor, &descriptor_config);
    }
    Add to user application initialization (typically the start of main()):
    configure_tc();

Workflow

Create variables

  1. Create a module software instance structure for the TC module to store the TC driver state while it is in use.
    struct tc_module tc_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 a module software instance structure for DMA resource to store the DMA resource state while it is in use.
    struct dma_resource example_resource;
    Note
    This should never go out of scope as long as the module is in use. In most cases, this should be global.

Configure TC

  1. Create a TC module configuration struct, which can be filled out to adjust the configuration of a physical TC peripheral.
    struct tc_config config_tc;
  2. Initialize the TC 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.
  3. Alter the TC settings to configure the counter width, wave generation mode, and the compare channel 0 value.
    config_tc.counter_size = TC_COUNTER_SIZE_16BIT;
    config_tc.wave_generation = TC_WAVE_GENERATION_NORMAL_PWM;
    config_tc.counter_16_bit.compare_capture_channel[0] = (0xFFFF / 4);
  4. Alter the TC settings to configure the PWM output on a physical device pin.
    config_tc.pwm_channel[0].enabled = true;
    config_tc.pwm_channel[0].pin_out = PWM_OUT_PIN;
    config_tc.pwm_channel[0].pin_mux = PWM_OUT_MUX;
  5. Configure the TC module with the desired settings.
    tc_init(&tc_instance, PWM_MODULE, &config_tc);
  6. Enable the TC module to start the timer and begin PWM signal generation.
    tc_enable(&tc_instance);

Configure DMA

  1. 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;
  2. Initialize the DMA resource configuration struct with the module's default values.
    config.peripheral_trigger = M2M_DMAC_TRIGGER_ID;
    Note
    This should always be performed before using the configuration struct to ensure that all values are initialized to known default settings.
  3. Allocate a DMA resource with the configurations.
    dma_allocate(resource, &config);
  4. 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;
  5. 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.
  6. Set the specific parameters for a DMA transfer with transfer size, source address, and destination address.
    descriptor_config.block_transfer_count = TRANSFER_SIZE;
    descriptor_config.source_address = (uint32_t)source_memory + TRANSFER_SIZE;
    descriptor_config.destination_address =
    (uint32_t)destination_memory + TRANSFER_SIZE;
  7. Create the DMA transfer descriptor.
    dma_descriptor_create(descriptor, &descriptor_config);
  8. Add the DMA transfer descriptor to the allocated DMA resource.
    dma_add_descriptor(&example_resource, &example_descriptor);
  9. Register a callback to indicate transfer status.
    dma_register_callback(&example_resource, transfer_done,
  10. The transfer done flag is set in the registered callback function.
    void transfer_done(struct dma_resource* const resource )
    {
    UNUSED(resource);
    transfer_is_done = true;
    }

Prepare data

  1. Setup memory content for validate transfer.
    for (i = 0; i < TRANSFER_SIZE*TRANSFER_COUNTER; i++) {
    source_memory[i] = i;
    }

Use Case

Code

Copy-paste the following code to your user application:

for(i=0;i<TRANSFER_COUNTER;i++) {
transfer_is_done = false;
dma_start_transfer_job(&example_resource);
while (!transfer_is_done) {
/* Wait for transfer done */
}
example_descriptor.SRCADDR.reg += TRANSFER_SIZE;
example_descriptor.DSTADDR.reg += TRANSFER_SIZE;
}
while(1);

Workflow

  1. Start the loop for transfer.
    for(i=0;i<TRANSFER_COUNTER;i++) {
    transfer_is_done = false;
    dma_start_transfer_job(&example_resource);
    while (!transfer_is_done) {
    /* Wait for transfer done */
    }
    example_descriptor.SRCADDR.reg += TRANSFER_SIZE;
    example_descriptor.DSTADDR.reg += TRANSFER_SIZE;
    }
  2. Set the transfer done flag as false.
    transfer_is_done = false;
  3. Start the transfer job.
    dma_start_transfer_job(&example_resource);
  4. Wait for transfer done.
    while (!transfer_is_done) {
    /* Wait for transfer done */
    }
  5. Update the source and destination address for next transfer.
    example_descriptor.SRCADDR.reg += TRANSFER_SIZE;
    example_descriptor.DSTADDR.reg += TRANSFER_SIZE;
  6. Enter endless loop.
    while(1);