¿Qué hay de malo con esta conversión en el código C para AVR?

8

He definido dos variables:

uint8_t a[2];
uint16_t b;

A continuación, quiero usar a como variable de tipo uint16_t , e. g.

b = (uint16_t)a;

¡Pero esto está mal! Mis programas no funcionan correctamente con dicho código. Todo está bien cuando sustituyo b por uint8_t b[2] y uso las operaciones elementwise.

¿Por qué?

    
pregunta

5 respuestas

16

a es un puntero a una matriz de bytes. Si lo convierte en un uint16_t y lo asigna a b , entonces b contendrá la dirección de la base de la matriz (donde se almacena) en SRAM. Si desea tratar los dos bytes de la matriz a como un entero, entonces use una unión como lo sugiere el usuario 14284, pero tenga en cuenta que la unión representará la matriz de bytes en el orden de bytes de memoria de la arquitectura (en AVR que sería little-endian, lo que significa que el byte 0 es el byte menos significativo). La forma de escribir eso en el código es:

union{
  uint8_t a[2];
  uint16_t b;
} x;

x.b[0] = 0x35;
x.b[1] = 0x4A;

// by virtue of the above two assignments
x.a == 0x4A35 // is true

Otra forma de hacer esto sin usar una unión es convertir a a un puntero uint16_t y luego desreferenciarlo así:

uint8_t a[2] = {0x35, 0x4A};
uint16_t b = *((uint16_t *) a);
b == 0x4A35; // because AVR is little endian

Si está utilizando el búfer para almacenar datos de big endian (por ejemplo, el orden de bytes de la red), entonces necesitará un intercambio de bytes para usar cualquiera de estas técnicas. Una forma de hacerlo sin ninguna rama o variable temporal es:

uint8_t a[2] = {0x35, 0x4A};
a[0] ^= a[1];
a[1] ^= a[0];
a[0] ^= a[1];

a[0] == 0x4A; // true
a[1] == 0x35; // true

Por cierto, esto no es un AVR o incluso un problema solo incrustado. El código de red de nivel de aplicación escrito para PC normalmente llama a funciones llamadas htonl , htons (host a la red, variantes de 32 y 16 bits) y ntohl , ntohs (red a host, 32 y 16 bits variantes) cuyas implementaciones dependen de la arquitectura de destino en cuanto a si intercambian los bytes o no (bajo el supuesto de que los bytes transmitidos "en el cable" siempre son big-endian cuando forman parte de palabras de múltiples bytes).

    
respondido por el vicatcu
3

Si su intención es concatenar las dos variables de 8 bits en una variable de 16 bits, use un union . Si desea convertir un solo miembro de a en b , especifique qué elemento de la matriz desea utilizar.

    
respondido por el Joe Hass
2

En tu código, solo estás lanzando el puntero a la matriz.

Debe convertir el valor señalado por a .

b = (uint16_t)*a;

Nunca utilicé AVR, pero si está trabajando con una arquitectura de 16 bits, debe asegurarse de que la palabra esté alineada. No hacerlo puede dar lugar a una excepción.

    
respondido por el Bruno Ferreira
0

Cada miembro de a es un número de 8 bits. No puede contener nada más grande. Convertirlo a 16 bits no hace nada para a . Simplemente extrae cualquier valor que a pueda mantener, y lo convierte a 16 bits para que coincida con el formato de b cuando el valor se almacena allí.

Ni siquiera te refieres a un miembro de a . Debe usar un [0] o un [1] (¡y no un [2]!). Si usa a por sí mismo, solo obtendrá la dirección de la misma. (Buena captura, Bruno).

Declarar que a es una matriz de dos números de 8 bits tampoco lo convierte en un número de 16 bits. Puedes hacer algunas cosas programáticamente para almacenar y recuperar valores de 16 bits usando secuencias de 8 bits, pero no de la forma en que pensabas.

    
respondido por el gbarry
0

Si desea convertir los bytes en a en un valor de 16 bits, y la representación es little-endian (los 8 bits más bajos del valor vienen en el primer byte), haga

uint16_t b = a[0] | (a[1] << 8);

Para una representación de big endian

uint16_t b = (a[0] << 8) | a[1];

Evite utilizar lanzamientos de punteros o uniones para hacer esto, ya que eso conduce a problemas de portabilidad.

    
respondido por el starblue

Lea otras preguntas en las etiquetas