3x3 Tastenmatrix inkl. Interrupt mit ATmega128 unter BASCOM-AVR

Markus

BASCOM-Experte
11. Jan. 2008
1.190
6
38
Lonsee
Sprachen
Hallo AVR-Welt,

auf was man beim Basteln nicht alles Stößt und mit welchen Problemen man sich rumschlagen muss :)

Mit diesem Kleinprojekt möchte ich Euch ein Thema vorstellen was sicher so mancher gut gebrauchen kann und um das ich auch bei meiner Wintergartensteuerung nicht herum gekommen bin.

Eine Tastatur für Tasteneingaben, realisiert als Ressourcen schonende 3x3 Tastenmatrix mit Interruptabfrage.

Merkmale der hier vorgestellen Lösung:
- Tastenmatrix 3x3 mit jeweils 3 Spalten und 3 Zeilen.
- Um die Tastenmatrix nicht ständig zu pollen und Rechenzeit zu verbraten habe ich mir eine Interrupt-Steuerung dazu überlegt.
- Es sollte möglich sein auch mehrere Tasten gleichzeitig betätigen und erkennen zu können.
- Über den zurückgelieferten Tastencode soll rechnerisch auf die einzelnen Tasten zurückgeschlossen werden können um später schöne Menüs und Funktionen zu realisieren.

Benötigte Ressourcen:
- 3 freie Eingange für Zeilen
- 3 freie Ausgänge für die Spalten
- 1 freier Interrupt

Umsetzung:
- Um auch mehr als eine Taste in beliebigen Zeilen/Spalten betätigen zu können benötigt man Entkopplungsdioden welche ich bei meinem Prototyp in die Spalten eingebaut habe. Man kann auch jede einzelne Taste mit einer Diode absicher. Es soll auch Tasten geben welche solche Dioden gleich mit beinhalten.

- Achtung: Man findet im Internet immer wieder Lösungsvorschläge (auch im BASCOM-AVR Buch "Programmieren der AVR RISC Mikrocontroller mit BASCOM-AVR") bei dem der BASCOM Befehl "debounce" verwendet wird. Ich habe in meinem Code explizit auf debounce verzichtet da debounce verhindert, dass mehrer Tasten gleichzeit in einer Zeile betätigt werden können. Das hängt mit der speziellen Umsetzung von debounce und der Tatsache zusammen, das ein PIN wieder "losgelassen" werden muss bevor ein neuer Signalzustand von debounce erkannt wird. Mit meiner Lösung funktioniert es besser.

- Ich habe eine kleine Wartezeit mit ein paar Milisekunden eingebaut. Diese Wartezeit dient a: zum entprellen der Tasten und b: ermöglicht es dem Benutzer auch mehrere Tasten gleichzeitig zu drücken. Der Mega ist sonst ohne Wartezeit zu schnell.

- In meinem Code gibt es zwei Varianten (Variante 1 und Variante 2) die über globale Konstanten umgeschaltet werden kann. Variante 1 ist dafür gedacht, die Auswertung der Tasten nach Interrupt über ein globales Flag gesteuert in der Hauptschleife zu machen. Variante zwei erledigt die Auswertung der Tastatur gleich in der Inetrrupt-Routine. Es bleibt Euch überlassen, welche Varinate Ihr ggf. einsetzen wollt.

- Jeder Taste ist über die Spalte und Zeile eine entsprechende Wertigkeit zugeordnet so das man später über ein wenig Rechnerei auch rausfinden kann, welche Tasten alle betätigt wurden die zu dem Interrupt geführt haben.

- Eine neue Tasteneingabe wird erst nach Freigabe des Interrupts ermöglicht. Damit kann man in Ruhe die Tasten asuwerten und auch Eingaben verhindern. Achtung: Doppelt betätigte Tasten und während der Interrupt-Freigabe bereits schon anstehende Interrupts werden durch löschen des Interrupt-Flags im Interrupt-Flag-Register zum Zeitpunkt der Tastenfreigabe verhindert.


