引言
在嵌入式系统的设计中,时间特性在很多应用中都是一个很重要的参数,很多控制逻辑和协议的实现,需要用到计时、超时和统计功能,时间长短的计时及精度、周期定时和超时的设定成为某些控制逻辑和协议的关键条件。笔者在为某车型设计其车身中央控制器时,输入条件的判定、输出逻辑的控制和LIN协议的实现都包含一些时间特性相关的功能[1],为此笔者设计了一个高效的定时器模块。下面就从设计原理、结构设计、定时器管理及应用上讲述下该定时器模块的设计与实现。
设计原理
在车身控制器(以下以BCM来代称)的设计中,其定时功能可以分为两类,一个是TIMEOUT超时,一个是COUNTER计时,前者超时时间已知,超时则触发相关操作,后者是计时请求,在一种状态发生变化时统计新的状态的维持时间。通过对BCM定时功能及特性进行分析,发现其定时应用的特点为:
1、定时精度要求不高,但定时数量比较多;
2、TIMEOUT超时中,有循环定时,也有单次定时和多次定时。
由于硬件资源有限,不可能为每一个定时应用单独分配一个硬件定时器,所以采用单个硬件定时器模拟多个软件定时器的方法,来满足应用中的定时需要[2]。首先根据定时应用的特点和分类,设计软件定时器的数据结构,以结构体数组的形式将这些软件定时器组织起来,数组成员便是各个软件定时器节点;然后在驱动程序的设计中设计良好的API接口,该API接口足够清晰,提供统一的调用接口,可以涵盖定时器所有功能,这样便在一个硬件定时器上实现了多个软件定时器。下面便详述一下定时器结构和驱动程序设计。
定时器结构设计
由于软件定时器数量繁多,根据定时应用的实现方式及特征设计一个良好的数据结构是非常必要的[3]。在其结构体设计中,其成员变量可以描述所有的“定时特征”并且提供良好的可读写接口。其定义如下所示:
typedef struct {
TimerState timer_state;
ulong timeout;
ulong duration;
unsigned cycle:1;
unsigned overow_ag:1;
unsigned cnt_times:8;
TimerId timer_id;
}Timer;
timer_state有STALL和RUNNING两种取值,表示该定时器是否处于运转状态;timeout是超时应用的超时值,duration表示定时器启动以来的计时时间长度;cycle表示该定时是周期定时还是单次定时;cnt_times是对多次定时(既非周期也非单次)的统计,当定时次数到达后,则停止该定时器;overow_ag标识是否发生超时溢出;所有的“软件定时器节点”组成Timer数组,其数据成员由timer_id枚举,其枚举类型TimerId定义如下所示:
typedef enum{
FEEDWATCHDOG_300MS,
INPUT_DETECT_MS,
LIN_TIMESLOT_MS,
SPEED_PULSE_ACCUMU_200MS,
ALLDR_LOCK_200MS,
ALARM_30S,
IGNITION_OFF_60S,
。..
。..
MAX_TIMER_NUM
}TimerId;
这样,定义数组Timer TIMER[MAX_TIMER_NUM],在初始化中,TIMER [i].timer_id=i;之后通过TIMER[timer_id]即可枚举某软件定时器节点。
引言
在嵌入式系统的设计中,时间特性在很多应用中都是一个很重要的参数,很多控制逻辑和协议的实现,需要用到计时、超时和统计功能,时间长短的计时及精度、周期定时和超时的设定成为某些控制逻辑和协议的关键条件。笔者在为某车型设计其车身中央控制器时,输入条件的判定、输出逻辑的控制和LIN协议的实现都包含一些时间特性相关的功能[1],为此笔者设计了一个高效的定时器模块。下面就从设计原理、结构设计、定时器管理及应用上讲述下该定时器模块的设计与实现。
设计原理
在车身控制器(以下以BCM来代称)的设计中,其定时功能可以分为两类,一个是TIMEOUT超时,一个是COUNTER计时,前者超时时间已知,超时则触发相关操作,后者是计时请求,在一种状态发生变化时统计新的状态的维持时间。通过对BCM定时功能及特性进行分析,发现其定时应用的特点为:
1、定时精度要求不高,但定时数量比较多;
2、TIMEOUT超时中,有循环定时,也有单次定时和多次定时。
由于硬件资源有限,不可能为每一个定时应用单独分配一个硬件定时器,所以采用单个硬件定时器模拟多个软件定时器的方法,来满足应用中的定时需要[2]。首先根据定时应用的特点和分类,设计软件定时器的数据结构,以结构体数组的形式将这些软件定时器组织起来,数组成员便是各个软件定时器节点;然后在驱动程序的设计中设计良好的API接口,该API接口足够清晰,提供统一的调用接口,可以涵盖定时器所有功能,这样便在一个硬件定时器上实现了多个软件定时器。下面便详述一下定时器结构和驱动程序设计。
定时器结构设计
由于软件定时器数量繁多,根据定时应用的实现方式及特征设计一个良好的数据结构是非常必要的[3]。在其结构体设计中,其成员变量可以描述所有的“定时特征”并且提供良好的可读写接口。其定义如下所示:
typedef struct {
TimerState timer_state;
ulong timeout;
ulong duration;
unsigned cycle:1;
unsigned overow_ag:1;
unsigned cnt_times:8;
TimerId timer_id;
}Timer;
timer_state有STALL和RUNNING两种取值,表示该定时器是否处于运转状态;timeout是超时应用的超时值,duration表示定时器启动以来的计时时间长度;cycle表示该定时是周期定时还是单次定时;cnt_times是对多次定时(既非周期也非单次)的统计,当定时次数到达后,则停止该定时器;overow_ag标识是否发生超时溢出;所有的“软件定时器节点”组成Timer数组,其数据成员由timer_id枚举,其枚举类型TimerId定义如下所示:
typedef enum{
FEEDWATCHDOG_300MS,
INPUT_DETECT_MS,
LIN_TIMESLOT_MS,
SPEED_PULSE_ACCUMU_200MS,
ALLDR_LOCK_200MS,
ALARM_30S,
IGNITION_OFF_60S,
。..
。..
MAX_TIMER_NUM
}TimerId;
这样,定义数组Timer TIMER[MAX_TIMER_NUM],在初始化中,TIMER [i].timer_id=i;之后通过TIMER[timer_id]即可枚举某软件定时器节点。
定时器管理
定时器管理包括启动、运行、停止、重启和读取计时时间等功能[4],相应API函数定义如下:
void TimerStart(TimerId timer_id,ulong timeout,Bool cycle,uchar cnt_times);
void TimerReStart(TimerId timer_id);
void TimerTick(void);
void TimerStop(TimerId timer_id);
void TimerStall(TimerId timer_id);
ulong TimerGet(TimerId timer_id);
启动定时器函数如下:
#dene RTIPERIOD 2
void TimerStart(TimerId timer_id,ulong timeout,Bool cycle,uchar cnt_times)
{
TIMER[timer_id].timer_state=RUNNING;
TIMER[timer_id].duration=0;
TIMER[timer_id].timeout=timeout/RTIPERIOD;
TIMER[timer_id].cycle=cycle;
TIMER[timer_id].cnt_times=cnt_times;
TIMER[timer_id].timer_id=timer_id;
}
可见,启动某个软件定时器便是设置由timer_id枚举的TIMER数组成员的各个成员变量,下面详细介绍软件定时器的运行。
作为所有软件定时器的基准源,硬件定时器设定为2ms的周期定时,在时钟中断服务程序中全局时钟嘀嗒Jiffs累加,TimerTicked置1,软件定时器运行函数如下:
void TimerTick(void)
{
uint timer_index;
if(0==TimerTicked)
{
return ;
}
for(timer_index=0;timer_index
{
if(RUNNING==TIMER[timer_index].timer_state)
{
TIMER[timer_index].duration++;
if(TIMER[timer_index].duration》=TIMER[timer_index].timeout)
{
TIMER[timer_index].overflow_ag=1;
if(TIMER[timer_index].cycle)
{
TimerReStart(timer_index);
}
else
{
TIMER[timer_index].cnt_times--;
if(0==TIMER[timer_index].cnt_times)
{
TimerStall(timer_index);
}
else
{
TimerReStart(timer_index);
}}}}}
TimerTicked=0;
}
软件定时器只有在有嘀嗒发生且自身状态为RUNNING的情况下才会运行,其计时时间-duration随嘀嗒而累加,如果duration达到超时值,则置overow_ag,然后判断该定时器是否周期定时器,是则重启定时-清零duration,否则判断是否多次定时,定时次数计数未满则重启定时,计数满后停止该定时器。
定时器应用
定时器模块在软件系统中是一个基本功能单元,它为其他模块或上层应用提供超时、计时服务[5]。以门锁的驱动为例讲述下定时器的应用。
在门锁闭锁时,BCM对门锁电机的闭锁驱动控制需要保持200ms的时间,在逻辑上便是输出一个200ms的控制脉冲,该部分代码如下:
。..
DrivePort(ALLDR_LK_OUT,DRIVEON);
TimerStart(ALLDR_LOCK_200MS,200,0,1);
if(1==TM[ALLDR_LOCK_200MS].overow_ag)
{
DrivePort(ALLDR_LK_OUT,DRIVEOFF);
TM[ALLDR_LOCK_200MS].overow_ag=0;
}
。..
可见,在设计良好的定时器驱动以后,实现某种定时应用非常简单,其他应用示例在此不再赘述。
结语
本文设计的定时器模块,逻辑清晰,使用方便,做为一个稳定的底层驱动,在实际应用中得到了很好的应用。