Необходимые знания: [HW] Kasutajaliidese moodul, [AVR] Digitaalsed sisendid-väljundid, [LIB] Sisend-väljundviigud, [LIB] Viide, [PRT] Lüliti
Как было сказано в главе, знакомящей с переключателями, у механических переключателей существует проблема дребезга контактов. Так как контакты сделаны из металла, у которого существует определенная эластичность, то в момент соединения или разъединения они дребезжат и в результате создается множество ложных срабатываний. Число срабатываний и их продолжительность зависит от переключателя, но в большинстве случаев это остаётся в пределах миллисекунды. Если переключатель используется для запуска электрического устройства, то это не создаёт больших проблем, но если для управления устройства, то многократное срабатывание может быть проблемой.
Основной метод избежания ложного срабатывания, возникшего в связи с дребезгом контактов, это фильтрация. Фильтровать можно как электрически, так и программно. При электрической фильтрации нужно соединить переключатель через фильтр нижних частот – к примеру, RC-фильтр – который сглаживает изменения напряжения и благодаря этому вывод микроконтроллера не обретает кратковременных значений. Программная фильтрация происходит по принципу, что значение вывода, куда подключается переключатель, считывается несколько раз и если все разы значение одинаково, тo делается вывод, что переключатель имеет постоянное положение и дребезга контактов не происходит. При любой фильтрации следует считаться с задержкой, которая происходит при определении состояния.
Электрическая фильтрация не используется у переключателей Домашней Лаборатории, так как в этом случае было бы невозможно упражняться в программной задаче ликвидации ложных срабатываний. Упражнение состоит из двух частей. Цель первого задания продемонстрировать дребезг контактов кнопок модуля Дигитальные входы-выходы. Для этого существует следующая программа, которая на каждое нажатие на кнопку зажигает следующий LED. При ложных нажатиях кнопки, LED-ы зажигаются несколько раз и довольно беспорядочно.
// // Программа для демонстрации дребезга контактов // модуля Дигитальные входы-выходы Домашней Лаборатории. // #include <homelab/pin.h> // // Обозначение выводов LED-ов и кнопок // pin leds[3] = { PIN(C, 5), PIN(C, 4), PIN(C, 3) }; pin button = PIN(C, 0); // // Основная программа // int main(void) { unsigned char new_value, old_value = 0; unsigned char index, counter = 0; // Настройка выводов LED-ов выходом for (index = 0; index < 3; index++) { pin_setup_output(leds[index]); } // Обозначение выводов кнопок входом pin_setup_input(button); // Бесконечный цикл while (true) { // Считывание состояния кнопки new_value = pin_get_value(button); // Контроль, нажата ли кнопка, // т.е. является ли новое состояние 1, а старое 0 if ((new_value) && (!old_value)) { // Увеличение счетчика и взятие 3 модуля counter = (counter + 1) % 3; // Значение счетчика зажиганию LED-а for (index = 0; index < 3; index++) { pin_set_to(leds[index], index != counter); } } // Запоминаем старое состояние old_value = new_value; } }
У программной фильтрации существует несколько методов. Это можно делать просто и сложно, у обоих вариантов свои преимущества и недостатки. Если программа такова, что нажатие на кнопку ожидается редко, то можно после регистрации нажатия кнопки добавить длинную паузу, что исключает реакцию на дополнительные включения, возникшие при дребезге. Так же при подобном решении следует учитывать, что если пользователь держит кнопку долго нажатой, то при отжатии кнопки, программа реагирует на возникшее ложное срабатывание.
Надежней программа, которая контролирует состояние кнопки в течении определённого времени несколько раз (чем больше и длиннее время, тем надежней). Далее преведено чтение фильтрованного значения функции кнопки Дигитального входного-выходного модуля:
// // Функция некоторых IO расширительных плат для чтения фильтрованных значений переключателя // unsigned char pin_get_debounced_value(pin button) { unsigned char buffer = 0xAA; unsigned char timeout = 100; // Ждем, пока состояние кнопки станет ясным или закончится выяснение // состояния while (timeout-- > 0) { // Содержание 8 местного (битного) положения буфера // Все предыдущие состояния (биты) сдвигаются влево и вправо // добавляется новое состояние (бит). buffer <<= 1; buffer |= (pin_get_value(button) ? 0x01 : 0x00); // Если все 8 бит в верхнем состоянии,то // кнопка точно нажата if (buffer == 0xFF) { return 1; } // Если все 8 бит в нижнем состоянии, то // кнопка точно не нажата if (buffer == 0x00) { return 0; } // Пауза 1 миллисекунда // Эта функция находится в библиотеке Домашней Лаборатории sw_delay_ms(1); } // Если состояние не удалось определить,то дагадываемся, // что кнопка не нажата. return 0; }
Данная функция использует функцию образования задержки, о чём подробнее рассказывает соответсвующее задание. Здесь не нужно много знать о функциях задержки кроме того, что создается 1 миллисекундная задержка для каждого состояния кнопки в конце цикла. Если кнопка в течении восьми раз находится в одном положении, это возвращает посчитанное состояние. Вся процедура может в случае нестабильности кнопки длиться до 100 миллисекунд. Функция уже содержится в библиотеке выводов, так что для прохождения примера не нужно добавлять это в свою программу. Для испытания этого придется первую часть задания немного изменить - привлечь в программу для создания задержки библиотеку и в месте, где значение кнопки было прочитано прямо, использовать функцию с фильтром. Результат следующий:
// // Программа для фильтрации дребезга контактов // модуля Дигитальные входы-выходы Домашней Лаборатории. // #include <homelab/delay.h> #include <homelab/pin.h> // // Обозначение выводов LED-ов и кнопок // pin leds[3] = { PIN(C, 5), PIN(C, 4), PIN(C, 3) }; pin button = PIN(C, 0); // // Основная программа // int main(void) { unsigned char new_value, old_value = 0; unsigned char index, counter = 0; // Настройка выводов LED-ов выходом for (index = 0; index < 3; index++) { pin_setup_output(leds[index]); } // Обозначение выводов кнопок входом pin_setup_input(button); // Бесконечный цикл while (true) { // Чтение состояния кнопки new_value = pin_get_debounced_value(button); // Контроль, нажата ли кнопка // и является ли новое состояние 1, а старое 0 if ((!new_value) && (old_value)) { // Увеличение счетчика и взятие 3 модуля counter = (counter + 1) % 3; // Зажигание LED-а соответствующего значению счётчика for (index = 0; index < 3; index++) { pin_set_to(leds[index], index != counter); } } // Запоминаем старое состояние old_value = new_value; } }
Если теперь опробовать программу, то LED-ы загораются точно в такой же последовательности, в которой пользователь нажимает кнопку.