目录
系统时钟来源
从图
中
可以看出I.MX6U的系统时钟来源于两部分
:
32.768KHz和24MHz的晶振
,
其中 32.768KHz晶振是I.MX6的RTC时钟源
,
24MHz晶振是I.MX6内核和其它外设的时钟源
。
7路PPL时钟源
I.
MX6U的外设有很多,不同的外设时钟源不同, NXP将这些外设的时钟源进行了分组,一共有7组,这7组时钟源都是从24MHz晶振PLL而来的
,
因此也叫做7组PLL
,
这7组PLL结构如图
:
1.
ARM_PLL (PLL1),此路PLL是供ARM内核使用的, ARM内核时钟就是由此PLL生成的,此PLL通过编程的方式最高可倍频到1.3GHz
。
2.
528_PLL(PLL2),此路PLL也叫做System_PLL,此路PLL是固定的22倍频,不可编程修改。
因此,此路PLL时钟=24MHz * 22=528MHz,这也是为什么此PLL叫做528-PLL的原因。此 PLL分出了4路PFD,分别为: PLL2_PFDO-PLL2_PFD3,这4路PFD和528_PLL共同作为其它很多外设的根时钟源。通常528_PLL和这4路PFD是1.MX6U内部系统总线的时钟源,比如内处理逻辑单元、DDR接口、NAND/NOR接口等等。
3.
USB1_PLL(PLL3),此路PLL主要用于USBPHY,此PLL也有四路PFD,为:PLL3_PFDO~PLL3_PFD3
,
USB1_PLL是固定的20倍频
,
因此USB1_PLL=24MHz*20=480MHz。USB1_PLL 虽然主要用于USB1PHY
,
但是其和四路PFD同样也可以作为其他外设的根时钟源。
4.
USB2_PLL(PLL7,没有写错!就是PLL7,虽然序号标为4,但是实际是PLL7),看名字就知道此路PLL是给USB2PHY使用的。同样的
,
此路PLL固定为20倍频
,
因此也是480MHz
。
5.
ENET_PLL(PLL6),此路PLL固定为20+5/6 倍频,因此 ENET_PLL=24MHz * (20+5/6),= 500MHz。此路PLL用于生成网络所需的时钟,可以在此PLL的基础上生成25/50/100/125MHz的网络时钟。
6.
VIDEO_PLL(PLL5),此路PLL用于显示相关的外设,比如LCD,此路PLL的倍频可以调整, PLL的输出范围在650MHz-1300MHz。此路PLL在最终输出的时候还可以进行分频,可选 1/2/4/8/16分频。
7.
AUDIO-PLL(PLL4),此路PLL用于音频相关的外设,此路PLL的倍频可以调整, PLL的输出范围同样也是650MHz-1300MHz,此路PLL在最终输出的时候也可以进行分频,可选1/2/4分频。
时钟树简介
上面讲了PLL时钟,其实整个系统时钟逻辑是从24M的外部时钟源输入然后生成了7路的PLL时钟,PLL时钟又生成了PFD时钟源,PFD时钟源下面就是一些外设时钟源(PLL也可以作为外设时钟源)。
在图中一共有三部分: CLOCK_SWITCHER.CLOCK ROOT GENERATOR和SYSTEM CLOCKS。其中左边的CLOCK_SWITCHER就是那7路PLL和8路PFD,右边的SYSTEM CLOCKS就是芯片外设,中间的CLOCK ROOT GENERATOR是最”复杂的!这一部分就像
给农田浇灌一样,把PLL7路时钟源比喻成7个不同的水源
,给左边的CLOCK_SWITCHER和右边的SYSTEMCLOCKS进行
浇灌不同的水
。外设时钟源是有多路可以选择的
,
CLOCK ROOT GENERATOR就负责从7路PLL和8路PFD中选择合适的时钟源给外设使用。
系统主频配置
1.
内核时钟源来自于PLL1,假如此时PLL为996MHz。
2.
通过寄存器CCM_CACRR的ARM_PODF位对PLL1进行分频
,
可选择1/2/4/8分频
,
假如我们选择2分频,那么经过分频以后的时钟频率是996/2=498MHz。
3.
大家不要被此处的2分频给骗了,此处没有进行2分频(我就被这个2分频骗了好久,主频一直配置不正确!)。
4.
经过第
2
步2分频以后的498MHz就是ARM的内核时钟
,
也就是I.MX6U的主频。经过上面几步的分析可知,假如我们要设置内核主频为528MHz,那么PLL1可以设置为1056MHz,寄存器CCM-CACRR的ARM-PODF位设置为2分频即可。同理,如果要将主频设置为696MHz,那么PLL1就可以设置为696MHz, CCM_CACRR的ARM_PODF设置为1分频即可。现在问题很清晰了,寄存器CCM_CACRR的ARM_PODF位很好设置, PLL1的频率可以通过寄存器CCM_ANALOG_PLL_ARMn来设置。接下来详细的看一下CCM_CACRR和CCM_ANALOG_PLL_ARMn 这两个寄存器
CCM_CACRR寄存器结构如图
:
寄存器CCM_CACRR只有ARM_PODF位,可以设置为0-7,分别对应1~8分频。如果要设置为2分频的话CCM_CACRR就要设置为1。
再来看一下寄存器CCM_ANALOG_PLL_ARMn,此寄存器结构如图示
:
在寄存器CCM_ANALOG_PLL_ARMn中重要的位如下
:
输出。
ENABLE:时钟输出使能位,此位设置为1使能PLL1输出,如果设置为0的话就关闭PLL1DIV_SELECT:此位设置PLL1的输出频率,可设置范围为: 54~108, PLLI CLK= Fin *div_seclec/2.0, Fin=24MHz。如果PLL1要输出1056MHz的话, div_select就要设置为88。在修改PLL1时钟频率的时候我们需要先将内核时钟源改为其他的时钟源
,
PLL1可选择的时钟源如图示
:
1.
pll
1_
sw
_
clk也就是PLL1的最终输出频率
。
2.
此处是一个选择器,选择pll1_sw_clk的时钟源,由寄存器CCM_CCSR的PLL1_SW_CLK_SEL位决定plll_sw_clk是选择pll1_main_clk还是step_clk。
正常情况下应该选择pll1_main_clk,但是如果要对pll1_main_clk(PLL1)的频率进行调整的话,比如我们要设置PLL1=1056MHz,此时就要先将plll_sw_clk切换到step_clk上。等 plll_main_clk调整完成以后再切换回来。
3.
此处也是一个选择器
,
选择step_clk的时钟源
,
由寄存器CCM_CCSR的STEP_SEL位来决定step_clk是选择osc_clk还是secondary_clk。
一般选择osc_clk,也就是24MHz的晶振。
寄存器CCM_CCSR,此寄存器结构如图示:
寄存器CCM_CCSR我们只用到了STEP_SEL、PLLI_SW_CLK_SEL这两个位,一个是用,来选择step
_
clk时钟源的,一个是用来选择pll1
_
sw
_
clk时钟源的。
到这里
,
修改I.MX6U主频的步骤就很清晰了
,
修改步骤如下
:
1.
设置寄存器CCSR的STEP_SEL位
,
设置step_clk的时钟源为24M的晶振。
2.
设置寄存器CCSR的PLL1_SW_CLK_SEL位,设置plll_sw_clk的时钟源为step_clk=24MHz,通过这一步我们就将I.MX6U的主频先设置为24MHz
,
直接来自于外部的24M晶振。
3.
设置寄存器CCM_ANALOG_PLL_ARMn,将pll1_main_clk(PLL1)设置为1056MHz。
4.
设置寄存器CCSR的PLLI SW CLK SEL位,重新将pll1 sw clk的时钟源切换回pll1
_
main
_
clk,切换回来以后的pll
1_
sw_clk就等于1056MHz。
5.
最后设置寄存器CCM_CACRR的ARM_PODF为2分频, I.MX6U的内核主频就为1056/2=528MHz。
PFD时钟设置
设置好主频以后还需要设置好其他的P
LL
和P
F
D时
钟,
PLL2、 PLL3和PLL7固定为528MHz、 480MHz和480MHz, PLL4-PLL6都是针对特殊外设的,用到的时候再设置。因此,接下来重点就是设置PLL2和PLL3的各自4路PFD, NXP推荐的这8路P
F
D频率
先设置PLL2的4路PFD频率,用到寄存器是CCM_ANALOG_PFD_528n,寄存器结构如图示:
从图可以看出,寄存器CCM
_
ANALOG
_
PFD
_
528n其实分为四组,分别对应PFD
0
-PFD3
,
每组8个bit,我们就以PFD
0
为例
,
看一下如何设置PLL2_PFD
0
的频率。PFD
0
对应的寄存器位如下
:
PFDO_FRAC: PLL2_PFD
0
的分频数, PLL2_PFD
0
的计算公式为528*18/PFDO_FRAC
,
此为可设置的范围为12-35。
如果PLL2_PFD
0
的频率要设置为352MHz的话PFD
0_
FRAC
=
528*18/352
=
27
PFDO_STABLE
:
此位为只读位
,
可以通过读取此位判断PLL2_PFD
0
是否稳定。
PFDO_CLKGATE
:
PLL2_PFD
0
输出使能位
,
为1的时候关闭PLL2_PFD
0
的输出
,
为0 的时候使能输出。
如果我们要设置PLL2_PFD
0
的频率为352MHz的话就需要设置PFD
0
_FRAC为27
,
PFDO_CLKGATE为0。
PLL2_PFD1-PLL2_PFD3设置类似
,
频率计算公式都是528*18/PFDX FRAC(X=1~3)
,
因此PLL2_PFD1=594MHz的话
,
PFD1_FRAC=16;
PLL2_PFD2=400MHz的话 PFD2_FRAC不能整除
,
因此取最近的整数值
,
即PFD2_FRAC=24
,
这样PLL2
_
PFD2实际为396MHz: PLL2
_
PFD3
=
297MH的话, PFD3
_
FRAC
=
32
。
接下来设置PLL3_PFD
0~
PLL3_PFD3这4路PFD的频率
,
使用到的寄存器是CCM
_
ANALOG
_
PFD_480n,此寄存器结构如图示:
从图可以看出,寄存器CCM_ANALOG_PFD_480n和CCM_ANALOG_PFD_528n的结构是一模一样的,只是一个是PLL2的,一个是PLL3的。寄存器位的含义也是一样的,只是频率计算公式不同
,
比如PLL3_PFDX=480*18/PFDX_FRAC(X=0-3)。如果PLL3_PFDO=720MHz 的话
,
PFDO_FRAC=12
;
如果PLL3_PFD1=540MHz的话
,
PFD1_FRAC=16;如果 PLL3_PFD2=508.2MHz的话
,
PFD2_FRAC=17
;
如果PLL3_PFD3=454.7MHz的话
,
PFD3_FRAC=19
。
AHB、IPG和PERCLK根时钟设置
7路PLL和8路PFD设置完成以后最后还需要设置AHB_CLK_ROOT和IPG_CLK_ROOT的时钟
,
I
.
MX
6U
外设根时钟可设置范围如示
:
图给出了大多数外设的根时钟设置范围, AHB_CLK_ROOT最高可以设置132MHz,IPG_CLK_ROOT和PERCLK_CLK_ROOT最高可以设置66MHz。那我们就将AHB_CLK_ROOTIPG_CLK_ROOT 和 PERCLK_CLK_ROOT分别设置为132MHz、 66MHz、 66MHz。AHB CLK ROOT 和IPG CLK ROOT 的设计如图所示:
1.
此选择器用来选择pre_periph_clk的时钟源,可以选择PLL2、PLL2_PFD2、PLL2_PFD
0
和PLL2_PFD2/2。寄存器CCM_CBCMR的PRE_PERIPH_CLK_SEL位决定选择哪一个,默认选择PLL2
_
PFD2,因此pre
_
periph
_
clk
=
PLL2
_
PFD2
=
396MHz
。
2.
此选择器用来选择 periph_clk的时钟源
,
由寄存器CCM_CBCDR的PERIPH_CLK_SEL位与 PLL_bypass_en2组成的或来选择。
当CCM_CBCDR的PERIPH_CLK_SEL位为0的时候periph clk
=
pr
_
periph
_
clk=396MHz。
3.
通过CBCDR的AHB-PODF位来设置”AHB_CLK_ROOT的分频值,可以设置1-8分频
,
如果想要AHB_CLK_ROOT=132MHz的话就应该设置为3分频: 396/3=132MHz。图中虽然写的是默认4分频
,
但是I.MX6U的内部boot rom 将其改为了3分频
!
4.
通过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分频。
寄存器CCM_CBCDR各个位的含义如下
:
PERIPH_CLK2_PODF
:
periph2 时钟分频
,
可设置0-7
,
分别对应1~8分频。
PERIPH2_CLK_SEL:选择peripheral2的主时钟,如果为0的话选择PLL2,如果为1的话选择 periph2_clk2_clk。修改此位会引起一次与MMDC的握手
,
所以修改完成以后要等待握手完成,握手完成信号由寄存器CCM CDHIPR中指定位表示。
PERIPH_CLK_SEL: peripheral主时钟选择,如果为0的话选择PLL2,如果为1的话选择 periph_clk2_clock。
修改此位会引起一次与 MMDC 的握手,所以修改完成以后要等待握手完成,握手完成信号由寄存器CCM_CDHIPR中指定位表示。
AXI-PODF: axi时钟分频,可设置0-7,分别对应1-8分频。
AHB_PODF
:
ahb时钟分频
,
可设置0~7
,
分别对应1~8分频。修改此位会引起一次与MMDC的握手
,
所以修改完成以后要等待握手完成,握手完成信号由寄存器CCM_CDHIPR中指定位表示。
PERIPH_CLK_SEL: peripheral主时钟选择,如果为0的话选择PLL2,如果为1的话选择 periph_clk2_clock。
修改此位会引起一次与MMDC的握手
,
所以修改完成以后要等待握手完成,握手完成信号由寄存器CCM_CDHIPR中指定位表示。
AXI-PODF: axi时钟分频,可设置0-7,分别对应1-8分频。
AHB_PODF
:
ahb时钟分频
,
可设置0~7
,
分别对应1~8 分频。修改此位会引起一次与MMDC的握手
,
所以修改完成以后要等待握手完成,握手完成信号由寄存器CCM_CDHIPR中指定位表示。
IPG_PODF: ipg时钟分频,可设置0-3,分别对应1-4分频。
AXI_ALT_CLK_SEL
:
axi_alt时钟选择
,
为0的话选择PLL2_PFD2
,
如果为1的话选择PLL3
_
PFD1。
AXI_CLK_SEL: axi时钟源选择,为0的话选择|periph_clk,为1的话选择axi_alt时钟。
FABRIC_MMDC_PODF: fabric/mmdc时钟分频设置,可设置0-7,分别对应1~8分频。
PERIPH2_CLK2_PODF: periph2_clk2 的时钟分频,可设置 0~7,分别对应 1~8 分频。
寄存器CCM_CBCMR寄存器结构如图所示
:
寄存器CCM_CBCMR各个位的含义如下
:
LCDIF1_PODF
:
ledifl的时钟分频
,
可设置0~7
,
分别对应1~8分频。
PRE_PERIPH2_CLK_SEL:pre_periph2时钟源选择
,
00选择PLL2,01选择PLL2_PFD2,10选择PLL2_PFD
0
,11选择PLL4。
PERIPH2_CLK2_SEL
:
periph2_clk2时钟源选择为0的时候选择pll3_sw_clk
,
为1的时候选择 OSC。
PRE-PERIPH_CLK_SEL: pre-periph时钟源选择, 00选择PLL2, 01选择PLL2-PFD2, 10选择PLL2_PFD
0
,11选择PLL2_PFD2/2。
PERIPH_CLK2_SEL
:
peripheral_clk2时钟源选择
,
00选择pll3_sw_clk
,
01选择osc_clk
,
10选择 pll2_bypass_clk。
寄存器CCM_CSCMR1
,
寄存器结构如图所示
:
此寄存器主要用于外设时钟源的选择,比如QSPI1、ACLK、GPMI、BCH等外设,我们重点看一下下面两个位:
PERCLK_CK_SEL
:
perclk时钟源选择
,
为0的话选择ipg
_
clk
,
为1的话选择osc
clk。
PERCLK_PODF
:
perclk的时钟分频
,
可设置
0
~7
,
分别对应1~8分频。
实验源码
#include "bsp_clk.h"
/*
* @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 : 初始化时钟
* @param : 无
* @return : 无
*/
void imx6u_clkinit(void)
{
uint16_t reg = 0;
/*初始化6u的主频为528MHz*/
if (((CCM->CCSR >>2) & 0x01) == 0)/*当前时钟使用pll1_main_clk也就是PLL1*/
{
/*系统时钟切换*/
CCM->CCSR &= ~(1 << 8); /*设置step_clk = osc_clk = 24M*/
CCM->CCSR |= (1 << 2); /*pll1_main_clk = step_clk = 24M*/
}
/*设置PLL1 = 1056MHZ*/
CCM_ANALOG->PLL_ARM = (88 & 0x7f); /*24M*88=1056M*/
CCM_ANALOG->PLL_ARM |= (1<<13); /*时能时钟*/
CCM->CACRR = 1; /*1056/2=528Mhz*/
/*切换为528MHz*/
CCM->CCSR &= ~(1 << 2);/*将pll_sw_clk时钟切换回pll1_main_clkk = 1056/2=528Mhz*/
/*设置PLL2的4路PFD*/
reg = CCM_ANALOG->PFD_528;
reg &= ~(0x3F3F3F3F);
reg |= (32 << 24); /* PLL2_PFD3=297MHZ*/
reg |= (24 << 16); /* PLL2_PFD2=396MHZ*/
reg |= (16 << 8); /* PLL2_PFD1=594MHZ*/
reg |= (27 << 0); /* PLL2_PFD0=352MHZ*/
CCM_ANALOG->PFD_528 = reg;
/*设置PLL3的4路PFD*/
reg = 0;
reg = CCM_ANALOG->PFD_480;
reg &= ~(0x3F3F3F3F);
reg |= (19 << 24); /* PLL3_PFD3=454.7MHZ*/
reg |= (17 << 16); /* PLL3_PFD2=508.2MHZ*/
reg |= (16 << 8); /* PLL3_PFD1=5540MHZ*/
reg |= (12 << 0); /* PLL3_PFD0=720MHZ*/
CCM_ANALOG->PFD_480 = reg;
/*设置AHB_ROOT=132MHz*/
CCM->CBCMR &= ~(3 << 18);
CCM->CBCMR |= 1 << 18; /*pre_periph clock=PLL2_PFD2=396MHz*/
CCM->CBCDR &= ~(1 << 25 );/*选择396MHz这条通道*/
while(CCM->CDHIPR & (1 << 5));/*等待握手信号完成*/
/*内部boot 已经设置好*/
// CCM->CBCDR &= ~(7 <<10 );
// CCM->CBCDR |= (2 <<10 ); /*396MHz/3=132M*/
// while(CCM->CDHIPR & (1 << 1));/*等待握手信号完成*/
/*设置IPG_CLK_ROOT最小3Mhz最大66Mhz*/
CCM->CBCDR &= ~(3 << 8 );
CCM->CBCDR |= (1 << 8 ); /*IPG_CLK_ROOT=AHB_CLK_ROOT/2=132/2=66MHz*/
/*设置PERCLK_CLK_ROOT时钟66Mhz*/
CCM->CSCMR1 &= ~(1 << 6); /* PERCLK_CLK_ROOT时钟源为IPG */
CCM->CSCMR1 &= ~(0x3F << 0); /*1分频 */
}