Вступление
Становится уже интереснее. Давайте соберем модель светофора!
В этом эксперименте не будет новых знаний по Ардуино, зато мы изучим пару новых фишек программирования.
Давайте решим как должен работать наш светофор:
- Предположим, сначала горит зеленый.
- Затем, когда его время истекает, он начинает мигать; предположим, что с интервалом в 1 сек. 3 раза.
- Потом ненадолго зажигается желтый (1сек).
- И включается красный.
- Спустя несколько секунд цикл повторяется
Необходимые компоненты
- 3 LED M5: красный, зеленый и желтый
- 3 резистора на 220R
Схема
Соберем вот эту схему:
Сборка аналогична опыту 2, но тут у нас уже 3 светодиода, и мы задействуем 3 цифровых порта. Каждый светодиод должен быть защищен своим резистором 220 Ом.
Обратите внимание на полярность светодиода: длинная ножка – это плюс, который подключается к цифрому порту, а короткая ножка соединяется с массой (GND).
Сопротивление ставится в разрыв цепи, все равно с какой стороны светодиода – плюса или земли. На схеме сопротивление поставлено со стороны земли (GND).
Вы можете использовать любые цифровые пины ардуино от 2 до 12. Сделайте это как вам удобней, но не забудьте скорректировать номера пинов в скетче.
Скетч
Наш первый скетч выглядит вот так:
[post-content id=706]
Результат
Светофор горит как задумано:
Объяснение
В скетче даны подробные комментария.
Обратите внимание, что, согласно лучших практик, все параметры приложения, сразу вынесены в глобальные константы в начале скетча. Всегда старайтесь выносить все параметры в глобальные константы в начале скетча. Это облегчит модернизацию и настройку и в дальнейшем.
Для простоты настройки и тестирования светофора, введен мультипликатор, коэффициент, с помощью которого можно изменять скорость работы светофора:
const float delayMultiply = 0.1; // число миганий
Цикл for
В скетче мы встретили новое ключевое слово языка C/С++ – for, с помощью которого нужная подпрограмма выполняется циклически заданное число раз. Рассмотрим ее пример:
for ( i = 0; i < 5; ++i) {
// …. Повторяемый код
}
i – это переменная, которая хранит номер текущего цикла. Назовем ее счетчиком. Счетчик может начинаться с любого числа. В данном случае он равен 0.
i < 5 задает условие, при котором цикл продолжается.
++ – это инкремент, т.е. увеличение значения на 1. Вместо этого инкремента может быть и другое выражение, которое изменяет счетчик циклов, например декремент –i. Также значения счетчика может быть изменено и внутри самой подпрограммы цикла.
Если мы хотим выйти из цикла, по условию, не выполняя все циклы, можно использовать оператор break («прерывать»), вот так:
for (i = 0; i < 5; ++i) {
Serial.println("Уроки Arduino на jarduino.ru");
break;
}
В данном случае, цикл будет выполнено всего 1 раз, поскольку в конце подпрограммы стоит принудительное завершение цикла с помощью оператора break.
Функции
Обратите внимание на большое число повторяющихся похожих блоков кода. Блоки отличаются лишь номером пина, и временем задержки.
Согласитесь, что повторять код – не очень рационально. Ведь в случае если нам понадобится внести в работу светофора какое-то изменение или усовершенствование, нам придется его проделать сразу во многих местах программы.
Более правильным здесь будет написать пару функций.
Функция – это именованная подпрограмма, которая принимает входные параметры (обычно есть хотя бы один параметр), использует их внутри себя (в теле функции) и может возвращать выходные параметры.
Давайте сделаем две функции:
- lightUp – зажигает заданный светодиод на заданное время.
- blink – мигает заданным светодиодом заданное число раз с заданным интервалом.
Каждая из этих функций будет иметь входные параметры:
- lightUp:
- pinLed – пин светодиода
- nDelay – продолжительность свечения светодиода
- blink
- pinLed – пин светодиода
- nDelay – продолжительность свечения светодиода и, одновременно, продолжительность паузы до следующего включения.
- nTimes – число циклов мигания
Все параметры будут целыми (int) константами (const). Запомните: если параметр функции или иная переменная не должна изменяться в ходе работы программы, настоятельно рекомендуется объявлять их как контенту, используя ключевое слово const. Это сократит число возможных ошибок в программе.
Для определения функции используется следующий шаблон:
<тип возвращаемого значения> <название функции>(<список параметров>) {
<подпрограмма - тело функции>
}
Список параметров – это перечисление через запятую объявление переменных, которые одновременно будут параметрами данной функции.
О возвращаемых параметрах мы расскажем в следующих экспериментах. Наши функции не будут возвращать никакие параметры. В этом случае в качестве типа возвращаемого параметра надо указывать тип void.
Вот так будет выглядеть наша первая функция, зажигающая светодиод на заданное время:
// Функция зажигает заданный светодиод на заданное время и гасит его.
void lightUp(const int pinLed, const int nDelay)
{
digitalWrite(pinLed, HIGH); //// turn on green LED
delay(nDelay);
digitalWrite(pinLed, LOW); // turn off green LED
}
Вот так мы ее используем:
lightUp(pinGrenLed, lightDelay); // зажечь светодиод, подключенный к пину pinGrenLed на lightDelay мсек.
Посмотрите как изменился наш скетч после введения функций:
/**
* (C) jarduino.ru 2019
* Изучение ардуино через опыты.
*
* Опыт №4.2 Светофор, продолжение
*
* Использование функций для минимизации повторяющихся фрагментов
*/
// Глобальные константы
const int pinRedLed = 12; // красный подключен к порту 12
const int pinYellowLed = 9; // желтый подклчен к порту 9
const int pinGrenLed = 6; // зеленый подключен к порту 6
const int lightDelay = 5000; // продолжительность свечения зеленого и крассного
const int blinkDelay = 500; // полупериод мигания
const int yelloDelay = 1000; // полупериод мигания
const int blinkTimes = 3; // число миганий
const float delayMultiply = 0.1; // число миганий
// настройка платы
void setup()
{
// инициализировть цифровые порты светофора на вывод:
pinMode(pinRedLed, OUTPUT);
pinMode(pinYellowLed, OUTPUT);
pinMode(pinGrenLed, OUTPUT);
}
// Функция зажигает заданный светодиод на заданное время и гасит его.
void lightUp(const int pinLed, const int nDelay)
{
digitalWrite(pinLed, HIGH); //// turn on green LED
delay(nDelay);
digitalWrite(pinLed, LOW); // turn off green LED
}
// Функция мигает светодиодом с заданным полупериодом заданное число раз
void blink(
const int pinLed, // пин светодиода
const int nDelay, // полупериод мигания
const int nTimes // число миганий
)
{
for (int i = 0; i < nTimes; ++i) // blinks for 3 times
{
delay(nDelay);
lightUp(pinLed, nDelay);
}
}
// Главный цикл программы
void loop()
{
// Зажечь зеленый
lightUp(pinGrenLed, lightDelay * delayMultiply);
// Помигать зеленым
blink(pinGrenLed, blinkDelay * delayMultiply, blinkTimes);
// Ненадолго зажечь желтый
lightUp(pinYellowLed, yelloDelay * delayMultiply);
// Зажечь красный
lightUp(pinRedLed, lightDelay * delayMultiply);
}
Стало не сколь прямолинейно, но зато гораздо гибче.
Заключение
Наша программа стала уже сложнее, а результат – интереснее.
Конечно, в реальной жизни светофор работает намного сложнее. Используя описанные здесь принципы, можно смоделировать работу любого светофора и применить его в дорожных симуляторах, в играх и при обучении детей правилам дорожного движения.
Здесь мы познакомились с такими концепциями программирования как циклы и функции и подготовились для создания чего-то более интересного.
Главная мысль этого урока: старайтесь минимизировать повторяющийся код. Это поможет в дальнейшей модернизации программы и в повторном использовании кода.
Помните, что основновной процесс написания программы – это именно непрерывный рефакторинг, т.е. постоянное улучшение программы. Модульность программы и устранение повторений – это первичные условия успешного рефакторинга в дальнейшем.