Как этот код отказа от нажатия кнопки?

Я контролирую кнопку с помощью микроконтроллера, где я пытаюсь позаботиться о четырех вещах вместе:

  1. Устранение дребезга в течение 50 мс при начале нажатия и 25 мс после отпускания.
  2. идентифицировать короткое нажатие, определяемое как отпускание кнопки в течение < 1 секунды
  3. определить длительное удержание, определяемое как когда проходит 1 секунда с момента нажатия кнопки
  4. спите как можно больше, когда ничего не делаете

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

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

Псевдокод:

Main loop {
  Sleep
}

Falling-Interrupt {
  Disable Falling-Interrupt
  Enable 50-millisecond-Debounce-Timer-Interrupt
}

50-millisecond-Debounce-Timer-Interrupt {
  if PushButton state is still LOW {
    Enable Rising-Interrupt
    Enable 1000-millisecond-Hold-Timer-Interrupt
  }
}

1000-millisecond-Hold-Timer-Interrupt {
  Register as Pushbutton long-hold
}

Rising-Interrupt {
  if (Time since Falling-Interrupt < 1000 millisecond) {
    Register as Button Short-press
  }
  Disable 1000-millisecond-Hold-Timer-Interrupt
  Enable 25-millisecond-Debounce-Timer-Interrupt
}

25-millisecond-Debounce-Timer-Interrupt {
   Enable Falling-Interrupt
} 
Лично я избегаю прерываний служебных процедур, когда могу. Я бы установил прерывание на 50 мс и выполнял всю обработку в основном, после сна. 25 мс: почему не 50 мс? Ни один пользователь не заметит.
@WoutervanOoijen: если я избегаю использования прерываний, как я могу обнаружить событие нажатия кнопки во время сна? Или вы имеете в виду, что я должен свести к минимуму фактический код внутри ISR? [И 25 против 50 мс было произвольным; Я просто использовал его, чтобы читателю было проще различать таймер устранения дребезга при запуске и таймер устранения дребезга при выпуске]
@Inga re: how can I detect a pushbutton event amidst sleep?Вы можете использовать только одно (1) прерывание: Falling-Interrupt. Просыпайтесь по этому прерыванию, выполняйте остальную часть устранения дребезга в основном цикле. Вернитесь в сон, если код показывает, что нажатие кнопки не было «настоящим».

Ответы (1)

Я не могу поместить код в комментарий, поэтому и отвечаю. Мой «каркас» для простых встраиваемых систем — это основной цикл с опросом. Для минимизации потребления тока основной цикл может ожидать, скажем, 50 мс в режиме ожидания. Я не знаю, какой UC вы используете, я знаком с PIC, которые могут выйти из спящего режима по прерыванию.

 set up an interrupt to wake me from sleep each 50 ms
 down_counter = 0
 for(;;){
    sleep();
    if( key down ){
       down_counter++;
       if( down_counter == 20 ){
          (start of) long_down detected
       }
    } else {
       if( down_counter > 1 && down_counter < 20 ){
          (end of) short press detected
       }
       down_counter = 0;
    }
 }
Это очень элегантно! Единственная небольшая слабость, которую я вижу, заключается в том, что в вашем подходе вы бы разбудили микроконтроллер 20 раз, поэтому, возможно, немного больше энергопотребление (тогда как в моем случае UC просыпается всего 2 или 3 раза). Мне был бы интересен ваш комментарий об этом аспекте, потому что все остальное, что мне нравится в этом фокусе основного цикла.
(Но я полагаю, что это очень мало потребляемого тока, учитывая, что вы в основном выполняете только две инструкции при каждом пробуждении.)
Одно из основных правил информатики гласит: «Не оптимизируй, если не знаешь, что у тебя узкое место». Не предполагайте, не вычисляйте и не измеряйте. Я думаю, что соотношение активного сна в моем подходе меньше 1:1000 (может быть, намного меньше, но это менее надежное предположение). Каково соотношение активного и спящего тока для вашего UC? Если это намного больше, чем 1:1000, вы не заметите разницы.
Это Atmega1280, и отношение активного и спящего тока действительно составляет около 1000:1. Я собираюсь попробовать этот подход и обновить свой вопрос в зависимости от результата.
Определенно думаю, что это путь. Единственное улучшение, которое я могу придумать, это сначала выйти из спящего режима при обнаружении первого нажатия кнопки, а затем переключиться на пробуждение по таймеру. После того, как нажатие кнопки будет полностью декодировано, переключитесь обратно на кнопку пробуждения.
@rocketmagnet: почему? чтобы сэкономить несколько% мощности по сравнению с более простым решением?
@WoutervanOoijen - Да, почти все. Это не так уж много улучшений, но это было своего рода точкой. По вашему предложению мало что можно улучшить.