Forum: Mikrocontroller und Digitale Elektronik Fehler in Multiplex Routine (führende Null)


von Peter (Gast)


Lesenswert?

Hallo,
ich habe aus dem Tutorial die Multiplexroutine für eine Uhr angewendet.

Es werden 4 7-Segment Anzeigen angesprochen um die Uhrzeit anzuzeigen.
Nun habe ich versucht, die erste Stelle (10er Stunden) in der Anzeige
auszuschalten wenn die 10er Stunden < 1 sind (führende Null 
unterdrücken)

Das funktioniert eigentlich ganz gut, aaaaber............
Nach 4 manchmal 5 Tagesdurchläufen (24 Std) bleibt die komplette
Anzeige dunkel und zeigt nichts mehr an. Meine Vermutung ist, dass
irgendwie der Speicher überläuft. Das ganze ist auf einem ATmega 8
in Assembler geschrieben. Hier der Code der Multiplexroutine.

Die vom Tutorial abweichenden Zeilen sind mit -------------------------
bzw. Kommentar gekennzeichnet. Kann mir jemand erklären was hier
falsch gemacht wurde?
1
Multiplexvorgang:
2
    
3
    push   temp
4
    push   temp1
5
    in     temp,sreg
6
    push   temp
7
    push   ZL
8
    push   ZH
9
10
    ldi    temp1,0
11
    out    Segment_PORT,temp1      
12
    ldi    ZL, low(Segment0)              
13
    ldi    ZH, high(Segment0)            
14
    lds    temp, NextSegment  
15
    add    ZL, temp
16
    adc    ZH, temp1
17
    
18
    ld     temp,Z                                   
19
    
20
    out    Anzeige_PORT, temp
21
   
22
    lds    temp1, NextDigit                  
23
    com    temp1                                
24
    out    Segment_PORT,temp1
25
    com    temp1
26
    lds    temp, NextSegment    
27
28
    inc    temp
29
    sec
30
    rol    temp1                                   
31
   
32
    cpi    StundenB, 1  ; betragen die StundenB  1? --------------
33
    brlo   merker4      ; brlo verzweigt wenn die StundenB < 1 (also 0 )
34
    
35
    cpi    temp1,0b11101111   
36
    brne   merker2
37
    ldi    temp,0
38
    ldi    temp1,0b11111110
39
40
    cpi    temp1,0b11111110 ; ----------------------------
41
    brne   merker2 ; ---------------------------------------
42
43
merker4:
44
    
45
    cpi    temp1,0b11110111   ; dritte Stelle erreicht? -------------
46
    brne   merker2 ; -----------------------------------------
47
    ldi    temp,0 ; ---------------------------------------------
48
    ldi    temp1, 0b11111110 ; --------------------------------------
49
50
merker2:
51
    sts    NextSegment, temp
52
    sts    NextDigit, temp1
53
    
54
    pop    ZH
55
    pop    ZL
56
    pop    temp
57
    out    sreg,temp
58
    pop    temp1
59
    pop    temp
60
61
reti

von Thomas (kosmos)


Lesenswert?

wenn du 3 mal pusht musst du auch 3 mal poppen.

Du brauchst in einer Interruptroutine nur das zu pushen was du auch 
veränderst, wenn die IRR nur temp verwendet brauchst du nicht temp2 
sichern.

Da läuft höchstwarscheinlich der Stackpointer über.

Du könntest im Programm eine Abfrage machen ob z.B. die Stelle bei 90% 
RAM nicht mehr FF ist irgendwas zum Blinken anfängt.

Simulier es doch mit dem AVR Studio durch, mach einen Breakpoint und 
lass den Simulator laufen.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Peter schrieb:
> Meine Vermutung ist, dass
> irgendwie der Speicher überläuft.
Falls diese Vermutung stimmt, dann passiert das außerhalb des gelisteten 
Codes.

