• JZ2440 裸机驱动 第10章 系统时钟和定时器


    本章目标 
        了解S3C2410/S3C2440的时钟体系结构
        掌握通过设置MPLL改变系统时钟的方法
        掌握在不同的频率下设置存储控制器的方法
        掌握PWM定时器的用法
        了解WATCHDOG定时器的用法
    10.1 时钟体系及各类时钟部件
    10.1.1 S3C2410/S3C2440时钟系统
        S3C2410/S3C2440的时钟控制逻辑既可以外接晶振,然后通过内部电路产生时钟源;也
    可以直接使用外部提供的时钟源,它们通过引脚的设置来选择。时钟控制逻辑给整个芯片提
    供3种时钟:FCLK用于CPU核;HCLK用于AHB总线上的设备,比如CPU核、存储控制器、
    中断控制器、LCD控制器、DMA和USB主机模块等;PCLK用于APB总线上的设备,比如
    WATCHDOG、IIS、I2C、PWM定时器、MMC接口、ADC、UART、GPIO、RTC和SPI。
        为了降低电磁干扰、降低板间布线的要求,S3C2410/S3C2440外接的晶振频率通常很低,
    本开发板上为12MHz,需要通过时钟控制逻辑的PLL提高系统时钟。
        S3C2410/S3C2440有两个PLL:MPLL和UPLL。UPLL专用于USB设备,MPLL用于设置
    FCLK、HCLK、PCLK。它们的设置相似,本书以MPLL为例。
        上电时,PLL没被启动,FCLK即等于外部输入时钟,称为Fin。
        若要提高系统时钟,需要开启PLL。
        PLL设置过程如下所示,请参考图10.1,跟随FCLK的图像了解启动过程。
        (1)上电几毫秒后,晶振输出稳定,FCLK = Fin(晶振频率),nRESET信号恢复高电平后,
    CPU开始执行命令。
        (2)可以在程序开头启动MPLL,设置MPLL的几个寄存器后,需要等待一段时间(Lock Time),
    MPLL输出才稳定。在这段时间(Lock Time)内,FCLK停振,CPU停止工作。Lock Time的长短由
    寄存器LOCKTIME设定。
        (3)Lock Time之后,MPLL输出正常,CPU工作在新的FCLK之下。
        FCLK、HCLK和PCLK的比例是可以改变的,设置它们三者的比例,启动MPLL只需要设置3个
    寄存器(对于S3C2440的一些时钟比例,还需要额外设置一个寄存器)。
        【1】LOCKTIME寄存器(LOCK TIME COUNT):用于设置“Lock Time”的长度。
        前面说过,MPLL启动后需要等待一段时间,使得其输出稳定。S3C2410中,位[23:12]用于UPLL,
    位[11:0]用于MPLL。S3C2440中,位[31:16]用于UPLL,位[15:0]用于MPLL。一般而言,使用它的
    默认值即可,S3C2410中默认值为0x00FF FFFF,S3C2440中默认值为0xFFFF FFFF。
        【2】MPLLCON寄存器(Main PLL Control):用于设置FCLK与Fin的倍数。
        位[19:12]的值称为MDIV,位[9:4]的值称为PDIV,位[1:0]的值称为SDIV。FCLK与Fin的关系
    有如下计算公式:
        ① 对于S3C2410: MPLL(FCLK) = (     m * Fin) / (p * 2^s)
        ② 对于S3C2440: MPLL(FCLK) = (2 * m * Fin) / (p * 2^s)
        其中:m = MDIV + 8,p = PDIV + 2,s = SDIV。
        当设置MPLLCON之后——相当于图10.1中的“首先使用软件设置PLL”,Lock Time就被自动插入。
    Lock Time之后,MPLL输出稳定,CPU工作在新的FCLK下。
        【3】CLKDIVN寄存器(CLOCK DIVIDER CONTROL):用于设置FCLK、HCLK、PCLK三者的比例。
        对于S3C2410、S3C2440,这个寄存器表现稍有不同,请参考表10.1和图10.2.
         对于S3C2440的一些时钟比例,还需要额外的设置一个寄存器CAMDIVN。图10.2中,
    HDIVN为CLKDIVN寄存器为位[2:1],PDIVN为位[0];HCLK4_HALF、HCLK3_HALF分
    别为CAMDIVN寄存器的位[9]、[8]。各种时钟对比对应的寄存器设置如图10.2所示。
         对于S3C2410,HDIVN是CLKDIVN寄存器的位[1];
         对于S3C2440,HDIVN是CLKDIVN寄存器的位[2:1],如果HDIVN非0,CPU的总线模式
    应该从“fast bus mode”变为“asynchronous bus mode”,这可以通过如下指令来完成:
    # MMU_SetAsyncBusMode
    mrc p15, 0, r0, c1, c0, 0
    orr r0, r0, #R1_nF:OR:RL_iA
    mcr p15, 0, r0, c1, c0, 0
        其中的“R1_nF:OR:R1_iA”等于0xC000 0000。如果HDIVN非0时,而CPU的总线模式仍是
    “fast bus mode”,则CPU的工作频率将自动变为HCLK,而不再是FCLK。
    10.1.2 PWM定时器
        S3C2410/S3C2440的定时器部件完全一样,共有5个16位定时器。其中定时器0、1、2、3
    有PWM功能;定时器4没有输出引脚。
        定时器部件的时钟源为PCLK,首先通过两个8位的预分频器降低频率:定时器0、1共用第
    一个预分频器,定时器2、3、4共用第二个预分频器。预分频器的输出将进入第二部分分频器,
    它们输出5种频率的时钟:2分频、4分频、8分频、16分频或者外部时钟TCLK0/TCLK1。每个
    定时器的工作时钟可以从这5种频率中选择。
        这两个预分频都可以通过TCFG0寄存器来设置,每个定时器工作在哪种频率下也可以通过
    TCFG1寄存器来选择。如图10.3所示,形象地说明定时器的结构。
     
                                                图10.3 定时器结构图
         上面只是确定了定时器的工作频率,至于定时器如何工作还得了解其内部结构,如图
    10.4所示。
         
        定时器内部控制逻辑的工作流程如下:
        (1)程序初始,设定TCMPBn、TCNTBn这两个寄存器,它们表示定时器n的比较值、
    初始计数值。
        (2)随之设置TCON寄存器启动定时器n,这时,TCMPBn、TCNTBn的值将被装入其
    内部寄存器TCMPn、TCNTn中。在定时器n的工作频率下,TCNTn开始减1计数,其值可
    通过读取TCNTOn寄存器得知。 
        (3)当TCNTn的值等于TCMPn的值时,定时器n的输出管脚TOUTn反转;TCNTn继续
    减1计数。
        (4)当TCNTn的值达到0时,其输出管脚TOUTn再次反转,并触发定时器n的中断(如果
     
    中断使能了的话)。
        (5)当TCNTn的值达到0时,如果TCON寄存器中将定时器n设为“自动加载”,则TCMPB0
    和TCNTB0寄存器的值将被自动装入TCMP0和TCNT0寄存器中,下一个计数流程开始。
        定时器n的输出管脚TOUTn初始状态为高电平,以后在TCNTn的值等于TCMPn的值、
    TCNTn的值时反转。也可以通过TCON寄存器设置其初始电平,这样TOUTn的输出就完全
    反相了。通过设置TCMPBn、TCNTBn的值可以设置管脚TOUTn输出信号的占空比,这就
    是所谓的PWM,所以这些定时器又被称为PWM定时器。
        下面讲解定时器时寄存器的使用方法。
        (1)TCFG0寄存器(TIMER CONFIGURATION)
        位[7:0]、位[15:8]分别被用于控制预分频器0、1,它们的值为0~255。经过预分频器出来
    的时钟频率为:PCLK/{prescaler value + 1}。
        (2)TCFG1寄存器
        经过预分频器得到的时钟将被2、4、8、16分频,除了这4种频率外,额外的,定时器0、1
    还可以工作在外接的TCLK0时钟下,定时器2、3、4还可以工作在外接的TCLK1时钟下。
        通过TCFG1寄存器来设置这5个定时器,分别工作于这5个频率中哪一个下,如表10.2所示。
        
        这样,定时器n的工作频率或者外接的TCLK0或TCLK1可以通过这个公式计算:
            定时器工作频率 = PCLK / {prescaler value +1} / {divider value}
            {prescaler value} = 0~255
            {divider value}    = 2、4、8、16
        (3)TCNTBn/TCMPBn寄存器(COUNT BUFFER REGISTER & COMPARE BUFFER REGISTER)。
        n为0~4,这两个寄存器都只用到位[15:0],TCNTBn中保存定时器的初始值,TCMPBn
    保存比较值。它们的值在启动定时器时,被传到定时器内部寄存器TCNTn、TCMPn中。
        没有TCMPB4,因为定时器4没有输出管脚。
        (4)TCNTOn寄存器(COUNT OBSERVATION)
        n为0~4,定时器n被启动后,内部寄存器TCNTn在其工作时钟下不断减1计数,可以通过
    读取TCNTOn寄存器得知其值。
        (5)TCON寄存器(TIMER CONTROL)
        它有以下4个作用:
        ① 第一次启动定时器时,手动将TCNTBn/TCMPBn寄存器中的数据装入内部寄存器
    TCNTn、TCMPn中。
        ② 启动、停止定时器。
        ③决定在定时器计数到达0时,是否自动将TCNTBn/TCMPBn寄存器的值装入内部
    寄存器TCNTn、TCMPn中。
        ④ 决定定时器的管脚TOUTn的输出电平是否反转、
        TCON寄存器位[3:0]、位[11:8]、位[15:12]、位[19:16]、位[22:20]分别用于定时器0~4。
    除了定时器因为没有输出引脚在没有“输出反转”位外,其他位的功能相似。表10.3以定时器
    0为例说明这些位的作用。
        
         在第一次使用定时器时,需要设置“手动更新”位为1,以使TCNTBn/TCMPBn寄存器的
    值装入内部寄存器TCNTn、TCMPn中。下一次如果还要设置这一位,需要先将它清0。
        定时器还有其他功能,比如DMA、Dead zone等,需要了解的读者清参考数据手册。
    寄存器中涉及它们的部分这里就省略了。
    10.1.3 WATCHDOG定时器
        WATCHDOG定时器可以像一般16位定时器一样用于产生周期性中断,也可以用于发
    出复位信号以重启失常的系统。它与PWM定时器的结构类似,如图10.5所示。
         同样,WATCHDOG定时器的8位分频器将PCLK分频后,被再次分频得到4种频率:
    16、32、64、128分频,WATCHDOG定时器可以选择工作在哪种频率之下。WTCNT
    寄存器按照其工作频率减1计数,当达到0时,可以产生中断信号,可以输出复位信号。
    在第一次使用WATCHDOG定时器时,需要向WTCNT寄存器中写入初始计数值,以后
    在计数值达到0时,自动从WATDAT寄存器中寄存器中装入,重新开始下一个计数周期。
        使用WATCHDOG定时器的“WATCHDOG功能”时,在正常的程序中,必须不断重新
    设置WTCNT寄存器使得它不为0,这样可以保证系统不被重启,这称为喂狗。
        WATCHDOG定时器所涉及的寄存器如下:
        (1)WTCON寄存器(WATCHDOG TIMER CONTROL)
        用于设置预分频系数,选择工作频率,决定是否使用中断、是否启用WATDOG功能(即
    是否输出复位信号),各位的作用如表10.4所示。
        
         与PWM定时器相似,WATDOG定时器的工作频率通过这个公式计算:
        WATDOG定时器工作频率 = PCLK / {prescaler value + 1} / {divider value}
        {prescaler value} = 0~255;{divider value} = 16、32、64、128
        (2)WTDAT寄存器(WATCHDOG TIMER DATA)。
        用于决定WATCHDOG定时器的超时周期,在定时器启动后,当计数达到0时,WTDAT
    寄存器的值会自动传入WTCNT寄存器。不过,第一次启动WATDOG定时器时,WTDAT
    寄存器的值会自动传入WTCNT寄存器。
        (3)WTCNT寄存器(WATCHDOG TIMER COUNT)。
        在启动WATDOG定时器前,必须往这个寄存器写入初始值。启动定时器后,它减1计数,
    当计数值到达0时:
        如果中断被使能的话发出中断;
        如果WATCHDOG功能被使能的话,发出复位信号,装载WTDAT寄存器的值并重新计数。
    10.2 MPLL和定时器操作实例
    10.2.1 程序设计
        本实例讲解MPLL、定时器的使用。首先启动MPLL提高系统时钟,初始化存储控制器使
    SDRAM工作在新的HCLK下,然后将定时器0设为0.5s产生一次中断,在中断程序中改变
    LED的状态。
    10.2.2 代码详解
        源码在/work/hardware/timer目录下。
        本实验的重点在4点:
        ① 设置/启动 MPLL;
        ② 根据HCLK设置存储控制器;
        ③ 初始化定时器0;
        ④ 初始化定时器中断。
        相关函数在init.c中。
    1.设置/启动 MPLL
        clock_init函数用于设置MPLL,本开发板的输入时钟频率Fin为12MHz,将FCLK、HCLK、
    PCLK分别设为200MHz、100MHz和50MHz,代码如下:
     1 行号
     2 23行 #define s3c2410_MPLL_200MHz    ((0x5c << 12) | (0x04 << 4) | (0x00))    /*MDIV = 0x5c, PDIV = 0x04, SDIV = 0*/
     3 24行 #define s3c2440_MPLL_200MHz    ((0x5c << 12) | (0x01 << 4) | (0x02))
     4 25行 /*
     5 26行 *对于MPLLCON寄存器,[19:12]为MDIV、[1:0]为SDIV
     6 27行 *有如下公式:
     7 28行 *    s3c2410:MPLL(FCLK) = (m * Fin)/(p * 2^s)
     8 29行 *    s3c2440:MPLL(FCLK) = (2*m*Fin)/(p * 2^s)
     9 30行 *    其中:m = MDIV + 8,p = PDIV +2, s = SDIV
    10 31行 *对于本开发板,Fin = 12MHz
    11 32行 *设置CLKDIVN,令分频比为:FCLK:HCLK:PCLK = 1:2:4
    12 33行 *FCLK = 200MHz,HCLK = 100MHz,PCLK = 50MHz
    13 34行 */
    14 35行 void clock_init(void)
    15 36行 {
    16 37行     //LOCKTIME = 0x00ff ffff    //使用默认值即可
    17 38行     CLKDIVN = 0x03;            //FCLK:HCLK:PCLK = 1:2:4,HDIVN = 1, PDIVN = 1
    18 39行 
    19 40行     /*如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode”*/
    20 41行 __asm__(
    21 42行     "mrc p15, 0, r1, c1, c0, 0
    "    //读出控制寄存器
    22 43行     "orr r1, r1, #0xc0000000
    "      //设置为“asynchronous bus mode”
    23 44行     "mcr p15, 0, r1, c1, c0, 0
    "    //写入控制寄存器
    24 45行 )
    25 46行 
    26 47行     /*判断是s3c2410还是s3c2440*/
    27 48行     if((GSTATUS1 == 0x32410000) || (GSTATUS1 == 0x32410002))
    28 49行     {
    29 50行         MPLLCON = S3C2410_MPLL_200MHz;    /*现在,FCLK = 200MHz,HCLK = 100MHz,PCLK = 50MHz*/
    30 51行     }
    31 52行     else
    32 53行     {
    33 54行         MPLLCON = S3C2440_MPLL_200MHz;
    34 55行     }
    35 56行 }
    36 57行 
    init.c->clock_init.c()
        如果处理器是S3C2410,使用第50行设置MPLL寄存器,令MDIV = 0x5c,PDIV = 0x04, 
    SDIV = 0,所以:
        MPLL(FCLK) = (m * Fin)/(p * 2^s) = (0x5c + 8) * 12MHz/((0x04 + 2)*2^0) = 200MHz
        HCLK = FCLK/2 = 100MHz
        PCLK = FCLK/4 = 50MHz
        如果处理器是S3C2440,使用第54行设置MPLL寄存器,使用第29行的公式可以计算
    出MPLL = 200MHz,所以FCLK、HCLK、PCLK分别为200MHz、100MHz和50MHz。 
    2.设置存储控制器
    memsetup函数被用来设置存储控制器,代码如下:
     1 行号
     2 58行/*
     3 59行*设置存储控制器以使用SDRAM
     4 60行*/
     5 61行void memsetup(void) 
     6 62行{
     7 63行    volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;
     8 64行    
     9 65行    /*这个函数之所以这样赋值,而不是像前面的实验(比如mmu实验)那样将配置值
    10 66行    *写在数组中,是因为要生成位置无关代码,使得这个函数可以被复制到
    11 67行    *SDRAM之前就可以在Steppingstone中运行
    12 68行    */    
    13 69行    /*存储控制器13个寄存器的值*/
    14 70行    p[0] = 0x22011110;    //BWSCON
    15 71行    P[1] = 0x00000700;    //BANKCON0
    16 72行    p[2] = 0x00000700;    //BANKCON1
    17 73行    p[3] = 0x00000700;    //BANKCON2
    18 74行    p[4] = 0x00000700;    //BANKCON3
    19 75行    p[5] = 0x00000700;    //BANKCON4
    20 76行    p[6] = 0x00000700;    //BANKCON5
    21 77行    p[7] = 0x00018005;    //BANKCON6
    22 78行    p[8] = 0x00018005;    //BANKCON7
    23 79行
    24 80行    /*REFRESH,
    25 81行    *HCLK = 12MHz :0x008c 07a3
    26 82行    *HCLK = 100MHz:0x008c 04f4
    27 83行    */
    28 84行    p[9]  = 0x008c04f4;
    29 85行    p[10] = 0x000000b1;    //BANKSIZE
    30 86行    p[11] = 0x00000030;    //MRSRB6
    31 87行    p[12] = 0x00000030;    //MRSRB7
    32 88行}
    33 89行
    init.c->memsetup()
        除REFRESH寄存器外,其他寄存器的值与第6章的实验程序一样。现在HCLK等于
    100MHz,REFRESH寄存器的值需要重新计算。参考第6章的公式可以计算:
        R_CNT = 2^11 + 1 - 100MHz * 7.8125uS = 0x04F4,
        所以,REFRESH = 0x008c0000 + R_CNT = 0x008c04f4。
        在连接脚本timer.lds中,全部代码的起始运行地址都被设为0x3000 0000,但是在执行
    memsetup函数时,代码还在内部SRAM(steppingston)中,为了能在此处运行这个函数,
    它应该是位置无关的。
    3.初始化定时器0
        timer0_init函数用于初始化定时器0,根据相关寄存器的格式并参考代码中的注释就可
    以理解这个函数,代码如下:
     1 行号
     2 124行/*
     3 125行*Timer input clock Frequency = PCLK / (prescaler value + 1) / (divider value)
     4 126行*(prescaler value) = 0~255
     5 127行*(divider value) = 2、4、8、16
     6 128行*本实验的Timer0的时钟频率 = 100MHz/(99 + 1)/(16) = 62500Hz
     7 129行*设置Timer0 0.5s触发一次中断
     8 130行*/
     9 131行void timer0_init(void)
    10 132行{
    11 133行    TCFG0    = 99;        //预分频器 0 = 99
    12 134行    TCFG1    = 0x03;      //选择16分频
    13 135行    TCNTB0   = 31250;     //0.5s触发一次中断
    14 136行    TCON    |= (1 << 1);  //手动更新
    15 137行    TCON     = 0x09;      //自动加载,清除“手动更新”位,启动定时器0
    16 138行}
    17 139行
    init.c->timer0_init()
    4.定时器中断
        head.S中调用timer0_init函数之后,定时器开始工作;调用init_irq函数使能定时器0
    中断、设置CPSR寄存器开启IRQ中断后,每当定时器0计数达到0时,就会触发中断。
    init_irq函数很简单,在init.c中,代码如下:
    行号
    140行/*
    141行*定时器0中断使能
    142行*/
    143行void init_irq(void)
    144行{
    145行    //定时器0中断使能
    146行    INTMSK &= (~(1 << 10));
    147行}
    init.c->init_irq()
        发生定时器中断时,CPU将调用其中断服务程序Timer0_Handler,它在interrupt.c中:
     1 行号
     2 03行void Timer0_Handler(void)
     3 04行{
     4 05行    /*
     5 06行    *每次中断令3个LED改变状态
     6 07行    */
     7 08行    if(INTOFFSET == 10)
     8 09行    {
     9 10行        GPFDAT = ~(GPFDAT & (0x7 << 4));
    10 11行    }
    11 12行    //清除中断
    12 13行    SRCPND = 1 << INTOFFSET;
    13 14行    INTPND = INTPND;
    14 15行}
    interrupt.c
        定时器0的中断使用SRCPND、INTPND寄存器中的位10来表示。中断服务程序
    Timer0_Handler先判断是否定时器0的中断,若是则反转3个LED的状态。
    10.2.3 实例测试
        在timer目录下执行make命令生成timer.bin可执行程序,烧入NAND Flash中执行,
    即可看到3个LED每1s闪烁一次。
        将head.S中对clock_init函数的调用去掉,不启动MPLL;并随之将init.c中的
    memsetup函数的REFRESH寄存器改为12MHz对应的0x008c 07a3.重新编译、烧写,
    可以看到差不多8sLED闪烁一次。
    附:代码:
    链接: https://pan.baidu.com/s/1kV24a9L 密码: tfab
  • 相关阅读:
    Trie树详解及其应用
    最长回文字符串_Manacher算法_(O(n))
    设置VisualStudio以管理员身份运行
    wcf使用JetEntityFrameworkProvider.dll写access数据库时,报"操作必须使用一个可更新的查询"错误的解决办法
    data:image字符转byte[]
    ID为XXXX的进程当前未运行
    在Windows2003 server 64位系统上使用ArcEngine开发的WCF服务
    关于position的relative和absolute分别是相对于谁进行定位的
    sql语句进行写数据库时,字符串含有'的处理方式
    EF中关于日期字值的处理
  • 原文地址:https://www.cnblogs.com/sz189981/p/7712295.html
Copyright © 2020-2023  润新知