在线时间66 小时
UID3183261
注册时间2015-11-22
NXP金币0
TA的每日心情 | 怒 2016-10-20 11:32 |
---|
签到天数: 1 天 [LV.1]初来乍到
高级会员
- 积分
- 545
- 最后登录
- 2022-3-1
|
我的芯片是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&WT_TYPE=Lab%20and%20Test%20Software&WT_VENDOR=FREESCA ...
|
|