Mal was ganz Neues - das 4x4-Tastenfeld

Beim Tastenfeld muss ich weiter ausholen. Wie ist es möglich, 16 Tasten mit nur 8 Leitungen abzufragen? Schauen wir uns einmal die folgende Abbildung an.

 

 

Die Tasten sitzen an den Kreuzungspunkten der Zeilen- und Spaltendrähte. Wird eine Taste gedrückt, dann wird eine bestimmte Zeilenleitung mit einer genau definierten Spaltenleitung verbunden. Aufgabe unseres Programms ist es jetzt, herauszufinden, wer mit wem verbunden ist. Dafür gibt es verschiedene Algorithmen. Zwei davon stelle ich Ihnen hier vor. Die Vorgehensweise orientiert sich an der Art der Verdrahtung und der Hardware der vorhandenen Schnittstellen.

 

Wie muss der Tastenblock an den ESP32 überhaupt angeschlossen werden? Wir können für jede der 8 Leitungen einen GPIO-Pin vorsehen. Allerdings ist die damit verbundene Decodierung der Tasten durch verschachtelte Schleifen die aufwendigere Softwarelösung, weil eben einzelne Leitungen angesprochen werden müssen. Der ESP32 hat keine Portregister wie die AVR-Controller (Arduino und Co.) mit 5- bis 8-Bit Datenbreite.

 

Die zweite Anschlussvariante benutzt daher den Porterweiterungsbaustein MCP23017, der an dem I2C-Bus hängt, welcher evtl. bereits ein Display versorgt. Anhand dieser Schaltung stelle ich die raffiniertere Reversal-Decoderlösung vor. Beide Lösungen bedienen sich der Pollingmethode zur Tastaturabfrage. Immer dann, wenn eine Tastenbetätigung erwartet wird, schaut das Programm nach, ob eine Taste gedrückt wird und wartet gegebenenfalls so lange, bis das geschieht.

 

Eine dritte Abfragemöglichkeit bestünde darin, durch zeitgesteuerte Programmunterbrechungen (aka Timer-Interrupts) die Tasten in festen Intervallen abzufragen und die Ergebnisse in einem Pufferspeicher abzulegen. Aus diesem Tastaturpuffer könnte sich das Hauptprogramm dann bei Bedarf die Zeichencodes, die den Tasten entsprechen, abholen. Wir nehmen hier die einfachere Pollingmethode.

 

Fassen wir das Bisherige zusammen. Wir haben also

·         zwei Methoden, die Tasten-Hardware anzuschließen: Einzelleitungen oder parallel

·         zwei Methoden, der Tastenabfrage: Polling oder Interruptbetrieb und

·         zwei Methoden, die Tasten durch Software zu decodieren: Schleifen oder reversal

 

Einzelleitungen mit Zeilen- Spaltenabfrage

Weil es beim ESP32 keine "Sammelports", wie bei den AVR-Controllern gibt, wo man mehrere Leitungen zu einem Portregister zusammenfasst, müssen die GPIOs eben einzeln bedient werden. Hardwaremäßig sieht das wie folgt aus. Ich habe die einzelnen Leitungen wie die farbcodierten Jumperkabel eingefärbt, um die Zuordnung eindeutig festzulegen. Das Schaltschema zeigt die folgende Abbildung. Die Pinbelegung und die elektrischen Eigenschaften des Tastenblocks kann man im Datenblatt nachlesen. Die elektrischen Werte sind alle unkritisch, weil die Kontakte ja kaum belastet werden.

 

 

Die Tastenabfrage ist in Form der Klasse KEYPAD_P gelöst, die ich in das Modul keypad.py integriert habe. Im gleichen Zug wurde die Tastenabfrage des LCD-Keypads so angepasst, dass sich auch dafür dieselbe API ergibt. Bei allen Klassen gibt es eine Methode key(), die angesprochen wird, wenn das aufrufende Programm eine Tastenbetätigung wünscht.

 

