Atmega8 empfängt bestimmte Bytes falsch über UART

OP #4180676
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
Angehängte Dateien:
Gast #4180701
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).
Gast #4180709
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.
Gast #4180713
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. :-)
Gast #4180719
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.
Gast #4180722
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren