C++14及以上的stm32 hal库警告

stm32 hal library warning with C++14 & above

我也在STM32社区论坛发了同样的帖子question,但是没有收到回复。

我在启用了 C++14 的项目中使用 stm32 HAL 库。它向我发出以下警告,我无法摆脱它。

../platform/stm32/l4/STM32L4xx_HAL_Driver/Inc/stm32l4xx_hal_rcc.h:735:57:

warning: conversion to void will not access object of type 'volatile uint32_t {aka volatile long unsigned int}' UNUSED(tmpreg); \

当调用 __GPIOX_CLK_ENABLE() 或 __HAL_RCC_GPIOX_CLK_ENABLE 时会发生这种情况。

有没有人能够摆脱上述警告并保持 HAL 源代码完好无损。

或任何可能的想法。

当前警告级别为-Wall。

我在使用 l4 和 f4 系列代码时遇到过上述问题。

示例代码:

int main(void)
{
    HAL_Init();

    __GPIOB_CLK_ENABLE();
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_InitStructure.Pin = GPIO_PIN_7;

    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
    GPIO_InitStructure.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);

    for (;;)
    {
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
        HAL_Delay(500);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
        HAL_Delay(500);
    }
}

罪魁祸首是 __GPIOB_CLK_ENABLE(),它被扩展为以下内容(在 ST 驱动程序中)。

#define __HAL_RCC_GPIOB_CLK_ENABLE()           do { \
                                                 __IO uint32_t tmpreg; \
                                                 SET_BIT(RCC->AHB2ENR, RCC_AHB2ENR_GPIOBEN); \
                                                 /* Delay after an RCC peripheral clock enabling */ \
                                                 tmpreg = READ_BIT(RCC->AHB2ENR, RCC_AHB2ENR_GPIOBEN); \
                                                 UNUSED(tmpreg); \
                                               } while(0)

我原来的问题是想找到一个解决方案,让底层的ST驱动完好无损。 一种可能的解决方案是使用直接寄存器访问,而无需通过提供方便的宏的库。

提前致谢。

问题是 -std=c++14volatile 表达式的语义转换为 (void),并为其引入明显*无条件警告,并且 ST 的编码人员试图使"triple sure" 将发生寄存器读取。

UNUSED()宏的定义是

#define UNUSED(x) ((void)(x))

__IO定义为

#define     __IO    volatile

那么 __HAL_RCC_GPIOB_CLK_ENABLE() 的扩展就是

do {
    volatile uint32_t tmpreg;
    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOBEN;
    /* Delay after an RCC peripheral clock enabling */
    tmpreg = RCC->AHB2ENR & RCC_AHB2ENR_GPIOBEN;
    ((void)(tmpreg));
} while(0)

寄存器的延时和read-back是各种STM32勘误表推荐的说法

A delay between an RCC peripheral clock enable and the effective peripheral enabling should be taken into account in order to manage the peripheral read/write to registers.

[...]

insert a dummy read operation from the corresponding register just after enabling the peripheral clock.

由于所有外设寄存器当然都声明为 volatile,一个仅包含相关寄存器的简单表达式将强制通过相同的外设总线进行具有必要等待状态的回读,因此这就足够了:

do {
    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOBEN;
    /* Delay after an RCC peripheral clock enabling */
    RCC->AHB2ENR;
} while(0)

其余的可能是针对一些有缺陷的编译器的过度设计的解决方法,但我还没有看到一个如此糟糕以至于可以优化 volatile 类型的表达式。

然而,存在边缘情况,将 volatile 变量强制转换为 (void),其中语义在 C++14 中显然发生了变化。

让我们举个简单的例子

void x() {
    volatile int t;
    t=1;
    ((void)(t));
}

Arm gcc 7.2.1 invoked with -O3 -mcpu=cortex-m4 -mthumb -Wall -x c++ -std=c++11 会将其编译为

x():
  sub sp, sp, #8
  movs r3, #1
  str r3, [sp, #4]
  ldr r3, [sp, #4]
  add sp, sp, #8
  bx lr

并且the same code compiled with -std=c++14

x():
  sub sp, sp, #8
  movs r3, #1
  str r3, [sp, #4]
  add sp, sp, #8
  bx lr

...和一个警告:

<source>: In function 'void x()':
<source>:5:13: warning: conversion to void will not access object of type 'volatile int'
     ((void)(t));
            ~^~

另请注意第二种情况中缺少的 ldr 指令。使用 C++14 写入后无法访问该变量。

My original question is intended to find out a solution, leaving the underlying ST driver intact. One possible solution would be to use the direct register access without going through the library provided convenient macro.

我建议继续前进并避免使用库,恕我直言,HAL 最好被视为示例或实施建议的集合。

*我找不到禁用它的方法。这并不意味着有 none.

您可以将代码提交到自己的存储库以解决该问题,并且仍然使用 c++14 编译代码。

/* Workaround for the broken UNUSED macro */
#include "stm32f3xx_hal_def.h"
#undef UNUSED
#define UNUSED(x) ((void)((uint32_t)(x)))

这需要在包含任何 HAL headers 之前添加。对我来说,在模块启用宏之后(即 #define HAL_WWDG_MODULE_ENABLED 行)但在包含实际 HAL headers 之前将其放入 stm32f3xx_hal_conf.h 文件中很方便。 我将所有源更新为 #include "stm32f3xx_hal_conf.h" 而不是单个 HAL headers.

之所以有效,是因为根据@berendi 的出色研究,警告来自 volatile 名称。通过首先将值转换为非易失性的值,可以避开 C++14 标准中的新条款。

正如@oliv 在回复@berendi 的回答中提到的,根本原因似乎是 GCC 中的错误,该错误已在较新的版本中得到修复。当我升级到 "Version 9-2019-q4-major" 工具链 (GCC 9.2) 时,警告消失了。

另外,对于STM32G0系列,ST将UNUSED的定义修改为:

#define UNUSED(X) (void)X      /* To avoid gcc/g++ warnings */

这使得早期版本的编译器的警告消失。