#include "rtc.h"
#include "cgc.h"

/**
  * @brief  Start the RTC counter operation.
  * @param  None
  * @retval None
  */
void RTC_Start(void)
{

    INTC_ClearPendingIRQ(RTC_IRQn); // clear INTRTC interrupt flag 
    NVIC_ClearPendingIRQ(RTC_IRQn); // clear INTRTC interrupt flag 
    INTC_EnableIRQ(RTC_IRQn);       // enable INTRTC interrupt 

    RTC->RTCC0  |= 0x80;    // starts counter operation 

    /* confirm the RTC is running */
    RTC->RTCC1 |= RTC_RTCC1_RWAIT_Msk;              //stops SEC to YEAR counters. Mode to read or write counter value           
    while((RTC->RTCC1 & RTC_RTCC1_RWST_Msk) == 0);  // wait RWST = 1 
    RTC->RTCC1 &= ~RTC_RTCC1_RWAIT_Msk;             // sets counter operation 
    while((RTC->RTCC1 & RTC_RTCC1_RWST_Msk) != 0);  // wait RWST = 0 
}

/**
  * @brief  Stop the RTC counter operation.
  * @param  None
  * @retval None
  */
void RTC_Stop(void)
{
    RTC->RTCC0  = 0x00;     /* stops counter operation */
    INTC_DisableIRQ(RTC_IRQn);      /* disable INTRTC interrupt */
    INTC_ClearPendingIRQ(RTC_IRQn); /* clear INTRTC interrupt flag */
    NVIC_ClearPendingIRQ(RTC_IRQn); /* clear INTRTC interrupt flag */
}

/**
  * @brief  Convert from 2 digit BCD to Binary.
  * @param  Value: BCD value to be converted.
  * @retval Converted word
  */
static uint8_t RTC_Bcd2ToByte(uint8_t Value)
{
  uint8_t tmp = 0;
  tmp = ((uint8_t)(Value & (uint8_t)0xF0) >> (uint8_t)0x4) * 10;
  return (tmp + (Value & (uint8_t)0x0F));
}
/**
  * @brief  Converts a 2 digit decimal to BCD format.
  * @param  Value: Byte to be converted.
  * @retval Converted byte
  */
static uint8_t RTC_ByteToBcd2(uint8_t Value)
{
  uint8_t bcdhigh = 0;
  
  while (Value >= 10)
  {
    bcdhigh++;
    Value -= 10;
  }
  
  return  ((uint8_t)(bcdhigh << 4) | Value);
}

/**
  * @brief  Set the RTC hour format.
  * @param  RTC_HourFormat: 
				@arg RTC_HourFormat_12
				@arg RTC_HourFormat_24
  * @retval None
  */
void RTC_SetHourFormat(uint8_t RTC_HourFormat)
{
	/* Check the parameters */
	assert_param(IS_RTC_HOURFORMAT(RTC_HourFormat));
	

    RTC->RTCC1 |= RTC_RTCC1_RWAIT_Msk;              // stops SEC to YEAR counters. Mode to read or write counter value          
	
	RTC->RTCC0 |= RTC_HourFormat;    // select counter hour format:24hour or 12hour
	
	RTC->RTCC1 &= ~RTC_RTCC1_RWAIT_Msk; 			// sets counter operation 
    while((RTC->RTCC1 & RTC_RTCC1_RWST_Msk) != 0);  // wait RWST = 0 	
}


/**
  * @brief  Set the RTC current time.
  * @param  RTC_TimeStruct: pointer to a RTC_TimeTypeDef structure that contains 
  *                        the time configuration information for the RTC.     
  * @retval None
  */
void RTC_SetTime(RTC_TimeTypeDef* RTC_TimeStruct)
{
	/* Check the parameters */
	assert_param(IS_RTC_HOUR24(RTC_TimeStruct->RTC_Hours));
	assert_param(IS_RTC_MINUTES(RTC_TimeStruct->RTC_Minutes));
	assert_param(IS_RTC_SECONDS(RTC_TimeStruct->RTC_Seconds));	
	

    RTC->RTCC1 |= RTC_RTCC1_RWAIT_Msk;              // stops SEC to YEAR counters. Mode to read or write counter value          
	
	RTC->HOUR |= RTC_ByteToBcd2(RTC_TimeStruct->RTC_Hours);
	RTC->MIN  = RTC_ByteToBcd2(RTC_TimeStruct->RTC_Minutes);
	RTC->SEC  = RTC_ByteToBcd2(RTC_TimeStruct->RTC_Seconds);
	if(RTC_TimeStruct->RTC_Hours >12)
	{
		RTC->RTCC0 |= RTC_HourFormat_24;
	}
	else
	{
		RTC->RTCC0 |= RTC_HourFormat_12;
		RTC->HOUR |= RTC_TimeStruct->RTC_H12;
	}

	RTC->RTCC1 &= ~RTC_RTCC1_RWAIT_Msk; 			// sets counter operation 
    while((RTC->RTCC1 & RTC_RTCC1_RWST_Msk) != 0);  // wait RWST = 0 	
}

/**
  * @brief  Set the RTC current date.
  * @param  RTC_DateStruct: pointer to a RTC_DateTypeDef structure that contains 
  *                         the date configuration information for the RTC.
  * @retval None
  */
void RTC_SetDate(RTC_DateTypeDef* RTC_DateStruct)
{
	assert_param(IS_RTC_YEAR(RTC_DateStruct->RTC_Year));
	assert_param(IS_RTC_MONTH(RTC_DateStruct->RTC_Month));
	assert_param(IS_RTC_DATE(RTC_DateStruct->RTC_Day));
	assert_param(IS_RTC_DATE(RTC_DateStruct->RTC_WeekDay));	
	
    RTC->RTCC1 |= RTC_RTCC1_RWAIT_Msk;              // stops SEC to YEAR counters. Mode to read or write counter value          

	RTC->DAY   =  RTC_ByteToBcd2(RTC_DateStruct->RTC_Day);
	RTC->WEEK  =  RTC_ByteToBcd2(RTC_DateStruct->RTC_WeekDay);
	RTC->MONTH =  RTC_ByteToBcd2(RTC_DateStruct->RTC_Month);
	RTC->YEAR  =  RTC_ByteToBcd2(RTC_DateStruct->RTC_Year);	
	
	RTC->RTCC1 &= ~RTC_RTCC1_RWAIT_Msk; 			// sets counter operation 
    while((RTC->RTCC1 & RTC_RTCC1_RWST_Msk) != 0);  // wait RWST = 0 	
	
}

/**
  * @brief  Get the RTC current date and time.
  * @param  RTC_DateStruct: pointer to a RTC_DateTypeDef structure that contains 
  *                         the date configuration information for the RTC.
  * @retval None
  */

void RTC_Get_CounterValue(RTC_CounterTypeDef *counter_val)
{
    RTC->RTCC1 |= RTC_RTCC1_RWAIT_Msk;              // stops SEC to YEAR counters. Mode to read or write counter value          
    while((RTC->RTCC1 & RTC_RTCC1_RWST_Msk) == 0);  // wait RWST = 1 

    counter_val->time.RTC_Seconds   = RTC->SEC;    
    counter_val->time.RTC_Minutes   = RTC->MIN;   
    counter_val->time.RTC_Hours     = RTC->HOUR;   
    counter_val->date.RTC_Day   	= RTC->DAY;    
    counter_val->date.RTC_WeekDay 	= RTC->WEEK;   
    counter_val->date.RTC_Month		= RTC->MONTH;  
    counter_val->date.RTC_Year  	= RTC->YEAR;   

	RTC->RTCC1 &= ~RTC_RTCC1_RWAIT_Msk; 			// sets counter operation 
    while((RTC->RTCC1 & RTC_RTCC1_RWST_Msk) != 0);  // wait RWST = 0 
}

/**
  * @brief  Set the specified RTC Alarm.
  * @note   
  * @param  RTC_AlarmStruct: pointer to a RTC_AlarmTypeDef structure that 
  *                          contains the alarm configuration parameters.     
  * @retval None
  */
void RTC_SetAlarm( RTC_Alarm_t * RTC_AlarmStruct)
{
	INTC_DisableIRQ(RTC_IRQn);		// disable INTRTC interrupt 
	RTC->RTCC1 &= ~0x80;
	RTC->RTCC1 |= 0x40;
	
    RTC->ALARMWM = RTC_ByteToBcd2(RTC_AlarmStruct->Alarm_Minute);
    RTC->ALARMWH = RTC_ByteToBcd2(RTC_AlarmStruct->Alarm_Hour);
    RTC->ALARMWW = RTC_AlarmStruct->Alarm_Week;
	
	RTC->RTCC1 |= 0x80;
	RTC->RTCC1 &= ~0x10;//set alarm match-bit clear
	INTC_ClearPendingIRQ(RTC_IRQn); // clear INTRTC interrupt flag 
	NVIC_ClearPendingIRQ(RTC_IRQn); // clear INTRTC interrupt flag 
    INTC_EnableIRQ(RTC_IRQn);       // enable INTRTC interrupt
}

/**
  * @brief  Get the specified RTC Alarm Value.
  * @note   
  * @param  alarm_val: pointer to a RTC_Alarm_t structure that 
  *                          contains the alarm configuration parameters.     
  * @retval None
  */
void RTC_Get_AlarmValue(RTC_Alarm_t * alarm_val)
{
	INTC_DisableIRQ(RTC_IRQn);		// disable INTRTC interrupt 
	RTC->RTCC1 &= ~0x80;
    alarm_val->Alarm_Minute = RTC->ALARMWM;
    alarm_val->Alarm_Hour = RTC->ALARMWH;
    alarm_val->Alarm_Week = RTC->ALARMWW;
	RTC->RTCC1 |= 0x80;
	RTC->RTCC1 &= ~0x10;//set alarm match-bit clear
	INTC_ClearPendingIRQ(RTC_IRQn); // clear INTRTC interrupt flag 
	NVIC_ClearPendingIRQ(RTC_IRQn); // clear INTRTC interrupt flag 
    INTC_EnableIRQ(RTC_IRQn);       // enable INTRTC interrupt
}

/**
  * @brief  Initializes the RTC registers according to the specified parameters 
  *         in RTC_InitStruct.
  * @param  RTC_InitStruct: pointer to a RTC_InitTypeDef structure that contains 
  *         the configuration information for the RTC peripheral.
  * @note   The RTC Prescaler register is write protected and can be written in 
  *         initialization mode only.  
  * @retval None
  */
void RTC_Init(RTC_InitTypeDef *RTC_InitStruct)
{
	/* Check the parameters */
	assert_param(IS_RTC_HOURFORMAT(RTC_InitStruct->RTC_HourFormat));
	assert_param(IS_RTC_MINUTES(RTC_InitStruct->RTC_Time.RTC_Minutes));
	assert_param(IS_RTC_SECONDS(RTC_InitStruct->RTC_Time.RTC_Seconds));	
	
	assert_param(IS_RTC_DATE(RTC_InitStruct->RTC_Date.RTC_Day));
	assert_param(IS_RTC_MONTH(RTC_InitStruct->RTC_Date.RTC_Month));
	assert_param(IS_RTC_YEAR(RTC_InitStruct->RTC_Date.RTC_Year));	
	assert_param(IS_RTC_WEEKDAY(RTC_InitStruct->RTC_Date.RTC_WeekDay));	
	
	CGC_PER0PeriphClockCmd(CGC_PER0Periph_RTC,ENABLE);
	
	if(RTC_InitStruct->RTC_Clk == RTC_FIL)
	{
		CGC->OSMC  |= 1<<4;
	}	
	MISC->RTCCL = RTC_InitStruct->RTC_Clk;

	RTC->RTCC0  = 0x00;     // stops counter operation 
	RTC->RTCC0 |= RTC_InitStruct->RTC_HourFormat;    // select counter hour format:24hour or 12hour
	if(RTC_InitStruct->RTC_1HZ_Output)
	{
		RTC->RTCC0 |= 0x20;
	}
	RTC->RTCC0 =  (RTC->RTCC0 & (~0x07)) | RTC_InitStruct->RTC_Period;//set constant rtc period interrupt
	RTC->RTCC1 &= (uint8_t)~0x08;  //when generate period interrupt,bit-4 becomes 1; writing 0 to erase this flag

    RTC->SEC   = RTC_ByteToBcd2(RTC_InitStruct->RTC_Time.RTC_Seconds);
    RTC->MIN   = RTC_ByteToBcd2(RTC_InitStruct->RTC_Time.RTC_Minutes);
	RTC->HOUR  = RTC_ByteToBcd2(RTC_InitStruct->RTC_Time.RTC_Hours);	
	if(RTC_InitStruct->RTC_HourFormat == RTC_HourFormat_12)
		RTC->HOUR |= RTC_InitStruct->RTC_Time.RTC_H12;
    RTC->DAY   = RTC_ByteToBcd2(RTC_InitStruct->RTC_Date.RTC_Day);
    RTC->WEEK  = RTC_ByteToBcd2(RTC_InitStruct->RTC_Date.RTC_WeekDay);
    RTC->MONTH = RTC_ByteToBcd2(RTC_InitStruct->RTC_Date.RTC_Month);
    RTC->YEAR  = RTC_ByteToBcd2(RTC_InitStruct->RTC_Date.RTC_Year);

	RTC->RTCC1 &= (uint8_t)~0x80;//When set alarm registor, it must set RTCC1 wale-bit(bit-8) 0
	if(RTC_InitStruct->RTC_Alarm_Onoff)
	{
		RTC->RTCC1 |= 0x40;		
	}
	else
	{
		RTC->RTCC1 |= 0x00;		
	}

	RTC->ALARMWM = RTC_ByteToBcd2(RTC_InitStruct->RTC_Alarm.Alarm_Minute);
	RTC->ALARMWH = RTC_ByteToBcd2(RTC_InitStruct->RTC_Alarm.Alarm_Hour);
	RTC->ALARMWW = RTC_InitStruct->RTC_Alarm.Alarm_Week;
	RTC->RTCC1 &= ~0x10;//set alarm match-bit clear
}



/**
  * @brief  Enable the specified RTC Alarm function .
  * @param  
  * @retval None
  */
void RTC_Set_AlarmOn(void)
{
    volatile uint16_t w_count;
    INTC_DisableIRQ(RTC_IRQn); // disable INTRTC interrupt 
    RTC->RTCC1 |= 0x80;       //RTC_ALARM_ENABLE

    for (w_count = 0U; w_count < 2; w_count++)
    {
        __NOP();
	}
	RTC->RTCC1 &= ~0x10;//set alarm match-bit clear
    INTC_ClearPendingIRQ(RTC_IRQn); // clear INTRTC interrupt flag 
    NVIC_ClearPendingIRQ(RTC_IRQn); // clear INTRTC interrupt flag 
    INTC_EnableIRQ(RTC_IRQn);       // enable INTRTC interrupt
}

/**
  * @brief  Disable the specified RTC Alarm function .
  * @param  
  * @retval None
  */
void RTC_Set_AlarmOff(void)
{
	INTC_DisableIRQ(RTC_IRQn);		// disable INTRTC interrupt 
	RTC->RTCC1 &= ~0x80;
	RTC->RTCC1 &= ~0x10;

	INTC_ClearPendingIRQ(RTC_IRQn); // clear INTRTC interrupt flag 
	NVIC_ClearPendingIRQ(RTC_IRQn); // clear INTRTC interrupt flag 
}

/**
  * @brief  Enable the specified RTC constant period interrupt function .
  * @param  period :set the constant period interrupt  
  * 	@arg    Half_Second
  * 	@arg    One_Second
  * 	@arg    One_Minute
  * 	@arg    One_Hour
  * 	@arg    One_Month
  * @retval None
  */
void  RTC_Set_ConstPeriodInterruptOn(RTC_Period_t period)
{
	INTC_DisableIRQ(RTC_IRQn);		// disable INTRTC interrupt */
	RTC->RTCC0 = (RTC->RTCC0 & (~0x07)) | period;
	RTC->RTCC1 &=~0x08;
	INTC_ClearPendingIRQ(RTC_IRQn); // clear INTRTC interrupt flag 
	NVIC_ClearPendingIRQ(RTC_IRQn); // clear INTRTC interrupt flag 
	INTC_EnableIRQ(RTC_IRQn);		// enable INTRTC interrupt 
}

/**
  * @brief  Disable the specified RTC constant period interrupt function .
  * @param 
  * @retval None
  */
void  RTC_Set_ConstPeriodInterruptOff(void)
{
    RTC->RTCC0 &= (uint8_t)~(0x07);
	RTC->RTCC1 &=~0x08;
	INTC_ClearPendingIRQ(RTC_IRQn); // clear INTRTC interrupt flag 
	NVIC_ClearPendingIRQ(RTC_IRQn); // clear INTRTC interrupt flag 
}