DÍA 166 DE 365

Fase 3: Comunicaciones y Redes Industriales

📖 Teoría a Estudiar

El Entorno Seguro (Safe Harbor)

El Bootloader es la única pieza de software de tu máquina que jamás se actualiza. Es inmutable. Por lo tanto, si programas un Menú Diagnóstico dentro del Bootloader, garantizas que sin importar qué tan corrompida esté la memoria Flash de la Aplicación, siempre tendrás un entorno seguro al cual regresar. Es tu bote salvavidas.

El Concepto de Polling en Modo Rescate

Cuando estamos en Modo Rescate (Bootloader), debemos desconfiar de todo. No usamos Interrupciones (NVIC), ni usamos DMA, ni dependemos de Timers complejos. Si esos periféricos están dañados por hardware, el rescate fallará. El Menú Diagnóstico se programa usando Polling Bloqueante (while loops) puro y crudo. Enviamos bytes por el UART y esperamos a que el usuario presione una tecla en su computadora de forma manual y sincrónica.

Para activar el menú, utilizamos un "Pin de Servicio" oculto en el PCB (ej. un Jumper en la placa base). El Bootloader lee ese pin en el milisegundo 1 del arranque. Si está conectado a tierra (GND), sabe que un técnico está frente a la máquina con su laptop conectada, e interrumpe el salto a la aplicación.

FLUJO DE ARRANQUE: BYPASS DE HARDWARE Y MENÚ CLI POWER ON Bootloader (Sec 0) ¿Pin Jumper PA0 == GND? NO (Normal) Jump_To_App() Ejecuta Súper Bucle SÍ (Abortar Salto) Command Line Interface >> SYS RECOVERY V1.0 1. Arrancar App 2. Borrar Flash App 3. Leer HardFault Log 4. Dump EEPROM BKP > _ Polling Blocked 💻 RS485 a USB PuTTY / TeraTerm

⚙️ Ejercicios Prácticos

Ejercicio 1: Modificaremos el bootloader_main.c que creamos en el Día 165. Añadiremos la inicialización cruda de un pin GPIO de entrada con resistencia Pull-Up interna (PA0). Si al encender, el técnico ha puenteado el pin PA0 a Tierra, abriremos un bucle infinito que inicializa el UART1 y lanza cadenas de texto, esperando que el técnico escriba números en su terminal serial para recuperar el control de la máquina.


/**
 * @file bootloader_main.c
 * @brief Gestor de Arranque con Menú Diagnóstico CLI (Recovery Mode)
 */

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

// Prototipo de salto (Del Día 165)
extern void Bootloader_JumpToApp(void);

// =========================================================================
// RUTINAS DE POLLING UART (Ligeras y Seguras para Bootloader)
// =========================================================================

void UART_Init_Simple(void) {
    // Activar reloj UART1 y GPIOA
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    
    // Configurar PA9 (TX) y PA10 (RX) en Modo Función Alternativa 7
    GPIOA->MODER |= (2 << (9*2)) | (2 << (10*2));
    GPIOA->AFR[1] |= (7 << 4) | (7 << 8);
    
    // Baudrate 9600 a 16MHz base
    USART1->BRR = 0x0683;
    USART1->CR1 |= USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
}

void UART_Print(const char *texto) {
    while (*texto) {
        while (!(USART1->SR & USART_SR_TXE)); // Esperar buffer vacío
        USART1->DR = *texto++;
    }
}

char UART_Read_Char_Blocking(void) {
    while (!(USART1->SR & USART_SR_RXNE)); // Bloquear hasta recibir
    return (char)USART1->DR;
}

// =========================================================================
// RUTINAS DE MENÚ DIAGNÓSTICO
// =========================================================================

