🛠 Acquisition ADC en DMA circulaire

GD32F103 (GigaDevice) — puce + cartes compatibles Blue Pill · 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

Objectifs

Décharger le CPU de l'acquisition analogique : configurer ADC0 en conversions continues pilotées par le DMA0 (7 canaux sur le GD32F103) en mode circulaire, pour remplir en permanence un tampon de 64 échantillons 12 bits sans aucune interruption de lecture. C'est la base d'un oscilloscope, d'un VU-mètre ou d'un filtre numérique. Vous manipulerez RCU (dont le diviseur d'horloge ADC, 14 MHz max), GPIO en mode AIN, ADC et DMA en SPL.

Matériel

Code complet

/* 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 */
        }
    }
}

Vérification attendue

Le tampon echantillons[] contient en permanence des valeurs 0-4095 cohérentes avec la tension d'entrée (0 V donne ~0, 3,3 V donne ~4095), rafraîchies sans que le code principal ne lise l'ADC. En injectant un signal périodique (sortie PWM filtrée, ou potentiomètre bougé lentement), on voit la forme d'onde dans le tampon via l'éditeur (view live). Le CPU reste disponible : une LED clignotante dans la boucle continue de clignoter régulièrement.

Erreurs fréquentes

Autres tutoriels pour cette carte

Exemples de code pour cette carte

← Retour à la fiche 🛠 Tous les tutoriels