Allgemeines:
- Aufgebaut und in Betrieb genommen wurde der Prototyp auf Lochraster in Verbindung mit einem STK500 + STK501, ATmega 128 mit extern 16MHz. Im Code sind aber keine weiteren Spezialitäten vorhanden so das dieser eigentlich auf jedem anderen Mega laufen müsste.
- Die Tastenmatrix ist ohne großen Aufwand beliebig auf 4x4 oder mehr erweiterbar. Zum Rechnen würd eich dann für den Tastencode den Datentyp WORD verwenden.
- Die Anordnung der Tasten in meinem Prototyp habe ich schon auf meine Wintergartensteuerung angepasst. Dort benötige ich zwei Tasten für Bschattung auf/zu, zwei Tasten für Licht heller/dunkler und ein Tastenkreuz mit Enter für Menüs und diverse andere Funktionen. Licht aus und Beschattungsantrieb stopp realisiere ich durch gleiczeitiges Betätigen der jeweiligen Tastenpaare. Die Tastatur ist und bleibt aber eine Matrix und Ihr könnt sie ja beliebig abändern.

So, ich glaube das war das Wichtigste in Kürze. Ihr findet wie üblich den BASCOM-AVR Source-Code sowie das Schaltbild und ein Bild vom Prototyp im Anhang.

Viel Spass beim Ausprobieren,

Markus
 

Anhänge

  • tastenmatrix.gif
    tastenmatrix.gif
    97,1 KB · Aufrufe: 389
  • Tastenmatrix.zip
    4,9 KB · Aufrufe: 460
  • Tastenmatrix_SCH.gif
    Tastenmatrix_SCH.gif
    30,2 KB · Aufrufe: 560
Verständnisfrage

Hallo,

ich habe mir den Quellcode einmal angesehen und bin da für mich auf ein paar ??? gestossen.

Vielleicht fehlen mir auch ein paar basics vom Verständnis :

Const Key_zeilen_pullup = &B00000111 ' PullUp Definition für PORTA

Der Ausdruck &B00000111 ist eine Zahl? was bedeutet in diesem Zusammenhang " &B "

Const Key_interrupt_mask = &B0111111 ' Interrupt Mask für Bit7
Was hat es mit dieser Maske auf sich ?


Ich hoffe die Fragen sind nich all zu blöd :confused::help:
 
Der Ausdruck &B00000111 ist eine Zahl? was bedeutet in diesem Zusammenhang " &B "

Hallo Ingo!

Wenn du in BASCOM eine binäre Zahl darstellen möchtest, dann stellst du der Zahl ein " &B " voran.
Möchtest du z.B. eine hexadezimale Zahl darstellen, dann musst du ein " &H " voranstellen. :wink:

Letztlich ist und bleibt es eine Zahl......
Du kannst natürlich auch die dezimale Variante nehmen, nur oftmals lässt sich die binäre Schreibweise einfach besser lesen. :D

Im o.g. Fall sieht man also gleich, dass bei PORT.A an den Pins 0, 1 und 2 die internen PullUp-Widerstände aktiviert wurden.
Markus hätte natürlich auch schreiben können:
Code:
Const Key_zeilen_pullup = 7

In beiden Fällen käme das selbe Ergebnis bei raus. :wink:



Zu deiner zweiten Frage, wegen der Konstante für die "Interrupt-Mask"......
Ich denke mal, dass Markus dies zu beginn verwenden wollte, es letzlich aber nicht mehr benötigte.
Die Konstante wird zu beginn nämlich erwähnt und auch einem Wert zugewiesen.......
später taucht sie aber nicht mehr auf.

So eine "Mask" verwendet man ganz gern, wenn man nur bestimmte Pins eines Ports berücksichtigen möchte.
Wenn du also zum Beispiel auf den Wechsel der Pins 1, 3 und 5 von PORT.B reagieren möchtest, dann kannst du dir so eine Konstante als "Maske" anlegen und mit " And " anwenden.

Beispiel:
Code:
Dim Pins As Byte
Const Pin_maske = &B00101010


Pins = PINB And Pin_maske

Als Ergebnis bekommst du dann nur den Wert der "ausgewählten" Pins von PORT.B !
Der Zustand aller anderen Pins an dem Port ist dabei egal.


Grüße,
Cassio
 
oh ja ...

DANKE für die erhellenden Worte :yes2:

&B hätte ich drauf kommen müssen .... :banghead:
B = Blöd oder eben binary :)


Das Thema mit der Maske denke ich habe ich nun auch gescheckt ... ist also wie eine Klammer um Pins zu einer Einheit logsich zusammenzufassen ...

Dann hoffe ich mal, dass meine Bauteile dann über kurz oder lang kommen.... dann werde ich mal mein Projekt in Gänze vorstellen ...
 
Hi,

