NuMaker tickless example

Committer:
ccli8
Date:
Tue Sep 12 16:16:43 2017 +0800
Revision:
1:eb1da9d36e12
Parent:
0:d7c3d9c9dbe4
Child:
2:dbe00b3a7545
Support NUC472/M453/M487

Who changed what in which revision?

UserRevisionLine numberNew contents of line
ccli8 1:eb1da9d36e12 1 #include "mbed.h"
ccli8 1:eb1da9d36e12 2 #include <limits.h>
ccli8 1:eb1da9d36e12 3 #include <vector>
ccli8 1:eb1da9d36e12 4
ccli8 1:eb1da9d36e12 5 #include "wakeup.h"
ccli8 1:eb1da9d36e12 6
ccli8 1:eb1da9d36e12 7 static void flush_stdio_uart_fifo(void);
ccli8 1:eb1da9d36e12 8 static void check_wakeup_source(uint32_t, bool deepsleep);
ccli8 1:eb1da9d36e12 9 static void idle_daemon(void);
ccli8 1:eb1da9d36e12 10
ccli8 1:eb1da9d36e12 11 #if (MBED_MAJOR_VERSION <= 5 && MBED_MINOR_VERSION <= 5 && MBED_PATCH_VERSION <= 6)
ccli8 1:eb1da9d36e12 12 EventFlags_ wakeup_eventflags;
ccli8 1:eb1da9d36e12 13 #else
ccli8 1:eb1da9d36e12 14 EventFlags wakeup_eventflags;
ccli8 1:eb1da9d36e12 15 #endif
ccli8 1:eb1da9d36e12 16
ccli8 1:eb1da9d36e12 17 int main() {
ccli8 1:eb1da9d36e12 18
ccli8 1:eb1da9d36e12 19 config_pwrctl();
ccli8 1:eb1da9d36e12 20 config_button_wakeup();
ccli8 1:eb1da9d36e12 21 config_wdt_wakeup();
ccli8 1:eb1da9d36e12 22 config_rtc_wakeup();
ccli8 1:eb1da9d36e12 23 /* TODO */
ccli8 1:eb1da9d36e12 24 //config_uart_wakeup();
ccli8 1:eb1da9d36e12 25 //config_i2c_wakeup();
ccli8 1:eb1da9d36e12 26
ccli8 1:eb1da9d36e12 27 /* Register idle handler which supports tickless */
ccli8 1:eb1da9d36e12 28 Thread::attach_idle_hook(idle_daemon);
ccli8 1:eb1da9d36e12 29
ccli8 1:eb1da9d36e12 30 while (true) {
ccli8 1:eb1da9d36e12 31
ccli8 1:eb1da9d36e12 32 printf("I am going to shallow/deep sleep\n");
ccli8 1:eb1da9d36e12 33 /* Flush STDIO UART before entering Idle/Power-down mode */
ccli8 1:eb1da9d36e12 34 fflush(stdout);
ccli8 1:eb1da9d36e12 35 flush_stdio_uart_fifo();
ccli8 1:eb1da9d36e12 36
ccli8 1:eb1da9d36e12 37 /* Wait for any wake-up event */
ccli8 1:eb1da9d36e12 38 uint32_t flags = wakeup_eventflags.wait_any(EventFlag_Wakeup_All, osWaitForever, true);
ccli8 1:eb1da9d36e12 39 if (flags & osFlagsError) {
ccli8 1:eb1da9d36e12 40 if (flags != osFlagsErrorTimeout) {
ccli8 1:eb1da9d36e12 41 printf("OS error code: 0x%08lX\n", flags);
ccli8 1:eb1da9d36e12 42 }
ccli8 1:eb1da9d36e12 43 continue;
ccli8 1:eb1da9d36e12 44 }
ccli8 1:eb1da9d36e12 45
ccli8 1:eb1da9d36e12 46 /* EventFlag_Wakeup_UnID is set in PWRWU_IRQHandler to indicate unidentified wake-up from power-down.
ccli8 1:eb1da9d36e12 47 * It could be deferred. Wait for it. */
ccli8 1:eb1da9d36e12 48 uint32_t flags2 = wakeup_eventflags.wait_any(EventFlag_Wakeup_UnID, 100, true);
ccli8 1:eb1da9d36e12 49 if (flags2 & osFlagsError) {
ccli8 1:eb1da9d36e12 50 if (flags2 != osFlagsErrorTimeout) {
ccli8 1:eb1da9d36e12 51 printf("OS error code: 0x%08lX\n", flags2);
ccli8 1:eb1da9d36e12 52 }
ccli8 1:eb1da9d36e12 53 else {
ccli8 1:eb1da9d36e12 54 flags2 = 0;
ccli8 1:eb1da9d36e12 55 }
ccli8 1:eb1da9d36e12 56 }
ccli8 1:eb1da9d36e12 57 flags |= flags2;
ccli8 1:eb1da9d36e12 58
ccli8 1:eb1da9d36e12 59 /* EventFlag_Wakeup_UnID means unidentified wake-up source from power-down.
ccli8 1:eb1da9d36e12 60 *
ccli8 1:eb1da9d36e12 61 * If EventFlag_Wakeup_UnID is set, we wake up from power-down mode (deep sleep) but its wake-up
ccli8 1:eb1da9d36e12 62 * source is not yet identified. Wait for it to be identified if no other wake-up sources have been
ccli8 1:eb1da9d36e12 63 * identified.
ccli8 1:eb1da9d36e12 64 *
ccli8 1:eb1da9d36e12 65 * If EventFlag_Wakeup_UnID is not set, we wake up from idle mode (shallow sleep). Wake-up source
ccli8 1:eb1da9d36e12 66 * would have been identified.
ccli8 1:eb1da9d36e12 67 */
ccli8 1:eb1da9d36e12 68 if (flags == EventFlag_Wakeup_UnID) {
ccli8 1:eb1da9d36e12 69 uint32_t flags3 = wakeup_eventflags.wait_any(EventFlag_Wakeup_All & ~EventFlag_Wakeup_UnID, 100, true);
ccli8 1:eb1da9d36e12 70 if (flags3 & osFlagsError) {
ccli8 1:eb1da9d36e12 71 if (flags3 != osFlagsErrorTimeout) {
ccli8 1:eb1da9d36e12 72 printf("OS error code: 0x%08lX\n", flags3);
ccli8 1:eb1da9d36e12 73 }
ccli8 1:eb1da9d36e12 74 else {
ccli8 1:eb1da9d36e12 75 flags3 = 0;
ccli8 1:eb1da9d36e12 76 }
ccli8 1:eb1da9d36e12 77 }
ccli8 1:eb1da9d36e12 78 flags |= flags3;
ccli8 1:eb1da9d36e12 79 }
ccli8 1:eb1da9d36e12 80
ccli8 1:eb1da9d36e12 81 /* Clear wake-up flags caused by short time EventFlags::wait_any() above. These wake-up flags are
ccli8 1:eb1da9d36e12 82 * just for program control and not actual wake-up events we want to track. This has a side effect of
ccli8 1:eb1da9d36e12 83 * losing actual wake-up events in this short time. */
ccli8 1:eb1da9d36e12 84 wakeup_eventflags.clear();
ccli8 1:eb1da9d36e12 85
ccli8 1:eb1da9d36e12 86 bool deepsleep = (flags & EventFlag_Wakeup_UnID) ? true : false;
ccli8 1:eb1da9d36e12 87
ccli8 1:eb1da9d36e12 88 /* Remove EventFlag_Wakeup_UnID if any other wake-up source is identified */
ccli8 1:eb1da9d36e12 89 if ((flags & ~EventFlag_Wakeup_UnID)) {
ccli8 1:eb1da9d36e12 90 flags &= ~EventFlag_Wakeup_UnID;
ccli8 1:eb1da9d36e12 91 }
ccli8 1:eb1da9d36e12 92 check_wakeup_source(flags, deepsleep);
ccli8 1:eb1da9d36e12 93
ccli8 1:eb1da9d36e12 94 printf("\n");
ccli8 1:eb1da9d36e12 95 }
ccli8 1:eb1da9d36e12 96
ccli8 1:eb1da9d36e12 97 return 0;
ccli8 1:eb1da9d36e12 98 }
ccli8 1:eb1da9d36e12 99
ccli8 1:eb1da9d36e12 100 void flush_stdio_uart_fifo(void)
ccli8 1:eb1da9d36e12 101 {
ccli8 1:eb1da9d36e12 102 UART_T *uart_base = (UART_T *) NU_MODBASE(STDIO_UART);
ccli8 1:eb1da9d36e12 103
ccli8 1:eb1da9d36e12 104 while (! UART_IS_TX_EMPTY(uart_base));
ccli8 1:eb1da9d36e12 105 }
ccli8 1:eb1da9d36e12 106
ccli8 1:eb1da9d36e12 107 void check_wakeup_source(uint32_t flags, bool deepsleep)
ccli8 1:eb1da9d36e12 108 {
ccli8 1:eb1da9d36e12 109 typedef std::pair<uint32_t, const char *> WakeupName;
ccli8 1:eb1da9d36e12 110
ccli8 1:eb1da9d36e12 111 static const WakeupName wakeup_name_arr[] = {
ccli8 1:eb1da9d36e12 112 WakeupName(EventFlag_Wakeup_Button1, "Button1"),
ccli8 1:eb1da9d36e12 113 WakeupName(EventFlag_Wakeup_Button2, "Button2"),
ccli8 1:eb1da9d36e12 114 WakeupName(EventFlag_Wakeup_LPTicker, "lp_ticker"),
ccli8 1:eb1da9d36e12 115 WakeupName(EventFlag_Wakeup_WDT_Timeout, "WDT timeout"),
ccli8 1:eb1da9d36e12 116 WakeupName(EventFlag_Wakeup_RTC_Alarm, "RTC alarm"),
ccli8 1:eb1da9d36e12 117 WakeupName(EventFlag_Wakeup_UART_CTS, "UART CTS"),
ccli8 1:eb1da9d36e12 118 WakeupName(EventFlag_Wakeup_I2C_AddrMatch, "I2C address match"),
ccli8 1:eb1da9d36e12 119
ccli8 1:eb1da9d36e12 120 WakeupName(EventFlag_Wakeup_UnID, "Unidentified"),
ccli8 1:eb1da9d36e12 121 };
ccli8 1:eb1da9d36e12 122
ccli8 1:eb1da9d36e12 123 const char *sleep_mode = deepsleep ? "deep sleep" : "shallow sleep";
ccli8 1:eb1da9d36e12 124
ccli8 1:eb1da9d36e12 125 if (flags) {
ccli8 1:eb1da9d36e12 126 const WakeupName *wakeup_name_pos = wakeup_name_arr;
ccli8 1:eb1da9d36e12 127 const WakeupName *wakeup_name_end = wakeup_name_arr + (sizeof (wakeup_name_arr) / sizeof (wakeup_name_arr[0]));
ccli8 1:eb1da9d36e12 128
ccli8 1:eb1da9d36e12 129 for (; wakeup_name_pos != wakeup_name_end; wakeup_name_pos ++) {
ccli8 1:eb1da9d36e12 130 if (flags & wakeup_name_pos->first) {
ccli8 1:eb1da9d36e12 131 printf("Wake up by %s from %s\n", wakeup_name_pos->second, sleep_mode);
ccli8 1:eb1da9d36e12 132 }
ccli8 1:eb1da9d36e12 133 }
ccli8 1:eb1da9d36e12 134 }
ccli8 1:eb1da9d36e12 135 }
ccli8 1:eb1da9d36e12 136
ccli8 1:eb1da9d36e12 137 #define US_PER_SEC (1000 * 1000)
ccli8 1:eb1da9d36e12 138 #define US_PER_OS_TICK (US_PER_SEC / OS_TICK_FREQ)
ccli8 1:eb1da9d36e12 139
ccli8 1:eb1da9d36e12 140 void dummy_cb(void) {}
ccli8 1:eb1da9d36e12 141
ccli8 1:eb1da9d36e12 142 void idle_daemon(void) {
ccli8 1:eb1da9d36e12 143
ccli8 1:eb1da9d36e12 144 const int max_us_sleep = (INT_MAX / OS_TICK_FREQ) * OS_TICK_FREQ;
ccli8 1:eb1da9d36e12 145 /* Keep track of the time asleep */
ccli8 1:eb1da9d36e12 146 LowPowerTimer asleep_watch;
ccli8 1:eb1da9d36e12 147 /* Will wake up the system (by lp_ticker) if no other wake-up event */
ccli8 1:eb1da9d36e12 148 LowPowerTimeout alarm_clock;
ccli8 1:eb1da9d36e12 149
ccli8 1:eb1da9d36e12 150 /* Never ends. The rtos will suspend this thread when there is something to do
ccli8 1:eb1da9d36e12 151 * either before osKernelSuspend actually suspend the system (and is not in svc)
ccli8 1:eb1da9d36e12 152 * or immediately after osKernelResume. */
ccli8 1:eb1da9d36e12 153 while (true) {
ccli8 1:eb1da9d36e12 154 /* Suspend the system */
ccli8 1:eb1da9d36e12 155 uint32_t ticks_to_sleep = osKernelSuspend();
ccli8 1:eb1da9d36e12 156 uint32_t elapsed_ticks = 0;
ccli8 1:eb1da9d36e12 157
ccli8 1:eb1da9d36e12 158 if (ticks_to_sleep) {
ccli8 1:eb1da9d36e12 159 uint64_t us_to_sleep = ticks_to_sleep * US_PER_OS_TICK;
ccli8 1:eb1da9d36e12 160
ccli8 1:eb1da9d36e12 161 if (us_to_sleep > max_us_sleep) {
ccli8 1:eb1da9d36e12 162 us_to_sleep = max_us_sleep;
ccli8 1:eb1da9d36e12 163 }
ccli8 1:eb1da9d36e12 164
ccli8 1:eb1da9d36e12 165 /* Start the asleep_watch and setup the alarm_clock to wake up the system in us_to_sleep */
ccli8 1:eb1da9d36e12 166 asleep_watch.start();
ccli8 1:eb1da9d36e12 167 alarm_clock.attach_us(dummy_cb, us_to_sleep);
ccli8 1:eb1da9d36e12 168
ccli8 1:eb1da9d36e12 169 /* Go to deep sleep */
ccli8 1:eb1da9d36e12 170 deepsleep();
ccli8 1:eb1da9d36e12 171
ccli8 1:eb1da9d36e12 172 /* Woken up by lp_ticker or other wake-up event */
ccli8 1:eb1da9d36e12 173 int us_asleep = asleep_watch.read_us();
ccli8 1:eb1da9d36e12 174
ccli8 1:eb1da9d36e12 175 /* Clean up asleep_watch and alarm_clock */
ccli8 1:eb1da9d36e12 176 asleep_watch.stop();
ccli8 1:eb1da9d36e12 177 asleep_watch.reset();
ccli8 1:eb1da9d36e12 178 alarm_clock.detach();
ccli8 1:eb1da9d36e12 179
ccli8 1:eb1da9d36e12 180 /* Translate us_asleep into ticks */
ccli8 1:eb1da9d36e12 181 elapsed_ticks = us_asleep / US_PER_OS_TICK;
ccli8 1:eb1da9d36e12 182 }
ccli8 1:eb1da9d36e12 183
ccli8 1:eb1da9d36e12 184 /* Resume the system */
ccli8 1:eb1da9d36e12 185 osKernelResume(elapsed_ticks);
ccli8 1:eb1da9d36e12 186 }
ccli8 1:eb1da9d36e12 187 }