Forum: Mikrocontroller und Digitale Elektronik Atmega 328p USART


von P. F. (funkpurzel)


Angehängte Dateien:

Lesenswert?

Ich sitze hier am AVR Studio 7 und möchte eine USART-Ausgabe 
programmieren. Zum ersten Test soll in einer Testschleife nur ein 
Zeichen (z.B. 0x10101010 ) ausgegeben werden.
Ich habe einen neuen Atmega328p per debugwire angeschlossen, das 
debugging funktioniert. Die fuses hatte ich zuvor auf externen Quarz 
„full swing“ progammiert und am Prozessor sehe das Clocksignal an Pin 10 
mit 16 Mhz in Full Swing.
Die Ausgabe beobachte ich mit dem Realterm und einem 60 Mhz – Oszi. Ich 
sehe auf den Geräten auch ein Zeichen pro Ausgabe. Leider aber stimmt 
Folgendes nicht:
Der Zeicheninhalt sieht am Oszi etwa aus wie (...Startbit), 00001000, (2 
Stopbits...) und die Zeichenzeit ist viel zu lang:  Am Oszi gemessen= 
1,4 ms, sie müsste dagegen

Char.-Zeit = 11Bit * 1/Baud = 11*(1/57600) = 0,19 ms betragen.

Das Realterm zeigt auch nur falsche Zeichen an.
Mein Code liegt in der Anlage "Listing_UART-Problem.txt"

Wer kann mir sagen, was ich falsch mache oder wie ich den Fehler weiter 
eingrenzen kann?

von Falk B. (falk)


Lesenswert?

@P. F. (funkpurzel)

>Stopbits...) und die Zeichenzeit ist viel zu lang:  Am Oszi gemessen=
>1,4 ms, sie müsste dagegen

>Char.-Zeit = 11Bit * 1/Baud = 11*(1/57600) = 0,19 ms betragen.

Das klingt nach ~ Faktor 8. Wahrscheinlich hast du die CLK-DIV8 Fuse 
noch gesetzt.

von P. F. (funkpurzel)


Lesenswert?

Danke für die schnelle Antwort, Falk.
Ich habe eine Fuse namens LOW.CKDIV8 gefunden, den Haken gelöscht und 
die Fuses programmiert. Ohne ein neuses Build hat sich die Chr.-Zeit 
sofort geändert auf grob 0,15 ms, sie könnte jetzt also stimmen. Aber 
leider ist der Zeicheninghalt unverändert, auch nach einem neuen Build.
Immerhin bin ich einen Schritt weiter.

von Nico W. (nico_w)


Lesenswert?

0x10101010 sind 4 Bytes. Das ist dir bewusst?

von Jim Beam (Gast)


Lesenswert?

Nico W. schrieb:
> 0x10101010 sind 4 Bytes. Das ist dir bewusst?

Genau, ändere das mal in 0b10101010...
Und sind im RS-Empfänger die (unüblichen) 2-StopBits eingestellt?

von M. K. (sylaina)


Lesenswert?

So sieht bei mir die UART aus:

uart.h
1
//
2
//  uart.h
3
//
4
//  Created by Michael Köhler on 20.10.15.
5
//
6
//
7
8
#ifndef uart_h
9
#define uart_h
10
11
#include <avr/io.h>
12
#include <avr/pgmspace.h>
13
#include <avr/eeprom.h>
14
15
#define commandComplete         '\n'
16
#define baudRate                38400UL
17
18
#define UART_MAXSTRLEN          40
19
20
#define UBRR_VAL (F_CPU+baudRate*8)/(baudRate*16)-1     // clever runden
21
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))             // Reale Baudrate
22
#define BAUD_ERROR ((BAUD_REAL*1000)/baudRate)          // Fehler in Promille, 1000 = kein Fehler.
23
24
#if ((BAUD_ERROR<990) || (BAUD_ERROR>1010))
25
#error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch!
26
#endif
27
28
void uartSetup(uint16_t ubbrValue);
29
void sendChar(unsigned char zeichen);
30
void sendString(const char* charOfString);
31
void sendString_p(const char* charOfString);
32
void sendString_e(const char* charOfString);
33
34
#endif /* uart_h */

