DÍA 147 DE 365

Fase 3: Comunicaciones y Redes Industriales

📖 Teoría a Estudiar

El Peligro del "Brick" (Ladrillo)

Escribir en la memoria Flash en pleno vuelo es la maniobra más peligrosa en sistemas embebidos. Si el voltaje del sistema cae por debajo de los 2.7V, la programación Flash puede corromper los bits adyacentes. Aún peor: si el procesador está escribiendo en el Sector 5 y de repente ocurre una Interrupción (ej. el SysTick del milisegundo), el CPU saltará a ejecutar código a otra parte mientras el hardware Flash está en estado inestable, provocando un HardFault que dejará la máquina inoperante (brickeada).

Desarme Global: __disable_irq()

En Bare-Metal IAP (In-Application Programming), antes de inyectar una ráfaga de datos en la Flash, el programador debe apagar absolutamente todo. Utilizamos la instrucción ensambladora __disable_irq(), conocida como Deshabilitador Global de Interrupciones. El CPU se vuelve sordo al mundo físico: los UARTs, los Timers y el teclado quedan congelados hasta que terminemos de quemar el silicio.

Arquitectura de Doble Banco (OTA Slotting)

Un archivo de firmware pesa alrededor de 30 KB. En nuestra arquitectura, dividiremos la Flash asimétricamente:

ARQUITECTURA DE ACTUALIZACIÓN IAP (IN-APPLICATION PROGRAMMING) RS485 Bus Chunk 1 (1KB) Chunk 2 (1KB) . . . Chunk N (1KB) Memoria RAM Búfer RX DMA CPU Ejecutando IAP __disable_irq(); escribir_flash(); __enable_irq(); Flash Memory (IAP) Sec 0: BOOTLOADER Sec 1-3: APP ACTUAL (Ejecutándose Ahora) Sec 4: DOWNLOAD SLOT Quemando nuevo Firmware aquí... Al terminar, Reboot. Bootloader mueve Sec 4 a Sec 1.

⚙️ Ejercicios Prácticos

Ejercicio 1: Construiremos la función de quemado seguro (In-Application Programmer). Tomará un bloque de datos que acabamos de recibir por Modbus (ej. un arreglo de uint8_t en RAM), desactivará las interrupciones del procesador, agrupará los bytes de 4 en 4 (Little-Endian) para formar Palabras (Words) de 32 bits, y los inyectará en la Flash sin colapsar el sistema.


/**
 * @file flash_iap.c
 * @brief Rutinas In-Application Programming (IAP) seguras con bloqueo de IRQ
 */

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

// Direcciones del Mapa de Memoria del STM32F4
#define FLASH_SECTOR_4_ADDR 0x08010000 // Zona segura de descarga (Download Slot)

// Dependencias de funciones Base de Flash (Día 146)
extern void flash_desbloquear(void);
extern void flash_bloquear(void);
extern void flash_borrar_sector(uint8_t sector);

/**
 * @brief Recibe un búfer de datos (ej. desde USART) y lo quema en la Flash
 * @param dir_destino Dirección física inicial en Flash (ej. 0x08010000)
 * @param buffer_datos Puntero al arreglo en memoria RAM (Payload)
 * @param longitud_bytes Cantidad de bytes a escribir
 * @return bool True si la verificación fue exitosa.
 */
