Адрес: 350059, г. Краснодар, переулок Плановый 25

Подключение нескольких кнопок к одному аналоговому входу

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

Обе схемы, вне зависимости от количества кнопок(в разумных пределах), в каждый активный(нажатие кнопок/кнопки) момент времени представляют из себя классический резистивный делитель напряжения. Но принципиальные отличия и особенности работы каждой из них имеют различные алгоритмы расчета элементов и конечное поведение. Все примеры в конце, после теории.

 
04  

Схема №1 — резистивно-последовательная

Принципиально схема может исполняться в 2 вариантах и выглядит следующим образом:

 
05  
Несмотря на кажущуюся схожесть схемы работают немного по разному
Несмотря на кажущуюся схожесть схемы работают немного по разному
 
06  

Различия схем заключаются в том, что при нажатии кнопки S4 на левой схеме, аналоговый вход считает максимальное напряжение — 5 В, а кнопка S1 даст минимальное напряжение сниженное, всеми резисторами в цепи. В правой схеме, наоборот, максимальное напряжение даст кнопка S1.

 
07  

Приведенные схемы — пример последовательного соединения резисторов. Как известно, для последовательного соединения резисторов:

 
08  
 
09  
Rобщ=R1+R2+...+RnRобщ=R1+R2+...+Rn
 
10  

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

 
11  
 
12  

В котором значения напряжений и сопротивлений находятся в зависимости, выражающейся следующей формулой:

 
13  
Uвых=Uвх×R2R1+R2Uвых=Uвх×R2R1+R2
 
14  

Из формулы видно, что зависимость выходного напряжения UвыхUвых от сопротивлений R1R1 и R2R2 нелинейная, а экпоненциальная. График это подтверждает. Единственный нюанс заключается в том, что на оси абсцисс номиналы резистора R1, представлены в номиналах стягивающего резистора R2 x=R2x=R2:

 
15  
Например, если $R_2=10 КОм$, то при $R_1=3 \times R_2=30 КОм, U_{вых}=1,25 В$
Например, если R2=10КОмR2=10КОм, то при R1=3×R2=30КОм,Uвых=1,25ВR1=3×R2=30КОм,Uвых=1,25В
 
16  

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

 
17  
Пример использования 4 резисторов для 5 кнопок. brСлева резисторы подобраны под равномерную дискретность напряжений, справа — резисторы одного номинала.
Пример использования 4 резисторов для 5 кнопок. 
Слева резисторы подобраны под равномерную дискретность напряжений, справа — резисторы одного номинала.
 
18  

В первом случае(левый график), номиналы резисторов подобраны таким образом, чтобы обеспечить равные интервалы при определении значений напряжения АЦП. Такой подход очень удобен для количества кнопок 2n2n,(при n2n⩾2), т. е. 4, 8, 16,… Удобство заключается в том, что результат работы АЦП при помощи битового сдвига можно округлить, при этом максимально нивелировать погрешности АЦП и номиналов резисторов. Пример:

 
19  
Предположим 4 кнопки генерируют напряжения — 5 В(первая кнопка без резистора), 3.75 В2.5 В1.25 В, что соответствует значениям АЦП — 1023767511255. Теперь, если к полученным значениям применить операцию битового сдвига, получим значения 4321 для кнопок, и 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 //
Весь диапазон жестко поделен на сектора значений.
Соответственно для большего количества кнопок диапазон будет уменьшаться.
 
20  

Но использовать этот вариант можно и с количеством кнопок не кратным 2n2n. В этом случае погрешность АЦП устраняется программно другими методами — внедрением значения ошибки.

 
21  

Недостаток такого способа заключается в ограничении количества кнопок – при увеличении их числа, номиналы резисторов кнопок, отвечающих за низкие напряжения(менее 1 В), растут в геометрической прогрессии.

 
22  

Формула для расчета номиналов резисторов для установки равных промежутков напряжений для количества кнопок kk, и привязанных к номиналу стягивающего регистра RcтRcт(если принять RcтRcт за единицу, то полученные коэффициенты можно использовать при расчете сопротивлений при другом заданном стягивающем сопротивлении):

 
23  

u=Uвхku=Uвхk — шаг дискретности выходного напряжения

 
24  
Rn=Uвх×Rстu×(kn)Rстn1i=1Ri,приnZ1nk1Rn=Uвх×Rстu×(k−n)−Rст−∑i=1n−1Ri, при n∈Z∣1⩽n⩽k−1
 
