En el Día 37 construimos una función para esperar el ACK del bus I2C usando un bucle while (!(I2C_SR & FLAG));. Si el chip EEPROM se desuelda de la placa base, el bit `FLAG` jamás cambiará a 1. El procesador entrará en un ciclo infinito conocido como Deadlock, ignorando las presiones del ADC y las interrupciones críticas.
Un Timeout en software puro (sin usar timers de hardware dedicados) se implementa con una variable numérica robusta (generalmente uint32_t). En cada iteración del bucle while, restamos uno a esta variable. El bucle ahora tiene dos condiciones de salida: salir porque el hardware respondió (Éxito), o salir porque el contador llegó a cero (Falla de Hardware).
En C bare-metal, el tiempo de expiración se calcula con base en la frecuencia de tu CPU. Si tu núcleo ARM opera a 48 MHz, un ciclo de reloj dura aprox. 20.8 ns. Si un bucle while vacío toma aproximadamente 4 ciclos de instrucción en compilarse, podemos calcular el valor de inicio del contador $N$ usando: $N = \frac{T_{max}}{t_{loop}}$, donde $t_{loop}$ es el tiempo que toma una iteración.
Ejercicio 1: Reescribe una rutina crítica de hardware (como esperar la respuesta de transmisión de un bit de I2C) utilizando un contador de escape. Aplicaremos un código de retorno estándar para avisarle a la Matriz de Estados que hubo una falla grave.
#include <stdint.h>
#include <stdbool.h>
// --- 1. DEFINICIONES DE HARDWARE Y CÓDIGOS DE ERROR ---
// Mapeo simulado del registro de estado I2C (Recordando el Día 37)
#define I2C_SR_REG (*((volatile uint8_t *)(0x40066001)))
#define I2C_FLAG_TXE (1 << 7) // Transmit buffer empty
#define ESTADO_OK 0x00
#define ERROR_I2C_TIMEOUT 0x01
// Definimos un límite empírico. Si el MCU corre a 48 MHz,
// 50,000 ciclos de bucle otorgan aproximadamente ~4 milisegundos.
// Tiempo de sobra para un periférico I2C a 100 kHz.
#define TIMEOUT_LIMITE 50000
// --- 2. FUNCIÓN CON BLOQUEO DE ESCAPE (FAIL-SAFE YIELD) ---
uint8_t I2C_Esperar_Transmision(void) {
// Inicializamos el contador de escape con su valor máximo
uint32_t contador_escape = TIMEOUT_LIMITE;
// El bucle evalúa dos cosas simultáneamente:
// 1. Que la bandera aún NO esté lista.
// 2. Que aún nos quede tiempo en el contador.
while ( !(I2C_SR_REG & I2C_FLAG_TXE) && (contador_escape > 0) ) {
// Reducimos el "tiempo de vida" del bucle en cada vuelta
contador_escape--;
}
// Al salir del bucle, debemos averiguar POR QUÉ salimos
if (contador_escape == 0) {
// Salimos porque el tiempo se agotó. ¡Falla de hardware!
// --- SECUENCIA FAIL-SAFE ---
// Forzamos la liberación del periférico para evitar colapso eléctrico.
// Aquí podrías apagar registros o reiniciar el I2C.
return ERROR_I2C_TIMEOUT;
}
// Salimos porque el hardware respondió rápido y el contador es > 0.
return ESTADO_OK;
}
// =====================================================================
// SÚPER BUCLE MAESTRO Y APLICACIÓN DE LA MATRIZ DE SEGURIDAD
// =====================================================================
extern void Valvula_Forzar_Cierre_Emergencia(void); // Del Día 33
void Guardar_Datos_EEPROM(void) {
// Intentamos transmitir y guardamos el código resultante
uint8_t resultado = I2C_Esperar_Transmision();
// Evaluación estricta de la función
if (resultado == ERROR_I2C_TIMEOUT) {
// ¡LA EEPROM NO RESPONDE!
// Quizás se cortó un cable por la vibración del despachador.
// Como estamos en un sistema crítico, la metrología es inválida.
Valvula_Forzar_Cierre_Emergencia();
// Mostrar "ERROR E05" en la pantalla LCD...
}
else {
// Continuar el despacho con normalidad...
}
}
Objetivo del día: Resiliencia Electromecánica en Ambientes Industriales.
Imagina que la máquina lleva 40 litros despachados y, por la vibración del motor de 220V, uno de los cables de la memoria EEPROM (donde guardas los litros cobrados) se desprende. Si no tuvieras este código, la función se quedaría esperando para siempre un "1" lógico del chip que jamás llegará. La CPU se congelaría y el gas seguiría saliendo por la manguera sin registro y sin control. Al implementar un Timeout local, el while revienta a los 4 milisegundos, aborta la escritura, retorna un código de error rojo a tu flujo principal y activa la subrutina de paro de emergencia, cerrando la válvula al instante. Has salvado la integridad física de la planta.
while que evalúe únicamente el registro de un periférico externo es una bomba de tiempo en sistemas industriales.0x01) para que la jerarquía superior ordene el Failsafe mecánico (desenergizar actuadores).