Радиолюбители знают, что это такое, для не знающих кратко - любая кнопка по факту при нажатии вначале выдаёт серию очень коротких импульсов, а затем уже окончательно замыкает цепь. Аналогичное поведение и при отпускании. Причина - упругость контактов и отсутствие абсолютно твёрдых материалов.
Есть самодельная схема, где ничего особо оригинального нет - AtMega328P, 7-сегментный светодиодный индикатор (пока что 4 секции, думаю расширить до 6-8), 4 кнопки и куча датчиков (пока только климатические - на них удобно отладку делать, потом будут и иные).
Основная задача - подавление дребезга контактов при нажатии. Есть два момента, делающие задачу проще:
1. Нажатия двух и более кнопок одновременно не предвидится вообще. То есть, это отслеживать не надо, хотя реализация схемы и позволит. Кнопки подключены через диоды на выходы, управляющие разрядами дисплея, и заведены на общий вход. Диоды - чтобы не было замыкания на управляющих выходах.
2. Повтор нажатий при удержании не нужен, как минимум, на текущем этапе. То есть, схема такая: "нажал - отработала - ждём отпускания".
Идея алгоритма такова:
В схеме реализовано прерывание по таймеру 50 раз в секунду (интервал 20 миллисекунд) для обновления дисплея. Это более чем достаточно для подавления дребезга и вполне устраивает меня по быстродействию.
Допустим, изначально состояние "все кнопки отпущены", назовём его начальным. Плюс имеется флаг "кнопка сработала", установлен в ноль. Если нажато несколько кнопок - сразу возврат в это состояние.
При нажатии одной кнопки (на несколько не реагируем, см. предыдущий абзац) записываем её состояние. Если не совпадает с предыдущим (была отпущена, а теперь нажата), ждём следующий опрос. Если совпадает - смотрим на флаг срабатывания: если ноль - выполняем действие и устанавливаем в единицу, если уже единица - просто ждём отпускания.
При отпускании всех кнопок сбрасываем флаг срабатывания в ноль и переходим в начальное состояние.
Просьба указать, если в логике работы есть ошибки.
Подавление дребезга контактов - алгоритм
В общем правильно, но сравнивать лучше с несколькими предыдущими состояниями. Если они все совпадают, тогда обрабатываем. Неплохо бы 3, но это получается задержка 60..80 ms. Это уже слишком близко к пределу 100 ms
s.xbatob, благодарю. Буду делать.
Вчера собрал схему на AVR (кроме кнопок), сделал обработчик прерывания, реализовал отсчёт времени (каждое 50-е прерывание). С дребезгом буду пробовать сегодня уже.
Rizado, может не совсем я прав, но для сглаживания дребезга контактов используются RC цепочки на землю или триггеры Шмидта. Но, ИМХО, получить квадратную форму сигнала все равно не получится, но хороший меандр - да. *SCRATCH*
Oleg65, Вы правы, знаю о таком способе. Но меня интересует программное подавление, чтобы меньше усложнять схему.
Но меня интересует программное подавление
Ваш длинный текст упрощаю.
Выявленное событие перехода кнопки в любое другое состояние: 0-> 1 или 1 -> 0 должно запускать таймер (таймаут), в течении которого система на другие переходы (из-за возможного дребезга) не реагирует. После таймаута возобновить "прослушку" событий переходов.
Rizado, может не совсем я прав, но для сглаживания дребезга контактов используются RC цепочки на землю или триггеры Шмидта. Но, ИМХО, получить квадратную форму сигнала все равно не получится, но хороший меандр - да. *SCRATCH*
Проехали. Уже лет 30 как. Всё давным-давно решается программно. И клавиши сканируются, так что RC цепочки тут не поставить.
user160, Вы не совсем правы. Основная проблема в том, что в это время надо выполнять другие действия, такие, как обновление экрана, вычисления, сбор информации с датчиков...
user160, Вы не совсем правы. Основная проблема в том, что в это время надо выполнять другие действия, такие, как обновление экрана, вычисления, сбор информации с датчиков...
Вероятно я неточно выразился. Я описал только ту часть алгоритма, которая обрабатывает события нажатий кнопки. Это отнюдь не означает, что все ресурсы компьютера должны быть сосредоточены только на кнопках.
user160, возможно, мы просто не совсем поняли друг друга :) .
В любом случае, считывание кнопок сегодня более-менее реализовал, пока разбираюсь - сделал из прибора часы :) , пусть хоть время и дату показывает. Если интересно, вот код функции, обновляющей дисплей и сканирующей кнопки. Не идеально, но работает:
Показать
void refreshDisplay() {
byte i;
boolean inLvl;
// три переменные ниже - типа byte, объявлены в теле программы; хотя последнюю можно и локально, потом перенесу
keyPressedPrev = keyPressedCurr;
keyPressedCurr = 0;
keysPressed = 0;
// digits - byte array[27], в него пишем сегменты, которые надо включать, для единицы это 0B01100000, там ещё некоторые и буквы, и упрощённый знак градуса, и т.д.
// LEDisplay - byte array[8], а здесь только коды символов, для цифр 0-9 они совпадают с цифрами, для A-F это с 10 по 15 и т.д.
for (i = 0; i <=7; i++) {
// сегменты пишем в регистр, экономия выводов
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, digits[LEDisplay[i]]);
digitalWrite(latchPin, HIGH);
digitalWrite(i + 2, HIGH);
delayMicroseconds(666); // задержки в 2/3 миллисекунды хватает для нормальной яркости, там разисторы по 330 Ом
// кнопки висят только на четырёх первых выводах, так что лишнее не сканируем
if (i < 4) {
inLvl = digitalRead(keyInputPin) == HIGH;
keyPressedCurr = (keyPressedCurr << 1) | inLvl;
if (inLvl) {
keysPressed++;
}
}
digitalWrite(i + 2, LOW);
}
// а если нажато больше одной - нам это не надо :)
if (keysPressed > 1) {
keyPressedCurr = 0;
}
}
Что именно выводить - готовится отдельно от отображения. Проще разбить на маленькие логические блоки и их отдельно тестировать.
код функции, обновляющей дисплей и сканирующей кнопки
По хорошему, в одной функции это делать нехорошо.
Лучше три функции:
- одна функция на дисплей,
- другая на прерывание от кнопок,
- третья таймер, который вновь разрешает прерывание.
Алгоритм таков: кнопка "дёрнула" прерывание. -> 2-я функция "слушатель" прерывания запретила повторные прерывание от кнопки и вызвала третью (таймер).
Не знаю, возможен ли асинхронный вызов функций в Ардуино, а он здесь как раз нужен.
Суть в том, что 2-я функция должна вызвать 3-ю функцию (таймер), а сама 2-я должна завершиться не дожидаясь окончания работы 3-й.
3-я функция, по окончании своей работы, снова разрешает "слушать" прерывание для второй функции.
1-я функция для дисплея - за скобками.
Как-то так. ИМХО
Мне так удобнее - быстродействие лучше.
Кстати, нормально работает, вчера вечером потестировал. Правки были ещё, но уже технические.
В общем, задача решена. Потренировался на том, с чего начинают большинство юзеров Arduino - часы с будильником, датой (с учётом високосного года) и т.д.
Работает идеально, алгоритм был правильный. Когда нарисую схему - выложу. Да, схемы нет, собиралось на макетной плате сразу :) .
За подсказки всем спасибо!
А разве стандартные SDK под контролеры не предостовляют таких средств ?
А разве стандартные SDK под контролеры не предостовляют таких средств ?
Их сложно с программой подружить, проще было своё с нуля написать. Давно уже сделал всё.
А разве стандартные SDK под контролеры не предостовляют таких средств ?
Там нет ничего сложного. Просто сам идея чисто программная: тикер, сдвиговой регистр, кое-какая логика его выходов... Для аппаратчиков не слишком понятная.
Но надо понимать, что по мере старения кнопки, возможно, придётся вводить коррективы. Особенно. если сдвиговый регистр всего лишь двухбитный. :(
s.xbatob, а там не будет частых нажатий. Включили, настроили, а дальше максимум 5 нажатий в сутки.