01 |
Речь в статье пойдет о двух разных схемах подключения большого количества кнопок к одному аналоговому входу Arduino. Принцип работы схем основан на чтении и интерпретации аналого-цифровым преобразователем микроконтроллера индивидуального напряжения, формируемого разными комбинациями отдельных участков схемы. |
|
03 |
Обе схемы, вне зависимости от количества кнопок (в разумных пределах), в каждый активный (нажатие кнопок/кнопки) момент времени представляют из себя классический резистивный делитель напряжения. Но принципиальные отличия и особенности работы каждой из них имеют различные алгоритмы расчета элементов и конечное поведение. Все примеры в конце, после теории. |
|
04 |
Схема №1 — резистивно-последовательная Принципиально схема может исполняться в 2 вариантах и выглядит следующим образом: |
По данной схеме реализована обратная связь о состоянии системы в проекте Codius.AutoFeeder 1.0.
Видео о проекте: |
05 |
Несмотря на кажущуюся схожесть схемы работают немного по разному
|
|
06 |
Различия схем заключаются в том, что при нажатии кнопки S4 на левой схеме, аналоговый вход считает максимальное напряжение — 5 В, а кнопка S1 даст минимальное напряжение, сниженное всеми резисторами в цепи. В правой схеме, наоборот, максимальное напряжение даст кнопка S1. |
|
07 |
Приведенные схемы — пример последовательного соединения резисторов. Как известно, для последовательного соединения резисторов: |
|
09 |
$$R_{общ}=R_1+R_2+...+R_n$$
|
|
10 |
При нажатии любой кнопки в схеме мы получаем классический резистивный делитель напряжения: |
|
12 |
В котором значения напряжений и сопротивлений находятся в зависимости, выражающейся следующей формулой: |
|
13 |
$$U_{вых}={U_{вх} \times {R_2 \over {R_1+R_2}} }$$
|
|
14 |
Из формулы видно, что зависимость выходного напряжения $U_{вых}$ от сопротивлений $R_1$ и $R_2$ нелинейная, а экпоненциальная. График это подтверждает. Единственный нюанс заключается в том, что на оси абсцисс номиналы резистора R1, представлены в номиналах стягивающего резистора R2 $x=R_2$: |
|
15 |
Например, если $R_2=10 КОм$, то при $R_1=3 \times R_2=30 КОм, U_{вых}=1,25 В$
|
|
16 |
Исходя из графика можно наглядно представить каким образом рассчитать номиналы резисторов в зависимости от количества кнопок. И здесь, как обычно, возможны 2 варианта — либо подбирать номиналы так, чтобы разбить интервал на равные промежутки напряжений, либо использовать в схеме резисторы только одного номинала: |
|
17 |
Пример использования 4 резисторов для 5 кнопок.
Слева резисторы подобраны под равномерную дискретность напряжений, справа — резисторы одного номинала. |
|
18 |
В первом случае (левый график), номиналы резисторов подобраны таким образом, чтобы обеспечить равные интервалы при определении значений напряжения АЦП. Такой подход очень удобен для количества кнопок $2^n$, (при $n \geqslant 2$), т. е. 4, 8, 16,… Удобство заключается в том, что результат работы АЦП при помощи битового сдвига можно округлить, при этом максимально нивелировать погрешности АЦП и номиналов резисторов. Пример: |
|
19 |
Предположим 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() {
} Но если пороговое значение 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 |
Но использовать этот вариант можно и с количеством кнопок не кратным $2^n$. В этом случае погрешность АЦП устраняется программно другими методами — внедрением значения ошибки. |
|
21 |
Недостаток такого способа заключается в ограничении количества кнопок – при увеличении их числа, номиналы резисторов кнопок, отвечающих за низкие напряжения (менее 1 В), растут в геометрической прогрессии. |
|
22 |
Формула для расчета номиналов резисторов для установки равных промежутков напряжений для количества кнопок $k$, и привязанных к номиналу стягивающего регистра $R_{cт}$ (если принять $R_{cт}$ за единицу, то полученные коэффициенты можно использовать при расчете сопротивлений при другом заданном стягивающем сопротивлении): |
|
23 |
$u={U_{вх} \over k}$ — шаг дискретности выходного напряжения |
|
24 |
$$R_{n}={{U_{вх} \times R_{ст}} \over {u \times (k-n)}}- R_{ст} - \sum^{n-1}_{i=1} {R_i}, \text { при } n \in \mathbb {Z} \mid 1\leqslant n \leqslant k-1$$
|
|
25 |
Поскольку первая кнопка замыкается без резистора, то резистор нулевого сопротивления опускаем, отсюда $n \leqslant k-1$ — эта кнопка даст результат равный $U_{вх}$ — $5В$. Рассмотрим расчет номиналов на примере приведенных 5 кнопок и 4 резисторов, стягивающий резистор $R_{ст}=10КОм$ (полученный результат будет в КОм): |
|
26 |
$$u={U_{вх} \over k}={5В \over 5}=1В $$
|
|
27 |
$$R_1={{5В \times 10КОм} \over {1В \times (5-1)}}- 10КОм - 0КОм=2,5КОм$$
|
|
28 |
$$R_2={{5В \times 10КОм} \over {1В \times (5-2)}}- 10КОм - (2,5КОм)=4,167КОм$$
|
|
29 |
$$R_3={{5В \times 10КОм} \over {1В \times (5-3)}}- 10КОм - (2,5КОм+4,167КОм)=8,333КОм$$
|
|
30 |
$$R_4={{5В \times 10КОм} \over {1В \times (5-4)}}- 10КОм - (2,5КОм+4,167КОм+8,333КОм)=25КОм$$
|
|
31 |
Результат: нам понадобятся резисторы следующих номиналов — 2.5 КОм, 4.167 КОм, 8.333 КОм, 25 КОм. |
|
32 |
Расположение резисторов с указанными номиналами и получаемые напряжения на каждой кнопке, в зависимости от выбранной схемы
|
|
33 | На заметку: |
Данный способ, автор считает наиболее предпочтительным. При чем, имеет смысл рассчитывать схему с количеством кнопок равным $2^n$, а использовать столько, сколько нужно. Например, если нужно 3 кнопки, то рассчитывать схему на 4 кнопки, а если нужно 5 кнопок, то схему сформировать исходя из 8 кнопок.
Этот способ также на программном уровне упрощает трактовку результатов, исключая дополнительные затраты ресурсов на проверку принадлежности к диапазонам. |
|
34 |
Для второго случая, все намного проще – установлены резисторы одного номинала. При чем лучше, если этот номинал будет меньше номинала стягивающего резистора $x$. В этом случае первые кнопки попадут в диапазон, где различия между напряжениями максимальны: |
|
35 |
На рисунке 10 резисторов, номиналом 0.1$x$ — например, при номинале стягивающего резистора 10 КОм, номинал резисторов кнопок — 1 КОм
|
|
36 |
Диапазон напряжений находится по формуле, при номинале стягивающего резистора $R_{ст}$ и номинале прочих резисторов $R$: |
|
37 |
$$U_{вых_{n}}=U_{вх} \times {{R_{ст}} \over {R \times n + {R_{ст}}}}$$
|
|
38 |
Но как видно из графика дискретность напряжений неминуемо снижается. В данном случаем можно пойти на хитрость – там, где разница между напряжениями становится критически минимальной, начать устанавливать резисторы большего номинала, увеличив дискретность: |
|
40 |
Основной отличительной особенностью резистивно-последовательной схемы является игнорирование нажатия нескольких кнопок — результат всегда будет указывать на нажатие единственной кнопки с меньшим по отношению к другим нажатым кнопкам суммарным сопротивлением: |
|
41 |
При одновременном нажатии 2 и 3 кнопки, ток потечет по контуру через 3 кнопку
|
|
42 | На заметку: |
Достоинства схемы:
Недостатки:
|
|
43 |
|
|
44 |
Схема №2 — резистивно-параллельная Принципиально схема может исполняться в 2 вариантах и выглядит следующим образом: |
|
45 |
Обе схемы абсолютно идентичны
|
|
46 |
Особенность данной схемы заключается в том, что резистор каждой кнопки задает необходимое напряжение на выходе — его номинал рассчитывается также как и в первой схеме. Схема исключает использование резисторов одного номинала. При нажатии двух или нескольких кнопок, генерируется индивидуальное напряжение, которое определяется исходя из параллельного подключения сопротивлений: |
|
48 |
$${\frac {1}{R_{общ}}}={\frac {1}{R_{1}}}+{\frac {1}{R_{2}}}+\ldots+{\frac {1}{R_{n}}}$$
|
|
49 |
И как следствие: |
|
50 |
$${R_{общ}}={\frac {1}{\frac {1}{{R_{1}}}+{\frac {1}{R_{2}}}+\ldots+{\frac {1}{R_{n}}}}}$$
|
|
51 |
Эта особенность (распознавание одновременно нажатых кнопок) является преимуществом при малом их количестве, и в то же время делает поведение схемы сложно предсказуемой при большом количестве кнопок. К этой ситуации мы вернемся чуть позже. |
|
52 | На заметку: |
Важно иметь ввиду, что при подключении одной кнопки без сопротивления, для генерации 5 В (это не противоречит общей концепции) и, при её нажатии, вся схема не будет реагировать на нажатия других кнопок. Эта ситуация является исключением в общем правиле однозначной идентификации каждой комбинации из нажатых кнопок.
|
|
53 |
Расчет номиналов сопротивлений схож с расчетом из резистивно-последовательной схемы, за тем лишь исключением, что каждое последующее сопротивление не нужно корректировать на полученные ранее: |
|
54 |
$$R_{n}={{U_{вх} \times R_{ст}} \over {U_{вых_{n}}}}- R_{ст}$$
|
|
55 |
Например, у нас есть схема из 3 кнопок, которые должны генерировать соответственно 1 В, 2.5 В и 4 В. Номинал стягивающего резистора 10 КОм, входное напряжение 5 В. Расчет номиналов резисторов будет следующим: |
|
56 |
$$R_1={{5В \times 10КОм} \over {1В}}- 10КОм=40КОм$$
|
|
57 |
$$R_2={{5В \times 10КОм} \over {2,5В}}- 10КОм=10КОм$$
|
|
58 |
$$R_3={{5В \times 10КОм} \over {4В}}- 10КОм=2,5КОм$$
|
|
59 |
Но самое интересное, в этой схеме всплывает тогда, когда одновременно нажимается несколько кнопок — а с тремя кнопками этих комбинаций 4: 1+2, 1+3, 2+3, 1+2+3. |
|
60 |
Немного математики... Количество комбинаций нажатий кнопок определяется по формуле числа сочетаний из общего числа $n$-объектов по $k$-штук в комбинации: |
|
61 |
$$C_n^k=\frac{n!}{(n-k)! \times k!}$$
|
|
62 |
Но поскольку у нас исключены нажатия по одной кнопке (поведение по умолчанию), и возможны нажатия не только 2, но и всех кнопок сразу, то общая формула для нахождения числа сочетаний будет находиться путем сложения числа сочетаний 2 кнопок, числа сочетаний 3 кнопок и т. д. и будет иметь вид: |
|
63 |
$$C_{n_{общ}}=С_n^2+C_n^3+\ldots+C_n^n$$
|
|
64 |
$$C_{n_{общ}}=\sum^{n}_{k=2} \frac {n!}{(n-k)! \times k!}$$
|
|
65 |
Например, для 5 кнопок, помимо 5 «официальных» обособленных нажатий по одной кнопке, общее количество комбинаций рассчитывается следующим образом. Количество комбинаций нажатий по две кнопки (1+2, 1+3, 1+4, 1+5, 2+3, ...) будет: |
|
66 |
$$C_5^2=\frac{5!}{(5-2)! \times 2!}=10$$
|
|
67 |
Количество комбинаций нажатий 3 кнопок (1+2+3, 1+2+4, 1+2+5, 2+3+4, ...): |
|
68 |
$$C_5^3=\frac{5!}{(5-3)! \times 3!}=10$$
|
|
69 |
Количество комбинаций нажатий 4 кнопок (1+2+3+4, 1+2+3+5, 1+2+4+5, 1+3+4+5, 2+3+4+5): |
|
70 |
$$C_5^4=\frac{5!}{(5-4)! \times 4!}=5$$
|
|
71 |
И количество одновременных нажатий 5 кнопок — $1$. Итого: |
|
72 |
$$C_{n_{общ}}=10+10+5+1=26$$
|
|
73 |
26 комбинаций! Помимо 5 стандартных. А для 6 кнопок возможных комбинаций уже 57 (не считая 6 нажатий по одной кнопке)!!! Но все бы ничего если бы не... |
|
74 |
Назад к кнопкам Вернемся к нашим 3 кнопкам и рассчитаем напряжение, которое будет получено, путем одновременного нажатия 1 и 3 кнопки (1 В и 4 В). Как известно, для двух параллельных сопротивлений, общее сопротивление находится по формуле: |
|
75 |
$$R={\frac {R_{1}R_{2}}{R_{1}+R_{2}}}$$
|
|
76 |
$$R={\frac {40КОм \times2{,}5КОм}{40КОм+2{,}5КОм}}=2{,}35КОм$$
|
|
77 |
$$U_{вых}={U_{вх} \times {R_{ст} \over {R+R_{ст}}} }=5В \times {10КОм \over {2{,}35КОм+10КОм}}=4{,}05В $$
|
|
78 |
Обратите внимание, что полученное значение очень близко к номиналу третьей кнопки — при условии погрешности номиналов резисторов и АЦП данная комбинация, с большой долей вероятности может быть интерпретирована как нажатие 3 кнопки. В случае увеличения количества кнопок, очень трудно спрогнозировать, каким образом будут интерпретироваться различные комбинации нажатий кнопок — очень вероятны ситуации, когда комбинации нажатых кнопок будут давать результат, не имеющий к ним никакого отношения. |
|
79 |
В случае одновременного нажатия всех трех кнопок: |
|
80 |
$$R={\frac {1} {\frac {1}{40КОм} +{\frac {1}{10КОм}}+{\frac {1}{2,5КОм}} }}=1,9КОм$$
|
${R_{общ}}={\frac {1}{\frac {1}{{R_{1}}}+{\frac {1}{R_{2}}}+\ldots+{\frac {1}{R_{n}}}}}$
|
81 |
$$U_{вых}={U_{вх} \times {R_{ст} \over {R+R_{ст}}} }=5В \times {10КОм \over {1{,}9КОм+10КОм}}=4{,}2В $$
|
|
82 | На заметку: |
Достоинства схемы:
Недостатки:
|
|
83 |
Хватит математики, в бой! Подкрепим теорию примерами на практике, работать будем с 4 кнопками. |
|
84 | На заметку: |
Для ускорения расчетов номиналов элементов схем данной статьи, автором написан Калькулятор.xlsx (24,9 KB), который можно совершенно бесплатно скачать и производить расчеты.
|
|
85 |
Пример №1. Резистивно-последовательная схема, номиналы резисторов будем подбирать под равные промежутки напряжения. Воспользовавшись калькулятором, получаем следующие номиналы резисторов — 3.334 КОм, 6.666 КОм, 20 КОм — как раз в наличии 3.3 КОм, 6.8 КОм и 20 КОм. Собираем схему: |
Принципиальная схема |
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 |
Но если этот код запустить в том виде, в котором он представлен то увидим, как погрешность преобразования АЦП вносит свои коррективы в его работу: |
|
91 |
Для исправления будем использовать битовый сдвиг, описанный в 18 абзаце. Если обратить внимание на погрешность получаемых значений, с учетом не идеально подобранных номиналов резисторов и их погрешности, она не превышает ±16 единиц. А это составляет $16+16=32=2^5$. Значит и сдвигать можно только на 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 закомментирована
|
|
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. Схема остается прежней — меняются только номиналы резисторов. |
|
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. Результат: |
|
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 |
Тестовая схема выглядит следующим образом: |
|
107 |
Скетч оставляем из примера №2, за тем лишь исключением, что модифицируем идентификационные значения. Для большей показательности не будем в коде отрабатывать сценарии одновременного нажатия большого количества кнопок (делается это очень просто, оставим для домашнего задания). Заменим 49 строку предыдущего скетча на: |
|
108 | Arduino (C++) |
1 int values[5] = {0, 769, 609, 511, 341};// Массив значений АЦП |
|
109 |
Запустив код, можно убедиться в недостатках этой схемы — одновременное нажатие 3 и 4 кнопки интерпретируется как нажатие 2 кнопки, а одновременное нажатие кнопок 2, 3 и 4 — как нажатие 1 кнопки. |
|
110 |
Демонстрационное видео: |
|
111 |
Подписывайтесь на канал
, чтобы быть в курсе обновлений! |
|
112 | На заметку: |
Исходники для скачивания:
Все одним файлом — Materials.rar (365 KB) |
|