Forum: Mikrocontroller und Digitale Elektronik Übertragung von Daten aus EEPROM über UART:Müll


von Karl-alfred R. (karl-alfred_roemer)


Lesenswert?

Hallo zusammen,

ich habe wieder mal ein komisches Problem:
Mein ATMEGA8 auf STK500 (mit 1MHz internem Oszillator betrieben)
soll über den UART Daten nach HTERM (PC, XP Home) schicken.

Bei einem Programm aus dem Tutorial klappt das genau dann,
wenn ich den CLOCK-Wert im Programm auf 4000000/9600 stelle
und in Hterm 2400 Baud einstelle, einwandfrei.
Mit der korrekten Einstellung 1000000/9600 und 9600Baud in
HTERM kommten nur falsche Zeichen.

Ein anderes Programm aus dem Tutorial bring immer falsche
Zeichen.

Mein eigenen Programm, welches ich angehangen habe, bringt
ebenfalls immer falsche Zeichen.

Es scheint etwas mit der Genauigkeit der internen Clock
zu tun zu haben, weil ja nicht nur mein Programm, sondern
auch die aus den Tutorials Probleme machen. Komischerweise
klappt aber ein Programm mit falschen Einstellungen.

Vielleicht habe ich aber auch einen dummen Fehler in meinem
Programm übersehen. Könnt ihr euch das mal kurz angucken?

VG
Karl

von Karl-alfred R. (karl-alfred_roemer)


Lesenswert?

Code vergessen;)

von Karl-alfred R. (karl-alfred_roemer)


Lesenswert?

Irgendwie scheint das Anhängen von Dateien momentan bei mir nicht
zu klappen. Deshalb habe ich es mal kurz hier hinein kopiert.
Bitten um Entschuldigung für die seltsame Formatierung.



;Werdender Datenlogger
; Timer1 zählt einen 24-Bit-Zähler im Sekundentakt hoch.
; Über INT0 wird ein Signal von einem Bewgungsmelder eingespeist,
; Zum Einspeisezeitpunkt wird der 24-Bitzähler ins EEPROM geschrieben.
; Daraus kann später der Zeitpunkt der Bewegung zurückgerechnet werden,
; falls man den Anfangszeitpunkt kennt.
; Über INT1 wird ein Taster angeschlossen. Wird dieser gedrückt,
; werden alle Daten aus dem EEPROM ausgelesen und über die Serielle
; Schnittstelle gesendet.

.include "m8def.inc"

.def temp1 = r16
.def temp2 = r17
.def zaehlerL = r18
.def zaehlerM = r19
.def zaehlerH = r20
.def LetzteAusloesungMerker = r21
.def LEDS  = r22
.def EEPROMDATEN = r23

.equ CLOCK = 4000000                ; Frequenz des Quarzes
.equ BAUD = 9600                    ; Baudrate
;.equ UBRRVAL = CLOCK/(BAUD*16)-1    ; Baudratenteiler

