Forum: Mikrocontroller und Digitale Elektronik probleme mit stoppuhr programm


von suboptimal (Gast)


Angehängte Dateien:

Lesenswert?

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

von Lasse S. (cowz) Benutzerseite


Lesenswert?

Das erste was mir auffällt:

cli in der Interruptroutine vom Stopptaster? Soll das da sein und wenn 
ja: warum?

Gruß,
Lasse

von suboptimal (Gast)


Lesenswert?

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

von suboptimal (Gast)


Lesenswert?

Hallo,

Das cli entfernen hilft aber auch nicht.

Grüße

von oldmax (Gast)


Lesenswert?

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

von suboptimal (Gast)


Lesenswert?

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

von Martin V. (oldmax)


Lesenswert?

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

von suboptimal (Gast)


Angehängte Dateien:

Lesenswert?

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

von Martin V. (oldmax)


Angehängte Dateien:

Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Hannes L. (hannes)


Lesenswert?

> Oh Gottohgottohgottohgottohgottohgottohgottohgottohgott.

Es gibt aber scheinbar doch Leute, die sowas herunterladen.

(Ich war's nicht...)

...

von Martin V. (oldmax)


Lesenswert?

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

von Hannes L. (hannes)


Lesenswert?

Naja, Word benutzt man schon im eigenen Interesse (wenn überhaupt) nur 
lokal.

...

von gast_1 (Gast)


Lesenswert?

aber schon lustig wie aus einem thread "probleme mit stoppuhr programm" 
ein thread "doc-Dateien" wird

von suboptimal (Gast)


Lesenswert?

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!

von oldmax (Gast)


Lesenswert?

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

von suboptimal (Gast)


Lesenswert?

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 eingefgt        
6
.include "LCD-Routine.asm"            ; LCD-Routinen werden hier eingefgt
7
.include "entprellen.asm"        ; Taster-Entprellen einfgen


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
Noch kein Account? Hier anmelden.