Секундомер ардуино: делаем своими руками на основе uno

Что еще можно сделать?

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

  • Анемометр — стационарный прибор для измерения скорости ветра;
  • Акселерометр — сенсор, позволяющий определять ускорение и ориентацию в пространстве;
  • Аналоговый термометр — аналоговый сенсор для измерения температуры;
  • Барометр — сенсор, позволяющий определять атмосферное давление и температуру;
  • Датчик влажности почвы — сенсор, позволяющий узнать о пересыхании земли
  • Датчик водорода — датчик для обнаружения водорода;
  • Датчик тока — аналоговый сенсор для измерения силы тока;
  • Датчик уровня воды — цифровой датчик уровня воды в ёмкости;
  • Датчик температуры и влажности — сенсор, предоставляющий информацию об окружающей температуре и влажности в виде цифрового сигнала;
  • Датчик пульса — аналоговый датчик для измерения частоты сердечных сокращений
  • Гироскоп — сенсор, позволяющий определять собственную угловую скорость.

Это лишь малая часть датчиков и сенсоров, которые вы можете использовать для создания своих устройств. Мы уже много интересного сделали и в планах еще много всего интересного сделать

Желаем вам отличных проектов. Подписывайтесь на нашу группу ВКонтакте.

Step 1: Prescalers and the Compare Match Register

The Uno has three timers called timer0, timer1, and timer2.  Each of the timers has a counter that is incremented on each tick of the timer’s clock.  CTC timer interrupts are triggered when the counter reaches a specified value, stored in the compare match register.  Once a timer counter reaches this value it will clear (reset to zero) on the next tick of the timer’s clock, then it will continue to count up to the compare match value again.  By choosing the compare match value and setting the speed at which the timer increments the counter, you can control the frequency of timer interrupts.
The first parameter I’ll discuss is the speed at which the timer increments the counter.  The Arduino clock runs at 16MHz, this is the fastest speed that the timers can increment their counters.  At 16MHz each tick of the counter represents 1/16,000,000 of a second (~63ns), so a counter will take 10/16,000,000 seconds to reach a value of 9 (counters are 0 indexed), and 100/16,000,000 seconds to reach a value of 99.
In many situations, you will find that setting the counter speed to 16MHz is too fast.  Timer0 and timer2 are 8 bit timers, meaning they can store a maximum counter value of 255.  Timer1 is a 16 bit timer, meaning it can store a maximum counter value of 65535.  Once a counter reaches its maximum, it will tick back to zero (this is called overflow).  This means at 16MHz, even if we set the compare match register to the max counter value, interrupts will occur every 256/16,000,000 seconds (~16us) for the 8 bit counters, and every 65,536/16,000,000 (~4 ms) seconds for the 16 bit counter.  Clearly, this is not very useful if you only want to interrupt once a second.
Instead you can control the speed of the timer counter incrementation by using something called a prescaler.  A prescaler dictates the speed of your timer according the the following equation:(timer speed (Hz)) = (Arduino clock speed (16MHz)) / prescaler
So a 1 prescaler will increment the counter at 16MHz, an 8 prescaler will increment it at 2MHz, a 64 prescaler = 250kHz, and so on.  As indicated in the tables above, the prescaler can equal 1, 8, 64, 256, and 1024.  (I’ll explain the meaning of CS12, CS11, and CS10 in the next step.) 
Now you can calculate the interrupt frequency with the following equation:interrupt frequency (Hz) = (Arduino clock speed 16,000,000Hz) / (prescaler * (compare match register + 1))
the +1 is in there because the compare match register is zero indexed
rearranging the equation above, you can solve for the compare match register value that will give your desired interrupt frequency:compare match register = [ 16,000,000Hz/ (prescaler * desired interrupt frequency) ] — 1
remember that when you use timers 0 and 2 this number must be less than 256, and less than 65536 for timer1
so if you wanted an interrupt every second (frequency of 1Hz):
compare match register = [16,000,000 / (prescaler * 1) ] -1
with a prescaler of 1024 you get:
compare match register = [16,000,000 / (1024 * 1) ] -1
= 15,624
since 256 < 15,624 < 65,536, you must use timer1 for this interrupt.

Шаг четвёртый. Добавляем звук

Устройство стало намного удобней, но теперь хочется добавить звуки. До Алисы мы со временем дойдем, а пока ограничимся простым пьезодинамиком. За издаваемые звуки его часто называют пищалкой или базером.

Для подключения динамика понадобятся всего два провода, один подключим к минусу, а второй к 9 контакту на Arduino

