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