Home > Blockchain >  Getting uint16_t value from uint8_t array in Keil
Getting uint16_t value from uint8_t array in Keil

Time:10-13

I am trying get variables from UART packet without using "<<" operator.

uint8_t buffer[8] = {0x11,0x22,0x33,0x44};
uint16_t val = *((uint16_t *)buffer);

If I try the code above in keil it is working. When I try it for the array in struct compiler doesn't give error but it goes to hardfault handler during runtime.

typedef struct
{
  uint8_t address;
  uint8_t opID; 
  uint8_t dataLen;
  uint8_t data[250];
  uint8_t crc[2];
}MODBUS;

MODBUS receivedData;
uint16_t val = *((uint16_t *)receivedData.data);

I also tried this(array in struct) in online c compiler. It is working without any problem. What should I do to use same thing in keil?

CodePudding user response:

*((uint16_t *)buffer); has two undefined behavior bugs:

  • buffer could be misaligned and converting to a larger pointer type could lead to misaligned access or a hardware trap/exception.
  • The code violates the pointer de-referencing type system, a so-called "strict aliasing violation". What is the strict aliasing rule? This is usually not a problem in embedded systems compilers, but regardless it is still undefined behavior and you shouldn't write code like that.

Additionally, you also have the potential problem that network endianess of the UART protocol might not match CPU endianess. What is CPU endianness? Most UART protocols use Big Endian.

To solve this, you could use memcpy as mentioned in another answer. Or more efficiently, a wrapper union:

typedef union
{
  uint8_t  u8  [8];
  uint16_t u16 [4];
} uart_buf_t;

This solves misalignment and strict aliasing both, but it will not solve potentially mismatching endianess. To solve that, you need to use bit shifts instead.

CodePudding user response:

The most likely reason of the hardfault is : You are using a core which doesn't support misalignment access. For example , the cortex M0 variant.

Suggestion:

  1. debug step by step, view the memory location of variables, especially the memory location of :

    receivedData.data

  2. try to access the location via byte-alignment, check whether a hardfault would occurs.

    uint8_t val = *((uint8_t *)receivedData.data);

If my guess is right, change the struct definition like following and see whether it could solve the problem.

typedef struct {
  uint8_t address;
  uint8_t opID; 
  uint8_t dataLen;
  uint8_t dummy_byte;
  
  uint8_t data[250];
  uint8_t crc[2];
}MODBUS;

CodePudding user response:

Usage of memcpy usually is the right way to handle such conversions, thus:

uint16_t target;
uint8_t source[]={1,2,3,4};
memcpy(&target, source, sizeof(target));

For changing an array of shorter integers into an array of longer ones you will have to modify it accordingly:

uint16_t target[2]={0};
uint8_t source[4]={1,2,3,4};
memcpy(target, source, sizeof(target));

Just remeber to adjust the sizes accordingly.

  • Related