Nuvoton
/
NuMaker-mbed-tickless-example
NuMaker tickless example
wakeup_rtc.cpp@12:b0d19e915d96, 2018-04-13 (annotated)
- Committer:
- ccli8
- Date:
- Fri Apr 13 10:48:07 2018 +0800
- Revision:
- 12:b0d19e915d96
- Parent:
- 11:0c4b39c54af2
- Child:
- 16:ed2c228cbc9c
Fix RTC alarm doesn't work with Mbed OS 5.8.0 or afterwards
This was caused by rework of RTC port released with Mbed OS 5.8.0.
For RTC alarm function, application needs to control RTC H/W directly
but collides with reworked RTC port.
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 |
4:da41f0e17d5a | 86 | int32_t sem_tokens = sem_rtc.wait(osWaitForever); |
ccli8 |
4:da41f0e17d5a | 87 | if (sem_tokens < 1) { |
ccli8 |
4:da41f0e17d5a | 88 | printf("RTC Alarm fails with Semaphore.wait(): %d\n", sem_tokens); |
ccli8 |
4:da41f0e17d5a | 89 | } |
ccli8 |
4:da41f0e17d5a | 90 | else { |
ccli8 |
4:da41f0e17d5a | 91 | /* Re-schedule RTC alarm in 3 secs */ |
ccli8 |
4:da41f0e17d5a | 92 | schedule_rtc_alarm(3); |
ccli8 |
4:da41f0e17d5a | 93 | } |
ccli8 |
4:da41f0e17d5a | 94 | } |
ccli8 |
4:da41f0e17d5a | 95 | } |
ccli8 |
4:da41f0e17d5a | 96 | |
ccli8 |
4:da41f0e17d5a | 97 | void schedule_rtc_alarm(uint32_t secs) |
ccli8 |
4:da41f0e17d5a | 98 | { |
ccli8 |
1:eb1da9d36e12 | 99 | /* time() will call set_time(0) internally to set timestamp if rtc is not yet enabled, where the 0 timestamp |
ccli8 |
1:eb1da9d36e12 | 100 | * corresponds to 00:00 hours, Jan 1, 1970 UTC. But Nuvoton mcu's rtc supports calendar since 2000 and 1970 |
ccli8 |
1:eb1da9d36e12 | 101 | * is not supported. For this test, a timestamp after 2000 is explicitly set. */ |
ccli8 |
1:eb1da9d36e12 | 102 | { |
ccli8 |
1:eb1da9d36e12 | 103 | static bool time_inited = false; |
ccli8 |
1:eb1da9d36e12 | 104 | |
ccli8 |
1:eb1da9d36e12 | 105 | if (! time_inited) { |
ccli8 |
1:eb1da9d36e12 | 106 | time_inited = true; |
ccli8 |
1:eb1da9d36e12 | 107 | |
ccli8 |
1:eb1da9d36e12 | 108 | #define CUSTOM_TIME 1256729737 |
ccli8 |
1:eb1da9d36e12 | 109 | set_time(CUSTOM_TIME); // Set RTC time to Wed, 28 Oct 2009 11:35:37 |
ccli8 |
1:eb1da9d36e12 | 110 | } |
ccli8 |
1:eb1da9d36e12 | 111 | } |
ccli8 |
1:eb1da9d36e12 | 112 | |
ccli8 |
10:d2e2c79389e1 | 113 | #if defined(TARGET_NUMAKER_PFM_NANO130) |
ccli8 |
10:d2e2c79389e1 | 114 | RTC_DisableInt(RTC_RIER_AIER_Msk); |
ccli8 |
10:d2e2c79389e1 | 115 | #else |
ccli8 |
1:eb1da9d36e12 | 116 | RTC_DisableInt(RTC_INTEN_ALMIEN_Msk); |
ccli8 |
10:d2e2c79389e1 | 117 | #endif |
ccli8 |
1:eb1da9d36e12 | 118 | |
ccli8 |
12:b0d19e915d96 | 119 | /* Schedule RTC alarm |
ccli8 |
12:b0d19e915d96 | 120 | * |
ccli8 |
12:b0d19e915d96 | 121 | * Mbed OS RTC API doesn't support RTC alarm function. To enable it, we need to control RTC H/W directly. |
ccli8 |
12:b0d19e915d96 | 122 | * Because RTC H/W doesn't inevitably record real date time, we cannot calculate alarm time based on time() |
ccli8 |
12:b0d19e915d96 | 123 | * timestamp. Instead, we fetch date time recorded in RTC H/W for our calculation of scheduled alarm time. |
ccli8 |
12:b0d19e915d96 | 124 | * |
ccli8 |
12:b0d19e915d96 | 125 | * Control flow would be: |
ccli8 |
12:b0d19e915d96 | 126 | * 1. Fetch RTC H/W date time. |
ccli8 |
12:b0d19e915d96 | 127 | * 2. Convert RTC H/W date time to timestamp: S_RTC_TIME_DATA_T > struct tm > time_t |
ccli8 |
12:b0d19e915d96 | 128 | * 3. Calculate alarm time based on timestamp above |
ccli8 |
12:b0d19e915d96 | 129 | * 4. Convert calculated timestamp back to RTC H/W date time: time_t > struct tm > S_RTC_TIME_DATA_T |
ccli8 |
12:b0d19e915d96 | 130 | * 5. Control RTC H/W to schedule alarm |
ccli8 |
12:b0d19e915d96 | 131 | */ |
ccli8 |
12:b0d19e915d96 | 132 | time_t t_alarm; |
ccli8 |
12:b0d19e915d96 | 133 | struct tm datetime_tm_alarm; |
ccli8 |
12:b0d19e915d96 | 134 | S_RTC_TIME_DATA_T datetime_hwrtc_alarm; |
ccli8 |
12:b0d19e915d96 | 135 | |
ccli8 |
12:b0d19e915d96 | 136 | /* Fetch RTC H/W date time */ |
ccli8 |
12:b0d19e915d96 | 137 | RTC_GetDateAndTime(&datetime_hwrtc_alarm); |
ccli8 |
1:eb1da9d36e12 | 138 | |
ccli8 |
12:b0d19e915d96 | 139 | /* Convert date time from H/W RTC to struct TM */ |
ccli8 |
12:b0d19e915d96 | 140 | rtc_convert_datetime_hwrtc_to_tm(&datetime_tm_alarm, &datetime_hwrtc_alarm); |
ccli8 |
12:b0d19e915d96 | 141 | |
ccli8 |
12:b0d19e915d96 | 142 | /* Convert date time of struct TM to POSIX time */ |
ccli8 |
12:b0d19e915d96 | 143 | if (! _rtc_maketime(&datetime_tm_alarm, &t_alarm, RTC_FULL_LEAP_YEAR_SUPPORT)) { |
ccli8 |
12:b0d19e915d96 | 144 | printf("%s: _rtc_maketime failed\n", __func__); |
ccli8 |
1:eb1da9d36e12 | 145 | return; |
ccli8 |
1:eb1da9d36e12 | 146 | } |
ccli8 |
1:eb1da9d36e12 | 147 | |
ccli8 |
12:b0d19e915d96 | 148 | /* Calculate RTC alarm time */ |
ccli8 |
12:b0d19e915d96 | 149 | t_alarm += secs; |
ccli8 |
12:b0d19e915d96 | 150 | |
ccli8 |
12:b0d19e915d96 | 151 | /* Convert POSIX time to date time of struct TM */ |
ccli8 |
12:b0d19e915d96 | 152 | if (! _rtc_localtime(t_alarm, &datetime_tm_alarm, RTC_FULL_LEAP_YEAR_SUPPORT)) { |
ccli8 |
12:b0d19e915d96 | 153 | printf("%s: _rtc_localtime failed\n", __func__); |
ccli8 |
12:b0d19e915d96 | 154 | return; |
ccli8 |
12:b0d19e915d96 | 155 | } |
ccli8 |
12:b0d19e915d96 | 156 | |
ccli8 |
12:b0d19e915d96 | 157 | /* Convert date time from struct TM to H/W RTC */ |
ccli8 |
12:b0d19e915d96 | 158 | rtc_convert_datetime_tm_to_hwrtc(&datetime_hwrtc_alarm, &datetime_tm_alarm); |
ccli8 |
1:eb1da9d36e12 | 159 | |
ccli8 |
12:b0d19e915d96 | 160 | /* Control RTC H/W to schedule alarm */ |
ccli8 |
12:b0d19e915d96 | 161 | RTC_SetAlarmDateAndTime(&datetime_hwrtc_alarm); |
ccli8 |
12:b0d19e915d96 | 162 | /* NOTE: When engine is clocked by low power clock source (LXT/LIRC), we need to wait for 3 engine clocks. */ |
ccli8 |
12:b0d19e915d96 | 163 | wait_us((NU_US_PER_SEC / NU_RTCCLK_PER_SEC) * 3); |
ccli8 |
1:eb1da9d36e12 | 164 | |
ccli8 |
1:eb1da9d36e12 | 165 | /* NOTE: The Mbed RTC HAL implementation of Nuvoton's targets doesn't use interrupt, so we can override vector |
ccli8 |
1:eb1da9d36e12 | 166 | handler (via NVIC_SetVector). */ |
ccli8 |
1:eb1da9d36e12 | 167 | /* NOTE: The name of symbol PWRWU_IRQHandler is mangled in C++ and cannot override that in startup file in C. |
ccli8 |
1:eb1da9d36e12 | 168 | * So the NVIC_SetVector call cannot be left out. */ |
ccli8 |
1:eb1da9d36e12 | 169 | NVIC_SetVector(RTC_IRQn, (uint32_t) RTC_IRQHandler); |
ccli8 |
1:eb1da9d36e12 | 170 | NVIC_EnableIRQ(RTC_IRQn); |
ccli8 |
1:eb1da9d36e12 | 171 | /* Enable RTC alarm interrupt and wake-up function will be enabled also */ |
ccli8 |
10:d2e2c79389e1 | 172 | #if defined(TARGET_NUMAKER_PFM_NANO130) |
ccli8 |
10:d2e2c79389e1 | 173 | RTC_EnableInt(RTC_RIER_AIER_Msk); |
ccli8 |
10:d2e2c79389e1 | 174 | #else |
ccli8 |
1:eb1da9d36e12 | 175 | RTC_EnableInt(RTC_INTEN_ALMIEN_Msk); |
ccli8 |
10:d2e2c79389e1 | 176 | #endif |
ccli8 |
1:eb1da9d36e12 | 177 | } |
ccli8 |
12:b0d19e915d96 | 178 | |
ccli8 |
12:b0d19e915d96 | 179 | /* |
ccli8 |
12:b0d19e915d96 | 180 | struct tm |
ccli8 |
12:b0d19e915d96 | 181 | tm_sec seconds after the minute 0-61 |
ccli8 |
12:b0d19e915d96 | 182 | tm_min minutes after the hour 0-59 |
ccli8 |
12:b0d19e915d96 | 183 | tm_hour hours since midnight 0-23 |
ccli8 |
12:b0d19e915d96 | 184 | tm_mday day of the month 1-31 |
ccli8 |
12:b0d19e915d96 | 185 | tm_mon months since January 0-11 |
ccli8 |
12:b0d19e915d96 | 186 | tm_year years since 1900 |
ccli8 |
12:b0d19e915d96 | 187 | tm_wday days since Sunday 0-6 |
ccli8 |
12:b0d19e915d96 | 188 | tm_yday days since January 1 0-365 |
ccli8 |
12:b0d19e915d96 | 189 | tm_isdst Daylight Saving Time flag |
ccli8 |
12:b0d19e915d96 | 190 | */ |
ccli8 |
12:b0d19e915d96 | 191 | static void rtc_convert_datetime_hwrtc_to_tm(struct tm *datetime_tm, const S_RTC_TIME_DATA_T *datetime_hwrtc) |
ccli8 |
12:b0d19e915d96 | 192 | { |
ccli8 |
12:b0d19e915d96 | 193 | datetime_tm->tm_year = datetime_hwrtc->u32Year - NU_TM_YEAR0; |
ccli8 |
12:b0d19e915d96 | 194 | datetime_tm->tm_mon = datetime_hwrtc->u32Month - 1; |
ccli8 |
12:b0d19e915d96 | 195 | datetime_tm->tm_mday = datetime_hwrtc->u32Day; |
ccli8 |
12:b0d19e915d96 | 196 | datetime_tm->tm_wday = datetime_hwrtc->u32DayOfWeek; |
ccli8 |
12:b0d19e915d96 | 197 | datetime_tm->tm_hour = datetime_hwrtc->u32Hour; |
ccli8 |
12:b0d19e915d96 | 198 | if (datetime_hwrtc->u32TimeScale == RTC_CLOCK_12 && datetime_hwrtc->u32AmPm == RTC_PM) { |
ccli8 |
12:b0d19e915d96 | 199 | datetime_tm->tm_hour += 12; |
ccli8 |
12:b0d19e915d96 | 200 | } |
ccli8 |
12:b0d19e915d96 | 201 | datetime_tm->tm_min = datetime_hwrtc->u32Minute; |
ccli8 |
12:b0d19e915d96 | 202 | datetime_tm->tm_sec = datetime_hwrtc->u32Second; |
ccli8 |
12:b0d19e915d96 | 203 | } |
ccli8 |
12:b0d19e915d96 | 204 | |
ccli8 |
12:b0d19e915d96 | 205 | static void rtc_convert_datetime_tm_to_hwrtc(S_RTC_TIME_DATA_T *datetime_hwrtc, const struct tm *datetime_tm) |
ccli8 |
12:b0d19e915d96 | 206 | { |
ccli8 |
12:b0d19e915d96 | 207 | datetime_hwrtc->u32Year = datetime_tm->tm_year + NU_TM_YEAR0; |
ccli8 |
12:b0d19e915d96 | 208 | datetime_hwrtc->u32Month = datetime_tm->tm_mon + 1; |
ccli8 |
12:b0d19e915d96 | 209 | datetime_hwrtc->u32Day = datetime_tm->tm_mday; |
ccli8 |
12:b0d19e915d96 | 210 | datetime_hwrtc->u32DayOfWeek = datetime_tm->tm_wday; |
ccli8 |
12:b0d19e915d96 | 211 | datetime_hwrtc->u32Hour = datetime_tm->tm_hour; |
ccli8 |
12:b0d19e915d96 | 212 | datetime_hwrtc->u32TimeScale = RTC_CLOCK_24; |
ccli8 |
12:b0d19e915d96 | 213 | datetime_hwrtc->u32Minute = datetime_tm->tm_min; |
ccli8 |
12:b0d19e915d96 | 214 | datetime_hwrtc->u32Second = datetime_tm->tm_sec; |
ccli8 |
12:b0d19e915d96 | 215 | } |