Forum: Mikrocontroller und Digitale Elektronik Verständnisproblem bei UART Übertragung


von Soder D. (soderdaen)


Lesenswert?

Hallo zusammen,

ich bin gerade dabei mich in die Mikrocontrollerprogrammierung 
hineinzuarbeiten und will mir gerade die Kommunikation zwischen µC und 
PC beibringen, hab da aber ein kleines Problemchen. Benutzt wird ein 
ATmega 328p im Arduino Uno.

Und zwar hab ich dieses Programm gefunden, was auch bestens 
funktioniert, selbst wenn ich den _delay_ms auf z.B. 1ms stelle.
1
#ifndef F_CPU
2
#define F_CPU 16000000UL
3
#endif
4
#define BAUDRATE 9600        
5
#define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1)
6
#include <util/delay.h>
7
#include <avr/io.h>
8
#include <avr/interrupt.h>
9
10
void USART_init(void);
11
void USART_send( unsigned char data);
12
void USART_putstring(char* StringPtr);
13
14
15
int main(void)
16
{
17
  char String[]="Hello world!!"; 
18
  USART_init();        
19
    
20
  while(1){
21
    USART_putstring(String);   
22
    _delay_ms(1000);        
23
  }  
24
  return 0;
25
}
26
27
28
void USART_send(unsigned char data)
29
{  
30
  while (!(UCSR0A & (1<<UDRE0)))
31
  {
32
  }
33
  UDR0= data;
34
35
}
36
37
void USART_putstring(char* StringPtr)
38
{
39
  while(*StringPtr != 0x00)
40
  {
41
    USART_send(*StringPtr);   
42
    StringPtr++;    
43
  }
44
}
45
46
void USART_init(void)
47
{
48
  UBRR0H = (uint8_t)(BAUD_PRESCALLER>>8);      
49
  UBRR0L = (uint8_t)(BAUD_PRESCALLER);      
50
  UCSR0B = (1<<TXEN0);              
51
  UCSR0C = ((1<<UCSZ00)|(1<<UCSZ01));
52
}

Funktioniert so weit so gut mir "Hello World" im Atmel Studio anzeigen 
zu lassen.

Ich habe jetzt also versucht ohne das UART_putstring mir was ausgeben zu 
lassen.
1
#ifndef F_CPU
2
#define F_CPU 16000000UL
3
#endif
4
#define BAUDRATE 9600        
5
#define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1)
6
#include <util/delay.h>
7
#include <avr/io.h>
8
#include <avr/interrupt.h>
9
10
void USART_init(void);
11
void USART_send( void);
12
void USART_putstring(char* StringPtr);
13
14
15
int main(void){
16
  USART_init();        
17
  while(1)
18
        {
19
         USART_send();
20
  _delay_ms(1000);      
21
  }  
22
  return 0;
23
}
24
25
26
void USART_send(void)
27
{  
28
  while (!(UCSR0A & (1<<UDRE0)))
29
  {
30
  }
31
  UDR0='a';
32
  UDR0='b';
33
  UDR0='c';
34
}
35
36
37
38
void USART_init(void)
39
{
40
  UBRR0H = (uint8_t)(BAUD_PRESCALLER>>8);      
41
  UBRR0L = (uint8_t)(BAUD_PRESCALLER);
42
  UCSR0B = (1<<TXEN0);      
43
  UCSR0C = ((1<<UCSZ00)|(1<<UCSZ01));
44
}

Hier will ich quasi jedes Zeichen selber ausgeben ohne diese in eine 
String zu verketten. Funktioniert fast. Als Ausgabe steht nämlich nur 
"ab" da und das "c" fällt weg. Selbst mit einem _delay_ms(1) nach 
UDR0='b' ist die Ausgabe weiterhin "ab". Bei einem delay von 10ms klappt 
das Ganze dann wieder und es steht korrekt "abc" da. Es ist also klar, 
dass es eine Timing Sache ist, aber warum genau weiß ich eben nicht. Bei 
dem oberen Code und einem delay von 1ms in der main funktioniert es ja 
eben doch....

von Peter II (Gast)


Lesenswert?

1
  while (!(UCSR0A & (1<<UDRE0)))
2
  {
3
  }
4
  UDR0='a';
5
  UDR0='b';
6
  UDR0='c';

du darfst halt code nicht einfach kopieren sondern muss ihn verstehen!

Das while prüft ob der Sendepuffer leer ist. Dann kann man genau 1 
Zeichen versenden. Da ist kein Platz für 3 zeichen.

von Soder D. (soderdaen)


Lesenswert?

Peter II schrieb:
>
1
>   while (!(UCSR0A & (1<<UDRE0)))
2
>   {
3
>   }
4
>   UDR0='a';
5
>   UDR0='b';
6
>   UDR0='c';
7
>
>
> du darfst halt code nicht einfach kopieren sondern muss ihn verstehen!
>
> Das while prüft ob der Sendepuffer leer ist. Dann kann man genau 1
> Zeichen versenden. Da ist kein Platz für 3 zeichen.

Deswegen frag ich ja nach.


Genau, dessen bin ich mir ja bewusst. Man kann ein Zeichen versenden, es 
werden aber 2 Zeichen versendet.

Mich wundert es auch, warum es beim ersten Beispiel dann geht. Im 
putstring wird beispielsweise das "H" an USART_send übergeben und 
übertragen. Dann wird ja das "e" zum USART_send übergeben und überprüft 
ob der Puffer nicht schon voll ist. Wenn der leer ist, dann wird das 
Zeichen eingelesen und übertragen.  Zwischen "H" wird übertragen und "e" 
wird übergeben liegen aber nur 4 Codezeilen, ich nehme an auch nicht 
gerade viele Maschinencodezeilen, warum ist dann der Puffer hier schon 
wieder leer und beim zweiten Fall nicht?

