Timer interrupt with arduino

Arduino 101 Curie Timer One Interrupt

With this tutorial you learn to use one of the timers available in the microcontroller. The library uses Timer 1 and this tutorial shows how to set up an interrupt at variable intervals to toggle the on-board LED. The interrupt is the only way to exit from a delay() function and it is used in this sketch to show how a delay instruction — that apparently stops the flow of the code execution — is temporarily exited to toggle the LED, then it is continued from where it was interrupted. The final effect is a 10 seconds blinking block with the same blinking rate, incremented every 10 seconds in four steps.

Hardware Required

The Circuit

image developed using Fritzing. No additional hardware is needed to use this tutorial.

Software Essentials

Libraries

CurieTimerOne.h is the library that provides access to the Timer 1 of the microcontroller. This library allows to set up the number of microseconds that the timer counts before it asserts an interrupt. The interrupt can be configured to call a specific function — the callback function — and each interrupt increments a counter. The same library is used to generate a PWM signal with duty cycle and period lenght fully customizable.

Functions

timedBlinkIsr() — is the function to which the sketch execution is passed when the interrupt is asserted. It toggles the state of the LED on D13 using the toggle variable that is changed using the «!» (NOT) operator at each call.

The code is written with a serial monitor feature that can be toggled commenting or uncommenting #define SERIAL_PORT_LOG_ENABLE 1 .

Источник

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:

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. Поэтому, при описании переменных в начале программы вам надо написать:

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

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

Источник

Multi-tasking the Arduino — Part 2

Timer Interrupts

New Subscription

Please sign in to subscribe to this guide.

You will be redirected back to this guide once you sign in, and can then subscribe to this guide.

Don’t call us, we’ll call you

In part 1 of this series, we learned how to use millis() for timing. But in order to make that work, we had to call millis() every time through the loop to see if it was time to do something. It is kind of a waste to be calling millis() more than once a millisecond, only to find out that the time hasn’t changed. Wouldn’t it be nice if we only had to check once per millisecond?

Timers and timer interrupts let us do exactly that. We can set up a timer to interrupt us once per millisecond. The timer will actually call us to let us know it is time to check the clock!

Arduino Timers

The Arduino Uno has 3 timers: Timer0, Timer1 and Timer2. Timer0 is already set up to generate a millisecond interrupt to update the millisecond counter reported by millis(). Since that is what we are looking for, we’ll get Timer0 to generate an interrupt for us too!

Frequency and Counts

Timers are simple counters that count at some frequency derived from the 16MHz system clock. You can configure the clock divisor to alter the frequency and various different counting modes. You can also configure them to generate interrupts when the timer reaches a specific count.

Timer0 is an 8-bit that counts from 0 to 255 and generates an interrupt whenever it overflows. It uses a clock divisor of 64 by default to give us an interrupt rate of 976.5625 Hz (close enough to a 1KHz for our purposes). We won’t mess with the freqency of Timer0, because that would break millis()!

Comparison Registers

Arduino timers have a number of configuration registers. These can be read or written to using special symbols defined in the Arduino IDE. For comprehensive description of all these registers and their functions, see the links in «For further reading» below.

We’ll set up a up a comparison register for Timer 0 (this register is known as OCR0A) to generate another interrupt somewhere in the middle of that count. On every tick, the timer counter is compared with the comparison register and when they are equal an interrupt will be generated.

The code below will generate a ‘TIMER0_COMPA’ interrupt whenever the counter value passes 0xAF.

Then we’ll define an interrupt handler for the timer interrupt vector known as «TIMER0_COMPA_vect». In this interrupt handler we’ll do all the stuff we used to do in the loop.

Which leaves us with a completely empty loop.

You can do anything you want in your loop now. You can even be decadent and use a delay()! The flashers and sweepers won’t care. They will still get called once per millisecond regardless!

For further reading:

This is just a simple example of what timers can do. For more in-depth information on the different types of timers and ways to configure them, check out the «Libraries and Links» page.

The Source Code:

Here’s the whole code, including the flashers and sweepers:

This guide was first published on Dec 01, 2014. It was last updated on Dec 01, 2014.

This page (Timer Interrupts) was last updated on Oct 04, 2022.

Источник

Adblock
detector