zoukankan      html  css  js  c++  java
  • Mini2440裸机开发之系统时钟配置

    首先,我们应该知道一点,Mini2440开发板在没有开启时钟前,整个开发板全靠一个12MHz的外部晶振提供频率来工作运行的,也就是说CPU、内存、UART、ADC等所有需要用到时钟频率的硬件都工作在12MHz下,而S3C2440A可以正常工作在400MHz下,可想而知两者速度相差会有多大了。

    如果CPU工作在12MHz频率下,开发板的使用效率非常低,所有依赖系统时钟工作的硬件,其工作效率也很低,比如,我们电脑里面经常提到的超频,超频就是让CPU工作在更高的频率下,让电脑运算速度更快,虽然频率是越高越好,但是由于硬件特性决定了任何一个设备都不可能无止境的超频,电脑超频时要考虑到CPU或主板发热过大,烧坏的危险,同样开发板的主板上的外设和CPU也有一个频率限度。

    ARM920T内核的S3C2440的最高正常工作频率如下:

    • CPU FCLK:400MHz;
    •  AHB 总线外设的HCLK:100MHz ;
    • APB 总线外设的PCLK:50MHz;

    那这一节我们就来介绍如何将FCLK时钟的频率提高到400MHZ。S3C2440有关时钟控制的信息位于芯片手册第七章。

    一、S3C2440上的工作时钟频率

    1.1 时钟信息

    S3C2440A 中的时钟控制逻辑可以产生时钟信号包括以下三种:

    • CPU 的FCLK:提供给ARM920T的时钟;
    • AHB 总线外设的HCLK :提供给用于ARM920T存储器控制器,中断控制器,LCD 控制器,DMA 和USB 主机模块的AHB总线的时钟;
    •  APB 总线外设的PCLK:提供给用于外设如WDT,IIS,I2C,PWM 定时器,MMC/SD 接口,ADC,UART,GPIO,RTC 和SPI 的APB 总线的时钟。

    S3C2440A 包含两个锁相环(PLL):一个提供给FCLK、HCLK 和PCLK,另一个专用于 USB 模块(48MHz)。时钟控制逻辑可以不使用PLL 来减慢时钟,并且可以由软件连接或断开各外设模块的时钟, 以降低功耗。

    为什么需要不同种类的时钟呢?

    由于不同的硬件外设工作时需要的额定频率不同,所以需要产生不同种类的时钟频率。也就是说,对于一些需要时钟工作的硬件,如果切断其时钟源,就不会再工作了,从而达到低功耗的目的,这也是便携嵌入式设备的一个特点。       

    1.2 时钟结构

    主时钟源来自一个外部晶振(XTIpll)或外部时钟(EXTCLK)。时钟发生包含了一个连接到外部晶振的振荡器(震荡放大器),还含有S3C2440A 所必须的两个用于产生高频率时钟的PLL(锁相环)。

    锁相环是实现倍频功能的,说白了就是将12MHz成倍的增加,达到实际所需频率。虽然锁相环有很多指标,咱们完全可以将其理解为一个时钟变换电路,低频晶振输入即可得到处理器所使用的较高频率的时钟。

    S3C2440里有两个PLL:

    • MPLL:MPLL用来产生FCLK、HCLK、PCLK的高频工作时钟;
    • UPLL:UPLL用来为USB提供工作频率;

    1.3 时钟源选择

    系统上电后,S3C2440处理器会自动锁存OM3和OM2引脚的电平值,这两个引脚用于选择外部时钟输入方式,如下表所示:

    模式 OM[3:2]

    MPLL状态

    UPLL状态

    主时钟源

    USB  时钟源

    00

    开启

    开启

    晶振

    晶振

    01

    开启

    开启

    晶振

    外部时钟

    10

    开启

    开启

    外部时钟

    晶振

    11

    开启

    开启

    外部时钟

    外部时钟

    注意:虽然MPLL 在复位后就开始,MPLL输出并没有作为系统时钟,直到软件写入有效值来设置MPLLCON 寄存器。在设置此值之前,是将外部晶振或外部时钟源提供的时钟直接作为系统时钟。即使用户不想改变MPLLCON 寄存器的默认值,用户也应当写入与之相同的值到MPLLCON 寄存器寄存器中。

    你可以从我们的mini2440开发板的电路图看到,开发板上的OM3和OM2均接地,即OM[3:2]=00。所以,时钟源为外部晶振。  

    开发板外部时钟频率太高容易受到外界环境的干扰,同时为了降低成本,通常开发板的外部晶振时钟频率都很低,Mini2440开发板就用用1个12MHz的晶振来提供时钟源。但是S3C2440处理器内部工作频率较高,这就需要用锁相环(PLL)来实现倍频功能。

    1.4 锁相环(PLL)

    时钟发生器之中作为一个电路的MPLL,参考输入信号的频率和相位同步出一个输出信号。在这种应用中,其包含了如下图所示的以下基本模块:

    • 用于生成与输入直流电压成比例的输出频率的压控振荡器(VCO);
    • 用于将 输入频率(Fin)按p分频的分频器P;

    • 用于将VCO输出频率按m 分频并输入到相位频率检测器(PFD)中的分频器M;

    • 用于将VCO输出频率按s 分频成为MPLL(输出频率来自MPLL 模块)的分频器S、鉴相器、电荷泵以及 环路滤波器。

    输出时钟频率MPLL与输入时钟频率Fin 有如下等式:

    $$MPLL=frac{2 imes m imes Fin}{p imes 2^s}$$

    $$m=M+8,p=P+2$$

    所以对于S3C2440、Fin与FCLK之间的关系为:

    $$FCLK = MPLL = frac{2 imes M imes Fin}{p imes 2^s} $$

    1.5 时钟控制逻辑

    时钟控制逻辑决定使用的时钟源,即使用PLL 时钟或直接使用外部时钟(XTIpll 或EXTCLK)。当配置了PLL为一个新频率值时,时钟控制逻辑先禁止FCLK,直至使用PLL 锁定时间使PLL 稳定输出。时钟控制逻 辑在上电复位时和从掉电模式中唤醒时同样是激活的。

    分析上图,系统时钟初始化流程如下(FCLK即MPLL锁相环的输出):

    1. 系统刚上电几毫秒后,FCLK等于外部晶振(OSC)的时钟频率,即FCLK=Fin;
    2. 当复位信号nRESET恢复高电平后,锁相环按照寄存器MPLLCON和CLKDIVN设定的倍频比例开始生成所需要的时钟频率FCLK,HCLK,PCLK。从图可以看到,从锁相环开始工作到输出新的稳定的频率值需要一定的时间(Lock Time,也叫锁相环的捕获时间),经过这段时间后,锁相环输出新的频率值,这时FCLK等于锁相环MPLL的输出,而HCLK,PCLK通过CLKDIVN寄存器控制分频比。寄存器LOCKTIME中的值对应着图中的Lock Time,初始化时一般将其设为0xFFFFFFFF,这是S3C2440数据手册上给出的默认值,一般按照这个值初始化LOCKTIME寄存器即可满足要求。

    1.6 FCLK、HCLK、PCLK关系

    当HDIV=0时,HCLK=FCLK;当HDIV=1时,HCLK=FCLK/2;

    当PDIV=0时,PCLK=HCLK;当PDIV=1时,PCLK=HCLK/2;

    三者关系通过CLKDIVN寄存器进行设置,S3C2440时钟设置时,还要额外设置CAMDIVN(0X4C000018,照相机时钟分割寄存器),寄存器如下表,HCLK4_HALF,HCLK3_HALF分别与CAMDIVN[9:8]对应,下表列出了各种时钟比例:

    注意:如果HDIVN设置为非0,CPU的总线模式要进行改变,默认情况下FCLK = HCLK,CPU工作在fast bus mode快速总线模式下,HDIVN设置为非0后, FCLK与HCLK不再相等,要将CPU改为asynchronous bus mod异步总线模式(S3C2440无同步总线模式),可以通过下面的嵌入汇编代码实现:

    mrc p15, 0, r1, c1, c0, 0        /* 读取CP15 C1寄存器 */ 
    orr r1, r1, #0xc0000000        /* 设置CPU总线模式 */ 
    mcr p15, 0, r1, c1, c0, 0        /* 写回CP15 C1寄存器*/

    1.7  总结

    相信你看完上面的关于初始化的,应该大概懂了初始化流程,但可能还是不知道怎么产生对应的MCLK、PCLK和FCLK。      

    那么,如何控制锁相环PLL的输出频率呢?S3C2440处理器内部有两个寄存器:

    • MPLLCON寄存器控制FCLK与Fin的比例关系;
    • CLKDIVN寄存器控制FCLK和HCLK、PCLK的比例关系;

    我们使用下图表示简要的表达这四者的关系:

    二、 时钟相关寄存器

    2.1 锁定时间计数寄存器(LOCKTIME)

    寄存器信息:

    寄存器 地址 R/W 描述 复位值
    LOCKTIME 0x4C000000 R/W PLL锁定时间计数寄存器 0xFFFFFFFF

    寄存器位信息:

    BWSCON   位 描述 初始状态
    U_LYIME [31:16] UCLK 的UPLL 锁定时间计数值(U_LTIME 300μs) 0xFFFF
    M_LTIME [15:0] FCLK,HCLK 和PCLK 的MPLL 锁定时间计数值(M_LTIME 300μs) 0xFFFF

    2.2 PLL控制寄存器(MPLLCON和UPLLCON)

     寄存器信息:

    寄存器 地址 R/W 描述 复位值
    MPLLCON 0x4C000004 R/W MPLL配置寄存器 0x00096030
    UPLLCON 0x4C000008 R/W UPLL配置寄存器 0x0004D030

    寄存器位信息:

    PLLCON 描述 初始状态
    MDIV [19:12] 主分频控制 0x96/0x4D
    PDIV [9:4] 预分频控制 0x03/0x03
    SDIV [1:0] 后分频控制 0x00/0x00

    注意: 在系统初始化阶段,当你设置MPLL 和UPLL 的值时,你必须首先设置UPLL 值再设置MPLL 值。(大约需要7 个NOP的间隔).

    MPLL控制寄存器:

    $$MPLL=frac{2 imes m imes Fin}{p imes 2^s}$$

    $$m=MDIV+8,p= PDIV +2, s = SDIV$$

     UPLL控制寄存器:

    $$UPLL=frac{ m imes Fin}{p imes 2^s}$$

    $$m=MDIV+8,p= PDIV +2, s = SDIV$$

    2.3 时钟分频控制寄存器(CLKDIVN)

     寄存器信息:

    寄存器 地址 R/W 描述 复位值
    CLKDIVN 0x4C000014 R/W 时钟分频控制寄存器 0x00000004

    寄存器位信息:

    CLKDIVN 描述 初始状态
    DIVN_UPLL [3]

    UCLK 选择寄存器(UCLK 必须为48MHz 给USB)

    0:UCLK = UPLL时钟      1:UCLK = UPLL 时钟 / 2

    当UPLL 时钟被设置为48MHz 时,设置为0

    当UPLL 时钟被设置为96MHz 时,设置为1

    0
    HDIVN [2:1]

    00:HCLK = FCLK/1

    01:HCLK = FCLK/2

    10:HCLK = FCLK/4  当CAMDIVN[9] = 0时

           HCLK = FCLK/8  当CAMDIVN[9] = 1时

    11:HCLK = FCLK/3 当CAMDIVN[8] = 0 时

           HCLK = FCLK/6 当CAMDIVN[8] = 1 时

    00
    PDIVN [0]

    0:PCLK 是和HCLK/1 相同的时钟

    1:PCLK 是和HCLK/2 相同的时钟

    0

    2.4 摄像头时钟分频控制寄存器(CAMDIVN)

     寄存器信息:

    寄存器名

    地址

    是否读写

    描述

    复位值

    CAMDIVN

    0x4C000018

    R/W

    摄像头时钟分频控制寄存器

    0x00000000

    寄存器位信息:

    CAMDIVN 描述 初始状态
    DVS_EN [12]

    0:关闭DVS
         ARM 内核将正常运行在FCLK(MPLL 输出)

    1:开启DVS
         ARM 内核将运行在与系统时钟的时钟(HCLK)

     0
    保留 [11]  保留  0
    保留 [10]  保留  0
    HCLK4_HALF [9]

    HDIVN分频因子选择位(当CLKIVN[2:1]位为10b时有效)

    0: HCLK=FCLK/4      1: HCLK=FCLK/8

     0
    HCLK3_HALF [8]

    HDIVN分频因子选择位(当CLKIVN[2:1]位为11b时有效)

    0: HCLK=FCLK/3   1: HCLK=FCLK/6

     0
    CAMCLK_SEL [4]

    0:使用UPLL 输出作为CAMCLK(CAMCLK=UPLL 输出)

    1:CAMCLK_DIV 的值分频得到CAMCLK

     0
    CAMCLK_DIV [3:0]  CAMCLK 分频因子设置寄存器(0 至15)

    摄像头时钟 = UPLL / [(CAMCLK_DIV +1)x2]
    此位在CAMCLK_SEL=1 时有效

     0

    2.5 FCLK值选择

    现在咱们应该有个大概的意识了,设置MPLLCON中相应的位,就可以通过Fin获得FCLK了。但是这家伙还得一步步的算MDIV、PDIV、SDIV的值,太麻烦了,对于我这懒人,这样不好不好。那么怎么得到这三者的值呢?

    虽然PLL给用户提供了灵活变换系统时钟的功能,但是,并不是任意的时钟下处理器都能正常工作,基于此种原因,为了照顾我等懒人,官方给出了系统时钟配置参考,如图所示:

    下面以第5行为例讲解,假设外部晶振输入为$12MHz$,$MDIV=92$,$PDIV=1$,$SDIV=1$,则:

    $$m=92+8=100,p=1+2=3,s=1$$

    $$FCLK=frac{2*100*12MHz}{3*2^1}=400MHz$$

    小技巧:由上面的分析可以看到,MDIV、PDIV和SDIV都是占用了MPLLCON的连续某几位,如PDIV是MPLLCON寄存器的第4~9位。因此,初始化时可以使用移位指令来实现对MPLLCON的初始化。

    2.6 UPLL值选择

     同上,此处不再介绍。

    三、系统时钟初始化步骤

    3.1 设置LockTime变频锁定时间

    LockTime变频锁定时间由LOCKTIME寄存器来设置,由于变频后开发板所有依赖时钟工作的硬件都需要一小段调整时间,即MPLL设置生效的等待时间。

    该时间计数通过设置LOCKTIME寄存器[31:16]来设置UPLL(USB时钟锁相环)调整时间,通过设置LOCKTIME寄存器 [15:0]设置MPLL调整时间,这两个调整时间数值一般用其默认值即可。

    3.2 设置FCLK,HCLK,PCLK三者之间的比例

    设置CLKDIVN寄存器:

    例如:已知系统外部晶振输入为12MHz,要求$FCLK=400MHz,HCLK=100MHz,PCLK=50MHz$,即$FCLK:HCLK:PCLK=1:4:8$,则$CLKDIV\_VAL=5$。

    3.3 设置CPU工作于异步模式

    如果HDIVN设置为非0,CPU的总线模式要进行改变,默认情况下FCLK = HCLK,CPU工作在fast bus mode快速总线模式下,HDIVN设置为非0后, FCLK与HCLK不再相等,要将CPU改为asynchronous bus mod异步总线模式(S3C2440无同步总线模式)。

    可以通过下面的嵌入汇编代码实现:

    mrc p15, 0, r1, c1, c0, 0        /* 读取CP15 C1寄存器 */ 
    orr r1, r1, #0xc0000000        /* 设置CPU总线模式 */ 
    mcr p15, 0, r1, c1, c0, 0        /* 写回CP15 C1寄存器*/

    3.4 设置FCLK与晶振输入频率(Fin)的倍数

    例如,已知系统外部晶振输入为12MHz,要求FCLK输出400MHz,经过计算可以得到$MDIV=92,PDIV=1,SDIV=1$。

    对于ARM汇编,可以用下面的指令进行初始化(设置MPLLCON寄存器):

       M_MDIV        EQU    92     ;Fin=12MHz,Fout=200MHz
       M_PDIV         EQU    1
       M_SDIV         EQU    1        ;S3C2440
       ldr       r0,=MPLLCON          ;将MPLLCON寄存器的地址值暂存于r0
       ldr       r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV)       ;将设置MPLLCON的值暂存于r1
       str       r1,[r0]                         ;将r1寄存器的值赋给MPLLCON寄存器

    如果要求FCLK输出400MHz,那么相对应的值$MDIV=92,PDIV=1,SDIV=1$。这两组值就是经常用到的。

    四、程序

    $Fin=12MHz,FCLK=400MHz,HCLK=100MHz,PCLK=50MHz$,下面是完整的时钟初始化代码。

    4.1 start.s

    ARM汇编版本:

    .text
    .global _start
    _start:
        /*  关闭看门狗 */        
        bl  disable_watchdog
        
        /*  设置系统时钟 */
        bl system_clock_init 
        
        /* 初始化栈 */
        bl stack_init
        
        /* 跳到main函数执行 */
        bl main     
        
    loop:
        b loop
    
    
    /* 关闭开门狗(关闭门狗中断,以及看门狗计数器,禁止复位信号输出)    */    
    #define  WTCON      0x53000000    /* 看门狗控制寄存器地址 #define等价于标准汇编里的EQU 用来定义常量 */     
    disable_watchdog:    
            ldr  r0,=WTCON             /*  伪指令加载WTCON值到r0  */
            mov  r1,#0x00
            str  r1,[r0]               /*  把[WTCON]内存单元清零  */           
            mov  pc,lr
    
    
    /*  初始化系统时钟 FCLK = 400MHz,HCLK = 100MHz, PCLK = 50MHz, UPLL=48MHz  */ 
    #define  LOCKTIME     0x4c000000         
    #define  MPLLCON      0x4c000004  
    #define  UPLLCON      0x4c000008  
    #define  CLKDIVN      0x4c000014  
    #define  M_MDIV       92       /* @Fin=12M  UPLL=400M  */
    #define  M_PDIV       1
    #define  M_SDIV       1
    #define  U_MDIV       56        /* @Fin=12M  UPLL=48M  */
    #define  U_PDIV       2
    #define  U_SDIV       2
    #define  DIVN_UPLL    0         /* FCLK:HCLK:PCLK=1:4:8 */ 
    #define  HDIVN        2         
    #define  PDIVN        1        
    system_clock_init:    
        /* 设置Lock Time  */
        ldr r0,=LOCKTIME
        ldr r1,=0xffffffff  
        str r1,[r0]
        
        /* 设置分频系数 */
        ldr r0,=CLKDIVN
        ldr r1,=((DIVN_UPLL<<3) | HDIVN <<1 | PDIVN)
        str r1,[r0]
        
        /* CPU改为异步总线模式 */
        mrc p15,0,r1,c1,c0,0
        orr r1,r1,#0xC0000000
        mcr p15,0,r1,c1,c0,0
        
        /* 设置UPLL */
        ldr r0,=UPLLCON  
        ldr r1,=((U_MDIV<<12) | (U_PDIV<<4) | U_SDIV)    
        str r1, [r0]  
        
        nop  
        nop  
        nop  
        nop  
        nop  
        nop
        nop
        
        /* 设置MPLL */
        ldr r0,=MPLLCON
        ldr r1,=((M_MDIV << 12) | (M_PDIV << 4) | M_SDIV)
        str r1,[r0]
        
        mov pc,lr
    
    
    
    /* 设置栈 自动分辨是nor flash 启动还是nand flash启动 */
    /* 先将一个数写道0地址,然后读出来判断跟写入的值是否一样;跟写入的一样则是nand flash启动,跟写入的值不一样则是nor flash 启动 */
    stack_init: 
        mov r1, #0
        ldr r0, [r1]                /* 读出原来的值备份 */
        str r1, [r1]                /* 0->[0] */ 
        ldr r2, [r1]                /* r2=[0] */
        cmp r1, r2                  /* r1==r2? 如果相等表示是NAND启动 */
        ldr sp, =0x40000000+ 0x1000   /* 先假设是nor启动,片内SRAM空间 */
        moveq sp, #0x1000              /* nand启动, 将栈设置在4k处 */
        streq r0, [r1]              /* 恢复原来的值 */
        mov pc,lr

    4.2 main.c

    /*GPIO registers*/
    #define GPBCON              (*(volatile unsigned long *)0x56000010)
    #define GPBDAT              (*(volatile unsigned long *)0x56000014)
    
    
    void delay(int tim){
        while(tim--);
    }
    
    int main(){
        /* 清零 */
        GPBCON &= ~(0x03 << 10);
        /* 设置为output */
        GPBCON |= (0x01 << 10);
        
        while(1){
            /* 将 GPB5 输出低电平 */
            GPBDAT &= ~(1<<5);
            delay(0x100000);
            /* 将 GPB5 输出高电平 */
            GPBDAT |= (1<<5);
            delay(0x100000);
        }
        return 0;
    }

    4.3 Makefile

    all:start.o main.o
        # 链接
        arm-linux-ld -Ttext 0x30000000  -o main.elf $^
        # 转为bin  -S 不从源文件中复制重定位信息和符号信息到目标文件中
        arm-linux-objcopy -O binary -S main.elf main.bin
        # 反汇编 -D反汇编所有段 
        arm-linux-objdump -D main.elf > main.dis
    
    %.o : %.S
        arm-linux-gcc -g -c $^
        
    %.o : %.c
        arm-linux-gcc -g -c $^
        
    .PHONY: clean
    clean:
        rm *.o *.elf *.bin *.dis

    将bin文件烧写进开发板,可以看到LED的闪烁速度比没有进行时钟初始化的时候快了好多。

    参考文章

    [1] 一起学mini2440裸机开发(三)--S3C2440时钟学习

    [2]S3c2440之时钟频率(修改综合版)

  • 相关阅读:
    sed命令详解 皇星客栈
    FD_set FD_zero 皇星客栈
    linux tr命令详解 皇星客栈
    解决中文乱码问题(Ubuntu) 皇星客栈
    FIFO深度 皇星客栈
    输入阻抗 皇星客栈
    APROM Data Flash LDROM 皇星客栈
    kubernetes 1.21 部署业务
    kubernetes 1.21部署 cephcsi rbd
    kubernetes 1.21 部署 dashboard
  • 原文地址:https://www.cnblogs.com/zyly/p/14868618.html
Copyright © 2011-2022 走看看