Forum: Mikrocontroller und Digitale Elektronik Atmega88P USART Kommunikation funktioniert nicht


von A. B. (developer_x)


Lesenswert?

Liebes Forum,
ich hänge hier fest und weiß einfach nicht warum:
Ich nutze einen Atmega88P mit einem MAX232 um Daten per UART an meine 
RS232 Schnittstelle an den PC zu senden. Ich habe ein kleines 
Fertigbauteil aus China, auf dem mein MAX232 sitzt, und mit dem ich 
früher schon prima arbeiten konnte, in Assembler, nun will ich in C 
arbeiten es will aber einfach nicht klappen. Auf dem Fertigbauteil (so 
ne Platine) gibt es auch LEDs die blinken, wenn Daten übertragen werden 
(RXD und TXD).

Zum Ablesen der eingehenden Daten benutze ich HTerm.

Das ist mein Code:
1
#include <avr/io.h>
2
#include <stdlib.h>
3
4
#define F_CPU 8000000
5
                         
6
#define BAUD 9600      
7
 
8
// Berechnungen
9
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)   // clever runden
10
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     // Reale Baudrate
11
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD) // Fehler in Promille, 1000 = kein Fehler.
12
 
13
#if ((BAUD_ERROR<990) || (BAUD_ERROR>1010))
14
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! 
15
#endif
16
17
void uart_init(void)
18
{
19
  UBRR0H = (unsigned char)(UBRR_VAL >> 8);
20
  UBRR0L = (unsigned char)(UBRR_VAL & 0xFF);
21
 
22
  UCSR0B |= (1<<TXEN0);  // UART TX einschalten
23
  UCSR0C = (1<<USBS0)|(3<<UCSZ00);
24
}
25
26
int uart_putc(unsigned char c)
27
{
28
    while (!(UCSR0A & (1<<UDRE0)))  /* warten bis Senden moeglich */
29
    {
30
    }                             
31
 
32
    UDR0 = c;                      /* sende Zeichen */
33
    return 0;
34
}
35
 
36
37
38
int main (void) 
39
{
40
   char c;
41
 
42
   uart_init();
43
 
44
   while (1) 
45
   {
46
       for (uint8_t i=0; i<=255; ++i) 
47
       {
48
          c = i;
49
          uart_putc( c );
50
          // verkuerzt: uart_putc( i + '0' );
51
       }
52
   }
53
 
54
   return 0; // never reached 
55
 
56
}

In meinem Terminalprogramm kommt nix an, das RXD Lämpchen auf meiner 
Platine blinkt fleißig, also dachte ich, wie wärs wenn du das RXD Kabel 
des Bauteils an den TXD des Atmegas dran machst und das TXD Kabel des 
Bauteils an den RXD des Atmegas: Dann kommt was in meinem Terminal an, 
was aber immer abwechselnd "000" bzw. "128" ist. Ich hab geguckt, aber 
in meinem HTerm habe ich alle Einstellungen so gelassen wie Sie sind 
außer die Baudrate auf 9600 zu setzen.

Kann mir jemand sagen, was ich falsch mache?

Danke,
m.f.G.: K.R.

von stefan us (Gast)


Lesenswert?

Klemme den AVR erstmal ab und verbinde am China Modul RxD mit TxD. Dann 
schau mit Hterm, ob das, was du sendest auch wieder zurück kommt. Wenn 
nicht, könnte dieses teil defekt sein.

Ansonsten stimmt vielleicht die Übertragungsrate nicht. Schwingt der 
Quarz wirklich 8Mhz? hast Du vieleicht versehentlich die Fuse CLKDIV8 
gesetzt.

Dan kann man mit einer blinkenden LED leicht testen:
1
while (1) {
2
  led an machen;
3
  _sleep_ms(1000);
4
  led aus machen;
5
  _sleep_ms(1000);
6
}

Die LED muss immer abwechselnd eine Sekunde an und aus sein. Wenn nicht, 
stimmt etwas mit dem Taktgeber nicht.

von A. B. (developer_x)


Lesenswert?

1. Wenn ich RX mit TX vom Chinamodul verbinde, kommt das am PC an was 
ich absende, daran liegts nicht.

2. Ja, das war in der Tat gesetzt, ich habs weggemacht

3. Ich habe keinen externen oszillator sondern der internen, und mein 
avr studio sowie ein test mit den leds bestätigt, 8 Mhz
Seitdem ich den Fuse verändert habe kommt in Hterm mal "000" mal "192" 
an, aber wie gesagt nichts sinnvolles.

