Если вы таким образом подключите к входу микроконтроллера кнопку или контакт реле, то здесь вас могут поджидать неожиданные эффекты в виде сбоев работы устройства и разных глюков. А вызваны эти эффекты будут таким явлением, как дребезг контактов.
Дребезг контактов возникает во время замыкания или размыкания контактов. Посмотрите на рисунок:
Изначально контакт разомкнут.
Когда мы начинаем замыкать контакт (нажимаем на кнопку), то замыкание происходит не сразу.
Это нам кажется, что мы нажали на кнопку мгновенно. Однако на самом деле, если растянуть время достаточно сильно, по получится, что мы нажимаем кнопку постепенно. На механическом контакте надо обеспечить достаточное усилие, чтобы он окончательно замкнулся, а контакты, как правило, пружинят, и поэтому какое-то время контакт находится в переходном процессе. То есть быстро-быстро замыкается-размыкается.
Если мы включаем этой кнопкой лампочку, то мы не заметим этот переходный процесс. Нам будет казаться, что лампочка сразу включилась после нажатия кнопки.
Однако быстродействие микроконтроллера таково, что он заметит все (или почти все) замыкания-размыкания переходного процесса. Это будет означать, что программа микроконтроллера столько раз отреагирует на сигнал от кнопки, сколько раз будет изменяться сигнал во время переходного процесса.
А мы то ожидаем, что одно нажатие кнопки - это одно переключение входа микроконтроллера. Но на самом деле это не так. Потому что дребезг контактов вносит свою лепту в усложнение жизни инженеров.
Представьте, что наши кнопки - это клавиатура телефона. Мы нажимаем цифру 8, подразумевая, что эта цифра будет набрана телефоном один раз. Но телефон вместо этого набирает 5 или 10 восьмёрок, потому что разработчики телефона не удосужились предусмотреть защиту от дребезга контактов. Станете вы пользоваться таким телефоном?
Ну и напоследок надо сказать, что время дребезга контактов зависит от качества контактов, и обычно составляет от 10 до 100 мс.
Есть два способа борьбы с дребезгом контактов:
Аппаратный
Программный
Далее рассмотрим в общих чертах оба способа.
Аппаратное подавление дребезга - это схемные решения, которые позволяют устранить этот неприятный эффект. Чаще всего это простая RC-цепь, или вообще только один конденсатор.
Принцип работы такой схемы простой: конденсатору требуется какое-то время для зарядки (или разрядки). А пока он полностью не зарядится, на вход микроконтроллера не поступит нужный сигнал. Этого времени хватает на то, чтобы переходный процесс успел завершиться. Таким образом и выполняется подавление дребезга.
Простая схема устранения дребезга контактов приведена на рисунке:
Номиналы элементов приблизительные. По идее надо их рассчитывать для каждого отдельного случая. Но в большинстве случаев они вполне подойдут.
Есть и более сложные схемы подавления дребезга контактов, которые не требуют расчёта, потому что выполняются на цифровых элементах. Например, схема на RS-триггере. Но в устройствах на микроконтроллерах использовать подобные ухищрения нет смысла.
Если уж мы используем микроконтроллер, то в подавляющем большинстве случаев нет смысла усложнять схему устройства и встраивать в неё элементы устранения дребезга. Потому что проще и дешевле организовать программное подавление дребезга.
Использовать аппаратное подавление дребезга в устройствах на микроконтроллерах имеет смысл только в очень редких случаях. Например, если микроконтроллер маломощный и даже малейшее расходование его ресурсов не на основную задачу нежелательно.
Самое простое и самое распространённое программное решение для борьбы с дребезгом - это временная задержка. Алгоритм простой:
При изменении уровня сигнала на входе на противоположный включаем таймер (например, на 100 миллисекунд).
После истечения задержки проверяем сигнал. Если он остался изменённым, то считаем, что кнопка нажата (или отпущена - в зависимости от того, какое изменение сигнала обнаружено). Если же он вернулся в исходное состояние, то считаем это помехой и не реагируем на сигнал.
Бывают особые случаи, когда быстрое переключение контактов - это обычное состояние системы. Ну например, если есть какой-то датчик, который по логике работы не может принимать фиксированное значение на длительное время. То есть идёт как бы непрерывный дребезг контактов. И в этом бесконечном потоке нам надо как-то определить, какой же всё-таки сигнал на входе.
В этом случае можно применить следующий алгоритм:
Посчитать количество и/или продолжительность замкнутого и разомкнутого состояния контакта в единицу времени (например, в секунду).
По наибольшему количеству (или времени) определить конечное состояние сигнала.
Например, если за секунду на входе у нас 50 раз была логическая 1, и 20 раз - логический 0, то можно считать, что на входе единица. Разумеется, здесь нужен индивидуальный подход в зависимости от задачи.
/* Программное подавление дребезга
*
* При каждом переходе от LOW к HIGH или от HIGH к LOW
* выполняется подавление дребезга во входном сигнале
* путем выборки по нескольким считываниям в течение
* нескольких миллисекунд. Входной сигнал не считается
* устоявшимся на уровне LOW или HIGH до тех пор, пока
* не будет считан по крайней мере в течение
* "debounce_count" (10) миллисекунд в новом состоянии.
*
* Примечания:
* Отрегулируйте значение debounce_count, чтобы
* отобразить время, в течение которого входной
* сигнал может дребезжать, прежде чем перейдет
* в устойчивое состояние.
*
*/
int inPin = 7; // номер входного вывода
int outPin = 13; // номер выходного вывода
int counter = 0; // сколько раз мы должны получить новое значение
int reading; // текущее значение, прочитанное с входного вывода
int current_state = LOW; // входное значение, полученное после подавления дребезга
// следующая переменная long, потому что время, измеренное в миллисекундах,
// быстро станет числом, большим, чем может храниться в int.
long time = 0; // время последней выборки входного вывода
int debounce_count = 10; // количество миллисекунд/выборок для решения, что сигнал на входе принял устойчивое состояние
void setup()
{
pinMode(inPin, INPUT);
pinMode(outPin, OUTPUT);
digitalWrite(outPin, current_state); // установить выход светодиода в начальное состояние
}
void loop()
{
// Если мы перешли к следующей миллисекунде
if(millis() != time)
{
reading = digitalRead(inPin);
if(reading == current_state && counter > 0)
{
counter--;
}
if(reading != current_state)
{
counter++;
}
// Если вход показывает одно значение достаточно долго, давайте переключим его
if(counter >= debounce_count)
{
counter = 0;
current_state = reading;
digitalWrite(outPin, current_state);
}
time = millis();
}
}