von Loddaar (Gast)


Lesenswert?

Soder D. schrieb:
> Zwischen "H" wird übertragen und "e"
> wird übergeben liegen aber nur 4 Codezeilen,

es kommt nicht auf die ANzahl der Codezeilen an,
sondern dass vor jedem Beschreiben des Register geschaut wird ob das 
vorherige Zeichen schon verarbeitet ist

von H.Joachim S. (crazyhorse)


Lesenswert?

Das erste Zeichen wird mehr oder weniger direkt ans Senderegister 
übergeben, das 2. landet erstmal im UART-Buffer (als Hardwareregister 
vorhanden).

von B. S. (bestucki)


Lesenswert?

Soder D. schrieb:
> warum ist dann der Puffer hier schon
> wieder leer und beim zweiten Fall nicht?

Der erste Wert wird in den Buffer geschrieben, dieser wird gleich von 
der Hardware gelesen und verarbeitet. Somit ist der Buffer wieder leer. 
Jetzt kannst du bereits den nächsten Wert in den Buffer schreiben. Der 
Buffer ist jetzt aber voll, bis das erste Zeichen gesendet wurde. Erst 
dann kannst du das nächste Zeichen schreiben.

von Michael U. (amiga)


Lesenswert?

Hallo,

weil meines Wissens der Mega328 einen 2Byte-Sendepuffer hat.

Für Dein Experiment wäre es so möglich:

[code]
>   while (!(UCSR0A & (1<<UDRE0)))
>   {
>   }
>   UDR0='a';

>   while (!(UCSR0A & (1<<UDRE0)))
>   {
>   }
>   UDR0='b';

>   while (!(UCSR0A & (1<<UDRE0)))
>   {
>   }
>   UDR0='c';
>
[\code]

Gruß aus Berlin
Michael

von Kirsch (Gast)


Lesenswert?

Soder D. schrieb:
> Mich wundert es auch, warum es beim ersten Beispiel dann geht.

weil in USART_send jedes mal gewartet wird bis der Puffer frei wird.

von Peter II (Gast)


Lesenswert?

Michael U. schrieb:
> weil meines Wissens der Mega328 einen 2Byte-Sendepuffer hat.

ich glaube er hat 1byte puffer und 1 Byte war gerade versendet wird.

von Soder D. (soderdaen)


Lesenswert?

Loddaar schrieb:
> Soder D. schrieb:
>> Zwischen "H" wird übertragen und "e"
>> wird übergeben liegen aber nur 4 Codezeilen,
>
> es kommt nicht auf die ANzahl der Codezeilen an,
> sondern dass vor jedem Beschreiben des Register geschaut wird ob das
> vorherige Zeichen schon verarbeitet ist

Ah, ich glaub ich hab mir zu wenig Gedanken um die while Schleife 
gemacht.
1
while(!(UCSR0A & (1<<UDRE0)));
2
UDR0= data;

Es wird überprüft ob eine 1 in UDRE0 steht. Angenommen Puffer ist voll. 
Also steht eine 0 in UDRE0. Diese wird negiert -> 1. Die Whileschleife 
wird also ausgeführt. Es ist aber keine Anweisung definiert, also wieder 
zurück in die Bedingung. Wenn der Puffer mal leer sein sollte wird UDRE0 
auf 1 gesetzt, negiert ergibt das 0. Das heißt wir springen aus der 
while Schleife und kümmern uns um UDR0.

So richtig verstanden ? Dann würde es für mich Sinn ergeben :P

EDIT: Übrigens danke für die schnellen und guten Rückmeldungen :)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Soder D. schrieb:
> So richtig verstanden ?

Richtig: Vor jedem Setzen von UDR0 muss man die while-Schleife laufen 
lassen.

Aber Dein Ansatz, die void USART_send()-Funktion abzuändern, ist falsch!

Lass sie wie sie ist und rufe stattdessen auf:

    USART_send('a');
    USART_send('b');
    USART_send('c');

Dann klappt das auch.

von Wolfgang (Gast)


Lesenswert?

Soder D. schrieb:
> Bei einem delay von 10ms klappt das Ganze dann wieder und es
> steht korrekt "abc" da.

Wenn du die Baudrate auf einen höheren Wert stellst, ist die Übertragung 
schneller und das Senden eines Zeichens dauer weniger lang. Bei eine 
Baudrate von 9600 kBd sollte allerdings die Übertragung eines Zeichens 
mit seinen 10Bit nach weniger als 1,1ms (=10*104µs) abgeschlossen sein.

von ThomasK (Gast)


Lesenswert?

Bei 9600 kBd sollte die Übertragung sogar in weniger als 1.1µs 
abgeschlossen sein :)

von Stefan K. (stefan64)


Lesenswert?

ThomasK schrieb:
> Bei 9600 kBd sollte die Übertragung sogar in weniger als 1.1µs
> abgeschlossen sein :)

Wenn Du solche Spitzfindigkeiten hier anbringst, dann solltest Du sie 
auch etwas erklären, da aus den Posts hervorgeht, dass Soder noch nicht 
sehr viel Erfahrung hat, ansonsten trägt das nur zur Verwirrung bei.

9600 kBd bedeutet 9,6 MegaBaud und damit wäre ein Zeichen in der Tat 
nach 1,1us versendet. Allerdings erlaubt kein AVR eine derart hohe 
Baudrate, die 1000-er Bezeichnung ist schlicht doppelt gemoppelt.

Gruß, Stefan

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.