嵌入式系统设计与实践:Linux篇
上QQ阅读APP看书,第一时间看更新

4.3 系统定时器

S3C2410有5个16 bit定时器。定时器0—3有脉宽调制功能(Pulse Width Modulation, PWM),定时器4是内部定时器,没有输出引脚。定时器0有Dead-zone发生器,可以保证一对反向信号不会同时改变状态,常用于大电流设备中。

定时器0—1共用一个8位的时钟分频器(8 bit prescaler),定时器2—4共用另外一个。每个定时器有一个时钟分频器,可以选择5种分频方法。每个定时器从各自的时钟分频器获取时钟信号。prescaler是可编程的,并依据TCFG0-1寄存器数值对PCLK进行分频。

当定时器被使能之后,定时器计数缓冲寄存器(TCNTBn)中初始的数值就被加载到递减计数器中。定时器比较缓冲寄存器(TCMPBn)中的初始数值被加载到比较寄存器中,以备与递减计数器数值进行比较,这种双缓冲特点可以让定时器在频率和占空比变化时输出的信号更加稳定。

每个定时器都有一个各自时钟驱动的16 bit递减计数器,当计数器数值为0时,产生一个定时中断,同时TCNTBn中的数值被再次载入递减计数器中再次开始计数。只有关闭定时器才不会重载。TCMPBn中的数值用于PWM,当递减计数器的数值和比较寄存器数值一样时,定时器改变输出电平,因此,比较寄存器决定了PWM输出的开启和关闭。

1.定时器的工作过程

S3C2410的PWM定时器采用双buffer机制,可以在不停止当前定时器的情况下设置下一轮定时操作。定时器值可以写到定时器计数缓冲寄存器TCNTBn,而当前定时的计数值可以从定时器观察寄存器TCNTOn获得,即,从TCNTBn获得的不是当前数值而是下一次计数的初始值。

自动加载功能被打开后,当TCNTn数值递减到0时,芯片自动将TCNTBn的数值拷贝到TCNTn,从而开始下一次循环,若TCNTBn数值为0,则不会有递减操作,定时器停止。

第一次启动定时器的过程如下:

(1)初始化TCNTBn和比较缓冲寄存器TCMPBn的数值;

(2)设置定时器的人工加载位,不管是否使用极性转换功能,都将极性转换位打开;(3)设置定时器的启动位来启动定时器,定时器启动后,TCNTn开始计数作减计数,同时清除人工加载位。

(4)当TCNTn的数值和TCMPn一致时,TOUTn从低变为高,直至TCNTn计数至0,定时器产生中断请求。

若定时器在计数过程中被停止,则TCNTn保持计数值,若需要设置新的数值需要人工加载。定时器的工作过程可以用图4.5表示。

图4.5 定时器的工作过程

定时器的工作过程解释如下:

(1)使能自动加载功能,设置TCNTBn=160, TCMPBn=110,设置人工加载位并配置极性转换位,人工加载位将TCNTBn、TCMPBn的数值加载到TCNTn(160)、TCMPn(110)。然后,设置TCNTBn、TCMPBn为80和40,作为下一次定时的参数。

(2)设置启动位,若人工加载位为0,极性转换关闭,自动加载开启,则定时器开始递减计数。

(3)当TCNTn的数值和TCMPn一致时,TOUTn从低变为高。

(4)当TCNTn计数至0,定时器产生中断请求,同时TCNTBn、TCMPBn的数值被自动加载到TCNTn、TCMPn,前者为80,后者为40。

(5)中断服务向量(ISR)将TCNTBn、TCMPBn设置为80和60。

(6)与(3)相似。

(7)与4相似,TCNTn、TCMPn,前者为80,后者为60。

(8)ISR服务程序中,将自动加载和中断请求关闭。

(9)与(6)、(3)相似。

(10)TCNTn为0, TCNTn不会自动加载新的数值,定时器被关闭。

(11)没有新的中断发生。

同时,由上面的工作过程可以看出,通过ISR或别的方法写入不同的TCMPBn的数值,就可以调节输出信号的占空比,实现脉宽调制PWM。

2.定时器各寄存器配置

定时器输入时钟频率计算公式如下:

定时器输入时钟频率=MCLK/{预分频值+1}/{分割值}

{预分频值}=1~255

{分割值}=2,4,8,16,32

其中,MCLK为系统时钟,预分频值和分割值分别通过定时器配置寄存器TCFG0和TCFG1设置。

(1)定时器配置寄存器

在S3C2410中定时器配置寄存器有TCFG0和TCFG1,表4.10、表4.11分别表示定时器配置寄存器0(TCFG0)、1(TCFG1)各个位的含义及初始值的情况。

表4.10 定时器配置寄存器TCFG0的设置

表4.11 定时器配置寄存器TCFG1的设置

MUXn(n=0~5为分割值)可以取1/2、1/4、1/8、1/16

(2)定时器控制寄存器TCON

定时器控制寄存器TCON如表4.12所示。

表4.12 定时器控制器寄存器TCON

注意:TIMER0对应bit[3:0]:bit[3]用于确定在TCNT0计数到0时,是否自动将TCMPB0和TCNTB0寄存器的值装入TCMP0和TCNT0寄存器中,bit[2]用于确定TOUT0是否反转输出,bit[1]用于手动更新TCMP0和TCNT0寄存器:在第一次使用定时器前,此位需要设为1,此时TCMPB0和TCNTB0寄存器的值装入TCMP0和TCNT0寄存器中,bit[0]用于启动TIMER0。

(3)定时器计数缓冲寄存器与比较缓冲寄存器(TCNTBn/TCMPBn)

