====== Эксперимент 20. Перетягивание каната ====== [[светильник-управляемый-по-usb|← Светильник, управляемый по USB]] | [[конспект-arduino:|Оглавление]] | Конец первой серии экспериментов! В этом эксперименте мы создаем еще одну игру, на этот раз нужно быстрее соперника нажать кнопку 20 раз. В старой версии блокнота хакера по ошибке были указаны резисторы на 100 кОм, которых нет в наборе. На самом деле в уроке смело можно и нужно использовать резисторы на 10 кОм из комплекта. ===== Список деталей для эксперимента ===== * 1 плата [[amp>product/arduino-uno|Arduino Uno]] * 1 беспаечная [[amp>collection/breadboards|макетная плата]] * 1 светодиодная [[amp>product/bar-led|шкала]] * 10 [[amp>product/resistor-220r|резисторов]] номиналом 220 Ом * 4 [[amp>product/resistor-10k|резисторов]] номиналом 10 кОм * 2 тактовых [[amp>product/tactile-button|кнопки]] * 2 [[amp>product/capacitor-ceramic-100n|керамических конденсатора]] номиналом 100 нФ * 1 [[amp>product/piezo-buzzer|пьезопищалка]] * 1 [[amp>product/chip-sn74hc14n|инвертирующий триггер Шмитта]] * 24 провода [[amp>product/wire-mm|«папа-папа»]] ==== Для дополнительного задания ==== * 1 сервопривод * 1 конденсатор 220 мкФ ===== Принципиальная схема ===== {{ :конспект-arduino:перетягивание-каната:p200-button-wrestling-schematic.svg.png?nolink&400 |}} ===== Схема на макетке ===== {{ :конспект-arduino:перетягивание-каната:p200-button-wrestling-bread.svg.png?nolink& |}} ===== Обратите внимание ===== * Схема подключения кнопок с использованием конденсаторов, резисторов и микросхемы 74HC14, которая называется инвертирующий триггер Шмитта, нужна для аппаратного подавления дребезга. Посмотрите [[видеоуроки:10-прерывания-и-аппаратная-стабилизация|видеоурок]] на эту тему. * В этом эксперименте нам нужно очень много цифровых портов, поэтому нам пришлось использовать порт 0. Пользоваться им неудобно из-за того, что он соединен с одним из каналов последовательного порта, поэтому перед прошивкой микроконтроллера нам придется отключать провод, идущий к пьезопищалке, а после прошивки подключать его обратно. ===== Скетч ===== #define BUZZER_PIN 0 #define FIRST_BAR_PIN 4 #define BAR_COUNT 10 #define MAX_SCORE 20 // глобальные переменные, используемые в прерываниях (см. далее) // должны быть отмечены как нестабильные (англ. volatile) volatile int score = 0; void setup() { for (int i = 0; i < BAR_COUNT; ++i) pinMode(i + FIRST_BAR_PIN, OUTPUT); pinMode(BUZZER_PIN, OUTPUT); // Прерывание (англ. interrupt) приостанавливает основную // программу, выполняет заданную функцию, а затем возобновляет // основную программу. Нам нужно прерывание на нажатие кнопки, // т.е. при смене сигнала с высокого на низкий, т.е. на // нисходящем (англ. falling) фронте attachInterrupt(INT1, pushP1, FALLING); // INT1 — это 3-й пин attachInterrupt(INT0, pushP2, FALLING); // INT0 — это 2-й пин } void pushP1() { ++score; } // функция-прерывание 1-го игрока void pushP2() { --score; } // функция-прерывание 2-го игрока void loop() { tone(BUZZER_PIN, 2000, 1000); // даём сигнал к старту. // пока никто из игроков не выиграл, обновляем «канат» while (abs(score) < MAX_SCORE) { int bound = map(score, -MAX_SCORE, MAX_SCORE, 0, BAR_COUNT); int left = min(bound, BAR_COUNT / 2 - 1); int right = max(bound, BAR_COUNT / 2); for (int i = 0; i < BAR_COUNT; ++i) digitalWrite(i + FIRST_BAR_PIN, i >= left && i <= right); } tone(BUZZER_PIN, 4000, 1000); // даём сигнал победы while (true) {} // «подвешиваем» плату до перезагрузки } ===== Пояснения к коду ===== * Код нашей обычной программы исполняется инструкция за инструкцией и если мы, например, проверяем состояние датчика, мы к нему обратимся только в те моменты, когда очередь дойдет до соответствующей инструкции. Однако мы можем использовать прерывания: * по наступлении //определенного события// * на //определенном порту// ход программы будет приостанавливаться для выполнения * //определенной функции//, а затем программа продолжит исполняться с того места, где была приостановлена. * Arduino Uno позволяет делать прерывания на портах 2 и 3. * В ''setup()'' прописывается инструкция ''attachInterrupt(interrupt, action, event)'', где * ''interrupt'' может принимать значения ''INT0'' или ''INT1'' для портов 2 и 3 соответственно. Можно задать эти значения и с помощью функции ''digitalPinToInterrupt(pin)'', где вместо ''pin'' указать номер пина. * ''action'' — имя функции, которая будет вызываться при наступлении события * ''event'' — событие, которое мы отслеживаем. Может принимать значение ''RISING'' (изменение от низкого уровня сигнала к высокому, от 0 к 1), ''FALLING'' (от высокого уровня к низкому, от 1 к 0), ''CHANGE'' (от 0 к 1 или от 1 к 0), ''LOW'' (при низком уровне сигнала). * Глобальные переменные, к которым мы обращаемся из функции, обрабатывающей прерывания, должны объявляться с использованием ключевого слова ''volatile'', как в данном эксперименте ''volatile int score = 0''. * Внутри функции, вызываемой по прерыванию, нельзя использовать ''delay()''. * Функция ''abs(value)'' возвращает абсолютное значение ''value'' (значение по модулю). Обратите внимание, что функция может сработать некорректно, если передавать ей выражение, которое еще не вычислено, например ''abs(++a)'', лучше передавать ей просто переменную. * Функция ''min(val1, val2)'' вернет меньшее из ''val1'' и ''val2''. * Функция ''max(val1, val2)'' вернет большее из ''val1'' и ''val2''. * В данном эксперименте мы вычисляем значение, которое записывается на светодиоды, прямо в ''digitalWrite()'' * Мы уже знакомы с логическим «и» (''&&''). Нередко нужен оператор «логическое «или»: ''||''. Он возвращает «истину», если хотя бы один из операндов имеет значение «истина». ''false || false'' вернет ''false'', а ''true || true'', ''true || false'' и ''false || true'' вернут ''true''. * Мы использовали ''while(true){}'' для того, чтобы ''loop()'' остановился после того, как кто-то выиграл: у ''while'' всегда истинное условие и он бесконечно ничего не выполняет! ===== Вопросы для проверки себя ===== - Каким образом мы подавляем дребезг аппаратно? - Для чего используются прерывания? - Каким образом можно включить обработку внешних прерываний? - О каких нюансах работы с уже известными нам вещами следует помнить при работе с прерываниями? - Как выбрать максимальное из двух значений? Минимальное? - Как получить абсолютное значение переменной? Чего следует избегать при использовании этой функции? - Когда оператор логическое «или» возвращает «ложь»? ===== Задания для самостоятельного решения ===== - Вместо светодиодной шкалы подключите сервопривод и измените код таким образом, чтобы перетягивание демонстрировалось путем отклонения сервопривода от среднего положения. ---- [[светильник-управляемый-по-usb|← Светильник, управляемый по USB]] | [[конспект-arduino:|Оглавление]] | Конец первой серии экспериментов!