Hallo
Ich habe ein Programm gemacht das in jeder Sekunde 25 mal den
Zählerwert vom 16Bit zähler und noch andere Werte über den UART an den
PC schickt. Wenn ich eine höhere Baudrate einstelle macht er mehr als
25 Messungen pro sekunde.
Warum das?
Beeinflusst der UART den Timer?
Ist der Timer überhaupt genau?
Gruss
Gestern kam der Nikolaus
wie so oft - der Fehler liegt beim Programmierer.
In deiner Timer0-ISR sendest du allerhand (rcall serout), das dauert
nunmal. Bei höherer Baudrate geht das Senden schneller, dementsprechend
ist die Timer0-ISR fertig, bevor der nächste Int kommt. Dauert das
Senden länger als bis zum nächsten Timer-Int, wird der nicht mehr zur
richtigen Zeit ausgeführt, sondern erst später. Diese Fehler summieren
sich, irgendwann gehen Ints komplett verloren.
Die alte Regel: ISR laufzeitmässig kurz halten.
Hallo crazy horse
Du scheinst ein Profi zu sein ich eher ein Einsteiger.
Ich habe deine Aussage verstanden nur weiss ich nicht wie ich das Lösen
soll.
Wie würdest du dass Konkret machen?
Das ist ISR?
ISR = Interrupt Service Routine
Normalerweise teilt man Interrupt-Handler die zeitintensive Arbeit
verrichten in Top Half und Bottom Half. Ersterer ist fix wie Sau und
erledigt den zeitkritischen Teil, letzterer werkelt dann irgendwann vor
sich hin, wenn Zeit ist.
Insbesondere sollte der Top Half nicht weiter gestört werden (wie für
ISRs üblich), während der Bottom Half entsprechend reentrant sein
sollte, sodaß während seiner Abarbeitung neue Interrupts bearbeitet
werden können.
Soviel zu meinem Wissen aus der Welt der PCs, ich nehme mal an das ist
auf uCs im Wesentlichen übertragbar.
Auszug aus dem ATMega8 Datenblatt
"The complete list of Vectors is shown in ?Interrupts? on page 44. The
list also determines the priority levels of the different interrupts.
The lower the address the higher is the priority level. RESET has the
highest priority, and next is INT0 ? the External Interrupt Request 0"
Hallo du Weihnachtsmann (keine Beleidigung),
irgendetwas passt nicht:
Du überträgst anscheinend 7 Bytes mit 9600 Bd. das sollte < 10 ms
daueren. Wenn der Timer wirklich nur alle 40 ms käme, könnten keine
Interrupts verloren gehen.
Ansonsten keine Warteschleifen auf das Ausgangsregister in deiner
Interruptroutine sonder den UART interrupt zum Senden des nächsten
Zeichens nutzen.
Hallo Eumel
Genau ich sende 7 Byte 25 mal in der Sekunde das wären also 175Byte pro
Sekunde ist eigentlich nicht viel.
Die Baudrate kann ich nur auf 19200 erhöhen höher macht er nicht mit.
Ein Nachteil ist sicher dass ich im Moment nur ein 6MHz Quartz habe
einen 16Mhz Quarz muss ich noch bestellen.
Du meinst ich sollte dies so ändern?
serout: ;Wert senden
sbis UCSRA,UDRE
out UDR, Sende
ret
Gruss
Weihnachtsmann
Wenn Du die UART nutzen willst, dann solltest du am besten gleich einen
Standardquarz nehmen, z.B. 7,3728MHz, 9,11,0592MHz, 14,7456MHz usw.
Damit stimmen dann alle Baudraten bis 115200 Baud.
Peter
Wenn ich das Senden auf ändere:
serout: ;Wert senden
sbis UCSRA,UDRE
out UDR, Sende
ret
empfange ich nichts mehr.
Ich weiss dass es Quarze gibt die auf die Bautraten passen.
Woher weiss ich welcher für welche Baudrate passt?
Gruss
Weihnachtsmann
Hallo Weihnachtsmann,
1. Das Senden im Interruptbetrieb würde folgendermaßen ablaufen:
du gibst ein Zeichen in das UART_Ausgangsregister, gibts den
UART-Sendeinterrupt (Ausgangsregister leer) frei. In der
Interruptroutine des Sendeinterrupts gibst du dann das jeweils folgende
Zeichen aus.
Die Warteschleife auf das Leerlaufen des Registers entfällt. Mit dem
letzten Zeichen (carriage return) sperrst du dann den Interrupt
wieder.
2. wenn ich deinen (für mich etwas unkonventionellen) Code für Timer 0
richtig interpretiere, dann unterbrichst du alle 9,8ms. Das sind
mitnichten 25 mal in der Sekunde. 256*(256-24)/6000000
Gruß
Einhart
Ein Byte übertragen braucht knapp über 1ms (>1/920) Zeit. Du überträgst
7 Stück davon, d.h. mindestens 7.6ms. Eumel hat's schon vorgerechnet:
IRQ alle 9,8ms ==> 2,2ms über, bestenfalls.
Wenn jetzt aber was dazwischen kommt, dann entfällt der Interrupt
natürlich.
Das könnte ja grad noch gehen, aber was nich so schön ist, bzw. NIX (so
wie Du es dir gedacht hast) bringt, ist das hier:
ldi temp, 0b00000000 ;Timer0 Aus
OUT TIMSK,temp
ldi temp, 0b00000010 ;Timer0 Ein
OUT TIMSK,temp
Du schaltest nur den IRQ aus, nicht den Zähler ;) und da durch ein IRQ
die Interrupts generell (I-Flag) ausgeschaltet sind ==> nutzlos.
Nimm lieber in TCCR0 den Takt raus, d.h. die niedrigsten 3 Bits auf 0.
Warum und wie Du auf 25Hz kommst, ist mir noch schleierhaft.
Wie oft trifft denn der RXC-IRQ auf? Lösch den doch mal raus und schau
nochmal, ob er dann mit 100Hz sendet.
dave
Nachtrag zum Thema: "wie macht man es richtig": Mangels
Interrupt-Prios behilft man sich üblicherweise so:
ISR schreibt Zählerwert in Ringbuffer
main-loop leert Ringbuffer über UART.
Und wenn dann noch die ISR ein Flag setzt, wenn der Puffer überläuft,
weißt Du auch, was los ist, wenn etwas schief geht.
Hallo
Also irgendwie funktioniert diese Sache immer weniger und bin immer
verzeifelter. Habe gedacht ich bringe die Sache auf die Reihe.
Ist mein Vorhaben überhaupt machbar?
Oder muss ich die Sache mit Logic-ICs aufbauen?
So wie ich es jetzt gemacht habe bringe ich es auf ca 142 Datensätze in
20 Sekunden.
Obwohl ich jetzt: 1024*(256-22)/6000000 = 0.039936
Ich bringe wenn ich will auch auf 500 Datensätzen aber mit kleinerem
Teiler.
Wenn ich dies:
ldi temp,Teiler
out TCCR0,temp ;Timer deaktivieren
Statt:
ldi temp, 0b00000000 ;Timer0 Aus
OUT TIMSK,temp
Diese eingebe läuft natürlich der Timer weiter.
Ich muss beides nehmen.
Gruss
Der Weihnachtsmann.
ldi temp1, 0 ; clr temp1
out tccr0, temp1
da glaub ich dir nicht, dasser nicht stehen bleibt...
wie oft sendest du jetzt daten auf den mc? lass doch mal den
rxc-interrupt wech
dave
Hallo
Stimmt er bleibt stehen vermutlich habe ich beim Kopieren den Falschen
Wert oder Befehl genommen.
Ohne rxc-interrupt ist er gleichschnell.
Gruss
Weihnachtsmann