Forum: Mikrocontroller und Digitale Elektronik Atmega8 empfängt bestimmte Bytes falsch über UART


von Konstantin L. (konze)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

nach einer längeren Pause (ein ganzes Informatikstudium) habe ich mal 
wieder mein STK500 ausgepackt und ein wenig herumprobiert. Das C 
Programm welches ich für den Atmega8 programmiert habe, empfängt über 
den UART ein Bytes welches ich vom meinem Computer sende, anschließend 
antwortet mir die Interrupt Service Routine ISR mit der Binärdarstellung 
des übertragenen Bytes (ich habe diese Programm geschrieben da ich zuvor 
Probleme hatte mit der Kommunikation).
Wenn ich dem Controller die Bytes 0x00, 0x01, 0x02, ..., 0x1f sende 
erhalte ich als Antwort die korrekte Binärdarstellung des Bytes. Sobald 
ich aber Bytes sende die größer sind als 0x1f bekomme ich eine 
Fehlerhafte Binärdarstellung als Antwort.

Hier ist der Programmcode:
1
// clock frequency 1Mhz 
2
#define F_CPU 1000000UL
3
4
// baud rate
5
#define BAUD 9600
6
#define BAUDRATE ((F_CPU)/(BAUD*16UL)-1)
7
8
#include <avr/io.h>
9
#include <avr/interrupt.h>
10
#include <util/delay.h>
11
#include <stdlib.h>
12
13
14
void uart_init (void);
15
void uart_putc (unsigned char data);
16
void uart_puts (unsigned char * str, uint8_t size);
17
18
// interrupt service routine for UART receiver interupt
19
ISR(USART_RXC_vect) {
20
21
    // get received char
22
    unsigned char received_char = UDR;
23
24
    uart_puts("received=", 9);
25
26
    if((received_char & (1<<7)) == (1<<7)) {
27
        uart_putc('1');
28
    } else {
29
        uart_putc('0');
30
    }
31
32
    if((received_char & (1<<6)) == (1<<6)) {
33
        uart_putc('1');
34
    } else {
35
        uart_putc('0');
36
    }
37
38
    if((received_char & (1<<5)) == (1<<5)) {
39
        uart_putc('1');
40
    } else {
41
        uart_putc('0');
42
    }
43
44
    if((received_char & (1<<4)) == (1<<4)) {
45
        uart_putc('1');
46
    } else {
47
        uart_putc('0');
48
    }
49
50
    if((received_char & (1<<3)) == (1<<3)) {
51
        uart_putc('1');
52
    } else {
53
        uart_putc('0');
54
    }
55
56
    if((received_char & (1<<2)) == (1<<2)) {
57
        uart_putc('1');
58
    } else {
59
        uart_putc('0');
60
    }
61
62
    if((received_char & (1<<1)) == (1<<1)) {
63
        uart_putc('1');
64
    } else {
65
        uart_putc('0');
66
    }
67
68
    if((received_char & (1<<0)) == (1<<0)) {
69
        uart_putc('1');
70
    } else {
71
        uart_putc('0');
72
    }
73
74
    uart_puts("\n\r",2);
75
}
76
77
78
// function to initialize UART
79
// dataformat 8N1
80
void uart_init (void) {
81
82
    // shift the register right by 8 bits
83
    UBRRH = (BAUDRATE>>8);
84
85
    // set baud rate
86
    UBRRL = BAUDRATE;            
87
88
    // enable receiver, transmitter and receiver interrupt
89
    UCSRB|= (1<<TXEN)|(1<<RXEN)|(1<<RXCIE);
90
91
    // 8bit data format
92
    UCSRC|= (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);   
93
94
}
95
96
// sends a single char over the UART
97
void uart_putc (unsigned char data) {
98
99
    // wait while register is free
100
    while (!( UCSRA & (1<<UDRE)));
101
    // load data in the register
102
    UDR = data;
103
}
104
105
// sends a string over the UART
106
void uart_puts (unsigned char * str, uint8_t size) {
107
108
    uint8_t i; 
109
110
    for(i = 0; i < size; i++) {
111
        uart_putc(str[i]);
112
    }
113
}
114
115
// receives a single char over the UART
116
unsigned char uart_getc (void) {
117
118
    // wait while data is being received
119
    while(!(UCSRA) & (1<<RXC));
120
    // return 8-bit data
121
    return UDR;
122
}
123
124
125
uint8_t main (void) {
126
127
    // enable interrupts
128
    sei();
129
130
    // enable uart
131
    uart_init(); 
132
133
    uart_puts("ready\n\r", 7);
134
135
    while(1) {
136
    } 
137
138
    return 0; 
139
}

