Módulo RTC del STM32H750: Guía Técnica y Ejemplos de Aplicación

El módulo RTC (Reloj de Tiempo Real) es un periférico de baja potencia en el STM32H750 que proporciona funciones de calendario y reloj. Incluso cuando la alimentación principal se desconecta, el RTC sigue funcionando si está alimentado por VBAT, ofreciendo una medición precisa de año, mes, día, hora, minuto y segundo. Es un componente central para implementar funciones como registro de marcas de tiempo, despertar programado y alarmas en sistemas embebidos, utilizados en registradores de datos, medidores inteligentes y controladores industriales.

1. Características y Arquitectura del RTC

Característica Parámetro/Descripción Aplicación Típica
Fuente de reloj LSE (oscilador externo 32.768 kHz), LSI (RC interno ~32 kHz), HSE con divisor. LSE ofrece la mayor precisión (±20 ppm).
Función de calendario Año (00-99), mes, día, día de la semana, hora, minuto, segundo. Manejo automático de años bisiestos. Registro de datos con sello temporal.
Alarmas Alarm A y Alarm B, configurables por día, hora, minuto y segundo. Despertar el sistema para tareas programadas.
Temporizador de despertar Wakeup Timer de hasta 17 bits (precisión en segundos). Muestreo periódico en modo de baja potencia.
Marca de tiempo (Timestamp) Captura activada por un pin externo. Registro del momento exacto de un evento o fallo.
Detección de intrusión (TAMP) Pines TAMP1/2/3 para detección de manipulación. Seguridad en dispositivos anti-manipulación.
Registros de respaldo 32 registros de 32 bits. Almacenar configuración o estado durante cortes de energía.
Baja potencia Funciona en modos Stop/Standby. Corriente < 1 μA (con LSE). Dispositivos alimentados por batería.

Ventajas específicas del STM32H750VBT6:

  • Dominio de respaldo dual: El RTC y los registros de respaldo están en el dominio D3, que puede operar independientemente del suministro principal.
  • Marca de tiempo con subsegundos: El registro RTC_SUBR permite precisión de milisegundos.
  • Calendario con auto-calibración: El registro CALIBR permite una calibración de ±488 ppm.
  • Funciones de seguridad: Los eventos TAMP pueden activar la detección de frecuencia a través de RTC_TAMPCR.TAMPFREQ.

1.1 Principio de Operación

El reloj del RTC (f_RTC) se deriva de una fuente de reloj principal (f_clk) mediante dos divisores en preescala: PREDIV_A (asíncrono) y PREDIV_S (síncrono). La fórmula fundamental es:

f_RTC = f_clk / ((PREDIV_A + 1) × (PREDIV_S + 1))Para un LSE de 32.768 kHz, una configuración común es PREDIV_A = 255 y PREDIV_S = 127, resultando en una tasa exacta de 1 Hz (32768 / (256 × 128) = 1).

Representación BCD: La hora y la fecha se almacenan en formato BCD (Decimal Codificado en Binario). Por ejemplo, las 14:35:27 se representan como 0x143527 en el registro TR.

Subsegundos y marca de tiempo: El registro SSR (Sub-second Register) es un contador decreciente que se recarga al inicio de cada segundo. El valor en milisegundos se puede calcular como: ms = ((PREDIV_S + 1) - SSR) * (1000 / (PREDIV_S + 1)). Un flanco ascendente en el pin RTC_TS puede capturar automáticamente la hora y fecha actuales para un evento externo.

1.2 Configuración a Nivel de Registro (Pseudocódigo)

A continuación se muestra un ejemplo simplificado de la inicialización del RTC directamente manipulando sus registros.

// 1. Habilitar acceso al dominio de respaldo
PWR->CR1 |= PWR_CR1_DBP;
while (!(PWR->CR1 & PWR_CR1_DBP));

