Software Debouncing para detectar si se presionó el interruptor durante T segundos

6

Necesito detectar si se presionó un interruptor durante más de un tiempo establecido, sin usar ningún registro de temporizador en mi código incrustado.

Estoy usando el software debouncing de la siguiente manera para detectar un interruptor normal presionando.

int buttonPress (void)
{
  static int downCount= 0; // static = value isn't lost between calls.

  int currentButton = ((IO0PIN & 0x00000200) == 0x00000200); 

  if (currentButton ) { // button is down

    if (downCount == 5) { // button has been down for 5 counts
      downCount++;      // increase the count so we don't trigger next time
      return 1;
    } else {              // some count other than 5
      if (downCount < 5)  // increase if it's less than 5
        downCount++;
    }
  } else   {    // button is up
    downCount = 0; 
  }
  return 0;
}   

Sin embargo, me gustaría detectar si este botón se ha presionado durante 5 segundos o más. El problema al que me enfrento es que parece que no puedo contar hasta 300,000 y, si no se alcanza, recurra al downCount original de 5 para marcar como una pulsación normal del botón.

(Dado que la velocidad de reloj de mi microcontrolador es de 60 MHz, tardará 5 segundos en llegar a 300,000).

He jugado un poco con la lógica de la siguiente manera, sin embargo, en el caso que se muestra a continuación, debido a la introducción de la declaración while , cuento hasta 300,000 independientemente de cuánto tiempo se ha presionado el botón.

if (currentButton ) {   // button is down

  if (downCount == 5) { // button has been down for 5 counts
    downCount++;      // increase the count so we don't trigger next time
    while (downCount > 5) { 
      if (debounce == 300000) {
        flag_5SEC = 1;
      } else if (debounce < 300000) {
         debounce++;
      }
    }
    return 1;
  } else {              // some count other than 5
    if (downCount < 5)  // increase if it's less than 5
      downCount++;
  }
}

¿Algún consejo o sugerencia sobre cómo realizar esta tarea?

Nota : quiero detectar un interruptor más largo presionando explícitamente en el código de rebote anterior, ya que hacer cualquier otra cosa puede tener efectos secundarios adversos en el resto del código, que otra persona había previamente escribió.

He logrado completar la tarea mencionada anteriormente utilizando temporizadores, sin embargo, desde entonces, he usado todos los registros de temporizadores (y resistencias de coincidencia disponibles) para tareas más importantes dentro del mismo proyecto.

No necesito detectar un retraso de tiempo preciso, simplemente si el botón ha estado inactivo durante más de, por ejemplo, 2-3 segundos.

El comentario de @DiBosco aclara aún más:

  

Entonces, lo que estás diciendo es que si presionas un interruptor, por ejemplo, veinte cuentas y se libera, actúa como una presión normal que hace una función, y si se presiona y se mantiene presionada durante dos segundos aproximadamente. realiza una función diferente ...

    
pregunta Rrz0

4 respuestas

15
  

He logrado completar la tarea mencionada anteriormente utilizando temporizadores, sin embargo, desde entonces, he usado todos los registros de temporizadores (y resistencias de coincidencia disponibles) para tareas más importantes dentro del mismo proyecto.

En ese caso, estás haciendo todo esto mal. A menos que necesite absolutamente las funciones de alta resolución, baja fluctuación u otras características de hardware de los temporizadores de hardware, debe hacer su sincronización en el software, utilizando uno de los temporizadores de hardware para generar interrupciones periódicas continuas que puede contar en el ISR. p>

Una "interrupción de latido" con un período de entre 100 µs y 100 ms (o más) es una característica común de los sistemas integrados. Esto le permite crear un número arbitrario de temporizadores de software.

Incluso si ya está utilizando todos los temporizadores de hardware, siempre que al menos uno de ellos esté haciendo algo periódico, como generar una señal PWM o crear un reloj para una interfaz en serie, puede habilitar interrupciones en él. y úsalos para tu base de tiempo de software. Es posible que el período no sea un valor "conveniente", pero ese es un simple problema de escala que el software puede resolver.

    
respondido por el Dave Tweed
6

Su código parece estar un poco apagado, y es difícil de entender realmente ya que no está claro dónde se encuentra ese bucle con respecto a la interrupción o lo que sea que establezca la función de Cuenta Descendente.

También parece que estás incrementando el contador de rebotes en dos lugares en el bucle while, lo que simplemente parece incorrecto.

Su solución en línea es problemática por muchas razones, entre las cuales su tiempo variará dependiendo de lo que suceda en el micro, a menos que se detenga y realice un ciclo cerrado mientras cuenta. Pero detenerse para contar durante cinco segundos también es una mala idea.

