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文件中設置并在源文件中定義(見下圖)。
總結
以上是生活随笔為你收集整理的ERIKA OS学习和使用总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 软考知识点梳理--项目范围说明书
- 下一篇: 有什么办法可以恢复如何让电脑恢复到昨天