25  

Поскольку первая кнопка замыкается без резистора, то резистор нулевого сопротивления опускаем, отсюда nk1n⩽k−1 — эта кнопка даст результат равный UвхUвх — 5В. Рассмотрим расчет номиналов на примере приведенных 5 кнопок и 4 резисторов, стягивающий резистор Rст=10КОмRст=10КОм(полученный результат будет в КОм):

 
26  
u=Uвхk=5В5=1Вu=Uвхk=5В5=1В
 
27  
R1=5В×10КОм1В×(51)10КОм0КОм=2,5КОмR1=5В×10КОм1В×(5−1)−10КОм−0КОм=2,5КОм
 
28  
R2=5В×10КОм1В×(52)10КОм(2,5КОм)=4,167КОмR2=5В×10КОм1В×(5−2)−10КОм−(2,5КОм)=4,167КОм
 
29  
R3=5В×10КОм1В×(53)10КОм(2,5КОм+4,167КОм)=8,333КОмR3=5В×10КОм1В×(5−3)−10КОм−(2,5КОм+4,167КОм)=8,333КОм
 
30  
R4=5В×10КОм1В×(54)10КОм(2,5КОм+4,167КОм+8,333КОм)=25КОмR4=5В×10КОм1В×(5−4)−10КОм−(2,5КОм+4,167КОм+8,333КОм)=25КОм
 
31  

Результат: нам понадобятся резисторы следующих номиналов — 2.5 КОм4.167 КОм8.333 КОм25 КОм.

 
32  
Расположение резисторов с указанными номиналами и получаемые напряжения на каждой кнопке, в зависимости от выбранной схемы
Расположение резисторов с указанными номиналами и получаемые напряжения на каждой кнопке, в зависимости от выбранной схемы
 
33 На заметку:
Данный способ, автор считает наиболее предпочтительным. При чем, имеет смысл рассчитывать схему с количеством кнопок равным 2n2n, а использовать столько, сколько нужно. Например, если нужно 3 кнопки, то рассчитывать схему на 4 кнопки, а если нужно 5 кнопок, то схему сформировать исходя из 8 кнопок.

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

 
34  

Для второго случая, все намного проще – установлены резисторы одного номинала. При чем лучше, если этот номинал будет меньше номинала стягивающего резистора xx. В этом случае первые кнопки попадут в диапазон, где различия между напряжениями максимальны:

 
35  
На рисунке 10 резисторов, номиналом 0.1$x$ — например, при номинале стягивающего резистора 10 КОм, номинал резисторов кнопок — 1 КОм
На рисунке 10 резисторов, номиналом 0.1xx — например, при номинале стягивающего резистора 10 КОм, номинал резисторов кнопок — 1 КОм
 
36  

Диапазон напряжений находится по формуле, при номинале стягивающего резистора RстRст и номинале прочих резисторов RR:

 
37  
Uвыхn=Uвх×RстR×n+RстUвыхn=Uвх×RстR×n+Rст
 
38  

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

 
39  
 
40  

Основной отличительной особенностью резистивно-последовательной схемы является игнорирование нажатия нескольких кнопок — результат всегда будет указывать на нажатие единственной кнопки с меньшим по отношению к другим нажатым кнопкам суммарным сопротивлением:

 
41  
При одновременном нажатии 2 и 3 кнопки, ток потечет по контуру через 3 кнопку
При одновременном нажатии 2 и 3 кнопки, ток потечет по контуру через 3 кнопку
 
42 На заметку:
Достоинства схемы:
  • предсказуемое поведение,
  • сравнительная легкость расчетов,
  • нажатие нескольких кнопок одновременно приводит к предсказуемому результату,
  • варианты реализации — одинаковый шаг изменения выходного напряжения на выходе или одинаковые номиналы используемых резисторов.

Недостатки:
  • накапливаемая погрешность подбираемых номиналов резисторов,
  • для первого случая(равномерная дискретность значений напряжений) сложность расчетов,
  • ограничение количества кнопок в диапазоне напряжений до 1 В,
  • принципиален порядок расположения резисторов на схеме.
 
43  
Есть ещё один комбинированный способ подключения нескольких кнопок к одному аналоговому входу МК, при котором при использовании одинаковых номиналов напряжений, на выходе получаются напряжения равномерной дискретности.

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

 
44  

Схема №2 — резистивно-параллельная

