/***************************************************************
 *Copyright (C), 2017, Shanghai Eastsoft Microelectronics Co., Ltd
 *文件名：  lib_scu.c
 *作  者：  Liut
 *版  本：  V1.00
 *日  期：  2017/07/14
 *描  述：  系统控制模块库函数
 *备  注：  适用于 ES8P508x芯片
 本软件仅供学习和演示使用，对用户直接引用代码所带来的风险或后果不承担任何法律责任。
 ***************************************************************/
#include "lib_scu.h"

/***************************************************************
  函数名：SCU_OpenXTAL
  描  述: 开启外部时钟
  输入值：无
  输出值：无
  返回值：系统时钟源
 ***************************************************************/
void SCU_OpenXTAL(void)
{
    SCU_RegUnLock();
    SCU->SCLKEN1.XTAL_EN = 1;
    SCU->SCLKEN0.XTAL_LP = 0;

    while (SCU->SCLKEN1.XTAL_RDY == 0); //等待外部时钟稳定

    SCU_RegLock() ;
}

/***************************************************************
  函数名：SCU_NMISelect
  描  述：设置NMI不可屏蔽中断
  输入值：不可屏蔽中断
  输出值：无
  返回值：无
 ***************************************************************/
void SCU_NMISelect(SCU_TYPE_NMICS NMI_Type)
{
    SCU_RegUnLock();
    SCU->NMICON.NMICS = NMI_Type;
    SCU_RegLock() ;
}

/***************************************************************
  函数名：SCU_GetPWRCFlagStatus
  描  述：获取PWRC复位状态寄存器标志位状态
  输入值：PWRC寄存器标志位
  输出值：无
  返回值：RESET/SET
 ***************************************************************/
FlagStatus  SCU_GetPWRCFlagStatus(SCU_TYPE_PWRC PWRC_Flag)
{
    FlagStatus bitstatus = RESET;

    if ((SCU->PWRC.Word & (uint32_t)PWRC_Flag) != (uint32_t)RESET)
        bitstatus = SET;
    else
        bitstatus = RESET;

    return  bitstatus;
}

/***************************************************************
  函数名：SCU_ClearPWRCFlagBit
  描  述：清除PWRC复位状态寄存器标志位
  输入值：PWRC寄存器标志位
  输出值：无
  返回值：无
 ***************************************************************/
void SCU_ClearPWRCFlagBit(SCU_TYPE_PWRC PWRC_Flag)
{
    SCU_RegUnLock() ;
    SCU->PWRC.Word &= ~((uint32_t)PWRC_Flag);
    SCU_RegLock() ;
}

/***************************************************************
  函数名：SCU_GetLVDFlagStatus
  描  述：获取LVDD寄存器标志位状态
  输入值：LVD寄存器标志位
  输出值：无
  返回值：RESET/SET
 ***************************************************************/
FlagStatus  SCU_GetLVDFlagStatus(SCU_TYPE_LVD0CON LVD_Flag)
{
    FlagStatus bitstatus = RESET;

    if ((SCU->LVDCON.Word & (uint32_t)LVD_Flag) != (uint32_t)RESET)
        bitstatus = SET;
    else
        bitstatus = RESET;

    return  bitstatus;
}

/***************************************************************
  函数名：SCU_SysClkSelect
  描  述：选择系统时钟
  输入值：时钟源
  输出值：无
  返回值：无
 ***************************************************************/
void SCU_SysClkSelect(SCU_TYPE_CLK_SEL Sysclk)
{
    SCU_RegUnLock() ;
    SCU->SCLKEN0.CLK_SEL = Sysclk;
    SCU_RegLock() ;
}

/***************************************************************
  函数名：SCU_GetSysClk
  描  述：获取系统时钟源
  输入值：无
  输出值：无
  返回值：系统时钟源
 ***************************************************************/
SCU_TYPE_SYSCLK SCU_GetSysClk(void)
{
    return (SCU_TYPE_SYSCLK)(SCU->SCLKEN0.CLK_SEL);
}

/***************************************************************
  函数名：SCU_HRCReadyFlag
  描  述：获取HRC稳定标志位
  输入值：无
  输出值：无
  返回值：RESET（不稳定）/SET（稳定）
 ***************************************************************/
FlagStatus SCU_HRCReadyFlag(void)
{
    FlagStatus bitstatus = RESET;

    if ((SCU->SCLKEN1.HRC_RDY) != (uint32_t)RESET)
        bitstatus = SET;
    else
        bitstatus = RESET;

    return  bitstatus;
}

