平台:smart210(tiny210v2)
CPU:S5PV210
目标:
设置APLL (提供MSYS domain 与DSYS domain 下各时钟的来源,最高1Ghz)
设置MPLL(提供MSYS domain 与DSYS domain 下各时钟的来源,最高2Ghz)
设置EPLL(Audio相关时钟)
设置VPLL(Video相关时钟,54Mhz)
知识储备:
1. MSYS主要为CPU,DRAM控制器,3D,IRAM,IROM,中断控制器等提供时钟
DSYS主要为显示相关部件FIMC,FIMD,JPEG,Multimedia IPs提供时钟
PSYS为外设I2S,SPI,I2C,UART等提供时钟
2. 官方文档有时钟domain的【时钟继承图】(自造词。。)
从图上我们能发现,MSYS domain包含有ARMCLK、HCLK_MSYS、PCLK_MSYS、HCLK_IMEM、SCLK_DMC0 这几个时钟信号,追本溯源,这些时钟信号主要都源于APLL或者MPLL经过各种分频倍频环节得到的,而其他的CLOCK domain下的子时钟也如此,换句话说,只要先把外置时钟源FIN=24Mhz转换成我们需要的APLL,MPLL这两个主要的PLL时钟,其余大部分的时钟我们就能轻松设置了。
3 官方还给出了直观的各子时钟的设置来源与推荐值:
4 同样的,官方P364给出了从FIN到设置最终子时钟的整个流程:
①打开APLL:APLL_CON0[31]=1;这里能看到bit31是使能控制端
②等待PLL锁定
③在锁定时钟稳定后,令APLL输出:CLK_SRC0[0]=1;这里能看到bit0(APLL_SEL)控制MUX_APLL的时钟输出,同时bit16(MUX_MSYS_SEL)控制了MUX_MSYS的输出
④改变分频值,设置PMS,其实就是PDIV,MDIV,SDIV的值,相关寄存器仍是APLL_CON0,参考步骤①的附图,而设置的PMS有什么用呢?我们最终要得到的是APLL的时钟,这个时钟通过FIN与PMS三个分频系数计算得到,使用的是下面这条公式 FOUT=MDIV*FIN/PDIV*2^SDIV-1)) 。下面附图给出了公式以及PMS的取值范围:
⑤设置各CLOCK domain下的子时钟的分频比,从而确定了最终的MSYS domain、PSYS domain与DSYS domain各子时钟的频率,相关寄存器CLK_DIV0、CLK_DIV1、CLK_DIV2,这里贴出主要的CLK_DIV0
5.好了,现在整理一下那5个步骤,其实顺序可以打乱,只要设置得当就行,这5个步骤中,有几个关键点要注意的:
①初始输入频率FIN为24Mhz,通过设置APLL_CON来控制PLL使能,以及设置PMS的值,设置完这些,我们基本上得到了APLL的FOUT,一般都是先想好需要多快的FOUT,再去设置PMS的值的。
②获取到FOUT APLL之后,通过设置CLK_SRC0的相关位把FOUT APLL作为MUX APLL的输出(从最上面那个【时钟继承图】上可以看出MUX APLL可以选择FIN或者FOUT APLL作为输出,输出的时钟名为SCLK_APLL )同时,我们还要通过设置CLK_SRC0的相关位如MUX_MSYS_SEL(【时钟继承图】上的又一个多路复用器)将SCLK_APLL作为该MUX的输出,输出的时钟名为MOUT_MSYS
③最后就是通过设置CLK_DIV0来设置各子时钟的分频比了,可以发现,只要有了MOUT_MSYS,MSYS domain里面的子时钟都能通过设置这个寄存器而得到!
6.下面再不厌其烦地给出直观的讲解图
终于到了代码阶段了,有了上面的知识储备,程序的事也是手到擒来是吧!
1.首先start.s关好看门狗,设置堆栈后,进入clock_init
.global _start _start: ldr r0, =0xE2700000 mov r1, #0 str r1, [r0] ldr sp, =0x40000000 bl clock_init bl main halt: b halt
2.clock.c中,有写好的clock_init函数
// 时钟相关寄存器 #define APLL_LOCK *volatile unsigned long *)0xE0100000) ) #define MPLL_LOCK *volatile unsigned long *)0xE0100008) ) #define APLL_CON0 *volatile unsigned long *)0xE0100100) ) #define APLL_CON1 *volatile unsigned long *)0xE0100104) ) #define MPLL_CON *volatile unsigned long *)0xE0100108) ) #define CLK_SRC0 *volatile unsigned long *)0xE0100200) ) #define CLK_SRC1 *volatile unsigned long *)0xE0100204) ) #define CLK_SRC2 *volatile unsigned long *)0xE0100208) ) #define CLK_SRC3 *volatile unsigned long *)0xE010020c) ) #define CLK_SRC4 *volatile unsigned long *)0xE0100210) ) #define CLK_SRC5 *volatile unsigned long *)0xE0100214) ) #define CLK_SRC6 *volatile unsigned long *)0xE0100218) ) #define CLK_SRC_MASK0 *volatile unsigned long *)0xE0100280) ) #define CLK_SRC_MASK1 *volatile unsigned long *)0xE0100284) ) #define CLK_DIV0 *volatile unsigned long *)0xE0100300) ) #define CLK_DIV1 *volatile unsigned long *)0xE0100304) ) #define CLK_DIV2 *volatile unsigned long *)0xE0100308) ) #define CLK_DIV3 *volatile unsigned long *)0xE010030c) ) #define CLK_DIV4 *volatile unsigned long *)0xE0100310) ) #define CLK_DIV5 *volatile unsigned long *)0xE0100314) ) #define CLK_DIV6 *volatile unsigned long *)0xE0100318) ) #define CLK_DIV7 *volatile unsigned long *)0xE010031c) ) #define CLK_DIV0_MASK 0x7fffffff #define APLL_MDIV 0x7d #define APLL_PDIV 0x3 #define APLL_SDIV 0x1 #define MPLL_MDIV 0x29b #define MPLL_PDIV 0xc #define MPLL_SDIV 0x1 #define set_pllmdiv, pdiv, sdiv) 1<<31 | mdiv<<16 | pdiv<<8 | sdiv) #define APLL_VAL set_pllAPLL_MDIV,APLL_PDIV,APLL_SDIV) #define MPLL_VAL set_pllMPLL_MDIV,MPLL_PDIV,MPLL_SDIV) //#define PLL_OFF //屏蔽此句才能顺利启用PLL设置 void clock_init) { // 1 设置各种时钟开关,暂时不使用PLL CLK_SRC0 = 0x0; //根据手册显示,0代表使用FINPLL即默认外置时钟源此时FIN=24Mhz) //CLK_SRC0寄存器中控制位:[0]->APLL_SEL,[4]->MPLL_SEL,[8]->EPLL_SEL,[12]->VPLL_SEL #ifndef PLL_OFF // 2 设置锁定时间,使用默认值即可 // 设置PLL后,时钟从Fin提升到目标频率时,需要一定的时间,即锁定时间 APLL_LOCK = 0x0000FFFF; MPLL_LOCK = 0x0000FFFF; // 3 设置分频 CLK_DIV0 = 0x14131440; //以APLL为例,这里CLK_DIV0设置了APLL_RATIO的值为0,则根据公式 ARMCLK=MOUT_MSYS/APLL_RATIO + 1)=MOUT_MSYS // 4 设置PLL //解析为APLL_CON0=APLL_VAL=1<<31|0x7d<<16|0x3<<8|0x1)意思就是APLL_CON0寄存器[31]位使能,允许PLL控制 //[25:16]位为MDIV(倍频因子)值,[13:8]为PDIV(分频因子)值,[2:0]为SDIV值,FOUT计算公式如下 // FOUT= MDIV * FIN / PDIV*2^SDIV-1)) = 0x7d*24/0x3*2^1-1))=1000 MHz //上式采用MDIV=0x7d125),PDIV=0x33),SDIV=0x11) APLL_CON0 = APLL_VAL; // FOUT = MDIV*FIN/PDIV*2^SDIV)=0x29b*24/0xc*2^1)= 667 MHz MPLL_CON = MPLL_VAL; // 5 设置各种时钟开关,使用PLL CLK_SRC0 = 0x10001111; #endif }