Timer de Arduino: juega con la temporización en tus proyectos

Timer Arduino UNO

Hace un tiempo publicamos más información sobre la función millis() de Arduino, ahora nos adentraremos más a fondo en el Timer de Arduino, para comenzar a trabajar con esta característica para la característica, comprender cómo gestiona el tiempo esta placa con MCU, además de otras funciones más allá de millis().

¿Qué es el Timer de Arduino?

timer de Arduino

El Timer de Arduino, o temporizador, es una función implementada por hardware (en el microcontrolador, con ayuda del cristal de cuarzo que genera los pulsos de reloj y que marca el «ritmo», sin necesidad de hardware externo ni CIs 555) que permite controlar eventos temporales gracias a unos relojes internos. Por ejemplo, realizar que una tarea se realice a intervalos, realizar mediciones de tiempo precisas, etc., de forma independiente del código del sketch.

Como Arduino UNO tiene un chip MCU que trabaja a 16 Mhz, se podrían ejecutar 16.000.000 cada segundo. Las instrucciones necesitan X ciclos para ejecutarse, no todas se ejecutan en los mismos ciclos de reloj, por ejemplo, las de 16-bits necesitan más ciclos en esta arquitectura AVR.

Imagina que usas la función delay(), esto bloqueará la ejecución en la MCU de Arduino hasta que transcurra el tiempo especificado y luego continuará con el programa, pero con el timer no se bloqueará. Se estará temporizando a la vez que la MCU sigue ejecutando otras instrucciones de forma simultánea. Esa es la gran ventaja.

El timer está relacionado con las interrupciones de Arduino, ya que serán ejecutados a través de ellas para atender alguna tarea específica. Dicho de otro modo, el Timer de Arduino es una función que se dispara en un tiempo determinado, ejecutando una función de interrupción. Por eso es importante también conocer estas interrupciones.

Modos

El Timer de Arduino tiene 2 modos de funcionamiento, pudiéndolo usar en:

  • Señal PWM: se puede controlar los pines de Arduino (~).
  • CTC (Clear timer on compare match): contabiliza el tiempo dentro de un contador y cuando alcanza el valor especificado en un registro de los timers, se ejecuta la interrupción.

¿Cuántos temporizadores tiene? Tipos de Timers

Arduino UNO funciones millis

Existen 3 temporizadores en las placas Arduino UNO, aunque puede haber más en otras placas superiores:

  • Timer 0: de 8-bit, pueden contar desde 0 a 255 (256 valores posibles). Usado por funciones como delay(), millis(), y micros(). No se recomienda su modificación para no alterar los programas.
  • Timer 1: igual al Timer 0. Usado por la biblioteca Servo en UNO (Timer 5 para MEGA).
  • Timer 2: de 16-bit, pudiendo ir desde 0 a 65.525 (65.536 valores posibles). Usado para la función tone(), si no se usa, se puede usar de forma libre para tu aplicación.
  • Timer 3, 4, 5 (solo en Arduino MEGA): todos de 16-bits.

¿Cómo funciona el Timer de Arduino?

Timers, temporizadores

Para poder trabajar con un Timer de Arduino, es vital conocer cómo funciona todo esto a nivel electrónico en el MCU de esta placa de desarrollo:

  • Frecuencia de reloj: es la cantidad de ciclos por segundo que es capaz de desarrollar, en el caso de Arduino son 16 Mhz, o lo que es lo mismo, la señal de reloj oscila 16.000.000 veces en un segundo (ciclos).
  • Periodo: se representa con la T, y se mide en segundos, y es la inversa de los ciclos. Por ejemplo, T=1/C, lo que dejaría como resultado 1/16000000 = 0.0000000625, el tiempo que tardaría en completarse cada ciclo. Y la frecuencia es la inversa del periodo, por lo que f = 1/T.
  • Ciclo: es cada una de las repeticiones de la señal que se dan por unidad de tiempo. En Arduino serían 16M en un segundo. O lo que es lo mismo, en este caso, cuando pasan 16 millones de ciclos ha pasado un segundo. Por tanto, se puede decir que un ciclo tarda 625 ns.
  • Flanco de una señal: las señales de reloj son cuadradas, y los flancos pueden ser de subida o de bajada. Un flanco es la línea recta de la señal cuando cambia de:
    • 0 (bajo) a 1 (alto): flanco de subida.
    • 1 (alto) a 0 (bajo): flanco de bajada.

Los flancos son importantes porque los temporizadores de Arduino miden los ciclos a partir de los flancos de la señal. Así el contador va incrementándose con cada ciclo y cuando llega al valor del registro, se ejecuta la interrupción.