Принципиально схема может исполняться в 2 вариантах и выглядит следующим образом:

 
45  
Обе схемы абсолютно идентичны
Обе схемы абсолютно идентичны
 
46  

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

 
47  
 
48  
1Rобщ=1R1+1R2++1Rn1Rобщ=1R1+1R2+…+1Rn
 
49  

И как следствие:

 
50  
Rобщ=11R1+1R2++1RnRобщ=11R1+1R2+…+1Rn
 
51  

Эта особенность(распознавание одновременно нажатых кнопок) является преимуществом при малом их количестве, и в то же время делает поведение схемы сложно предсказуемой при большом количестве кнопок. К этой ситуации мы вернемся чуть позже.

 
52 На заметку:
Важно иметь ввиду, что при подключении одной кнопки без сопротивления, для генерации 5 В(это не противоречит общей концепции) и, при её нажатии, вся схема не будет реагировать на нажатия других кнопок. Эта ситуация является исключением в общем правиле однозначной идентификации каждой комбинации из нажатых кнопок.
 
53  

Расчет номиналов сопротивлений схож с расчетом из резистивно-последовательной схемы, за тем лишь исключением, что каждое последующее сопротивление не нужно корректировать на полученные ранее:

 
54  
Rn=Uвх×RстUвыхnRстRn=Uвх×RстUвыхn−Rст
 
55  

Например, у нас есть схема из 3 кнопок, которые должны генерировать соответственно 1 В2.5 В и 4 В. Номинал стягивающего резистора 10 КОм, входное напряжение 5 В. Расчет номиналов резисторов будет следующим:

 
56  
R1=5В×10КОм1В10КОм=40КОмR1=5В×10КОм1В−10КОм=40КОм
 
57  
R2=5В×10КОм2,5В10КОм=10КОмR2=5В×10КОм2,5В−10КОм=10КОм
 
58  
R3=5В×10КОм4В10КОм=2,5КОмR3=5В×10КОм4В−10КОм=2,5КОм
 
59  

Но самое интересное, в этой схеме всплывает тогда, когда одновременно нажимается несколько кнопок — а с тремя кнопками этих комбинаций 4: 1+21+32+31+2+3.

 
60  

Немного математики...

Количество комбинаций нажатий кнопок определяется по формуле числа сочетаний из общего числа nn-объектов по kk-штук в комбинации:

 
61  
Ckn=n!(nk)!×k!Cnk=n!(n−k)!×k!
 
62  

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

 
63  
Cnобщ=С2n+C3n++CnnCnобщ=Сn2+Cn3+…+Cnn
 
64  
Cnобщ=nk=2n!(nk)!×k!Cnобщ=∑k=2nn!(n−k)!×k!
 
65  

Например, для 5 кнопок, помимо 5«официальных» обособленных нажатий по одной кнопке, общее количество комбинаций рассчитывается следующим образом. Количество комбинаций нажатий по две кнопки(1+21+31+41+52+3, ...) будет:

 
66  
C25=5!(52)!×2!=10C52=5!(5−2)!×2!=10
 
67  

Количество комбинаций нажатий 3 кнопок(1+2+31+2+41+2+52+3+4, ...):

 
68  
C35=5!(53)!×3!=10C53=5!(5−3)!×3!=10
 
69  

Количество комбинаций нажатий 4 кнопок(1+2+3+41+2+3+51+2+4+51+3+4+52+3+4+5):

 
70  
C45=5!(54)!×4!=5C54=5!(5−4)!×4!=5
 
71  

И количество одновременных нажатий 5 кнопок — 11. Итого:

 
72  
Cnобщ=10+10+5+1=26Cnобщ=10+10+5+1=26
 
73  

26 комбинаций! Помимо 5 стандартных. А для 6 кнопок возможных комбинаций уже 57(не считая 6 нажатий по одной кнопке)!!! Но все бы ничего если бы не...

 
74  

Назад к кнопкам

Вернемся к нашим 3 кнопкам и рассчитаем напряжение, которое будет получено, путем одновременного нажатия 1 и 3 кнопки(1 В и 4 В). Как известно, для двух параллельных сопротивлений, общее сопротивление находится по формуле:

 
75  
R=R1R2R1+R2R=R1R2R1+R2
 
76  
R=40КОм×2,5КОм40КОм+2,5КОм=2,35КОмR=40КОм×2,5КОм40КОм+2,5КОм=2,35КОм
 
