Forum: Mikrocontroller und Digitale Elektronik Mega328p UART sendet doppelt


von Bastler87 (Gast)


Lesenswert?

Hallo,

ich habe mit einem Atmega 328p ein Problem mit dem senden per UART. Ich 
habe mittlerweile viel herumprobiert und komme einfach nicht zu einer 
Lösung bzw. Eingrenzung des Problems.

Wenn ich das folgende Programm (modifiziert aus dem Tutorial) nehme, 
kann ich per hterm Zeichen senden und diese werden auch korrekt 
zurückgesendet.
1
#define BaudRate 9600UL
2
#define F_CPU    16000000UL
3
4
int main (void) {
5
6
  uint16_t ubrr = (uint16_t) ((uint32_t)F_CPU / (16*BaudRate)-1);
7
8
  UBRR0H = (uint8_t)(ubrr >> 8);
9
  UBRR0L = (uint8_t)(ubrr);
10
11
  UCSR0B = (1 << RXEN0)|(1 << TXEN0);
12
  // 8N1
13
  UCSR0C = (1 << UCSZ01)|(1 << UCSZ00);
14
15
  unsigned char buffer;
16
17
  while (1) {
18
    while ( !(UCSR0A & (1<<RXC0)) ) ;
19
   buffer = UDR0;
20
21
    while ( !( UCSR0A & (1<<UDRE0)) );
22
    UDR0 = buffer;
23
24
  }
25
  return 0;
26
}

Versuche ich allerdings zum debuggen nur zeichen zu senden, werden diese 
immer doppelt gesendet...
1
#define BaudRate 9600UL
2
#define F_CPU    16000000UL
3
4
int main (void) {
5
6
  uint16_t ubrr = (uint16_t) ((uint32_t)F_CPU / (16*BaudRate)-1);
7
8
  UBRR0H = (uint8_t)(ubrr >> 8);
9
  UBRR0L = (uint8_t)(ubrr);
10
11
  UCSR0B = (1 << RXEN0)|(1 << TXEN0);
12
  // 8N1
13
  UCSR0C = (1 << UCSZ01)|(1 << UCSZ00);
14
15
  unsigned char buffer='A';
16
17
18
    while ( !( UCSR0A & (1<<UDRE0)) );
19
    UDR0 = buffer;
20
21
  while(1);
22
  return 0;
23
}

=> Es wird immer AA empfangen.

Das Problem tritt also nur auf, wenn ich Daten sende. Wenn ich Daten per 
hterm an den mc sende, werden diese richtig zurückgesendet. Ein 
Baudratenproblem schließe ich deshalb aus.

Ich bin für jeden Tip dankbar
Bastler87

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Das liegt daran, dass UDR ein Doublebuffer Feature hat. Wenn Du UDRE 
testest, wird dieses gleich wieder 1, wenn das erste Byte von UDR in das 
Ausgaberegister übernommen wurde. Daher kann ein 2. Byte in UDR 
platziert werden, wodurch UDRE dann für eine Bytelaufzeit 0 wird.

Im oberen Codesegment wird das durch das Warten auf RXC abgefangen, 
wodurch das Ganze funktioniert. Wenn Du nur ein Byte senden willst, 
musst Du auf TXC warten, bevor Du ein neues Byte scheibst. Wenn man 
ganze Arrays sendet, stört das Doublebuffering nicht, im Gegenteil. Es 
ermöglicht einen lückenlosen Datenstrom.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Wenn das zweite Codebeispiel tatsächlich das getestete Programm ist, ist 
das Verhalten nicht erklärbar. Denn da wird exakt einmal etwas in UDR0 
geschrieben.

von Felix (Gast)


Lesenswert?

Ein Blick ins .lss File könnte noch einen Hinweis liefern. Schau dir mal 
an, was an Assemblercode generiert wurde. In diesem sollten die 
C-Quellcodezeilen als Kommentare stehen, womit die entsprechende Stelle 
des Sendens identifizierbar sein sollte.

von Bastler87 (Gast)


Lesenswert?

Knut B. schrieb:
> Das liegt daran, dass UDR ein Doublebuffer Feature hat. Wenn Du UDRE
> testest, wird dieses gleich wieder 1, wenn das erste Byte von UDR in das
> Ausgaberegister übernommen wurde. Daher kann ein 2. Byte in UDR
> platziert werden, wodurch UDRE dann für eine Bytelaufzeit 0 wird.


Hallo,

ich teste in der ersten while Schleife lediglich ob das Register frei 
ist, und schreibe dann buffer dort hinen. Es kann doch eigentlich kein 
zweites Byte geschrieben werden, da das Programm dann in der nächsten 
while Schleife endlos hängt...
1
    while ( !( UCSR0A & (1<<UDRE0)) );
2
    UDR0 = buffer;
3
4
    while(1);

Felix schrieb:
> Ein Blick ins .lss File könnte noch einen Hinweis liefern.


