Reputation: 43
In my application microcontroller stm32f103 is receiving by USART fixed lenght messages, they contains gps velocity which is big endian data. But elements in structure are small endian. Is there any way without doing it manually to write it in correct way?
typedef struct {
uint32_t test1;
uint16_t test2;
}Mst;
uint8_t myArray[6] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
void main()
{
Mst * myStruct_p = (Mst)myArray;
}
But after that myStruct_p->test1
equals 0x030201
, but should be 0x010203
, and myStruct_p->test2
equals 0x0605
, but should be 0x0506
.
Upvotes: 3
Views: 1285
Reputation: 33631
Casting in that way won't work.
You can do a "deserialize" operation.
Although this might be slower than some other methods, it allows you to control the protocol better (e.g. the struct
member order doesn't have to follow the protocol) And, there might be padding in the struct, which would show up if we added (e.g.) uint32_t test3;
to the end of the struct.
Here's the code:
#include <stdio.h>
#include <stdint.h>
typedef struct {
uint32_t test1;
uint16_t test2;
} Mst;
uint8_t myArray[6] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
uint32_t
get32(uint8_t **base)
{
uint8_t *ptr;
uint32_t val = 0;
ptr = *base;
for (int len = sizeof(uint32_t); len > 0; --len) {
val <<= 8;
val |= *ptr++;
}
*base = ptr;
return val;
}
uint16_t
get16(uint8_t **base)
{
uint8_t *ptr;
uint16_t val = 0;
ptr = *base;
for (int len = sizeof(uint16_t); len > 0; --len) {
val <<= 8;
val |= *ptr++;
}
*base = ptr;
return val;
}
int
main(void)
{
Mst myS;
uint8_t *arr = myArray;
myS.test1 = get32(&arr);
myS.test2 = get16(&arr);
printf("test1=%8.8X test2=%4.4X\n",myS.test1,myS.test2);
return 0;
}
UPDATE:
Yes guys that will work but, i would like to use the processor as little as possible. This is rather manualy putting bytes in to correct order. Also Procedure
Mst * struct_p = (Mst*)myArray
works safe, because while definingstruct
i use__attribute__((packed))
, just forgoten to write this
I was going to mention/suggest packed
as a possibility.
In either case you can use [under GNU]: byteswap.h
to get bswap_*
. Endian swapping is quite common, so these are [highly] optimized for the given arch. They can even invoke compiler intrinsics (e.g. __builtin_bswap32
) which utilize any special instructions the arch has (e.g. x86
has the bswap
instruction, and arm
has rev16
).
So, you can do [i.e. replace the for
loop with] (e.g.) bswap_*
:
#include <stdio.h>
#include <stdint.h>
#include <byteswap.h>
typedef struct {
uint32_t test1;
uint16_t test2;
uint32_t test3;
} __attribute__((__packed__)) Mst;
uint8_t myArray[] = {
0x01, 0x02, 0x03, 0x04,
0x05, 0x06,
0x07, 0x08, 0x09, 0x0A
};
void
getall(Mst *myS)
{
myS->test1 = bswap_32(myS->test1);
myS->test2 = bswap_16(myS->test2);
myS->test3 = bswap_32(myS->test3);
}
int
main(void)
{
Mst *myS = (Mst *) myArray;
getall(myS);
printf("test1=%8.8X test2=%4.4X test3=%8.8X\n",
myS->test1,myS->test2,myS->test3);
return 0;
}
Upvotes: 1
Reputation: 44
If you just need to convert 8bit data into big endian let say data format will be in order (A B C D) and in little endian data format will be (D C B A) if final result will be stored in 32bit variable and if 16 bit variable then format will (A B) and (B A) .
So this can be done with little bit shifting technique if you know your UART data comming in which format.
Let us first assume that your UART data comes in Little endian and you want it in big endian then you can do like this.
//no structure required
uint32_t myValue;
uint16_t value;
uint8_t myArray[4] = { 0x04, 0x03, 0x02, 0x01 };
// little endian data for 32 bit
uint8_t A[2] = {0x02 ,0x01}; // little endian data for 16 bit
void main()
{
myValue = myArray[3]<<24 | myArray[2]<<16 | myArray[1]<<8 | myArray[0]; // result in big endian 32 bit value
value = A[1]<<8 | A[0]; // result in big endian 16bit value
}
Upvotes: 0
Reputation: 68023
As it is ARM-Cortex M3 we can use special processor instructions. ARM CMSIS have a very handy intrinsic functions __REV
& __REV16
which actually compile to the single machine code instruction.
typedef union
{
struct
{
uint32_t test1;
uint16_t test2;
};
uint8_t bytes[6];
}Mst;
Mst mst = {.bytes = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }};
void main()
{
mst.test1 = __REV(mst.test1);
mst.test2 = __REV16(mst.test2);
}
Upvotes: 2
Reputation: 225537
Overlaying a struct pointer onto a byte array may not necessarily work due to alignment issues and structure padding.
The proper way would be to first use memcpy
to copy over the elements:
Mst myStruct;
memcpy(&myStruct.test1, myArray, sizeof(myStruct.test1);
memcpy(&myStruct.test2, myArray + 4, sizeof(myStruct.test2);
Then use ntohs
and ntohl
which converts 16 and 32 bit values respectively from big endian format to the host's endianness.
myStruct.test1 = ntohl(myStruct.test1);
myStruct.test2 = ntohs(myStruct.test2);
Upvotes: 0