El STM32H725 ofrece capacidades avanzadas para la adquisición de datos de alta velocidad. En esta implementación, se detalla cómo configurar el ADC1 en modo diferencial, utiliazndo un temporizador (TIM2) como fuente de disparo y un esquema de doble búfer mediante DMA para garantizar un procesamiento de datos continuo y sin pérdidas.
Configuración del Sistema
Para lograr un rendimiento óptimo, la configuración en el entorno de desarrollo debe considerar los siguientes puntos:
- ADC: Configurado en modo de entrada diferencial con disparo externo activado por el evento de actualización del temporizador.
- DMA: Se debe habilitar el modo circular y la gestión de doble búfer para permitir que el hardware alterne automáticamente entre dos áreas de memoria.
- Timer: El temporizador actúa como el reloj maestro de muestreo, definiendo la frecuencia exacta de adquisición.
Implementación del Código
Inicialización y Registro de Callbacks
A diferencia del flujo estándar, aquí registramos manualmente las funciones de transferencia completa para ambos búferes del DMA (M0 y M1) y gestionamos el inicio del flujo de datos de forma secuencial.
/* Inicialización de periféricos */
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_TIM2_Init();
MX_USART3_UART_Init();
/* Registro manual de callbacks de interrupción DMA */
HAL_DMA_RegisterCallback(hadc1.DMA_Handle, HAL_DMA_XFER_CPLT_CB_ID, OnBuffer0Complete);
HAL_DMA_RegisterCallback(hadc1.DMA_Handle, HAL_DMA_XFER_M1CPLT_CB_ID, OnBuffer1Complete);
HAL_DMA_RegisterCallback(hadc1.DMA_Handle, HAL_DMA_XFER_ERROR_CB_ID, OnTransferError);
/* 1. Configuración e inicio del modo MultiBuffer del DMA */
HAL_DMAEx_MultiBufferStart_IT(hadc1.DMA_Handle,
(uint32_t)&ADC1->DR,
(uint32_t)raw_buffer_0,
(uint32_t)raw_buffer_1,
SAMPLES_PER_BLOCK);
/* 2. Activación del ADC para esperar el trigger */
HAL_ADC_Start(&hadc1);
/* 3. Inicio del temporizador para generar la señal de disparo */
HAL_TIM_Base_Start(&htim2);
Gestión de Interrupciones (Callbacks)
Estas funciones se ejecutan cuando uno de los dos búferes está lleno. Se utiliza un indicador de estado para notificar al bucle principal qué sección de memoria está lista para ser procesada.
void OnTransferError(DMA_HandleTypeDef *hdma) {
error_flag_count++;
}
void OnBuffer0Complete(DMA_HandleTypeDef *hdma) {
target_ready_index = 1;
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_1); // Indicador visual de actividad
}
void OnBuffer1Complete(DMA_HandleTypeDef *hdma) {
target_ready_index = 2;
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_9); // Indicador visual de actividad
}
Bucle de Procesamiento de Datos
El bucle principle monitorea la variable target_ready_index y transfiere los datos del búfer activo a un almacenamiento persistente en la SRAM de mayor tamaño.
while (total_captured < MAX_STORAGE_SIZE) {
if (target_ready_index == 1) {
uint32_t remaining = MAX_STORAGE_SIZE - total_captured;
uint32_t to_copy = (remaining < SAMPLES_PER_BLOCK) ? remaining : SAMPLES_PER_BLOCK;
memcpy(&global_storage[total_captured], raw_buffer_0, to_copy * sizeof(uint16_t));
total_captured += to_copy;
target_ready_index = 0;
}
else if (target_ready_index == 2) {
uint32_t remaining = MAX_STORAGE_SIZE - total_captured;
uint32_t to_copy = (remaining < SAMPLES_PER_BLOCK) ? remaining : SAMPLES_PER_BLOCK;
memcpy(&global_storage[total_captured], raw_buffer_1, to_copy * sizeof(uint16_t));
total_captured += to_copy;
target_ready_index = 0;
}
if (error_flag_count > 0) {
error_flag_count = 0;
// Manejo de errores de bus o transferencia
}
}
/* Detener adquisición tras completar la captura */
HAL_ADC_Stop(&hadc1);
HAL_TIM_Base_Stop(&htim2);
Análisis de Parámetros y Resultados
Para una frecuencia de muestreo de aproximadamente 97.65 kHz y un tamaño de búfer de 1024 puntos, el tiempo requerido para llenar un bloque individual es de 10.49 ms. El ciclo completo de doble búfer es de 20.98 ms, lo que equivale a una frecuencia de ciclo de ~47.66 Hz.
La diferencia de fase de 180° observada en los pines de depuración (PB1 y PC9) confirma que el hardware alterna correctamente entre los dos espacios de memoria designados.
Resultados de Caracterización (ADC1 Diferencial)
| Métrica | Entrada Flotante | Entrada a Tierra | Carga de 50 Ω |
|---|---|---|---|
| Media (LSB) | 33055.46 | 31940.28 | 31931.44 |
| Ruido RMS (LSB) | 158.11 | 4.96 | 4.62 |
| SNR (dB) | 46.33 | 76.40 | 77.01 |
| ENOB (bits) | 7.40 | 12.40 | 12.50 |
Script de Aálisis en MATLAB
Este script permite procesar los datos exportados para evaluar el rendimiento dinámico del convertidor.
%% Script de Análisis de Ruido ADC
clear; clc;
% Configuración de parámetros
fs_adc = 195312.5; % Frecuencia efectiva
bits_res = 16;
v_ref = 2.5;
% Carga de datos
muestras = load('captura_adc.txt');
n_puntos = length(muestras);
t_vector = (0:n_puntos-1)/fs_adc;
lsb_val = v_ref / (2^bits_res);
% Cálculo de métricas
valor_medio = mean(muestras);
rms_ruido = std(muestras);
ruido_v_rms = rms_ruido * lsb_val;
snr_calculado = 20*log10((v_ref/2)/ruido_v_rms);
enob_estimado = (snr_calculado - 1.76)/6.02;
fprintf('--- Resultados del Análisis ---\n');
fprintf('Puntos procesados: %d\n', n_puntos);
fprintf('SNR: %.2f dB | ENOB: %.2f bits\n', snr_calculado, enob_estimado);
% Visualización
subplot(2,1,1);
plot(t_vector, muestras);
title('Señal en el Dominio del Tiempo');
xlabel('Tiempo (s)'); ylabel('Código ADC');
subplot(2,1,2);
espectro = abs(fft(muestras)/n_puntos);
f_eje = fs_adc*(0:floor(n_puntos/2))/n_puntos;
plot(f_eje, espectro(1:length(f_eje)));
title('Espectro de Amplitud');
xlabel('Frecuencia (Hz)'); ylabel('Magnitud');