NuMaker tickless example

Committer:
SHLIU1@OANBE02333.nuvoton.com
Date:
Thu Feb 25 15:25:00 2021 +0800
Revision:
21:5424989f037f
Parent:
20:759aab916d47
Child:
23:a2a48e3ef326
Support the both V5.X and V6.X for mbed-os

Who changed what in which revision?

UserRevisionLine numberNew 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 20:759aab916d47 13 #if defined(TARGET_NANO100)
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 20:759aab916d47 36 #if defined(TARGET_NANO100)
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 18:e236110ce841 59 #elif defined(TARGET_M451) || defined(TARGET_M480) || defined(TARGET_M251)
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 20:759aab916d47 116 #if defined(TARGET_NANO100)
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 20:759aab916d47 175 #if defined(TARGET_NANO100)
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 }