Forum: Compiler & IDEs Empfange nur Kauderwelsch am UART (Computer)


von Konstantin L. (konze)


Lesenswert?

Hallo zusammen,

ich versuche gerade mich ein wenig mit dem UART bzw. USART vertraut zu 
machen. Ich habe ein paar Beispiele im Internet gesucht und konnte diese 
auch nachvollziehen und habe mich dann dazu zu entschlossen die Theorie 
in die Praxis zu übertragen.
Hier kommt mein Programmcode:
main.c
1
/* main.c */
2
3
#include <avr/io.h>
4
#include <avr/delay.h>
5
#include "uart.h"
6
7
#ifndef F_CPU
8
#define F_CPU 8000000UL
9
#endif
10
11
12
int main(void) {
13
14
  uart_init();
15
16
  unsigned char i = 0;
17
18
  while(1) {
19
    for(i = 0; i < 128; i++) {
20
21
      uart_putc((uint8_t)i);
22
23
      _delay_ms(1);
24
25
    }
26
  }
27
28
  return 0;
29
}

uart.h
1
/* uart.h */
2
3
#ifndef UART_H
4
#define UART_H
5
6
#include <avr/io.h>
7
8
extern void uart_init (void);
9
10
static inline int uart_putc (const uint8_t c) {
11
12
  /* wait until UDR is ready for new value */
13
  while (!(UCSRA & (1 << UDRE)));
14
15
  /* write UDR starts transmission */
16
  UDR = c;
17
18
  return 1;
19
}
20
21
static inline uint8_t uart_getc_wait (void) {
22
23
  /* wait until something was received */
24
  while (!(UCSRA & (1 << RXC)));
25
26
  /* return received char */
27
  return UDR;
28
}
29
30
static inline int uart_getc_nowait (void) {
31
32
  /* return received char or -1 */
33
  return (UCSRA & (1 << RXC)) ? (int) UDR : -1;
34
}
35
36
#endif /* UART_H  */

uart.c
1
/* uart.c */
2
3
#include "uart.h"
4
5
#define BAUDRATE 19200UL
6
#define UBRR_BAUD   ((F_CPU/(16UL*BAUDRATE))-1)
7
8
void uart_init (void) {
9
10
  UBRRH = (uint8_t) (UBRR_BAUD>>8);
11
  UBRRL = (uint8_t) (UBRR_BAUD & 0x0ff);
12
13
  /* turn on UART receiver and transmitter */
14
  /* data mode 8N1, asynchronous */
15
  UCSRB = (1 << RXEN) | (1 << TXEN);
16
  UCSRC = (1 << URSEL) | (1 << UCSZ1) | (1 << UCSZ0);
17
18
  /* flush receiver register (remove invalid values) */
19
  do
20
  {
21
    UDR;
22
  }
23
  while (UCSRA & (1 << RXC));
24
}

Zum Einsatz kommt bei mir ein ATmega8L auf einem STK500 (wobei ich einen 
AVR ISP MK II als Programmiergerät einsetze da ich nur einen 
USB-zu-Seriell Adapter habe). Der ATmega8 erhält seinen Takt von einem 
externen 8MHz Quarz (entsprechende Fuses: Low: 0xFD, High: D9). Als 
USB-zu-Seriell Adapter verwende ich einen Keyspan USA-19HS.
Wie man im Programm sehen kann werden alle ASCII-Zeichen hintereinander 
über den UART übertragen. Der UART Modus ist 19200 Baud 8N1.
Leider empfange ich am Computer nur Kauderwelsch, siehe Foto:
https://dl.dropboxusercontent.com/u/6454333/mikrocontoller.net/uart_rcv01.png

Ich habe auch noch eine Messung mit meinem Logic Sniffer durchgeführt:
https://dl.dropboxusercontent.com/u/6454333/mikrocontoller.net/uart_measure01.png

In der Messung kann man sehen, dass die 117 (u), 118 (v), 119 (w) usw. 
übertragen werden.
Ich bin mit meinem Latein am Ende, was läuft hier falsch?

Vielen Dank und viele Grüße

konze

von Peter II (Gast)


Lesenswert?

wenn du in der main

#ifndef F_CPU
#define F_CPU 8000000UL
#endif

schreibst, woher soll dann die uart.c die F_CPU kennen?

von Konstantin L. (konze)


Lesenswert?

Wenn ich '#define F_CPU 8000000UL'  in die uart.c verlege ändert sich 
nichts. Im Makefile ist die Taktrate auch nochmal angegeben.