Aber der Reihe nach. Der Konstruktor der Klasse KEYPAD_P erwartet zwei Listen mit je 4 Pinnummern für die Zeilen und die Spalten des Tastenblocks. Sie werden im Hauptprogramm bestückt, etwa so wie es in der Abbildung vorgegeben ist.

 

col=[15,5,18,19]

row=[13,12,14,27]

 

Dann rufen Sie damit den Konstruktor auf

 

from keypad import KEYPAD_P

kp=KEYPAD_P(row, col)

 

Hinweis:

Im Handel sind auch Folientastaturen erhältlich. Die sind aber nicht pinkompatibel mit der hier verwendeten Tastatur.

 

Programmtechnisch steckt Folgendes dahinter:

 

class KEYPAD_P: # indexed mit Schleifen

    # Index ist die Tastennummer

    keyNumber=[0x31,0x00,0x01,0x02,

               0x10,0x11,0x12,0x20,

               0x21,0x22,0x03,0x13,

               0x23,0x33,0x30,0x32]

   

    asciiCode="0123456789\x08\x0b\x0c\x0d*+"

   

    # row und col sind Listen der Pinnummern)

    # z.B. col=[15,5,18,19] row=[13,12,14,27]

    def __init__(self, row, col):

        self.rowPin=[Pin(i,Pin.OUT) for i in row]

        self.colPin=[Pin(i,Pin.IN,Pin.PULL_UP) for i in col]

 

Damit die Zuweisung der Pinnummern auf Pin-Objekte in einem Rutsch geht, habe ich mich eines Tricks von MicroPython bedient, der List-Comprehension. In den Listenkontext (eckige Klammern) ist durch die Anweisung Pin(i,Pin.OUT) die Vorschrift verpackt, wie mit den Elementen der Lisen row oder col zu verfahren ist, um daraus ein Pinobjekt zu generieren. Die Instanzvariablen rowPin und colPin sind also Listen von Pinobjekten. In rowPin sind die Objekte Ausgänge, in colPin Eingänge mit aktiviertem Pullup. Letzteres spart hier externe Widerstände.

 

Die Lösung mit den Pin-Listen habe ich deshalb gewählt, damit die Abfrage wenigstens in Form von for-Schleifen geschehen kann und nicht für jede der 16 Kombinationen eigener Programmcode erzeugt werden muss. Die Methode key() erledigt die Hauptarbeit. Sie stellt die Taste fest und liefert eine Tastennummer zurück, während die Methode debounceKey() zusätzlich, falls erforderlich, für das Entprellen der Tasten sorgt und als Ausgabe ebenfalls die Tastennummer als Ausgabe zur Verfügung stellt.

 

    def key(self):

        for i in range(4):

            self.rowPin[i].value(1)

        for j in range(4):  # row setting

            self.rowPin[j].value(0)

            for k in range(4):  # query cols

                w=self.colPin[k].value()

                if w==0:

                    code=j<<4 | k

                    #print(hex(code))

                    self.rowPin[j].value(1)

                    try:

                        return KEYPAD_P.keyNumber.index(code)

                    except:

                        return-1

            self.rowPin[j].value(1)

        return -1

        

    def debounceKey(self, debounce=1):

        for i in range(debounce):

            k1=self.key()

            if k1 != -1:

                sleep(0.01)

                k2=self.key()

                if k2 == k1:

                    return k1

        return -1

 

Wie arbeitet key()?

 

Die Zeilenpin-Objekte 0..3 werden alle auf 1 gesetzt. (Sie erinnern sich, MicroPython sieht die obere Bereichsgrenze (4) als exclusiv an.) In der Grafik wird die Taste 6 gedrückt. Sie schließt Zeile 1 mit Spalte 2 kurz.

 

 

