IMX6ULL 主频和时钟配置

NXP主频与时钟配置

寄存器简要说明

1、系统时钟来源

IMX6U系统时钟主要来源于两部分:32.768KHz 和 24MHz 的晶振。
32.768KHz 的晶振是 IMX6U 的 RTC 时钟源, 24MHz晶振是 IMX6U 内核和其它外设的时钟源。
NXP 将这些外设的时钟源进行了分组,一共有 7 组,这 7 组时钟源都是从 24MHz 晶振 PLL 而来的,因此也叫做 7 组 PLL
如图所示:
在这里插入图片描述
下图是时钟树,共有三部分: CLOCK_SWITCHER、CLOCK ROOT GENERATOR 和 SYSTEM CLOCKS;
1、左边的 CLOCK_SWITCHER 7 路 PLL 和8 路 PFD;
2、右边的 SYSTEM CLOCKS 就是芯片外设;
3、中间的 CLOCK ROOT GENERATOR 负责从 7 路 PLL 和 8 路 PFD 中选择合适的时钟源给外设使用。
在这里插入图片描述


2、内核时钟设置

1.1 时钟设置流程

根据时钟树可以看到ARM 内核时钟如图所示:
在这里插入图片描述
由图中可知:
①、内核时钟源来自于 PLL1
②、通过寄存器 CCM_CACRR 的 ARM_PODF 位对 PLL1 进行分频,可选择 1/2/4/8 分频
③、大家不要被此处的 2 分频给骗了,此处没有进行 2 分频
④、经过第②步分频以后的时钟就是 ARM 的内核时钟,也就是 IMX6U 的主频
流程类似:设置IMX6U 的主频设置为 528MHz
①假设PLL1=1056MHz -> ②假选择 2 分频=528MHz -> ③528MHz -> ④内核时钟= 528MHz

1.2 时钟设置的寄存器配置

①、寄存器 CCM_CACRR 的 ARM_PODF
②、PLL1 的频率通过寄存器 CCM_ANALOG_PLL_ARMn 来设置

寄存器 CCM_CACRR 如图所示:
在这里插入图片描述
寄存器 CCM_ANALOG_PLL_ARMn 如图所示:
在这里插入图片描述

在修改 PLL1 时钟频率的时候我们需要先将内核时钟源改为其他的时钟源, PLL1 可选择的时钟源如图所示:
在这里插入图片描述
①、 pll1_sw_clk 也就是 PLL1 的最终输出频率
②、此处是一个选择器,选择 pll1_sw_clk 的时钟源,由寄存器 CCM_CCSR 的PLL1_SW_CLK_SEL 位决定 pll1_sw_clk 是选择 pll1_main_clk 还是 step_clk
③、此处也是一个选择器,选择 step_clk 的时钟源,由寄存器 CCM_CCSR 的 STEP_SEL 位来决定 step_clk 是选择 osc_clk 还是 secondary_clk

上面3个过程,只用到了一个寄存器 CCM_CCSR:
在这里插入图片描述
寄存器 CCM_CCSR 我们只用到了 STEP_SEL、 PLL1_SW_CLK_SEL 这两个位,一个是用来选择 step_clk 时钟源的,一个是用来选择 pll1_sw_clk 时钟源的:
①、 设置寄存器 CCSR 的 STEP_SEL 位,设置 step_clk 的时钟源为 24M 的晶振。
②、设置寄存器 CCSR 的 PLL1_SW_CLK_SEL 位,设置 pll1_sw_clk 的时钟源为
step_clk=24MHz,通过这一步我们就将 I.MX6U 的主频先设置为 24MHz,直接来自于外部的
24M 晶振。
③、设置寄存器 CCM_ANALOG_PLL_ARMn,将 pll1_main_clk(PLL1)设置为 1056MHz。
④、设置寄存器 CCSR 的 PLL1_SW_CLK_SEL 位,重新将 pll1_sw_clk 的时钟源切换回
pll1_main_clk,切换回来以后的 pll1_sw_clk 就等于 1056MHz。
⑤、最后设置寄存器 CCM_CACRR 的 ARM_PODF 为 2 分频, I.MX6U 的内核主频就为
1056/2=528MHz。

3、PFD 时钟设置

主频设置好以后我们要设置 PLL 和 PFD 时钟
NXP 推荐的这 8 路 PFD 频率如表所示:
在这里插入图片描述
举例:
设置 PLL2 的 4 路 PFD 频率,用到寄存器是 CCM_ANALOG_PFD_528n;
在这里插入图片描述
PFD0_FRAC: PLL2_PFD0 的分频数, PLL2_PFD0 的计算公式为 52818/PFD0_FRAC,此为 可 设 置 的 范 围 为 12~35 。 如 果 PLL2_PFD0 的 频 率 要 设 置 为 352MHz 的 话PFD0_FRAC=52818/352=27。

