/* * The MIT License (MIT) * * Copyright (c) 2021 Renesas Electronics Corporation * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include "hal_data.h" #include "ra_config.h" #include "ra_rtc.h" #include #include #include "hal_data.h" #include "ra_rtc.h" static R_RTC_Type *rtc_reg = (R_RTC_Type *)0x40044000; static R_SYSTEM_Type *system_reg = (R_SYSTEM_Type *)0x4001E000; #if defined(VECTOR_NUMBER_RTC_ALARM) ra_rtc_cb_t ra_rtc_func_alarm = NULL; void *ra_rtc_param_alarm = NULL; #endif #if defined(VECTOR_NUMBER_RTC_PERIOD) ra_rtc_cb_t ra_rtc_func_period = NULL; void *ra_rtc_param_period = NULL; #endif static inline uint8_t int_to_bcd(int num) { return (uint8_t)(((num / 10) << 4) | (num % 10)); } static inline int bcd_to_int(uint8_t bcd) { return (int)(((bcd >> 4) * 10) + (bcd & 0x0F)); } int ra_rtc_get_year(void) { return bcd_to_int((uint8_t)rtc_reg->RYRCNT) + 2000; } int ra_rtc_get_month(void) { return bcd_to_int((uint8_t)rtc_reg->RMONCNT); } int ra_rtc_get_date(void) { return bcd_to_int((uint8_t)rtc_reg->RDAYCNT); } int ra_rtc_get_hour(void) { return bcd_to_int((uint8_t)(0x3f & rtc_reg->RHRCNT)); } int ra_rtc_get_minute(void) { return bcd_to_int((uint8_t)rtc_reg->RMINCNT); } int ra_rtc_get_second(void) { return bcd_to_int((uint8_t)rtc_reg->RSECCNT); } int ra_rtc_get_weekday(void) { return bcd_to_int((uint8_t)rtc_reg->RWKCNT); } #if defined(VECTOR_NUMBER_RTC_PERIOD) void ra_rtc_period_on() { // Enable periodic interrupt rtc_reg->RCR1_b.PIE = 1; while (!rtc_reg->RCR1_b.PIE) { ; } // Enable NVIC RTC Alarm interrupt R_BSP_IrqCfg((IRQn_Type const)RTC_PERIOD_IRQn, (uint32_t)RA_PRI_RTC_WKUP, (void *)NULL); R_BSP_IrqEnable((IRQn_Type const)RTC_PERIOD_IRQn); } void ra_rtc_period_off() { // Disable NVIC RTC Alarm interrupt R_BSP_IrqDisable((IRQn_Type const)RTC_PERIOD_IRQn); // Disable periodic interrupt rtc_reg->RCR1_b.PIE = 0; while (rtc_reg->RCR1_b.PIE) { ; } } // period // 6 : every 1/256(s) // 7 : every 1/128(s) // 8 : every 1/64(s) // 9 : every 1/32(s) // 10 : every 1/16(s) // 11 : every 1/8(s) // 12 : every 1/4(s) // 13 : every 1/2(s) // 14 : every 1(s) // 15 : every 2(s) void ra_rtc_set_period_time(uint32_t period) { if ((period < 6) | (period > 15)) { return; } ra_rtc_period_off(); uint8_t rcr1 = rtc_reg->RCR1; rcr1 &= (uint8_t) ~R_RTC_RCR1_PES_Msk; rcr1 |= (uint8_t)(period << R_RTC_RCR1_PES_Pos); rtc_reg->RCR1 = rcr1; while (rtc_reg->RCR1 != rcr1) { ; } ra_rtc_period_on(); } void ra_rtc_set_period_func(void *cb, void *param) { ra_rtc_period_off(); ra_rtc_func_period = (ra_rtc_cb_t)cb; ra_rtc_param_period = param; ra_rtc_period_on(); } #endif #if defined(VECTOR_NUMBER_RTC_ALARM) void ra_rtc_alarm_on() { // Enable alarm interrupt rtc_reg->RCR1_b.AIE = 1; while (!rtc_reg->RCR1_b.AIE) { ; } // Enable NVIC RTC Alarm interrupt R_BSP_IrqCfg((IRQn_Type const)RTC_ALARM_IRQn, (uint32_t)RA_PRI_RTC_WKUP, (void *)NULL); R_BSP_IrqEnable((IRQn_Type const)RTC_ALARM_IRQn); } void ra_rtc_alarm_off() { // Disable NVIC RTC Alarm interrupt R_BSP_IrqDisable((IRQn_Type const)RTC_ALARM_IRQn); // Disable alarm interrupt rtc_reg->RCR1_b.AIE = 0; while (rtc_reg->RCR1_b.AIE) { ; } } void ra_rtc_set_alarm_time(int hour, int min, int week_flag) { ra_rtc_alarm_off(); // Configure the alarm rtc_reg->RMINAR = (uint8_t)int_to_bcd(min); rtc_reg->RHRAR = (uint8_t)int_to_bcd(hour); if (week_flag <= 0x06) { rtc_reg->RWKAR = (uint8_t)week_flag; } rtc_reg->RMINAR_b.ENB = 1; rtc_reg->RHRAR_b.ENB = 1; if (week_flag <= 0x06) { rtc_reg->RWKAR_b.ENB = 1; } else { rtc_reg->RWKAR_b.ENB = 0; } ra_rtc_alarm_on(); } void ra_rtc_set_alarm_func(void *cb, void *param) { ra_rtc_period_off(); ra_rtc_func_alarm = (ra_rtc_cb_t)cb; ra_rtc_param_alarm = param; ra_rtc_period_on(); } #endif // adj: adjustment bit (number of sub clocks) // 0 : no adjustment // 0<: // 0>: // aadjp: specify adjustment period // 0: 1 minute (RTC_PERIOD_MINUTE) // 1: 10 seconds (RTC_PERIOD_SECOND) void ra_rtc_set_adjustment(int adj, int aadjp) { int tmp_int; aadjp &= 1; if (adj == 0) { // no adjustment rtc_reg->RADJ = 0x00; while (rtc_reg->RADJ != 0x00) { ; } } else if (adj > 0) { // plus adjustment rtc_reg->RADJ = 0x00; while (rtc_reg->RADJ != 0x00) { ; } // enable auto adjustment rtc_reg->RCR2_b.AADJE = 1; while (rtc_reg->RCR2_b.AADJE != 1) { ; } rtc_reg->RCR2_b.AADJP = aadjp == RTC_PERIOD_MINUTE ? RTC_PERIOD_MINUTE : RTC_PERIOD_SECOND; while (rtc_reg->RCR2_b.AADJP != aadjp) { ; } tmp_int = 0x40 | (0x3F & adj); rtc_reg->RADJ = (uint8_t)tmp_int; while (rtc_reg->RADJ != (uint8_t)tmp_int) { ; } } else { // minus adjustment rtc_reg->RADJ = 0x00; while (rtc_reg->RADJ != 0x00) { ; } // enable adjustment rtc_reg->RCR2_b.AADJE = 1; while (rtc_reg->RCR2_b.AADJE != 1) { ; } rtc_reg->RCR2_b.AADJP = aadjp == RTC_PERIOD_MINUTE ? RTC_PERIOD_MINUTE : RTC_PERIOD_SECOND; while (rtc_reg->RCR2_b.AADJP != aadjp) { ; } tmp_int = 0x80 | (0x3F & abs(adj)); rtc_reg->RADJ = (uint8_t)tmp_int; while (rtc_reg->RADJ != (uint8_t)tmp_int) { ; } } } uint8_t ra_rtc_get_adjustment(void) { return rtc_reg->RADJ; } bool ra_rtc_set_time(ra_rtc_t *time) { // Write 0 to RTC start bit rtc_reg->RCR2_b.START = 0x0; // Wait for start bit to clear while (0 != rtc_reg->RCR2_b.START) { ; } // Alarm enable bits are undefined after a reset, // disable non-required alarm features rtc_reg->RWKAR_b.ENB = 0; rtc_reg->RDAYAR_b.ENB = 0; rtc_reg->RMONAR_b.ENB = 0; rtc_reg->RYRAREN_b.ENB = 0; // Operate RTC in 24-hr mode rtc_reg->RCR2_b.HR24 = 0x1; rtc_reg->RYRCNT = (uint16_t)int_to_bcd(time->year % 100); rtc_reg->RMONCNT = (uint8_t)int_to_bcd(time->month); rtc_reg->RDAYCNT = (uint8_t)int_to_bcd(time->date); rtc_reg->RHRCNT = (uint8_t)int_to_bcd(time->hour); rtc_reg->RMINCNT = (uint8_t)int_to_bcd(time->minute); rtc_reg->RSECCNT = (uint8_t)int_to_bcd(time->second); rtc_reg->RWKCNT = (uint8_t)int_to_bcd(time->weekday); // Start the clock rtc_reg->RCR2_b.START = 0x1; // Wait until the start bit is set to 1 while (1 != rtc_reg->RCR2_b.START) { ; } return true; } bool ra_rtc_get_time(ra_rtc_t *time) { time->year = (uint16_t)(bcd_to_int((uint8_t)rtc_reg->RYRCNT) + 2000); time->month = (uint8_t)bcd_to_int(rtc_reg->RMONCNT); time->date = (uint8_t)bcd_to_int(rtc_reg->RDAYCNT); time->hour = (uint8_t)bcd_to_int((uint8_t)(0x3f & rtc_reg->RHRCNT)); time->minute = (uint8_t)bcd_to_int((uint8_t)rtc_reg->RMINCNT); time->second = (uint8_t)bcd_to_int((uint8_t)rtc_reg->RSECCNT); time->weekday = (uint8_t)bcd_to_int((uint8_t)rtc_reg->RWKCNT); return true; } static void wait(volatile int count) { while (count--) { __asm__ __volatile__ ("nop"); } } // source // 0: subclock // 1: LOCO static void ra_rtc_set_subclock(uint8_t source) { // Set RTC clock input from sub-clock, and supply to RTC module rtc_reg->RCR4_b.RCKSEL = source; if (0 == source) { R_BSP_SoftwareDelay(100, BSP_DELAY_UNITS_MILLISECONDS); } else { R_BSP_SoftwareDelay(200, BSP_DELAY_UNITS_MICROSECONDS); } // Stop the clock rtc_reg->RCR2_b.START = 0x0; // Wait for start bit to clear while (0 != rtc_reg->RCR2_b.START) { ; } if (source == 1) { rtc_reg->RFRH = 0; rtc_reg->RFRL = (uint16_t)0x00ff; // assume 32.768khz } rtc_reg->RCR2_b.CNTMD = 0; while (0 != rtc_reg->RCR2_b.CNTMD) { ; } // Reset the RTC unit rtc_reg->RCR2_b.RESET = 0x1; // Wait until reset is complete while (0 != rtc_reg->RCR2_b.RESET) { ; } // Start the clock rtc_reg->RCR2_b.START = 0x1; // Wait until the start bit is set to 1 while (1 != rtc_reg->RCR2_b.START) { ; } } bool ra_rtc_init(uint8_t source) { system_reg->PRCR = 0xA503; // Check if the MCU has come from a cold start (power on reset) if (0 == system_reg->RSTSR2_b.CWSF) { // cold start system_reg->VBTCR1_b.BPWSWSTP = 1; // Set the warm start flag system_reg->RSTSR2_b.CWSF = 1; // Disable the sub-clock oscillator system_reg->SOSCCR_b.SOSTP = 1; // Wait for register modification to complete while (1 != system_reg->SOSCCR_b.SOSTP) { ; } // Start sub-clock system_reg->SOSCCR_b.SOSTP = 0; // Perform 8 delay iterations for (uint8_t i = 0; i < 8; i++) { // Wait in while loop for ~0.5 seconds wait(0xFFFFE); } } else { // Start sub-clock system_reg->SOSCCR_b.SOSTP = 0; // Wait for the register modification to complete while (0 != system_reg->SOSCCR_b.SOSTP) { ; } } system_reg->PRCR = 0xA500; // call back #if defined(VECTOR_NUMBER_RTC_ALARM) ra_rtc_func_alarm = NULL; #endif #if defined(VECTOR_NUMBER_RTC_PERIOD) ra_rtc_func_period = NULL; #endif if ((rtc_reg->RCR2_b.START == 0) || (rtc_reg->RCR4_b.RCKSEL != source)) { rtc_reg->RCR1 = 0; rtc_reg->RCR2 = 0; ra_rtc_set_subclock(source); } return true; } bool ra_rtc_deinit(void) { #if defined(VECTOR_NUMBER_RTC_ALARM) ra_rtc_alarm_off(); ra_rtc_func_alarm = NULL; ra_rtc_param_alarm = NULL; #endif #if defined(VECTOR_NUMBER_RTC_PERIOD) ra_rtc_period_off(); ra_rtc_func_period = NULL; ra_rtc_param_period = NULL; #endif rtc_reg->RCR4_b.RCKSEL = 1; rtc_reg->RCR1 = 0; return true; } void rtc_alarm_periodic_isr(void) { IRQn_Type irq = R_FSP_CurrentIrqGet(); #if defined(VECTOR_NUMBER_RTC_PERIOD) if (irq == RTC_PERIOD_IRQn) { if (ra_rtc_func_period) { ra_rtc_func_period(ra_rtc_param_period); } } #endif #if defined(VECTOR_NUMBER_RTC_ALARM) if (irq == RTC_ALARM_IRQn) { if (ra_rtc_func_alarm) { ra_rtc_func_alarm(ra_rtc_param_alarm); } } #endif R_BSP_IrqStatusClear(irq); } #if defined(VECTOR_NUMBER_RTC_CARRY) void rtc_carry_isr(void) { IRQn_Type irq = R_FSP_CurrentIrqGet(); R_BSP_IrqStatusClear(irq); } #endif