Timer en Arduino – Temporizador
Es indudable que realizar una medición de tiempo precisa en un proyecto autónomo es de suma importancia por lo que todo aquel que esté aprendiendo a utilizar el Arduino, deberá entender como trabajar con los temporizadores o TIMERS del ARDUINO.
El Timer con Arduino está fuertemente relacionado con lo que vimos en la entrada pasada, de las Interrupciones con Arduino (timer arduino interrupt), y esto es dado a que como ya lo anticipábamos en ese post, los timers serán ejecutados por medio de una interrupción para atender alguna tarea en especifica.
Si ya has usado la instrucción delay del Arduino sabes que dicha instrucción nos permite temporizar, sin embargo, el problema de esa función es que BLOQUEA el Arduino y debemos esperar hasta que el tiempo del delay transcurra para poder continuar con el programa y esto en muchos casos es INVIABLE.
La ventaja de el timer con Arduino es justamente esa, que podremos temporizar el tiempo, cuando simultáneamente estamos realizando otra tarea con la placa de desarrollo.
En resumen, el Timer del Arduino es un Temporizador que se programa en el Arduino que se dispara en un tiempo predeterminado. Y en cada momento que este temporizador se dispara, el Arduino ejecuta una función de Interrupción. Por ejemplo, se puede programar un timer que se dispare a cada segundo, o a cada 3 segundos y así sucesivamente, para que el Arduino cumpla una tarea específica.
Modos de Timers
Cuando hablamos de Timer en Arduino, podemos pensar en 2 modos de funcionamiento en los cuales se puede aplicar esta importante característica.
Entonces el timer lo podemos usar en:
1.Una señal PWM: Es la señal que podemos controlar en los pines del Arduino con el símbolo (~), la cual vamos a abordar en detalle en otra entrada del sitio web.
2.CTC (Clear timer on compare match): Esta es la opción que vamos a usar el día de hoy y simplemente como su nombre lo indica, el sistema contabiliza un tiempo dentro de un contador y cuando este contador alcanza el valor del registro de los timers, se ejecuta la interrupción del Arduino.
2.CTC (Clear timer on compare match): Esta es la opción que vamos a usar el día de hoy y simplemente como su nombre lo indica, el sistema contabiliza un tiempo dentro de un contador y cuando este contador alcanza el valor del registro de los timers, se ejecuta la interrupción del Arduino.
A continuación se muestra los modos que pueden ser configurados en los registros del microcontrolador para configurar el Timer en Arduino. Sin embargo, si tu vas a utilizar librerías para la configuración será mucho más facil de alplicarlo.
Todo estará basado en el datasheet del ATmega328p.
Tipos de Timers
Dado que el timer es un recurso del microcontrolador del Arduino podremos realizar el conteo de tiempo sin la necesidad de ningún hardware externo. O sea controlaremos el Time en Arduino (Tiempo en Arduino).
Cada placa de Arduino posee un cristal de cuarzo, por ejemplo, para el caso del Arduino UNO, MEGA, Pro Mini tenemos un cristal de cuarzo de 16MHz. Ese cristal es el encargado de darle los tiempos de ejecución (ticks) del programa interno del Arduino.
El Timer se vale de ese cristal, donde teóricamente podríamos ejecutar una interrupción cada 1/16.000.000 segundos o lo que se conoce como 1 tick. Cada instrucción consume “x” ciclos de reloj, entonces tenemos que dividir este ciclo por la frecuencia de dicho reloj. La mayoría de las instrucciones consumen un ciclo; por eso se considera que la arquitectura AVR da (casi) 1MIPS por Mhz.
Sólo los saltos y algunas instrucciones de 16 bits necesitan más ciclos; eso es algo que pasa en todas las arquitecturas
Sin embargo, para ejecutar las instrucciones propias del Arduino, muchas veces consumimos más de 1 tick. Los Timers disponibles en Arduino poseen un registro el cual nos dice cuántos ticks son necesarios para disparar el timer.
Dependiendo de la placa de Arduino que poseas, tendrás disponible para tus proyectos los siguientes Timers
Timer 0 Arduino
Es de 8bits. Usado para las funciones delay(), millis(), micros(). Es conveniente NO modificar este TIMER para evitar alterar estas funciones que son muy comunes en nuestros códigos
Timer 1 Arduino
Es de 16 bits. Usado por la librería Servo en Arduino Uno (Timer5 para Arduino Mega). Se usa principalmente para el control de Servos, sin embargo, si no estas controlado servos, puedes usar este timer 1 arduino libremente.
Timer 2 Arduino
Es de 8 bits. Usado en la función tone(). Si no usas este módulo, puedes emplear el timer 2 Arduino libremente para temporizar otra aplicación.
Timer 3, 4, 5 Arduino Mega
solo disponibles en los Mega. Todos de 16bits. Muy utilizados principalmente para el control de Servos, sin embargo también eres libre de usarlos como te convenga.
Prescaler y Registros de Comparación
Como ya lo detallamos anteriormente por causa del cristal de 16MHz los temporizadores pueden incrementar sus contadores a 16MHz, cada tick del contador representa 1 / 16,000,000 de segundo (~ 63ns), por lo que un contador, por ejemplo, tardará un incremento de10 en 625ns (muy rápido)
Vemos que la velocidad del contador a 16MHz es demasiado rápida. Y dado que el Timer0 y el timer2 son temporizadores de 8 bits, significa que pueden almacenar un valor de contador máximo de 255. El Timer1 es un temporizador de 16 bits, lo que significa que puede almacenar un valor de contador máximo de 65535. Una vez que un contador alcanza su máximo valor, este volverá a cero. (Esto se llama desbordamiento).
Esto significa que a 16MHz, incluso si configuramos el registro de comparación de comparación con el valor máximo del contador, se producirán interrupciones cada 256 / 16,000,000 segundos (~ 16us) para los contadores de 8 bits, y cada 65,536 / 16,000,000 (~ 4 ms) segundos para el Contador de 16 bits. Claramente, esto continua siendo muy rápido, y estar interrumpiendo el programa constantemente no tiene mucho sentido.
Para controlar la velocidad del incremento del temporizador utilizamos el prescaler el cual establece la veloidad de temporización con la siguiente ecuación:
(velocidad del temporizador (Hz)) = (velocidad del reloj Arduino (16MHz)) / prescaler
Por lo tanto, un prescaler de 1 incrementará el contador a 16MHz, un preescaler de 8 lo incrementará a 2MHz, un preescaler de 64 = 250kHz, y así sucesivamente.
El prescaler puede ser igual a 1, 8, 64, 256 y 1024.
La frecuencia de interrupción viene dado por la siguiente ecuación:
frecuencia de interrupción (Hz) = (velocidad de reloj Arduino 16,000,000Hz) / (preescaler * (registro de comparación + 1))
el +1 está ahí porque el registro de comparación está indexado a cero
Al reorganizar la ecuación anterior, se puede resolver el valor de registro de comparación que le dará la frecuencia de interrupción deseada:
Registro de comparación = [16,000,000Hz / (preescalador * frecuencia de interrupción deseada)] – 1
recuerde que cuando usa los temporizadores 0 y 2, este número debe ser menor que 256 y menor que 65536 para el temporizador1
así que si deseas una interrupción a cada segundo (frecuencia de 1Hz):
Registro de comparación = [16,000,000 / (preescaler * 1)] -1
con un prescaler de 1024 obtienes:
Registro de comparación = [16,000,000 / (1024 * 1)] -1= 15,624
desde 256 <15,624 <65,536, debe usar el TIMER1 para esta interrupción.
Registros del TIMER
Internamente, el microcontrolador del Arduino es manejado por diferentes registros. La gran ventaja de Arduino es que muchas veces nosotros como usuarios, no tenemos que entrar en detalle como configurar estos registros internos, ya que la propia interfaz de Arduino hace toda la configuración por nosotros. Sin embargo, en aplicaciones como el Timer, si no empleamos librerías, deberemos optar por modificar estos registros manualmente. Los registros de temporizador más importantes son:
Comparadores
Utilizados para comparar el estado del contador del timer, con el resultado de la configuración establecida mediante el pre-escalador (si es que lo usamos…) con el fin de ejecutar una acción si la comparación resulta en una igualdad.
Configuración de timers Arduino
Comenzaremos estableciendo el modo CTC para poder activar las interrupciones cada que ocurra un desbordamiento del TIMER. Esto sucede cuando el registro contador (TCNTn) coincida con el valor del registro de comparación de salida (OCRnA/B).
Los registros deben ser vistos en el propio datasheet del microcontrolador que estamos usando, por eso deben tener cuidado y ver cada hoja de datos. La ventaja que nos da Arduino IDE es que nos entrega ya definidos los registros correspondientes a nuestra placa.
La configuración de los timers de Arduino se hacen dentro de setup() del Sketch siempre desactivando las interrupciones antes de comenzar a configurar el/los timer/s y re-activarlas al final.
La configuración del código de la interrupción se predefine con la función:
ISR(interrupcion) {
// Su codigo va aqui
}
y la interrupción la configuraremos usando las constantes pre-configuradas para cada placa que para nuestro caso será TIMERn_COMPA_vect
Finalmente, los registros que usaremos son:
TCCRnA/B
Registros de control para poder configurar el timer en modo CTC y el pre-escalador.
Timer0/2 (Arduino UNO y Mega 2560)
- TCCRnA debe estar todo en 0 excepto por el bit WGM01 que debe estar en 1 para configurar el modo CTC.
Timer1 (Arduino UNO y Mega 2560), Timer3/4/5 (Arduino Mega 2560)
- TCCRnA debe estar todo en 0 para nuestro uso
- TCCRnB debe estar todo en 0 excepto por el bit WGMn2 que debe estar en 1 para configurar el modo CTC y los bits del pre-escalador que necesitemos si es que usaremos uno
TCNTn
Registro contador. Por lo general lo inicializaremos en 0 al comenzar.
OCRnA
Registro comparador de salida. Luego de definir nuestro pre-escalador, este registro lo usaremos con un valor específico para que las interrupciones ocurran cuando el valor del contador (TCNTn) coincida con éste registro.
TIMSKn
Registro de configuración de interrupción. En nuestro caso lo usaremos con todos los bits en 0 menos el bit OCIEnA que deberá estar en 1 para indicarle a nuestro timer que usaremos el registro OCRnA para comparar
Modo normal con interrupción por timer – desbordamiento
El siguiente ejemplo muestra cómo parpadear el LED en el pin 13 de la placa Arduino UNO a intervalos de 1 segundo y usando la interrupción de desbordamiento del temporizador:
#define ledPin 13
void setup()
{
pinMode(ledPin, OUTPUT);
// Configuração do timer1
TCCR1A = 0; // El registro de control A queda todo en 0, pines OC1A y OC1B deshabilitados
TCCR1B = 0; //limpia el registrador
TCCR1B |= (1<<CS10)|(1 << CS12); // configura prescaler para 1024: CS12 = 1 e CS10 = 1
TCNT1 = 0xC2F7; // inicia timer para desbordamiento 1 segundo
// 65536-(16MHz/1024/1Hz) = 49911 = 0xC2F7
TIMSK1 |= (1 << TOIE1); // habilita la interrupcion del TIMER1
}
void loop()
{
//loop principal.
}
ISR(TIMER1_OVF_vect) //interrupcion del TIMER1
{
TCNT1 = 0xC2F7; // Renicia TIMER
digitalWrite(ledPin, digitalRead(ledPin) ^ 1); //invierte el estado do led
}
En este ejemplo, el temporizador se configuró en modo normal, con los pines OC1A y OC1B desconectados (TCCR1A = 0). Se seleccionó Prescaler de 1024 a través del registro TCCR1B. Para que el temporizador se desborde cada segundo, es necesario comenzar su valor con la diferencia entre su valor máximo (65536) y el período deseado. El período se calcula teniendo en cuenta la frecuencia del oscilador y el prescaler seleccionado, además de la frecuencia de interrupción deseada. Finalmente, la interrupción del TIMER1 se habilitó a través del bit T0IE1 del registro TIMSK1. El parpadeo del LED se realiza en la rutina de interrupción donde es necesario volver a cargar el temporizador para el conteo correcto.
#define ledPin 13
void setup()
{
pinMode(ledPin, OUTPUT);
// Configuração do TIMER1
TCCR1A = 0; // El registro de control A queda todo en 0
TCCR1B = 0; //limpia registrador
TCNT1 = 0; //Inicializa el temporizador
OCR1A = 0x3D09; // carga el registrador de comparación: 16MHz/1024/1Hz = 15625 = 0X3D09
TCCR1B |= (1 << WGM12)|(1<<CS10)|(1 << CS12); // modo CTC, prescaler de 1024: CS12 = 1 e CS10 = 1
TIMSK1 |= (1 << OCIE1A); // habilita interrupción por igualdade de comparación
}
void loop()
{
//loop principal.
}
ISR(TIMER1_COMPA_vect) // interrupción por igualdade de comparación en TIMER1
{
digitalWrite(ledPin, digitalRead(ledPin) ^ 1); //invierte estado del LED
}
En este ejemplo, se utiliza el modo CTC, por lo que el valor del conteo del temporizador se compara constantemente con el registro OCR1A. Para el funcionamiento, se seleccionó el modo CTC, el bit WGM12 = 1 y el prescaler establecido en 1024. El valor de comparación se cargó en el registro OCR1A y finalmente se habilitó la interrupción de comparación. El LED se invierte en la rutina de interrupción, aquí no es necesario restablecer el temporizador con un valor, porque el modo CTC restablece el temporizador cuando se alcanza el valor de comparación.
La librería de timerone Arduino puede ser descargada directamente del repositorio de Arduino junto con su documentación (Arduino Timer Library). Para eso abra el Sketch del Arduino, y en la propia pestaña Sketch (parte superior) diríjase a “Include Library” y luego a “manage Libraries”, y aquí busque por TimerOne e instale.
Actualmente Arduino NO tiene una librería OFICIAL para el manejo de TIMERs en Arduino debido a que estos TIMERs son usados por funciones base del arduino, como el delay, millis, micros, servo, etc. Tal y como lo detallamos en el apartado anterior. Por lo tanto, si comenzamos a modificar el timer 1 de Arduino, no podremos usar la librería Servo, pues esta presentaría inconsistencias.
#include<TimerOne.h>
Esto va a crear automáticamente dentro del código un objeto llamado Timer1, el cual procedemos a configurar con las siguientes instrucciones.
Timer1.initialize(period)
La instrucción Timer1.inizialize de Arduino inicializa el timer con el valor de period, este valor es el tiempo en el que queremos que se dispare el temporizador y debe ser escrito en microsegundos.
Timer1.attachInterrupt( ISR_Callback) ;
Activa la función especifica que será ejecutada como interrupción, cada que el TIMER se dispare. Similar a como lo vimos en la entrada pasada.
Timer1.detachInterrupt( ISR_Callback) ;
Desactiva la función de interrupción que se ejecuta cada que el TIMER se dispara.
Timer1.read() ;
En este ejemplo veremos un temporizador Arduino con LCD
void setup()
{
pinMode(ledPin, OUTPUT);
// Configuração do timer1
TCCR1A = 0; // El registro de control A queda todo en 0, pines OC1A y OC1B deshabilitados
TCCR1B = 0; //limpia el registrador
TCCR1B |= (1<<CS10)|(1 << CS12); // configura prescaler para 1024: CS12 = 1 e CS10 = 1
TCNT1 = 0xC2F7; // inicia timer para desbordamiento 1 segundo
// 65536-(16MHz/1024/1Hz) = 49911 = 0xC2F7
TIMSK1 |= (1 << TOIE1); // habilita la interrupcion del TIMER1
}
void loop()
{
//loop principal.
}
ISR(TIMER1_OVF_vect) //interrupcion del TIMER1
{
TCNT1 = 0xC2F7; // Renicia TIMER
digitalWrite(ledPin, digitalRead(ledPin) ^ 1); //invierte el estado do led
}
En este ejemplo, el temporizador se configuró en modo normal, con los pines OC1A y OC1B desconectados (TCCR1A = 0). Se seleccionó Prescaler de 1024 a través del registro TCCR1B. Para que el temporizador se desborde cada segundo, es necesario comenzar su valor con la diferencia entre su valor máximo (65536) y el período deseado. El período se calcula teniendo en cuenta la frecuencia del oscilador y el prescaler seleccionado, además de la frecuencia de interrupción deseada. Finalmente, la interrupción del TIMER1 se habilitó a través del bit T0IE1 del registro TIMSK1. El parpadeo del LED se realiza en la rutina de interrupción donde es necesario volver a cargar el temporizador para el conteo correcto.
Interrupción del modo CTC en comparación
El siguiente ejemplo muestra cómo parpadear el LED usando el modo CTC y generando interrupción en comparación:
void setup()
{
pinMode(ledPin, OUTPUT);
// Configuração do TIMER1
TCCR1A = 0; // El registro de control A queda todo en 0
TCCR1B = 0; //limpia registrador
TCNT1 = 0; //Inicializa el temporizador
OCR1A = 0x3D09; // carga el registrador de comparación: 16MHz/1024/1Hz = 15625 = 0X3D09
TCCR1B |= (1 << WGM12)|(1<<CS10)|(1 << CS12); // modo CTC, prescaler de 1024: CS12 = 1 e CS10 = 1
TIMSK1 |= (1 << OCIE1A); // habilita interrupción por igualdade de comparación
}
void loop()
{
//loop principal.
}
ISR(TIMER1_COMPA_vect) // interrupción por igualdade de comparación en TIMER1
{
digitalWrite(ledPin, digitalRead(ledPin) ^ 1); //invierte estado del LED
}
En este ejemplo, se utiliza el modo CTC, por lo que el valor del conteo del temporizador se compara constantemente con el registro OCR1A. Para el funcionamiento, se seleccionó el modo CTC, el bit WGM12 = 1 y el prescaler establecido en 1024. El valor de comparación se cargó en el registro OCR1A y finalmente se habilitó la interrupción de comparación. El LED se invierte en la rutina de interrupción, aquí no es necesario restablecer el temporizador con un valor, porque el modo CTC restablece el temporizador cuando se alcanza el valor de comparación.
La librería de timerone Arduino puede ser descargada directamente del repositorio de Arduino junto con su documentación (Arduino Timer Library). Para eso abra el Sketch del Arduino, y en la propia pestaña Sketch (parte superior) diríjase a “Include Library” y luego a “manage Libraries”, y aquí busque por TimerOne e instale.
Actualmente Arduino NO tiene una librería OFICIAL para el manejo de TIMERs en Arduino debido a que estos TIMERs son usados por funciones base del arduino, como el delay, millis, micros, servo, etc. Tal y como lo detallamos en el apartado anterior. Por lo tanto, si comenzamos a modificar el timer 1 de Arduino, no podremos usar la librería Servo, pues esta presentaría inconsistencias.
SINTAXIS
Veamos como utilizar esta libería para el uso primordial de temporización, en una entrada futura, la analizaremos para el manejo de la señal PWM con Arduino.
Para comenzar procedemos llamando la librería
Esto va a crear automáticamente dentro del código un objeto llamado Timer1, el cual procedemos a configurar con las siguientes instrucciones.
Timer1.initialize(period)
La instrucción Timer1.inizialize de Arduino inicializa el timer con el valor de period, este valor es el tiempo en el que queremos que se dispare el temporizador y debe ser escrito en microsegundos.
Timer1.attachInterrupt( ISR_Callback) ;
Activa la función especifica que será ejecutada como interrupción, cada que el TIMER se dispare. Similar a como lo vimos en la entrada pasada.
Timer1.detachInterrupt( ISR_Callback) ;
Desactiva la función de interrupción que se ejecuta cada que el TIMER se dispara.
Timer1.read() ;
Lee el tiempo transcurrido desde la última transferencia en microsegundos.
A continuación veremos ejemplos del timerone arduino.
TimerOne Arduino Ejemplo
Una de las aplicaciones a ser utilizadas con los timers es realizar con Arduino un Temporizador o un contador de tiempo. Por lo tanto en el siguiente ejemplo haremos un temporizador con Arduino usando la librería timerone. El resultado lo visualizaremos en un LCD 20×4 con Arduino.
En el siguiente ejemplo podremos configurar bien sea el timer arduino uno, timer arduino nano, timer arduino mega o como en mi caso el timer arduino leonardo. Va a funcionar igualmente.
#define LED 13 //Declara el PIN 13 como LED
//Llama las librerías
#include <LiquidCrystal.h>
#include <TimerOne.h>
//Variable para la interrupción
volatile long int Time=0;
//Configuración del LCD
LiquidCrystal lcd(9, 8, 5, 4, 3, 2); //(RS, E, D4,D5, D6, D7)
void setup()
{
pinMode(LED,OUTPUT); //LED como Saída
lcd.begin(20, 4); // Inicia el LCD 20x04 (columnas, filas)
lcd.setCursor(2, 0); // Coloca el cursor en las coordenadas (2,0)
lcd.print("TIMER COM ARDUINO"); // Escribe no LCD
lcd.setCursor(0, 1); // Coloca el cursor en las coordenadas (0,1)
lcd.print("Temporizador:"); // Escribe en el LCD
Timer1.initialize(1000000); //Configura el TIMER en 1 Segundo
Timer1.attachInterrupt(Temporizador) ; //Configura la interrupción del Timer 1
}
void loop() {
// put your main code here, to run repeatedly:
digitalWrite(LED,HIGH);
delay(2000);
digitalWrite(LED,LOW);
delay(2000);
}
//Función de la Interrupción cuando se ejecuta el TIMER
void Temporizador(void)
{
//Incrementa el timer
Time++;
//Resetea el contador cuando llega a 1000 segundos
if(Time>1000){
Time=0;
}
//Muestra en el LCD el valor actual del temporizador
lcd.setCursor(14, 2);
lcd.print(" ");
lcd.setCursor(14, 2);
lcd.print(Time);
}
No hay comentarios:
Publicar un comentario