Danke für deine hilfe, würde dir noch was einfallen woran es liegen 
könnte?

von Bernhard F. (bernhard_fr)


Lesenswert?

K. R. schrieb:

>
1
>   UCSR0C = (1<<USBS0)|(3<<UCSZ00);
2
>

2 Stopbits ? ist das gewollt?

> Ich hab geguckt, aber
> in meinem HTerm habe ich alle Einstellungen so gelassen wie Sie sind
> außer die Baudrate auf 9600 zu setzen.

HTerm ist per Default auf einem Stopbit.

Ich weiß zwar nicht ob das schon weiterhilft, aber das fiel mir nur 
gerade spontan auf.

von A. B. (developer_x)


Lesenswert?

Du hast recht, ich habe jetzt hterm auf 2 umgestellt, hat nicht 
geklappt,

Dann habe ich hterm wieder auf 1 stopbit umgestellt und es mit folgender 
ersatzzeile versucht:
1
UCSR0C = (1<<UMSEL01)|(1<<UCSZ01)|(1<<UCSZ00);
Klappt aber auch nicht :?

Noch jemand eine Idee?

Danke für eure Hilfe,
m.f.G.: K.R.

von Stefan E. (sternst)


Lesenswert?

Bernhard F. schrieb:
> Ich weiß zwar nicht ob das schon weiterhilft

Mehr Stopbits zu senden, als der Empfänger erwartet, ist nie ein 
Problem.


@ K. R.:
Mach mal eine Pause (1ms) nach dem uart_init().

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

K. R. schrieb:
> Dann habe ich hterm wieder auf 1 stopbit umgestellt und es mit folgender
> ersatzzeile versucht:
> UCSR0C = (1<<UMSEL01)|(1<<UCSZ01)|(1<<UCSZ00);
> Klappt aber auch nicht :?

 Und auch das sollst du nicht tun. ATMEL sagt, dass UMSEL01=1 und
 UMSEL00=0 RESERVED ist.

von Jens K. (blauzahnmeister)


Lesenswert?

Hallo.
Ist evt. die Fuse  am MC gesetzt, die Deine Clock durch 8 dividiert *
Dann ist einfach alles was Du tust 8 fach zu langsam...

von A. B. (developer_x)


Lesenswert?

Nein, die Fuse ist nicht gesetzt, war, jetzt nicht mehr.

Das ist mein aktueller Code, ich warte jetzt 2 ms nach dem 
Initialisieren,
und weiß ehrlich gesagt net was ich ganz konkret im UART Control 
Register denn sonst einstellen soll:
1
#include <avr/io.h>
2
#include <stdlib.h>
3
#include <util/delay.h>
4
5
#define F_CPU 8000000
6
                         
7
#define BAUD 9600      
8
 
9
// Berechnungen
10
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)   // clever runden
11
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     // Reale Baudrate
12
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD) // Fehler in Promille, 1000 = kein Fehler.
13
 
14
#if ((BAUD_ERROR<990) || (BAUD_ERROR>1010))
15
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! 
16
#endif
17
18
void uart_init(void)
19
{
20
  UBRR0H = (unsigned char)(UBRR_VAL >> 8);
21
  UBRR0L = (unsigned char)(UBRR_VAL & 0xFF);
22
 
23
  UCSR0B |= (1<<TXEN0);  // UART TX einschalten
24
  UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);  // Asynchron 8N1 
25
}
26
27
int uart_putc(unsigned char c)
28
{
29
    while (!(UCSR0A & (1<<UDRE0)))  /* warten bis Senden moeglich */
30
    {
31
    }                             
32
 
33
    UDR0 = c;                      /* sende Zeichen */
34
    return 0;
35
}
36
 
37
int main (void) 
38
{
39
   char c;
40
 
41
   uart_init();
42
   _delay_ms(2);
43
   while (1) 
44
   {
45
       for (uint8_t i=0; i<=255; ++i) 
46
       {
47
          c = i;
48
          uart_putc( c );
49
          // verkuerzt: uart_putc( i + '0' );
50
       }
51
   }
52
 
53
   return 0; // never reached 
54
 
55
}

Das hier im Forum genannte Tutorial empfiehlt aber UMSEL zu ändern:
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Der_UART#UART_initialisieren
Dort heißt es:
1
/* UART-Init Bsp. ATmega16 */
2
 