und die uart.c
1
//
2
//  uart.c
3
//
4
//  Created by Michael Köhler on 20.10.15.
5
//
6
7
#include "uart.h"
8
9
10
void uartSetup(uint16_t ubbrValue){
11
    // Baudrate einstellen
12
    UBRR0H = ubbrValue >> 8;
13
    UBRR0L = ubbrValue & 0xFF;
14
    
15
    UCSR0B |= (1<<TXEN0)|(1 << RXEN0)|(1<<RXCIE0);  // UART TX/RX einschalten, RXIE einschalten, Datenempfang wird in der ISR behandelt.
16
}
17
void sendChar(unsigned char zeichen){
18
    {
19
        /* Wait for empty transmit buffer */
20
        while ( !( UCSR0A & (1<<UDRE0)) );
21
        /* Put data into buffer, sends the data */
22
        UDR0 = zeichen;
23
    }
24
}
25
void sendString(const char* charOfString){
26
    //send string from ram
27
    /* while *charOfString != '\0' */
28
    while (*charOfString) {
29
        sendChar(*charOfString);
30
        charOfString++;
31
    }
32
}
33
void sendString_p(const char* charOfString){
34
    //send string from flash
35
    register char c;
36
    /* while *charOfString != '\0 */
37
    while ( (c = pgm_read_byte(charOfString++)) ) {
38
        sendChar(c);
39
    }
40
}
41
42
void sendString_e(const char* charOfString){
43
    //send string from eeprom
44
    register char c;
45
    /* while *charOfString != '\0 */
46
    while ( (c = eeprom_read_byte((uint8_t*)charOfString++)) ) {
47
        sendChar(c);
48
    }
49
}

Datenempfang kasper ich in der ISR und im Mainloop entsprechend ab.

von Karl M. (Gast)


Lesenswert?

Hallo,

ich habe da noch einen Tipp.
M. K. schrieb:
> #define UBRR_VAL (F_CPU+baudRate*8)/(baudRate*16)-1

Die Berechnung mit Rundung ist korrekt, aber man kann die Formel aus dem 
Datenblatt Table 20-1. auch übersichtlicher schreiben:
1
#define UBRR_VAL (uint32_t)(1.0 *F_CPU /16 /baudRate -0.5)

von Ralph S. (jjflash)


Lesenswert?

Sein Hauptproblem ist, wie Jim Beam schreibt:

Jim Beam schrieb:
> Nico W. schrieb:
>> 0x10101010 sind 4 Bytes. Das ist dir bewusst?
>
> Genau, ändere das mal in 0b10101010...
> Und sind im RS-Empfänger die (unüblichen) 2-StopBits eingestellt?

Solange

UDR0 = 0X10101010;

besteht, ist das gleichbedeutend mit UDR0= 0x10 = 0b10000000. Hier ist 
dann genau ein einzelnes Bit gesetzt welches er auch auf dem Oszi sieht 
!

von M. K. (sylaina)


Lesenswert?

Karl M. schrieb:
> Hallo,
>
> ich habe da noch einen Tipp.
> M. K. schrieb:
>> #define UBRR_VAL (F_CPU+baudRate*8)/(baudRate*16)-1
>
> Die Berechnung mit Rundung ist korrekt, aber man kann die Formel aus dem
> Datenblatt Table 20-1. auch übersichtlicher schreiben:
>
1
> #define UBRR_VAL (uint32_t)(1.0 *F_CPU /16 /baudRate -0.5)
2
>

Und ne Floatrechnung draus stricken? Warum sollte man das tun? Klar, 
geht auch.

von C.K. (Gast)


Lesenswert?

M. K. schrieb:
> Und ne Floatrechnung draus stricken? Warum sollte man das tun? Klar,
> geht auch.

Nein, da in der Berechnung nur Konstanten vorkommen, berechnet der 
Compiler bereits das Ergebnis. Zur Laufzeit ist dann bereits der feste 
Wert bekannt.

von M. K. (sylaina)


Lesenswert?

C.K. schrieb:
> Nein, da in der Berechnung nur Konstanten vorkommen, berechnet der
> Compiler bereits das Ergebnis. Zur Laufzeit ist dann bereits der feste
> Wert bekannt.

