/********************************** (C) COPYRIGHT ******************************* * File Name : ch32v20x_rtc.c * Author : WCH * Version : V1.0.0 * Date : 2021/06/06 * Description : This file provides all the RTC firmware functions. * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ #include "ch32v20x_rtc.h" /* RTC_Private_Defines */ #define RTC_LSB_MASK ((uint32_t)0x0000FFFF) /* RTC LSB Mask */ #define PRLH_MSB_MASK ((uint32_t)0x000F0000) /* RTC Prescaler MSB Mask */ /********************************************************************* * @fn RTC_ITConfig * * @brief Enables or disables the specified RTC interrupts. * * @param RTC_IT - specifies the RTC interrupts sources to be enabled or disabled. * RTC_IT_OW - Overflow interrupt * RTC_IT_ALR - Alarm interrupt * RTC_IT_SEC - Second interrupt * * @return NewState - new state of the specified RTC interrupts(ENABLE or DISABLE). */ void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState) { if(NewState != DISABLE) { RTC->CTLRH |= RTC_IT; } else { RTC->CTLRH &= (uint16_t)~RTC_IT; } } /********************************************************************* * @fn RTC_EnterConfigMode * * @brief Enters the RTC configuration mode. * * @return none */ void RTC_EnterConfigMode(void) { RTC->CTLRL |= RTC_CTLRL_CNF; } /********************************************************************* * @fn RTC_ExitConfigMode * * @brief Exits from the RTC configuration mode. * * @return none */ void RTC_ExitConfigMode(void) { RTC->CTLRL &= (uint16_t) ~((uint16_t)RTC_CTLRL_CNF); } /********************************************************************* * @fn RTC_GetCounter * * @brief Gets the RTC counter value * * @return RTC counter value */ uint32_t RTC_GetCounter(void) { uint16_t high1 = 0, high2 = 0, low = 0; high1 = RTC->CNTH; low = RTC->CNTL; high2 = RTC->CNTH; if(high1 != high2) { return (((uint32_t)high2 << 16) | RTC->CNTL); } else { return (((uint32_t)high1 << 16) | low); } } /********************************************************************* * @fn RTC_SetCounter * * @brief Sets the RTC counter value. * * @param CounterValue - RTC counter new value. * * @return RTC counter value */ void RTC_SetCounter(uint32_t CounterValue) { RTC_EnterConfigMode(); RTC->CNTH = CounterValue >> 16; RTC->CNTL = (CounterValue & RTC_LSB_MASK); RTC_ExitConfigMode(); } /********************************************************************* * @fn RTC_SetPrescaler * * @brief Sets the RTC prescaler value * * @param PrescalerValue - RTC prescaler new value * * @return none */ void RTC_SetPrescaler(uint32_t PrescalerValue) { RTC_EnterConfigMode(); RTC->PSCRH = (PrescalerValue & PRLH_MSB_MASK) >> 16; RTC->PSCRL = (PrescalerValue & RTC_LSB_MASK); RTC_ExitConfigMode(); } /********************************************************************* * @fn RTC_SetAlarm * * @brief Sets the RTC alarm value * * @param AlarmValue - RTC alarm new value * * @return none */ void RTC_SetAlarm(uint32_t AlarmValue) { RTC_EnterConfigMode(); RTC->ALRMH = AlarmValue >> 16; RTC->ALRML = (AlarmValue & RTC_LSB_MASK); RTC_ExitConfigMode(); } /********************************************************************* * @fn RTC_GetDivider * * @brief Gets the RTC divider value * * @return RTC Divider value */ uint32_t RTC_GetDivider(void) { uint32_t tmp = 0x00; tmp = ((uint32_t)RTC->DIVH & (uint32_t)0x000F) << 16; tmp |= RTC->DIVL; return tmp; } /********************************************************************* * @fn RTC_WaitForLastTask * * @brief Waits until last write operation on RTC registers has finished * * @return none */ void RTC_WaitForLastTask(void) { while((RTC->CTLRL & RTC_FLAG_RTOFF) == (uint16_t)RESET) { } } /********************************************************************* * @fn RTC_WaitForSynchro * * @brief Waits until the RTC registers are synchronized with RTC APB clock * * @return none */ void RTC_WaitForSynchro(void) { RTC->CTLRL &= (uint16_t)~RTC_FLAG_RSF; while((RTC->CTLRL & RTC_FLAG_RSF) == (uint16_t)RESET) { } } /********************************************************************* * @fn RTC_GetFlagStatus * * @brief Checks whether the specified RTC flag is set or not * * @param RTC_FLAG- specifies the flag to check * RTC_FLAG_RTOFF - RTC Operation OFF flag * RTC_FLAG_RSF - Registers Synchronized flag * RTC_FLAG_OW - Overflow flag * RTC_FLAG_ALR - Alarm flag * RTC_FLAG_SEC - Second flag * * @return none */ FlagStatus RTC_GetFlagStatus(uint16_t RTC_FLAG) { FlagStatus bitstatus = RESET; if((RTC->CTLRL & RTC_FLAG) != (uint16_t)RESET) { bitstatus = SET; } else { bitstatus = RESET; } return bitstatus; } /********************************************************************* * @fn RTC_ClearFlag * * @brief Clears the RTC's pending flags * * @param RTC_FLAG - specifies the flag to clear * RTC_FLAG_RSF - Registers Synchronized flag * RTC_FLAG_OW - Overflow flag * RTC_FLAG_ALR - Alarm flag * RTC_FLAG_SEC - Second flag * * @return none */ void RTC_ClearFlag(uint16_t RTC_FLAG) { RTC->CTLRL &= (uint16_t)~RTC_FLAG; } /********************************************************************* * @fn RTC_GetITStatus * * @brief Checks whether the specified RTC interrupt has occurred or not * * @param RTC_IT - specifies the RTC interrupts sources to check * RTC_FLAG_OW - Overflow interrupt * RTC_FLAG_ALR - Alarm interrupt * RTC_FLAG_SEC - Second interrupt * * @return The new state of the RTC_IT (SET or RESET) */ ITStatus RTC_GetITStatus(uint16_t RTC_IT) { ITStatus bitstatus = RESET; bitstatus = (ITStatus)(RTC->CTLRL & RTC_IT); if(((RTC->CTLRH & RTC_IT) != (uint16_t)RESET) && (bitstatus != (uint16_t)RESET)) { bitstatus = SET; } else { bitstatus = RESET; } return bitstatus; } /********************************************************************* * @fn RTC_ClearITPendingBit * * @brief Clears the RTC's interrupt pending bits * * @param RTC_IT - specifies the interrupt pending bit to clear * RTC_FLAG_OW - Overflow interrupt * RTC_FLAG_ALR - Alarm interrupt * RTC_FLAG_SEC - Second interrupt * * @return none */ void RTC_ClearITPendingBit(uint16_t RTC_IT) { RTC->CTLRL &= (uint16_t)~RTC_IT; } #if defined(CH32V20x_D8) || defined(CH32V20x_D8W) /******************************************************************************* * @fn Calibration_LSI * * @brief LSI calibration * * @param cali_Lv : calibration level * Level_32 - 1.2ms 1100ppm * Level_64 - 2.2ms 1000ppm * Level_128 - 4.2ms 800ppm * * @return None */ void Calibration_LSI(Cali_LevelTypeDef cali_Lv) { uint32_t i; int32_t cnt_offset; int32_t Freq = 0; uint8_t retry = 0; uint32_t cnt_32k = 0; Freq = SystemCoreClock; // Coarse tuning OSC->LSI32K_CAL_CFG &= ~RB_OSC_CNT_VLU; OSC->LSI32K_CAL_CFG |= 0; while(1) { OSC->LSI32K_CAL_CTRL |= RB_OSC_CAL_EN; OSC->LSI32K_CAL_STATR |= RB_OSC_CAL_CNT_OV; OSC->LSI32K_CAL_STATR |= RB_OSC_CAL_IF_END; while(!(OSC->LSI32K_CAL_STATR & RB_OSC_CAL_IF_END)); i = OSC->LSI32K_CAL_STATR; OSC->LSI32K_CAL_CTRL &= ~RB_OSC_CAL_EN; OSC->LSI32K_CAL_CTRL |= RB_OSC_CAL_EN; OSC->LSI32K_CAL_STATR |= RB_OSC_CAL_CNT_OV; OSC->LSI32K_CAL_STATR |= RB_OSC_CAL_IF_END; cnt_32k = RTC_GetCounter(); while(RTC_GetCounter() == cnt_32k); OSC->LSI32K_CAL_STATR |= RB_OSC_CAL_CNT_OV; while(OSC->LSI32K_CAL_STATR & RB_OSC_CAL_IF_END); while(!(OSC->LSI32K_CAL_STATR & RB_OSC_CAL_IF_END)); i = OSC->LSI32K_CAL_STATR; cnt_offset = (i & 0x3FFF) + OSC->LSI32K_CAL_OV_CNT * 0x3FFF - 2000 * (Freq / 1000) / CAB_LSIFQ; if(((cnt_offset > -(20 * (Freq / 1000) / 36000)) && (cnt_offset < (20 * (Freq / 1000) / 36000))) || retry > 2) break; retry++; cnt_offset = (cnt_offset > 0) ? (((cnt_offset * 2) / (40 * (Freq / 1000) / 36000)) + 1) / 2 : (((cnt_offset * 2) / (40 * (Freq / 1000) / 36000)) - 1) / 2; OSC->LSI32K_TUNE += cnt_offset; } OSC->LSI32K_CAL_CFG &= ~RB_OSC_CNT_VLU; OSC->LSI32K_CAL_CFG |= 2; OSC->LSI32K_CAL_CTRL &= ~RB_OSC_CAL_EN; OSC->LSI32K_CAL_CTRL |= RB_OSC_CAL_EN; OSC->LSI32K_CAL_STATR |= RB_OSC_CAL_IF_END; OSC->LSI32K_CAL_STATR |= RB_OSC_CAL_CNT_OV; // Fine tuning // After configuring the fine-tuning parameters, discard the two captured values (software behavior) and judge once, only one time is left here while(!(OSC->LSI32K_CAL_STATR & RB_OSC_CAL_IF_END)); i = OSC->LSI32K_CAL_STATR; OSC->LSI32K_CAL_CTRL &= ~RB_OSC_CAL_EN; OSC->LSI32K_CAL_CTRL |= RB_OSC_CAL_EN; OSC->LSI32K_CAL_STATR |= RB_OSC_CAL_IF_END; OSC->LSI32K_CAL_STATR |= RB_OSC_CAL_CNT_OV; cnt_32k = RTC_GetCounter(); while(RTC_GetCounter() == cnt_32k); OSC->LSI32K_CAL_STATR |= RB_OSC_CAL_CNT_OV; while(OSC->LSI32K_CAL_STATR & RB_OSC_CAL_IF_END); while(!(OSC->LSI32K_CAL_STATR & RB_OSC_CAL_IF_END)); i = OSC->LSI32K_CAL_STATR; cnt_offset = (i & 0x3FFF) + OSC->LSI32K_CAL_OV_CNT * 0x3FFF - 8000 * (1 << 2) * (Freq / 1000000) / 256 * 1000 / (CAB_LSIFQ / 256); cnt_offset = (cnt_offset > 0) ? ((((cnt_offset * 2 * 100) / (748 * ((1 << 2) / 4) * (Freq / 1000) / 36000)) + 1) / 2) << 5 : ((((cnt_offset * 2 * 100) / (748 * ((1 << 2) / 4) * (Freq / 1000) / 36000)) - 1) / 2) << 5; OSC->LSI32K_TUNE += cnt_offset; OSC->LSI32K_CAL_CFG &= ~RB_OSC_CNT_VLU; OSC->LSI32K_CAL_CFG |= cali_Lv; OSC->LSI32K_CAL_CTRL &= ~RB_OSC_CAL_EN; OSC->LSI32K_CAL_CTRL |= RB_OSC_CAL_EN; OSC->LSI32K_CAL_STATR |= RB_OSC_CAL_IF_END; OSC->LSI32K_CAL_STATR |= RB_OSC_CAL_CNT_OV; // Fine tuning // After configuring the fine-tuning parameters, discard the two captured values (software behavior) and judge once, only one time is left here while(!(OSC->LSI32K_CAL_STATR & RB_OSC_CAL_IF_END)); i = OSC->LSI32K_CAL_STATR; OSC->LSI32K_CAL_CTRL &= ~RB_OSC_CAL_EN; OSC->LSI32K_CAL_CTRL |= RB_OSC_CAL_EN; OSC->LSI32K_CAL_STATR |= RB_OSC_CAL_IF_END; OSC->LSI32K_CAL_STATR |= RB_OSC_CAL_CNT_OV; cnt_32k = RTC_GetCounter(); while(RTC_GetCounter() == cnt_32k); OSC->LSI32K_CAL_STATR |= RB_OSC_CAL_CNT_OV; while(OSC->LSI32K_CAL_STATR & RB_OSC_CAL_IF_END); while(!(OSC->LSI32K_CAL_STATR & RB_OSC_CAL_IF_END)); OSC->LSI32K_CAL_CTRL &= ~RB_OSC_CAL_EN; i = OSC->LSI32K_CAL_STATR; cnt_offset = (i & 0x3FFF) + OSC->LSI32K_CAL_OV_CNT * 0x3FFF - 8000 * (1 << cali_Lv) * (Freq / 1000000) / 256 * 1000 / (CAB_LSIFQ / 256); cnt_offset = (cnt_offset > 0) ? ((((cnt_offset * 2 * 100) / (748 * ((1 << cali_Lv) / 4) * (Freq / 1000) / 36000)) + 1) / 2) << 5 : ((((cnt_offset * 2 * 100) / (748 * ((1 << cali_Lv) / 4) * (Freq / 1000) / 36000)) - 1) / 2) << 5; OSC->LSI32K_TUNE += cnt_offset; } #endif