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
Das erste was mir auffällt: cli in der Interruptroutine vom Stopptaster? Soll das da sein und wenn ja: warum? Gruß, Lasse
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:
1 | .include "m8def.inc" |
2 | |
3 | .org 0x000 |
4 | rjmp RESET |
5 | .org INT0addr ; External Interrupt0 Vector Address |
6 | reti |
7 | .org INT1addr ; External Interrupt1 Vector Address |
8 | reti |
9 | .org OC2addr ; Output Compare2 Interrupt Vector Address |
10 | reti |
11 | .org OVF2addr ; Overflow2 Interrupt Vector Address |
12 | reti |
13 | .org ICP1addr ; Input Capture1 Interrupt Vector Address |
14 | reti |
15 | .org OC1Aaddr ; Output Compare1A Interrupt Vector Address |
16 | reti |
17 | .org OC1Baddr ; Output Compare1B Interrupt Vector Address |
18 | reti |
19 | .org OVF1addr ; Overflow1 Interrupt Vector Address |
20 | reti |
21 | .org OVF0addr ; Overflow0 Interrupt Vector Address |
22 | reti |
23 | .org SPIaddr ; SPI Interrupt Vector Address |
24 | reti |
25 | .org URXCaddr ; USART Receive Complete Interrupt Vector Address |
26 | reti |
27 | .org UDREaddr ; USART Data Register Empty Interrupt Vector Address |
28 | reti |
29 | .org UTXCaddr ; USART Transmit Complete Interrupt Vector Address |
30 | reti |
31 | .org ADCCaddr ; ADC Interrupt Vector Address |
32 | reti |
33 | .org ERDYaddr ; EEPROM Interrupt Vector Address |
34 | reti |
35 | .org ACIaddr ; Analog Comparator Interrupt Vector Address |
36 | reti |
37 | .org TWIaddr ; Irq. vector address for Two-Wire Interface |
38 | reti |
39 | .org SPMaddr ; SPM complete Interrupt Vector Address |
40 | reti |
41 | .org SPMRaddr ; SPM complete Interrupt Vector Address |
42 | reti |
43 | |
44 | .org INT_VECTORS_SIZE |
45 | RESET: ; hier beginnt das Hauptprogramm |
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
> Oh Gottohgottohgottohgottohgottohgottohgottohgottohgott.
Es gibt aber scheinbar doch Leute, die sowas herunterladen.
(Ich war's nicht...)
...
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
Naja, Word benutzt man schon im eigenen Interesse (wenn überhaupt) nur lokal. ...
aber schon lustig wie aus einem thread "probleme mit stoppuhr programm" ein thread "doc-Dateien" wird
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
1 | ;************************************************************************** |
2 | |
3 | /* Include Dateien */
|
4 | |
5 | .include "Vector-Tabelle.asm" ; Vector-Tabelle wird hier eingef�gt |
6 | .include "LCD-Routine.asm" ; LCD-Routinen werden hier eingef�gt |
7 | .include "entprellen.asm" ; Taster-Entprellen einf�gen |
fast die ersten Zeilen meines Codes ... Grüße
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.