Много кнопок на одном пине. Урок 20. Ардуино
Привет! Как контролировать много кнопок на одном пине Ардуино. И как много кнопок можно подключить. Как разделить нажатия разных кнопок. Попробуем собрать схему и написать программу для такого разделения.
В одном из недавних уроков мы разбирали использование прерываний. Посмотрите тот пост, если пропустили или уже забыли. Сегодня мы используем его для новой схемы.

Дребезг кнопки
Но в первую очередь, опять поговорим о дребезге кнопки. Используя прерывания мы не можем использовать функции задержки программы в функции прерывания. Поэтому решить проблему дребезга придется решить аппаратным путем.
Для этого добавим в схему конденсатор. Соединим конденсатор последовательно с кнопкой, таким образом при замыкании кнопки конденсатор будет быстро заряжен. А при выключении кнопки, конденсатор медленно разрядится, таким образом эффект дребезга будет сглажен. На графике плоттера Ардуино можно увидеть разницу двух схем.

Подключим несколько кнопок
Разделить кнопки в программе можно по различным показаниям напряжения на аналоговом пине. Чтобы этого добиться, подключим кнопки через резисторы последовательно и считаем показания на аналоговом пине. Таким образом, чем ближе кнопка к делителю напряжения, тем большие показания напряжения будут считаны.

Не забудем подключить конденсатор после кнопок, чтобы сгладить сигналы от всех нажатий кнопок.
На графике плоттера мы видим, что каждая кнопка выдает свой уровень напряжения и в программе мы можем отделить каждую кнопку друг от друга.

Прерывание
Теперь займемся программой. Мы хотим использовать прерывание при нажатии на кнопку, чтобы в основном цикле программы у нас была возможность опрашивать датчики или управлять светодиодами. А кнопками мы будем выполнять дополнительные функции программы.
Добавим прерывание, а чтобы оно срабатывало как нужно соединим второй пин Ардуино и выход первой кнопки. Таким образом при нажатии на любую кнопку на второй пин попадет достаточное напряжение, чтобы включить прерывание.
Напишем функцию, которая определит показания на аналоговом пине и выведет в монитор порта соответствующую подпись.
void setup() < Serial.begin(9600); pinMode(buttonPin, INPUT); attachInterrupt(0, swap, RISING); >void swap()< buttonState = analogRead(buttonPin); if (buttonState >= 1000) if (buttonState >= 513 && buttonState < 1000)if (buttonState >= 300 && buttonState < 500)if (buttonState >= 250 && buttonState < 300)if (buttonState >= 200 && buttonState < 250)if (buttonState >= 150 && buttonState < 200)Serial.println(str); >
Показания для каждой кнопки вы можете посмотреть в мониторе порта.
Полный текст программы
Финальная схема будет выглядеть таким образом.

