Ya hemos considerado el diseño del temporizador Arduino muchas veces, millis (), que le permite ajustar la lógica del código mediante temporizadores. La desventaja de este método es la necesidad de sondear constantemente la construcción del temporizador para comprobar si ha funcionado. En consecuencia, el código en el bucle principal debe ser «transparente«, es decir, no debe contener retrasos ni bucles cerrados largos, simplemente código. Esto no es tan crítico para las temporizaciones con un período largo (minutos, muchos segundos), pero para realizar acciones con una frecuencia alta y estrictamente especificada cualquier pequeño retraso en el bucle principal puede ser un gran problema. Una forma de salir de la situación puede ser con una interrupción del temporizador. Recuerde la lección sobre interrupciones de hardware: una interrupción le permite «salir» de cualquier fragmento de código que se esté ejecutando actualmente en el bucle principal de Arduino, ejecutar el bloque de código deseado que se encuentra dentro de la interrupción y volver a donde lo dejó y continuar con la ejecución. Por tanto, es prácticamente una ejecución paralela de tareas. En este tutorial, aprenderemos cómo hacer esto usando el temporizador de hardware.
¿Por qué utilizar interrupciones de temporizador?
- Generación de señales.
- Medida de tiempo.
- Ejecución paralela de tareas.
- Completar una tarea después de un período de tiempo estrictamente especificado.
- Y mucho más…
Temporizadores.
Las interrupciones en Arduino son generadas por un temporizador de hardware separado, que se encuentra en Arduino en algún lugar cerca del núcleo. El temporizador de hardware, también conocido como contador, realiza una tarea muy simple: cuenta los «tics» del generador de reloj (que establece la frecuencia de todo el sistema) y, dependiendo del modo de funcionamiento, puede solo reiniciarse o enviar una señal al microcontrolador en ciertos valores de contador. Por lo tanto, la «resolución» del temporizador es un tic (reloj) del oscilador maestro, a 16 MHz es 0.0625 microsegundos. El segundo punto importante a entender es el siguiente: el temporizador-contador funciona y cuenta los pulsos en paralelo al núcleo computacional. Es por eso que la generación de una señal PWM, incluso a alta frecuencia, no tiene absolutamente ningún efecto en la ejecución del código, todo sucede en paralelo.
En arduino nano (atmega328) tenemos tres de estos temporizadores, y cada uno puede activar una interrupción independiente según su período. El contador de tiempo en las funciones millis () y micros () funcionan simplemente interrumpiendo el temporizador 0. Si ponemos a cero el temporizador 0, perderemos la cuenta de tiempo correcta (y, posiblemente, PWM en los pines 5 y 6). Algunas bibliotecas también usan interrupciones de temporizador, por ejemplo, Servo usa el primero y la función incorporada tone ()– el segundo. También discutimos en las lecciones que los temporizadores son los que están involucrados en generar una señal PWM en sus pines, y cuando el temporizador se reconfigura, el PWM suele apagarse, comenzar a trabajar en un modo diferente o cambiar la frecuencia PWM.
A diferencia de la generación de señales PWM y las interrupciones Arduino de hardware, los desarrolladores de Arduino no implementan el control de interrupciones del temporizador en el kernel y las bibliotecas estándar, por lo que trabajaremos con interrupciones utilizando bibliotecas de terceros. Puede trabajar con el temporizador directamente, como se describe en la hoja de datos (datasheet), pero esto no está incluido en este curso. Para el primer y segundo temporizador, se pueden encontrar bibliotecas antiguas llamadas timerOne y timerTwo. Pero usaremos la biblioteca: GyverTimers, que permite la configuración flexible de todos los temporizadores en atmega328 (Arduino UNO / Nano) y atmega2560 (Arduino Mega). Puede descargar la biblioteca a través de un enlace directo. Consideremos las principales herramientas de la biblioteca.
Biblioteca GyverTimers.
La biblioteca GyverTimers le permite generar interrupciones de temporizador con una frecuencia específica en el canal de temporizador seleccionado o en varios canales a la vez con un cambio de fase: las interrupciones ocurrirán con la misma frecuencia, pero se “desplazarán” entre sí. También puede configurar la acción para mostrar el temporizador: habilitar / deshabilitar / alternar: el temporizador controlará el pin seleccionado independientemente del núcleo computacional de Arduino, por lo que puede generar una onda cuadrada (señal cuadrada), tanto de ciclo único como de dos y tres tiempos con cambio de fase ajustable.
Hay tres temporizadores disponibles para Arduino Nano / UNO / Pro Mini: Timer0 , Timer1 , Timer2. Para Arduino MEGA – seis: Timer0 , Timer1 , Timer2 , Timer3, Timer4 , Timer5 . En la biblioteca, los temporizadores se describen como objetos, el acceso se produce como de costumbre a través de un punto, por ejemplo Timer1.stop();
Tabla de temporizador ATmega328p
Temporizador | Profundidad de bits | Frecuencia | Periodo | Salidas | Pin de Arduino | Pin del Mc |
Timer0 | 8 bits | 61 Hz .. 1 MHz | 16 384 .. 1 μs | CHANNEL_A | D6 | PD6 |
CHANNEL_B | D5 | PD5 | ||||
Timer1 | 16 bits | 0,24 Hz .. 1 MHz | 4200 000 .. 1 μs | CHANNEL_A | D9 | PB1 |
CHANNEL_B | D10 | PB2 | ||||
Timer2 | 8 bits | 61 Hz .. 1 MHz | 16 384 .. 1 μs | CHANNEL_A | D11 | PB3 |
CHANNEL_B | D3 | PD3 |
Tabla de temporizador ATmega2560
Temporizador | Profundidad de bits | Frecuencia | Periodo | Salidas | Pin de Arduino | Pin Mc |
Timer0 | 8 bits | 61 Hz .. 1 MHz | 16 384 .. 1 μs | CHANNEL_A | 13 | PB7 |
CHANNEL_B | 4 | PG5 | ||||
Timer1 | 16 bits | 0,24 Hz .. 1 MHz | 4200 000 .. 1 μs | CHANNEL_A | 11 | PB5 |
CHANNEL_B | 12 | PB6 | ||||
CHANNEL_C | 13 | PB7 | ||||
Timer2 | 8 bits | 61 Hz .. 1 MHz | 16 384 .. 1 μs | CHANNEL_A | 10 | PB4 |
CHANNEL_B | 9 | PH6 | ||||
Timer3 | 16 bits | 0,24 Hz .. 1 MHz | 4200 000 .. 1 μs | CHANNEL_A | 5 | PE3 |
CHANNEL_B | 2 | PE4 | ||||
CHANNEL_C | 3 | PE5 | ||||
Timer4 | 16 bits | 0,24 Hz .. 1 MHz | 4200 000 .. 1 μs | CHANNEL_A | 6 | PH3 |
CHANNEL_B | 7 | PH4 | ||||
CHANNEL_C | 8 | PH5 | ||||
Timer5 | 16 bits | 0,24 Hz .. 1 MHz | 4200 000 .. 1 μs | CANAL_A | 46 | PL3 |
CHANNEL_B | 45 | PL4 | ||||
CHANNEL_C | 44 | PL5 |
Periodo máximo
La tabla anterior muestra los rangos para el reloj de 16 MHz estándar de Arduino. Para otro reloj del sistema, el período máximo se calcula mediante la fórmula, donde F_CPU es la frecuencia del sistema en Hz:
- Temporizadores de 8 bits: (1000000UL / F_CPU) * (1024 * 256)
- Temporizadores de 16 bits: (1000000UL / F_CPU) * (1024 * 65536)
Ajuste de frecuencia / período
setPeriod (período);- establece el período en microsegundos y pone en marcha el temporizador. Devuelve el período real en μs (precisión limitada por la resolución del temporizador).
setFrequency (frecuencia);- establece la frecuencia en hercios y pone en marcha el temporizador. Devuelve la frecuencia real en Hz (precisión limitada por la resolución del temporizador).
setFrequencyFloat ( frecuencia Float);- configurando la frecuencia en Hertz e iniciando el temporizador, se permiten fracciones decimales. Devuelve la frecuencia real (precisión limitada por la resolución del temporizador).
Control del temporizador
- pause () ;- pausar el conteo del temporizador sin reiniciar el contador.
- resume () ; – seguir contando después de una pausa.
- stop () ;- dejar de contar y poner a cero el contador.
- restart () ;- reiniciar el temporizador (reinicia el contador)
Interrupciones
- enableISR (canal, fase );- iniciar interrupciones en el canal seleccionado con el cambio de fase seleccionado. Si no se especifica nada, se seleccionarán el canal A y la fase 0.
- Canal – CHANNEL_A, CHANNEL_B o CHANNEL_C (¡vea la tabla de arriba!)
- Fase – valor numérico 0-359.
- disableISR ( canal ) ;- deshabilitar interrupciones en el canal seleccionado. Si no se especifica nada, se seleccionará el CHANNEL_A.
La biblioteca proporciona acceso directo a la interrupción sin el «Arduino» attachInterrupt, lo que reduce el tiempo necesario para llamar a la función del controlador de interrupciones. Una interrupción con una frecuencia configurada se procesará en un bloque de la forma ISR ( canal ) {}, ejemplo:
ISR ( TIMER1_A ) { // tu codigo } ISR ( TIMER1_B ) { // tu codigo } ISR ( TIMER2_B ) { // tu codigo } ISR ( TIMER0_A ) { // tu codigo }
Salidas de hardware
- outputEnable ( canal, modo ) ; – habilitar el control de la salida de hardware del temporizador
- Canal: CHANNEL_A o CHANNEL_B (y CHANNEL_C para ATmega2560, consulte la tabla de temporizadores).
- Modo: TOGGLE_PIN, CLEAR_PIN, SET_PIN (alternar / deshabilitar, valor Bajo / habilitar pin por temporizador, valor Alto)
- outputDisable ( canal ) ; – deshabilitar la salida del temporizador
- Canal: CHANNEL_A o CHANNEL_B (y CHANNEL_C para Mega2560, consulte la tabla de temporizadores)
- outputState ( canal, estado ) ;- cambiar manualmente el estado del canal. Por ejemplo, establecer canales en diferentes estados para comenzar a generar una onda push-pull.
- Canal: CHANNEL_A o CHANNEL_B (y CHANNEL_C para ATmega2560, consulte la tabla de temporizadores).
- Condición: ALTO o BAJO
Importante: al generar una onda cuadrada, la frecuencia real será la mitad de la especificada debido a las peculiaridades del propio temporizador. Vea ejemplos con ondas mas abajo.
Cambio de fase (desde 1.6)
Con ayuda PhaseShift ( fuente, ángulo ) puede cambiar las interrupciones o cambiar los pines en el canal seleccionado, fuente – en fase, ángulo – ángulo de cambio en grados de 0 a 360.
- Para los temporizadores de 8 bits, puede establecer el desplazamiento solo para el segundo canal (CHANNEL_B)
- Los de 16 bits pueden mover los tres canales.
Configuración predeterminada
Usando el método setDefault () puede restablecer la configuración del temporizador a los valores predeterminados de Arduino: frecuencia y modo de funcionamiento.
Ejemplo de todas las funciones de la biblioteca comentadas
// Demostración de todas las funciones de la biblioteca #include "GyverTimers.h" void setup() { // Reconfigure el temporizador y establezca un período o frecuencia // Todas las funciones devuelven un período / frecuencia real, que puede diferir de los ingresados Timer2.setPeriod(1000); // Establezca un período específico de 1000 μs (~ 1000 Hz), devolverá el período real en μs Timer0.setFrequency(250); // Establezca la frecuencia de interrupción del temporizador en Hz, devolverá la frecuencia real en hercios Timer1.setFrequencyFloat(50.20); // Establezca la frecuencia con mayor precisión, en números fraccionarios, relevante para bajas frecuencias y temporizador 1 // A partir de este momento, el temporizador ya se ha reconfigurado y funciona con la frecuencia / período seleccionado // Conecta una interrupción del temporizador, a partir de este momento se empezarán a llamar las interrupciones Timer0.enableISR(); // Conectar la interrupción estándar, canal A, sin desplazamiento de fase Timer2.enableISR(CHANNEL_B, 180); // Conecte el temporizador de interrupción 2, canal B, fase inicial - 180 grados Timer1.enableISR(CHANNEL_A, 60); // ¡Conecte el canal de interrupción A, configure la fase para el canal A disponible solo para el temporizador 1! Timer1.enableISR(CHANNEL_B, 120); // Conecta la segunda interrupción del temporizador 1 y establece el cambio de fase para esta secuencia // La interrupción ya comenzará a llamarse // Si de repente es necesario desactivar la interrupción sin detener el temporizador Timer1.disableISR(CHANNEL_B); // A partir de ahora, la interrupción B ya no se llamará // Si necesita suspender el temporizador COMPLETAMENTE, el hardware Timer2.pause(); // A partir de este momento, el temporizador está en su lugar, el contenido del contador permanece intacto // Ahora el temporizador puede volver al servicio Timer2.resume(); // El cronómetro sigue contando desde el mismo lugar // Si necesita detener completamente el temporizador y restablecer el contenido del contador Timer1.stop(); // El temporizador está encendido, el contador se reinicia // Devuelve el temporizador a cero Timer1.restart(); // El temporizador se reinicia, comienza a contar desde el principio // Si necesita devolver el Arduino estándar - configuración del temporizador Timer0.setDefault(); // Ahora el temporizador funciona en modo estándar } //vectores interrumpcion ISR ( TIMER1_A ) { } ISR ( TIMER1_B ) { } ISR ( TIMER2_B ) { } ISR ( TIMER0_A ) { } void loop() { }
Ejemplo sencillo con interrupciones
// Un ejemplo de una generación de interrupciones simple por un temporizador de hardware #include "GyverTimers.h" void setup() { Serial.begin(9600); Timer1.setFrequency(3); // Temporizador 1 de alta precisión para la primera interrupción, frecuencia - 3 Hertz //Timer1.setPeriod(333333); // ¡mismo! La frecuencia de 3 Hz es un período de 333 333 microsegundos //Timer1.setFrequencyFloat(4.22); // Si necesita una frecuencia fraccionaria en Hz Timer1.enableISR(); // Iniciar interrupción (canal A predeterminado) // inicia el segundo temporizador Timer2.setPeriod(1000000); // Establece el período del temporizador 1000000 μs -> 1 Hz Timer2.enableISR(CHANNEL_A); // O simplemente .enableISR (), activa una interrupción en el canal A del temporizador 2 pinMode(13, OUTPUT); // parpadeará led } void loop() {} // Interrumpcion en temporizador 1 ISR ( TIMER1_A ) { // escribir en la serie Serial.println("timer1"); } //Interrumpcion en temporizador 2 ISR ( TIMER2_A ) { // genera una onda cuadrada de 1 khz, parpadea digitalWrite ( 13 ,! digitalRead ( 13 )) ; //Serial.println("timer2 "); }
Interrupción sencilla en dos canales
// Un ejemplo de generación de interrupciones de dos canales en un temporizador con un período EQUAL, pero fuera de fase // dos flujos de interrupción desplazados 180 grados (inversión completa) #include "GyverTimers.h" void setup() { Serial.begin(9600); Serial.print("Real timer frequency is : "); // Muestra la frecuencia real, la real puede diferir de la dada (limitada por la resolución del temporizador) Serial.println(Timer1.setFrequencyFloat(2.50)); // La frecuencia de interrupción es de 2.5 Hz, use .setFrequency (...) para enteros delay(1000); Timer1.enableISR(CHANNEL_A, 0); // El primer canal es A, la fase inicial es 0 grados Timer1.enableISR(CHANNEL_B, 180); // El segundo canal es B, la fase inicial es de 180 grados } void loop() {} // dos interrupciones en un temporizador ISR ( TIMER1_A ) { De serie. println ( "¡Interrupción del canal A!" ) ; // Interrumpcion A } ISR ( TIMER1_B ) { De serie. println ( "¡Interrupción del canal B!" ) ; // Interrumpcion B }
Generación de una Onda
// Un ejemplo de generación de un onda en el temporizador 2, canal B (D3 en Arduino UNO) #include "GyverTimers.h" void setup() { pinMode(3, OUTPUT); // configurar pin como salida // debido a la peculiaridad de generar un meandro por el temporizador // ¡la frecuencia debe especificarse el doble de lo necesario! Timer2.setFrequency(500 * 2); // establece la frecuencia del temporizador en Hz Timer2.outputEnable(CHANNEL_B, TOGGLE_PIN); // en el momento en que se activa el temporizador, el pin cambiará } void loop() { }