Клавиатуры и кнопки на Arduino
Цифровая клавиатура слева. Аналоговая клавиатура справа.
Когда я приступил к сборке dropController и camController, подходящие навигационные клавиатуры найти не удавалось — доступные варианты оказывались либо слишком дорогими, либо не подходили по функциональности, и я решил сделать свои. Это были несложные клавиатуры со стандартной схемой подключения кнопок. Каждая кнопка была привязана к отдельному пину Arduino, то есть для 5 кнопок требовалось 5 пинов. Такой подход устраивал до тех пор, пока не возникла необходимость подключить дополнительные соленоидные клапаны — и тут выяснилось, что свободных пинов не осталось.
Я возобновил поиск готовых решений и обнаружил клавиатуру Keyes Keypad на Taobao. Они недорогие и компактнее тех, что я изготавливал самостоятельно. Вдобавок они задействуют лишь один пин — это аналоговые клавиатуры, работающие через единственный аналоговый вход Arduino.
Принципиальное различие между цифровой и аналоговой клавиатурой состоит в способе считывания нажатий и обработке полученного значения. У цифровой клавиатуры каждый пин/кнопку приходится опрашивать отдельно. К примеру: if left_key is pressed, if right_key is pressed. Аналоговая же клавиатура использует единственную переменную, значение которой меняется в зависимости от нажатой кнопки. К примеру: if keypress is left, if keypress is up. На мой взгляд, с таким подходом работать несколько проще, хотя писать код можно любым удобным способом.
Цифровая клавиатура
Схема подключения каждой отдельной кнопки достаточно проста. Присутствует провод GND с подтягивающим к земле резистором 10 кОм (pull-down), провод VCC/+5V и сигнальный провод, идущий к цифровому пину Arduino. Подтягивающий резистор обеспечивает состояние LOW, когда кнопка не нажата. При нажатии резистор 10 кОм шунтируется, и на выходе устанавливается состояние HIGH.
Обе приведённые схемы идентичны. Внутри кнопки левая и правая стороны электрически связаны между собой. Это хорошо видно по условному обозначению кнопок:
Сборка клавиатуры сводится к добавлению необходимого числа кнопок.
Ниже представлена используемая мной раскладка:
В сети имеется множество материалов по данной теме, а на официальном сайте Arduino есть хорошее введение: Arduino Button Tutorial
Для обнаружения нажатий клавиш я применяю библиотеку Button от Alexander Brevig. Она заметно упрощает код. В начале скетча задаётся, какие кнопки подключены к каким пинам и какой тип подтяжки используется — pull-down (к GND) или pull-up (к +5V). Я всегда применял pull-down.
Button ok_button = Button(2, PULLDOWN);
Button rt_button = Button(3, PULLDOWN);
Button dn_button = Button(4, PULLDOWN);
Button up_button = Button(5, PULLDOWN);
Button lf_button = Button(6, PULLDOWN);
Затем в основном теле скетча нажатия клавиш определяются одной строкой:
ok_button.uniquePress();
Функция возвращает TRUE или FALSE и может применяться различными способами. Например:
if ( ok_button.uniquePress() ) { Serial.Print( "OK button was pressed."); }
Библиотека позволяет фиксировать уникальные нажатия, текущее состояние кнопки (нажата или отпущена) и многое другое. Её можно найти на Arduino Playground: Button Library
Аналоговая клавиатура
Аналоговая клавиатура формирует различное напряжение (аналоговое значение) в зависимости от того, какая кнопка нажата. Это значение считывается единственным аналоговым пином Arduino. Теоретически такой подход медленнее использования цифровых пинов, однако на практике разницы я не ощутил. К тому же аналоговая процедура, которую я использую (позаимствованная из интернета), обеспечивает более чёткий отклик по сравнению с той, что я применял для цифровой клавиатуры. С цифровым вариантом у меня по-прежнему наблюдались проблемы с дребезгом и ложными срабатываниями (хотя особых усилий по их устранению я не предпринимал).
Аналоговые клавиатуры я приобретаю на Taobao в Китае, но они есть и на eBay. Собрать подобную клавиатуру своими руками несложно — кнопки располагаются по схеме делителя напряжения (а точнее, при пяти кнопках — по схеме резисторной лестницы).
Самодельная аналоговая навигационная клавиатура на одном пине
Вот простая клавиатура, собранная на макетной плате. Сигнальный провод от кнопок подключён к пину A0 Arduino.
Здесь использованы резисторы на 2,2 кОм, поскольку они были в наличии, но подойдут и другие номиналы — главное, чтобы они были достаточно большими. Резисторы на 1 кОм и 2 кОм тоже вполне годятся.
Скетч для тестирования клавиатуры
Следующий скетч считывает значение с аналогового пина и выводит его в Serial Monitor.
/*
Keypad Tester 01. Created by Aiten Bexultan
*/
// Глобальные переменные
const byte keypadPin = A0;
int val = 0;
void setup()
{
Serial.begin(9600);
while (!Serial) { ; }
Serial.println(F("Start\n"));
// Настраиваем аналоговый пин на вход и включаем внутренний подтягивающий резистор
pinMode(keypadPin, INPUT_PULLUP);
}
void loop()
{
val = getKeyValue();
Serial.println( val );
delay(100);
}
int getKeyValue()
{
int pinVal;
pinVal = analogRead(keypadPin);
return pinVal;
}
С резисторами на 2,2 кОм были получены следующие значения:
Ни одна кнопка не нажата = 1021–1023
Первая кнопка = 14 или 15
Вторая кнопка = 72 или 73
Третья = 124
Четвёртая = 170
Пятая = 212 или 213
Из-за разброса параметров компонентов ваши значения, вероятнее всего, будут несколько отличаться.
Активация подтягивающего резистора на аналоговом пине означает подачу на него напряжения +5V (фактически чуть меньше), поэтому без нажатия кнопок пин показывает значение около 1023.
Обратите внимание: нажатие первой кнопки напрямую замыкает A0 на GND. Подтягивающий резистор на пине предохраняет Arduino от короткого замыкания, а реальный ток при этом составляет лишь около 1 миллиампера.
Если перенести подключение GND на противоположный конец цепочки, значения кнопок поменяются местами: первый пин теперь будет давать 212, а пятый — 14.
В скетче можно напрямую использовать значение, возвращаемое analogRead(), однако более удобный подход — использовать метки направлений. Это позволяет писать код в стиле if (buttonPress==LEFT) {} или if (buttonPress==DOWN) {}.
/*
Keypad Tester 02. Created by Aiten Bexultan
*/
// Глобальные переменные
const byte NONE = 0;
const byte LEFT = 1;
const byte UP = 2;
const byte RIGHT = 3;
const byte DOWN = 4;
const byte SELECT = 5;
const byte keypadPin = A0;
byte key = 0;
void setup()
{
Serial.begin(9600);
while (!Serial) { ; }
Serial.println(F("Start\n"));
pinMode(keypadPin, INPUT_PULLUP); // настраиваем аналоговый пин на вход
}
void loop()
{
key = getKey();
if (key==LEFT) { Serial.println("LEFT"); }
if (key==RIGHT) { Serial.println("RIGHT"); }
if (key==UP) { Serial.println("UP"); }
if (key==DOWN) { Serial.println("DOWN"); }
if (key==SELECT) { Serial.println("SELECT"); }
if (key==NONE) { Serial.println("NO KEY PRESSED"); }
delay(100);
}
/*
Значения клавиш:
Left / Первая кнопка = 15
Up / Вторая кнопка = 72
Down / Третья = 124
Right / Четвёртая = 170
Select / Пятая = 212
Ни одна не нажата = 1021
Из-за допусков компонентов и даже температуры
нужно проверять диапазон значений.
Я обычно использую +/- 20. Это даёт:
Left - 0 до 35
Up - 52 до 92
Down - 104 до 144
Right - 150 до 190
Select - 192 до 232
*/
byte getKey()
{
int val = 0;
byte button = 0;
val = analogRead(keypadPin);
button = NONE; // используем NONE как значение по умолчанию
if ( val <= 35) { button = LEFT; } // влево
else if ( (val >= 52) && (val <=92) ) { button = UP; } // вверх
else if ( (val >= 104) && (val <= 144 ) ) { button = DOWN; } // вниз
else if ( (val >= 150) && (val <= 190) ) { button = RIGHT; } // вправо
else if ( (val >= 192) && ( val <=232) ) { button = SELECT; } // выбор
return button;
}
Запустите скетч и откройте Serial Monitor — вы увидите примерно следующее:
При нажатии каждой кнопки в Serial Monitor отображается её название. Если ни одна кнопка не нажата, выводится сообщение «NO KEY PRESSED». Если результат отличается от ожидаемого, попробуйте скорректировать диапазоны в функции getKey().