"); //-->
By 下家山 Q群 75303301 上海松江文汇路928号258室 松江大学城
上海索漫科技 http://www.xiajiashan.com 专注嵌入式(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启动系统时钟代码流程通过一层层的调用,系统时钟已布置好了。但,至此,仅仅是理清了整个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)时才有用。
By 下家山 Q群 75303301 上海松江文汇路928号258室 松江大学城
上海索漫科技 http://www.xiajiashan.com 专注嵌入式(ARM7,Cortex-M0,Cortex-M3,ARM9,linux)培训转载:请注明,作者,下家山 请尊重原创!
专栏文章内容及配图由作者撰写发布,仅供工程师学习之用,如有侵权或者其他违规问题,请联系本站处理。 联系我们
相关推荐
高精度50Hz时基信号源(CD4060)
Primech AI与香港华懋物业服务有限公司签署谅解备忘录
三菱FX系列PLC教程 50 —— FX系列的二进制平方根指令
MICROCHIP的能用于ICD2的PIC18F452的DEMO板原理图下载
三菱FX系列PLC教程 47 —— FX系列的数据处理指令
【嵌入式TCP/IP协议研究与实现】
三菱FX系列PLC教程 48 —— FX系列的先入先出读出指令(FIFO)
LPC2106 arm开发板原理图
TDK在面向高功率密度应用的直流-直流转换器模块中引入全遥测技术
Logic
罗德与施瓦茨和u-blox合作验证符合中国GNSS新国标的车载导航模块
三菱FX系列PLC教程 46 —— FX系列的字左移指令
【一种实时嵌入式操作系统内核DeltaCORE的设计与实现】
数字正弦波发生器电路
实现物流和零售自动化——第1部分
LED交通信号灯系统的硬件设计
【一种嵌入式文件系统的设计与实现】
Vishay推出新型Cyllene 2 IC以升级红外遥控应用的VSOP383xx系列前置放大电路
FERNRIDE携手QNX,打造符合功能安全认证的自动驾驶码头牵引车解决方案
基于NXP S32K312 MCU和MC33772C AFE汽车12V BMS应用方案
罗德与施瓦茨和高通合作激发拟议的FR3频段潜力
用CD4046组成的调频信号发生器
1Hz时钟信号源电路(CD4060、CD4027)
JTAG ICE 原理图
输出稳定的正弦波信号源电路
三菱FX系列PLC教程 49 —— FX系列的先入先出写入指令(FIFO)
【嵌入式应用交叉调试器的设计与实现】
【嵌入式实时多任务微内核核心研究】
MemryX AI 加速卡结合 Rockchip RK3588 多路物体检测解决方案
罗德与施瓦茨和高通合作验证了机器学习增强的信道状态信息反馈技术