4、AHB、 IPG 和 PERCLK 根时钟设置

7 路 PLL 和 8 路 PFD 设置完成以后最后还需要设置 AHB_CLK_ROOT 和 IPG_CLK_ROOT的时钟以及 PERCLK_CLK_ROOT 时钟频率;

应用解析

1. 时钟配置流程

step 1:使能 I.MX6U 所有外设时钟
step 2:设置 ARM 内核时钟为 528MHz
step 3:设置 PLL2(SYS PLL)各个 PFD
step 4:设置 PLL3(USB1)各个 PFD
step 5:设置 AHB 时钟 最小 6Mhz, 最大 132Mhz
step 6:设置 IPG_CLK_ROOT 最小 3Mhz,最大 66Mhz
step 7:设置 PERCLK_CLK_ROOT 时钟

2. 代码分析

step 1: 使能 I.MX6U 所有外设时钟

    void clk_enable(void)
    {
        CCM->CCGR0 = 0XFFFFFFFF;
        CCM->CCGR1 = 0XFFFFFFFF;
        CCM->CCGR2 = 0XFFFFFFFF;
        CCM->CCGR3 = 0XFFFFFFFF;
        CCM->CCGR4 = 0XFFFFFFFF;
        CCM->CCGR5 = 0XFFFFFFFF;
        CCM->CCGR6 = 0XFFFFFFFF;
    }

修改 I.MX6U 主频的步骤如下:
step 2: 设置主频,设置 ARM 内核时钟为 528MHz:
寄存器 CCM_CCSR 结构图:
在这里插入图片描述

    /* 设置寄存器 CCSR 的 STEP_SEL 位,当为0时, step_clk 的时钟源为 24M 的晶振。
     * 设置寄存器 CCSR 的 PLL1_SW_CLK_SEL 位,在设置好CCSR 的 STEP_SEL 位后,或上 (1 << 2), 
     * 设置 pll1_sw_clk 的时钟源为 step_clk时钟源,即 24MHz
     */
    if((((CCM->CCSR) >> 2) & 0x1 ) == 0) 	/* 判断当前pll1_sw_clk使用的是不是pll1_main_clk时钟 */
    {	
        /* 如果是,就换成备用时钟  1<<7的意思是将 0000 0001按照二进制左移8位,也就是1000 0000,
         * 寄存器 ~(1 << 8)的意思是得到的结果是 ~(0000 0001【STEP_SEL位】 0000 0000) 
         * 取反为 1111 1110 1111 1111 
         * 设置寄存器 CCSR 的 STEP_SEL 位,当STEP_SEL等于 0 时,step_clk 的时钟源为 24M 的晶振。*/
        CCM->CCSR &= ~(1 << 8);				

        /* 设置寄存器 CCSR 的 PLL1_SW_CLK_SEL 位,配置pll1_sw_clk时钟源为 step_clk 即为24MHz,
         *(通过这一步我们就将 I.MX6U 的主频先设置为 24MHz *
         *  将 1111 1110 1111 1111 | 0000 0000 0000 0100 = 0000 0000 0000 1【PLL1_SW_CLK_SEL位】000*/
        CCM->CCSR |= (1 << 2);				
    }

设置寄存器 CCM_ANALOG_PLL_ARMn,将 pll1_main_clk(PLL1)设置为 1056MHz:
寄存器 CCM_ANALOG_PLL_ARMn:
在这里插入图片描述

    /* 设置寄存器 CCM_ANALOG_PLL_ARMn,将 pll1_main_clk(PLL1)设置为 1056MHz */
    /* 0x7F等于0111 1111也就是代表着寄存器 CCM_ANALOG_PLL_ARMn 的 [6:0]位,
     * 也就是寄存器的输出频率,((88 << 0) & 0X7F)= 88,0101 1000 
     * (1 << 13) | ((88 << 0) & 0X7F)=0010 0000 0101 1000 即[6:0]位对应的还是88,
     * 而第14位代表BYPASS_CLK_SRC,BYPASS_CLK_SRC = 1代表CLK1 - Select the CLK1_N/CLK1_P,
     * 所以频率位 CLK=24 * 88 /2.0 = 1056 */
    CCM_ANALOG->PLL_ARM = (1 << 13) | ((88 << 0) & 0X7F); 

