¿Cómo creo una interrupción de temporizador con Arduino?

9

Estoy tratando de crear una interrupción de retraso de tiempo con Arduino. Me gustaría usar la función interrupts (), porque es una interrupción interna.

Ejemplo: Digamos que me gustaría encender y apagar una luz, solo con el momento de la interrupción.

Hay un código de muestra, pero usa interrupciones externas (attachInterrupt ()). Me gustaría seguir usando las interrupciones internas.

Aleatorio
fuente
2
Creo que el punto que también mostró Kortuk es que attachInterrupt es algo abstracto, no está adjuntando ningún componente externo :)
clabacchio
Este artículo puede ayudarte. engblaze.com/…
Seth Archer Brown

Respuestas:

10

El blog de Noah Stahl tiene un ejemplo de parpadeo de un LED con Timer2 . Con eso y la hoja de datos, debería poder adaptarla a la interrupción que quiera usar, es decir, la interrupción cuya función normal puede darse el lujo de abandonar o está dispuesto a modificar. Timer2 se usa generalmente para algunas funciones PWM.

Su ejemplo cita el ATmega2560; Puedo confirmar que también funciona con un ATmega328p. Mire alrededor de su sitio para obtener ejemplos más útiles de interrupción de Arduino.

Editar:

Aquí está mi versión ligeramente editada, principalmente en los comentarios, del código de Noah. Llame a Timer2init () desde la función setup () de Arduino después de inicializar cualquier estructura de datos o hardware relacionado, porque el tiempo y la interrupción comenzarán una vez que lo haga.

F / ex, lo usé para multiplexar una pantalla de 3 dígitos y 7 segmentos, así que antes de inicializar el temporizador, inicialicé los registros de E / S de la pantalla y borré los datos de la pantalla en el lugar donde el ISR lo buscará.

Hay una tabla en los comentarios de algunos datos de tiempo útiles de la hoja de datos y mis propios cálculos como referencia para configurar otro esquema de tiempo.

La macro ISR () se encarga de crear el código de entrada y salida de interrupción para un ISR en lugar de la entrada y salida de una función normal, y de vincularlo con el vector de interrupción adecuado. El resto de esa función es 1) el código que se ejecutará en cada interrupción, y 2) el código de código para restablecer el temporizador para la próxima interrupción.

Tal como está escrito, esto debería aparecer en un boceto .pde o .ino (o un archivo .cpp, si usa eclipse, f / ex). El boceto necesita #definir LEDPIN, y setup () necesita llamar a Timer2init (). La función de bucle puede estar vacía o no; el LED debería comenzar a parpadear en la descarga (bueno, literalmente, después de llamar a Timer2init ()).

/*
 * From sample interrupt code published by Noah Stahl on his blog, at:
 * http://arduinomega.blogspot.com/p/arduino-code.html
 * 
 */


/*** FUNC

Name:           Timer2init

Function:       Init timer 2 to interrupt periodically. Call this from 
                the Arduino setup() function.

Description:    The pre-scaler and the timer count divide the timer-counter
                clock frequency to give a timer overflow interrupt rate:

                Interrupt rate =  16MHz / (prescaler * (255 - TCNT2))

        TCCR2B[b2:0]   Prescaler    Freq [KHz], Period [usec] after prescale
          0x0            (TC stopped)     0         0
          0x1                1        16000.        0.0625
          0x2                8         2000.        0.500
          0x3               32          500.        2.000
          0x4               64          250.        4.000
          0x5              128          125.        8.000
          0x6              256           62.5      16.000
          0x7             1024           15.625    64.000


Parameters: void

Returns:    void

FUNC ***/

void Timer2init() {

    // Setup Timer2 overflow to fire every 8ms (125Hz)
    //   period [sec] = (1 / f_clock [sec]) * prescale * (255-count)
    //                  (1/16000000)  * 1024 * (255-130) = .008 sec


    TCCR2B = 0x00;        // Disable Timer2 while we set it up

    TCNT2  = 130;         // Reset Timer Count  (255-130) = execute ev 125-th T/C clock
    TIFR2  = 0x00;        // Timer2 INT Flag Reg: Clear Timer Overflow Flag
    TIMSK2 = 0x01;        // Timer2 INT Reg: Timer2 Overflow Interrupt Enable
    TCCR2A = 0x00;        // Timer2 Control Reg A: Wave Gen Mode normal
    TCCR2B = 0x07;        // Timer2 Control Reg B: Timer Prescaler set to 1024
}



/*** FUNC

Name:       Timer2 ISR

Function:   Handles the Timer2-overflow interrupt

Description:    Maintains the 7-segment display

Parameters: void

Returns:    void

FUNC ***/

ISR(TIMER2_OVF_vect) {
    static unsigned int led_state = 0; // LED state

    led_state = !led_state;         // toggles the LED state
    digitalWrite(TOGGLE_PIN, led_state);

    TCNT2 = 130;     // reset timer ct to 130 out of 255
    TIFR2 = 0x00;    // timer2 int flag reg: clear timer overflow flag
};
JRobert
fuente
(@Kortuk: El comentario al que hace referencia fue mi observación de varios comentaristas aquí y no estaba dirigido a usted personalmente y fue innecesario. Pido disculpas y lo eliminé.) Amplié mi respuesta como sugirió y espero que sea ahora no solo demostrativo, sino también instructivo. Incluye comentarios que he escrito en el código para mi propio uso (es decir: si puedo entenderlos dentro de 6 meses, alguien más también podrá hacerlo), así como algunas instrucciones de "cómo usar" en el responder. Gracias por sus sugerencias
JRobert
Tenga en cuenta que las preescalas de 32 y 128 no están disponibles para timer0 y timer1 (al menos con atmega328).
tuupola
Es bueno saberlo, gracias. Lo uso para Timer2 (hasta ahora) y es básicamente un drop-in.
JRobert
5

