如何 convert/align 我的 uint8_t 指针缓冲区指向 uint32 以使其与自动生成的 HAL 函数一起工作

How to convert/align my uint8_t pointer buffer to uint32 for it to work with auto-generated HAL functions

在我自动生成的用于实现 CRC 的 HAL 代码中,我有以下功能:

uint32_t HAL_CRC_Accumulate(CRC_HandleTypeDef *hcrc, uint32_t pBuffer[], uint32_t BufferLength)
{
  uint32_t index;      /* CRC input data buffer index */
  uint32_t temp = 0U;  /* CRC output (read from hcrc->Instance->DR register) */

  /* Change CRC peripheral state */
  hcrc->State = HAL_CRC_STATE_BUSY;

  switch (hcrc->InputDataFormat)
  {
    case CRC_INPUTDATA_FORMAT_WORDS:
      /* Enter Data to the CRC calculator */
      for (index = 0U; index < BufferLength; index++)
      {
        hcrc->Instance->DR = pBuffer[index];
      }
      temp = hcrc->Instance->DR;
      break;

    case CRC_INPUTDATA_FORMAT_BYTES:
      temp = CRC_Handle_8(hcrc, (uint8_t *)pBuffer, BufferLength);
      break;

    case CRC_INPUTDATA_FORMAT_HALFWORDS:
      temp = CRC_Handle_16(hcrc, (uint16_t *)(void *)pBuffer, BufferLength);    /* Derogation MisraC2012 R.11.5 */
      break;
    default:
      break;
  }

  /* Change CRC peripheral state */
  hcrc->State = HAL_CRC_STATE_READY;

  /* Return the CRC computed value */
  return temp;
}

问题是我自己的 inBuff 是 uint8_t * inBuff 类型的,我可以在自动生成的代码中看到它需要一个 uint32_t 作为输入,然后再将其类型转换为uint8_t 因为我为我的 CRC 使用了 CRC_INPUTDATA_FORMAT_BYTES 选项。需要我的 uint8_t buff 的原因是让它与我的其余代码(相当大的项目)一起工作。有什么理由可以在不损坏我自己的 inBuff 内容的情况下以有效的方式解决这个问题?它需要正确对齐。

How to convert/align my uint8_t pointer buffer to uint32 for it to work with auto-generated HAL functions

什么都不做。

The problem is that my own inBuff is of type uint8_t * inBuff and I can see in the auto-generated code that it needs a uint32_t as input

所以投指针。

HAL_CRC_Accumulate(..., (uint32_t*)your_buffer, ...)

Is there any reason to work around this in an efficient way without damaging my own inBuff content?

没有。 (?)

是的,令人不快的是它不使用 void* 指针,或者每个 INPUTDATA_FORMAT 不只是单独的 API。不过,您无需执行任何操作,只需传递指针值 - 内部例程无论如何都会通过 uint8_t* 正确的句柄访问它们。

INPUTDATA_FORMAT_BYTES 所需的对齐方式为 1,在字节边界处。

  1. 将您的缓冲区分配到所需的大小加上 sizeof(uint32_t) - 1

例如,如果你的缓冲区静态分配给SIZE,那么你可以这样做:

uint8_t buff[SIZE + sizeof(uint32_t) - 1];
  1. 将指针指向缓冲区中的最低地址,该地址与 uint32_t:
  2. 对齐
size_t addr = (size_t)buff + sizeof(uint32_t) - 1;
uint8_t* inBuff = (uint8_t*)(addr / sizeof(uint32_t) * sizeof(uint32_t));

问题:

  • 不打算通过 pointed-at 类型访问指针参数的函数不应使用该类型声明。如果 HAL_CRC_Accumulate 从未打算将 pBuffer 作为 uint32_t 的(数组)访问,那么它不应该使用该类型。然后它应该被声明为 uint8_t*.
  • 将指向数组第一项的 uint8_t* 传递给接受 uint32_t* 的函数是非常危险的代码,确实可能导致错位问题。你的大部分问题归结为:为什么这个函数使用uint32_t[]?这没有任何意义。
  • uint32* 转换为 uint16_t* 同样是不好的做法和未定义的行为,以防传递此 uint16_t* 的函数将访问该参数作为 uint16_t .不仅因为可能的错位,还因为严格的别名。同样,如果该函数无意使用 uint16_t* 访问 uint16_t 对象,那么它不应该使用该指针类型。同样,整个代码似乎应该适用于 uint8_t* 类型。
  • 像这样“链接”多个转换 (uint16_t *)(void *) 是无稽之谈。 void* 没有增加任何东西,也没有解决任何问题。
  • 通常情况下,计算 CRC 的函数将在 const 合格缓冲区上运行,因为它不应该更改任何内容。如果 so-called“FCS”(计算的校验和)应该附加在数据缓冲区的末尾,那么最好单独进行设计。
  • 因为在评论中提到了 MISRA-C:以上所有内容在 mission-critical 代码库中都是特别不可接受的。 Auto-generated 代码不是草率的、可能会破坏类型使用的借口——恰恰相反。比咨询规则 11.5 更重要的是,您多次违反了(必需)MISRA-C:2012 规则 11.3,并且 none 符合 MISRA 标准。

STMCubeMX 不会生成最好的代码,您已经找到了一个很好的例子。

首先,他们 (ST) 确实希望您传入一个 uint32_t 缓冲区,因为他们希望数据在 32 位边界上对齐。看到这一行:

hcrc->Instance->DR = pBuffer[index];

他们期望读取一个 uint32_t 值并将其写入 32 位 DR 寄存器。未对齐的指针会在那里导致错误。

如果缓冲区实际上未在 32 位边界上对齐,则您尝试将 uint8_t 缓冲区转换为 uint32_t 可能会出现问题。你可以这样排列你的:

uint8_t mybuf[128] __attribute__((aligned(4)));

您仍然需要将指针转换为 uint32_t* 以将其传递给 HAL_CRC_Accumulate,这样编译器就不会报错,但它会工作,因为它确实对齐了!

我强烈建议您不要修改 CubeMX 生成的代码。除非您的代码添加到 ST 代码的特殊 /* USER */ 部分,否则每次重新生成项目时它都会丢失。这会一遍又一遍地咬你的屁股。