Und er rechnet es dennoch als float, dann castet er es in ne 32 bit 
Interger um das Ergebnis letztendlich in ein 16 bit Register zu 
schreiben. Ich finde: Schön ist anders. Und damit bin ich nicht alleine 
denn auch, aber nicht nur, hier im Artikel zum UART wird die 
Festkommaarithmetik verwendet zur Berechnung des UBRR-Wertes.
Seite 185 des aktuellen Datenblattes zum Atmega328P (2018, DS40002016A) 
zeigt auch nur Festkommaarithmetik:
1
#define MYUBRR FOSC/16/BAUD-1
Und Table 20-1 zeigt auch keine Gleitkomma-Arithemtik.

von Nico W. (nico_w)


Lesenswert?

Ralph S. schrieb:
> Solange
>
> UDR0 = 0X10101010;
> besteht, ist das gleichbedeutend mit UDR0= 0x10 = 0b10000000.

Nein. 0x10 == 0b00010000.

von M. K. (sylaina)


Lesenswert?

Nico W. schrieb:
> Ralph S. schrieb:
>> Solange
>>
>> UDR0 = 0X10101010;
>> besteht, ist das gleichbedeutend mit UDR0= 0x10 = 0b10000000.
>
> Nein. 0x10 == 0b00010000.

gleichbedeutend != genau oder gleich

Ralph meinte, es ist egal ob man 0x10 oder 0b10000000 schreibt, in 
beiden Fällen ist in dem Byte nur ein Bit gesetzt und alle anderen sind 
0 und damit hat er völlig recht.
Du hast natürlich auch recht: 0x10 == 0b00010000 aber das ist ein klein 
wenig was anderes, was Ralph meinte ;)

von Thomas E. (thomase)


Lesenswert?

M. K. schrieb:
> Und er rechnet es dennoch als float, dann castet er es in ne 32 bit
> Interger um das Ergebnis letztendlich in ein 16 bit Register zu
> schreiben. Ich finde: Schön ist anders. Und damit bin ich nicht alleine

Doch, damit bist du ganz alleine. Wen interessiert es, wie das auf dem 
PC ausgerechnet wird?

von M. K. (sylaina)


Angehängte Dateien:

Lesenswert?

Thomas E. schrieb:
> Doch, damit bist du ganz alleine. Wen interessiert es, wie das auf dem
> PC ausgerechnet wird?

Anbei ein Screenshot aus dem aktuellen Datenblatt. Ich sehe keine 
Gleitkomma-Berechnung, was also erzählt ihr für einen Scheiß?

von Erwin D. (Gast)


Lesenswert?

M. K. schrieb:
> Ich sehe keine
> Gleitkomma-Berechnung, was also erzählt ihr für einen Scheiß?

Warum regst du dich so enorm auf?
Was spielt das für eine Rolle?
Wenn der PC mit dem Präprozessor vor dem Compilieren einmalig eine µs 
länger braucht, merkst du das garantiert nicht, oder doch?

Also reg' dich wieder ab und wisch dir den Schaum vom Mund :-)

von Karl M. (Gast)


Lesenswert?

Hallo,

was M. K.immer noch nicht verstanden hat, ist die von ihm gezeigte 
Berechnung rundet ab und das von mir verwende vorgehen rundet auf.
D.h. man addiert noch 0,5 zur Rechnung dazu.

Und es ist, wie schon geschrieben UBRR_VAL wird als Konstante im 
Comilerlauf erzeugt und nicht per Programmcode im AVR berechnet.
1
#define UBRR_VAL (uint32_t)(1.0 *F_CPU /16 /baudRate -0.5)

M. K. schrieb:
> Thomas E. schrieb:
>> Doch, damit bist du ganz alleine. Wen interessiert es, wie das auf dem
>> PC ausgerechnet wird?
>
> Anbei ein Screenshot aus dem aktuellen Datenblatt. Ich sehe keine
> Gleitkomma-Berechnung, was also erzählt ihr für einen Scheiß?

M. K. schrieb:
> Thomas E. schrieb:
>> Doch, damit bist du ganz alleine. Wen interessiert es, wie das auf dem
>> PC ausgerechnet wird?
>
> Anbei ein Screenshot aus dem aktuellen Datenblatt. Ich sehe keine
> Gleitkomma-Berechnung, was also erzählt ihr für einen Scheiß?

