DÍA 190 DE 365

Fase 4: FreeRTOS y Conectividad IoT

📖 Teoría a Estudiar

El Mutex: La Llave del Baño Público

El término Mutex proviene de Mutual Exclusion (Exclusión Mutua). A diferencia de un Semáforo Binario (que usamos ayer para que el hardware le "avisara" al software), el Mutex es un token de Propiedad. Funciona exactamente igual que la llave del baño de una gasolinera.

Solo hay una llave (El Mutex). Solo hay un baño (El bus I2C1). Si la Tarea A quiere usar el I2C, primero debe pedirle la llave al RTOS. Si la llave está libre, entra y cierra la puerta. Si en ese momento el RTOS interrumpe a la Tarea A, y la Tarea B (incluso si tiene la máxima prioridad del universo) intenta usar el I2C, el RTOS le dirá: "La puerta está cerrada. Te pongo en estado BLOCKED hasta que la Tarea A devuelva la llave".

Esto garantiza matemáticamente que una transacción de hardware (leer un sensor, enviar bytes) jamás será interrumpida a la mitad por otro hilo que quiera usar el mismo recurso.

Mutex vs. Semáforo Binario (La regla de Propiedad)

Podrías pensar que son lo mismo, pero el Mutex tiene una regla estricta: Solo el Hilo que adquirió el Mutex puede liberarlo. Un Semáforo Binario puede ser liberado por cualquiera (incluso por una interrupción de hardware). Un Mutex jamás puede ser tomado o devuelto dentro de una Interrupción (ISR).

EXCLUSIÓN MUTUA: PROTEGIENDO EL BUS I2C TASK A: MEMORIA (Prioridad Normal) TASK B: SENSOR (Prioridad Alta) I2C MUTEX Llave PERIFÉRICO I2C1 SDA SCL 1. Acquire() OK 2. Escribiendo EEPROM... Preempción! (Task B despierta) 3. Acquire() FALLA! Llave ocupada. Task B -> BLOCKED La CPU regresa a Task A 4. Release() 5. RTOS le da el Mutex y lee el Sensor

⚙️ Ejercicios Prácticos

Ejercicio 1: Implementaremos el blindaje del bus I2C. Modificaremos nuestro driver `I2C_Master` para que no ejecute ni una sola señal de START físico sin antes pedirle la llave al RTOS. Si el Hilo de Mantenimiento está escribiendo en la memoria EEPROM I2C, y el Hilo de Metrología quiere leer la Presión por I2C, el Kernel congelará elegantemente al Hilo de Metrología por unos pocos microsegundos hasta que el bus esté despejado.


/**
 * @file i2c_hal_rtos.c
 * @brief Driver I2C protegido por Mutex contra colisiones asíncronas
 */

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

// ID Global del Mutex
osMutexId_t i2c_mutex_id;

// =======================================================
// INICIALIZACIÓN (Llamado en el main, antes del RTOS)
// =======================================================
void I2C_Driver_Init(void) {
    // 1. Configurar pines SDA y SCL (Open-Drain, Pull-up) y reloj del I2C...
    // (Código Bare-Metal omitido por brevedad)
    
    // 2. CREAR EL MUTEX EN EL RTOS
    const osMutexAttr_t mutex_attr = {
        .name = "I2C_Lock",
        // 'osMutexPrioInherit' previene un bug avanzado llamado "Inversión de Prioridad"
        .attr_bits = osMutexPrioInherit 
    };
    
    i2c_mutex_id = osMutexNew(&mutex_attr);
}

// =======================================================
// FUNCIÓN ENVOLTORIO PROTEGIDA (Llamada por cualquier hilo)
// =======================================================
bool I2C_EscribirDatos(uint8_t direccion_esclavo, uint8_t registro, uint8_t* datos, uint16_t longitud) {
    
    // 1. ADQUIRIR LA LLAVE DEL BAÑO (El Mutex)
    // El hilo llamador pide el Mutex. Si otro hilo lo tiene, el RTOS saca 
    // a este hilo de la CPU (Estado BLOCKED) e inyecta la constante 'osWaitForever'
    // lo que significa que el hilo dormirá eternamente hasta que el bus se libere.
    osStatus_t status = osMutexAcquire(i2c_mutex_id, osWaitForever);
    
    if (status != osOK) {
        return false; // Error catastrófico del Kernel
    }
    
    // --- ZONA CRÍTICA (Acceso Exclusivo Garantizado) ---
    // A partir de esta línea, sabemos al 100% que somos los reyes del I2C.
    // Nadie en todo el microcontrolador nos puede interrumpir para usarlo.
    
    // Secuencia física Bare-Metal: START, Address, Data, STOP...
    I2C1->CR1 |= I2C_CR1_START;
    // ... (Transferencia física omitida) ...
    
    // --- FIN ZONA CRÍTICA ---
    
    // 2. DEVOLVER LA LLAVE (Vital)
    // Si olvidas esta línea, el bus I2C quedará bloqueado para siempre.
    // Al liberar el Mutex, si había un hilo de Alta Prioridad esperando en 
    // la línea 'osMutexAcquire', el RTOS nos aplicará un Context Switch y 
    // le dará la CPU a ese hilo inmediatamente.
    osMutexRelease(i2c_mutex_id);
    
    return true; // Transacción limpia y exitosa
}

// =======================================================
// LAS TAREAS DE APLICACIÓN (Despreocupadas)
// =======================================================

// Tarea 1: Guarda logs de transacciones
void Thread_EEPROM_Backup(void *argument) {
    uint8_t datos_ticket[32] = { /* ... */ };
    for(;;) {
        // Llama a la función protegida
        I2C_EscribirDatos(0x50, 0x00, datos_ticket, 32);
        osDelay(5000);
    }
}

// Tarea 2: Monitorea presión del gas
void Thread_Sensor_Presion(void *argument) {
    uint8_t buffer_rx[2];
    for(;;) {
        // También llama a la misma función protegida simultáneamente.
        // El Mutex interno se encarga del tráfico. ¡No hay colisiones!
        I2C_EscribirDatos(0x28, 0x10, buffer_rx, 2); // Comando de lectura
        osDelay(10);
    }
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Blindaje de Periféricos Críticos contra HardFaults.

Un Despachador de Gas LP moderno utiliza una pantalla OLED monocromática en la tarjeta principal de la electrónica (para uso exclusivo del técnico de mantenimiento) conectada al bus I2C. En ese mismo bus de dos hilos, tienes soldada la memoria EEPROM fiscal 24LC256 donde se guardan los cortes de caja de las ventas de gasolina/gas.

Si la `UI_Task` intenta dibujar un menú de configuración técnica y la `Network_Task` intenta descargar el último ticket vendido para enviarlo por WiFi a la nube al mismo exacto tiempo, chocarían a nivel de silicio. Los bits eléctricos del comando de pantalla y el comando de la memoria se mezclarían, enviando basura ilegible al bus I2C. En el mejor de los casos, la pantalla arroja estática; en el peor, borras la memoria fiscal. Al rediseñar todos tus controladores Bare-Metal (UART, SPI, I2C) inyectándoles candados **Mutex** al inicio y al final de cada transmisión, tu arquitectura de software es ahora "Thread-Safe" (Segura contra Hilos). Los periféricos físicos jamás fallarán por concurrencia.

📝 Resumen del Día