В прошлом уроке мы поняли принцип считывания значения с матричной клавиатуры и написали простой код, который реализует данное действие. Прежде чем переходить к более сложной задаче – сохранению введённого значения с клавиатуры, попробуем реализовать функцию работы с замком разного типа, и открытию его по кнопке с обратной стороны двери, а также сделаем звуковую и световую индикацию при открытии.
Мы рисовали общую схему нашего устройства, но сейчас посмотрим на элементы, с которыми мы будем работать в данном уроке. Собственно – их 5. – это зуммер, реле с 2 типами контактов, светодиод, показывающий состояние прохода и кнопка мгновенного действия. Подготовим для всех них понятные имена
#define ledAccess 8 // Светодиод - разрешающий
#define ledDenied 9 // Светодиод - запрещающий
#define buttonOpen 10 // Кнопка разблокировки
#define lock 11 // Контакт замка
#define buzzer 12 // Контакт динамика
Также нам понадобится новая переменная – флаг, который будет указывать тип замка. Замок у нас может быть электромагнитный или электромеханический – отличаются по принципу действия. Если на магните нужно снимать напряжение в момент открытия, то на механике, наоборот подавать напряжение на соленойд для открытия.
bool lockType = 1; // Тип замка - электромеханический = 1. Электромагнитный = 0
Теперь зададим режим работы пинов нашей периферии. Как не сложно догадаться на вход у нас будет настроен только контакт кнопки, все остальные устройства являются исполнительными и будут настроены на выход. Чтобы не вешать внешнее сопротивление, настроим внутреннюю резистивную подтяжку на входе кнопки.
pinMode(buttonOpen, INPUT); // контакт кнопки на вход с подтяжкой
digitalWrite(buttonOpen, HIGH);
pinMode(lock, OUTPUT); // контакт замка на выход
pinMode(ledAccess, OUTPUT); // разрешающий светодиод
pinMode(ledDenied, OUTPUT); // запрещающий светодиод
pinMode(buzzer, OUTPUT); // зуммер
Первым делом попытаемся избавиться от задержки delay() в ардуино, чтобы во время ожидания, наш контроллер мог решать другие задачи. Нам это нужно будет, для управлением временем открытия замка, зажиганию диодов, подачи звука, и чтобы в это время мы могли решать другие задачи, такие как, опрос клавиатуры, кнопки выхода и считывателя.
К счастью, в ардуино уже есть для этого инструменты. Попытаюсь дать короткое объяснение, почему delay это плохо. Для этого отвлечёмся и представим абстрактную задачу, где один светодиод у нас моргает каждую 1 секунду, а другой, каждые 0,2 секунды. Возьмём обычный пример мигания через delay
void setup()
{
pinMode(8, OUTPUT); // Пины 8 и 9 конфигурируем на выход
pinMode(9, OUTPUT);
}
void loop()
{
digitalWrite(8, HIGH); // В этом блоке подаём 5в на pin8, ждём 1 секунду, снимаем напряжение, опять ждём 1 секунду
delay(1000);
digitalWrite(8, LOW);
delay(1000);
digitalWrite(9, HIGH); // В этом блоке подаём 5в на pin9, ждём 200мс, снимаем напряжение, опять ждём 200мс
delay(200);
digitalWrite(9, LOW);
delay(200);
}
Естественно, в данном примере, они мигают по очереди, потому-что пока выполняется delay, контроллер делает бесполезные операции в цикле. Можно конечно сделать мигание второго диода в прерывании, но опять же, использовать там delay не получится, чтобы выдержать 200мс.
Можно извратиться, и рассчитать задержки свечения светодиода, который мигает чаще, так, чтобы создать второму диоду нужные 1 секунду простоя и свечения.
void setup()
{
pinMode(8, OUTPUT); //Пины 8 и 9 конфигурируем на выход
pinMode(9, OUTPUT);
}
void loop()
{
digitalWrite(8, HIGH);
digitalWrite(9, HIGH); // набираем задержку 1000мс блоками из 200мс
delay(200); // первые 200мс
digitalWrite(9, LOW);
delay(200); // уже 400мс
digitalWrite(9, HIGH);
delay(200); // 600мс
digitalWrite(9, LOW);
delay(200); // теперь 800мс
digitalWrite(9, HIGH);
delay(200); // набрали 1000мс, можно погасить led8
digitalWrite(8, LOW);
digitalWrite(9, LOW); //опять набираем задержку 1000мс блоками из 200мс
delay(200);
digitalWrite(9, HIGH);
delay(200);
digitalWrite(9, LOW);
delay(200);
digitalWrite(9, HIGH);
delay(200);
digitalWrite(9, LOW);
delay(200);
}
Всё работает как надо, но согласитесь, это конкретное извращение. Также, в такой код будет невероятно трудно вставить другие задачи, требующие многозадачности.
Как ещё можно реализовать задержку, чтобы в это время, можно было делать другой код? Самое лучшее решение – запустить условие проверки с некой переменной, которая постоянно увеличивает значение. В ардуино уже реализована такая переменная millis() , которая увеличивается каждую миллисекунду на 1. Её максимальное значение равно 4294967295мс, что приблизительно составляет почти 50 суток. Пока не будем думать, о том, что она может переполнится (в этом ничего страшного нет), и посмотрим, как мы можем использовать эту переменную
unsigned long currentTime; // Переменная текущего времени
if (millis() - currentTime > 1000) // Если время контроллера millis, больше переменной на 1000, то запускаем условие if
{
currentTime = millis(); // Приравниваем переменную текущего времени к времени контроллера, чтобы через 1000мс опять сработал наш цикл.
//здесь любое действие.
}
Всё просто! Мы используем вспомогательную переменную текущего времени currentTime, На старте её значение равно = 0, как и millis() = 0. Но millis() у нас постоянно увеличивается, а currentTime – нет. Поэтому через 1000мс, сработает условие if (millis() – currentTime > 1000), т.е. мы создали задержку в 1с, но код при этом выполнялся циклично в loop, и мы могли успеть обработать много периферии. Потом мы просто обновляем нашу переменную currentTime, и она снова ждёт сравнения с millis(), когда она вырастет на 1000мс. Попробуем сделать вышестоящую задачу, мигание двумя светодиодами с разной задержкой. но при этом вставим в loop какой нибудь код, пусть будет отправка в терминал, и проверим, как это работает. Я сделал для наглядности вторую переменную currentTime2, для второго светодиода.
unsigned long currentTime; // Переменная времени диода 8
unsigned long currentTime2; // Переменная времени диода 9
bool ledState=0; // Переменная состояния диода 8
bool ledState2=0; // Переменная состояния диода 9
unsigned long a; // Переменная для проверки работоспособности кода в условиях задержки
void setup()
{
Serial.begin(9600); // Конфигурируем серийный порт
pinMode(8, OUTPUT); //Пины 8 и 9 конфигурируем на выход
pinMode(9, OUTPUT);
}
void loop()
{
if (millis() - currentTime > 1000) // Проверяем время для первого диода (1000мс)
{
currentTime = millis();
ledState=!ledState; // Меняем состояние первого диода на противоположное
digitalWrite(8, ledState);
}
if (millis() - currentTime2 > 200) // Проверяем время для второго диода (200мс)
{
currentTime2 = millis();
ledState2=!ledState2; // Меняем состояние второго диода на противоположное
digitalWrite(9, ledState2);
}
a++;
Serial.println (a);
}
Для состояний светодиодов, возьмём переменную bool ledState и ledState2. Мы задали её тип как boolean, а это значит что она может принимать два значения – true и false, что в принципе равно 0 и 1. Чтобы не писать по несколько раз HIGH, LOW, мы подставляем туда состояние светодиода ledState. А меняем его каждый раз логическим отрицанием (!) на противоположное значение.
ledState=!ledState;
В конце цикла loop, впишем команду инкрементирования переменной a++, и отправки значения в серийный порт, чтобы отследить, будет ли выполняться этот код в момент ожидания задержек.
a++;
Serial.println (a);
Смотрим, что получилось
Видно, что теперь у нас и светодиоды моргают с заданными задержками (1000мс и 200мс) и в серийный порт с постоянством отправляется новая переменная. Отлично – теперь мы можем получить какие-то задатки многозадачности, и продолжить писать код для нашего устройства в следующей статье.
Добавить комментарий
Для отправки комментария вам необходимо авторизоваться.