Warum auch sollte man das sehen?

Die Formel rechnet mit reellen Zahlen, da können auch Nachkommastellen 
entstehen!
Erst in der Implementierung im eigentlichen Atmel AVR Code muss man sich 
entscheiden, wie man die Berechnung im Zahlenraum N/ oder Z/ durchführt.

Alles klar?

von Thomas E. (thomase)


Lesenswert?

M. K. schrieb:
> was also erzählt ihr für einen Scheiß?

Was willst du denn?

von M. K. (sylaina)


Lesenswert?

Karl M. schrieb:
> was M. K.immer noch nicht verstanden hat, ist die von ihm gezeigte
> Berechnung rundet ab und das von mir verwende vorgehen rundet auf.

1. Wie kommst du denn zur der Erkenntnis, dass ich das nicht verstanden 
habe?
2. Warum sollte das Abrunden schlecht sein?
3. Was ist an deiner Lösung übersichtlicher als an meiner?
4. Warum castest du auf uint32_t wo doch ein uint16_t sinnvoller ist?

von Karl M. (Gast)


Lesenswert?

M. K. schrieb:
> 2. Warum sollte das Abrunden schlecht sein?
da im Mittel bei 50% aller Fälle der Fehler um Eins zu klein ist.

Es werden bei Runden mit +0,5 alle Rechnungen mit Nachkommastelle im 
Bereich von [0..0,5) auf 0 und [0,5..1) auf +1 gerundet.

Beispiel:
Gegeben: F=8Mhz, Teiler(Prescaler)=1/16, Baudrate=57600

A) Rechnung mit Abrunden (trunc):
UBBR = (uint32_t)(8Mhz/16/57600-1) = (uint32_t)(7,6806) = 7
Reale Baudrate = 8MHz/16/(7+1) = 62500 [Bit/s]
Fehler Berechnung: error = 62500/57600-1 = +8,50%

B) Rechnung mit Runden +0,5:
UBBR = (uint32_t)(8Mhz/16/57600+0,5-1) = (uint32_t)(8Mhz/16/57600-0,5) = 
(uint32_t)(8,1806) = 8
Reale Baudrate = 8MHz/16/(8+1) = 55555,6 [Bit/s]
Fehler Berechnung: error = 55555,6/57600-1 = -3,55%

Ups ist ja besser!
Die reale Baudrate ist nicht gut (err >1%) aber es zeigt den Rechenweg!

> 4. Warum castest du auf uint32_t wo doch ein uint16_t sinnvoller ist?

Warum sollte das so sein - bei einer Konstanten?

Wenn man einen (16Bit) Bereichsüberlauf feststellen wollte, wie könnte 
man das machen?

von BlaBla (Gast)


Lesenswert?

Wie wäre es denn hier mit?
1
/** @brief  UART Baudrate Expression
2
 *  @param  xtalCpu  system clock in Mhz, e.g. 4000000L for 4Mhz          
3
 *  @param  baudRate baudrate in bps, e.g. 1200, 2400, 9600     
4
 */
5
#define UART_BAUD_SELECT(baudRate,xtalCpu) (((xtalCpu)+8UL*(baudRate))/(16UL*(baudRate))-1UL)
6
7
/** @brief  UART Baudrate Expression for ATmega double speed mode
8
 *  @param  xtalCpu  system clock in Mhz, e.g. 4000000L for 4Mhz           
9
 *  @param  baudRate baudrate in bps, e.g. 1200, 2400, 9600     
10
 */
11
#define UART_BAUD_SELECT_DOUBLE_SPEED(baudRate,xtalCpu) ((((xtalCpu)+4UL*(baudRate))/(8UL*(baudRate))-1)|0x8000)

von M. K. (sylaina)


Lesenswert?

Karl M. schrieb:
> da im Mittel bei 50% aller Fälle der Fehler um Eins zu klein ist.

Du hast dir also meine Rechnung überhaupt nicht angeschaut. Sonst 
wüsstest du, dass mit meiner Lösung genau das Selbe herauskommt wie mit 
deiner Lösung.

