www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik probleme mit stoppuhr programm


Autor: suboptimal (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Lasse S. (cowz) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das erste was mir auffällt:

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

Gruß,
Lasse

Autor: suboptimal (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: suboptimal (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Das cli entfernen hilft aber auch nicht.

Grüße

Autor: oldmax (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
Eine_unterbrechung:                    ; Einsprung in die ISR
                    Push R16           ; Register R16 zwischenspeichern
                    Push R17           ; Register R17 zwischenspeichern
                    LDI R16,0b11111111 ; eine Maske, um Signale zu drehen
                    LDS R17, Eingaenge ; hier hast du z.B. eine Port abgel.
                    XOR R16,R17        ; 0 zu 1, 1 zu 0, Ergebnis in R16
                    LDS Taster         ; Da Eing. auf 0 gezogen, ist in Taster eine 1, wenn betätigt.
                    POP R17            ; R17 zuerst vom Stack
                    POP R16            ; Register haben wieder alte Werte
                    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

Autor: suboptimal (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Martin Vogel (oldmax)
Datum:

Bewertung
0 lesenswert
nicht 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:
.include "m8def.inc"
 
.org 0x000
       rjmp RESET
.org INT0addr                 ; External Interrupt0 Vector Address
       reti                   
.org INT1addr                 ; External Interrupt1 Vector Address
       reti                   
.org OC2addr                  ; Output Compare2 Interrupt Vector Address
       reti                   
.org OVF2addr                 ; Overflow2 Interrupt Vector Address
       reti                   
.org ICP1addr                 ; Input Capture1 Interrupt Vector Address
       reti                   
.org OC1Aaddr                 ; Output Compare1A Interrupt Vector Address
       reti                   
.org OC1Baddr                 ; Output Compare1B Interrupt Vector Address
       reti                   
.org OVF1addr                 ; Overflow1 Interrupt Vector Address
       reti                   
.org OVF0addr                 ; Overflow0 Interrupt Vector Address
       reti                   
.org SPIaddr                  ; SPI Interrupt Vector Address
       reti                   
.org URXCaddr                 ; USART Receive Complete Interrupt Vector Address
       reti                   
.org UDREaddr                 ; USART Data Register Empty Interrupt Vector Address
       reti                   
.org UTXCaddr                 ; USART Transmit Complete Interrupt Vector Address
       reti                   
.org ADCCaddr                 ; ADC Interrupt Vector Address
       reti                   
.org ERDYaddr                 ; EEPROM Interrupt Vector Address
       reti                   
.org ACIaddr                  ; Analog Comparator Interrupt Vector Address
       reti                   
.org TWIaddr                  ; Irq. vector address for Two-Wire Interface
       reti                   
.org SPMaddr                  ; SPM complete Interrupt Vector Address
       reti                   
.org SPMRaddr                 ; SPM complete Interrupt Vector Address
       reti                   
 
.org INT_VECTORS_SIZE
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
LOOP:  ; hier werden alle wiederhplenden Programmteile aufgerufen
  RCALL Lese_Port_A
  RCALL Lese Port_B
  RCALL Check_IN_Flag
  .....  ; Aktionen aufgrund irgendwelcher Flags. z. BREQ End_Main
  RCALL Set_Send_Buffer
  RCALL Schreibe_Port_x
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
Lese_Port_A: 

          RET ; noch kein Inhalt

Lese_Port_B: 

          RET ; noch kein Inhalt

Check_In_Flag: 

          RET ; noch kein Inhalt
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

Autor: suboptimal (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Martin Vogel (oldmax)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Oh Gottohgottohgottohgottohgottohgottohgottohgottohgott.

Es gibt aber scheinbar doch Leute, die sowas herunterladen.

(Ich war's nicht...)

...

Autor: Martin Vogel (oldmax)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Hannes Lux (hannes)
Datum:

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

...

Autor: gast_1 (Gast)
Datum:

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

Autor: suboptimal (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: oldmax (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: suboptimal (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

;**************************************************************************

/* Include Dateien */

.include "Vector-Tabelle.asm"         ; Vector-Tabelle wird hier eingefgt        
.include "LCD-Routine.asm"            ; LCD-Routinen werden hier eingefgt
.include "entprellen.asm"        ; Taster-Entprellen einfgen


fast die ersten Zeilen meines Codes ...

Grüße

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.