373 lines
12 KiB
C
373 lines
12 KiB
C
/********************************** (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
|