在STM32开发中经常会用到独立看门狗(IWDG)和低功耗模式,看门狗是为了检测和解决由软件错误引起的故障,低功耗模式是为了在CPU不需要继续运行时进入到休眠模式用以节省电能。其中独立看门狗的时钟由独立的RC振荡器(STM32F10x一般为40kHz)提供,即使在主时钟出现故障时,也仍然有效,因此可以在停止和待机模式下工作。而且独立看门狗一旦启动,除了系统复位,它不能再被停止。但这样引发的一个问题是当MCU进入到低功耗模式后由于CPU停止运行无法喂狗,会导致系统频繁复位。那如何解决这个问题呢,难道独立看门狗和低功耗模式没法同时使用?
一个很好的方式是在休眠模式下通过RTC定时唤醒来喂狗,喂完够在进入继续进入到休眠模式。比如看门狗复位的时间间隔为10s。那么在进入休眠模式前设置RTC闹钟中断时间为5s。这样每隔5s唤醒一次喂一次狗。便可以很好的解决这个问题。
while(1) { // 执行任务 Task1(); Task2(); // .. // 喂狗 dev_iwdg_feed(); // 进入待机模式开关 if(m_bEnterStandByMode) { // 使能外部中断,GPIOB3,用以MCU从待机模式唤醒 dev_exti_enable(TRUE); ENTERSTOPMODE: // 设置RTC闹钟,5秒钟产生一次RTC闹钟中断*/ dev_rtc_setAlarm(5); // 进入停止模式(低功耗),直至外部中断触发时被唤醒 PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI); // 是否是RTC闹钟中断唤醒 if(dev_rtc_isAlarm()) { // 喂狗 dev_iwdg_feed(); // 喂完狗继续进入停止模式 goto ENTERSTOPMODE; } // 禁止外部中断 dev_exti_enable(FALSE); // 从停止模式唤醒后恢复系统时钟 dev_clk_restore(); } }
以下是完整的参考代码:
//********************************************************************************************** // STM32F10x StopMode RTC Feed Dog // compiler: Keil UV3 // 2013-01-04 , By friehood //********************************************************************************************** #include "stm32f10x_lib.h" #include "platform_config.h" static Boolean g_bRTCAlarm = FALSE; /******************************************************************************* * Function Name : RCC_Configuration * Description : Configures the different system clocks. * Input : None * Output : None * Return : None *******************************************************************************/ void RCC_Configuration(void) { /* RCC system reset(for debug purpose) */ RCC_DeInit(); /* Enable HSE */ RCC_HSEConfig(RCC_HSE_ON); /* Wait till HSE is ready */ if(RCC_WaitForHSEStartUp() == SUCCESS) { /* Enable Prefetch Buffer */ FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //FLASH时序控制 //推荐值:SYSCLK = 0~24MHz Latency=0 // SYSCLK = 24~48MHz Latency=1 // SYSCLK = 48~72MHz Latency=2 //FLASH_SetLatency(FLASH_Latency_1); //警告:修改为1会对DMA值有影响(如ADC采集值会错位) FLASH_SetLatency(FLASH_Latency_2); /* HCLK = SYSCLK */ RCC_HCLKConfig(RCC_SYSCLK_Div1); /* PCLK2 = HCLK */ RCC_PCLK2Config(RCC_HCLK_Div1); /* PCLK1 = HCLK/2 */ RCC_PCLK1Config(RCC_HCLK_Div2); /* PLLCLK = 12MHz * 3 = 36 MHz */ RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_3); /* Enable PLL */ RCC_PLLCmd(ENABLE); /* Wait till PLL is ready */ while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) { } /* Select PLL as system clock source */ RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); /* Wait till PLL is used as system clock source */ while(RCC_GetSYSCLKSource() != 0x08) { } } /* Enable PWR and BKP clock */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); /* Enable AFIO clock */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); } /******************************************************************************* * Function Name : NVIC_Configuration * Description : Configures the nested vectored interrupt controller. * Input : None * Output : None * Return : None *******************************************************************************/ void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; #ifdef VECT_TAB_RAM /* Set the Vector Table base location at 0x20000000 */ NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); #else /* VECT_TAB_FLASH */ /* Set the Vector Table base location at 0x08000000 */ NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0); #endif /* Configure one bit for preemption priority */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); } /******************************************************************************* * Function Name : SysTick_Configuration * Description : Configures the SysTick to generate an interrupt each 1 millisecond. * Input : None * Output : None * Return : None *******************************************************************************/ void SysTick_Configuration(void) { /* Select AHB clock(HCLK) as SysTick clock source */ SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK); /* Set SysTick Priority to 3 */ NVIC_SystemHandlerPriorityConfig(SystemHandler_SysTick, 3, 0); /* SysTick interrupt each 1ms with HCLK equal to 72MHz */ SysTick_SetReload(72000); /* Enable the SysTick Interrupt */ SysTick_ITConfig(ENABLE); } /******************************************************************************* * Function Name : Delay * Description : Inserts a delay time. * Input : nTime: specifies the delay time length, in milliseconds. * Output : None * Return : None *******************************************************************************/ void Delay(u32 nTime) { /* Enable the SysTick Counter */ SysTick_CounterCmd(SysTick_Counter_Enable); TimingDelay = nTime; while(TimingDelay != 0); /* Disable the SysTick Counter */ SysTick_CounterCmd(SysTick_Counter_Disable); /* Clear the SysTick Counter */ SysTick_CounterCmd(SysTick_Counter_Clear); } /******************************************************************************* * Function Name : RTC_Configuration * Description : Configures RTC clock source and prescaler. * Input : None * Output : None * Return : None *******************************************************************************/ void RTC_Configuration(void) { EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; /* RTC clock source configuration ------------------------------------------*/ /* Allow access to BKP Domain */ PWR_BackupAccessCmd(ENABLE); /* Reset Backup Domain */ BKP_DeInit(); /* Enable the LSI OSC */ RCC_LSICmd(ENABLE); /* Wait till LSI is ready */ while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET){} /* Select the RTC Clock Source */ RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); /* Enable the RTC Clock */ RCC_RTCCLKCmd(ENABLE); /* RTC configuration -------------------------------------------------------*/ /* Wait for RTC APB registers synchronisation */ RTC_WaitForSynchro(); /* Set RTC prescaler: set RTC period to 1sec */ RTC_SetPrescaler(40000); /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); /* Enable the RTC Alarm interrupt */ RTC_ITConfig(RTC_IT_ALR, ENABLE); /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); /* Configure EXTI Line17(RTC Alarm) to generate an interrupt on rising edge */ EXTI_ClearITPendingBit(EXTI_Line17); EXTI_InitStructure.EXTI_Line = EXTI_Line17; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); /* Enable the RTC Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = RTCAlarm_IRQChannel; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } /******************************************************************************* * Function Name : RTCAlarm_IRQHandler * Description : This function handles RTC Alarm interrupt request. * Input : None * Output : None * Return : None *******************************************************************************/ void RTCAlarm_IRQHandler(void) { if(RTC_GetITStatus(RTC_IT_ALR) != RESET) { /* Set the RTC alarm flag */ g_bRTCAlarm = TRUE; /* Clear EXTI line17 pending bit */ EXTI_ClearITPendingBit(EXTI_Line17); /* Check if the Wake-Up flag is set */ if(PWR_GetFlagStatus(PWR_FLAG_WU) != RESET) { /* Clear Wake Up flag */ PWR_ClearFlag(PWR_FLAG_WU); } /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); /* Clear RTC Alarm interrupt pending bit */ RTC_ClearITPendingBit(RTC_IT_ALR); /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); } } /******************************************************************************* * Function Name : dev_rtc_setAlarm * Description : 设置RTC闹钟. * Input : 闹钟时间 * Output : None * Return : None *******************************************************************************/ void dev_rtc_setAlarm(u32 AlarmValue) { /* Clear the RTC SEC flag */ RTC_ClearFlag(RTC_FLAG_SEC); /* Wait clear RTC flag sccess */ while(RTC_GetFlagStatus(RTC_FLAG_SEC) == RESET); /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); /* Sets the RTC alarm value */ RTC_SetAlarm(RTC_GetCounter() + AlarmValue); /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); } /******************************************************************************* * Function Name : dev_rtc_isAlarm * Description : RTC闹钟是否触发 * Input : None * Output : None * Return : TRUE:已触发,FALSE,未触发 *******************************************************************************/ Boolean dev_rtc_isAlarm(void) { if(g_bRTCAlarm) { /* Clear the RTC alarm flag */ g_bRTCAlarm = FALSE; return TRUE; } return FALSE; } void dev_iwdg_init(void) { /* Enable write access to IWDG_PR and IWDG_RLR registers */ IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); /* IWDG counter clock: 40KHz(LSI) / 256 = 0.15625 KHz */ IWDG_SetPrescaler(IWDG_Prescaler_256); /* Set counter reload value to 1562 */ IWDG_SetReload(1562); // 10s /* Reload IWDG counter */ IWDG_ReloadCounter(); /* Enable IWDG (the LSI oscillator will be enabled by hardware) */ IWDG_Enable(); } void dev_iwdg_feed(void) { IWDG_ReloadCounter(); } /******************************************************************************* * Function Name : dev_clk_restore * Description : Restore system clock after wake-up from STOP: enable HSE, PLL * and select PLL as system clock source. * Input : None * Output : None * Return : None *******************************************************************************/ void dev_clk_restore(void) { /* Enable HSE */ RCC_HSEConfig(RCC_HSE_ON); /* Wait till HSE is ready */ HSEStartUpStatus = RCC_WaitForHSEStartUp(); if(HSEStartUpStatus == SUCCESS) { /* Enable PLL */ RCC_PLLCmd(ENABLE); /* Wait till PLL is ready */ while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) { } /* Select PLL as system clock source */ RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); /* Wait till PLL is used as system clock source */ while(RCC_GetSYSCLKSource() != 0x08) { } } } /******************************************************************************* * Function Name : EXTI_Configuration * Description : Configures EXTI Line3. * Input : None * Output : None * Return : None *******************************************************************************/ void EXIT_Configuration(void) { EXTI_InitTypeDef EXTI_InitStructure; GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource3); EXTI_ClearITPendingBit(EXTI_Line3); EXTI_InitStructure.EXTI_Line = EXTI_Line3; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); } void dev_exti_enable(Boolean bEnable) { NVIC_InitTypeDef NVIC_InitStructure; /* Clear the Key Button EXTI line pending bit */ EXTI_ClearITPendingBit(EXTI_Line3); NVIC_ClearIRQChannelPendingBit(EXTI3_IRQChannel); NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQChannel; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = bEnable ? ENABLE : DISABLE; NVIC_Init(&NVIC_InitStructure); } /******************************************************************************* * Function Name : main * Description : Main program. * Input : None * Output : None * Return : None *******************************************************************************/ int main(void) { /* System Clocks Configuration */ RCC_Configuration(); /* NVIC configuration */ NVIC_Configuration(); /* Configure RTC clock source and prescaler */ RTC_Configuration(); /* Configure the SysTick to generate an interrupt each 1 millisecond */ SysTick_Configuration(); /* Configures EXTI Line3 */ EXIT_Configuration(); /* IWDG initialize*/ dev_iwdg_init(); while(1) { // 执行任务 Task1(); Task2(); // .. // 喂狗 dev_iwdg_feed(); // 进入待机模式开关 if(m_bEnterStandByMode) { // 使能外部中断,GPIOB3,用以MCU从待机模式唤醒 dev_exti_enable(TRUE); ENTERSTOPMODE: // 设置RTC闹钟,5秒钟产生一次RTC闹钟中断*/ dev_rtc_setAlarm(5); // 进入停止模式(低功耗),直至外部中断触发时被唤醒 PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI); // 是否是RTC闹钟中断唤醒 if(dev_rtc_isAlarm()) { // 喂狗 dev_iwdg_feed(); // 喂完狗继续进入停止模式 goto ENTERSTOPMODE; } // 禁止外部中断 dev_exti_enable(FALSE); // 从停止模式唤醒后恢复系统时钟 dev_clk_restore(); } } }