Ich kann leider kein Assembler, deshalb hier die main.lss
1
00000080 <main>:
2
3
int main (void) {
4
5
  uint16_t ubrr = (uint16_t) ((uint32_t)F_CPU / (16*BaudRate)-1);
6
7
  UBRR0H = (uint8_t)(ubrr >> 8);
8
  80:  10 92 c5 00   sts  0x00C5, r1  ; 0x8000c5 <__TEXT_REGION_LENGTH__+0x7e00c5>
9
  UBRR0L = (uint8_t)(ubrr);
10
  84:  87 e6         ldi  r24, 0x67  ; 103
11
  86:  80 93 c4 00   sts  0x00C4, r24  ; 0x8000c4 <__TEXT_REGION_LENGTH__+0x7e00c4>
12
13
  UCSR0B = (1 << RXEN0)|(1 << TXEN0);
14
  8a:  88 e1         ldi  r24, 0x18  ; 24
15
  8c:  80 93 c1 00   sts  0x00C1, r24  ; 0x8000c1 <__TEXT_REGION_LENGTH__+0x7e00c1>
16
  // 8N1
17
  UCSR0C = (1 << UCSZ01)|(1 << UCSZ00);
18
  90:  86 e0         ldi  r24, 0x06  ; 6
19
  92:  80 93 c2 00   sts  0x00C2, r24  ; 0x8000c2 <__TEXT_REGION_LENGTH__+0x7e00c2>
20
21
  unsigned char buffer='A';
22
23
24
    while ( !( UCSR0A & (1<<UDRE0)) );
25
  96:  80 91 c0 00   lds  r24, 0x00C0  ; 0x8000c0 <__TEXT_REGION_LENGTH__+0x7e00c0>
26
  9a:  85 ff         sbrs  r24, 5
27
  9c:  fc cf         rjmp  .-8        ; 0x96 <main+0x16>
28
    UDR0 = buffer;
29
  9e:  81 e4         ldi  r24, 0x41  ; 65
30
  a0:  80 93 c6 00   sts  0x00C6, r24  ; 0x8000c6 <__TEXT_REGION_LENGTH__+0x7e00c6>
31
  a4:  ff cf         rjmp  .-2        ; 0xa4 <main+0x24>
32
33
000000a6 <_exit>:
34
  a6:  f8 94         cli
35
36
000000a8 <__stop_program>:
37
  a8:  ff cf         rjmp  .-2        ; 0xa8 <__stop_program>



Irgendiwe habe ich das Gefühl, dass das Problem an meinem billigen 
USB-UART Adapter mit ch340G Chip liegt. Ich habe ihn allerding unter 
Archlinux und Win10 mit gleichem Ergebnis getestet.

Sende ich A, wird AA angezeigt.
Sende ich AB, wird ABAB angezeigt.

Auffallend ist auch, dass die Wiederholung der Zeichen mit einer kurzen 
Verzögerung angezeigt wird.

Es wird also AB_Verzögerung_AB empfangen...

von Peter K. (peterka2000)


Lesenswert?

Welche Hardware nutzt du denn zum Debuggen/ Programmieren?

Kann es sein, dass geflasht wird, und der Prozessor kurz danach resettet 
wird?
Probiere doch mal, in der while-Schleife Zeichen zu senden, vielleicht 
noch ein Delay mit rein. Passiert es dann immernoch?

von eProfi (Gast)


Lesenswert?

Ziemlich sicher wird ein Reset ausgelöst.
Die Reset-Ursache (watchdog, Versorgung...) findest Du im MCU Status 
Register MCUSR. Siehe Datenblatt "Reset sources".

Beachte, dass der CH340 über einen C mit dem Reset-Pin verbunden ist, 
damit der PC einen Neustart auslösen kann.

von Bastler87 (Gast)


Lesenswert?

eProfi schrieb:
> Ziemlich sicher wird ein Reset ausgelöst.

Hallo,

ich programmiere über ISP mit einem AVRDragon. Ich versorge den 
Controller auch über den Dragon und hatte ihn zum Resett immer aus dem 
USB-Port gezogen...

Der Dragon hat dann nach ein paar Sekunden den Mega328p resettet.

Wenn ich den Dragon an der Spannung lasse und lediglich den M328p 
resette funktioniert alles wie erwartet...

Vielen Dank für eure Hilfe ihr habt mir den Sonntag gerettet :-)

von Jim M. (turboj)


Lesenswert?

Bastler87 schrieb:
> Controller auch über den Dragon und hatte ihn zum Resett immer aus dem
> USB-Port gezogen...

AFAIK ist das eine ziemlich sichere Methode den Dragon zu töten, wenn 
das Target selbstversorgt ist.

Dabei raucht dann der Pegelwander ab, und das ist so ein besch... SMD 
Teil das man nur mit Rework-Station ordentlich verarbeiten kann...

von eProfi (Gast)


Lesenswert?

> Der Dragon hat dann nach ein paar Sekunden den Mega328p resettet.
Deshalb gebe ich nach dem Init der Seriellen ein kurzes Lebenszeichen 
oder Willkommensmeldung aus, auch wenn es nur ein 'r' ist.
So weiß man gleich: Reset wurde ausgelöst, Verbindung steht, Baudrate 
stimmt.

von Stefan F. (Gast)


Lesenswert?

Mein Programmieradapter schickt noch einen zweiten Reset Impuls 
hinterher, wenn ich am Target den reset-Taster drücke.

Das ist mir zum ersten mal aufgefallen, als ich eine LED blitzen liess, 
um den Programmstart anzuzeigen.

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.