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
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
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.
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
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
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.
> 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.
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.
>>> 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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.