Este tipo de función normalmente se maneja en micros mediante el uso de un contador de tics también conocido como latido del corazón. Básicamente, es una práctica común reservar un temporizador para que actúe como un reloj para su código. El temporizador generaría una interrupción regular, tal vez 10 veces por segundo.

El manejador de interrupciones puede ocuparse de numerosas funciones de mantenimiento, como el desmontaje de interruptores, el parpadeo de los LED, la verificación de los tiempos de espera de las comunicaciones, etc. También puede incluir el conteo de cuánto tiempo se ha presionado un botón. Cuando se ha presionado durante un período de tiempo adecuado, dicho controlador llama al controlador adecuado para ese evento.

Este método le permite usar un solo temporizador para numerosas funciones.

    
respondido por el Trevor_G
4

Dado que los botones son operados por humanos muy lentos, en relación con la velocidad del procesador, puede salirse con un sondeo lento. Cada 10 milisegundos por ejemplo.
Puede crear fácilmente una función para manejar muchos botones, con muchos patrones de presión y acciones de retención.

Una versión muy simple de lo que uso a menudo se ve así. Solo tiene un indicador de espera de 5 segundos, ninguna de las prensas cuenta y el reconocimiento de indicadores (los indicadores se establecerán inmediatamente después de borrar nuevamente). Pero con un poco de creatividad puedes agregar muchas cosas de esta manera. Pero se volverá un poco largo.
Incluso puede agregar canales de entrada a la máquina y hacer que sean denunciados de esta manera.

#define BUTTON_INTERVAL 10
#define BUTTON_COUNT    1

/* Typedef for a button */
typedef struct button_s {
    void* port;         /* Use GPIO port type, whatever your platform uses, maybe even via bitbanding */
    uint32_t hi_time;   /* Time high */
    uint8_t pin;        /* Pin number */
    uint8_t flags;      /* Output flags */
} button_t;

/* The array of all buttons */
button_t buttons[BUTTON_COUNT];

/* Handle button polling */
void buttonHandler(void){
    uint8_t i;
    for(i=0;i<NR_OF_BUTTONS; i++){
        button_t *b = buttons[i];
        uint8_t state = *b->port & (1<<b->pin);
        if(state){
            // Saturating unsigned 32-bit addition 
            // your cpu might have an instruction for this eg: arm __uqadd(a,b))
            if(b->hi_time < 0xFFFFFFFF) b->hi_time += BUTTON_INTERVAL;
        }else{
            b->hi_time = 0;
        }
        // One of many possible flags comparisons:
        if( b->hi_time >= 5000 ){
            b->flags |= BUTTON_HI_5S;
        }
    }
}

/* Example of flag usage */
void waitForButton(){
    if( button[0]->flags & BUTTON_HI_5S ){
        button[0]->flags &= ~BUTTON_HI_5S;
        // ..stuff..
    }
}

Los puntos clave son, si vas por tu cuenta:
- Una estructura para todos los datos, incluso puede reasignar claves sobre la marcha si lo desea.
- Botones infinitos utilizando estructuras. ¡Nunca vuelvas a escribir este código!
- Saturando los temporizadores, no se desbordan.
- Banderas, fácilmente sustituibles por llamadas al sistema operativo.

    
respondido por el Jeroen3
3

Si estoy entendiendo tus necesidades correctamente, quieres algo como esto:

void SwitchCheck(void)
{   
    DebounceTimer = false; // If using timer flag
    if (currentButton) 
        {
        debounce++; 
        }
    else 
        {
        if (debounce > NORMAL_OPERATION)
              {
              // Do normal stuff here
              }
        debounce = 0;
        } 
    if (debounce == MAX_TIMEOUT) 
         {
         // Do your held down thing here
         }
}

Llama a esto desde tu bucle principal. Lo ideal es que solo lo llame después de haber configurado un bit en una interrupción del temporizador correctamente, para que tenga una sincronización precisa.

A lo largo de los años, he hecho muchas, muchas veces a lo largo de los años, cambiar el rebote sin temporizadores (aunque en realidad no me gusta esto), solo hago llamadas desde el ciclo principal y funciona. Sin embargo, si está buscando precisión en su tiempo de cinco segundos, esto no será lo suficientemente bueno.

Edición adicional:

En tu temporizador ISR:

TimerISR()
{
DebounceTimer = true;
// Other stuff    
}

En main:

if (DebounceTimer)
    SwitchCheck();

O simplemente llámelo desde la pantalla principal sin la marca de verificación si no está usando un temporizador y experimente empíricamente con MAX_TIMEOUT.

    
respondido por el DiBosco

Lea otras preguntas en las etiquetas