DÍA 167 DE 365

Fase 3: Comunicaciones y Redes Industriales

📖 Teoría a Estudiar

El Principio de Falla Segura (Fail-Safe)

Un sistema de grado industrial prefiere estar apagado, bloqueado y pitando alarmas, antes que operar con la más mínima incertidumbre. Este es el principio Fail-Safe.

El POST (Power-On Self-Test) es una subrutina bloqueante que se ejecuta inmediatamente después de que el procesador inicializa sus relojes (PLL) y el Power Sequence (Día 155), pero **antes** de habilitar las interrupciones generales y el Súper Bucle. Es una barrera de peaje. Si un solo componente físico no responde al llamado (Ping), el Súper Bucle jamás se ejecuta.

Pinging Hardware en Bare-Metal

A diferencia del software de alto nivel, en Bare-Metal podemos tocar los transistores directamente para diagnosticar daños físicos:

POWER-ON SELF-TEST (POST) BARE-METAL FLOWCHART APP ENTRY 1. SYSTEM_POST_RUN() I2C Ping (0x50) ¿ACK Recibido? Sí ADC Barreras ATEX ¿0.5V < V < 3.0V? Sí ENTRAR SÚPER BUCLE No (I2C Muerto) No (Corto ATEX) POST_HALT() Apagar todo. Error Modbus. Reglas del POST: 1. Ejecución Secuencial Estricta. 2. Sin uso de DMA ni NVIC. 3. Polling con Timeouts cortos. 4. Si falla, el equipo es un ladrillo hasta ser reparado físicamente.

⚙️ Ejercicios Prácticos

Ejercicio 1: Construiremos nuestro módulo post_system.c. Programaremos una función bloqueante de comprobación I2C (I2C_Ping) para asegurarnos de que la memoria EEPROM fiscal está viva y conectada. Luego, revisaremos el ADC que programamos en el Día 162 para validar las barreras de protección ATEX. Si cualquier prueba falla, llamaremos a una función de "Paro de POST" que encenderá un LED de estado en rojo fijo y transmitirá la falla en la red Modbus de forma repetitiva para que la computadora central lo registre.


/**
 * @file post_system.c
 * @brief Subrutinas de Power-On Self-Test para validación de Hardware
 */

#include "stm32f401xe.h"
#include <stdbool.h>
#include <stdint.h>

// Dependencias
extern void inicializar_i2c1(void);
extern void inicializar_monitor_atex(void);
extern uint16_t adc_leer_canal_sencillo(uint8_t canal);
extern void Bloqueo_Legal_Fatal(uint32_t codigo_error); // Día 158

// Códigos de Error POST
#define ERR_POST_I2C_EEPROM    0xE001
#define ERR_POST_ATEX_BARRERA  0xE002
#define ERR_POST_FLASH_CRC     0xE003

// Dirección de la EEPROM de nuestra base de datos (Ej. 24LC256)
#define EEPROM_I2C_ADDR 0x50

// =========================================================================
// 1. HERRAMIENTAS DE DIAGNÓSTICO (PINGS FÍSICOS)
// =========================================================================

/**
 * @brief Envía la dirección de un esclavo al bus I2C y espera un ACK.
 *        Funciona como un "Ping" de red, pero a nivel de silicio.
 * @return true si el chip contestó, false si no hay nadie o hay corto.
 */
bool POST_Ping_I2C_Device(uint8_t device_address) {
    
    uint32_t timeout = 10000; // Timeout por ciclos de reloj para evitar congelamiento
    
    // 1. Generar condición de START en el bus I2C
    I2C1->CR1 |= I2C_CR1_START;
    while (!(I2C1->SR1 & I2C_SR1_SB)) {
        if (--timeout == 0) return false; // Bus atascado (SDA pegado a GND)
    }
    
    // 2. Enviar dirección del chip con el bit de Write (0)
    I2C1->DR = (device_address << 1);
    
    // 3. Esperar confirmación (ADDR) o fallo de reconocimiento (AF)
    timeout = 10000;
    while (!(I2C1->SR1 & I2C_SR1_ADDR) && !(I2C1->SR1 & I2C_SR1_AF)) {
        if (--timeout == 0) return false;
    }
    
    // 4. Analizar el resultado
    bool chip_vivo = false;
    if (I2C1->SR1 & I2C_SR1_ADDR) {
        // Limpiar bandera ADDR leyendo SR1 y SR2
        (void)I2C1->SR1;
        (void)I2C1->SR2;
        chip_vivo = true;
    } else if (I2C1->SR1 & I2C_SR1_AF) {
        // Acknowledge Failure: El chip no está o se quemó
        I2C1->SR1 &= ~I2C_SR1_AF; // Limpiar bandera
    }
    
    // 5. Condición de STOP para liberar el bus
    I2C1->CR1 |= I2C_CR1_STOP;
    
    return chip_vivo;
}

