/************************************************************************************
* @Copyright (c)	  :(C)2020, Qindao ieslab Co., Ltd
* @FileName   		  :hc32_clktrim_driver.c
* @Author           :QDIES PLATFORM TEAM
* @Version          :V1.0
* @Date             :2020-07-01
* @Description      :
************************************************************************************/
#include "UWater_clktrim_driver.h"
#include "UWater_rtcc_driver.h"
#include "UWater_frame_app.h"
#include "SOE.h"

volatile uint8_t u8TrimTestFlag = 0;

static u8 clktrim_machine_state = CLKTRIM_MACHINE_S0;
volatile u8 TrimOkFlag = 0;
static u8 trim_cnt = 0;
static CLKTRIM_INFO clktrim_info;
volatile uint32_t u32CalCnt; 

void ClkTrimInit(void)
{
	stc_trim_cfg_t stcCfg;

    //打开TRIM外设时钟
    Sysctrl_SetPeripheralGate(SysctrlPeripheralTrim, TRUE);
     
    //TRIM校准流程
    stcCfg.enMON     = TrimMonDisable;
    stcCfg.enREFCLK  = TrimRefMskXTL;               //参考时钟:外部低速时钟
    stcCfg.enCALCLK  = TrimCalMskRCH;               //待校准时钟:内部高速时钟
    stcCfg.u32RefCon = REF_CON;                     //100ms校准时间（增加该时间可提高TRIM精度）
    stcCfg.u32CalCon = 0xffffffffu;                 //配置为默认值
    Trim_Init(&stcCfg);
    
    ///< 打开TRIM中断使能
    Trim_EnableIrq();
    ///< 使能并配置TRIM 系统中断
    EnableNvic(CLKTRIM_IRQn, IrqLevel3, TRUE);
}
/*******************************************************************************
 * TRIM 结束
 ******************************************************************************/ 
static void clktrim_close(void)
{
	Trim_Stop(); //停止校准
	Sysctrl_SetPeripheralGate(SysctrlPeripheralTrim, FALSE); //关TRIM外设时钟
	Trim_DisableIrq(); //TRIM中断禁止
	NVIC_DisableIRQ(CLKTRIM_IRQn); //关TRIM系统中断
	trim_cnt = 0;
}
///***************************************************************************
//* @fun_name : void StartClkTrim(void)
//* @brief    : 开启时钟校准接口，供外部调用
// ***************************************************************************/
//void StartClkTrim(void)
//{
//    s_clktrim_start_flg = TRUE;
//}
/*******************************************************************************
 * TRIM 是否空闲
 ******************************************************************************/ 
u8 ClkTrimIfIdle(void)
{
	if(CLKTRIM_MACHINE_S0 == clktrim_machine_state)
	{
		return TASK_IDLE;
	}
	else
	{
		return TASK_BUSY;
	}
}
/***************************************************************************
* @fun_name : void ClktrimMainMachine(void)
* @brief    : 时钟校准模块主任务机，放在主程序空间的主循环中
 ***************************************************************************/