Сопротивление здесь не понадобится, да и полярности динамик не имеет — не важно какую ножку подключать к плюсу, а какую к минусу

В программе добавились три строки: первая задаёт контакт для работы с динамиком, вторая и третья выдают звуки. Для этого используется функция tone. В этой функции можно задать частоту и продолжительность звукового сигнала.

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

Оставляйте комментарии, какие электронные проекты вам интересны, что осталось непонятно и о чём рассказать в следующих статьях.

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 &lt;&lt; WGM12); // CTC mode
TCCR1B |= (1 &lt;&lt; CS12); // 256 prescaler
TIMSK1 |= (1 &lt;&lt; OCIE1A); // enable timer compare interrupt
interrupts(); // enable all interrupts
}

ISR(Timer1_COMPA_vect) // timer compare interrupt service routine
{
digitalWrite(ledPin, digitalRead(ledPin) ^ 1); // toggle LED pin
}

void loop()
{
// your program here…
}

Базовые вызовы

/**
 * Init ISR (Interrupt service routine) for the timer and start timer.
 * 
 * General algorithm
 * http://www.robotshop.com/letsmakerobots/arduino-101-timers-and-interrupts
 * 1. CPU frequency 16Mhz for Arduino
 * 2. maximum timer counter value (256 for 8bit, 65536 for 16bit timer)
 * 3. Divide CPU frequency through the choosen prescaler (16000000 / 256 = 62500)
 * 4. Divide result through the desired frequency (62500 / 2Hz = 31250)
 * 5. Verify the result against the maximum timer counter value (31250 < 65536 success).
 *    If fail, choose bigger prescaler.
 * 
 * Example: to set timer clock period to 20ms (50 operations per second == 50Hz)
 * 
 * 1) on 16MHz CPU (AVR Arduino)
 *   use prescaler 1:8 (TIMER_PRESCALER_1_8) and adjustment=40000-1:
 *   16000000/8/50=40000, minus 1 cause count from zero.
 * 
 * 2) on 80MHz CPU (PIC32MX ChipKIT)
 *   use prescaler 1:64 (TIMER_PRESCALER_1_64) and adjustment=25000-1:
 *   80000000/64/50=25000, minus 1 cause count from zero.
 *
 * 3) on 84MHz CPU (SAM Arduino Due)
 *   use prescaler 1:128 (TIMER_PRESCALER_1_128) and adjustment=13125-1:
 *   80000000/128/50=13125, minus 1 cause count from zero.
 * 
 * Timer interrupt handler timer_handle_interrupts would be called every 20ms
 * (50 times per second == 50Hz freq) in this case.
 * 
 * @param timer
 *   system timer id: use TIMER_DEFAULT for default timer
 *   or _TIMER1...TIMER9, _TIMER1_32BIT..._TIMER9_32BIT for specific timer.
 *   note: availability of specific timer depends on the platform.
 * @param prescaler
 *   timer prescaler (1, 2, 4, 8, 16, 32, 64, 128, 256, 1024),
 *   use constants: PRESCALER_1_1, PRESCALER_1_2, PRESCALER_1_8,
 *   PRESCALER_1_16, PRESCALER_1_32, PRESCALER_1_64, PRESCALER_1_128
 *   PRESCALER_1_256, PRESCALER_1_1024
 *   note: availability of specific prescaler depends on the platform.
 * @param adjustment
 *   adjustment divider after timer prescaled - timer compare match value.
 */
void timer_init_ISR(int timer, int prescaler, int adjustment);

/**
 * Stop ISR (Interrupt service routine) for the timer.
 * 
 * @param timer
 *     system timer id for started ISR
 */
void timer_stop_ISR(int timer);

/**
 * Timer ISR (Interrupt service routine) handler.
 * Implementation must be provided in module with user code.
 * 
 * @param timer
 *     system timer id for started ISR 
 */
void timer_handle_interrupts(int timer);

Реле

Реле — это электромеханические устройства, используемые для управления питанием подключенных устройств.

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

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

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

При отсутствии тока, проходящего через катушку, реле находится в нормально замкнутом состоянии:

Когда выключатель закрыт, ток течет через катушку, и электромагнитное поле, создаваемое катушкой, приводит к тому, что высоковольтная клемма переключается в нормально открытое положение:

При работе с индуктивными нагрузками, такими как реле, соленоиды, двигатели или шаговые двигатели, важно защитить Вашу цепь от обратного ЭДС. Обратный ЭДС возникает, когда электромагнитное поле, окружающее катушку, падает обратно в катушку

