How can I pass a struct in a function with input argument unsigned 32 bit array?

Viewed 297

I want to calculate the CRC value of some data in STM32 micro controller. The HAL function to calculate the CRC has the following footprint:

uint32_t HAL_CRC_Calculate(CRC_HandleTypeDef *hcrc, uint32_t pBuffer[], uint32_t BufferLength);

My data are stored in a struct:

struct caldata_tag {
    float K_P_Htng;
    uint16_t K_I_Htng;
    uint16_t K_D_Htng;
    uint16_t K_P_Coolg; } caldata;

Who is the safest and appropriate way to pass the struct to the HAL_CRC_Calculate() function?

I am thinking about this :

#define U32BUFFERSIZE sizeof(struct caldata_tag)/sizeof(uint32_t)
uint32_t buffer[U32BUFFERSIZE];
uint32_t crcValue;

/*  calculate the crc value of the data */
memcpy(buffer,&localStruct,U32BUFFERSIZE);

crcValue = HAL_CRC_Calculate(&CrcHandle,buffer,U32BUFFERSIZE);

but I am thinking that is an ugly way, could you tell me if it is ok? OR if you have a better idea?

3 Answers

Who is the safest and appropriate way to pass the struct to the HAL_CRC_Calculate()function?

Challenges:

  • HAL_CRC_Calculate() apparently wants to calculate the CRC based on multiples of uint32_t.

  • The size of struct caldata_tag may not be a multiple of the size of uint32_t.

  • struct caldata_tag may contain padding of an unknown state in caldata.

Use a union of struct caldata_tag and a large enough uint32_t array. Zero it, copy the members and then calculate the CRC.

I am thinking that is an ugly way, could you tell me if it is ok? OR if you have a better idea?

Form a helper function.

// Find the quotient of sizeof caldata_tag / sizeof(uint32_t), rounded up
#define U32BUFFERSIZE ((sizeof(struct caldata_tag) + sizeof(uint32_t) - 1)/sizeof(uint32_t))

uint32_t caldata_CRC(CRC_HandleTypeDef *hcrc, const struct caldata_tag *p) {
  // u's size will be a multiple of sizeof uint32_t
  union {
    uint32_t u32[U32BUFFERSIZE];
    struct caldata_tag tag;
  } u = { {0} };  // zero every thing

  // copy the members, not the padding
  u.tag.K_P_Htng = p->K_P_Htng;
  u.tag.K_I_Htng = p->K_I_Htng;
  u.tag.K_D_Htng = p->K_D_Htng;
  u.tag.K_P_Coolg = p->K_P_Coolg;

  return HAL_CRC_Calculate(hcrc, u.u32, U32BUFFERSIZE);
}

Use

uint32_t crcValue =  caldata_CRC(&CrcHandle, &caldata);

[Update]

Further research indicates that the BufferLength is a count of uint8_t, uint16_t, uint32_t depending on hcrc->InputDataFormat. OP has not provided that, yet if that can be set to uint8_t. then code only needs to worry about padding in struct caldata.

#define U8BUFFERSIZE sizeof(struct caldata_tag)

uint32_t caldata8_CRC(CRC_HandleTypeDef *hcrc, const struct caldata_tag *p) {
  // u's size will be a multiple of sizeof uint32_t
  union {
    uint32_t u32[U32BUFFERSIZE];
    struct caldata_tag tag;
  } u = { {0} };  // zero every thing

  // copy the members, not the padding
  u.tag.K_P_Htng = p->K_P_Htng;
  u.tag.K_I_Htng = p->K_I_Htng;
  u.tag.K_D_Htng = p->K_D_Htng;
  u.tag.K_P_Coolg = p->K_P_Coolg;

  return HAL_CRC_Calculate(hcrc, u.u32, U8BUFFERSIZE);
}

If the compiler allows __attribute__((__packed__)), @sephiroth answer is a good way to go.

You can use a pointer that points directly to the beginning of the struct, without having to use the support buffer:

uint32_t *p = (uint32_t*)&localStruct;

There are 2 problems with this:

The first one is that you might get unexpected results if the compiler is doing padding on the struct; you can solve this by adding the (packed) attribute to the struct to tell the compiler not to do any padding

struct __attribute__((__packed__)) caldata_tag { 
  //... 
}

The other problem is that your structure size isn't a multiple of 32, so it can't be represented in an array of uint32_t without having 16 random bits at the end of the last element. The same goes for your example, bur i think in this case you are discarding the last element of buffer because U32BUFFERSIZE should be equal to 2, so you are ignoring K_P_Coolg and the 16 random bits beside it when calculating the crc.

My suggestion when working with crc and stuff like that is using 8 bit buffers instead of 32 bit ones, as it completely eliminates the latter problem.

No workaround is needed. According to the documentation for the function HAL_CRC_Calculate: "By default, the API expects a uint32_t pointer as input buffer parameter. Input buffer pointers with other types simply need to be cast in uint32_t and the API will internally adjust its input data processing based on the handle field hcrc->InputDataFormat."

So set the field correctly in the first parameter and you can pass a pointer to bytes to the function.

Related