#include <stdint.h>
#include "bl2101.h"


uint32_t SystemCoreClock  = __HSI;   				      //System Clock Frequency (Core Clock)
uint32_t CyclesPerUs      = (__HSI / 1000000); 		      //Cycles per micro second


/****************************************************************************************************************************************** 
* 函数名称: 
* 功能说明: This function is used to update the variable SystemCoreClock and must be called whenever the core clock is changed
* 输    入: 
* 输    出: 
* 注意事项: 
******************************************************************************************************************************************/
void SystemCoreClockUpdate(void)    
{
	uint8_t Clk_Div = 1;
	
	if((SYS->CLKSEL & (0x01 << SYS_CLKSEL_SYS_POS)) == 0)
	{
		SystemCoreClock  = __HSI; 
	}
	else
	{
		Clk_Div = 0x01 << ((SYS->CLKSEL & (0x07 << SYS_CLKSEL_DIV_POS)) >> SYS_CLKSEL_DIV_POS);
		
		if(((SYS->CLKSEL & (0x03 << SYS_CLKSEL_SRC_POS))>> SYS_CLKSEL_SRC_POS) == 0x00)
		{
			SystemCoreClock  = __HSI / Clk_Div; 
		}
		else if(((SYS->CLKSEL & (0x03 << SYS_CLKSEL_SRC_POS)) >> SYS_CLKSEL_SRC_POS) == 0x01)
		{
			SystemCoreClock  = __LSI / Clk_Div; 
		}
		else if(((SYS->CLKSEL & (0x03 << SYS_CLKSEL_SRC_POS)) >> SYS_CLKSEL_SRC_POS) == 0x02)
		{
			SystemCoreClock  = __HSE / Clk_Div; 
		}	
	}
	
	CyclesPerUs = SystemCoreClock/1000000;
}


void Switch_CLK_48MHZ(void)
{
	PMU->RCHFCR = 0x01 << PMU_RCHFCR_EN_POS;               //RCHF使能
	SYS->CLKSEL &= ~(0x01 << SYS_CLKSEL_SYS_POS);          //系统时钟选择为RCHF
	SYS->CLKDIV_EN &= ~(0x01 << SYS_CLKDIV_EN_DIV_POS);    //分频时钟禁止输出
}


void Switch_CLK_48MHZ_DIV2(void)
{
	Switch_CLK_48MHZ();                                 //切换时钟时首先切回到内部48MHZ
	
	SYS->CLKSEL &= ~(0x07 << SYS_CLKSEL_DIV_POS);       
	SYS->CLKSEL |= 0x01 << SYS_CLKSEL_DIV_POS;          //分频时钟选择2分频

	SYS->CLKSEL &= ~(0x03 << SYS_CLKSEL_SRC_POS);       //时钟源选择内部48MHZ
	
	SYS->CLKDIV_EN |= 0x01 << SYS_CLKDIV_EN_DIV_POS;    //分频时钟输出 
	
	SYS->CLKSEL |= 0x01 << SYS_CLKSEL_SYS_POS;          //系统时钟选择分频时钟
}


void Switch_CLK_48MHZ_DIV4(void)
{
	Switch_CLK_48MHZ();                                 //切换时钟时首先切回到内部48MHZ
	
	SYS->CLKSEL &= ~(0x07 << SYS_CLKSEL_DIV_POS);       
	SYS->CLKSEL |= 0x02 << SYS_CLKSEL_DIV_POS;          //分频时钟选择4分频

	SYS->CLKSEL &= ~(0x03 << SYS_CLKSEL_SRC_POS);       //时钟源选择内部48MHZ
	
	SYS->CLKDIV_EN |= 0x01 << SYS_CLKDIV_EN_DIV_POS;    //分频时钟输出 
	
	SYS->CLKSEL |= 0x01 << SYS_CLKSEL_SYS_POS;          //系统时钟选择分频时钟
}


void Switch_CLK_XTAL(void)
{
	uint32_t k = 0;
	
	Switch_CLK_48MHZ();                                 //切换时钟时首先切回到内部48MHZ
	
	SYS->XTALCR |= 0x01 << SYS_XTAL_EN_POS;             //XTAL使能
	
	for(k = 0; k < 100000;k++);                         //XTAL使能后，至少等待2ms再使用
	
	SYS->CLKSEL &= ~(0x07 << SYS_CLKSEL_DIV_POS);       //分频时钟选择1分频
	
	SYS->CLKSEL &= ~(0x03 << SYS_CLKSEL_SRC_POS);       
	SYS->CLKSEL |= 0x02 << SYS_CLKSEL_SRC_POS;          //时钟源选择XTAL
	
	SYS->CLKDIV_EN |= 0x01 << SYS_CLKDIV_EN_DIV_POS;    //分频时钟输出 
	
	SYS->CLKSEL |= 0x01 << SYS_CLKSEL_SYS_POS;          //系统时钟选择分频时钟
}


