The STM32F4xx are able to handle external or internal events in order to wake up the core (WFE). The wakeup event can be generated either by:
唤醒事件管理 STM32 可以处理外部或内部事件来唤醒内核(WFE)。唤醒事件可以通过下述配置产生: 在外设的控制寄存器使能一个中断,但不在NVIC中使能,同时在Cortex-M3的系统控制寄存器中使能SEVONPEND位。 配置一个外部或内部EXTI线为事件模式,当CPU从WFE恢复后,因为对应事件线的挂起位没有被置位, SEVONPEND位,这个位是什么作用呢?权威指南这么说: 发生异常悬起时请发送事件,用于在一个新的中断悬起时从 WFE 指令处唤醒。
在以下条件下执行WFI(等待中断)或WFE(等待事件)指令: – 设置Cortex-M3系统控制寄存器中的SLEEPDEEP位 注:为了进入停止模式,所有的外部中断的请求位(挂起寄存器(EXTI_PR)) 如果执行WFI进入停止模式: 设置任一外部中断线为中断模式(在NVIC中必须使能相应的外部中断向量)。 如果执行WFE进入停止模式: 设置任一外部中断线为事件模式。 //进入停止模式 { u32 tmpreg tmpreg = PWR->CR; tmpreg &= ~(1<<1); //清除PWR_CR的PDDS tmpreg |=(1<<0); //设置PWR_CR的 LPDS PWR->CR = tmpreg; SCB->SCR|=1<<2; //使能SLEEPDEEP位 (SYS->CTRL) __WFI(); //执行WFI指令 }
//进入待机模式 void Sys_Standby(void) { SCB->SCR|=1<<2; // 使能SLEEPDEEP位 (SYS->CTRL) RCC->APB1ENR|=1<<28; // 使能电源时钟 PWR->CSR|=1<<8; // 设置WKUP用于唤醒 PWR->CR|=1<<2; // 清除Wake-up 标志(WUF位) PWR->CR|=1<<1; // PDDS置位 __WFI(); // 执行WFI指令 }
So it appears the main purpose is to enable wakeups without generating an interrupt It's not mentioned in that guide and I'm not sure how applicable is to the STM32 architecture For example you may have an application where it's important to capture that a sub-microsecond event has occurred, 4.3.3 of the reference manual says: Has anybody managed to set WFE and exit it using "an interrupt enabled in the peripheral control register but not in the NVIC" ? I haven't tried it myself, but it looks like there are two way to do it. The ST reference manual seems to be referring the method 1 below: 1) Using the Event mask register. Beside from enabling the interrupt in the peripheral control, you also need to set the Event Mask Register (EXTI_EMR) (section 8.3.2) to enable the peripheral to trigger the event signal. And base on the circuit diagram in figure 16 (section 8.2.2) you also need to make sure you set the interrupt source as rising edge trigger or falling edge trigger. (so in total you must set up as least two registers on top of the peripheral's interrupt enable). This control bit is in bit[4] of NVIC System Control Register (0xE000ED10) is SEVONPEND control for NVIC. When a new interrupt pending occurred, this should be able to generate a internal event in the NVIC to wake up the core To use the event mask resister, I think the event needs to be one of those linked to an exti line. However, setting the SEVONPEND bit works perfectly :) If anyone's looking, theres a firmware library function to do it: NVIC_SystemLPConfig(NVIC_LP_SEVONPEND, ENABLE);
I have noticed that my __WFE(); sometimes returns immdiately. This is described in D.7.68 of the ARMv7M Architecture Application Level Reference Manual: If the Event Register is set, Wait For Event clears it and returns immediately. By calling WFE twice in succession: If the Event Register is Set, the first WFE will clear it, and the second will sleep. The arm "Application Level" manual suggests calling ClearEventRegister(), Use the following assembly sequence: SEV ; Send Event , this set the event register to 1 Excellent, thanks again Joseph! before calling WFE make sure that you clear the pending bit in the nvic I forgot to mention a corner case: If you use SEV - WFE - WFE sequence, and if there is a interrupt take place int loopexit = 0; while ( loopexit == 0 ) { __WFE( ); // Try to sleep if ( Check_If_Work_Need_To_Be_Done( ) ) { Do_Some_Work( ); } loopexit = Check_If_Want_To_Exit_Sleep( ); } In this way it doesn't matter what is the current value of the event register,
Here is an example of using WFE with EXTI. Hope that helps you. int main( void ) { /*!< At this stage the microcontroller clock setting is already configured, this is done through SystemInit() function which is called from startup file (startup_stm32f37x.s) before to branch to application main. To reconfigure the default setting of SystemInit() function, refer to system_stm32f37x.c file */ /* Configure PA0 in interrupt mode */ EXTI0_Config( ); /* Configure LEDs */ STM_EVAL_LEDInit( LED1 ); STM_EVAL_LEDInit( LED2 ); STM_EVAL_LEDInit( LED4 ); /* SysTick interrupt each 10 ms */ if ( SysTick_Config( SystemCoreClock / 100 ) ) { /* Capture error */ while ( 1 ); } /* LED1 On */ STM_EVAL_LEDOn( LED1 ); /* Request to enter STOP mode with regulator in low power mode */ PWR_EnterSTOPMode( PWR_Regulator_LowPower, PWR_STOPEntry_WFE ); /* LED1 On */ STM_EVAL_LEDOn( LED2 ); while ( 1 ) { } } /** * @brief Configure PA0 in interrupt mode * @param None * @retval None */ static void EXTI0_Config( void ) { EXTI_InitTypeDef EXTI_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; /* Enable GPIOA clock */ RCC_AHBPeriphClockCmd( RCC_AHBPeriph_GPIOA, ENABLE ); /* Configure PA0 pin as input floating */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_Init( GPIOA, &GPIO_InitStructure ); /* Enable SYSCFG clock */ RCC_APB2PeriphClockCmd( RCC_APB2Periph_SYSCFG, ENABLE ); /* Connect EXTI0 Line to PA0 pin */ SYSCFG_EXTILineConfig( EXTI_PortSourceGPIOA, EXTI_PinSource0 ); /* Configure EXTI0 line */ EXTI_InitStructure.EXTI_Line = EXTI_Line0; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init( &EXTI_InitStructure ); }
/* init the TIM7 */ RCC->APB1ENR |= RCC_APB1ENR_TIM7EN; TIM7->PSC = 7; // => 1 MHz timer clock frequency (at PCLK=8 MHz) TIM7->ARR = 1000 - 1; // => 1 kHz interrupt rate TIM7->DIER = TIM_DIER_UIE; // enable the update interrupt NVIC_EnableIRQ( TIM7_IRQn ); TIM7->CR1 |= TIM_CR1_CEN; // start the timer RCC->APB1ENR |= RCC_APB1Periph_PWR; /* Configure PA0 pin as input floating */ RCC->AHBENR |= RCC_AHBPeriph_GPIOA; RCC->APB2ENR |= RCC_APB2Periph_SYSCFG;
Seeing the following explanation for WFE command from ARM,
If the event register is 0,
If the event register is 1, That is why the second WFE works fine.
We have 2 instructions for entering low-power standby state where most clocks are gated: WFI and WFE. |
|
来自: goodwangLib > 《STM32F4》