// 2. Seleccionar y estabilizar LSE
RCC->BDCR |= RCC_BDCR_LSEON;
while (!(RCC->BDCR & RCC_BDCR_LSERDY));
// Configurar fuente de reloj RTC como LSE
RCC->BDCR = (RCC->BDCR & ~RCC_BDCR_RTCSEL) | RCC_BDCR_RTCSEL_0;
RCC->BDCR |= RCC_BDCR_RTCEN;

// 3. Desbloquear y entrar en modo de inicialización
RTC->WPR = 0xCA;
RTC->WPR = 0x53;
RTC->ISR |= RTC_ISR_INIT;
while (!(RTC->ISR & RTC_ISR_INITF));

// 4. Establecer preescaladores para 1 Hz
RTC->PRER = (255 << 16) | 127; // PREDIV_A=255, PREDIV_S=127

// 5. Cargar hora y fecha iniciales (BCD)
RTC->TR = 0x120000; // 12:00:00
RTC->DR = 0x2401015; // Lunes 1 de Enero de 2024

// 6. Salir del modo de inicialización y bloquear
RTC->ISR &= ~RTC_ISR_INIT;
while (RTC->ISR & RTC_ISR_INIT);
RTC->WPR = 0xFF;

1.3 Uso Simplificado con la Biblioteca HAL

RTC_TTypeDef sHora = {0};
RTC_DateTypeDef sFecha = {0};

sHora.Hours   = 12;
sHora.Minutes = 0;
sHora.Seconds = 0;

sFecha.WeekDay = RTC_WEEKDAY_MONDAY;
sFecha.Month   = RTC_MONTH_JANUARY;
sFecha.Date    = 1;
sFecha.Year    = 24; // Asumido como 2024

HAL_RTC_SetTime(&hrtc, &sHora, RTC_FORMAT_BCD);
HAL_RTC_SetDate(&hrtc, &sFecha, RTC_FORMAT_BCD);

2. Ejemplo Completo de Aplicación

El siguiente código demuestra una inicialización robusta del RTC, incluyendo la detección automática de la fuente de reloj, el uso de registros de respaldo para guardar el estado y la configuración de alarmas e interrupciones de despertar.

#include "main.h"
#include <stdio.h>

RTC_HandleTypeDef hrtc;

// Tabla para cálculo del día de la semana
static const uint8_t tabla_dia_semana[12] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};

// Funciones auxiliares para registros de respaldo
void GuardarDatoBackup(uint32_t posicion, uint32_t valor) {
    HAL_PWR_EnableBkUpAccess();
    HAL_RTCEx_BKUPWrite(&hrtc, posicion, valor);
}

uint32_t LeerDatoBackup(uint32_t posicion) {
    return HAL_RTCEx_BKUPRead(&hrtc, posicion);
}

// Función principal de inicialización del RTC
uint8_t Configurar_RTC(void) {
    uint16_t marca_inicializacion = LeerDatoBackup(0);
    hrtc.Instance = RTC;
    hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
    hrtc.Init.AsynchPrediv = 0x7F; // 127
    hrtc.Init.SynchPrediv = 0xFF;  // 255

    if (HAL_RTC_Init(&hrtc) != HAL_OK) {
        return 1; // Error
    }

    // Si es la primera vez o un reset por manipulación, establecer valores por defecto
    if ((marca_inicializacion != 0xAAAA) && (marca_inicializacion != 0xAAAB)) {
        RTC_TTypeDef hora_defecto = {0};
        RTC_DateTypeDef fecha_defecto = {0};
        hora_defecto.Hours = 8;
        hora_defecto.Minutes = 30;
        hora_defecto.Seconds = 0;
        fecha_defecto.Year = 24;
        fecha_defecto.Month = RTC_MONTH_JANUARY;
        fecha_defecto.Date = 1;
        fecha_defecto.WeekDay = RTC_WEEKDAY_MONDAY;

        HAL_RTC_SetTime(&hrtc, &hora_defecto, RTC_FORMAT_BCD);
        HAL_RTC_SetDate(&hrtc, &fecha_defecto, RTC_FORMAT_BCD);
    }
    return 0;
}