von Sepp (Gast)


Lesenswert?

Hallo!

Wahrscheinlich ein Baudraten Fehler.
Wenn Du dir die Länge der Bits im Logic Sniffer ansiehst, sind diese 
dann 52µs? Ich kann das nur schlecht erkennen auf deinem Plot.
Wenn ja, dann ist das Problem am PC!

von Karl H. (kbuchegg)


Lesenswert?

Konstantin Lübeck schrieb:
> ändert sich
> nichts. Im Makefile ist die Taktrate auch nochmal angegeben.


Dann schmeiss sie überall anders raus.
Wenn du dieselbe Angabe mehrfach im System hast, dann bringt dir das im 
besten Fall nichts. Im schlechtesten Fall stimmen die einzelnen Angaben 
nicht überein und du suchst dir einen Wolf.

von Sepp (Gast)


Lesenswert?

Natürlich nicht vergessen:

Wie genau ist Dein Takt? Wenns ein Quarz ist, ist die Genauigkeit es 
vermutlich OK.

von Sepp (Gast)


Lesenswert?

Ich sehe HIER jetzt kein Problem mit:

>#ifndef F_CPU
>#define F_CPU 8000000UL
>#endif

Solange F_CPU im makefile angegeben ist, passiert hier ja nichts. 
Außerdem hat er ja eh einen 8Mhz Quarz.

Prinzipiell kann man sicher aber überlegen, ob man es so machen will.

von Karl H. (kbuchegg)


Lesenswert?

> Wie man im Programm sehen kann werden alle ASCII-Zeichen hintereinander

Um nachprüfbare Ergebnisse zu erhalten, sende erst mal nur immer 
dasselbe Zeichen. Dann kann man auch mal mit dem Oskar messen bzw. 
gewinnt ev. aus dem was am PC ankommt eine gewisse Idee, was falsch sein 
könnte.
1
int main(void) {
2
3
  uart_init();
4
5
  while(1) {
6
    uart_putc( 'x' );
7
    _delay_ms(10);
8
  }
9
10
  return 0;
11
}

von Karl H. (kbuchegg)


Lesenswert?

Sepp schrieb:
> Ich sehe HIER jetzt kein Problem mit:

Wir sehen massenhaft Probleme mit dieser Form hier im Forum.

>>#ifndef F_CPU
>>#define F_CPU 8000000UL
>>#endif
>
> Solange F_CPU im makefile angegeben ist, passiert hier ja nichts.

WENN F_CPU im makefile angegeben ist.
Und wenn nicht?
Dann wird hier stillschweigend eine Taktfrequenz angenommen.

Im nächsten C-File steht
1
#ifndef F_CPU
2
#define F_CPU 4000000UL
3
#endif

Solange er im makefile diese ganzen #ifndef durch eine eigene Angabe mit 
zb 16Mhz ausser Kraft setzt, funktioniert alles wie gewollt. Aber wehe 
er vergisst auf die Angabe im makefile. Dann gibt es keine Fehlermeldung 
und diverse Programmteile (zb Delays, UART, LCD, ...) rechnen im 
schlimmsten Fall mit ihren jeweiligen Privatwerten für F_CPU, die nicht 
übereinstimmen müssen.

Es ist besser, wenn eine im makefile fehlende F_CPU Angabe einen Fehler 
verursacht, als wie wenn sich der Compiler anhand diverser 'Altlasten' 
aus kopiertem Code irgendwelche F_CPU Angaben aus den Fingern saugt.
Dafür zu sorgen, dass im Falle eines Programmiererversäumnisses es zu 
einer Fehlermeldung kommt, ist durchaus eine Form des defensiven 
Programmierens.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:

> gewinnt ev. aus dem was am PC ankommt eine gewisse Idee, was falsch sein
> könnte.

Es hat mit Sicherheit was mit dem Timing (sprich tatsächliche 
Taktfrequenz zu F_CPU Angaben, bzw. Einstellung auf dem PC) zu tun.
Der Serial/USB Wandler da drinnen ist dann momentan auch nur eine 
potentielle Fehlerquelle mehr.

von Karl H. (kbuchegg)


Lesenswert?

arbeite mal die
AVR Checkliste
durch. Punkt 3.2

von Konstantin L. (konze)


Lesenswert?

Vielen Dank für eure Unterstützung, ich denke ich habe den Fehler 
gefunden. Aber ich weiß noch nicht wie ich diesen Beheben kann.

