09. 主频和时钟配置
- 硬件原理图分析
- 7路PLL时钟源
- 时钟树简介
- 内核时钟系统主频设置
- CCM_CACRR
- CCSR
- CCM_ANALOG_PLL_ARMn
- 代码实例
- PFD时钟设置
- PLL2的4路设置
- PLL3的4路设置
- 代码实例
- AHB、IPG和PERCLK根时钟设置
- AHB_CLK_ROOT 和 IPG_CLK_ROOT
- PERCLK_CLK_ROOT
- CCM_CBCDR
- CCM_CBCMR
- CCM_CSCMR1
- 代码实例
硬件原理图分析
- 32.768kHz的晶振,供给RTC实时时钟工作。该晶振不会参与到系统时钟去
- 在6U的T16和T17这两个IO上,接一个24MHz的晶振,提供整个系统的时钟,是内核和其他外设的时钟源
7路PLL时钟源
I.MX6U 的外设很多,不同外设的时钟源不同,NXP将这些外设的是中原进行分组,一共7组
- ARM_PLL(PLL1),是供ARM内核使用的,可以通过编程的方式最高可倍频到1.3GHz
- 528_PLL(PLL2),也叫做System_PLL,是固定的22倍频,不可编程修改。此路PLL时钟=24MHz*22=528MHz。此路分出4路,PLL2_PFD0~PLL2_PFD3,这四路和528PLL共同作为其他很多外设的根时钟源,通常也是I.MX6U内部系统总线的时钟源
- USB1_PLL(PLL3),主要用于USBPHY,也有四路,PLL3_PFD0~PLL_PFD3。是固定的20倍频,也就是480MHz。虽然主要用于USB1PHY,但是和其他四路也可以作为其他外设的根时钟源
- USB2_PLL(PLL7),给USB2PHY使用,也是固定的22倍频
- ENET_PLL(PLL6),固定为20+5/6倍频,也就是500MHz。用于生成网络所需的时钟,可以在此基础上生成25/50/100/125MHz
- VIDEO_PLL(PLL5),用于显示相关的外设,比如LCD,倍频可以调整,范围是650MHz~1300MHz,最终输出的时候还可以进行分频,可选1/2/4/8/16
- AUDIO_PLL(PLL4),用于音频相关的外设,倍频可以调整,范围是650MHz~1300MHz,最终输出的时候还可以进行分频,可选1/2/4
时钟树简介
左边的 CLOCK_SWITCHER 就是我们上一小节讲解的那 7 路 PLL 和8 路 PFD,右边的 SYSTEM CLOCKS 就是芯片外设,中间的 CLOCK ROOT GENERATOR 给左边和右边进行牵线搭桥。就负责从7路PLL和8路PFD中选择合适的时钟源给外设用,具体操作就是配置相关寄存器
以ESAI为例,ESAI的时钟图
- 第一部分是时钟源选择器,ESAI有4个可选时钟源:PLL4、PLL5、PLL_PFD2和PLL3_SW_CLK。具体哪一路是由寄存器CCM->CSCMR2和ESAI_CLK_SEL位来决定的
- 第二部分是ESAI的前级分频,分频值由寄存器CCM_CS1CDR的ESAI_CLK_PRED来确定的,可以设置1~8分频。假如选择PLL4=650MHz,前级选择2分频,那么此时就是650/2=325MHz
- 第三部分又是一个分频器。分频值由寄存器CCM_CS1CDR 的 ESAI_CLK_PODF 来决定,可设置 1~8 分频。假如我们设置为 8 分频的话,经过此分频器以后的时钟就是 325/8=40.625MHz。因此最终进入到 ESAI 外设的时钟就是40.625MHz。
内核时钟系统主频设置
- 通过CCM_CACRR的ARM_PODF位(bit3 ~ 0) 对PLL1进行分频,后面没有2分频。
- PLL1=pll1_sw_clk_sel,通过CCSR的pll1_sw_clk_sel(bit 2)选择,如果为1,就是main_clk,如果是0,就是step_clk。
- step_clk 通过CCSR的 step_sel 位(bit 8)有两路可以选择,如果是0,频率就是osc_clk,也就是晶振。
- 时钟切换为临时时钟,也就是step_clk 后 就可以通过寄存器CCM_ANALOG_PLL_ARM的DIV_SELECT位(bit 6~0)修改PLL1的频率。公式为:PLL=24*div_select/2,单位是MHz。例如如果要设置PLL为1056,那么div_select就设置为88。还要设置enable 位(bit 13)为1,也就是使能输出
- 在切换回PLL1之前,设置CACRR为main_clk
CCM_CACRR
ARM_PODF可以设置0 ~ 7,分别对应1 ~ 8分频。如果要设置内核主频为528MHz,设置2分频,PLL1=1056MHz;如果要设置内核主频为696MHz,就设置1分频,PLL1=696MHz,因为PLL1有最低值和最高值。如果要设置700MHz,只能寻找最接近的值设置
CCSR
通过CCSR寄存器的 pll1_sw_clk_sel 进行数据选择。在修改PLL1的时候,也就是设置系统时钟的时候需要给6ULL一个临时的时钟,也就是step_clk ,在修改PLL1的时候需要将 pll1_sw_clk_sel 修改为 step_clk。
CCM_ANALOG_PLL_ARMn
- ENABLE:时钟输出使能位,设置为1,使能PLL1输出,设置为0就关闭PLL1输出
- DIV_SELECT:设置PLL1的输出频率,范围54~108。CLK=Fin*div_select/2,Fin=24MHz
修改PLL1时钟频率时要先将内核时钟源改为其他的时钟源,可选时钟源如下:
代码实例
// 使能外设时钟
void clk_enable()
{
CCM->CCGR0=0xffffffff;
CCM->CCGR1=0xffffffff;
CCM->CCGR2=0xffffffff;
CCM->CCGR3=0xffffffff;
CCM->CCGR4=0xffffffff;
CCM->CCGR5=0xffffffff;
CCM->CCGR6=0xffffffff;
}
// 初始化时钟
void imx6u_clkinit()
{
if((((CCM_CCSR)>>2)&0x1)==0) // 如果此位为0,就是main_clk,需要切换为1
{
CCM->CCSR &= ~(1<<8); // 设置step_clk=osc_clk
CCM->CCSR |= (1<<2); // 切换为step_clk,就可以修改PLL1
}
// 设置PLL1为1056MHz
CCM_ANALOG->PLL_ARM = (1<<13)|((88<<0)&0x7f); // &0x7f就是取低7位
CCM->CACRR = 1; // 设置为2分频
CCM->CCSR &= ~(1<<2); // 切换回时钟
}
PFD时钟设置
PLL2的4路设置
用到的寄存器是CCM_ANALOG_PFD_528n
- PFD0_FRAC:设置分频数,PLL2_PFD0 计算公式为528*18/PFD0_FRAC,可设置范围12~35
- PFD0_STABLE:此位为只读位,判断PLL2_PFD0是否稳定
- PFD0_CLKGATE:输出使能位,为1的时候关闭PLL2_PFD0的输出,为0的时候使能输出
PLL3的4路设置
用到的寄存器是CCM_ANALOG_PFD_480n
结构类似,只是计算公式不同。PLL3_PFDX=480*18/PFDX_FRAC
代码实例
reg = CCM_ANALOG->PFD_528;
reg &= ~(0x3f3f3f3f); // 将所有位全部清0
reg |= (32<<24); // PLL2_PFD3=297
reg |= (24<<16); // PLL2_PFD2=396
reg |= (16<<8); // PLL2_PFD1=594
reg |= (27<<0); // PLL2_PFD0=352
CCM_ANALOG->PFD_528=reg;
reg = CCM_ANALOG->PFD_490;
reg &= ~(0x3f3f3f3f); // 将所有位全部清0
reg |= (19<<24); // PLL3_PFD3=720
reg |= (17<<16); // PLL3_PFD2=508.2
reg |= (16<<8); // PLL3_PFD1=540
reg |= (12<<0); // PLL3_PFD0=720
CCM_ANALOG->PFD_480=reg;
AHB、IPG和PERCLK根时钟设置
因为PERCLK_CLK_ROOT 和 IPG_CLK_ROOT 要用到AHB_CLK_ROOT.
I.MX6U外设根时钟可设置范围如下,一般直接设置为最大,让性能最大
AHB_CLK_ROOT 和 IPG_CLK_ROOT
①此选择器用来选择 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 的分频值,可以设置 1~8 分频,如果想要 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
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 中指定位表示。
- 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
- LCDIF1_PODF: lcdif1 的时钟分频,可设置 0 ~ 7,分别对应 1~8 分频。
- PRE_PERIPH2_CLK_SEL: pre_periph2 时钟源选择, 00 选择 PLL2, 01 选择 PLL2_PFD2,10 选择 PLL2_PFD0, 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_PFD0, 11 选择PLL2_PFD2/2。
- PERIPH_CLK2_SEL: peripheral_clk2 时钟源选择, 00 选择 pll3_sw_clk, 01 选择 osc_clk,10 选择 pll2_bypass_clk。
CCM_CSCMR1
- PERCLK_CK_SEL: perclk 时钟源选择,为 0 的话选择 ipg clk,为 1 的话选择 osc clk。
- PERCLK_PODF: perclk 的时钟分频,可设置 0 ~ 7,分别对应 1~8 分频。
在修改如下时钟选择器或者分频器的时候会引起与 MMDC 的握手发生:mmdc_podf、periph_clk_sel、periph2_clk_sel、arm_podf、ahb_podf。
发生握手信号以后需要等待握手完成,寄存器 CCM_CDHIPR 中保存着握手信号是否完成,如果相应的位为 1 的话就表示握手没有完成,如果为 0 的话就表示握手完成,另外在修改 arm_podf 和 ahb_podf 的时候需要先关闭其时钟输出,等修改完成以后再打开,否则的话可能会出现在修改完成以后没有时钟输出的问题。
代码实例
- AHB_CLK_ROOT 初始化
- 设置CMCBR寄存器的 PRE_PERIPH_CLK_SEL 位为0
- 设置CMCDR寄存器的 AHB_PODF 位(bit 12~10)为2,也就是3分频
// 设置AHB=132MHz
CCM->CBCMR &= ~(3<<18);
CCM->CBCMR |= (1<<18); // 设置pre_periph_clk=PLL2_CLK =396MHz
CCM->CBCDR &= ~(1<<25);// periph_clk = pre_periph_clk=396MHz
while(CCM->CDHIPR & (1<<5));
// 设置3分频的时候如果没有关闭AHB_CLK_ROOT的输出,会出错,但是内部boot rom设置了3分频
/*
CCM->CBCDR &= ~(7<<10); // 将10~12位清零
CCM->CBCDR |= (2<<10); // 设置3分频
while(CCM->CDHIPR & (1<<1))); // 等待握手信号完成
*/
- IPG_CLK_ROOT 初始化
- 设置CBCDR寄存器IPG_PODF = 1,也就是2分频
CCM->CBCDR &= ~(3<<8);
CCM->CBCDR |= (1<<8);
- PERCLK_CLK_ROOT 初始化
- 设置CSCMR1 寄存器的PERCLK_CLK_SEL 位为0,表示PERCLK的时钟源为IPG
CCM->CSCMR1 &= ~(1<<6);
CCM->CSCMR1 &= ~(0x3f<<0); // 1分频就是置0