Nano Hash - криптовалюты, майнинг, программирование

Переключение логики устранения дребезга в C

Я наткнулся на этот код Ганссле по поводу устранения дребезга переключателей. Код кажется довольно эффективным, и несколько вопросов, которые у меня есть, могут быть очень очевидными, но я был бы признателен за разъяснения.

  • Почему он проверяет 10 мс на нажатие кнопки и 100 мс на ее отпускание. Разве он не может просто проверить 10 мс на пресс-релиз?
  • Является ли опрос этой функции каждые 5 мс от основного наиболее эффективным способом ее выполнения, или я должен проверить прерывание на выводе, и когда есть прерывание, изменить вывод на GPI и перейти к процедуре опроса, и после того, как мы выведем значение переключателя контакт обратно в режим прерывания?

#define CHECK_MSEC  5   // Read hardware every 5 msec
#define PRESS_MSEC  10  // Stable time before registering pressed
#define RELEASE_MSEC    100 // Stable time before registering released
// This function reads the key state from the hardware.
extern bool_t RawKeyPressed();

// This holds the debounced state of the key.
bool_t DebouncedKeyPress = false;

// Service routine called every CHECK_MSEC to
// debounce both edges
void DebounceSwitch1(bool_t *Key_changed, bool_t *Key_pressed)
{
    static uint8_t Count = RELEASE_MSEC / CHECK_MSEC;
    bool_t RawState;
    *Key_changed = false;
    *Key_pressed = DebouncedKeyPress;
    RawState = RawKeyPressed();
    if (RawState == DebouncedKeyPress) {
        // Set the timer which will allow a change from the current state.
        if (DebouncedKeyPress) Count = RELEASE_MSEC / CHECK_MSEC;
        else                 Count = PRESS_MSEC / CHECK_MSEC;
    } else {
        // Key has changed - wait for new state to become stable.
        if (--Count == 0) {
            // Timer expired - accept the change.
            DebouncedKeyPress = RawState;
            *Key_changed=true;
            *Key_pressed=DebouncedKeyPress;
            // And reset the timer.
            if (DebouncedKeyPress) Count = RELEASE_MSEC / CHECK_MSEC;
            else                 Count = PRESS_MSEC / CHECK_MSEC;
        }
    }
}
25.01.2018

Ответы:


1

Почему он проверяет 10 мс на нажатие кнопки и 100 мс на ее отпускание.

Как говорится в сообщении в блоге, "Мгновенно реагируйте на ввод данных пользователем" и "Задержка в 100 мс весьма заметна".

Таким образом, основная причина, по-видимому, заключается в том, чтобы подчеркнуть, что make-debounce должен быть коротким, чтобы make фиксировался «немедленно» человеческим смыслом, и чтобы прерывание debounce было менее чувствительным ко времени.

Это также подтверждается абзацем в конце сообщения: "Как я уже писал в апрельском выпуске, большинство коммутаторов демонстрируют показатель отказов менее 10 мс. В сочетании с моим наблюдением, что ответ в 50 мс кажется мгновенным, разумно выберите период устранения отказов в диапазоне от 20 до 50 мс."

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

Разве он не может просто проверить 10 мс на пресс-релиз?

Конечно, почему бы и нет? Как он написал, это должно работать, хотя он писал (как указано выше), что предпочитает более длительные периоды устранения дребезга (от 20 до 50 мс).

Является ли опрос этой функции каждые 5 мс из основного наиболее эффективным способом ее выполнения

Нет. Как писал автор, "Все эти алгоритмы предполагают наличие таймера или другого периодического вызова, вызывающего дебаунсер". Другими словами, это всего лишь один способ реализации программное устранение дребезга, а показанные примеры основаны на обычном прерывании по таймеру, вот и все.

Кроме того, в 5 мс нет ничего волшебного; как говорит автор: "Для быстрого отклика и относительно низких вычислительных затрат я предпочитаю частоту тиков в несколько миллисекунд. Идеально от одной до пяти миллисекунд".

или я должен проверить прерывание на выводе, и когда есть прерывание, изменить вывод на GPI и перейти к процедуре опроса, а после того, как мы выведем значение, переключить вывод обратно в режим прерывания?

Если вы реализуете это в коде, вы обнаружите, что довольно неприятно иметь прерывание, которое блокирует нормальное выполнение кода на 10–50 мс за раз. Ничего страшного, если проверка состояния входного контакта — это единственное, что делается, но если аппаратное обеспечение делает что-то еще, например, обновляет дисплей или мерцает некоторыми мигающими огнями, ваша процедура устранения дребезга в обработчике прерываний вызовет заметное дрожание/заикание. Другими словами, то, что вы предлагаете, не является практической реализацией.

То, как работают программные подпрограммы устранения дребезга на основе периодических прерываний по таймеру (показаны в исходном сообщении в блоге и в других местах), занимает очень короткое время, всего пару десятков циклов или около того, и не прерывает другой код ни на какое время. значительное количество времени. Это просто и практично.

Вы можете комбинировать периодическое прерывание по таймеру и прерывание по входному контакту (изменение состояния), но, поскольку накладные расходы многих программных средств устранения дребезга, основанных только на прерываниях по таймеру, ничтожны, как правило, не стоит пытаться комбинировать два — - код становится очень, очень сложным, а сложный код (особенно на встроенном устройстве) имеет тенденцию быть сложным/дорогим в обслуживании.

