1

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?

Lundin
  • 195,001
  • 40
  • 254
  • 396
alig24
  • 13
  • 4

3 Answers3

2

*((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.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Union is fine provided it’s C99 or newer, for older standards it’s also UB. – alagner Oct 12 '21 at 12:47
  • @alagner This part of the standard (ISO 9899:2018 6.5.2.3/3) has not changed since C90. An informative foot note was added in C99 but that's irrelevant. Foot notes are not normative. – Lundin Oct 12 '21 at 12:51
  • @Lundin it worked actually but I have one problem. When I use `union` and copy bytes to variable(MODBUS receivedData) it loses first byte of `union` typed data. Lets say my buffer is {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08}. When I copy this it becomes like this: address=0x01, opID=0x02, dataLen=0x03, data[250]={0x05,0x06,0x07 ....}. It only loses first byte. If I dont use uint16_t in `union` it can copy all without losing any byte. – alig24 Oct 13 '21 at 06:45
  • @alig24 Sounds like you should ask a new question about that, posting the modified code. – Lundin Oct 13 '21 at 06:47
  • Okay it still cant use odd byte for the first byte of uint16_t, I understood. – alig24 Oct 13 '21 at 07:07
  • @alig24 I have no idea what you are talking about, post the code in a new question. – Lundin Oct 13 '21 at 07:38
0

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.

alagner
  • 3,448
  • 1
  • 13
  • 25
0

Note: The target in question is a variant of Cortex®-M0+, which supports misaligned access. So the following assumes turn irrelevant.

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;
Zongru Zhan
  • 546
  • 2
  • 8
  • 1
    It's better to re-order the members than to insert padding manually. Anyway, that's not the cause of the bug. The undefined behavior cast is the cause. – Lundin Oct 12 '21 at 12:45
  • When I use dummy byte it worked too. – alig24 Oct 12 '21 at 13:06