Forum: Mikrocontroller und Digitale Elektronik Mega8 Uhr auf UART ausgeben


von Friedrich K. (fiete)


Lesenswert?

Hallo leute,

Ich arbeite gerade das Tutorial durch und in Ermangelung eines LCD's 
habe ich mir gedacht, das Zeitsignal auf UART auszugeben.

Das funktioniert auch alles bestens nach ca. 12h arbeit (jaja, bin noch 
neu).

Das Vorgehen ist, dass ich den nicht bei null anfange zu zäheln, sondern 
bei der Ascii-Null (0x30). Das Ergebnis gebe ich dann sekündlich auf 
UART aus.

Was noch nicht zufriedenstellend ist:

Ich brauche dafür 9 Register. Denn jede Stelle an der Uhr (hh:mm:ss) 
bekommt ein Register, das sind schonmal 6. +2 Statusregister +temp1

Meine Frage:

Kann man das optimieren?
Welche stellen sind unschön?

Gruß, fiete
1
.include "m8def.inc"
2
3
.def temp1    =r16
4
.def flag    =r17
5
.def subcount  =r18
6
7
.def lsekunden  =r19
8
.def hsekunden  =r20
9
.def lminuten  =r21
10
.def hminuten  =r22
11
.def lstunden  =r23
12
.def hstunden  =r24
13
14
15
.org 0x0000       ;reset Handler
16
    rjmp main
17
18
.org OVF0addr      ;sprung zu 't0_OverFlow' bei Timer0 - Overflow
19
    rjmp t0_OverFlow
20
21
22
23
;#################################################################################
24
;#  AUTOMATIC BAUDRATE CALCULATION  -- AUTOMATIC BAUDRATE CALCULATION  -- AUTOMA #
25
;#################################################################################
26
27
.equ F_CPU = 3686400                            ; Systemtakt in Hz
28
.equ BAUD  = 9600                               ; Baudrate
29
 
30
; Berechnungen
31
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  ; clever runden
32
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))     ; Reale Baudrate
33
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)  ; Fehler in Promille
34
 
35
.if ((BAUD_ERROR>10) || (BAUD_ERROR<-10))       ; max. +/-10 Promille Fehler
36
  .error "Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!"
37
.endif
38
;#################################################################################
39
40
41
42
main:
43
    ldi temp1, HIGH(RAMEND)        ;init stack
44
    out SPH, temp1
45
    ldi temp1, LOW(RAMEND)
46
    out SPL, temp1
47
48
49
    ldi temp1, HIGH(UBRR_VAL)      ;set baudrate
50
    out UBRRH, temp1
51
    ldi temp1, LOW(UBRR_VAL)
52
    out UBRRL, temp1
53
54
55
    ldi temp1, (1<<URSEL) | (3<<UCSZ0)  ;set framerate to 8Bit
56
    out UCSRC, temp1
57
58
    
59
    sbi UCSRB, TXEN      ;init UCSRB
60
61
62
    ldi temp1, 0b00000100  ;set divider
63
    out TCCR0, temp1
64
    
65
66
    ldi temp1, 1<<TOIE0    ;allow interrupt at T0 overflow
67
    out TIMSK, temp1
68
    
69
    sei            ;init interrupt
70
71
72
reset1:
73
    ldi lsekunden,  47    
74
    ldi lminuten,  48    ;register mit Startwerten laden.
75
    ldi lstunden,  48    ;48 entspricht einer Ascii-null
76
    ldi hsekunden,  48    
77
    ldi hminuten,  48    ;l = low und entspricht 'einern'
78
    ldi hstunden,  48    ;h = high und entspricht 'zehnern'
79
    clr subcount
80
    clr flag
81
    
82
    sei
83
    
84
85
86
loop:   cpi   flag,2
87
    breq reset1
88
    cpi   flag,0        ;Warteschleife, während Timer läuft.
89
    breq loop        ;Interrupt durch Timer0 Overflow
90
91
    mov   temp1,hstunden    ;Dezimal-Stelle Stunden in temp1
92
    rcall send        ;Dann senden
93
94
    mov   temp1,lstunden    
95
    rcall send
96
97
    ldi   temp1,':'
98
    rcall send
99
    
100
    mov   temp1,hminuten
101
    rcall send
102
103
    mov   temp1, lminuten
104
    rcall send
105
106
    ldi   temp1,':'
107
    rcall send
108
    
109
    mov   temp1,hsekunden
110
    rcall send
111
112
    mov   temp1,lsekunden
113
    rcall send
114
115
    ldi   temp1,10
116
    rcall send
117
    
118
    ldi   temp1,13
119
    rcall send
