// 配置系统时钟
// 杭州秒寻科技有限公司
// 周涛
// 2023-09-11

#include "includes.h"

// 配置系统时钟，包括HSE，PLL
uint8 sysrcc_config_pll()
{
    ErrorStatus errFlag = ERROR;

    RCC_DeInit(); // 反向初始化时钟

    // 配置总线时钟分频因子
    RCC_HCLKConfig(RCC_SYSCLK_Div1); // AHB高速外设总线（122.88MHz）
    RCC_PCLK2Config(RCC_HCLK_Div2);  // APB2(高速)（61.44MHz）
    RCC_PCLK1Config(RCC_HCLK_Div4);  // APB1(低速)（30.72MHz）

    RCC_HSEConfig(RCC_HSE_ON); // 使能HSE
    errFlag = RCC_WaitForHSEStartUp();
    if (errFlag == SUCCESS) // HSE已启动
    {

        // 配置主PLL（输出122.88MHz）
        RCC_PLLConfig(RCC_PLLSource_HSE, 8, 160, 2, 4);     // M:8，N：160，P:2，Q:4
        RCC_PLLCmd(ENABLE);                                 // 使能主PLl
        while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) // 等待主PLL稳定
        {
        }

        // 配置系统时钟源
        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); // PLL被当成系统时钟源
        while (RCC_GetSYSCLKSource() != 0x08)      // 等待时钟源切换
        {
        }

        // 配置调压器，均衡性能与功耗
        // RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);		   // 使能电源接口时钟
        // PWR_MainRegulatorModeConfig(PWR_Regulator_Voltage_Scale3); // 调压等级3

        // 配置FLASH预取指，指令缓存，数据缓存和等待状态
        FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_5WS;
    }
    else
    {
        return 0;
    }

    return 1;
}

// 配置输出时钟(MCO2)，给CPLD使用
void sysrcc_config_mco2()
{
    GPIO_InitTypeDef stGpio = {0};

    // 初始化MCO2
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_MCO);

    stGpio.GPIO_Mode = GPIO_Mode_AF;
    stGpio.GPIO_OType = GPIO_OType_PP;
    stGpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
    stGpio.GPIO_Speed = GPIO_Speed_50MHz;
    stGpio.GPIO_Pin = GPIO_Pin_9;
    GPIO_Init(GPIOC, &stGpio);
}

// 配置I2SPLL（输出61.44MHz时钟到I2S接口）
void sysrcc_config_i2spll()
{
    // 配置PLLI2S分频因子 n:400，q:4（SAI），r:2（I2S）
    RCC_PLLI2SConfig(160, 4, 4); // 输出时钟到i2s接口

    RCC_I2SCLKConfig(RCC_I2S2CLKSource_PLLI2S); // 配置I2S时钟源

    RCC_PLLI2SCmd(ENABLE); // 使能时钟
}

// 配置用户时钟
void sysrcc_config_user_clk()
{
    // 配置MCO2输出
    sysrcc_config_mco2();

    // 配置I2SPLL
    sysrcc_config_i2spll();
}