6
\$\begingroup\$

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> 
\$\endgroup\$

2 Answers 2

18
\$\begingroup\$

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...

\$\endgroup\$
5
  • \$\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\$ Commented Jul 7, 2012 at 20:01
  • 3
    \$\begingroup\$ _t indicates, conventionally, that its naming a type \$\endgroup\$ Commented 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\$ Commented Jul 7, 2012 at 23:01
  • 2
    \$\begingroup\$ +1. I use this to remove any ambiguity and for ultimate portability \$\endgroup\$ Commented 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, given uint32_t flag;, the statements flag &= ~0x00004000; and flag &= ~0x00010000; will behave as expected, but flag &= ~0x00008000; will behave rather differently. \$\endgroup\$ Commented Jan 22, 2015 at 21:45
8
\$\begingroup\$

The more-or-less standard definition of a byte in C is 'unsigned char'.

\$\endgroup\$
2
  • \$\begingroup\$ Ah the unsigned part did the job, char on it's own compiles without warnings. \$\endgroup\$ Commented 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\$ Commented Jul 7, 2012 at 18:31

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.