#include "lks32mc09x_sys.h"
#include "lks32mc09x_user_manual.h"

/**
 * @brief 数字模块时钟使能
 * @param nModule 模块编号
 * @param state 使能或禁用状态
 */
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
    REG_WRITE(SYS0->PROTECT, 0x7a83);
    if (state)
    {
        REG_SET(SYS0->CLK_FEN, nModule);
    }
    else
    {
        REG_RESET(SYS0->CLK_FEN, nModule);
    }
    REG_WRITE(SYS0->PROTECT, 0);
}

/**
 * @brief 软复位模块
 * @param nModule 模块编号
 */
void SYS_SoftResetModule(uint32_t nModule)
{
    REG_WRITE(SYS0->PROTECT, 0x7a83);
    REG_SET(SYS0->SFT_RST, nModule);
    REG_RESET(SYS0->SFT_RST, nModule); // 触发复位后需清除复位位
    REG_WRITE(SYS0->PROTECT, 0);
}

/**
 * @brief 软复位所有模块
 */
void SYS_SoftReset_all(void)
{
    REG_WRITE(SYS0->PROTECT, 0x7a83);
    REG_WRITE(SYS0->SFT_RST, 0xFFFFFFFF);
    REG_WRITE(SYS0->SFT_RST, 0x00000000); // 触发复位后需清除所有复位位
    REG_WRITE(SYS0->PROTECT, 0);
}

/**
 * @brief 选择系统时钟
 * @param SYS_MCLK_x 时钟源选择
 * @see 使用外部晶振时，除了晶振使能外，还需要配置
 * SYS_AFE_REG1_XTAL_FLT_BIT  [9] 晶振低通滤波关闭 1:关闭 0:打开
 * SYS_AFE_REG1_XTAL_SCHT_BIT [4] 晶振选择反相器或者施密特 1:反相器 0:施密特
 * SYS_AFE_REG2_XTAL_RSEL_BIT [8] 晶体起振电路电阻调节 0:P端电阻维持原值 1: P端电阻增加一倍
 */
void SYS_MclkChoice(uint32_t SYS_MCLK_x)
{
    REG_WRITE(SYS0->PROTECT, 0x7a83);
    if (SYS_MCLK_x & 0x8000) // 使用外部晶振
    {
        REG_SET(SYS_AFE_REG5, BIT4);
    }
    else
    {
        REG_RESET(SYS_AFE_REG5, BIT4);
    }
    REG_WRITE_BIT(SYS0->CLK_CFG, 0x3ff, SYS_MCLK_x);
    {
        int cnt;
        for (cnt = 0; cnt < 4000; cnt++)
        {
            __NOP();
        }
    }
    REG_WRITE(SYS0->PROTECT, 0);
}

/**
 * @brief UART时钟分频
 * @param div 分频系数
 */
void SYS_UartClkDiv(uint16_t div)
{
    REG_WRITE(SYS0->PROTECT, 0x7a83);
    REG_WRITE(SYS0->CLK_DIV2, div);
    REG_WRITE(SYS0->PROTECT, 0);
}

/**
 * @brief I2C时钟分频
 * @param div 分频系数
 */
void SYS_I2CClkDiv(uint16_t div)
{
    REG_WRITE(SYS0->PROTECT, 0x7a83);
    REG_WRITE(SYS0->CLK_DIV0, div);
    REG_WRITE(SYS0->PROTECT, 0);
}

/**
 * @brief SPI时钟分频
 * @param div 分频系数
 */
void SYS_SPIClkDiv(uint16_t div)
{
    // 接口保留，09的spi分频只在spi模块里设置即可
}

/**
 * @brief 获取当前MCU时钟
 * @return 当前MCU时钟频率
 */
uint32_t SYS_ReadMcuClk(void)
{
    uint32_t clk    = 96000000;
    uint32_t clkdiv = (SYS_CLK_CFG & SYS_CLK_CFG_CLK_DIV_MASK) >> SYS_CLK_CFG_CLK_DIV_POS;
    uint32_t clksel = (SYS_CLK_CFG & SYS_CLK_CFG_CLK_SEL_MASK) >> SYS_CLK_CFG_CLK_SEL_POS;
    switch (clksel) //  0: HRC 1: PLL 2: LRC
    {
        case 0: // HRC
            clk = 8000000;
            break;
        case 1: // PLL
            switch (clkdiv)
            {
                case 0xff: // 96M
                    clk = 96000000;
                    break;
                case 0x55: // 48M
                    clk = 48000000;
                    break;
                case 0x11: // 24M
                    clk = 24000000;
                    break;
                case 0x01: // 12M
                    clk = 12000000;
                    break;
                case 0x00: // 12M
                    clk = 12000000;
                    break;
                default: // 逐位计算
                    clk = 0;
                    for (int i = 0; i < 8; i++)
                    {
                        if (clkdiv & (1 << i))
                        {
                            clk += 12000000;
                        }
                        else
                        {
                            __NOP();
                        }
                    }
                    break;
            }
            break;
        case 2: // LRC
            clk = 32000;
            break;
        default:
            break;
    }
    return clk;
}

