查看: 1095|回复: 1

[已解决] 我的芯片是KL25,我想用下面的函数输出96M的MCGPLLCLK

[复制链接]
  • TA的每日心情

    2016-10-20 11:32
  • 签到天数: 1 天

    [LV.1]初来乍到

    12

    主题

    108

    帖子

    0

    高级会员

    Rank: 4

    积分
    545
    最后登录
    2022-3-1
    发表于 2016-11-1 11:45:39 | 显示全部楼层 |阅读模式
    我的芯片是KL25,我想用下面的函数输出96M的MCGPLLCLK,晶振为8M,但是只要我倍频至96M芯片就异常,函数返回0,因为USB要48MHZ,我必须倍频,但是这个总出问题,求论坛大神帮忙解决一下,小弟在此谢过了。。。。。

    /* Functon name : pll_init
    *                                                                                                                                            晶振频率(/时钟源)
    *说明:PLL时钟输出;MCGOUTCLK = MCGPLLCLK = -------------------- X PLL倍频
    *                                                                                                                                                  PLL分频
    *
    * Parameters: crystal_val - external clock frequency in Hz either from a crystal or square
    *                           wave clock source (外部时钟源的大小不能超过50M)
    *             hgo_val     - selects whether low power or high gain mode is selected
    *                           for the crystal oscillator. This has no meaning if an
    *                           external clock is used.
    *             erefs_val   - 选择外部时钟源:0为时钟源,1为晶振
    *             prdiv_val   - PLL分频 范围1-25
    *             vdiv_val    - PLL倍频 范围24-50
    *
    * Return value : PLL frequency (Hz) or error code
    */
    int pll_init(int crystal_val, unsigned char hgo_val, unsigned char erefs_val, signed char prdiv_val, signed char vdiv_val)
    {
      unsigned char frdiv_val;
      unsigned char temp_reg;
      unsigned char prdiv, vdiv;
      short i;
      int ref_freq;
      int pll_freq;

      // 检查是否在FEI模式
      if (!((((MCG->S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) == 0x0) && // check CLKS mux has selcted FLL output
          (MCG->S & MCG_S_IREFST_MASK) &&                                  // 检查FLL是否用内部时钟
          (!(MCG->S & MCG_S_PLLST_MASK))))                                 // check PLLS mux has selected FLL
      {
        return 0x1;      // return error code                                               
      }

      // 检查外部时钟源或者晶振是否超过50MHZ
      if  (crystal_val > 50000000) {return 0x21;}

      // check crystal frequency is within spec. if crystal osc is being used as PLL ref
      if (erefs_val)      //检测外部晶振是否在3M-32M之间
      {
        if ((crystal_val < 3000000) || (crystal_val > 32000000)) {return 0x22;} // return 1 if one of the available crystal options is not available
      }

      // make sure HGO will never be greater than 1. Could return an error instead if desired.
      if (hgo_val > 0)   //该值只能是0、 1,以防误写
      {
        hgo_val = 1; // force hgo_val to 1 if > 0
      }

      // Check PLL divider settings are within spec.
      if ((prdiv_val < 1) || (prdiv_val > 25)) {return 0x41;}  //检查PLL分频值是否超出规定范围1-25
      if ((vdiv_val < 24) || (vdiv_val > 50)) {return 0x42;}   //检查PLL因子是否超出范围24-50

      // Check PLL reference clock frequency is within spec.
      ref_freq = crystal_val / prdiv_val;
      if ((ref_freq < 2000000) || (ref_freq > 4000000)) {return 0x43;}//检查PLL是指是否在2M-4M之间MCG->C5

      // Check PLL output frequency is within spec.
      pll_freq = (crystal_val / prdiv_val) * vdiv_val;
      if ((pll_freq < 48000000) || (pll_freq > 100000000)) {return 0x45;}//PLL输出时钟在48M-100M之间

      // configure the MCG_C2 register
      // the RANGE value is determined by the external frequency. Since the RANGE parameter affects the FRDIV divide value
      // it still needs to be set correctly even if the oscillator is not being used

      temp_reg = MCG->C2;
      temp_reg &= ~(MCG_C2_RANGE0_MASK | MCG_C2_HGO0_MASK | MCG_C2_EREFS0_MASK); // clear fields before writing new values

      if (crystal_val <= 8000000)//外部时钟源或者晶振小等于 8M时的情况
      {
        temp_reg |= (MCG_C2_RANGE0(1) | (hgo_val << MCG_C2_HGO0_SHIFT) | (erefs_val << MCG_C2_EREFS0_SHIFT));
      }  //时钟范围中速和最低3位的选择
      else                       //外部时钟源或者晶振大于 8M时的情况
      {
        temp_reg |= (MCG_C2_RANGE0(2) | (hgo_val << MCG_C2_HGO0_SHIFT) | (erefs_val << MCG_C2_EREFS0_SHIFT));
      }   //时钟范围高速和最低3位 的选择
      MCG->C2 = temp_reg;   //对MCG->C2寄存器的配置

      // determine FRDIV based on reference clock frequency
      // since the external frequency has already been checked only the maximum frequency for each FRDIV value needs to be compared here.
      if (crystal_val <= 1250000) {frdiv_val = 0;}  //外部时钟源或者晶振小等于1.25M
      else if (crystal_val <= 2500000) {frdiv_val = 1;} //外部时钟源或者晶振小等于2.5M
      else if (crystal_val <= 5000000) {frdiv_val = 2;} //外部时钟源或者晶振小等于5M
      else if (crystal_val <= 10000000) {frdiv_val = 3;}//外部时钟源或者晶振小等于10M
      else if (crystal_val <= 20000000) {frdiv_val = 4;}//外部时钟源或者晶振小等于20M
      else {frdiv_val = 5;}                                                                                                                 //外部时钟源或者晶振小等于50M,为下面提供值

      // Select external oscillator and Reference Divider and clear IREFS to start ext osc
      // If IRCLK is required it must be enabled outside of this driver, existing state will be maintained
      // CLKS=2, FRDIV=frdiv_val, IREFS=0, IRCLKEN=0, IREFSTEN=0
      temp_reg = MCG->C1;
      temp_reg &= ~(MCG_C1_CLKS_MASK | MCG_C1_FRDIV_MASK | MCG_C1_IREFS_MASK); // Clear values in these fields
      temp_reg |= (MCG_C1_CLKS(2) | MCG_C1_FRDIV(frdiv_val)); // Set the required CLKS and FRDIV values
      MCG->C1 = temp_reg;  //MCG->C1寄存器配置,选择外部时钟,FLL分频,内部时钟使能

      // if the external oscillator is used need to wait for OSCINIT to set
      if (erefs_val) //如果选择的是晶振为时钟源,则需要等待晶振就绪
      {
        for (i = 0 ; i < 20000 ; i++)
        {
          if (MCG->S & MCG_S_OSCINIT0_MASK) break; // jump out early if OSCINIT sets before loop finishes
        }
      if (!(MCG->S & MCG_S_OSCINIT0_MASK)) return 0x23; // check bit is really set and return with error if not set
      }

      // wait for Reference clock Status bit to clear
      for (i = 0 ; i < 2000 ; i++) //等待FLL选择外部时钟源
      {
        if (!(MCG->S & MCG_S_IREFST_MASK)) break; // jump out early if IREFST clears before loop finishes
      }
      if (MCG->S & MCG_S_IREFST_MASK) return 0x11; // check bit is really clear and return with error if not set

      // Wait for clock status bits to show clock source is ext ref clk
      for (i = 0 ; i < 2000 ; i++) //判断外部时钟源是否被选择
      {
        if (((MCG->S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) == 0x2) break; // jump out early if CLKST shows EXT CLK slected before loop finishes
      }
      if (((MCG->S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x2) return 0x1A; // check EXT CLK is really selected and return with error if not

      // Now in FBE
      // It is recommended that the clock monitor is enabled when using an external clock as the clock source/reference.
      // It is enabled here but can be removed if this is not required.
      MCG->C6 |= MCG_C6_CME0_MASK;  //开启时钟监控,也可不开

      // Configure PLL
      // Configure MCG_C5
      // If the PLL is to run in STOP mode then the PLLSTEN bit needs to be OR'ed in here or in user code.
      temp_reg = MCG->C5;
      temp_reg &= ~MCG_C5_PRDIV0_MASK;
      temp_reg |= MCG_C5_PRDIV0(prdiv_val - 1);    //set PLL ref divider
      MCG->C5 = temp_reg;  //配置PLL的分频值

      // Configure MCG_C6
      // The PLLS bit is set to enable the PLL, MCGOUT still sourced from ext ref clk
      // The loss of lock interrupt can be enabled by seperately OR'ing in the LOLIE bit in MCG_C6
      temp_reg = MCG->C6; // store present C6 value
      temp_reg &= ~MCG_C6_VDIV0_MASK; // clear VDIV settings
      temp_reg |= MCG_C6_PLLS_MASK | MCG_C6_VDIV0(vdiv_val - 24); // write new VDIV and enable PLL
      MCG->C6 = temp_reg; // update MCG_C6  PLL倍频

      // wait for PLLST status bit to set
      for (i = 0 ; i < 2000 ; i++)  //等待PLL时钟输出
      {
        if (MCG->S & MCG_S_PLLST_MASK) break; // jump out early if PLLST sets before loop finishes
      }
      if (!(MCG->S & MCG_S_PLLST_MASK)) return 0x16; // check bit is really set and return with error if not set

      // Wait for LOCK bit to set
      for (i = 0 ; i < 4000 ; i++)  //等待PLL时钟锁定
      {
        if (MCG->S & MCG_S_LOCK0_MASK) break; // jump out early if LOCK sets before loop finishes
      }
      if (!(MCG->S & MCG_S_LOCK0_MASK)) return 0x44; // check bit is really set and return with error if not set

      // Use actual PLL settings to calculate PLL frequency
      prdiv = ((MCG->C5 & MCG_C5_PRDIV0_MASK) + 1);
      vdiv = ((MCG->C6 & MCG_C6_VDIV0_MASK) + 24);

      // now in PBE
            //选择PLL/FLL输出
      MCG->C1 &= ~MCG_C1_CLKS_MASK; // clear CLKS to switch CLKS mux to select PLL as MCG_OUT

      // Wait for clock status bits to update
      for (i = 0 ; i < 2000 ; i++)//等待输出为PLL时钟
      {
        if (((MCG->S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) == 0x3) break; // jump out early if CLKST = 3 before loop finishes
      }
      if (((MCG->S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x3) return 0x1B; // check CLKST is set correctly and return with error if not

      // Now in PEE
        //如果初始化OK!,则得到输出的PLL频率
      return ((crystal_val / prdiv) * vdiv); //MCGOUT equals PLL output frequency
    } // pll_init


    最佳答案

    请参考FRDM-KL25 sample code 包中的USB 例程,该例程包是针对的FRDM-KL25,外部时钟也是8M cache.nxp.com/files/32bit/software/KL25_SC.exe?fpsp=1&amp;WT_TYPE=Lab%20and%20Test%20Software&amp;WT_VENDOR=FREESCA ...
    回复

    使用道具 举报

    该用户从未签到

    124

    主题

    3600

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    5781
    最后登录
    1970-1-1
    发表于 2016-11-1 13:55:05 | 显示全部楼层
    请参考FRDM-KL25 sample code 包中的USB 例程,该例程包是针对的FRDM-KL25,外部时钟也是8M
    1. cache.nxp.com/files/32bit/software/KL25_SC.exe?fpsp=1&WT_TYPE=Lab%20and%20Test%20Software&WT_VENDOR=FREESCALE&WT_FILE_FORMAT=exe&WT_ASSET=Downloads&fileExt=.exe
    复制代码

    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-5-3 03:17 , Processed in 0.128407 second(s), 22 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表