01 · Punto de partida
El tiempo como recurso de hardware
En el tema 13 mediste tiempos con el costo de las instrucciones, y en el 14 usaste un bucle que «se fija si ya pasó un rato». Ambos enfoques gastan procesador en esperar. Un temporizador hace lo contrario: cuenta por su cuenta, con el mismo reloj que el procesador o uno derivado de él, y solo molesta cuando hay algo que decir. Mientras tanto, la CPU puede hacer otra cosa o dormir.
Un temporizador es, en el fondo, un registro contador que se incrementa con cada pulso de reloj, más tres capacidades que se construyen sobre él:
- Desbordamiento: avisar cuando el contador llega a un valor límite. De ahí salen la base de tiempo y las tareas periódicas.
- Comparación: hacer algo cuando el contador alcanza un valor determinado. De ahí sale el PWM.
- Captura: guardar el valor del contador en el instante en que ocurre un evento externo. De ahí sale la medición de períodos y anchos de pulso.
Como siempre usamos el STM32F103: tiene el temporizador avanzado TIM1 y los de propósito general TIM2, TIM3 y TIM4, todos de 16 bits y con cuatro canales. Los nombres y las ideas son casi idénticos en otras familias.
02 · El mecanismo
El contador, el prescaler y la recarga
Tres registros definen el comportamiento básico de un temporizador, y se entienden como una cadena:
- El prescaler (
PSC) divide la frecuencia del reloj: el contador avanza una cuenta cadaPSC + 1pulsos del reloj del temporizador. - El contador (
CNT) sube desde 0 con cada cuenta. - El registro de recarga (
ARR, auto-reload) fija el valor máximo: cuandoCNTllega aARR, en la cuenta siguiente vuelve a 0 y se produce el evento de actualización (update event), que levanta una bandera y, si está habilitado, una interrupción.
Esto da las dos fórmulas que más vas a usar en todo el curso:
| Magnitud | Fórmula | Ejemplo con 72 MHz, PSC = 71, ARR = 999 |
|---|---|---|
| Frecuencia de cuenta | f_cuenta = f_clk / (PSC + 1) | 72 MHz / 72 = 1 MHz (1 µs por cuenta) |
| Frecuencia de desbordamiento | f_ovf = f_clk / ((PSC + 1) · (ARR + 1)) | 1 MHz / 1000 = 1 kHz |
| Período de desbordamiento | T = (PSC + 1) · (ARR + 1) / f_clk | 1 ms |
Dos trampas aparecen enseguida y conviene grabárselas: el divisor es PSC + 1 y el número de pasos es ARR + 1, porque ambos cuentan desde 0. Escribir ARR = 1000 esperando 1000 pasos da 1001. Y como los registros son de 16 bits, tanto PSC como ARR admiten como máximo 65535: el producto (PSC + 1) · (ARR + 1) puede llegar hasta unos 4300 millones, que a 72 MHz son 59,6 segundos. Para períodos más largos se cuentan desbordamientos por software.
Otro dato práctico: los temporizadores TIM2 a TIM4 cuelgan del bus APB1, que en el STM32F103 se limita a 36 MHz. Pero cuando el divisor de ese bus es distinto de 1, el hardware duplica el reloj de los temporizadores, de modo que con el reloj del sistema en 72 MHz los temporizadores también reciben 72 MHz. Mirar el reloj real del temporizador en el manual y en el árbol de relojes (tema 10) evita una discrepancia de un factor 2 en todos los cálculos.
Para generar la interrupción periódica de 1 kHz del tema 15 con TIM2, la configuración completa es:
#define RCC_APB1ENR (*(volatile uint32_t *)0x4002101C)
#define TIM2_CR1 (*(volatile uint32_t *)0x40000000)
#define TIM2_DIER (*(volatile uint32_t *)0x4000000C)
#define TIM2_SR (*(volatile uint32_t *)0x40000010)
#define TIM2_EGR (*(volatile uint32_t *)0x40000014)
#define TIM2_PSC (*(volatile uint32_t *)0x40000028)
#define TIM2_ARR (*(volatile uint32_t *)0x4000002C)
void tim2_iniciar_1khz(void) {
RCC_APB1ENR |= 1u; // 1) reloj del TIM2 (sin esto, escribir los registros no hace nada)
TIM2_PSC = 71; // 2) 72 MHz / 72 = 1 MHz
TIM2_ARR = 999; // 3) 1000 cuentas = 1 ms
TIM2_EGR = 1u; // 4) forzar un evento de actualización: carga PSC y ARR
TIM2_SR = 0; // y borrar la bandera que ese evento levantó
TIM2_DIER |= 1u; // 5) habilitar la interrupción de actualización (UIE)
NVIC_SetPriority(TIM2_IRQn, 2); // 6) prioridad y habilitación en el NVIC, al final
NVIC_EnableIRQ(TIM2_IRQn);
TIM2_CR1 |= 1u; // 7) CEN: recién ahora empieza a contar
}
volatile uint32_t ms; // compartida con main (tema 16)
void TIM2_IRQHandler(void) {
TIM2_SR &= ~1u; // la bandera UIF se borra escribiendo un 0
ms++;
}
El paso 4 es una fuente clásica de confusión: PSC y ARR están «precargados», es decir, lo que escribís se copia a los registros activos recién en el próximo evento de actualización. Si no lo forzás con el bit UG, el temporizador arranca con los valores viejos durante el primer período. Y como forzarlo levanta la bandera, hay que borrarla antes de habilitar la interrupción, o la rutina se ejecuta una vez de más al arrancar.
Observá también que, esta vez, la bandera se borra escribiendo un 0 y no un 1 como en el controlador EXTI del tema 15. Cada periférico define su propia convención de borrado, y el manual es la única referencia confiable. Un error de convención produce exactamente la tormenta de interrupciones descripta en aquel tema.
03 · Salidas controladas por el contador
Comparación y PWM
Cada canal de un temporizador tiene un registro de comparación (CCRx) cuyo valor el hardware compara con el contador en cada cuenta. En el modo PWM (modulación por ancho de pulso) la salida del canal se mantiene en alto mientras CNT < CCR y en bajo el resto del período. El resultado es una onda cuadrada cuya frecuencia la fija el desbordamiento y cuyo ciclo de trabajo (la fracción del tiempo en alto) vale CCR / (ARR + 1).
Subir o bajar CCR mueve el flanco de bajada sin tocar la frecuencia. Para que el cambio sea limpio se activa la precarga del canal: el valor nuevo se aplica en el próximo desbordamiento y nunca a mitad de un pulso, que produciría uno anómalo. Para regular el brillo de un LED o la velocidad de un motor, el programa solo escribe CCR1 y el hardware hace el resto, sin interrupciones ni intervención del procesador. Una configuración completa de TIM3 canal 1, 20 kHz, ciclo de trabajo del 25 %, en el pin PA6:
// (el reloj de GPIOA y AFIO ya está habilitado, como en el tema 9)
GPIOA_CRL = (GPIOA_CRL & ~(0xFu << 24)) | (0xBu << 24); // PA6: función alternativa, push-pull, 50 MHz
RCC_APB1ENR |= (1u << 1); // reloj del TIM3
TIM3_PSC = 0; // sin divisor: la mayor resolución posible
TIM3_ARR = 3599; // 72 MHz / 3600 = 20 kHz
TIM3_CCR1 = 900; // 900 / 3600 = 25 % de ciclo de trabajo
TIM3_CCMR1 = (6u << 4) | (1u << 3); // OC1M = 110: PWM modo 1; OC1PE: precarga del CCR1
TIM3_CCER = 1u; // CC1E: conectar el canal al pin
TIM3_CR1 = (1u << 7); // ARPE: precarga también de ARR
TIM3_EGR = 1u; // cargar los valores precargados
TIM3_CR1 |= 1u; // CEN: arrancar
void brillo_poner(uint16_t porcentaje) { // de 0 a 100
TIM3_CCR1 = (uint32_t)porcentaje * (3600u) / 100u; // el hardware lo aplica en el próximo período
}
Hay un compromiso de diseño que el PWM expone con claridad: la frecuencia y la resolución compiten por el mismo reloj. Como f_pwm · (ARR + 1) = f_cuenta y la frecuencia de cuenta no puede superar la del reloj, a 72 MHz una señal de 20 kHz tiene como máximo 3600 pasos (unos 11,8 bits), una de 100 kHz solo 720 (9,5 bits) y una de 1 MHz apenas 72. Para llegar a los 16 bits completos de resolución no se puede pasar de 72 MHz / 65536 ≈ 1,1 kHz.
| Aplicación | Frecuencia típica | Por qué |
|---|---|---|
| Brillo de un LED | ≥ 200 Hz, mejor 1 kHz | Por debajo de unos 100 Hz se percibe parpadeo |
| Motor de corriente continua | 20 kHz o más | Fuera del rango audible: sin silbido |
| Servomotor de modelismo | 50 Hz fijos | El ancho del pulso (1 a 2 ms) codifica el ángulo; el período es de 20 ms |
| Fuente conmutada pequeña | 100 kHz a 1 MHz | Componentes magnéticos más chicos, a costa de resolución |
| «DAC» con un filtro RC | Lo más alta posible con resolución suficiente | El filtro promedia el PWM y entrega una tensión continua |
El servomotor es un buen ejemplo numérico. Con PSC = 71 el contador cuenta a 1 MHz (1 µs por cuenta) y con ARR = 19999 el período es de 20 ms, 50 Hz. Un pulso de 1,5 ms es CCR1 = 1500 (posición central); 1 ms y 2 ms son los extremos. Quedan 1000 pasos de 1 µs para recorrer todo el giro: más que suficiente. Elegir el prescaler para que la unidad del contador sea una magnitud útil (1 µs aquí) simplifica mucho el código y evita errores de conversión.
04 · Medir en lugar de generar
Captura de entrada: medir pulsos
La captura de entrada es la operación inversa. En lugar de que el contador gobierne un pin, un flanco en el pin copia el valor del contador a un registro (CCRx) y, si se desea, interrumpe. Restando dos capturas consecutivas se obtiene el tiempo entre dos flancos con la resolución del contador y sin que influya la latencia de la interrupción: el instante quedó anotado por el hardware en el momento exacto, aunque la rutina tarde en ejecutarse.
Es la forma correcta de medir la frecuencia de un sensor de velocidad, el ancho de un pulso de un sonar o el período de una señal de un anemómetro. Con el canal 1 de TIM4 (pin PB6), contando a 1 MHz:
RCC_APB1ENR |= (1u << 2); // reloj del TIM4
TIM4_PSC = 71; // 1 MHz: cada cuenta es 1 µs
TIM4_ARR = 0xFFFF; // contar libremente los 16 bits
TIM4_CCMR1 = 1u; // CC1S = 01: la captura 1 toma la señal del pin TI1
TIM4_CCER = 1u; // CC1E: captura habilitada, flanco de subida
TIM4_DIER |= (1u << 1); // CC1IE: interrumpir en cada captura
NVIC_EnableIRQ(TIM4_IRQn);
TIM4_CR1 |= 1u;
volatile uint16_t periodo_us; // compartida con main; un dato de 16 bits es atómico
static uint16_t anterior;
void TIM4_IRQHandler(void) {
uint16_t actual = (uint16_t)TIM4_CCR1; // leer el registro de captura también borra la bandera
periodo_us = (uint16_t)(actual - anterior); // resta sin signo de 16 bits
anterior = actual;
}
La resta sin signo hace el trabajo difícil: funciona aunque el contador haya dado la vuelta entre las dos capturas. Si la primera captura fue 65000 y la segunda 464, el contador pasó por 65535 y 0 en el medio, y la resta módulo 65536 da 1000: el valor correcto. Es el mismo principio que usarás para el reloj de milisegundos en el apartado siguiente, y el resultado del programa de Python lo comprueba.
Dos límites determinan qué se puede medir así:
- El rango: un contador de 16 bits a 1 MHz mide períodos de hasta 65,5 ms. Más allá, la resta es ambigua y hay que contar desbordamientos aparte, o bajar la frecuencia de cuenta.
- La resolución: el error es de ±1 cuenta. Medir una señal de 1 kHz con cuentas de 1 µs da 1000 cuentas por período, un error de hasta 0,1 %; pero con una señal de 50 kHz son solo 20 cuentas por período y el error llega al 5 %. Si la señal es rápida conviene aumentar la frecuencia de cuenta (con
PSC = 0se cuenta a 72 MHz) o, mejor, medir varios períodos juntos y dividir.
Para señales muy rápidas existe el método complementario: contar los pulsos que llegan durante un intervalo fijo (la «ventana»). Se configura un temporizador como contador de eventos externos y otro como ventana de 1 s: los pulsos contados son los hercios. Vale lo mismo que antes pero al revés: el método de contar es preciso en frecuencias altas y el de medir el período en frecuencias bajas. Estos mismos contadores, con dos canales, sirven también para leer un codificador de cuadratura (encoder) directamente por hardware, sin interrupciones.
05 · La base de tiempo del programa
El tick del sistema
La mayoría de los programas necesitan una noción simple del tiempo: «¿pasaron 100 ms desde que prendí el LED?». La solución estándar es un tick: una interrupción periódica (típicamente cada 1 ms) que incrementa un contador global. Los Cortex-M traen para esto un temporizador dentro del propio núcleo, el SysTick: un contador descendente de 24 bits que se recarga solo y genera la excepción SysTick_Handler. Al ser parte del núcleo y no del fabricante, funciona igual en cualquier Cortex-M y deja libres los temporizadores del chip.
static volatile uint32_t ms; // milisegundos desde el arranque
void SysTick_Handler(void) { ms++; } // una sola línea: la ISR más corta posible
void tick_iniciar(void) {
SysTick_Config(SystemCoreClock / 1000u); // recarga = 72000 − 1: una interrupción por milisegundo
// SysTick_Config deja el SysTick con la prioridad más baja; para el tick de un superloop es lo correcto
}
uint32_t millis(void) { return ms; } // la lectura de 32 bits es atómica (tema 16)
El registro de recarga del SysTick es de 24 bits, así que a 72 MHz el período máximo es 16 777 216 / 72 MHz ≈ 233 ms; SysTick_Config devuelve 1 si el valor pedido no entra. Con millis() se implementan esperas sin bloquear, la herramienta central de los temas 14 y 19:
static uint32_t t_led;
void tarea_led(void) {
if ((uint32_t)(millis() - t_led) >= 500u) { // ¿pasaron 500 ms?
t_led += 500u; // avanzar por el período, sin acumular el atraso
led_conmutar();
}
}
Dos detalles de esta función valen oro. El primero: la comparación se hace restando (ahora − inicio ≥ plazo) y no sumando (ahora ≥ inicio + plazo). Con un contador de 32 bits de milisegundos, el valor da la vuelta a los 49,7 días; la suma desborda y produce una condición que se cumple demasiado pronto, mientras que la resta, que opera módulo 232, da siempre el tiempo transcurrido correcto mientras el plazo sea menor que la mitad del rango. Es la misma aritmética que en la captura. El segundo: se avanza con t_led += 500 y no con t_led = millis(), para que el retraso de una vuelta no se acumule y el LED mantenga exactamente su período medio.
Y una limitación que no es de software: la precisión del tick es la del reloj. Un cristal típico tiene una tolerancia de ±20 partes por millón, que son 1,7 segundos de error por día; el oscilador RC interno de un microcontrolador ronda ±1 %, o sea unos 14 minutos por día. Para medir intervalos cortos no importa; para un reloj de pared, sí (y se usa un cristal de 32,768 kHz, tema 10).
Si se necesita una resolución mejor que 1 ms, se puede leer el valor actual del contador del SysTick y combinarlo con ms para obtener microsegundos. Pero como son dos lecturas de variables que la interrupción modifica, vale el mismo cuidado que en el laboratorio del tema 16: hay que verificar que el tick no haya avanzado en el medio.
06 · Diseño
Repartir los temporizadores
Un microcontrolador tiene un número pequeño de temporizadores y los proyectos reales los disputan. Algunas reglas para repartirlos:
- Los canales de un temporizador comparten el contador. Los cuatro canales de
TIM3tienen la misma frecuencia (la delARR); solo difiere elCCRde cada uno. Cuatro motores con la misma frecuencia de PWM caben en un temporizador; un motor a 20 kHz y un servo a 50 Hz necesitan temporizadores distintos. - Los pines no se eligen libremente. Cada canal sale por un pin concreto, que se puede cambiar solo a unas pocas alternativas (remapeo). En el STM32F103, por ejemplo,
TIM2usa PA0 a PA3,TIM3usa PA6, PA7, PB0 y PB1, yTIM4usa PB6 a PB9. Antes de decidir el reparto hay que mirar qué pin necesita cada señal y en qué temporizador cae. - Conviene reservar uno para el tiempo del sistema (o usar el SysTick, que no compite con nadie) y no usar para PWM el temporizador que genera la base de tiempo, porque cambiar su frecuencia desordenaría todo lo demás.
- El prescaler más chico da más resolución, pero acorta el período máximo; el más grande alarga el período y empeora la resolución. Se elige el menor que cubra el rango necesario.
- Documentá el reparto. Una tabla con temporizador, canal, pin, frecuencia y función, en el mismo lugar donde está la tabla de prioridades del tema 15, ahorra días de depuración cuando el proyecto crece.
| Recurso | Función | Configuración |
|---|---|---|
| SysTick | Tick de 1 ms | Recarga 71 999, prioridad más baja |
| TIM3 canal 1 (PA6) | PWM del motor | PSC 0, ARR 3599 → 20 kHz, 3600 pasos |
| TIM2 canal 2 (PA1) | Servo | PSC 71, ARR 19999 → 50 Hz, pasos de 1 µs |
| TIM4 canal 1 (PB6) | Captura del sensor de velocidad | PSC 71 → 1 µs por cuenta, hasta 65,5 ms |
07 · Analogía con programación
Tres cálculos de temporizador en Python
El programa automatiza tres tareas que vas a repetir en cada proyecto: buscar el PSC y el ARR que mejor aproximan una frecuencia, restar capturas de un contador de 16 bits aunque haya dado la vuelta (y ver cómo crece el error con la frecuencia), y comparar plazos de un reloj de milisegundos de 32 bits en el borde del desborde.
Python en tu navegador. Los bloques son editables: cambiá los valores y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.
F_TIM = 72_000_000 # Hz, reloj del temporizador
# --- Parte 1: elegir PSC y ARR para una frecuencia de desbordamiento -------------------
def mejor(f_objetivo):
mejor = None
for psc in range(0, 65536):
n = F_TIM / (f_objetivo * (psc + 1)) # valor ideal de (ARR + 1)
arr1 = round(n)
if not 2 <= arr1 <= 65536:
continue
f_real = F_TIM / ((psc + 1) * arr1)
err = abs(f_real - f_objetivo) / f_objetivo * 100
if mejor is None or err < mejor[0] - 1e-12: # a igual error, queda el PSC menor (más resolución)
mejor = (err, psc, arr1 - 1, f_real)
return mejor
print("Parte 1: frecuencias de desbordamiento con un reloj de 72 MHz")
for f in (1000, 440, 50, 20000, 1):
err, psc, arr, f_real = mejor(f)
print(f" {f:6d} Hz -> PSC={psc:5d} ARR={arr:5d} real={f_real:12.6f} Hz error={err:.4f} %")
# --- Parte 2: medir un período con captura y contador de 16 bits ------------------------
TICK_US = 1.0 # 1 MHz
def periodo(c1, c2):
return (c2 - c1) & 0xFFFF # aritmética sin signo de 16 bits
print()
print("Parte 2: captura de flancos con contador de 16 bits (1 tick = 1 µs)")
for c1, c2 in ((1000, 2000), (65000, 464), (65535, 0), (10, 10)):
print(f" c1={c1:5d} c2={c2:5d} período = {periodo(c1, c2):5d} µs")
print(f" período máximo sin contar desbordes: {0xFFFF * TICK_US / 1000:.3f} ms")
print(" error de cuantización (±1 tick) según la frecuencia medida:")
for f in (100, 1000, 10000, 50000):
ticks = 1e6 / f
print(f" {f:6d} Hz -> {ticks:8.0f} ticks por período, error hasta {100/ticks:.3f} %")
# --- Parte 3: el reloj de milisegundos que da la vuelta ------------------------------------
M = 2**32
def mal(ahora, inicio, plazo): return ahora >= (inicio + plazo) % M # lo que hace C con uint32_t
def bien(ahora, inicio, plazo): return ((ahora - inicio) % M) >= plazo
print()
print("Parte 3: plazo de 100 ms que empieza a 50 ms del desborde de 32 bits (49,7 días)")
inicio = M - 50
for ahora in (M - 20, M - 1, 0, 49, 50, 60):
print(f" ahora={ahora:11d} comparar sumando: {str(mal(ahora, inicio, 100)):5s} restando: {str(bien(ahora, inicio, 100))}")
Resultado: en la parte 1, 1 kHz sale con PSC = 1 y ARR = 35999 (error nulo; PSC = 0 exigiría ARR = 71999, que no entra en 16 bits), 50 Hz con PSC = 23 y ARR = 59999, y 20 kHz con PSC = 0 y ARR = 3599. La nota de 440 Hz no tiene solución exacta: la mejor combinación (PSC = 3, ARR = 40908) da 440,000978 Hz, un error de 0,0002 %. Para 1 Hz aparece PSC = 1124 y ARR = 63999, igual de válido que el habitual 7199 y 9999: hay muchas soluciones, y se elige la de menor prescaler. En la parte 2 las capturas 65000 y 464 dan 1000 µs, igual que 1000 y 2000, aunque el contador haya dado la vuelta; y el error de ±1 cuenta pasa de 0,01 % a 100 Hz a 5 % a 50 kHz. En la parte 3, la comparación sumando dispara a los 30 ms en lugar de los 100 (con ahora = M − 20), mientras que la resta recién da verdadero a los 100 ms (ahora = 50).
Probá pedir otras frecuencias (por ejemplo 38 400 Hz, la frecuencia de portadora de un control remoto de 38 kHz) y observá cuáles tienen error nulo y cuáles no. Una frecuencia solo se obtiene exacta si f_clk / f es un número entero que se pueda descomponer en dos factores de hasta 65 536.
08 · Exploración interactiva
Configurar un temporizador
La calculadora aplica las fórmulas del tema. Elegí el reloj del temporizador, el prescaler, el valor de recarga y el ciclo de trabajo pedido. Verás la frecuencia de cuenta, la de desbordamiento, el valor de CCR1 resultante, la resolución del PWM, la señal esperada en la salida y la carga que supondría atender cada desbordamiento por interrupción.
Un temporizador de 16 bits con un canal de PWM
Señal en la salida (dos períodos)
—
Cambiá los valores para ver cómo se reparte el reloj entre frecuencia y resolución.
Recorrido guiado
- Con los valores iniciales (72 MHz, PSC 71, ARR 999, 25 %): el temporizador cuenta a 1 MHz, desborda a 1 kHz,
CCR1 = 250y hay 1000 pasos (≈ 10 bits). Es la base de tiempo de 1 ms del apartado 2. - Poné PSC en 0 y ARR en 3599: el PWM sube a 20 kHz,
CCR1 = 900y la resolución a 3600 pasos (≈ 11,8 bits). Es la configuración del PWM para un motor, fuera del rango audible. Si cada desbordamiento interrumpiera, la carga sería del 4,8 %. - Con PSC 0, bajá ARR a 999: ahora la frecuencia es de 72 kHz pero la resolución cae a 1000 pasos y la carga de interrupciones sube al 17,3 %. Más frecuencia cuesta resolución, porque el reloj se reparte entre las dos.
- Poné PSC en 71, ARR en 19 999 y ciclo de 5 %: 50 Hz y
CCR1 = 1000, es decir, un pulso de 1 ms. Con 10 % el pulso es de 2 ms. Son los extremos de un servomotor, con 1000 pasos de 1 µs entre uno y otro. - Poné PSC en 7199 y ARR en 9999: desborda a 1 Hz. Con ciclo de 50 %, la salida está en alto medio segundo y en bajo medio segundo: un LED de «latido». La frecuencia de cuenta (10 kHz) es muy baja para cualquier otra cosa.
- Poné ARR en 65 535 con PSC 0 a 72 MHz: la resolución es de 16 bits, pero la frecuencia cae a 1,1 kHz. Es el límite que calculaste en el apartado 3. Cambiá el reloj a 8 MHz y observá cómo todas las magnitudes se escalan.
Desafío: ¿se pueden tener 20 kHz y 16 bits de resolución a la vez?
Con un solo contador de 16 bits y un reloj de 72 MHz, no: el límite es f_pwm · pasos ≤ f_clk, y 20 kHz con 65 536 pasos exigiría un reloj de 1,3 GHz. Existen trucos que hacen el compromiso más llevadero: el modo de alineación al centro (para el mismo ARR la frecuencia de PWM es la mitad, a cambio de una señal más simétrica y con menos armónicos), la modulación del ciclo de trabajo entre dos valores vecinos (dithering), o los temporizadores de alta resolución que algunas familias traen con relojes de GHz. Pero la solución habitual es reducir la exigencia: 11 o 12 bits son suficientes para casi cualquier motor o LED.
09 · Método de trabajo
Método para configurar un temporizador
- Escribí qué debe hacer, con números: una frecuencia, un ciclo de trabajo, un rango a medir, una tolerancia. Sin números no se puede calcular.
- Averiguá el reloj real del temporizador en el árbol de relojes (recordá la duplicación del bus de periféricos) y anotalo.
- Calculá
PSCyARR. Empezá por el prescaler más chico que dejeARR + 1 ≤ 65 536, o por el que dé una unidad de cuenta cómoda (1 µs, 10 µs). Verificá el error de frecuencia, y que el productof_pwm · pasosentregue la resolución que necesitás. - Asigná temporizador, canal y pin consultando la tabla de funciones alternativas del chip, y revisá que ningún otro recurso use el mismo pin o el mismo temporizador con otra frecuencia.
- Configurá en orden: reloj del periférico,
PSCyARR, canal, precarga, forzado del evento de actualización, borrado de banderas, interrupción en el temporizador y en el NVIC y, por último, el bit que lo pone en marcha. - Si hay interrupción, aplicá las reglas del tema 16: una ISR mínima, la bandera se borra según la convención del periférico, y los datos compartidos son
volatile. - Verificá con un instrumento, no con el cálculo: medí la frecuencia y el ciclo de trabajo en el pin con un osciloscopio o un analizador lógico y compará con el valor teórico. Un factor 2 indica casi siempre un error en el reloj del temporizador.
- Documentá el uso en la tabla de recursos: qué temporizador, qué canal, qué pin, con qué
PSCyARR, y por qué.
Si la señal no aparece, el diagnóstico es una cadena de comprobaciones muy parecida a la de las compuertas de interrupción: ¿el temporizador tiene reloj? ¿está contando (se lee CNT dos veces y cambia)? ¿el canal está habilitado en CCER y en modo PWM? ¿el pin está configurado como función alternativa y tiene el reloj del puerto y de AFIO? ¿el pin es realmente el del canal elegido?
10 · Comprensión
Confusiones frecuentes
«Con PSC = 71 el reloj se divide por 71»
Se divide por 72: el prescaler cuenta desde 0, por eso su divisor es PSC + 1. Lo mismo ocurre con ARR, que da ARR + 1 pasos. Olvidar el «+ 1» produce un error pequeño (1,4 % en el primer caso) que pasa inadvertido en las pruebas y falla en un cronómetro o en un protocolo.
«Para generar PWM hace falta una interrupción»
No: el hardware compara el contador con CCR y maneja el pin solo, sin intervención del procesador. La interrupción solo se necesita si el programa debe hacer algo en cada período, como cambiar el ciclo de trabajo siguiendo una tabla. Hacer el PWM «a mano» con una ISR consume la CPU y tiene jitter.
«Puedo tener cualquier frecuencia y cualquier resolución»
Se reparten un mismo reloj: f_pwm · pasos = f_cuenta. A 72 MHz, más frecuencia significa menos pasos. Hay que decidir cuál de las dos importa más para la carga.
«Cambié el PSC y no pasó nada»
El prescaler tiene un registro de precarga que se carga recién en el próximo evento de actualización. Forzalo con el bit UG de EGR y borrá la bandera resultante, o esperá un período completo con los valores anteriores.
«Comparar con ahora ≥ inicio + plazo es lo mismo que restar»
Solo hasta que el contador da la vuelta. La suma desborda y la condición se vuelve verdadera mucho antes, o nunca. La resta módulo 232 es correcta en todo el rango, y es el único modo que vale la pena escribir.
«El tick de 1 ms es exacto»
Es exacto en cuanto lo sea el reloj: ±20 ppm con un cristal (1,7 s por día) y ±1 % con el oscilador RC interno (14 minutos por día). Además se retrasa si las interrupciones están deshabilitadas más de 1 ms, porque se pierde un tick (tema 16).
«Medir el período de una señal rápida es solo contar más fino»
Hay un límite doble: una cuenta más rápida acorta el rango antes de dar la vuelta (a 72 MHz un contador de 16 bits da la vuelta en 0,91 ms), y la propia ISR de captura no puede dispararse a más de unas pocas decenas de kHz. Más allá se cuenta en una ventana de tiempo o se usa DMA.
11 · Práctica guiada
Predecí antes de comprobar
1. Calcular PSC y ARR
Con un reloj de temporizador de 48 MHz, calculá un par PSC y ARR para (a) 2 kHz con la mayor resolución posible y (b) 1 Hz.
Ver solución
(a) 48 000 000 / 2000 = 24 000 pasos; con PSC = 0 alcanza: ARR = 23 999, que cabe en 16 bits y da la mayor resolución (24 000 pasos). (b) 48 000 000 pasos en total, que hay que repartir en dos factores de hasta 65 536. Por ejemplo PSC = 4799 (divide por 4800) y ARR = 9999: 4800 × 10 000 = 48 000 000. Otra solución válida es PSC = 47 999 y ARR = 999.
2. El ciclo de trabajo
Un PWM de 20 kHz con ARR = 3599 a 72 MHz. ¿Qué CCR da un ciclo del 35 %? ¿Cuál es el cambio más fino de ciclo de trabajo que se puede pedir?
Ver solución
CCR = 0,35 × 3600 = 1260. El paso mínimo es una cuenta: 1 / 3600 = 0,0278 %, lo que equivale a unos 11,8 bits de resolución. Si se pidiera 33,333 %, el valor entero más cercano (1200) da un 33,333 % exacto; con 33,3 % el redondeo daría 1199, un error imperceptible.
3. El servo
Un servo espera pulsos de 1 a 2 ms cada 20 ms. Con un reloj de 72 MHz, ¿qué PSC y ARR usarías para que 1 cuenta sea 1 µs? ¿Qué CCR da el centro (1,5 ms)? ¿Cuántas posiciones distintas hay en el recorrido?
Ver solución
PSC = 71 (72 MHz / 72 = 1 MHz) y ARR = 19 999 (20 000 cuentas = 20 ms). El centro es CCR = 1500. Entre 1000 y 2000 hay 1000 posiciones de 1 µs. Con PSC = 0 no se podría: 72 MHz / 50 Hz = 1 440 000 pasos, más de los 65 536 que admite el contador.
4. Medir una señal rápida
Una señal de 12 kHz se mide con captura a 1 MHz. ¿Cuál es el error máximo? ¿Qué pasa con la cuenta a 72 MHz, y cuál es la frecuencia mínima medible en ese caso?
Ver solución
A 1 MHz hay 1 000 000 / 12 000 ≈ 83,3 cuentas por período: el error de ±1 cuenta es de hasta 1,2 %. A 72 MHz son 6000 cuentas: 0,017 %. Pero el contador da la vuelta a los 65 536 / 72 MHz ≈ 0,91 ms, así que la menor frecuencia medible sin contar desbordes es de unos 1,1 kHz; para señales más lentas hay que bajar la frecuencia de cuenta o contar desbordamientos.
5. El reloj que da la vuelta
millis() vale 4 294 967 000 cuando se arranca un plazo de 500 ms. ¿A qué valor de millis() termina, y qué da millis() − inicio cuando millis() vale 100? ¿Y si se compara con ahora ≥ inicio + 500?
Ver solución
232 = 4 294 967 296, así que hasta el desborde faltan 296 ms. El plazo termina cuando el contador vale 500 − 296 = 204. Cuando millis() vale 100, la resta sin signo da 100 + 296 = 396 ms: todavía no se cumplió, correcto. Con la suma, inicio + 500 se desborda y vale 204, así que la condición ahora ≥ 204 ya es verdadera apenas se arranca el plazo (porque ahora vale 4 294 967 000): termina de inmediato, un error de 500 ms.
6. La carga de los temporizadores
El tick de 1 kHz tiene una ISR de 1,5 µs, y la captura de un sensor interrumpe a 10 kHz con una ISR de 3 µs. Se suman 0,4 µs de entrada y salida a cada una. ¿Qué fracción del procesador consumen?
Ver solución
Tick: (1,5 + 0,4) / 1000 = 0,19 %. Captura: (3 + 0,4) / 100 = 3,4 %. Total ≈ 3,6 %: una carga cómoda. Sin embargo, si la captura llegara a 50 kHz sin cambiar la ISR, serían (3,4 / 20) = 17 %, y habría que pasar a contar en ventana o a DMA.
12 · Síntesis
Ideas para recordar
- Un temporizador es un contador de hardware que avanza solo con el reloj; sobre él se construyen el desbordamiento (base de tiempo), la comparación (PWM) y la captura (medición), sin gastar instrucciones.
- El contador avanza cada
PSC + 1pulsos y desborda cadaARR + 1cuentas:f_ovf = f_clk / ((PSC + 1) · (ARR + 1)). Los dos registros son de 16 bits; el «+ 1» es la trampa más común. - Hay que conocer el reloj real del temporizador (se duplica en los buses con divisor), forzar un evento de actualización para cargar los valores precargados y respetar la convención de borrado de cada bandera.
- En el PWM la frecuencia la fija el desbordamiento y el ciclo de trabajo vale
CCR / (ARR + 1); la frecuencia y la resolución se reparten el reloj:f_pwm · pasos = f_cuenta. - La captura de entrada anota el contador en el instante exacto de un flanco, sin que importe la latencia de la interrupción; la resta sin signo da el período aunque el contador haya dado la vuelta, con error de ±1 cuenta.
- El tick del sistema (SysTick) incrementa un contador global cada milisegundo; los plazos se comparan restando (
ahora − inicio ≥ plazo), nunca sumando, para sobrevivir al desborde de los 49,7 días. - La precisión del tiempo es la del oscilador: ±20 ppm con cristal, ±1 % con el RC interno.
- Los canales de un temporizador comparten frecuencia y los pines son fijos: conviene repartir temporizadores, canales y pines en una tabla antes de escribir código, y verificar el resultado con un instrumento.
¿Listo para el tema 18?
Antes de avanzar, calculá el PSC y el ARR para una interrupción cada 1 ms con un reloj de 72 MHz; decí cuántos pasos de resolución tiene un PWM de 20 kHz y por qué no se puede tener más; y explicá por qué se compara un plazo con una resta y no con una suma. Si podés justificar las tres, entendés los temporizadores.
En el próximo tema, un taller, ponemos todo en práctica: una base de tiempo de 1 ms, tareas periódicas, medición de períodos y un control en lazo cerrado que reúne los temporizadores, las interrupciones y las buenas prácticas de los temas anteriores.