Python exemplarisch - RPi Tutorial
deutsch     english

SCHALTER & BUTTONS, DIGITALEINGANG

 

Alle Programme können von hier heruntergeladen werden.


 

Einführung

 

Schalter und Tasten werden bei Mikroprozessor-Systemen häufig als Eingabegeräte verwendet. Sie können als digitale Sensoren mit zwei Zuständen betrachtet werden. In Abhängigkeit von der konkreten Situation werden diese Zustände off/on, offen/geschlossen, losgelassen/gedrückt, tief/hoch, falsch/ wahr, 0/1, 0V/3.3V, Low/High, usw. genannt. Obwohl Schalter die einfachsten mit dem GPIO verbundenen Sensoren sind, ist der Umgang mit ihnen aus folgenden Gründen nicht immer ganz einfach:

  • Ein GPIO-Eingang sollte nie "undefiniert" sein. Dies geschieht, wenn der Input-Pin "offen", d.h. nicht verbunden ist.
Auf den ersten Blick vermeiden wir dies bei der Verwendung eines Umschalters zwischen GND und VCC.  
Das ist aber keine gute Idee, denn beim Umschalten bleibt der Ausgang eine kurze Zeit offen.
  • Die Lösung besteht darin, einen Widerstand zu verwenden, der den GPIO-Eingang entweder auf logisch 0 oder 1 "zieht". In den meisten Fällen ist sein Wert in der Grössenordnung von 10 kOhm (nicht wesentlich kleiner, um zu vermeiden, dass zu viel Strom fliesst).

    Pullup-Widerstand
    Pulldown-Widerstand
    Sind die linken Kontakte nicht geschlossen, so ist der GPIO-Zustand High
    Sind die rechten Kontakte nicht geschlossen, so ist der GPIO-Zustand Low
  • Es tritt aber noch ein anderes Problem auf: Wenn der bewegte Schalterkontakt den Gegenkontakt berührt, so "prallt" er in der Regel einmal oder mehrmals zurück in den geöffneten Zustand, bis die Kontakte vollständig geschlossen sind. Prellen kann auch auftreten, wenn der Schalter geöffnet wird: Er kann dann ein oder mehrmals in den geschlossenen Zustand "zurückprallen", bis er ganz offen ist.

    Es gibt mehrere Möglichkeiten, die Auswirkungen des Schalterprellens zu eliminieren, beispielsweise unter Verwendung eines Flip-Flops oder durch Deaktivieren der Erfassung der Schalterzustands während kurzer Zeit (10 bis 100 ms).
    (Weitere Informationen zum Schalterprellen findet man hier.)

    Da die meisten elektronischen Schaltungen Pullup-Widerstände verwenden, bevorzugen wir Pullups gegenüber Pulldowns. Der Pullup-/Pulldown-Widerstand kann ein Teil der Eingangsstufe eines Mikroprozessorsystems sein, wie dies beim Raspberry Pi der Fall ist. Hier kann jeder GPIO-Input softwaremässig mit einem Pullup/Pulldown oder ohne Widerstand definiert werden (sogenannter "floating input").

    hand1

    Beachten Sie, dass negative Spannungen und Spannungen über 3.3 V den Raspberry Pi zerstören können. Deshalb sollten Sie nie einen Schalter an die 5V-Versorgung (Pin # 2) anschliessen und sicher stellen, dass kein anderes externes Gerät, das mehr als 3.3V liefert, am GPIO angeschlossen wird.

 

 

Experiment 1: Zustand eines Schalters/Buttons erkennen

 

Ziel:
Einen Drucktaster (Button) an den GPIO anschliessen und seinen Zustand erkennen. Wenn die Taste gedrückt ist, wird eine LED und/oder ein Buzzer eingeschaltet. Wenn die Taste losgelassen wird, wird sie/er ausgeschaltet.

Schaltschema:
Verwenden Sie eine Steckplatine und realisieren die folgende Verkabelung. Achten Sie darauf, dass Sie die 3.3V und 5V DC-Ausgänge (3.3 V an Pin # 1, 5 V an Pin # 2) nicht verwechseln. Sie können den Buzzer weglassen, aber es macht mehr Spass, wenn Sie einen haben.

Programm: [►]

# Button1.py

import RPi.GPIO as GPIO
import time

# Button pin
P_BUTTON = 40 # adapt to your wiring
P_LED = 22  # adapt to your wiring

def setup():
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(P_BUTTON, GPIO.IN, GPIO.PUD_UP)
    GPIO.setup(P_LED, GPIO.OUT)

setup()
while True:
    if GPIO.input(P_BUTTON) == GPIO.LOW:
        GPIO.output(P_LED, GPIO.HIGH)
    else:
        GPIO.output(P_LED, GPIO.LOW)
    time.sleep(0.01)
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

Bemerkungen:
LEDs müssen Sie immer über einen Vorwiderstand anschliessen, da ihr Strom maximal 5-15 mA betragen darf (den maximalen Wert entnehmen Sie dem Datenblatt). Dies ist wie bei einer gewöhnlichen Halbleiter-Diode dann der Fall, wenn ihre Spannung 0.7- 1V beträgt. Verwenden Sie keine "sehr enge" Schleife, da diese unnötig viel Prozessorzeit beansprucht. Hier erlauben wir dem Prozessor mit time.sleep(0.01) jeweils für 10 ms zu "schlafen".

Wegen der Endlosschleife muss das Programm "gewaltsam" abgebrochen werden, indem man den Python-Prozess beendet. Dies kann beispielsweise so gemacht werden, dass man in einer Linux-Shell sudo pkill python eintippt. (Bei Verwendung von TigerJython oder Geany wird der Prozess beim nächsten Download automatisch gestoppt, siehe unter Entwicklungstools.)

Viele Anwendungen werden durch Tasten gesteuert, insbesondere um sie sauber zu beenden. Es wird vorgeschlagen, dass Sie einen kleinen Tastenschalter auf eine einreihige weibliche Steckerleiste in einfacher oder doppelter Distanz der GPIO Pins löten. Die Taste kann irgendwo zwischen einem GND und einem GPIO I/O Pin eingesteckt werden.

switch7

switch5switch6

 

 

 

Experiment 2: Zustand durch Drücken eines Buttons wechseln

 

Ziel:
Durch Drücken der Taste (Buttons) wird eine LED oder ein Buzzer eingeschaltet, beim nochmaligen Drücken ausgeschaltet.

Schaltschema:
Das gleiche wir oben.

Programm:[►]

# Button2.py

import RPi.GPIO as GPIO
import time

P_BUTTON = 40 # adapt to your wiring
P_LED = 22    # adapt to your wiring

def setup():
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(P_BUTTON, GPIO.IN, GPIO.PUD_UP)
    GPIO.setup(P_LED, GPIO.OUT)
    
setup()
isLedOn = False
isButtonReady = True
while True:
    if isButtonReady and GPIO.input(P_BUTTON) == GPIO.LOW:  # pressed
        isButtonReady = False
        if not isLedOn:
            isLedOn = True
            GPIO.output(P_LED, GPIO.HIGH)
        else:
            isLedOn = False
            GPIO.output(P_LED, GPIO.LOW)
    if GPIO.input(P_BUTTON) == GPIO.HIGH:  # released
        isButtonReady = True
    time.sleep(0.01) # remove this line to experience bouncing
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

Bemerkungen:
Sie benötigen ein Flag isLedOn, um den aktuellen Zustand der LED/Buzzer zu speichern, damit Sie ihn wechseln können, wenn der Button wieder gedrückt wird. Das zweite Flag isButtonReady speichert die Information, ob die Taste losgelassen wurde. Wenn Sie die Zeile time.sleep (0.01) entfernen, verhält sich das Programm schlecht. Der Grund ist das "Prellen" des Buttons, wie es oben erläutert wurde. Versuchen Sie zu verstehen, warum der Prelleffekt durch den Einbau einer Zeitverzögerung eliminiert wird.

 

 

Experiment 3: Verwendung einer eventbasierten Button-Bibliothek

  Da die Button-Aktionen jederzeit auftreten können, sollten sie wie Ereignisse (Events) behandelt werden, die eine sogenannte Callbackfunktion aufrufen (in der Assembler- und C-Programmierung auch Interrupt-Routine genannt). Da das Programmieren mit Interrupts und Events fortgeschrittene Programmierkenntnisse voraussetzt, stellen wir hier ein kleines Software-Modul button.py (einen "Button Handler") zur Verfügung, das die folgenden Button-Events unterstützt: BUTTON_PRESSED, BUTTON_RELEASED, BUTTON_LONGPRESSED, BUTTON_CLICKED und BUTTON_DOUBLECLICKED. Laden Sie das Modul button.py von hier herunter und kopieren Sie es in dasselbe Verzeichnis des Raspberry Pi, in dem sich Ihr Programm befindet. Wenn Sie den Quellcode oder die Python doc studieren, sehen Sie, dass es eine Klassenbibliothek ist, die eine beliebige Anzahl Buttons unterstützt.

Ziel:
Testen Sie mit Hilfe unseres Button-Moduls die folgenden Button-Events: Drücken, Loslassen, langes Drücken, Klicken, Doppelklicken. Das Hauptprogramm ist lediglich damit beschäftigt, eine LED blinken zu lassen (bzw. zu Beepen). Mit einem Doppelklick wird das Programm beendet.

Schaltschema:
Das gleiche wie oben.

Programm:[►]

# Button3.py

from button import *
import RPi.GPIO as GPIO
import time

P_BUTTON = 15 # adapt to your wiring
P_LED = 7     # adapt to your wiring

def setup():
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(P_LED, GPIO.OUT)
    button = Button(P_BUTTON) 
    button.addXButtonListener(onButtonEvent)

def onButtonEvent(button, event):
    global isRunning
    if event == BUTTON_PRESSED:
        print "pressed"
    elif event == BUTTON_RELEASED:
        print "released"
    elif event == BUTTON_LONGPRESSED:
       print "long pressed"
    elif event == BUTTON_CLICKED:
        print "clicked"
    elif event == BUTTON_DOUBLECLICKED:
        print "double clicked"
        isRunning = False
       
setup()
isRunning = True
while isRunning:
    GPIO.output(P_LED, GPIO.HIGH)    
    time.sleep(0.1)
    GPIO.output(P_LED, GPIO.LOW)    
    time.sleep(0.1)
GPIO.cleanup()
print "all done"
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

Bemerkungen:
Wie Sie sehen, führt das Hauptprogramm das Blinken unabhängig von der Erfassung der Button-Events aus. Jedes Mal, wenn ein Button-Event eintritt, wird die Callbackfunktion onButtonEvent() vom System aufgerufen. Eine wichtige Regel sagt, keinen lang dauernden Code in einem Callback auszuführen, da dies die Ereigniserkennung während einer gewissen Zeit blockieren kann. Um die Ereigniserkennung zu aktivieren, müssen Sie den Callback onButtonEvent() durch den Aufruf addXButtonListener(onButtonEvent) registrieren.

Bevor Ihr Programm beendet wird, sollten Sie den Befehl GPIO.cleanup () aufrufen, damit das GPIO in den Grundzustand zurückgesetzt wird (alle Ports als Eingang definiert).

Wenn Sie den Klick und Doppelklick nicht brauchen, ist es günstiger, die Funktion addButtonListener() zu verwenden und nur die Callbackfunktionen BUTTON_PRESSED, BUTTON_RELEASED und BUTTON_LONGPRESSED zu registrieren. Dadurch entfällt eine kurze Verzögerung, die benötigt wird, um zwischen Klick und Doppelklick zu unterscheiden.

 

 

Eine schöne Anwendung: Erzeugung von Morsecode

 

Es ist einfach, einen Morsecode-Generator mit dem Buzzer zu erstellen. Wir zeigen hier, wie man ein Programm durch Verwendung von Funktionen strukturieren sollte, damit es besser lesbar ist.

Ziel:
Spielen Sie irgendeinen Text im Morsecode ab, beispielsweise einen "CQ-Aufruf" als Radio-Amateur (HAM).

Schaltschema:
Das gleiche wie oben.

Programm:[►]

# MorseGen.py

import RPi.GPIO as GPIO
import time

P_BUZZER = 22 # adapt to your wiring
dt = 0.1 # adapt to your speed

morse = {
'a':'.-'   , 'b':'-...' , 'c':'-.-.' , 'd':'-..'  , 'e':'.'    ,
'f':'..-.' , 'g':'--.'  , 'h':'....' , 'i':'..'   , 'j':'.---' ,
'k':'-.-'  , 'l':'.-..' , 'm':'--'   , 'n':'-.'   , 'o':'---'  ,
'p':'.--.' , 'q':'--.-' , 'r':'.-.'  , 's':'...'  , 't':'-'    ,
'u':'..-'  , 'v':'...-' , 'w':'.--'  , 'x':'-..-' , 'y':'-.--' ,
'z':'--..' , '1':'.----', '2':'..---', '3':'...--', '4':'....-',
'5':'.....', '6':'-....', '7':'--...', '8':'---..', '9':'----.',
'0':'-----', '-':'-....-', '?':'..--..', ',':'--..--', ':':'---...',
'=':'-...-'}

def s(n):  # wait
    time.sleep(n * dt)

def setup():
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(P_BUZZER, GPIO.OUT)
    
def dot():
    GPIO.output(P_BUZZER, GPIO.HIGH)
    s(1)
    GPIO.output(P_BUZZER, GPIO.LOW)
    s(1)

def dash():
    GPIO.output(P_BUZZER, GPIO.HIGH)
    s(3)
    GPIO.output(P_BUZZER, GPIO.LOW)
    s(1)

def transmit(text):
    for c in text:
        if c == " ":
            s(4)
        else:
            c = c.lower()
            if c in morse:
                k = morse[c]
                for x in k:
                    if x == '.':
                        dot()
                    else:
                        dash()
            s(2)

setup()
transmit("cq de hb9abh pse k")
GPIO.cleanup()
print "all done"
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

Bemerkungen:
Das Standard-Timing des Morsecodes in Bezug auf die Zeitdauer dt eines einzelnen Punktes ist das Folgende:

 Punkt  dt
 Strich  3 * dt
 Pause zwischen Punkten und Strichen im selben Zeichen  dt
 Pause zwischen Zeichen  3 * dt
 Pause zwischen Wörtern  7 * dt (*)

(*) kann für ein leichteres Lesen erhöht werden