时钟切换回 pll1_main_clk:
寄存器 CCM_CCSR 结构图:在这里插入图片描述

    /* 通过修改寄存器,将 pll_sw_clk 时钟切换回 pll1_main_clk */
    CCM->CCSR &= ~(1 << 2);  

在这里插入图片描述

    /* 寄存器 CCM_CACRR 只有 ARM_PODF 位,可以设置为 0~7,分别对应 1~8 分频。
     * 如果要设置为2分频的话CCM_CACRR就要设置为1
     * 最后设置寄存器 CCM_CACRR 的 ARM_PODF 为 2 分频,ARM 内核时钟为 pll1_sw_clk/2=1056/2=528Mhz */
    CCM->CACRR = 1;   

step 2: PFD 时钟设置:
寄存器 CCM_ANALOG_PFD_528n 结构:
在这里插入图片描述

    /* 先设置 PLL2 的 4 路 PFD 频率,用到寄存器是 CCM_ANALOG_PFD_528n, */
	reg = CCM_ANALOG->PFD_528;

    /* 寄存器 CCM_ANALOG_PFD_528n 其实分为四组,分别对应PFD0~PFD3。
     * PFD0_FRAC[5:0],PFD1_FRAC[5:0],PFD2_FRAC[5:0],PFD3_FRAC[5:0]
     * PFD0_STABLE,判断 PLL2_PFD0 是否稳定,PFD0_CLKGATE: PLL2_PFD0 输出使能位,
     * 1 的时候关闭 PLL2_PFD0 的输出,为 0 的时候使能输出。
     * 清除原来的设置 */
	reg &= ~(0X3F3F3F3F);	

    /* 计算公式为 528*18/PFD0_FRAC */
	reg |= 32<<24;				/* 寄存器内容:0010 0000 = 32,PLL2_PFD3=528*18/32=297Mhz */
	reg |= 24<<16;				/* 寄存器内容:0001 1000 = 24,PLL2_PFD2=528*18/24=396Mhz(DDR使用的时钟,最大400Mhz) */
	reg |= 16<<8;				/* 寄存器内容:0001 0000 = 16,PLL2_PFD1=528*18/16=594Mhz */
	reg |= 27<<0;				/* 寄存器内容:0001 1011 = 27,PLL2_PFD0=528*18/27=352Mhz */

/* 将 reg 赋值给寄存器之后,将参数 reg 用于下一个 PLL 调用。设置PLL2_PFD0~3 */
	CCM_ANALOG->PFD_528=reg;	

step 3: 设置PLL3(USB1)各个PFD:
寄存器 CCM_ANALOG_PFD_480n 结构:
在这里插入图片描述

    /* 设置 PLL3_PFD0~PLL3_PFD3 这 4 路 PFD 的频率 , 使用到的寄存器是CCM_ANALOG_PFD_480n。*/
	/* 设置PLL3(USB1)各个PFD */
	reg = 0;					/* 清零   */
	reg = CCM_ANALOG->PFD_480;
	reg &= ~(0X3F3F3F3F);		/* 清除原来的设置 					 */
	reg |= 19<<24;				/* PLL3_PFD3=480*18/19=454.74Mhz 	*/
	reg |= 17<<16;				/* PLL3_PFD2=480*18/17=508.24Mhz 	*/
	reg |= 16<<8;				/* PLL3_PFD1=480*18/16=540Mhz		*/
	reg |= 12<<0;				/* PLL3_PFD0=480*18/12=720Mhz	 	*/
	CCM_ANALOG->PFD_480=reg;	/* 设置PLL3_PFD0~3 					*/	

step 4: AHB、IPG 和 PERCLK 根时钟设置:
寄存器 CCM_CBCDR 结构:
在这里插入图片描述
寄存器 CCM_CBCMR 结构:
在这里插入图片描述
寄存器 CCM_CSCMR1 结构:
在这里插入图片描述