120
121
    nop            ;Warten, bis Senden komplett fertig
122
    nop
123
124
    ldi   flag,0        ;flag = 0 --> nicht mehr senden im loop
125
126
    rjmp loop        ;Sprung zu loop, bis nächst Interr.
127
128
129
send:
130
131
    sbis UCSRA,UDRE      ;warten auf Sendebereitschaft
132
    rjmp send
133
    
134
    out UDR, temp1      ;senden
135
    ret    
136
137
     
138
        
139
140
141
t0_OverFlow:
142
    
143
    push temp1        ;Statussicherung
144
    in   temp1,SREG
145
    push temp1
146
147
    inc   subcount      ;Wenn 14.Subcount-> weiter
148
    cpi   subcount,10      
149
    brne end_interrupt    ;          end_interrupt sonst
150
    
151
    ldi   flag,1        ;Flag setzen <--> 1s vergangen 
152
                ;triggert Output in loop
153
    clr   subcount      ;Subcount zurücksetzen
154
  
155
    inc   lsekunden      ;einer-sekunde um 1 erhöhen
156
    cpi   lsekunden,  58    ;maximum (10) erreicht?
157
158
    brne end_interrupt    ; nein --> interrupt beenden 
159
                ; (output in loop durch flag getriggert)    
160
  
161
    ldi   lsekunden,  48    ; ja -->  einer-sekunden auf '0'
162
    inc   hsekunden      ;      zehner-sek um 1 erhöhen
163
    cpi   hsekunden,  54    ;      maximum (6) erreicht?
164
165
    brne end_interrupt
166
  
167
    ldi   hsekunden,  48    ;  und so weiter ....
168
    inc   lminuten
169
    cpi   lminuten,   58
170
  
171
    brne end_interrupt
172
    
173
    ldi   lminuten,   48
174
    inc   hminuten
175
    cpi   hminuten,   54
176
177
    brne end_interrupt
178
179
    ldi   hminuten,  48
180
    inc   lstunden
181
182
    cpi   lstunden,  50    ;ist lstunden auf 2?
183
    breq twelve        ;ja --> auf 12 prüfen
184
185
continue:
186
    cpi   lstunden,  58    ;maximum (10) erreicht?
187
    brne end_interrupt    ;nein --> interrupt beenden
188
    
189
                ;ja
190
    ldi   lstunden,   48    ;lstunden auf 0 setzen 
191
    inc   hstunden      ;hstunden um 1 erhöhen
192
    
193
    rcall end_interrupt    ;interrupt beenden
194
    
195
196
twelve:  cpi   hstunden,  49    ;hstunden auf 1?
197
    brne continue           ;nein --> continue
198
    ldi   flag,2        ;ja, flag 2 setzen
199
                        ;initiiert sprung zu reset1
200
    pop   temp1
201
    out   SREG,temp1
202
    pop   temp1
203
    rcall loop
204
205
end_interrupt:
206
    pop   temp1
207
    out   SREG,temp1
208
    pop   temp1
209
    reti

von holger (Gast)


Lesenswert?

>Kann man das optimieren?

Klar, zähl im BCD Code. Viele RTCs machen das so.

von Falk B. (falk)


Lesenswert?

@  Friedrich Kpunkt (fiete)

>Das Vorgehen ist, dass ich den nicht bei null anfange zu zäheln, sondern
>bei der Ascii-Null (0x30). Das Ergebnis gebe ich dann sekündlich auf
>UART aus.

So weit, so gut.

>Ich brauche dafür 9 Register. Denn jede Stelle an der Uhr (hh:mm:ss)
>bekommt ein Register, das sind schonmal 6. +2 Statusregister +temp1

Naja, für den Anfang OK.

>Kann man das optimieren?

Durch Nutzung von Speicher: SRAM, siehe AVR-Tutorial: SRAM

>Welche stellen sind unschön?

Für den Anfnag ist das OK. Schönen Programmierstil lernst du u.a. durch 
Durcharbeiten des AVR-Tutorial

MFG
Falk

von spess53 (Gast)


Lesenswert?

Hi

Dann leg das Ganze doch im Ram ab.

MfG Spess

von Friedrich K. (fiete)


Lesenswert?

Danke für die Antworten.

Im RAM ablegen hatt ich auch schon gedacht, aber da muss ich noch viel 
drüber nachdenken, wie genau das geschehen soll. Aber ich hab schon 
ideen.

BCD-Code muss ich mir mal angucken, was das ist, und wie man das 
verwerten kann.