In der folgenden j-Schleife werden die Zeilenleitungen von oben nach unten der Reihe nach einzeln auf GND-Pegel gesetzt. Die eingeschlossene k-Schleife prüft nun, ob eine der Spaltenleitungen auf 0-Potenzial liegt. In diesem Fall bilden wir den rohen Tastencode aus Zeilen- und Spaltennummer. Die Zeilennummer j liefert das höherwertige Nibble des code-Bytes, die Spaltennummer das niederwertige Nibble. Dann setzen wir den Zeilenausgang wieder auf 1 und verlassen mit return und der Tastennummer im Gepäck die k-Schleife und die Funktion. Wurde keine Taste gedrückt, laufen beide Schleifen bis zum Ende durch, und die Rückgabe ist der Wert -1. Die rohen Tastencodes werden in diesem Beispiel in positiver Logik ermittelt. Die Bits erhalten ihre Wertigkeit aus den Laufindizes der Schleifen, nicht vom Pegel auf den Leitungen.

 

Der Tastencode wird als Index des rohen Tastencodes in der Liste keyNumber ermittelt. Die rohen Tastencodes müssen also einfach nur an die Position in der Liste gesetzt werden, deren Index der gewünschten Tastennummer entspricht. Hier erst einmal die Codeliste:

 

    keyNumber=[0x31,0x00,0x01,0x02,

               0x10,0x11,0x12,0x20,

               0x21,0x22,0x03,0x13,

               0x23,0x33,0x30,0x32]

 

Zur Erinnerung, Listen werden beginnend mit der 0 indiziert. Die Taste, die in der 3. Zeile in der Spalte 1 liegt, ist die 0. Also muss der Hexcode 0x31 an die Position 0 in der Liste. Alles klar?

Nein - nicht so ganz? Gut, noch ein Beispiel. Denken Sie daran, Zeilen, Spalten und Listeneinträge werden ab der 0 hoch gezählt.

 

Die Taste C soll die Tastennummer 12 liefern. Sie liegt in der 2. Zeile in Spalte 3. Die Zeilenbits bilden das High-Nibble des rohen Tastencodes. Die 0x02 für Zeile 2, um 4 Bit nach links verschoben (oder mit 16 multipliziert) ergibt im Code-Byte 0x20. Das wird mit 0x03 oderiert. Der rohe Tastencode ist demnach 0x23 und muss an der 12. Position in der Liste stehen. Jetzt vergleichen Sie das mit der keyNumber-Liste – stimmt!

 

KEYPAD_P.keyNumber.index(code)

 

Um den Index eines Listeneintrags feststellen zu können, gibt es die eingebaute Funktion index(), die als Methode des Listenobjekts aufgerufen wird. keyNumber ist eine Klassenvariable und muss daher mit dem Klassenbezeichner KEYPAD_P als Prefix referenziert werden. In code wird der rohe Tastencode übergeben.

 

Aus dieser Beschreibung können wir schließen, dass die 1 am schnellsten entdeckt wird (0,50ms) und dass der Zustand "keine Taste" am längsten braucht (1,34ms). Werden zwei Tasten oder mehr gleichzeitig gedrückt, dann wird die weiter oben und/oder links liegende Taste decodiert und die andere(n) verworfen, da die Schleifen diese Tasten nicht mehr abfragen. Damit ist auch das Problem der Mehrdeutigkeit gelöst.

 

Die Methode debouceKey() nimmt mit dem Parameter debounce eine Ganzzahl, die angibt, wie oft die interne Schleife durchlaufen werden soll. Mechanische Tasten schließen und öffnen nicht exakt zu einem festgelegten Zeitpunkt, sondern der Schaltkontakt flattert jeweils während einer kurzen Zeitspanne, was zu chaotischem Öffnen und Schließen des Kontakts führt. Diesen Vorgang nennt man Tastenprellen (aka engl. Bouncing). Um nun sicherzustellen, dass eine Taste wirklich gedrückt ist, muss man die Schaltzustände zu verschiedenen Zeitpunkten einlesen und mit einander vergleichen. Werden in kurzem zeitlichem Abstand die gleichen Zustände festgestellt, dann gilt die Taste als gedrückt. Diese Prüfung führt die Methode debounceKey() durch, und der Wert im Parameter debounce gibt an, wie oft diese Prüfung durchgeführt werden soll. Der Defaultwert dafür ist 1.

 

Wenn der Vergleich positiv verläuft, wird der Tastencode zurückgegeben, andernfalls der Wert -1, der andeutet, dass entweder keine Taste gedrückt wurde oder, dass der Schaltzustand bis zuletzt nicht eindeutig war.

 

Zum Testen müssen eine Reihe von Modulen ins Flash des ESP32 hochgeladen worden sein. Am besten ist es, wenn Sie alle eingangs genannten Module herunterladen und zum ESP32 schicken. Das sind die Dateien:

 

LCD-Standard-Modul

HD44780U-I2C-Erweiterung zum LCD-Modul

Keypad-Modul

i2cbus-Modul für standardisierten Zugriff auf den Bus

mcp.py hardwarededizierte Methoden für den MCP23017

 

Die Datei keypad_p_test.py öffnen sie bitte in einem Editorfenster. Bevor Sie loslegen, noch ein wichtiger Hinweis. Suchen Sie bitte im Editor nach den folgenden beiden Zeilen.

 

#kp=KEYPAD_I2C(ibus,keyHwadr) #  Hardware Objekt am I2C-Bus

kp=KEYPAD_P(rows,cols) #  HW-objekt mit Parallelanschluss

 

Die erste Zeile ist auskommentiert, weil sie nur für die weiter unten beschriebene zweite Anschlussmethode via I2C-Bus gilt. Hier muss die zweite Zeile ausgeführt werden, darf also kein Kommentarzeichen haben.

 

Wenn das passt, können Sie das Programm mit F5 starten. Damit sind alle Importe und Declarationen für den Test des Tastenfelds erledigt. Alles für jeden Test erneut von Hand einzugeben, dauert zu lange und ist fehleranfällig.

 

Das Tastenfeld ist korrekt angeschlossen? Dann los! Geben Sie bitte den folgenden Befehl ein.

 

>>> kp.key()

    -1

 

Wenn Sie keine Taste gedrückt hatten, ist das OK!. Rufen Sie jetzt mit Pfeil nach oben den letzten Befehl noch einmal in die Eingabezeile zurück. Jetzt die Taste 1 drücken und die Eingabezeile mit Enter beenden.

 

>>> kp.key()

    1

 

Dasselbe mit der Taste C

 

>>> kp.key()

    12

 

Prima! Einige höhere, von der Hardware unabhängige Methoden zur Tastenabfrage besprechen wir nach dem folgenden Kapitel. Darin geht es um die effektivere Softwarelösung und den Tastenfeldanschluss via I2C.

 

I2C-Anschluss des 4x4-Tastenfelds und die
Reversal-Abfrage am Parallelport

Der Vorteil dieser Methode liegt eindeutig darin, dass nicht einzelne Leitungen der Reihe nach abgefragt werden müssen. Stattdessen wird einer der 8-Bit-Ports (A) des MCP23017 halbiert. Das obere Nibble steuert die Zeilenleitungen, das untere Nibble die Spaltenleitungen, aber eben nicht einzeln, sondern parallel. Ebenso gut wäre es, den Port A für die Zeilen und B für die Spalten einzusetzen. Für größere Tastenmatrizen mit mehr als zweimal vier Leitungen ist das unumgänglich.

 

 

Angesteuert wird der MCP23017 über den I2C-Bus und damit parallel zum Display und evtl. weiteren I2C-Einheiten. Das ist möglich, weil alle Schnittstellen unterschiedliche Hardwareadressen haben. Für die Adresse 0x20 müssen die drei Adresspins des MCP23017 alle auf GND-Potenzial liegen. In der obigen Darstellung sind die Leitungen AD0, AD1 und AD2 nicht beschaltet und daher reagiert der Baustein auf die Adresse 0x27. Übrigens, passen Sie auf, dass Sie wirklich einen MCP23017 bekommen. Sein Brüderchen, der MCP23S17, wird über den SPI-Bus angesteuert und ist hier nicht brauchbar.

 