/**
 * @brief 掉电检测配置
 * @param SYS_PVDSEL_x 掉电检测配置值
 */
void SYS_SetPowerFailureDetection(uint32_t SYS_PVDSEL_x)
{
    REG_WRITE(SYS0->PROTECT, 0x7a83);
    REG_WRITE_BIT(SYS0->AFE_REG0, (BIT11 | BIT12 | BIT13 | BIT14), SYS_PVDSEL_x);
    REG_WRITE(SYS0->PROTECT, 0);
}

/**
 * @brief 复位所有外设到默认状态（芯片上电后的状态）
 * @warning 不复位的寄存器有：
 *              复位事件记录寄存器
 *              后备存储器
 *              ram
 *              校准寄存器（仅限于上电会自动加载的校准寄存器）
 *              只读的寄存器
 *              部分flash寄存器（如FLASH->WDATA等，写入操作无意义）
 *              所有内核的寄存器
 */
void SYS_ResetPeripheral(void)
{
    SYS_ResetIO();
    SYS_ResetPeripheralInternal();
}
/**
 * @brief 复位所有内部外设为默认值(除io的所有模块)
 * @warning 不复位的寄存器有：
 *              复位事件记录寄存器
 *              后备存储器
 *              ram
 *              校准寄存器（仅限于上电会自动加载的校准寄存器）
 *              只读的寄存器
 *              部分flash寄存器（如FLASH->WDATA等，写入操作无意义）
 *              所有内核的寄存器
 */
void SYS_ResetPeripheralInternal(void)
{
    uint32_t flg = __get_PRIMASK();
    __disable_irq();
    SYS->PROTECT      = 0x7a83;
    SYS->SFT_RST      = (0xffffffff & ~BIT11); // 复位除GPIO之外的所有模块
    SYS->SFT_RST      = 0;
    SYS->AFE_REG0     = 0;
    SYS->AFE_REG1     = 0;
    SYS->AFE_REG2     = 0;
    SYS->AFE_REG3     = 0;
    SYS->AFE_REG4     = 0;
    SYS->AFE_REG5     = 0;
    SYS->AFE_REG7     = 0;
    SYS->AFE_DAC_CTRL = 0;
    SYS->AFE_DAC0     = 0;
    SYS->AFE_DAC1     = 0;

    SYS->CLK_CFG = 0;
    {
        int cnt;
        for (cnt = 0; cnt < 4000; cnt++)
        {
            __NOP();
        }
    }
    SYS->IO_CFG   = 0;
    SYS->DBG_CFG  = 0;
    SYS->CLK_DIV0 = 0;
    SYS->CLK_DIV1 = 0;
    SYS->CLK_DIV2 = 0;
    SYS->CLK_FEN  = 0;
    SYS->RAM_LOCK = 0;
    SYS->SFT_RST  = 0;
    SYS->FLSE     = 0;
    SYS->FLSP     = 0;
    SYS->PROTECT  = 0;

    FLASH->CFG    = 0xe0;
    FLASH->ADDR   = 0;
    FLASH->NADDR  = 0;
    FLASH->NKEY   = 0;
    uint32_t data = FLASH->PROTECT; // 更新falsh状态
    __set_PRIMASK(flg);
}
/**
 * @brief 复位所有IO为默认值（gpio以及部分和io有关的sys寄存器）
 */
void SYS_ResetIO(void)
{
    uint32_t flg = __get_PRIMASK();
    __disable_irq();
    SYS_WR_PROTECT = 0x7a83;
    SYS_SFT_RST    = BIT11; // 复位GPIO，GPIO0 GPIO1 GPIO2三个模块的所有寄存器
    SYS_SFT_RST    = 0;
    SYS_AFE_REG1 &= ~BIT8;                                           // 关闭LDO输出到P0.0
    SYS_AFE_REG2 &= ~(BIT15 | BIT14 | BIT13 | BIT11 | BIT10 | 0xff); // 关闭dac、vref、vbgp、opa输出到P0.0
    SYS_IO_CFG     = 0;                                              // 恢复SWD&RST为默认状态
    SYS_WR_PROTECT = 0;
    __set_PRIMASK(flg);
}