void Switch_CLK_XTAL_DIV2(void)
{
	uint32_t k = 0;
	
	Switch_CLK_48MHZ();                                 //切换时钟时首先切回到内部48MHZ
	
	SYS->XTALCR |= 0x01 << SYS_XTAL_EN_POS;             //XTAL使能
	
	for(k = 0; k < 100000;k++);                         //XTAL使能后，至少等待2ms再使用
	
	SYS->CLKSEL &= ~(0x07 << SYS_CLKSEL_DIV_POS);       
	SYS->CLKSEL |= 0x01 << SYS_CLKSEL_DIV_POS;          //分频时钟选择2分频
	
	SYS->CLKSEL &= ~(0x03 << SYS_CLKSEL_SRC_POS);       
	SYS->CLKSEL |= 0x02 << SYS_CLKSEL_SRC_POS;          //时钟源选择XTAL
	
	SYS->CLKDIV_EN |= 0x01 << SYS_CLKDIV_EN_DIV_POS;    //分频时钟输出 
	
	SYS->CLKSEL |= 0x01 << SYS_CLKSEL_SYS_POS;          //系统时钟选择分频时钟
}


void Switch_CLK_XTAL_DIV4(void)
{
	uint32_t k = 0;
	
	Switch_CLK_48MHZ();                                 //切换时钟时首先切回到内部48MHZ
	
	SYS->XTALCR |= 0x01 << SYS_XTAL_EN_POS;             //XTAL使能
	
	for(k = 0; k < 100000;k++);                         //XTAL使能后，至少等待2ms再使用
	
	SYS->CLKSEL &= ~(0x07 << SYS_CLKSEL_DIV_POS);       
	SYS->CLKSEL |= 0x02 << SYS_CLKSEL_DIV_POS;          //分频时钟选择4分频
	
	SYS->CLKSEL &= ~(0x03 << SYS_CLKSEL_SRC_POS);       
	SYS->CLKSEL |= 0x02 << SYS_CLKSEL_SRC_POS;          //时钟源选择XTAL
	
	SYS->CLKDIV_EN |= 0x01 << SYS_CLKDIV_EN_DIV_POS;    //分频时钟输出 
	
	SYS->CLKSEL |= 0x01 << SYS_CLKSEL_SYS_POS;          //系统时钟选择分频时钟
}


void Switch_CLK_32KHZ(void)
{
	Switch_CLK_48MHZ();                                 //切换时钟时首先切回到内部48MHZ
	
	SYS->CLKSEL &= ~(0x07 << SYS_CLKSEL_DIV_POS);       //分频时钟选择1分频
	
	SYS->CLKSEL &= ~(0x03 << SYS_CLKSEL_SRC_POS);       
	SYS->CLKSEL |= 0x01 << SYS_CLKSEL_SRC_POS;          //时钟源选择内部32KHZ
	
	SYS->CLKDIV_EN |= 0x01 << SYS_CLKDIV_EN_DIV_POS;    //分频时钟输出 
	
	SYS->CLKSEL |= 0x01 << SYS_CLKSEL_SYS_POS;          //系统时钟选择分频时钟
}

/****************************************************************************************************************************************** 
* 函数名称: 
* 功能说明: The necessary initializaiton of systerm
* 输    入: 
* 输    出: 
* 注意事项: 
******************************************************************************************************************************************/
void SystemInit(uint8_t clk)
{		
#ifndef FPGA_SIMULATOR	
	
	uint32_t TRIM_POW_Addr = 0x3FF0;
	uint32_t TRIM_RC_Addr = 0x3FF4;
	uint32_t TRIM_POW_Data = 0;
	uint32_t delay = 0;
	
	TRIM_POW_Data = OTP_Read_2(TRIM_POW_Addr);
	TRIM_POW_Data = TRIM_POW_Data & 0xFFFF0FFF;
	PMU->TRIM_POW = TRIM_POW_Data;
	
	for(delay = 0;delay < 100000;delay++);
	
	PMU->TRIM_RC = OTP_Read_2(TRIM_RC_Addr);
	
	for(delay = 0;delay < 100000;delay++);
	
	
	switch(clk)
	{
		case SYS_CLK_48MHz:			        //0 内部高频48MHz RC振荡器
			
			Switch_CLK_48MHZ();
		
		break;
		
		case SYS_CLK_48MHz_DIV2:			//1 内部高频48MHz RC振荡器  2分频
			
			Switch_CLK_48MHZ_DIV2();
		
		break;
		
		case SYS_CLK_48MHz_DIV4:			//2 内部高频48MHz RC振荡器  4分频
			
			Switch_CLK_48MHZ_DIV4();
		
		break;
		
		case SYS_CLK_XTAL:			        //3 外部晶体振荡器（4-32MHz）
			
			Switch_CLK_XTAL();
		
		break;
		
		case SYS_CLK_XTAL_DIV2:			    //4 外部晶体振荡器（4-32MHz）  2分频
			
			Switch_CLK_XTAL_DIV2();
		
		break;
		
		case SYS_CLK_XTAL_DIV4:			    //5 外部晶体振荡器（4-32MHz）  4分频
			
			Switch_CLK_XTAL_DIV4();
		
		break;
		
		case SYS_CLK_32KHz:			        //6 内部低频32KHz RC振荡器
			
			Switch_CLK_32KHZ();
		
		break;	
	}
	
	SystemCoreClockUpdate();

#endif
}


