DÍA 189 DE 365

Fase 4: FreeRTOS y Conectividad IoT

📖 Teoría a Estudiar

El Antipatrón del Flag (Polling)

En Bare-Metal, cuando una interrupción de hardware ocurría, solíamos encender una variable booleana (flag_pulso = true;). Luego, en el Súper Bucle, escribíamos if(flag_pulso == true) { hacer_algo(); }.

En un RTOS, hacer esto es desastroso. Significa que la Tarea debe estar constantemente girando en un bucle preguntando "¿Ya ocurrió? ¿Ya ocurrió?", desperdiciando el 100% del procesador y causando inanición a las tareas menores.

La Solución: El Semáforo Binario

Un Semáforo Binario es una bandera administrada directamente por el Kernel del Sistema Operativo. Funciona como una "Ficha" o "Token" que solo puede valer 0 o 1.

  1. La Tarea (Software): Llama a la función osSemaphoreAcquire(). Si el semáforo está vacío (0), el RTOS arranca a la Tarea de la CPU y la pone en estado BLOCKED (coma inducido). Consumo de CPU: 0%.
  2. La Interrupción (Hardware): Cuando el sensor detecta un evento, la ISR dispara osSemaphoreRelease(). Esto inyecta el Token (1) en el semáforo.
  3. El Milagro de Latencia Cero: Al recibir el Token, el Kernel de FreeRTOS despierta a la Tarea instantáneamente (pasa a READY/RUNNING). La tarea se roba la ficha (la vuelve a poner en 0), procesa la matemática pesada de los litros, y vuelve a dormirse esperando el siguiente pulso.

Regla de Oro: Los Semáforos Binarios se usan para Sincronización (Signaling) de un evento. No los confundas con los Mutexes (Día 190), que se usan para proteger variables compartidas.

SINCRONIZACIÓN ISR A TAREA VÍA SEMÁFORO BINARIO 1. HARDWARE (Sensor) 2. KERNEL (RTOS) 3. SOFTWARE (Task) TIEMPO → ESTADO: BLOCKED osSemaphoreAcquire() = Espera Vacío (0) Pulso Físico! EXTI_IRQHandler osSemaphoreRelease() Da el Token Lleno (1) WAKE UP! ESTADO: RUNNING 1. Despierta de inmediato 2. Consume el token (0) Vacío (0) litros_totales += 0.05 Matemática pesada segura Vuelve a BLOCKED

⚙️ Ejercicios Prácticos

Ejercicio 1: Reestructuraremos el sensor de flujo de gas. Quitaremos toda la matemática de la interrupción (ISR) y la moveremos a un Hilo de Alta Prioridad. Usaremos osSemaphoreNew para crear el candado, osSemaphoreRelease en la ISR para liberar el pulso, y osSemaphoreAcquire(id, osWaitForever) en la Tarea para obligarla a dormir para siempre hasta que el hardware avise.


/**
 * @file sensor_hall_rtos.c
 * @brief Sincronización Hardware-Software mediante Semáforos Binarios
 */

#include "stm32f401xe.h"
#include "cmsis_os2.h"

// ID Global de nuestro Semáforo
osSemaphoreId_t sem_pulso_caudalimetro;

// Variables protegidas de la aplicación
float litros_despachados = 0.0f;
const float LITROS_POR_PULSO = 0.025f; // 25ml por pulso del Hall

// =======================================================
// 1. LA INTERRUPCIÓN HARDWARE (ISR) - Rápida y Estúpida
// =======================================================
// Se dispara cuando el imán del caudalímetro pasa por el sensor (Pin PB0)
void EXTI0_IRQHandler(void) {
    
    // 1. Limpiar bandera de interrupción del hardware
    EXTI->PR |= (1 << 0);
    
    // 2. MAGIA RTOS: Soltar el semáforo (Dar el Token)
    // En CMSIS-OS v2, esta función es segura para usarse desde ISR.
    // El RTOS sabrá que, al terminar esta ISR, debe forzar un Cambio de 
    // Contexto inmediato a la Tarea que estaba esperando este semáforo.
    osSemaphoreRelease(sem_pulso_caudalimetro);
    
    // ¡FIN! Cero matemáticas, cero latencia. Salimos en microsegundos.
}

// =======================================================
// 2. EL HILO DE SOFTWARE (Tarea de Alta Prioridad)
// =======================================================
void Thread_Procesar_Caudal(void *argument) {
    
    for (;;) {
        // 1. DORMIR PROFUNDAMENTE HASTA EL EVENTO
        // 'osWaitForever' asegura que si no hay pulsos, la tarea consumirá 0% de CPU.
        // Cuando la ISR haga Release, esta función despertará y consumirá el Token.
        osSemaphoreAcquire(sem_pulso_caudalimetro, osWaitForever);
        
        // --- A PARTIR DE AQUÍ ESTAMOS EN LA TAREA (No en la ISR) ---
        
        // 2. Ejecutar matemática de punto flotante pesada con calma
        litros_despachados += LITROS_POR_PULSO;
        
        // 3. Evaluar límites y accionar válvulas si es necesario
        if (litros_despachados >= LIMITES_DE_VENTA) {
            Valvula_Cerrar();
        }
        
        // Al terminar el bucle, vuelve arriba y se Bloquea otra vez.
    }
}

// =======================================================
// INICIALIZACIÓN
// =======================================================
int main(void) {
    SystemClock_Config();
    osKernelInitialize();
    
    // Configurar el Pin B0 como entrada con interrupción (EXTI)
    // Hardware_EXTI_Init(); 
    
    // CREACIÓN DEL SEMÁFORO BINARIO
    // Parámetro 1: Cuenta máxima (1 = Binario).
    // Parámetro 2: Cuenta inicial (0 = Empieza vacío, bloqueando a quien lo pida).
    sem_pulso_caudalimetro = osSemaphoreNew(1, 0, NULL);
    
    // Crear el Hilo con Alta Prioridad
    const osThreadAttr_t attr_caudal = {
        .name = "FlowTask",
        .stack_size = 512,
        .priority = osPriorityHigh, 
    };
    osThreadNew(Thread_Procesar_Caudal, NULL, &attr_caudal);
    
    osKernelStart();
    while (1);
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Sincronización Diferida (Deferred Interrupt Processing).

En el Bare-Metal de la Fase 2, programamos la función matemática `Calcular_Precio_y_Flujo()` directamente dentro de la rutina de interrupción `EXTI0_IRQHandler`. En aquel entonces funcionaba. Pero ahora, con el procesador ejecutando un RTOS, protocolos de red WiFi e interfaces gráficas complejas, dejar matemática de punto flotante dentro de una ISR es un pecado arquitectónico. Las ISR bloquean al RTOS entero; si una ISR tarda mucho, los Ticks del sistema se pierden y la máquina entera se desincroniza.

Al implementar **Semáforos Binarios**, hemos separado el "Aviso" del "Trabajo". El hardware avisa instantáneamente y huye de la ISR. La matemática pesada se realiza en `Thread_Procesar_Caudal`. Esta técnica profesional se llama **Procesamiento de Interrupción Diferida**. Mantiene el Kernel sano, los Ticks precisos y garantiza que jamás perderás un solo pulso del sensor, incluso bombeando Gas LP a máxima presión.

📝 Resumen del Día