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 | extern char Uart_RxChar(void);
|
4 | static void foo(uint16_t value);
|
5 |
|
6 |
|
7 | int main(void)
|
8 | {
|
9 | while(1)
|
10 | {
|
11 | uint8_t lowByte;
|
12 | uint8_t highByte;
|
13 | uint16_t value;
|
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 | static void foo(uint16_t value)
|
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 | char Uart_RxChar(void)
|
4 | {
|
5 | return UDR0;
|
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
1 | value = lowByte | (uint16_t)highByte<<8;
|
korrekt ist (siehe z.B. auch
Beitrag "Re: 4 uint8_t in uint32_t konvertieren").
Könnt ihr mir bitte auf die Sprünge helfen?