Arduino interrupt timing



attachInterrupt()

Description

Digital Pins With Interrupts

The first parameter to attachInterrupt() is an interrupt number. Normally you should use digitalPinToInterrupt(pin) to translate the actual digital pin to the specific interrupt number. For example, if you connect to pin 3, use digitalPinToInterrupt(3) as the first parameter to attachInterrupt() .

Uno, Nano, Mini, other 328-based

Uno WiFi Rev.2, Nano Every

all digital pins

Mega, Mega2560, MegaADK

2, 3, 18, 19, 20, 21 (pins 20 & 21 are not available to use for interrupts while they are used for I2C communication)

Micro, Leonardo, other 32u4-based

all digital pins, except 4

MKR Family boards

0, 1, 4, 5, 6, 7, 8, 9, A1, A2

2, 3, 9, 10, 11, 13, A1, A5, A7

Nano 33 BLE, Nano 33 BLE Sense

all digital pins

all digital pins (Only pins 2, 5, 7, 8, 10, 11, 12, 13 work with CHANGE)

Notes and Warnings

Note
Inside the attached function, delay() won’t work and the value returned by millis() will not increment. Serial data received while in the function may be lost. You should declare as volatile any variables that you modify within the attached function. See the section on ISRs below for more information.

Using Interrupts

Interrupts are useful for making things happen automatically in microcontroller programs and can help solve timing problems. Good tasks for using an interrupt may include reading a rotary encoder, or monitoring user input.

If you wanted to ensure that a program always caught the pulses from a rotary encoder, so that it never misses a pulse, it would make it very tricky to write a program to do anything else, because the program would need to constantly poll the sensor lines for the encoder, in order to catch pulses when they occurred. Other sensors have a similar interface dynamic too, such as trying to read a sound sensor that is trying to catch a click, or an infrared slot sensor (photo-interrupter) trying to catch a coin drop. In all of these situations, using an interrupt can free the microcontroller to get some other work done while not missing the input.

About Interrupt Service Routines

ISRs are special kinds of functions that have some unique limitations most other functions do not have. An ISR cannot have any parameters, and they shouldn’t return anything.

Generally, an ISR should be as short and fast as possible. If your sketch uses multiple ISRs, only one can run at a time, other interrupts will be executed after the current one finishes in an order that depends on the priority they have. millis() relies on interrupts to count, so it will never increment inside an ISR. Since delay() requires interrupts to work, it will not work if called inside an ISR. micros() works initially but will start behaving erratically after 1-2 ms. delayMicroseconds() does not use any counter, so it will work as normal.

Typically global variables are used to pass data between an ISR and the main program. To make sure variables shared between an ISR and the main program are updated correctly, declare them as volatile .

For more information on interrupts, see Nick Gammon’s notes.

Syntax

attachInterrupt(digitalPinToInterrupt(pin), ISR, mode) (recommended)
attachInterrupt(interrupt, ISR, mode) (not recommended)
attachInterrupt(pin, ISR, mode) (Not recommended. Additionally, this syntax only works on Arduino SAMD Boards, Uno WiFi Rev2, Due, and 101.)

Parameters

interrupt : the number of the interrupt. Allowed data types: int .
pin : the Arduino pin number.
ISR : the ISR to call when the interrupt occurs; this function must take no parameters and return nothing. This function is sometimes referred to as an interrupt service routine.
mode : defines when the interrupt should be triggered. Four constants are predefined as valid values:

LOW to trigger the interrupt whenever the pin is low,

CHANGE to trigger the interrupt whenever the pin changes value

RISING to trigger when the pin goes from low to high,

FALLING for when the pin goes from high to low.

The Due, Zero and MKR1000 boards allow also:

HIGH to trigger the interrupt whenever the pin is high.

Источник

Arduino Timer and Interrupt Tutorial

Arduino Timer and Interrupt Tutorial

