We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
System:
- Keil MDK
- Apollo3 MCU
- Keil RTX
- Arm compiler 5.06 Update 6 (build 750)
Problem:Inside keil rtx function: os_idle_demon we suspend rtos and then enter deepsleep mode
But, it looks like calling rt_suspend inside os_idle_demon results in 90 uA current draw, preventing deepsleep mode current savings (we should only be seeing around 10uA current)
Function:
/// \brief The idle demon that runs when no other thread is ready to run void os_idle_demon (void) { for (;;) { _mcu_manage_idle(); } } static inline void _mcu_manage_idle(void) { extern const uint32_t k_stimer_ticks_per_rtos_tick; // defined in RTX_Conf_CM.c uint32_t rtos_ticks_to_sleep; uint32_t rt_suspend(void); // defined in RTX, avoid using os_suspend because it sets event register // We need to do this BASEPRI+WFE because // // a) If we call os_suspend(), and an interrupt that triggers thread work // occurs, there might be a chance that we mistakenly enter deep sleep // even though there is work to do. // // b) We can't call os_suspend() with IRQs disabled, because os_suspend() // makes an SVC call and will HardFault if IRQs are disabled. // // c) We can call rt_suspend() (which is what os_suspend() calls internally) // but we have to set BASEPRI to ensure that SysTick/PendSV does not // inadvertently run and corrupt RTOS state while rt_suspend() modifies it. // // d) This leaves us with the issue of an IRQ being triggered while this is // all occurring. To solve this, we use the WFE instruction instead of the // WFI instruction. This will ensure that if an IRQ is triggered, the next // WFE instruction will simply clear the event register without putting // the CPU to sleep, because any IRQ sets the event register when it is // triggered. __set_BASEPRI(0xFF); // prevent PendSV from being handled rtos_ticks_to_sleep = rt_suspend(); __set_BASEPRI(0); // _enable_irq() ? if (0 == rtos_ticks_to_sleep) { goto do_resume; } { // bracket avoids "bypassed initialization" warnings from goto do_resume // Configure STIMER B/1 to wake us up // Don't use STIMER A/0 because RTX is already using that IRQ handler const uint32_t stimer_sleep_start = am_hal_stimer_counter_get(); const uint32_t stimer_sleep_max = 0x80000000u / k_stimer_ticks_per_rtos_tick * k_stimer_ticks_per_rtos_tick; // round down to whole RTOS tick const uint64_t stimer_sleep_ticks64 = (uint64_t)k_stimer_ticks_per_rtos_tick * (uint64_t)rtos_ticks_to_sleep; const uint32_t stimer_sleep_ticks = stimer_sleep_ticks64 < stimer_sleep_max ? stimer_sleep_ticks64 : stimer_sleep_max; am_hal_stimer_int_enable(AM_HAL_STIMER_INT_COMPAREB); am_hal_stimer_int_clear(AM_HAL_STIMER_INT_COMPAREB); am_hal_stimer_compare_delta_set(1, stimer_sleep_ticks); NVIC_SetPriority(STIMER_CMPR1_IRQn, 0xFF); NVIC_EnableIRQ(STIMER_CMPR1_IRQn); /////////////////// ENTER SLEEP disable_some_peripherals(); // i2c, spi ..etc SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; __WFE(); enable_some_peripherals(); /////////////////// EXIT SLEEP // Figure out how long we stopped. const uint32_t stimer_sleep_end = am_hal_stimer_counter_get(); rtos_ticks_to_sleep = ((stimer_sleep_end - stimer_sleep_start) + (k_stimer_ticks_per_rtos_tick / 2)) / k_stimer_ticks_per_rtos_tick; // Disable STIMER B/1 now that we're done with it. NVIC_DisableIRQ(STIMER_CMPR1_IRQn); am_hal_stimer_int_disable(AM_HAL_STIMER_INT_COMPAREB); } // avoid warnings from goto do_resume do_resume: os_resume(rtos_ticks_to_sleep); // start scheduler back up and inform how many ticks surpassed. __SEV(); __WFE(); // clear CPU event flag }
Why is rt_suspend causing such (relatively) high current draw and how can we fix this?