1 前言
对于反复出现的事件的处理,例如周期事件,或处理在特定时间点触发的事件,需要OS提供一种定时机制。对此,OSEK提供了一个两阶段的概念来实现,周期性事件被同步注册到一个计数器(counter),在此基础上OS向应用提供定时器机制(alarm mechanisms)。
2 计数器(Counters)
OSEK会提供至少一个计数器,该计数器可以由硬件计时器或软件计时器触发。计数器的分辨率通常称为tick,表示计数器单位变化对应的时间;OSEK并没有位计数器提供标准的API接口,但OSEK需要负责相关alarm的处理机制。
3 alarm管理
系统生成时,会为定时器(alarm)预定义一个溢出值,该值可以是关于某一真实计数值的一个相对值(相对定时器,relative alarm),也可以直接取一个绝对值(绝对定时器,absolute alarm)。举例来说,可以设置定时器相对于当前时间四个后溢出(闹钟铃响),这就是相对定时器;也可以设置定时器到每天早上七点溢出,这就是绝对定时器。
具体来说,在系统生成时,会对定时器进行静态配置:
• 将定时器与一个计数器绑定,即将alarm与counter关联起来;
• 将定时器与一个任务或定时器回调函数绑定起来,这主要取决于该定时器的作用。
也就是说,计数器和定时器(Counters and alarms)都是静态配置绑定的(如图1所示),且定时器溢出后所需的处理也是通过静态配置来定义的。
图1 计数器和定时器静态配置关联示意图
OSEK需要提供一种服务,即当定时器(alarm)溢出时,可以设置去激活一个任务,或设置发起一个时间,亦或者调用执行定时器回调函数(alarm callback routine)。其中,定时器回调函数是由应用层软件注册的一个较短(执行快)的函数,在该回调函数中可以调用的系统接口如图2所示,即只可以调用SuspendAllInterrupts和ResumeAllInterrupts,且当其执行时,需要关闭二类中断。此外,OS还应提供接口以取消定时器或获取某一定时器的当前状态。
图2 系统接口调用限制规范
根据定时器溢出后是否复位重复运行,可以将定时器分为单次定时器(single alarms)和周期定时器(cyclic alarms)。
4 定时器回调函数(Alarm-callback routines)
定时器回调函数有点类似ISR,既不可以有入参,也不可以有返回值。
/* Declare the callback function. */
#define ALARMCALLBACK(name) void name(void)
/* Example for an alarm-callback routine: */
ALARMCALLBACK(BrakePedalStroke)
{
/* do application processing */
}
关于定时器回调函数的处理级别(processing level),一般主要为调度器或ISR所用,这取决于具体的实现方式。
5 定时器相关系统API
/*
Parameter (In): AlarmID Reference to alarm
Parameter (Out): Info Reference to structure with constants of the alarm base
*/
StatusType GetAlarmBase ( AlarmType <AlarmID>, AlarmBaseRefType <Info> )
/*
Parameter (In): AlarmID Reference to an alarm
Parameter (Out): Tick Relative value in ticks before the alarm <AlarmID> expires.
It is up to the application to decide whether for example a CancelAlarm may still be useful
*/
StatusType GetAlarm ( AlarmType <AlarmID>, TickRefType <Tick>)
/*
Parameter (In): AlarmID: Reference to the alarm element
increment:Relative value in ticks
cycle: Cycle value in case of cyclic alarm. In case of single alarms, cycle shall be zero.
Parameter (Out): none
The system service occupies the alarm <AlarmID> element. After <increment> ticks have elapsed, the task assigned to the alarm <AlarmID> is activated or the assigned event (only for extended tasks) is set or the alarm-callback routine is called.
The behaviour of <increment> equal to 0 is up to the implementation.
If the relative value <increment> is very small, the alarm may expire, and the task may become ready or the alarm-callback may be called before the system service returns to the user.
If <cycle> is unequal zero, the alarm element is logged on again immediately after expiry with the relative value <cycle>.
The alarm <AlarmID> must not already be in use.
To change values of alarms already in use the alarm shall be cancelled first.
If the alarm is already in use, this call will be ignored and the error E_OS_STATE is returned
*/
StatusType SetRelAlarm ( AlarmType <AlarmID>, TickType <increment>, TickType <cycle> )
5.1 相对定时器设置
显然,alarm的使用与freeRTOS中的软件定时器的使用极为相似,甚至可以说是如出一辙。其中,SetRelAlarm 用于设置并启动相对定时器,主要有以下几个注意点:
• 当<AlarmID>对应的定时器启动后,为系统服务所占有;这也就意味着,在调用SetRelAlarm时,必须保证该定时器并未在使用中;此外,如果想要更改正在使用中的定时器,则必须先取消该定时器,然后再更改;否则,此次更改会被忽略,同时返回错误E_OS_STATE;
• 若入参<increment>为0,其行为取决于具体的系统实现;• 若入参<increment>为一个很小的值,则可能会使得在SetRelAlarm返回应用层前,就发生了定时器溢出,或者导致一个任务被唤醒进入就绪态,亦或者对应的定时器回调函数会直接被调用;
• 如果<cycle>为非零值,则当定时器溢出后,会即刻以同样的<increment>重载该定时器。
5.2 绝对定时器设置
/*
Parameter (In): AlarmID Reference to the alarm element start Absolute value in ticks
cycle Cycle value in case of cyclic alarm. In case of single alarms, cycle shall be zero.
Parameter (Out): none
*/
StatusType SetAbsAlarm ( AlarmType <AlarmID>, TickType <start>, TickType <cycle> )
关于绝对定时器的设置,主要有以下几点需要注意:
• 当<AlarmID>对应的定时器启动后,为系统服务所占有;这也就意味着,在调用SetAbsAlarm时,必须保证该定时器并未在使用中;此外,如果想要更改正在使用中的定时器,则必须先取消该定时器,然后再更改;否则,此次更改会被忽略,同时返回错误E_OS_STATE,这一点与相对定时器相同;
• 入参<start>是一个绝对值,如果在调用SetRelAlarm,前就已经溢出了,则应等待counter计数到下一个循环,并在此到达<start>时,才可以触发定时器溢出,这一点与freeRTOS的处理完全不同;举例来说,14点钟设置13点半的闹钟,则只能等到第二天下午两点,闹钟才会响了;• 若入参<start>非常接近当前的计数值(counter value),则可能会使得在SetRelAlarm返回应用层前,就发生了定时器溢出,或者导致一个任务被唤醒进入就绪态,亦或者对应的定时器回调函数会直接被调用;
• 如果<cycle>为非零值,则当定时器溢出后,会即刻以同样的<increment>重载该定时器。
5.3 其余API
/*
Parameter (In): AlarmID Reference to alarm
Parameter (Out): Info Reference to structure with constants of the alarm base
*/
StatusType GetAlarmBase ( AlarmType <AlarmID>, AlarmBaseRefType <Info> )
/*
Parameter (In): AlarmID Reference to an alarm
Parameter (Out): Tick Relative value in ticks before the alarm <AlarmID> expires.
It is up to the application to decide whether for example a CancelAlarm may still be useful
*/
StatusType GetAlarm ( AlarmType <AlarmID>, TickRefType <Tick>)
/*
Parameter (In): AlarmID Reference to an alarm
Parameter (Out): none
*/
StatusType CancelAlarm ( AlarmType <AlarmID> )
FYI.