STM32L476从STOP模式唤醒后ADC DMA无法工作问题求助
STM32L476 STOP模式RTC唤醒后ADC DMA失效问题解决
问题概述
STM32L476进入STOP模式经RTC唤醒后,ADC DMA无法工作,多次重新初始化无效。相关细节:
- 芯片型号:STM32L476
- 休眠模式:STOP模式,RTC唤醒
- 唤醒后USB可重新初始化,终端通信正常
- 仅STOP模式存在该问题,SLEEP模式下ADC DMA触发正常
- 芯片可正常进入STOP模式并唤醒
- 唤醒后寄存器状态:
- ADC ISR=0x00000003, CR=0x10000005, CFGR=0x80001903
- TIM3 CR1=0x0001, CNT=6606, SR=0x001f
- ADC转换由TIM3事件触发,已启用DMA连续请求
- 唤醒后读取ADCbuffer无更新,无ADC及DMA中断
相关代码
uint32_t ADCbuffer[14]; // DMA/ADC result buffer static uint32_t tick = 0; 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(); MX_DMA_Init(); MX_ADC1_Init(); MX_USART1_UART_Init(); MX_TIM3_Init(); MX_USB_DEVICE_Init(); MX_RTC_Init(); MX_TIM2_Init(); MX_ADC2_Init(); /* USER CODE BEGIN 2 */ //BEEEEPPP HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2); htim2.Instance->CCR2 = htim2.Instance->ARR/2; HAL_Delay(200); htim2.Instance->CCR2 = 0; //Start the ADC and triggers //ADC1 regular conversions are triggered by timer3 which also gates the dividers HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2); HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3); HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_4); htim3.Instance->CCR1 = htim3.Instance->ARR; htim3.Instance->CCR4 = 2000; GPIOB->ASCR |=0x01; HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED); HAL_Delay(200); HAL_ADC_Start_DMA(&hadc1, ADCbuffer,14); __HAL_ADC_ENABLE_IT(&hadc1, ADC_IT_EOS); HAL_ADCEx_InjectedStart(&hadc2); uint8_t HiMsg[] = "hello\r\n"; CDC_Transmit_FS(HiMsg, strlen(HiMsg)); //Now we have a short delay to allow some initial readings to come in from the ADC HAL_Delay(500); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { if (HAL_GetTick() - tick > 30000) { tick = HAL_GetTick(); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, 0); //LED OFF HAL_SuspendTick(); HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 20480, RTC_WAKEUPCLOCK_RTCCLK_DIV16); //32768/16 = 2048 __disable_irq(); HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON, PWR_STOPENTRY_WFI); __enable_irq(); HAL_RTCEx_DeactivateWakeUpTimer(&hrtc); SystemClock_Config(); HAL_ResumeTick(); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, 1); //LED ON // init ADC hereeee // ... MX_USB_DEVICE_Init(); //Bip HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2); htim2.Instance->CCR2 = htim2.Instance->ARR/2; HAL_Delay(2); htim2.Instance->CCR2 = 0; HAL_Delay(2000); uint8_t HiMsg[] = "hello\r\n"; CDC_Transmit_FS(HiMsg, strlen(HiMsg)); HAL_Delay(50); HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_2); } /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }
问题分析与解决方案
核心原因
STOP模式下,ADC和DMA的外设时钟会被关闭,唤醒后仅重新初始化系统时钟不足以恢复外设的正确状态,尤其是DMA的请求通道和ADC的触发配置可能处于异常状态。结合寄存器状态:
- ADC ISR=0x00000003 表示
ADRDY和EOS位置位,但后续无新转换,说明ADC未正确响应触发信号 - TIM3 SR=0x001f 表示多个标志位(如UIF、CC1IF等)置位,残留标志阻塞了触发逻辑
修复步骤
唤醒后强制重置ADC和DMA外设
先通过RCC寄存器重置外设,再执行初始化,确保外设回到初始状态:// 重置ADC1 __HAL_RCC_ADC1_FORCE_RESET(); HAL_Delay(1); __HAL_RCC_ADC1_RELEASE_RESET(); // 重置DMA通道(根据实际使用的DMA通道修改,示例为DMA1) __HAL_RCC_DMA1_FORCE_RESET(); HAL_Delay(1); __HAL_RCC_DMA1_RELEASE_RESET(); // 重新初始化外设 MX_DMA_Init(); MX_ADC1_Init();清除TIM3和ADC的所有挂起标志
唤醒后先清除定时器和ADC的残留标志,避免触发逻辑被阻塞:__HAL_TIM_CLEAR_FLAG(&htim3, TIM_FLAG_ALL); __HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_ALL);重新启动ADC DMA和定时器触发
重置初始化后,重新执行校准、启动DMA转换和定时器PWM输出:HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED); HAL_Delay(200); HAL_ADC_Start_DMA(&hadc1, ADCbuffer,14); __HAL_ADC_ENABLE_IT(&hadc1, ADC_IT_EOS); // 重启TIM3所有PWM通道 HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1); HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_2); HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_3); HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_4); HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2); HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3); HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_4); htim3.Instance->CCR1 = htim3.Instance->ARR; htim3.Instance->CCR4 = 2000;优化STOP模式进入前的外设关闭流程
进入STOP前主动关闭ADC DMA和定时器,避免外设处于半工作状态:// 进入STOP前关闭相关外设 HAL_ADC_Stop_DMA(&hadc1); __HAL_ADC_DISABLE_IT(&hadc1, ADC_IT_EOS); HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1); HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_2); HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_3); HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_4);
修改后的唤醒后代码片段
__enable_irq(); HAL_RTCEx_DeactivateWakeUpTimer(&hrtc); SystemClock_Config(); HAL_ResumeTick(); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, 1); //LED ON // 重置并重新初始化ADC和DMA __HAL_RCC_ADC1_FORCE_RESET(); HAL_Delay(1); __HAL_RCC_ADC1_RELEASE_RESET(); __HAL_RCC_DMA1_FORCE_RESET(); HAL_Delay(1); __HAL_RCC_DMA1_RELEASE_RESET(); MX_DMA_Init(); MX_ADC1_Init(); // 清除残留标志 __HAL_TIM_CLEAR_FLAG(&htim3, TIM_FLAG_ALL); __HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_ALL); // 重新启动定时器PWM HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2); HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3); HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_4); htim3.Instance->CCR1 = htim3.Instance->ARR; htim3.Instance->CCR4 = 2000; // 重新校准并启动ADC DMA HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED); HAL_Delay(200); HAL_ADC_Start_DMA(&hadc1, ADCbuffer,14); __HAL_ADC_ENABLE_IT(&hadc1, ADC_IT_EOS); MX_USB_DEVICE_Init(); //Bip HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2); htim2.Instance->CCR2 = htim2.Instance->ARR/2; HAL_Delay(2); htim2.Instance->CCR2 = 0; HAL_Delay(2000); uint8_t HiMsg[] = "hello\r\n"; CDC_Transmit_FS(HiMsg, strlen(HiMsg)); HAL_Delay(50); HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_2);
额外注意事项
- 确认DMA通道的优先级配置,避免唤醒后被其他高优先级外设抢占
- 检查ADC时钟源在唤醒后的配置是否正确,确保ADC时钟稳定
- 若使用连续转换模式,需确认ADC的
CONT位在唤醒后是否正确置位
内容的提问来源于stack exchange,提问作者Nguyễn Phụng Thái Cường
相关产品推荐
相关产品推荐

