cancel
Showing results for 
Search instead for 
Did you mean: 

Delay function using __WFI()

aa_talaat
Associate II
Posted on April 26, 2015 at 17:04

Hello,

I have seen and implemented timer based delay function either based on TIM or systick, and they rely on a variable being inc/dec by the ISR till it satisfies a certain condition inside the Delay loop which allow it to return to the caller.

My concern with this technique is that it keeps the CPU running in an infinite loop till the expiry of the delay value which is consuming more power.

I thought of having the delay function configure the timer to generate an interrupt update once the duration specified by the user is achieved, and then go into sleep mode using __WFI().

My concern with this technique it that is not reliable because while the CPU is in Sleep waiting for the TIM interrupt to happen another interrupt would happen, then the delay call is returned to the called without waiting for the right time.

I thought of disabling all interrupts except for the delay timer before calling the __WFI() within the delay function, and then restoring back all interrupts state, but this is not good because it will not allow any other interrupt to be serviced till the delay expires.

In that case, the high power version based on infinite loop would be better despite its power consumption.

I read about Sleep on Exit. Not sure how to set it using the peripheral library but it will not help because I do a lot of activities in the main loop.

Read a lot about difference between WFI and WFE but I gave up to understand  but I am not sure that using WFE will not help in my case.

Is there a best of both worlds in generating delays?

Thank you.
1 REPLY 1
aa_talaat
Associate II
Posted on April 26, 2015 at 19:41

I think I may have found a solution based on a post in the same forum.

To resolve the issue of CPU waking up due to a different interrupt, the __WFI() call within the delay function can be enclosed in a while loop that tests a bool variable being set by the timer ISR. So, in case while in sleep mode, the CPU is woke up by a different ISR, this ISR will not be setting this bool variable. The CPU will execute the ISR, and come back to the while loop of the delay function. The while loop will still be true and the __WFI() will be called again. Here is the delay function:

/* This function will configure TIM7 to count till user provided milli seconds, then generates an interrupt.
* Timer is configured, then CPU put to sleep. CPU will wakeup upon timer interrupt generation.
*/
void Delay(uint16_t msCount){
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
/* clear any pending interrupt before enabling */
TIM_ClearITPendingBit(TIM7, TIM_IT_Update);
// Configure Timer period Interrupt duration
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; // Input Clock is 8Mhz
TIM_TimeBaseInitStruct.TIM_Prescaler = 8000-1; // 8Mhz/8000 = 1KHz = 1 ms
TIM_TimeBaseInitStruct.TIM_Period = msCount; // 1ms x msCount
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM7, &TIM_TimeBaseInitStruct);
/* clear any pending interrupt before enabling */
TIM_ClearITPendingBit(TIM7, TIM_IT_Update);
TIM_SetCounter(TIM7,0); // Clear Timer counter
endOfDelay = 0; // Rest the flag we are going to test on before enabling the interrupt
TIM_ITConfig(TIM7, TIM_IT_Update, ENABLE);
while (endOfDelay == 0){
__WFI(); // Put CPU to sleep. Wake up upon timer expire interrupt.
// If another interrupt wakes the CPU, the CPU will execute
// this interrupt's ISR and them come back to this loop and go to sleep
// again till the delay really expires
}

and here is the corresponding ISR

/*
* Triggered after user specified number of milliseconds to wake up the CPU from WFI as a user defined delay in the main loop
* Notify main loop by posting a delay end message.
*/
void TIM7_IRQHandler(void)
{
if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM7, TIM_IT_Update);
// Disable the timer Interrupt itself
TIM_ITConfig(TIM7, TIM_IT_Update, DISABLE); // Disable timer so that not be triggered again unless by explicitly by user
endOfDelay = 1; // The delay function will know that time to go back to the caller
}
}