DÍA 117 DE 365

Fase 2: Fundamentos de Hardware y Electrónica

📖 Teoría a Estudiar

El Disparador de Hardware (Hardware Triggering)

Los periféricos en la arquitectura ARM Cortex-M están interconectados por debajo de la mesa. En lugar de que tu código C ejecute ADC1->CR2 |= SWSTART para pedirle una lectura al ADC, podemos configurar el ADC para que reaccione ciegamente a una señal externa, llamada EXTSEL (External Event Select).

Esa señal puede venir de un pin físico, pero lo más poderoso es conectarla a un Hardware Timer (como el TIM3). Configuramos el Timer para que genere un evento llamado TRGO (Trigger Output) cada vez que su contador se reinicia. De esta forma, el Timer marca el compás, el ADC ejecuta el baile y el DMA recoge los boletos, todo en hardware puro.

La Cadena de Eventos Zero-CPU

  1. TIM3 (1 kHz): El Timer llega a su límite (ARR) cada 1 milisegundo y dispara un pulso TRGO interno.
  2. ADC1 (Scan Mode): Detecta el pulso TRGO. Despierta, lee el pin PA1 (Presión) y luego el pin PA2 (Temp).
  3. ADC1 (DMA Request): Por cada canal convertido, el ADC envía una petición de transferencia al DMA.
  4. DMA2 (Stream 0): Atrapa el dato del registro ADC1->DR y lo mueve directamente a nuestro arreglo en memoria RAM estática.
  5. CPU (Cortex-M): Está completamente dormido o procesando telemetría por Wi-Fi. Costo computacional = 0%.
CADENA DE ADQUISICIÓN 100% AUTÓNOMA (ZERO-OVERHEAD) TIM3 (Cronómetro) Frecuencia = 1 kHz MMS = TRGO (Update) TRGO Event (1000 veces x seg) ADC1 (Modo Scan) PA1 PA2 EXTEN = Flanco Subida EXTSEL = TIM3 TRGO ADC->DR DMA Req (DDS=1) DMA2 Stream 0 CIRC = 1 SRAM Array[0] Array[1] CPU: 0% Uso

⚙️ Ejercicios Prácticos

Ejercicio 1: Modifica la estructura del Día 116. Esta vez, en lugar de usar SWSTART en el main, configuraremos el TIM3 para que genere un evento TRGO cada milisegundo (1 kHz). Luego, configuraremos el ADC1 para que escuche ese evento usando los bits EXTEN y EXTSEL.


/**
 * @file adc_dma_timer.c
 * @brief Adquisición 100% Autónoma (ADC + DMA + TIM3)
 */

#include "stm32f401xe.h"

// Búfer en RAM para Presión y Temperatura
volatile uint16_t adc_buffer[2] = {0, 0}; 