А программа таким
const int buttonPin = 0; int buttonState = 0; String str; void setup() < Serial.begin(9600); pinMode(buttonPin, INPUT); attachInterrupt(0, swap, RISING); >void swap()< buttonState = analogRead(buttonPin); if (buttonState >= 1000) if (buttonState >= 513 && buttonState < 1000)if (buttonState >= 300 && buttonState < 500)if (buttonState >= 250 && buttonState < 300)if (buttonState >= 200 && buttonState < 250)if (buttonState >= 150 && buttonState < 200)Serial.println(str); > void loop() < //buttonState = analogRead(buttonPin); //Serial.println(buttonState); //delay(50); >
Заключение
Мы подключили к Ардуино несколько кнопок. Использовали для этого только один пин. А также, избавились от дребезга кнопки с помощью конденсатора. В ближайшем проекте мы попробуем использовать такую схему для управления телевизором. Потому что, схема с кнопками очень похожа на пульт ДУ.
Arduino: Подключение нескольких кнопок к одному аналоговому входу
Речь в статье пойдет о двух разных схемах подключения большого количества кнопок к одному аналоговому входу Arduino. Принцип работы схем основан на чтении и интерпретации аналого-цифровым преобразователем микроконтроллера индивидуального напряжения, формируемого разными комбинациями отдельных участков схемы.
Обе схемы, вне зависимости от количества кнопок ( в разумных пределах), в каждый активный ( нажатие кнопок/кнопки) момент времени представляют из себя классический резистивный делитель напряжения. Но принципиальные отличия и особенности работы каждой из них имеют различные алгоритмы расчета элементов и конечное поведение. Все примеры в конце, после теории.
Схема №1 — резистивно-последовательная
Принципиально схема может исполняться в 2 вариантах и выглядит следующим образом:
По данной схеме реализована обратная связь о состоянии системы в проекте Codius.AutoFeeder 1.0.
Видео о проекте:
Несмотря на кажущуюся схожесть схемы работают немного по разному
Различия схем заключаются в том, что при нажатии кнопки S4 на левой схеме, аналоговый вход считает максимальное напряжение — 5 В, а кнопка S1 даст минимальное напряжение, сниженное всеми резисторами в цепи. В правой схеме, наоборот, максимальное напряжение даст кнопка S1 .
Приведенные схемы — пример последовательного соединения резисторов. Как известно, для последовательного соединения резисторов:
$$R_<общ>=R_1+R_2+. +R_n$$общ>
При нажатии любой кнопки в схеме мы получаем классический резистивный делитель напряжения:
В котором значения напряжений и сопротивлений находятся в зависимости, выражающейся следующей формулой:
Из формулы видно, что зависимость выходного напряжения $U_$ от сопротивлений $R_1$ и $R_2$ нелинейная, а экпоненциальная. График это подтверждает. Единственный нюанс заключается в том, что на оси абсцисс номиналы резистора R1 , представлены в номиналах стягивающего резистора R2 $x=R_2$:
Например, если $R_2=10 КОм$, то при $R_1=3 \times R_2=30 КОм, U_<вых>=1,25 В$вых>
Исходя из графика можно наглядно представить каким образом рассчитать номиналы резисторов в зависимости от количества кнопок. И здесь, как обычно, возможны 2 варианта — либо подбирать номиналы так, чтобы разбить интервал на равные промежутки напряжений, либо использовать в схеме резисторы только одного номинала:
Пример использования 4 резисторов для 5 кнопок.
Слева резисторы подобраны под равномерную дискретность напряжений, справа — резисторы одного номинала.
В первом случае ( левый график), номиналы резисторов подобраны таким образом, чтобы обеспечить равные интервалы при определении значений напряжения АЦП. Такой подход очень удобен для количества кнопок $2^n$, ( при $n \geqslant 2$), т. е. 4, 8, 16,… Удобство заключается в том, что результат работы АЦП при помощи битового сдвига можно округлить, при этом максимально нивелировать погрешности АЦП и номиналов резисторов. Пример:
Предположим 4 кнопки генерируют напряжения — 5 В ( первая кнопка без резистора), 3.75 В, 2.5 В, 1.25 В, что соответствует значениям АЦП — 1023, 767, 511, 255. Теперь, если к полученным значениям применить операцию битового сдвига, получим значения 4, 3, 2, 1 для кнопок, и 0 для состояния покоя:
1
2
3
4
5
6
7
8
9
10
11
void setup () < Serial.begin(9600); Serial.println( (1023 >> 8) + 1); // 4 Serial.println( (767 >> 8) + 1); // 3 Serial.println( (511 >> 8) + 1); // 2 Serial.println( (255 >> 8) + 1); // 1 > void loop () < >
Сдвигаем на 8 бит потому из 10-битного значения нужно оставить всего 2 бита, в которых закодированы значения от 0 до 3. К ним прибавляется единица, чтобы отделить полученные состояния от состояния спокойствия — 0. В случае использования большего количества кнопок сдвиг нужно уменьшать — для 8 кнопок до 7, для 16 до 6 и т. д.
Но если пороговое значение 1023 никогда не будет превышено, то значения 767, 511 и 255 могут колебаться и в большую и в меньшую сторону. Так вот, чтобы значение 512 не давало результат 3, а значение 256 не давало результат 2, от преобразуемого значения нужно вычесть половину диапазона между значениями — 128.
1
2
3
4
5
6
for ( int i = 1023; i >= 0; i—) < int result = i; Serial.print(i); Serial.print( ": " ); Serial.println( ((result - 128) >> 8) + 1); >
В этом случае погрешность АЦП будет устранена — ошибок уже не будет.
Результат:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1023: 4 . 896: 4 895: 3 . 640: 3 639: 2 . 384: 2 383: 1 . 128: 1 127: 0 // . // состояние покоя 0: 0 //
Весь диапазон жестко поделен на сектора значений.
Соответственно для большего количества кнопок диапазон будет уменьшаться.
Но использовать этот вариант можно и с количеством кнопок не кратным $2^n$. В этом случае погрешность АЦП устраняется программно другими методами — внедрением значения ошибки.
Недостаток такого способа заключается в ограничении количества кнопок – при увеличении их числа, номиналы резисторов кнопок, отвечающих за низкие напряжения ( менее 1 В), растут в геометрической прогрессии.
Формула для расчета номиналов резисторов для установки равных промежутков напряжений для количества кнопок $k$, и привязанных к номиналу стягивающего регистра $R_$ ( если принять $R_$ за единицу, то полученные коэффициенты можно использовать при расчете сопротивлений при другом заданном стягивающем сопротивлении):
$u= \over k>$ — шаг дискретности выходного напряжения
Подключение кнопок к одному аналоговому входу
Цель этой статьи – показать как подключить несколько кнопок ( для пользовательского ввода), использую только один аналоговый вход. Это позволит оставить остальные цифровые I/O пины для других задач, например, для подключения LCD дисплея и т.д.
Для этого прежде всего нужно знать как использовать функцию analogRead ( ).
В этой статье мы покажем как считывать отдельные нажатия кнопок.
Напомним, контакт аналогового входа подключен к аналого-цифровому преобразователю ( АЦП) в микроконтроллере Arduino. Он имеет разрядность десять бит, и может возвращать численное значение от 0 до 1023, которое связано с аналоговым напряжением от 0 до 5 вольт.
LiquidCrystal lcd (12 , 11, 5, 4, 2, 3);
pinMode (A5 , INPUT);
a = analogRead (5 );
lcd.setCursor (0 ,0);
lcd.setCursor (0 ,1);
lcd.print ( » value is:»);
Вы можете просмотреть видео, которое показывает как контроллер измеряет напряжение на переменном резисторе 10 кОм и выводит на дисплей значения от 0 до 1023.
С уменьшением сопротивления потенциометра, значение, возвращенное analogRead ( ) увеличивается. Поэтому при определенных значениях сопротивления, analogRead ( ) вернет определенные числовые значения.
Если мы соберем схему из, например, пяти кнопок и резисторов, которые позволят задавать различное напряжение на аналоговом выводе, каждое считанное напряжение вызовет функцию analogRead ( ) и считает определенное значение. И таким образом мы можем прочитать статус каждой из кнопок, с помощью одного аналогового входа.
Схема подключения кнопок:
На видео вы можете увидеть как это работает на практике:
Откуда берется ток? При использовании pinMode ( А5, INPUT); в цепь включается внутренний подтягивающий резистор в микроконтроллере, который дает нам ~ 4,8 В.
Некоторые из вас могли заметить, что когда самая правая кнопка нажата, цепь закорочена между А5 и GND. Когда эта кнопка нажата, ток составляет менее одного миллиампера в связи с использованием нагрузочного резистора защищая нас от короткого замыкания. Также обратите внимание, что вы можете использовать не только А5, а любой аналоговый вход.
Как показано в видео, значения, которые возвращает analogRead ( ):
- 1023 не нажата кнопка ( по умолчанию)
- 454 кнопка один
- 382 кнопки два
- 291 кнопка три
- 168 для кнопки четыре
- 0 для кнопки пять
Так что для нашего проекта при нажатии на кнопку должно считывать определенное значение, возвращенного с помощью analogRead ( ). Выбор одинаковых резисторов дает нам довольно широкий разброс между значениями АЦП. Однако эти показания могут незначительно изменяться из-за теплового коэффициента или паразитных сопротивлений цепи.
Так что при создании схемы вам следует замерять значения для каждой кнопки создать библиотеку значений в программе при считывании с аналогового входа. Это важно если создаете несколько проектов, одинаковые резисторы одной партии имеют небольшой разброс значений.
Пример, который показывает, как выводить номер кнопки на дисплей:
LiquidCrystal lcd (12 , 11, 5, 4, 2, 3);
pinMode (A5 , INPUT_PULLUP); // sets analog pin for input > int readButtons (int pin) // returns the button number pressed, or zero for none pressed // int pin is the analog pin number to read
c=analogRead (pin ); // get the analog value if (c >1000)
b=0; // buttons have not been pressed
b=1; // button 1 pressed
b=2; // button 2 pressed
b=3; // button 3 pressed
b=4; // button 4 pressed
b=5; // button 5 pressed
if (a ==0) // no buttons pressed
lcd.setCursor (0 ,1);
lcd.print ( «Press a button»);
if (a >0) // someone pressed a button!
lcd.setCursor (0 ,2);
lcd.print ( «Pressed button»);
delay (1000 ); // give the human time to read LCD
> На видео показана работа программы.
10.01.2012, 56421 просмотр.
найдена ошибка в скетче!
Приведенный здесь скетч отказывается воспринимать правильно нажатия, а вместо этого значения скачут как им вздумается от 0 до 1023. Автор забыл включить подтягивающие резисторы, о которых в тексте идет речь. Правильно инициализировать порт так: pinMode (A5, INPUT_PULLUP); Всем удачи!
Подключение нескольких кнопок к одному выводу Ардуино
Иногда, делая более мощный проект на Arduino Uno, случается, что не хватает цифровых выводов. В сегодняшней статье мы покажем вам интересный способ уменьшить количество контактов, для используемых кнопок.
Как вы знаете, самый простой и, вероятно, самый надежный способ подключения кнопок — это тот, при котором каждая кнопка подключается к отдельному контакту ввода/вывода. Однако иногда случается, что для реализации всех функций нашего устройства нам необходимо сохранить ценные цифровые выводы.
Мы можем, например, соединить 16 кнопок, используя всего 8 контактов, пересекая строки со столбцами. Еще один интересный вариант, в котором используется только один аналоговый вывод — это создание делителя напряжения.
Принцип работы и схема
Arduino Uno имеет до 6 аналоговых выводов (АЦП), способных измерять напряжение в диапазоне 0-5 В с точностью до 10 бит (что переводится в значения от 0 до 1023).
Зная закон Ома, мы можно создать делителя напряжения, то есть 5В (напряжение питания) можно разделить, например, на 1, 2, 3, 4 и 5 В. Для этого нам будет достаточно обычных резисторов (1,1кОм) соединенных последовательно. Один конец этой цепи подключаем к 5В, а другой к минусу питания.