; Berechnungen
.equ UBRRVAL   = ((CLOCK+BAUD*8)/(BAUD*16)-1)  ; clever runden
.equ BAUD_REAL  = (CLOCK/(16*(UBRRVAL+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


.org 0x0000
       rjmp main              ; Reset Handler
.org INT0addr                 ; External Interrupt0 Vector Address
       rjmp int0_handler
.org INT1addr                 ; External Interrupt1 Vector Address
       rjmp int1_handler
.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
       rjmp timer1_compare
.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


main:

    ; Stackpointer initialisieren
        ldi     temp1, LOW(RAMEND)
        out     SPL, temp1
        ldi     temp1, HIGH(RAMEND)
        out     SPH, temp1


        ; Baudrate einstellen Frame-Format: 8 Bit-1 Stopbit - No Parity
        ldi     temp1, LOW(UBRRVAL)
        out     UBRRL, temp1
        ldi     temp1, HIGH(UBRRVAL)
        out     UBRRH, temp1

        ldi     temp1, (1<<URSEL)|(3<<UCSZ0) ;
        out     UCSRC, temp1
        sbi     UCSRB, TXEN                 ; TX (Senden) aktivieren
      ; Baudrate einstellen Frame-Format: 8 Bit-1 Stopbit - No Parity



    ;paar Kleinigkeiten Initialisieren
      ldi r16, 0xFF
        out DDRB, r16               ;Port B als Ausgang Konfigurieren
    ldi ZaehlerL, 0
    ldi ZaehlerM, 0
    ldi ZaehlerH, 0
    ldi LetzteAusloesungMerker, 8


    ;INT0 und INT1 konfigurieren
        ldi temp1, (1 << SE) | (1 << ISC00) | (1 << ISC10) ; Sleep 
enabled (IDLE) und beide INTS reagieren auf beide Flanken
        out MCUCR, temp1

        ldi temp1, 0b11000000  ; INT0 und INT1 aktivieren
        out GICR, temp1


        ;EEPROM-Zeiger initialisieren
        ldi     ZL,low(daten)               ; der Z-Zeiger wird hier 
exclusiv
        ldi     ZH,high(daten)              ; für die Datenadressierung 
verwendet



        ;Timer1 konfigurieren
        ldi     temp1, high( ((997640 / 64) - 1) )
        out     OCR1AH, temp1
        ldi     temp1, low( ((997640 / 64) - 1) )
        out     OCR1AL, temp1

        ldi     temp1, ( 1 << WGM12 ) | (1 << CS10) | ( 1 << CS11 )
        out     TCCR1B, temp1

        ldi     temp1, 1 << OCIE1A  ; OCIE1A: Interrupt bei Timer 
Compare
        out     TIMSK, temp1

        sei



loop:
        rjmp    loop



timer1_compare:                    ; Timer 1 Output Compare Handler
        push    temp1               ; temp1 1 sichern
        in      temp1,sreg          ; SREG sichern
    push    temp1



    ;Der letzte Auslösungsmerker soll nur bis sagen wir 255 
(willkührlich) hochgezählt werden
    cpi LetzteAusloesungMerker,255        ;Dann soll er dort stehen 
bleiben, weil was
    brsh LetzterAusloesungsmerkerEnde      ;länger her ist, ist egal
    inc LetzteAusloesungMerker
        LetzterAusloesungsmerkerEnde:



        ;Sekundenzähler inkrementieren --------------
       inc zaehlerL
      cpi zaehlerL,255
    brne EndeZaehlerIncrementieren
      inc zaehlerM
      cpi zaehlerM,255
    brne EndeZaehlerIncrementieren
      inc zaehlerH
    EndeZaehlerIncrementieren:
    ;Sekundenzähler inkrementieren --------------




      mov temp1, ZaehlerL ; oder LetzteAusloesungMerker ;
    com temp1
    out PORTB, temp1     ; r16 ins IO-Register PORTB ausgeben

      pop     temp1
        out     sreg,temp1          ; sreg wieder herstellen
        pop     temp1
reti                        ; das wars. Interrupt ist fertig




int0_handler:
         push temp1             ; Das SREG in temp1 sichern. Vorher
         in   temp1, SREG       ; muss natürlich temp1 gesichert werden
     push temp1

     ; Nach jedem ERFOLGREICHEN Trigger wird LetzteAuslösungMerker auf 
NULL gesetzt
     ; Bei jedem Timerinterupt wird der LetzteAuslösungMerker um 1 
erhöht bis zu einem willkührlichen Grenzwert
     ; Der Sinn der Sache ist der, dass nicht dauerhaft getriggert wird 
und der Speicher vollgemüllt wird.

     cpi LetzteAusloesungMerker,10
     BRLO end_int0_handler



         ;Die drei Bytes der Zaehlerdaten nach und nach ins EEPROM 
schreiben
     mov EEPROMDATEN, ZaehlerH
         rcall   EEPROM_write
       adiw    ZL,1                        ; Zeiger erhöhen
       mov EEPROMDATEN, ZaehlerM
     rcall   EEPROM_write
         adiw    ZL,1                        ; Zeiger erhöhen
     mov EEPROMDATEN, ZaehlerL
     rcall   EEPROM_write
     adiw    ZL,1                        ; Zeiger erhöhen
     ;Die drei Bytes der Zaehlerdaten nach und nach ins EEPROM 
geschrieben



         ;ldi LEDS, 0
     mov LEDS, ZL
     com LEDs
         out PORTB, LEDS
     ldi LetzteAusloesungMerker, 0



     end_int0_handler:
      pop temp1
         out SREG, temp1        ; Die Register SREG und temp1 wieder
         pop temp1              ; herstellen
reti




int1_handler:
         push temp1             ; Das SREG in temp1 sichern. Vorher
         in   temp1, SREG       ; muss natürlich temp1 gesichert werden
     push temp1
     push ZL
     push ZH



         ldi     ZL, 0
       ldi     ZH, 0
         rcall   EEPROM_print



     pop ZH
     pop ZL
      pop temp1
         out SREG, temp1        ; Die Register SREG und temp1 wieder
         pop temp1              ; herstellen
reti



EEPROM_write:
    sbic    EECR, EEWE                  ; prüfe ob der letzte 
Schreibvorgang beendet ist
    rjmp    EEPROM_write                ; wenn nein, nochmal prüfen

    out     EEARH, ZH                   ; Adresse schreiben
    out     EEARL, ZL                   ;
    out     EEDR,EEPROMDATEN            ; Daten  schreiben
  in      temp2,sreg                  ; SREG sichern
    push    temp2                       ; SREG sichern
    cli                                 ; Interrupts sperren, die 
nächsten
                                        ; zwei Befehle dürfen NICHT
                                        ; unterbrochen werden
    sbi     EECR,EEMWE                  ; Schreiben vorbereiten
    sbi     EECR,EEWE                   ; Und los !
    sei                  ; Interupts wieder freigeben

  pop   temp2
    out     sreg, temp2                 ; SREG wieder herstellen
ret




; EEPROM Lesezugriff auf Strings + UART Ausgabe
EEPROM_print:
    sbic    EECR,EEWE           ; prüf ob der vorherige Schreibzugriff
                                ; beendet ist
    rjmp    EEPROM_print        ; nein, nochmal prüfen

    out     EEARH, ZH           ; Adresse laden
    out     EEARL, ZL

    sbi     EECR, EERE          ; Lesevorgang aktivieren
    in      temp1, EEDR         ; Daten in CPU Register kopieren

  ;Test ob Ende des EEPROMS erreicht ist
  cpi    ZH,1
  brne    Weiter_weil_EndeEEPROM_noch_nicht_erreicht
    cpi     ZL,255              ; Endes des EEPROMS erreicht?
    breq    eep_print_end       ; falls 0, Funktion beenden
  Weiter_weil_EndeEEPROM_noch_nicht_erreicht:
    ;Test ob Ende des EEPROMS erreicht ist

    rcall   sendbyte            ; ansonsten Byte senden...
    adiw    ZL,1                ; Adresse um 1 erhöhen...
    rjmp    EEPROM_print        ; und zum Anfang der Funktion
  eep_print_end:
ret




; sendbyte: sendet das Byte aus "data" über den UART
sendbyte:
    sbis    UCSRA, UDRE         ; warten bis das UART bereit ist
    rjmp    sendbyte
    out     UDR, temp1
    ret




.ESEG

Daten:
    .db     0

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Die internen Zeitgeber sind zu ungenau. Du kannst versuchen, den 
OSCCAL-Wert für den internen Oszillator bei 1Mhz oder 8Mhz anzupassen. 
Empfehlenswert wäre aber auf jeden Fall ein Baudratenquarz, wenn Du auch 
noch höhere Baudraten als 9600 anstreben solltest.

von Michael U. (amiga)


Lesenswert?

Hallo,

wenn Du im Terminal 2400 einstellen mußt und im AVR 9600 und im AVR 
behauptest, mit CLOCK = 4000000 zu takten, dann behaupte ich jetzt:
Du taktest den AVR mit 1MHz und nicht mit 4MHz, Fuses prüfen!

Das der interne RC-Oszillator zu ungenau ist, wurde schon geschrieben.
Im AVR ein Stück Miniprogramm, daß ein Stück Text mit einer kurzen Pause 
dazwischen ständig sendet.
AVR und Terminalprogramm auf gleiche Baudrate einstellen.

Programm rein, starten.
Wenn jetzt falsche Zeichen kommen:

Im AVR den CLOCK-Wert in 2% Schritten nach oben und nach unten testen, 
bis es klappt. Soweit testen, bis nach richtig wieder falsch kommt und 
die Mitte nehmen. Das ist der Takt, mit dem der AVR wirklich läuft.
Uncalibriert hatte ich da schonmal 20% Abweichung.

Erstmal aber klären, wie die Fusebits gesetzt sind.

Wenn Du den realen Wert gefunden hast (geht eigentlich mit wenigen 
Versuchen, ich benutze das, wenn ich schnell einen UART zum Debug 
brauche), ist der auch recht stabil bei üblicher Zimmertemperatur.
Bei läuft normalerweise 38400Baud, der relative Fehler ist ohnehin der 
gleiche. Der gefundene Wert stimmt natürlich nur für diesen AVR...

Gruß aus Berlin
Michael

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Karl-alfred Römer schrieb:
> Bitten um Entschuldigung für die seltsame Formatierung.

Besser, Du liest mal die Regeln/Formatierungshinweise, die groß und 
breit über dem Eingabefenster stehen!


Peter

von Karl-alfred R. (karl-alfred_roemer)


Lesenswert?

Problem gelöst!!!

Der Fehler war folgender: Ich habe in HTERM ASCII anzeigen lassen.
Und wenn ich nun beliebige Zahlenwerte (die Zeitwerten in meiner
Schaltung aber nicht den gewohnten alphabetischen Zeichen entsprechen),
sende, dann kann das ja nur Gemüse werden!!!

Heute kam ich dann mal auf die Idee, Zahlenwerte zu schicken,
die ASCII-Zeichen entsprechen. Das hat dann sofort einwandfrei
funktioniert. Und dann kam ich auf die Idee, in HTERM, anstatt
ASCII HEX anzuzeigen. Und nun entspricht die Ausgabe genau dem,
was mir auch beim Auslesen des EEPROMs mit AVR-Studio angezeigt
wird. Das Programm war also die ganze Zeit einwandfrei. stirnrunzel

Dennoch war die Sache lehrreich: Denn ich habe durch euch und durch
Googlen erfahren, dass der interne Oszillator nicht statisch
falsch sein kann, sondern auch noch mit der Außentemperatur und
der Versorgungsspannung schwankt.
Weil die Schaltung aber später bei höheren bzw auch schwankenden
Temperaturen eingesetzt werden soll, werde ich wohl langfristig
dennoch gucken müssen, ob ich nicht einen Baudratenquarz verwende.

Peter, es war spät in der Nacht und ich habe den Code zuerst versucht
als Datei anzuhängen, was aber irgendwie nicht geklappt hat. Dann habe
ich ihn mit Cut & Paste in die Mail hinein geschrieben. War zu Müde,
ihn auch noch zu formatieren. Ich weiß, das ist eine Zumutung, aber ich
dachte, hier sind so extreme Cracks, dass die das trotzdem lesen können
und mit bloßem hinschauen den Fehler sofort sehen.

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


Lesenswert?

> stirnrunzel
Sieh dir mal irgendwas zum Thema Zahlenformate, Binärzahlen, Hexzahlen, 
ASCII und verwandte Themen an.

Es ist klar, dass im EEPROM irgendwelche Binärwerte stehen. Wenn du so 
einen Wert unverändert über die SIO schickst, kommt der am anderen Ende 
genauso unverändert an. Und wenn das z.B. eine 0x99 = 0b10011001 = 153 
ist, dann bekommst du als ASCII-Zeichen ein Sonderzeichen, das je nach 
Terminal(-einstellung) evtl. angezeigt wird, oder auch nicht...

> Dann habe ich ihn mit Cut & Paste in die Mail hinein geschrieben.
Dann nimm wenigstens die Tokens [/c] und [c] zur automatischen 
Formatierung. Das wollte Peter damit sagen.

von Karl H. (kbuchegg)


Lesenswert?

Karl-alfred Römer schrieb:

> Peter, es war spät in der Nacht und ich habe den Code zuerst versucht
> als Datei anzuhängen, was aber irgendwie nicht geklappt hat. Dann habe
> ich ihn mit Cut & Paste in die Mail hinein geschrieben. War zu Müde,
> ihn auch noch zu formatieren.

Musst du auch nicht.
Schreib einfach nur [ avrasm ] darüber und [ /avrasm ] (ohne die 
Leerzeichen) darunter und du bist fertig. Genauso wie es in der Box über 
deinem Schreibfenster steht. Dort wo die größe Überschrift 
'Formatierung' lautet.

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


Lesenswert?

>>> Dann habe ich ihn mit Cut & Paste in die Mail hinein geschrieben.
>> Dann nimm wenigstens die Tokens [/c] und [c] zur automatischen
>> Formatierung. Das wollte Peter damit sagen.
> Schreib einfach nur [ avrasm ] darüber und [ /avrasm ]
OK, ich sehs ein. Das ist Assembler-Code :-/

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.