rtems系统时钟原理

 

一:从应用看原理

二:从RTEMS4.6.99.3-GP32说起

RTEMS4.6.99.3-GP32启动系统时钟代码流程

四:关于TICK

五:怎么实现年,月,日,时,分,秒

TICK的发生

七:定时器实现TICK计数

八:结束

 

一:从应用看原理

     每个系统都有一种时钟来支持,常常听到的某某系统支持“时分复用”(亦或叫“轮转调度”),TCP/IP中超时重传,我们每天用的手机能准确计算年,月,日,时,分,秒(手机中都带操作系统的,motorolaA1200就是带linux操作系统的),还有Linux中常看到的Jifferies。。。。,这些都是通过系统时钟来实现的。

二:从RTEMS4.6.99.3-GP32说起

当然,这种系统时钟是需要硬件支持,象rtems46.99.3中的GP32 bsp就是通过2400timer4来实现的。(注意,既然系统把timer4用来做系统时钟,timer4就不能用来做其他的定时操作了)

那么,rtems中是如何实现这种系统时钟的呢?下面,我以rtems4.6.99.3中的gp32为例来阐述这个问题:(注意,gp32是针对sumsung2400,我这里以sumsung2410讲解

RTEMS中如果要用到时钟,

      首先,需要在应用程序中定义系统配置文件。如,在GP32自带的例子ticker中,(../testsuites/samples/ticker/system.h)就有如下语句:

      #define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER

      系统就根据这条语句启动系统时钟。那么系统是怎么根据这条语句启动系统时钟的呢?请看其脉络:

三:RTEMS4.6.99.3-GP32启动系统时钟代码流程

 

   通过一层层的调用,系统时钟已布置好了。但,至此,仅仅是理清了整个rtems代码的组织方式。那么,rtems是怎样用2400timer4实现系统时钟的呢?

 

四:关于TICK

这就涉及到定时器了的原理了。

定时器是通过中断来实现的,在函数Clock_driver_support_initialize_hardware()中,通过设置2400timer4的相关寄存器。(因为,我在2410上调试的,所以函数Clock_driver_support_initialize_hardware()中我加了rTCFG0 = rTCFG0 & ~(0xffffff) | 0x000f00 ;)即可定义系统多长时间tick一次,也即rtems的时间精度(粒度)。其中,BSP_Configuration.microseconds_per_tick就是在应用程序中配置的值,单位是微秒。如果,希望系统的精度达到1毫秒,则应定义BSP_Configuration.microseconds_per_tick = 1000。在pppd例程中定义的是BSP_Configuration.microseconds_per_tick = 10000,即10ms。因此,可知系统每10ms发生一次tick。而在ticker例程中,并没有定义CONFIGURE_MICROSECONDS_PER_TICK,此时系统采用默认配置,其值为10ms。(参考confdefs.h)。

 五:怎么实现年,月,日,时,分,秒

我们每天用电脑,但想没想过电脑右下角的时钟是这么实现计时的呢?还有,我们每天用的手机其时间是这么实现的呢?下面,通过rtems中的ticker例程来剖析其中奥秘。

ticker例程的init.c中,定义了一个rtems_time_of_day类型的结构体time,并初始化为    time.year   = 1988;

  time.month  = 12;

  time.day    = 31;

  time.hour   = 9;

  time.minute = 0;

  time.second = 0;

  time.ticks  = 0;

接着,通过调用rtems_clock_set函数把time结构体传给内核,作为系统时钟的时间基准。内核中,根据先前定义的CONFIGURE_MICROSECONDS_PER_TICK换算成“每秒产生的tick数”进行计数。以CONFIGURE_MICROSECONDS_PER_TICK = 10000为例,则系统时钟“每秒产生的tick数”为100。因此,系统时钟每tick一次,time.ticks1,当达到100时向秒进位,即time.second1(同时,time.ticks复位到0)。Time.second计数到60时又向前进一,即time.minute 1(同时,time.second复位到0)。以此类推,直到time.year,这个就不用我多说了。

而,系统通过调用rtems_clock_get函数即可获得当前时间值。这样一个系统时钟就运行起来了。

 

集以上所说,其实还有一个根本性的问题,不知有人产生过疑问没有?

追本逐末,时钟的根源是tick,那么tick是这么产生的呢?

 

 六:TICK的发生

 

Tick是通过中断实现的。

回到上面的脉络图,在函数Install_clock中调用的Clock_driver_support_install_isr这个函数,就是注册时钟中断号。Clock_driver_support_install_isr这个函数调用了系统函数BSP_install_rtems_irq_handler,而BSP_install_rtems_irq_handler传给内核一个很重要的参数clock_isr_data,它是个结构体,内容如下:

rtems_irq_connect_data clock_isr_data = {BSP_INT_TIMER4,

                                         (rtems_irq_hdl)Clock_isr,

                                         clock_isr_on,

                                         clock_isr_off,

                                         clock_isr_is_on,

                                         3,     /* unused for ARM cpus */

                                         0 };   /* unused for ARM cpus */

这个结构体有两个很重要的成员,即BSP_INT_TIMER4Clock_isr。前者告诉BSP_install_rtems_irq_handler函数,要注册的中断源是timer4;后者是一个中断句柄(句柄就是函数的入口地址),用中断术语描述就叫ISRinterrupt service routine中断服务例程),当timer4中断发生时,即跳到Clock_isr执行。我们再来看Clock_isr里面的内容。