//u8 check_count=0;
void ClktrimMainMachine(void)
{
	switch(clktrim_machine_state)
	{
		case CLKTRIM_MACHINE_S0:
		{	
			if(FrameCheckMsgApp(MsgBLE_Clktrim))
			{
				FrameClearMsgApp(MsgBLE_Clktrim);
				clktrim_info.adjust_direction_flg = DIRECTIONLESS;       //无方向
                TrimOkFlag = 0;
                ClkTrimInit();
                Trim_Run(); //开启TRIM校准流程
                trim_cnt = 1;
                RtccSetTimer(RTCC_TIMER_CLKTRIM_TOTAL, 20);
                clktrim_machine_state = CLKTRIM_MACHINE_S1;   
			}
		}
		break;		
		case CLKTRIM_MACHINE_S1:
		{
			if((TRIM_DONE == TrimOkFlag)                              //校准完成
				|| (TRIM_OVERFLOW == TrimOkFlag)                      //加计数器（待校准时钟）溢出，XTL异常
				|| (trim_cnt >= TRIM_MAX_NUM)                         //超过校准允许最大次数
				|| (0 == RtccCheckTimer(RTCC_TIMER_CLKTRIM_TOTAL))) //超时时间到
			{
				FrameSetMsgApp(MsgAddr_Change);//蓝牙重启消息
				SaveNewSoe(clktrim_info.diff_value&0xFFFF, (clktrim_info.diff_value>>16)&0xFF);//时钟校准
				SaveNewSoe(M0P_SYSCTRL->RCH_CR_f.TRIM, 0x99);//时钟校准
				
				trim_cnt = 0;
				clktrim_close();
				clktrim_machine_state = CLKTRIM_MACHINE_S0;
			}
		}
		break;
		
		default:
		{
			clktrim_close();
			clktrim_machine_state = CLKTRIM_MACHINE_S0;
		}
		break;
	}
}
/*******************************************************************************
 * TRIM 中断服务程序
 ******************************************************************************/  
void ClkTrim_IRQHandler(void)
{
	if(TRUE == Trim_GetIntFlag(TrimStop))
	{
		u32CalCnt = Trim_CalCntGet();  //获取待校准时钟的计数值

		if(u32CalCnt == CAL_CON_TARGET)  //计数值等于标准值
		{
			Trim_Stop();
			///< 校准结束,此时的TRIM值即为最佳频率值
			TrimOkFlag = TRIM_DONE;
		}
		else if(u32CalCnt < CAL_CON_TARGET) //计数值小于标准值  需要增大trim值
		{
			Trim_Stop();
			if(clktrim_info.adjust_direction_flg == DIRECTIONLESS || clktrim_info.adjust_direction_flg == UPWARD)  
			{
				clktrim_info.adjust_direction_flg = UPWARD;	
				clktrim_info.diff_value = CAL_CON_TARGET - u32CalCnt;    //得到差值	
				///< 为达到目标精度，TRIM值加1，继续校准	
				M0P_SYSCTRL->RCH_CR_f.TRIM += 1;
				Trim_Run();
				trim_cnt++;
			}
			else //方向发生改变
			{
				if((CAL_CON_TARGET - u32CalCnt) > clktrim_info.diff_value)  //当前的差值大于上一个差值 用上一个的trim值
				{
					 M0P_SYSCTRL->RCH_CR_f.TRIM +=1;
					 TrimOkFlag = TRIM_DONE;
				}
				else
				{
					 TrimOkFlag = TRIM_DONE;
				}
			}
		}
		else if(u32CalCnt > CAL_CON_TARGET) //计数值大于标准值  需要减小trim值
		{
			Trim_Stop();
			if(clktrim_info.adjust_direction_flg == DIRECTIONLESS || clktrim_info.adjust_direction_flg == DOWNWARD)  
			{
				clktrim_info.adjust_direction_flg = DOWNWARD;	
				clktrim_info.diff_value =  u32CalCnt - CAL_CON_TARGET; //得到差值
				///< 为达到目标精度，TRIM值减1，继续校准	
				M0P_SYSCTRL->RCH_CR_f.TRIM -= 1;
				Trim_Run();
				trim_cnt++;
			}
			else //方向发生改变
			{
				if((u32CalCnt - CAL_CON_TARGET) > clktrim_info.diff_value)  //当前的差值大于上一个差值 用上一个的trim值
				{
					 M0P_SYSCTRL->RCH_CR_f.TRIM -= 1;
					 TrimOkFlag = TRIM_DONE;
				}
				else
				{
					 TrimOkFlag = TRIM_DONE;
				}
			}			
		}
	}
	if(TRUE == Trim_GetIntFlag(TrimCalCntOf))
	{
		Trim_Stop();
		///< 待校准时钟计数器溢出，XTL可能异常
		TrimOkFlag = TRIM_OVERFLOW;
	}
}
