How to set timer 1 IRQ frequency in Arduino?

Control Preciso con Timer1 en Arduino

29/06/2023

Valoración: 4.95 (15205 votos)

En el fascinante mundo de la electrónica y la programación de microcontroladores, la gestión del tiempo es un pilar fundamental. A menudo, cuando comenzamos a programar con Arduino, nos familiarizamos con funciones simples como delay() para introducir pausas en nuestro código. Sin embargo, estas pausas tienen una limitación crucial: son bloqueantes. Esto significa que, mientras el microcontrolador está en un delay(), no puede realizar ninguna otra tarea. Para aplicaciones donde la precisión, la multitarea y la reactividad son esenciales, necesitamos una solución más sofisticada: los temporizadores de hardware y las interrupciones.

How to set timer 1 IRQ frequency in Arduino?
Arduino page for it here. Download from here or just install TimerOne using the Arduino libraries manager //Set the Timer 1 IRQ frequency Timer1.initialize(10000); //Set the timer period in uS (this function will attempt to match using the available prescaller settings //Set the timer 1 IRQ fucntion to be called

Aquí es donde entra en juego el Timer1 de Arduino, un recurso poderoso que, cuando se maneja correctamente, desbloquea un nuevo nivel de control sobre tus proyectos. A diferencia de los temporizadores de 8 bits (como Timer0 o Timer2), el Timer1 es un temporizador de 16 bits, lo que le permite contar períodos de tiempo mucho más largos y con una resolución excepcional, ideal para tareas que requieren una temporización precisa y repetitiva sin paralizar el resto del programa.

Índice de Contenido

¿Qué son las Interrupciones (IRQ) y por qué son vitales?

Una Interrupción (IRQ, por sus siglas en inglés, Interrupt Request) es una señal que le indica al procesador que debe detener su ejecución actual y atender una tarea de mayor prioridad. Piensa en ello como una alarma que suena en tu teléfono; no importa lo que estés haciendo, la alarma te obliga a prestarle atención. En el contexto de Arduino y los temporizadores, una IRQ ocurre cuando un temporizador alcanza un valor predefinido (o desborda), lo que dispara una función especial conocida como Rutina de Servicio de Interrupción (ISR, Interrupt Service Routine).

La principal ventaja de las interrupciones es que permiten la ejecución de código de manera no bloqueante. Esto significa que tu programa principal puede seguir ejecutándose, realizando otras tareas, mientras el temporizador cuenta en segundo plano. Cuando el temporizador alcanza su marca, se ejecuta la ISR, y luego el programa principal retoma su curso exactamente donde lo dejó. Esta capacidad es invaluable para:

  • Controlar múltiples dispositivos simultáneamente.
  • Monitorear sensores a intervalos exactos.
  • Generar señales periódicas (como PWM de alta precisión).
  • Implementar algoritmos de control en tiempo real.

Configurando el Timer1 con la Librería TimerOne

Aunque es posible configurar el Timer1 directamente manipulando los registros del microcontrolador (lo que ofrece el máximo control pero es más complejo), la comunidad de Arduino ha desarrollado librerías que simplifican enormemente este proceso. La librería TimerOne es una de las más populares y fáciles de usar para este propósito. Si aún no la tienes, puedes instalarla fácilmente desde el Gestor de Librerías del IDE de Arduino, buscando 'TimerOne'.

Pasos para Configurar la Frecuencia de IRQ del Timer1:

La configuración básica del Timer1 para generar interrupciones a una frecuencia específica implica unos pocos pasos con la librería TimerOne. Veamos el código proporcionado y desglosémoslo:

#include <TimerOne.h> // Incluir la librería TimerOne void setup() { // 1. Establecer el período del temporizador en microsegundos // Timer1.initialize(10000); // Esto establece un período de 10,000 microsegundos (10 ms) // lo que resulta en una frecuencia de 100 Hz (1,000,000 us / 10,000 us) // 2. Adjuntar la función de interrupción (ISR) // Timer1.attachInterrupt(timerIsr); // 'timerIsr' es la función que se llamará cuando el temporizador desborde } void loop() { // Aquí va el código principal de tu programa // El Timer1 y su ISR se ejecutan en segundo plano } // * //  RUTINA DE SERVICIO DE INTERRUPCIÓN (ISR) DEL TIMER 1  // * void timerIsr() { // Aquí colocas el código que quieres que se ejecute periódicamente // ¡Mantén esta función lo más corta y eficiente posible! // Evita funciones como delay(), Serial.print(), y operaciones complejas. } 

