Nuvoton
/
NuMaker-mbed-tickless-example
NuMaker tickless example
wakeup_rtc.cpp@16:ed2c228cbc9c, 2020-02-21 (annotated)
- Committer:
- ccli8
- Date:
- Fri Feb 21 11:19:30 2020 +0800
- Revision:
- 16:ed2c228cbc9c
- Parent:
- 12:b0d19e915d96
- Child:
- 17:0f81445cbbf0
Update to mbed-os 5.15.1 and accompanying modifications
1. Remove OS_IDLE_THREAD_STACK_SIZE because mbed-os has has reasonable default value.
2. Configure tickless-from-us-ticker to false. It is needed to run tickless mode with lp_ticker instead of us_ticker for wake-up.
3. Disable WDT wake-up when Mbed OS watchog API is enabled.
4. Fix deprecated API.
5. Update readme.
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 "wakeup.h" |
ccli8 |
1:eb1da9d36e12 | 3 | #include "rtc_api.h" |
ccli8 |
1:eb1da9d36e12 | 4 | #include "mbed_mktime.h" |
ccli8 |
1:eb1da9d36e12 | 5 | |
ccli8 |
12:b0d19e915d96 | 6 | /* Micro seconds per second */ |
ccli8 |
12:b0d19e915d96 | 7 | #define NU_US_PER_SEC 1000000 |
ccli8 |
12:b0d19e915d96 | 8 | |
ccli8 |
12:b0d19e915d96 | 9 | /* Timer clock per second |
ccli8 |
12:b0d19e915d96 | 10 | * |
ccli8 |
12:b0d19e915d96 | 11 | * NOTE: This dependents on real hardware. |
ccli8 |
12:b0d19e915d96 | 12 | */ |
ccli8 |
12:b0d19e915d96 | 13 | #if defined(TARGET_NUMAKER_PFM_NANO130) |
ccli8 |
12:b0d19e915d96 | 14 | #define NU_RTCCLK_PER_SEC (__LXT) |
ccli8 |
12:b0d19e915d96 | 15 | #else |
ccli8 |
12:b0d19e915d96 | 16 | #define NU_RTCCLK_PER_SEC ((CLK->CLKSEL3 & CLK_CLKSEL3_SC0SEL_Msk) ? __LIRC : __LXT) |
ccli8 |
12:b0d19e915d96 | 17 | #endif |
ccli8 |
12:b0d19e915d96 | 18 | |
ccli8 |
12:b0d19e915d96 | 19 | /* Start year of struct TM*/ |
ccli8 |
12:b0d19e915d96 | 20 | #define NU_TM_YEAR0 1900 |
ccli8 |
12:b0d19e915d96 | 21 | /* Start year of POSIX time (set_time()/time()) */ |
ccli8 |
12:b0d19e915d96 | 22 | #define NU_POSIX_YEAR0 1970 |
ccli8 |
12:b0d19e915d96 | 23 | /* Start year of H/W RTC */ |
ccli8 |
12:b0d19e915d96 | 24 | #define NU_HWRTC_YEAR0 2000 |
ccli8 |
1:eb1da9d36e12 | 25 | |
ccli8 |
4:da41f0e17d5a | 26 | static Semaphore sem_rtc(0, 1); |
ccli8 |
4:da41f0e17d5a | 27 | |
ccli8 |
4:da41f0e17d5a | 28 | static void rtc_loop(void); |
ccli8 |
4:da41f0e17d5a | 29 | static void schedule_rtc_alarm(uint32_t secs); |
ccli8 |
1:eb1da9d36e12 | 30 | |
ccli8 |
12:b0d19e915d96 | 31 | /* Convert date time from H/W RTC to struct TM */ |
ccli8 |
12:b0d19e915d96 | 32 | static void rtc_convert_datetime_hwrtc_to_tm(struct tm *datetime_tm, const S_RTC_TIME_DATA_T *datetime_hwrtc); |
ccli8 |
12:b0d19e915d96 | 33 | /* Convert date time from struct TM to H/W RTC */ |
ccli8 |
12:b0d19e915d96 | 34 | static void rtc_convert_datetime_tm_to_hwrtc(S_RTC_TIME_DATA_T *datetime_hwrtc, const struct tm *datetime_tm); |
ccli8 |
12:b0d19e915d96 | 35 | |
ccli8 |
10:d2e2c79389e1 | 36 | #if defined(TARGET_NUMAKER_PFM_NANO130) |
ccli8 |
10:d2e2c79389e1 | 37 | /* This target doesn't support relocating vector table and requires overriding |
ccli8 |
10:d2e2c79389e1 | 38 | * vector handler at link-time. */ |
ccli8 |
10:d2e2c79389e1 | 39 | extern "C" void RTC_IRQHandler(void) |
ccli8 |
10:d2e2c79389e1 | 40 | #else |
ccli8 |
1:eb1da9d36e12 | 41 | void RTC_IRQHandler(void) |
ccli8 |
10:d2e2c79389e1 | 42 | #endif |
ccli8 |
1:eb1da9d36e12 | 43 | { |
ccli8 |
1:eb1da9d36e12 | 44 | /* Check if RTC alarm interrupt has occurred */ |
ccli8 |
10:d2e2c79389e1 | 45 | #if defined(TARGET_NUMAKER_PFM_NANO130) |
ccli8 |
10:d2e2c79389e1 | 46 | if (RTC->RIIR & RTC_RIIR_AIF_Msk) { |
ccli8 |
10:d2e2c79389e1 | 47 | /* Clear RTC alarm interrupt flag */ |
ccli8 |
10:d2e2c79389e1 | 48 | RTC->RIIR = RTC_RIIR_AIF_Msk; |
ccli8 |
10:d2e2c79389e1 | 49 | |
ccli8 |
10:d2e2c79389e1 | 50 | wakeup_eventflags.set(EventFlag_Wakeup_RTC_Alarm); |
ccli8 |
10:d2e2c79389e1 | 51 | } |
ccli8 |
10:d2e2c79389e1 | 52 | #elif defined(TARGET_NUMAKER_PFM_NUC472) |
ccli8 |
1:eb1da9d36e12 | 53 | if (RTC->INTSTS & RTC_INTSTS_ALMIF_Msk) { |
ccli8 |
1:eb1da9d36e12 | 54 | /* Clear RTC alarm interrupt flag */ |
ccli8 |
1:eb1da9d36e12 | 55 | RTC->INTSTS = RTC_INTSTS_ALMIF_Msk; |
ccli8 |
1:eb1da9d36e12 | 56 | |
ccli8 |
1:eb1da9d36e12 | 57 | wakeup_eventflags.set(EventFlag_Wakeup_RTC_Alarm); |
ccli8 |
1:eb1da9d36e12 | 58 | } |
ccli8 |
1:eb1da9d36e12 | 59 | #else |
ccli8 |
1:eb1da9d36e12 | 60 | if (RTC_GET_ALARM_INT_FLAG()) { |
ccli8 |
1:eb1da9d36e12 | 61 | /* Clear RTC alarm interrupt flag */ |
ccli8 |
1:eb1da9d36e12 | 62 | RTC_CLEAR_ALARM_INT_FLAG(); |
ccli8 |
1:eb1da9d36e12 | 63 | |
ccli8 |
1:eb1da9d36e12 | 64 | wakeup_eventflags.set(EventFlag_Wakeup_RTC_Alarm); |
ccli8 |
1:eb1da9d36e12 | 65 | } |
ccli8 |
1:eb1da9d36e12 | 66 | #endif |
ccli8 |
4:da41f0e17d5a | 67 | |
ccli8 |
4:da41f0e17d5a | 68 | /* Wake up RTC loop to schedule another RTC alarm */ |
ccli8 |
4:da41f0e17d5a | 69 | sem_rtc.release(); |
ccli8 |
1:eb1da9d36e12 | 70 | } |
ccli8 |
1:eb1da9d36e12 | 71 | |
ccli8 |
1:eb1da9d36e12 | 72 | void config_rtc_wakeup(void) |
ccli8 |
1:eb1da9d36e12 | 73 | { |
ccli8 |
5:fdfb7a10cc59 | 74 | static Thread thread_rtc(osPriorityNormal, 2048); |
ccli8 |
4:da41f0e17d5a | 75 | |
ccli8 |
4:da41f0e17d5a | 76 | Callback<void()> callback(&rtc_loop); |
ccli8 |
4:da41f0e17d5a | 77 | thread_rtc.start(callback); |
ccli8 |
1:eb1da9d36e12 | 78 | } |
ccli8 |
1:eb1da9d36e12 | 79 | |
ccli8 |
4:da41f0e17d5a | 80 | void rtc_loop(void) |
ccli8 |
1:eb1da9d36e12 | 81 | { |
ccli8 |
4:da41f0e17d5a | 82 | /* Schedule RTC alarm in 3 secs */ |
ccli8 |
4:da41f0e17d5a | 83 | schedule_rtc_alarm(3); |
ccli8 |
1:eb1da9d36e12 | 84 | |
ccli8 |
4:da41f0e17d5a | 85 | while (true) { |
ccli8 |
16:ed2c228cbc9c | 86 | sem_rtc.acquire(); |
ccli8 |
16:ed2c228cbc9c | 87 | |
ccli8 |
16:ed2c228cbc9c | 88 | /* Re-schedule RTC alarm in 3 secs */ |
ccli8 |
16:ed2c228cbc9c | 89 | schedule_rtc_alarm(3); |
ccli8 |
4:da41f0e17d5a | 90 | } |
ccli8 |
4:da41f0e17d5a | 91 | } |
ccli8 |
4:da41f0e17d5a | 92 | |
ccli8 |
4:da41f0e17d5a | 93 | void schedule_rtc_alarm(uint32_t secs) |
ccli8 |
4:da41f0e17d5a | 94 | { |
ccli8 |
1:eb1da9d36e12 | 95 | /* time() will call set_time(0) internally to set timestamp if rtc is not yet enabled, where the 0 timestamp |
ccli8 |
1:eb1da9d36e12 | 96 | * corresponds to 00:00 hours, Jan 1, 1970 UTC. But Nuvoton mcu's rtc supports calendar since 2000 and 1970 |
ccli8 |
1:eb1da9d36e12 | 97 | * is not supported. For this test, a timestamp after 2000 is explicitly set. */ |
ccli8 |
1:eb1da9d36e12 | 98 | { |
ccli8 |
1:eb1da9d36e12 | 99 | static bool time_inited = false; |
ccli8 |
1:eb1da9d36e12 | 100 | |
ccli8 |
1:eb1da9d36e12 | 101 | if (! time_inited) { |
ccli8 |
1:eb1da9d36e12 | 102 | time_inited = true; |
ccli8 |
1:eb1da9d36e12 | 103 | |
ccli8 |
1:eb1da9d36e12 | 104 | #define CUSTOM_TIME 1256729737 |
ccli8 |
1:eb1da9d36e12 | 105 | set_time(CUSTOM_TIME); // Set RTC time to Wed, 28 Oct 2009 11:35:37 |
ccli8 |
1:eb1da9d36e12 | 106 | } |
ccli8 |
1:eb1da9d36e12 | 107 | } |
ccli8 |
1:eb1da9d36e12 | 108 | |
ccli8 |
10:d2e2c79389e1 | 109 | #if defined(TARGET_NUMAKER_PFM_NANO130) |
ccli8 |
10:d2e2c79389e1 | 110 | RTC_DisableInt(RTC_RIER_AIER_Msk); |
ccli8 |
10:d2e2c79389e1 | 111 | #else |
ccli8 |
1:eb1da9d36e12 | 112 | RTC_DisableInt(RTC_INTEN_ALMIEN_Msk); |
ccli8 |
10:d2e2c79389e1 | 113 | #endif |
ccli8 |
1:eb1da9d36e12 | 114 | |
ccli8 |
12:b0d19e915d96 | 115 | /* Schedule RTC alarm |
ccli8 |
12:b0d19e915d96 | 116 | * |
ccli8 |
12:b0d19e915d96 | 117 | * Mbed OS RTC API doesn't support RTC alarm function. To enable it, we need to control RTC H/W directly. |
ccli8 |
12:b0d19e915d96 | 118 | * Because RTC H/W doesn't inevitably record real date time, we cannot calculate alarm time based on time() |
ccli8 |
12:b0d19e915d96 | 119 | * timestamp. Instead, we fetch date time recorded in RTC H/W for our calculation of scheduled alarm time. |
ccli8 |
12:b0d19e915d96 | 120 | * |
ccli8 |
12:b0d19e915d96 | 121 | * Control flow would be: |
ccli8 |
12:b0d19e915d96 | 122 | * 1. Fetch RTC H/W date time. |
ccli8 |
12:b0d19e915d96 | 123 | * 2. Convert RTC H/W date time to timestamp: S_RTC_TIME_DATA_T > struct tm > time_t |
ccli8 |
12:b0d19e915d96 | 124 | * 3. Calculate alarm time based on timestamp above |
ccli8 |
12:b0d19e915d96 | 125 | * 4. Convert calculated timestamp back to RTC H/W date time: time_t > struct tm > S_RTC_TIME_DATA_T |
ccli8 |
12:b0d19e915d96 | 126 | * 5. Control RTC H/W to schedule alarm |
ccli8 |
12:b0d19e915d96 | 127 | */ |
ccli8 |
12:b0d19e915d96 | 128 | time_t t_alarm; |
ccli8 |
12:b0d19e915d96 | 129 | struct tm datetime_tm_alarm; |
ccli8 |
12:b0d19e915d96 | 130 | S_RTC_TIME_DATA_T datetime_hwrtc_alarm; |
ccli8 |
12:b0d19e915d96 | 131 | |
ccli8 |
12:b0d19e915d96 | 132 | /* Fetch RTC H/W date time */ |
ccli8 |
12:b0d19e915d96 | 133 | RTC_GetDateAndTime(&datetime_hwrtc_alarm); |
ccli8 |
1:eb1da9d36e12 | 134 | |
ccli8 |
12:b0d19e915d96 | 135 | /* Convert date time from H/W RTC to struct TM */ |
ccli8 |
12:b0d19e915d96 | 136 | rtc_convert_datetime_hwrtc_to_tm(&datetime_tm_alarm, &datetime_hwrtc_alarm); |
ccli8 |
12:b0d19e915d96 | 137 | |
ccli8 |
12:b0d19e915d96 | 138 | /* Convert date time of struct TM to POSIX time */ |
ccli8 |
12:b0d19e915d96 | 139 | if (! _rtc_maketime(&datetime_tm_alarm, &t_alarm, RTC_FULL_LEAP_YEAR_SUPPORT)) { |
ccli8 |
12:b0d19e915d96 | 140 | printf("%s: _rtc_maketime failed\n", __func__); |
ccli8 |
1:eb1da9d36e12 | 141 | return; |
ccli8 |
1:eb1da9d36e12 | 142 | } |
ccli8 |
1:eb1da9d36e12 | 143 | |
ccli8 |
12:b0d19e915d96 | 144 | /* Calculate RTC alarm time */ |
ccli8 |
12:b0d19e915d96 | 145 | t_alarm += secs; |
ccli8 |
12:b0d19e915d96 | 146 | |
ccli8 |
12:b0d19e915d96 | 147 | /* Convert POSIX time to date time of struct TM */ |
ccli8 |
12:b0d19e915d96 | 148 | if (! _rtc_localtime(t_alarm, &datetime_tm_alarm, RTC_FULL_LEAP_YEAR_SUPPORT)) { |
ccli8 |
12:b0d19e915d96 | 149 | printf("%s: _rtc_localtime failed\n", __func__); |
ccli8 |
12:b0d19e915d96 | 150 | return; |
ccli8 |
12:b0d19e915d96 | 151 | } |
ccli8 |
12:b0d19e915d96 | 152 | |
ccli8 |
12:b0d19e915d96 | 153 | /* Convert date time from struct TM to H/W RTC */ |
ccli8 |
12:b0d19e915d96 | 154 | rtc_convert_datetime_tm_to_hwrtc(&datetime_hwrtc_alarm, &datetime_tm_alarm); |
ccli8 |
1:eb1da9d36e12 | 155 | |
ccli8 |
12:b0d19e915d96 | 156 | /* Control RTC H/W to schedule alarm */ |
ccli8 |
12:b0d19e915d96 | 157 | RTC_SetAlarmDateAndTime(&datetime_hwrtc_alarm); |
ccli8 |
12:b0d19e915d96 | 158 | /* NOTE: When engine is clocked by low power clock source (LXT/LIRC), we need to wait for 3 engine clocks. */ |
ccli8 |
12:b0d19e915d96 | 159 | wait_us((NU_US_PER_SEC / NU_RTCCLK_PER_SEC) * 3); |
ccli8 |
1:eb1da9d36e12 | 160 | |
ccli8 |
1:eb1da9d36e12 | 161 | /* NOTE: The Mbed RTC HAL implementation of Nuvoton's targets doesn't use interrupt, so we can override vector |
ccli8 |
1:eb1da9d36e12 | 162 | handler (via NVIC_SetVector). */ |
ccli8 |
1:eb1da9d36e12 | 163 | /* NOTE: The name of symbol PWRWU_IRQHandler is mangled in C++ and cannot override that in startup file in C. |
ccli8 |
1:eb1da9d36e12 | 164 | * So the NVIC_SetVector call cannot be left out. */ |
ccli8 |
1:eb1da9d36e12 | 165 | NVIC_SetVector(RTC_IRQn, (uint32_t) RTC_IRQHandler); |
ccli8 |
1:eb1da9d36e12 | 166 | NVIC_EnableIRQ(RTC_IRQn); |
ccli8 |
1:eb1da9d36e12 | 167 | /* Enable RTC alarm interrupt and wake-up function will be enabled also */ |
ccli8 |
10:d2e2c79389e1 | 168 | #if defined(TARGET_NUMAKER_PFM_NANO130) |
ccli8 |
10:d2e2c79389e1 | 169 | RTC_EnableInt(RTC_RIER_AIER_Msk); |
ccli8 |
10:d2e2c79389e1 | 170 | #else |
ccli8 |
1:eb1da9d36e12 | 171 | RTC_EnableInt(RTC_INTEN_ALMIEN_Msk); |
ccli8 |
10:d2e2c79389e1 | 172 | #endif |
ccli8 |
1:eb1da9d36e12 | 173 | } |
ccli8 |
12:b0d19e915d96 | 174 | |
ccli8 |
12:b0d19e915d96 | 175 | /* |
ccli8 |
12:b0d19e915d96 | 176 | struct tm |
ccli8 |
12:b0d19e915d96 | 177 | tm_sec seconds after the minute 0-61 |
ccli8 |
12:b0d19e915d96 | 178 | tm_min minutes after the hour 0-59 |
ccli8 |
12:b0d19e915d96 | 179 | tm_hour hours since midnight 0-23 |
ccli8 |
12:b0d19e915d96 | 180 | tm_mday day of the month 1-31 |
ccli8 |
12:b0d19e915d96 | 181 | tm_mon months since January 0-11 |
ccli8 |
12:b0d19e915d96 | 182 | tm_year years since 1900 |
ccli8 |
12:b0d19e915d96 | 183 | tm_wday days since Sunday 0-6 |
ccli8 |
12:b0d19e915d96 | 184 | tm_yday days since January 1 0-365 |
ccli8 |
12:b0d19e915d96 | 185 | tm_isdst Daylight Saving Time flag |
ccli8 |
12:b0d19e915d96 | 186 | */ |
ccli8 |
12:b0d19e915d96 | 187 | static void rtc_convert_datetime_hwrtc_to_tm(struct tm *datetime_tm, const S_RTC_TIME_DATA_T *datetime_hwrtc) |
ccli8 |
12:b0d19e915d96 | 188 | { |
ccli8 |
12:b0d19e915d96 | 189 | datetime_tm->tm_year = datetime_hwrtc->u32Year - NU_TM_YEAR0; |
ccli8 |
12:b0d19e915d96 | 190 | datetime_tm->tm_mon = datetime_hwrtc->u32Month - 1; |
ccli8 |
12:b0d19e915d96 | 191 | datetime_tm->tm_mday = datetime_hwrtc->u32Day; |
ccli8 |
12:b0d19e915d96 | 192 | datetime_tm->tm_wday = datetime_hwrtc->u32DayOfWeek; |
ccli8 |
12:b0d19e915d96 | 193 | datetime_tm->tm_hour = datetime_hwrtc->u32Hour; |
ccli8 |
12:b0d19e915d96 | 194 | if (datetime_hwrtc->u32TimeScale == RTC_CLOCK_12 && datetime_hwrtc->u32AmPm == RTC_PM) { |
ccli8 |
12:b0d19e915d96 | 195 | datetime_tm->tm_hour += 12; |
ccli8 |
12:b0d19e915d96 | 196 | } |
ccli8 |
12:b0d19e915d96 | 197 | datetime_tm->tm_min = datetime_hwrtc->u32Minute; |
ccli8 |
12:b0d19e915d96 | 198 | datetime_tm->tm_sec = datetime_hwrtc->u32Second; |
ccli8 |
12:b0d19e915d96 | 199 | } |
ccli8 |
12:b0d19e915d96 | 200 | |
ccli8 |
12:b0d19e915d96 | 201 | static void rtc_convert_datetime_tm_to_hwrtc(S_RTC_TIME_DATA_T *datetime_hwrtc, const struct tm *datetime_tm) |
ccli8 |
12:b0d19e915d96 | 202 | { |
ccli8 |
12:b0d19e915d96 | 203 | datetime_hwrtc->u32Year = datetime_tm->tm_year + NU_TM_YEAR0; |
ccli8 |
12:b0d19e915d96 | 204 | datetime_hwrtc->u32Month = datetime_tm->tm_mon + 1; |
ccli8 |
12:b0d19e915d96 | 205 | datetime_hwrtc->u32Day = datetime_tm->tm_mday; |
ccli8 |
12:b0d19e915d96 | 206 | datetime_hwrtc->u32DayOfWeek = datetime_tm->tm_wday; |
ccli8 |
12:b0d19e915d96 | 207 | datetime_hwrtc->u32Hour = datetime_tm->tm_hour; |
ccli8 |
12:b0d19e915d96 | 208 | datetime_hwrtc->u32TimeScale = RTC_CLOCK_24; |
ccli8 |
12:b0d19e915d96 | 209 | datetime_hwrtc->u32Minute = datetime_tm->tm_min; |
ccli8 |
12:b0d19e915d96 | 210 | datetime_hwrtc->u32Second = datetime_tm->tm_sec; |
ccli8 |
12:b0d19e915d96 | 211 | } |