/***************************************************************
  函数名：SCU_XTALReadyFlag
  描  述：获取XTAL稳定标志位
  输入值：无
  输出值：无
  返回值：RESET（不稳定）/SET（稳定）
 ***************************************************************/
FlagStatus  SCU_XTALReadyFlag(void)
{
    FlagStatus bitstatus = RESET;

    if ((SCU->SCLKEN1.XTAL_RDY) != (uint32_t)RESET)
        bitstatus = SET;
    else
        bitstatus = RESET;

    return  bitstatus;
}

/***************************************************************
  函数名：SCU_LOSCReadyFlag
  描  述：获取LOSC稳定标志位
  输入值：无
  输出值：无
  返回值：RESET（不稳定）/SET（稳定）
 ***************************************************************/
FlagStatus  SCU_PLLReadyFlag(void)
{
    FlagStatus bitstatus = RESET;

    if ((SCU->SCLKEN1.PLL_RDY) != (uint32_t)RESET)
        bitstatus = SET;
    else
        bitstatus = RESET;

    return  bitstatus;
}

/***************************************************************
 函数名：SystemClockConfig
 描  述：系统时钟配置：内部时钟，20MHZ，打开所有外设时钟
 输入值：无
 输出值：无
 返回值：无
***************************************************************/
void SystemClockConfig(void)
{
    uint32_t Prot_Temp;

    Prot_Temp = SCU->PROT.PROT;

    if (Prot_Temp != 0)                     //写保护了
        SCU_RegUnLock();                    //解锁

    SCU_HRC_Enable();                       //使能内部20MHZ

    while (SCU_HRCReadyFlag() != SET);      //等待时钟开启

    SCU_SysClkSelect(CLK_SEL_HRC);       //选择内部20MHZ为系统时钟

    SCU_SysClk_Div1();                      //系统时钟后分频1:1

    SystemCoreClock = 20000000;

    if (Prot_Temp != 0)                     //写保护了
        SCU_RegLock();                      //打开写保护
}

/***************************************************************
  函数名：DeviceClockAllEnable
  描  述：打开所有外设时钟
  输入值：无
  输出值：无
  返回值：无
 ***************************************************************/
void DeviceClockAllEnable(void)
{
    uint32_t Prot_Temp;

    Prot_Temp = SCU->PROT.PROT;

    if (Prot_Temp != 0)                     //写保护了
        SCU_RegUnLock();                    //解锁

    SCU->PCLKEN0.Word = 0xFFFFFFFF;
    SCU->PCLKEN1.Word = 0xFFFFFFFF;         //打开所有外设时钟

    if (Prot_Temp != 0)                     //写保护了
        SCU_RegLock();                      //打开写保护
}

/***************************************************************
  函数名：DeviceClockAllDisable
  描  述：关闭所有外设时钟
  输入值：无
  输出值：无
  返回值：无
 ***************************************************************/
void DeviceClockAllDisable(void)
{
    uint32_t Prot_Temp;

    Prot_Temp = SCU->PROT.PROT;

    if (Prot_Temp != 0)                     //写保护了
        SCU_RegUnLock();                    //解锁

    SCU->PCLKEN0.Word = 0x00000000;         //关闭所有外设时钟，scu无法关闭
    SCU->PCLKEN1.Word = 0x00000000;

    if (Prot_Temp != 0)                     //写保护了
        SCU_RegLock();                  //打开写保护
}

/***************************************************************
  函数名: DeviceClockAllEnableButIAP
  描  述: 打开除IAP模块以外所有外设时钟
  输入值: 无
  输出值: 无
  返回值: 无
 ***************************************************************/
void DeviceClockAllEnableButIAP(void)
{
    uint32_t prot_tmp;
    prot_tmp = SCU->PROT.PROT;

    if (prot_tmp != 0)                 //写保护已开
        SCU_RegUnLock();               //解锁写保护

    SCU->PCLKEN0.Word = 0xFFFFFFFB;
    SCU->PCLKEN1.Word = 0xFFFFFFFF;    //打开除IAP以外所有外设时钟

    if (prot_tmp != 0)                 //写保护已开
        SCU_RegLock();                 //打开写保护

    return;
}

/***************************************************************
  函数名：SystemClockSelect
  描  述：系统时钟选择
  输入值：CLKx 系统时钟源选择
  输出值：无
  返回值：无
 ***************************************************************/
