DÍA 141 DE 365

Fase 3: Comunicaciones y Redes Industriales

📖 Teoría a Estudiar

El Dominio de Respaldo (Backup Domain)

A diferencia de los periféricos normales (como los Timers o el USART) que mueren en el instante en que desconectas el suministro principal (VDD), el STM32 tiene un bunker interno aislado llamado Dominio de Respaldo. Este bloque de silicio se alimenta desde un pin dedicado llamado VBAT. Si le conectas una batería de reloj (CR2032 de 3V), el RTC interno y sus registros de respaldo (Backup Registers) seguirán funcionando durante años, incluso si la placa principal está apagada.

Para proteger este bunker de escrituras accidentales durante un apagón o fluctuaciones de voltaje (brown-outs), el hardware obliga al programador a ingresar múltiples "llaves" lógicas antes de poder cambiar la hora. Esto previene que un CPU errático sobreescriba la fecha fiscal por error.

La Matemática del Cristal LSE (32.768 kHz)

El RTC interno utiliza un oscilador de ultra-bajo consumo llamado LSE (Low-Speed External). Para que funcione, debes soldar un diminuto cristal cilíndrico a los pines físicos PC14 y PC15.

¿Por qué la industria electrónica eligió exactamente 32,768 Hz? Porque $32768 = 2^{15}$. Utilizando un par de divisores (Prescalers) basados en compuertas lógicas simples, el procesador puede dividir la frecuencia exactamente a $1\text{ Hz}$ absoluto (1 segundo perfecto) sin usar decimales ni procesadores flotantes.

La fórmula oficial de frecuencia del STM32 es:

$$ f_{RTC} = \frac{f_{LSE}}{(PREDIV\_A + 1) \times (PREDIV\_S + 1)} $$

ARQUITECTURA DE ALTO AISLAMIENTO: RTC INTERNO Y DOMINIO VBAT Dominio Principal (VDD) CPU Cortex-M APB1 Bus / Relojes +3.3V (VDD) FIREWALL Unlock: DBP=1 Key: CA, 53 Backup Domain (VBAT Island) 3V Pin VBAT 32.768 kHz PC14 PC15 Async Div PREDIV_A=127 256 Hz Sync Div PREDIV_S=255 1 Hz Shadow Registers TR (Hora BCD) DR (Fecha BCD) El tiempo sobrevive aunque el CPU Cortex-M muera

⚙️ Ejercicios Prácticos

Ejercicio 1: Habilitaremos el acceso al Dominio de Respaldo a través del bloque de Potencia (PWR). Encenderemos el cristal LSE, esperaremos a que se estabilice (LSERDY), e inyectaremos la secuencia mágica en el registro de protección de escritura WPR del RTC para configurar la hora inicial usando formato BCD.


/**
 * @file rtc_interno.c
 * @brief Driver Bare-Metal para el RTC Interno (Dominio de Respaldo y LSE)
 */

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

// Macros BCD idénticas a las utilizadas para el I2C (Día 124)
#define DEC_A_BCD(val)  (((val / 10) << 4) | (val % 10))
#define BCD_A_DEC(val)  (((val >> 4) * 10) + (val & 0x0F))

