Forum: Mikrocontroller und Digitale Elektronik Falsche Zeichen per UART


von Dennis S. (eltio)


Lesenswert?

Hallo zusammen,
ich weiß, dass falsche Zeichen auf der UART oft an der fehlerhaften 
Baudrate liegen. Leider sehe ich bei meinem Code den Fehler nicht. Ich 
benutze einen ATMega8-16PU, CuteCom und einen Polulu CP2102 unter Linux.
Die Fuse-Bits sind 0xE1 und 0xD9, im Makefile ist F_CPU = 1000000. Sieht 
jemand den Fehler?

Gruß
Dennis
1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
// define some macros
5
#define BAUD 9600                                   // define baud
6
#define BAUDRATE ((F_CPU)/(BAUD*16UL)-1)
7
8
void uart_init(void)
9
{
10
    UBRRH = (BAUDRATE>>8);                      // shift the register right by 8 bits
11
    UBRRL = BAUDRATE;                           // set baud rate
12
    UCSRB|= (1<<TXEN)|(1<<RXEN);                // enable receiver and transmitter
13
    UCSRC|= (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);   // 8bit data format
14
}
15
16
// function to send data
17
void uart_transmit (char data)
18
{
19
    while (!( UCSRA & (1<<UDRE)));                // wait while register is free
20
    UDR = data;                                   // load data in the register
21
}
22
23
// function to receive data
24
char uart_recieve (void)
25
{
26
    while(!(UCSRA) & (1<<RXC));                   // wait while data is being received
27
    return UDR;                                   // return 8-bit data
28
}
29
30
int main( void )
31
{
32
33
    DDRB = 0xFF;
34
    PORTB = 0xFF;
35
36
    UCSRB |= (1<<TXEN);
37
    uart_init();
38
39
    while(1){
40
        _delay_ms( 500 );
41
        uart_transmit('x');
42
    }
43
}

von Stefan S. (sschultewolter)


Lesenswert?

Warum glaubst du, 9600 ist eine gute Baudrate für einen Mega mit 1MHz? 
Wie in deinem Beispiel wundern mich falschen Zeichen nicht. Du hast hier 
eine Fehlerquote von 7% !!. Nutze die Makros aus der <util/setbaud.h> 
Libary.

Denn um mit 9600 ordentlich Zeichen zu empfangen und/oder senden, sollte 
in UCSRA das Bit für U2X gesetzt sein. Beispiele findest du ebenfalls in 
der <util/setbaud.h>

von Dennis S. (eltio)


Lesenswert?

Stefan S. schrieb:
> Warum glaubst du, 9600 ist eine gute Baudrate für einen Mega mit 1MHz?
> Wie in deinem Beispiel wundern mich falschen Zeichen nicht. Du hast hier
> eine Fehlerquote von 7% !!. Nutze die Makros aus der <util/setbaud.h>
> Libary.
>
> Denn um mit 9600 ordentlich Zeichen zu empfangen und/oder senden, sollte
> in UCSRA das Bit für U2X gesetzt sein. Beispiele findest du ebenfalls in
> der <util/setbaud.h>

Ahh... okay, vielen Dank! Die Baudrate habe ich gewählt, weil ich dachte 
die wäre schon langsam genug... Mir fehlt einfach völlig das Gefühl 
dafür. Mit 4800 Baud funktioniert es, aber ich schaue mir auch mal das 
U2X-Bit und die Header-Datei an.

Gruß
Dennis

von Mein grosses V. (vorbild)


Lesenswert?

Dennis S. schrieb:
> Mit 4800 Baud funktioniert es, aber ich schaue mir auch mal das
> U2X-Bit und die Header-Datei an.

Setz das Bit einfach und du hast 9K6.

von Dennis S. (eltio)


Lesenswert?

Mein grosses V. schrieb:
> Setz das Bit einfach und du hast 9K6.

Jepp... läuft alles! Aber um den Lerneffekt zu erhöhen: wie komme ich 
auf die maximal sinnvolle Baudrate in Abhängigkeit von der CPU-Frequenz? 
Mal abgesehen, dass die Fehlerrate nicht zu hoch sein sollte (<1%?).

Gruß
Dennis

von Stefan S. (sschultewolter)


Lesenswert?

Ich schau mir für sowas immer diesen Rechner an.
http://www.gjlay.de/helferlein/avr-uart-rechner.html

Wenn möglich, 0 - 0,2% max. Fehlerate.

von M. K. (sylaina)


Lesenswert?

