circuitpython/ports/renesas-ra/ra/ra_rtc.c

431 lines
12 KiB
C
Raw Normal View History

/*
* 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 <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include "hal_data.h"
#include "ra_config.h"
#include "ra_rtc.h"
#include <stdio.h>
#include <stdlib.h>
#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