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