1 votos

Sondeo de varios botones desde una interrupción

Estoy tratando de sondear 3 botones diferentes de un watchdog tiempo de interrupción, en un ATtiny13.

Mi código funciona perfectamente para los botones individuales, sin embargo parece que no puedo ser capaz de sondear los 3 en un bucle. Todos los botones están asociados a un número arbitrario para su 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 parece producirse ninguna detección. En este ejemplo simplemente conmuto 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
    }}}

Los if-else apilados tampoco funcionan:

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 pertinente 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 esquema del circuito: enter image description here

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

1voto

user141447 Puntos 88

Hice muchos progresos, hubo 2 problemas.

Como sugirieron Martin y kkrambo, existía el problema de seguir correctamente el estado de los 3 botones. El código que publiqué mantenía una variable de historia estática. Ahora el código principal incluye 3 variables de historia 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 era de sincronización, que me sugirió el usuario Tom Carpenter en este hilo . Sus comentarios fueron:

@Polyphil intenta añadir algunas instrucciones nop. Puedes usar lo siguiente: #define nop() __asm__ __volatile__ ("nop \n\t "), y luego en tu código haz nop();nop();nop(); justo antes de hacer la sentencia return.

@Polyphil las entradas en el ATTiny tienen una latencia de dos ciclos de reloj debido a una cadena sincronizadora, por lo que se tarda 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. Añadir un nop hace que el procesador espere un ciclo de reloj.

de ahí el nops().

De momento, el circuito se comporta como se espera.

0voto

chews Puntos 1507

¿Con qué frecuencia llama al WDT? ¿Por qué no elegir un WDT prescaller de modo que usted no necesita a debounce en absoluto? Si sólo compruebas los botones cada (digamos) 50-100ms, entonces deberían rebotar por sí mismos y tu código puede ser tan simple como comprobar cada uno de los botones secuencialmente y actuar sobre el estado.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X