La función attachInterrupt () en realidad está adjuntando una interrupción a un cambio de estado externo en un pin, no tiene otras opciones.

En la misma página, las opciones de modo se enumeran como:

El modo define cuándo se debe activar la interrupción. Cuatro constantes están predefinidos como valores válidos:

  • BAJO para activar la interrupción siempre que el pin esté bajo,
  • CAMBIAR para activar la interrupción siempre que el pin cambie de valor
  • RISING para disparar cuando el pin va de bajo a alto,
  • FALLING para cuando el pin va de mayor a menor.

Lamento ser el portador de malas noticias, esa es una de las primeras cosas que busqué también.

Kortuk
fuente
Creo que quiere decir que quiere usar un temporizador interno, en lugar de un dispositivo externo ... pero no conozco muy bien a Arduino, así que no puedo decir si es posible
clabacchio
@clabacchio, estoy diciendo que la única opción es usar un disparador externo, no hay una función interna de temporizador.
Kortuk
Ah, bien :) pero al menos las tablas Arduino tienen temporizadores?
clabacchio
Sí, así es como logran cosas como el retraso.
Kortuk
1
@ icarus74 ATMega328 realmente tiene 3 temporizadores (uno es 16b y dos son 8b) pero todos son utilizados por Arduino. Uno se usa para funciones como delay () y millis () y los tres se usan para PWM (puede encontrar más información en la función 'init ()', archivo 'cableado.c' en Arduino IDE).
vasco
2

Este artículo sobre PWM aclarará muchas de sus dudas sobre el uso de los temporizadores Arduino. Hay dos temporizadores de 8 bits y un temporizador de 16 bits en Arduino. No hay una API de alto nivel para enganchar la función ISR directamente en los temporizadores, que se envía con el SDK de Arduino (es decir, como una biblioteca estándar), sino un método de nivel algo inferior para establecer Registros de funciones especiales y aritmética de bits / operaciones en ellos. Sin embargo, hay una biblioteca contribuida por el usuario llamada Timer one .

icarus74
fuente
En realidad, hay varias combinaciones diferentes de temporizadores posibles dependiendo de a qué Arduino se esté refiriendo. La respuesta es engañosa.
Aparentemente el
@SeeminglySo, ¿te gustaría elaborar? Si está hablando del hardware Arduino, tenga en cuenta que la respuesta está en el contexto de la pregunta y también el momento en que se hace la pregunta.
icarus74
El Arduino Mega (basado en ATmega1280) se lanzó el 26 de marzo de 2009, y el Mega 2560 (ATmega2560) se lanzó el 24 de septiembre de 2010, ambos mucho antes de que se hiciera esta pregunta. Ambos microcontroladores tienen más de 2x 8 bit y 1x 16 bit timer / counter especificados en la respuesta.
Aparentemente el
La mayoría de las interacciones que he visto hasta ahora tienen una referencia inequívoca a Arduino para referirse a Duemilanove o Uno, es decir, las placas basadas en la serie 328. Otras placas siempre han sido explícitamente calificadas por la serie de UP no. o Mega, Nano, Micro, etc. De todos modos, aceptaré humildemente la corrección. En este contexto, una aclaración es mejor.
icarus74
1

Arduino está utilizando los tres temporizadores en ATMega328. Timer1(16 bits) se utiliza para funciones como delay()y millis()y para la salida PWM en los pines 5 y 6. Los otros dos temporizadores, Timer0y Timer2se utilizan para la salida PWM en los pines 3, 9, 10, 11.

Por lo tanto, no hay una función Arduino para la interrupción del temporizador. Pero, hay una manera. Puede usar este código para habilitar la interrupción del temporizador en Timer2:

ISR(TIMER2_OVF_vect) {
  // Interrupt routine.
}

void setup() {
  // Enable Timer2 interrupt.
  TIMSK2 = (0<<OCIE2A) | (1<<TOIE2);
}

void loop() {
  // Your main loop.
}

Escribí este código sin probar, por lo que es posible que haya cometido un error. En ese caso, consulte la hoja de datos, p.156 .

Si desea cambiar la frecuencia del temporizador (preescaler), simplemente cambie el registro TCCR2A. Para obtener más información, consulte la hoja de datos de la página 153. ¡Pero si cambia la frecuencia del temporizador, también cambia la frecuencia de la señal PWM en dos pines de salida!

vasco
fuente
AFAIK en ATmega328 Timer0y Timer2son de 8 bits y solo Timer1es de 16 bits.
tuupola
No esto no es correcto. Es Timer0, no Timer1, que se usa para delay () y millis () y para la salida PWM en los pines 5 y 6. Timer0 es un temporizador de 8 bits. Ver, por ejemplo, temporizadores e interrupciones Arduino .
Peter Mortensen