NuMaker tickless example

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?

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 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 }