Analicemos las funciones clave:

  1. Timer1.initialize(period_us):

    • Esta es la función principal para configurar el temporizador.
    • El argumento period_us se especifica en microsegundos (µs).
    • La librería calcula automáticamente el preescalador y los valores de registro necesarios para lograr el período deseado.
    • Para calcular la frecuencia a partir del período, usa la fórmula: Frecuencia (Hz) = 1,000,000 / Período (µs).
    • Por ejemplo, si period_us es 10,000 (como en el ejemplo), la frecuencia será 1,000,000 / 10,000 = 100 Hz. Esto significa que la función timerIsr() se llamará 100 veces por segundo.
  2. Timer1.attachInterrupt(function_name):

    • Esta función asocia una rutina de servicio de interrupción específica con el evento del Timer1.
    • function_name debe ser el nombre de una función que no tome argumentos y no retorne ningún valor (void function_name()).
    • Esta función se ejecutará automáticamente cada vez que el temporizador alcance el período configurado.
  3. void timerIsr():

    • Esta es tu ISR. Aquí es donde pondrás el código que necesita ejecutarse con la frecuencia exacta que configuraste.
    • Es crucial que el código dentro de una ISR sea lo más conciso y rápido posible. Operaciones que consumen mucho tiempo, como delay(), o funciones de comunicación serial (Serial.print()), deben evitarse, ya que podrían interferir con otras interrupciones o con la ejecución del programa principal.
    • Si necesitas actualizar variables que también se usan en el loop() principal, decláralas como volatile para asegurar que el compilador no las optimice incorrectamente.

Otras Funciones Útiles de TimerOne:

La librería TimerOne también proporciona funciones para un control más dinámico:

  • Timer1.stop(): Detiene el temporizador. La ISR dejará de ejecutarse.
  • Timer1.start(): Inicia o reanuda el temporizador después de haber sido detenido.
  • Timer1.restart(): Reinicia el conteo del temporizador a cero sin cambiar su período.
  • Timer1.setPeriod(period_us): Permite cambiar el período del temporizador en tiempo de ejecución. Esto es útil si necesitas ajustar la frecuencia de tus interrupciones dinámicamente.

Casos de Uso Prácticos del Timer1

La capacidad de generar interrupciones periódicas abre un abanico de posibilidades para tus proyectos:

  • Parpadeo de LED Preciso: En lugar de usar delay(), puedes hacer que un LED parpadee con una frecuencia exacta sin bloquear el resto de tu código, permitiendo, por ejemplo, leer un sensor al mismo tiempo.
  • Lectura de Sensores Periódica: Asegúrate de que un sensor se lea cada cierto intervalo de tiempo (ej., cada 50 ms) para mantener los datos actualizados de manera consistente, sin importar otras operaciones en el loop().
  • Control de Motores: Generar señales PWM de alta frecuencia para controlar la velocidad o posición de motores, donde la estabilidad de la señal es crítica.
  • Muestreo de Audio o Señales: Para aplicaciones que requieren el muestreo regular de una señal analógica a una tasa constante.
  • Gestión de Múltiples Tareas: Un Timer1 puede ser el corazón de un sistema donde varias tareas necesitan ser ejecutadas en intervalos de tiempo fijos, como un sistema operativo muy básico.

Comparativa: Timer1 vs. Otros Métodos de Temporización

Es importante entender por qué el Timer1 y las interrupciones de hardware son superiores a otros métodos para ciertas aplicaciones:

Método de TemporizaciónTipoPrecisiónBloqueanteVentajasDesventajas
delay()SoftwareBaja (milisegundos)Fácil de usar para pausas simples.Bloquea completamente el CPU; impreciso para tareas críticas.
millis()/micros()Software (basado en Timer0)Media (milisegundos/microsegundos)NoPermite lógica no bloqueante; útil para control de tiempo general.Requiere gestión manual del tiempo; puede haber "deriva" si el código es muy largo; no garantiza ejecución en un instante exacto.
Timer Hardware (IRQ)HardwareAlta (microsegundos)NoEjecución periódica y automática de código; muy preciso; ideal para tareas críticas de tiempo real; permite al CPU hacer otras cosas.Configuración inicial más compleja (aunque la librería la simplifica); ISRs deben ser muy cortas y eficientes.

