Nuvoton
/
NuMaker-mbed-tickless-example
NuMaker tickless example
Diff: main.cpp
- Revision:
- 1:eb1da9d36e12
- Parent:
- 0:d7c3d9c9dbe4
- Child:
- 2:dbe00b3a7545
--- a/main.cpp Mon Sep 11 08:38:00 2017 +0000 +++ b/main.cpp Tue Sep 12 16:16:43 2017 +0800 @@ -1,8 +1,187 @@ -#include "mbed.h" - -int main() { - // TODO - - return 0; -} - +#include "mbed.h" +#include <limits.h> +#include <vector> + +#include "wakeup.h" + +static void flush_stdio_uart_fifo(void); +static void check_wakeup_source(uint32_t, bool deepsleep); +static void idle_daemon(void); + +#if (MBED_MAJOR_VERSION <= 5 && MBED_MINOR_VERSION <= 5 && MBED_PATCH_VERSION <= 6) +EventFlags_ wakeup_eventflags; +#else +EventFlags wakeup_eventflags; +#endif + +int main() { + + config_pwrctl(); + config_button_wakeup(); + config_wdt_wakeup(); + config_rtc_wakeup(); + /* TODO */ + //config_uart_wakeup(); + //config_i2c_wakeup(); + + /* Register idle handler which supports tickless */ + Thread::attach_idle_hook(idle_daemon); + + while (true) { + + printf("I am going to shallow/deep sleep\n"); + /* Flush STDIO UART before entering Idle/Power-down mode */ + fflush(stdout); + flush_stdio_uart_fifo(); + + /* Wait for any wake-up event */ + uint32_t flags = wakeup_eventflags.wait_any(EventFlag_Wakeup_All, osWaitForever, true); + if (flags & osFlagsError) { + if (flags != osFlagsErrorTimeout) { + printf("OS error code: 0x%08lX\n", flags); + } + continue; + } + + /* EventFlag_Wakeup_UnID is set in PWRWU_IRQHandler to indicate unidentified wake-up from power-down. + * It could be deferred. Wait for it. */ + uint32_t flags2 = wakeup_eventflags.wait_any(EventFlag_Wakeup_UnID, 100, true); + if (flags2 & osFlagsError) { + if (flags2 != osFlagsErrorTimeout) { + printf("OS error code: 0x%08lX\n", flags2); + } + else { + flags2 = 0; + } + } + flags |= flags2; + + /* EventFlag_Wakeup_UnID means unidentified wake-up source from power-down. + * + * If EventFlag_Wakeup_UnID is set, we wake up from power-down mode (deep sleep) but its wake-up + * source is not yet identified. Wait for it to be identified if no other wake-up sources have been + * identified. + * + * If EventFlag_Wakeup_UnID is not set, we wake up from idle mode (shallow sleep). Wake-up source + * would have been identified. + */ + if (flags == EventFlag_Wakeup_UnID) { + uint32_t flags3 = wakeup_eventflags.wait_any(EventFlag_Wakeup_All & ~EventFlag_Wakeup_UnID, 100, true); + if (flags3 & osFlagsError) { + if (flags3 != osFlagsErrorTimeout) { + printf("OS error code: 0x%08lX\n", flags3); + } + else { + flags3 = 0; + } + } + flags |= flags3; + } + + /* Clear wake-up flags caused by short time EventFlags::wait_any() above. These wake-up flags are + * just for program control and not actual wake-up events we want to track. This has a side effect of + * losing actual wake-up events in this short time. */ + wakeup_eventflags.clear(); + + bool deepsleep = (flags & EventFlag_Wakeup_UnID) ? true : false; + + /* Remove EventFlag_Wakeup_UnID if any other wake-up source is identified */ + if ((flags & ~EventFlag_Wakeup_UnID)) { + flags &= ~EventFlag_Wakeup_UnID; + } + check_wakeup_source(flags, deepsleep); + + printf("\n"); + } + + return 0; +} + +void flush_stdio_uart_fifo(void) +{ + UART_T *uart_base = (UART_T *) NU_MODBASE(STDIO_UART); + + while (! UART_IS_TX_EMPTY(uart_base)); +} + +void check_wakeup_source(uint32_t flags, bool deepsleep) +{ + typedef std::pair<uint32_t, const char *> WakeupName; + + static const WakeupName wakeup_name_arr[] = { + WakeupName(EventFlag_Wakeup_Button1, "Button1"), + WakeupName(EventFlag_Wakeup_Button2, "Button2"), + WakeupName(EventFlag_Wakeup_LPTicker, "lp_ticker"), + WakeupName(EventFlag_Wakeup_WDT_Timeout, "WDT timeout"), + WakeupName(EventFlag_Wakeup_RTC_Alarm, "RTC alarm"), + WakeupName(EventFlag_Wakeup_UART_CTS, "UART CTS"), + WakeupName(EventFlag_Wakeup_I2C_AddrMatch, "I2C address match"), + + WakeupName(EventFlag_Wakeup_UnID, "Unidentified"), + }; + + const char *sleep_mode = deepsleep ? "deep sleep" : "shallow sleep"; + + if (flags) { + const WakeupName *wakeup_name_pos = wakeup_name_arr; + const WakeupName *wakeup_name_end = wakeup_name_arr + (sizeof (wakeup_name_arr) / sizeof (wakeup_name_arr[0])); + + for (; wakeup_name_pos != wakeup_name_end; wakeup_name_pos ++) { + if (flags & wakeup_name_pos->first) { + printf("Wake up by %s from %s\n", wakeup_name_pos->second, sleep_mode); + } + } + } +} + +#define US_PER_SEC (1000 * 1000) +#define US_PER_OS_TICK (US_PER_SEC / OS_TICK_FREQ) + +void dummy_cb(void) {} + +void idle_daemon(void) { + + const int max_us_sleep = (INT_MAX / OS_TICK_FREQ) * OS_TICK_FREQ; + /* Keep track of the time asleep */ + LowPowerTimer asleep_watch; + /* Will wake up the system (by lp_ticker) if no other wake-up event */ + LowPowerTimeout alarm_clock; + + /* Never ends. The rtos will suspend this thread when there is something to do + * either before osKernelSuspend actually suspend the system (and is not in svc) + * or immediately after osKernelResume. */ + while (true) { + /* Suspend the system */ + uint32_t ticks_to_sleep = osKernelSuspend(); + uint32_t elapsed_ticks = 0; + + if (ticks_to_sleep) { + uint64_t us_to_sleep = ticks_to_sleep * US_PER_OS_TICK; + + if (us_to_sleep > max_us_sleep) { + us_to_sleep = max_us_sleep; + } + + /* Start the asleep_watch and setup the alarm_clock to wake up the system in us_to_sleep */ + asleep_watch.start(); + alarm_clock.attach_us(dummy_cb, us_to_sleep); + + /* Go to deep sleep */ + deepsleep(); + + /* Woken up by lp_ticker or other wake-up event */ + int us_asleep = asleep_watch.read_us(); + + /* Clean up asleep_watch and alarm_clock */ + asleep_watch.stop(); + asleep_watch.reset(); + alarm_clock.detach(); + + /* Translate us_asleep into ticks */ + elapsed_ticks = us_asleep / US_PER_OS_TICK; + } + + /* Resume the system */ + osKernelResume(elapsed_ticks); + } +}