I have the following code in my microcontroler program:
int analogValue = ADCH; // ADC Data Register
//
// Simple analog comparator.
// If analogValue lower than threshold then toggle output high,
// Otherwise toggle it low.
//
if ( analogValue > 128 ) {
PORTB = 0; // Port B Data Register
} else {
PORTB = _BS( outputPin ); // Port B Data Register
}
Where:
- ADCH is the register that contains the value from the ADC
- PORTB is a dital output port that toggles an LED
Looking at the resulting assembly code, I noticed that it is doing a 16 bit compare (lines 40-44) where strictly speaking only 8 bits would have been sufficient:
40: 90 e0 ldi r25, 0x00 ; 0
42: 81 38 cpi r24, 0x81 ; 129
44: 91 05 cpc r25, r1
46: 14 f0 brlt .+4 ; 0x4c <__SREG__+0xd>
48: 18 ba out 0x18, r1 ; PORTB
4a: f5 cf rjmp .-22 ; 0x36 <__CCP__+0x2>
4c: 28 bb out 0x18, r18 ; PORTB
4e: f3 cf rjmp .-26 ; 0x36 <__CCP__+0x2>
I realize I declared analogValue as int, which indeed is 16 bit on AVR, but ...
How can I instruct the compiler to use 8 bit comparison? The Arduino IDE allows me to use byte, but avr-gcc by default doesn't.
Check this page for the complete program and its disassembled resulting code.
EDIT1:
Changing int
to char
changes the assembly code to:
14: 11 24 eor r1, r1 ; r1 = 0
3e: 18 ba out 0x18, r1 ; PORTB
Basically skipping the test entirely.
EDIT2: (Thnx: Wouter van Ooijen)
Changing int
to unsigned char
changes the assembly code to:
3c: 85 b1 in r24, 0x05 ; ADCH
3e: ...
40: 87 fd sbrc r24, 7 ; compare < 128 (well optimized)
42: 02 c0 rjmp .+4 ; 0x48 <__SREG__+0x9>
44: 18 ba out 0x18, r1 ; 24
46: f7 cf rjmp .-18 ; 0x36 <__CCP__+0x2>
48: 98 bb out 0x18, r25 ; 24
4a: f5 cf rjmp .-22 ; 0x36 <__CCP__+0x2>
2 Answers 2
I actually think a better practice that avoids this architectural ambiguity is to include <stdint.h>
then use declarative types like:
- uint8_t for unsigned 8-bit integers
- int8_t for signed 8-bit integers
- uint16_t for unsigned 16-bit integers
- uint32_t for unsigned 32-bit integers
and so on...
-
\$\begingroup\$ This appears to work (checked the assembly code again). Wasn't familiar with these types at all. Where does the
_t
come from? \$\endgroup\$jippie– jippie2012年07月07日 20:01:34 +00:00Commented Jul 7, 2012 at 20:01 -
3\$\begingroup\$ _t indicates, conventionally, that its naming a type \$\endgroup\$vicatcu– vicatcu2012年07月07日 20:25:15 +00:00Commented Jul 7, 2012 at 20:25
-
3\$\begingroup\$ These are standard fixed width integer types, introduced in C99. You may need to #include <stdint.h>. en.wikipedia.org/wiki/C_data_types#Fixed_width_integer_types \$\endgroup\$Toby Jaffey– Toby Jaffey2012年07月07日 23:01:20 +00:00Commented Jul 7, 2012 at 23:01
-
2\$\begingroup\$ +1. I use this to remove any ambiguity and for ultimate portability \$\endgroup\$Jon L– Jon L2012年07月08日 02:05:11 +00:00Commented Jul 8, 2012 at 2:05
-
\$\begingroup\$ A danger when using such types is that expression types are often non-portable in surprising ways. For example, on a system where
int
is 16 bits, givenuint32_t flag;
, the statementsflag &= ~0x00004000;
andflag &= ~0x00010000;
will behave as expected, butflag &= ~0x00008000;
will behave rather differently. \$\endgroup\$supercat– supercat2015年01月22日 21:45:49 +00:00Commented Jan 22, 2015 at 21:45
The more-or-less standard definition of a byte in C is 'unsigned char'.
-
\$\begingroup\$ Ah the
unsigned
part did the job,char
on it's own compiles without warnings. \$\endgroup\$jippie– jippie2012年07月07日 18:23:39 +00:00Commented Jul 7, 2012 at 18:23 -
1\$\begingroup\$ A famous C 'gotcha' is that the compiler is free to interpret 'char' as either 'signed char' or 'unsiged char', so the effective range of plain 'char' that you can count on is 0..127. Use plain 'char' only for 7-bit ASCII, never for anything else. \$\endgroup\$Wouter van Ooijen– Wouter van Ooijen2012年07月07日 18:31:22 +00:00Commented Jul 7, 2012 at 18:31
Explore related questions
See similar questions with these tags.