🛠 Acquisition ADC en DMA circulaire
Objectives
Offload analog acquisition from the CPU: configure ADC0 for continuous conversions driven by DMA0 (7 channels on the GD32F103) in circular mode, to permanently fill a buffer of 64 12-bit samples without any read interruptions. This is the basis for an oscilloscope, a VU meter, or a digital filter. You will manipulate RCU (including the ADC clock divider, max 14 MHz), GPIO in AIN mode, ADC, and DMA using SPL.
Hardware
- GD32F103C8T6 / CBT6 Blue Pill compatible board (reminder: ADC2 is absent on C4/C6/C8/CB, work on ADC0)
- Signal to acquire on an analog input, e.g., PA1 (channel IN1) >
- Signal never above 3.3 V (respect 2.6-3.6 V supply)
- USART0 console (PA9/PA10) to print the buffer, SWD programmer
Complete code
/* dma_adc.c — GD32F103C8, SPL V2.2.x : ADC0 continue + DMA0 CH0 circulaire. */
#include "gd32f10x.h"
#define NSAMPLES 64
static uint16_t echantillons[NSAMPLES];
int main(void)
{
dma_parameter_struct dp;
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_ADC0);
rcu_periph_clock_enable(RCU_DMA0);
/* APB2 = 108 MHz -> /8 = 13,5 MHz <= 14 MHz max (datasheet) */
rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8);
gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, GPIO_PIN_1);
/* ADC0 : canal régulier unique, IN1, conversions continues sans trigger */
adc_deinit(ADC0);
adc_mode_config(ADC_MODE_FREE);
adc_special_function_config(ADC0, ADC_SCAN_MODE, DISABLE);
adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, ENABLE);
adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);
adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 1);
adc_regular_channel_config(ADC0, 0, 1, ADC_SAMPLETIME_239POINT5);
adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL,
ADC_EXTTRIG_REGULAR_NONE);
adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);
adc_dma_mode_enable(ADC0);
adc_enable(ADC0);
adc_calibration_enable(ADC0);
/* DMA0 canal 0 : ADC0_RDATA -> echantillons[], circulaire, 16 bits */
dma_deinit(DMA0, DMA_CH0);
dp.peripheral_address = (uint32_t)&ADC_RDATA(ADC0);
dp.memory_address = (uint32_t)echantillons;
dp.transfer_mode = DMA_MODE_CIRCULAR;
dp.peripheral_source = DMA_PERIPHERAL_ADC0;
dp.peripheral_width = DMA_PERIPHERAL_WIDTH_16BIT;
dp.memory_width = DMA_MEMORY_WIDTH_16BIT;
dp.number = NSAMPLES;
dp.priority = DMA_PRIORITY_HIGH;
dma_init(DMA0, DMA_CH0, &dp);
dma_flag_clear(DMA0, DMA_CH0, DMA_FLAG_G);
dma_enable(DMA0, DMA_CH0);
adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);
rcu_periph_clock_enable(RCU_USART0); /* console 115200, voir t. console */
while (1) {
/* le DMA remplit le tampon en continu ; le CPU est libre */
if (dma_flag_get(DMA0, DMA_CH0, DMA_FLAG_HT) == SET) {
dma_flag_clear(DMA0, DMA_CH0, DMA_FLAG_HT);
/* première moitié prête : la traiter ou l'imprimer */
}
}
}
Expected verification
The echantillons[] buffer permanently contains values 0-4095 consistent with the input voltage (0 V gives ~0, 3.3 V gives ~4095), refreshed without the main code reading the ADC. By injecting a periodic signal (filtered PWM output, or slowly moved potentiometer), you see the waveform in the buffer via the editor (live view). The CPU remains available: a blinking LED in the main loop continues to blink regularly.
Common errors
- Buffer stuck at 0 or 4095: wrong ADC channel number, pin not in AIN mode, or ADC clock > 14 MHz (choose the appropriate
RCU_CKADC_...divider for 108 MHz APB2).
- DMA not starting:
adc_dma_mode_enableforgotten, or software trigger not launched, or DMA source peripheralDMA_PERIPHERAL_ADC0inconsistent with the ADC used. - Half-false data:
peripheral_width8 bits against 16-bit register; align widths onADC_RDATA(16 bits). - Read race: reading the buffer while the DMA is overwriting it; in circular mode, work by halves (HT/TC flags) or double buffering.
- Confusing DMA controllers: the GD32F103 has DMA0 (7 channels) and DMA1 (5 channels); the channel/peripheral mapping is fixed by hardware, verify that ADC0 is indeed on CH0 of DMA0 in your configuration.
- Forgetting ADC calibration: without
adc_calibration_enable, the first samples may be biased.
Other tutorials for this board
Code examples for this board
clignotante.inoArduinobouton-led.inoArduinorespiration.inoArduinopotentiometre.inoArduinopont-uart.inoArduinooled-i2c.inoArduino