This tutorial shows the use of timers and interrupts for Arduino boards. As Arduino programmer you have probably used timers and interrupts without even knowing it’s there, because all the low level hardware stuff is hidden by the Arduino API. Many Arduino functions uses timers, for example the time functions: delay(), millis() and micros(), the PWM functions analogWrite(), the tone() and the noTone() function, even the Servo library uses timers and interrupts.

Buy the Arduino from: Banggood | Amazon

What is a timer?

A timer, A.K.A. counter is a piece of hardware built in the Arduino controller. It is like a clock, and can be used to measure time events.

The timer can be programmed by some special registers. You can configure the pre-scaler for the timer, or the mode of operation and many other things.

The Arduino board is based on the Atmel AVR ATmega168 or the ATmega328 microchip. These chips are pin compatible and only differ in the size of internal memory. Both have 3 timers, called Timer0, Timer1 and Timer2. Timer0 and Timer2 are 8bit timer, where Timer1 is a 16bit timer.

The most important difference between 8bit and 16bit timer is the timer resolution. 8bits means 256 values (two to the power of 8) where 16bit means 65536 values (two to the power of 16) which is much higher resolution.

The Arduino Mega series is based on the Atmel AVR ATmega1280 or the ATmega2560. They are almost identical to previous chips but only differs in memory size. These chips have 6 timers. First 3 timers (Timer 0, Timer1 and Timer2) are identical to the ATmega168/328. Timer3, Timer4 and Timer5 are all 16bit timers, similar to Timer1.

All timers depends on the system clock of your Arduino system. Normally the system clock is 16MHz, but the Arduino Pro 3/3V is 8Mhz, so be careful when writing your own timer functions.
The timer hardware can be configured with some special timer registers. In the Arduino firmware, all timers were configured to a 1kHz frequency and interrupts are generally enabled.

Clock select and timer frequency

Different clock sources can be selected for each timer independently. To calculate the timer frequency (for example 2Hz using Timer1) you will need:

1. CPU frequency 16Mhz for Arduino
2. maximum timer counter value (256 for 8bit, 65536 for 16bit timer)
3. Divide CPU frequency through the chosen pre-scaler (16000000 / 256 = 62500)
4. Divide result through the desired frequency (62500 / 2Hz = 31250)
5. Verify the result against the maximum timer counter value (31250 Interrupts must be generally enabled

  • the according Interrupt mask must be enabled
  • Interrupts can generally enabled or disabled with the function interrupts() or noInterrupts(). By default in the Arduino firmware interrupts are enabled. Interrupt masks are enabled / disabled by setting or clearing bits in the Interrupt mask register (TIMSKx).

    When an interrupt occurs, a flag in the interrupt flag register (TIFRx) is been set. This interrupt will be automatically cleared when entering the ISR or by manually clearing the bit in the interrupt flag register.

    The Arduino functions attachInterrupt() and detachInterrupt() can only be used for external interrupt pins. These are different interrupt sources, not discussed here.

    Timer interrupts

    A timer can generate different types of interrupts. The register and bit definitions can be found in the processor data sheet (Atmega328 or Atmega2560) and in the I/O definition header file (iomx8.h for Arduino, iomxx0_1.h for Arduino Mega in the hardware/tools/avr/include/avr folder). The suffix x stands for the timer number (0..5), the suffix y stands for the output number (A,B,C), for example TIMSK1 (Timer1 interrupt mask register) or OCR2A (Timer2 output compare register A).

    Timer Overflow:

    Timer overflow means the timer has reached is limit value. When a timer overflow interrupt occurs, the timer overflow bit TOVx will be set in the interrupt flag register TIFRx. When the timer overflow interrupt enable bit TOIEx in the interrupt mask register TIMSKx is set, the timer overflow interrupt service routine ISR(TIMERx_OVF_vect) will be called.

    Output Compare Match:

    When a output compare match interrupt occurs, the OCFxy flag will be set in the interrupt flag register TIFRx . When the output compare interrupt enable bit OCIExy in the interrupt mask register TIMSKx is set, the output compare match interrupt service ISR(TIMERx_COMPy_vect) routine will be called.

    Timer Input Capture:

    When a timer input capture interrupt occurs, the input capture flag bit ICFx will be set in the interrupt flag register TIFRx. When the input capture interrupt enable bit ICIEx in the interrupt mask register TIMSKx is set, the timer input capture interrupt service routine ISR(TIMERx_CAPT_vect) will be called.

    PWM and timer

    There is fixed relation between the timers and the PWM capable outputs. When you look in the data sheet or the pinout of the processor these PWM capable pins have names like OCRxA, OCRxB or OCRxC (where x means the timer number 0..5). The PWM functionality is often shared with other pin functionality.

    The Arduino has 3 timers and 6 PWM output pins. The relation between timers and PWM outputs is:

    • Pins 5 and 6: controlled by Timer0
    • Pins 9 and 10: controlled by Timer1
    • Pins 11 and 3: controlled by Timer2

    On the Arduino Mega we have 6 timers and 15 PWM outputs:

    • Pins 4 and 13: controlled by Timer0
    • Pins 11 and 12: controlled by Timer1
    • Pins 9 and10: controlled by Timer2
    • Pin 2, 3 and 5: controlled by timer 3
    • Pin 6, 7 and 8: controlled by timer 4
    • Pin 46, 45 and 44:: controlled by timer 5

    Usefull 3rd party libraries

    Some 3rd party libraries exists, that uses timers:

    • Ken Shirrifs IR library using Timer2 – Send and receive any kind of IR remote signals
    • Timer1 and Timer3 library using Timer1 or tiner3 – easy way to write your own timer interrupt service routines.

    Examples

    Blinking LED with compare match interrupt

    The first example uses the Timer1 in CTC mode and the compare match interrupt to toggle a LED. The timer is configured for a frequency of 2Hz. The LED is toggled in the interrupt service routine.

    /*
    * timer and interrupts
    * Timer1 compare match interrupt example
    * more infos: https://oscarliang.com
    */

    #define ledPin 13

    void setup()
    <
    pinMode(ledPin, OUTPUT);

    // initialize Timer1
    noInterrupts(); // disable all interrupts
    TCCR1A = 0;
    TCCR1B = 0;
    TCNT1 = 0;

    OCR1A = 31250; // compare match register 16MHz/256/2Hz
    TCCR1B |= (1 = 100
    #include “Arduino.h”
    #else
    #include “WConstants.h”
    #endif

    // Encoder Pins
    #define encLtA 2
    #define encLtB 3
    #define encRtA 11
    #define encRtB 12
    #define ledPin 13

    #define LT_PHASE_A digitalRead(encLtA)
    #define LT_PHASE_B digitalRead(encLtB)
    #define RT_PHASE_A digitalRead(encRtA)
    #define RT_PHASE_B digitalRead(encRtB)

    static volatile int8_t encDeltaLt, encDeltaRt;
    static int8_t lastLt, lastRt;
    int encLt, encRt;

    ISR( Timer2_COMPA_vect )
    <
    int8_t val, diff;

    digitalWrite(ledPin, HIGH); // toggle LED pin
    val = 0;
    if( LT_PHASE_A )
    val = 3;
    if( LT_PHASE_B )
    val ^= 1; // convert gray to binary
    diff = lastLt – val; // difference last – new
    if( diff & 1 ) < // bit 0 = value (1)
    lastLt = val; // store new as next last
    encDeltaLt += (diff & 2) – 1; // bit 1 = direction (+/-)
    >

    val = 0;
    if( RT_PHASE_A )
    val = 3;
    if( RT_PHASE_B )
    val ^= 1; // convert gray to binary
    diff = lastRt – val; // difference last – new
    if( diff & 1 ) < // bit 0 = value (1)
    lastRt = val; // store new as next last
    encDeltaRt += (diff & 2) – 1; // bit 1 = direction (+/-)
    >
    digitalWrite(ledPin, LOW); // toggle LED pin
    >
    void QuadratureEncoderInit(void)
    <
    int8_t val;

    Источник

    Arduino и прерывания таймера

    Привет, Хабр! Представляю вашему вниманию перевод статьи «Timer interrupts» автора E.

    Предисловие

    Плата Arduino позволяет быстро и минимальными средствами решить самые разные задачи. Но там где нужны произвольные интервалы времени (периодический опрос датчиков, высокоточные ШИМ сигналы, импульсы большой длительности) стандартные библиотечные функции задержки не удобны. На время их действия скетч приостанавливается и управлять им становится невозможно.

    В подобной ситуации лучше использовать встроенные AVR таймеры. Как это сделать и не заблудиться в технических дебрях даташитов, рассказывает удачная статья, перевод которой и предлагается вашему вниманию.

    В этой статье обсуждаются таймеры AVR и Arduino и то, как их использовать в Arduino проектах и схемах пользователя.

    Что такое таймер?

    Как и в повседневной жизни в микроконтроллерах таймер это некоторая вещь, которая может подать сигнал в будущем, в тот момент который вы установите. Когда этот момент наступает, вызывается прерывание микроконтроллера, напоминая ему что-нибудь сделать, например выполнить определенный фрагмент кода.

    Таймеры, как и внешние прерывания, работают независимо от основной программы. Вместо выполнения циклов или повторяющегося вызова задержки millis() вы можете назначить таймеру делать свою работу, в то время как ваш код делает другие вещи.

    Итак, предположим, что имеется устройство, которое должно что-то делать, например мигать светодиодом каждые 5 секунд. Если не использовать таймеры, а писать обычный код, то надо установить переменную в момент зажигания светодиода и постоянно проверять не наступил ли момент ее переключения. С прерыванием по таймеру вам достаточно настроить прерывание, и затем запустить таймер. Светодиод будет мигать точно вовремя, независимо от действий основной программы.

    Как работает таймер?

    Он действует путем увеличения переменной, называемой счетным регистром. Счетный регистр может считать до определенной величины, зависящей от его размера. Таймер увеличивает свой счетчик раз за разом пока не достигнет максимальной величины, в этой точке счетчик переполнится и сбросится обратно в ноль. Таймер обычно устанавливает бит флага, чтобы дать вам знать, что переполнение произошло.

    Вы можете проверять этот флаг вручную или можете сделать таймерный переключатель — вызывать прерывание автоматически в момент установки флага. Подобно всяким другим прерываниям вы можете назначить служебную подпрограмму прерывания (Interrupt Service Routine или ISR), чтобы выполнить заданный код, когда таймер переполнится. ISR сама сбросит флаг переполнения, поэтому использование прерываний обычно лучший выбор из-за простоты и скорости.

    Чтобы увеличивать значения счетчика через точные интервалы времени, таймер надо подключить к тактовому источнику. Тактовый источник генерирует постоянно повторяющийся сигнал. Каждый раз, когда таймер обнаруживает этот сигнал, он увеличивает значение счетчика на единицу. Поскольку таймер работает от тактового источника, наименьшей измеряемой единицей времени является период такта. Если вы подключите тактовый сигнал частотой 1 МГц, то разрешение таймера (или период таймера) будет:

    T = 1 / f (f это тактовая частота)
    T = 1 / 1 МГц = 1 / 10^6 Гц
    T = (1 ∗ 10^-6) с

    Таким образом разрешение таймера одна миллионная доля секунды. Хотя вы можете применить для таймеров внешний тактовый источник, в большинстве случаев используется внутренний источник самого чипа.

    Типы таймеров

    В стандартных платах Arduino на 8 битном AVR чипе имеется сразу несколько таймеров. У чипов Atmega168 и Atmega328 есть три таймера Timer0, Timer1 и Timer2. Они также имеют сторожевой таймер, который можно использовать для защиты от сбоев или как механизм программного сброса. Вот некоторые особенности каждого таймера.

    Timer0:
    Timer0 является 8 битным таймером, это означает, что его счетный регистр может хранить числа вплоть до 255 (т. е. байт без знака). Timer0 используется стандартными временными функциями Arduino такими как delay() и millis(), так что лучше не запутывать его если вас заботят последствия.

    Timer1:
    Timer1 это 16 битный таймер с максимальным значением счета 65535 (целое без знака). Этот таймер использует библиотека Arduino Servo, учитывайте это если применяете его в своих проектах.

    Timer2:
    Timer2 — 8 битный и очень похож на Timer0. Он используется в Arduino функции tone().

    Timer3, Timer4, Timer5:
    Чипы ATmega1280 и ATmega2560 (установлены в вариантах Arduino Mega) имеют три добавочных таймера. Все они 16 битные и работают аналогично Timer1.

    Конфигурация регистров

    Для того чтобы использовать эти таймеры в AVR есть регистры настроек. Таймеры содержат множество таких регистров. Два из них — регистры управления таймера/счетчика содержат установочные переменные и называются TCCRxA и TCCRxB, где x — номер таймера (TCCR1A и TCCR1B, и т. п.). Каждый регистр содержит 8 бит и каждый бит хранит конфигурационную переменную. Вот сведения из даташита Atmega328:

    Board Digital Pins Usable For Interrupts
    TCCR1A
    Бит 7 6 5 4 3 2 1 0
    0x80 COM1A1 COM1A0 COM1B1 COM1B0 WGM11 WGM10
    ReadWrite RW RW RW RW R R RW RW
    Начальное значение 0 0 0 0 0 0 0 0
    TCCR1B
    Бит 7 6 5 4 3 2 1 0
    0x81 ICNC1 ICES1 WGM13 WGM12 CS12 CS11 CS10
    ReadWrite RW RW R RW RW RW RW RW
    Начальное значение 0 0 0 0 0 0 0 0

    Наиболее важными являются три последние бита в TCCR1B: CS12, CS11 и CS10. Они определяют тактовую частоту таймера. Выбирая их в разных комбинациях вы можете приказать таймеру действовать на различных скоростях. Вот таблица из даташита, описывающая действие битов выбора:

    CS12 CS11 CS10 Действие
    0 0 0 Нет тактового источника (Timer/Counter остановлен)
    0 0 1 clk_io/1 (нет деления)
    0 1 0 clk_io/8 (делитель частоты)
    0 1 1 clk_io/64 (делитель частоты)
    1 0 0 clk_io/256 (делитель частоты)
    1 0 1 clk_io/1024 (делитель частоты)
    1 1 0 Внешний тактовый источник на выводе T1. Тактирование по спаду
    1 1 1 Внешний тактовый источник на выводе T1. Тактирование по фронту

    По умолчанию все эти биты установлены на ноль.

    Допустим вы хотите, чтобы Timer1 работал на тактовой частоте с одним отсчетом на период. Когда он переполнится, вы хотите вызвать подпрограмму прерывания, которая переключает светодиод, подсоединенный к ножке 13, в состояние включено или выключено. Для этого примера запишем Arduino код, но будем использовать процедуры и функции библиотеки avr-libc всегда, когда это не делает вещи слишком сложными. Сторонники чистого AVR могут адаптировать код по своему усмотрению.

    Сначала инициализируем таймер:

    Регистр TIMSK1 это регистр маски прерываний Таймера/Счетчика1. Он контролирует прерывания, которые таймер может вызвать. Установка бита TOIE1 приказывает таймеру вызвать прерывание когда таймер переполняется. Подробнее об этом позже.

    Когда вы устанавливаете бит CS10, таймер начинает считать и, как только возникает прерывание по переполнению, вызывается ISR(TIMER1_OVF_vect). Это происходит всегда когда таймер переполняется.

    Дальше определим функцию прерывания ISR:

    Сейчас мы можем определить цикл loop() и переключать светодиод независимо от того, что происходит в главной программе. Чтобы выключить таймер, установите TCCR1B=0 в любое время.

    Как часто будет мигать светодиод?

    Timer1 установлен на прерывание по переполнению и давайте предположим, что вы используете Atmega328 с тактовой частотой 16 МГц. Поскольку таймер 16-битный, он может считать до максимального значения (2^16 – 1), или 65535. При 16 МГц цикл выполняется 1/(16 ∗ 10^6) секунды или 6.25e-8 с. Это означает что 65535 отсчетов произойдут за (65535 ∗ 6.25e-8 с) и ISR будет вызываться примерно через 0,0041 с. И так раз за разом, каждую четырехтысячную секунды. Это слишком быстро, чтобы увидеть мерцание.

    Если мы подадим на светодиод очень быстрый ШИМ сигнал с 50% заполнением, то свечение будет казаться непрерывным, но менее ярким чем обычно. Подобный эксперимент показывает удивительную мощь микроконтроллеров — даже недорогой 8-битный чип может обрабатывать информацию намного быстрей чем мы способны обнаружить.

    Делитель таймера и режим CTC

    Чтобы управлять периодом, вы можете использовать делитель, который позволяет поделить тактовый сигнал на различные степени двойки и увеличить период таймера. Например, вы бы хотели мигания светодиода с интервалом одна секунда. В регистре TCCR1B есть три бита CS устанавливающие наиболее подходящее разрешение. Если установить биты CS10 и CS12 используя:

    то частота тактового источника поделится на 1024. Это дает разрешение таймера 1/(16 ∗ 10^6 / 1024) или 6.4e-5 с. Теперь таймер будет переполняться каждые (65535 ∗ 6.4e-5с) или за 4,194с. Это слишком долго.

    Но есть и другой режим AVR таймера. Он называется сброс таймера по совпадению или CTC. Вместо счета до переполнения, таймер сравнивает свой счетчик с переменой которая ранее сохранена в регистре. Когда счет совпадет с этой переменной, таймер может либо установить флаг, либо вызвать прерывание, точно так же как и в случае переполнения.

    Чтобы использовать режим CTC надо понять, сколько циклов вам нужно, чтобы получить интервал в одну секунду. Предположим, что коэффициент деления по-прежнему равен 1024.

    Расчет будет следующий:

    Вы должны добавить дополнительную единицу к числу отсчетов потому что в CTC режиме при совпадении счетчика с заданным значением он сбросит сам себя в ноль. Сброс занимает один тактовый период, который надо учесть в расчетах. Во многих случаях ошибка в один период не слишком значима, но в высокоточных задачах она может быть критичной.

    Функция настройки setup() будет такая:

    Также нужно заменить прерывание по переполнению на прерывание по совпадению:

    Сейчас светодиод будет зажигаться и гаснуть ровно на одну секунду. А вы можете делать все что угодно в цикле loop(). Пока вы не измените настройки таймера, программа никак не связана с прерываниями. У вас нет ограничений на использование таймера с разными режимами и настройками делителя.

    Вот полный стартовый пример который вы можете использовать как основу для собственных проектов:

    Помните, что вы можете использовать встроенные ISR функции для расширения функций таймера. Например вам требуется опрашивать датчик каждые 10 секунд. Но установок таймера, обеспечивающих такой долгий счет без переполнения нет. Однако можно использовать ISR чтобы инкрементировать счетную переменную раз в секунду и затем опрашивать датчик когда переменная достигнет 10. С использованием СТС режима из предыдущего примера прерывание могло бы выглядеть так:

    Поскольку переменная будет модифицироваться внутри ISR она должна быть декларирована как volatile. Поэтому, при описании переменных в начале программы вам надо написать:

    Послесловие переводчика

    В свое время эта статья сэкономила мне немало времени при разработке прототипа измерительного генератора. Надеюсь, что она окажется полезной и другим читателям.

    Источник