[sape_tizer]
Чт. Июн 1st, 2023

Что и как строить на земельном участке или обо всем понемногу

Все о инструментах, строительстве, работах, АРДУИНО, электрика, электроника и многое другое

Урок 7.3- Подпрограммы управления на кнопках

В прошлом уроке мы подключили LCD и научились извлекать звук из Ардуино. Т.е. фактически мы уже сделали полдела для полного функционала задуманных часов! Теперь пришло время разобраться с управлением к нашему контроллеру. Не будем тянуть кота за… бубенчики, переходим сразу к теме разговора.

Кнопочное управление.

Как и было сказано выше в нашем проекте будет 3 кнопки которые будут выполнять следующие функции: Слева- МИНУС, Середина- МЕНЮ, Справа- ПЛЮС. Про управление поговорим позже когда будем привязывать кнопки к требуемым функциям, сейчас нам надо указать контроллеру что мы нажимаем, например, кнопку МИНУС а не какую другую. Опять же для решения данной задачи есть несколько путей. Начнем с самого легкого. Собираем любую схему как было написано в уроке 7.1.  Теперь давайте проследим какие значения нам будут выдавать кнопки при нажатии. Давайте вместе напишем простейшую программу для отслеживания состояния порта А0 и выводом значений в Serial. Попробуйте сами написать без моей подсказки. Если не можете- давайте писать вместе пошагово.

void setup(){

Serial.begin(9600);// подключаем Serial

}

void loop() {

Serial.println(analogRead(A0));// выдаем в порт все что мы считали с порта

}

Данная программа содержит ВСЕГО 6 строк и начнет читать данные и с огромной скоростью передавать на монитор Serial значения считанные с порта А0 что не всегда удобно и вызывает рябь в глазах. Давайте допишем условие что пока на входе логический ноль данные не передаем.Схема подключения кнопок к часам на АРДУИНО

Схема подключения кнопок к часам на АРДУИНО логический ноль нам обеспечит резистор  который подключен напрямую к минусовому проводу в цепи управления кнопок. В данной схеме это резистор R5. При нажатии кнопок напряжение на А0 повысится в соответствии с подключенными резисторами в резистивном делителе, и вот на этом событии можно поставить условие на вывод. Давайте посчитаем грубо. У нас 4 резистора по 1 кОм подключены к 5 В. На каждом резисторе напряжение составляет 5 Вольт/ 4 резистора= 1,25В. Это если не учитывать резистора R5 который дополнительно зашунтирует входную цепь входа А0.

void setup(){

Serial.begin(9600);// подключаем Serial

}

void loop() {

if (analogRead(A0)>5)//если напряжение больше 5 бит

{

Serial.println(analogRead(A0));// выдаем в порт все что мы считали с порта

}

}

Вам может показаться странным строка if (analogRead(A0)>5)//если напряжение больше 5 бит и как напряжение может оказаться больше 5 бит???!!! Оно же измеряется в Вольтах!!! И вы будете правы. НО! Как было написано раньше, максимальное напряжение на входах Ардуино- 5В, вход А0- 10 битный, т.е. он раскладывает напряжение 5 Вольт на 1024 ступеньки по (5 Вольт/ 1024 бит)=0,00488 Вольт и прописав в условии analogRead(A0)>5 мы отбрасываем помехи до (0,00488 Вольт* 5 бит)= 0,0244 Вольта, которые могут вызвать ложное срабатывание операторов Serial.println(analogRead(A0));// выдаем в порт все что мы считали с порта. Кроме того вы, возможно, обратили внимание  что при нажатии кнопки всего ОДИН раз вылетает целый список разных значений. Добавьте после строки Serial.println(analogRead(A0));// выдаем в порт все что мы считали с порта  в новой строке delay (100); и вы будете получать по 1 значению каждые 100 миллисекунд. При нормальном нажатии получится всего лишь одно значение. Этот прием мы тоже будем использовать при написании обработчика нажатий кнопок.

Опрос кнопок на Ардуино с отображением в Serial
Опрос кнопок на Ардуино с отображением в Serial

