Nuvoton / Mbed OS NuMaker-mbed-tickless-example

Files at this revision

API Documentation at this revision

Comitter:
ccli8
Date:
Tue Sep 12 16:16:43 2017 +0800
Parent:
0:d7c3d9c9dbe4
Child:
2:dbe00b3a7545
Commit message:
Support NUC472/M453/M487

Changed in this revision

EventFlags_.cpp Show annotated file Show diff for this revision Revisions of this file
EventFlags_.h Show annotated file Show diff for this revision Revisions of this file
README.md Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed_app.json Show annotated file Show diff for this revision Revisions of this file
wakeup.h Show annotated file Show diff for this revision Revisions of this file
wakeup_button.cpp Show annotated file Show diff for this revision Revisions of this file
wakeup_i2c.cpp Show annotated file Show diff for this revision Revisions of this file
wakeup_pwrctl.cpp Show annotated file Show diff for this revision Revisions of this file
wakeup_rtc.cpp Show annotated file Show diff for this revision Revisions of this file
wakeup_uart.cpp Show annotated file Show diff for this revision Revisions of this file
wakeup_wdt.cpp Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/EventFlags_.cpp	Tue Sep 12 16:16:43 2017 +0800
@@ -0,0 +1,95 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2017 ARM Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "mbed.h"
+/* EventFlags is supported from mbed-os-5.5.6. Before then, we need EventFlags_. */
+#if (MBED_MAJOR_VERSION <= 5 && MBED_MINOR_VERSION <= 5 && MBED_PATCH_VERSION <= 6)
+
+#include "EventFlags_.h"
+#include <string.h>
+#include "mbed_error.h"
+#include "mbed_assert.h"
+
+namespace rtos {
+
+EventFlags_::EventFlags_()
+{
+    constructor();
+}
+
+EventFlags_::EventFlags_(const char *name)
+{
+    constructor(name);
+}
+
+void EventFlags_::constructor(const char *name)
+{
+    memset(&_obj_mem, 0, sizeof(_obj_mem));
+    memset(&_attr, 0, sizeof(_attr));
+    _attr.name = name ? name : "application_unnamed_event_flags";
+    _attr.cb_mem = &_obj_mem;
+    _attr.cb_size = sizeof(_obj_mem);
+    _id = osEventFlagsNew(&_attr);
+    MBED_ASSERT(_id);
+}
+
+uint32_t EventFlags_::set(uint32_t flags)
+{
+    return osEventFlagsSet(_id, flags);
+}
+
+uint32_t EventFlags_::clear(uint32_t flags)
+{
+    return osEventFlagsClear(_id, flags);
+}
+
+uint32_t EventFlags_::get() const
+{
+    return osEventFlagsGet(_id);
+}
+
+uint32_t EventFlags_::wait_all(uint32_t flags, uint32_t timeout, bool clear)
+{
+    return wait(flags, osFlagsWaitAll, timeout, clear);
+}
+
+uint32_t EventFlags_::wait_any(uint32_t flags, uint32_t timeout, bool clear)
+{
+    return wait(flags, osFlagsWaitAny, timeout, clear);
+}
+
+EventFlags_::~EventFlags_()
+{
+    osEventFlagsDelete(_id);
+}
+
+uint32_t EventFlags_::wait(uint32_t flags, uint32_t opt, uint32_t timeout, bool clear)
+{
+    if (clear == false) {
+        opt |= osFlagsNoClear;
+    }
+
+    return osEventFlagsWait(_id, flags, opt, timeout);
+}
+
+}
+
+#endif  // mbed-os-5.5.6
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/EventFlags_.h	Tue Sep 12 16:16:43 2017 +0800
@@ -0,0 +1,103 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2017 ARM Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef EVENT_FLAG_H
+#define EVENT_FLAG_H
+
+#include <stdint.h>
+#include "mbed.h"
+/* EventFlags is supported from mbed-os-5.5.6. Before then, we need EventFlags_. */
+#if (MBED_MAJOR_VERSION <= 5 && MBED_MINOR_VERSION <= 5 && MBED_PATCH_VERSION <= 6)
+    
+#include "platform/NonCopyable.h"
+
+namespace rtos {
+/** \addtogroup rtos */
+/** @{*/
+
+/** The EventFlags_ class is used to signal or wait for an arbitrary event or events.
+ @note 
+ EventFlags_ support 31 flags so the MSB flag is ignored, it is used to return an error code (@a osFlagsError)
+ @note
+ Memory considerations: The EventFlags_ control structures will be created on current thread's stack, both for the mbed OS
+ and underlying RTOS objects (static or dynamic RTOS memory pools are not being used).
+*/
+class EventFlags_ : private mbed::NonCopyable<EventFlags_> {
+public:
+    /** Create and Initialize a EventFlags_ object */
+    EventFlags_();
+
+    /** Create and Initialize a EventFlags_ object
+
+     @param name name to be used for this EventFlags_. It has to stay allocated for the lifetime of the thread.
+    */
+    EventFlags_(const char *name);
+
+    /** Set the specified Event Flags.
+      @param   flags  specifies the flags that shall be set.
+      @return  event flags after setting or error code if highest bit set (@a osFlagsError).
+     */
+    uint32_t set(uint32_t flags);
+
+    /** Clear the specified Event Flags.
+      @param   flags  specifies the flags that shall be cleared. (default: 0x7fffffff - all flags)
+      @return  event flags before clearing or error code if highest bit set (@a osFlagsError).
+     */
+    uint32_t clear(uint32_t flags = 0x7fffffff);
+
+    /** Get the currently set Event Flags.
+      @return  set event flags.
+     */
+    uint32_t get() const;
+
+    /** Wait for all of the specified event flags to become signaled.
+      @param   flags    specifies the flags to wait for.
+      @param   timeout  timeout value or 0 in case of no time-out. (default: osWaitForever)
+      @param   clear    specifies wether to clear the flags after waiting for them. (default: true)
+      @return  event flags before clearing or error code if highest bit set (@a osFlagsError).
+     */
+    uint32_t wait_all(uint32_t flags = 0, uint32_t timeout = osWaitForever, bool clear = true);
+
+    /** Wait for any of the specified event flags to become signaled.
+      @param   flags    specifies the flags to wait for. (default: 0)
+      @param   timeout  timeout value or 0 in case of no time-out. (default: osWaitForever)
+      @param   clear    specifies wether to clear the flags after waiting for them. (default: true)
+      @return  event flags before clearing or error code if highest bit set (@a osFlagsError).
+     */
+    uint32_t wait_any(uint32_t flags = 0, uint32_t timeout = osWaitForever, bool clear = true);
+
+    ~EventFlags_();
+
+private:
+    void constructor(const char *name = NULL);
+    uint32_t wait(uint32_t flags, uint32_t opt, uint32_t timeout, bool clear);
+    osEventFlagsId_t                _id;
+    osEventFlagsAttr_t              _attr;
+    mbed_rtos_storage_event_flags_t _obj_mem;
+};
+
+}
+
+#endif  // mbed-os-5.5.6
+
+#endif
+
+/** @}*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.md	Tue Sep 12 16:16:43 2017 +0800
@@ -0,0 +1,71 @@
+# Example for tickless support on Nuvoton targets
+
+This is an example to demonstrate [tickless](https://en.wikipedia.org/wiki/Tickless_kernel) support on
+Nuvoton Mbed-enabled boards.
+With tickless support, user program would enter **Idle** mode (shallow sleep) or **Power-down** mode (deep sleep)
+automatically when CPU is idle.
+
+## Supported platforms
+- [NuMaker-PFM-NUC472](https://developer.mbed.org/platforms/Nuvoton-NUC472/)
+- [NuMaker-PFM-M453](https://developer.mbed.org/platforms/Nuvoton-M453/)
+- [NuMaker-PFM-M487](https://developer.mbed.org/platforms/NUMAKER-PFM-M487/)
+
+## Supported wake-up source
+- Button(s)
+- lp_ticker (internal with tickless)
+- WDT timeout
+- RTC alarm
+- UART CTS state change (TODO)
+- I2C address match (TODO)
+
+## Execution
+To run this tickless example, target board must run with no ICE connected.
+With ICE connected, system will enter **Idle** mode (shallow sleep) rather than **Power-down** mode (deep sleep)
+even though `deepsleep()` is called.
+
+### Modes
+There are two modes on Nuvoton Mbed-enabled boards:
+
+#### MASS mode
+This mode supports drap-n-drop programming and VCOM. For retrieving chip information, ICE is connected and system cannot enter
+**Power-down** mode.
+
+#### Debug mode
+This mode supports debugger and VCOM. But without entering debug session for debugging, ICE isn't connected and system can enter
+**Power-down** mode (deep sleep).
+
+So to run this tickless example, user must switch to **MASS** mode first for drag-n-drop programming and 
+then switch to **Debug** mode without entering debug session for running the code.
+
+### Note for terminal output
+With your terminal program configured with 9600/8-N-1, you would see output with:
+<pre>
+I am going to shallow/deep sleep
+Wake up by WDT timeout from <b>deep sleep</b>
+
+I am going to shallow/deep sleep
+Wake up by <b>Unidentified</b> from <b>deep sleep</b>
+
+I am going to shallow/deep sleep
+Wake up by Button2 from <b>deep sleep</b>
+
+I am going to shallow/deep sleep
+Wake up by RTC alarm from <b>deep sleep</b>
+
+I am going to shallow/deep sleep
+Wake up by Button1 from <b>deep sleep</b>
+</pre>
+
+1. If you see `shallow sleep` rather than `deep sleep`, you just enter **Idle** mode (shallow sleep) rather than
+**Power-down** mode (deep sleep). And you need to check your environment.
+1. If you see `Unidentified`, it means chip is waken up from **Power-down** mode but its wake-up source is unidentified.
+Usually it is `lp_ticker` which is internal with the tickless mechanism.
+
+## Known issues
+1. `Ticker`, `Timeout`, and `Timer` will misbehave with this example. These timers rely on 
+internal `us_ticker`, which rely on high-res clocks HXT/HIRC, which are stopped in idle time to save power.
+The issue is to be addressed in next mbed OS version.
+
+1. Interrupt callback and asynchronous transfer in `Serial`, `SPI`, `CAN`, etc. won't work with this example.
+These peripherals rely on high-res clocks HXT/HIRC to operate, which are stopped in idle time to save power.
+Same as above, the issue will be addressed in next mbed OS version.
--- 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);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed_app.json	Tue Sep 12 16:16:43 2017 +0800
@@ -0,0 +1,22 @@
+{
+    "target_overrides": {
+        "NUMAKER_PFM_NUC472": {
+            "target.macros_add": ["OS_IDLE_THREAD_STACK_SIZE=512"],
+            "target.gpio-irq-debounce-enable-list": "SW1, SW2",
+            "target.gpio-irq-debounce-clock-source": "GPIO_DBCTL_DBCLKSRC_IRC10K",
+            "target.gpio-irq-debounce-sample-rate": "GPIO_DBCTL_DBCLKSEL_256"
+        },
+        "NUMAKER_PFM_M453": {
+            "target.macros_add": ["OS_IDLE_THREAD_STACK_SIZE=512"],
+            "target.gpio-irq-debounce-enable-list": "SW2, SW3",
+            "target.gpio-irq-debounce-clock-source": "GPIO_DBCTL_DBCLKSRC_LIRC",
+            "target.gpio-irq-debounce-sample-rate": "GPIO_DBCTL_DBCLKSEL_16"
+        },
+        "NUMAKER_PFM_M487": {
+            "target.macros_add": ["OS_IDLE_THREAD_STACK_SIZE=512"],
+            "target.gpio-irq-debounce-enable-list": "SW2, SW3",
+            "target.gpio-irq-debounce-clock-source": "GPIO_DBCTL_DBCLKSRC_LIRC",
+            "target.gpio-irq-debounce-sample-rate": "GPIO_DBCTL_DBCLKSEL_16"
+        }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wakeup.h	Tue Sep 12 16:16:43 2017 +0800
@@ -0,0 +1,38 @@
+#ifndef __WAKEUP_H__
+#define __WAKEUP_H__
+
+#include <vector>
+#include "mbed.h"
+/* EventFlags is supported from mbed-os-5.5.6. Before then, we need EventFlags_. */
+#if (MBED_MAJOR_VERSION <= 5 && MBED_MINOR_VERSION <= 5 && MBED_PATCH_VERSION <= 6)
+#include "EventFlags_.h"
+#endif
+
+enum EventFlag_Wakeup {
+    EventFlag_Wakeup_Button1        = (1 << 0),
+    EventFlag_Wakeup_Button2        = (1 << 1),
+    EventFlag_Wakeup_LPTicker       = (1 << 2),
+    EventFlag_Wakeup_WDT_Timeout    = (1 << 3),
+    EventFlag_Wakeup_RTC_Alarm      = (1 << 4),
+    EventFlag_Wakeup_UART_CTS       = (1 << 5),
+    EventFlag_Wakeup_I2C_AddrMatch  = (1 << 6),
+    
+    EventFlag_Wakeup_UnID           = (1 << 7),
+    
+    EventFlag_Wakeup_All            = 0xFF,
+};
+
+#if (MBED_MAJOR_VERSION <= 5 && MBED_MINOR_VERSION <= 5 && MBED_PATCH_VERSION <= 6)
+extern EventFlags_ wakeup_eventflags;
+#else
+extern EventFlags wakeup_eventflags;
+#endif
+
+void config_pwrctl(void);
+void config_button_wakeup(void);
+void config_wdt_wakeup(void);
+void config_rtc_wakeup(void);
+void config_uart_wakeup(void);
+void config_i2c_wakeup(void);
+
+#endif  // target-power.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wakeup_button.cpp	Tue Sep 12 16:16:43 2017 +0800
@@ -0,0 +1,40 @@
+#include "mbed.h"
+#include "wakeup.h"
+
+#if defined(TARGET_NUMAKER_PFM_NUC472)
+// SW
+#define BUTTON1     SW1
+#define BUTTON2     SW2
+
+#elif defined(TARGET_NUMAKER_PFM_M453)
+// SW
+#define BUTTON1     SW2
+#define BUTTON2     SW3
+
+#elif defined(TARGET_NUMAKER_PFM_M487)
+// SW
+#define BUTTON1     SW2
+#define BUTTON2     SW3
+
+#endif
+
+static InterruptIn button1(BUTTON1);
+static InterruptIn button2(BUTTON2);
+static void button1_release(void);
+static void button2_release(void);
+
+void config_button_wakeup(void)
+{
+    button1.rise(&button1_release);
+    button2.rise(&button2_release);
+}
+
+void button1_release(void)
+{
+    wakeup_eventflags.set(EventFlag_Wakeup_Button1);
+}
+
+void button2_release(void)
+{
+    wakeup_eventflags.set(EventFlag_Wakeup_Button2);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wakeup_i2c.cpp	Tue Sep 12 16:16:43 2017 +0800
@@ -0,0 +1,101 @@
+#include "mbed.h"
+#include "wakeup.h"
+#include "PeripheralPins.h"
+
+#define I2C_ADDR    (0x90)
+
+#if defined(TARGET_NUMAKER_PFM_NUC472)
+// I2C
+#define I2C_SDA     D14
+#define I2C_SCL     D15
+
+#elif defined(TARGET_NUMAKER_PFM_M453)
+// I2C
+#define I2C_SDA     D14
+#define I2C_SCL     D15
+
+#elif defined(TARGET_NUMAKER_PFM_M487)
+// I2C
+#define I2C_SDA     D9
+#define I2C_SCL     D8
+
+#endif
+
+/* NOTE: Per test (on NUC472/M453/M487), we could handle in time from idle mode (shallow sleep) wake-up, 
+ *       but fail from power-down mode (deep sleep). */
+
+/* Support wake-up by I2C traffic */
+static Semaphore sem_i2c(0, 1);
+
+static void poll_i2c(void);
+/* This handler is to be called in I2C interrupt context (which is extended by Nuvoton's I2C HAL implementation 
+ * on mbed OS) to support wake-up by I2C traffic. */
+extern "C" void nu_i2c_wakeup_handler(I2C_T *i2c_base);
+
+void config_i2c_wakeup(void)
+{
+    /* I2C engine is clocked by external I2C bus clock, so its support for wake-up is irrespective of HXT/HIRC
+     * which are disabled during deep sleep (power-down). */
+    
+    static Thread thread_i2c;
+    
+    Callback<void()> callback(&poll_i2c);
+    thread_i2c.start(callback);
+}
+
+static void poll_i2c(void)
+{
+    static I2CSlave i2c_slave(I2C_SDA, I2C_SCL);
+
+    static char i2c_buf[32];
+    
+    i2c_slave.address(I2C_ADDR);
+    
+    while (true) {
+        int32_t sem_tokens = sem_i2c.wait(osWaitForever);
+        if (sem_tokens < 1) {
+            continue;
+        }
+    
+        bool has_notified_wakeup = false;
+        /* This timer is to check if there is I2C traffic remaining. */
+        Timer timer;
+        timer.start();
+        
+        /* With no I2C traffic for 5 ms, we go back to wait on next I2C traffic. */
+        while (timer.read_high_resolution_us() < 5000) {
+            /* We shall call I2CSlave::receive to enable I2C interrupt again which may be disabled in handling 
+             * I2C interrupt in Nuvoton's I2C HAL implementation on mbed OS. */
+            int addr_status = i2c_slave.receive();
+            switch (addr_status) {
+                case I2CSlave::ReadAddressed:
+                    if (! has_notified_wakeup) {
+                        has_notified_wakeup = true;
+                        wakeup_eventflags.set(EventFlag_Wakeup_I2C_AddrMatch);
+                    }
+                    i2c_slave.write(i2c_buf, sizeof (i2c_buf));
+                    timer.reset();
+                    break;
+                
+                case I2CSlave::WriteAddressed:
+                    if (! has_notified_wakeup) {
+                        has_notified_wakeup = true;
+                        wakeup_eventflags.set(EventFlag_Wakeup_I2C_AddrMatch);
+                    }
+                    i2c_slave.read(i2c_buf, sizeof (i2c_buf));
+                    timer.reset();
+                    break;
+            }
+        }
+
+        /* Clear wake-up events by I2C traffic which would have been handled above */
+        sem_i2c.wait(0);
+    }
+}
+
+void nu_i2c_wakeup_handler(I2C_T *i2c_base)
+{
+    (void) i2c_base;
+    
+    sem_i2c.release();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wakeup_pwrctl.cpp	Tue Sep 12 16:16:43 2017 +0800
@@ -0,0 +1,21 @@
+#include "mbed.h"
+#include "wakeup.h"
+
+/* Power-down wake-up interrupt handler */
+void PWRWU_IRQHandler(void)
+{
+    CLK->PWRCTL |= CLK_PWRCTL_PDWKIF_Msk;
+    
+    wakeup_eventflags.set(EventFlag_Wakeup_UnID);
+}
+
+void config_pwrctl(void)
+{
+    SYS_UnlockReg();
+    CLK->PWRCTL |= CLK_PWRCTL_PDWKIEN_Msk;
+    SYS_LockReg();
+    /* NOTE: The name of symbol PWRWU_IRQHandler is mangled in C++ and cannot override that in startup file in C.
+     *       So the NVIC_SetVector call cannot be left out. */
+    NVIC_SetVector(PWRWU_IRQn, (uint32_t) PWRWU_IRQHandler);
+    NVIC_EnableIRQ(PWRWU_IRQn);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wakeup_rtc.cpp	Tue Sep 12 16:16:43 2017 +0800
@@ -0,0 +1,90 @@
+#include "mbed.h"
+#include "wakeup.h"
+#include "rtc_api.h"
+#include "mbed_mktime.h"
+
+#define YEAR0       1900
+
+static void schedule_rtc_alarm(void *secs_);
+Event<void()> *schedule_rtc_alarm_event;
+
+void RTC_IRQHandler(void)
+{
+    /* Check if RTC alarm interrupt has occurred */
+#if defined(TARGET_NUMAKER_PFM_NUC472)
+    if (RTC->INTSTS & RTC_INTSTS_ALMIF_Msk) {
+        /* Clear RTC alarm interrupt flag */
+        RTC->INTSTS = RTC_INTSTS_ALMIF_Msk;
+
+        wakeup_eventflags.set(EventFlag_Wakeup_RTC_Alarm);
+    }
+#else
+    if (RTC_GET_ALARM_INT_FLAG()) {
+        /* Clear RTC alarm interrupt flag */
+        RTC_CLEAR_ALARM_INT_FLAG();
+
+        wakeup_eventflags.set(EventFlag_Wakeup_RTC_Alarm);
+    }
+#endif
+}
+
+void config_rtc_wakeup(void)
+{
+    Callback<void()> callback(&schedule_rtc_alarm, (void *) 3);
+    static RtosTimer timer(callback, osTimerPeriodic);
+    timer.start(4000);
+}
+
+void schedule_rtc_alarm(void *secs_)
+{
+    uint32_t secs = (uint32_t) secs_;
+    
+    /* time() will call set_time(0) internally to set timestamp if rtc is not yet enabled, where the 0 timestamp 
+     * corresponds to 00:00 hours, Jan 1, 1970 UTC. But Nuvoton mcu's rtc supports calendar since 2000 and 1970 
+     * is not supported. For this test, a timestamp after 2000 is explicitly set. */ 
+    {
+        static bool time_inited = false;
+    
+        if (! time_inited) {
+            time_inited = true;
+        
+            #define CUSTOM_TIME  1256729737
+            set_time(CUSTOM_TIME);  // Set RTC time to Wed, 28 Oct 2009 11:35:37
+        }
+    }
+    
+    RTC_DisableInt(RTC_INTEN_ALMIEN_Msk);
+    
+    time_t t = time(NULL);
+    t += secs; // Schedule RTC alarm after secs
+    
+    // Convert timestamp to struct tm
+    struct tm timeinfo;
+    if (_rtc_localtime(t, &timeinfo) == false) {
+        printf("config_rtc_alarm() fails\n");
+        return;
+    }
+
+    S_RTC_TIME_DATA_T rtc_datetime;
+    
+    // Convert struct tm to S_RTC_TIME_DATA_T
+    rtc_datetime.u32Year        = timeinfo.tm_year + YEAR0;
+    rtc_datetime.u32Month       = timeinfo.tm_mon + 1;
+    rtc_datetime.u32Day         = timeinfo.tm_mday;
+    rtc_datetime.u32DayOfWeek   = timeinfo.tm_wday;
+    rtc_datetime.u32Hour        = timeinfo.tm_hour;
+    rtc_datetime.u32Minute      = timeinfo.tm_min;
+    rtc_datetime.u32Second      = timeinfo.tm_sec;
+    rtc_datetime.u32TimeScale   = RTC_CLOCK_24;
+    
+    RTC_SetAlarmDateAndTime(&rtc_datetime);
+    
+    /* NOTE: The Mbed RTC HAL implementation of Nuvoton's targets doesn't use interrupt, so we can override vector
+             handler (via NVIC_SetVector). */
+    /* NOTE: The name of symbol PWRWU_IRQHandler is mangled in C++ and cannot override that in startup file in C.
+     *       So the NVIC_SetVector call cannot be left out. */
+    NVIC_SetVector(RTC_IRQn, (uint32_t) RTC_IRQHandler);
+    NVIC_EnableIRQ(RTC_IRQn);
+    /* Enable RTC alarm interrupt and wake-up function will be enabled also */
+    RTC_EnableInt(RTC_INTEN_ALMIEN_Msk);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wakeup_uart.cpp	Tue Sep 12 16:16:43 2017 +0800
@@ -0,0 +1,77 @@
+#include "mbed.h"
+#include "wakeup.h"
+
+#if defined(TARGET_NUMAKER_PFM_NUC472)
+// Serial
+#define SERIAL_RX   PF_0
+#define SERIAL_TX   PD_15
+#define SERIAL_CTS  PD_13
+#define SERIAL_RTS  PD_14
+
+#elif defined(TARGET_NUMAKER_PFM_M453)
+// Serial
+#define SERIAL_RX   A2
+#define SERIAL_TX   A3
+#define SERIAL_CTS  A4
+#define SERIAL_RTS  A5
+
+#elif defined(TARGET_NUMAKER_PFM_M487)
+// Serial
+#define SERIAL_RX   D13
+#define SERIAL_TX   D10
+#define SERIAL_CTS  D12
+#define SERIAL_RTS  D11
+
+#endif
+
+/* This handler is to be called in UART interrupt context (which is extended by Nuvoton's UART HAL implementation 
+ * on mbed OS) to support wake-up by UART CTS state change. */
+extern "C" void nu_uart_cts_wakeup_handler(UART_T *uart_base);
+
+static void poll_serial(void);
+static void serial_tx_callback(Serial *serial_);
+
+/* Support wake-up by UART CTS state change */
+static Semaphore sem_serial(0, 1);
+
+void config_uart_wakeup(void)
+{
+    static Thread thread_serial;
+    
+    Callback<void()> callback(&poll_serial);
+    thread_serial.start(callback);
+}
+
+static void poll_serial(void)
+{
+    static Serial serial(SERIAL_TX, SERIAL_RX);
+
+    /* UART CTS wake-up: clock source is not limited.
+     * UART data wake-up: clock source is required to be LXT/LIRC. */
+    serial.set_flow_control(SerialBase::RTSCTS, SERIAL_RTS, SERIAL_CTS);
+
+    /* We need to register one interrupt handler to enable interrupt. */
+    Callback<void()> callback((void (*)(Serial *)) &serial_tx_callback, (Serial *) &serial);
+    serial.attach(callback, mbed::SerialBase::TxIrq);
+    
+    while (true) {
+        int32_t sem_tokens = sem_serial.wait(osWaitForever);
+        if (sem_tokens < 1) {
+            continue;
+        }
+    
+        wakeup_eventflags.set(EventFlag_Wakeup_UART_CTS);
+    }
+}
+
+static void serial_tx_callback(Serial *serial_)
+{
+    (void) serial_;
+}
+
+void nu_uart_cts_wakeup_handler(UART_T *uart_base)
+{
+    (void) uart_base;
+    
+    sem_serial.release();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wakeup_wdt.cpp	Tue Sep 12 16:16:43 2017 +0800
@@ -0,0 +1,41 @@
+#include "mbed.h"
+#include "wakeup.h"
+
+void WDT_IRQHandler(void)
+{
+    /* Check WDT interrupt flag */
+    if (WDT_GET_TIMEOUT_INT_FLAG()) {
+        WDT_CLEAR_TIMEOUT_INT_FLAG();
+        WDT_RESET_COUNTER();
+    }
+
+    // Check WDT wake-up flag
+    if (WDT_GET_TIMEOUT_WAKEUP_FLAG()) {
+        WDT_CLEAR_TIMEOUT_WAKEUP_FLAG();
+        
+        wakeup_eventflags.set(EventFlag_Wakeup_WDT_Timeout);
+    }
+}
+
+void config_wdt_wakeup()
+{
+    /* Enable IP module clock */
+    CLK_EnableModuleClock(WDT_MODULE);
+
+    /* Select IP clock source */
+    CLK_SetModuleClock(WDT_MODULE, CLK_CLKSEL1_WDTSEL_LIRC, 0);
+
+    SYS_UnlockReg();
+    /* Alarm every 2^14 LIRC clocks, disable system reset, enable system wake-up */
+    WDT_Open(WDT_TIMEOUT_2POW14, 0, FALSE, TRUE);
+    SYS_LockReg();
+
+    /* NOTE: The name of symbol WDT_IRQHandler is mangled in C++ and cannot override that in startup file in C.
+     *       So the NVIC_SetVector call cannot be left out. */
+    NVIC_SetVector(WDT_IRQn, (uint32_t) WDT_IRQHandler);
+    NVIC_EnableIRQ(WDT_IRQn);
+    SYS_UnlockReg();
+    /* Enable WDT timeout interrupt */
+    WDT_EnableInt();
+    SYS_LockReg();
+}