¿Cómo puedo indicarle a mi compilador que use bytes de 8 bits en lugar de enteros de 16 bits?

Tengo el siguiente código en mi programa de microcontrolador:

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
}

Donde:

  • ADCH es el registro que contiene el valor del ADC
  • PORTB es un puerto de salida digital que alterna un LED

Mirando el código ensamblador resultante, noté que está haciendo una comparación de 16 bits (líneas 40-44) donde, estrictamente hablando, solo 8 bits habrían sido suficientes:

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>

Me doy cuenta de que declaré analogValue como int , que de hecho es de 16 bits en AVR, pero...

¿Cómo puedo indicarle al compilador que use la comparación de 8 bits? El IDE de Arduino me permite usar byte , pero avr-gcc por defecto no lo hace.

Consulte esta página para ver el programa completo y su código resultante desensamblado.

EDITAR1:

Cambiar inta charcambia el código ensamblador a:

14:   11 24           eor     r1, r1          ; r1 = 0
3e:   18 ba           out     0x18, r1        ; PORTB

Básicamente saltándose la prueba por completo.

EDIT2: (Gracias: Wouter van Ooijen)

Cambiar inta unsigned charcambia el código ensamblador a:

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>

Respuestas (2)

De hecho, creo que una mejor práctica que evita esta ambigüedad arquitectónica es incluir <stdint.h>y luego usar tipos declarativos como:

  • uint8_t para enteros de 8 bits sin signo
  • int8_t para enteros de 8 bits con signo
  • uint16_t para enteros de 16 bits sin signo
  • uint32_t para enteros de 32 bits sin signo

y así...

Esto parece funcionar (verificó el código de ensamblaje nuevamente). No estaba familiarizado con estos tipos en absoluto. ¿De dónde _tviene?
_t indica, convencionalmente, que está nombrando un tipo
Estos son tipos enteros estándar de ancho fijo, introducidos en C99. Es posible que deba #incluir <stdint.h>. en.wikipedia.org/wiki/C_data_types#Fixed_width_integer_types
+1. Lo uso para eliminar cualquier ambigüedad y para la máxima portabilidad.
Un peligro al usar tales tipos es que los tipos de expresión a menudo no son portátiles de formas sorprendentes. Por ejemplo, en un sistema donde inthay 16 bits, dadas uint32_t flag;las declaraciones flag &= ~0x00004000;y flag &= ~0x00010000;se comportarán como se esperaba, pero flag &= ~0x00008000;se comportarán de manera bastante diferente.

La definición más o menos estándar de un byte en C es 'char sin firmar'.

Ah, la unsignedparte hizo el trabajo, charpor sí sola se compila sin advertencias.
Un famoso 'gotcha' de C es que el compilador es libre de interpretar 'char' como 'char signado' o 'char no siged', por lo que el rango efectivo de 'char' simple con el que puede contar es 0..127. Use 'char' simple solo para ASCII de 7 bits, nunca para nada más.