4.1 AHB
	/*设置AHB时钟 最小6Mhz, 最大132Mhz (boot rom自动设置好了可以不用设置)*/
    /* 清除设置  ~(3 << 18) = ~(1100 0000 0000 0000 0000) = 0011 1111 1111 1111 1111 
     * 寄存器 CCM_CBCMR的PERIPH2_CLK2_SEL位: 
     * periph2_clk2 时钟源选择为 0 的时候选择 pll3_sw_clk,为 1 的时候选择 OSC
     * PRE_PERIPH2_CLK_SEL为0 选择 PLL2, 01 选择 PLL2_PFD2,10 选择 PLL2_PFD0, 
     * 11 选择 PLL4,这里选择的是 00 
     */ 
	CCM->CBCMR &= ~(3 << 18); 

    /* 0111 1111 1111 1111 1111 代表的意思是 
     * PERIPH2_CLK2_SEL为1的 1 的时候选择 OSC,
     * PRE_PERIPH2_CLK_SEL为0 选择 PLL2, 01 选择 PLL2_PFD2,10 选择 PLL2_PFD0, 
     * 11 选择 PLL4,这里选择的是 01
     */
	CCM->CBCMR |= (1 << 18);	/* pre_periph_clk=PLL2_PFD2=396MHz */

	CCM->CBCDR &= ~(1 << 25);	/* periph_clk=pre_periph_clk=396MHz */

	while(CCM->CDHIPR & (1 << 5));/* 等待握手完成 */
		

4.2 

	/* 修改AHB_PODF位的时候需要先禁止AHB_CLK_ROOT的输出,但是
	 * 我没有找到关闭AHB_CLK_ROOT输出的的寄存器,所以就没法设置。
	 * 下面设置AHB_PODF的代码仅供学习参考不能直接拿来使用!!
	 * 内部boot rom将AHB_PODF设置为了3分频,即使我们不设置AHB_PODF,
	 * AHB_ROOT_CLK也依旧等于396/3=132Mhz。*/
#if 0
	/* 要先关闭AHB_ROOT_CLK输出,否则时钟设置会出错 */
	CCM->CBCDR &= ~(7 << 10);	/* CBCDR的AHB_PODF清零 */
	CCM->CBCDR |= 2 << 10;		/* AHB_PODF 3分频,AHB_CLK_ROOT=132MHz */
	while(CCM->CDHIPR & (1 << 1));/
* 等待握手完成 */
#endif
	

4.3 IPG & PERCLK_PODF
	/* 5、设置IPG_CLK_ROOT最小3Mhz,最大66Mhz (boot rom自动设置好了可以不用设置)*/
	CCM->CBCDR &= ~(3 << 8);	/* CBCDR的IPG_PODF清零 */
	CCM->CBCDR |= 1 << 8;		/* IPG_PODF 2分频,IPG_CLK_ROOT=66MHz */
	
	/* 6、设置PERCLK_CLK_ROOT时钟 */
	CCM->CSCMR1 &= ~(1 << 6);	/* PERCLK_CLK_ROOT时钟源为IPG */
	CCM->CSCMR1 &= ~(7 << 0);	/* PERCLK_PODF位清零,即1分频 */

代码例子

/*
 * @description	: 使能I.MX6U所有外设时钟
 * @param 		: 无
 * @return 		: 无
 */
void clk_enable(void)
{
	CCM->CCGR0 = 0XFFFFFFFF;
	CCM->CCGR1 = 0XFFFFFFFF;
	CCM->CCGR2 = 0XFFFFFFFF;
	CCM->CCGR3 = 0XFFFFFFFF;
	CCM->CCGR4 = 0XFFFFFFFF;
	CCM->CCGR5 = 0XFFFFFFFF;
	CCM->CCGR6 = 0XFFFFFFFF;
}

/*
 * @description	: 初始化系统时钟,设置系统时钟为792Mhz,并且设置PLL2和PLL3各个
 				  PFD时钟,所有的时钟频率均按照I.MX6U官方手册推荐的值.
 * @param 		: 无
 * @return 		: 无
 */