Noch eine Frage: gibt es ein "Skip If equal"? Ich habe es mir mühvoll 
zusammengebastelt, aber zufrieden bin ich damit nicht. Ware das folgende 
eine Möglichkeit: SREG in temp laden und dann Z-Flag mit Sbis checken

von Karl H. (kbuchegg)


Lesenswert?

Da kannst du dann auch gleich einen Branch if equal machen

Noch was
    ldi   hminuten,  48

Da frage ich mich doch gleich, was der spezielle Zahlenwert 48 mit den 
Minuten zu tun hat. Nach langen Analysen kommt man dann drauf, dass das 
eigentlich der ASCII Code von '0' ist. Schreib das doch gleich so hin

    ldi   hminuten, '0'

dann sieht man das gleich viel besser. Ein

    inc   lsekunden             ;einer-sekunde um 1 erhöhen
    cpi   lsekunden, '9' + 1    ;maximum (10) erreicht?

erzählt mir im Code schon viel mehr über die Absicht des Programmierers 
an dieser Stelle als ein

    inc   lsekunden      ;einer-sekunde um 1 erhöhen
    cpi   lsekunden,  58    ;maximum (10) erreicht?

wobei hier pikanterweise auch noch die zahlenmässige Ähnlichkeit zu 59 
(der höchsten Sekundenzahl in einer Minute) hinzukommt und man an einen 
Tippfehler denken könnte

von Falk B. (falk)


Lesenswert?

@  Friedrich Kpunkt (fiete)

>Noch eine Frage: gibt es ein "Skip If equal"? Ich habe es mir mühvoll
>zusammengebastelt, aber zufrieden bin ich damit nicht. Ware das folgende
>eine Möglichkeit: SREG in temp laden und dann Z-Flag mit Sbis checken

Hör auf das Rad neu zu erfinden und mach es so wie der Rest der Welt.

AVR-Tutorial: Vergleiche

von Friedrich K. (fiete)


Lesenswert?

Danke für die Kritik Karlheinz. Genau sowas ist enorm hilfreich, vor 
allem, weil man selber schnell den Überblick verlieren kann. Freut mich 
sehr, dass du dir die Mühe gemacht hast, das anzugucken.

Falk: Die sache ist, dass ich absoluter neuling bin. Es so zu machen 
'wie der rest der Welt' ist nicht so einfach, wenn man nicht weiß, wie 
der Rest der Welt es macht.
Ein "Skip if Equal" gibt es offensichtlich nicht, und die Lösung mit 
breq erfordert das setzen eines neuen Zweiges. Das wollte ich mir 
einfach nur ersparen und wissen, wie der Rest der Welt ein solches 
Problem löst.
Sei nachsichtig mit solchen Anfängerfragen, ich frage nicht um euch zu 
nerven, sondern weil ich eben nicht das Rad ständig neu erfinden möchte.

Gruß, fiete

von Karl H. (kbuchegg)


Lesenswert?

Friedrich Kpunkt schrieb:

> Ein "Skip if Equal" gibt es offensichtlich nicht, und die Lösung mit
> breq erfordert das setzen eines neuen Zweiges.

Was willst du uns damit sagen?

> Das wollte ich mir
> einfach nur ersparen und wissen, wie der Rest der Welt ein solches
> Problem löst.

Mittels branch.
Diese Skip Befehle sind zwar toll aber so universell eigentlich auch 
nicht. branch (in allen Varianten) kann man immer benutzen, skip ist 
eher die Aussnahme, die manchmal geht, wenn man auf ein bestimmtes Bit 
in einem Register testen will. Völlig sinnlos ist es aber, das SREG 
zuerst in ein Register zu transferieren um dann ein bestimmtes Bit zu 
testen. Genau dafür gibt es die branch Befehle, die das SREG direkt zur 
Entscheidung benutzen. Und kein Mensch verbietet, dass das Ziel eines 
Branch nur eine Instruktion entfernt ist und somit effektiv ganz einfach 
die nächste Anweisung übersprungen (ge-skippt) wird.

von Friedrich K. (fiete)


Lesenswert?

okok, ich benutze branch.

Was beim Branch ein bissel das problem ist, dass ich noch nicht genau 
begreife, wo der ret hin geht.
Er bekommt die Adresse ja aus dem Satack. Läd jeder Branch eine 
Rücksprung-Adresse in den Stack. Warum ich mich so gegen den Branch 
wehre ist, dass ich den Stack noch nicht so recht verstanden habe, und 
bei ret schon mehrmals mist passiert ist.

Ich werde mich wohl nochmal mit dem Stack auseinandersetzen müssen...

gruß, fiete

von Karl H. (kbuchegg)


Lesenswert?