***Derzeitiges Setup:***
Ich habe ein paar Veränderungen an meinem Programm gemacht, sodass ich 
die Übertragung von Hand auslösen kann und dass immer das gleiche 
Zeichen übertragen wird. Zudem habe ich aus allen Dateien #define F_CPU 
entfernt, da diese schon im Makefile gesetzt wird. Mein Programm sieht 
nun wie folgt aus:

main.c
1
/* main.c */
2
3
#include <avr/io.h>
4
#include "uart.h"
5
6
int main(void) {
7
8
  uart_init();
9
10
  /* PB0 as input with pullup */
11
  DDRB = ~0x01;
12
  PORTB = 0x01;
13
14
  /* PC0-5 as output, all off*/
15
  DDRC = 0xff;
16
  PORTC = ~0x00;
17
18
  while(1) {
19
20
    /* PB0 high: transmit once and turn on LED*/
21
    if((PINB & 0x01) == 0 ) {
22
23
      PORTC = ~(1<<PC0);
24
      uart_putc('a');
25
26
      while((PINB & 0x01) == 0) {}
27
28
    } else {
29
      PORTC = ~(1<<PC1);
30
    }
31
  }
32
33
  return 0;
34
}

uart.h
1
/* uart.h */
2
3
#ifndef UART_H
4
#define UART_H
5
6
#include <avr/io.h>
7
8
extern void uart_init (void);
9
10
static inline int uart_putc (const uint8_t c) {
11
12
  /* wait until UDR is ready for new value */
13
  while (!(UCSRA & (1 << UDRE)));
14
15
  /* write UDR starts transmission */
16
  UDR = c;
17
18
  return 1;
19
}
20
21
static inline uint8_t uart_getc_wait (void) {
22
23
  /* wait until something was received */
24
  while (!(UCSRA & (1 << RXC)));
25
26
    /* return received char */
27
    return UDR;
28
}
29
30
static inline int uart_getc_nowait (void) {
31
32
  /* return received char or -1 */
33
  return (UCSRA & (1 << RXC)) ? (int) UDR : -1;
34
}
35
36
#endif /* UART_H  */

uart.c
1
/* uart.c */
2
3
#include "uart.h"
4
5
#define BAUD        9600UL
6
#define UBRR_BAUD   ((F_CPU/(16UL*BAUD))-1)
7
8
void uart_init (void) {
9
10
  UBRRH = (uint8_t) (UBRR_BAUD>>8);
11
  UBRRL = (uint8_t) (UBRR_BAUD & 0x0ff);
12
13
14
  /* turn on UART receiver and transmitter */
15
  /* data mode 8N1, asynchronous */
16
  UCSRB = (1 << RXEN) | (1 << TXEN);
17
  UCSRC = (1 << URSEL) | (1 << UCSZ1) | (1 << UCSZ0);
18
19
  /* flush receiver register (remove invalid values) */
20
    do
21
    {
22
        UDR;
23
    }
24
    while (UCSRA & (1 << RXC));
25
}

An der Hardware habe ich fast nichts verändert, ich habe das 8MHz Quarz 
gegen ein 16MHz Quarz ersetzt. Im Makefile habe ich somit F_CPU auf 
16000000UL gesetzt.

Die Ausgabe des Compilers sieht wie folgt aus:
1
make all 
2
Building file: ../main.c
3
Invoking: AVR Compiler
4
avr-gcc -Wall -Os -fpack-struct -fshort-enums -std=gnu99 -funsigned-char -funsigned-bitfields -mmcu=atmega8 -DF_CPU=16000000UL -MMD -MP -MF"main.d" -MT"main.d" -c -o "main.o" "../main.c"
5
Finished building: ../main.c
6
 
7
Building file: ../uart.c
8
Invoking: AVR Compiler
9
avr-gcc -Wall -Os -fpack-struct -fshort-enums -std=gnu99 -funsigned-char -funsigned-bitfields -mmcu=atmega8 -DF_CPU=16000000UL -MMD -MP -MF"uart.d" -MT"uart.d" -c -o "uart.o" "../uart.c"
10
Finished building: ../uart.c
11
 
12
Building target: uart_simple.elf
13
Invoking: AVR C Linker
14
avr-gcc -Wl,-Map,uart_simple.map -mmcu=atmega8 -o "uart_simple.elf"  ./main.o ./uart.o   
15
Finished building target: uart_simple.elf
16
 
17
Invoking: AVR Create Extended Listing
18
avr-objdump -h -S uart_simple.elf  >"uart_simple.lss"
19
Finished building: uart_simple.lss
20
 
