====== Эксперимент 7. Бегущий огонёк ====== [[конспект-arduino:пульсар|← Пульсар]] | [[конспект-arduino:|Оглавление]] | [[конспект-arduino:мерзкое-пианино|Мерзкое пианино →]] В этом эксперименте мы заставляем огонёк бежать по светодиодной шкале. ===== Список деталей для эксперимента ===== * 1 плата [[amp>product/arduino-uno|Arduino Uno]] * 1 беспаечная [[amp>collection/breadboards|макетная плата]] * 1 светодиодная [[amp>product/bar-led|шкала]] * 10 [[amp>product/resistor-220r|резисторов]] номиналом 220 Ом * 11 проводов [[amp>product/wire-mm|«папа-папа»]] ===== Принципиальная схема ===== {{ :конспект-arduino:7_scheme.svg.png?nolink& |}} ===== Схема на макетке ===== {{ :конспект-arduino:7_bread.svg.png?nolink& |}} ===== Обратите внимание ===== * Обратите внимание, что в данном эксперименте резисторы установлены между катодами и землей в отличие от эксперимента [[конспект-arduino:пульсар|пульсар]]. * Мы подключаем светодиоды к цифровым портам, начиная с порта 2. Мы можем использовать порты 0 и 1, но они являются каналами передачи данных последовательного порта и для каждой перепрошивки платы придется отключать устройства, подключенные к ним. ===== Скетч ===== // светодиодная шкала подключена к группе пинов расположенных // подряд. Даём понятные имена первому и последнему пинам #define FIRST_LED_PIN 2 #define LAST_LED_PIN 11 void setup() { // в шкале 10 светодиодов. Мы бы могли написать pinMode 10 // раз: для каждого из пинов, но это бы раздуло код и // сделало его изменение более проблематичным. // Поэтому лучше воспользоваться циклом. Мы выполняем // pinMode для (англ. for) каждого пина (переменная pin) // от первого (= FIRST_LED_PIN) до последнего включительно // (<= LAST_LED_PIN), всякий раз продвигаясь к следующему // (++pin увеличивает значение pin на единицу) // Так все пины от 2-го по 11-й друг за другом станут выходами for (int pin = FIRST_LED_PIN; pin <= LAST_LED_PIN; ++pin) pinMode(pin, OUTPUT); } void loop() { // получаем время в миллисекундах, прошедшее с момента // включения микроконтроллера unsigned int ms = millis(); // нехитрой арифметикой вычисляем, какой светодиод // должен гореть именно сейчас. Смена будет происходить // каждые 120 миллисекунд. Y % X — это остаток от // деления Y на X; плюс, минус, скобки — как в алгебре. int pin = FIRST_LED_PIN + (ms / 120) % 10; // включаем нужный светодиод на 10 миллисекунд, затем — // выключаем. На следующем проходе цикла он снова включится, // если гореть его черёд, и мы вообще не заметим отключения digitalWrite(pin, HIGH); delay(10); digitalWrite(pin, LOW); } ===== Пояснения к коду ===== * С помощью выражения ''for'' мы организуем //цикл со счетчиком//. В данном случае для настройки портов на выход. Чтобы сделать такой цикл, нужно: * Инициализировать переменную-счетчик, присвоив ей первоначальное значение. В нашем случае: ''int pin = FIRST_LED_PIN'' * Указать условие, до достижения которого будет повторяться цикл. В нашем случае: ''pin %%<=%% LAST_LED_PIN'' * Определить правило, по которому будет изменяться счетчик. В нашем случае ''++pin'' (см. ниже об операторе ''++''). * Например, можно сделать цикл ''for (int i = 10; i > 0; i = i - 1)''. В этом случае: * Переменной ''i'' присваивается значение 10 * Это значение удовлетворяет условию ''i > 0'' * Поэтому блок кода, помещенный в цикл, выполняется первый раз * Значение ''i'' уменьшается на единицу, согласно заданному правилу, и принимает значение 9 * Блок кода выполняется второй раз. * Всё повторяется снова и снова вплоть до значения ''i'' равного 0 * Когда ''i'' станет равна 0, условие ''i > 0'' не выполнится, и выполнение цикла закончится * Контроллер перейдет к коду, следующему за циклом ''for'' * Помещайте код, который нужно зациклить, между парой фигурных скобок ''{}'', если в нем больше одной инструкции. * Переменная-счетчик, объявляемая в операторе ''for'', может использоваться внутри цикла. Например, в данном эксперименте ''pin'' последовательно принимает значения от 2 до 11 и, будучи переданной в ''pinMode'', позволяет настроить 10 портов одной строкой, помещенной в цикл. * Переменные-счетчики видны только внутри цикла. Т.е. если обратиться к ''pin'' до или после цикла, компилятор выдаст ошибку о необъявленной переменной. * Конструкция ''i = i - 1'' в пояснении выше не является уравнением! Мы используем оператор присваивания ''='' для того, чтобы в переменную ''i'' поместить значение, равное текущему значению ''i'', уменьшенному на 1. * Выражение ''++pin'' — это т.н. оператор //инкремента//, примененный к переменной ''pin''. Эта инструкция даст тот же результат, что ''pin = pin + 1'' * Аналогично инкременту работает оператор //декремента// ''%%--%%'', уменьшающий значение на единицу. Подробнее об этом в статье про [[программирование:константы-переменные-арифметика#о_компактной_записи_и_области_видимости|арифметические операции]]. * Тип данных ''unsigned int'' используют для хранения целых чисел без знака, т.е. только //неотрицательных//. За счет лишнего бита, который теперь не используется для хранения знака, мы можем хранить в переменной такого типа значения до 65 535. * Функция ''millis'' возвращает количество миллисекунд, прошедших с момента включения или перезагрузки микроконтроллера. Здесь мы используем ее для отсчета времени между переключениями светодиодов. * С помощью выражения ''(ms / 120) % 10'' мы определяем, который из 10 светодиодов должен гореть сейчас. Перефразируя, мы определяем какой отрезок длиной в 120 мс идет сейчас и каков его номер внутри текущего десятка. Мы добавляем порядковый номер отрезка к номеру того порта, который в текущем наборе выступает первым. * То, что мы гасим светодиод с помощью ''digitalWrite(pin, LOW)'' всего через 10 мс после включения не заметно глазу, т.к. очень скоро будет вновь вычислено, какой из светодиодов включать, и он будет включен — только что погашенный или следующий. ===== Вопросы для проверки себя ===== - Почему в данном эксперименте мы подключаем светодиодную шкалу, не используя транзистор? - Если бы мы включали светодиоды только на портах 5, 6, 7, 8, 9, что нужно было бы изменить в программе? - С помощью какой другой инструкции можно выполнить действие, эквивалентное ''++pin''? - В чем разница между переменными типов ''int'' и ''unsigned int''? - Что возвращает функция ''millis()''? - Как в данном эксперименте мы вычисляем номер порта, на котором нужно включить светодиод? ===== Задания для самостоятельного решения ===== - Измените код так, чтобы светодиоды переключались раз в секунду. - Не выключая порты, сделайте так, чтобы огонёк бежал только по средним четырем делениям шкалы. - Переделайте программу так, чтобы вместо ''int pin = FIRST_LED_PIN + (ms / 120) % 10'' перемещением огонька управлял цикл ''for'' - Не меняя местами провода, измените программу так, чтобы огонёк бегал в обратном направлении. ---- [[конспект-arduino:пульсар|← Пульсар]] | [[конспект-arduino:|Оглавление]] | [[конспект-arduino:мерзкое-пианино|Мерзкое пианино →]]