mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Mega8 Uhr auf UART ausgeben


Autor: Friedrich K. (fiete)
Datum:

Bewertung
0 lesenswert
nicht 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
.include "m8def.inc"

.def temp1    =r16
.def flag    =r17
.def subcount  =r18

.def lsekunden  =r19
.def hsekunden  =r20
.def lminuten  =r21
.def hminuten  =r22
.def lstunden  =r23
.def hstunden  =r24


.org 0x0000       ;reset Handler
    rjmp main

.org OVF0addr      ;sprung zu 't0_OverFlow' bei Timer0 - Overflow
    rjmp t0_OverFlow



;#################################################################################
;#  AUTOMATIC BAUDRATE CALCULATION  -- AUTOMATIC BAUDRATE CALCULATION  -- AUTOMA #
;#################################################################################

.equ F_CPU = 3686400                            ; Systemtakt in Hz
.equ BAUD  = 9600                               ; Baudrate
 
; Berechnungen
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  ; clever runden
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))     ; Reale Baudrate
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)  ; Fehler in Promille
 
.if ((BAUD_ERROR>10) || (BAUD_ERROR<-10))       ; max. +/-10 Promille Fehler
  .error "Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!"
.endif
;#################################################################################



main:
    ldi temp1, HIGH(RAMEND)        ;init stack
    out SPH, temp1
    ldi temp1, LOW(RAMEND)
    out SPL, temp1


    ldi temp1, HIGH(UBRR_VAL)      ;set baudrate
    out UBRRH, temp1
    ldi temp1, LOW(UBRR_VAL)
    out UBRRL, temp1


    ldi temp1, (1<<URSEL) | (3<<UCSZ0)  ;set framerate to 8Bit
    out UCSRC, temp1

    
    sbi UCSRB, TXEN      ;init UCSRB


    ldi temp1, 0b00000100  ;set divider
    out TCCR0, temp1
    

    ldi temp1, 1<<TOIE0    ;allow interrupt at T0 overflow
    out TIMSK, temp1
    
    sei            ;init interrupt


reset1:
    ldi lsekunden,  47    
    ldi lminuten,  48    ;register mit Startwerten laden.
    ldi lstunden,  48    ;48 entspricht einer Ascii-null
    ldi hsekunden,  48    
    ldi hminuten,  48    ;l = low und entspricht 'einern'
    ldi hstunden,  48    ;h = high und entspricht 'zehnern'
    clr subcount
    clr flag
    
    sei
    


loop:   cpi   flag,2
    breq reset1
    cpi   flag,0        ;Warteschleife, während Timer läuft.
    breq loop        ;Interrupt durch Timer0 Overflow

    mov   temp1,hstunden    ;Dezimal-Stelle Stunden in temp1
    rcall send        ;Dann senden

    mov   temp1,lstunden    
    rcall send

    ldi   temp1,':'
    rcall send
    
    mov   temp1,hminuten
    rcall send

    mov   temp1, lminuten
    rcall send

    ldi   temp1,':'
    rcall send
    
    mov   temp1,hsekunden
    rcall send

    mov   temp1,lsekunden
    rcall send

    ldi   temp1,10
    rcall send
    
    ldi   temp1,13
    rcall send

    nop            ;Warten, bis Senden komplett fertig
    nop

    ldi   flag,0        ;flag = 0 --> nicht mehr senden im loop

    rjmp loop        ;Sprung zu loop, bis nächst Interr.


send:

    sbis UCSRA,UDRE      ;warten auf Sendebereitschaft
    rjmp send
    
    out UDR, temp1      ;senden
    ret    

     
        


t0_OverFlow:
    
    push temp1        ;Statussicherung
    in   temp1,SREG
    push temp1

    inc   subcount      ;Wenn 14.Subcount-> weiter
    cpi   subcount,10      
    brne end_interrupt    ;          end_interrupt sonst
    
    ldi   flag,1        ;Flag setzen <--> 1s vergangen 
                ;triggert Output in loop
    clr   subcount      ;Subcount zurücksetzen
  
    inc   lsekunden      ;einer-sekunde um 1 erhöhen
    cpi   lsekunden,  58    ;maximum (10) erreicht?

    brne end_interrupt    ; nein --> interrupt beenden 
                ; (output in loop durch flag getriggert)    
  
    ldi   lsekunden,  48    ; ja -->  einer-sekunden auf '0'
    inc   hsekunden      ;      zehner-sek um 1 erhöhen
    cpi   hsekunden,  54    ;      maximum (6) erreicht?

    brne end_interrupt
  
    ldi   hsekunden,  48    ;  und so weiter ....
    inc   lminuten
    cpi   lminuten,   58
  
    brne end_interrupt
    
    ldi   lminuten,   48
    inc   hminuten
    cpi   hminuten,   54

    brne end_interrupt

    ldi   hminuten,  48
    inc   lstunden

    cpi   lstunden,  50    ;ist lstunden auf 2?
    breq twelve        ;ja --> auf 12 prüfen

continue:
    cpi   lstunden,  58    ;maximum (10) erreicht?
    brne end_interrupt    ;nein --> interrupt beenden
    
                ;ja
    ldi   lstunden,   48    ;lstunden auf 0 setzen 
    inc   hstunden      ;hstunden um 1 erhöhen
    
    rcall end_interrupt    ;interrupt beenden
    

twelve:  cpi   hstunden,  49    ;hstunden auf 1?
    brne continue           ;nein --> continue
    ldi   flag,2        ;ja, flag 2 setzen
                        ;initiiert sprung zu reset1
    pop   temp1
    out   SREG,temp1
    pop   temp1
    rcall loop

end_interrupt:
    pop   temp1
    out   SREG,temp1
    pop   temp1
    reti

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Kann man das optimieren?

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

Autor: Falk Brunner (falk)
Datum:

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

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

Dann leg das Ganze doch im Ram ab.

MfG Spess

Autor: Friedrich K. (fiete)
Datum:

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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

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

Autor: Falk Brunner (falk)
Datum:

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

Autor: Friedrich K. (fiete)
Datum:

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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

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

Autor: Friedrich K. (fiete)
Datum:

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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

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


   ldi r16, 0x20   ; lade irgendwas in r16
   ldi r17, 0x30   ; lade was anderes nach r17

   cpi r16, r17    ; vergleiche r16 mit r17
   breq mach_was_bei_gleich   ; wenn r16 und r17 gleich waren, dann
                              ; gehst bei mach_was_bei_gleich weiter

                              ; andernfalls geht es hier weiter
   ... code bei Ungleich

mach_was_bei_gleich:
   ... 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.

Autor: Friedrich K. (fiete)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ah Haaa!

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

Autor: Friedrich K. (fiete)
Datum:

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

Autor: Friedrich K. (fiete)
Datum:
Angehängte Dateien:

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

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.