STM32F103RB_T6中__WFI函数未正常工作的技术求助
我正使用STM32CubeIDE在STM32F103RB_T6上进行C语言开发。奇怪的是,__WFI()函数并未等待中断触发就自行返回,我已在stm32f1xx_it.c的所有中断函数中设置断点,但均未触发。我希望main函数中的switch执行完成后,仅在中断发生时才进入下一轮循环,不想通过全局变量等临时方法解决,想排查问题根源。
以下是main.c文件内容:
#include "main.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "states.h" #include "creators.h" #include "structures.h" #include "displays.h" /* 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 ---------------------------------------------------------*/ UART_HandleTypeDef huart2; /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_USART2_UART_Init(void); /* USER CODE BEGIN PFP */ /* 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(); /* USER CODE BEGIN SysInit */ TestInit(); /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART2_UART_Init(); /* USER CODE BEGIN 2 */ currentStateMain = CATEGORY_SELECTION; /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ switch(currentStateMain) { ... } //__HAL_UART_DISABLE_IT(&huart2, UART_IT_TC); __WFI(); //__HAL_UART_ENABLE_IT(&huart2, UART_IT_TC); } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } } /** * @brief USART2 Initialization Function * @param None * @retval None */ static void MX_USART2_UART_Init(void) { /* USER CODE BEGIN USART2_Init 0 */ /* USER CODE END USART2_Init 0 */ /* USER CODE BEGIN USART2_Init 1 */ /* USER CODE END USART2_Init 1 */ huart2.Instance = USART2; huart2.Init.BaudRate = 115200; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart2) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN USART2_Init 2 */ /* USER CODE END USART2_Init 2 */ } /** * @brief GPIO Initialization Function * @param None * @retval None */ static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* USER CODE BEGIN MX_GPIO_Init_1 */ /* USER CODE END MX_GPIO_Init_1 */ /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOA_CLK_ENABLE(); /*Configure GPIO pins : PA9 PA10 PA11 PA12 */ GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12; GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; GPIO_InitStruct.Pull = GPIO_PULLDOWN; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* EXTI interrupt init*/ HAL_NVIC_SetPriority(EXTI9_5_IRQn, 2, 0); HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); HAL_NVIC_SetPriority(EXTI15_10_IRQn, 2, 0); HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); /* USER CODE BEGIN MX_GPIO_Init_2 */ /* USER CODE END MX_GPIO_Init_2 */ } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */
请问可能是有中断发生但未被检测到吗?该如何排查?希望按规范实现需求,不采用临时方案。
是的,确实存在**中断已触发但未进入中断服务函数(ISR)**的可能,或者有未被清除的中断挂起标志导致__WFI()直接返回。以下是逐步排查步骤:
1. 检查中断挂起寄存器,确认是否有未处理的中断
__WFI()会在有中断挂起时直接返回,哪怕该中断被屏蔽或者优先级不够。可以在调用__WFI()前读取以下寄存器:
- 内核层面:读取
SCB->ICSR寄存器,查看PENDSTSET(SysTick挂起)、PENDSVSET(PendSV挂起)、ISRPENDING(外部中断挂起)位 - 外设层面:
- GPIO中断:读取
EXTI->PR寄存器,查看对应引脚的挂起位(比如PA9对应EXTI9,看PR9位) - UART中断:读取
USART2->SR寄存器,查看是否有未清除的中断标志(比如RXNE、TC等)
- GPIO中断:读取
可以添加临时调试代码(调试完移除):
// 在__WFI()前添加 uint32_t scb_icr = SCB->ICSR; uint32_t exti_pr = EXTI->PR; uint32_t usart_sr = USART2->SR; // 用调试器查看这几个变量的值,或者通过UART输出
如果发现某个挂起位为1,说明该中断已经触发但未被处理,__WFI()因此直接返回。
2. 检查SysTick中断是否被默认启用
HAL_Init()会默认初始化SysTick并开启其中断(用于HAL的延时函数)。如果你的代码中没有用到HAL延时,但SysTick中断依然在运行,那么每隔1ms(默认配置)就会触发一次SysTick中断,导致__WFI()频繁返回。
可以在HAL_Init()后手动关闭SysTick中断验证:
HAL_Init(); HAL_SuspendTick(); // 关闭SysTick中断
如果__WFI()能正常等待了,就说明问题出在SysTick上。后续可以根据需求调整SysTick配置,或者在进入低功耗前关闭它,退出时再开启。
3. 验证GPIO中断配置的完整性
从你的MX_GPIO_Init()代码看,已经配置了PA9-PA12为上升沿触发、下拉输入,也开启了对应的EXTI中断通道,但要注意:
- 检查
stm32f1xx_it.c中是否实现了EXTI9_5_IRQHandler()和EXTI15_10_IRQHandler(),并且调用了HAL_GPIO_EXTI_IRQHandler()处理具体引脚。比如:
void EXTI9_5_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_9); } void EXTI15_10_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_10); HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_11); HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12); }
如果没有实现这些Handler,或者没有调用HAL的处理函数,中断触发后无法清除挂起位,会导致__WFI()反复返回。
- 检查
HAL_GPIO_EXTI_Callback()是否被正确实现。如果只是实现了Handler但没写Callback,HAL库会默认清除中断标志,但如果你的Callback里没有清除标志(或者写错了引脚),也会导致挂起位一直置1。
4. 检查UART中断的配置
你的代码中注释了UART发送完成中断的使能/禁用,但UART初始化时开启了TX_RX模式,默认情况下HAL会开启哪些中断?可以查看HAL_UART_Init()的实现,或者直接读取USART2->CR1寄存器,确认是否有未预期的中断被开启(比如RXNEIE)。如果UART引脚有噪声导致误触发RX中断,也会让__WFI()返回。
5. 检查全局中断是否被启用
虽然HAL_Init()会默认开启全局中断(__enable_irq()),但如果代码中某个地方不小心调用了__disable_irq(),会导致所有中断被屏蔽,但此时如果有中断挂起,__WFI()依然会返回(因为挂起标志存在),但ISR不会执行。可以在__WFI()前检查全局中断状态:
// 检查PRIMASK寄存器,0表示全局中断开启,1表示关闭 uint32_t primask = __get_PRIMASK();
6. 调试器的影响
有些调试器在连接时会导致__WFI()行为异常,比如调试器的“halt on interrupt”选项被开启,或者调试器本身会周期性唤醒CPU。可以尝试断开调试器,用硬件方式验证(比如LED闪烁)__WFI()是否正常等待。
内容的提问来源于stack exchange,提问作者MieszkoPasierbek