21
Create Flash image (ihex format)
22
avr-objcopy -R .eeprom -O ihex uart_simple.elf  "uart_simple.hex"
23
Finished building: uart_simple.hex
24
 
25
Create eeprom image (ihex format)
26
avr-objcopy -j .eeprom --no-change-warnings --change-section-lma .eeprom=0 -O ihex uart_simple.elf  "uart_simple.eep"
27
Finished building: uart_simple.eep
28
 
29
Invoking: Print Size
30
avr-size --format=avr --mcu=atmega8 uart_simple.elf
31
AVR Memory Usage
32
----------------
33
Device: atmega8
34
35
Program:     164 bytes (2.0% Full)
36
(.text + .data + .bootloader)
37
38
Data:          0 bytes (0.0% Full)
39
(.data + .bss + .noinit)
40
41
42
Finished building: sizedummy

Da ich die Taktquelle geändert habe, musste ich auch die Fuses ändern zu 
High: 0xd9 und Low: 0xff.

Ausgabe von AVRDude
1
Launching /usr/local/CrossPack-AVR-20130212/bin/avrdude -pm8 -cavrisp2 -Pusb -u -Uflash:w:uart_simple.hex:a -Ulfuse:w:0xff:m -Uhfuse:w:0xd9:m 
2
Output:
3
4
avrdude: AVR device initialized and ready to accept instructions
5
6
Reading | ################################################## | 100% 0.01s
7
8
avrdude: Device signature = 0x1e9307
9
avrdude: NOTE: FLASH memory has been specified, an erase cycle will be performed
10
         To disable this feature, specify the -D option.
11
avrdude: erasing chip
12
avrdude: reading input file "uart_simple.hex"
13
avrdude: input file uart_simple.hex auto detected as Intel Hex
14
avrdude: writing flash (164 bytes):
15
16
Writing | ################################################## | 100% 0.08s
17
18
avrdude: 164 bytes of flash written
19
avrdude: verifying flash memory against uart_simple.hex:
20
avrdude: load data flash data from input file uart_simple.hex:
21
avrdude: input file uart_simple.hex auto detected as Intel Hex
22
avrdude: input file uart_simple.hex contains 164 bytes
23
avrdude: reading on-chip flash data:
24
25
Reading | ################################################## | 100% 0.05s
26
27
avrdude: verifying ...
28
avrdude: 164 bytes of flash verified
29
avrdude: reading input file "0xff"
30
avrdude: writing lfuse (1 bytes):
31
32
Writing | ################################################## | 100% 0.00s
33
34
avrdude: 1 bytes of lfuse written
35
avrdude: verifying lfuse memory against 0xff:
36
avrdude: load data lfuse data from input file 0xff:
37
avrdude: input file 0xff contains 1 bytes
38
avrdude: reading on-chip lfuse data:
39
40
Reading | ################################################## | 100% 0.00s
41
42
avrdude: verifying ...
43
avrdude: 1 bytes of lfuse verified
44
avrdude: reading input file "0xd9"
45
avrdude: writing hfuse (1 bytes):
46
47
Writing | ################################################## | 100% 0.00s
48
49
avrdude: 1 bytes of hfuse written
50
avrdude: verifying hfuse memory against 0xd9:
51
avrdude: load data hfuse data from input file 0xd9:
52
avrdude: input file 0xd9 contains 1 bytes
53
avrdude: reading on-chip hfuse data:
54
55
Reading | ################################################## | 100% 0.00s
56
57
avrdude: verifying ...
58
avrdude: 1 bytes of hfuse verified
59
60
avrdude done.  Thank you.
61
62
avrdude finished

***FEHLERQUELLE:***
Nun habe ich nochmal mit meinem Logic Sniffer eine Messung durchgeführt 
und bin bei 9600 Baud auf das folgende Ergebnis gekommen:
https://dl.dropboxusercontent.com/u/6454333/mikrocontoller.net/uart_measure_9600_baud.png