Peter schrieb:
> Nach 4 manchmal 5 Tagesdurchläufen (24 Std) bleibt die komplette
> Anzeige dunkel
Immer zur gleichen Zeit?
Oder immer nach der gleichen Zeit?

Und: wie sieht die Hardware aus?

von Route_66 H. (route_66)


Lesenswert?

Thomas O. schrieb:
> wenn du 3 mal pusht musst du auch 3 mal poppen.

Welche Software hast Du analysiert?
Er PUSHt 5 mal und POPt 5 mal, beides in der richtigen Reihenfolge

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Thomas O. schrieb:
> Da läuft höchstwarscheinlich der Stackpointer über.
Das ginge aber viel schneller...
Wenn ż. B. mit 200 Hz gemultiplext wird, dann ist nach 5 Sekunden 1k 
Speicher verbraucht. Und nach 5 Minuten sind 60 kByte durch. Da dürfte 
also viel früher was passieren.

: Bearbeitet durch Moderator
von Thomas (kosmos)


Lesenswert?

sorry sind 5 und 5, aber der Code ist ja auch nicht vollständig. Wird 
jeder IR-Aufruf mit reti beendet? Nicht da da weitergesprungen wird ohne 
die IRR zu beenden.

von Peter (Gast)


Lesenswert?

Erst einmal vielen Dank für die Teilnahmen.

Um einen Tagesdurchlauf zu simulieren setzte ich im Timer1 den
OCR1AH auf 100 (normal ist dieser Wert 39.999 (4 MHz / 100)
1
    ldi temp1, high(100);4.000.000 / 100 = 40.000
2
    out OCR1AH, temp1
3
    ldi temp1, low(100) ;  39999
4
    out OCR1AL, temp1
5
    ldi temp1, (1 << CS10)| (1<<WGM12) 
6
    out TCCR1B, temp1
7
    ldi temp1, 1 << OCIE1A      
8
    out TIMSK, temp1
9
10
    ldi temp1, ( 1 << CS00 ) | ( 1 << CS00 ); Vorteiler für TIMER0 + 1
11
    out TCCR0,temp1
12
    ldi temp1, (1 << OCIE1A) | ( 1 << TOIE0 ) ; OCIE1A: & TOEI0: Interrupt bei Timer Compare u. Timer0 Overvlow
13
    out TIMSK, temp1
14
    ldi cnt, 0 
15
    ldi temp2, 0
16
    sei

Für 4 oder mal 5 Tagesdurchläufe wird die führende Null unterdrückt.
Dann schaltet die komplette Anzeige auf dunkel wenn 23:59 auf 0:00
wechselt - und nur dann (nie zu einer anderen Zeit)

von Thomas (kosmos)


Lesenswert?

Vielleicht liegst ja am Stundensprung. Aber ohne Infos kann man nur 
vermuten oder raten.

von Route_66 H. (route_66)


Lesenswert?

Peter schrieb:
> Erst einmal vielen Dank für die Teilnahmen.

Merkst Du nicht, dass du liefern musst?

Lothar M. schrieb:
> Peter schrieb:
>> Meine Vermutung ist, dass
>> irgendwie der Speicher überläuft.
> Falls diese Vermutung stimmt, dann passiert das außerhalb des gelisteten
> Codes.

Gib Deinen Bezeichnern temp, temp1,... verständliche Namen. Kommentiere 
den Quelltext brauchbar und dann hänge ihn hier an!

von Peter (Gast)


Lesenswert?

Hallo, nochmal eine Zusatzinformation:

Wenn ich die von mir eingefügten Zeilen (siehe Posting 1 )
auskommentiere, läuft es einwandfrei (aber mit führender Null)

Ich habe nur die gekennzeichneten Zeilen hinzugefügt um die
führende Null zu unterdrücken, dann allerdings tritt das beschriebene
Problem auf - und zwar nur wenn die Anzeige von 4 Stellen (23:59)
auf 3 Stellen (0:00) wechselt (4 oder 5 mal funktioniert die 
Umschaltung)

Bin Ratlos

von foobar (Gast)


Lesenswert?

Ich finde es ziemlich merkwürdig, die 0-Unterdrückung in der 
Multiplex-Routine zu machen - insb wenn die noch auf ein anscheinend 
global reserviertes Register (StundenB) zugreifen muß.  Wie auch immer, 
so sollte es gehen:
1
    ...
2
    inc    temp
3
    sec
4
    rol    temp1
5
6
    ; reset after 4th digit or after 3rd when StundenB is 0
7
    cpi    temp,4
8
    breq   merker4
9
10
    cpi    temp,3
11
    brne   merker2
12
    cpi    StundenB,0
13
    brne   merker2
14
15
    ; reset to first digit
16
merker4:
17
    ldi    temp,0
18
    ldi    temp1,0b11111110
19
20
    ; store for next interrupt
21
merker2:
22
    sts    NextSegment,temp
23
    sts    NextDigit,temp1
24
    ...

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Peter schrieb:
> cpi    StundenB, 1  ; betragen die StundenB  1? --------------
> brlo   merker4      ; brlo verzweigt wenn die StundenB < 1 (also 0 )

Nur mit ISR code und ohne Deklarationen wird dir wohl kaum
jemand helfen können.

Auf jeden Fall machst du es falsch.
1
  1) In der ISR-Routine:
2
        Du gibst Digit für Digit aus, also zuerst auf Digit 1
3
        prüfen (Stunden Zehner). Wenn es nicht Digit 1 ist, brauchst
4
        du dich nicht weiter um die führende Null zu kümmern - einfach
5
        deine Zeilen überspringen.
6
        Wenn Digit 1 und Wert=0, alle Segmente ausschalten.
oder
1
 
2
2) In MainLoop:
3
      Wenn in RAM für Digit 1 Wert Null steht, einfach mit SPACE
4
     (alle Segmente aus) ersetzen.

: Bearbeitet durch User
von H.Joachim S. (crazyhorse)


Lesenswert?

Ich würde ausschliesslich Variante2 benutzen. Die Ausgabe gibt stur das 
aus was da steht, das ist auch logischer.
-macht die ISR einfacher, schneller und universeller
-willst du vielleicht irgendwann tatsächlich eine 0 anzeigen, dann fängt 
man an in der der ISR rumzudoktern

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Peter schrieb:
> Für 4 oder mal 5 Tagesdurchläufe wird die führende Null unterdrückt.
> Dann schaltet die komplette Anzeige auf dunkel wenn 23:59 auf 0:00
> wechselt - und nur dann (nie zu einer anderen Zeit)
Probier mal aus, den Interrupt während der Berechnung der Neuberechnung 
der Anzeige zu sperren. Denn es sieht so aus, wie wenn du in der ISR auf 
"halbfertige" Displaydaten zugreifen würdest.

foobar schrieb:
> Ich finde es ziemlich merkwürdig, die 0-Unterdrückung in der
> Multiplex-Routine zu machen
Ich würde auch in der Anzeigeroutine einfach nur genau das anzeigen, was 
im Speicher steht. Dass dort was brauchbares steht, dafür ist die 
Routine zuständig, die die Anzeigedaten berechnet.

Der Postbote, kontrolliert ja auch nicht den Inhalt der Briefe, die er 
zustellen muss, auf Rechtschreibfehler, sondern er stellt sie einfach 
nur zu. Für Rechtschreibfehler ist der Schreiber zuständig.

: Bearbeitet durch Moderator
von Thomas (kosmos)


Lesenswert?

Einfach einen Vergleich ob Stunde >0 und <10 und dann überspringt du die 
Ausgabe für die eine 7Segment LED

von Peter D. (peda)


Lesenswert?

foobar schrieb:
> Ich finde es ziemlich merkwürdig, die 0-Unterdrückung in der
> Multiplex-Routine zu machen

Ja, das verbraucht nur unnötig CPU-Zeit. Man macht es natürlich bei der 
Dezimal- und 7-Segmentumwandlung der Variablen.

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.