Sondeo de varios botones de una interrupción

1

Estoy intentando sondear 3 botones diferentes de una interrupción de temporizador de vigilancia, en un ATtiny13.

Mi código funciona perfectamente para botones individuales, sin embargo, parece que no puedo sondear los 3 en un bucle. Todos los botones están asociados a un número arbitrario para identificación en 0x01,0x02 y 0x04.

Por ejemplo, este código sondea el botón 0x02 y funciona bien:

ISR (WDT_vect){
if (debounce(0x02)==1){
    PORTB ^= _BV(PB1);//flip led 1
}}

Sin embargo, si intento sondear los 3 en un bucle, no se produce ninguna detección. En este ejemplo, simplemente alterno el mismo led para los 3 botones:

ISR (WDT_vect){     
    for (int d=0x01;d<0x04;d<<1){
    if (debounce(d)==1){
        PORTB ^= _BV(PB1);//flip led 1
    }}}

Apilado si-si no funciona tampoco:

ISR (WDT_vect){

if (debounce(0x02)==1){
    PORTB ^= _BV(PB1);//flip led 1
} else  if (debounce(0x04)==1){
    PORTB ^= _BV(PB3);//flip led 2
}
}

El resto del código relevante abreviado para mayor claridad:

/***
 * curbtn: one of 0x01,0x02,0x04, matches mute, vol+,vol-
 * returns 1 if the button is considered pressed
 */
uint8_t debounce(uint8_t  curbtn){
static uint8_t button_history = 0;
uint8_t pressed = 0;
button_history = button_history << 1;
button_history |= read_btn(curbtn);
if ((button_history & 0b11000111) == 0b00000111) {
    pressed = 1;
    button_history = 0b11111111;
}
return pressed;
}
/**
 * sets up ports to read a specific button
 * returns 1 if the selected button is pressed
 */
uint8_t read_btn(uint8_t  curbtn){
uint8_t ret=0x00;
if (curbtn==0x01){
    DDRB &=~_BV(PB2);//PB2 en entree
    PORTB |=_BV(PB2);//pull-up actif
    ret= ( (PINB & _BV(PB2)) == 0 );
} else if (curbtn==0x02){
    DDRB |=_BV(PB2);//PB2 en sortie
    PORTB &=~_BV(PB2);//PB2 a 0
    DDRB &=~_BV(PB0);//PB0 en entree
    PORTB |=_BV(PB0);//pull up sur PB0
    ret= ( (PINB & _BV(PB0)) == 0 );
} else if (curbtn==0x04){
    DDRB |=_BV(PB0);//PB0 en sortie
    PORTB &=~_BV(PB0);//PB0 a 0
    DDRB &=~_BV(PB4);//PB4 en entree
    PORTB |=_BV(PB4);//pull up sur PB4
    ret= ((PINB & (1<<PB4)) == 0);//lecture de PB0
}
return ret;
}

Este es el diseño del circuito:

Me gustaría saber si voy en la dirección correcta y de qué manera debería corregirse mi código de sondeo.

    
pregunta Polyphil

2 respuestas

1

Hizo muchos progresos, hubo 2 problemas.

Según lo sugerido por Martin y kkrambo, estaba el problema de rastrear correctamente el estado de los 3 botones. El código que publiqué mantenía una variable histórica estática. Ahora el código principal incluye 3 variables de historial diferentes.

uint8_t mute_history=0;
uint8_t volp_history=0;
uint8_t volm_history=0;

uint8_t read_btn(uint8_t  curbtn){
uint8_t ret=0x00;
if (curbtn==0x01){
    DDRB &=~_BV(PB2);//PB2 en entree
    PORTB |=_BV(PB2);//pull-up actif
    nop();nop();nop();nop();
    ret= ( (PINB & _BV(PB2)) == 0 );
} else if (curbtn==0x02){
    DDRB |=_BV(PB2);//PB2 en sortie
    PORTB &=~_BV(PB2);//PB2 a 0
    DDRB &=~_BV(PB0);//PB0 en entree
    PORTB |=_BV(PB0);//pull up sur PB0
    nop();nop();nop();nop();
    ret= ( (PINB & _BV(PB0)) == 0 );
} else if (curbtn==0x04){
    DDRB |=_BV(PB0);//PB0 en sortie
    PORTB &=~_BV(PB0);//PB0 a 0
    DDRB &=~_BV(PB4);//PB4 en entree
    PORTB |=_BV(PB4);//pull up sur PB4
    nop();nop();nop();nop();
    ret= ((PINB & (1<<PB4)) == 0);//lecture de PB0
}
return ret;
}

uint8_t debounce(uint8_t  *button_history,uint8_t  curbtn){
uint8_t pressed = 0;
*button_history = *button_history << 1;
*button_history |= read_btn(curbtn);
if ((*button_history & 0b11000111) == 0b00000111) {
    pressed=1;
    *button_history = 0b11111111;
}
return pressed;
}


ISR (WDT_vect){

if (debounce(&volp_history,0x02)==1){
    PORTB ^= _BV(PB3);//flip led 2
}

if (debounce(&mute_history,0x01)==1){
    PORTB &= ~_BV(PB1);//turn off
    PORTB &= ~_BV(PB3);//turn off
}

if (debounce(&volm_history,0x04)==1){
    PORTB ^= _BV(PB1);//flip led 2
}

}

El segundo problema fue cronometrado, que me sugirió el usuario Tom Carpenter en este hilo . Sus comentarios fueron:

  

@Polyphil intenta agregar algunas instrucciones nop. Puede usar lo siguiente: #define nop () __asm__ __volatile__ ("nop \ n \ t"), y luego en su código do nop (); nop (); nop (); justo antes de hacer la declaración de retorno.

     

@Polyphil las entradas en el ATTiny tienen una latencia de dos ciclos de reloj debido a una cadena de sincronización, por lo que se requieren al menos 2-3 ciclos de reloj después de cambiar el valor de pull-up antes de que se refleje en el registro PIN. Agregar un nop hace que el procesador espere un ciclo de reloj.

por lo tanto, los nops ().

El circuito se comporta como se espera en este momento.

    
respondido por el Polyphil
0

¿Con qué frecuencia llama a la WDT? ¿Por qué no elegir un preclasificador de WDT para que no tenga que rebotar en absoluto? Si solo verifica los botones cada (digamos) 50-100 ms, entonces deben rebotar y su código puede ser tan simple como simplemente verificar cada uno de los botones de forma secuencial y actuar en función del estado.

    
respondido por el bigjosh

Lea otras preguntas en las etiquetas