RTOS示例
以下针对所有操作系统的示例在功能组织上是相同的。通用的“驱动程序”部分展示了数据库初始化(main() 函数)、实时事务的启动和提交(realtime_job() 函数)以及通过与操作系统无关的数据库 API 进行数据库访问的代码流程。系统相关的代码部分实现了定时器和回调功能。针对 FreeRTOS™、INTEGRITY OS™ 和 VxWorks™ 展示了基于定时器的系统相关方法,针对 AUTOSAR 架构和 Deos™ 展示了回调方法。源代码用 C 语言编写,为简洁起见省略了一些细节。
下面的代码展示了基于定时器实现的与操作系统无关的“公共部分”。
#ifndef MCO_REALTIME_TIMER_H
#define MCO_REALTIME_TIMER_H
#if defined(_LINUX) || defined (_MACOS) /* POSIX */
#include <sys/time.h>
typedef struct itimerval sys_timer_t;
#endif
void timer_handler (mco_db_h db);
void init_sys_timer();
void clear_sys_timer(sys_timer_t *timer);
void set_sys_timer(sys_timer_t *timer, mco_db_h db, mco_interval_t deadline);
#endif
int realtime_job(mco_db_h db, mco_interval_t deadline)
{
....
mco_trans_rt_params_t tx_params = MCO_TRANS_DEFAULT_PARAMS;
int n;
set_sys_timer(&timer, db, deadline/2);
/* Start the database transaction */
tx_params.deadline = deadline;
mco_trans_rt_start(db, MCO_READ_WRITE, MCO_TRANS_FOREGROUND, &tx_params, &t);
/*
* Continue until there is external data or the transaction is not aborted
* either due to timeout expiration or because of some other database error.
*/
for (n = 0; n < N_INSERTS && mco_get_last_error(t) == MCO_S_OK; ++n) {
.....
/* Extract data from some external source (device, network,...) and add to the
database */
receive_some_data(&id, &val);
rc = Measurement_new(t, &obj);
if (rc == MCO_S_OK) {
Measurement_id_put(&obj, id);
Measurement_val_put(&obj, val);
}
}
rc = mco_trans_commit(t);
clear_sys_timer(&timer);
return rc;
int main ()
{
.....
init_sys_timer();
realtime_job();
close_database();
return 0;
}
VxWorks™
以下代码展示了通过看门狗定时器设施实现的 VxWorks™。任何任务都可以创建一个看门狗定时器,并在指定延迟后使用它在系统时钟中断服务例程(ISR)的上下文中运行指定的例程。驱动看门狗定时器的硬件定时器以可编程速率生成中断,相关的中断服务例程会扫描每个定时器,递减与正在运行的定时器相关的计数器,并调用任何已发现超时的看门狗定时器所关联的超时例程。
#include "timer_implementation.h"
/* system timer operations */
#if defined(_VXWORKS)
#include "vxWorks.h"
#include "logLib.h"
#include "wdLib.h"
static WDOG_ID wdId;
void init_sys_timer() {
/* create a watchdog */
wdId = wdCreate( );
}
void set_sys_timer(sys_timer_t *timer, mco_db_h db, mco_interval_t deadline) {
/* setup a watchdog */
wdStart ( wdId, sysClkRateGet( ) * deadline , timer_handler, (int) db );
}
void clear_sys_timer(sys_timer_t *timer) {
/* remove the watchdog */
wdDelete( wdId );
}
#endif
FreeRTOS
FreeRTOS™ 中控制定时器的实现使用了软件定时器服务。有一个专用的“Tmr Svc”(定时器服务或守护进程)任务,它维护着一个有序的软件定时器列表,即将到期的定时器位于列表前端。定时器服务任务并非持续运行:从定时器列表中,该任务知晓每次定时器到期时它必须唤醒的时间。当定时器到期时,定时器服务任务会调用其回调函数(定时器回调函数)。FreeRTOS™ 软件定时器支持两种不同类型的软件定时器,这些类型在创建定时器时进行配置:一次性定时器,即定时器到期后不再重新启动;以及自动重载定时器,到期后会自动重新启动。示例代码运行一个一次性软件定时器来控制交易的截止时间。因此,FreeRTOS™ 本身必须配置为支持软件定时器(在 FreeRTOSConfig.h 中定义 configUSE_TIMERS 为 1),并且必须赋予其足够的优先级以及时处理事件。
#include <mco.h>
#include "common.h"
#include "timer_implementation.h"
/* system timer operations */
#if defined(_FREERTOS)
#include <FreeRTOS.h>
static mco_db_h myDB;
void TimerProc( TimerHandle_t xTimer ) {
timer_handler( myDB );
}
void init_sys_timer() {
}
void set_sys_timer(sys_timer_t *timer, mco_db_h db, mco_interval_t deadline)
{
/* Keep the reference to the current database connection */
myDB = db;
xTimerCreate( "eXdb-timer", pdMS_TO_TICKS(deadline), pdFALSE, TimerProc );
}
void clear_sys_timer(sys_timer_t *timer) {
}
#endif
INTEGRITY
下面的 INTEGRITY OS™ 实现使用 PrimaryClock 和 Alarm 来实现控制定时器。Clock 是一个允许访问硬件定时器的对象。Clock 可用于读取时间、设置时间或设置闹钟。每个 Clock 都有自己的闹钟。这允许在同一个硬件定时器上设置多个闹钟。实际上,内核将多个闹钟复用到硬件定时器的单个闹钟上。PrimaryClock 是每个板子上都有的标准滴答定时器。此定时器由调度器滴答驱动,通常以 60Hz 的频率滴答。Alarm 可以设置为在指定时间触发,或者在经过指定的时间间隔后触发。Alarm 可以设置为重复模式,在这种情况下,Alarm 触发后会再次触发。
在指定的时间间隔过后,系统会自动设置警报,而无需程序显式地设置新的警报。根据 INTEGRITY™ 的基于消息的架构,警报由内核构造为在其时钟对象上发送空消息。这使得警报既可以同步获取,也可以异步获取。通过调用 SynchronousReceive() 可以同步获取警报,在此过程中获取任务会阻塞,直到警报触发。通过调用 AsynchronousReceive() 可以异步获取警报,在此过程中获取任务可以继续执行其业务,直到警报触发,此时任务会收到通知。
示例代码利用在主时钟上设置的同步闹钟来实现定时器。
#include <mco.h>
#include "common.h"
#include "timer_impl.h"
/* system timer operations */
#if defined(_INTEGRITY)
#include <INTEGRITY.h>
Clock AlarmClock;
Task TimerTask;
volatile int stop_timer_task = 0;
mco_db_h timer_connection = 0;
static int TimerProc( Address param) {
do {
SynchronousReceive( (Connection)AlarmClock, NULL );
/* Call the timer handler */
timer_handler( * ((mco_db_h*)param) );
} while (!stop_timer_task);
return 0;
}
int init_sys_timer() {
Error E;
Value P;
Value W;
Time res;
Time AI;
timer_unit tm;
E = GetClockResolution( HighestResStandardClock, &res);
if ( E == Success ) {
printf("Timer resolution is %.6f microsec\n", res.Seconds * 1000000.0 + res.Fraction / (0xFFFFFFFF/1000000.0) );
} else {
printf("GetClockResolution() failed, %u\n", E );
}
printf("Testing mco_system_get_current_time()...\n");
tm = mco_system_get_current_time();
AI.Seconds = 0;
AI.Fraction = (0xFFFFFFFF/1000) * 123;
E = SetClockAlarm( HighestResStandardClock, false, NULLTime, &AI );
if ( E != Success ) {
printf("SetClockAlarm(...) == %u\n", E);
} else {
E = SynchronousReceive( (Connection)HighestResStandardClock, NULL );
if ( E != Success ) {
printf("SynchronousReceive(...) == %u\n", E);
}
}
tm = mco_system_get_current_time()-tm;
printf("Sleeping for 123000 microsec took %u microsec\n", tm);
E = CreateVirtualClock(HighestResStandardClock, CLOCK_READTIME|CLOCK_ALARM, &AlarmClock);
if ( E != Success ) {
printf("CreateVirtualClock(...) == %u\n", E);
return 0;
}
/* setup the task */
E = GetPriorityAndWeight( CurrentTask(), &P, &W );
if ( E == Success ) {
E = CommonCreateTaskWithArgument ( P, (TASKENTRYPOINT)TimerProc, (Address)&timer_connection, 1024, "eXdb-Timer", &TimerTask); /* use CommonCloseTask()*/
if ( E != Success ) {
printf("CommonCreateTask(...) == %u\n", E);
return 0;
}
SetPriorityAndWeight( TimerTask, P, W, true);
RunTask(TimerTask);
} else {
printf("GetPriorityAndWeight( CurrentTask(), ...) == %u\n", E);
return 0;
}
return 1;
}
void shutdown_sys_timer() {
Error E;
Time AI;
AI.Seconds = 0;
AI.Fraction = 1;
stop_timer_task = 1;
E = SetClockAlarm( AlarmClock, false, NULLTime, &AI );
if ( E != Success ) {
printf("SetClockAlarm(...) == %u\n", E);
}
SynchronousReceive( (Connection)TimerTask, NULL );
CommonCloseTask( TimerTask );
CloseClock( AlarmClock );
}
int set_sys_timer(sys_timer_t *timer, mco_db_h db, mco_interval_t deadline) {
Error E;
Time AI;
Time increment;
/* Set the alarm */
AI.Seconds = deadline / 1000;
AI.Fraction = (0xFFFFFFFF/1000) * (deadline % 1000);
E = GetClockResolution( AlarmClock, &increment);
if ( E != Success ) {
printf("GetClockResolution(...) == %u\n", E);
return 0;
}
if ( (increment.Seconds * 1000 + increment.Fraction / (0xFFFFFFFF/1000)) < deadline ) {
timer_connection = db;
E = SetClockAlarm( AlarmClock, false, NULLTime, &AI );
if ( E != Success ) {
printf("SetClockAlarm(...) == %u\n", E);
return 0;
}
} else {
printf("Timer resolution %.6f microsec is not enough for the deadline %u msec\n", 1000000.0 * increment.Seconds + increment.Fraction / (0xFFFFFFFF/1000000), deadline);
return 0;
}
return 1;
}
void clear_sys_timer(sys_timer_t *timer) {
Value overruns;
Error E;
Time AI;
AI.Seconds = 0;
AI.Fraction = 1;
E = SetClockAlarm( AlarmClock, false, NULLTime, &AI );
if ( E != Success ) {
printf("SetClockAlarm(...) == %u\n", E);
}
E = ClearClockAlarmOverruns( AlarmClock, &overruns);
if ( E != Success ) {
printf("ClearClockAlarmOverruns(...) == %u\n", E);
}
}
#endif