A diferencia del SysTick que cuenta hacia atrás para tareas del sistema operativo, los STM32 cuentan con múltiples Temporizadores de Hardware (ej. TIM2 a TIM11). Son periféricos masivos y complejos capaces de contar hacia arriba, hacia abajo, conectarse directamente a pines físicos para generar ondas (PWM) e incluso encadenarse entre ellos.
Para generar un evento a una frecuencia exacta, el reloj maestro que ingresa al timer (ej. 84MHz) debe ser dividido matemáticamente por dos registros fundamentales:
La ecuación maestra para calcular la frecuencia de disparo de la interrupción es:
Nota Bare-Metal: Los "+1" existen porque los registros informáticos comienzan en cero. Para dividir entre 100, debes escribir 99 en el registro.
Un ingeniero descuidado configuraría su timer asumiendo que corre a 42MHz (la velocidad máxima del bus APB1). Pero el hardware de ST tiene una trampa en el circuito de reloj: Si el divisor del bus APB1 no es 1, la frecuencia enviada a los Timers se multiplica por 2 automáticamente. Así, aunque el bus corra a 42MHz, el reloj interno del TIM2 será de 84MHz.
Ejercicio 1: Queremos generar un evento cada segundo exacto ($1\text{ Hz}$) usando el TIM2. Si nuestro reloj es de 84,000,000 Hz, usaremos la ecuación:
Si $\text{PSC} = 83999$ (es decir, $84000 - 1$), el timer cuenta a $1000\text{ Hz}$ ($1\text{ ms}$).
Si $\text{ARR} = 999$ (es decir, $1000 - 1$), contará mil veces antes de desbordarse.
Resultado: 1000 ms = 1 Segundo.
/**
* @file main.c
* @brief Configuración TIM2 para base de tiempo 1Hz
*/
#include "stm32f401xe.h"
void inicializar_timer2_1Hz(void) {
// 1. Habilitar el reloj del periférico TIM2 en el bus APB1
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
// 2. Configurar el Prescaler (PSC)
// TIM_CLK = 84 MHz. Dividimos entre 84,000 para obtener 1 kHz de tick.
TIM2->PSC = 84000 - 1;
// 3. Configurar el Auto-Reload Register (ARR)
// Queremos que el contador desborde al llegar a 1000 ticks (1 segundo)
TIM2->ARR = 1000 - 1;
// 4. Habilitar la interrupción por desbordamiento (Update Interrupt)
// Limpiamos la bandera UIF por seguridad antes de empezar
TIM2->SR &= ~TIM_SR_UIF;
TIM2->DIER |= TIM_DIER_UIE;
// 5. Configurar el NVIC (Árbitro)
// Le asignamos una prioridad moderada (ej. 3) y lo encendemos
NVIC_SetPriority(TIM2_IRQn, 3);
NVIC_EnableIRQ(TIM2_IRQn);
// 6. Arrancar el Timer (Enable Counter)
TIM2->CR1 |= TIM_CR1_CEN;
}
// Vector de interrupción del TIM2
void TIM2_IRQHandler(void) {
// Verificar si la interrupción fue causada por un Update Event (UIF)
if (TIM2->SR & TIM_SR_UIF) {
// ¡Este código se ejecuta con precisión atómica cada 1.000000 segundos!
// ideal para cálculos de flujo (Caudal = Litros / Segundo)
// calcular_tasa_flujo();
// Limpiar la bandera para evitar el bucle infinito
TIM2->SR &= ~TIM_SR_UIF;
}
}
int main(void) {
// SysTick_Init();
// Valvula_Init();
inicializar_timer2_1Hz();
while(1) {
// ...
}
return 0;
}
Objetivo del día: Cálculo Metrológico en el Dominio del Tiempo.
En el Día 102 logramos que el EXTI1 contara ciegamente cada pulsación de la turbina del caudalímetro. Sabemos cuántos litros han pasado en total, pero en la industria petrolera también necesitamos conocer el Caudal Instantáneo (Litros por Minuto - LPM) para saber si la bomba está funcionando correctamente.
Para calcular velocidad, necesitas un lapso de tiempo exacto. Al configurar el TIM2 con una ventana de 1.000 segundos a través de PSC y ARR, el hardware te proporcionará ese lapso sin margen de error. Cuando la interrupción TIM2_IRQHandler se dispara, simplemente lees cuántos pulsos capturó el EXTI1 en ese periodo, calculas los LPM y pones el contador parcial a cero para el siguiente segundo. Las dos interrupciones trabajan en paralelo formando un medidor metrológico perfecto.
TIM2) operan independientemente del CPU. Su velocidad depende del bus al que están conectados y de los multiplicadores internos del RCC.TIMx->SR) dentro del ISR antes de salir.