// Callback de bajo nivel para configuración del reloj
void HAL_RTC_MspInit(RTC_HandleTypeDef* hrtc_ptr) {
    RCC_OscInitTypeDef osc_cfg = {0};
    RCC_PeriphCLKInitTypeDef periph_clk_cfg = {0};

    __HAL_RCC_RTC_CLK_ENABLE();
    HAL_PWR_EnableBkUpAccess();

    // Intentar con LSE, si falla usar LSI
    RCC->BDCR |= RCC_BDCR_LSEON;
    for (volatile uint32_t i = 0; i < 200000; i++) {
        if (RCC->BDCR & RCC_BDCR_LSERDY) break;
    }

    if (RCC->BDCR & RCC_BDCR_LSERDY) {
        osc_cfg.OscillatorType = RCC_OSCILLATORTYPE_LSE;
        osc_cfg.LSEState = RCC_LSE_ON;
        HAL_RCC_OscConfig(&osc_cfg);
        periph_clk_cfg.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
        GuardarDatoBackup(0, 0xAAAA); // Marca: LSE activo
    } else {
        osc_cfg.OscillatorType = RCC_OSCILLATORTYPE_LSI;
        osc_cfg.LSIState = RCC_LSI_ON;
        HAL_RCC_OscConfig(&osc_cfg);
        periph_clk_cfg.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
        GuardarDatoBackup(0, 0xAAAB); // Marca: LSI activo (menos preciso)
    }

    periph_clk_cfg.PeriphClockSelection = RCC_PERIPHCLK_RTC;
    HAL_RCCEx_PeriphCLKConfig(&periph_clk_cfg);
}

// Función para configurar alarma con interrupción
void ConfigurarAlarmaSemanal(uint8_t dia_semana, uint8_t hora, uint8_t min, uint8_t seg) {
    RTC_AlarmTypeDef sAlarm = {0};
    sAlarm.AlarmTime.Hours = hora;
    sAlarm.AlarmTime.Minutes = min;
    sAlarm.AlarmTime.Seconds = seg;
    sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_WEEKDAY;
    sAlarm.AlarmDateWeekDay = dia_semana;
    sAlarm.Alarm = RTC_ALARM_A;
    sAlarm.AlarmMask = RTC_ALARMMASK_NONE; // Coincidencia exacta
    HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BCD);

    HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
}

// Función para configurar temporizador de despertar periódico
void ConfigurarDespertarPeriodico(uint8_t seleccion_reloj, uint16_t cuenta) {
    HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, cuenta, seleccion_reloj);
    HAL_NVIC_SetPriority(RTC_WKUP_IRQn, 3, 0);
    HAL_NVIC_EnableIRQ(RTC_WKUP_IRQn);
}

// Rutinas de servicio de interrupción
void RTC_Alarm_IRQHandler(void) {
    HAL_RTC_AlarmIRQHandler(&hrtc);
}

void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc_ptr) {
    // Acción al activarse la alarma
    printf("¡Alarma activada!\n");
}

void RTC_WKUP_IRQHandler(void) {
    HAL_RTCEx_WakeUpTimerIRQHandler(&hrtc);
}

void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc_ptr) {
    // Acción en cada despertar periódico
    printf("Temporizador de despertar.\n");
}