Consideraciones y Mejores Prácticas

  • ISRs Cortas y Rápidas: Reitera la importancia de mantener el código dentro de timerIsr() lo más minimalista posible. Evita cualquier operación que pueda tomar un tiempo considerable.
  • Variables Volátiles: Si tu ISR modifica una variable que también se lee o escribe en el loop() principal, declárala con la palabra clave volatile (ej., volatile int contador;). Esto le dice al compilador que la variable puede cambiar en cualquier momento (fuera del flujo normal del programa) y que no debe optimizar su acceso.
  • Deshabilitar Interrupciones Temporalmente: Si necesitas realizar operaciones complejas con variables compartidas entre la ISR y el código principal, es una buena práctica deshabilitar las interrupciones brevemente (noInterrupts();) antes de acceder a la variable y volver a habilitarlas (interrupts();) después. Esto previene que una interrupción ocurra a mitad de una operación sobre la variable, lo que podría llevar a datos corruptos.
  • Depuración Limitada: No uses Serial.print() dentro de una ISR, ya que es una función que consume mucho tiempo y podría causar problemas. Para depurar, puedes alternar un pin digital y usar un osciloscopio o LED.

Preguntas Frecuentes (FAQ)

¿Cuál es la frecuencia máxima/mínima que puedo configurar con Timer1?

La frecuencia máxima y mínima depende de la frecuencia del reloj del microcontrolador (generalmente 16 MHz para la mayoría de Arduinos) y de los preescaladores disponibles. El Timer1 es de 16 bits, lo que significa que puede contar hasta 65535. Un período de 1 microsegundo es una frecuencia de 1 MHz. La librería TimerOne intentará encontrar el preescalador más cercano. Frecuencias muy altas (por encima de unos pocos cientos de KHz) pueden ser difíciles de lograr con precisión, y frecuencias muy bajas (períodos de muchos segundos) también pueden tener limitaciones o requerir cálculos más complejos.

¿Puedo tener múltiples temporizadores ejecutándose al mismo tiempo?

Sí, la mayoría de las placas Arduino (como el Uno) tienen múltiples temporizadores de hardware (Timer0, Timer1, Timer2). Cada uno puede configurarse de forma independiente para generar sus propias interrupciones a diferentes frecuencias. Sin embargo, Timer0 es utilizado por funciones de Arduino como millis() y micros(), por lo que es mejor evitar modificarlo directamente si dependes de esas funciones.

¿Qué sucede si mi ISR es demasiado larga o consume demasiado tiempo?

Si tu ISR es demasiado larga, puede tener varias consecuencias negativas: puede causar que se pierdan interrupciones subsiguientes (si el temporizador desborda de nuevo antes de que la ISR termine), puede afectar la estabilidad y el rendimiento de tu programa principal, e incluso puede causar reinicios inesperados del microcontrolador. La regla de oro es mantenerlas lo más cortas y rápidas posible.

¿Cómo calculo el período en microsegundos si quiero una frecuencia específica?

Para obtener el período en microsegundos a partir de una frecuencia deseada en Hertz (Hz), utiliza la fórmula: Período (µs) = 1,000,000 / Frecuencia (Hz). Por ejemplo, si quieres una interrupción cada 200 Hz, el período sería 1,000,000 / 200 = 5,000 µs.

¿Por qué mi período no es exactamente el que configuré?

La librería TimerOne utiliza los preescaladores disponibles del microcontrolador para ajustar la frecuencia. A veces, debido a las limitaciones de los valores discretos de los preescaladores, no es posible lograr una frecuencia o un período *exacto* al microsegundo. La librería elegirá la configuración más cercana posible. Para la mayoría de las aplicaciones, la pequeña diferencia es insignificante, pero para aplicaciones de muy alta precisión, esto debe tenerse en cuenta.

Dominar el uso del Timer1 y las interrupciones es un paso crucial para llevar tus proyectos Arduino a un nivel superior de sofisticación y eficiencia. Te permite crear sistemas que responden de manera precisa a eventos de tiempo, sin sacrificar la capacidad de realizar otras tareas concurrentemente. Al aplicar estos conocimientos, tus proyectos serán más robustos, reactivos y profesionales, abriendo la puerta a un sinfín de aplicaciones de tiempo real.

Si quieres conocer otros artículos parecidos a Control Preciso con Timer1 en Arduino puedes visitar la categoría Librerías.

Subir