系统时钟来源

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

I.MX6U 的外设有很多,不同的外设时钟源不同,这些外设的时钟源分了7组,这 7 组时钟源都是从 24MHz 晶振 PLL 而来的,因此也叫做 7 组 PLL.

时钟树

由下面的时钟切换图可知,IMX6U总共有7组PLL:

PLL1(996M)、PLL2(528M)、PLL3(480M)、PLL4、PLL5、PLL6、PLL7

其中PLL2、PLL3各自有4组PFD,这些PFD以PLL为基础,经过不同的分频系数可以产生不同频率的时钟,在参考手册的相关寄存器的地方,可以看到官方有推荐的配置参数。(《IMX6UL参考手册》

①、 ARM_PLL(PLL1),此路 PLL 是供 ARM 内核使用的,ARM 内核时钟就是由此 PLL生成的,此 PLL 通过编程的方式最高可倍频到 1.3GHz。
②、528_PLL(PLL2),此路 PLL 也叫做 System_PLL,此路 PLL 是固定的 22 倍频,不可编程修改。因此,此路 PLL 时钟=24MHz * 22 = 528MHz,这也是为什么此 PLL 叫做 528_PLL 的原因。此 PLL 分出了 4 路 PFD,分别为:PLL2_PFD0PLL2_PFD3,这 4 路 PFD 和 528_PLL共同作为其它很多外设的根时钟源。通常 528_PLL 和这 4 路 PFD 是 I.MX6U 内部系统总线的时钟源,比如内处理逻辑单元、DDR 接口、NAND/NOR 接口等等。
③、USB1_PLL(PLL3),此路 PLL 主要用于 USBPHY,此 PLL 也有四路 PFD,为:PLL3_PFD0
PLL3_PFD3,USB1_PLL 是固定的 20 倍频,因此 USB1_PLL=24MHz *20=480MHz。
USB1_PLL虽然主要用于USB1PHY,但是其和四路PFD同样也可以作为其他外设的根时钟源。
④、USB2_PLL(PLL7,没有写错!就是 PLL7,虽然序号标为 4,但是实际是 PLL7),看名字就知道此路PLL是给USB2PHY 使用的。同样的,此路PLL固定为20倍频,因此也是480MHz。
⑤、ENET_PLL(PLL6),此路 PLL 固定为 20+5/6 倍频,因此 ENET_PLL=24MHz * (20+5/6) = 500MHz。此路 PLL 用于生成网络所需的时钟,可以在此 PLL 的基础上生成 25/50/100/125MHz的网络时钟。
⑥、VIDEO_PLL(PLL5),此路 PLL 用于显示相关的外设,比如 LCD,此路 PLL 的倍频可以调整,PLL 的输出范围在 650MHz1300MHz。此路 PLL 在最终输出的时候还可以进行分频,可选 1/2/4/8/16 分频。
⑦、AUDIO_PLL(PLL4),此路 PLL 用于音频相关的外设,此路 PLL 的倍频可以调整,PLL的输出范围同样也是 650MHz
1300MHz,此路 PLL 在最终输出的时候也可以进行分频,可选1/2/4 分频。

PLL:接收锁相环输出时钟和锁相环旁路时钟。 简单来说,锁相环就是用来将外部晶振进行倍频,以实现稳定且高频的时钟信号。

内核时钟设置(PLL1)

①、pll1_sw_clk 也就是 PLL1 的最终输出频率。
②、此处是一个选择器,选择 pll1_sw_clk 的时钟源,由寄存器 CCM_CCSR 的PLL1_SW_CLK_SEL 位决定 pll1_sw_clk 是选择 pll1_main_clk 还是 step_clk。正常情况下应该选择 pll1_main_clk,但是如果要对 pll1_main_clk(PLL1)的频率进行调整的话,比如我们要设置PLL1=1056MHz,此时就要先将 pll1_sw_clk 切换到 step_clk 上。等 pll1_main_clk 调整完成以后再切换回来。
③、此处也是一个选择器,选择 step_clk 的时钟源,由寄存器 CCM_CCSR 的 STEP_SEL 位来决定 step_clk 是选择 osc_clk 还是 secondary_clk。一般选择 osc_clk,也就是 24MHz 的晶振。

首先要使用CCM_CCSR寄存器来决定使用时钟源来进行配置。

①、 设置寄存器 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。

PFD 时钟设置

设置好主频以后我们还需要设置好其他的 PLL 和 PFD 时钟,PLL1 上一小节已经设置了,PLL2、PLL3 和 PLL7 固定为 528MHz、480MHz 和 480MHz,PLL4~PLL6 都是针对特殊外设的,用到的时候再设置。因此,接下来重点就是设置 PLL2 和 PLL3 的各自 4 路 PFD,这 8 路 PFD 频率如表 所示:

设置PFD0~3需要按照寄存器的所在位进行置数。以下是寄存器CCM_ANALOG_PFD_528n的结构。

频率计算公式

PLL2_PFDX=528*18/PFDX_FRAC(X=1~3)。

*PLL3_PFDX=48018/PFDX_FRAC(X=0~3)**。

例如设置PFD3的的频率为297MHz需要进行以下设置。

1
2
3
4
5
unsigned int reg = 0;
int x;
reg &= ~(0X3F3F3F3F); //清楚以前设置
x = 32; //528*18/297
reg |= (x<<24); //24是寄存器的位置

AHB、IPG 和 PERCLK 根时钟设置

7 路 PLL 和 8 路 PFD 设置完成以后最后还需要设置 AHB_CLK_ROOT 和 IPG_CLK_ROOT的时钟,I.MX6U 外设根时钟可设置范围如图:

AHB_CLK_ROOT 最高可以设置 132MHz,IPG_CLK_ROOT 和PERCLK_CLK_ROOT 最高可以设置66MHz。

①、此选择器用来选择 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。
②、此选择器用来选择 periph_clk 的时钟源,由寄存器 CCM_CBCDR 的 PERIPH_CLK_SEL位与 PLL_bypass_en2 组成的或来选择。当 CCM_CBCDR 的 PERIPH_CLK_SEL 位为 0 的时候periph_clk=pr_periph_clk=396MHz。
③、通过 CBCDR 的 AHB_PODF 位来设置 AHB_CLK_ROOT 的分频值,可以设置 18 分频,如果想要 AHB_CLK_ROOT=132MHz 的话就应该设置为 3 分频:396/3=132MHz。图 中虽然写的是默认 4 分频,但是 I.MX6U 的内部 boot rom 将其改为了 3 分频!
④、通过 CBCDR 的 IPG_PODF 位来设置 IPG_CLK_ROOT 的分频值,可以设置 1
4 分频,IPG_CLK_ROOT 时钟源是 AHB_CLK_ROOT,要想 IPG_CLK_ROOT=66MHz 的话就应该设置2 分频:132/2=66MHz。

设置 PERCLK_CLK_ROOT 时钟频率

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 分频。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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官方手册推荐的值.
*/
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*/
{
CCM->CCSR &= ~(1 << 8); /* 配置step_clk时钟源为24MH OSC */
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));/* 等待握手完成 */
/* 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分频 */
}