void SystemClockSelect(SCU_TYPE_CLK_SEL CLK_SEL)
{
    SCU_RegUnLock();                        //解锁

    switch (CLK_SEL)
    {
        case CLK_SEL_HRC:
            SCU_HRC_Enable();

            while (SCU_HRCReadyFlag() != SET);

            break;

        case CLK_SEL_XTAL:
            SCU_XTAL_Enable();

            while (SCU_XTALReadyFlag() != SET);

            break;

        default:
            break;
    }

    SCU->SCLKEN0.CLKFLT_BY = 0; //系统时钟不为48M，不旁路时钟滤波
    SCU->SCLKEN0.SYSCLK_DIV = 0;
    SCU->SCLKEN0.CLK_SEL = CLK_SEL;

    SCU_RegLock();
}


/***************************************************************
  函数名：PLLClock_Config
  描  述：PLL时钟配置,并设置PLL时钟为系统时钟
  输入值：pll_en:是否开启PLL，pll_origin：pll时钟源选择，pll_out：pll输出频率选择，sys_pll：系统时钟是否使用PLL时钟
  输出值：无
  返回值：无
 ***************************************************************/
void PLLClock_Config(TYPE_FUNCEN pll_en, SCU_PLL_Origin  pll_origin, SCU_PLL_Out pll_out, TYPE_FUNCEN sys_pll)
{
    uint32_t i_buf;
    uint32_t n_buf = 29000;

    SCU_RegUnLock();

    if (pll_en == DISABLE) //如果PLL配置为禁止，则直接禁止PLL，并返回
    {
        SCU->SCLKEN0.PLL_MUX = 0;
        SCU->SCLKEN1.PLL_BYLOCK = 0;
        SCU->SCLKEN0.CLKFLT_BY = 0x00;
        SCU->SCLKEN1.PLL_EN = 0;
        return;
    }

    if ((pll_origin == SCU_PLL_HRC))        //如果使用内部高速时钟，需开启内部高速时钟
    {
        if (SCU->SCLKEN1.HRC_RDY == 0)
        {
            SCU->SCLKEN1.HRC_EN = 1;

            while (SCU->SCLKEN1.HRC_RDY == 0); //等待HRC开启
        }
    }

    if ((pll_origin == SCU_PLL_XTAL_32K)
            || (pll_origin == SCU_PLL_XTAL_4M)
            || (pll_origin == SCU_PLL_XTAL_8M)
            || (pll_origin == SCU_PLL_XTAL_16M)
            || (pll_origin == SCU_PLL_XTAL_20M))          //如果使用外部时钟，需开启外部时钟
    {
        if (SCU->SCLKEN1.XTAL_RDY == 0)
        {
            SCU->SCLKEN1.XTAL_EN = 1;
            SCU->SCLKEN0.XTAL_LP = 0;

            while (SCU->SCLKEN1.XTAL_RDY == 0); //等待XTAL开启
        }
    }

    switch (pll_origin)
    {
        case SCU_PLL_HRC:
            SCU->SCLKEN1.PLL_REF_SEL = 0x00;
            break;

        case SCU_PLL_LRC:
            SCU->SCLKEN1.PLL_REF_SEL = 0x02;
            break;

        case SCU_PLL_XTAL_32K:
            SCU->SCLKEN1.PLL_REF_SEL = 0x03;
            break;

        case SCU_PLL_XTAL_4M:
            SCU->SCLKEN1.PLL_REF_SEL = 0x04;
            break;

        case SCU_PLL_XTAL_8M:
            SCU->SCLKEN1.PLL_REF_SEL = 0x05;
            break;

        case SCU_PLL_XTAL_16M:
            SCU->SCLKEN1.PLL_REF_SEL = 0x06;
            break;

        case SCU_PLL_XTAL_20M:
            SCU->SCLKEN1.PLL_REF_SEL = 0x07;
            break;

        default:
            break;
    }

    SCU->SCLKEN1.PLL_48M_SEL = pll_out;   //配置PLL输出为32或48Mhz

    SCU->SCLKEN1.PLL_EN = 1;

    for (i_buf = 0; i_buf < n_buf; i_buf++)    //标志位查询，超时等待时间20ms
    {
        if (SCU->SCLKEN1.PLL_RDY == SET)
            break;
    }

    if (sys_pll == ENABLE)
    {

        if (pll_out == SCU_PLL_32M)
        {
            SCU->SCLKEN0.CLKFLT_BY = 0;
        }
        else
        {
            SCU->SCLKEN0.CLKFLT_BY = 0x55;  //48M 时钟滤波旁路
        }

        SCU->SCLKEN1.PLL_BYLOCK = 1;
        SCU->SCLKEN0.PLL_MUX = 1;
    }
    else
    {
        SCU->SCLKEN0.PLL_MUX = 0;
        SCU->SCLKEN1.PLL_BYLOCK = 0;
        SCU->SCLKEN0.CLKFLT_BY = 0x00;
    }

    SCU_RegLock();
}

/*************************END OF FILE**********************/



