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?
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.
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.
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.
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(); |
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.
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).
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 :-/ )
>> 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.
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.
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.
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.
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.
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.
> Wir kennen ja deinen verwenden Atmel AVR nicht.
Ich hatte gehofft, dass der TE das irgendwann von selbst merkt.
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.
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!
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 '---------------------------------------
Stefan U. schrieb: >> den Avr > > Und der wäre welcher? doodle schrieb: > ich habe soeben ein Programm für den Atmega8 geschrieben
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.
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
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.
> 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).
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.