Таким образом, на каждом соединении мы получаем разный потенциал, который мы можем считать с помощью аналогового вывода Arduino.
Для того чтобы выбранное напряжение подавалось на аналоговый порт Arduino, достаточно подключить одну ножку кнопки к резистору, а другую — непосредственно к аналоговому выводу Ардуино. Кроме того, необходимо аналоговый вывод подключить к земле, чтобы всегда было определенное состояние даже если ни одна кнопка не нажата.
Кнопочное управление
Цель заключается в том, чтобы прочитать правильное значение на аналоговом порту. В качестве первого скетча — программа, которая в основном цикле выводит в монитор последовательного порта каждые 100 мс значение, считанное с АЦП.
Теперь, зная эти значения, мы можем объявить несколько полезных констант:
#define BTN_PIN A0 #define BTN1 255 #define BTN2 340 #define BTN3 512 #define BTN4 1023
Это значения, рассчитанные аналого-цифровым преобразователем. Следующая функция будет возвращать true или false в зависимости от того, нажата ли кнопка:
bool isAnalogBtnPressed (int analogVal, int btnValue)
Она принимает два параметра: analogVal, то есть значение, считываемое с помощью функции analogRead, и btnValue, то есть значение для нажатой кнопки.
Как видите, мы используем функцию abs (), чтобы проверить, насколько далеко фактическое показание АЦП от того значения, которую должна представлять кнопка. Если это расстояние не больше 20, функция вернет true. Мы установили здесь определенный порог ошибки из-за того, что измерение АЦП не всегда идеально.
Теперь мы можем написать основной цикл программы, в котором в монитор последовательного порта будет выводиться сообщение о нажатии соответствующей кнопки:
void loop () < int btnData = analogRead (BTN_PIN); if (isAnalogBtnPressed (btnData, BTN1)) < Serial.println («Button 1 pressed»); >else if (isAnalogBtnPressed (btnData, BTN2)) < Serial.println («Button 2 pressed»); >else if (isAnalogBtnPressed (btnData, BTN3)) < Serial.println («Button 3 pressed»); >else if (isAnalogBtnPressed (btnData, BTN4)) < Serial.println («Button 4 pressed»); >delay (100); >
не забываем про соответствующую настройку в функции setup ():
void setup ()
Вот и все Благодаря этой простой процедуре мы уменьшили количество контактов с 4 до 1, но при этом ничто не мешает добавить туда еще больше кнопок.
При использовании этого метода помните, что мы не сможем нажимать более одной кнопки за раз. Если мы нажмем кнопку одновременно с напряжением 4 В и кнопкой с 2 В, то Arduino распознает кнопку с более высоким напряжением (т. е. выиграет меньше сопротивление).
