DÍA 260 DE 365

Fase 5: Diseño Industrial y Proyecto Final

📖 Teoría a Estudiar

IAP (In-Application Programming) vs ICP (In-Circuit Programming)

Cuando usas un cable ST-Link conectándolo a los pines SWDIO y SWCLK, estás haciendo ICP. El procesador es un esclavo apagado y el ST-Link manipula sus registros desde afuera. Pero cuando el procesador está encendido, lee un archivo y decide reescribirse a sí mismo, eso es IAP.

Para lograr IAP, el STM32 debe ejecutar código desde el Sector 0 (Bootloader) para poder borrar y escribir en los Sectores 4 y 5 (Aplicación). No puedes borrar el sector desde el cual estás ejecutando el código; es como serrar la rama del árbol sobre la que estás sentado.

La Física de la Memoria Flash

Escribir en la memoria Flash no es como escribir en la RAM. Tiene reglas de hardware estrictas:

  1. El Candado (Lock): Por seguridad contra ruidos eléctricos y code runaways, el controlador de Flash está bloqueado. Debes enviarle dos "Llaves Mágicas" consecutivas (`0x45670123` y `0xCDEF89AB`) para desbloquearlo.
  2. Borrado por Sectores: No puedes borrar un solo byte. Debes borrar el sector completo (ej. 64 KB enteros). Borrar un sector cambia todos los bits a 1 (hexadecimal 0xFF). Esta operación puede tardar hasta 2 segundos.
  3. Escritura por Palabras (Word): Solo puedes escribir sobre bits que estén en 1 (borrados) para cambiarlos a 0. El STM32F401 requiere que escribas en bloques alineados (Bytes, Half-Words o Words). Para 3.3V (Voltaje nominal), usamos paralelismo de 32 bits (Word).
FLUJO IAP (IN-APPLICATION PROGRAMMING): SD A FLASH INTERNA SD Card FAT32 (SPI) update.bin Tamaño: 90 KB FatFs f_read() RAM (STM32) Buffer: 512 Bytes Lectura bloque a bloque (No cabe todo en RAM) HAL_FLASH_ Program() FLASH INTERNA (0x08010000) 1. FLASH_Erase_Sector(4) Pone todo en 0xFFFFFFFF (Tarda 1 seg) Bloque 0 (512B) Bloque 1 (512B) Bloque 2 (512B) ... Escribiendo 0xFFFFFFFF 2. Verificación CRC32 HAL_FLASH_Lock()

⚙️ Ejercicios Prácticos

El Circuito SD (SPI): La tarjeta micro SD se conectará al STM32 mediante un bus SPI secundario (ej. SPI2). Necesitas 4 pines: CS, SCK, MISO, MOSI. Todas las señales lógicas son de 3.3V (¡No le metas 5V a la SD o se freirá!). Es crítico colocar resistencias Pull-Up de 10kΩ en las líneas de datos para estabilizar el bus durante el arranque.

Ejercicio 1: El Quemador de Silicio (Flash Driver). Escribiremos la función en C que toma un arreglo de RAM y lo inyecta a la fuerza en la memoria Flash no volátil, cumpliendo con la secuencia de desbloqueo, borrado y escritura exigida por STMicroelectronics.


/**
 * @file iap_flash_driver.c
 * @brief Driver In-Application Programming para escritura de Flash (MISRA-C)
 */

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

// =========================================================================
// DEFINICIONES DEL TARGET
// =========================================================================
// Empezaremos a borrar/escribir desde el Sector 4
#define APP_BASE_ADDRESS      (0x08010000U)
#define APP_SECTOR_START      FLASH_SECTOR_4

// =========================================================================
// BORRADO DEL SECTOR (PREPARACIÓN DEL LIENZO)
// =========================================================================
/**
 * @brief Borra la memoria Flash asignada a la aplicación principal.
 *        ADVERTENCIA: Destruye el código de la App actual. Bloquea el CPU.
 * @retval bool: TRUE si el borrado fue exitoso.
 */
bool IAP_Borrar_Slot_Aplicacion(void) {
    
    FLASH_EraseInitTypeDef EraseInitStruct;
    uint32_t SectorError = 0U;
    bool status = true;

    // 1. Desbloquear el acceso al hardware Flash
    HAL_FLASH_Unlock();
    
    // 2. Limpiar banderas de errores pasados
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | 
                           FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);

    // 3. Configurar la estructura de Borrado
    EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
    EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3; // 2.7V a 3.6V
    EraseInitStruct.Sector = APP_SECTOR_START;
    
    // En nuestro mapa, la App ocupa el Sector 4 (64KB) y Sector 5 (128KB)
    EraseInitStruct.NbSectors = 2U; 

    // 4. Ejecutar la aniquilación (Puede tardar hasta 2 segundos)
    // El Watchdog debe alimentarse antes de llamar esto, o el chip se reiniciará.
    if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) {
        status = false; // El borrado falló (Flash desgastada o caída de voltaje)
    }

    // 5. Volver a bloquear el cerrojo por seguridad temporal
    HAL_FLASH_Lock();
    
    return status;
}

