Распространенной задачей является подключение кнопок к микроконтроллеру. Несмотря на кажущуюся простоту, эта задача имеет некоторые, возможно неочевидные особенности.
Будем рассматривать наиболее простой и распространенный вариант кнопки, зачастую называемый тактовой кнопкой, имеющую два нормально разомкнутых контакта, замыкаемых при нажатии на кнопку.
Подключение кнопки к микроконтроллеру
Если мы подключим один из контактов, например, к общему проводу («земле»), а второй к выбранному выводу микроконтроллера, переключенного в режим входа, то выяснится, что такой метод не работает. При нажатии кнопки вывод микроконтроллера соединяется с землей, и программа будет считывать (с помощью функции digitalRead) логический 0 с этого вывода, но при отпущенной кнопке вывод микроконтроллера не будет соединен ни с какой линией, что часто называют «висит в воздухе». В таком режиме программа будет считать с вывода и 0 и 1 совершенно случайным образом.
Правильное подключение предполагает, что в разомкнутом состоянии вывод микроконтроллера должен быть соединен через резистор, например с шиной питания, а в замкнутом - с землей, либо наоборот. Сопротивление резистора не должно быть слишком маленьким, чтобы ток, текущий через него при замкнутых контактах кнопки не был слишком большим. Обычно используют значения порядка 10-100 кОм. Оба варианта подключения можно изобразить следующим образом:

Первый вариант предпочтительнее, поскольку подтягивающие к +5В резисторы уже есть внутри микроконтроллера – их нужно только программно включить. Кнопка будет либо соединять вывод микроконтроллера с землей, либо разъединять, и тогда он "притянется" резистором к +5В.
После того, как вывод микроконтроллера установлен в режим входа, чтобы включить на нем подтягивающий резистор нужно "записать" в него 1.
Пример программы, зажигающей светодиод на 13 выводе при нажатии кнопки на 2 выводе будет выглядеть примерно так:
pinMode(13, OUTPUT); //13й вывод - выход
pinMode(2, INPUT); //2й – вход. Здесь кнопка, замыкающая на землю
digitalWrite(2, HIGH); //включаем подтягивающий резистор
}
void loop() {
digitalWrite(13, !digitalRead(2));
}
Обращаем внимание на то, что значение, прочитанное с 2 вывода, инвертируется с помощью оператора «!», поскольку при нажатии на кнопку будет считываться «0», а при ее отпускании «1».
Дребезг контактов
На практике зачастую приходится бороться с таким явлением, как дребезг контактов, которое заключается в том, что при соприкосновении или расхождении контактов в механических переключающих устройствах, таких, как реле или кнопка, происходит многократное замыкание и размыкание. Схематично это может быть представлено следующим образом:

Чтобы микроконтроллер не обработал такую пачку переключений как множественное нажатие и отпускание кнопки нужно либо применить специальную схему, либо побороть дребезг программно. Идея программной борьбы проста – после того, как произошло переключение (от момента нажатия или отпускания кнопки), в течение некоторого защитного интервала времени игнорировать любые другие переключения.
Существует специальная библиотека Bounce, упрощающая борьбу с дребезгом контактов, и имеющая дополнительные возможности:
http://www.arduino.cc/playground/Code/Bounce
http://www.arduino.cc/playground/uploads/Code/Bounce.zip
Как и в большинстве случаев, установка библиотеки сводится к распаковке архива в подпапку \hardware\libraries\ папки с ПО Arduino.
Полная документация на библиотеку может быть найдена на сайте разработчика, а здесь рассмотрим только наиболее важное.
Bounce – конструктор объекта
Вызов:
Bounce имя_объекта = Bounce(вывод, интервал);
Создает экземпляр класса Bounce, принимает номер вывода, с которого будет считываться сигнал, и длительность защитного интервала в миллисекундах. После создания объекта можно вызывать его методы.
Метод Bounce::update
Вызов:
имя_объекта.update()
Возвращает значение типа int – истину, если состояние вывода изменилось, ложь, если нет.
Метод Bounce::read
Вызов:
имя_объекта.read()
Возвращает значение типа int – состояние вывода.
Метод Bounce::rebounce
Вызов:
имя_объекта.rebounce(время_повтора)
Повторяет последнее произошедшее событие через заданное время в миллисекундах.
Рассмотрим исходный код программы, посылающей в последовательный порт сообщение «pressed» при нажатии кнопки, сообщение «released» при ее отпускании, и повторяющей сообщение «pressed» каждые пол секунды при удержании кнопки.
Bounce bouncer = Bounce(2, 40); //создаем экземпляр класса Bounce для 2 вывода
void setup()
{
pinMode(2, INPUT); //переключаем 2 вывод в режим входа
digitalWrite(2, 1); //включаем на нем подтягивающий резистор
Serial.begin(9600); //установка порта на скорость 9600 бит/сек
}
void loop()
{
if (bouncer.update()) { //если произошло событие
if (bouncer.read()==0) { //если кнопка нажата
Serial.println("pressed"); //вывод сообщения о нажатии
bouncer.rebounce(500); //повторить событие через 500мс
} else {
Serial.println("released"); //вывод сообщения об отпускании
}
}
}