Сегодня мы соберем секундомер, взяв за основу схему 4-битного подключения
ЖК-дисплея к Arduino из прошлого занятия.
В Tinkercad это будет выглядеть так:
#includeПрограмма получилась очень простая, правда и умеет она не слишком много, да и точность нашего секундомера оставляет желать лучшего. Секундомер запускается сразу при включении и нет возможности запускать и останавливать секундомер когда это потребуется. Конечно, можно воспользоваться кнопкой "Сброс" контроллера, но это не очень удобно, поэтому добавим кнопку, которая позволит запускать и останавливать секундомер. Добавить кнопку и запрограммировать её можно так, как мы это делали на уроке "Зажигаем огни". Кнопки и светодиоды., в котором мы рассмотрели различные варианты программирования кнопки, в том числи и с учетом "дребезга контактов". Кнопку подключаем к выводу D2.LiquidCrystal lcd(3, 5, 10, 11, 12, 13); long Seconds; // переменная Seconds будет содержать количество секунд void setup() { lcd.begin(16, 2); lcd.clear(); } void loop() { lcd.setCursor(0, 0); // устанавливаем курсор в вехний левый угол дисплея lcd.print(Seconds); // выводим результат на дисплей delay(1000); // ждём 1 секунду Seconds += 1; // увеличиваем значение переменной Seconds на 1 }
В Tinkercad полностью схема будет выглядеть так:
#include < LiquidCrystal.h> LiquidCrystal lcd(3, 5, 10, 11, 12, 13); long Seconds; boolean button; // в эту переменную быдум записывать состояние кнопки. boolean btnUp = true; // была ли кнопка отпущена? void setup() { lcd.begin(16, 2); lcd.clear(); pinMode(2, INPUT); // Программируем вывод D2 как вход, и подключаем к нему кнопку. } void loop() { boolean btn = digitalRead(2); // Присваиваем переменной btn значение вывода D8 if (!btn && btnUp) // Если кнопка нажата, но до этого была отпущена { delay(1); boolean btn = digitalRead(2); // Присваиваем переменной btn значение вывода D8 if (!btn) button=!button; // если кнопка нажата, переменной button присвоить противоположенное значение delay(1); // Ждем 1 милисекунду. } btnUp = btn; // запоминаем последнее состояние кнопки для нового цикла lcd.setCursor(0, 0);// выводим результат на дисплей lcd.print(Seconds); delay(1000); //ждём 1 секунду if (button == 1){ Seconds += 1; } // увеличиваем Seconds на 1, только если кнопка была нажата }И в принципе все работает, в нужный момент мы можем кнопкой запустить секундомер, или остановить его, а потом продолжить счет, а сбросить показания секндомера можно кнопкой "Сброс" платы Arduino.
#include < LiquidCrystal.h> LiquidCrystal lcd(3, 5, 10, 11, 12, 13); long Seconds; int button; // кнопка "Старт/Стоп" void setup() { lcd.begin(16, 2); lcd.clear(); pinMode(2, INPUT); // Программируем вывод D2 как вход attachInterrupt(0, btn, FALLING); // "Ловим" кнопку при переходе с высокого сигнала на низкий и переходим в цикл batton } void btn () { button = !button; //"переворачиваем" значение при нажатии кнопки } void loop() { lcd.setCursor(0, 0); // выводим результат на дисплей lcd.print(Seconds); delay(1000); //ждём 1 секунду Seconds += button; // прибовляем к переменной Seconds значение переменной button }
Вместо программы обработки "дребезга" здесь появилась строка
attachInterrupt(0, btn, FALLING);
Функция attachInterrupt() - это функция прерывания.
Прерывание - это сигнал, который сообщает процессору о наступлении какого-либо
события, которое требует незамедлительного внимания. Процессор должен
отреагировать на этот сигнал, прервав выполнение текущих инструкций и передав
управление обработчику прерывания (ISR, Interrupt Service Routine).
Обработчик - это обычная функция, которую мы пишем сами и помещаем туда тот код,
который должен отреагировать на событие. В нашем примере это обработчиком
прерывания является подпрограмма btn() - она выполняется один раз, когда
прерывание произошло.
После обслуживания прерывания ISR функция завершает свою работу и процессор с
удовольствием возвращается к прерванным занятиям - продолжает выполнять код
с того места, в котором остановился.
Синтаксис вызова: attachInterrupt(interrupt, function, mode)
Аргументы функции:
interrupt - номер вызываемого прерывания (стандартно 0 - для 2-го пина,
1 - для 3-го пина),
function - название вызываемой функции при прерывании (важно - функция
не должна ни принимать, ни возвращать какие-либо значения),
mode - условие срабатывания прерывания.
Прерывания в Ардуино можно разделить на несколько видов:
attachInterrupt(0, btn, FALLING);работает следующим образом, во первых отслеживается пин D2, именно он отвечает за прерывание int_0. Выполняется прерывание один раз при изменении сигнала от HIGH к LOW, т.е. сразу как будет нажата кнопка. И если кнопка была нажата, то управление передается подпрограмме btn(), которое изменит значение переменной button, и если значение переменной button = 1 - секундомер будет считать, так как переменная Seconds будет увеличивать на значение button каждую секунду, а если button=0, то Seconds изменяться не будет.
#include < LiquidCrystal.h> LiquidCrystal lcd(3, 5, 10, 11, 12, 13); long Seconds; int button; // кнопка "Старт/Стоп" void setup() { lcd.begin(16, 2); lcd.clear(); pinMode(2, INPUT); // Программируем вывод D2 как вход pinMode(7, INPUT); // Программируем вывод D7 как вход attachInterrupt(0, btn, FALLING); // "Ловим" кнопку при переходе с высокого сигнала на низкий и переходим в цикл batton } void btn () { button = !button; //"переворачиваем" значение при нажатии кнопки } void loop() { lcd.setCursor(0, 0); // выводим результат на дисплей lcd.print(Seconds); delay(1000); //ждём 1 секунду Seconds += button; // прибовляем к переменной Seconds значение переменной button Seconds = Seconds * digitalRead(7); //если ножали на кнопку 2 то счотчик обнулится }Просто умножим переменную Seconds на считанное с вывода 7 значение! И если кнопка не нажата, то ничего и не произойдет, так как вывод подтянут через резистор к плюсу питания и на нем всегда высокий уровень, или единица, а умножение на 1 никак не изменит значение переменной Second. Но если кнопку нажать, но на выводе 7 будет низкий уровень или ноль, а при умножении не 0 переменная Second станет равной нулю.