Forum: Mikrocontroller und Digitale Elektronik LCD Probleme mit Timerinterrupt


von Luke (Gast)


Lesenswert?

Hallo!

Ich programmiere einen Atmega 168 in Assembler. Meine Elektronik besteht 
aus einem LCD display, Drehimpulsgeber, Ausgangs- und Eingangsregister 
usw.

Ich lasse in einer Schleife dauernd Daten auf dem LCD anzeigen (schreibe 
alles ins DDRAM vom LCD).
Hat ja alles wunderbar geklappt. Brauchte jetzt aber einen Timer1 
Interrupt, da ich für Berechnungen einen genauen Zeitwert(5s) brauche. 
Seitdem ich mit dem Timer1 interrupt arbeite, zeigt das LCD nach ca. 3s 
in einer Zeile den Wert den ich anzeigen lasse versetzt um 3 Kästchen 
nochmal was nicht sein sollte.

Also im Grunde zeigt es Blödsinn an. Ich nehme an da der Interrupt 
irgendwann unterbricht gibt es beim LCD Probleme und er zeigt manche 
sachen falsch an?

PS: LCD ist nicht zeitkritisch. ALso es würde auch ausreichen alle 2 
sekunden zu aktualisieren ( Bis auf den Drehimpulsgeber, den muss ich so 
schnell als möglich aktualisieren)

Bin dankbar um Hilfe.

Liebe Grüße
Luke

von Karl H. (kbuchegg)


Lesenswert?

Luke schrieb:

> Also im Grunde zeigt es Blödsinn an. Ich nehme an da der Interrupt
> irgendwann unterbricht gibt es beim LCD Probleme und er zeigt manche
> sachen falsch an?

Nö.
Die Ansteuerung eines LCD ist insofern nicht zeitkritisch, als ein 
Interrupt an jeder Stelle unterbrechen darf, sofern er
* das SREG sichert und wieder herstellt
* nichts an den restlichen gebrauchten Registern verändert

D.h. wenn die ISR sauber programmiert ist, und der CPU Zustand nach der 
ISR exakt demjenigen vor Aufruf der ISR entspricht, dann gibt es auch 
keine Probleme. Denn: die ANsteuerung ist insofern nicht zeitkritisch, 
als alle Vorgänge zwar eine UNtergrenze für einzuhaltende Zeiten haben, 
aber keine Obergrenze. Wenn sich der Aufruf der ISR also nur dadurch 
bemerkbar macht, dass zwischen 2 Befehlen in der LCD Ausgabe etwas mehr 
Zeit vergeht als gewöhnlich, dann ist das überhaupt kein Problem. Aber: 
Das darf auch nur die einzige Art und Weise sein, wie sich der ISR 
Aufruf an dieser Stelle bemerkbar macht! Alle restlichen gebrauchten 
Register MÜSSEN ihre Werte behalten!

> Bin dankbar um Hilfe.

Dein Programm?
Aber ehe du es postest: Überprüf mal, ob deine ISR insbesondere das SREG 
sichert und wiederherstellt, bzw. alle Register, die auch in den LCD 
Routinen benutzt werden. WEnn du dir nicht sicher bist, dann sichere in 
der ISR ALLE benutzten Register und stell sie am Ende der ISR wieder 
her. So bist du erst mal auf der sicheren Seite, auch wenn du unter 
Umständen etwas Zeit verbrutzelst.

von Luke (Gast)


Lesenswert?

Danke für die schnelle Antwort!

Also habe jetzt meine ganzen Arbeitsregister in der ISR gesichert.
Ich hab in meinem Programm jedoch sehr viele Speicherregister die sich 
im dseg befinden um alle möglichen Werte abzuspeichern. Müssen diese 
Register ebenfalls abgespeichert werden?
Ich denke nur die Arbeitsregister?!

Hab ebenfalls viele Strings, die ich dann in der LCD schleife aufrufe.

