Verständnisproblem bei UART Übertragung

OP #4414616
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....
Gast #4414619
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.
OP #4414636
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?
Gast #4414643
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
#4414648
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.
#4414650
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
OP #4414678
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 :)
Gast #4414702
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.
#4415765
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

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 10 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.