Это вызывает большой обратный ток в катушке, который может повредить другие компоненты в цепи

Обратный ЭДС возникает, когда электромагнитное поле, окружающее катушку, падает обратно в катушку. Это вызывает большой обратный ток в катушке, который может повредить другие компоненты в цепи.

Для предотвращения обратного электромагнитного поля, диод должен быть размещен параллельно катушке.

Готовим программное обеспечение

После того как вы купили нужные детали и их вам доставили — самое время подготовить программное обеспечение для того, чтобы мы могли взаимодействовать с нашим микроконтроллером. Нам нужно на наш компьютер установить Arduino IDE.

Как мы писали в обзорной статье про эту среду — используя программную среду Arduino IDE, можно, основываясь лишь на минимальных знаниях C++, решать самые разные творческие задачи, связанные с программированием и моделированием. Arduino IDE — это программная среда разработки, предназначенная для программирования одноимённой платы.

3.1 Скачиваем и устанавливаем ПО

Выбираем нужную версию, жмем «Just Download» и скачиваем:

После того как скачали ПО — запускам установку, открыв скачанный .EXE файл:

Дальше мы проходим все обычные шаги установки, как при установке любого другого приложения — соглашаемся с «лицензионным соглашением», ставим галочки, выбираем папку для установки и жмем ОК:

3.2 Запускам ПО и включаем русский язык

После того как мы прошли процесс установки мы увидим на рабочем столе иконку нашей Arduino IDE:

Нажимаем на иконку и видим процесс загрузки программы:

В итоге мы увидим такое окно:

Включаем русский язык.

Для включения русскоязычного интерфейса Arduino IDE нам нужно перейти в нужную вкладку и выбрать русский язык в списке:

File → Preferences → Language

Да, теперь, на этом шаге, у нас уже есть все комплектующие и установлено нужное программное обеспечение.

Reading quadrature encoders with a timer

The next example uses Timer2 and the compare match interrupt to read the encoder inputs.

Timer2 is initialized by default to a frequency of 1kHz (1ms period). In the interrupt service routine the state of all encoder pins is read and a state machine is used to eliminate false readings. Using the timer interrupt is much easier to handle than using 4 input change interrupts.

The signals of a quadrature encoder is a 2bit Gray code. Only 1 bit is changing from state to state. A state machine is perfect to check the signal and count the encoder ticks. The timer must be fast enough, to recognize each state change. For the Pololu wheel encoders used here, the 1ms timer is fast enough.

The following example has been modified to work with Arduino V1.x

/*
* timer and interrupts
* Timer2 compare interrupt example. Quadrature Encoder
* more infos: https://oscarliang.com
*
* Credits:
* based on code from Peter Dannegger
* http://www.mikrocontroller.net/articles/Drehgeber
*/
#if ARDUINO &gt;= 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 &amp; 1 ){ // bit 0 = value (1)
lastLt = val; // store new as next last
encDeltaLt += (diff &amp; 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 &amp; 1 ){ // bit 0 = value (1)
lastRt = val; // store new as next last
encDeltaRt += (diff &amp; 2) – 1; // bit 1 = direction (+/-)
}
digitalWrite(ledPin, LOW); // toggle LED pin
}
void QuadratureEncoderInit(void)
{
int8_t val;

cli();
TIMSK2 |= (1&lt;&lt;OCIE2A);
sei();
pinMode(encLtA, INPUT);
pinMode(encRtA, INPUT);
pinMode(encLtB, INPUT);
pinMode(encRtB, INPUT);

val=0;
if (LT_PHASE_A)
val = 3;
if (LT_PHASE_B)
val ^= 1;
lastLt = val;
encDeltaLt = 0;

val=0;
if (RT_PHASE_A)
val = 3;
if (RT_PHASE_B)
val ^= 1;
lastRt = val;
encDeltaRt = 0;

encLt = 0;
encRt = 0;
}

int8_t QuadratureEncoderReadLt( void ) // read single step encoders
{
int8_t val;

cli();
val = encDeltaLt;
encDeltaLt = 0;
sei();
return val; // counts since last call
}

int8_t QuadratureEncoderReadRt( void ) // read single step encoders
{
int8_t val;

cli();
val = encDeltaRt;
encDeltaRt = 0;
sei();
return val; // counts since last call
}

void setup()
{
Serial.begin(38400);
pinMode(ledPin, OUTPUT);
QuadratureEncoderInit();
}
void loop()
{
encLt += QuadratureEncoderReadLt();
encRt += QuadratureEncoderReadRt();
Serial.print(“Lt: “);
Serial.print(encLt, DEC);
Serial.print(” Rt: “);
Serial.println(encRt, DEC);
delay(1000);
}