Записываем/ запоминаем значения всех трех кнопок и можем переходить к дальнейшему программированию наших часов НО! Данный способ хорош для устройств имеющих надежное питание и малые пульсации питающего напряжения. Например: вы отладили часы на 5 В, затем подключили их к  литиевому аккумулятору  напряжением 4,2 В, соответственно все значения кнопок, которые вы прописали в контроллер могут уплыть и кнопки просто перестанут работать (на самом деле не должны так как в программе мы будем ставить условия на биты а не напряжение), или вы собрали готовое устройство а кнопки при сборке перепутали местами, или после сборки делителя запаяли не те резисторы. Это конечно не так страшно но можно немного заморочиться и сделать так что ничего паять и перепрограммировать не придется. Начнем осваивать более сложный, но и более гибкий путь, тем более что в проектах не всегда используются 3-4 кнопки. При использовании большого количества головная боль по прописыванию кнопок становится просто дикой.

Суть данного способа заключается в том что при старте контроллер проверяет свою память  в секции setup на наличие ячеек со значением ноль (ничего не записано) и если там действительно находится ноль то переходит к функции присваивания значений кнопкам. Если же при проверке памяти значение ячейки отличается от ноля то просто считываем данные из ячеек и присваиваем кнопкам! Все вроде просто до смешного, НО! Ячейки памяти могут вместить в себя 256 байт т.е. 8 бит информации, порт А0 как и все входы Ах 10 битный и «впихнуть» 1024 бита в 256 бита просто не получится. Выходов, как всегда, много, например выделять на одну кнопку четыре ячейки памяти (4 ячейки *256 бит)=1024 бит, просто складывать их и получать значение кнопок, можно просто посчитать до значения отклонения. Как это объяснить. Ну например нажатие кнопки S2 подаст на вход А0 517 бит, что больше 256 в 2 раза и еще остается 5 бит, иначе говоря 517=256+256+5. Т.к. 1024 бита содержит в 4 раза больше информации чем 256 бит то делим полученное значение на 4, т.е. 517/4=129,25, контроллер округлит это до 129 и запишет 129 в нужную ячейку. При прочтении ячейки контроллер должен уже умножить значение ячейки на 4, в итоге мы получим 129*4=516. Вроде уже меньше на 1 бит но в нашем случае это не критично (посмотрите выше на картинку- значения плавают в довольно большом пределе). Обработчик нажатия кнопок будет содержать условие на небольшое отклонение от изначально полученного. Т.е. значение в пределах ±20 бит  от полученного будут считаться нажатием на данную кнопку. Почему именно 20 и вообще зачем это нужно? Ну 20- это величина отклонения, вы можете указывать и большую и меньшую но при больших количествах кнопок следует ее уменьшить чтобы исключить возможность ложного срабатывания соседних по пределу кнопок, кроме того никто не защищен от дребезга контактов кнопок, а данное отклонение позволяет немного расширить предел срабатывания кнопок. Надеюсь на скриншоте выше видно, как значения при нажатии кнопки меняются от 239 до 255. Вероятность что вы попадете точно в указанное вами значение очень велика, но можно просто указать предел отклонения и ни о чем больше не думать нежели контроллер будет считать и улавливать передаваемые значения.

Кроме того данный способ можно еще модернизировать. Для это можно воспользоваться командами highByte и lowByte. Как работают данные операторы рассмотрим на примере ниже который я использовал в проекте паяльной станции:

iValue = analogRead(A2); // число > 255 разбиваем на байты (макс. 65536)
byte hi = highByte(iValue); // старший байт
byte low = lowByte(iValue); // младший байт

Видим что переменная iValue принимает значение пришедшее с порта А2, примем её как в примере выше равной 517. Дальше происходит присвоение переменной hi старшего байта. Что это означает. Это значит что программа делит данное число на 256 и выделяет целую часть. Иначе говоря в нашем случае мы 517 делим на 256, получается 2,01953125. Переменная hi будет равна 2 т.к. целая часть из получившегося произведения равна 2.  Дальше присваиваем переменной low  значение младшего байта, иначе говоря остаток от деления. Повторю пример выше 517= А х 256 + В, где А-старший байт, В- младший байт или в нашем случае 517=2 х 256 + 5. Младший байт в нашем случае будет равен 5. Эти две переменные мы записываем в память и обратно собираем оператором word. В результате опять получаем  посланное ранее значение. В отличии от описанного выше мы получаем точное значение.

