本节主要涉及imx6ull的时钟配置,包括arm内核时钟配置,外设时钟配置,以及imx6ull的时钟树设置。主要参考的是imx6ull的Reference(《IMX6ULL 参考手册》)。
一、时钟树
imxull的时钟来源于俩部分,一部分是实时时钟,32.768MHZ,用于提供RTC的时钟。另一部分是24MHZ的晶振,用于提供内核以及其他外设的时钟。我们的时钟树就是从24M时钟经过倍频,分频得来的,共有7路PLL。
具体的时钟树就不放上来了,截图实在是看不清楚。其实就是通过设置一些寄存器,设置多路选择器和分频器,输出指定的频率。
1.配置主时钟
我们将 I.MX6ULL 的主频设置为 528MHz。我们如果要设置2分屏的话,PLL1必须设置为1056M的输出。 ARM_PLL 是由CCM_ANALOG_PLL_ARM[DIV_SELECT]设置的,计算公式是PLL output frequency = Fref * DIV_SEL/2。所以 经过计算,CCM_ANALOG_PLL_ARM[DIV_SELECT]=88。2分屏的设置为CCM_CACRR[ARM_PODF]=0x2。
我们在设置主频的时侯需要先将主频设置为备用模式(必须得有一个主频,单片机才可以工作,即使是设置寄存器)。
从上图中可以看到先要设置CCSR[step_sel]=0,然后设置CCSR[pll1_sw_clk_sel]=1。
所以综上所述;
1.CCSR[step_sel]=0; //备用时钟设置
2.CCSR[pll1_sw_clk_sel]=1; //切换备用时钟
3.CCM_ANALOG_PLL_ARM[DIV_SELECT]=88; //设置主频
4.CCM_CACRR[ARM_PODF]=0x1; //设置2分频
5.CCSR[pll1_sw_clk_sel]=0; //切换回主时钟
2.配置其他PLL和PFD
以下是我们要设置的PLL和PFD:
PLL2_PFD0的计算公式为528*18/PFD0_FRAC
CCM_ANALOG_PFD_528[PFD0_FRAC] = 0x1B; //PFD0=352M
CCM_ANALOG_PFD_528[PFD1_FRAC] = 0x10; //PFD1=594M
CCM_ANALOG_PFD_528[PFD2_FRAC] = 0x18; //PFD2=396M
CCM_ANALOG_PFD_528[PFD3_FRAC] = 0x20; //PFD3=297M
PLL2_PFD0的计算公式为480*18/PFD3_FRAC
CCM_ANALOG_PFD_480[PFD0_FRAC] = 0xC; //PFD0=720M
CCM_ANALOG_PFD_480[PFD1_FRAC] = 0x10; //PFD1=540M
CCM_ANALOG_PFD_480[PFD2_FRAC] = 0x11; //PFD2=508.2M
CCM_ANALOG_PFD_480[PFD3_FRAC] = 0x13; //PFD3=454.7M
3. AHB、 IPG 和 PERCLK 根时钟设置
设置完主频和其他PLL PFD之后我们还需要设置AHB_CLK_ROOT,IPG_CLK_ROOT,PERCLK_CLK_ROOT。AHB_CLK_ROOT 最高可以设置 132MHz,IPG_CLK_ROOT和PERCLK_CLK_ROOT最高可以设置66MHz。
#AHB_CLK_ROOT设置
CBCMR[PRE_PERIPH_CLK_SEL] = 01 //选择396M
CBCDR[PERIPH_CLK_SEL] =0 //需要握手信号
CBCDR[AHB_PODF]=010 //3分频,需要握手信号
#IPG_CLK_ROOT设置
CBCDR[IPG_PODF]=01 //设置2分频
#PERCLK_CLK_ROOT设置
CSCMR1[PERCLK_CLK_SEL] = 0 //多路选择
CSCMR1[PERCLK_PODF] = 000000 //1分频
寄存器 CCM_CDHIPR判断握手信号是否完成,1 握手没有完成,0握手完成。
在修改 arm_podf 和 ahb_podf 的时候需要先关闭其时钟输出,等修改完成以后再打开
二、编写程序
#include "bsp_clk.h"
//使能时钟
int enable_clock(void){
CCM->CCGR0 = 0xffffffff;
CCM->CCGR1 = 0xffffffff;
CCM->CCGR2 = 0xffffffff;
CCM->CCGR3 = 0xffffffff;
CCM->CCGR4 = 0xffffffff;
CCM->CCGR5 = 0xffffffff;
CCM->CCGR6 = 0xffffffff;
return 0;
}
void init_imx6u_clock(void){
/*1.配置主时钟*/
if(((CCM->CCSR>>2) & 0x1) == 0){//判断现在是否使用的是主时钟
CCM->CCSR &= ~(1<<8); //设置备用时钟
CCM->CCSR |= 1<<2; //切换到备用时钟
}
//设置主频
CCM_ANALOG->PLL_ARM &= ~(0x7f); //0-6位清零
CCM_ANALOG->PLL_ARM |= (1<<13) | (88); //0-6位设置为88,13位为使能位
//设置2分频
CCM->CACRR = 0x1;
//切换回主时钟
CCM->CCSR &= ~(1<<2);
/*2.配置其他PLL和PFD*/
/*PPL2*/
unsigned int reg = CCM_ANALOG->PFD_528;
reg &= ~(0x3f3f3f3f); //PFD每个Byte的0-5位清零
reg |= 0x1b << 0; //PFD0=352M
reg |= 0x10 << 8; //PFD1=594M
reg |= 0x18 << 16; //PFD2=396M
reg |= 0x20 << 24; //PFD3=297M
CCM_ANALOG->PFD_528 = reg;
/*PPL3*/
reg = CCM_ANALOG->PFD_480;
reg &= ~(0x3f3f3f3f); //PFD每个Byte的0-5位清零
reg |= 0xC << 0; //PFD0=720M
reg |= 0x10 << 8; //PFD1=540M
reg |= 0x11 << 16; //PFD2=508.2M
reg |= 0x13 << 24; //PFD3=454.7M
CCM_ANALOG->PFD_480 = reg;
/*3. AHB、 IPG 和 PERCLK 根时钟设置*/
#if 0
/*AHB_CLK_ROOT设置为132M*/
CCM->CBCMR &= (~(3 << 18)) | (1 << 18); //选择396M
CCM->CBCDR &= ~(1 << 25);
while(CCM->CDHIPR & (1 << 5)); //等待握手完成
CCM->CBCDR &= (~(7 << 10)) | (2 << 10); //设置3分频
while(CCM->CDHIPR & (1 << 1)); //等待握手完成
#endif
/*IPG_CLK_ROOT设置为66M*/
CCM->CBCDR &= (~(3 << 8)) | (1 << 8); //设置2分频
/*PERCLK_CLK_ROOT设置为66M*/
CCM->CSCMR1 &= ~(1<<6); //多路选择
CCM->CSCMR1 &= ~(0x7); //1分频 不是3F?
}
总结:I.MX6ULL的时钟系统还是很复杂的,本节我们也只是讲解了如何进行主频、PLL、 PFD 和一些总线时钟的设置。