Por tanto, una vez se sabe esto, si se tiene 16 Mhz en la MCU de Arduino, y se emplea un Timer de 8-bit, se puede decir que se producirán las interrupciones cada 16 μs (256/16000000) o 4 ms para los de 16-bit (65536/16000000). Por tanto, si configuras el registro del contador de 16 bits al máximo, con el valor 65535, entonces se producirá la interrupción a los 4 ms para ejecutar la tarea que sea.

Cuando el contador llega al valor máximo posible, volverá a 0 nuevamente. Es decir, se produce un desbordamiento y volverá a contar desde el comienzo.

Para controlar la velocidad de incremento del temporizador también se puede usar un prescaler, que toma valores 1, 8, 64, 256 y 1024 y altera la temporización de esta forma:

Velocidad del temporizador (Hz) = Frecuencia de reloj de Arduino / Prescaler

Si es 1 el prescaler el controlador se incrementará a 16 Mhz, si es 8 a 2 Mhz, si es 64 a 250 kHz, y así sucesivamente. Recuerda que habrá un comparador de estado del contador del timer para ir comparando el valor del contador y el pre-escalador hasta que son iguales y entonces ejecuta una acción. Por tanto, la frecuencia de interrupción viene dada por la fórmula:

+1 es porque el registro del contador está indexado a 0, es decir, no comienza a contar en 1, sino en 0.

Velocidad de interrupción (Hz) = Frecuencia de reloj de Arduino / Prescaler · (valor del registro de comparador + 1)

Afortunadamente, no debemos modificar los registros de los Timers de Arduino, ya que se ocupará de ello las bibliotecas que usemos en el código. Pero si no se usan, sí que se deberían configurar.

Ejemplos en Arduino IDE

Arduino IDE, tipos de datos, programación

Para poder comprender todo esto un poco mejor, aquí muestro dos códigos de sketch para Arduino IDE con los que se puede llegar a experimentar el uso de los timers. El primero es un código que hará titilar un LED conectado al pin 8 de Arduino cada segundo:


#define ledPin 8
void setup()
{
  pinMode(ledPin, OUTPUT);
  // Configurar Timer1
  TCCR1A = 0;                        //Registro control A a 0, pines OC1A y OC1B deshabilitados
  TCCR1B = 0;                        //Limpia el registrador
  TCCR1B |= (1<<CS10)|(1 << CS12);   //Configura prescaler a 1024: CS12 = 1 y CS10 = 1
  TCNT1 = 0xC2F8;                    //Iniciar timer para desbordamiento a 1 segundo
                                     //65536-(16MHz/1024/1Hz - 1) = 49912 = 0xC2F8 en hexadecimal
  
  TIMSK1 |= (1 << TOIE1);           //Habilitar interrupción para Timer1
}
void loop()
{
}
ISR(TIMER1_OVF_vect)                              //Interrupción del TIMER1 
{
  TCNT1 = 0xC2F7;                                 // Reniciar Timer1
  digitalWrite(ledPin, digitalRead(ledPin) ^ 1); //Invierte el estado del LED
}

Programar el titileo o parpadeo del LED, como en el anterior caso a cada segundo, pero esta vez usando CTC, es decir, comparación:


#define ledPin 8
void setup()
{
  pinMode(ledPin, OUTPUT);
  
  // Configuración Timer1
  TCCR1A = 0;                //Registro de control A a 0
  TCCR1B = 0;                //Limpiar registro
  TCNT1  = 0;                //Inicializar el temporizador
  OCR1A = 0x3D08;            //Carga el valor del registro de comparación: 16MHz/1024/1Hz -1 = 15624 = 0X3D08
  TCCR1B |= (1 << WGM12)|(1<<CS10)|(1 << CS12);   //Modo CTC, prescaler de 1024: CS12 = 1 y CS10 = 1  
  TIMSK1 |= (1 << OCIE1A);  //Habilita interrupción por igualdad de comparación
}
void loop()
{
}
ISR(TIMER1_COMPA_vect)          //Interrupción por igualdad de comparación en TIMER1
{
  digitalWrite(ledPin, digitalRead(ledPin) ^ 1);   //Invierte el estado del LED
}

Más sobre la programación de Arduino

Comprar una placa Arduino UNO Rev3

Sé el primero en comentar

Deja tu comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

*

*

  1. Responsable de los datos: Miguel Ángel Gatón
  2. Finalidad de los datos: Controlar el SPAM, gestión de comentarios.
  3. Legitimación: Tu consentimiento
  4. Comunicación de los datos: No se comunicarán los datos a terceros salvo por obligación legal.
  5. Almacenamiento de los datos: Base de datos alojada en Occentus Networks (UE)
  6. Derechos: En cualquier momento puedes limitar, recuperar y borrar tu información.