STM32 SPI硬件和严格的别名警告

时间:2016-10-11 04:18:55

标签: c++ microcontroller stm32 hal type-punning

我已经看到这个主题已经在许多其他问题中讨论过,但我找不到我的具体案例的答案。

我正在使用STM32F0微控制器。 SPI接收/发送FIFO的顶部可通过存储器访问访问。这个特殊的微控制器允许我从FIFO的顶部读取/写入8位或16位。更确切地说,当执行LDRB / STRB指令时,从FIFO /向FIFO弹出/推送8位,并且当执行LDRH / STRH指令时,从/向/向FIFO弹出/推送16位。

STMicroelectronic提供的硬件抽象层提出了这种语法来读取SPI FIFO。

return *(volatile uint8_t*)&_handle->Instance->DR; // Pop 1 byte
return *(volatile uint16_t*)&_handle->Instance->DR; // Pop 2 byte

*(volatile uint8_t*)&_handle->Instance->DR = val; // Push 1 byte
*(volatile uint16_t*)&_handle->Instance->DR = val; // Push 2 bytes

其中DR是指向SPI FIFO顶部的uint32_t*

我使用这种语法构建了我的软件,它确实可以正常工作。唯一的问题是,g ++会引发很多关于类型惩罚的警告。更确切地说:

  

Inc / drivers / SPI.h:70:50:警告:解除引用类型惩罚指针将破坏严格别名规则[-Wstrict-aliasing]     return *(volatile uint16_t*)&_handle->Instance->DR;

经过一些阅读后,看起来在C ++中使用union并不是一个好主意。无论如何我确实尝试过,但遇到了一些问题。实际上通过联合中的指针访问内存会使我的微控制器崩溃,就像未对齐的内存访问一样。

static_cast和reinterpret_cast会抛出与C风格的强制转换相同的警告

我不能将memcpyvoid*一起使用,因为我的最终目标是使编译器使用LDRB / STRB和LDRH / STRH指令。

其他人建议我在Stack Overflow上找到的解决方案依赖于用例。

有什么建议吗?

3 个答案:

答案 0 :(得分:2)

我建议为这份工作创建两个特定的指针。您可以在初始化期间或静态创建它们,这样您就不必每次都创建它们。

static uint8_t * const DR_Byte = (uint8_t * const)&_handle->Instance->DR;
static uint16_t * const DR_Word = (uint16_t * const)&_handle->Instance->DR;

然后简单地阅读:

uint8_t read_byte = *DR_Byte;
uint16_t read_word = *DR_Word;

并写:

*DR_Byte = byte_to_write;
*DR_Word = word_to_write;

或类似的东西。

答案 1 :(得分:1)

所以看起来有一种方法可以让GCC吃掉没有抱怨的打字。就像Realtime Rik一样。我也成功地压制了警告

void* p = &_handle->Instance->DR;
(uint8_t*) p = val;

我后退了一步并重新考虑了我要做的事情,最终决定用 fno-strict-aliasing

来禁用严格的别名

为什么呢?根据我的理解,严格别名是一种优化,而不是功能要求。我的软件设计用于打字,因此严格的别名只是我无法承受的优化。或者至少,我认为最好禁用它,而不是试图欺骗编译器,因为我相信我实际上并没有做类型惩罚。

答案 2 :(得分:1)

我使用STM的 LL API 而不是HAL。 /STM32F0xx_LL_Driver/inc/stm32f0xx_ll_spi.h文件的一部分如下:

/**
  * @brief  Read 8-Bits in the data register
  * @rmtoll DR           DR            LL_SPI_ReceiveData8
  * @param  SPIx SPI Instance
  * @retval RxData Value between Min_Data=0x00 and Max_Data=0xFF
  */
__STATIC_INLINE uint8_t LL_SPI_ReceiveData8(SPI_TypeDef *SPIx)
{
  return (uint8_t)(READ_REG(SPIx->DR));
}

/**
  * @brief  Read 16-Bits in the data register
  * @rmtoll DR           DR            LL_SPI_ReceiveData16
  * @param  SPIx SPI Instance
  * @retval RxData Value between Min_Data=0x00 and Max_Data=0xFFFF
  */
__STATIC_INLINE uint16_t LL_SPI_ReceiveData16(SPI_TypeDef *SPIx)
{
  return (uint16_t)(READ_REG(SPIx->DR));
}

/**
  * @brief  Write 8-Bits in the data register
  * @rmtoll DR           DR            LL_SPI_TransmitData8
  * @param  SPIx SPI Instance
  * @param  TxData Value between Min_Data=0x00 and Max_Data=0xFF
  * @retval None
  */
__STATIC_INLINE void LL_SPI_TransmitData8(SPI_TypeDef *SPIx, uint8_t TxData)
{
  *((__IO uint8_t *)&SPIx->DR) = TxData;
}

/**
  * @brief  Write 16-Bits in the data register
  * @rmtoll DR           DR            LL_SPI_TransmitData16
  * @param  SPIx SPI Instance
  * @param  TxData Value between Min_Data=0x00 and Max_Data=0xFFFF
  * @retval None
  */
__STATIC_INLINE void LL_SPI_TransmitData16(SPI_TypeDef *SPIx, uint16_t TxData)
{
  *((__IO uint16_t *)&SPIx->DR) = TxData;
}

来自READ_REG文件的/STM32F0xx_LL_Driver/inc/stm32f0xx.h是宏,定义为:

#define READ_REG(REG)         ((REG))

您的问题如何,当您通过此_handle->Instance->DR构造访问spi数据寄存器时,您已取消引用指针Instance并且DRvolatile uint32_t。所以你只需要施放,这应该有效:

return (uint8_t)_handle->Instance->DR;
return (uint16_t)_handle->Instance->DR;

最后关于未对齐访问:我不知道如何保证,但是应该与ARM微控制器一起工作。我自动生成的链接描述文件在每个部分都有说明. = ALIGN(4);

.rodata :
{
  . = ALIGN(4);
  *(.rodata)         /* .rodata sections (constants, strings, etc.) */
  *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
  . = ALIGN(4);
} >FLASH

我希望它会对你有所帮助。