ERIKA是一款开源的、遵循OSEK/VDX标准的实时操作系统。
一、周期任务的实现(方式一:使用普通COUNTER,Alarm通过SetRelAlarm()函数手动启动和设置)
(1)code.c
/* ERIKA Enterprise. */ #include "ee.h" /* TASKs */ DeclareTask( Task1 ); extern void clock_handler( void ); ISR( stm0_handler ) { osEE_tc_stm_set_sr0_next_match( 1000U ); IncrementCounter( myCounter ); //(1)在stm中断中为counter计数。counter在OIL文件的定义(COUNTER myCounter),用于唤醒Task1的Alarm会与myCounter绑定, } //并且也会和Task1绑定,并通过函数SetRelAlarm() 来设置Task1的唤醒周期T,一旦myCounter计数达到T,就唤醒Task1. TASK(Task1) { toggle_led( LED_1 ); TerminateTask(); //每个任务的最后必须添加这一函数用于结束任务 }int main(void) { osEE_tc_stm_set_clockpersec(); osEE_tc_stm_set_sr0( 1000U, 1U ); //设置stm leds_init(); SetRelAlarm( AlarmTask1, 2000U, 500U ); //(2)设置用于激活Task1的Alarm的开始时间和周期 StartOS( OSDEFAULTAPPMODE ); //启动erika os。在本条语句之前,不能使用其他erika os的原语,除非在启用startupHook并在其中可以调用部分原语 return 0; }
(2)OIL
CPU mySystem { OS myOs { EE_OPT = "OSEE_DEBUG"; //OS的一些可选项 EE_OPT = "OS_EE_APPL_BUILD_DEBUG"; EE_OPT = "OS_EE_BUILD_DEBUG"; CPU_DATA = TRICORE { //CPU类型必须选择TRICORE CPU_CLOCK = 300.0; MULTI_STACK = TRUE; COMPILER = GCC; IDLEHOOK = FALSE; }; MCU_DATA = TC29X { //MCU选择相应的芯片型号,这里选择TC29x后再编译,ERIKA就会引入TC29x对应的寄存器和外设等相应的定义(默认是TC27x的)
DERIVATIVE = "tc297tf"; //下面这两个参数影响不大 REVISION = "B"; }; KERNEL_TYPE = OSEK { //内核类型。除了选择正常的OSEK,还可以选择FP/EDF/FRSK/HR,这些类型是ERIKA在OSEK标准之外自己加入的一些一致性类 CLASS = BCC1; //一致性类 BCC1和BCC2只支持基础任务,不能使用waitevent等阻塞相关的功能,可以共享堆栈;而ECC1和ECC2支持扩展任务,可以使用阻塞相关 }; //功能,但因为阻塞,必须使用私有堆栈;BCC1和ECC1不支持pending activation,即连续激活一个任务时,能否挂起激活次数;BCC2和ECC2支持。 }; APPDATA periodic_task { APP_SRC = "code.c"; }; TASK Task1 { //设置Task1的属性 PRIORITY = 1; // 设置优先级 STACK = PRIVATE { //使用私有栈 SIZE = 1024; }; SCHEDULE = FULL; //参与基于优先级的抢占 }; COUNTER myCounter; //定义counter ALARM AlarmTask1 { //将Alarm与counter及task进行绑定 COUNTER = myCounter; ACTION = ACTIVATETASK { TASK = Task1; }; }; ISR TimerISR { // 设置STM的相关属性 CATEGORY = 2; //ISR的类型设置为type2,即由Erika进行管理。ISR Type1和ISR Type2的区别是:ISR1是一般的中断,与OS无关,handler中只能执行少数OS API; SOURCE = "STM0SR0"; //ISR2由OS进行管理,该类型的中断类似于任务。Handler中可以执行OS API,该类型的中断需要在OIL文件中声明;ISR1的优先级必须高于ISR2。 HANDLER = "stm0_handler"; //使用的STM资源和中断处理函数名 PRIORITY = 1; }; };
由此可以看出.c文件用于编写任务实体,而OIL文件中定义任务属性,如下图所示。
二、周期任务的实现(方式二:使用Hardware COUNTER——System Timer,且Alarm自动启动和设置)
(1)code.c
/* ERIKA Enterprise. */ #include "ee.h"
/* TASKs */ DeclareTask( Task1 ); TASK(Task1) { toggle_led( LED_1 ); TerminateTask(); }
// 不用初始化STM,也不用在STM中断里为COUNTER计数,Alarm设置为自启动,且启动时间和周期在OIL文件中设置
int main(void) { leds_init(); StartOS( OSDEFAULTAPPMODE ); return 0; }
(2)OIL文件
CPU mySystem { OS myOs { EE_OPT = "OSEE_DEBUG"; //OS的一些可选项 EE_OPT = "OS_EE_APPL_BUILD_DEBUG"; EE_OPT = "OS_EE_BUILD_DEBUG"; CPU_DATA = TRICORE { //CPU类型必须选择TRICORE CPU_CLOCK = 300.0; MULTI_STACK = TRUE; COMPILER = GCC; IDLEHOOK = FALSE; }; MCU_DATA = TC29X { //MCU选择相应的芯片型号,这里选择TC29x后再编译,ERIKA就会引入TC29x对应的寄存器和外设等相应的定义(默认是TC27x的)
DERIVATIVE = "tc297tf"; //下面这两个参数影响不大 REVISION = "B"; }; KERNEL_TYPE = OSEK { //内核类型。除了选择正常的OSEK,还可以选择FP/EDF/FRSK/HR,这些类型是ERIKA在OSEK标准之外自己加入的一些一致性类 CLASS = BCC1; //一致性类 BCC1和BCC2只支持基础任务,不能使用waitevent等阻塞相关的功能,可以共享堆栈;而ECC1和ECC2支持扩展任务,可以使用阻塞相关 }; //功能,但因为阻塞,必须使用私有堆栈;BCC1和ECC1不支持pending activation,即连续激活一个任务时,能否挂起激活次数;BCC2和ECC2支持。 }; APPDATA periodic_task { APP_SRC = "code.c"; }; TASK Task1 { //设置Task1的属性 PRIORITY = 1; // 设置优先级 STACK = PRIVATE { //使用私有栈 SIZE = 1024; }; SCHEDULE = FULL; //参与基于优先级的抢占 };
COUNTER system_timer_1 {
CPU_ID = 0x0; //指定system timer是哪个内核的,一个内核只能使用一个system timer
MINCYCLE = 1; //alarm周期的最小tick数
MAXALLOWEDVALUE = 2147483647; //counter的最大tick计数值
TICKSPERBASE = 1; // 每个计数单元(unit)包含的tick数,即时基
TYPE = HARDWARE { //设置使用hardware counter
DEVICE = "STM_SR0"; //设置system counter所使用的stm资源
SYSTEM_TIMER = TRUE;
PRIORITY = 2;
};
SECONDSPERTICK = 0.001; //设置tick的粒度,这里为每个tick为1ms
};
ALARM AlarmTask1 {
COUNTER = system_timer_1; //与hardware counter进行绑定
ACTION = ACTIVATETASK { TASK = Task1; }; //与对应的task进行绑定
AUTOSTART = TRUE { ALARMTIME = 500; CYCLETIME = 2000; }; //设置alarm自启动,同时设置开始时间和周期。任务也可以设置autostart
};
};
三、Erika如何应用在多核中
(1)master.c
/* ERIKA Enterprise. */ #include "shared.h"
//master.c对应于core0,其任务TaskCore0执行后,延迟200ms唤醒TaskCore1,TaskCore1延迟200ms唤醒TaskCore2.
TASK(TaskCore0) { led_blink(OSEE_TRIBOARD_2X5_LED_1); SetRelAlarm( AlarmCore1, 200, 0 ); TerminateTask(); } OsEE_reg myErrorCounter; void ErrorHook(StatusType Error) //当OS运行出错时进行ErrorHook进行处理,ErrorHook通过OIL文件设置是否启用。 { (void)Error; ++myErrorCounter; led_blink(OSEE_TRIBOARD_2X5_ALL_LEDS); } void idle_hook_core0(void); void idle_hook_core0(void) { idle_hook_body(); } /* * MAIN TASK */ int main(void) { StatusType status; AppModeType mode; CoreIdType const core_id = GetCoreID(); if (core_id == OS_CORE_ID_MASTER) { //注意main函数,每个核都会进入main函数,core0启动另外两个核,而core1和core2只需要进行各自的初始化工作 /* Init leds */ osEE_tc2x5_leds_init(); StartCore(OS_CORE_ID_1, &status); StartCore(OS_CORE_ID_2, &status); mode = OSDEFAULTAPPMODE; } else { mode = DONOTCARE; } StartOS(mode); return 0; }
(2)slave1.c和slave2.c
#include "shared.h" void idle_hook_core1(void); void idle_hook_core1(void) { idle_hook_body(); } TASK(TaskCore1) { led_blink(OSEE_TRIBOARD_2X5_LED_2); SetRelAlarm( AlarmCore2, 200, 0 ); TerminateTask(); } #include "shared.h" void idle_hook_core2(void); void idle_hook_core2(void) { idle_hook_body(); } TASK(TaskCore2) { led_blink(OSEE_TRIBOARD_2X5_LED_3); TerminateTask(); }
(3)OIL文件
CPU test_application { OS EE { /* EE_OPT = "OS_EE_VERBOSE"; */ EE_OPT = "OSEE_DEBUG"; EE_OPT = "OSEE_ASSERT"; EE_OPT = "OS_EE_APPL_BUILD_DEBUG"; EE_OPT = "OS_EE_BUILD_DEBUG"; //EE_OPT = "OSEE_TC_CLONE_OS"; CPU_DATA = TRICORE { ID = 0x0; CPU_CLOCK = 300.0; COMPILER = GCC; IDLEHOOK = TRUE { HOOKNAME = "idle_hook_core0"; }; }; CPU_DATA = TRICORE { //启用core1 ID = 0x1; MULTI_STACK = TRUE; IDLEHOOK = TRUE { HOOKNAME = "idle_hook_core1"; }; }; CPU_DATA = TRICORE { //启用core2 ID = 0x2; IDLEHOOK = TRUE { HOOKNAME = "idle_hook_core2"; }; }; MCU_DATA = TC29X { DERIVATIVE = "tc297tf"; REVISION = "BD"; }; STATUS = EXTENDED; ERRORHOOK = TRUE; //使用errorhook USERESSCHEDULER = FALSE; USEORTI = TRUE; KERNEL_TYPE = OSEK { CLASS = ECC1; RQ = MQ; //就绪任务列表类型选择:RQ=LL,用链表,复杂度为O(n),n为就绪队列中的任务数;RQ=MQ,用多队列,复杂度为O(1) }; }; APPDATA tricore_mc { APP_SRC="master.c"; APP_SRC="slave1.c"; APP_SRC="slave2.c"; }; TASK TaskCore0 { CPU_ID = 0x0; //指定任务所属内核 PRIORITY = 1; }; TASK TaskCore1 { CPU_ID = 0x1; PRIORITY = 1; }; TASK TaskCore2 { CPU_ID = 0x2; PRIORITY = 1; }; COUNTER system_timer_core0 { CPU_ID = 0x0; MINCYCLE = 1; MAXALLOWEDVALUE = 2147483647; TICKSPERBASE = 1; TYPE = HARDWARE { DEVICE = "STM_SR0"; SYSTEM_TIMER = TRUE; PRIORITY = 2; }; SECONDSPERTICK = 0.001; }; COUNTER system_timer_core1 { CPU_ID = 0x1; MINCYCLE = 1; MAXALLOWEDVALUE = 2147483647; TICKSPERBASE = 1; TYPE = HARDWARE { DEVICE = "STM_SR0"; SYSTEM_TIMER = TRUE; PRIORITY = 2; }; SECONDSPERTICK = 0.001; }; COUNTER system_timer_core2 { CPU_ID = 0x2; MINCYCLE = 1; MAXALLOWEDVALUE = 2147483647; TICKSPERBASE = 1; TYPE = HARDWARE { DEVICE = "STM_SR0"; SYSTEM_TIMER = TRUE; PRIORITY = 2; }; SECONDSPERTICK = 0.001; }; ALARM AlarmCore0 { COUNTER = system_timer_core0; ACTION = ACTIVATETASK { TASK = TaskCore0; }; AUTOSTART = TRUE { ALARMTIME = 500; CYCLETIME = 2000; }; }; ALARM AlarmCore1 { COUNTER = system_timer_core1; ACTION = ACTIVATETASK { TASK = TaskCore1; }; }; ALARM AlarmCore2 { COUNTER = system_timer_core2; ACTION = ACTIVATETASK { TASK = TaskCore2; }; }; };
四、Erika特点
(1)Erika的任务调度:①完全抢占式任务(Full Preemptive):参与基于优先级的抢占式任务调度;②非抢占式任务(Non Preemptive):不会被其他任务抢占;③混合式任务。
(2)堆栈使用:①main堆栈用于运行main()函数,当一个task设置堆栈为shared时,将会共享使用main堆栈;而当task设置了private堆栈时,则分配私有堆栈;②ISR1类中断发生时,使用当前激活的堆栈;ISR2型中断使用main堆栈。
(3)Erika优先级最大值:127