void inicializar_adquisicion_autonoma(void) {
    
    // --- 1. ACTIVAR RELOJES (GPIO, ADC, DMA2, TIM3) ---
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
    RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
    RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
    
    // Configurar PA1 (Presión) y PA2 (Temp) en Modo Analógico
    GPIOA->MODER |= GPIO_MODER_MODE1 | GPIO_MODER_MODE2;
    GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPD1 | GPIO_PUPDR_PUPD2);

    // ====================================================
    // 2. CONFIGURACIÓN DEL TIM3 (Trigger Maestro)
    // ====================================================
    TIM3->PSC = 84 - 1;       // Reloj Timer a 1 MHz
    TIM3->ARR = 1000 - 1;     // Disparo cada 1000 µs (1 ms = 1 kHz)
    
    // Generar TRGO (Trigger Output) cuando ocurra un Evento de Actualización (Update/Overflow)
    TIM3->CR2 &= ~TIM_CR2_MMS;
    TIM3->CR2 |= (2 << TIM_CR2_MMS_Pos); // MMS = 010 (Update)
    
    // ====================================================
    // 3. CONFIGURACIÓN DEL DMA2 (El Transportista)
    // ====================================================
    DMA2_Stream0->CR &= ~DMA_SxCR_EN;
    while(DMA2_Stream0->CR & DMA_SxCR_EN); 
    
    DMA2_Stream0->CR &= ~DMA_SxCR_CHSEL; // Canal 0
    DMA2_Stream0->PAR = (uint32_t)&ADC1->DR;
    DMA2_Stream0->M0AR = (uint32_t)&adc_buffer[0];
    DMA2_Stream0->NDTR = 2; // Dos sensores
    
    // MINC (Incrementar RAM), CIRC (Modo Circular), Tamaño 16 bits
    DMA2_Stream0->CR |= DMA_SxCR_MINC | DMA_SxCR_CIRC | 
                        (1 << DMA_SxCR_MSIZE_Pos) | (1 << DMA_SxCR_PSIZE_Pos);
    DMA2_Stream0->CR |= DMA_SxCR_EN;

    // ====================================================
    // 4. CONFIGURACIÓN DEL ADC1 (El Esclavo del Timer)
    // ====================================================
    ADC1->CR2 &= ~ADC_CR2_ADON;
    ADC1->CR1 |= ADC_CR1_SCAN; 
    
    ADC1->SQR1 |= (1 << ADC_SQR1_L_Pos); // 2 canales
    ADC1->SQR3 |= (1 << 0) | (2 << 5);   // CH1 y CH2
    
    // DMA Enable y DMA Disable Selection (Continuo Request)
    ADC1->CR2 |= ADC_CR2_DMA | ADC_CR2_DDS;
    
    // HARDWARE TRIGGER: Reaccionar al TIM3
    // EXTEN = 01 (Disparo en flanco de subida)
    ADC1->CR2 |= (1 << ADC_CR2_EXTEN_Pos); 
    // EXTSEL = 1000 (Valor 8 corresponde al TIM3_TRGO en el STM32F4)
    ADC1->CR2 |= (8 << ADC_CR2_EXTSEL_Pos); 
    
    ADC1->CR2 |= ADC_CR2_ADON;
    for(volatile int i=0; i<10000; i++); // Estabilización
    
    // ====================================================
    // 5. ARRANCAR EL CORAZÓN DEL SISTEMA
    // ====================================================
    // Solo encendemos el Timer. El ADC y el DMA reaccionarán solos para siempre.
    TIM3->CR1 |= TIM_CR1_CEN;
}

int main(void) {
    
    inicializar_adquisicion_autonoma();
    
    while(1) {
        // En cualquier microsegundo aleatorio del día, tu CPU puede leer estas variables.
        // Siempre tendrán la lectura física de hace menos de 1 milisegundo.
        // ¡Magia asíncrona pura!
        
        uint16_t crudo_presion = adc_buffer[0];
        uint16_t crudo_temp = adc_buffer[1];
        
        // (Aquí llamarías a tu filtro pasa-bajos de promedio móvil del Día 113
        // y a la matemática de compensación termovolumétrica del Día 115)
    }
    
    return 0;
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Determinismo Temporal en Muestreo Metrológico.

En el procesamiento de señales, el teorema de Nyquist-Shannon es implacable: si el tiempo entre tus muestras (lecturas) varía por un microsegundo de latencia (Jitter), tus filtros digitales matemáticos y cálculos de promedio generarán ruido irreal en lugar de limpiarlo.

Al encadenar el TIM3 ➡️ ADC1 ➡️ DMA2, acabas de inyectar rigor matemático absoluto al muestreo de tu Despachador de Gas. La matriz de hardware de tu placa garantiza que la presión y la temperatura se capturen y almacenen en la SRAM cada $1000\text{ \mu s}$ exactos, incluso si tu núcleo Cortex-M se traba momentáneamente o si una interrupción prioritaria ocurre al mismo tiempo. Es ingeniería civil aplicada al diseño de silicio: carreteras separadas, flujo ininterrumpido.

📝 Resumen del Día