void Menu_Modo_Recuperacion(void) {
    
    UART_Init_Simple();
    
    UART_Print("\r\n==================================\r\n");
    UART_Print("   RECOVERY MODE - BOOTLOADER V1.0  \r\n");
    UART_Print("==================================\r\n");
    
    while(1) {
        UART_Print("\r\n[1] Forzar Arranque (Jump To App)\r\n");
        UART_Print("[2] Formatear Aplicacion (Erase Sector 1-3)\r\n");
        UART_Print("[3] Leer Caja Negra (HardFault Logs)\r\n");
        UART_Print("[4] Leer Variables TMR (Precios Oficiales)\r\n");
        UART_Print("\r\n>> Esperando Comando: ");
        
        char comando = UART_Read_Char_Blocking();
        UART_Print(&comando); // Eco visual para el usuario
        UART_Print("\r\n");
        
        switch (comando) {
            case '1':
                UART_Print("Iniciando secuencia de salto...\r\n");
                Bootloader_JumpToApp();
                break;
                
            case '2':
                UART_Print("Borrando sectores... ESPERE.\r\n");
                // Flash_Unlock();
                // Flash_Erase_Sector(1);
                UART_Print("Aplicacion destruida exitosamente.\r\n");
                break;
                
            case '3':
                // Extraer logs de la bóveda (Día 150)
                // Usamos snprintf ligero o casteos matemáticos
                UART_Print("Ultimo PC Falla: 0x080045A2\r\n");
                break;
                
            default:
                UART_Print("Error: Comando desconocido.\r\n");
                break;
        }
    }
}

// =========================================================================
// SÚPER BUCLE DEL BOOTLOADER
// =========================================================================

int main(void) {
    
    // 1. CONFIGURACIÓN DEL PIN SECRETO (PA0)
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    // PA0 como Input (00)
    GPIOA->MODER &= ~(3 << 0);
    // Activar resistencia Pull-Up interna (01) en PA0
    GPIOA->PUPDR &= ~(3 << 0);
    GPIOA->PUPDR |=  (1 << 0); 
    
    // Breve pausa para estabilización eléctrica del GPIO
    for(volatile int i=0; i<500; i++);
    
    // 2. LECTURA DE BYPASS
    // Si el pin está en 0 (Conectado a GND mediante Jumper)
    if ((GPIOA->IDR & GPIO_IDR_ID0) == 0) {
        
        // ¡Se ha detectado presencia técnica! 
        // Abortar salto automático y abrir CLI de rescate.
        Menu_Modo_Recuperacion(); 
        
        // El programa se quedará en el menú para siempre.
    }
    
    // 3. ARRANQUE NORMAL
    // Si nadie está tocando el pin, asumimos operación industrial de campo.
    Bootloader_JumpToApp();
    
    while(1) {}
    
    return 0;
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Mantenibilidad Extrema (Disaster Recovery Plan).

Los algoritmos en Bare-Metal son rápidos, pero sin un sistema operativo de por medio, un error matemático en la Aplicación puede "colgar" el hardware por completo. Si subes por RS485 una versión de firmware donde configuraste mal el reloj PLL, la máquina ni siquiera logrará ejecutar la primera línea de código del RS485 para aceptar una corrección. Es la temida "Pérdida de Comunicación Permanente". En este escenario, las empresas cambian la placa entera (miles de dólares en pérdidas).

Al programar tu **Menú CLI Diagnóstico** incrustado en el Bootloader, le has dado a tu Despachador un "Modo Dios". Si la máquina muere, envías a un técnico. El técnico destapa el cabezal de la gasolinera, coloca un pequeño jumper (puente de plástico) en el pin "RECOVERY" de tu PCB, y reinicia la máquina. Automáticamente, el Bootloader aborta la catástrofe y abre una terminal de texto en la laptop del técnico. Desde ahí, puede teclear el número `2` para borrar el firmware corrupto e instalar uno funcional desde cero usando el protocolo XMODEM, todo en el sitio, sin cambiar componentes físicos y en menos de 5 minutos.

📝 Resumen del Día