深入时钟与定时器

时间:2021-10-22 21:34:55
  • 前言
    这篇主要介绍arm9时钟与定时器方面,根据s3c2440手册第七章的内容来看,涉及到不少的知识点,power管理、时钟、usb时钟、camera等,接下来只介绍时钟方面,其余部分以后再说
  • 简介
    系统时钟是整个电路的心脏,了解系统时钟结构对于后面学习定时器,UART等使用具有非常重要的作用,总体来说,与s3c2440处理器有关的时钟主要有4种: FIN, FCLK, HCLK和PCLK
    1.FIN:外部输入晶振频率
    2.FCLK: 主要用于CPU核
    3.HCLK:主要用于与AHB总线互连的设备(如存储控制器, LCD控制器, 中断控制器及DMA等)上
    4.PCLK:主要用于与APB总线互连的低速设备(如定时器, UART, ADB等)上
  • 图解时钟
    s3c2440处理器系统时钟分为两个部分, 外部有时钟输入引脚,内部用2个锁相环将外部输入时钟倍频到处理器工作说需要的时钟, 外部时钟频率太高容易受到外部的干扰,因此一般外部时钟频率比较低, 如下图:
    深入时钟与定时器
    上图*有三部分需要讲解:
    1.时钟源选择
    在第七章的开始部分,就介绍了2440选择时钟的配置,主要根据CPU的OM2,OM3输入引脚的电平决定:
    深入时钟与定时器
    在一般的开发板上,主时钟和USB时钟全部是由外部的12M晶体振荡器提供的。所以在定制硬件的时候,直接将OM3和OM2接于地上,以选择外部的晶振作为主时钟源和USB时钟源。
    2.锁相环MPLL+分频比
    锁相环可以理解为时钟频率放大电路,涉及到两个寄存器
    ■MPLLCON: 控制FCLK与FIN的比例关系
    深入时钟与定时器
    MPLLCON与UPLLCON寄存器是一样的,统称为PLLCON寄存器,那么,如何计算FCLK呢,根据手册可知:
    深入时钟与定时器
    上图中的Mpll即我们想要的FCLK,即MPLL(FCLK) = (2 * 100 * 12)/(6 * 2^1), 因为我们想把FCLK(Mpll)设置为200MHZ,经过计算可以得到, MDIV=92, PDIV=4, SDIV=1。
    ■CLKDIVN:用于控制FCLK, HCLK和PCLK之间的比例关系
    深入时钟与定时器
    即 CLKDIVN = 0x03; // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1
    注意,手册上有这么一句话:
    深入时钟与定时器
    如果HDIVN为1的话,需要更改总线的模式,因为如果HDIVN为1,但是CPU bus mode为fast bus mode,那么它的频率将变为HCLK即100MHZ的频率(200MHz->100MHz,搞成节能了)
    所以时钟初始化如下:

    // LOCKTIME = 0x00ffffff; // 使用默认值即可,在下面会讨论
    CLKDIVN = 0x03; // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1

    /* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */
    __asm__(
    "mrc p15, 0, r1, c1, c0, 0\n" /* 读出控制寄存器 */
    "orr r1, r1, #0xc0000000\n" /* 设置为“asynchronous bus mode” */
    "mcr p15, 0, r1, c1, c0, 0\n" /* 写入控制寄存器 */
    );
    MPLLCON = ((0x5c<<12)|(0x04<<4)|(0x01))

    上面代码注释中,关于LOCKTIME的赋值,这个又是怎么回事儿呢,还是根据手册
    深入时钟与定时器
    图中很清晰,刚开始上电, 在没有检测OM引脚电平之前,FCLK = 外部输入时钟OSC(FIN)
    之后arm核检测到OM引脚电平后,根据我们设定的寄存器MPLLCON的值MPLL锁相环启作用,一点点开始倍频,后经过Lock Time时间后倍频到我们想要的值,所以在这里我们默认值即可(0xFFFFFFFF)
    UCLK先不用考虑, USB1.0/2.0的正常工作频率为48MHZ
    3.UPLL锁相环
    将外部时钟倍频到USB设备正常工作所需要的时钟频率, 工作原理与上面类似
    以上,最终效果如下图:
    深入时钟与定时器
    以上关于2440时钟的配置就讲完了,接下来讲下定时器

  • 定时器
    由上面可知,定时器是ABP总线设备,在PCLK的时钟频率下工作,s3c2440有5个16位定时器,定时器0,1,2,3有脉冲调制(PWM)功能, 因此这4个定时器也被称为PWM定时器,定时器4是一个内部定时器,无外部输出引脚,定时器的时钟源虽然是PCLK,但是还得通过内部的两个分频器分频后, 才能得到想要的工作频率,然后输出作为定时器的工作的时钟,定时器0,1公用一个分频器,其他3个定时器公用另一个分频器,如下图
    深入时钟与定时器
    虽然定时器比较多,但是工作原理相同,只需要理解一个定时器工作原理即可, 对于某一个定时器,其内部原理如图:
    深入时钟与定时器
    ■TCON:用于控制定时器的开关等操作
    ■TCMPBn,TCNTBn:用于存储定时器的比较值与初始值(缓存)
    ■TCMPn, TCNTn:定时器内部寄存器,用户无需进行操作, 当TCON的“手动更新位置1”,即TCMPBn,TCNTBn的值就会加载到TCMPn, TCNTn
    ■TCNTOn:查看当前定时器的计数情况,即当前TCNTn的值
  • 定时器的工作原理描述,以定时器0为例:
    1.首先,将TCMPB0,TCNTB0附初值
    2.然后,设置定时控制器TCON,将TCON的第1位置1(手动更新位), 这样TCMPB0,TCNTB0赋值给TCMPB0,TCNTB0
    3.启动定时器–>设置TCON第3位为1(当TCNT0的值减到0时,TCMPB0的值会自动加载到寄存器TCMP0和TCNT0中), 第0位为1(开启定时器)
    4.此时,定时器会减1计数, 即TCNT0进行减1计数, 当TCMP0与TCNT0相等时, TOUT0翻转
    之后介绍一下相关的寄存器在手册中的定义:
    ■TCON:对于定时器0来讲:
    深入时钟与定时器
    其他的寄存器都是可以存储16位大小的值
  • 定时器的分频
    前面提到了定时器0的使用方法,但是没有提到其最终的工作所需要频率(1s中能记多少个数)
    深入时钟与定时器
    从上图中可以看到(以时钟0为例),定时器挂接如APB总线上,有两个分频器,一级分频器所对应的寄存器 TCFG0
    深入时钟与定时器
    由上图可知TCFG0不是一个定时器对应一个(不要误以为TCFG0对应定时器0),TCFG0控制2个一级的定时器分频器,其中的0-7位对应定时器0,1(与最上面定时器那个大图相吻合)的分频器,
    我们还可以获取信息,最终的定时器的频率 Frequency = PCLK /{prescaler value + 1} / { divider value}
    TCFG0的0-7位设置了prescaler value,即目前能得出f1 = PCLK /{prescaler value + 1}
    接下来是2级分频器,通过上图由TCFG1控制
    深入时钟与定时器
    对于上面的分频参数是为了下面更好的举例,当然不是绝对要设置成这个值
    例:我们想要定时器0的输入时钟为62.5kHz,即1s计数62500次
    因为PCLK=50MHz, 则50MHz/62.5KHz = 800,即需要进行800的分频
    所以一级分频系数为100,二级分频系数为8即可满足要求,
    即 62500 = 50MHz/{99 + 1} /{ 8 }
    所以如果想要定时器0,1s中产生一次中断,可以这么写

    void timer0_init(void)
    {
    TCFG0 = 99; // 预分频器0 = 99
    TCFG1 = 0x02; // 选择8分频
    TCNTB0 = 62500; // 1秒钟触发一次中断
    TCMPB0 = 0;
    TCON |= (1<<1); // 手动更新(将TCNTB0,TCMPB0更新入TCNT0, TCMP0)
    TCON = 0x09; // 自动加载,清“手动更新”位,启动定时器0
    }

  • PWM功能
    对于定时器0来讲,上面的例子中TCNT0 1s内减到0,则TOUT0反转(电平变化), 但是定时器0还有个特性,就是如果TCNT0减到与比较值TCMP0相等,则TOUT0也发生反转,也就是说上面的例子将 TCMPB0 改成31250后,则TCNT0从62500减到31250(与TCMP0相等)TOUT0发生一次反转(用时0.5s),TCNT0减到0又发生反转(用时0.5s),这样,1s内发生两次反转,如此循环,输出引脚0.5s反转一次,即占空比50%(波形0.5秒为高电平,0.5秒为低电平这样循环的波形)

    void timer0_init(void)
    {
    TCFG0 = 99; // 预分频器0 = 99
    TCFG1 = 0x02; // 选择8分频
    TCNTB0 = 62500; // 1秒钟触发一次中断
    TCMPB0 = 31250;
    TCON |= (1<<1); // 手动更新(将TCNTB0,TCMPB0更新入TCNT0, TCMP0)
    TCON = 0x09; // 自动加载,清“手动更新”位,启动定时器0
    }

    注意,如果将上面代码TCMPB0=31250,改为别的值,比如TCNTB0 /4,则输出的波形一定发生变化(比如0.25秒为高电平,0.75s为低电平这样循环的波形),即占空比也发生变化,这就是所谓的PWM功能