定时器计数缓冲寄存器包括TCNTB0—TCNTB5;而定时器比较缓冲寄存器包括TCMPB0—TCMPB4。定时器计数缓冲寄存器TCNTBn如表4.13所示,定时器比较缓冲寄存器TCMPBn如表4.14所示。

表4.13 定时器计数缓冲寄存器TCNTBn

表4.14 定时器比较缓冲寄存器TCMPBn

(4)定时器观察寄存器

定时器观察寄存器包括TCNTO0~TCNTO5,定时器观察缓冲寄存器TCNTOn如表4.15所示。

表4.15 定时器观察缓冲寄存器TCMPBn

例:在S3C2410中定时器配置寄存器TCFG0和TCFG1的值分别设为119和0x03,这个寄存器用于设置控制逻辑(Control Logic)的时钟,设外部时钟源的频率MCLK为12MHz,求时钟输出频率为多少。

分析:时钟输出频率计算公式如下:

      Timer input clock Frequency = MCLK / {prescaler value+1} / {divider value}

对于TIMER0,预分频值prescaler value = TCFG0[7:0]为119(0b1110111),分割值divider value由TCFG1[3:0]确定,本例中取值为0b011(0x03),因为取值0b000时,分割值为1/2;取值0b001时,分割值为1/4;取值0b010时,分割值为1/8;取值0b011时,分割值为1/16,所以:

时钟输出频率= 12MHz/(119+1)/(16) = 6250Hz

例:内核时钟FLCK(主频)、总线时钟HCLK、I/O接口时钟PCLK之间的关系通常设置为1:4:8的分频关系,如果说主频FLCK为400MHz,那么HLCK是100 MHz, PLCK是50 MHz。阅读下列程序回答以下问题。

      #definde rLOCKTIME   (*(volatile unsigned *)0x4c000000)
      #definde rMPLLCON   (*(volatile unsigned *)0x4c000004)
      #definde rUPLLCON   (*(volatile unsigned *)0x4c000008)
      #definde rCLKCON    (*(volatile unsigned *)0x4c00000c)
      #definde rCLKSLOW   (*(volatile unsigned *)0x4c000010)
      #definde rCLKDIVN   (*(volatile unsigned *)0x4c000014)
      void ChangeMPllValue(int mdiv, int pdiv, int sdiv)
      {
        rMPLLCON=(mdiv<<12)|(pdiv<<4)|sdiv;
      }
      void ChangeClockDivider(int hdivn, int pdivn)
      {
        rCLKDIVN=(hdivn<<1)|pdivn;
        if(hdivn)
          MMU_SetAsyncBusMode();
        else
          MMU_SetFastBusMode();
      }
      void ChangeUPllValue(int mdiv, int pdiv, int sdiv)
      {
        rUPLLCON=(mdiv<<12)|(pdiv<<4)|sdiv;
      }

如果在主程序中有如下引用:

      ChangeClockDivider(1,1);
      ChangeUPllValue(0xa1,0x3,0x1);

问:

(1)FCLK:HCLK:PCLK的比值是多少?

(2)FCLK为多少MHz?

3. S3C2410实时时钟RTC

在一个嵌入式系统中,实时时钟单元可以提供可靠的时钟,包括时分秒和年月日;即使在系统处于关机状态下,它也能正常工作(通常采用后备电池供电),它的外围也不需要太多的辅助电路,典型情况就是只需要一个高精度的晶振。S3C2410的实时时钟RTC具有下列特点:

● 时钟数据采用BCD编码;

● 能够对闰年的年月日进行自动处理;

● 具有告警功能,当系统处于关机状态时,能产生告警中断;

● 无2000 年问题;

● 具有独立的电源输入;

● 提供毫秒级时钟中断,该中断可用作嵌入式操作系统的内核时钟。

RTC器件是一种能够提供日历/时钟、数据存储等功能的专用集成电路。RTC发送8位BCD码数据到CPU,传送的数据包括秒、分、小时、星期、日期、月份与年份。

(1)S3C2410的实时时钟控制寄存器RTCCON

表4.16是S3C2410的实时时钟控制寄存器RTCCON各位的设置值与相应的功能。

表4.16 RTC时钟控制寄存器RTCCON

(2)RTC报警寄存器RTCALM

表4.17是RTC报警寄存器RTCALM中的各个位,取值可以是0或1, 0表示禁止报警,1表示报警使能。

表4.17 RTC报警寄存器RTCALM

0:禁止报警

1:报警使能

(3)RTC报警时间设置寄存器

RTC模块的寄存器组BCDSEC、BCDMIN、BCDDAY、BCDDATE、BCDMON、BCDYEAR分别用于存放秒、分、小时、日、月、年,如表4.18所示。

表4.18 时间设置寄存器

例:下列程序是S3C2410处理器的实时时钟(RTC)模块,根据语句RTCCON =0x01;请分析RTC的功能、初始年份为、在函数display外RTCCON的值为多少?

      void display()
      {
      int y, m, d, s, mi;
      RTCCON =0x01;   //报警使能
      while(1)
       {
      if(BCDYEAR==0x99)
        y=0x1999;
      else
        y=0x2000+BCDYEAR;
      m=BCDMON;
      d=BCDDAY;
      mi= BCDMIN;
      s=BCDSEC;
      if(s! =0)
        break;
      printf("time:%4x  / %02x /%02x\n", y, m, d);
      RTCCON =0x0;
       }
      }
      void main()
      {
      RTCCON  =0x01;
      BCDYEAR =0x08;
      BCDMON  =0x08;
      BCDDAY  = 0x08;
      BCDMIN  =0x08;
      BCDSEC  =0x59;
      RTCCON  =0x00;
      while(1)
       {
       display();
       delay(200);
       }
      }

分析:语句RTCCON =0x01;表示RTC使能、无复位、分频1/32768,初始年份为2008,在函数display外RTCCON的值为0x00。