6
\$\begingroup\$

I am monitoring a pushbutton with a microcontroller where I am trying to take care of four things together:

  1. 50-msec debounce upon push-begin and 25-msec debounce upon push-release
  2. identify a short-press, defined as when button released within < 1 second
  3. identify a long-hold, defined as when 1 second passes since button push-begin
  4. sleep as much as possible when not doing anything else

Below is a short pseudocode of what I have implemented so far. I think it covers all of these cases.

Do you see any possible refinements or potential issues? (E.g., I am interested in any subtle cases that might be blindspots for my approach.)

Pseudocode:

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
} 
\$\endgroup\$
  • \$\begingroup\$ Personally I avoid interrupts service routines whenever I can. I would set up a 50 ms interrupt, and do all processing in the main, after the sleep. 25 ms: why not 50 ms? No user will notice. \$\endgroup\$ – Wouter van Ooijen Oct 13 '12 at 15:46
  • \$\begingroup\$ @WoutervanOoijen: If I avoid using interrupts, how can I detect a pushbutton event amidst sleep? Or do you mean I should minimize the actual code inside the ISR? [And the 25 vs 50 ms was arbitrary; I just used it to make it easier for the reader to distinguish between the upon-begin debounce timer and the upon-release debounce timer] \$\endgroup\$ – boardbite Oct 13 '12 at 15:49
  • 3
    \$\begingroup\$ @Inga re: how can I detect a pushbutton event amidst sleep? You can use only one (1) interrupt: the Falling-Interrupt. Wake up on this interrupt, do the rest of debouncing in the main loop. Go back to sleep, if the code figures that the button press wasn't "real". \$\endgroup\$ – Nick Alexeev Oct 13 '12 at 18:36
7
\$\begingroup\$

I can't put code in a comment, hence an answer. My 'framework' for simple embedded systems is the main loop with polling. To minimize current consumption the main loop can wait let's say 50 ms in idle mode. I don't know which uC you use, I am familiar with PICs, which can awake from a sleep by an interrupt.

 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;
    }
 }
\$\endgroup\$
  • \$\begingroup\$ This is very elegant! The one slight weakness I see is that in your approach, you'd wake up the microcontroller 20 times, so possibly a bit more power consumption (whereas in mine, the uC wakes up just 2 or 3 times). I'd be interested in your comment about that aspect, because everything else I like about this main-loop-focus. \$\endgroup\$ – boardbite Oct 13 '12 at 19:53
  • \$\begingroup\$ (But I suppose it is very little current draw, given that you are mostly running just two instructions on each wakeup.) \$\endgroup\$ – boardbite Oct 13 '12 at 20:03
  • \$\begingroup\$ One of the basic rules of informatics is "don't optimize unless you know that you have a bottleneck". Don't geuss, calculate or measure. I think the active sleep ratio of my approach is less than 1:1000 (maybe much much less, but that is a less safe guess). What is the ratio of active versus sleep current for your uC? If it is much more than 1:1000 you won't notice the difference. \$\endgroup\$ – Wouter van Ooijen Oct 13 '12 at 20:29
  • \$\begingroup\$ It is an Atmega1280, and the active vs sleep current is indeed around the 1000:1 ratio. I am going to try this approach out and update my Question based on the result. \$\endgroup\$ – boardbite Oct 13 '12 at 20:33
  • 1
    \$\begingroup\$ Definitely think this is the way to go. The one improvement I can think of would be to initially wake from sleep on detecting the first button press, then switch to waking up by timer. After the button press has been fully decoded, switch back to wake on button. \$\endgroup\$ – Rocketmagnet Oct 13 '12 at 21:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.