3
void uart_init(void)
4
{
5
  UBRRH = UBRR_VAL >> 8;
6
  UBRRL = UBRR_VAL & 0xFF;
7
 
8
  UCSRB |= (1<<TXEN);  // UART TX einschalten
9
  UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);  // Asynchron 8N1 
10
}
Nur das URSEL beim ATmega88P UMSEL00 heißt.

Ich bedanke mich erstmal für euer Engagement, hat jemand noch eine Idee?
m.f.G. K.R.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

K. R. schrieb:
> Nur das URSEL beim ATmega88P UMSEL00 heißt.

Wie kommst Du denn auf dieses schmale Brett?

von Stefan E. (sternst)


Lesenswert?

K. R. schrieb:
> Nur das URSEL beim ATmega88P UMSEL00 heißt.

Nein, das sind zwei grundverschiedene Bits, die rein gar nichts 
miteinander zu tun haben.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Hier mal einige portable gestaltete UART-Funktionen, die auf so ziemlich 
jedem ATmega funktionieren:
1
#define BAUD  9600L
2
#include <util/setbaud.h>
3
4
#ifdef UBRR0H
5
6
#define UART0_UBRRH                             UBRR0H
7
#define UART0_UBRRL                             UBRR0L
8
#define UART0_UCSRA                             UCSR0A
9
#define UART0_UCSRB                             UCSR0B
10
#define UART0_UCSRC                             UCSR0C
11
#define UART0_UDRE_BIT_VALUE                    (1<<UDRE0)
12
#define UART0_UCSZ1_BIT_VALUE                   (1<<UCSZ01)
13
#define UART0_UCSZ0_BIT_VALUE                   (1<<UCSZ00)
14
#ifdef URSEL0
15
#define UART0_URSEL_BIT_VALUE                   (1<<URSEL0)
16
#else
17
#define UART0_URSEL_BIT_VALUE                   (0)
18
#endif
19
#define UART0_TXEN_BIT_VALUE                    (1<<TXEN0)
20
#define UART0_UDR                               UDR0
21
#define UART0_U2X                               U2X0
22
        
23
#else
24
25
#define UART0_UBRRH                             UBRRH
26
#define UART0_UBRRL                             UBRRL
27
#define UART0_UCSRA                             UCSRA
28
#define UART0_UCSRB                             UCSRB
29
#define UART0_UCSRC                             UCSRC
30
#define UART0_UDRE_BIT_VALUE                    (1<<UDRE)
31
#define UART0_UCSZ1_BIT_VALUE                   (1<<UCSZ1)
32
#define UART0_UCSZ0_BIT_VALUE                   (1<<UCSZ0)
33
#ifdef URSEL
34
#define UART0_URSEL_BIT_VALUE                   (1<<URSEL)
35
#else
36
#define UART0_URSEL_BIT_VALUE                   (0)
37
#endif
38
#define UART0_TXEN_BIT_VALUE                    (1<<TXEN)
39
#define UART0_UDR                               UDR
40
#define UART0_U2X                               U2X
41
42
#endif //UBRR0H
43
44
static void
45
uart_init (void)
46
{
47
    UART0_UBRRH = UBRRH_VALUE;                                                                      // set baud rate
48
    UART0_UBRRL = UBRRL_VALUE;
49
50
#if USE_2X
51
    UART0_UCSRA |= (1<<UART0_U2X);
52
#else
53
    UART0_UCSRA &= ~(1<<UART0_U2X);
54
#endif
55
56
    UART0_UCSRC = UART0_UCSZ1_BIT_VALUE | UART0_UCSZ0_BIT_VALUE | UART0_URSEL_BIT_VALUE;
57
    UART0_UCSRB |= UART0_TXEN_BIT_VALUE;                                                            // enable UART TX
58
}
59
60
static void
61
uart_putc (unsigned char ch)
62
{
63
    while (!(UART0_UCSRA & UART0_UDRE_BIT_VALUE))
64
    {
65
        ;
66
    }
67
68
    UART0_UDR = ch;
69
}
70
71
static void
72
uart_puts (char * s)
73
{
74
    while (*s)
75
    {
76
        uart_putc (*s);
77
        s++;
78
    }
79
}
80
81
static void
82
uart_puts_P (PGM_P s)
83
{
84
    uint8_t ch;
85
86
    while ((ch = pgm_read_byte(s)) != '\0')
87
    {
88
        uart_putc (ch);
89
        s++;
90
    }
91
}

Viel Spaß,

Frank

von A. B. (developer_x)


Lesenswert?

Wie ich darauf komme?

http://www.atmel.com/images/doc2553.pdf
Seite 8 Tabelle unten.

von Bernhard F. (bernhard_fr)


Lesenswert?

O.K. habe mal deinen Code ins Studio geworfen und durchlaufen lassen.

Ergebnis: 4 Warnings mit entsprechenden Messages.

alle sagen "int overflow in Expression" bzw. "in expansion of Macro"

Dazu kommt noch: Wenn irgendwelche undefinierten Zeichen ankommen ist es 
meist die Baudrate.

Im Register steht auch eine 0x0162, was ja wohl vollkommen daneben ist.

Versuch mal bitte statt
1
  UBRR0H = (unsigned char)(UBRR_VAL >> 8);
2
  UBRR0L = (unsigned char)(UBRR_VAL & 0xFF);

einfach
1
UBRRO = 51;

und dazu eventuell noch den vorgeschlagenen Delay nach der 
Initialisierung.
Was ergibt das ?

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

K. R. schrieb:
> Wie ich darauf komme?
>
> http://www.atmel.com/images/doc2553.pdf
> Seite 8 Tabelle unten.

Da steht nichts davon, dass URSEL nun UMSEL00 heisst. Da steht nur, dass 
die alte Position für URSEL mittlerweile für andere Zwecke genutzt wird.

von A. B. (developer_x)


Lesenswert?

1. Der Code von Frank M funktioniert direkt.

2. Wenn ich UBRR0 = 51 setze klappt es auch.

Erstmal danke für die Antworten, jetzt klappts wuhu! :)

Aber: Warum? Habe ich in das Baudraten register die Baudrate falsch 
eingetragen?

von Bernhard F. (bernhard_fr)


Lesenswert?

Gerade den Beitrag von Frank M. gesehen.

Wer mag mal erkären:
- warum dort die Baudrate als 9600_l_ definiert ist?
- Was das damit zu tun hat, dass 9600 x 16 größer als 65535 ist ?
- Was "int overflow" in diesem Zusammenhang dem Anwender sagen sollte?

: Bearbeitet durch User
von Stefan E. (sternst)


Lesenswert?

Bernhard F. schrieb:
> O.K. habe mal deinen Code ins Studio geworfen und durchlaufen lassen.
>
> Ergebnis: 4 Warnings mit entsprechenden Messages.

In der Tat, da fehlt ein L an der Baudrate.
1
#define BAUD 9600L
Ohne dem läuft z.B. (BAUD*16) über.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

K. R. schrieb:
> 1. Der Code von Frank M funktioniert direkt.

Freut mich. Das Schöne daran ist, dass der nicht nur auf dem ATmega88 
funktioniert.

> Aber: Warum? Habe ich in das Baudraten register die Baudrate falsch
> eingetragen?

Dann stimmte wohl tatsächlich etwas mit dem Baudratenregister nicht. Ja, 
das L fehlte :-)

Deine Methode zur Berechnung ist auch veraltet. Aktuell sollte man 
besser die Preprocessor-Konstante BAUD setzen und das Include 
<util/setbaud.h> benutzen. Durch Unterstützung von U2X werden dann auch 
die Werte benutzt, die am besten zu Deiner F_CPU passen.

: Bearbeitet durch Moderator
von Thomas E. (thomase)


Lesenswert?

Bernhard F. schrieb:
> Wer mag mal erkären:
> - warum dort die Baudrate als 9600_l_ definiert ist?
> - Was das damit zu tun hat, dass 9600 x 16 größer als 65535 ist ?
> - Was "int overflow" in diesem Zusammenhang dem Anwender sagen sollte?

Gerechnet wird mit 16-Bit-Integer.
16-Bit-Integer geht von -32768 bis 32767. Alles, was über diesen 
Zahlenraum hinausgeht, bedingt einen Overflow.

9600 * 16 ist, ob im Kopf oder auf dem Taschenrechner, 153600.
In 16-Bit-Integer jedoch 22528.
Netterweise gibt es dann besagte Warnung. Ignoriert man die, 
funktioniert es nicht.

Macht man in der Berechung einen Faktor long, wird alles mit long 
gerechnet. Also von -2147483684 bis 2147483683.

9600L * 16 = 153600. Dann passt das.

mfg.

von A. B. (developer_x)


Lesenswert?

Gut danke an alle!

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.