🛠 Acquisition ADC en DMA circulaire
Objetivos
Descargar la CPU de la adquisición analógica: configurar ADC0 en conversiones continuas gestionadas por DMA0 (7 canales en el GD32F103) en modo circular, para rellenar permanentemente un búfer de 64 muestras de 12 bits sin ninguna interrupción de lectura. Esta es la base de un osciloscopio, un vúmetro o un filtro digital. Manipulará RCU (incluido su divisor de reloj ADC, máx. 14 MHz), GPIO en modo AIN, ADC y DMA mediante SPL.
Material
- Tarjeta GD32F103C8T6 / CBT6 compatible Blue Pill (recordatorio: ADC2 no está presente en C4/C6/C8/CB, trabaje con ADC0)
- Señal a adquirir en una entrada analógica, por ejemplo, PA1 (canal IN1) > SIN VERIFICAR: la correspondencia pin/canal debe confirmarse en la hoja de datos Rev 2.9
- Señal que nunca supere los 3,3 V (respetar la alimentación de 2,6-3,6 V)
- Consola USART0 (PA9/PA10) para imprimir el búfer, programador SWD
Código completo
/* 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 */
}
}
}
Verificación esperada
El búfer echantillons[] contiene permanentemente valores de 0-4095 coherentes con la tensión de entrada (0 V da ~0, 3,3 V da ~4095), actualizados sin que el código principal lea el ADC. Al inyectar una señal periódica (salida PWM filtrada, o potenciómetro movido lentamente), se observa la forma de onda en el búfer a través del editor (vista en vivo). La CPU sigue disponible: un LED parpadeante en el bucle principal continúa parpadeando con regularidad.
Errores frecuentes
- Búfer bloqueado en 0 o 4095: número de canal ADC incorrecto, pin no configurado en modo AIN, o reloj ADC > 14 MHz (elegir el divisor RCU_CKADC_... adecuado para 108 MHz de APB2).
- DMA que no arranca: falta adc_dma_mode_enable, o disparo por software no lanzado, o dispositivo fuente DMA_PERIPHERAL_ADC0 incoherente con el ADC utilizado.
- Datos parcialmente erróneos: peripheral_width de 8 bits contra registro de 16 bits; alinear los anchos con ADC_RDATA (16 bits).
- Condición de carrera: leer el búfer mientras el DMA lo sobrescribe; en modo circular, trabajar por mitades (flags HT/TC) o con doble búfer.
- Confundir los controladores DMA: el GD32F103 tiene DMA0 (7 canales) y DMA1 (5 canales); el mapeo canal/dispositivo está fijado por el hardware, verifique que ADC0 esté correctamente en CH0 de DMA0 en su configuración.
- Olvidar la calibración del ADC: sin adc_calibration_enable, las primeras muestras pueden estar sesgadas.
Otros tutoriales para esta placa
Ejemplos de código para esta placa
clignotante.inoArduinobouton-led.inoArduinorespiration.inoArduinopotentiometre.inoArduinopont-uart.inoArduinooled-i2c.inoArduino