Hallo Freunde,
ich habe ein Problem mit dem UART auf einem ATmega168 und zwar sende ich
einen „String“ zu einem anderen Atmega. Dieser liegt die meiste Zeit im
Tiefschlaf und soll mit dem besagten String auf geweckt werden. Das
funktioniert eigentlich auch alles.
Allerdings reagiert der Empfänger nur auf ein bestimmtes Bitmuster mit
einer definierten Zeit dazwischen und dieses Muster muss er auch
mehrmals empfangen. Hört sich nicht so schwierig an, aber ich hab da ein
gravierendes Problem.
Als Beispiel: Der Empfänger wacht mit den Charaktern „\0“ pause “\0 112“
auf. Das ist einer Funktion verpackt und wird 3x hintereinander weg
aufgerufen.
„\0“ pause “\0 112“
„\0“ pause “\0 112“
„\0“ pause “\0 112“
Die Pause ist im ersten Packet um das 1.5 fache größer als bei den
folgenden Packten (wie man im Bild sehen kann). Und ich bekomme es
nicht hin, dass er die Pausen gleich groß macht. Wenn ich die Pausen
weglasse, sendet er das alles ganz normal.
Der Empfänger kann nicht umprogrammiert werden und steht auch nicht zur
Diskussion!!
Hat da jemand eine Idee?
MFG
BuB
Hab jetzt den Code nicht so genau analysiert. Aber solange du in deiner
Sende-Funktion mit einer while-Schleife auf deinen Timer wartest, kannst
du dir den Timer auch sparen und mit den den Funktionen _delay_ms() bzw.
_delay_us() der avr-libc die Verzögerungszeiten erreichen. Ob das jetzt
dein Problem löst, kann ich nicht sagen. Das sollte nur mal eine
Bemerkung am Rande sein.
Ja, das stimmt wohl. Aber ist ja auch nicht so, als ob ich das noch
nicht getestet hätte.
Also Timer, als auch delay haben den selben Effekt!
Trotzdem danke
Setz' hier das Rücksetzen von TCNT0 hinter die Timer-Initialisierung.
Da gehört es hin. Bis dahin rennt der Timer nämlich mit Fullspeed
(s.u., zählt also die Zyklen). Das sind zwar nicht viele, aber DELAY
wird vermutlich auch nicht groß sein.
Weshalb Du das hier:
1
>voidtimer0_stop(){
2
>
3
>TCNT0=0;
4
>
5
>TCCR0A&=~(1<<CS02);
6
>
7
>}
so benannt hast, wie Du es benannt hast, ist mir ein Rätsel. Es stoppt
den Timer nicht, sondern lässt ihn mit Full Speed rennen (CS00 bleibt
stehen nach vorhergehendem timer0_start). Und wieder solltest Du TCNT
hinterher auf 0 setzen, wenn Du irgendeinen definierten Zustand im
Auge hast.
ja Danke,
das ist aber nur ein Formalfehler, denn der Timer wird sonst nicht
benögit und stört niemanden. Wenn der Timer wieder gebraucht läuft er
wieder mit der richtigen Geschwindigtkeit. Ist aber alles richtig.
Aber ich ändere es. Leider löst das immer noch nicht mein Problem.
Viele Grüße
BuB
> // Hier ein Delay zeigt �hnliches verhalten> //_delay_ms(10);> TCNT0 = 0;> while(TCNT0 < _DELAY);>> uart_putc('\0');
ich denke, dass das ganze im Projekt etwas Zeitkritisches sein wird, von
daher würde ich dir empfehlen, dass du die while-Schlaufe entfernst und
anstelle auf einen Software-Counter abfragst, der durch ein Interrupt
inkrementiert wird. Du musst dir vorstellen, dass der MCU bei der
while-schlaufe sich wunderbar ausruht, und wenn etwas genau zu diesem
Zeitpunkt passiert verpasst du das in der Software.
zu
> uart_putc('\0');
Das sind doch 2 Byte oder? eines für '\' und eines für '0' und soweit
ich weiss ist putc nur für ein Chrakter gedacht, und nicht für Strings.
Das das im Gesamtprojekt nicht optimal ist mir ja klar, aber ich
verstehe nicht das dieselbe Funktion, hintereinander aufgerufen, die
erste Pause wesentlich länger ist als die anderen 2.
und das ist immer und immer wieder so. also nachdem delay in der
hauptschleife.
'\0' bedeutet end of String und ist 1.char, wenn ich mich nicht täusche!
Man könnte auch eine 0 senden, das wäre das selbe.
vg
Ich kenne Deine Entwicklungsumgebung nicht, aber ich kann mir
vorstellen, daß der Compiler durch die Code-Optimierung Deine Pause beim
zweiten Aufruf schon vorgegriffen hat. - Andernfalls kann natürlich ein
Interrupt diese Pause beeinflussen.
Worauf basiert diese Pause eigentlich: Warten durch stupide Auslastung
der CPU (NOP in einer Schleife) oder wirklich ein Timer zu Grunde liegt.
Vergessen wir auch nicht die Hardware: Während das "\0 112" noch im
Ausgangsfifo liegt, wird das nächste "\0" schon hinterher geschoben.
Anschließend wird gewartet. Auf dem Oszi sieht es so aus, als wäre die
zweite Pause kürzer. Ich vermute mal, daß die Pause genau eine
Zeichenlänge kürzer ist. (Grundgedanke hierbei ist, daß das Ding einen
Hardware-Fifo hat und Programm nicht wartet bis das gesendete Zeichen
wirklich auf der Leitung ist, sondern direkt mit dem nächsten Befehl
weitermacht.)
Also der Timer0 läuft im angegebenen Quellcode ständig durch.
Weil in der Funktion send_new die Funktion timer0_start am Anfang und
timer0_stop am ende ausfgeführt wird.
1
voidtimer0_start(){
2
3
TCNT0=0;
4
5
TCCR0A|=(1<<CS02)|(1<<CS00);
6
7
}
8
9
voidtimer0_stop(){
10
11
TCNT0=0;
12
13
TCCR0A&=~(1<<CS02);
14
15
// das sieht mittlerweile so aus
16
//TCCR0A &= ~( (1<<CS02)|(1<<CS00) );
17
18
}
Das Oszibild zeigt das was er sendet. Das Signal ist das vom TX_Pin,
allerdings invertiert aufgenommen.
Also die IDE ist AVRStudio. An die Code Optimierung habe ich auch schon
gedacht und hab diese abgeschaltet. Das macht keine Veränderungen.
Die Pause in der send_new warte bis der Timer0 bis _DELAY gezählt hat
und das wird doch nicht weg optimiert.
1
voiduart_putc(uint8_tc){
2
3
while(!(UCSR0A&(1<<UDRE0)));
4
UDR0=c;
5
}
Ich dachte, das wenn ich auf das UDRE0 warte, dann der HardwareFIFO leer
ist?
Da stört mich noch was, wenn ich vor dem 1. uart_putc('\0') ein
uart_putc(255) oder ein anderes Zeichen sende, stimmen die Pausen mit
den anderen überein.
Würde sich was ändern, wenn ich Über einen Interrupt sende?! Oder wie
sollte ich sonst senden?
TCCR0A |= (1<<CS02) | (1<<CS00);
bist du dir sicher, das du nicht vielleicht TCCR0B meinst?
void uart_init(uint8_t ubrrval){
UBRR0H = ubrrval >> 8;
UBRR0L = ubrrval & 0xff;
hallo? ... UBRR0 sind zwei 8bit register, da gehört eine 16bit zahl
rein. ubrrval>>8 ist in deinem fall immer 0 ...
Ansonsten kann ich auch nur empfehlen jegliche optimierung auszuschalten
...
Okay, bei der uart_init hast du recht. Aber bei 8 Mhz und 2400 baud ist
der Wert 207 (uint8_t <= 255), dürfte hier nichts ausmachen.
Bei dem Timer sollte es bei dem Mega168 schon das TCCR0B richtig. Ich
die falsche main kopiert, weil ich schon soviel getest hab, da wird man
doch verrückt. Die ist vom 3290, aber macht den selben Fehler. Sorry,
auch stefan ernst somit hat auch recht. ;-)
Bit_u_Byte schrieb:
> Die Pause ist im ersten Packet um das 1.5 fache größer als bei den> folgenden Packten (wie man im Bild sehen kann).
Ich würde sagen, die ist um ein Bytezeit größer.
Dein uart_putc() testet nämlich nicht, ob das Senden beendet ist.
Die 1.Zeit startet mit Sendepuffer leer, die nächsten mit 1 Byte im
Puffer.
Peter
achja ... einige baudraten sind nicht nativ einzustellen, weil die
berechnung für das ratenregister gerundet wird. damit können dann bei
empfängerstellen die evtl geringfügig anders abtasten zu fehlern kommen.
häng deinen sender doch mal an ein PC + hterm oder soetwas und schau
dir an was da empfangen wird.
Du rätselst doch nicht etwa immer noch, oder?
Peter hat doch den Nagel exakt auf den Kopf getroffen.
Bei der ersten Pause gilt:
Pause = Timer_Verzögerung - Zeit_für_ein_Zeichen
Bei den folgenden Pausen gilt:
Pause = Timer_Verzögerung - 2 * Zeit_für_ein_Zeichen
0x25 (0x45) TCCR0B FOC0A FOC0B – – WGM02 CS02 CS01 CS00
0x24 (0x44) TCCR0A COM0A1 COM0A0 COM0B1 COM0B0 – – WGM01 WGM00
TCCR0B ist zum timer starten!
und dann der tipp zum timerproblem:
1
loop_until_bit_is_set(UCSRA,UDRE);
2
UDR=cmd;
3
loop_until_bit_is_set(UCSRA,UDRE);
dann springt er erst wieder zurück ins programm wenn das byte gesendet
ist, denn sonst macht er das im hintergrund... siehe peters hinweis
S. T. schrieb:
> und dann der tipp zum timerproblem:>
1
>loop_until_bit_is_set(UCSRA,UDRE);
2
>UDR=cmd;
3
>loop_until_bit_is_set(UCSRA,UDRE);
4
>
> dann springt er erst wieder zurück ins programm wenn das byte gesendet> ist, denn sonst macht er das im hintergrund... siehe peters hinweis
Nicht ganz. Er wartet, bis das Senden des Bytes anfängt. Es fließt also
weiterhin 1 mal Zeit_für_ein_Zeichen in die Pause mit ein, aber die
Pausen sollten dann wenigstens gleich lang sein.
>Hallo Freunde,>>ich habe ein Problem mit dem UART auf einem ATmega168 und zwar sende ich>Beim atmega3290 ist das CS02 CS01 CS00 im TCCR0A, aber das ist ja egal.
hmm...