Kinetis MCG

/*!
 * @brief Calculates the PLL divider setting for a desired output frequency.
 *
 * This function calculates the correct reference clock divider (\c PRDIV) and
 * VCO divider (\c VDIV) to generate a desired PLL output frequency. It returns the
 * closest frequency match with the corresponding \c PRDIV/VDIV
 * returned from parameters. If a desired frequency is not valid, this function
 * returns 0.
 *
 * @param refFreq    PLL reference clock frequency.
 * @param desireFreq Desired PLL output frequency.
 * @param prdiv      PRDIV value to generate desired PLL frequency.
 * @param vdiv       VDIV value to generate desired PLL frequency.
 * @return Closest frequency match that the PLL was able generate.
 */
uint32_t CLOCK_CalcPllDiv(uint32_t refFreq, uint32_t desireFreq, uint8_t *prdiv, uint8_t *vdiv);

 

uint32_t CLOCK_CalcPllDiv(uint32_t refFreq, uint32_t desireFreq, uint8_t *prdiv, uint8_t *vdiv)
{
    uint8_t ret_prdiv;           /* PRDIV to return. */
    uint8_t ret_vdiv;            /* VDIV to return.  */
    uint8_t prdiv_min;           /* Min PRDIV value to make reference clock in allowed range. */
    uint8_t prdiv_max;           /* Max PRDIV value to make reference clock in allowed range. */
    uint8_t prdiv_cur;           /* PRDIV value for iteration.    */
    uint8_t vdiv_cur;            /* VDIV value for iteration.     */
    uint32_t ret_freq = 0U;      /* PLL output fequency to return. */
    uint32_t diff = 0xFFFFFFFFU; /* Difference between desireFreq and return frequency. */
    uint32_t ref_div;            /* Reference frequency after PRDIV. */

    /*
       Steps:
       1. Get allowed prdiv with such rules:
          1). refFreq / prdiv >= FSL_FEATURE_MCG_PLL_REF_MIN.
          2). refFreq / prdiv <= FSL_FEATURE_MCG_PLL_REF_MAX.
       2. For each allowed prdiv, there are two candidate vdiv values:
          1). (desireFreq / (refFreq / prdiv)).
          2). (desireFreq / (refFreq / prdiv)) + 1.
          If could get the precise desired frequency, return current prdiv and
          vdiv directly. Otherwise choose the one which is closer to desired
          frequency.
     */

    // 参考时钟(16M) 除以 PLL分频系数 必须位于 8M - 16M hz (参考手册“MCG mode switching”)
    if ((refFreq < FSL_FEATURE_MCG_PLL_REF_MIN) ||
        (refFreq > (FSL_FEATURE_MCG_PLL_REF_MAX * (FSL_FEATURE_MCG_PLL_PRDIV_MAX + FSL_FEATURE_MCG_PLL_PRDIV_BASE))))
    {
        return 0U;
    }

    // PLL分频系数 必须位于合适范围之内
    prdiv_max = refFreq / FSL_FEATURE_MCG_PLL_REF_MIN;
    prdiv_min = (refFreq + FSL_FEATURE_MCG_PLL_REF_MAX - 1U) / FSL_FEATURE_MCG_PLL_REF_MAX;//(16M + 16M -1) / 16M = 1

    // fMCGOUTCLK * 2 = 24M 首先我们期望得到输出频率 120M hz, 这里乘以2为得到 (OSCCLK / PLL_R) × M
    desireFreq *= 2U;

    /* PRDIV traversal. */
    for (prdiv_cur = prdiv_max; prdiv_cur >= prdiv_min; prdiv_cur--)
    {
        //参考时钟OSCCLK为 16M hz 参考时钟除以PLL分频 得到 (OSCCLK / PLL_R)
        ref_div = refFreq / prdiv_cur;//16M / 2 = 8M

        //2倍的期望频率 除以 分频后的时钟 得到 M 倍增因子
        vdiv_cur = desireFreq / ref_div;//240M / 8M = 30 { (fMCGOUTCLK *2)/(OSCCLK / PLL_R) = M }

        //如果倍增因子 M  小于15 或者 大于 47 则继续下一个for循环, 找到合适的倍增因子
        if ((vdiv_cur < FSL_FEATURE_MCG_PLL_VDIV_BASE - 1U) || (vdiv_cur > FSL_FEATURE_MCG_PLL_VDIV_BASE + 31U))
        {
            /* No VDIV is available with this PRDIV. */
            continue;
        }
               //倍增因子 乘以 分频后的时钟 得到 2*fMCGOUTCLK
        ret_freq = vdiv_cur * ref_div;//30 * 8M = 240M {M * (OSCCLK / PLL_R)} = fMCGOUTCLK * 2
  
        //如果倍增因子 大于 16
        if (vdiv_cur >= FSL_FEATURE_MCG_PLL_VDIV_BASE) //30 > 16
        {
            //如果得到的 2*fMCGOUTCLK 和 2倍期望输出时钟频率(2*desireFreq)相等
            if (ret_freq == desireFreq)
            {
                //写入PLL 分频数 MCG_C5[PRDIV0]
                *prdiv = prdiv_cur - FSL_FEATURE_MCG_PLL_PRDIV_BASE; //2 - 1 = 1
                //写入倍增因子 MCG_C6[VDIV0]
                *vdiv = vdiv_cur - FSL_FEATURE_MCG_PLL_VDIV_BASE; //30(M ==C6[VDIV0]) - 16 = 14
                //返回 期望的输出时钟 fMCGOUTCLK
                return ret_freq / 2U; //(OSCCLK / PLL_R) × M
            }
            /* New PRDIV/VDIV is closer. */
            if (diff > desireFreq - ret_freq) //若 0xFFFFFFFFU > 2倍fMGCOUTclk - 2倍
            {
                diff = desireFreq - ret_freq;
                ret_prdiv = prdiv_cur;
                ret_vdiv = vdiv_cur;
            }
        }
        vdiv_cur++; //倍增因子自增 继续寻找
       
        //如果倍增因子 小于 47
        if (vdiv_cur <= (FSL_FEATURE_MCG_PLL_VDIV_BASE + 31U))
        {
            ret_freq += ref_div;
            /* New PRDIV/VDIV is closer. */
            if (diff > ret_freq - desireFreq)
            {
                diff = ret_freq - desireFreq;
                ret_prdiv = prdiv_cur;
                ret_vdiv = vdiv_cur;
            }
        }
    }

    if (0xFFFFFFFFU != diff)
    {
        /* PRDIV/VDIV found. */
        *prdiv = ret_prdiv - FSL_FEATURE_MCG_PLL_PRDIV_BASE;
        *vdiv = ret_vdiv - FSL_FEATURE_MCG_PLL_VDIV_BASE;
        ret_freq = (refFreq / ret_prdiv) * ret_vdiv;
        return ret_freq / 2U;
    }
    else
    {
        /* No proper PRDIV/VDIV found. */
        return 0U;
    }
}

 

desireFreq  为 f_{MCGOUTclk}   refFreq 为 OSCCLK   prdiv_cur 为 PLL_R(MCG_C5[PRDIV0])    vdiv_cur 为 MCG_C6[VDIV0]

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值