Forum: Mikrocontroller und Digitale Elektronik Probleme mit dem UART und komischen Zeiten


von Bit_u_Byte (Gast)


Angehängte Dateien:

Lesenswert?

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

von Gast (Gast)


Lesenswert?

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.

von Bit_u_Byte (Gast)


Lesenswert?

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

von Hc Z. (mizch)


Lesenswert?

1
> void timer0_start(){
2
>  
3
>  TCNT0 = 0;  
4
>
5
>  TCCR0A |= (1<<CS02) | (1<<CS00); 
6
>
7
>}

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
> void timer0_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.

von Bit_u_Byte (Gast)


Lesenswert?

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

von Patrick B. (p51d)


Lesenswert?

> // 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.

von Bit_u_Byte (Gast)


Lesenswert?

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

von Stefan E. (sternst)


Lesenswert?

Mich persönlich verwundert viel mehr, dass die Pause überhaupt endlich 
ist. Timer0 läuft nämlich überhaupt nie (falsches Register), weshalb das 
hier
1
  while(TCNT0 < _DELAY);
eigentlich eine Endlosschleife sein müsste.

von Karl H. (kbuchegg)


Lesenswert?

Dein Oszibild zeigt was genau?
(Welcher Pin / was wurde da angezeigt?)

von Patrick G. (pattyman)


Lesenswert?

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.)

von Bit_u_Byte (Gast)


Lesenswert?

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
void timer0_start(){
2
  
3
  TCNT0 = 0;  
4
5
  TCCR0A |= (1<<CS02) | (1<<CS00); 
6
7
}
8
9
void timer0_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
void uart_putc(uint8_t c){
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?

von S. T. (cmdrkeen)


Lesenswert?

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






1
//UART einschalten
2
  uint16_t bautrate;
3
  bautrate = F_CPU / (UART_BAUT_RATE * 16L) - 1;  //UART BAUT RATE REGISTER setzen
4
  UBRRH= (uint8_t)(bautrate >> 8);
5
  UBRRL= (uint8_t)(bautrate);
6
7
  UCSRB |= 1<<TXEN; // den UART transciever anschalten
8
  UCSRB |= 1<<RXEN; // den UART reciever anschalten
hat sich bewährt zum uart anschalten
1
loop_until_bit_is_set(UCSRA, UDRE);
2
UDR=cmd;
und zum senden  (bitte die nullen noch einfügen ... meine registernamen 
stammen von einem 2313)

von Bit_u_Byte (Gast)


Lesenswert?

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. ;-)

von S. T. (cmdrkeen)


Lesenswert?

ja dann stell doch mal die aktuelle c-datei in einen anhang ... 
vielleicht hast du dir ja neue fehler reineditiert.

von Peter D. (peda)


Lesenswert?

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

von S. T. (cmdrkeen)


Lesenswert?

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.

von Bit_u_Byte (Gast)


Angehängte Dateien:

Lesenswert?

Das ist jetzt die aktuelle main mit dem aktuellen Oszibild für den 
mega3290

von Stefan E. (sternst)


Lesenswert?

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

von Bit_u_Byte (Gast)


Lesenswert?

Okay,doch! das hört sich ja gut an.
Aber wie kann ich umsetzen, das er nach dem 1 Zeichen die Pause so 
macht, wie die anderen?

von S. T. (cmdrkeen)


Lesenswert?

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

von Stefan E. (sternst)


Lesenswert?

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.

von Bit_u_Byte (Gast)


Lesenswert?

Okay, die Pausen sind jetzt ziemlich gleich. SUPER!
Mit dem:
1
 loop_until_bit_is_set(UCSRA, UDRE);
2
3
 UDR=cmd;
4
5
 loop_until_bit_is_set(UCSRA, UDRE);
Danke!

Beim atmega3290 ist das  CS02 CS01 CS00 im TCCR0A, aber das ist ja egal.

von S. T. (cmdrkeen)


Lesenswert?

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

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.