rtems_isr Clock_isr(rtems_vector_number vector)

{

  Clock_driver_ticks += 1;

  Clock_driver_support_at_tick();

  rtems_clock_tick();

}/*为了简洁起见,我去掉了注释和非执行语句*/

函数Clock_driver_support_at_tick();主要完成中断寄存器的复位清零动作,不然第二次中断就不能发生了。

全局变量Clock_driver_ticks就是我们要找的答案,看到没有,每发生一次中断,它的值加1

再来看函数rtems_clock_tick();其实这个函数才是真正的为系统时钟服务。跟进去发现它调用了_TOD_Tickle_ticks();函数,这个函数是个内敛函数位于cpukit/rtems/src/clocktick.c。它做了两件事情#define _TOD_Tickle_ticks() /

  _TOD_Current.ticks++; /

  _Watchdog_Ticks_since_boot++

注意了,这里也有个全局变量加1的动作,其实,这才是我们要找的真正的答案。系统时钟实现计时就是通过取当前tick_TOD_Current.ticks

这里又会产生一个疑惑,在发生中断时两个全局变量都做了加1的动作,那么time.ticks计数到100后向前进1而自己清零的动作在哪里呢?这个问题说来又话长了,我这里只告诉大家其代码在哪些位置,具体实现你们去琢磨。

   函数rtems_initialize_executive_early(cpukit/sapi/src/exinit.c)-à _TOD_Handler_initialization(cpukit/score/src/coretod.c)

 

七:定时器实现TICK计数

   RTEMS中时间的准确性依赖于tick的实现,而tick是依赖于定时器4timer4)的实现,timer4每发生一次中断tick实现加1的计数。Timer4是怎么实现定时器的中断的呢?

   回到上面的脉络图

   #define Clock_driver_support_initialize_hardware() /

  do { /

        uint32_t cr; /

        uint32_t freq,m,p,s; /

        /*set prescale to 15*/ /       

        rTCFG0 = rTCFG0 & ~(0xffffff) | 0x000f00 ; /

        /* set MUX for Timer4 to 1/16 */ /

        cr=rTCFG1 & 0xFFF0FFFF; /

        rTCFG1=(cr | (3<<16)); /

        freq = get_PCLK(); /

        /* set TIMER4 counter, input freq=PLCK/16/16Mhz*/ /

        freq = (freq /16)/16; /

        rTCNTB4 = ((freq / 1000) * BSP_Configuration.microseconds_per_tick) / 1000; /

        /*unmask TIMER4 irq*/ /

        rINTMSK&=~BIT_TIMER4; /

        /* start TIMER4 with autoreload */ /

        cr=rTCON & 0xFF8FFFFF; /

        rTCON=(cr|(0x6<<20)); /

        rTCON=(cr|(0x5<<20)); /

    } while (0)

   要弄懂定时器的工作原理,需要搞清楚三个量

   定时器的输入频率 freq

   定时器的缓冲计数器TCNTB内部有一个同名的递减计数器TCNT

   定时器的计数比较器TCMPB内部有一个同名的递减计数器TCMP,Timer4中没有这个寄存器

   定时器有个输入频率,这个输入频率的来源是PCLKPCLK的频率来自与板子上的晶振),通过rTCFG0rTCFG1两个寄存器即可设置PCLK的分频参数(针对s3c2410来说,PCLK50.7M,不能把这么高的频率输入给定时器,而要进行分频),之后即可得到定时器4的输入频率freq = PCLK/16/16。以PCLK= 50.7M记,freq = 198046HZ,即1秒发生198046次脉冲。

   定时器的计数器TCNT根据脉冲频率计数,每发生一次脉冲减1

   TCNT递减到0,则触发中断(TCNTTCNTB中装载初始值,开始下一轮计数)。

   定时公式:

   T  = { 1 / freq} * TCNTB

   如果BSP_Configuration.microseconds_per_tick = 10000,则刚好能实现每10ms发生一次中断(tick)。  

   注意:TCMPB在调制脉冲(PWM)时才有用

总结,在RTEMS中,tick是维系整个系统时间的根本,多任务的分时复用,各种超时动作,重传动作,都是通过tick来实现的。这里的tick就如Linux中的Jifferies

   至此,RTEMS系统时钟原理介绍完毕!

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值