Guten Morgen allerseits,
das Problem des "Verheiratens" von zwei uint8_t zu einem uint16_t taucht
hier ja regelmäßig auf, aber ich habe bei der Suche keinen Beitrag
gefunden, der dieses Verhalten beschreibt...
Es geht darum, zwei via UART empfangene Bytes (High und Low-Byte) zu
einem uint16_t zu konvertieren, allerdings funktioniert es nicht ganz
wie erwartet. Folgender (abgespeckter aber lauffähiger) Code produziert
das Verhalten:
*main.c*
1
#include<avr/io.h>
2
3
externcharUart_RxChar(void);
4
staticvoidfoo(uint16_tvalue);
5
6
7
intmain(void)
8
{
9
while(1)
10
{
11
uint8_tlowByte;
12
uint8_thighByte;
13
uint16_tvalue;
14
15
highByte=(uint8_t)Uart_RxChar();
16
lowByte=(uint8_t)Uart_RxChar();
17
value=lowByte|(uint16_t)highByte<<8;
18
19
foo(value);
20
}
21
}
22
23
24
staticvoidfoo(uint16_tvalue)
25
{
26
if(value>1000)
27
{
28
PORTA|=(1<<PA0);
29
}
30
else
31
{
32
PORTA&=~(1<<PA0);
33
}
34
}
*uart.c*
1
#include<avr/io.h>
2
3
charUart_RxChar(void)
4
{
5
returnUDR0;
6
}
Der AVR-GCC 3.3.1.27 mit Optimierung O1 erzeugt bei mir folgendes
Assembler Listing (Ausschnitt):
1
int main(void)
2
{
3
94: ef 92 push r14
4
96: ff 92 push r15
5
98: 1f 93 push r17
6
9a: cf 93 push r28
7
9c: df 93 push r29
8
{
9
uint8_t lowByte;
10
uint8_t highByte;
11
uint16_t value;
12
13
highByte = (uint8_t)Uart_RxChar();
14
9e: 0e 94 60 00 call 0xc0 ; 0xc0 <Uart_RxChar>
15
lowByte = (uint8_t)Uart_RxChar();
16
a2: 0e 94 60 00 call 0xc0 ; 0xc0 <Uart_RxChar>
17
value = lowByte | (uint16_t)highByte<<8;
18
a6: 20 e0 ldi r18, 0x00 ; 0
19
a8: e9 01 movw r28, r18
20
aa: 90 e0 ldi r25, 0x00 ; 0
21
ac: 8c 2b or r24, r28
22
ae: 9d 2b or r25, r29
23
}
24
25
26
static void foo(uint16_t value)
27
{
28
if (value > 1000)
29
b0: 33 e0 ldi r19, 0x03 ; 3
30
b2: 89 3e cpi r24, 0xE9 ; 233
31
b4: 93 07 cpc r25, r19
32
b6: 10 f0 brcs .+4 ; 0xbc <main+0x28>
33
{
34
PORTA |= (1<<PA0);
35
b8: 10 9a sbi 0x02, 0 ; 2
36
ba: f1 cf rjmp .-30 ; 0x9e <main+0xa>
37
}
38
else
39
{
40
PORTA &= ~(1<<PA0);
41
bc: 10 98 cbi 0x02, 0 ; 2
42
be: ef cf rjmp .-34 ; 0x9e <main+0xa>
Hier ergeben sich mir mehrere Fragen:
1. Warum wird der Rückgabewert des ersten Aufrufs von Uart_RxChar()
überhaupt nicht beachtet?
2. Warum verwendet der Compiler bei movw r28, r18 implizit das
undefinierte Register r19?
Die einfachste und wohl unwahrscheinlichste Antwort ist, der Compiler
hat ein "mov r19, r24" nach dem ersten Aufruf von Uart_RxChar()
vergessen, aber es ist wohl alles ganz anders und der Fehler sitzt
vielmehr vor dem Rechner! Ich bin eigentlich der Meinung, dass die
relevante Codezeile
Christoph U. schrieb:> Der AVR-GCC 3.3.1.27 mit Optimierung O1 erzeugt bei mir folgendes
3.3.1.27 klingt nicht nach einer GCC-Version.
Bei mir (GCC 4.7.2) wird folgendes erzeugt, wenn ich das für den
ATmega1281 compilieren lasse:
1
.global main
2
.type main, @function
3
main:
4
/* prologue: function */
5
/* frame size = 0 */
6
/* stack size = 0 */
7
.L__stack_usage = 0
8
.L5:
9
call Uart_RxChar
10
mov r17,r24
11
call Uart_RxChar
12
mov r29,r17
13
ldi r18,0
14
mov r28,r18
15
movw r18,r28
16
or r18,r24
17
movw r24,r18
18
cpi r24,-23
19
sbci r25,3
20
brlo .L3
21
sbi 0x2,0
22
rjmp .L5
23
.L3:
24
cbi 0x2,0
25
rjmp .L5
Immer noch ein wenig umständlich mit dem Hin- und Hergeschiebe zwischen
r18 und r28, aber sollte korrekt sein.
Dein Problem sieht mir wie eine Inkarnation von GCC PR46779 aus:
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46779
Vermutlich benutzt du eine buggige Version von Atmels AVR-Toolchain.
Vielen Dank für die schnellen Antworten.
@Peter:
-O1 war die Standard-Einstellung. Bei -Os ist es aber genauso.
@Jörg:
Ich habe die im AVR Studio 5.1 angegebene Nummer fälschlicherweise als
Version interpretiert. Die wirkliche Version ist 4.5.1 und ist wohl
demnach schon ein wenig betagter. Ich wollte sowieso schon länger mal
das AVR Studio 6 installieren. In jedem Fall ist es gut zu wissen, dass
in diesem Fall wohl doch mal der Compiler Schuld hatte und nicht ich.
Also vielen Dank für den Hinweis.
Christoph U. schrieb:> Die wirkliche Version ist 4.5.1 und ist wohl> demnach schon ein wenig betagter.
Ja, und vor allem leidet diese Version an besagtem Bug. Prinzipiell
war der Bug wohl schon früher da, aber in noch älteren Versionen
trat er praktisch nicht in Erscheinung.
Christoph U. schrieb:> #include <avr/io.h>>> char Uart_RxChar(void)> {> return UDR0;> }
Fehlt da nicht noch ein Warten auf den UART-Receive-Complete?
while (!(UCSRA & (1<<RXC)));
oder so?
Korrekt, das angegebene Programm war bloß als abgespeckte Version zum
Nachvollziehen des Verhaltens gedacht. Im richtigen Programm gibt's auch
die Abfrage und noch viel mehr. :)
Trotzdem Danke!
Jörg Wunsch schrieb:> Immer noch ein wenig umständlich mit dem Hin- und Hergeschiebe zwischen> r18 und r28, aber sollte korrekt sein.
Der widerporstige PR41076:
http://gcc.gnu.org/PR41076
Olek schrieb:> ist es falsch in so einem Fall die union zu verwenden?
Nein, aber mühsamer;-)
Auf einem 8bitter sollte das sogar effizienter ablaufen.
Er sollte einfach die Adresse +1 Rechnen.
Habs jetzt aber nicht genauer angeschaut...
mfg Andreas
....sorry für doppelPost, aber die Frage geht mir grade nicht aus dem
Kopf.
macht der Compiler, wenn du es so schreiben würdest, den selben Fehler?
value = (lowByte | (((uint16_t)highByte)<<8));
Olek schrieb:> macht der Compiler, wenn du es so schreiben würdest, den selben Fehler?>> value = (lowByte | (((uint16_t)highByte)<<8));
Der Compiler ersetzt nicht starr C-Text durch Assemblercode, sondern
walkt ihn ca 200 mal durch. Wenn dabei ein 16-Bit Register aus mehreren
8-Bit Registern aufgebaut wird, kann PR46779 auftreten.
Das ist abhängig von den Optimierungseinstellungen, vom Kontext, in dem
der Code steht, etc. Mit einer Union kann der Fehler auch auftreten.
Versuch mal
volatile uint8_t lowByte;
volatile uint8_t highByte;
uint16_t value;
So ähnlich konnte ich mal avr-gcc 4.5.3 überlisten, doch richtig zu
kompilieren.
(Oder nimm besser 4.7.2 )
Johann L. schrieb:> Olek schrieb:>> macht der Compiler, wenn du es so schreiben würdest, den selben Fehler?>>>>> value = (lowByte | (((uint16_t)highByte)<<8));>> Der Compiler ersetzt nicht starr C-Text durch Assemblercode, sondern> walkt ihn ca 200 mal durch. Wenn dabei ein 16-Bit Register aus mehreren> 8-Bit Registern aufgebaut wird, kann PR46779 auftreten.
Das hier sollte dann aber immer funktionieren:
value = lowByte + highByte * 256;