Angehängt habe ich eine Screenshot von der Kommunikation mit dem 
Atmega8. Hier werden hintereinander die folgenden Bytes übertragen:
0x01, 0x02, 0x03, 0x1d, 0x1e, 0x1f und 0x20. Für 0x20 sollte ich aber 
00100000 empfangen anstatt 01100000.

Ich hoffe ihr könnt mir helfen.

Vielen Dank und viele Grüße

Konze

von m.n. (Gast)


Lesenswert?

Ohne jetzt genau nachzurechnen: 9600 Bd @ 1 MHz?
Die Baudrate scheint mir ein wenig zu ungenau zu werden.

von Klaus (Gast)


Lesenswert?

Informatikstudium? Hüstel. In der Baumschule? (Entschuldige, aber 
irgendwie konnte ich mir das nicht verkneifen).


Ich zitiere da mal Falk: In einer Interruptroutine macht man nichts als 
ein Flag zu setzen und das dann in der Hauptschleife zu verarbeiten.

Verschiebe mal das ganze Gewusel in eine eigene Funktion. Und die ganzen 
einzelnen if-Befehle kannst Du Dir sparen. Ist ja furchtbar - 
insbesondere für jemanden, der das studiert hat ist das eher "AUA".
1
   unsigned char i;
2
   for (i = 0x80; i != 0; i >>= 1)
3
     uart_putc(0x30 + (received_char & i) ? 1 : 0);

(Ist nicht kompiliert worden).

von chris_ (Gast)


Lesenswert?

>Informatikstudium? Hüstel. In der Baumschule? (Entschuldige, aber
>irgendwie konnte ich mir das nicht verkneifen).

Ist mir auch etwas sauer aufgestoßen. Allerdings ist der Code relativ 
klar und man sollte die Strukturen einfach halten und keine 
Verschachtelungen verwenden.
Dein Code dürfte nicht direkt den MISRA-Rules entsprechen. Außerdem 
sollte man der "Klarheit" wegen uint8_t aus der stdlib.h anstatt 
"unsigned char" verwenden.

von Klaus (Gast)


Lesenswert?

Ach und achte darauf zwischen den gesendeten Zeichen lange Pausen zu 
machen. Du sendest rund die 16-fache Datenmenge die Du empfängst. Oder 
Du nimmst einen Puffer, der dann aber die notwendige Pause lediglich 
nach hinten verschiebt.

So etwas solltest Du aber eigentlich wissen. :-)

von Konstantin L. (konze)


Lesenswert?

@m.n.: Danke für deine Antwort! mit 8MHz funktioniert es nun

Zum Code: der ist nicht für den Produktiven Einsatz gedacht sondern 
dafür dem Fehler auf die schliche zu kommen.

von Stefan F. (Gast)


Lesenswert?

Wenn kleine Werte nocht korrekt übertragen werden, und Fehler erst 
auftreten, wenn die oberen Bits 1 sind, dann liegt es mit sehr hoher 
warscheinlichkeit an einem ungenauen Takt.

Falls du den internen R/C Oszillator verwendest, kann es daran liegen. 
Der R/C Oszillator aller ATmegas eigent sich nicht für zuverlässige UART 
Kommunikation.

Laut Datenblatt des ATmega 8 Seite 159 hast du bei der Kombination 1Mhz 
9600Baud schon 7% Abweichung vom Soll - unter der Annahne, dass die 1Mhz 
100% stimmen. Wenn du das mit 10 Multiplizierst (1 Startbit, 8 
Datenbits, 1 Stopbit) hast du 70%. Für eine funktionierende Übertragung 
musst du unter 50% bleiben.

Also selbst mit einek 1Mhz Quarz würde das nicht zuverlässig 
funktionieren.

Wechsle auf 4800 Baud. Dann hast du bei einer idealen 1Mhz Quelle nur 
noch 0,2% Abweichung. Da stehen die Chancen auch viel besser, dass es 
mit R/C Oszillator klappt.

Wenn der R/C Oszillator z.B. 3% vom Soll abweicht, und du dazu die 0,2% 
addierst und das dann mit 10 Bits multiplizierst, hast du insgesamt 32% 
Abweichung. Das ist deutlich unter 50% - also gut.

von Stefan F. (Gast)


Lesenswert?

Nachtrag: Wenn du das U2X Bit auf 1 setzt und UBBR verdoppelst (auf 12), 
dann werden die 9600Mhz auch mit nur 0,2% Abweichung erzeugt. Auch da 
sgeht aus der Tabelle auf Seite 159 hervor.

Wenn du einen anderen ATmega verwendest, schau halt in dessen 
Datenblatt. Ich glaube, das Prinzip ist jedoch bei allen Atmegas gleich.

von Konstantin L. (konze)


Lesenswert?

@Stefan: Vielen Dank für deine Antwort! Sehr aufschlussreich.

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.