Habe aber dasselbe Problem schonmal gehabt, dass mir das LCD versetzt 
Blödsinn anzeigt (da war die ISR noch nicht aktiv) und da ist mir 
aufgefallen dass ich in jedem Schleifendurchlauf den Cursor vom LCD 
ausschalte. Habe dies dann nur einmal in der Initialisierung des LCD 
gmacht und dann war es weg.

Kanns da noch immer ein Problem geben?

LG

von Luke (Gast)


Lesenswert?

Das sind die Programmausschnitte vom Timer1 interrupt den ich verwende. 
Ist hier ein Fehler erkennbar?
Interrupt funktioniert ja super nur eben macht dann das LCD was es will.

.cseg

.org 0x0000
  rjmp  init

.org OVF1addr
  jmp timer1_overflow



Timer1_init:

  ldi    temp,0b00000000
  sts    TCCR1A,temp

  ldi    temp,0b00000010
  sts    TCCR1B,temp

  ldi    temp,0b00000001
  sts    TIMSK1,temp

  ret


timer1_overflow:
  push  temp
  push  temp3
  push  temp2
  push   temp1 ; temp 1 sichern

  in     temp1,sreg ; SREG sichern


  call  Berechnung


  out   sreg,temp1 ; sreg wieder herstellen
  pop   temp1
  pop  temp
  pop  temp3
  pop  temp2

  reti

von Karl H. (kbuchegg)


Lesenswert?

Luke schrieb:

> timer1_overflow:
>   push  temp
>   push  temp3
>   push  temp2
>   push   temp1 ; temp 1 sichern
>
>   in     temp1,sreg ; SREG sichern
>
>
>   call  Berechnung
>
>
>   out   sreg,temp1 ; sreg wieder herstellen
>   pop   temp1
>   pop  temp
>   pop  temp3
>   pop  temp2

Vergleich mal die Reihenfolgen :-)

Die müssen schon sauber ineinander geschachtelt sein, so dass die pop 
die exakte Umkehrung der push sind!

  push temp             <----------------+
  push temp1            <-------------+  |
                                      |  |
  ...                                 |  |
                                      |  |
  pop  temp1            <-------------+  |
  pop  temp             <----------------+


hast du hier eine Verdrehung, dann vertauscht du effektiv die Inhalte 
der Register bei einem ISR-Aufruf.

Ein Stack ist wie ein Stapel Teller. Den Teller den du als letztes 
drauflegst, den kriegst du auch als erstes wieder.

push X   heißt     Inhalt vom Teller X auf den Stack oben drauf
pop  X   heißt     obersten Inhalt vom Stack auf den Teller X legen

Da sucht sich keiner zusammen, wie die korrekte Reihenfolge sein muss. 
Das musst schon du machen!

von Karl H. (kbuchegg)


Lesenswert?

1
   in     temp1,sreg ; SREG sichern
2
3
4
   call  Berechnung
5
6
7
   out   sreg,temp1 ; sreg wieder herstellen

aber nur, wenn innerhalb von 'Berechnung' das temp1 nicht verändert 
wurde. Wird es das, dann schreibst du irgendwas nach SREG zurück, aber 
sicher nicht den Wert, den du dir vor dem call Berechnung in temp1 
'gesichert' hast.

von Peter D. (peda)


Lesenswert?

Luke schrieb:
> Ich nehme an da der Interrupt
> irgendwann unterbricht gibt es beim LCD Probleme und er zeigt manche
> sachen falsch an?

Nur wenn der Interrupt auch auf das LCD ausgibt. Das geht schief. Das 
LCD ist nicht reentrant.

von Icke ®. (49636b65)


Lesenswert?

Luke schrieb:
> ldi    temp,0b00000000
>   sts    TCCR1A,temp
>
>   ldi    temp,0b00000010
>   sts    TCCR1B,temp
>
>   ldi    temp,0b00000001
>   sts    TIMSK1,temp

Was mir noch auffällt, m.E. ist "out" der richtige Befehl, um 
I/O-Register zu beschreiben und nicht "sts".

von Karl H. (kbuchegg)


Lesenswert?

