¿Cómo es este código Pushbutton Debounce?

Estoy monitoreando un botón pulsador con un microcontrolador donde estoy tratando de encargarme de cuatro cosas juntas:

  1. Rebote de 50 mseg al presionar el inicio y rebote de 25 mseg al presionar la liberación
  2. identificar una pulsación corta, definida como cuando se suelta el botón en menos de 1 segundo
  3. identificar una retención prolongada, definida como cuando pasa 1 segundo desde que se presionó el botón de inicio
  4. dormir tanto como sea posible cuando no esté haciendo nada más

A continuación se muestra un breve pseudocódigo de lo que he implementado hasta ahora. Creo que cubre todos estos casos.

¿Ve alguna posible mejora o problema potencial? (Por ejemplo, estoy interesado en cualquier caso sutil que pueda ser un punto ciego para mi enfoque).

Pseudocódigo:

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
} 
Personalmente evito interrumpir las rutinas de servicio siempre que puedo. Configuraría una interrupción de 50 ms y haría todo el procesamiento en el principal, después de la suspensión. 25 ms: ¿por qué no 50 ms? Ningún usuario lo notará.
@WoutervanOoijen: Si evito el uso de interrupciones, ¿cómo puedo detectar un evento de botón en medio del sueño? ¿O quiere decir que debería minimizar el código real dentro de la ISR? [Y el 25 vs 50 ms fue arbitrario; Solo lo usé para que sea más fácil para el lector distinguir entre el temporizador de rebote al comenzar y el temporizador de rebote al soltar]
@Inga re: how can I detect a pushbutton event amidst sleep?Puede usar solo una (1) interrupción: Falling-Interrupt. Despiértate con esta interrupción, haz el resto del rebote en el ciclo principal. Vuelva a dormir, si el código determina que la presión del botón no fue "real".

Respuestas (1)

No puedo poner código en un comentario, por lo tanto, una respuesta. Mi 'marco' para sistemas integrados simples es el bucle principal con sondeo. Para minimizar el consumo de corriente, el bucle principal puede esperar, digamos, 50 ms en modo inactivo. No sé qué uC usa, estoy familiarizado con los PIC, que pueden despertarse de un sueño por una interrupción.

 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;
    }
 }
¡Esto es muy elegante! La única debilidad leve que veo es que en su enfoque, despertaría el microcontrolador 20 veces, por lo que posiblemente un poco más de consumo de energía (mientras que en el mío, el uC se activa solo 2 o 3 veces). Me interesaría tu comentario sobre ese aspecto, porque todo lo demás me gusta de este enfoque de bucle principal.
(Pero supongo que es un consumo de corriente muy pequeño, dado que en su mayoría está ejecutando solo dos instrucciones en cada activación).
Una de las reglas básicas de la informática es "no optimice a menos que sepa que tiene un cuello de botella". No adivine, calcule o mida. Creo que la relación de sueño activo de mi enfoque es inferior a 1:1000 (tal vez mucho menos, pero esa es una suposición menos segura). ¿Cuál es la relación entre la corriente activa y la corriente de reposo para su uC? Si es mucho más de 1:1000, no notará la diferencia.
Es un Atmega1280, y la corriente activa frente a la de reposo es de hecho alrededor de la relación 1000:1. Voy a probar este enfoque y actualizar mi pregunta en función del resultado.
Definitivamente creo que este es el camino a seguir. La única mejora en la que puedo pensar sería despertarme inicialmente del modo de suspensión al detectar la primera pulsación del botón, y luego cambiar a despertarme con un temporizador. Después de que la pulsación del botón se haya decodificado por completo, vuelva a activar el botón.
@rocketmagnet: ¿por qué? para ahorrar unos pocos% de energía en relación con una solución más fácil?
@WoutervanOoijen - Sí, eso es todo. No es una gran mejora, pero ese era el punto. No hay muchas mejoras que se pueden hacer en su sugerencia.