77  
Uвых=Uвх×RстR+Rст=5В×10КОм2,35КОм+10КОм=4,05ВUвых=Uвх×RстR+Rст=5В×10КОм2,35КОм+10КОм=4,05В
 
78  

Обратите внимание, что полученное значение очень близко к номиналу третьей кнопки — при условии погрешности номиналов резисторов и АЦП данная комбинация, с большой долей вероятности может быть интерпретирована как нажатие 3 кнопки. В случае увеличения количества кнопок, очень трудно спрогнозировать, каким образом будут интерпретироваться различные комбинации нажатий кнопок — очень вероятны ситуации, когда комбинации нажатых кнопок будут давать результат, не имеющий к ним никакого отношения.

 
79  

В случае одновременного нажатия всех трех кнопок:

 
80  
R=1140КОм+110КОм+12,5КОм=1,9КОмR=1140КОм+110КОм+12,5КОм=1,9КОм
Rобщ=11R1+1R2++1RnRобщ=11R1+1R2+…+1Rn
81  
Uвых=Uвх×RстR+Rст=5В×10КОм1,9КОм+10КОм=4,2ВUвых=Uвх×RстR+Rст=5В×10КОм1,9КОм+10КОм=4,2В
 
82 На заметку:
Достоинства схемы:
  • простота подбора номиналов,
  • не важен порядок установки резисторов — каждый номинал включается в схему с соответствующей кнопкой,
  • возможно наращивание дополнительного функционала за счет интерпретаций комбинаций нажатий.

Недостатки:
  • подходит только для малого количества кнопок(до 5) — при включении в схему более 5 кнопок, поведение схемы при одновременном нажатии двух и более кнопок становится непредсказуемым.
 
83  

Хватит математики, в бой!

Подкрепим теорию примерами на практике, работать будем с 4 кнопками.

 
84 На заметку:
Для ускорения расчетов номиналов элементов схем данной статьи, автором написан Калькулятор.xlsx (24,9 KB), который можно совершенно бесплатно скачать и производить расчеты.
 
85  

Пример №1Резистивно-последовательная схема, номиналы резисторов будем подбирать под равные промежутки напряжения. Воспользовавшись калькулятором, получаем следующие номиналы резисторов — 3.334 КОм6.666 КОм20 КОм — как раз в наличии 3.3 КОм6.8 КОм и 20 КОм. Собираем схему:

Принципиальная схема
Принципиальная схема
86  
 
87  

Ниже представлен код, который выдает сообщение каждый раз, как состояние нажатия кнопок меняется. Скетч, с учетом программного устранения дребезга:

 
88 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
int pinIn = A0; int keyValue = 0; // Состояние покоя void setup() { pinMode(pinIn, INPUT); Serial.begin(9600); } void loop() { int newKeyValue = GetKeyValue(); // Получаем актуальное состояние кнопок с коррекцией дребезга if (keyValue != newKeyValue) { // Если новое значение не совпадает со старым - реагируем на него keyValue = newKeyValue; // Актуализируем переменную хранения состояния if (keyValue > 0) { // Если значение больше 0, значит кнопка нажата Serial.println("Key pressed: " + String(keyValue)); } else { // Если 0, то состояние покоя Serial.println("all keys are not pressed"); } } } int GetKeyValue() { // Функция устраняющая дребезг static int oldKeyValue; // Переменная для хранения предыдущего значения состояния кнопок static long lastChange; // Переменная для хранения времени последнего изменения состояния int actualKeyValue = analogRead(pinIn); // Получаем актуальное состояние if ((actualKeyValue != oldKeyValue) && (millis() - lastChange > 200)) { // Пришло новое значение, и с последнего изменения прошло достаточно времени oldKeyValue = actualKeyValue; // Запоминаем новое значение lastChange = millis(); // Обнуляем таймер } return oldKeyValue; // Отправляем старое, либо уже модифицированное новое значение }
 
89  

Но если этот код запустить в том виде, в котором он представлен то увидим, как погрешность преобразования АЦП вносит свои коррективы в его работу:

 
90  
 
91  

