¿Cómo puedo hacer un pestillo en AVR sin interrupciones?

1

Quiero hacer un circuito simple para que al presionar un botón, un LED se ilumine y permanezca encendido hasta que se vuelva a presionar el botón. Todavía no he profundizado en interrupciones en AVR, y me preguntaba si es posible hacerlo en un bucle while .

Este es mi código:

#include <avr/io.h>

#define INPUT (1 << PINB3)
#define OUTPUT (1 << PINB0)

int main(void)
{
    DDRB = 0x00;        //everything input
    PORTB = 0x00;       //no pud

    DDRB |= 0x1;        //PB0 is an output
    PORTB = 0x00;       //PB0 is low and PUD is off

    uint8_t prev = PINB;

    while(1)
    {
        if ((prev & INPUT) < (PINB & INPUT)) {      //rise edge
            if ((PORTB & OUTPUT) == 0x0) {      //if output was low
                PORTB |= OUTPUT;        //make output high
            } else {
                PORTB &= ~OUTPUT;       //make output low
            }
        }
        prev = PINB;
    }
}

El interruptor solo funciona parcialmente, supongo que debido a la naturaleza del bucle while y que almacena la entrada anterior muy rápido.

Creo que el problema radica en mi método para detectar un flanco ascendente del botón pulsador. ¿Cuál sería un método mejor?

    
pregunta tgun926

3 respuestas

0

No estoy satisfecho con las respuestas, ya que no proporcionan los requisitos de lo que quiero.

El funcionamiento de un pestillo según la pregunta es algo como esto:

Estoimplicaqueelbotónpulsadorpuedemantenersepresionadoindefinidamente,perolaluzdebepermanecerenelmismoestado.

El problema que encontré radica en la forma en que se detectó un flanco ascendente. En mi código inicial, se tomó una muestra del valor de pin anterior y, casi inmediatamente después, se tomó una muestra del valor actual. La comparación para el flanco de subida requiere dos cálculos aritméticos y una comparación, y el valor de la entrada puede cambiar entre.

Al muestrear los valores actuales y anteriores de PIN por separado, obtuve una tasa de éxito de ~ 90%:

while (1) {
    cur = PINB;     //sample current input

    if ((prev & (1 << PINB3)) < (cur & (1 << PINB3))) {     //rise edge
        PINB |= (1 << PINB0);       //toggle led
    }   
    prev = PINB;    //sample previous input
}

Al agregar un breve retraso de 10 ms entre el muestreo de las entradas, se logró una tasa de éxito de ~ 100%:

#define F_CPU    1000000UL // Crystal frequency.
#define WAIT_MS  10 // Time in ms

#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
    DDRB = (1 << PINB0); // PINB0 is output, others input.

    uint8_t cur = PINB;
    uint8_t prev = PINB;

    while (1) {
        cur = PINB;     //sample current input

        if ((prev & (1 << PINB3)) < (cur & (1 << PINB3))) {     //rise edge
            PINB |= (1 << PINB0);       //toggle led
        }   
        prev = PINB;
        _delay_ms(WAIT_MS); // Wait.
    }
}
    
respondido por el tgun926
1

Su problema podría estar rebotando. Cuando presionas o sueltas un botón, este rebota. (ver foto). Esto se debe a los rebotes mecánicos de los contactos eléctricos dentro del propio botón.

Tienesquetenerestoencuentayrechazarlosrebotes.Estosepuedehacerenelsoftware.Existeunagrancantidaddemétodos,usandotemporizador,interrupciones,ciclosdeespera,etc.Busqueenlawebyelijalaimplementaciónquemejorseadapteasusistema.

Tambiénpuedesconsultaraquí: debouncing-buttons

    
respondido por el Blup1980
1

Debe habilitar la resistencia de pull-up para la entrada de su interruptor. Luego conecte un lado del interruptor a su entrada AVR, y el otro lado a la GND.

El otro problema, señaló Blup1980 es el rebote, que puede ser omitido en el software.

Tal vez algo como esto:

#include <util/delay.h>

#define F_CPU    1000000UL // Your crystal frequency.
#define WAIT_MS  250 // Time in ms to wait for debounce.

int main(void)
{
    DDRB = (1 << PINB0); // PINB0 is output, others input.
    PORTB = (1 << PINB3); // Enable pull-up resistor on PINB3.

    while (1) {
        // Check if PINB3 is low (switch closed).
        if (!(PINB & (1 << PINB3))) {
            PORTB ^= (1 << PINB0); // Invert PINB0 status (XOR).
            _delay_ms(WAIT_MS); // Wait for debounce. 
        }
    }
}

Como ha dicho, desea hacer esto sin interrupciones, la única forma de evitar el cambio de conmutador en el software es _delay_ms () la ejecución del código durante un período de tiempo determinado. Tendré que experimentar con WAIT_MS ).

Otra solución sería establecer una bandera al invertir el estado de PINB3 y usar un temporizador para esperar un tiempo, luego generar una interrupción, que borraría esa bandera.

Nota: si utiliza el enfoque de temporizador, la variable (marca) debería definirse como volátil .

    
respondido por el Golaž

Lea otras preguntas en las etiquetas