// =========================================================================
// 2. EJECUTOR PRINCIPAL DEL POST
// =========================================================================

/**
 * @brief Corre la secuencia entera de pruebas de hardware de arranque.
 *        Si algo falla, no retorna. Secuestra la máquina.
 */
void System_POST_Run(void) {
    
    // Las inicializaciones base ya deben haberse hecho (Relojes, Power Sequence)
    
    // --- TEST 1: COMUNICACIONES INTERNAS (I2C) ---
    // Si la EEPROM fiscal está muerta, no podemos guardar ventas ni leer el 
    // totalizador. Es un componente crítico intransigible.
    if (!POST_Ping_I2C_Device(EEPROM_I2C_ADDR)) {
        Bloqueo_Legal_Fatal(ERR_POST_I2C_EEPROM);
        while(1); // Muerte (Halt)
    }
    
    // --- TEST 2: SEGURIDAD INTRÍNSECA (ATEX) ---
    // Leemos la barrera del sensor de flujo (Día 162). 
    // Un valor normal debe rondar los 3.0V (ej. 3700 en ADC de 12 bits)
    uint16_t voltaje_barrera = adc_leer_canal_sencillo(0);
    
    // Si hay menos de 0.5V, el fusible se quemó.
    // Si hay exactamente 4095 sostenido, podría haber un corto al riel de 3.3V.
    if (voltaje_barrera < 620 || voltaje_barrera == 4095) {
        Bloqueo_Legal_Fatal(ERR_POST_ATEX_BARRERA);
        while(1); 
    }
    
    // --- TEST 3: INTEGRIDAD DE LA BASE DE DATOS (TMR) ---
    // Verificar que las variables TMR críticas no se hayan corrompido
    // mientras la máquina estuvo apagada por un fallo de batería de respaldo (RTC).
    // TMR_Leer(&precio_oficial); 
    
    // Si llegamos hasta aquí, el corazón de la máquina late perfecto.
    // Retornamos silenciosamente y dejamos que la Aplicación respire.
}

// =========================================================================
// INTEGRACIÓN EN LA APLICACIÓN PRINCIPAL
// =========================================================================

int main(void) {
    
    // 1. SECUENCIA FÍSICA INICIAL (Día 155)
    // Inicializar relojes, activar Power Sequence escalonado.
    // Hardware_Init_Basic();
    
    // 2. EXAMEN MÉDICO DEL HARDWARE (POST)
    System_POST_Run();
    
    // 3. SI EL POST PASÓ: Inicializamos el Súper Bucle
    // Configurar interrupciones globales, DMA, Watchdogs y Timers.
    // ...
    
    while(1) {
        // Ejecución Industrial Normal
        // ...
    }
    
    return 0;
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Validación de Arranque Tolerante a Fallos y Autodiagnóstico.

Los ingenieros de software a menudo olvidan que el hardware envejece y se desgasta. Una soldadura fría de fábrica en el chip de la EEPROM podría aguantar dos años, y de pronto agrietarse por la vibración de la calle. Si tu Despachador enciende después de un apagón y asume que el chip de memoria está bien, realizará ventas todo el día, y cuando intente guardar el corte de caja en la noche, fallará, perdiendo miles de pesos en información sin reportada.

Al introducir el **Sistema POST (Power-On Self-Test)**, has cerrado la puerta a la incertidumbre. En la fracción de segundo en que el despachador enciende su pantalla, el STM32 se comunica como un cirujano con cada periférico de tu placa PCB. Verifica eléctricamente si la memoria, las barreras físicas y los conversores están vivos. Si el chip EEPROM no tira de la línea I2C (ACK), el POST intercepta la ejecución de la máquina, bloquea el Súper Bucle, y envía la orden `0xE001` (Falla EEPROM) a la pantalla LCD y al servidor SCADA. Has transformado un error catastrófico y silencioso de pérdida de datos, en un diagnóstico predictivo e inmediato para el equipo de mantenimiento.

📝 Resumen del Día