STM32 HAL库原子操作编译问题解决指南

STM32 HAL库原子操作编译问题解决指南(ATOMIC Undefined)

现象描述:

​ 近日在新建开发工程时,发现所用的HAL的部分文件版本已经比较老了,于是将该部分文件用新版本替换后加入到工程中。却在编译时报出了如下图所示错误

HAL提供的UART驱动文件中报出了ATOMIC_CLEAR_BITATOMIC_SET_BIT 的函数隐式声明警告。

原因分析:

​ 在新版本的HAL库中,部分外设(例如上述的UART等)的驱动实现底层使用到了原子操作的API,而由于种种原因我们的工程中并没有包含这部分的实现,因而在链接时链接器无法正确定位到相关实现。

​ 在stm32f4xx.h头文件中,可以找到普通位操作的实现,但是缺失了原子操作的实现。这也照应了上述的错误警示。

解决方案:

  • 可以在stm32f4xx_hal_confstm32f4xx_hal_def文件中,找一个位置将ATOMIC相关的API宏映射到普通的位操作API上。

修改后清扫工程,重新编译即可。

为什么可以这么修改?

原子操作的目的在于防止出现数据竞争现象 ,这常见于多核CPU。而我们常用的都是单核Cortex-M处理器,在单核的环境下,大多数寄存器操作本身具有足够的原子性,对于新版本的 HAL 库,只是使用 ATOMIC_* 前缀来更明确地表示这些操作应该是原子的。

  • 如果确实需要原子操作,可将下列代码复制到stm32f4xx_hal_confstm32f4xx_hal_def中。
c 复制代码
#define ATOMIC_SET_BIT(REG, BIT)                             \
  do {                                                       \
    uint32_t val;                                            \
    do {                                                     \
      val = __LDREXW((__IO uint32_t *)&(REG)) | (BIT);       \
    } while ((__STREXW(val,(__IO uint32_t *)&(REG))) != 0U); \
  } while(0)

#define ATOMIC_CLEAR_BIT(REG, BIT)                           \
  do {                                                       \
    uint32_t val;                                            \
    do {                                                     \
      val = __LDREXW((__IO uint32_t *)&(REG)) & ~(BIT);      \
    } while ((__STREXW(val,(__IO uint32_t *)&(REG))) != 0U); \
  } while(0)

这段代码使用了Cortex-M处理器的硬件原子指令,拥有硬件级的原子操作性能。

LDREX

  • 从内存加载值,并标记该内存区域为独占访问。
  • 建立了一个独占监视器。

STREX

  • 尝试将值存储到内存,如果内存仍然处于独占状态(没有被其他操作修改),则存储成功并返回0。
  • 如果内存已被修改,则存储失败并返回1。如果返回非0值会重新执行整个操作,最终确保成功完成原子修改。

  • 查阅资料后发现,该问题主要集中在Keil的老ARMCC5编译器,ARMCC5基于C99标准,对原子操作的支持并不好,而ARMCC6则基于更新的C11标准,内部完整支持了原子操作。因此还有一种解决办法是将工程重新转换到ARMCC6环境下进行编译。当然,半道转换编译环境是有一定风险的,该方法我也没有试过,因此此处仅作为一个参考。

总结:

​ 新版本HAL库使用了ATOMIC_SET_BITATOMIC_CLEAR_BIT宏。若由于编译器问题或某些原因导致缺失相关原子操作的实现,对于常见的单核环境下,可直接将其进行宏映射到普通位操作上。若有需要,也可加入自身的原子操作。将编译环境(Keil IDE)由ARMCC5改换到较新的ARMCC6或可解决问题,但是需要面临代码兼容性的风险,不建议中途更换编译环境。

相关推荐
leaves falling2 小时前
C语言内存函数-
c语言·开发语言
leaves falling3 小时前
c语言-扫雷游戏
c语言·单片机·游戏
梁洪飞3 小时前
clk学习
linux·arm开发·嵌入式硬件·arm
至为芯4 小时前
IP6537至为芯支持双C口快充输出的45W降压SOC芯片
c语言·开发语言
大话数据分析4 小时前
开源免费!AI 编程助手 OpenCode 保姆级教程
经验分享
浩瀚地学4 小时前
【Java】JDK8的一些新特性
java·开发语言·经验分享·笔记·学习
eewj5 小时前
STM32中FCLK时钟信号的作用
stm32·单片机·嵌入式硬件
淘晶驰AK6 小时前
ESP32和STM32哪个更容易学?
stm32·单片机·嵌入式硬件
楼田莉子7 小时前
Linux学习之磁盘与Ext系列文件
linux·运维·服务器·c语言·学习
__万波__7 小时前
STM32L475实现精度更好的delay函数
stm32·单片机·嵌入式硬件