Урок 7. Бегущий огонек


В этом эксперименте мы заставляем огонёк бежать по светодиодной шкале.

Список деталей для эксперимента

Принципиальная схема

Схема на макетке

Обратите внимание

  • Обратите внимание, что в данном эксперименте резисторы установлены между катодами и землей в отличие от эксперимента пульсар.
  • Мы подключаем светодиоды к цифровым портам, начиная с порта 2. Мы можем использовать порты 0 и 1, но они являются каналами передачи данных последовательного порта и для каждой перепрошивки платы придется отключать устройства, подключенные к ним.

Скетч

  1.     // светодиодная шкала подключена к группе пинов расположенных
    
  2.     // подряд. Даём понятные имена первому и последнему пинам
    
  3.     #define FIRST_LED_PIN  2
    
  4.     #define LAST_LED_PIN   11
    
  5.  
  6.     void setup(
    )
    
  7.     {
    
  8.       // в шкале 10 светодиодов. Мы бы могли написать pinMode 10
    
  9.       // раз: для каждого из пинов, но это бы раздуло код и
    
  10.       // сделало его изменение более проблематичным.
    
  11.       // Поэтому лучше воспользоваться циклом. Мы выполняем
    
  12.       // pinMode для (англ. for) каждого пина (переменная pin)
    
  13.       // от первого (= FIRST_LED_PIN) до последнего включительно
    
  14.       // (<= LAST_LED_PIN), всякий раз продвигаясь к следующему
    
  15.       // (++pin увеличивает значение pin на единицу)
    
  16.       // Так все пины от 2-го по 11-й друг за другом станут выходами
    
  17.       for (
    int pin = FIRST_LED_PIN; pin <= LAST_LED_PIN; ++pin)
    
  18.         pinMode(pin, OUTPUT)
    ;
    
  19.     }
    
  20.  
  21.     void loop(
    )
    
  22.     {
    
  23.       // получаем время в миллисекундах, прошедшее с момента
    
  24.       // включения микроконтроллера
    
  25.       unsigned int ms = millis(
    )
    ;
    
  26.       // нехитрой арифметикой вычисляем, какой светодиод
    
  27.       // должен гореть именно сейчас. Смена будет происходить
    
  28.       // каждые 120 миллисекунд. Y % X — это остаток от
    
  29.       // деления Y на X; плюс, минус, скобки — как в алгебре.
    
  30.       int pin = FIRST_LED_PIN + (ms / 120
    ) % 10
    ;
    
  31.       // включаем нужный светодиод на 10 миллисекунд, затем —
    
  32.       // выключаем. На следующем проходе цикла он снова включится,
    
  33.       // если гореть его черёд, и мы вообще не заметим отключения
    
  34.       digitalWrite(pin, HIGH)
    ;
    
  35.       delay(
    10
    )
    ;
    
  36.       digitalWrite(pin, LOW)
    ;
    
  37.     }
    

Пояснения к коду

  • С помощью выражения for мы организуем цикл со счетчиком. В данном случае для настройки портов на выход. Чтобы сделать такой цикл, нужно:
  • Инициализировать переменную-счетчик, присвоив ей первоначальное значение. В нашем случае: int pin = FIRST_LED_PIN
  • Указать условие, до достижения которого будет повторяться цикл. В нашем случае: pin <= LAST_LED_PIN
  • Определить правило, по которому будет изменяться счетчик. В нашем случае ++pin (см. ниже об операторе ++).
  • Например, можно сделать цикл for (int i = 10; i > 0; i = i - 1). В этом случае:
  1. Переменной i присваивается значение 10
  2. Это значение удовлетворяет условию i > 0
  3. Поэтому блок кода, помещенный в цикл, выполняется первый раз
  4. Значение i уменьшается на единицу, согласно заданному правилу, и принимает значение 9
  5. Блок кода выполняется второй раз.
  6. Всё повторяется снова и снова вплоть до значения i равного 0
  7. Когда i станет равна 0, условие i > 0 не выполнится, и выполнение цикла закончится
  8. Контроллер перейдет к коду, следующему за циклом 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
  • Не меняя местами провода, измените программу так, чтобы огонёк бегал в обратном направлении.
Теги: 
Источник: 

wiki.amperka.ru