И снова здравствуйте! В своем прошлом уроке я показал какими способами можно подключить кнопку к микроконтроллеру и программно обработать ее нажатие. А в этой части я расскажу о неприятном явлении – дребезге контактов и как с ним бороться программным способом.
Дребезг контактов возникает в электромеханических коммутационных устройствах и аппаратах (кнопках, реле, герконах, переключателях, контакторах, магнитных пускателях и др.) при замыкании или размыкании контактов. В момент замыкания происходят многократные неконтролируемые замыкания и размыкания контактов за счет упругости материалов и деталей контактной системы — некоторое время контакты «подпрыгивают» при соударениях, говоря проще, при нажатии и отпускании кнопки она переходит в нужное состояние не сразу. Какое-то время контакты кнопки «дребезжат» между собой, что будет воспринято микроконтроллером как многократные импульсы. Количество этих импульсов может превышать тысячи. Наглядно дребезг можно увидеть на осциллограмме, на которой показан момент отпускания кнопки: как это выглядит на осциллограмме:
Бороться с дребезгом контактов можно двумя способами: аппаратным и программным. В первом случае кнопку подключают, например, через RS триггеры:
Но это тема отдельной статьи и рассматриваться в этом курсе не будет. В интернете можно найти огромное количество информации по этой теме.
Во втором случае применяют специальные алгоритмы, помогающие исключить случайные нажатия кнопки.
При программном формировании очищенного от дребезга контакта сигнала наибольшее распространение получили три из них:
- Путём установки временной задержки — программа, обнаружив замкнутое состояния контакта, игнорирует его состояние на время, заведомо большее длительности дребезга.
- Методом подсчёта числа совпадающих значений сигнала замкнутости — программа многократно считывает состояние контакта, и, если последовало определённое количество подтверждений замкнутости, (определяется экспериментально и выбирается в пределах от 10 до 100) контакт считается устойчиво замкнутым.
- Методом подсчета времени устойчивого состояния - программа в течении заданного времени многократно считывает состояние контакта. Если в течении заданного времени не обнаружено ни одного изменения состояния на противоположное, то контакт считается устойчиво замкнутым. В противном случае, если было обнаружено изменение состояния в течении заданного времени, то подсчет времени прерывается (или продолжается, но с установкой флага или подсчетом количества изменений состояния для оценки физического состояния механических контактов) и контакт считается разомкнутым или с неустойчивым состоянием (если такая информация используется в программе).
Самый простой способ борьбы с дребезгом контактов программным способом — это использование задержек. Сегодня мы его и рассмотрим.
Чтобы избавиться от их паразитного влияния нужно обнаружить нажатие кнопки, приостановить выполнение программы и реализовать некоторую задержку. Время задержки необходимо выбрать таким образом, чтобы оно превышало дребезг контактов. Такую же процедуру задержки нужно реализовать и после обнаружения отпускания кнопки.
Чтобы это как-то отследить и определить, что это было именно нажатие, а не дребезг, необходимо отслеживать нажатие некоторое время, ну или некоторое количество тактов или циклов. Для этого в начале функции main() до бесконечного необходимо добавить еще одну переменную. Назову я её butcount, так как имя переменной должно как-то само за себя говорить и тем самым достигается ещё большая читабельность кода.
unsigned char butcount=0;
Чтобы воспользоваться данной переменной, нужно применить ещё одно условие. И у нас будет условие в условии. Это всё допустимо и очень широко используется. И в зависимости от этого условия данная переменная будет наращиваться (инкрементировать). Условием будет достижение данной переменной определённой величины. То есть, чтобы значение переменной не достигало 5. Это число можно подобрать экспериментальным путем.
if ((PINB&(1 << PORTB0)) == 0)
{
if(butcount < 5)
{
butcount++;
}
А когда значение данной переменной достигнет значения 5, то попадём мы в тело оператора else, который необходимо добавить и в его теле написать следующий код:
else
{
PORTD |= (1<<PORTD0); //Если кнопка нажата - зажигаем светодиод D1
}
}
То есть мы как раз после достижения пятёрки и будет обрабатываться нажатие кнопки и включать светодиод.
По идее, здесь необходимо обнулить нашу переменную, но мы это можно делать также постепенно, используя тело оператора else, только другого — того, который у нас был и тело которого выполняется при низком уровне на ножке, к которой подключена кнопка. Вот таким будет его тело:
{
if(butcount > 0)
{
butcount--;
}
else
{
PORTD &= ~(1<<PORTD0); //Если кнопка отжата - гасим светодиод D1
}
}
Данный код чем-то похож на предыдущий, только здесь идёт, наоборот декрементирование переменной, и как только её значение опять достигнет нуля, то мы и попадём в обработку отжатия кнопки, тем самым полностью избавимся от дребезга контактов. И чем старее и некачественнее будет наша тактовая кнопка, тем большее значение переменной в условии необходимо применять.
Весь код программы, а я его полностью взял с предыдущего урока, будет таким:
#define F_CPU 8000000UL //Рабочая частота МК (8МГц)
#include <avr/io.h>
#include <util/delay.h> //для _delay_ms()
int main(void)
{
DDRD = 0xff; //Переключаем порт D на выход
DDRB = 0x00; //Переключаем порт B на вход
PORTD = 0x00; //устанавливаем все выходы порта в логический 0
PORTB |= (1 << PORTB1); //Подключаем встроенный подтягивающий резистор
unsigned char butcount=0;
while (1)
{
//Проверям состояние кнопки 1
if ((PINB&(1 << PORTB0)) == 0)
{
if(butcount < 5)
{
butcount++;
}
else
{
PORTD |= (1<<PORTD0); //Если кнопка нажата - зажигаем светодиод D1
}
}
else
{
if(butcount > 0)
{
butcount--;
}
else
{
PORTD &= ~(1<<PORTD0); //Если кнопка отжата - гасим светодиод D1
}
}
//Проверям состояние кнопки 2
if ((PINB&(1 << PORTB1)) == 0)
{
PORTD |= (1<<PORTD1); //Если кнопка нажата - зажигаем светодиод D2
}
else
{
PORTD &= ~(1<<PORTD1); //Если кнопка отжата - гасим светодиод D2
}
//Проверям состояние кнопки 3
if ((PINB&(1 << PORTB2)) == 0)
{
PORTD &= ~(1<<PORTD2); //Если кнопка отжата - гасим светодиод D3
}
else
{
PORTD |= (1<<PORTD2); //Если кнопка нажата - зажигаем светодиод D3
}
}
}
Программную задержку я применил только для обработки первой кнопки, для остальных кнопок код будет полностью идентичным.
Если есть вопросы и предложения по этому уроку, пишите комментарии буду рад ответить на ваши вопросы.
Мой блог в ЖЖ: http://evgenij-byvshev.livejournal.com
Привет!
Этот пост был выбран Академией Голоса и попал в список программы поддержки качественных образовательных постов.
Ссылка на твой пост будет опубликована в отчете Академии.
Спасибо за полезный контент (ノ◕ヮ◕)ノ*:・゚✧
А вот это совсем неожиданно! Спасибо!
На фоне "котиков", Ваша статья, как из другой реальности :)
А "котики" мне как раз и помогают ))