int main(void) {
    HAL_Init();
    SystemClock_Config();
    Configurar_RTC();

    // Configurar un despertar cada 5 segundos
    ConfigurarDespertarPeriodico(RTC_WAKEUPCLOCK_CK_SPRE_16BITS, 5 - 1);

    // Configurar una alarma para los miércoles a las 10:30:00
    ConfigurarAlarmaSemanal(RTC_WEEKDAY_WEDNESDAY, 10, 30, 0);

    uint8_t hora, min, seg, dia, mes, anio, semana;
    while (1) {
        // Leer y mostrar hora y fecha cada 10 ciclos
        HAL_RTC_GetTime(&hrtc, (RTC_TTypeDef*)&hora, RTC_FORMAT_BCD);
        HAL_RTC_GetDate(&hrtc, (RTC_DateTypeDef*)&anio, RTC_FORMAT_BCD);
        // ... (Código para formatear y mostrar la información)

        HAL_Delay(1000);
    }
}
</stdio.h>

3. Resumen de Funciones Clave de la HAL

3.1 Inicialización

HAL_RTC_Init(RTC_HandleTypeDef *hrtc): Inicializa el RTC según la estructura de configuración proporcionada. Requiere un reloj RTC ya seleccionado.

HAL_RTC_WaitForSynchro(RTC_HandleTypeDef *hrtc): Debe llamarse después de la inicialización para sincronizar el dominio de reloj del RTC con el del APB.

3.2 Acceso a Hora y Fecha

HAL_RTC_SetTime() / HAL_RTC_GetTime(): Establecen o leen la hora actual. El parámetro de formato puede ser RTC_FORMAT_BIN (binario) o RTC_FORMAT_BCD.

HAL_RTC_SetDate() / HAL_RTC_GetDate(): Establecan o leen la fecha actual. Se debe leer la hora y la fecha en secuencia para obtener valores consistentes.

3.3 Funciones de Alarma

HAL_RTC_SetAlarm_IT(): Configura una alarma y habilita su interrupción. Los campos de la estructura RTC_AlarmTypeDef se pueden combinar con máscaras (RTC_ALARMMASK_) para un control flexible de los eventos.

3.4 Funciones Avanzadas

HAL_RTCEx_SetWakeUpTimer_IT(): Configura un temporizador de despertar de cuenta decreciente para generar interrupciones periódicas, ideal para despertar el sistema de modos de sueño profundo.

HAL_RTCEx_SetTimeStamp(): Habilita la captura automática de la marca de tiempo en un pin de evento externo.

HAL_RTCEx_BKUPWrite() / HAL_RTCEx_BKUPRead(): Permiten leer y escribir en los registros de respaldo (DR0-DR31) que sobreviven a un reinicio del sistema.

4. Consideraciones Críticas de Diseño

  • Acceso al Dominio de Respaldo: Cualquier operación de configuración del RTC requiere una llamada previa a HAL_PWR_EnableBkUpAccess().
  • Estabilidad del Reloj: El LSE tarda ~500 ms en estabilizarse. Es recomendable verificar la bandera RCC_FLAG_LSERDY antes de continuar. El LSI es significativamente menos preciso.
  • Sincronización: Después de inicializar o cambiar la hora, es crucial llamar a HAL_RTC_WaitForSynchro() para evitar lecturas inconsistentes.
  • Baja Potencia: El RTC puede despertar el procesador desde los modos STOP y STANDBY, siendo fundamental para diseños con restricciones energéticas.
  • Prioridad de Interrupción: La interrupción de la alarma o del despertar debe tener una prioridad suficientemente alta para garantizar su procesamiento oportuno.

4.1 Estrategias de Implementación

Escenario Solución Implementación Clave
Alta precisión de marca de tiempo Combinar el registro de subsegundos SSR con un contador DWT. Calcular micros = (prediv - SSR) * (1000000 / (prediv+1)).
Optimización energética Entrar en modo STOP2 y usar el temporizador de despertar del RTC. La corriente consumida puede ser <10 μA.
Almacenamiento de estado crítico Utilizar los registros de respaldo para guardar estado o configuración. Leer al arrancar para determinar el contexto anterior.
Seguridad en el arranque Almacenar un hash de la configuración en un registro de respaldo. Verificar integridad en el enicio.

Etiquetas: STM32H750 RTC HAL Library Low Power Real-Time Clock

Publicado el 6-5 04:18