Шаг второй. Таймер обратного отсчёта

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

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

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

Устройство можно считать условно рабочим — оно отсчитывает заданное время, но для этого плату приходится каждый раз включать вручную. Исправим это, добавим бесконтактный сенсор.

Создаем скетч для Arduino Nano

После этого мы можем поступить двумя способами. Первый — добавить код управления светодиодом вручную и его скомпилировать, или второй — выбрать готовую заготовку в Arduino IDE.

Если мы идем по первому пути — мы должны добавить следующий код в наше приложение:

int redPin = 12;

void setup() {
  // initialize Leds
  pinMode(redPin, OUTPUT);
}

void loop() {
  digitalWrite(redPin, HIGH);
  delay(1000);
  digitalWrite(redPin, LOW);
  delay(1000);
}

Второй вариант — это выбор уже готового проекта в нашей IDE. Для этого нужно сделать следующее.

File → Examples → 01. Basics → Blink
(Файл → Примеры → 01. Основы → Моргание)

После чего мы увидим код в нашем окне программы:

И здесь важный момент — нужно нажать стрелку сверху, чтобы скомпилировать скетч. После чего вы увидите надпись «Компиляция скетча» (Compiling sketch…) слева и справа процентную шкалу. В свою очередь стрелка запуска сверху поменяет свой цвет:

После этого светодиод начнет мигать.

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

Как использовать релейный модуль с устройствами высокого напряжения

Сначала давайте посмотрим на принципиальную схему. Как описано ранее, мы будем использовать адаптер 5 В в качестве отдельного источника питания для электромагнита, подключенного к JDVcc и заземляющему выводу. Вывод Arduino 5V будет подключен к выводу Vcc модуля, а вывод 7 к входному выводу In1 для управления реле. Теперь для части «высокое напряжение» нам понадобится вилка, розетка и кабель с двумя проводами. Один из двух проводов будет обрезан и подключен к общему и нормально разомкнутому контакту выходного разъема модуля. Таким образом, в этой конфигурации, когда мы активируем реле, мы получим замкнутую и рабочую высоковольтную цепь.

Ниже коснемся того, как сделать кабель. Нам нужны вилка, розетка и кабель. Аккуратно обрезаем кабель и обрезаем один из проводов, как показано на рисунке ниже. Подключаем их к нормально разомкнутым контактам релейного модуля. Также подключаем концы кабеля к вилке и розетке.

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

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

Минимальные частоты и разрядность таймеров

Варианты вызовов timer_init_ISR_2Hz (2Гц, период 500мс) и timer_init_ISR_1Hz (1Гц, период 1с) на PIC32MX 80МГц будут работать только с 32-битными таймерами (_TIMER2_32BIT и _TIMER4_32BIT; TIMER_DEFAULT — по умолчанию = _TIMER4_32BIT), т.к. при 16-битных режимах таймеров PIC32MX 80МГц комбинация «делитель частоты» (prescaler — максимальный вариант 1/256) + «поправка периода» (adjustment — максимальный вариант 2^16=65536-1) дают минимальную частоту 5Гц (период — 200мс):
80000000/256/65536 = 4.8Гц

На PIC32 32-битные таймеры создаются комбинацией 2х 16-битных таймеров:

  • Timer4(32бит) = Timer4(16бит)+Timer5(16бит)
  • Timer2(32бит) = Timer2(16бит)+Timer3(16бит)

поэтому при использовании таймера _TIMER2_32BIT, обычные таймеры _TIMER2 и _TIMER3 будут заняты, при использовании _TIMER4_32BIT — заняты будут _TIMER4 и _TIMER5.

На AVR/Arduino можно получить частоту 1Гц стандартными делителями на 16-битном таймере.

На SAM/Arduino Due все таймеры 32-битные.

Тип данных для параметра adjustment — unsigned int.

  • На PIC32 разрядность int — 32 бит, этого хватит и для 16-тибитного режима таймера (если не закладывать значение более 2^16=65536-1) и для 32-битного (пойдет любое значение до 2^32=4294967296-1).
  • На SAM разрядность int — 32 бит, все таймеры 32-битные.
  • На AVR разрядность int — 16 бит, это опять же, как раз достаточно для 16-битных таймеров.

Таким образом, хотя разрядность параметра adjustment с типом int будет разной на разных платформах, во всех случаях значение будет соответствовать аппаратным свойствам таймеров.

Оцените статью:
Оставить комментарий