// =========================================================================
// ESCRITURA BLOQUE A BLOQUE (WORD x WORD)
// =========================================================================
/**
 * @brief Escribe un bloque de datos (descargado de la SD) en la Flash.
 * @param start_address: Dirección de Flash destino
 * @param p_data: Puntero al buffer en RAM (ej. array de 512 bytes)
 * @param length_bytes: Cantidad de bytes a escribir (debe ser múltiplo de 4)
 * @retval bool: TRUE si la escritura y validación tuvieron éxito.
 */
bool IAP_Escribir_Bloque(uint32_t start_address, const uint8_t* p_data, uint32_t length_bytes) {
    
    bool status = true;
    uint32_t current_addr = start_address;
    uint32_t words_to_write = length_bytes / 4U; // Escribimos en trozos de 32 bits (4 bytes)
    
    // Convertimos el puntero de 8 bits a 32 bits para facilitar la lectura
    // MISRA: Cast de punteros validado por diseño de hardware
    const uint32_t* p_data_32 = (const uint32_t*)p_data;
    
    HAL_FLASH_Unlock();
    
    for (uint32_t i = 0U; i < words_to_write; i++) {
        
        // Escribimos una "Palabra" (32 bits)
        if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, current_addr, p_data_32[i]) == HAL_OK) {
            
            // VERIFICACIÓN INMEDIATA FÍSICA (Read-Back)
            // Leemos el silicio directamente para ver si los electrones se atraparon
            if (*(__IO uint32_t*)current_addr != p_data_32[i]) {
                status = false;
                break; // Corrupción física (Death sector)
            }
            
            current_addr += 4U; // Avanzamos 4 bytes físicos
            
        } else {
            status = false; // Falló el comando de programación
            break;
        }
    }
    
    HAL_FLASH_Lock();
    return status;
}

// =========================================================================
// RUTINA DE ORQUESTACIÓN DEL BOOTLOADER (Pseudocódigo de flujo FATFS)
// =========================================================================
/*
void Bootloader_Update_From_SD(void) {
    if (FatFs_FileExists("update.bin")) {
        // 1. Borramos el lienzo
        IAP_Borrar_Slot_Aplicacion();
        
        // 2. Leemos la SD por trozos
        uint32_t flash_ptr = APP_BASE_ADDRESS;
        uint8_t buffer_sd[512];
        uint32_t bytes_leidos = 0;
        
        while (FatFs_Read("update.bin", buffer_sd, 512, &bytes_leidos) == OK && bytes_leidos > 0) {
            
            // Si el último bloque no es múltiplo de 4, lo rellenamos con 0xFF
            while (bytes_leidos % 4 != 0) {
                buffer_sd[bytes_leidos++] = 0xFF;
            }
            
            // 3. Quemamos el bloque en el procesador
            IAP_Escribir_Bloque(flash_ptr, buffer_sd, bytes_leidos);
            flash_ptr += bytes_leidos;
        }
        
        FatFs_Rename("update.bin", "update_done.bin");
        NVIC_SystemReset(); // Reinicio maestro para arrancar la nueva App
    }
}
*/
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Soporte de Mantenimiento Offline y Recuperación ante Bricking.

El diseño de un sistema industrial no termina cuando la máquina sale de la fábrica; comienza ahí. Si un Despachador instalado a 500 kilómetros de tu base pierde la conexión a internet de forma permanente (ej. quiebra el proveedor de SIMs M2M), tu estrategia Over-The-Air es inútil.

Al dotar a tu Bootloader con capacidades **IAP mediante Tarjeta SD**, has creado el protocolo de rescate definitivo ("Sneakernet OTA"). El operador de la gasolinera solo necesita recibir un archivo `.bin` por WhatsApp, pasarlo a una tarjeta micro SD con su celular, insertarla en la ranura interna del Despachador y reiniciar el equipo. El Bootloader detectará el archivo "update.bin", borrará el código de la aplicación de forma segura (verificando cada bloque escrito con Read-Back Verification) y reiniciará la máquina renovada. Has eliminado la necesidad de programadores especializados (ST-Link) en campo, reduciendo tus costos de soporte técnico (SLA) a cero. Este nivel de autonomía es el sello de un producto verdaderamente maduro.

📝 Resumen del Día