原创 RTEMS系统时钟原理

2014-7-9 19:55 4150 29 30 分类: 消费电子

作者,下家山(转载请注明作者及出处,请尊重原创)

 

专注嵌入式(ARM7,Cortex-M0,Cortex-M3,ARM9,linux)培训 一:从应用看原理

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

二:从RTEMS4.6.99.3-GP32说起

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

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

RTEMS中如果要用到时钟,

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

      #define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER

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

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

 
2.jpg

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

四:关于TICK

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

定时器是通过中断来实现的,在函数Clock_driver_support_initialize_hardware()中,通过设置2400的timer4的相关寄存器。(因为,我在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)。

By 下家山 Q群 75303301        上海松江文汇路928号258室    松江大学城   

上海索漫科技  http://www.xiajiashan.com  专注嵌入式(ARM7,Cortex-M0,Cortex-M3,ARM9,linux)培训五:怎么实现年,月,日,时,分,秒

我们每天用电脑,但想没想过电脑右下角的时钟是这么实现计时的呢?还有,我们每天用的手机其时间是这么实现的呢?下面,通过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.ticks加1,当达到100时向秒进位,即time.second加1(同时,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_TIMER4和Clock_isr。前者告诉BSP_install_rtems_irq_handler函数,要注册的中断源是timer4;后者是一个中断句柄(句柄就是函数的入口地址),用中断术语描述就叫ISR(interrupt 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。

By 下家山 Q群 75303301        上海松江文汇路928号258室    松江大学城   

上海索漫科技  http://www.xiajiashan.com  专注嵌入式(ARM7,Cortex-M0,Cortex-M3,ARM9,linux)培训  再来看函数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是依赖于定时器4(timer4)的实现,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中没有这个寄存器)

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

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

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

   定时公式:

   T  = { 1 / freq} * TCNTB

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

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

 专注嵌入式(ARM7,Cortex-M0,Cortex-M3,ARM9,linux)培训转载:请注明,作者,下家山   请尊重原创!

 

 

arm

文章评论1条评论)

登录后参与讨论

roy1979.chou_250166599 2011-11-21 23:07

GOOD
相关推荐阅读
用户1037328 2016-04-17 17:59
好的理论可以避免犯错——读《丈量世界》有感!
     《丈量世界》,早就因为仰慕高斯而想看这本书      1.殊途同归      这本书写的是高斯和洪堡两个人,为什么要把这两个人放在一起写呢?因为高斯是一个宅男,不喜欢什么旅游,考察!他觉得...
用户1037328 2016-04-05 09:10
叛逆在爱因斯坦性格中的重要性(一)
叛逆在爱因斯坦性格中的重要性(一) 《爱因斯坦传》读书笔记      昨天晚上,读完了2016年书单的第二部书,《爱因斯坦传》。我想梳理一下自己读这本书的收获,或者说感悟!在读这...
用户1037328 2016-04-05 09:09
叛逆在爱因斯坦性格中的重要性(二)
叛逆在爱因斯坦性格中的重要性(二) 《爱因斯坦传》读书笔记                                四  叛逆性 在爱因斯坦的同时期科学家中,我想比他IQ...
用户1037328 2014-07-09 19:47
RTEMS在S3C2410上的移植(一)
作者:下家山(转载,请注明) 1:RTEMS开发环境建立 1.1开发环境建立前的准备工作 因为RTEMS开发环境主要用的是GNU的工具链,所以需要一台装有Linux的HOST. 1.2...
用户1037328 2014-07-09 19:45
深入浅出学Makefile<三,如何自动生成Makefile>
(请尊重原创,转载请注明,作者:下家山) 五: 运行make编译得到可执行文件 root@parson-desktop:/home/parson/tmp/automk# make gcc ...
我要评论
1
29
关闭 站长推荐上一条 /2 下一条