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文件中設置並在源文件中定義(見下圖)。
  •  

     


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM