/********************************** (C) COPYRIGHT *******************************
* File Name          : ch32v30x_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.
* Attention: This software (modified or not) and binary are used for 
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
#include "ch32v30x_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 high1a = 0, high1b = 0, high2a = 0, high2b = 0;
    uint16_t low1 = 0, low2 = 0;

    do{
        high1a = RTC->CNTH;
        high1b = RTC->CNTH;
    }while( high1a != high1b );

    do{
        low1 = RTC->CNTL;
        low2 = RTC->CNTL;
    }while( low1 != low2 );

    do{
        high2a = RTC->CNTH;
        high2b = RTC->CNTH;
    }while( high2a != high2b );

    if(high1b != high2b)
    {
        do{
            low1 = RTC->CNTL;
            low2 = RTC->CNTL;
        }while( low1 != low2 );
    }

    return (((uint32_t)high2b << 16) | low2);
}

/*********************************************************************
 * @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)
{
    uint16_t high1a = 0, high1b = 0, high2a = 0, high2b = 0;
    uint16_t low1 = 0, low2 = 0;

    do{
        high1a = RTC->DIVH;
        high1b = RTC->DIVH;
    }while( high1a != high1b );

    do{
        low1 = RTC->DIVL;
        low2 = RTC->DIVL;
    }while( low1 != low2 );

    do{
        high2a = RTC->DIVH;
        high2b = RTC->DIVH;
    }while( high2a != high2b );

    if(high1b != high2b)
    {
        do{
            low1 = RTC->DIVL;
            low2 = RTC->DIVL;
        }while( low1 != low2 );
    }

    return ((((uint32_t)high2b & (uint32_t)0x000F) << 16) | low2);
}

/*********************************************************************
 * @fn      RTC_WaitForLastTask
 *
 * @brief   Waits until last write operation on RTC registers has finished
 *          Note-
 *          This function must be called before any write to RTC registers.
 * @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
 *          Note-
 *          This function must be called before any read operation after an APB reset
 *          or an APB clock stop.
 *          
 * @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  The new state of RTC_FLAG (SET or RESET)
 */
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;
}