void imx6u_clkinit(void)
{
	unsigned int reg = 0;
	/* 1、设置ARM内核时钟为792MHz */
	
	/* 1.1、判断当前ARM内核是使用的那个时钟源启动的,
	 *
	 *      正常情况下ARM内核是由pll1_sw_clk驱动的,而pll1_sw_clk有两个来源:pll1_main_clk和tep_clk。
	 *      如果我们要让ARM内核跑到792M的话那必须选择pll1_main_clk作为pll1的时钟源。
	 *      如果我们要修改pll1_main_clk时钟的话就必须先将pll1_sw_clk从pll1_main_clk切换到step_clk,
	 *		当修改完pll1_main_clk以后在将pll1_sw_clk切换回pll1_main_clk。而step_clk的时钟源可以选择板子上的24MHz晶振。
	 */
	
	if((((CCM->CCSR) >> 2) & 0x1 ) == 0) 	/* 判断当前pll1_sw_clk使用的是不是pll1_main_clk时钟*/
	{	
		// 如果是,就换成备用时钟  1<<7的意思是将 0000 0001按照二进制左移8位,也就是1000 0000,
		// 寄存器 ~(1 << 8)的意思是得到的结果是 0000 0001 0000 0000 
		CCM->CCSR &= ~(1 << 8);				/* 配置step_clk时钟源为24MH OSC,设置寄存器 CCSR 的 STEP_SEL 位,设置 step_clk 的时钟源为 24M 的晶振。 */	
		CCM->CCSR |= (1 << 2);				/* 配置pll1_sw_clk时钟源为step_clk,这样就使用了备用时钟 */
	}

	/* 1.2、设置pll1_main_clk为792MHz
	 *
	 *      因为pll1_sw_clk进ARM内核的时候会被二分频!
	 *      配置CCM_ANLOG->PLL_ARM寄存器
	 *      bit13: 1 使能时钟输出
	 *      bit[6:0]: 66, 由公式:Fout = Fin * div_select / 2.0,792=24*div_select/2.0,
	 *              		得出:div_select=    66 
	 */
	CCM_ANALOG->PLL_ARM = (1 << 13) | ((66 << 0) & 0X7F); 	/* 配置pll1_main_clk=792MHz */
	CCM->CCSR &= ~(1 << 2);									/* 将pll_sw_clk时钟重新切换回pll1_main_clk */
	CCM->CACRR = 0;											/* ARM内核时钟为pll1_sw_clk/1=792/1=792Mhz */

	/* 2、设置PLL2(SYS PLL)各个PFD */
	reg = CCM_ANALOG->PFD_528;
	reg &= ~(0X3F3F3F3F);		/* 清除原来的设置 						*/
	reg |= 32<<24;				/* PLL2_PFD3=528*18/32=297Mhz 	*/
	reg |= 24<<16;				/* PLL2_PFD2=528*18/24=396Mhz(DDR使用的时钟,最大400Mhz) */
	reg |= 16<<8;				/* PLL2_PFD1=528*18/16=594Mhz 	*/
	reg |= 27<<0;				/* PLL2_PFD0=528*18/27=352Mhz  	*/
	CCM_ANALOG->PFD_528=reg;	/* 设置PLL2_PFD0~3 		 		*/

	/* 3、设置PLL3(USB1)各个PFD */
	reg = 0;					/* 清零   */
	reg = CCM_ANALOG->PFD_480;
	reg &= ~(0X3F3F3F3F);		/* 清除原来的设置 					 */
	reg |= 19<<24;				/* PLL3_PFD3=480*18/19=454.74Mhz 	*/
	reg |= 17<<16;				/* PLL3_PFD2=480*18/17=508.24Mhz 	*/
	reg |= 16<<8;				/* PLL3_PFD1=480*18/16=540Mhz		*/
	reg |= 12<<0;				/* PLL3_PFD0=480*18/12=720Mhz	 	*/
	CCM_ANALOG->PFD_480=reg;	/* 设置PLL3_PFD0~3 					*/	

	/* 4、设置AHB时钟 最小6Mhz, 最大132Mhz (boot rom自动设置好了可以不用设置)*/
	CCM->CBCMR &= ~(3 << 18); 	/* 清除设置*/ 
	CCM->CBCMR |= (1 << 18);	/* pre_periph_clk=PLL2_PFD2=396MHz */
	CCM->CBCDR &= ~(1 << 25);	/* periph_clk=pre_periph_clk=396MHz */
	while(CCM->CDHIPR & (1 << 5));/* 等待握手完成 */
		

#if 0
	/* 要先关闭AHB_ROOT_CLK输出,否则时钟设置会出错 */
	CCM->CBCDR &= ~(7 << 10);	/* CBCDR的AHB_PODF清零 */
	CCM->CBCDR |= 2 << 10;		/* AHB_PODF 3分频,AHB_CLK_ROOT=132MHz */
	while(CCM->CDHIPR & (1 << 1));/
* 等待握手完成 */
#endif
	
	/* 5、设置IPG_CLK_ROOT最小3Mhz,最大66Mhz (boot rom自动设置好了可以不用设置)*/
	CCM->CBCDR &= ~(3 << 8);	/* CBCDR的IPG_PODF清零 */
	CCM->CBCDR |= 1 << 8;		/* IPG_PODF 2分频,IPG_CLK_ROOT=66MHz */
	
	/* 6、设置PERCLK_CLK_ROOT时钟 */
	CCM->CSCMR1 &= ~(1 << 6);	/* PERCLK_CLK_ROOT时钟源为IPG */
	CCM->CSCMR1 &= ~(7 << 0);	/* PERCLK_PODF位清零,即1分频 */
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梅尔文

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值