Единственный случай, который я могу придумать (но я всего лишь любитель, а не EE ни в коем случае!) Это если вы хотите минимизировать энергопотребление, например. работа от батареи и использование прерывания входного контакта для перевода устройства в режим частичной или полной мощности из спящего режима и т. п.

(На самом деле, если у вас также есть миллисекундный или субмиллисекундный счетчик (не обязательно основанный на прерывании, но, возможно, счетчик циклов или аналогичный), вы можете использовать прерывание входного вывода и счетчик циклов для обновления состояния входа на первом изменить, а затем десенсибилизировать его на определенное время после этого, сохраняя значение счетчика циклов при изменении состояния.Однако вам необходимо обрабатывать переполнение счетчика, чтобы избежать ситуации, когда давнее событие, кажется, произошло совсем недавно , из-за переполнения счетчика.)


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

Обратите внимание, что я не хочу подразумевать, что это «лучшее» в каком-либо смысле; Я только хочу, чтобы вы показали один подход, который я не часто встречал, но который может иметь некоторые полезные свойства в некоторых случаях использования. Здесь также количество циклов сканирования (в миллисекундах), в течение которых игнорируются входные изменения (10 для замыкания/выключения-включения, 10 для прерывания/включения-выключения), являются лишь примерными значениями; используйте осциллограф или метод проб и ошибок, чтобы найти наилучшие значения для вашего варианта использования. Если это подход, который вы считаете более подходящим для вашего варианта использования, чем другие бесчисленные альтернативы, то есть.

Идея проста: используйте один байт на кнопку для записи состояния, при этом младший значащий бит описывает состояние, а семь других битов являются счетчиком снижения чувствительности (длительности устранения дребезга). Всякий раз, когда происходит изменение состояния, следующее изменение рассматривается только через несколько циклов сканирования.

Преимущество этого заключается в немедленном реагировании на изменения. Он также допускает различные длительности make-debounce и break-debounce (в течение которых состояние контакта не проверяется).

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

Во-первых, вы определяете количество сканирований, при которых входы десенсибилизируются после перерыва и после замыкания. Они варьируются от 0 до 127 включительно. Точные значения, которые вы используете, полностью зависят от вашего варианта использования; это просто заполнители.

#define  ON_ATLEAST   10  /* 0 to 127, inclusive */
#define  OFF_ATLEAST  10  /* 0 to 127, inclusive */

Для каждой кнопки у вас есть один байт состояния, переменная state ниже; инициализируется значением 0. Допустим, (PORT & BIT) — это выражение, которое вы используете для проверки этого конкретного входного вывода, оценивая его как true (не ноль) для ON и false (ноль) для OFF. Во время каждого сканирования (в вашем прерывании таймера) вы делаете

if (state > 1)
    state -= 2;
else
if ( (!(PORT & BIT)) != (!state) ) {
    if (state)
        state = OFF_ATLEAST*2 + 0;
    else
        state = ON_ATLEAST*2 + 1;
}

В любой момент вы можете проверить состояние кнопки, используя (state & 1). Это будет 0 для ВЫКЛ и 1 для ВКЛ. Кроме того, если (state > 1), то эта кнопка недавно была включена (если state & 1) или выключена (если state & 0) и поэтому не чувствительна к изменениям состояния входного контакта.

25.01.2018
  • Спасибо за объяснение, это очень помогло. Я прочитал сообщение в блоге и заметил период от 20 до 50 мс, который вы повторно процитировали. Я должен был объяснить свое замешательство немного лучше. Похоже, он установил период выпуска 100 мс (или любое другое более длительное значение периода) в случае кнопочного переключателя? В случае кулисного переключателя это не имеет большого значения, я могу оставить период отпускания и нажатия как значение короткого периода (10 мс или что-то в этом роде)? Хотя он опубликовал хороший общий код для всех приложений. Или, возможно, дайте мне знать, если я слишком много думаю :-P 25.01.2018
  • @CodeModeOn: я думаю, вы придаете слишком большое значение фактическим значениям в примере. Я вижу их только как четко установленные заполнители, не имеющие особого значения... выбранные не по практическим причинам (например, это хорошие значения для использования), а для того, чтобы сделать код более понятным и изменить его для собственное использование. Я сам часто делаю то же самое в своих примерах кода. 25.01.2018

  • 2

    В дополнение к принятому ответу, если вы просто хотите опрашивать коммутатор откуда-то каждые n мс, нет необходимости во всей запутанности и сложности из этой статьи. Просто сделайте это:

    static bool prev=false;
    ...
    
    /*** execute every n ms ***/
    bool btn_pressed = (PORT & button_mask) != 0;
    bool reliable = btn_pressed==prev;
    prev = btn_pressed;
    
    if(!reliable)
    {
      btn_pressed = false; // btn_pressed is not yet reliable, treat as not pressed
    }
    
    // <-- here btn_pressed contains the state of the switch, do something with it
    

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

    Как отмечено в статье, электромеханический дребезг переключателей чаще всего меньше 10мс. Вы можете легко измерить дребезг с помощью осциллографа, подключив переключатель между любым источником постоянного тока и землей (предпочтительно последовательно с токоограничивающим резистором).

    25.01.2018
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..