Das Thema mit der Maske denke ich habe ich nun auch gescheckt ... ist also wie eine Klammer um Pins zu einer Einheit logsich zusammenzufassen ...

... nein, leider nicht :p Es ist eher wie ein Sieb durch das nur bestimmte Bits durchkommen.

logische UND (oder AND) Verknüpfung ...
A && B => X
0 && 0 => 0
0 && 1 => 0
1 && 0 => 0
1 && 1 => 1
Nur wenn beide Bits eine 1 beinhalten hat auch das entsprechende Ergebnisbit eine 1
Wenn nun A dein PIN-Register ist (zB PINC) und B deine Maske dann kannst du mit der Maske nur die Bits durchlassen die dich auch interessieren. Da du bei einem Ausgangsport mit PIN auch den Zustand des Ausgangs abfragen kannst der dich aber evtl nicht interessiert kannst du die entsprechenden Bits damit ausblenden.

Beispiel:

DIM Eingaenge AS Byte
DDRC = &B11011010 ' Alle mit einer 1 sind Ausgänge. Also sind nur C.0 , C.2 und C.5 Eingänge
Eingaenge = PINC AND &B00100101

Damit kommen dann nur die Bits 0, 2 und 5 bis in die Bytevariable durch. Egal was die anderen Bits am Port sagen. Diese anderen Bits werden in der Variable "Eingaenge" immer 0 ein da die Maske an der Stelle eine 0 hat. (x && 0 => 0)
Da die Bitwertigkeiten identisch bleiben (Bit0=>1, Bit2=>4, Bit5=>32) bekommst du nur 8 verschiedene Werte ...
00000000 -> 0
00000001 -> 1
00000100 -> 4
00000101 -> 5
00100000 -> 32
00100001 -> 33
00100100 -> 36
00100101 -> 37

Gruß
Dino
 
rücksetzen des INT Flags

noch eine Frage ...

Sehe ich das richtig, dass der 74HC11 das INT Flag setzt ? Wie setze ich den denn wieder zurück ? in BASCOM klar, aber wie wirkt sich das auf das Gatter aus ? oder ist das wie eine drehtür, dass sich selber wieder zurücksetze und dann bei erneutem betätigen wieder den HIGH ausgang setzt ?
 
Der IC besitzt 3 AND-Gatter mit je 3 Eingängen. Der Ausgang eines Gatters ist dann und nur dann High, wenn alle dazugehörenden Eingänge High sind. Ist (mindestens) einer der Eingänge low, geht auch der Ausgang low.

Ich habe mir jetzt Markus's Code nicht angesehen, mMn macht aber folgendes Sinn:
Erstmal werden die 3 Zeilen-Beine mittels Pullup hochgezogen, die 3 Spalten-Beine auf Gnd gelegt. Der Interrupt des AVR triggert auf fallende Flanke (also ist so einzustellen, oder ggf low Pegel). Da die Taster offen sind, sind die 3 Gatter-Eingänge High, also auch der Ausgang high. Wird irgendein Taster betätigt, geht einer der Gate-Eingänge, und damit auch der Ausgang low -> der AVR-IRQ löst aus.

Wrd der Taster wieder losgelassen, geht der Eingang (und der Ausgang) wieder high, beim Pegel-IRQ würde die Anforderung aufgehoben werden, beim Flanken-IRQ bliebe das Flag stehen. Kommt darauf an, wie man im Programm damit umgehen will - den auslösenden Taster kann man so nicht rekonstruieren (wenn er wieder losgelassen wurde).

Ist der Taster noch gedrückt (Reaktionszeit auf den IRQ liegt im Bereich von ca 7 Takten+ Verarbeitung->Achtung Prellen) kann man in der ISR (ggf auch erst im Hauptprogramm) versuchen, den auslösenden Taster zu demultiplexen -> indem man nacheinander immer nur eine der Spalten auf low läßt (die anderen High), und die Zeilen-Pins ausliest.

Der I2C-Portexpander aus Deinem anderen Thread besitzt übrigens dieselbe Funktion. Ändert sich einer der 8 Pegel, wird der /INT-Ausgang solange low(?), bis der Pegel wieder zurückgesetzt wurde, oder Du den Expander liest.
 
Ah. OK.

Ja, das ganz wird mittels zwei verschiedener Lösungsansätze im CODE behandelt.
Ich glaube mir war nur nicht ganz das Funktionspronzip des Gatters klar.

DANKE!
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)