Folgender Code kann helfen:
1
#define baudRate                19200UL
2
3
#define UBRR_VAL (F_CPU+baudRate*8)/(baudRate*16)-1     // clever runden
4
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))             // Reale Baudrate
5
#define BAUD_ERROR ((BAUD_REAL*1000)/baudRate)          // Fehler in Promille, 1000 = kein Fehler.
6
7
#if ((BAUD_ERROR<990) || (BAUD_ERROR>1010))
8
#error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch!
9
#endif

Wenn der Fehler größer oder gleich 1 % ist bleibt der Compiler mit einem 
Fehler stehen ;)

Ich finde deine Namensgebung übrigens ungünstig. Dein BAUDRATE ist ja in 
Wirklichkeit UBRR.

: Bearbeitet durch User
von Mein grosses V. (vorbild)


Lesenswert?

Dennis S. schrieb:
> wie komme ich
> auf die maximal sinnvolle Baudrate in Abhängigkeit von der CPU-Frequenz?
> Mal abgesehen

Der Controller muß das empfangene Byte verarbeiten können, bevor das 
nächste aufläuft. Angenommen du kalibrierst den Oszillator auf 921600Hz, 
dann hast du eine Fehlerrate von 0. Egal bei welcher Standardbaudrate. 
Aber bei 115K2 hast du nur 80 Takte Zeit, bis das nächste Byte auflaufen 
kann. Das ist ne Menge Zeit und geht auch problemlos, aber rumtrödeln, 
zB. in irgendwelchen ISRs, darfst du nicht. D.h. wenn empfangen wird, 
wird empfangen und fast nichts anderes.

Gibt deine Anwendung das nicht her, mußt du mit der Baudrate runter oder 
mit dem Takt hoch.

Das ist alles eine Frage von Tests, Erfahrung und Wissen, wo die Grenzen 
liegen.

Beim Senden dagegen, hast du im Prinzip alle Zeit der Welt.

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


Angehängte Dateien:

Lesenswert?

Dennis S. schrieb:
> Die Fuse-Bits sind 0xE1 und 0xD9
Es ist übrigens auch keine allzu gute Idee, den internen RC-Oszillator 
mit seinen +-3% Fehler für die serielle Schnitte zu verwenden:
1
At 5V, 25°C and 1.0MHz Oscillator frequency selected, this calibration 
2
gives a frequency within ±3% of the nominal frequency.

von M. K. (sylaina)


Lesenswert?

Lothar M. schrieb:
> Dennis S. schrieb:
>> Die Fuse-Bits sind 0xE1 und 0xD9
> Es ist übrigens auch keine allzu gute Idee, den internen RC-Oszillator
> mit seinen +-3% Fehler für die serielle Schnitte zu verwenden:
 Na da kann man dann selbst kalibrieren, dann kommt man auf 1 %…ist dann 
zwar immer noch sehr grenzwertig, kann aber funktionieren.

von Stefan F. (Gast)


Lesenswert?

R/C Oszillator und serielle Schnittstelle:
Zum Debuggen reichts, ich würde sie Schnittstelle aber nicht funktional 
nutzen.

von Dennis S. (eltio)


Lesenswert?

Klasse, vielen Dank schon mal für den Input! :-)

von Dennis S. (eltio)


Lesenswert?

Michael K. schrieb:
> Folgender Code kann helfen:
Wenn ich die setbaud.h einbinde und BAUD_TOL gegebenenfalls umdefiniere 
sollte das die gleiche Funktionalität haben oder? Mal abgesehen davon, 
dass du einen Fehler schmeißen lässt und in der AVRlib lediglich eine 
Warnung ausgegeben wird.

Gruß
Dennis

von Stefan K. (stefan64)


Lesenswert?

Den CP2102 kannst Du auch auf Nicht-Standard-Baudraten konfigurieren. 
Dafür gibt es ein Tool vom Chip-Hersteller. Damit kannst Du auch höhere 
Baudraten komplett ohne Fehler erreichen. Ich benutze z.B. 125kbaud bei 
einem 8Mhz-Quarz.
(Den FTDI kann man ähnlich behandeln, beim CP2102 finde ich es aber 
wesentlich einfacher).

Gruß, Stefan

: Bearbeitet durch User
von M. K. (sylaina)


Lesenswert?

Stefan U. schrieb:
> R/C Oszillator und serielle Schnittstelle:
> Zum Debuggen reichts, ich würde sie Schnittstelle aber nicht funktional
> nutzen.

