我已经看到这个主题已经在许多其他问题中讨论过,但我找不到我的具体案例的答案。
我正在使用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风格的强制转换相同的警告
我不能将memcpy
与void*
一起使用,因为我的最终目标是使编译器使用LDRB / STRB和LDRH / STRH指令。
其他人建议我在Stack Overflow上找到的解决方案依赖于用例。
有什么建议吗?
答案 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
并且DR
为volatile 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
我希望它会对你有所帮助。