Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Revision 1:eb1da9d36e12, committed 2017-09-12
- 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
--- /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();
+}