Laut Wikipedia beträgt die Bitzeit bei einer Baudrate von 9600: 104us 
(Quelle: http://de.wikipedia.org/wiki/UART). Bei meiner Messung bin ich 
aber auf eine Bitzeit von 454us gekommen, also mehr als 4 mal so lang.


Checklist 3.2:
+ Int. Oszillator wird nicht verwendet sondern 16MHz Quarz.
+ Habe nur 8MHz und 16MHz zur Hand.
+ Taktrate wird genau angegeben über F_CPU während des Kompilierens.
+ entfällt, s.o.
+ Fuses sind folgendermaßen eingestellt: High: 0xd9, Low: 0xff (ext. 
crystal / resonator high freq.; start-up time 16K CK + 64ms) siehe: 
http://www.engbedded.com/fusecalc/
+ CLKDIV gibt es nicht beim ATmega8.
+ entfällt, s.o.
+ Wenn ich das Quarz während der Laufzeit entferne, geht nichts mehr => 
Quarz wird somit als Taktquelle verwendet.
+ Hier liegt der Fehler, aber ich weiß nicht wo?
+ URSEL bit ist gesetzt.
+ Habe ich gelesen und verstanden

Sonstige Fehlerquellen:
+ Datenübertragung "funktioniert" (Wenn ich den Taster drücke wird etwas 
gesendet, ansonsten ist es still)
+ Ich gehe davon aus dass das STK500 das regelt.
+ PD0 auf RX und PD1 auf TX.
+ Ja, alle Programme wurden auf die Baudrate 9600 und 8N1 gestellt.
+ Ich kann mit dem USB-zu-Seriell Adapter ohne Probleme eine AVR auf dem 
STK500 programmieren => USB-zu-Seriell funktioniert.
+ Nein.
+ Welche Pins sind gemeint? Ein D-Sub 9 habe nur 9 Pins + Masse über das 
Gehäuse.

Ich hoffe ich habe keine Details übersehen.

Viele Grüße

konze

von Konstantin L. (konze)


Lesenswert?

Nachtrag:
Ich habe soeben das folgende Programm auf den ATmega8 geladen:
1
/* main.c */
2
3
#include <avr/io.h>
4
#include <util/delay.h>
5
6
int main(void) {
7
8
  /* set PB0 as input */
9
  DDRB = ~0x01;
10
  PORTB = 0x01;
11
12
  /* set PD0-1 as output */
13
  DDRD = 0xff;
14
  PORTD = ~0x00;
15
16
  /* set PC0-1 as output*/
17
  DDRC = 0x03;
18
  PORTC = ~0x02;
19
20
  while(1) {
21
    if((PINB & 0x01) == 0 ) {
22
      break;
23
    }
24
  }
25
26
  PORTC = ~0x01;
27
28
  while(1) {
29
    PORTD = 0xff;
30
    _delay_ms(1000);
31
    PORTD = 0x00;
32
    _delay_ms(1000);
33
  }
34
35
  return 0;
36
}

Fuses und Hardware wie eben.

Wenn ich mit meinem Logic Sniffer die Zeit messe zwischen positiver und 
negativer Flanke an PD0 dann beträgt die Dauer nicht 1s sondern 4,5. 
Dies ist fast genau der selbe Faktor wie er auch bei den Zeiten am UART 
auftritt. Ich hoffe dies dient zu Lösung des Problems.

Viele Grüße

konze

von Stefan E. (sternst)


Lesenswert?

Konstantin Lübeck schrieb:
> Laut Wikipedia beträgt die Bitzeit bei einer Baudrate von 9600: 104us
> (Quelle: http://de.wikipedia.org/wiki/UART). Bei meiner Messung bin ich
> aber auf eine Bitzeit von 454us gekommen, also mehr als 4 mal so lang.

Das entspricht ziemlich genau einer anderen gebräuchlichen 
Quarzfrequenz, nämlich 3,6864 MHz. Also Lupe raus holen, und nochmal 
schauen, was tatsächlich auf dem Quarz steht.

Nachtrag: Ich bin nicht wirklich vertraut mit dem STK500, aber ist 
3,6864 MHz nicht auch die Standardeinstellung des STK500-Taktgenerators? 
Also auch noch mal checken, ob das STK500 richtig für die Verwendung 
eines Quarzes konfiguriert ist. Ja, ich weiß, dein Quarz-Zieh-Test 
scheint das zu belegen, die Symptome sprechen aber dagegen.

von Konstantin L. (konze)


Lesenswert?

Ich habe den Fehler gefunden:
Auf dem STK500 gibt es einen Jumper OSCSEL dieser hat zwei Positionen 0 
und 1.
Wenn man den Jumper auf 1 steckt, dann wird eine vom STK500 generierte 
Frequenz angelegt nämlich: 3,6864 MHz.
Wenn man den Jumper auf 0 steckt, dann wird das Quarz welches man in den 
Sockel CRYSTAL gesteckt als Taktquelle verwendet.

Ich hatte die ganze Zeit den OSCSEL Jumper in der falschen Stellung. 
Jetzt Funktioniert alles wunderbar.

Viele Dank an alle!

konze

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.