Für den MCP23017 habe ich ein Modul erstellt. Die Klasse MCP23017 stellt eine ganze Reihe von Konstanten und Methoden bereit, die eine übersichtliche und einfache Programmierung des Bausteins ermöglichen, ohne dass man sich mit den Methoden des I2C-Moduls herumschlagen muss. Die Tastenabfrage ist hiermit in 7 Zeilen passiert. Obwohl keine Schleifen zu durchlaufen sind, ist dieser Algorithmus scheinbar langsamer wie der oben beschriebene, dauert aber für jede Taste gleich lang (ca. 4ms). Das liegt aber nicht an der Methode an sich, sondern am I2C-Bus, der uns ausbremst. Hätte der ESP32 von vorn herein ansprechbare Parallelports, dann wäre diese Methode eindeutig die schnellere. Die AVR-Processoren mit ihren Parallelports profitieren also von diesem Algorithmus.

 

Die Klasse KEYPAD_I2C erbt von der Klasse MCP23017 und kann daher direkt auf deren Namensraum zugreifen. Die Liste keyNumber der Rohcodes sieht etwas anders aus, wie die bereits bekannte, der Konstruktor auch.

 

class KEYPAD_I2C(MCP): # reversal

    # Keypad  Reihe 0 1 2 3 Spalte 0 1 2 3

    # MCP GPIOA Pin 4 5 6 7        0 1 2 3

    keyNumber=[0x7D,0xEE,0xED,0xEB,

               0xDE,0xDD,0xDB,0xBE,

               0xBD,0xBB,0xE7,0xD7,

               0xB7,0x77,0x7E,0x7B]

 

    asciiCode="0123456789\x08\x0b\x0c\x0d*+"

 

    HWADR=const(0x20)

    KeyMask=const(0b00001111)  # OUT=0 IN=1

    # 0 is OUTPUT - 1 is INPUT

    # Die Zeilen sind zunächst Output

   

    def __init__(self,i2cbus,hwadr=HWADR):

        self.hwadr=hwadr

        self.i = i2cbus

        super().__init__(i2cbus,hwadr)

        print("Keypad_I2C @", hex(hwadr))

 

Der Konstruktor nimmt vom Hauptprogramm als Positionsparameter ein I2CBus-Objekt und als optionalen Parameter die Hardwareadresse des MCP23017. Beide reicht er an die Klasse MCP23017 weiter, die unter dem Alias MCP importiert wurde.

 

Aber schauen wir uns den Wunderalgorithmus einmal an.

 

    def key(self):

        self.changeIODIRA(0x00,KeyMask,True)

        self.setGPIOA(KeyMask)

        iod=self.getGPIOA()

        self.invertIODIRA(True)

        self.setGPIOA(iod)

        iod=self.getGPIOA()

        #print(hex(iod),bin(iod))

        try:

            return KEYPAD_I2C.keyNumber.index(iod)

        except ValueError as e:

            return -1

 

Der Code erscheint wesentlich geradliniger und besteht, die Exception-Behandlung nicht mitgerechnet, aus 7 Anweisungen.

 

changeIODIRA(0x00,KeyMask,True)

KeyMask hat den Wert 0b00001111. Wir undieren das Datenrichtungsregister A mit 0b00000000, löschen also alle Bits und setzen dann durch Oderieren mit KeyMask das Low-Nibble als Eingänge. True sorgt dafür, dass für diese Eingänge die Pullups eingeschaltet werden. Die vier Zeilenleitungen sind jetzt Ausgänge die Spaltenleitungen Eingänge.

 

setGPIOA(KeyMask)

KeyMask hat immer noch den Wert 0b00001111. Somit werden alle Zeilenleitungen gleichzeitig auf GND-Potential gelegt.

 

iod=self.getGPIOA()

Die Eingänge von GPIOA werden erfasst. Mit den Pullups ergibt das den Wert

 

0bxxxx1101

 

Das High-Nibble interessiert im Moment (noch) nicht.

 

 

 

invertIODIRA(True)

Wir invertieren das Datenrichtungsregister A, Eingänge werden zu Ausgängen und umgekehrt. True - die Pullups der Eingangsleitungen werden aktiviert..

 

setGPIOA(iod)

S2 führt jetzt Low-Potential. Wir lesen GPIOA ein und erhalten den Wert

 

0b10111101 = 0xDB

 

