Платформа Arduino была первоначально спроектирована в 2005 году и первоначально предназначалась для того, чтобы люди, мало знакомые с электроникой и программированием, могли конструировать разнообразные электронные устройства. Но со временем она получила широкое распространение не только в кругах начинающих знакомиться с электроникой, но и среди профессионалов в сфере электроники.
В отличие от языков программирования для микроконтроллеров AVR, ARM, PIC, STM, в которых нужно хорошо представлять структуру этих микроконтроллеров, язык программирования для платформы Arduino исключительно простой и понятный. Достаточно легко понять, к примеру, как работают функции digitalWrite(), AnalogWrite(), Delay() и др. не вникая в суть машинного языка, который спрятан внутри них. Также не нужно вникать в суть различных регистров микроконтроллера, которые используются для управления этими процессами.
Но тем не менее, для лучшего понимания всех этих процессов, желательно все таки немного погрузиться внутрь этих процессов. К примеру, функция delay() используется для установки таймеров и битов регистров счета микроконтроллера AVR ATmega, являющегося основой платы Arduino.
В этой статье мы рассмотрим как без использования функции delay() управлять задержками в программе, непосредственно имея дело с регистрами микроконтроллера. Для этого мы будем использовать программную среду Arduino IDE. Мы будем устанавливать соответствующие биты регистра таймера и использовать прерывание переполнения таймера (Timer Overflow Interrupt) чтобы переключать (включать/выключать) состояние светодиода каждый раз когда происходит прерывание. Для контроля длительности задержки в схеме будут использоваться кнопки, с помощью которых можно будет изменять заранее загружаемое значение в биты таймера.
Что такое таймеры
Что же представляют собой таймеры в современной электронике? Фактически это определенный вид прерываний. Это простые часы, которые могут измерять длительность какого-нибудь события. Каждый микроконтроллер имеет встроенные часы (осциллятор), в плате Arduino Uno этот осциллятор работает на частоте 16 МГц. Частота влияет на скорость обработки. Чем выше частота, тем выше скорость обработки. Таймер использует счетчик, который считает с определенной скоростью, зависящей от частоты осциллятора. В плате Arduino Uno состояние счетчика увеличивается на 1 каждые 62 нано секунды (1/16000000 секунды). Фактически, это время за которое плата Arduino Uno переходит от одной инструкции к другой.
Таймеры в Arduino Uno
В плате Arduino Uno используется три таймера:
Timer0: 8-битный таймер, используемый в таких функциях как delay(), millis().
Timer1: 16-битный таймер, используемый в библиотеке для управления серводвигателями.
Timer2: 8-битный таймер, используемый в функции tone().
Регистры таймеров в Arduino Uno
Для изменения конфигурации таймеров в плате Arduino Uno используются следующие регистры:
1. Timer/Counter Control Registers (TCCRnA/B) – управляющие регистры таймера/счетчика
Эти регистры содержат основные управляющие биты таймера и используются для управления предварительными делителями частоты (предделителями) таймера. Они также позволяют управлять режимом работы таймера с помощью битов WGM.
Формат этих регистров:
Биты CS12, CS11, CS10 в регистре TCCR1B устанавливают коэффициент деления предделителя, то есть скорость часов таймера. В плате Arduino Uno можно установить коэффициент деления предделителя равный 1, 8, 64, 256, 1024.
2. Timer/Counter Register (TCNTn) – регистры таймера/счетчика
Эти регистры используются для управления счетчиками и для установки заранее загружаемого значения.
Формула для расчета заранее загружаемого значения (preloader value) для необходимого интервала времени (Time) в секундах выглядит следующим образом:
TCNTn = 65535 – (16x1010xTime in sec / Prescaler Value)
Чтобы для таймера 1 (timer1) задать время равное 2 секундам, получим:
TCNT1 = 65535 – (16x1010x2 / 1024) = 34285
Прерывания таймеров в Arduino
Прерывания таймеров являются видом программных прерываний. В Arduino присутствуют следующие виды прерываний таймеров.
Прерывания переполнения таймера (Timer Overflow Interrupt)
Это прерывание происходит всегда, когда значение счетчика достигает его максимального значения, например, для 16-битного счетчика это 65535. Соответственно, процедура обработки (обслуживания) прерывания (ISR) вызывается когда бит прерывания переполнения таймера установлен (enabled) в TOIEx присутствующем в регистре масок прерываний TIMSKx.
ISR Format:
ISR(TIMERx_OVF_vect)
{
}
Output Compare Register (OCRnA/B) – регистр сравнения выхода
Процедура обработки прерывания сравнения выхода (Output Compare Match Interrupt) вызывается при вызове функции TIMERx_COMPy_vect если установлен бит/флаг OCFxy в регистре TIFRx. Эта процедура обработки прерывания (ISR) становится доступной при помощи установки бита OCIExy, присутствующем в регистре маски прерываний TIMSKx.
Захват входа таймера (Timer Input Capture)
Процедура обработки этого прерывания вызывается если установлен бит/флаг ICFx в регистре флагов прерываний таймера (TIFRx - Timer Interrupt Flag Register). Эта процедура обработки прерываний становится доступной при установке бита ICIEx в регистре маски прерываний TIMSKx.
Необходимые компоненты
- Плата Arduino Uno (купить на AliExpress).
- ЖК дисплей 16х2 (купить на AliExpress).
- Резисторы 10 кОм (2 шт.) и 2,2 кОм (купить на AliExpress).
- Светодиод (любого цвета) (купить на AliExpress).
- Кнопка (2 шт.).
- Источник питания с напряжением 5 В.
Работа схемы
Схема устройства представлена на следующем рисунке.
Необходимые соединения между платой Arduino Uno и ЖК дисплеем 16х2 представлены в следующей таблице:
ЖК дисплей 16х2 | Arduino UNO |
VSS | GND |
VDD | +5V |
V0 | к среднему контакту потенциометра для контроля контрастности ЖК дисплея |
RS | 8 |
RW | GND |
E | 9 |
D4 | 10 |
D5 | 11 |
D6 | 12 |
D7 | 13 |
A | +5V |
K | GND |
Две кнопки через подтягивающие резисторы 10 кОм подключены к контактам 2 и 4 платы Arduino Uno, а светодиод подключен к контакту 7 Arduino через резистор 2,2 кОм.
Собранная схема устройства будет выглядеть примерно следующим образом:
Программирование таймеров в плате Arduino UNO
В этом проекте мы будем использовать прерывание переполнения таймера (Timer Overflow Interrupt) и использовать его для включения и выключения светодиода на определенные интервалы времени при помощи установки заранее определяемого значения (preloader value) регистра TCNT1 с помощью кнопок. Полный код программы будет приведен в конце статьи, здесь же рассмотрим его основные части.
Для отображения заранее определяемого значения используется ЖК дисплей, поэтому необходимо подключить библиотеку для работы с ним.
#include<LiquidCrystal.h>
Анод светодиода подключен к контакту 7 платы Arduino, поэтому определим (инициализируем) его как ledPin.
#define ledPin 7
Затем сообщим плате Arduino к каким ее контактам подключен ЖК дисплей.
LiquidCrystal lcd(8,9,10,11,12,13);
Установим заранее определенное значение (preloader value) равное 3035 – это будет соответствовать интервалу времени в 4 секунды. Формула для расчета этого значения приведена выше в статье.
float value = 3035;
Затем в функции void setup() установим режим работы ЖК дисплея 16х2 и высветим приветственное сообщение на нем на несколько секунд.
lcd.begin(16,2);
lcd.setCursor(0,0);
lcd.print("ARDUINO TIMERS");
delay(2000);
lcd.clear();
Затем контакт, к которому подключен светодиод, установим в режим вывода данных, а контакты, к которым подключены кнопки – в режим ввода данных.
pinMode(ledPin, OUTPUT);
pinMode(2,INPUT);
pinMode(4,INPUT);
После этого отключим все прерывания.
noInterrupts();
Далее инициализируем Timer1.
TCCR1A = 0;
TCCR1B = 0;
Загрузим заранее определенное значение (3035) в TCNT1.
TCNT1 = value;
Затем установим коэффициент деления предделителя равный 1024 при помощи конфигурирования битов CS в регистре TCCR1B.
TCCR1B |= (1 << CS10)|(1 << CS12);
Разрешим вызов процедуры обработки прерывания переполнения счетчика с помощью установки соответствующего бита в регистре маски прерываний.
TIMSK1 |= (1 << TOIE1);
Теперь разрешим все прерывания.
interrupts();
Теперь процедура обработки прерывания переполнения счетчика будет отвечать за включение и выключение светодиода с помощью функции digitalWrite. Состояние светодиода будет меняться каждый раз когда будет происходить прерывание переполнения счетчика.
ISR(TIMER1_OVF_vect)
{
TCNT1 = value;
digitalWrite(ledPin, digitalRead(ledPin) ^ 1);
}
В функции void loop() предварительно загружаемое значение увеличивается и уменьшается на 10 (инкрементируется и декрементируется) при помощи кнопок в схеме. Также это значение отображается на экране ЖК дисплея 16х2.
if(digitalRead(2) == HIGH)
{
value = value+10; //увеличиваем preload value
}
if(digitalRead(4)== HIGH)
{
value = value-10; //уменьшаем preload value
}
lcd.setCursor(0,0);
lcd.print(value);
}
Исходный код программы
Далее приведен полный текст программы. Работа нашего проекта продемонстрирована на видео, приведенном в конце статьи.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#include<LiquidCrystal.h> //подключение библиотеки для работы с ЖК дисплеем #define ledPin 7 LiquidCrystal lcd(8,9,10,11,12,13); float value = 3035; //Preload timer value (3035 for 4 seconds) void setup() { lcd.begin(16,2); lcd.setCursor(0,0); lcd.print("ARDUINO TIMERS"); delay(2000); lcd.clear(); pinMode(ledPin, OUTPUT); pinMode(2,INPUT); pinMode(4,INPUT); noInterrupts(); // отключаем все прерывания TCCR1A = 0; TCCR1B = 0; TCNT1 = value; // preload timer TCCR1B |= (1 << CS10)|(1 << CS12); // 1024 prescaler (коэффициент деления предделителя) TIMSK1 |= (1 << TOIE1); // enable timer overflow interrupt ISR (разрешаем вызов процедуры обработки прерывания переполнения счетчика) interrupts(); // разрешаем все прерывания } ISR(TIMER1_OVF_vect) // процедура обработки прерывания переполнения счетчика { TCNT1 = value; // preload timer digitalWrite(ledPin, digitalRead(ledPin) ^ 1); //включаем и выключаем светодиод } void loop() { if(digitalRead(2) == HIGH) { value = value+10; //Incement preload value } if(digitalRead(4)== HIGH) { value = value-10; //Decrement preload value } lcd.setCursor(0,0); lcd.print(value); } |
Спасибо, дорогой комрад за урок!
Спасибо и вам что оценили мой труд