В продолжение курса по обучению работы с микроконтроллером Arduino собирём трёх-цветный светофор с несколькими режимами работы. Научимся управлять сразу несколькими светодиодами, а также реализуем задержку в неблокирующем режиме (без использования функции delay).
В продолжение нашего знакомства с Arduino сегодня мы рассмотрим следующий простейший пример – светофор, какой мы видим каждый день на дорогах, который будет включаться, и выключаться по нажатию кнопки.
Необходимые компоненты
- 3 светодиода (зелёный, жёлтый и красный)
- Push-кнопка
- 3 резистора на 220 [Ом]
- Резистор на 10 [кОм]
Сбор схемы
Соединяем 13 пин Arduino с длинной ногой зелёного светодиода, а короткую ногу через резистор в 220 [Ом] соединяем с “землёй”. Аналогично соединяем жёлтый светодиод через 12 пин и красный через 11 пин. 7 пин через резистор 10 кОм подключим к “земле” и к кнопке.
Схема, необходимая для работы светофора собрана, теперь необходимо подключить кнопку, для управления им.
При установке кнопки есть некоторая хитрость – кнопку надо устанавливать на разделительную полосу между двумя половинками брэдборда.
Теперь подключаем кнопку. Тут всё очень просто – кнопка проводит в обоих направлениях, в отличие от различных диодов, поэтому нет никакой разницы, какой из контактов будет соединён с землёй. Соедините один контакт кнопки с портом №7 Arduino и этот же контакт соедините через резистор в 10 [кОм] с Землёй, а другой с питанием в 5 [В] – рельса со значком +.
Сборка на этом закончена! Приступаем к программированию Arduino!
Программируем Arduino
// Задаём номера портов для светодиодов
int GPin = 13; // Зелёный
int YPin = 12; // Жёлтый
int RPin = 11; // Красный
int switchPin = 7; // Порт кнопки
boolean lastButton = LOW; // Устанавливаем последнее значение кнопки - выключено
boolean currentButton = LOW; // Устанавливаем текущее значение кнопки - включено
boolean ledOn = false; // Состояние светофора: false - выключен (мигает жёлтый), true - включен
boolean yellowOn = LOW; // Мигающий жёлтый.
unsigned long loopTime; // Вспомогающая переменная для цикла
unsigned long currentTime; // Переменная хранящая текущее значение времени
void setup() {
// Устанавливаем 3 порта как выход для светодиодов и один как вход для отслеживания кнопки
pinMode(GPin, OUTPUT);
pinMode(YPin, OUTPUT);
pinMode(RPin, OUTPUT);
pinMode(switchPin, INPUT);
currentTime = millis();
loopTime = currentTime;
}
// Функция для лучшего отрабатывания нажатия на кнопку - ждёт установившегося значения, а затем передаёт его в программу.
boolean debounce(boolean last)
{
boolean current = digitalRead(switchPin);
if(last != current)
{
delay(5);
current = digitalRead(switchPin);
}
return current;
}
void loop() {
currentButton = debounce(lastButton);
if(lastButton == LOW && currentButton == HIGH) // При нажатии на кнопку включаем\выключаем светофор
{
ledOn = !ledOn;
}
lastButton = currentButton;
currentTime = millis();
// Цикл действий при выключенном светофоре
if(currentTime >= (loopTime + 500) && ledOn == false)
{
yellowOn = !yellowOn; // Инвертируем значение переменной.
digitalWrite(GPin, LOW);
digitalWrite(YPin, yellowOn);
digitalWrite(RPin, LOW);
loopTime = currentTime;
}
// Цикл действий при включенном светофоре
if(ledOn == true) {
// Горит зелёный
if(currentTime >= loopTime && currentTime < (loopTime + 10000)) {
digitalWrite(GPin, HIGH);
digitalWrite(YPin, LOW);
digitalWrite(RPin, LOW);
}
// Зелёный начинает мигать
if(currentTime >= (loopTime + 10000) && currentTime < (loopTime + 10500)) {
digitalWrite(GPin, LOW);
}
if(currentTime >= (loopTime + 10500) && currentTime < (loopTime + 11000)) {
digitalWrite(GPin, HIGH);
}
if(currentTime >= (loopTime + 11000) && currentTime < (loopTime + 11500)) {
digitalWrite(GPin, LOW);
}
if(currentTime >= (loopTime + 11500) && currentTime < (loopTime + 12000)) {
digitalWrite(GPin, HIGH);
}
if(currentTime >= (loopTime + 12000) && currentTime < (loopTime + 12500)) {
digitalWrite(GPin, LOW);
}
if(currentTime >= (loopTime + 12500) && currentTime < (loopTime + 13000)) {
digitalWrite(GPin, HIGH);
}
if(currentTime >= (loopTime + 13000) && currentTime < (loopTime + 13500)) {
digitalWrite(GPin, LOW);
}
if(currentTime >= (loopTime + 13500) && currentTime < (loopTime + 1400)) {
digitalWrite(GPin, HIGH);
}
if(currentTime >= (loopTime + 14000) && currentTime < (loopTime + 14300)) {
digitalWrite(GPin, LOW);
}
// Загорается жёлтый
if(currentTime >= (loopTime + 14300) && currentTime < (loopTime + 17500)) {
digitalWrite(YPin, HIGH);
}
// Загорается красный
if(currentTime >= (loopTime + 17500) && currentTime < (loopTime + 27500)) {
digitalWrite(YPin, LOW);
digitalWrite(RPin, HIGH);
}
// Загорается красный с жёлтым
if(currentTime >= (loopTime + 27500) && currentTime < (loopTime + 30500)) {
digitalWrite(YPin, HIGH);
}
// Загорается зелёный
if(currentTime >= (loopTime + 30500)) {
digitalWrite(GPin, HIGH);
digitalWrite(YPin, LOW);
digitalWrite(RPin, LOW);
loopTime = currentTime;
}
}
}
Код достаточно прост. Если светофор выключен, то просто мигает жёлтым, иначе включаем и выключаем определённые светодиоды через определённые промежутки времени.
Конечно, вместо столько сложного метода мы могли бы использовать простую задержку методом delay(), однако в использовании функции delay() есть один очень большой минус, который не должна обладать наша программа. Во время паузы – процессор не реагирует на другие операции, таким образом, во время задержки, например, на горение красного светодиода, сколько кнопку не нажимай – светофор не выключиться. Чтобы выключить светофор – вам придётся попадать по кнопке в тот момент, когда изменяется состояние светодиода, что, согласитесь, очень неудобно. Поэтому мы используем метод millis(), которые возвращает количество миллисекунд, прошедшее с момента запуска приложения, и отсчитываем нужные нам интервалы времени.
Ну что, сохраняем нашу программу (скетч) и загружаем её в Arduino. Смотрим что у нас получилось.
Задавайте любые, интересующие Вас, вопросы, а я отвечу на них в комментариях к этому посту. До скорых встреч!
Подскажите, почему у меня все время горит зеленый?
где ошибка? 🙂
Кстати, во время компиляции выдает ошибку на последнюю фигурную скобку
error: expected declaration before ‘}’ token
у вас просто последние строки закоментированы
Ответ для Владимир: Спасибо за помощь. Действительно код был неправильно интерпретирован блогом. Исправил.Спасибо
Как вариант кнопку можно повесить на внешнее прерывание, тогда нажатие во время паузы Delay() будет отрабатываться правильно.
Ответ для Вася Пупкин: именно так. Спасибо