Icke ®. schrieb:
> Luke schrieb:
>> ldi    temp,0b00000000
>>   sts    TCCR1A,temp
>>
>>   ldi    temp,0b00000010
>>   sts    TCCR1B,temp
>>
>>   ldi    temp,0b00000001
>>   sts    TIMSK1,temp
>
> Was mir noch auffällt, m.E. ist "out" der richtige Befehl, um
> I/O-Register zu beschreiben und nicht "sts".

Stimmt schon so wie er's hat.
Beim Mega168 sind diese Register per OUT nicht erreichbar.

von Icke ®. (49636b65)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Stimmt schon so wie er's hat.
> Beim Mega168 sind diese Register per OUT nicht erreichbar.

Aha, wieder was gelernt.

von Karl H. (kbuchegg)


Lesenswert?

Icke ®. schrieb:
> Karl Heinz Buchegger schrieb:
>> Stimmt schon so wie er's hat.
>> Beim Mega168 sind diese Register per OUT nicht erreichbar.
>
> Aha, wieder was gelernt.

:-)
Ist einer der Gründe, warum ich reale Programme nicht in Assembler 
schreibe. Das ist mir zu mühsam, mich immer daran zu erinnern bzw. 
nachzuschlagen, bei welchem Prozessor ein OUT sein muss und bei welchem 
ein STS. Das kann der Compiler viel zuverlässiger.

von Peter D. (peda)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Das kann der Compiler viel zuverlässiger.

Der Assembler kann das auch (mit Macros):
1
;---------------------- macros for extended IO access -------------------
2
.macro  xout
3
.if     @0 > 0x3F
4
        sts     @0, @1
5
.else
6
        out     @0, @1
7
.endif
8
.endmacro
9
;---------------------------
10
.macro  xin
11
.if     @1 > 0x3F
12
        lds     @0, @1
13
.else
14
        in      @0, @1
15
.endif
16
.endmacro
17
;---------------------------
18
.macro  xlpm
19
.if FLASHEND > 0x7FFF
20
        elpm    @0, @1
21
.else
22
        lpm     @0, @1
23
.endif
24
.endmacro
25
;------------------------------------------------------------------------

von Luke (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> in     temp1,sreg ; SREG sichern
>
>
>    call  Berechnung
>
>
>    out   sreg,temp1 ; sreg wieder herstellen
>
> aber nur, wenn innerhalb von 'Berechnung' das temp1 nicht verändert
> wurde. Wird es das, dann schreibst du irgendwas nach SREG zurück, aber
> sicher nicht den Wert, den du dir vor dem call Berechnung in temp1
> 'gesichert' hast.



Ja das stimmt hab das schon vor push und nach pop geschrieben. Hab den 
Fehler schon eingrenzen können. Das Problem dürfte irgendwo in meiner 
Berechnung liegen. Schalte ich diese weg, dann gibt es kein Problem.

Muss mir jez anschaun was ich da für einen Käse programmiert hab gg

Danke für die vielen Antworten!

LG

von Soifssib (Gast)


Lesenswert?

home

von Peter D. (peda)


Lesenswert?

Luke schrieb:
> Das Problem dürfte irgendwo in meiner
> Berechnung liegen.

Ja, das ist schon fast unheimlich. Zu 99% wird bei Codeausschnitten 
immer nur der Teil gepostet, an dem es nicht (allein) liegt.

Auch scheint eine geradezu krankhafte Furcht zu bestehen, den Forum Host 
zu überlasten. Dabei wäre es doch das aller einfachste, den 
vollständigen Code als Anhang (Zip bei mehreren Dateien) zu senden.

von Icke ®. (49636b65)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Ist einer der Gründe, warum ich reale Programme nicht in Assembler
> schreibe.

Ja, ich stoße mittlerweile auch an die Grenzen der Fußlatscherei und 
werde mich wohl oder übel mit C anfreunden müssen. Die Syntax ist zwar 
gräßlich, aber vieles wird leichter zu erledigen sein.

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.