This use case shows how to set up a burst DMA transfer between a peripheral register set and the main system memory.
In this use case, the DMA is configured for:
- Burst length: 2 bytes (one 16-bit result from the ADC peripheral)
- Transfer count: 1024 (512 16-bit samples)
- Source: ADC channel 0 result register
- Destination: Buffer located in RAM
- Source address reload mode: End of burst
- Destination address reload mode: End of transaction
- Source and destination address direction mode: Increment
In this use case data is copied from the 16-bit ADC channel 0 result register to a buffer in RAM, each time the ADC indicates that a new conversion has completed.
Setup steps
Prerequisites
For the setup code of this use case to work, the following must be added to the project:
- System Clock Manager Service (sysclk)
The ADC must be configured according to the XMEGA ADC driver Quick Start basic use case.
Example code
Add to application C-file:
#define DMA_CHANNEL 0
#define DMA_BUFFER_SIZE 1024
static uint16_t adc_samples[DMA_BUFFER_SIZE / 2];
static void dma_init(void)
{
memset(&dmach_conf, 0, sizeof(dmach_conf));
DMA_CH_SRCRELOAD_BURST_gc);
DMA_CH_DESTRELOAD_TRANSACTION_gc);
(uint16_t)(uintptr_t)&ADCA.CH0RES);
(uint16_t)(uintptr_t)adc_samples);
}
Add to main()
:
Workflow
- Define the DMA channel that will be used for the transfer for convenience:
- Define the array length that will be the used for the source and destination buffers located in RAM:
#define DMA_BUFFER_SIZE 1024
- Create a global array that will hold the ADC sample result data copied by the DMA controller channel when it is triggered (the buffer size is halved as each sample is two bytes long):
static uint16_t adc_samples[DMA_BUFFER_SIZE / 2];
- Create a function
dma_init()
to intialize the DMA:
static void dma_init(void)
{
}
- Create config struct for DMA channel:
- Make sure the configuration structure is zeroed out to ensure that all values are reset to their defaults before writing new values:
memset(&dmach_conf, 0, sizeof(dmach_conf));
- Configure the DMA channel for two byte bursts (the size of a single ADC conversion) with a transfer length equal to the size of the destination buffer:
- Configure the DMA channel to reset the source address at the end of each burst transfer, and the destination addresses at the end of the complete transaction (i.e. after
DMA_BUFFER_SIZE
bytes copied):
- Configure the DMA channel to increment the source and destination addresses after each byte transferred:
- Configure the DMA channel source and destination addresses to the ADC module channel 0 result registers and RAM buffer respectively:
(uint16_t)(uintptr_t)&ADCA.CH0RES);
(uint16_t)(uintptr_t)adc_samples);
- Set the DMA channel trigger source to the ADC module channel 0 complete event:
- Configure the DMA channel in single shot mode, so that each time it is triggered it will perform one bust transfer only:
- Enable the DMA module so that channels can be configured in it:
-
- Attention
- Calling dma_enable() will result in a soft-reset of the entire DMA module, clearing all channel configurations. If more than one DMA channel is to be configured, this function should be called only once in the application initialization procedure only.
- Set up the DMA channel interrupt to run at low interrupt priority, and link it to the user created
dma_transfer_done()
function:
- Write the DMA channel configuration to the DMA and enable it so that it can be triggered to start the transfer:
- Initialize the clock system:
- Call our DMA init function:
Usage steps
Example code
Add to, e.g., main loop in application C-file:
Workflow
- Start an ADC conversion, result will be automatically copied to the global buffer when complete:
- Wait for the ADC to complete before triggering the next conversion by polling the ADC channel 0 complete interrupt flag: