1

Радиолюбители знают, что это такое, для не знающих кратко - любая кнопка по факту при нажатии вначале выдаёт серию очень коротких импульсов, а затем уже окончательно замыкает цепь. Аналогичное поведение и при отпускании. Причина - упругость контактов и отсутствие абсолютно твёрдых материалов.

Есть самодельная схема, где ничего особо оригинального нет - AtMega328P, 7-сегментный светодиодный индикатор (пока что 4 секции, думаю расширить до 6-8), 4 кнопки и куча датчиков (пока только климатические - на них удобно отладку делать, потом будут и иные).
Основная задача - подавление дребезга контактов при нажатии. Есть два момента, делающие задачу проще:
1. Нажатия двух и более кнопок одновременно не предвидится вообще. То есть, это отслеживать не надо, хотя реализация схемы и позволит. Кнопки подключены через диоды на выходы, управляющие разрядами дисплея, и заведены на общий вход. Диоды - чтобы не было замыкания на управляющих выходах.
2. Повтор нажатий при удержании не нужен, как минимум, на текущем этапе. То есть, схема такая: "нажал - отработала - ждём отпускания".

Идея алгоритма такова:

В схеме реализовано прерывание по таймеру 50 раз в секунду (интервал 20 миллисекунд) для обновления дисплея. Это более чем достаточно для подавления дребезга и вполне устраивает меня по быстродействию.
Допустим, изначально состояние "все кнопки отпущены", назовём его начальным. Плюс имеется флаг "кнопка сработала", установлен в ноль. Если нажато несколько кнопок - сразу возврат в это состояние.
При нажатии одной кнопки (на несколько не реагируем, см. предыдущий абзац) записываем её состояние. Если не совпадает с предыдущим (была отпущена, а теперь нажата), ждём следующий опрос. Если совпадает - смотрим на флаг срабатывания: если ноль - выполняем действие и устанавливаем в единицу, если уже единица - просто ждём отпускания.
При отпускании всех кнопок сбрасываем флаг срабатывания в ноль и переходим в начальное состояние.

Просьба указать, если в логике работы есть ошибки.

ᛈᚺ'ᚾᚷᛚᚢᛁ ᛗᚷᛚᚹ'ᚾᚨᚠᚺ ᚲᛏᚺᚢᛚᚺᚢ ᚱ'lᚷᛖᚺ ᚹᚷᚨᚺ'ᚾᚨᚷᛚ ᚠᚺᛏᚨᚷᚾ

Asus Prime B460M-K, i5-10500, Intel 630 UHD + NVIDIA GeForce RTX3060, DDR4 32 GB, SSD 1TB + HDD 2TB | Linux Mint 22.3 Cinnamon + Windows 11

2

В общем правильно, но сравнивать лучше с несколькими предыдущими состояниями. Если они все совпадают, тогда обрабатываем. Неплохо бы 3, но это получается задержка 60..80 ms. Это уже слишком близко к пределу 100 ms

3

s.xbatob, благодарю. Буду делать.
Вчера собрал схему на AVR (кроме кнопок), сделал обработчик прерывания, реализовал отсчёт времени (каждое 50-е прерывание). С дребезгом буду пробовать сегодня уже.

ᛈᚺ'ᚾᚷᛚᚢᛁ ᛗᚷᛚᚹ'ᚾᚨᚠᚺ ᚲᛏᚺᚢᛚᚺᚢ ᚱ'lᚷᛖᚺ ᚹᚷᚨᚺ'ᚾᚨᚷᛚ ᚠᚺᛏᚨᚷᚾ

Asus Prime B460M-K, i5-10500, Intel 630 UHD + NVIDIA GeForce RTX3060, DDR4 32 GB, SSD 1TB + HDD 2TB | Linux Mint 22.3 Cinnamon + Windows 11

4

Rizado, может не совсем я прав, но для сглаживания дребезга контактов используются RC цепочки на землю или триггеры Шмидта. Но, ИМХО, получить квадратную форму сигнала все равно не получится, но хороший меандр - да.  bw

5

Oleg65, Вы правы, знаю о таком способе. Но меня интересует программное подавление, чтобы меньше усложнять схему.

ᛈᚺ'ᚾᚷᛚᚢᛁ ᛗᚷᛚᚹ'ᚾᚨᚠᚺ ᚲᛏᚺᚢᛚᚺᚢ ᚱ'lᚷᛖᚺ ᚹᚷᚨᚺ'ᚾᚨᚷᛚ ᚠᚺᛏᚨᚷᚾ

Asus Prime B460M-K, i5-10500, Intel 630 UHD + NVIDIA GeForce RTX3060, DDR4 32 GB, SSD 1TB + HDD 2TB | Linux Mint 22.3 Cinnamon + Windows 11

6

Rizado пишет:

Но меня интересует программное подавление

Ваш длинный текст упрощаю.
Выявленное событие перехода кнопки в любое другое состояние: 0-> 1 или 1 -> 0 должно запускать таймер (таймаут), в течении которого система на другие переходы (из-за возможного дребезга) не реагирует. После таймаута возобновить "прослушку" событий переходов.

Debian amd64 13.5.0.

7

Oleg65 пишет:

Rizado, может не совсем я прав, но для сглаживания дребезга контактов используются RC цепочки на землю или триггеры Шмидта. Но, ИМХО, получить квадратную форму сигнала все равно не получится, но хороший меандр - да.  bw

Проехали. Уже лет 30 как. Всё давным-давно решается программно. И клавиши сканируются, так что RC цепочки тут не поставить.

8

user160, Вы не совсем правы. Основная проблема в том, что в это время надо выполнять другие действия, такие, как обновление экрана, вычисления, сбор информации с датчиков...

ᛈᚺ'ᚾᚷᛚᚢᛁ ᛗᚷᛚᚹ'ᚾᚨᚠᚺ ᚲᛏᚺᚢᛚᚺᚢ ᚱ'lᚷᛖᚺ ᚹᚷᚨᚺ'ᚾᚨᚷᛚ ᚠᚺᛏᚨᚷᚾ

Asus Prime B460M-K, i5-10500, Intel 630 UHD + NVIDIA GeForce RTX3060, DDR4 32 GB, SSD 1TB + HDD 2TB | Linux Mint 22.3 Cinnamon + Windows 11

9

Rizado пишет:

user160, Вы не совсем правы. Основная проблема в том, что в это время надо выполнять другие действия, такие, как обновление экрана, вычисления, сбор информации с датчиков...

Вероятно я неточно выразился. Я описал только ту часть алгоритма, которая обрабатывает события нажатий кнопки. Это отнюдь не означает, что все ресурсы компьютера должны быть сосредоточены только на кнопках.

Debian amd64 13.5.0.

10

user160, возможно, мы просто не совсем поняли друг друга ab .
В любом случае, считывание кнопок сегодня более-менее реализовал, пока разбираюсь - сделал из прибора часы ab , пусть хоть время и дату показывает. Если интересно, вот код функции, обновляющей дисплей и сканирующей кнопки. Не идеально, но работает:

+ открыть спойлер
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;
  }
}

Что именно выводить - готовится отдельно от отображения. Проще разбить на маленькие логические блоки и их отдельно тестировать.

ᛈᚺ'ᚾᚷᛚᚢᛁ ᛗᚷᛚᚹ'ᚾᚨᚠᚺ ᚲᛏᚺᚢᛚᚺᚢ ᚱ'lᚷᛖᚺ ᚹᚷᚨᚺ'ᚾᚨᚷᛚ ᚠᚺᛏᚨᚷᚾ

Asus Prime B460M-K, i5-10500, Intel 630 UHD + NVIDIA GeForce RTX3060, DDR4 32 GB, SSD 1TB + HDD 2TB | Linux Mint 22.3 Cinnamon + Windows 11

11

Rizado пишет:

код функции, обновляющей дисплей и сканирующей кнопки

По хорошему, в одной функции это делать нехорошо.
Лучше три функции:

  • одна функция на дисплей,

  • другая на прерывание от кнопок,

  • третья таймер, который вновь разрешает прерывание.

Алгоритм таков: кнопка "дёрнула" прерывание. -> 2-я функция "слушатель" прерывания запретила повторные прерывание от кнопки и вызвала третью (таймер).
Не знаю, возможен ли асинхронный вызов функций в Ардуино, а он здесь как раз нужен.
Суть в том, что 2-я функция должна вызвать 3-ю функцию (таймер), а сама 2-я должна завершиться не дожидаясь окончания работы 3-й.

3-я функция, по окончании своей работы, снова разрешает "слушать" прерывание для второй функции.

1-я функция для дисплея - за скобками.

Как-то так. ИМХО

Debian amd64 13.5.0.

12

Мне так удобнее - быстродействие лучше.
Кстати, нормально работает, вчера вечером потестировал. Правки были ещё, но уже технические.

ᛈᚺ'ᚾᚷᛚᚢᛁ ᛗᚷᛚᚹ'ᚾᚨᚠᚺ ᚲᛏᚺᚢᛚᚺᚢ ᚱ'lᚷᛖᚺ ᚹᚷᚨᚺ'ᚾᚨᚷᛚ ᚠᚺᛏᚨᚷᚾ

Asus Prime B460M-K, i5-10500, Intel 630 UHD + NVIDIA GeForce RTX3060, DDR4 32 GB, SSD 1TB + HDD 2TB | Linux Mint 22.3 Cinnamon + Windows 11

13

В общем, задача решена. Потренировался на том, с чего начинают большинство юзеров Arduino - часы с будильником, датой (с учётом високосного года) и т.д.
Работает идеально, алгоритм был правильный. Когда нарисую схему - выложу. Да, схемы нет, собиралось на макетной плате сразу ab .

За подсказки всем спасибо!

ᛈᚺ'ᚾᚷᛚᚢᛁ ᛗᚷᛚᚹ'ᚾᚨᚠᚺ ᚲᛏᚺᚢᛚᚺᚢ ᚱ'lᚷᛖᚺ ᚹᚷᚨᚺ'ᚾᚨᚷᛚ ᚠᚺᛏᚨᚷᚾ

Asus Prime B460M-K, i5-10500, Intel 630 UHD + NVIDIA GeForce RTX3060, DDR4 32 GB, SSD 1TB + HDD 2TB | Linux Mint 22.3 Cinnamon + Windows 11

14

А разве стандартные SDK под контролеры не предостовляют таких средств ?

XMPP: mogul82@jabber.ru Фанбой AMD

15

mogul82 пишет:

А разве стандартные SDK под контролеры не предостовляют таких средств ?

Их сложно с программой подружить, проще было своё с нуля написать. Давно уже сделал всё.

ᛈᚺ'ᚾᚷᛚᚢᛁ ᛗᚷᛚᚹ'ᚾᚨᚠᚺ ᚲᛏᚺᚢᛚᚺᚢ ᚱ'lᚷᛖᚺ ᚹᚷᚨᚺ'ᚾᚨᚷᛚ ᚠᚺᛏᚨᚷᚾ

Asus Prime B460M-K, i5-10500, Intel 630 UHD + NVIDIA GeForce RTX3060, DDR4 32 GB, SSD 1TB + HDD 2TB | Linux Mint 22.3 Cinnamon + Windows 11

16

mogul82 пишет:

А разве стандартные SDK под контролеры не предостовляют таких средств ?

Там нет ничего сложного. Просто сам идея чисто программная: тикер, сдвиговой регистр, кое-какая логика его выходов... Для аппаратчиков не слишком понятная.
Но надо понимать, что по мере старения кнопки, возможно, придётся вводить коррективы. Особенно. если сдвиговый регистр всего лишь двухбитный. ac

17

s.xbatob, а там не будет частых нажатий. Включили, настроили, а дальше максимум 5 нажатий в сутки.

ᛈᚺ'ᚾᚷᛚᚢᛁ ᛗᚷᛚᚹ'ᚾᚨᚠᚺ ᚲᛏᚺᚢᛚᚺᚢ ᚱ'lᚷᛖᚺ ᚹᚷᚨᚺ'ᚾᚨᚷᛚ ᚠᚺᛏᚨᚷᚾ

Asus Prime B460M-K, i5-10500, Intel 630 UHD + NVIDIA GeForce RTX3060, DDR4 32 GB, SSD 1TB + HDD 2TB | Linux Mint 22.3 Cinnamon + Windows 11