NuMaker tickless example

Committer:
ccli8
Date:
Fri Apr 13 09:15:30 2018 +0800
Revision:
11:0c4b39c54af2
Parent:
6:de274e3aed9d
Child:
13:c1c711d573f2
Update to Mbed OS 5.8.2

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