O que há de errado com essa conversão no código C para o AVR?


8

Eu defini duas variáveis:

uint8_t a[2];
uint16_t b;

Em seguida, quero usar acomo variável do tipo uint16_t, por exemplo

b = (uint16_t)a;

Mas isso está errado! Meus programas não funcionam corretamente com esse código. Tudo está OK quando eu substituir ba uint8_t b[2]e operações de utilização elemento a elemento.

Por quê?

avr  c 

5
Por que você não joga alguns valores no seu exemplo e nos diz qual é a sua expectativa de "correto" para que possamos realmente ajudar sem especular sobre sua intenção semântica.
vicatcu

1
Isso seria muito melhor para o Stack Overflow.
Sharptooth 19/09/12

Respostas:


16

aé um ponteiro para uma matriz de bytes. Se você o converter em um uint16_t e atribuí-lo a b, ele bconterá o endereço da base da matriz (onde está armazenado) na SRAM. Se você deseja tratar os dois bytes da matriz acomo um número inteiro, use uma união como sugerido pelo usuário14284, mas lembre-se de que a união representará a matriz de bytes na ordem de bytes da arquitetura da arquitetura (no AVR, seria pouco -endian, que significa byte 0 é o byte menos significativo). A maneira de escrever isso no código é:

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

Outra maneira de fazer isso sem usar uma união é converter aem um ponteiro uint16_t e desreferenciar da seguinte forma:

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

Se você estiver usando o buffer para armazenar grandes dados endian (por exemplo, ordem de bytes da rede), precisará trocar de bytes para usar qualquer uma dessas técnicas. Uma maneira de fazer isso sem ramificações ou variáveis ​​temporárias é:

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

Aliás, isso não é um AVR ou mesmo um problema incorporado. Nível de aplicativo de rede código escrito para PCs normalmente chamadas de funções chamadas htonl, htons(host para rede, de 32 e 16-bit variantes) e ntohl, ntohs(rede de host, de 32 e 16-bit variantes), cuja implementações são alvo dependente da arquitetura para saber se eles troque os bytes ou não (sob a suposição de que os bytes transmitidos 'on the wire' são sempre big-endian quando fazem parte de palavras de vários bytes).


Esta é uma ótima resposta. A chave ' a' por si só é um ponteiro.
Jon G

2
"Outra maneira de fazer isso sem usar uma união é converter um ponteiro em uint16_t e desreferenciá-lo" - Na verdade, esse tipo de conversão freqüentemente quebra regras estritas de alias. Você não deve fazer isso a menos que esteja compilando -fno-strict-aliasing.
Jim Paris

3

Se sua intenção é concatenar as duas variáveis ​​de 8 bits em uma variável de 16 bits, use a union. Se você deseja converter um único membro aem b, especifique qual elemento da matriz você deseja usar.


2

No seu código, você está convertendo apenas o ponteiro para a matriz.

Você precisa converter o valor apontado por a.

b = (uint16_t)*a;

Eu nunca usei o AVR, mas se você estiver trabalhando com uma arquitetura de 16 bits, precisará garantir que a palavra esteja alinhada. Não fazer isso pode resultar em uma exceção.


1
... essa não é absolutamente a intenção dele ... ele quer que b seja relacionado aos dois elementos de a (isso desconsideraria totalmente a [1]), também não há exceções ou restrições sobre o que você pode lançar no avr-gcc
vicatcu

0

Cada membro de a é um número de 8 bits. Não pode conter nada maior. A conversão para 16 bits não faz nada para a . Ele apenas extrai qualquer valor que um possa ser capaz de manter e o converte em 16 bits, para que ele corresponda ao formato de b quando o valor é armazenado lá.

Você nem se referiu a um membro de um . Você deve usar um [0] ou um [1] (e não um [2]!). Se você usa um por si só, apenas obtém o endereço dele. (Boa captura, Bruno).

Declarando um a ser uma matriz de dois números de 8 bits não significa que seja um número de 16 bits, ou. Você pode fazer algumas coisas programaticamente para armazenar e recuperar valores de 16 bits usando sequências de 8 bits, mas não da maneira que estava pensando.


0

Se você deseja converter os bytes em aum valor de 16 bits, e a representação é little-endian (os 8 bits mais baixos do valor vêm no primeiro byte), faça

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

Para uma representação big endiana, faça

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

Evite o uso de ponteiros ou uniões para fazer isso, pois isso leva a problemas de portabilidade.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.