🛠 Acquisition ADC en DMA circulaire

GD32F103 (GigaDevice) — chip + Blue-Pill-compatible boards · avance · Schéma : IMG-gd32f103-avance-2-dma-adc.svg — flux ADC0 vers DMA0 canal 0 vers un tampon en SRAM en mode circulaire, sans intervention CPU, sur GD32F103C8

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

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

Other tutorials for this board

Code examples for this board

← Back to the board 🛠 All tutorials