Nuvoton
/
NuMaker-mbed-tickless-example
NuMaker tickless example
main.cpp@1:eb1da9d36e12, 2017-09-12 (annotated)
- 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?
User | Revision | Line number | New 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 | } |