Ohja, für was Kommerzielles würde ich das auch nicht machen, nur für 
etwas im Bastelkeller.

von M. K. (sylaina)


Lesenswert?

Dennis S. schrieb:
> Wenn ich die setbaud.h einbinde und BAUD_TOL gegebenenfalls umdefiniere
> sollte das die gleiche Funktionalität haben oder? Mal abgesehen davon,
> dass du einen Fehler schmeißen lässt und in der AVRlib lediglich eine
> Warnung ausgegeben wird.

Genau, nur eine Warnung gibts nicht bei korrekter Einbindung. Ist der 
Fehler 1% oder größer wird ein Fehler geworfen und der Linker wird nicht 
weiter kompiliert.
Wieso sollte es denn eine Warnung geben?

von Dennis S. (eltio)


Lesenswert?

Es ist auch nur eine Bastelei! Sind externe RC-Oszillatoren tendenziell 
besser als die internen? Oder lohnt sich der Unterschied nicht und man 
"muss" gleich auf einen Quarzoszillator ausweichen?

Gruß
Dennis

von M. K. (sylaina)


Lesenswert?

Dennis S. schrieb:
> Sind externe RC-Oszillatoren tendenziell
> besser als die internen?

Besser als externe RC-Oszillatoren? Eher nicht. Aber so ein 
Quarz-Oszillator ist deutlich besser/genauer/stabiler als ein 
RC-Oszillator ;)

von Dennis S. (eltio)


Lesenswert?

Michael K. schrieb:
> Genau, nur eine Warnung gibts nicht bei korrekter Einbindung. Ist der
> Fehler 1% oder größer wird ein Fehler geworfen und der Linker wird nicht
> weiter kompiliert.
> Wieso sollte es denn eine Warnung geben?

Ich bin jetzt von dem Quellcode auf der Homepage ausgegangen [1]. In den 
Zeilen 222 und 227 wird doch nach meinem Verständnis die Abweichung 
errechnet und lediglich eine Warnung ausgegeben...

Gruß
Dennis

[1] http://www.nongnu.org/avr-libc/user-manual/setbaud_8h_source.html

von M. K. (sylaina)


Lesenswert?

Dennis S. schrieb:
> Michael K. schrieb:
>> Genau, nur eine Warnung gibts nicht bei korrekter Einbindung. Ist der
>> Fehler 1% oder größer wird ein Fehler geworfen und der Linker wird nicht
>> weiter kompiliert.
>> Wieso sollte es denn eine Warnung geben?
>
> Ich bin jetzt von dem Quellcode auf der Homepage ausgegangen [1]. In den
> Zeilen 222 und 227 wird doch nach meinem Verständnis die Abweichung
> errechnet und lediglich eine Warnung ausgegeben...
>
> Gruß
> Dennis
>
> [1] http://www.nongnu.org/avr-libc/user-manual/setbaud_8h_source.html

Da du meinen Beitrag zitiert hattest bin ich davon ausgegangen, dass du 
meinen Codeteil meintest.
Ich benutze setbaud.h nicht, ehrlich gesagt finde ich das auch etwas 
überflüssig. Sich mal ne Stunde mit der Materie beschäftigen und man 
baut sich sowas selbst. Meine #defines kennst du ja oben schon, die 
Funktion zum setzen der Baudrate (in extra Header- und C-File für den 
UART) schaut dann so aus:
1
void uart_init(uint8_t baud)   
2
{
3
  /*set UBRR for baudrate */
4
  UBRRH = (baud >> 8);
5
  UBRRL = baud;
6
  /* Enable receiver and transmitter */
7
  UCSRB |= (1<<RXEN)|(1<<TXEN) | (1<<RXCIE);
8
  /*set Framesize with register UCSRC, initial value: 8 bit, 1 stopbit, look at doc2503 for atmega32 */
9
}

von Falk B. (falk)


Lesenswert?


von Falk B. (falk)


Lesenswert?

@  Michael Köhler (sylaina)

>Da du meinen Beitrag zitiert hattest bin ich davon ausgegangen, dass du
>meinen Codeteil meintest.

>Funktion zum setzen der Baudrate (in extra Header- und C-File für den
>UART) schaut dann so aus:

>  /*set UBRR for baudrate */
>  UBRRH = (baud >> 8);
>  UBRRL = baud;

BAUD ist nicht UBRR, schlechte Namenswahl! Warum kopiert man nicht 
einfach den Teil aus dem Tutorial, dort steht es gescheit drin!

https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Der_UART#UART_initialisieren

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.