В этом эксперименте мы создаем еще одну игру, на этот раз нужно быстрее соперника нажать кнопку 20 раз.
СПИСОК ДЕТАЛЕЙ ДЛЯ ЭКСПЕРИМЕНТА
- 1 плата Arduino Uno;
- 1 беспаечная макетная плата;
- 1 светодиодная шкала;
- 10 резисторов номиналом 220 Ом, 4 резисторов номиналом 100 кОм;
- 2 тактовых кнопки;
- 2 керамических конденсатора номиналом 100 нФ;
- 1 пьезопищалка;
- 1 инвертирующий триггер Шмитта;
- 24 провода «папа-папа».
ДЕТАЛИ ДЛЯ ДОПОЛНИТЕЛЬНОГО ЗАДАНИЯ
- 1 сервопривод, 1 конденсатор 220 мкФ.
ПРИНЦИПИАЛЬНАЯ СХЕМА
СХЕМА НА МАКЕТНОЙ ПЛАТЕ
ОБРАТИТЕ ВНИМАНИЕ
- Схема подключения кнопок с использованием конденсаторов, резисторов и микросхемы 74HC17, которая называется инвертирующий триггер Шмитта, нужна для аппаратного подавления дребезга.
- В этом эксперименте нам нужно очень много цифровых портов, поэтому нам пришлось использовать порт 0. Пользоваться им неудобно из-за того, что он соединен с одним из каналов последовательного порта, поэтому перед прошивкой микроконтроллера нам придется отключать провод, идущий к пьезопищалке, а после прошивки подключать его обратно.
СКЕТЧ
скачать скетч для Arduino IDE
#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 соответственно
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 всегда истинное условие и он бесконечно ничего не выполняет!
ВОПРОСЫ ДЛЯ ПРОВЕРКИ СЕБЯ
- Каким образом мы подавляем дребезг аппаратно?
- Для чего используются прерывания?
- Каким образом можно включить обработку внешних прерываний?
- О каких нюансах работы с уже известными нам вещами следует помнить при работе с прерываниями?
- Как выбрать максимальное из двух значений? Минимальное?
- Как получить абсолютное значение переменной? Чего следует избегать при использовании этой функции?
- Когда оператор логическое «или» возвращает «ложь»?
ЗАДАНИЯ ДЛЯ САМОСТОЯТЕЛЬНОГО РЕШЕНИЯ
- Вместо светодиодной шкалы подключите сервопривод и измените код таким образом, чтобы перетягивание демонстрировалось путем отклонения сервопривода от среднего положения.
С оригиналом статьи вы можете ознакомиться на сайте Amperka.ru
ЭКСПЕРИМЕНТ 19 | ОГЛАВЛЕНИЕ |