void inicializar_rtc_lse(void) {
    
    // 1. ACTIVAR RELOJES DEL SISTEMA BASE
    RCC->APB1ENR |= RCC_APB1ENR_PWREN; // Activar el bloque de Poder
    
    // 2. ROMPER EL FIREWALL DEL DOMINIO DE RESPALDO
    // El bit DBP (Disable Backup Domain Write Protection) debe ser '1'
    PWR->CR |= PWR_CR_DBP; 
    
    // 3. ENCENDER EL OSCILADOR DE CUARZO LSE (32.768 kHz)
    // El registro BDCR (Backup Domain Control Register) sobrevive al apagado del CPU
    RCC->BDCR |= RCC_BDCR_LSEON;
    
    // Esperar pasivamente hasta que el hardware confirme oscilación estable
    while(!(RCC->BDCR & RCC_BDCR_LSERDY));
    
    // 4. SELECCIONAR EL RELOJ Y ENCENDER EL RTC
    // Máscara: Limpiar bits primero, luego seleccionar LSE (01)
    RCC->BDCR &= ~RCC_BDCR_RTCSEL; 
    RCC->BDCR |= RCC_BDCR_RTCSEL_0; 
    
    RCC->BDCR |= RCC_BDCR_RTCEN; // RTC Clock Enable
    
    // ==========================================================
    // 5. SECUENCIA CRIPTOGRÁFICA DE DESBLOQUEO DEL RTC
    // ==========================================================
    // Escribimos las llaves maestras en el Write Protection Register
    RTC->WPR = 0xCA;
    RTC->WPR = 0x53;
    
    // 6. ENTRAR EN MODO INICIALIZACIÓN
    // Le pedimos al RTC que detenga temporalmente la cuenta para configurar
    RTC->ISR |= RTC_ISR_INIT;
    while(!(RTC->ISR & RTC_ISR_INITF)); // Esperar la bandera "Init Flag"
    
    // 7. CONFIGURAR LOS PRESCALERS MATEMÁTICOS PARA LLEGAR A 1 HZ
    // Usamos el registro PRER. 
    // Async Prescaler (Bits 16-22): 127
    // Sync Prescaler (Bits 0-14): 255
    // Fórmula: 32768 / ((127+1) * (255+1)) = 1 Hz exacto.
    RTC->PRER = (127 << 16) | (255);
    
    // 8. FIJAR LA HORA INICIAL (Ejemplo: 14:30:00) EN BCD
    // Bit 22 determina Formato de 24h (AM/PM). Lo dejamos en 0.
    uint32_t hora   = DEC_A_BCD(14);
    uint32_t minuto = DEC_A_BCD(30);
    uint32_t seg    = DEC_A_BCD(0);
    
    RTC->TR = (hora << 16) | (minuto << 8) | seg;
    
    // Fijar Fecha (Ejemplo: 10 de Septiembre del 2026, Jueves=4)
    uint32_t año = DEC_A_BCD(26);
    uint32_t mes = DEC_A_BCD(9);
    uint32_t dia = DEC_A_BCD(10);
    uint32_t sem = 4;
    
    RTC->DR = (año << 16) | (sem << 13) | (mes << 8) | dia;
    
    // 9. SALIR DEL MODO INICIALIZACIÓN Y SELLAR LAS PUERTAS
    RTC->ISR &= ~RTC_ISR_INIT;
    
    // Escribir un código falso bloquea la protección de nuevo
    RTC->WPR = 0xFF; 
}

/**
 * @brief Lee la hora actual (Shadow Registers) de forma segura
 */
void obtener_hora_actual(uint8_t* h, uint8_t* m, uint8_t* s) {
    
    // Al leer el registro TR (Time Register), el hardware congela mágicamente
    // el registro DR (Date Register) hasta que lo leas también.
    // Esto asegura que no leas "14:59:59" y en el siguiente ciclo leas "Ayer", 
    // previniendo la corrupción temporal.
    uint32_t tiempo_crudo = RTC->TR;
    uint32_t fecha_cruda = RTC->DR; // Lectura obligatoria para descongelar
    
    (void)fecha_cruda; // Ignorar el warning si no usamos la fecha
    
    // Extraer y convertir BCD a Decimal Humano
    *h = BCD_A_DEC((tiempo_crudo >> 16) & 0x3F);
    *m = BCD_A_DEC((tiempo_crudo >> 8)  & 0x7F);
    *s = BCD_A_DEC((tiempo_crudo >> 0)  & 0x7F);
}

int main(void) {
    
    // (Este bloque se ejecuta sólo si el Dominio de Respaldo se quedó sin batería.
    // En producción, primero revisarías el registro RTC->ISR bit INITS para ver 
    // si el año ya estaba configurado previamente).
    inicializar_rtc_lse();
    
    uint8_t hora, minuto, segundo;
    
    while(1) {
        
        obtener_hora_actual(&hora, &minuto, &segundo);
        
        // El reloj corre impulsado por el cristal LSE sin consumir CPU.
        // Si el microcontrolador se apaga por 3 días, al volver a encenderlo,
        // la función "obtener_hora_actual" seguirá dándote el segundo exacto.
        
    }
    
    return 0;
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Sello de Tiempo Incorruptible y Anti-Tampering.

El fraude metrológico es implacable. En el Día 124 añadimos un módulo I2C externo (DS3231). Pero los cables I2C son accesibles al operador de la máquina. Si un vándalo abre el chasis del Despachador y arranca físicamente los cables `SDA` y `SCL` del RTC, y no cuentas con un plan de respaldo, tu sistema perdería la marca de tiempo (Timestamp) de todas las ventas fiscales.

Al dominar el RTC Interno del STM32, has creado un escudo impenetrable. El reloj está incrustado microscópicamente en la misma pastilla de silicio que el procesador ARM Cortex-M. No hay cables externos que se puedan cortar. La pequeña batería tipo botón soldada a la placa mantendrá el reloj latiendo aunque desconecten el sistema entero de la red eléctrica. Si el despachador de gas es manipulado, tu firmware registrará la hora exacta de la fechoría. Y lo mejor de todo: reduces los costos de ensamblaje (BOM) eliminando la necesidad de chips externos costosos.

📝 Resumen del Día