Дребезг контактов — явление, с которым знаком каждый электронщик. В этой статье речь пойдет об программном способе борьбы с данным явлением.
Введение
Была одна задача, в которой надо было реализовать программный антидребезг контактов для относительно большого числа кнопок, при этом хотелось, чтоб алгоритм был максимально легким, и не занимал много процессорного времени. Полазив по интернету, нашел несколько вариантов реализации, и выбрал наиболее понравившийся.
Простой алгоритм антидребезга
Идея состоит в следующем: ждем, пока кнопка будет нажата. Далее, через время Δt выполняем повторное чтение. Если кнопка по-прежнему находится в нажатом состоянии, то считаем, что кнопка нажата устойчиво, и выполняем установку соответствующего флага. Время Δt выбирается заведомо больше времени дребезга контактов, что-то в диапазоне 10-100мс.
Я немного модифицировал этот алгоритм для удобной реализации на конечных автоматах [2]. Алгоритм на СИ выгладит примерно так.
Пишем функцию, которая читает текущее состояние всех входов, и результат заносит в одну переменную, в которой каждый бит отвечает за одну кнопку. Если бит установлен в 1, то контакты соответствующей кнопки замкнуты в данный момент времени, если 0, то разомкнуты:
//Читаем состояние всех кнопок и помещаем результат в одну переменную //где каждый бит - это текущее состояние GPIO кнопки static uint32_t GetCurrentValues(void) { uint32_t r = (STATE_BUTT_1 << 0) | (STATE_BUTT_2 << 1) | (STATE_BUTT_3 << 2) | (STATE_BUTT_4 << 3) | (STATE_BUTT_5 << 4) ......... | (STATE_BUTT_16 << 15); return r; }
Далее, сам алгоритм антидребезга. Его идея заключается в том, что мы выполняем чтение состояния кнопок через равные промежутки времени следующим образом:
//Timer - программный либо аппаратный счетчик, //который увеличивает свое значение на 1 //через равные промежутки времени, //например, каждую 1мс switch (state) { case 0: if (Timer >= dT) { value1 = GetCurrentValues(); Timer = 0; state = 1; } break; case 1: if (Timer >= dT) { value2 = GetCurrentValues(); PressedButtons = value1 & value2; //Результат Timer = 0; state = 0; } break; }
Здесь весь прикол заключается в том, что алгоритм антидребезга для 32-х входов выполняется одной строчкой:
PressedButtons = value1 & value2
т.е. если на предыдущем и текущем моменте чтения было получено значение 1, операция «лог. И» даст 1, иначе 0. И это все одновременно для всех 32-х бит! Круто. Далее, выполняем побитовое обращение к переменной PressedButtons, и читаем состояние кнопок далее по тексту программы.
Алгоритм можно улучшить:
switch (state) { case 0: value1 = 0; value2 = 0; Timer = 0; state = 1; break; case 1: if (Timer >= dT) { value1 = GetCurrentValues(); PressedButtons = value1 & value2; Timer = 0; state = 2; } break; case 2: if (Timer >= dT) { value2 = GetCurrentValues(); PressedButtons = value1 & value2; Timer = 0; state = 1; } break; }
Здесь мы обновляем значение PressedButtons в два раза чаще.
Добавляем обратную связь
Проблема этого алгоритма заключается в том, что в промежуточных состояниях, когда на предыдущем и текущем периодах считывания были получили разные значения, мы считаем кнопку не нажатой:
№ value1 value2 rez 1 0 0 0 2 0 1 0 << 3 1 0 0 << 4 1 1 1
Эта таблица истинности для операции «лог. И», которая показывает, как работает текущий алгоритм.
Это может вызвать проблемы, если после нажатия на кнопку и завершения переходных процессов, контакт все равно остается не надежным, и теряется не небольшие промежутки времени. Это может случаться, например, из-за износа контактной группы. В этом случае, в состояниях номер 2 и 3 было бы корректней использовать предыдущее состояние кнопки, т.е. если ранее мы зафиксировали нажатое состояние кнопки, то новое состояние так же будет нажатым, и наоборот:
№ value1 value2 old_rez rez 1 0 0 0 0 2 0 0 1 0 3 0 1 0 0 << 4 0 1 1 1 << 5 1 0 0 0 << 6 1 0 1 1 << 7 1 1 0 1 8 1 1 1 1
где old_rez — предыдущее состояние кнопки, которое зарегистрировал алгоритм антидребезга.
Состояния 3, 4, 5, 6 — неустойчивые, поэтому в этих случаях в качестве результата rez используется предыдущий результат old_rez.
С тем, что мы хотим получить в результате мы определились, теперь давайте разберемся, как превратить полученную таблицу истинности в программную реализацию. Руководствуясь параграфом из учебника по информатике за 10 класс [3] получаем логическое выражение, которое удовлетворяет нашей таблице истинности:
rez = ((!value1) & value2 & old_rez) | (value1 & (!value2) & old_rez) | (value1 & value2 & (!old_rez)) | (value1 & value2 & old_rez);
Немного страшно? Но это ничего. По запросу «Упрощение логических выражений онлайн» находим утилиту [4], которая все это дело упрощает до состояния:
rez = (value1 & value2) | (value1 & old_rez) | (value2 & old_rez);
Уже намного лучше. На этом этапе можно считать, что домашнее задание по информатике мы выполнили, переходим к коду.
static uint32_t calc(uint32_t a, uint32_t b, uint32_t old) { return (a & b) | (a & old) | (b & old); } //Вызывается в бесконечном цикле в main(): void Buttons_Process(void) { static uint32_t value1; static uint32_t value2; switch (state) { case 0: // Инициализация value1 = 0; value2 = 0; PressedButtons = 0; Timer = 0; state = 1; break; case 1: if (Timer >= dT) { value1 = GetCurrentValues(); PressedButtons = calc(value1, value2, PressedButtons); Timer = 0; state = 2; } break; case 2: if (Timer >= dT) { value2 = GetCurrentValues(); PressedButtons = calc(value1, value2, PressedButtons); Timer = 0; state = 1; } break; } }
Обратная связь здесь и заключается в том, что результат PressedButtons зависит от его предыдущего значения: PressedButtons = calc(value1, value2, PressedButtons)
В итоге мы получили продвинутую систему программного антидребезга, которая может одновременно обрабатывать 32 кнопки (64, если использовать uint64_t), при этом отнимает совсем мало процессорного времени. Логические операции — сила! 😀
На этом все, всем спасибо за внимание, и до новых встреч!))
Литература
- Дребезг контактов [Электронный ресурс] // Википедия. URL: https://ru.wikipedia.org/wiki/%D0%94%D1%80%D0%B5%D0%B1%D0%B5%D0%B7%D0%B3_%D0%BA%D0%BE%D0%BD%D1%82%D0%B0%D0%BA%D1%82%D0%BE%D0%B2
- Татарчевский Владимир: «Применение Switch-технологии при разработке прикладного программного обеспечения для микроконтроллеров. Часть 1» // Компоненты и технологии, №2’2007
- Составление логического выражения по таблице истинности и его упрощение // URL: https://www.лена24.рф/Информатика_10_кл_Босова/20.3.html
- Онлайн-утилита упрощения логических выражений [Электронный ресурс] // URL: // https://www.kontrolnaya-rabota.ru/s/mathlogic