NuMaker tickless example

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);
+    }
+}