[smart210] 通过PLL设置各时钟频率的方法以及代码注释

平台: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
}

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注