cancel
Showing results for 
Search instead for 
Did you mean: 

Alter CAN Message Data, Array to Int

KMew
Senior III

Hello,

This is a mixture of a C question and a CAN communication question, as I want to find the simplest solution for what I'm trying to do. I have an 8 byte CAN message that I successfully receive and store in an array (uint8_t RXData[8]). I am sending part of this data (RxData[0 to 3]) to a display on the STM32H7B3I-EVAL evaluation board.

I want to store the data in a single uint32_t variable (since RxData[0 to 3] represents a single piece of data. Specifically, a voltage). So storing it in pieces of an array makes it difficult to do arithmetic on it (scale the 0-65535 range of uint32_t to represent a voltage range of 0-60V).

What is the simplest way to do this?

1 ACCEPTED SOLUTION

Accepted Solutions
JPeac.1
Senior

Assuming RxData is 32-bit aligned, easiest way is:

NewValue = (uint32_t *) &RxData[4];

otherwise loop from [4] to [7], NewValue |= (NewValue << 8) | RxData[i];

This ignores the CAN protocol being used. It will work for CANopen, which is usually little-endian, but not for other CAN protocols which are big-endian. Not enough information to fully answer the question.

View solution in original post

9 REPLIES 9
KMew
Senior III

To add an example of what I'm looking for:

uint8_t RxData[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88)

End Goal:

uint32_t NewValue = 0x55667788

JPeac.1
Senior

Assuming RxData is 32-bit aligned, easiest way is:

NewValue = (uint32_t *) &RxData[4];

otherwise loop from [4] to [7], NewValue |= (NewValue << 8) | RxData[i];

This ignores the CAN protocol being used. It will work for CANopen, which is usually little-endian, but not for other CAN protocols which are big-endian. Not enough information to fully answer the question.

Hello JPeac,

Thank you for the reply! This solution actually works perfectly for me! CANopen is little-endian and works for what I'm trying to do. If I encounter a big endian scenario, I can just flip the direction (i.e. instead of Data[i]. have the for loop be Data[size-i] instead).

Thank you very much!

with uint64_t NewValue.

i usually just memcpy(&NewValue,&RxData,sizeof(NewValue))

and then i do all the bitshifting/masking i need.

Using pointers is a nogo for my applications due to RxData fast changes

and multithread (RTOS)

we dont need to firmware by ourselves, lets talk

Memcpy with bit-shifting is likely a better idea for scalability too, I would imagine! That way, I could define how much is bit shifted based on what segment of data is needed. It would require a function call that defines how much to bit shift, but the flexibility would be better.

Thank you for this suggestion too!

Piranha
Chief II

The most universal and recommended way is using a union:

union msg_u {
	uint8_t bytes[8];
	uint32_t values[2];
} RxData;

> Assuming RxData is 32-bit aligned

Cortex-M3 and higher supports unaligned accesses.

Endianess can be dealt easily with REV, REV16 and REVSH instruction macros:

https://www.keil.com/pack/doc/cmsis/Core/html/group__intrinsic__CPU__gr.html#ga4717abc17af5ba29b1e4c055e0a0d9b8

Hello Piranha,

This is an interesting way. By putting the RxData at the end, you are defining the variable name. When reading the CAN message in HAL_FDCAN_GetRxMessage, I'll have to use RxData.bytes in your case, correct? If so, is RxData.values also automatically filled in with the same data, but in the different format? Or is there an additional step?

Yes, you pass an address of the RxData.bytes to the driver, but after that read RxData.values . No additional steps necessary, because it is actually the same memory (8 bytes). Union members just give a different "views" on it. You can put a structure there also, but be aware of a possible alignment/padding issues, which must be solved by structure packing.