阿尔法系统时钟和外设时钟

一、系统时钟

1.时钟树

I.MX6U 的系统主频为 528MHz,但是默认情况下内部 boot rom 会将 I.MX6U 的主频设置为396MHz

阿尔法系统时钟和外设时钟_第1张图片

I.MX6U-ALPHA 开发板的系统时钟来源于两部分: 32.768KHz 和24MHz 的晶振,其中 32.768KHz 晶振是 I.MX6U 的 RTC 时钟源24MHz 晶振是 I.MX6U 内核和其它外设的时钟源
 

时钟树:

阿尔法系统时钟和外设时钟_第2张图片

 时钟树一共有三部分: CLOCK_SWITCHER、 CLOCK ROOT GENERATOR 和SYSTEM CLOCKS。CLOCK_SWITCHER 是 7 路 PLL 和8 路 PFD,SYSTEM CLOCKS 是芯片外设时钟,CLOCK ROOT GENERATOR 从 7 路PLL 和 8 路 PFD 中选择合适的时钟源经过分频给外设使用。

注:中间的每一条不同颜色的线表示来自不同的时钟源,结合左边图标注释分析,比如,黄色的标志表示选择时钟源寄存器的位设置,红色的标志表示时钟源分频系数寄存器位的设置。  
 

为了方便生成时钟,阿尔法从24MHz晶振生出来7路PLL。PLL2和PLL3又生成4路的PDF0-3。

ARM_PLL(PLL1):供 ARM 内核使用的, ARM 内核时钟就是由此 PLL生成的,此 PLL 通过编程的方式最高可倍频到 1.3GHz

528_PLL(PLL2):也叫做 System_PLL,PLL2 是固定的 22 倍频,PLL2 =24MHz * 22 = 528MHz,PLL2又分为4路PFD: PLL2_PFD0~PLL2_PFD3。这 4 路 PFD 和 528_PLL
共同作为内部系统总线的时钟源其它很多外设的根时钟源

USB1_PLL(PLL3):主要用于 USBPHY,PLL3是固定的20倍分频,USB1_PLL=24MHz *20=480MHz
 

AUDIO_PLL(PLL4):用于音频相关的外设
 

VIDEO_PLL(PLL5):用于显示相关的外设
 

ENET_PLL(PLL6):用于生成网络所需的时钟,PLL6固定为20+5/6倍频, ENET_PLL=24MHz * (20+5/6)= 500MHz
 

USB2_PLL(PLL7):用于USB2PHY,PLL7固定为20倍频,因此PLL7=480MHz

时钟树配置外设时钟:(ESAI外设为例)

 (1)这是时钟源选择器,有4个可选的时钟源: PLL4、 PLL5、 PLL3_PFD2 和 pll3_sw_clk 。 具 体 选 择 哪 一 路 作 为 ESAI 的 时 钟 源 是 由 寄 存 器 CCM->CSCMR2 的ESAI_CLK_SEL 位来决定的(正上方黄色标志部分)

阿尔法系统时钟和外设时钟_第3张图片

(2)时钟分频器,分频值由寄存器 CCM_CS1CDR 的 ESAI_CLK_PRED来确定的,可设置 1~8 分频(浅蓝色是3bit分配器, 图标)

(3)和2一样的作用,经过两次分频得到ESAI_CLK_ROOT外设时钟源

2.内核时钟

目的:将ARM 内核时钟设置为528Mhz

(1)内核时钟源来自于 PLL1,然后经过一次2分频得到系统时钟(后面的灰色的分频器是static divider,不进行分频),所以要设置系统时钟为528MHz, 需要修改PLL1的值为528 * 2 = 1056MHz < 1.3GHz,PLL1 的频率通过寄存器 CCM_ANALOG_PLL_ARMn 来设置
 

CCM_CACRR 寄存器(分频)

阿尔法系统时钟和外设时钟_第4张图片
ARM_PODF只用了第3位,设置为2分频,寄存器写入1即可

CCM_ANALOG_PLL_ARMn(频率)

阿尔法系统时钟和外设时钟_第5张图片
13位:时钟输出使能,1:使能PLL1输出;0:关闭PLL1输出
 DIV_SELECT:  设置 PLL1 的输出频率PLL1 CLK = Fin *div_seclec/2.0, Fin=24MHz