Friedrich Kpunkt schrieb:
> okok, ich benutze branch.
>
> Was beim Branch ein bissel das problem ist, dass ich noch nicht genau
> begreife, wo der ret hin geht.

Was für ein ret?

> Er bekommt die Adresse ja aus dem Satack.

Nein.

> Läd jeder Branch eine
> Rücksprung-Adresse in den Stack.

Hä.
Das eine hat mit dem anderen nichts zu tun!

branch if equal
kann man übersetzen mit: Spring dort und da hin, wenn das Zero-Flag 
gesetzt ist. Da in der Überwiegenden Mehrzahl der Fälle, so etwas nach 
einem Vergleich gemacht wird, und das Zero-Flag in diesem Fall anzeigt, 
dass die beiden zu vergleichenden Werte gleich gross sind, ist ein 
gesetztes Zero-Flag daher in den meisten Fällen identisch mit: equal

> Warum ich mich so gegen den Branch
> wehre ist, dass ich den Stack noch nicht so recht verstanden habe, und
> bei ret schon mehrmals mist passiert ist.

Da brauchst du keinen Stack und auch keinen ret.

Du redest von 'Branch' denkst aber an 'Call'
Das ist eine ganz andere Baustelle


1
   ldi r16, 0x20   ; lade irgendwas in r16
2
   ldi r17, 0x30   ; lade was anderes nach r17
3
4
   cpi r16, r17    ; vergleiche r16 mit r17
5
   breq mach_was_bei_gleich   ; wenn r16 und r17 gleich waren, dann
6
                              ; gehst bei mach_was_bei_gleich weiter
7
8
                              ; andernfalls geht es hier weiter
9
   ... code bei Ungleich
10
11
mach_was_bei_gleich:
12
   ... code bei Gleichheit

kein Mensch braucht da einen Stack oder ein return für irgendetwas.
Was ich nicht verstehe: In deinem Code wimmelt es doch nur so von brne 
(also Branch if not equal). Warum hast du jetzt so grosse Probleme mit 
einem breq (Branch if equal). Das ist doch einfach nur die Umkehrung der 
Bedingung wann der branch genommen wird.

von Friedrich K. (fiete)


Lesenswert?

Ah Haaa!

ok, das erklärt einiges kopfzerbrechen das ich bisweilen hatte. Ich noch 
viel zu lernen habe...

von Friedrich K. (fiete)


Lesenswert?

Karl heinz Buchegger schrieb:
> Was ich nicht verstehe: In deinem Code wimmelt es doch nur so von brne
> (also Branch if not equal). Warum hast du jetzt so grosse Probleme mit
> einem breq (Branch if equal). Das ist doch einfach nur die Umkehrung der
> Bedingung wann der branch genommen wird.

Ja, ich habe diese Codeteile aus dem Tutorial übernommen (nicht kopiert! 
ich schreibe alles selbst - ab), geglaubt sie verstanden zu haben und 
dann halt verwendet. Ich dachte der Branch ist eine art interrupt, der 
dann irgendwann wieder beendet wird. Aber nun weiß ich, dass ich das mit 
call verwechsel. Jetzt verstehe ich auch euer unverständnis.

von Friedrich K. (fiete)


Angehängte Dateien:

Lesenswert?

So, habe jetzt nen bissel weiter dran rumgebastelt. Jetzt benötigt man 
kein einziges Register, weil alles im RAM abgelegt wird.

Nun habe ich allerdings noch ein Problem. Die Uhr geht ca. 1%nach und 
ich hab nach vielen Überlegungen immernoch keinen Schimmer, woran es 
liegt.

Auch zahlreiche Threats hab ich schon durchgelesen, aber nur die zwei 
Fehlerquellen gefunden:

-"Schaltsekunden" fallen weg, da CTC Prescaler auf 1024 und Overflow: 
3600-1

- Der externe Takt des STK500 ist falsch (3.683400 MHz) Das kann ich 
leider nicht überprüfen, weder direkt noch indirekt.

- Es werden Interrupts verschluckt
das kann ich mir allerdings wirklich nicht vorstellen, weil ich 
Interruptzeiten von max 70 takten habe. Der nächste Interrupt ist um 5 
Größenordungen höher.

Ferner ist die Ausgabe im Hyper-Terminal komisch. Das Terminal ist ja in 
zwei Bereiche geteilt. Diese werden aber irgendwie unterschiedlich 
dargestellt. Im weißen Bereich sind IMMER alle Zahlen hübsch 
untereinander. Scrollt man nach oben, dann bekommt man in unregelmäßigen 
Abständen Fragmente. Hab mal einen Screenshot angehängt.

Ideen, was da los ist?

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.