AVR-GCC. Button bounce, “χοροπήδημα διακόπτη” (sic).

Όλες οι μηχανικές επαφές, είτε πρόκειται για διακόπτες, μπουτόν, ρελέ όταν κλείνουν έχουν την τάση να εμφανίζουν μία ταλάντωση στην έξοδο τους πριν φτάσουν σε ηρεμία (από εδώ και στο εξής bounce). Στην ουσία η έξοδος “χοροπηδά” από ανοικτός σε κλειστός μερικές φορές. Όταν ο διακόπτης συνδέεται σε έναν μικροελεγκτή ο μικροελεγκτής αντιλαμβάνεται το φαινόμενο αυτό σαν πολλαπλούς παλμούς και πράττει ανάλογα. Όταν γράφουμε λοιπόν κάποιο πρόγραμμα πρέπει να λάβουμε υπόψιν το φαινόμενο αυτό. Παρακάτω βλέπουμε την έξοδο ενός διακόπτη την ώρα που κλείνει σε έναν παλμογράφο. Δείτε πόσες άστατη είναι η έξοδος.

Ας δούμε το παρακάτω παράδειγμα κώδικα που το μόνο που κάνει είναι να αλλάζει την κατάσταση ενός led κάθε φορά που πατάμε ένα μπουτόν.

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


/*Η παρακάτω δήλωση ελέγχει αν στον compiler έχουμε βάλει την παράμετρο F_CPU.
  Αν όχι, την περνά. */

#ifndef F_CPU
    #warning "H F_CPU den exei oristei 8a parei thn timh 4000000"
    #define F_CPU 4000000UL  //  Use unsigned long
#endif

#define LOW  0
#define HIGH 1

#define FALSE 0
#define TRUE  1

#define LED    PB1
#define BUTTON PB0




uint8_t button_is_pressed(void);
void toggle_led(void);

int main(void){

    uint8_t lastButton = LOW;
    uint8_t led_on = FALSE;

    DDRB |= (1<<LED);     // PB1 (LED) ως έξοδος.

    DDRB &= ~(1<<BUTTON); // PB0 (BUTTON) ως είσοδος.
    PORTB |= (1<<BUTTON); // PB0 ενεργοποίηση Pull-Up αντίστασης.


    while(1){

        if (bit_is_set(PINB,BUTTON)>>BUTTON == HIGH && lastButton == LOW){

            led_on = !led_on;
            lastButton = HIGH;
        }  else  {

            lastButton = bit_is_set(PINB,BUTTON)>>BUTTON;
        };

        if (led_on) {
            PORTB |= (1<<LED);
        } else {
            PORTB &= ~(1<<LED);
        };


    };

    return 0;
}


Αν προσέξατε στο παραπάνω βίντεο η συμπεριφορά του συστήματος δεν είναι σωστή. Ο μικροελεγκτής φαίνεται να χάνει αρκετά πατήματα του μπουτόν. Αυτό ακριβώς οφείλεται στο bounce του μπουτόν.

Ας δοκιμάσουμε τώρα μία παραλλαγή του κώδικα που κάνει debounce το μπουτόν.

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


/*Η παρακάτω δήλωση ελέγχει αν στον compiler έχουμε βάλει την παράμετρο F_CPU.
  Αν όχι, την περνά. */

#ifndef F_CPU
    #warning "H F_CPU den exei oristei 8a parei thn timh 4000000"
    #define F_CPU 4000000UL  //  Use unsigned long
#endif

#define LOW  0
#define HIGH 1

#define FALSE 0
#define TRUE  1

#define LED    PB1
#define BUTTON PB0




uint8_t debounce(uint8_t);


int main(void){

    uint8_t lastButton = LOW;
    uint8_t led_on = FALSE;

    uint8_t currentButton = LOW;

    DDRB |= (1<<LED);     // PB1 (LED) ως έξοδος.

    DDRB &= ~(1<<BUTTON); // PB0 (BUTTON) ως είσοδος.
    PORTB |= (1<<BUTTON); // PB0 ενεργοποίηση Pull-Up αντίστασης.


    while(1){

        currentButton = debounce(lastButton);

        if (lastButton == LOW && currentButton == HIGH) {
            led_on = !led_on;
        };

        lastButton = currentButton;

        if (led_on) {
            PORTB |= (1<<LED);
        } else {
            PORTB &= ~(1<<LED);
        };


    };

    return 0;
}

/* Η παρακάτω συνάρτηση ελέγχει την κατάσταση του button
   διαδοχικά αφήνοντας να περάσουν 25 msec. Χρόνος που
   επιτρέπει στην έξοδο του button να ηρεμήσει.
*/
uint8_t debounce(uint8_t last){

    uint8_t current = bit_is_set(PINB,BUTTON)>>BUTTON;

    if (last != current){
    _delay_ms(25);
    current = bit_is_set(PINB,BUTTON)>>BUTTON;
  };

  return current;
}
 

Βλέπουμε πως η συμπεριφορά του συστήματος βελτιώθηκε πάρα πολύ. Αυτό που κάναμε στην ουσία είναι να εισάγουμε μία μικρή καθυστέρηση ώστε να προλαβαίνει να “ηρεμήσει” η έξοδος του μπουτόν. Η μέθοδος αυτή δεν είναι και τόσο αποδοτική. Μία καλύτερη μέθοδος θα ήταν να γίνεται ο έλεγχος του πλήκτρου μέσω κάποιου timer interrupt.

Υπάρχουν πολύ μέθοδοι για το debounce ενός μηχανικού διακόπτη. Υπάρχουν και μέθοδοι που δεν απαιτούν παρέμβαση στον κώδικα. Με την βοήθεια κυκλώματος (π.χ. με ένα R-C φίλτρο). Υπάρχουν ακόμη και ειδικά chip όπως το MAX6816 και το MC14490.

Advertisements
This entry was posted in AVR, Electronics and tagged , , , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s