🛠 Acquisition ADC en DMA circulaire
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
- Carte GD32F103C8T6 / CBT6 compatible Blue Pill (rappel : ADC2 absente des C4/C6/C8/CB, travaillez sur ADC0)
- Signal à acquérir sur une entrée analogique, par ex. PA1 (canal IN1) >
- Signal jamais au-dessus de 3,3 V (respecter 2,6-3,6 V d'alimentation)
- Console USART0 (PA9/PA10) pour imprimer le tampon, programmeur SWD
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
- Tampon figé à 0 ou 4095 : mauvais numéro de canal ADC, broche pas en mode AIN, ou horloge ADC > 14 MHz (choisir le diviseur RCU_CKADC_... adapté à 108 MHz d'APB2).
- DMA qui ne part pas : adc_dma_mode_enable oublié, ou software trigger non lancé, ou périphérique source DMA_PERIPHERAL_ADC0 incohérent avec l'ADC utilisée.
- Données à moitié fausses : peripheral_width 8 bits contre registre 16 bits ; aligner les largeurs sur ADC_RDATA (16 bits).
- Course de lecture : lire le tampon pendant que le DMA l'écrase ; en circulaire, travailler par moitiés (flags HT/TC) ou double tampon.
- Confondre les contrôleurs DMA : le GD32F103 a DMA0 (7 canaux) et DMA1 (5 canaux) ; le mapping canal/périphérique est fixé par le matériel, vérifiez que ADC0 est bien sur CH0 de DMA0 dans votre configuration.
- Oublier la calibration ADC : sans adc_calibration_enable, les premiers échantillons peuvent être biaisés.
Autres tutoriels pour cette carte
Exemples de code pour cette carte
clignotante.inoArduinobouton-led.inoArduinorespiration.inoArduinopotentiometre.inoArduinopont-uart.inoArduinooled-i2c.inoArduino