在嵌入式系统中经常会用到定时器和实时时钟的功能,这里首先介绍定时器和实时时钟的概念。
(1) 定时器是指82C54这样的可编程Timer,通常具有固定频率的输入脉冲,Timer对该脉冲记数,当记数到所设置的值后,定时器可触发CPU生产中断,因而定时器一般用于产生1ms-50ms的事件,来驱动相应的应用程序执行某个动作。
(2) 实时时钟,也简称为RTC,是一个独立的电路单元,可提供精确的日期和时间参数,通常包括“年-月-日”,“时:分:秒”。RTC一般带有后备电池,通过扩展总线或SPI接口与系统相连。
与标准的PC一样,英创嵌入式网络模块带有一个每秒中断18.2次的系统定时器Timer0,每次中断间隔时间约为55ms,操作系统中和时间相关的功能都将和系统定时器有关。此外模块中还为用户保留了一个16-bit可编程定时器Timer1,其输入频率通常为1MHz,特别适合用做ms级的定时器。目前英创模块中一部分型号还同时支持RTC,如NetBox-II、ETR100E等,图1指出ETR100E上的RTC。

图1 ETR100E上的RTC
对还没有在模块上支持RTC的型号,ETR186和ETR232i,都在其评估地板上扩展有RTC单元,如图2所示。用户可参考评估板电路图,为自己的应用底板扩展RTC单元。

图2 ETR232i评估地板上扩展的RTC
由于操作系统提供的日期和时间都是根据系统定时器生成的,所以为了让系统的时间与当前真实时间一致,就需要用RTC的时间去同步系统的时间。对在模块上已带了RTC单元的型号,如NetBox-II、ETR100E等,BIOS在上电初始化期间已进行了RTC对系统时钟的同步操作。目前对ETR186T和ETR232i,由于不确定最终系统是否有RTC,所以BIOS没有做RTC的同步操作。用户应用程序可以很方便的实现同步的操作:
#include
#include “ds1302.h”
……
struct time t;
struct date d;
……
RTC.getdate( &d ); // 取RTC的日期参数
RTC.gettime( &t ); // 取RTC的时间参数
t.ti_hund = 0; // 设置百分秒为0
setdate( &d ); // 设置系统的日期
settime( &t ); // 设置系统的时间
注意,进行RTC对系统时钟的同步操作之前,RTC本身应当被正确的初始化,并设置有效的当前时间。
以下对定时器和实时时钟的常用方式作简单介绍。
(1) 构造1ms – 50ms的定时任务发生器
通过启动定时器Timer1,并安装中断程序响应Timer1记数结束事件。Timer1的输入频率为1MHz,即时间分辨率为1us,若设置Timer1的分频值为1000,则定时中断间隔1ms,若设置为10000,则定时中断间隔为10ms。作为定时任务发生器,中断服务程序只需设置一个全局变量标志,而上层的应用程序则检查该全局变量标志,当标志被设置时就执行相应的定时任务并清除该标志。
不建议设置小于1ms的定时间隔,因为这样会占用过多CPU开销。
有关Timer1定时中断程序的安装及应用,请在BC的IDE环境中打开光盘中software\drivers目录下的tmrdemo.prj,参阅相关的代码。
(2) 构造几百毫秒至一两秒的定时任务发生器
对构造这个时间量级的定时任务发生器,最好是加载软中断int 0x1C,每次系统定时中断将自动调用软中断int 0x1C,所以int 0x1C的定时间隔就是55ms(精确值为54.925ms)。在中断程序中可通过对一全局变量计数,来表示时间间隔,而上层应用程序则判断该全局变量计数值来决定是否执行定时任务,如设置计数阈值为18,表示1s时间间隔,注意当开始执行定时任务时,需把全局变量计数值清零。
BC集成开发环境的在线帮助中有关于如何加载int 0x1C的代码例程,可搜索关键词getvect或setvect,以打开相关的在线帮助窗口。
(3) 整点时间操作
这里所谓的整点时间操作是指每10秒、每5分钟、每小时等等这样的定时任务,可以通过简单的获取系统时间参数来判断是否执行定时任务。如每10秒进行定时操作:
struct time t;
unsigned char lastsecond;
// 初始化相关变量
gettime( &t );
lastsecond = t.ti_sec;
……
//定时任务判断
gettime( &t );
if( (t.ti_sec != lastsecond) && ( (t.ti_sec%10) == 0) )
{
lastsecond = t.ti_sec;
//执行定时任务
……
}
注意由于定时操作的判断是使用了DOS的系统调用,因此不能把上述代码放在硬件中断服务程序中,以避免中断重入而引起系统crush。