Estoy tratando de sondear 3 botones diferentes desde una interrupción de tiempo de espera 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 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 trato de sondear los 3 en un bucle, parece que 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
}}}
Apilados if-else 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 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 se debe corregir mi código de sondeo.
Progresó mucho, hubo 2 problemas.
Como sugirieron Martin y kkrambo, existía el problema de rastrear correctamente el estado de los 3 botones. El código que publiqué mantuvo una variable de historial 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 el del tiempo, que me sugirió el usuario Tom Carpenter en este hilo . Sus comentarios fueron:
@Polyphil intente agregar algunas instrucciones de nop. Puede usar lo siguiente: #define nop() __asm__ __volatile__ ("nop \n\t"), y luego en su código haga nop();nop();nop(); justo antes de hacer la declaración de devolución.
@Polyphil, las entradas en ATTiny tienen una latencia de dos ciclos de reloj debido a una cadena sincronizadora, por lo que se necesitan 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.
de ahí los nops().
El circuito se comporta como se esperaba en este momento.
¿Con qué frecuencia llama al WDT? ¿Por qué no elegir un precaller WDT para no tener que eliminar rebotes en absoluto? Si solo verifica los botones cada (digamos) 50-100 ms, entonces deberían rebotar y su código puede ser tan simple como simplemente verificar cada uno de los botones secuencialmente y actuar sobre el estado.
gran josh
gran josh
Polífilo
krambo
debounce()
un seguimiento del estado del botón de una invocación a la siguiente? Si es así, ¿puede rastrear múltiples botones diferentes o solo un botón?Polífilo
button_history_xx
para cada botón.Martín
Lundin
jimmyb
Polífilo
Lundin
jimmyb