Leggere un pulsante su GPIO con antirimbalzo software

Da raspibo.
Jump to navigation Jump to search

Come si collega un pulsante

Un circuito per collegare un pulsante e' il seguente:

Pulsante.png

Quando il pulsante e' aperto, il circuito e' chiuso verso massa attraverso le sue resistenze. Quando il pulsante e' chiuso la corrente fluisce attraverso la resitenza da 10Kohm e porta l'estremo collegato alla resistenza da 1Kohm al potenziale di 3.3V. La resistenza da 1K serve solo per limitare la corrente trasperita al pin di input (non e' necessaria ma e' una piccola protezione da incidenti di collegamento).

Purtroppo i contatti fisici non sono mai perfetti e spesso ogni pressione del pulsante viene ricevuta dalla CPU come un treno di impulsi prima che lo stato diventi stabile.

in Python

In Raspian c'e' il pacchetto python-rpi.gpio che consente di accedere a GPIO in modo semplice dal linguaggio Python. Potete installare RPI.gpio con in seguente comando:

$ sudo apt-get install python-rpi.gpio python3-rpi.gpio

A partire dalla versione 0.5 supporta anche la gestione degli interrupt.

Iniziamo con ordine. Per leggere semplicemente l'input potremmo scrivere un programma simile: (Il pulsante e' collegato a GPIO23, pin 16).

#!/usr/bin/env python

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)

GPIO.setup(23, GPIO.IN)

value = GPIO.input(23)

print value

GPIO.cleanup()

Cosi' si puo' vedere se il pulsante e' premuto o rilasciato.

Di solito pero' si vuole compiere un'azione quando il pulsante viene premuto. E' in teoria possibile fare un ciclo continuo e continuare a leggere il valore del pulsante. Quando si rileva che il valore e' cambiato si compie l'azione corrispondente. Questo approccio e' molto inefficiente perche' la CPU continua a lavorare per non fare nulla, per attendere che avvenga qualcosa (si chiama busy wait).

Il programma che segue e' un esempio di pulsante passo-passo realizzato via software. Per fare l'esperimento collegate il pulsante al GPIO23/pin16 e un led con la resistenza di limitazione a GPIO22/pin15.

#!/usr/bin/env python3

import RPi.GPIO as GPIO
import threading
import time

sem = threading.Semaphore(0)

time23 = 0

def cb23():
        global time23
        time23 = time.time()
        sem.release()

GPIO.setmode(GPIO.BCM)

GPIO.setup(23, GPIO.IN)
GPIO.setup(22, GPIO.OUT, initial=GPIO.LOW)

GPIO.add_event_detect(23, GPIO.BOTH, callback=cb23)

value23 = 0
timeout = None
value22 = 0
while True:
        try:
                new=sem.acquire(timeout=timeout)
        except:
                break

        now = time.time()
        if now - time23 > 0.05:
                tmpvalue23 = GPIO.input(23)
                if tmpvalue23 != value23:
                        value23 = tmpvalue23
                        if value23 == 1:
                                value22 = 1 - value22
                                if value22 == 1:
                                        GPIO.output(22, GPIO.HIGH)
                                else:
                                        GPIO.output(22, GPIO.LOW)

        if new:
                timeout = 0.05
        else:
                timeout = None

print("cleanup")
GPIO.cleanup()

E' un programma un po' piu' complesso, quindi lo spiego. La funzione cb23 verra' richiamata ogni volta che il sistema rivela un fronte di salita (da 0 a 1) o di discesa (da 1 a 0) nel segnale del pulsante. Questa funzione semplicemente annota il tempo dell'evento in time23 e risveglia il programma principale con la chiamata sem.release().

Il programma principale definisce GPIO23 come input e GPIO22 come output poi definisce cb23 come funzione da richiamare ogni qual volta avviene una variazione nel valore di GPIO23.

Nel ciclo principale si attende un evento (acquire) o lo scadere di un timeout, inizialmente infinito.

Quando la acquire esce new e' vero se e' arrivato un evento, falso se e' uscito per timeout.

Se lo stato del gpio23 e' stabile da piu' di 50ms si analizza se ha assunto un nuovo valore e si agisce di conseguenza (in questo caso ogni volta che diventa 1 si inverte il valire di value22).

Se il loop degli eventi e' stato attivato da un evento si pone il timeout a 50ms, altrimenti se era un timeout, il timeout viene posto a infinito (None).

Poniamo che il valore del pulsante sia 0 e che io prema il pulsante. Al primo impulso viene risvegliato il loop principale ma il valore attuale non viene considerato, pero' viene messo il timeout a 50ms. Per effetto dei contatti fisici imperfetti la funzione cb puo' essere chiamata molte volte (mettete una stampa di controllo se non ci credete!). Tutte le volte verra' risvegliato il loop del programma principale e spostato il timeout a 50ms dall'ultimo impulso. Solo quando per 50ms non succedera' nulla la funzione acquire uscira' per timeout e quindi si puo' andare a vedere quale e' il valore stabile di gpio23.

Questo programma potrebbe anche controllare piu' pulsanti in parallelo. Occorre una funzione cb per ogni pulsante che aggiorni una variabile time associata.

Tutto il blocco "if now - time23 > 0.05:" deve essere replicato per ogni pulsante associando l'azione corrispondente alla pressione.

in C

Si puo' partire ragionando su questo codice che alla base del modulo python:

(non riesco ad inserire un link) code.google.com/p/raspberry-gpio-python/source/browse/source/event_gpio.c