08/09/2017
En el fascinante universo del desarrollo de sistemas embebidos, y especialmente con los potentes microcontroladores STM32, uno de los pilares fundamentales es la gestión del tiempo. Ya sea para crear un simple parpadeo de un LED, gestionar la comunicación por un protocolo o implementar complejas máquinas de estado, saber "cuánto tiempo ha pasado" es crucial. La librería HAL de STMicroelectronics nos ofrece una puerta de entrada sencilla a esta funcionalidad a través de una función omnipresente: HAL_GetTick. Pero, ¿qué se esconde realmente detrás de esta simple llamada? ¿Cómo funciona y, lo que es más importante, cuándo debemos buscar alternativas más potentes? Acompáñanos en este análisis profundo para desentrañar los secretos del tiempo en STM32.

¿Qué es y Cómo Funciona Realmente HAL_GetTick?
A primera vista, HAL_GetTick() es una función simple que devuelve un número: la cantidad de milisegundos que han transcurrido desde que el microcontrolador se encendió y se inicializó la librería HAL. Es la base para otra función muy conocida, HAL_Delay(), y para los mecanismos de 'timeout' en casi todas las funciones de comunicación del HAL (UART, SPI, I2C).
Para entender su magia, debemos descomponer el mecanismo que lo impulsa, que es una colaboración entre hardware y software:
- El Temporizador SysTick: El corazón de todo es un temporizador de hardware estándar de 24 bits llamado SysTick, que forma parte integral de todos los procesadores ARM Cortex-M. Es un temporizador de cuenta regresiva simple pero fiable.
- La Inicialización con
HAL_InitTick(): Cuando llamas aHAL_Init()al principio de tu programa, esta a su vez invoca aHAL_InitTick(). Esta función tiene una misión clave: configurar el temporizador SysTick para que genere una interrupción exactamente cada 1 milisegundo. Para lograrlo, utiliza la frecuencia del reloj del sistema (SystemCoreClock) y la divide por 1000 para saber cuántos "ticks" de reloj caben en un milisegundo. - La Interrupción y
HAL_IncTick(): Una vez configurado, el SysTick comienza su cuenta regresiva. Cada vez que llega a cero, dispara una interrupción. El procesador detiene momentáneamente lo que está haciendo para ejecutar una función especial llamadaSysTick_Handler(). Dentro de este manejador de interrupción, se llama a otra función del HAL:HAL_IncTick(). - La Variable Global
uwTick: La funciónHAL_IncTick()hace algo muy simple: incrementa en uno una variable global de tipouint32_tllamadauwTick. Esta variable es, en esencia, nuestro contador de milisegundos.
Finalmente, cuando llamas a HAL_GetTick(), lo único que hace esta función es devolver el valor actual de la variable uwTick. Es un sistema elegante y eficiente para tareas de temporización no críticas.
El Atributo __weak: La Llave para la Personalización
Una de las características más brillantes del diseño del HAL es el uso extensivo del atributo __weak en sus funciones. Funciones como HAL_GetTick(), HAL_IncTick() y HAL_InitTick() están declaradas como "débiles".
¿Qué significa esto? Significa que el compilador las incluye por defecto, pero si tú, como desarrollador, creas una función con exactamente el mismo nombre y firma en tu propio código (por ejemplo, en main.c), el enlazador descartará la versión débil del HAL y usará la tuya en su lugar. Esto te da un poder inmenso para sobreescribir y personalizar el comportamiento del sistema de temporización sin tener que modificar los archivos de la librería HAL, manteniendo tu proyecto limpio y portable.
Por ejemplo, si quisieras que el tick del sistema fuera cada 10ms para reducir la carga de interrupciones, podrías redefinir HAL_InitTick() para configurar el SysTick a 100Hz y luego redefinir HAL_IncTick() para que incremente la variable uwTick en 10 en cada interrupción.
Cuando el Milisegundo no es Suficiente: Alternativas de Alta Precisión
El sistema basado en SysTick es fantástico para muchas aplicaciones, pero tiene sus limitaciones. La resolución está fijada en 1 milisegundo, lo cual es una eternidad en el mundo de la electrónica moderna. ¿Qué pasa si necesitas medir la duración de un pulso corto, perfilar el tiempo de ejecución de una función crítica o implementar un protocolo que requiere temporización en el rango de microsegundos? Aquí es donde debemos buscar alternativas más poteras.
Opción 1: Usar un Temporizador de Hardware (TIM)
Los microcontroladores STM32 vienen equipados con un arsenal de temporizadores de propósito general (TIMs) que son increíblemente versátiles y potentes. A diferencia del simple SysTick, estos temporizadores pueden ser de 16 o incluso 32 bits y ofrecen una plétora de funcionalidades. Para obtener una fuente de tiempo en microsegundos, podemos configurar uno de estos temporizadores como un contador de libre ejecución (free-running).
El proceso general es el siguiente:
- Seleccionar un Temporizador: Elige un temporizador que no estés usando. Los temporizadores de 32 bits como TIM2 o TIM5 (disponibles en muchas familias como la F4 o F7) son ideales porque tardan mucho más en desbordarse.
- Habilitar el Reloj: No olvides habilitar el reloj para el periférico del temporizador que has elegido. Por ejemplo:
__HAL_RCC_TIM5_CLK_ENABLE();. - Configurar el Prescaler: El prescaler divide la frecuencia del reloj que alimenta al temporizador. Para obtener una resolución de 1 microsegundo, debemos configurarlo para que el temporizador se incremente cada 1 µs. La fórmula es:
Prescaler = (FrecuenciaRelojDelTimer / 1,000,000) - 1. La frecuencia del reloj del timer se puede obtener con funciones comoHAL_RCC_GetPCLK1Freq(). - Configurar el Periodo (ARR): Para un contador de libre ejecución, queremos que cuente lo máximo posible antes de reiniciarse. Por lo tanto, configuramos el registro de auto-recarga (Auto-Reload Register, ARR) a su valor máximo (0xFFFFFFFF para un timer de 32 bits).
- Iniciar el Temporizador: Finalmente, habilitamos el contador para que empiece a correr.
Una vez configurado, puedes leer el valor del contador en cualquier momento accediendo directamente a su registro CNT (ej. TIM5->CNT). Esto te da una marca de tiempo con resolución de microsegundos, con un impacto casi nulo en el rendimiento de la CPU, ya que el hardware hace todo el trabajo de conteo.
Opción 2: El Contador de Ciclos del Core (DWT)
Para la máxima precisión posible, podemos acudir a una joya oculta dentro del propio núcleo Cortex-M: la unidad DWT (Data Watchpoint and Trace). Esta unidad, parte de las herramientas de depuración del core, incluye un contador de 32 bits llamado DWT CYCCNT (Cycle Counter) que, como su nombre indica, se incrementa con cada ciclo de reloj de la CPU.
Habilitarlo es sorprendentemente sencillo:
// Solo necesita ejecutarse una vez al inicio CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; DWT->CYCCNT = 0; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;A partir de ese momento, el registro DWT->CYCCNT te dará el número exacto de ciclos de reloj que han pasado. Para convertir esto a microsegundos, simplemente divides este valor por la frecuencia del core en MHz (SystemCoreClock / 1,000,000). Esta es la herramienta definitiva para el perfilado de código de alta precisión, permitiéndote medir el coste de ejecución de unas pocas líneas de código.
Tabla Comparativa de Métodos de Temporización
| Característica | SysTick (HAL_GetTick) | Temporizador Hardware (TIM) | Contador de Ciclos (DWT) |
|---|---|---|---|
| Resolución | Milisegundos (ms) | Configurable (típicamente µs) | Ciclo de reloj (nanosegundos) |
| Impacto en CPU | Bajo (interrupción cada 1ms) | Casi nulo (solo lectura de registro) | Insignificante |
| Recursos Usados | SysTick (1 por core) | Un periférico TIM | Unidad de depuración del Core |
| Caso de Uso Ideal | Delays, timeouts, tareas sencillas | Control de motores, protocolos, eventos de alta frecuencia | Perfilado de código, mediciones de muy corta duración |
Preguntas Frecuentes (FAQ)
- P: ¿Puedo seguir usando
HAL_Delay()si implemento mi propio sistema de tiempo con un TIM? - R: No directamente.
HAL_Delay()depende internamente deHAL_GetTick(). Si quieres que las funciones del HAL sigan funcionando, la mejor estrategia es sobreescribirHAL_GetTick()para que utilice tu temporizador de hardware. Deberás leer el contador en µs y dividirlo entre 1000 para devolver un valor en milisegundos. De esta forma, todo el ecosistema HAL se beneficiará de tu fuente de tiempo personalizada. - P: ¿Qué ocurre cuando el contador de
HAL_GetTick()(uwTick) se desborda? - R: Al ser un entero de 32 bits sin signo que se incrementa cada milisegundo, se desbordará (volverá a cero) después de aproximadamente 49.7 días. Para la mayoría de las aplicaciones esto no es un problema. El código que mide intervalos de tiempo (ej.
if (tiempo_actual - tiempo_anterior > intervalo)) funciona correctamente incluso a través del desbordamiento gracias a la aritmética modular de los enteros sin signo, siempre y cuando el intervalo sea menor que la mitad del periodo de desbordamiento. - P: Si uso un sistema operativo en tiempo real (RTOS) como FreeRTOS, ¿debo preocuparme por el SysTick?
- R: ¡Absolutamente! La mayoría de los RTOS toman el control completo del temporizador SysTick para gestionar su propio "tick" de sistema, que es el pulso que usan para planificar y cambiar entre tareas. En un entorno RTOS, nunca debes modificar la configuración del SysTick directamente. Si necesitas una base de tiempo para tu aplicación, es obligatorio usar un temporizador de hardware (TIM) diferente.
Conclusión: Elige la Herramienta Correcta para el Trabajo
HAL_GetTick es una herramienta magnífica y un excelente punto de partida. Proporciona una base de tiempo simple y fiable que es suficiente para una gran cantidad de aplicaciones. Sin embargo, como desarrolladores, es fundamental conocer sus limitaciones y no tener miedo de explorar las potentes alternativas que el hardware de STM32 nos ofrece. Entender cómo funcionan los temporizadores de hardware y el contador de ciclos del DWT no solo resuelve los problemas de precisión, sino que también abre la puerta a un desarrollo más avanzado, eficiente y profesional. La próxima vez que necesites medir el tiempo, piensa en tus requisitos y elige sabiamente: el tick del reloj adecuado puede marcar la diferencia entre un proyecto que funciona y un proyecto que brilla.
Si quieres conocer otros artículos parecidos a STM32: El Secreto detrás de HAL_GetTick y el Tiempo puedes visitar la categoría Juegos.
