You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

STM32WB55程序Keil运行正常,STM32CubeIDE卡在get_us()函数

切换到STM32CubeIDE编译后,TIM2相关的wait_us()函数卡在get_us()里是IDE的bug吗?

我有一个运行在STM32WB55上的测试TIM2定时器的小程序,里面包含延时函数wait_us()。用Keil uVision 5编译后运行正常,但换成STM32CubeIDE编译就会卡在get_us()函数里。这是STM32CubeIDE的bug吗?


main.c代码

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void PeriphCommonClock_Config(void);
static void MX_GPIO_Init(void);
/* USER CODE BEGIN PFP */
extern void initialize_tim2(void);
extern void wait_us(volatile uint32_t us);
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

/* Configure the peripherals common clocks */
  PeriphCommonClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */
  initialize_tim2();
  TIM2->CR1 |= 0x0001;
  wait_us(10);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    HAL_GPIO_TogglePin(GPIOB, LD1_Pin);
    HAL_Delay(500);
  }
  /* USER CODE END 3 */
}

TIM2相关代码

#include "stm32wbxx.h"

volatile uint32_t tim2_counter;

void initialize_tim2(void)
{
  RCC->APB1ENR1 |= 0x00000001;
  TIM2->CR1 = 0x0004;
  TIM2->CR2 = 0x0000;
  TIM2->PSC = 0;
  TIM2->ARR = 32;
  TIM2->DIER = 0x0001;
  TIM2->CNT = 0;
  NVIC_SetPriority(TIM2_IRQn, 0);
  tim2_counter = 0;
}

void TIM2_IRQHandler(void)
{
  TIM2->SR &= (~0x001F);
  ++tim2_counter;
}

uint32_t get_us(void)
{
  return tim2_counter;
}

void wait_us(volatile uint32_t us)
{
  volatile uint32_t tickstart, now, IRQ_status;
  
  // Get current interrupt status of TIM2 and enable it.
  // Needs to be done with all interrupts disabled to prevent race condition?
  __disable_irq();
  IRQ_status = NVIC_GetEnableIRQ(TIM2_IRQn);
  NVIC_EnableIRQ(TIM2_IRQn);
  __enable_irq();
  
  tickstart = get_us();
  now = tickstart;                //<-processing never reaches here under STM32CubeIDE.
  while((now - tickstart) < us)
  {
    now = get_us();
  }
  
  // Return to previous interrupt status of TIM2.
  // Needs to be done with all interrupts disabled to prevent race condition?
  __disable_irq();
  if(0 == IRQ_status)
  {
    NVIC_DisableIRQ(TIM2_IRQn);
  }
  else
  {
    NVIC_EnableIRQ(TIM2_IRQn);
  }
  __enable_irq();
}

问题根源:不是IDE的bug,是编译优化差异导致的

这和STM32CubeIDE本身无关,核心是CubeIDE使用的GCC编译器与Keil的ARMCC编译器优化策略不同,导致变量更新的可见性出了问题:

  1. GCC默认优化等级(-O2)下,会把get_us()函数内联,并且可能将tim2_counter缓存到寄存器中——哪怕加了volatile,编译器也可能认为中断函数的修改不会影响当前上下文的寄存器值,导致循环里读取的一直是旧值,永远满足(now - tickstart) < us的条件,陷入死循环。
  2. Keil的ARMCC对volatile变量的处理更保守,不会做这种激进的缓存优化,所以能正常运行。

解决方法

方法1:阻止get_us()被内联

给函数添加__attribute__((noinline))属性,强制每次调用都从内存读取最新的tim2_counter值:

uint32_t __attribute__((noinline)) get_us(void)
{
  return tim2_counter;
}

方法2:添加内存屏障

在get_us()中加入数据同步屏障,强制编译器刷新寄存器缓存,读取内存中的最新值:

uint32_t get_us(void)
{
  __DSB(); // 数据同步屏障,确保内存访问完成
  return tim2_counter;
}

方法3:降低编译优化等级(不推荐)

在CubeIDE项目设置中,将优化等级从默认的-O2改为-O0(无优化),但会导致代码体积增大、运行效率降低,仅适合临时排查问题。

额外代码优化建议

  • TIM2_IRQHandler中无需清除所有SR寄存器位,只需清除更新中断标志位即可:TIM2->SR &= ~TIM_SR_UIF,避免误清除其他未处理的中断标志。
  • 初始化TIM2前建议先复位外设:RCC->APB1RSTR1 |= RCC_APB1RSTR1_TIM2RST; RCC->APB1RSTR1 &= ~RCC_APB1RSTR1_TIM2RST;,确保寄存器处于初始状态。

内容的提问来源于stack exchange,提问作者TRPh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 14:40:18