Wir treffen hier auf eine negative Logik, das heißt es zählen die Bits, die nicht gesetzt sind. Man kann es auch so sehen, für die Spalten, das Low-Nibble, fehlt von der 15 das Bit mit der Wertigkeit 4. Somit bleiben 15 – 4 = 11 = 0x0B übrig. Bei den Zeilen liegt Bit1 auf Low, es fehlt im Nibble also die 2. 15 – 2 = 13. Das High-Nibble erhält den Wert 0xD0. Bitweise oderiert ergibt sich 0xD0 | 0x0B = 0xDB

 

KEYPAD_I2C.keyNumber.index(iod)

Die Rohwerte stehen wieder an entsprechender Stelle in der Liste keyNumber. Der Index eines Werts liefert die Tastennummer wie im ersten Beispiel. Damit haben beide Methoden denselben Aufruf und liefern das sinngemäß gleiche Ergebnis. Der Weg dorthin ist ein anderer, aber nach außen hin spielt das keine Rolle. Die Methode debounceKey() besitzt in beiden Klassen sogar denselben Code. Die beiden Klassen liefern also mit Ausnahme des Konstruktors Instanzen mit derselben API.

 

OK! Das war jetzt weit ausgeholt, aber ich denke, es ist wichtig, die grundlegenden Mechanismen einer Matrixabfrage verstanden zu haben und gegen einander abwägen zu können. Lassen Sie uns noch einen Blick auf die Klasse KEYPAD werfen, welche hardwareübergreifende Methoden für die Benutzung von Tastaturen anbietet.

 

 

Klasse KEYPAD – komplexere Tastendienste

Der Konstruktor erwartet eine Instanz der Klassen KEYPAD_P oder KEYPAD_I2C. Sogar KEYPAD_LCD – Instanzen werden trotz des geringen Zeichenumfangs akzeptiert. Ein Trick soll nicht unerwähnt bleiben. Die Methode kp.key(), besser deren Referenz, wird der Instanz-Variable self.key zugewiesen. In MicroPython kann man das tun. Das kann helfen, ellenlange Aufrufe von importierten Methoden zu verkürzen. Aus self.k.key() wird self.key(). Sie meinen, das sind doch Peanuts? Na ja, dann stellen Sie sich doch einmal vor, statt k müsste keyPad_I2C_Instanz eingegeben werden und das 20 mal oder öfter.

 

class KEYPAD:

   

    # kp ist ein KEYPADXXX-Objekt, wird uebergeben

    def __init__(self,kp,d=None):

        self.k=kp

        self.disp=d

        self.key=kp.key

       

    def waitForKey(self,timeout=5,ASCII=False):

        now=time()

        end=(now+timeout if timeout!=0 else now+10)

        while 1:

            k=self.key()

            if k!=-1:

                if ASCII:

                    k=self.k.asciiCode[k]

                return k

            if timeout==0: end=time()+10

            if time()>=end:

                if ASCII:

                    return "\xFF"

                else:

                    return -1

 

    def asciiKey(self):

        n=self.key()

        if 0<=n<=15:

            return self.k.asciiCode[n]

        return "\xFF"

       

    def padInput(self,delay=0.5,xp=0,yp=0):

        s=""

        x=xp; y=yp

        while 1:

            taste=self.asciiKey()

            if taste != -1:

                if '0'<=taste<='9' or taste=='+' or taste=='*':

                    s+=taste

                    if self.disp:

                        x=self.disp.writeAt(taste,x,yp)

                    else:

                        x+=1

                    sleep(delay)

                elif taste=="\x08":

                    if self.disp and x>xp:

                        self.disp.clearFT(x-1,yp,x)

                    x=(x-1 if x>xp else xp)

                    s=s[:x-xp]

                    sleep(delay)

                elif taste == "\x0d":

                    return s

 

Die drei Methoden dieser Klasse erfüllen nützliche Zwecke.

 

