ERIKA OS学习和使用总结

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

 五、问题及解决方案

(1)TC297下,使用BootLoader时如何修改ERIKA OS在ROM中的位置?——以ERIKA其实地址在0xa00e0020为例,修改链接脚本:

  • 链接脚本所在位置:
  • 修改内容:

(2)TC397下如何运行ERIKA OS?

  • 首先,在创建TC397的工程后,修改OIL文件,在OS设置中添加OSEE_TC_LINK_BMHD选项。
  • 然后编译工程,此时生成的可执行文件中包含2部分内容:①ERIKA OS的二进制代码;②TC397的BMHD(注意:ERIKA生成的BMHD与官方出厂的BMHD在应用程序起始地址的设置上有所不同,不过可以放心烧写,后续也可以改回出厂设置)。

  • 先通过MemTool工具将BMHD烧写到DFlash中的UCBS中,再将ERIKA代码烧写到PFlash中,二者的区分如下图所示,其中阴影部分是BMHD,前面4行为ERIKA的二进制代码,夹在BMHD中间的一行无效,不需要烧写。

(3)TC397下ERIKA如何运行在多核环境下?

  • 实际上与单核无异,不过在实际研究过程中遇到了一个坑,这里记录一下。具体问题为:在调试模式下,ERIKA正常运行,而上电复位后只执行了初始化过程,而没有正常运行,甚至连Cpu0都没有正常运行ERIKA。于是与单核下的OIL设置进行了对比,发现有一个选项设置,即OSEE_DEBUG,如下图所示。将其注释掉即可解决。

 (4)Erika如何使用iLLD底层驱动库?——底层驱动库实际上就是将MCU的寄存器操作或一系列寄存器操作用函数进行封装,与直接进行寄存器操作无异。但iLLD库除了寄存器操作外,还进行了中断和陷阱相关的设计(包括:中断和陷阱向量表的定义、中断服务函数的定义等),而Erika OS已经将中断和陷阱的处理纳入自己的管理范围,且管理方式(主要是相应的函数)与iLLD不同,因此在中断和陷阱方面iLLD与Erika存在不兼容的问题,从而需要舍弃iLLD中的相应部分,而将其他主要部分融入到Erika中,具体方法如下:

  • ①将iLLD中除了中断与陷阱以外的库文件添加到工程,其中被排除的主要是Cpu文件夹中的Irq和Trap子文件夹,以及CStart文件夹(Erika模仿其编写了相应的内核启动文件,因此不再需要该文件夹),如图所示。
  • ②设置包含路径。就像在Hightec中使用iLLD一样,需要包含库的相应路径,一般在工程设置中的“path and symbols”中设置。由于eclipse-phonon中相应的设置不起作用,因此采用了在oil文件中设置编译属性的方式进行了路径包含,如下图所示。
  •  

     ③设置需要被编译的源文件。Erika工程中的源文件,除了OS的源代码文件外都不会自动参与编译,而是需要手动设置,因此需要将iLLD中需要用到的源文件设置为编译对象,具体设置在oil文件中进行,这里我们设置了基本的源文件,如IfxCpu_cfg.c、IfxSrc_cfg.c、IfxCpu.c、IfxSrc.c,还设置了需要用到的外设对应的源文件,包括IfxStm.c和IfxPort.c,如图所示。

  • ④ 使用实例——设置STMsr1中断,并在中断中闪灯,具体程序如下所示。在Erika中使用iLLD时,主要是中断服务函数的定义方法会有所不同。在HighTec中设置一个外设的中断时,首先要设置相应外设的中断优先级并开启中断,然后通过IFX_INTERRUPT宏来定义相应优先级的中断向量和中断服务函数(使用硬件管理中断时);而在ErikaOS下,前面设置优先级和开启中断是与前者相同的,直接用iLLD中的相应函数即可,不同之处在于中断向量和中断服务函数的定义,中断向量的定义在ErikaOS的源文件ee_tc_intvec.c中进行,而中断服务函数的声明需要在oil文件中设置并在源文件中定义(见下图)。
  •  

     

posted @ 2021-05-01 10:55  凉风SK  阅读(4349)  评论(4编辑  收藏  举报