Forum: Mikrocontroller und Digitale Elektronik Tastenmatrix


von doodle (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich habe soeben ein Programm für den Atmega8 geschrieben, mit welchem 
ich die Eingabe von einer 4*4 Tastenmatrix ausgeben möchte.
Dabei wird von dem uC eine Zeile nach der anderen auf high gelegt und 
dann der Zustand der Spalten abgefragt. Ein Timer sorgt dafür, dass die 
gesamte Matrix innerhalb von 0,01s abgefragt wird.
Leider klappt das nicht so, wie ich es mir vorstelle, auf dem LCD wird 
zwar "Eingabe:" ausgegeben (hier liegt also kein Fehler), aber bei 
Tastendruck ändert sich nichts. Findet einer von euch meinen Fehler?

von Stefan F. (Gast)


Lesenswert?

Führe das Programm mit einem Debugger aus oder gib zwischendurch 
Log-Meldungen aus, um den Fehler einzukreisen.

In so vielen Zeilen Code den Fehler durch Sichtkontrolle zu suchen, 
halte ich für die falsche Herangehensweise.

Die Art, wie du dort die Interruptroutine verwendest, kommt mir arg 
seltsam vor. Kannst mal erklären, wie dieses Programm funktionieren 
soll? Ein Ablaufdiagramm wäre hilfreich.

von doodle (Gast)


Lesenswert?

Danke für die schnelle Antwort!
Die Interruptroutine soll die 1, welche zu Beginn in PinD4 steht, immer 
um eins nach links schieben, um eine Zeile nach der anderen abzufragen. 
Dabei soll PORTD0...3 unangetastet bleiben, weil diese Pins den Eingang 
für die Spalten darstellen.

von Stefan F. (Gast)


Lesenswert?

Das ist doch sinnlos, wenn dein Hauptprogramm am Ende darauf wartet, 
dass alle Zeilen durchlaufen wurden. Das kannst du dann auch gleich in 
der lesematrix() Funktion machen, dann ist es auch viel einfacher zu 
debuggen.

Mir ist gerade was aufgefallen: Ich sehe keinen Code, der Timer startet 
und auch keinen Code, der die Interrupts aktiviert.

von doodle (Gast)


Lesenswert?

Stefan U. schrieb:
> Mir ist gerade was aufgefallen: Ich sehe keinen Code, der Timer startet
> und auch keinen Code, der die Interrupts aktiviert.

Ich bis gerade auch nicht! Der Fehler ist behoben, leider bleibt PORTD = 
0, wenn ich ihn mir auf dem LCD anzeigen lasse,obwohl sich eigentlich 
die höherwertigen Bits ändern müssten...

Habe folgendes für den Timer hinzugefügt:
1
TIMSK |= (1<<TOIE2);
2
  sei();

von Stefan F. (Gast)


Lesenswert?

Wie gesagt, baue Log-Meldungen ein. Und gebe sie auch einem Bildschirm 
aus, auf dem man sie vernünftige lesen kann. Bei mir heißt das: Serielle 
Verbindung zum PC und dort läuft das Hammer Terminal.

von Stefan F. (Gast)


Lesenswert?

Wo ist der Code, der den Timer startet?

von doodle (Gast)


Lesenswert?

Ganz doof gefragt, wird der nicht mit Einstellen des Prescalers 
gestartet?
1
TCCR2 |= (1<<CS21) | (1<<CS20);  //prescaler 32

Habe ich eben nicht noch mal gesondert geschrieben, steht aber in der 
main.c so drin (also auch die, welche oben angehängt ist).

von doodle (Gast)


Lesenswert?

PS: Du hast recht, dass man das alles eigtl. auch ohne Interrupt gut 
realisieren kann. Da ich jetzt diesen Ansatz gewählt habe, möchte ich 
den aber auch gerne zu Ende bringen, einfach aus Prinzip (oder Trotz :-/ 
)

von Stefan F. (Gast)


Lesenswert?

>> Wo ist der Code, der den Timer startet?
> Ganz doof gefragt, wird der nicht mit Einstellen des
> Prescalers gestartet?

Nein

> Da ich jetzt diesen Ansatz gewählt habe, möchte ich
> den aber auch gerne zu Ende bringen, einfach aus Prinzip

Dann mache nicht zwei Fässer gleichzeitig auf!

Starte einen Timer und schalte eine LED über dessen OCR Ausgang an/aus. 
Als nächsten Schritt fügst du eine Interruptroutine hinzu und schaltest 
die LED stattdessen per Software um.

Danach erst bringst du deine Matrix ein.

von doodle (Gast)


Lesenswert?

Stefan U. schrieb:
> Starte einen Timer und schalte eine LED über dessen OCR Ausgang an/aus.
> Als nächsten Schritt fügst du eine Interruptroutine hinzu und schaltest
> die LED stattdessen per Software um.

Das habe ich schon gemacht, auch schon mit dem ADC um die Helligkeit 
einstellen zu können. Da gab es eben keine Probleme!
Und da bei den Prescaler select Bits im Datenblatt ausdrücklich steht 
"No clock source (Timer/Counter stopped)" wenn alle besagten Bits = 0 
sind, war ich davon ausgegangen, dass bei allen anderen Prescaler 
Einstellungen der Timer gestartet wird.

von doodle (Gast)


Lesenswert?

Stefan U. schrieb:
>>> Wo ist der Code, der den Timer startet?
>> Ganz doof gefragt, wird der nicht mit Einstellen des
>> Prescalers gestartet?
>
> Nein

Hier habe ich in das AVR-GCC-Tutorial geschaut, da wird auch nichts 
weiter gemacht, als den prescaler setzen, die Timer-Interrupts erlauben 
und globale Interrupts erlauben:
1
 // Timer 0 konfigurieren
2
  TCCR0 = (1<<CS01); // Prescaler 8
3
4
  // Overflow Interrupt erlauben
5
  TIMSK |= (1<<TOIE0);
6
7
  // Global Interrupts aktivieren
8
  sei();

Ich bin dankbar für jede Hilfe, bloß hier erkenne ich wirklich nicht, 
was ich noch hätte ändern müssen, um den Timer zu starten.

von Karl M. (Gast)


Lesenswert?

Hi doodle,

warum liest Du nicht das spezielle und richtige Datenblatt zu deinem 
Atmel AVR?

Ein AVR-GCC-Tutorial mal als Blick hinter die Kulissen reichen, aber den 
real verwendeten Atmel AVR kann man nur korrekt über sein Datenblatt 
ansprechen.

von Karl M. (Gast)


Lesenswert?

Hi,

was Stefan U. wahrscheinlich meint, ist, dass außer dem Timer Overflow 
Modus 0, alle weiteren Modi konfiguriert werden müssen.

Wir kennen ja deinen verwenden Atmel AVR nicht.

Wenn die µC mit 1MHz läuft, der Timer0 Prescaler 1/32 beträgt und alle 
256 Ticks ein Interrupt Overflow erfolgt, dann wird die ISR alle ~122Hz 
ausgeführt.

Das liegt dann daran, dass im Mode 0 der Zähler TCNT0 von 0 bis 255 
läuft und bei Überlauf von 255->0 eine Timer0 Interrupt Request 
generiert wird.

Wie geschrieben, das steht alles sehr genau im Datenblatt.

von doodle (Gast)


Lesenswert?

Karl M. schrieb:
> warum liest Du nicht das spezielle und richtige Datenblatt zu deinem
> Atmel AVR?

Natürlich richte ich mich nach dem (richtigen) Datenblatt. Ich habe mir 
dazu die "modes of operation" durchgelesen und bin zu dem Schluss 
gekommen, den Timer im "normal mode" mit einem prescaler von 32 zu 
nutzen. Daraufhin bin ich Bit für Bit alle Timer/Counter2-Register mit 
Blick auf meine Anwendung durchgegangen und habe die notwendigen Bits 
gesetzt.
Ich weiß, dass die Ratschläge, die einzelnen "Einheiten" eines 
Programmes voneinander getrennt zu erlernen, gut gemeint sind; 
allerdings habe ich eben schon Versuche mit den Timern, PWM etc. 
gemacht.

von Stefan F. (Gast)


Lesenswert?

> Wir kennen ja deinen verwenden Atmel AVR nicht.

Ich hatte gehofft, dass der TE das irgendwann von selbst merkt.

von doodle (Gast)


Lesenswert?

Karl M. schrieb:
> Wenn die µC mit 1MHz läuft, der Timer0 Prescaler 1/32 beträgt und alle
> 256 Ticks ein Interrupt Overflow erfolgt, dann wird die ISR alle ~122Hz
> ausgeführt.

Ich betreibe den Avr mit 8MHz, ein prescaler von 32 ergibt somit eine 
Frequenz von 980Hz, das ist mir schon klar.

von Stefan F. (Gast)


Lesenswert?

> den Avr

Und der wäre welcher?

von doodle (Gast)


Lesenswert?

Ok, es sieht ganz danach aus, als ob ich den Fehler in meinem Programm 
mithilfe von trial and error finden muss; wenn ich damit nicht weiter 
komme, probiere ich halt etwas anderes aus.
Ich möchte euch trotzdem für eure Hilfe danken!

von Karl M. (Gast)


Lesenswert?

Sorry Stefan Us,

er lass deine Beiträge wohl nur oberflächlich.

Ich verweise auf Beitrag von Peter Dannegger, er hat einen sehr schönen 
und effiziente Code geschrieben.

Beitrag "Re: Tastenmatrix 3x4 Problem"

Aber Achtung auch dieses Taster prellen und man muss noch jede erkannte 
Taste entprellen!
Ich nutze dieses Basiscode auch zur Erledigung der Aufgabe, es werden 
DTMF Codes eingegeben und über ein Soundmodul (AVR PWM) ausgegeben.

'---------------------------------------
' Der Einfachheit halber wird angenommen,
' dass ein Keypad an einem Port
' angeschlossen wurde.
'---------------------------------------
'
'        Col0 Col1 Col2
'        |    |    |
'  D6 ---x----x----x--Row0 (1, 2, 3)
'  D5 ---x----x----x--Row1 (4, 5, 6)
'  D4 ---x----x----x--Row2 (7, 8, 9)
'  D3 ---x----x----x--Row3 (*, 0, #)
'        |    |    |
'        |    |    |
'  D2 ---+    |    |
'  D1 --------+    |
'  D0 -------------+
'
'  Legende.
'  x     : Taster
'  -,+,| : Leitung
'---------------------------------------

von doodle (Gast)


Lesenswert?

Stefan U. schrieb:
>> den Avr
>
> Und der wäre welcher?

doodle schrieb:
> ich habe soeben ein Programm für den Atmega8 geschrieben

von Karl M. (Gast)


Lesenswert?

Danke doodle ,

hatte ich auch überlesen, so wie die 8MHz RC Oszillator.

von Hans (Gast)


Lesenswert?

Hallo,

üblicher Weise hängt man Col-0, Col-1 und Col-2 über einen Widerstand 
(z.b. 10k) nach VCC. Über Row-0 bis Row-3 wird dann eine Low ausgegeben 
die dann bei einem Tastendruck an D0 bis D2 gelesen wird.

von doodle (Gast)


Lesenswert?

Danke Karl für den Link!
Wie oben schon geschrieben habe ich wohl am meisten Erfolg, wenn ich 
hier bei mir vor Ort herumprobiere, das Programm ist wirklich etwas zu 
unübersichtlich, als dass man sich da mal eben hineinarbeiten kann.
Wenigstens ist in diesem Thread nie das Wort "Glaskugel" gefallen, davor 
hatte ich am meisten Angst, haha :'D

von doodle (Gast)


Lesenswert?

Hans schrieb:
> üblicher Weise hängt man Col-0, Col-1 und Col-2 über einen Widerstand
> (z.b. 10k) nach VCC. Über Row-0 bis Row-3 wird dann eine Low ausgegeben
> die dann bei einem Tastendruck an D0 bis D2 gelesen wird.
>



Ja, einen ähnlichen Ansatz hatte ich auch verfolgt, nur dass nicht ein 
low, sondern ein high erkannt werden sollte.

von Stefan F. (Gast)


Lesenswert?

> es sieht ganz danach aus, als ob ich den Fehler in meinem Programm
> mithilfe von trial and error finden muss

Warum übergehst du meinen Ratschlag bezüglich der Log Meldungen oder 
Debugger? Wenn du unbedingt trial & error willst, dann mach das. Aber 
dann bitte hier nicht um Hilfe.


Wenn das jetzt die Initialisierung für deinen ATmega8 ist, dann ist sie 
in ordnung, soweit ich das auf die schnelle beurteilen kann:

> TCCR2 |= (1<<CS21) | (1<<CS20);  //prescaler 32
> TIMSK |= (1<<TOIE2);
> sei();

Wenn du zur Kontrolle in der ISR eine LED einschaltest, müsste sie 
gleich nach dem Programmstart an gehen. Tut sie das?

Was machen denn deine Zeilen-Ausgäge? Werden die nacheinander auf High 
geschaltet, oder nicht? Werden sie überhaupt geschaltet?

Dazu könntest du dort mal LED's anschließen. Oder die Spannung messen. 
An einem Pin der ein Rechteck-Signal führt müsste ein normales 
Multimeter den Mittelwert der Spannung messen, als knapp über 1 Volt (da 
1/4 der Zeit auf High).

von Peter D. (peda)


Lesenswert?

Der Code ist wirklich sehr komplex und schwer zu durchschauen.
Ich sehr nirgends, wo die Inputs abgefragt werden.
Und mal wird PORTC, mal PORTD benutzt.
Zeig mal den Schaltplan.

von Eric B. (beric)


Lesenswert?

Was liefert dir die Funktion greytonormal als Rückgabewert wenn 
eingabe nicht 0, 1, 2, 4 oder 8 ist?
Welche Wert kriegt die Variable zeile in der Funktion lesematrix?

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.