Karl M. schrieb:
> Ups ist ja besser!
> Die reale Baudrate ist nicht gut (err >1%) aber es zeigt den Rechenweg!

Ja, es zeigt den Rechnenweg. Aber dann rechne doch bei dem 
Abrunden-Beispiel auch bitte mit meiner Lösung:

Ups, da kommt ja genau das Selbe raus wie bei deiner Lösung. Hättest du 
dir meine Lösung genauer angesehen hättest du gesehen, dass auch meine 
Gleichung 0.5 dazu addiert. ;)
Und da in der Gleichung nur Integer-Werte stehen wird der Compiler auch 
direkt das Ganze als Integer-Rechnung behandeln, der Typcast entfällt 
hier völlig.

Karl M. schrieb:
> Warum sollte das so sein - bei einer Konstanten?

Weil du ein '(uint32_t)' davor schreibst. Das ist in diesem Falle nicht 
sinnvoll, damit zwingst du den Compiler, auch bei einer Konstante, den 
Wert als 32-bit Integer zu behandeln. Und das bei einem Wert der bei 
weitem nicht mal 16 Bit groß wird und der nur in ein 16 Bit Register 
rein soll.

Karl M. schrieb:
> Wenn man einen (16Bit) Bereichsüberlauf feststellen wollte, wie könnte
> man das machen?

Na die Kombination aus Taktrate und Baudrate, die durchaus Sinn machen 
sollte, will ich mal sehen, die die 0xffff reisst. Die meisten, 
sinnvollen, Kombinationen reissen ja nicht mal die 0xff.

: Bearbeitet durch User
von Erwin D. (Gast)


Lesenswert?

M. K. schrieb:
> Ups, da kommt ja genau das Selbe raus wie bei deiner Lösung.

Und warum pochst du dann so dermaßen hartnäckig auf deine Lösung?
Nur, weil deine Lösung beim Compilieren eine µs schneller ist?
Spielt das eine Rolle?
Was kommt im Endeffekt raus?
> Ups, da kommt ja genau das Selbe raus wie bei deiner Lösung.

Wir drehen uns im Kreis, merkst du das?

Ich könnte dich verstehen, wenn die Berechnung zur Laufzeit ausgeführt 
würde. Aber so wird sie nur ein einziges Mal ausgeführt. Und zwar VOR 
dem Compilieren. Und außerdem vom PC.

Und jetzt hör doch bitte auf mit der Spiegelfechterei.
Was bringt das?

von M. K. (sylaina)


Lesenswert?

Erwin D. schrieb:
> Und warum pochst du dann so dermaßen hartnäckig auf deine Lösung?

Weil Karl sagte, dass meine Lösung schlechter sei und das ist Quatsch. 
Sie ist mindestens genauso gut wie seine Lösung. Ich halte es hier für 
völlig legitim, dass ich meine Lösung verteidige.

von Thomas E. (thomase)


Lesenswert?

M. K. schrieb:
> Weil du ein '(uint32_t)' davor schreibst. Das ist in diesem Falle nicht
> sinnvoll, damit zwingst du den Compiler, auch bei einer Konstante, den
> Wert als 32-bit Integer zu behandeln. Und das bei einem Wert der bei
> weitem nicht mal 16 Bit groß wird und der nur in ein 16 Bit Register
> rein soll.

Was regst du dich eigentlich so künstlich auf? Du lässt selbst doch auch 
mit 32Bit rechnen. Davor bewahrt dich auch nicht, daß du nicht weißt, 
was du da tust.

Der Präprozessor und nicht der Compiler, rechnet per default mit 16Bit. 
Es sei denn, in der Rechnung befindet sich ein 32Bit-Wert. Was mit F_CPU 
der Fall ist. Dann kommt der Präprozessor von selbst drauf, das mit 
32Bit zu rechnen.

M. K. schrieb:
> Weil Karl sagte, dass meine Lösung schlechter sei und das ist Quatsch.
> Sie ist mindestens genauso gut wie seine Lösung.

Also ist deine Lösung besser. "Mindestens genauso gut" drückt 
schließlich genau das aus. Da sie das gleiche Ergebnis liefert, ist sie 
aber maximal gleichwertig.

> Ich halte es hier für völlig legitim, dass ich meine Lösung verteidige.

Jaja.

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.