可写入范围:54~108,所以PLL1 要输出 1056MHz , div_select 要设置为 88
 

 (2)但是在修改 PLL1 时钟频率的时候需要先将内核时钟源改为其他的时钟源,因为一般默认选择PLL1提供时基并且修改时必须要有时钟给系统提供时基。

阿尔法系统时钟和外设时钟_第6张图片

pll1_sw_clk 是 PLL1的输出频率时钟来源:pll1_main_clk 和 step_clk,由寄存器 CCM_CCSR 的PLL1_SW_CLK_SEL 位决定

 而step_clk 的时钟源可以来自osc_clk和secondary_clk,一般选择 osc_clk(24MHz 的晶振),由寄存器 CCM_CCSR的STEP_SEL位决定阿尔法系统时钟和外设时钟_第7张图片

/* 1.1、判断当前ARM内核是使用的那个时钟源启动的,正常情况下ARM内核是由pll1_sw_clk驱动的,而
	 *      pll1_sw_clk有两个来源:pll1_main_clk和tep_clk。
	 *      如果我们要让ARM内核跑到528M的话那必须选择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*/
	{	
		CCM->CCSR &= ~(1 << 8);				/* 配置step_clk时钟源为24MH OSC */	
		CCM->CCSR |= (1 << 2);				/* 配置pll1_sw_clk时钟源为step_clk */
	}

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

首先判断ARM内核使用的时钟源,然后设置寄存器 CCSR 的 STEP_SEL 位,设置 step_clk 的时钟源为 24M 的晶振,再设置寄存器 CCSR 的 PLL1_SW_CLK_SEL 位,设置 pll1_sw_clk 的时钟源为step_clk=24MHz,将 I.MX6U 的主频先设置为 24MHz,直接来自于外部的24M 晶振
 

 时钟源修改完成后,可以修改PLL1的频率为1056MHz,CCM_ANALOG_PLL_ARMn低6位写入88并使能13位即可。设置寄存器 CCSR 的 PLL1_SW_CLK_SEL 位,重新将 pll1_sw_clk 的时钟源切换回pll1_main_clk,切换回来以后的 pll1_sw_clk 就等于 1056MHz。最后设置寄存器CCM_CACRR 的 ARM_PODF 为 2 分频, MX6U 的内核主频就为1056/2=528MHz
 

3.FPD时钟

设置系统时钟后还要设置其他的PLL和PFD时钟,主要设置 PLL2 和 PLL3 的4 路 PFD

8路FPD频率表:

阿尔法系统时钟和外设时钟_第8张图片

 设置 PLL2 的 4路 PFD 频率,使用到的寄存器是 CCM_ANALOG_PFD_528n

阿尔法系统时钟和外设时钟_第9张图片

刚好分为四组,每组 8 个 bit,分别对应PFD0~PFD3,PFDx_FRAC: PLL2_PFDx 的分频数
频率计算公式:PLL2_PFDx = 528*18/PFDx_FRAC

若要设置 PLL2_PFD0 的频率为 352MHz,则PFD0_FRAC=528*18/352=27,其他的同理。

设 置 PLL3的4 路 PFD 的 频 率 , 使 用 到 的 寄 存 器 是CCM_ANALOG_PFD_480n
频率计算公式:PLL3_PFDX=480*18/PFDX_FRAC(X=0~3),除了公式不一样其他都是一样的。

uint8_t reg = 0;

/* 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 					*/	

技巧:将8-13设置为16,其余位不受影响

GPIO->DR  &=  ~(0X00003F00)    /* 将相应的位清0*/

GPIO->DR  |=  16 << 8  /*从第8位写入16*/

4.AHB, IPG, PERCLK时钟

7 路 PLL 和 8 路 PFD 设置完成以后还需要设置 AHB_CLK_ROOT 和 IPG_CLK_ROOT的时钟
外设根时钟频率表:

 将AHB_CLK_ROOT、IPG_CLK_ROOT 和 PERCLK_CLK_ROOT 分 别 设 置 为 132MHz 、 66MHz 、 66MHz

阿尔法系统时钟和外设时钟_第10张图片

