Хотите попробовать Arduino, но нет времени на чтение книжек? С этим руководством вы сможете в кратчайшие сроки опробовать в действии бо́льшую часть функций Arduino.
В брошюре кратко, но в то же время доступно описываются все основные понятия, необходимые для реализации собственных электронных идей. Брошюра идеально подходит в качестве руководства по использованию наборов «Матрёшка». На самом деле конспект создан специально для них и уже включён в комплект «Матрёшек». Однако, если у вас уже есть Arduino и радиодетали, можете приобрести конспект отдельно.
Первая часть брошюры представляет собой вводную в электричество. В ней объясняются такие простые понятия, как резистор, электрическая схема, закон Ома и т.д. Чтобы теория не забылась, а прочно укоренилась в уме начинающего, во второй части брошюры приведены 20 простых заданий.
При написании брошюры приоритетными задачами были краткость и доступность, а не точность и общность. На 84-х страницах описаны, конечно, далеко не все аспекты схемотехники и функции Arduino. Брошюра призвана заинтересовать начинающего, избавить от рутины и в максимально сжатые сроки помочь определиться: нужно ему это или нет.
В этом эксперименте мы создаем еще одну игру, на этот раз нужно быстрее соперника нажать кнопку 20 раз.
В старой версии блокнота хакера по ошибке были указаны резисторы на 100 кОм, которых нет в наборе. На самом деле в уроке смело можно и нужно использовать резисторы на 10 кОм из комплекта.
Список деталей для эксперимента
#define BUZZER_PIN 0
#define FIRST_BAR_PIN 4
#define BAR_COUNT 10
#define MAX_SCORE 20
// глобальные переменные, используемые в прерываниях (см. далее)
// должны быть отмечены как нестабильные (англ. volatile)
score
setup
i i BAR_COUNT i
pinModei FIRST_BAR_PIN, OUTPUT
pinModeBUZZER_PIN, OUTPUT
// Прерывание (англ. interrupt) приостанавливает основную
// программу, выполняет заданную функцию, а затем возобновляет
// основную программу. Нам нужно прерывание на нажатие кнопки,
// т.е. при смене сигнала с высокого на низкий, т.е. на
// нисходящем (англ. falling) фронте
attachInterruptINT1, pushP1, FALLING // INT1 — это 3-й пин
attachInterruptINT0, pushP2, FALLING // INT0 — это 2-й пин
pushP1 score // функция-прерывание 1-го игрока
pushP2 score // функция-прерывание 2-го игрока
loop
toneBUZZER_PIN, , // даём сигнал к старту.
// пока никто из игроков не выиграл, обновляем «канат»
score MAX_SCORE
bound mapscore, MAX_SCORE, MAX_SCORE, , BAR_COUNT
left minbound, BAR_COUNT
right maxbound, BAR_COUNT
i i BAR_COUNT i
digitalWritei FIRST_BAR_PIN, i left i right
toneBUZZER_PIN, , // даём сигнал победы
// «подвешиваем» плату до перезагрузки
Пояснения к коду
setup
// настраиваем пин №13 в режим выхода,
// т.е. в режим источника напряжения
pinMode, OUTPUT
loop
// подаём на пин 13 «высокий сигнал» (англ. «high»), т.е.
// выдаём 5 вольт. Через светодиод побежит ток.
// Это заставит его светиться
digitalWrite, HIGH
// задерживаем (англ. «delay») микроконтроллер в этом
// состоянии на 100 миллисекунд
delay
// подаём на пин 13 «низкий сигнал» (англ. «low»), т.е.
// выдаём 0 вольт или, точнее, приравниваем пин 13 к земле.
// В результате светодиод погаснет
digitalWrite, LOW
// замираем в этом состоянии на 900 миллисекунд
delay
// после «размораживания» loop сразу же начнёт исполняться
// вновь, и со стороны это будет выглядеть так, будто
// светодиод мигает раз в 100 мс + 900 мс = 1000 мс = 1 сек
Вопросы для проверки себя
Начало работы с Arduino
Если у вас Windows и Arduino IDE из zip-файла, установите драйверы из папки
Подключите Arduino к компьютеру через USB
Запустите Arduino IDE
Жмите «Upload» на панели инструментов для прошивки платы!
Перепрошивать плату можно сколько угодно раз. Программа сохраняется после обесточивания платы.
Внешний вид Arduino IDE
Если прошивка не удаётся
Плата получает питание, горит светодиод «ON»
Драйверы под Windows установились корректно, и в диспетчере устройств вы видите устройство «Arduino Uno»
Вы выбрали правильную модель платы и правильный порт (пункты 5 и 6)
Эксперимент 1. Маячок
Список деталей для эксперимента
номиналом 220 Ом
Схема на макетке
Не забудьте, как соединены рельсы в беспаечной . Если на вашей макетке красная и синяя линии вдоль длинных рельс прерываются в середине, значит проводник внутри макетки тоже прерывается!
Катод («минус») светодиода — короткая ножка, именно её нужно соединять с землёй (GND)
Не пренебрегайте резистором, иначе светодиод выйдет из строя
Выбрать резистор нужного номинала можно с помощью или с помощью мультиметра в режиме измерения сопротивления
Плата Arduino имеет три пина GND, используйте любой из них
Пояснения к коду
выполняется один раз при запуске микроконтроллера. Обычно она используется для конфигурации портов микроконтроллера и других настроек
запускается процедура , которая выполняется в бесконечном цикле. Именно этим мы пользуемся в данном примере, чтобы маячок мигал постоянно
и должны присутствовать в любой программе (скетче), даже если вам не нужно ничего выполнять в них — пусть они будут пустые, просто не пишите ничего между фигурными скобками. Например:
Обращайте внимание на в концах строк. Не стирайте их там, где они есть, и не добавляйте лишних. Вскоре вы будете понимать, где они нужны, а где нет.
не возвращает никакого значения и принимает два параметра:
— номер цифрового порта, на который мы отправляем сигнал
— значение, которое мы отправляем на порт. Для цифровых портов значением может быть(высокое, единица) или (низкое, ноль)
Если в качестве второго параметра вы передадите функции значение,
отличное от ,, или , компилятор может не выдать ошибку, но считать, что передано . Будьте внимательны
Обратите внимание, что использованные нами константы: , , , , пишутся заглавными буквами, иначе компилятор их не распознает и выдаст ошибку. Когда ключевое слово распознано, оно подсвечивается синим цветом в Arduino IDE
Вопросы для проверки себя
Что будет, если подключить к земле анод светодиода вместо катода?
Что будет, если подключить светодиод с резистором большого номинала (например, 10 кОм)?
Что будет, если подключить светодиод без резистора?
Зачем нужна встроенная функция ? Какие параметры она принимает?
С помощью какой встроенной функции можно заставить микроконтроллер ничего не делать?
В каких единицах задается длительность паузы для этой функции?
Задания для самостоятельного решения
Сделайте так, чтобы маячок светился полсекунды, а пауза между вспышками была равна одной секунде
Измените код примера так, чтобы маячок включался на три секунды после запуска устройства, а затем мигал в стандартном режиме
Эксперимент 2. Маячок с нарастающей яркостью
Для дополнительного задания
еще 1 светодиод
еще 1 резистор номиналом 220 Ом
еще 2 провода
Не любой порт Arduino поддерживает , если вы хотите регулировать напряжение, вам подойдут пины, помеченные символом тильда «~». Для
Arduino Uno это пины 3, 5, 6, 9, 10, 11
// даём разумное имя для пина №9 со светодиодом
// (англ. Light Emitting Diode или просто «LED»)
// Так нам не нужно постоянно вспоминать куда он подключён
#define LED_PIN 9
// настраиваем пин со светодиодом в режим выхода,
// как и раньше
// выдаём неполное напряжение на светодиод
// (он же ШИМ-сигнал, он же PWM-сигнал).
// Микроконтроллер переводит число от 0 до 255 к напряжению
// от 0 до 5 В. Например, 85 — это 1/3 от 255, // т.е. 1/3 от 5 В, т.е. 1,66 В.
// держим такую яркость 250 миллисекунд
// выдаём 170, т.е. 2/3 от 255, или иными словами — 3,33 В.
// Больше напряжение — выше яркость!
// все 5 В — полный накал!
// ждём ещё немного перед тем, как начать всё заново
Идентификаторы переменных, констант, функций (в этом примере идентификатор )
являются одним словом (т.е. нельзя создать идентификатор ).
Идентификаторы могут состоять из латинских букв, цифр и символов подчеркивания . При этом идентификатор не может начинаться с цифры.
Регистр букв в идентификаторе имеет значение. Т.е. , и с точки зрения компилятора — различные идентификаторы
Идентификаторы, создаваемые пользователем, не должны совпадать с предопределенными идентификаторами и стандартными конструкциями языка; если среда разработки подсветила введенный идентификтор каким-либо цветом, замените его на другой
просто говорит компилятору заменить все вхождения заданного идентификатора на значение, заданное после пробела (здесь ), эти директивы помещают в начало кода. В конце данной директивы точка с запятой не допустима
Названия идентификаторов всегда нужно делать осмысленными, чтобы при возвращении к ранее написанному коду вам было ясно, зачем нужен каждый из них
помещается между парой слеш-звездочка и звездочка-слеш */
— номер порта, на который мы отправляем сигнал
— значение , которое мы отправляем на порт. Он может принимать целочисленное значение от 0 до 255, где 0 — это 0%, а 255 — это 100%
Какие из следующих идентификаторов корректны и не вызовут ошибку?
Что произойдет, если создать директиву #define HIGH LOW?
Почему мы не сможем регулировать яркость светодиода, подключенного к порту 7?
Какое усреднённое напряжение мы получим на пине 6, если вызовем функцию ?
Какое значение параметра нужно передать функции , чтобы получить усреднённое напряжение 2 В?
Отключите питание, отключите светодиод от 9-го порта и подключите к 11-му. Измените программу так, чтобы схема снова заработала
Измените код программы так, чтобы в течение секунды на светодиод последовательно подавалось усреднённое напряжение 0, 1, 2, 3, 4, 5 В
Возьмите еще один светодиод, резистор на 220 Ом и соберите аналогичную схему на этой же макетке, подключив светодиод к пину номер 3 и другому входу GND, измените программу так, чтобы светодиоды мигали в противофазу: первый выключен, второй горит максимально ярко и до противоположного состояния
Эксперимент 3. Светильник с управляемой яркостью
Ножки тактовой кнопки, расположенные с одной стороны, разомкнуты, когда кнопка не нажата. Ножки, расположенные друг напротив друга на противоположных сторонах макетки находятся на одной «рельсе». Воспользовавшись этим, мы можем расположить резистор с одной стороны макетки, а провод, подключаемый к порту Arduino, с другой стороны.
В данном эксперименте мы подключаем кнопки по с подтягивающим резистором.
Для того, чтобы данный вариант программы работал, важно, чтобы кнопки были подключены к портам, находящимся рядом друг с другом, т.е. имеющим соседние номера.
// на основе номера кнопки вычисляем номер её пина keyPin = i + FIRST_KEY_PIN;
// считываем значение с кнопки. Возможны всего 2 варианта:
// * высокий сигнал, 5 вольт, истина — кнопка отпущена
// * низкий сигнал, земля, ложь — кнопка зажата boolean keyUp = digitalRead
// проверяем условие «если не кнопка отпущена». Знак «!»
// рассчитываем высоту ноты в герцах в зависимости от
// клавиши, которую рассматриваем на данном этапе цикла.
// Мы получим значение 3500, 4000 или 4500
// Заставляем пищалку пищать с нужной частотой в течение
// 20 миллисекунд. Если клавиша останется зажатой, пищалка
// вновь зазвучит при следующем проходе loop, а мы услышим
// непрерывный звук
Благодаря тому, что в начале программы мы определили и , мы можем подключать произвольное количество кнопок к любым идущим друг за другом цифровым пинам, и для корректировки программы нам не придется менять параметры цикла . Изменить понадобится лишь эти константы:
цикл в любом случае пробегает от 0 до ;
перед считыванием порта мы задаем смещение на номер первого используемого порта —
возвращает состояние порта, номер которого передан ей параметром . Это может быть состояние или . Или, выражаясь иначе: высокое напряжение или низкое, 1 или 0, или
Поскольку мы получаем с порта одно из двух состояний, мы сохраняем его в переменную уже знакомого нам типа , и можем работать с ней как с логическим значением.
Мы используем логический оператор отрицания «не» . Если имеет значение 0,
выражениебудет иметь значение 1 и наоборот.
Поскольку мы собрали схему с подтягивающим резистором, при нажатии кнопки мы будем получать на соответствующем порте 0.
Действия, описанные в условном выражении , выполняются, когда его условие имеет значение «истина» (единица). Поэтому для выполнения действия по нажатию, мы инвертируем сигнал с кнопки.
Почему мы не настраивали порты, к которым подключены кнопки, как , но устройство работает?
Каким образом мы избежали написания отдельного когда для чтения каждой кнопки?
Почему разные «ноты», издаваемые пищалкой, звучат с разной громкостью?
Для чего мы использовали оператор логического отрицания ?
Сделайте так, чтобы наше пианино звучало в диапазоне от 2 кГц до 5 кГц.
Добавьте еще 2 кнопки и измените программу так, чтобы можно было извлечь 5 различных нот.
Подключите кнопки по схеме со стягивающим резистором и измените программу так, чтобы она продолжала работать.
Эксперимент 9. Миксер
, если вы используете мотор с проводами, которые плохо втыкаются в макетку Для дополнительного задания
еще 1 кнопка
Защитный диод нам нужен для того, чтобы ток обратного направления, который начнет создавать двигатель, вращаясь по инерции, не вывел из строя транзистор.
Не перепутайте полярность диода, иначе, открыв транзистор, вы устроите короткое замыкание!
Причину отсутствия подтягивающих/стягивающих резисторов в схеме вы поймете, ознакомившись с программой.
Мы подключили питание схемы к выходу Vin платы микроконтроллера, потому что, в отличие выхода 5V, отсюда можно получить напряжение, подключенное к плате, без изменений и без ограничений по величине тока.
// скорость (англ. speed) мотора при нажатии очередной кнопки
#define SPEED_STEP (255 / (BUTTON_COUNT – 1))
// на самом деле, в каждом пине уже есть подтягивающий
// резистор. Для его включения необходимо явно настроить пин
// как вход с подтяжкой (англ. input with pull up)
i = ; i < BUTTON_COUNT; ++ipinModei + FIRST_BUTTON_PIN, INPUT_PULLUP;
// если кнопка отпущена, нам она не интересна. Пропускаем
// оставшуюся часть цикла for, продолжая (англ. continue) // его дальше, для следующего значения i
digitalReadi + FIRST_BUTTON_PIN;
// кнопка нажата — выставляем соответствующую ей скорость
// мотора. Нулевая кнопка остановит вращение, первая
// заставит крутиться в полсилы, вторая — на полную speed = i * SPEED_STEP;
// подача ШИМ-сигнала на мотор заставит его крутиться с
// указанной скоростью: 0 — стоп машина, 127 — полсилы,
// 255 — полный вперёд!
Мы использовали новый режим работы портов: . На цифровых портах Arduino есть встроенные подтягивающие резисторы, которые можно включить указанным образом одновременно с настройкой порта на вход. Именно поэтому мы не использовали резисторы при сборке схемы.
На каждой итерации цикла мы задаем мотору скорость вращения, пропорциональную текущему значению счетчика. Но выполнение инструкций не дойдет до назначения новой скорости, если при проверке нажатия кнопки она окажется отпущенной.
Инструкция , которая выполнится в этом случае, отменит продолжение данной итерации цикла и выполнение программы продолжится со следующей. А мотор будет крутиться со скоростью, заданной при последнем нажатии на какую-то из кнопок.
Зачем в схеме использован диод?
Почему мы использовали полевой MOSFET-транзистор, а не биполярный?
Почему мы не использовали резистор между портом Arduino и затвором транзистора?
Как работает инструкция , использованная в цикле ?
Внесите единственное изменение в программу, после которого максимальной скоростью вращения мотора составит половину от возможной.
Перепишите программу без использования инструкции .
Добавьте в схему еще одну кнопку, чтобы у миксера стало три режима. Понадобилось ли изменять что-либо в программе?
Эксперимент 10. Кнопочный переключатель
Мы могли бы один из контактов кнопки соединить проводом напрямую с одним из входов GND, но мы сначала «раздали» «землю» на длинную рельсу макетки. Если мы работаем с макетной платой, так поступать удобнее, т.к. в схеме могут появляться новые участки, которые тоже нужно будет соединить с «землей»
Также полезно руководствоваться соображениями аккуратности изделия, поэтому катод светодиода мы соединяем с другим входом GND отдельным проводом, который не мешает нам работать в середине макетки.
boolean buttonWasUp = // была ли кнопка отпущена? boolean ledEnabled = // включен ли свет?
pinModeLED_PIN, OUTPUT; pinModeBUTTON_PIN, INPUT_PULLUP;
// определить момент «клика» несколько сложнее, чем факт того,
// что кнопка сейчас просто нажата. Для определения клика мы
boolean buttonIsUp = digitalReadBUTTON_PIN;
// возникающий в момент замыкания/размыкания пластин кнопки,
// запоминаем последнее состояние кнопки для новой итерации
buttonWasUp = buttonIsUp;
Поскольку мы сконфигурировали вход кнопки как , при нажатии на кнопку на данном входе мы будем получать 0. Поэтому мы получим значение («истина») в булевой переменной(«кнопка отпущена»), когда кнопка отпущена.
(«и») возвращает значение «истина» только в случае истинности обоих его операндов. Взглянем на так называемую таблицу истинности для
выражения buttonWasUp && !buttonIsUp («кнопка была отпущена и кнопка не отпущена»):
Здесь рассмотрены все возможные сочетания предыдущего и текущего состояний кнопки и мы видим, что наш условный оператор сработает только в случае, когда кнопка нажата только что: предыдущее состояние 1 («была отпущена»), а текущее 0 («не отпущена»).
Через 10 миллисекунд мы проверяем еще раз, нажата ли кнопка: этот интервал больше, чем длительность «дребезга», но меньше, чем время, за которое человек успел бы дважды нажать на кнопку. Если кнопка всё еще нажата, значит, это был не дребезг.
Мы передаем в не конкретное значение или , а просто булеву переменную. В зависимости от того, какое значение было для нее вычислено, светодиод будет зажигаться или гаситься.
Последняя инструкция в buttonWasUp = buttonIsUp сохраняет текущее состояние кнопки в переменную предыдущего состояния, ведь на следующей итерации текущее состояние уже станет историей.
В каком случае оператор возвращает значение «истина»?
Что такое «дребезг»?
Как мы с ним боремся в программе?
Как можно избежать явного указания значения уровня напряжения при вызове ?
Измените код так, чтобы светодиод переключался только после отпускания кнопки.
Добавьте в схему еще одну кнопку и доработайте код, чтобы светодиод зажигался только при нажатии обеих кнопок.
правого. Компилятор не знает наших намерений и ошибку не выдаст, а мы можем нечаянно изменить значение какой-нибудь переменной и затем долго разыскивать ошибку.
(«если») — один из ключевых в большинстве языков программирования. С его помощью мы можем выполнять не только жестко заданную последовательность действий, но принимать решения, по какой ветви алгоритма идти, в зависимости от неких условий.
У логического выражения lightness < threshold есть значение: или . Мы вычислили его и поместили в булеву переменную («слишком темно»). Таким образом мы как бы говорим «если слишком темно, то включить светодиод»
С таким же успехом мы могли бы сказать «если освещенность меньше порогового уровня, то включить светодиод», т.е. передать в всё логическое выражение:
может быть расширен конструкцией («иначе»). Блок кода или единственная инструкция, следующий за ней, будет выполнен только если логическое выражение в имеет значение , «ложь». Правила, касающиеся фигурных скобок, такие же. В нашем эксперименте мы написали «если слишком темно, включить светодиод, иначе выключить светодиод».
Если мы установим фоторезистор между аналоговым входом и землей, наше устройство будет работать наоборот: светодиод будет включаться при увеличении количества света. Почему?
Какой результат работы устройства мы получим, если свет от светодиода будет падать на фоторезистор?
Если мы все же установили фоторезистор так, как сказано в предыдущем вопросе, как нам нужно изменить программу, чтобы устройство работало верно?
Обязательно ли указывать, какие инструкции выполнять, если условие в операторе ложно?
Чем отличается оператор от оператора ?
Если мы используем конструкцию if (условие) действие1; else действие2;, может ли быть ситуация, когда ни одно из действий не выполнится? Почему?
Перепишите программу без использования переменной с сохранением функционала устройства.
Добавьте в схему еще один светодиод. Дополните программу так, чтобы при падении освещенности ниже порогового значения включался один светодиод, а при падении освещенности ниже половины от порогового значения включались оба светодиода.
Измените схему и программу так, чтобы светодиоды включались по прежнему принципу, но светились тем сильнее, чем меньше света падает на фоторезистор.
Эксперимент 6. Пульсар
номиналом 1 кОм
Светодиодная шкала — это несколько светодиодов в одном корпусе. Нам нужно чтобы питание шло к их анодам, а катоды направлялись к земле. Скорее всего на вашей шкале аноды находятся со стороны маркировки. Если шкала не светится, когда должна, попробуйте перевернуть ее.
База биполярного транзистора — это его средняя ножка. Если повернуть транзистор плоской стороной к себе, ножками вниз, то левая ножка это коллектор, а правая — эмиттер.
Если эту схему собрать без резистора между базой транзистора и портом Arduino, мы практически устроим короткое замыкание порта на землю. Рано или поздно это выведет из строя транзистор или ножку микроконтроллера.
Зачем здесь вообще транзистор? Без него такое количество светодиодов будет потреблять больше тока, чем 40 мА, которые может себе позволить цифровой пин платы. Поэтому мы берем питание из порта 5V, рассчитанного на ток до 500 мА, а на цифровой порт ставим транзистор, чтобы с помощью малого тока управлять большим.
В данном случае мы включили 10 светодиодов параллельно, каждый через отдельный резистор. Включать их через один резистор неправильно: даже светодиоды из одной партии имеют минимальный разброс вольт-амперных характеристик, вследствие чего они:
Светились бы с различной яркостью
Из-за минимальной разницы во времени включения, больший ток, прошедший через первый включившийся светодиод, мог бы вывести его из строя. И так по цепочке.
#define CONTROL_PIN 9
// переменные верхнего уровня, т.е. объявленные вне функций,
// называют глобальными. Их значения сохраняются всё время,
// пока работает микроконтроллер
// увеличиваем значение яркости на единицу, чтобы нарастить
// яркость. Однако яркость не должна быть более 255, поэтому
// используем операцию остатка от деления, чтобы при
// достижении значения 255, следующим значением снова стал 0 // Y % X — это остаток от деления Y на X;
// плюс, минус, делить, умножить, скобки — как в алгебре.
// подаём вычисленный ШИМ-сигнал яркости на пин с базой
// управляющего транзистора
// ждём 10 мс перед следующим наращиванием яркости. Таким
// образом, полный накал будет происходить в течение
// 256×10 = 2560 мс
Как мы уже знаем, в качестве принимает значения от 0 до 255. Если передать значение из-за пределов этого диапазона, функция сработает, но в общем случае вы получите неожиданный результат.
дает остаток от деления на . Если меньше , т.е. целая часть результата деления равна 0, оператор будет возвращать . Таким образом:
brightness + 1 меньше 256, в записывается значение
Как только brightness + 1 принимает значение 256, результатом (brightness + 1)
становится 0 и на следующей итерации всё начинается сначала.
работает только с целыми операндами.
(brightness + 1) % 256 скобки используются для назначения порядка действий. Операция имеет больший приоритет, чем , а сложение нам нужно выполнять раньше. С операциями умножения и деления оператор взятия остатка имеет одинаковый приоритет.
Почему у светодиодной шкалы на 10 сегментов 20 ножек?
Зачем в схеме биполярный транзистор?
За счет чего увеличивается яркость шкалы?
Почему после достижения значения 255 переменная обнуляется?
Измените программу так, чтобы яркость шкалы росла только до половины от максимальной.
Измените программу так, чтобы шкала становилась максимально яркой в три раза быстрее, без изменения функции .
Измените исходную программу так, чтобы такой же результат был получен без использования операции, но с применением условного оператора .
Эксперимент 7. Бегущий огонёк
Обратите внимание, что в данном эксперименте резисторы установлены между катодами и землей в отличие от эксперимента .
Мы подключаем светодиоды к цифровым портам, начиная с порта 2. Мы можем использовать порты 0 и 1, но они являются каналами передачи данных последовательного порта и для каждой перепрошивки платы придется отключать устройства, подключенные к ним.
// светодиодная шкала подключена к группе пинов расположенных
// подряд. Даём понятные имена первому и последнему пинам
#define FIRST_LED_PIN 2 #define LAST_LED_PIN 11
// в шкале 10 светодиодов. Мы бы могли написать pinMode 10 // раз: для каждого из пинов, но это бы раздуло код и
// сделало его изменение более проблематичным.
// Поэтому лучше воспользоваться циклом. Мы выполняем
// pinMode для (англ. for) каждого пина (переменная pin) // от первого (= FIRST_LED_PIN) до последнего включительно
// (<= LAST_LED_PIN), всякий раз продвигаясь к следующему
// (++pin увеличивает значение pin на единицу)
// Так все пины от 2-го по 11-й друг за другом станут выходами pin = FIRST_LED_PIN; pin <= LAST_LED_PIN; ++pin
// получаем время в миллисекундах, прошедшее с момента
// включения микроконтроллера ms = millis
// нехитрой арифметикой вычисляем, какой светодиод
// должен гореть именно сейчас. Смена будет происходить
// каждые 120 миллисекунд. Y % X — это остаток от
// деления Y на X; плюс, минус, скобки — как в алгебре. pin = FIRST_LED_PIN +
// включаем нужный светодиод на 10 миллисекунд, затем —
// выключаем. На следующем проходе цикла он снова включится,
// если гореть его черёд, и мы вообще не заметим отключения
delay; digitalWritepin, LOW;
С помощью выражения мы организуем цикл со счетчиком. В данном случае для настройки портов на выход. Чтобы сделать такой цикл, нужно:
переменную-счетчик, присвоив ей первоначальное значение. В нашем случае:int pin = FIRST_LED_PIN
Указать условие, до достижения которого будет повторяться цикл. В нашем случае: pin <= LAST_LED_PIN
Определить правило, по которому будет изменяться счетчик. В нашем случае (см.
ниже об операторе ).
Переменной присваивается значение 10
Это значение удовлетворяет условию
Поэтому блок кода, помещенный в цикл, выполняется первый раз
Значение уменьшается на единицу, согласно заданному правилу, и принимает значение 9
Блок кода выполняется второй раз.
Всё повторяется снова и снова вплоть до значения равного 0
Когда станет равна 0, условие не выполнится, и выполнение цикла закончится
Контроллер перейдет к коду, следующему за циклом
объявляемая в операторе , может использоваться внутри цикла.
Например, в данном эксперименте последовательно принимает значения от 2 до 11 и,
будучи переданной в, позволяет настроить 10 портов одной строкой, помещенной в цикл.
видны только внутри цикла. Т.е. если обратиться к до или после цикла, компилятор выдаст ошибку о необъявленной переменной.
i = i – 1 в пояснении выше не является уравнением! Мы используем оператор присваивания для того, чтобы в переменную поместить значение, равное текущему значению , уменьшенному на 1.
— это т.н. оператор , примененный к переменной . Эта
инструкция даст тот же результат, что pin = pin + 1
Аналогично инкременту работает оператор , уменьшающий значение на единицу. Подробнее об этом в статье про .
используют для хранения целых чисел без знака, т.е. только. За счет лишнего бита, который теперь не используется для хранения знака, мы можем хранить в переменной такого типа значения до 65 535.
возвращает количество миллисекунд, прошедших с момента включения или перезагрузки микроконтроллера. Здесь мы используем ее для отсчета времени между переключениями светодиодов.
С помощью выражения (ms / 120) % 10 мы определяем, который из 10 светодиодов должен гореть сейчас. Перефразируя, мы определяем какой отрезок длиной в 120 мс идет сейчас и каков его номер внутри текущего десятка. Мы добавляем порядковый номер отрезка к номеру того порта, который в текущем наборе выступает первым.
То, что мы гасим светодиод с помощью всего через 10 мс после включения не заметно глазу, т.к. очень скоро будет вновь вычислено, какой из светодиодов включать, и он будет включен — только что погашенный или следующий.
Почему в данном эксперименте мы подключаем светодиодную шкалу, не используя транзистор?
Если бы мы включали светодиоды только на портах 5, 6, 7, 8, 9, что нужно было бы изменить в программе?
С помощью какой другой инструкции можно выполнить действие, эквивалентное ?
В чем разница между переменными типов и ?
Что возвращает функция ?
Как в данном эксперименте мы вычисляем номер порта, на котором нужно включить светодиод?
Измените код так, чтобы светодиоды переключались раз в секунду.
Не выключая порты, сделайте так, чтобы огонёк бежал только по средним четырем делениям шкалы.
3. Переделайте программу так, чтобы вместо int pin = FIRST_LED_PIN + (ms / 120) %
перемещением огонька управлял цикл
Не меняя местами провода, измените программу так, чтобы огонёк бегал в обратном направлении.
Эксперимент 8. Мерзкое пианино
номиналом 10 кОм
еще 2 резистора номиналом 10 кОм