Для исправления будем использовать битовый сдвиг, описанный в 18 абзаце. Если обратить внимание на погрешность получаемых значений, с учетом не идеально подобранных номиналов резисторов и их погрешности, она не превышает ±16 единиц. А это составляет 16+16=32=2516+16=32=25. Значит и сдвигать можно только на 5 бит — результат будет стабильным. Но для упрощения интерпретации будем сдвигать на 8 бит, чтобы результатом получать номер нажатой кнопки 1...4. Откорректируем несколько строк:

 
92 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
int pinIn = A0; int keyValue = 0; // Состояние покоя void setup() { pinMode(pinIn, INPUT); Serial.begin(9600); } void loop() { int newKeyValue = GetKeyValue(); // Получаем актуальное состояние кнопок с коррекцией дребезга if (keyValue != newKeyValue) { // Если новое значение не совпадает со старым - реагируем на него keyValue = newKeyValue; // Актуализируем переменную хранения состояния if (keyValue > 0) { // Если значение больше 0, значит кнопка нажата Serial.println("Key pressed: " + String(keyValue)); } else { // Если 0, то состояние покоя Serial.println("all keys are not pressed"); } } } int GetKeyValue() { // Функция устраняющая дребезг static int oldKeyValue; // Переменная для хранения предыдущего значения состояния кнопок static long lastChange; // Переменная для хранения времени последнего изменения состояния int actualKeyValue = analogRead(pinIn); // Получаем актуальное состояние
//actualKeyValue = ((actualKeyValue - 16) >> 5) + 1; // Для 32 кнопок
//actualKeyValue = ((actualKeyValue - 32) >> 6) + 1; // Для 16 кнопок
//actualKeyValue = ((actualKeyValue - 64) >> 7) + 1; // Для 8 кнопок
actualKeyValue = ((actualKeyValue - 128) >> 8) + 1; // Для 4 кнопок
if ((actualKeyValue != oldKeyValue) && (millis() - lastChange > 200)) { // Пришло новое значение, и с последнего изменения прошло достаточно времени oldKeyValue = actualKeyValue; // Запоминаем новое значение lastChange = millis(); // Обнуляем таймер } return oldKeyValue; // Отправляем старое, либо уже модифицированное новое значение }
 
93  

Но поскольку речь все же идет о работе АЦП, а он может начать преобразование в момент дребезга и выдать некорректный результат...

 
94  
В примере из предыдущего скетча раскомментирована 30 строка — для 8 кнопок, а 31 закомментирована
В примере из предыдущего скетча раскомментирована 30 строка — для 8 кнопок, а 31 закомментирована
 
95  

...модифицируем код таким образом, чтобы нажатие генерировалось только после получения 10 подряд идущих одинаковых значений(модифицированная функция GetKeyValue()):

 
96 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int GetKeyValue() { // Функция устраняющая дребезг static int count; static int oldKeyValue; // Переменная для хранения предыдущего значения состояния кнопок static int innerKeyValue; int actualKeyValue = analogRead(pinIn); // Получаем актуальное состояние //actualKeyValue = ((actualKeyValue - 16) >> 5) + 1; // Для 32 кнопок //actualKeyValue = ((actualKeyValue - 32) >> 6) + 1; // Для 16 кнопок //actualKeyValue = ((actualKeyValue - 64) >> 7) + 1; // Для 8 кнопок actualKeyValue = ((actualKeyValue - 128) >> 8) + 1; // Для 4 кнопок if (innerKeyValue != actualKeyValue) { // Пришло значение отличное от предыдущего count = 0; // Все обнуляем и начинаем считать заново innerKeyValue = actualKeyValue; // Запоминаем новое значение } else { count += 1; // Увеличиваем счетчик } if ((count >= 10) && (actualKeyValue != oldKeyValue)) { // Счетчик преодолел барьер, можно иницировать смену состояний oldKeyValue = actualKeyValue; // Присваиваем новое значение } return oldKeyValue; }
 
97  

Пример №2Резистивно-последовательная схема, номиналы резисторов будут одинаковые — 3.3 КОм. Количество кнопок также 4. Схема остается прежней — меняются только номиналы резисторов.

 
98  
 
99  

Воспользовавшись калькулятором (24,9 KB), получаем следующие напряжения на выходе(с соответствующими значениями АЦП) — 5 В(1023), 3.76 В(769), 3.01 В(615) и 2.51 В(513). Для проверки на модифицированной схеме запускаем скетч:

 
100 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
int pinIn = A0; int keyValue = 0; // Состояние покоя void setup() { pinMode(pinIn, INPUT); Serial.begin(9600); } void loop() { int newKeyValue = GetKeyValue(); // Получаем актуальное состояние кнопок с коррекцией дребезга if (keyValue != newKeyValue) { // Если новое значение не совпадает со старым - реагируем на него keyValue = newKeyValue; // Актуализируем переменную хранения состояния if (keyValue > 0) { // Если значение больше 0, значит кнопка нажата Serial.println("Key pressed: " + String(keyValue)); } else if (keyValue < 0) { // Если -1 - неизвестное состояние, незапрограммированное нажатие Serial.println("unknown pressed"); } else { // Если 0, то состояние покоя Serial.println("all keys are not pressed"); } } } int GetKeyValue() { // Функция устраняющая дребезг static int count; static int oldKeyValue; // Переменная для хранения предыдущего значения состояния кнопок static int innerKeyValue; // Здесь уже не можем использовать значение АЦП, так как оно постоянно меняется в силу погрешности int actualKeyValue = GetButtonNumberByValue(analogRead(pinIn)); // Преобразовываем его в номер кнопки, тем самым убирая погрешность if (innerKeyValue != actualKeyValue) { // Пришло значение отличное от предыдущего count = 0; // Все обнуляем и начинаем считать заново innerKeyValue = actualKeyValue; // Запоминаем новое значение } else { count += 1; // Увеличиваем счетчик } if ((count >= 10) && (actualKeyValue != oldKeyValue)) { oldKeyValue = actualKeyValue; // Запоминаем новое значение } return oldKeyValue; } int GetButtonNumberByValue(int value) { // Новая функция по преобразованию кода нажатой кнопки в её номер int values[5] = {0, 513, 615, 769, 1023}; int error = 15; // Величина отклонения от значений - погрешность for (int i = 0; i <= 4; i++) { // Если значение в заданном диапазоне values[i]+/-error - считаем, что кнопка определена if (value <= values[i] + error && value >= values[i] - error) return i; } return -1; // Значение не принадлежит заданному диапазону }
 
101  

В коде написана дополнительная функция GetButtonNumberByValue(), проверяющая причастность полученного АЦП значения к диапазонам, закрепленным за каждой кнопкой, с учетом заданного значения ошибки error. Результат:

 
102  
 
103  

Пример №3Резистивно-параллельная схема, количество кнопок также 4. Для упрощения будем использовать номиналы резисторов из примера №1 — 3.3 КОм6.8 КОм10 КОм и 20 КОм. Стягивающий резистор также 10 КОм. Расчетные напряжения получаемые на выходе(с соответствующими значениями АЦП) будут следующими: 3.76 В(769), 2.98 В(609), 2.5 В(511) и 1.67 В(341).

 
104  

Как раз для примера, опять же при помощи калькулятора (24,9 KB), рассчитаем значение общего сопротивления и выходного напряжения, получаемое при одновременном нажатии 2, 3 и 4(6.8 КОм10 КОм и 20 КОм) кнопок. Получаем общее сопротивление 3.37 КОм, и выходное напряжение — 3.74 В(765), что как видно очень близко к значениям первой кнопки. Посмотрим что будет происходить в реальности.

Принципиальная схема
Принципиальная схема
105  

Тестовая схема выглядит следующим образом:

 
106  
 
107  

Скетч оставляем из примера №2, за тем лишь исключением, что модифицируем идентификационные значения. Для большей показательности не будем в коде отрабатывать сценарии одновременного нажатия большого количества кнопок(делается это очень просто, оставим для домашнего задания). Заменим 49 строку предыдущего скетча на:

 
108 Arduino (C++)
1
int values[5] = {0, 769, 609, 511, 341};// Массив значений АЦП
 
109  

Запустив код, можно убедиться в недостатках этой схемы — одновременное нажатие 3 и 4 кнопки интерпретируется как нажатие 2 кнопки, а одновременное нажатие кнопок 2, 3 и 4 — как нажатие 1 кнопки.+

 
Оцените эту запись блога:
Фазное регулирование нагрузки переменного тока с п...
Прошивка Arduino Pro Mini через Nano

Похожие статьи

 

Комментарии

Нет созданных комментариев. Будь первым кто оставит комментарий.
Уже зарегистрированны? Войти на сайт
Гость
25.11.2017
Если вы хотите зарегистрироваться, пожалуйста заполните формы имени и имя пользователя.
Яндекс.Метрика
Рейтинг@Mail.ru

Контакты

+7 (918) 456-43-40 whatsapp app

+7 (918) 6-333-715 whatsapp app

Адрес

г. Краснодар, переулок Плановый 25

© 2017 Спектр Бытовых Услуг (Муж на час)