|
| 1 | +/****************************************************************************** |
| 2 | + * The MIT License |
| 3 | + * |
| 4 | + * Copyright (c) 2010 LeafLabs LLC. |
| 5 | + * |
| 6 | + * Permission is hereby granted, free of charge, to any person |
| 7 | + * obtaining a copy of this software and associated documentation |
| 8 | + * files (the "Software"), to deal in the Software without |
| 9 | + * restriction, including without limitation the rights to use, copy, |
| 10 | + * modify, merge, publish, distribute, sublicense, and/or sell copies |
| 11 | + * of the Software, and to permit persons to whom the Software is |
| 12 | + * furnished to do so, subject to the following conditions: |
| 13 | + * |
| 14 | + * The above copyright notice and this permission notice shall be |
| 15 | + * included in all copies or substantial portions of the Software. |
| 16 | + * |
| 17 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| 18 | + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| 19 | + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| 20 | + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
| 21 | + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| 22 | + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| 23 | + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 24 | + * SOFTWARE. |
| 25 | + *****************************************************************************/ |
| 26 | + |
| 27 | +/** |
| 28 | + Inspired of the F1xx version adapted for the F4xx, not much F1xx left. |
| 29 | + author : Martin Ayotte, 2015. |
| 30 | + */ |
| 31 | + |
| 32 | +#include "RTClock.h" |
| 33 | + |
| 34 | +static rtc_dev rtc = { |
| 35 | + .regs = RTC_BASE, |
| 36 | +// .handlers = { [NR_RTC_HANDLERS - 1] = 0 }, |
| 37 | +}; |
| 38 | + |
| 39 | +rtc_dev *RTC = &rtc; |
| 40 | + |
| 41 | + |
| 42 | +RTClock::RTClock() { |
| 43 | + RTClock(RTCSEL_HSE, 7999, 124); |
| 44 | +} |
| 45 | + |
| 46 | +RTClock::RTClock(rtc_clk_src src) { |
| 47 | + RTClock(src, 0, 0); |
| 48 | +} |
| 49 | + |
| 50 | +RTClock::RTClock(rtc_clk_src src, uint16 sync_prescaler, uint16 async_prescaler) { |
| 51 | + uint32 t = 0; |
| 52 | + RCC_BASE->APB1ENR |= RCC_APB1RSTR_PWRRST; |
| 53 | + dbg_printf("RCC_BASE->APB1ENR = %08X\r\n", RCC_BASE->APB1ENR); |
| 54 | + dbg_printf("before bkp_init\r\n"); |
| 55 | + bkp_init(); // turn on peripheral clocks to PWR and BKP and reset the backup domain via RCC registers. |
| 56 | + // (we reset the backup domain here because we must in order to change the rtc clock source). |
| 57 | + dbg_printf("before bkp_disable_writes\r\n"); |
| 58 | + bkp_disable_writes(); |
| 59 | + dbg_printf("before bkp_enable_writes\r\n"); |
| 60 | + bkp_enable_writes(); // enable writes to the backup registers and the RTC registers via the DBP bit in the PWR control register |
| 61 | + dbg_printf("RCC_BASE->CFGR = %08X\r\n", RCC_BASE->CFGR); |
| 62 | + RCC_BASE->CFGR |= (0x08 << 16); // Set the RTCPRE to HSE / 8. |
| 63 | + dbg_printf("RCC_BASE->CFGR = %08X\r\n", RCC_BASE->CFGR); |
| 64 | + |
| 65 | + switch (src) { |
| 66 | + case RTCSEL_LSE : |
| 67 | + dbg_printf("Preparing RTC for LSE mode\r\n"); |
| 68 | + if ((RCC_BASE->BDCR & 0x00000300) != 0x0100) |
| 69 | + RCC_BASE->BDCR = 0x00010000; // Reset the entire Backup domain |
| 70 | + RCC_BASE->BDCR = 0x00008101; |
| 71 | + dbg_printf("RCC_BASE->BDCR = %08X\r\n", RCC_BASE->BDCR); |
| 72 | + while (!(RCC_BASE->BDCR & 0x00000002)) { |
| 73 | + if (++t > 1000000) { |
| 74 | + dbg_printf("RCC_BASE->BDCR.LSERDY Timeout !\r\n"); |
| 75 | + dbg_printf("RCC_BASE->BDCR = %08X\r\n", RCC_BASE->BDCR); |
| 76 | + return; |
| 77 | + } |
| 78 | + } |
| 79 | + dbg_printf("RCC_BASE->BDCR = %08X\r\n", RCC_BASE->BDCR); |
| 80 | + rtc_enter_config_mode(); |
| 81 | + if (sync_prescaler == 0 && async_prescaler == 0) |
| 82 | + RTC_BASE->PRER = 255 | (127 << 16); |
| 83 | + else |
| 84 | + RTC_BASE->PRER = sync_prescaler | (async_prescaler << 16); |
| 85 | + break; |
| 86 | + case RTCSEL_LSI : |
| 87 | + dbg_printf("Preparing RTC for LSI mode\r\n"); |
| 88 | + if ((RCC_BASE->BDCR & 0x00000300) != 0x0200) |
| 89 | + RCC_BASE->BDCR = 0x00010000; // Reset the entire Backup domain |
| 90 | + RCC_BASE->BDCR = 0x00008204; |
| 91 | + RCC_BASE->CSR |= 0x00000001; |
| 92 | + dbg_printf("RCC_BASE->BDCR = %08X\r\n", RCC_BASE->BDCR); |
| 93 | + while (!(RCC_BASE->CSR & 0x00000002)) { |
| 94 | + if (++t > 1000000) { |
| 95 | + dbg_printf("RCC_BASE->CSR.LSIRDY Timeout !\r\n"); |
| 96 | + dbg_printf("RCC_BASE->CSR = %08X\r\n", RCC_BASE->CSR); |
| 97 | + return; |
| 98 | + } |
| 99 | + } |
| 100 | + dbg_printf("RCC_BASE->BDCR = %08X\r\n", RCC_BASE->BDCR); |
| 101 | + rtc_enter_config_mode(); |
| 102 | + if (sync_prescaler == 0 && async_prescaler == 0) |
| 103 | + RTC_BASE->PRER = 249 | (127 << 16); |
| 104 | + else |
| 105 | + RTC_BASE->PRER = sync_prescaler | (async_prescaler << 16); |
| 106 | + break; |
| 107 | + case RTCSEL_DEFAULT: |
| 108 | + case RTCSEL_HSE : |
| 109 | + dbg_printf("Preparing RTC for HSE mode\r\n"); |
| 110 | + if ((RCC_BASE->BDCR & 0x00000300) != 0x0300) |
| 111 | + RCC_BASE->BDCR = 0x00010000; // Reset the entire Backup domain |
| 112 | + RCC_BASE->BDCR = 0x00008304; |
| 113 | + dbg_printf("RCC_BASE->BDCR = %08X\r\n", RCC_BASE->BDCR); |
| 114 | + rtc_enter_config_mode(); |
| 115 | + if (sync_prescaler == 0 && async_prescaler == 0) |
| 116 | + RTC_BASE->PRER = 7999 | (124 << 16); |
| 117 | + else |
| 118 | + RTC_BASE->PRER = sync_prescaler | (async_prescaler << 16); |
| 119 | + break; |
| 120 | + case RTCSEL_NONE: |
| 121 | + dbg_printf("Preparing RTC for NONE mode\r\n"); |
| 122 | + if ((RCC_BASE->BDCR & 0x00000300) != 0x0000) |
| 123 | + RCC_BASE->BDCR = 0x00010000; // Reset the entire Backup domain |
| 124 | + RCC_BASE->BDCR = RCC_BDCR_RTCSEL_NONE; |
| 125 | + //do nothing. Have a look at the clocks to see the diff between NONE and DEFAULT |
| 126 | + break; |
| 127 | + } |
| 128 | + RCC_BASE->CR |= 0x00000040; // Turn to 24hrs mode |
| 129 | +// dbg_printf("before rtc_clear_sync\r\n"); |
| 130 | +// rtc_clear_sync(); |
| 131 | +// dbg_printf("before rtc_wait_sync\r\n"); |
| 132 | +// rtc_wait_sync(); |
| 133 | + rtc_exit_config_mode(); |
| 134 | + dbg_printf("end of rtc_init\r\n"); |
| 135 | +} |
| 136 | + |
| 137 | +/* |
| 138 | +RTClock::~RTClock() { |
| 139 | + //to implement |
| 140 | +} |
| 141 | +*/ |
| 142 | + |
| 143 | +void RTClock::setTime (time_t time_stamp) { |
| 144 | + unsigned char years = 0; |
| 145 | + unsigned char months = 0; |
| 146 | + unsigned char monthLength = 0; |
| 147 | + unsigned char wdays = 0; |
| 148 | + unsigned char hours = 0; |
| 149 | + unsigned char mins = 0; |
| 150 | + unsigned char secs = 0; |
| 151 | + unsigned long days; |
| 152 | + |
| 153 | + secs = time_stamp % 60; |
| 154 | + time_stamp /= 60; // now it is minutes |
| 155 | + mins = time_stamp % 60; |
| 156 | + time_stamp /= 60; // now it is hours |
| 157 | + hours = time_stamp % 24; |
| 158 | + time_stamp /= 24; // now it is days |
| 159 | + wdays = ((time_stamp + 4) % 7) + 1; // Sunday is day 1 |
| 160 | + |
| 161 | + while((unsigned)(days += (LEAP_YEAR(years) ? 366 : 365)) <= time_stamp) { |
| 162 | + years++; |
| 163 | + } |
| 164 | + |
| 165 | + days -= LEAP_YEAR(years) ? 366 : 365; |
| 166 | + time_stamp -= days; // now it is days in this year, starting at 0 |
| 167 | + |
| 168 | + for (months = 0; months < 12; months++) { |
| 169 | + if (months == 1) { // february |
| 170 | + if (LEAP_YEAR(years)) { |
| 171 | + monthLength = 29; |
| 172 | + } else { |
| 173 | + monthLength = 28; |
| 174 | + } |
| 175 | + } else { |
| 176 | + monthLength = monthDays[months]; |
| 177 | + } |
| 178 | + |
| 179 | + if (time_stamp >= monthLength) { |
| 180 | + time_stamp -= monthLength; |
| 181 | + } else { |
| 182 | + break; |
| 183 | + } |
| 184 | + } |
| 185 | + months++; // jan is month 1 |
| 186 | + days = time_stamp + 1; // day of month |
| 187 | + rtc_enter_config_mode(); |
| 188 | + RTC_BASE->TR = ((hours / 10) << 20) | ((hours % 10) << 16) | ((mins / 10) << 12) | ((mins % 10) << 8) | ((secs / 10) << 4) | (secs % 10); |
| 189 | + RTC_BASE->DR = ((years / 10) << 20) | ((years % 10) << 16) | (wdays << 13) | ((months / 10) << 12) | ((months % 10) << 8) | ((days / 10) << 4) | (days % 10); |
| 190 | + rtc_exit_config_mode(); |
| 191 | +} |
| 192 | + |
| 193 | +void RTClock::setTime (struct tm* tm_ptr) { |
| 194 | + rtc_enter_config_mode(); |
| 195 | + RTC_BASE->TR = ((tm_ptr->tm_hour / 10) << 20) | ((tm_ptr->tm_hour % 10) << 16) | |
| 196 | + ((tm_ptr->tm_min / 10) << 12) | ((tm_ptr->tm_min % 10) << 8) | |
| 197 | + ((tm_ptr->tm_sec / 10) << 4) | (tm_ptr->tm_sec % 10); |
| 198 | + RTC_BASE->DR = ((tm_ptr->tm_year / 10) << 20) | ((tm_ptr->tm_year % 10) << 16) | (tm_ptr->tm_wday << 13) | |
| 199 | + ((tm_ptr->tm_mon / 10) << 12) | ((tm_ptr->tm_mon % 10) << 8) | |
| 200 | + ((tm_ptr->tm_mday / 10) << 4) | (tm_ptr->tm_mday % 10); |
| 201 | + rtc_exit_config_mode(); |
| 202 | +} |
| 203 | + |
| 204 | +time_t RTClock::getTime() { |
| 205 | + int years = 10 * ((RTC_BASE->DR & 0x00F00000) >> 20) + ((RTC_BASE->DR & 0x000F0000) >> 16); |
| 206 | + int months = 10 * ((RTC_BASE->DR & 0x00001000) >> 12) + ((RTC_BASE->DR & 0x00000F00) >> 8); |
| 207 | + int days = 10 * ((RTC_BASE->DR & 0x00000030) >> 4) + (RTC_BASE->DR & 0x000000F); |
| 208 | + int hours = 10 * ((RTC_BASE->TR & 0x00300000) >> 20) + ((RTC_BASE->TR & 0x000F0000) >> 16); |
| 209 | + int mins = 10 * ((RTC_BASE->TR & 0x00007000) >> 12) + ((RTC_BASE->TR & 0x0000F00) >> 8); |
| 210 | + int secs = 10 * ((RTC_BASE->TR & 0x00000070) >> 4) + (RTC_BASE->TR & 0x0000000F); |
| 211 | + // seconds from 1970 till 1 jan 00:00:00 of the given year |
| 212 | + time_t t = (years + 30) * SECS_PER_DAY * 365; |
| 213 | + for (int i = 0; i < years; i++) { |
| 214 | + if (LEAP_YEAR(i)) { |
| 215 | + t += SECS_PER_DAY; // add extra days for leap years |
| 216 | + } |
| 217 | + } |
| 218 | + // add days for this year, months start from 1 |
| 219 | + for (int i = 1; i < months; i++) { |
| 220 | + if ( (i == 2) && LEAP_YEAR(years)) { |
| 221 | + t += SECS_PER_DAY * 29; |
| 222 | + } else { |
| 223 | + t += SECS_PER_DAY * monthDays[i - 1]; //monthDays array starts from 0 |
| 224 | + } |
| 225 | + } |
| 226 | + t += (days - 1) * SECS_PER_DAY + hours * SECS_PER_HOUR + mins * SECS_PER_MIN + secs; |
| 227 | + return t; |
| 228 | +} |
| 229 | + |
| 230 | +struct tm* RTClock::getTime(struct tm* tm_ptr) { |
| 231 | + tm_ptr->tm_year = 10 * ((RTC_BASE->DR & 0x00F00000) >> 20) + ((RTC_BASE->DR & 0x000F0000) >> 16); |
| 232 | + tm_ptr->tm_mon = 10 * ((RTC_BASE->DR & 0x00001000) >> 12) + ((RTC_BASE->DR & 0x00000F00) >> 8); |
| 233 | + tm_ptr->tm_mday = 10 * ((RTC_BASE->DR & 0x00000030) >> 4) + (RTC_BASE->DR & 0x000000F); |
| 234 | + tm_ptr->tm_hour = 10 * ((RTC_BASE->TR & 0x00300000) >> 20) + ((RTC_BASE->TR & 0x000F0000) >> 16); |
| 235 | + tm_ptr->tm_min = 10 * ((RTC_BASE->TR & 0x00007000) >> 12) + ((RTC_BASE->TR & 0x0000F00) >> 8); |
| 236 | + tm_ptr->tm_sec = 10 * ((RTC_BASE->TR & 0x00000070) >> 4) + (RTC_BASE->TR & 0x0000000F); |
| 237 | + return tm_ptr; |
| 238 | +} |
| 239 | + |
| 240 | +void RTClock::createAlarm(voidFuncPtr function, time_t alarm_time_t) { |
| 241 | +// rtc_set_alarm(alarm_time_t); //must be int... for standardization sake. |
| 242 | +// rtc_attach_interrupt(RTC_ALARM_SPECIFIC_INTERRUPT, function); |
| 243 | +} |
| 244 | + |
| 245 | +void RTClock::createAlarm(voidFuncPtr function, tm* alarm_tm) { |
| 246 | +// time_t alarm = mktime(alarm_tm);//convert to time_t |
| 247 | +// createAlarm(function, alarm); |
| 248 | +} |
| 249 | + |
| 250 | +void RTClock::attachSecondsInterrupt(voidFuncPtr function) { |
| 251 | +// rtc_attach_interrupt(RTC_SECONDS_INTERRUPT, function); |
| 252 | +} |
| 253 | + |
| 254 | +void RTClock::detachSecondsInterrupt() { |
| 255 | +// rtc_detach_interrupt(RTC_SECONDS_INTERRUPT); |
| 256 | +} |
| 257 | + |
| 258 | +void RTClock::setAlarmATime (tm * tm_ptr, bool hours_match, bool mins_match, bool secs_match, bool date_match) { |
| 259 | + rtc_enter_config_mode(); |
| 260 | + unsigned int bits = ((tm_ptr->tm_mday / 10) << 28) | ((tm_ptr->tm_mday % 10) << 24) | |
| 261 | + ((tm_ptr->tm_hour / 10) << 20) | ((tm_ptr->tm_hour % 10) << 16) | |
| 262 | + ((tm_ptr->tm_min / 10) << 12) | ((tm_ptr->tm_min % 10) << 8) | |
| 263 | + ((tm_ptr->tm_sec / 10) << 4) | (tm_ptr->tm_sec % 10); |
| 264 | + if (!date_match) bits |= (1 << 31); |
| 265 | + if (!hours_match) bits |= (1 << 23); |
| 266 | + if (!mins_match) bits |= (1 << 15); |
| 267 | + if (!secs_match) bits |= (1 << 7); |
| 268 | + RTC_BASE->ALRMAR = bits; |
| 269 | + RTC_BASE->CR |= (1 << RTC_CR_ALRAIE_BIT); // turn on ALRAIE |
| 270 | + rtc_exit_config_mode(); |
| 271 | +} |
| 272 | + |
| 273 | + |
| 274 | +void RTClock::setAlarmATime (time_t alarm_time, bool hours_match, bool mins_match, bool secs_match, bool date_match) { |
| 275 | + struct tm* tm_ptr = gmtime(&alarm_time); |
| 276 | + setAlarmATime(tm_ptr, hours_match, mins_match, secs_match, date_match); |
| 277 | +} |
| 278 | + |
| 279 | + |
| 280 | +void RTClock::turnOffAlarmA() { |
| 281 | + rtc_enter_config_mode(); |
| 282 | + RTC_BASE->CR &= ~(1 << RTC_CR_ALRAIE_BIT); // turn off ALRAIE |
| 283 | + rtc_exit_config_mode(); |
| 284 | +} |
| 285 | + |
| 286 | + |
| 287 | +void RTClock::setAlarmBTime (tm * tm_ptr, bool hours_match, bool mins_match, bool secs_match, bool date_match) { |
| 288 | + rtc_enter_config_mode(); |
| 289 | + unsigned int bits = ((tm_ptr->tm_mday / 10) << 28) | ((tm_ptr->tm_mday % 10) << 24) | |
| 290 | + ((tm_ptr->tm_hour / 10) << 20) | ((tm_ptr->tm_hour % 10) << 16) | |
| 291 | + ((tm_ptr->tm_min / 10) << 12) | ((tm_ptr->tm_min % 10) << 8) | |
| 292 | + ((tm_ptr->tm_sec / 10) << 4) | (tm_ptr->tm_sec % 10); |
| 293 | + if (!date_match) bits |= (1 << 31); |
| 294 | + if (!hours_match) bits |= (1 << 23); |
| 295 | + if (!mins_match) bits |= (1 << 15); |
| 296 | + if (!secs_match) bits |= (1 << 7); |
| 297 | + RTC_BASE->ALRMBR = bits; |
| 298 | + RTC_BASE->CR |= (1 << RTC_CR_ALRBIE_BIT); // turn on ALRBIE |
| 299 | + rtc_exit_config_mode(); |
| 300 | +} |
| 301 | + |
| 302 | + |
| 303 | +void RTClock::setAlarmBTime (time_t alarm_time, bool hours_match, bool mins_match, bool secs_match, bool date_match) { |
| 304 | + struct tm* tm_ptr = gmtime(&alarm_time); |
| 305 | + setAlarmBTime(tm_ptr, hours_match, mins_match, secs_match, date_match); |
| 306 | +} |
| 307 | + |
| 308 | + |
| 309 | +void RTClock::turnOffAlarmB() { |
| 310 | + rtc_enter_config_mode(); |
| 311 | + RTC_BASE->CR &= ~(1 << RTC_CR_ALRBIE_BIT); // turn off ALRBIE |
| 312 | + rtc_exit_config_mode(); |
| 313 | +} |
| 314 | + |
| 315 | + |
| 316 | +void RTClock::setPeriodicWakeup(uint16 period) { |
| 317 | + rtc_enter_config_mode(); |
| 318 | + dbg_printf("before setting RTC_BASE->WUTR\r\n"); |
| 319 | + RTC_BASE->WUTR = period; // set the period |
| 320 | + dbg_printf("before setting RTC_BASE->CR.WUCKSEL\r\n"); |
| 321 | + RTC_BASE->CR &= ~(3); RTC_BASE->CR |= 4; // Set the WUCKSEL to 1Hz (0x00000004) |
| 322 | + if (period == 0) |
| 323 | + RTC_BASE->CR &= ~(1 << RTC_CR_WUTIE_BIT); // if period is 0, turn off periodic wakeup interrupt. |
| 324 | + else { |
| 325 | + dbg_printf("before turn ON RTC_BASE->CR.WUTIE\r\n"); |
| 326 | + RTC_BASE->CR |= (1 << RTC_CR_WUTIE_BIT); // turn on WUTIE |
| 327 | + } |
| 328 | + dbg_printf("RCC_BASE->CR = %08X\r\n", RCC_BASE->CR); |
| 329 | + rtc_exit_config_mode(); |
| 330 | + rtc_enable_wakeup_event(); |
| 331 | + nvic_irq_enable(NVIC_RTC); |
| 332 | + nvic_irq_enable(NVIC_RTCALARM); |
| 333 | + dbg_printf("setPeriodicWakeup() done !\r\n"); |
| 334 | +} |
| 335 | + |
| 336 | +extern "C" { |
| 337 | +void __irq_rtc(void) { |
| 338 | + dbg_printf("__irq_rtc() called !\r\n"); |
| 339 | + *bb_perip(&EXTI_BASE->PR, EXTI_RTC_WAKEUP_BIT) = 1; |
| 340 | +} |
| 341 | +void __irq_rtcalarm(void) { |
| 342 | + dbg_printf("__irq_rtcalarm() called !\r\n"); |
| 343 | + *bb_perip(&EXTI_BASE->PR, EXTI_RTC_ALARM_BIT) = 1; |
| 344 | +} |
| 345 | +} |
0 commit comments