int val = word(hi, low); // собираем значение по указанным ячейкам

Единственно что вы должны запомнить- значение не должно быть больше 65536, иначе вам придется делить старший байт снова и использовать для записи уже не 2 ячейки а 3.

Итак мы имеем- рабочую схему из 3 кнопок, значения этих самых кнопок. Осталось только запомнить эти самые значения. Опять же повторюсь, вы можете напрямую занести значения кнопок в скетч и ни о чем больше не думать но мы не ищем легких путей и поэтому сделаем универсальный скетч для ЛЮБОГО подключения кнопок! Давайте подключим еще одну библиотеку, которая будет работать с памятью Ардуино. Ничего скачивать уже не надо, данная  библиотека есть в наличии в интерфейсе Ардуино и поэтому делаем следущее: Ардуино- Скетч- Подключить библиотеку- EEPROM. У вас появится в самом верху надпись #include <EEPROM.h>, что означает что вы подключили библиотеку для работы с энергонезависимой памятью Ардуино. Что значит энергонезависимой? Т.е. при отключении питания данные в EEPROM остаются неизменными и не стираются как оперативные данные. Теперь мы можем указать что мы хотим сделать с памятью- записать в нее или напротив- считать. Теперь применив оператор  EEPROM.write(2, hi);  мы запишем в ячейку номер 2 значение старшего байта из примера выше и EEPROM.write(3, low);  значение младшего байта в ячейку 3.

Считать эти значения мы можем операторами:

byte hi=EEPROM.read(2); // считываем значение из 2 ячейки
byte low=EEPROM.read(3); // считываем значение из 3 ячейки

и затем опять же пересобрать

int val = word(hi, low); // собираем значение по указанным ячейкам

Данный способ нужно вызвать только один раз при старте, т.е в секции setup мы должны указать что если значение во второй и  третьей ячейке равно нулю то начинаем функцию присваивания кнопок. Почему именно проверять нужно сразу две ячейки? Ну у вас значение кнопки может быть меньше 256, т.е. во второй ячейке будет записан ноль. Если же значение кратное 256 (512,768,1024 и т.д.) то младший байт будет равен нулю т.к. прибавлять уже ничего не надо будет, т.е. в ячейке младшего байта будет тоже ноль. Поэтому и надо проверять условие сразу по обоим ячейкам. А что делать если вы уже записали значения кнопок, сделали новый блок кнопок и значения в памяти уже больше не подходят для нового блока. Давайте предусмотрим и этот вариант. Т.е. сделаем операцию очистки для EEPROM. Для этого задействуем еще один вывод Ардуино. Например А1. Дописываем в секцию setup следующий код.

// если необходимо переприсвоить кнопки то ставится перемычка и включается устройство

if (analogRead(A1)>10) //сброс ЕЕПРОМ для повторной настройки кнопок
{
byte count=0;
while(count!=100)
{
EEPROM.write(count, 0); //обнуляем ЕЕПРОМ с 0 до 100 ячейки
count++;
}

Следует подтянуть вывод А1 к Gnd через резистор 1кОм и от точки соединения А1 и резистора вывести контакт для установки перемычки или кнопки между точкой соединения и шиной +5В. Этот код нужно поставить выше кода присваивания кнопок. После сброса перемычку необходимо снять, если была кнопка то кнопку отпустить.

Вот такой интересный прием позволит вам программировать кнопки как Вам угодно. Для дальнейших проектов удобно написать готовую функцию и менять в коде только количество кнопок… ну и их описания. Надеюсь урок понравился. Кому что не понятно- пишите в комментариях.

<-Урок 7.2 Урок 7.4 часть 1->

 

Добавить комментарий