bool IAP_Grabar_Bloque_OTA(uint32_t dir_destino, uint8_t *buffer_datos, uint16_t longitud_bytes) {
    
    // 1. MEDIDA DE SEGURIDAD EXTREMA: Deshabilitar Interrupciones.
    // Si el SysTick, el UART o el teclado interrumpen al CPU mientras
    // inyecta transistores Flash, el chip generará un HardFault.
    __disable_irq(); // Instrucción CMSIS para apagar el NVIC
    
    flash_desbloquear();
    
    // Configurar la Flash para escribir en tamaño "Word" (32 bits = 4 bytes).
    // Requiere que el voltaje PVD sea mínimo de 2.7V.
    FLASH->CR &= ~FLASH_CR_PSIZE;
    FLASH->CR |= (2 << FLASH_CR_PSIZE_Pos); // PSIZE = 10 (x32)
    
    FLASH->CR |= FLASH_CR_PG; // Encender el Programador
    
    // 2. EMPAQUETADO LITTLE-ENDIAN Y ESCRITURA
    // Recorremos el buffer de 4 en 4 bytes
    for (uint16_t i = 0; i < longitud_bytes; i += 4) {
        
        // El ARM Cortex-M es Little-Endian. Ensamblamos la palabra de 32 bits
        // colocando el primer byte en la parte más baja (LSB).
        uint32_t palabra_32 = 0;
        palabra_32 |= (uint32_t)buffer_datos[i];
        
        if ((i + 1) < longitud_bytes) palabra_32 |= ((uint32_t)buffer_datos[i + 1] << 8);
        if ((i + 2) < longitud_bytes) palabra_32 |= ((uint32_t)buffer_datos[i + 2] << 16);
        if ((i + 3) < longitud_bytes) palabra_32 |= ((uint32_t)buffer_datos[i + 3] << 24);
        
        // Escribimos atómicamente en la Flash (El hardware paraliza el CPU hasta acabar)
        *(__IO uint32_t*)(dir_destino + i) = palabra_32;
        
        // Esperamos confirmación del hardware Flash
        while (FLASH->SR & FLASH_SR_BSY);
        
        // Revisamos si ocurrió un error eléctrico durante la grabación
        if (FLASH->SR & (FLASH_SR_PGSERR | FLASH_SR_PGPERR | FLASH_SR_PGAERR)) {
            FLASH->CR &= ~FLASH_CR_PG;
            flash_bloquear();
            __enable_irq(); // Restaurar el sistema antes de retornar error
            return false;
        }
    }
    
    FLASH->CR &= ~FLASH_CR_PG; // Apagar programador
    flash_bloquear();
    
    // 3. RESTAURAR EL SISTEMA A LA NORMALIDAD
    __enable_irq();
    
    // 4. VERIFICACIÓN (READ-BACK)
    // El software debe leer la Flash a velocidad normal y comparar con la RAM
    // para certificar que el firmware nuevo no está corrupto.
    for (uint16_t i = 0; i < longitud_bytes; i++) {
        uint8_t byte_grabado = *(__IO uint8_t*)(dir_destino + i);
        if (byte_grabado != buffer_datos[i]) {
            return false; // Verificación fallida (Bit Flip)
        }
    }
    
    return true; // Éxito total.
}

int main(void) {
    
    // Supongamos que recibimos un comando especial del Servidor
    // if (comando_iniciar_ota) {
    //      flash_desbloquear();
    //      flash_borrar_sector(4); // Limpiamos el Sector 4 para prepararlo
    //      flash_bloquear();
    // }
    
    while(1) {
        
        // Si el DMA descargó un bloque binario (Chunk) de 1024 bytes desde el RS485...
        // if (bandera_chunk_ota_recibido) {
        //     
        //     // Calculamos dónde le toca escribirse
        //     uint32_t offset = chunk_actual * 1024;
        //     uint32_t direccion_quemado = FLASH_SECTOR_4_ADDR + offset;
        //
        //     bool ok = IAP_Grabar_Bloque_OTA(direccion_quemado, rx_buffer_dma, 1024);
        //     
        //     if (ok) {
        //         enviar_modbus_ack();
        //     } else {
        //         enviar_modbus_error("Error IAP de Silicon");
        //     }
        // }
    }
    
    return 0;
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Soporte para Firmware Over-The-Air (OTA).

El código metrológico que escribiste en el Día 115 funciona bien hoy, pero en el año 2028, la autoridad fiscal podría cambiar las reglas de redondeo de precios. Si tienes 5,000 Despachadores instalados a lo largo del país, no puedes enviar ingenieros a destapar cada máquina. Las gaseras exigen equipos actualizables remotamente.

Al implementar esta rutina **IAP Segura**, has convertido tu Despachador en un equipo del Siglo XXI. El Administrador Central sube un archivo `.bin` al software SCADA. El servidor envía los bytes fragmentados a través del cable RS485. Tu STM32 intercepta los bloques con el DMA, apaga sus interrupciones brevemente (aislándose del mundo), quema los transistores del Sector 4 (Download Slot) y se vuelve a encender para pedir el siguiente bloque. Todo de forma transparente. Una vez descargado todo el firmware, la máquina hará un reinicio controlado, tu futuro Bootloader detectará el archivo nuevo y el Despachador despertará con su nueva personalidad algorítmica.

📝 Resumen del Día