(1)先选择 pre_periph_clk 的时钟源,可以选择 PLL2、 PLL2_PFD2、 PLL2_PFD0和 PLL2_PFD2/2。寄存器 CCM_CBCMR 的 PRE_PERIPH_CLK_SEL 位决定选择哪一个,默认选择 PLL2_PFD2,pre_periph_clk=PLL2_PFD2=396MHz

阿尔法系统时钟和外设时钟_第11张图片

(2)选择 periph_clk 的时钟源,由寄存器 CCM_CBCDR 的 PERIPH_CLK_SEL位与 PLL_bypass_en2 组成的或来选择。当 CCM_CBCDR 的 PERIPH_CLK_SEL 位为 0 的时候periph_clk=pr_periph_clk=396MHz

 阿尔法系统时钟和外设时钟_第12张图片

 

(3)设置 AHB_CLK_ROOT 的分频值,由CBCDR 的 AHB_PODF 位决定,可以设置 1~8 分频,如果想要 AHB_CLK_ROOT=132MHz 的话就应该设置为 3 分频: 396/3=132MHz。虽然默认 4 分频,但是MX6U 的内部 boot rom 将其改为了 3 分频

(4)设置 IPG_CLK_ROOT 的分频值,IPG_CLK_ROOT 时钟源是 AHB_CLK_ROOT,要想IPG_CLK_ROOT= 66MHz 的话就应该设置2 分频: 132/2=66MHz

(5)设置 PERCLK_CLK_ROOT 时钟频率

阿尔法系统时钟和外设时钟_第13张图片

PERCLK_CLK_ROOT 来 源 有 两 种 : OSC(24MHz) 和IPG_CLK_ROOT,由寄存器 CCM_CSCMR1 的 PERCLK_CLK_SEL 位来决定。如果为 0 选择PERCLK_CLK_ROOT 的 时 钟 源 是 IPG_CLK_ROOT=66MHz 。 可 以 通 过 寄 存 器CCM_CSCMR1 的 PERCLK_PODF 位来设置分频,如果要设置 PERCLK_CLK_ROOT 为 66MHz的话就要设置为 1 分频
 

注:在修改如下时钟选择器或者分频器的时候会引起与 MMDC 的握手发生

mmdc_podf
periph_clk_sel
periph2_clk_sel
arm_podf
ahb_podf

发生握手信号以后需要等待握手完成,寄存器 CCM_CDHIPR 中保存着握手信号是否完成,如果相应的位为 1 的话就表示握手没有完成,如果为 0 的话就表示握手完成。
 

	/* 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));/* 等待握手完成 */
		
	/* 修改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
	
	/* 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	: 初始化系统时钟,设置系统时钟为792Mhz,并且设置PLL2和PLL3各个
 				  PFD时钟,所有的时钟频率均按照I.MX6U官方手册推荐的值.
 * @param 		: 无
 * @return 		: 无
 */
void imx6u_clkinit(void)
{
	unsigned int reg = 0;
	/* 1.1、判断当前ARM内核是使用的那个时钟源启动的,正常情况下ARM内核是由pll1_sw_clk驱动的,而
	 *      pll1_sw_clk有两个来源:pll1_main_clk和tep_clk。
	 *      如果我们要让ARM内核跑到528M的话那必须选择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*/
	{	
		CCM->CCSR &= ~(1 << 8);				/* 配置step_clk时钟源为24MH OSC */	
		CCM->CCSR |= (1 << 2);				/* 配置pll1_sw_clk时钟源为step_clk */
	}

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

	/* 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));/* 等待握手完成 */
		
	/* 修改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
	
	/* 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)读取寄存器的值

/*读取GPIO的DR寄存器的第pin为的值 */
(GPIO->DR) >> pin) & 0x1      /*先将该为右移pin位,然后&上0x1得到该位的值*/

(2)写入寄存器的值:&= (清0)  , |= (置1) 

/*分别将DR寄存器的pin位清0和置1*/
GPIO->DR &= ~(1 << pin);   /*清0*/
	
GPIO->DR |= (1 << pin); /*置1 */

/*将8-13设置为16,其余位不受影响*/

GPIO->DR  &=  ~(0X00003F00)    /* 将8-13相应的位清0*/

GPIO->DR  |=  16 << 8  /*从第8位写入16*/

你可能感兴趣的:(#,MX6U,ALPHA,单片机,fpga开发,stm32,linux)