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编译器优化策略不同,导致变量更新的可见性出了问题:
- GCC默认优化等级(
-O2)下,会把get_us()函数内联,并且可能将tim2_counter缓存到寄存器中——哪怕加了volatile,编译器也可能认为中断函数的修改不会影响当前上下文的寄存器值,导致循环里读取的一直是旧值,永远满足(now - tickstart) < us的条件,陷入死循环。 - 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
相关产品推荐
相关产品推荐

