Hallo,
ich versuche gerade mit einem Atmega 8 eine Stoppuhr zu basteln.
Starten mit einem Taster funktioniert gut.
Die Pasuenfunktion mittels Interrupt funktioniert auch gut.
Wenn ich aber die Stopptaste drücke (ebenfalls ein Interrupt) dann
springt die Anzeige zwar auf Null, aber ich bekommen die Uhr nicht
wieder zum laufen.
Vielleicht könnte sich jemand mal meinen Code angucken und mir einen
Tipp geben.
Vielen Dank und Grüße
sub
p.s. Ich habe auch noch ein Problem mit meinem LCD was ich nicht ganz
verstehe.
Beim ersten Start wird auf beiden Zeilen des Displays ein Text
ausgegeben (für spätere Zwecke).
Wenn ich nun ein Reset mache dann wird die 2.Zeile nicht mehr
dargestellt!
Kann mir jemand sagen voran das liegen kann?
Ich benutze ein Reichelt LCD162
Hallo,
Das soll verhindern das er nach dem drücken der stopptaste den
timerinterrupt auslöst.
Damit wollte ich den rücksprung nach dem stoppinterrupt auf das
hauptprogramm reduzieren.
Da ich ja nicht weiß wo er nach dem stoppinterrupt reti hinspringt
Grüße
Hi
Also, Grundsätzlich springt ein Programm aus einem Interrup zurück an
die Stelle, wo er vorher unterbrochen hat. Egal, ob geschachtelt, oder
nicht. Es sei denn, du manipulierst den Stack.....
Also, stell dir vor, du sitzt auf dem Sofa und liest ein Buch.
(Standartprogramm) Es klingelt. (ahh, ein Interrupt ) Du unterbrichst,
legst ein Lesezeichen zwischen die Seiten und gehst an die Tür
(Interruptserviceroutine oder kurz ISR). Da pfeift in der Küche der
Teekessel ( schon wieder etwas, was man nicht ignorieren soll, nicht nur
wegen dem unangenehmen pfeifen. Also wieder Interrupt..). Du
entschuldigst dich kurz bei deinem Besucher, gehst in die Küche und
stellst den teekessel ab. (das ist auch eine ISR) Fertig, jetzt gehst du
zur Tür, wimmelst den Besucher ab ( ähh Tante Klara, ich hab überhaupt
nix zu essen im Hause.. ) und schließt die Tür. Dann gehst du zurück zu
deinem Buch, (das Standartprogramm..)schlägst die Seite mit dem
Lesezeichen auf und genießt den Rest des Tages hoffentlich ohne weitere
Interrupts..
Dein Prozessor ist ja nun kein denkender Mensch, also merkt er sich auf
dem Stack die Adresse des nächsten fälligen Befehles. Dann bearbeitet er
die ISR. Mit RETI holt er diese Adresse vom Stack und springt dort hin,
wo er den nächsten Befehlscode findet.
Wichtig ist in einer ISR, das alle benutzten Register vorher gerettet
werden. Also, du benutzt um ein paar Werte hin und herzuladen, z.B. R16
und R17
Also muß am Anfang der ISR der Inhalt der Register zwischengespeichert
werden, da du ja nicht weißt, an welcher Stelle im Programm ist der
Interrupt ausgelöst worden und welche Registerwerte grad aktuell waren.
Das sieht dann so aus:
1
Eine_unterbrechung: ; Einsprung in die ISR
2
Push R16 ; Register R16 zwischenspeichern
3
Push R17 ; Register R17 zwischenspeichern
4
LDI R16,0b11111111 ; eine Maske, um Signale zu drehen
5
LDS R17, Eingaenge ; hier hast du z.B. eine Port abgel.
6
XOR R16,R17 ; 0 zu 1, 1 zu 0, Ergebnis in R16
7
LDS Taster ; Da Eing. auf 0 gezogen, ist in Taster eine 1, wenn betätigt.
8
POP R17 ; R17 zuerst vom Stack
9
POP R16 ; Register haben wieder alte Werte
10
RETI ; Rücksprung aus ISR
Das wäre eine typische ISR.
Um es etwas leichter zu machen, warum kein Polling für die Taster ? Der
µC ist so schnell, da ist dein Finger auf dem Schalter ja nun auch nicht
so schnell, das er in µSek genaue Werte liefert. Ich halte es so, das
ich am Anfang der Programmschleife die Eingänge lese, diese dann
bearbeite und am Ende werden dann die Ausgänge gesetzt. Habe ich
Interrupts, so werden lediglich Bits (Flags) gesetzt, die dann im
Programm berücksichtigt werden. Das hält eine ISR klein und
übersichtlich.
Gruß oldmax
Hallo,
Also ich habe schon so ziemlich alles mit meinem Stoppinterrupt
probiert.
Wenn Du dir mein Quellcode anguckst habe ich oben diverse register
definiert, um ,so wie Du es vorgeschlagen hast, sie als Flag zu
benutzen. (z.B. Stopptaste, Starttaste...)
Das hat leider nicht viel gebracht. Außerdem habe ich vorher auch
probiert temp und temp1 zu pushen und zu popen (jaja dummer ausdruck,
aber ...), weil ich mit diesen beiden hauptsächlich arbeite. Das hat
aber dazu geführt, daß das Programm noch weniger funktionierte. So ist
z.B. nach drücken des Stopptasters eine Art Reset ausgeführt worden,
damit meine ich es wurde mir auf einmal wieder die Start LCD-Anzeige
angezeit. Mir war und ist absolut schleierhaft warum nach der ISR nicht
wieder ins Hauptprog zurückgesprungen wurde sondern weit davor(nämlich
zur LCD-Ausgabe).
Zur Stackmanipulation habe ich hier im Forum gelesen, daß davon
abzuraten ist, weil das irgendwie zu Problemen führen kann, deswegen
habe ich mich damit nicht weiter beschäftigt. Was ich jetzt aber mal tun
werde.
Dazu habe ich noch ein paar fragen:
1. Gebe ich die Adresse eines Labels an oder des ersten Befehls nach
diesem Label?
2. Adressen sind doch 2Byte groß, richtig? Muß ich zuerst das Lowbyte
pushen und dann das Highbyte, oder umgekehrt?
Außerdem wäre es nett falls mir jemand sagen könnte, ob ich denn
grundlegende Fehler in meiner Programmiereung eingebaut habe, denn ich
kann keine finden und das Problem beschäftigt mich schon seit drei
Tagen.
Im Moment versuche ich mir einzureden, daß die ISR schuld ist und nicht
meine Programmierung. Bin mir aber leider nicht sicher, ob das nur
Wunschdenken ist.
Grüße
Hi
Ich glaube, nachdem ich deinen Code überflogen habe, das du die
eigentliche Programmschleife gar nicht aufrufst, sondern ab und zu in
die Initialisierung des Stack stolperst. Ich vermisse auch die
Deklaration der ISR. Dazu hab ich mal einen kleinen Auszug aus dem
Tutorial hergenommen.
Dieses RJMP Reset überspringt den Bereich der ISR Deklaration, das ist
wichtig, sonst bearbeitet dein Controller diese als normale
Unterprogramme, bevor der Stack initialisiert werden kann. Wie du
siehst, steht hinter manchen .Org und dem ISR-Namen gleich ein RETI.
D.h. dafür gibt es keine ISR, andernfalls stände hier ein RJMP
ISR_Irgendwas
Die ISR wird dann im Programm als Unterprogramm definiert, allerdings
dann mit einem RETI am Ende. Keinesfalls sollte eine ISR von der
Programmschleife aufgerufen werden.
Hier mal der Auszug aus dem Tutorial:
Die Struktur eines Programmes sollte sein:
Deklaration der Variablen
Startpunkt des Programmes festlegen
Deklaration der ISR Vektortabelle
Deklaration weiterer Unterprogramme ** kann auch hinter dem
Hauptprogramm erfolgen.
Eintritt Hauptprogramm:
Stack initialisieren, das hast du gemacht.
Variablen vorbesetzen, Register und Ports initialisieren, , auch ok
allerdings würde ich dafür schon mal ein RCall Init opfern, hält das
Hauptprogramm schlank...
Nach aufruf der Initialisierung Interrupts freigeben und dann
1
LOOP: ; hier werden alle wiederhplenden Programmteile aufgerufen
2
RCALL Lese_Port_A
3
RCALL Lese Port_B
4
RCALL Check_IN_Flag
5
..... ; Aktionen aufgrund irgendwelcher Flags. z. BREQ End_Main
6
RCALL Set_Send_Buffer
7
RCALL Schreibe_Port_x
8
End_Main: RJmp LOOP
Nun schreibst du eine Einzelnen Routinen. Um dein Programm zwischendurch
zu prüfen brauchst du zuerst nur das Gerüst zu erstellen
1
Lese_Port_A:
2
3
RET ; noch kein Inhalt
4
5
Lese_Port_B:
6
7
RET ; noch kein Inhalt
8
9
Check_In_Flag:
10
11
RET ; noch kein Inhalt
12
usw.....
Dann füllst du die Unterprogramme nach und nach mit Code. Irgendwann
stellst du fest, es macht Sinn, aus Unterprogrammen weitere UP's
aufzurufen, damit diese klein und leicht zu kommentieren sind.
Bei dieser Vorgehensweise solltest du den Überblick behalten, auch bei
umfangreichen Programmen.
Gruß oldmax
Hallo,
So ich habe meinen Code jetzt mal ein wenig umgeschrieben, so wie martin
es empfohlen hat. (wenn ich es richtig verstanden habe).
Das Problem ist damit nicht behoben. Ganz im Gegenteil ;)
Ich habe jetzt mal eine Verständnis frage:
Wenn ich mein Prog laufen lasse dann fängt es doch bei main an.
Dann nach dem ich alles init und rcall Geschichten abgearbeitet habe
läuft mein Prog nur noch in der loop-Schleife.
Selbst wenn ein Interrupt ausgelöst wurde.
Richtig soweit ?
Wenn ja dann ist es mir schleierhaft wie es passieren kann, daß wenn ich
den Stopptaster drücke (int1), mein Prog wieder zum rcall lcd_start
springen kann und mir meine LCD-Zeilen ausgibt (bzw. nur die 1. Zeile
und nie wieder die 2. auch nicht nach einem Reset)
Hat irgendjemand vielleicht da noch einen Tipp für mich?
Grüße
sub
Hi
Ok, bevor ich hier stundenlang deinen Code versuche zu verstehen, mal
ein Aufbau eines Programmes von mir. Ich weiß nicht, ob deine
Zusatzdateien so eingebunden werden, wie du erwartest. Daher hab ich mal
das Gerüst eines meiner Programme angehängt. Achte mal auf die .DSeg,
.ESeg und .CSeg Anweisung. Möglich, das dies bei dir der Grund ist. Wenn
mich mal einer fragt, warum ich soviele Variablen einsetze, na ja, ich
hab mir ein Programm geschrieben, da kopier ich einfach die
Variablennamen hinein und kann dann das laufende Programm im Controller
über die Variablen prüfen. Dieses Programm steht hier irgendwo auch zur
Verfügung, ich kann es aber gern nochmal reinstellen.
Gruß oldmax
Martin Vogel schrieb:
> Ok, bevor ich hier stundenlang deinen Code versuche zu verstehen, mal> ein Aufbau eines Programmes von mir.
Oh Gottohgottohgottohgottohgottohgottohgottohgottohgott.
Source als DOC, sach mal gehts noch.
Peter
Hi
sry, aber sollte schnell gehen,hät's ja auch in den Beitrag schreiben
können. Außerdem bin ich ein ehrlicher Lump, der nur gute böse-Absichten
hat....
Gruß oldmax
Hallo,
Also langsam glaube ich es liegt an meinen include-Dateien.
Aber wo genau soll ich die den einfügen?
Gibt es richtige Stellen und falsche?
Ich habe sie an diversen Stellen eingefügt und getestet, daß hat aber
nicht viel gebracht.
Und in deinem Beispiel sind irgenwie keine weiter include-Dateien, außer
der atmega8, drinn.
Grüße
sub
p.s.
Seit dem ich das Prog in viele Unterprogramm unterteilt habe spinnen im
Übrigen beide Interrupts ein bißchen!
Hi
Und in deinem Beispiel sind irgenwie keine weiter include-Dateien, außer
der atmega8, drinn.
Das hast du richtig gesehen und was sagt uns das ? Mein Beispiel sollte
dich auf die Anweisungen .DSEG (Datensegment) , .ESEG (Epromsegment) und
. CSEG (Codesegment) hinweisen. Dann solltest du auch beachten, das
Interruptroutinen nicht mit RCALL aus der Programmschleife aufgerufen
werden. Nur in der ISR-Vektortabelle steht ein RJMP- also ein Sprung und
kein Call (Unterprogrammaufruf). Versuche mal deine Include-Dateien an
die richtigen Stellen im Programm einzufügen. Also Daten in das
Datensegment und Code in das Codesegment.
Daten (Variablen) hab ich beim Überflieggen nicht gesehen, aber die
ISR-Vektortabelle, die du einbinderst ist Ciode und das gehört halt in
ein .CSEG .
Gruß oldmax
Hallo,
Und in deinem Beispiel sind irgenwie keine weiter include-Dateien, außer
der atmega8, drinn.
Den Rest lese ich mir nachher durch bin gerade knapp an Zeit, aber