Die Methode waitForKey(delay) wartet delay Sekunden auf die Betätigung einer Taste. Wird in dieser Zeit eine Taste gedrückt, dann gibt die Methode die Tastennummer zurück, andernfalls -1. Die Methode wartet ewig, falls an delay der Wert 0 übergeben wird. Als Standard wird die rohe Tastennummer zurückgegeben. Setzt man ascii = True erfolgt stattdessen die Rückgabe des ASCII-Codes der Taste.

 

Je nach dem Definitionsstring asciiCode in den KEYPADXXX-Klassen liefert die Methode asciiKey() ein normales ASCII-Zeichen statt der Tastennummer zurück. Welche Zeichen das sein sollen, legen Sie mit Hilfe der Zeichenkette in asciiString fest. Die Position des Zeichens im String entspricht der Tastennummer, welche die Methode key() liefert. Nicht druckbare Zeichen, wie \n werden als Hexcode angegeben. Zum Beispiel entspricht \n also \x0D. Meine Definition sieh so aus:

 

asciiCode="0123456789\x08\x0b\x0c\x0d*+"

 

Übrigens, wenn Sie mehrere asciiCode-Definitionsstrings anlegen, können Sie eine Tastenmehrfachbelegung etablieren. Allerdings ist dann nicht nur die Beschriftung der Tasten eine Herausforderung.

 

Die Methode padInput() erlaubt schließlich die Eingabe von Ziffernfolgen ergänzt durch "*" und "+", welches durch die Taste "#" eingegeben wird. Taste "A" löscht rückwärts und Taste "D" übernimmt den String zur weiteren Verarbeitung. Wurde dem Konstruktor ein Display-Objekt übergeben, dann erscheinen die Zeichen auch im Display. Ohne Display-Objekt ist Blindflug angesagt.

 

Drei optionale Parameter steuern die Eingabe. delay (default = 0.5s) schützt vor ungewollter Mehrfacheingabe eines Zeichens, weil die key-Methode einfach zu schnell ist, brauchen wir eine Verzögerung. xp und yp legen die Position im Display fest, ab der die Eingabe dargestellt wird. Default ist die linke obere Cursorposition, entsprechend "home".

 

An dieser Stelle angekommen, testen wir jetzt die drei Methoden. Falls Sie das noch nicht getan haben, schicken Sie jetzt bitte alle Module ins Flash des ESP32 und öffnen Sie das Programm keypad_p_test.py in einem Editorfenster. Je nach der gewählten Anschlussart der Tastatur muss genau eine der folgenden Zeilen entkommentiert sein.

 

#kp=KEYPAD_I2C(ibus,keyHwadr) #  Hardware Objekt am I2C-Bus

kp=KEYPAD_P(rows,cols) #  HW-objekt mit Parallelanschluss

 

Starten Sie das Programm mit F5. Wenn der REPL-Prompt (>>> ) wieder erscheint, sind Sie bereit für die folgenden Tests über die Kommandozeile.

 

>>> k.waitForKey(3)

5

>>> k.waitForKey(3)

-1

>>> 

 

Im ersten Fall wurde innerhalb von 3 Sekunden die Taste 5 gedrückt. Beim zweiten Aufruf wurde der Tastendruck versäumt.

 

>>> k.asciiKey()

-1

>>> k.asciiKey()

'*'

>>> 

Jetzt wurde beim ersten Aufruf keine Taste gedrückt und beim zweiten die "*"-Taste. Beachten Sie, dass nicht der Tastencode 14 sondern der ASCII-Code für "*" zurückgegeben wurde.

 

>>> k.padInput(0.3,3,1)

'1234567'

>>> 

 

Mit einer Verzögerung von 0,3 Sekunden wird das Tastenfeld abgefragt. Die Eingaben werden ab der Position Spalte 3 und Zeile 1 im Display angezeigt. Stellen Sie die Totzeit nicht zu lang ein, denn sonst werden Tastenbetätigungen übersprungen. Bei zu kurzem Totzeitwert erscheinen Eingaben mehrfach. Um die Eingabe abzuschließen, drücken Sie die Taste D.

 

Als Ergebnis des vorangegangenen Befehls sollten Sie Ihre Eingabe sowohl auf dem Display als auch im Terminalfenster sehen.