Введение
Как микроконтроллеры отслеживают время и дату? Обычный микроконтроллер обладает функцией таймера, который стартует от нуля при подаче напряжения питания, а затем начинает считать. В мире Arduino мы можем использовать функцию , чтобы узнать, сколько прошло миллисекунд с того времени, когда было подано напряжение питания. Когда вы снимете и снова подадите питания, она начнет отсчет с самого начала. Это не очень удобно, когда дело доходит до работы с часами и датами.
Вот здесь и будет удобно использование микросхемы RTC (Real Time Clock, часов реального времени). Эта микросхема с батарейкой 3В или каким-либо другим источником питания следит за временем и датой. Часы/календарь обеспечивают информацию о секундах, минутах, часах, дне недели, дате, месяце и годе. Микросхема корректно работает с месяцами продолжительностью 30/31 день и с високосными годами. Связь осуществляется через шину I2C (шина I2C в данной статье не обсуждается).
Если напряжение на главной шине питания Vcc падает ниже напряжения на батарее Vbat, RTC автоматически переключается в режим низкого энергопотребления от резервной батареи. Резервная батарея – это обычно миниатюрная батарея (в виде «монетки», «таблетки») напряжением 3 вольта, подключенная между выводом 3 и корпусом. Таким образом, микросхема по-прежнему будет следить за временем и датой, и когда на основную схему будет подано питание, микроконтроллер получит текущие время и дату.
В этом проекте мы будем использовать DS1307. У этой микросхемы вывод 7 является выводом SQW/OUT (выходом прямоугольных импульсов). Вы можете использовать этот вывод для мигания светодиодом и оповещения микроконтроллера о необходимости фиксации времени. Мы будем делать и то, и другое. Ниже приведено объяснение работы с выводом SQW/OUT.
Для управления работой вывода SQW/OUT используется регистр управления DS1307.
Бит 7 | Бит 6 | Бит 5 | Бит 4 | Бит 3 | Бит 2 | Бит 1 | Бит 0 |
---|---|---|---|---|---|---|---|
OUT | SQWE | RS1 | RS0 |
- Бит 7: управление выходом (OUT)
- Этот бит управляет выходным уровнем вывода SQW/OUT, когда выход прямоугольных импульсов выключен. Если SQWE = 0, логический уровень на выводе SQW/OUT равен 1, если OUT = 1, и 0, если OUT = 0. Первоначально обычно этот бит равен 0.
- Бит 4: включение прямоугольных импульсов (SQWE)
- Этот бит, когда установлен в логическую 1, включает выходной генератор. Частота прямоугольных импульсов зависит от значений битов RS0 и RS1. Когда частота прямоугольных импульсов настроена на значение 1 Гц, часовые регистры обновляются во время спада прямоугольного импульса. Первоначально обычно этот бит равен 0.
- Биты 1 и 0: выбор частоты (RS)
- Эти биты управляют частотой выходных прямоугольных импульсов, когда выход прямоугольных импульсов включен. Следующая таблица перечисляет частоты прямоугольных импульсов, которые могут быть выбраны с помощью данных битов. Первоначально обычно эти биты равны 1.
RS1 | RS0 | Частота импульсов и уровень на выходе SQW/OUT | SQWE | OUT |
---|---|---|---|---|
1 Гц | 1 | x | ||
1 | 4,096 кГц | 1 | x | |
1 | 8,192 кГц | 1 | x | |
1 | 1 | 32,768 кГц | 1 | x |
x | x | |||
x | x | 1 | 1 |
Данная таблица поможет вам с частотой:
Частота импульсов | Бит 7 | Бит 6 | Бит 5 | Бит 4 | Бит 3 | Бит 2 | Бит 1 | Бит 0 |
---|---|---|---|---|---|---|---|---|
1 Гц | 1 | |||||||
4,096 кГц | 1 | 1 | ||||||
8,192 кГц | 1 | 1 | ||||||
32,768 кГц | 1 | 1 | 1 |
Если вы подключили светодиод и резистор к выводу 7 и хотите, чтобы светодиод мигал с частотой 1 Гц, то должны записать в регистр управления значение 0b00010000. Если вам нужны импульсы 4,096 кГц, то вы должны записать 0b000100001. В этом случае, чтобы увидеть импульсы вам понадобится осциллограф, так как светодиод будет мигать так быстро, что будет казаться, что он светится постоянно. Мы будем использовать импульсы с частотой 1 Гц.
Основные принципы работы PID контроллера (регулятора)
Как мы уже выяснили, PID (ПИД) состоит из таких понятий как пропорциональный, интегральный, дифференциальный. Допустим, нам необходимо создать машину которая бы останавливалась бы строго в заданном месте. Без использования PID контроллера реализовать такой алгоритм действия машины достаточно сложно вследствие наличия у любого движущегося тела момента инерции.
Более подробно о том, что такое PID контроллер (регулятор) и как он работает, вы можете прочитать в Википедии. Но как применить принципы работы PID контроллера в микроконтроллере? Этой информации в Википедии уже нет.
Основные принципы работы PID контроллера показаны на следующей картинке. Рассмотрим кратко основные составляющие этого процесса.
PID контроллер получил такое название из-за принципов обработки сигналов ошибки, которые возникают в управляемом им процессе. На представленном рисунке вы можете увидеть, в пропорциональной части регулятора ошибка (сигнал ошибки) умножается на константу Kp. В интегральной части ошибка умножается на константу Ki и затем интегрируется, а в дифференциальной части ошибка умножается на константу Kd, а затем дифференцируется. После всего этого все эти три значения суммируются чтобы сформировать выходное значение регулятора. В PID контроллере параметры Kp, Kd и Ki называются коэффициентами усиления. Также они называются P, I и D параметрами регулятора. Эти коэффициенты настраиваются индивидуально, чтобы обеспечить выполнение заданного набора требований к системе, например, насколько чувствительной или устойчивой она должна быть. Рассмотрим каждый из этих параметров более подробно.
P-параметр (пропорциональный)
Допустим, ошибка в системе изменяется во времени, как показано красной линией на представленном рисунке. В пропорциональном контроллере эта ошибка умножается на коэффициент усиления Kp. То есть если ошибка большая, то и значение ошибки на выходе контроллера будет большим, если ошибка равна 0, то и на выходе будет 0, если ошибка отрицательная – на выходе также будет отрицательное значение.
I-параметр (интегральный)
В интегральном контроллере происходит суммирование всех значений ошибки, при этом полученная сумма умножается на константу Ki. В подобном контроллере легко видеть, что результатом интегрирования является область лежащая под кривой: на представленном рисунке эта кривая показана синим цветом для положительной области и желтым цветом для отрицательной области. В сложных системах интегральный контроллер используется для устранения постоянной ошибки, возникающей при работе системы
При этом неважно насколько мала величина ошибки, все равно суммирование достаточно большого их числа обеспечит достаточное значение на выходе контроллера. На представленном рисунке эта ошибка показана зеленым цветом
D-параметр (дифференциальный)
Дифференциальный контроллер показывает скорость изменения ошибки. Когда изменение величины ошибки происходит достаточно медленно (как на представленном рисунке), мы в качестве стартовой позиции (то есть кривой, аппроксимирующей это изменение) можем использовать синусоиду. При этом на выходе дифференциального контроллера будет маленькая величина – зеленая линия на представленном рисунке. И чем быстрее будет изменяться ошибка, тем больше будет становиться значение на выходе контроллера.
Значение на выходе PID контроллера является суммой с выхода трех рассмотренных контроллеров. Но необязательно чтобы всегда работали эти три контроллера, при желании любой из этих контроллеров можно выключить из работы, просто приравняв нулю коэффициент усиления в его ветви. К примеру, если мы установим D-параметр в ноль, то мы получим PI контроллер, а если мы установим I-параметр в ноль, то мы получим PD контроллер.
Код Ардуино с использованием прерываний
Чтобы поворотный энкодер работал, нам необходимо постоянно отслеживать изменения в сигналах DT и CLK. Чтобы определить, когда происходят такие изменения, мы можем постоянно их опрашивать (как мы это делали в нашем предыдущем скетче). Однако это не лучшее решение по нижеприведенным причинам:
- Мы должны постоянно выполнять проверку, чтобы увидеть, изменилось ли значение. Если уровень сигнала не изменится, циклы будут потрачены впустую.
- С момента возникновения события до момента проверки будет наблюдаться задержка. Если нам нужно отреагировать немедленно, мы будем остановлены этой задержкой.
- Можно полностью пропустить изменение сигнала, если длительность изменения коротка.
Широко распространенным решением является использование прерывания. С прерыванием вам не нужно постоянно опрашивать конкретное событие. Это освобождает Ардуино для выполнения какой-то другой работы, не пропуская событие.
Подключение энкодера к Ардуино с учетом прерывания
Поскольку большинство Ардуино (включая Ардуино UNO) имеют только два внешних прерывания, мы можем отслеживать только изменения в сигналах DT и CLK. Вот почему мы удалили соединение вывода SW с предыдущей схемы подключения.
Итак, теперь схема подключения выглядит так:
Некоторые платы (например, Ардуино Mega 2560) имеют больше внешних прерываний. Если у вас есть что-то из этого, вы можете оставить соединение для вывода SW и расширить рисунок ниже, чтобы использовать код и для кнопки.
Код Ардуино
Ниже приведен скетч, демонстрирующий использование прерываний при чтении углового энкодера:
// Входы поворотного энкодера #define CLK 2 #define DT 3 int counter = 0; int currentStateCLK; int lastStateCLK; String currentDir =""; void setup() { // Устанавливаем контакты энкодера как входы pinMode(CLK,INPUT); pinMode(DT,INPUT); // Настройка последовательного монитора Serial.begin(9600); // Считываем начальное состояние CLK lastStateCLK = digitalRead(CLK); // Вызов updateEncoder () при обнаружении любого изменения максимума/минимума при прерывании 0 (вывод 2) или прерывании 1 (вывод 3) attachInterrupt(0, updateEncoder, CHANGE); attachInterrupt(1, updateEncoder, CHANGE); } void loop() { //Здесь какой-либо полезный код } void updateEncoder(){ // Считываем текущее состояние CLK currentStateCLK = digitalRead(CLK); // Если последнее и текущее состояние CLK различаются, то произошел импульс. // Реагируем только на одно изменение состояния, чтобы избежать двойного счета if (currentStateCLK != lastStateCLK && currentStateCLK == 1){ // Если состояние DT отличается от состояния CLK, то // энкодер вращается против часовой стрелки, поэтому уменьшаем if (digitalRead(DT) != currentStateCLK) { counter --; currentDir ="CCW"; } else { // Энкодер вращается по часовой стрелке, поэтому увеличиваем counter ++; currentDir ="CW"; } Serial.print("Direction: "); Serial.print(currentDir); Serial.print(" | Counter: "); Serial.println(counter); } // Запоминаем последнее состояние CLK lastStateCLK = currentStateCLK; }
Обратите внимание, что основной цикл этой программы loop() остается пустым, поэтому Ардуино ничего не делает. Между тем, эта программа отслеживает изменение значения на цифровом выводе 2 (соответствует прерыванию 0) и цифровом выводе 3 (соответствует прерыванию 1)
Другими словами, программа ищет изменение напряжения с HIGH на LOW или с LOW на HIGH, которое происходит при повороте ручки
Между тем, эта программа отслеживает изменение значения на цифровом выводе 2 (соответствует прерыванию 0) и цифровом выводе 3 (соответствует прерыванию 1). Другими словами, программа ищет изменение напряжения с HIGH на LOW или с LOW на HIGH, которое происходит при повороте ручки.
Когда это происходит, вызывается функция updateEncoder() (часто называемая подпрограммой обслуживания прерывания или просто ISR ). Код в этой функции выполняется, а затем программа возвращается к исполнению основного кода с момента прерывания.
За все это отвечают две строки в коде. Эта функция attachInterrupt() которая сообщает Ардуино, какой вывод следует контролировать, какой ISR выполнять, если прерывание срабатывает, и какой тип триггера следует искать.
Кухонный таймер Ардуино с энкодером
Сейчас рассмотрим, как сделать таймер на Ардуино своими руками с энкодером и LCD. Принцип управления, подобен предыдущему варианту. Поворотом ручки энкодера можно задать необходимый временной интервал, а нажатием на ручку можно запускать и останавливать обратный отсчет времени. Далее размещена схема сборки проекта на Arduino Nano, этот проект можно собрать и на плате Arduino Uno.
Скетч таймера обратного отсчета времени
#include <Wire.h> // библиотека для протокола I2C #include <LiquidCrystal_I2C.h> // библиотека для LCD 1602 LiquidCrystal_I2C LCD(0x27, 20, 2); // присваиваем имя дисплею #include <RotaryEncoder.h> // библиотека для энкодера RotaryEncoder encoder(4, 2); // пины подключение энкодера (DT, CLK) // задаем шаг энкодера, максимальное и минимальное значение #define STEPS 1 #define POSMIN 0 #define POSMAX 30 int lastPos, newPos; boolean buttonWasUp = true; byte w = 0; int SEC = 0; int MIN = 0; unsigned long timer; void setup() { pinMode(6, INPUT_PULLUP); // пин для кнопки энкодера encoder.setPosition(0 / STEPS); pinMode(10, OUTPUT); // подключаем светодиод и зуммер pinMode(12, OUTPUT); digitalWrite(10, HIGH); LCD.init(); // инициализация дисплея LCD.backlight(); // включение подсветки LCD.setCursor(2, 0); LCD.print("TIMER STOP"); LCD.setCursor(5, 1); LCD.print(MIN); LCD.print(" : "); LCD.print(SEC); } void loop() { // проверяем положение ручки энкодера encoder.tick(); newPos = encoder.getPosition() * STEPS; if (newPos < POSMIN) { encoder.setPosition(POSMIN / STEPS); newPos = POSMIN; } else if (newPos > POSMAX) { encoder.setPosition(POSMAX / STEPS); newPos = POSMAX; } // если положение изменилось - меняем переменную MIN и выводим на дисплей if (lastPos != newPos) { MIN = newPos; lastPos = newPos; LCD.clear(); LCD.setCursor(2, 0); LCD.print("TIMER STOP"); LCD.setCursor(5, 1); LCD.print(MIN); LCD.print(" : "); LCD.print(SEC); } // если была нажата кнопка энкодера запускаем отсчет времени boolean buttonIsUp = digitalRead(6); if (buttonWasUp && !buttonIsUp && MIN > 0) { delay(10); buttonIsUp = digitalRead(6); if (!buttonIsUp) { if (SEC == 0) { SEC = 60; MIN = MIN - 1; } if (MIN < 0 ) { MIN = 0; } digitalWrite(10, LOW); w = 1; } } buttonWasUp = buttonIsUp; // запоминаем состояние кнопки while (w == 1 ) { // если прошло 995 мс - вычитаем одну секунду от переменной SEC if (millis() - timer > 993) { timer = millis(); SEC = SEC - 1; // если отсчет закончился - обнуляемся, включаем сигнал и выходим из цикла if (SEC == 0 && MIN == 0) { lastPos = 0; newPos = 0; MIN = 0; SEC = 0; LCD.clear(); LCD.setCursor(2, 0); LCD.print("TIMER STOP"); LCD.setCursor(5, 1); LCD.print(MIN); LCD.print(" : "); LCD.print(SEC); digitalWrite(10, HIGH); tone(12, 100); delay(500); noTone(12); w = 0; } // если секунды дошли до нуля - вычитаем одну минуту if (SEC == 0 && w==1) { SEC = 59; MIN = MIN - 1; if (MIN < 0 ) { MIN = 0; } } // если из цикла while еще не вышли - выводим информацию на дисплей if (w == 1) { LCD.clear(); LCD.setCursor(2, 0); LCD.print("TIMER START"); LCD.setCursor(5, 1); LCD.print(MIN); LCD.print(" : "); LCD.print(SEC); } } // если была нажата кнопка - обнуляем переменные и выходим из цикла buttonIsUp = digitalRead(6); if (buttonWasUp && !buttonIsUp) { delay(10); buttonIsUp = digitalRead(6); if (!buttonIsUp) { lastPos = 0; newPos = 0; MIN = 0; SEC = 0; LCD.clear(); LCD.setCursor(2, 0); LCD.print("TIMER STOP"); LCD.setCursor(5, 1); LCD.print(MIN); LCD.print(" : "); LCD.print(SEC); digitalWrite(10, HIGH); w = 0; } } buttonWasUp = buttonIsUp; // запоминаем состояние кнопки } }
Пояснения к коду:
- частоту звукового сигнала можно изменить через команду tone();
- для скетча потребуется установить библиотеку RotaryEncoder.
Проблемы с контекстом прерываний
Для обмена данными между кодом обработчика прерывания и остальной частью вашей программы необходимо принять дополнительные меры.
Переменные обычно должны быть типа . говорит компилятору о необходимости избегать оптимизаций, которые предполагают, что переменная не может спонтанно измениться. Поскольку ваша функция может изменять переменные, пока ваша программа использует их, компилятор нуждается в этой подсказке. Но одного часто не хватает.
При доступе к общим переменным прерывания, как правило, должны быть отключены. Даже с , если обработчик прерывания изменит многобайтную переменную между последовательностью команд, эта переменная может быть прочитана неправильно. Если данные состоят из нескольких переменных, например, массив и счетчик, прерывания, как правило, должны быть отключены на протяжении всего кода, который получает доступ к данным.
Работа схемы
Структурно рассматриваемое нами устройство состоит из 4-х модулей: модуль датчиков, модуль управления, модуль отображения и модуль реле.
В модуле датчиков, схема которого представлена на рисунке ниже, мы имеем два инфракрасных датчика с инфракрасными диодами, потенциометр, компаратор (на операционном усилителе) и светодиоды. Потенциометр предназначен для установки опорного напряжения компаратора (для регулировки чувствительности устройства). Инфракрасные датчики обнаруживают отсутствие или присутствие объекта (человека) в поле своего действия и выдают соответствующие сигналы на компаратор. Компаратор сравнивает два напряжения и обеспечивает соответствующий цифровой сигнал на своем выходе. В представленном устройстве мы использовали два компаратора (поскольку у нас два модуля инфракрасных датчиков), реализованных на микросхеме LM358.
Модуль управления построен на основе платы Arduino UNO, которая используется для управления всем устройством. Выходы компараторов подсоединены к цифровым контактам 14 и 19 Arduino. Плата Arduino считывает сигналы с этих цифровых контактов и на их основе выдает соответствующие управляющие сигналы на реле, которое управляет включением и выключением цепи электрической лампочки (bulb).
Модуль отображения состоит из ЖК дисплея 16×2, на котором будет отображаться число людей, находящихся в комнате и статус света (включен/выключен) когда никого нет в комнате.
Модуль реле состоит из транзистора BC547 и реле на 5 В, управляющим включением/выключением электрической лампочки. Транзистор необходим для управления реле поскольку плата Arduino не обеспечивает необходимые значения токов и напряжений для этого. Плата Arduino будет подавать управляющие команды на транзистор, а он с помощью реле будет включать и выключать электрическую лампочку.
Полная схема устройства представлена на следующем рисунке.
Выходы инфракрасных датчиков непосредственно подключены к цифровым контактам Arduino 14(A0) и 19(A5). База транзистора через резистор подключена к контакту 2 Arduino. ЖК дисплей подсоединен к Arduino в 4-битном режиме. Контакты RS и EN ЖК дисплея подключены к контактам 13 и 12 Arduino. Контакты данных ЖК дисплея D4-D7 подключены к контактам 11-8 Arduino.
Простейший вариант
Пример конструктора для самодельной сборки таймера задержки отключения:
При желании возможно самостоятельно собрать реле времени по следующей схеме:
Времязадающим элементом является конденсатор С1, в стандартной комплектации КИТ-набора он имеет следующие характеристики: 1000 мкФ/16 В, время задержки в этом случае составляет приблизительно 10 минут. Регулировка времени осуществляется переменным резистором R1. Питание платы 12 Вольт. Управление нагрузкой производится через контакты реле. Плату можно не делать, а собрать на макетной плате или навесным монтажом.
Для того, чтобы сделать реле времени, нам понадобятся следующие детали:
Правильно собранное устройство не нуждается в настройке и готово к работе. Данное самодельное реле задержки времени было описано в журнале «Радиодело» 2005.07.
Подключение DS1307 к дисплею LCD 1602 i2c
Схема подключения часов реального времени с дисплеем
Рассмотрим два варианта подключения модуля RTC к Arduino и текстового дисплея 1602. В первом варианте используется протокол SPI для модуля часов, поэтому к предыдущей схеме потребуется добавить только дисплей с iic модулем. А в скетче следует расскомментировать соответствующую строку. После внесения правок в схему и программу — загрузите следующий скетч в микроконтроллер.
Скетч. Часы с экраном LCD 1602 и DS1302
#include <iarduino_RTC.h> iarduino_RTC time(RTC_DS1302,6,8,7); // для модуля DS1302 - RST, CLK, DAT #include <Wire.h> // библиотека для устройств I2C #include <LiquidCrystal_I2C.h> // подключаем библиотеку для дисплея LiquidCrystal_I2C LCD(0x27,16,2); // присваиваем имя дисплею void setup() { delay(300); LCD.init(); // инициализация LCD дисплея LCD.backlight(); // включение подсветки дисплея time.begin(); time.settime(0, 30, 18, 12, 6, 20, 5); // 0 сек, 30 мин, 18 часов, 12, июня, 2020, четверг } void loop() { // если прошла 1 секунда выводим время на дисплей if (millis() % 1000 == 0) { LCD.setCursor(0,0); LCD.print(time.gettime("d M Y, D")); LCD.setCursor(4,1); LCD.print(time.gettime("H:i:s")); delay(1); } }
- в данном примере с помощью команды можно выводить на текстовый экран текущее время и дату в разном формате.
При подключении экрана и модуля RTC к одной шине iic — порты SDA(A4) и SCL(A5), следует указать в скетче какой модуль используется. Схема подключения размещена выше, кроме того вы можете использовать сканер шины i2c для того, чтобы увидеть адреса устройств. Если адреса, которые установлены производителем по умолчанию вы не изменяли — соберите схему и загрузите следующий пример в плату.
Схема подключения DS1307 и LCD по i2c
Скетч. Часы с LCD 1602 и DS1302 I2C Arduino
#include <iarduino_RTC.h> iarduino_RTC time(RTC_DS1307); // для модуля DS1307 с i2C #include <Wire.h> // библиотека для устройств I2C #include <LiquidCrystal_I2C.h> // подключаем библиотеку для дисплея LiquidCrystal_I2C LCD(0x27,16,2); // присваиваем имя дисплею void setup() { delay(300); LCD.init(); // инициализация LCD дисплея LCD.backlight(); // включение подсветки дисплея time.begin(); time.settime(0, 30, 18, 12, 6, 20, 5); // 0 сек, 30 мин, 18 часов, 12, июня, 2020, четверг } void loop() { // если прошла 1 секунда выводим время на дисплей if (millis() % 1000 == 0) { LCD.setCursor(0,0); LCD.print(time.gettime("d M Y, D")); LCD.setCursor(4,1); LCD.print(time.gettime("H:i:s")); delay(1); } }
ДОКУМЕНТАЦИЯ
Документация
Конструктор
Класс GTimer позволяет работать как с миллисекундным, так и с микросекундным таймером. В общем виде пример выглядит так:
Где type это MS (,мс, миллисекундный таймер) или US (мкс, микросекундный), period – период в мс или мкс соответственно.
Настройки по умолчанию
- При создании таймера можно ничего не указывать : GTimer myTimer; , тогда таймер будет сконфигурирован как миллисекундный и не запустится
- Если указать только тип таймера (MS/US) GTimer myTimer(MS); , таймер настроится на выбранный режим (мс/мкс) и не запустится
- Если указать тип таймера и интервал GTimer myTimer(US, 5000); , таймер настроится на выбранный режим (мс/мкс) и запустится в режиме интервала
Режимы работы
Таймер может работать в режиме интервалов и в режиме таймаута:
- Интервалы. Запуск – метод setInterval(время) с указанием времени. В режиме интервалов таймер срабатывает (метод isReady() возвращает true) каждый раз при достижении указанного периода и автоматически перезапускается. Удобно для периодических действий
- Таймаут. Запуск – метод setTimeout(время) с указанием времени. В режиме таймаута таймер срабатывает (метод isReady() возвращает true) только один раз при достижении указанного периода и автоматически отключается. Для повторного запуска нужно вызвать .setTimeout() с указанием периода, или просто .start() – запустит таймер на новый круг с прежним периодом
Управление таймером
Для управления состоянием таймера есть следующие методы:
- start() – запускает (перезапускает) таймер с последним установленным временем
- stop() – останавливает таймер
- resume() – продолжает отсчёт таймера с момента остановки
- reset() – сбрасывает таймер (отсчёт периода/таймаута начинается заново)
- isEnabled() – возвращает true, если таймер работает (если он не stop() или не вышел таймаут)
Таймер с обратным отсчетом на Arduino.
https://youtube.com/watch?v=gMJ1K1JJ8TM
Ссылки на компоненты:
Часы реального времени Ds3231 – http://ali.pub/enqbc
Модуль расширителя интерфейса (I2C)- http://ali.pub/dwj5n
ЖК-дисплей LCD1602 (синий экран)- http://ali.pub/alnru
Таймер с обратным отсчетом на Arduino
Деньги можно перечислить на карту Сбербанка России:
4276 5400 2194 5088
Контроллер для сауны
Подпишитесь!
2015, Arduinoprom.ru – блог Чилингаряна Грачика. Все авторские права на тексты принадлежат ему.
При размещении текстов и видеоматериалов на сторонних ресурсах активная гиперссылка ОБЯЗАТЕЛЬНА.
Все логотипы и товарные знаки, размещенные на сайте, принадлежат только их законным владельцам (правообладателям).
РАБОТАЕМ ПО ТАЙМЕРУ В ARDUINO
Я думаю все знают классический алгоритм создания таймера на millis() – счётчике аптайма:
Несколько таймеров
Данный алгоритм позволяет спокойно переходить через переполнение millis() без потери периода, но имеет один большой минус – время считается с момента последнего вызова таймера, и при наличии задержек в коде таймер будет накапливать погрешность, отклоняясь от “ритма”. Недавно я придумал более точный алгоритм таймера на миллис, который соблюдает свой период даже после пропуска хода!
Улучшенный таймер
Данный таймер имеет механику классического таймера с хранением переменной таймера, а его период всегда кратен PERIOD и не сбивается. Эту конструкцию можно упростить до
В этом случае алгоритм получается короче, кратность периодов сохраняется, но теряется защита от пропуска вызова и переполнения millis().
В этой библиотеке реализован полноценный таймер на счётчике аптайма, позволяющий даже “приостановить” счёт (не приостанавливая сам аптайм).
Примечание: таких таймеров можно создать сколько душе угодно (пока хватит памяти), что позволяет создавать сложные программы с кучей подзадач, но для функционирования данного таймера нужен “чистый” loop с минимальным количеством задержек, или вообще без них. Всё таки таймер “опрашивается” в ручном режиме. Таймер, который не боится задержек, делается на прерывании таймера, смотрите вот эту библиотеку.
Защита от помех DC
Раздельное питание
Один из лучших способов защититься от помех по питанию – питать силовую и логическую части от отдельных источников питания: хороший малошумящий источник питания на микроконтроллер и модули/сенсоры, и отдельный на силовую часть. В автономных устройствах иногда ставят отдельный аккумулятор на питание логики, и отдельный мощный – на силовую часть, потому что стабильность и надёжность работы очень важна.
Искрогасящие цепи DC
При размыкании контактов в цепи питания индуктивной нагрузки происходит так называемый индуктивный выброс, который резко подбрасывает напряжение в цепи вплоть до того, что между контактами реле или выключателя может проскочить электрическая дуга (искра). В дуге нет ничего хорошего – она выжигает частички металла контактов, из за чего они изнашиваются и со временем приходят в негодность. Также такой скачок в цепи провоцирует электромагнитный выброс, который может навести в электронном устройстве сильные помехи и привести к сбоям или даже поломке! Самое опасное, что индуктивной нагрузкой может являться сам провод: вы наверняка видели, как искрит обычный выключатель света в комнате. Лампочка – не индуктивная нагрузка, но идущий к ней провод имеет индуктивность. Для защиты от выбросов ЭДС самоиндукции в цепи постоянного тока используют обыкновенный диод, установленный встречно-параллельно нагрузке и максимально близко к ней. Диод просто закоротит на себя выброс, и все дела:
Где VD – защитный диод, U1 – выключатель (транзистор, реле), а R и L схематично олицетворяют индуктивную нагрузку. Диод нужно ОБЯЗАТЕЛЬНО ставить при управлении индуктивной нагрузкой (электромотор, соленоид, клапан, электромагнит, катушка реле) при помощи транзистора, то есть вот так:
При управлении ШИМ сигналом рекомендуется ставить быстродействующие диоды (например серии 1N49xx) или диоды Шоттки (например серии 1N58xx), максимальный ток диода должен быть больше или равен максимальному току нагрузки.
Фильтры
Если силовая часть питается от одного источника с микроконтроллером, то помехи по питанию неизбежны. Простейший способ защитить МК от таких помех – конденсаторы по питанию как можно ближе к МК: электролит 6.3V 470 uF (мкФ) и керамический на 0.1-1 мкФ, они сгладят короткие просадки напряжения. Кстати, электролит с низким ESR справится с такой задачей максимально качественно.
Ещё лучше с фильтрацией помех справится LC фильтр, состоящий из индуктивности и конденсатора. Индуктивность нужно брать с номиналом в районе 100-300 мкГн и с током насыщения больше, чем ток нагрузки после фильтра. Конденсатор – электролит с ёмкостью 100-1000 uF в зависимости опять же от тока потребления нагрузки после фильтра. Подключается вот так, чем ближе к нагрузке – тем лучше:
Подробнее о расчёте фильтров можно почитать здесь.
Важные страницы
- Набор GyverKIT – большой стартовый набор Arduino моей разработки, продаётся в России
- Каталог ссылок на дешёвые Ардуины, датчики, модули и прочие железки с AliExpress у проверенных продавцов
- Подборка библиотек для Arduino, самых интересных и полезных, официальных и не очень
- Полная документация по языку Ардуино, все встроенные функции и макро, все доступные типы данных
- Сборник полезных алгоритмов для написания скетчей: структура кода, таймеры, фильтры, парсинг данных
- Видео уроки по программированию Arduino с канала “Заметки Ардуинщика” – одни из самых подробных в рунете
- Поддержать автора за работу над уроками
- Обратная связь – сообщить об ошибке в уроке или предложить дополнение по тексту (alex@alexgyver.ru)
Сборка
При реализации проекта я столкнулся с двумя основными трудностями:
- Как определить, работает ли помпа, чтобы знать, когда начинать и заканчивать отсчет.
- Как коммутировать 170В на 20 анодов (по 10 на каждую лампу).
Так как делал я все это ради удовольствия, то решил дать волю инженерной мысли. Мне захотелось выяснить, удастся ли обнаружить магнитное поле помпы, чтобы определять момент готовности шота. Это бы также позволило обойтись без лишних вмешательств в машину – эдакий своеобразный малоинвазивный подход. В довершение ко всему, один близкий друг предоставил свою мастерскую и дал некоторые дельные советы, посодействовав в сборке удачного корпуса. Еще раз спасибо, Нилс!
Датчик паров спирта MQ-3 Ардуино
Сенсор паров спирта MQ-3 Ардуино
Сенсор MQ-3 — недорогой полупроводниковый прибор для измерения наличия паров спирта в воздухе при концентрации от 0,05 мг/л до 10 мг/л. Алкотестер на основе чувствительного элемента SnO2 использует химическую реакцию для определения уровня алкоголя. У этого сенсора в чистом низкая воздухе электропроводность, которая повышается линейно при росте концентрации паров алкоголя.
MQ-3 обладает высокой чувствительностью и защищен от помех, например, дыма и паров углеводородных газов. Аналоговый датчик имеет 4 вывода: питание VCC и земля Gnd, а также цифровой и аналоговый вывод. Для точных измерений необходимо достичь рабочей температуры при помощи встроенного нагревателя (около 40 градусов), подержав MQ-3 Arduino во включенном состоянии в течение 10 минут.
Реле времени на микросхемах
Транзисторные схемы таймеров имеют много недостатков: сложность определения времени задержки, необходимость разрядки конденсатора перед следующим пуском, малые интервалы срабатывания. Микросхема NE555, получившая название «интегральный таймер», давно завоевала популярность. Ее применяют в промышленности, но можно увидеть множество схем, по которым делают реле времени своими руками.
Временная выдержка задается сопротивлениями R2, R4 и конденсатором С1. Контакт подключения нагрузки К1.1 замыкается при нажатии на кнопку SB1, а затем он самостоятельно размыкается после задержки, продолжительность которой определяется из формулы: tи = 1.1R2∙R4∙C1.
При повторном нажатии на кнопку процесс повторяется.
Во многих бытовых приборах применяются микросхемы с реле времени. Инструкция для пользования — это необходимый атрибут правильной эксплуатации. Она также составляется для таймеров, созданных своими руками. От этого зависит их надежность и долговечность.
Схема работает от простейшего блока питания на 12 В из трансформатора, диодного моста и конденсатора. Ток потребления составляет 50 мА, а реле коммутирует нагрузку до 10 А. Регулируемую задержку можно сделать от 3 до 150 с.