Nucleus SE RTOS初始化和启动
Nucleus SE RTOS初始化和啟動
Nucleus SE RTOS initialization and start-up
對于任何類型的操作系統(tǒng),都有某種類型的啟動機制。具體的工作方式因系統(tǒng)而異。通常說操作系統(tǒng)會“啟動”。這是“bootstrap”的縮寫,它描述了CPU如何從一個完全沒有內存的內存中獲得穩(wěn)定的程序執(zhí)行狀態(tài)。傳統(tǒng)上,一小片軟件被加載到內存中;它可以簡單地保存在ROM中。在過去,它可能是通過電腦前面板上的開關輸入的。這個“引導加載器”會讀入一個更復雜的引導程序,而這個程序又會加載并啟動操作系統(tǒng)。這就是桌面計算機今天開始運行的過程;BIOS中的代碼尋找可引導的設備(硬盤驅動器或CD-rom),從中可以加載引導程序,從而加載操作系統(tǒng)。
嵌入式系統(tǒng)的操作系統(tǒng)也可以用這種方式初始化。操作系統(tǒng)確實是從桌面操作系統(tǒng)衍生出來的。但對于大多數“經典”rtos,使用的是更簡單(因此更快)的過程。
操作系統(tǒng)只是一個軟件。如果這個軟件已經在內存中了——比如說某種形式的ROM——那就是簡單地安排CPU的重置順序,最終執(zhí)行操作系統(tǒng)的初始化代碼。這就是大多數RTOS的工作方式,Nucleus SE也不例外。
大多數嵌入式軟件開發(fā)工具包都包含必要的啟動代碼,以處理CPU重置并到達main()函數的入口點。Nucleus SE發(fā)行版代碼本身與此過程無關,因為它的目的是盡可能地便于移植。相反,它提供了main()函數,該函數控制CPU并初始化和啟動操作系統(tǒng);稍后將對此進行詳細描述。
內存初始化
Nucleus SE代碼中所有靜態(tài)變量的聲明都以ROM或RAM作為前綴,以指示它們可能位于哪里。這兩個define符號是在nuse_types.h中定義的,應該進行設置以適應正在使用的開發(fā)工具包(編譯器和鏈接器)的功能。通常,ROM可以設置為const,RAM留空。
所有的ROM變量都是靜態(tài)初始化的,這是合乎邏輯的。沒有RAM變量是靜態(tài)初始化的(因為這只適用于某些工具箱,這些工具箱安排從ROM到RAM的自動復制);包含顯式初始化代碼,本文將詳細介紹這些代碼。
Nucleus SE不在RAM中保存任何“恒定”的數據,在小型系統(tǒng)中,RAM可能供不應求。不使用復雜的數據結構來描述內核對象,而是使用一系列表(數組),這些表(數組)可以很容易地在ROM或RAM中找到。
main()函數
以下是Nucleus SE main()函數的完整代碼:
void main(void)
{
NUSE_Init(); /* initialize kernel data */
/* user initialization code here */
NUSE_Scheduler(); /* start tasks */
}
操作順序非常簡單:
首先調用NUSE_Init()函數。這將初始化所有Nucleus SE數據結構,并在下面詳細介紹。
接下來,用戶可以插入任何特定于應用程序的初始化代碼,這些代碼將在任務調度器啟動之前執(zhí)行。關于這段代碼可以實現什么的更多細節(jié)可以在本文后面找到。
最后,啟動Nucleus SE調度程序(NUSE_scheduler())。本文后面還將更詳細地研究這一點。
NUSE_Init()函數
此函數用于初始化所有Nucleus SE內核變量和數據結構。以下是完整代碼:
void NUSE_Init(void)
{
U8 index;/* global data */NUSE_Task_Active = 0;NUSE_Task_State = NUSE_STARTUP_CONTEXT;#if NUSE_SYSTEM_TIME_SUPPORT
NUSE_Tick_Clock = 0;
#endif#if NUSE_SCHEDULER_TYPE
== NUSE_TIME_SLICE_SCHEDULER
NUSE_Time_Slice_Ticks = NUSE_TIME_SLICE_TICKS;
#endif/* tasks */#if
((NUSE_SCHEDULER_TYPE != NUSE_RUN_TO_COMPLETION_SCHEDULER)
|| NUSE_SIGNAL_SUPPORT || NUSE_TASK_SLEEP
|| NUSE_SUSPEND_ENABLE || NUSE_SCHEDULE_COUNT_SUPPORT)
for (index=0; index
{
NUSE_Init_Task(index);
}#endif/* partition pools */#if NUSE_PARTITION_POOL_NUMBER != 0for (index=0; index
{
NUSE_Init_Partition_Pool(index);
}#endif/* mailboxes */#if NUSE_MAILBOX_NUMBER != 0for (index=0; index
{
NUSE_Init_Mailbox(index);
}#endif/* queues */#if NUSE_QUEUE_NUMBER != 0for (index=0; index
{
NUSE_Init_Queue(index);
}#endif/* pipes */#if NUSE_PIPE_NUMBER != 0for (index=0; index
{
NUSE_Init_Pipe(index);
}#endif/* semaphores */#if NUSE_SEMAPHORE_NUMBER != 0for (index=0; index
{
NUSE_Init_Semaphore(index);
}#endif/* event groups */#if NUSE_EVENT_GROUP_NUMBER != 0for (index=0; index
{
NUSE_Init_Event_Group(index);
}#endif/* timers */#if NUSE_TIMER_NUMBER != 0for (index=0; index
{
NUSE_Init_Timer(index);
}#endif
}
首先,初始化一些全局變量:
NUSE_Task_Active–當前活動任務的索引–設置為零;計劃程序可能會在適當的時候對此進行修改。
NUSE_Task_State設置為NUSE_STARTUP_CONTEXT,這表示API功能僅限于以下任何應用程序初始化代碼。
如果啟用了系統(tǒng)時間支持,則用戶時鐘設置為零。
如果啟用了時間片調度程序,則會將NUSE_time_slice_Ticks設置為配置的時間片值NUSE_time_slice_Ticks。
然后,調用一系列函數來初始化內核對象:
調用NUSE_Init_Task()來初始化每個任務的數據結構。只有在選擇了“運行到完成”計劃程序并且信號、任務掛起和計劃計數都未配置時,才會忽略此調用(因為這種組合將導致沒有與任務相關的RAM數據結構,因此不需要進行初始化)。
調用NUSE_Init_Partition_Pool()初始化每個分區(qū)池對象。如果沒有配置分區(qū)池,則忽略調用。
調用NUSE_Init_Mailbox()初始化每個郵箱對象。如果沒有配置郵箱,則忽略調用。
調用NUSE_Init_Queue()初始化每個隊列對象。如果沒有配置隊列,則忽略調用。
調用NUSE_Init_Pipe()初始化每個管道對象。如果沒有配置管道,則忽略調用。
調用NUSE_Init_Semaphore()初始化每個信號量對象。如果沒有配置信號量,則忽略調用。
調用NUSE_Init_Event_Group()初始化每個事件組對象。如果沒有配置事件組,則忽略調用。
調用NUSE_Init_Timer()初始化每個計時器對象。如果沒有配置計時器,則忽略調用。
初始化任務
以下是NUSE_Init_Task()的完整代碼:
void NUSE_Init_Task(NUSE_TASK task)
{
#if NUSE_SCHEDULER_TYPE != NUSE_RUN_TO_COMPLETION_SCHEDULER
NUSE_Task_Context[task][15]
/* SR */
NUSE_STATUS_REGISTER;
NUSE_Task_Context[task][16] =
/* PC */
NUSE_Task_Start_Address[task];
NUSE_Task_Context[task][17] =
/* SP */
(U32 *)NUSE_Task_Stack_Base[task] +
NUSE_Task_Stack_Size[task];
#endif#if NUSE_SIGNAL_SUPPORT ||
NUSE_INCLUDE_EVERYTHING
NUSE_Task_Signal_Flags[task] = 0;
#endif#if NUSE_TASK_SLEEP ||
NUSE_INCLUDE_EVERYTHING
NUSE_Task_Timeout_Counter[task] = 0;
#endif#if NUSE_SUSPEND_ENABLE ||
NUSE_INCLUDE_EVERYTHING
#if NUSE_INITIAL_TASK_STATE_SUPPORT
||
NUSE_INCLUDE_EVERYTHING
NUSE_Task_Status[task] =
NUSE_Task_Initial_State[task];
#else
NUSE_Task_Status[task] = NUSE_READY;
#endif#endif#if NUSE_SCHEDULE_COUNT_SUPPORT ||
NUSE_INCLUDE_EVERYTHING
NUSE_Task_Schedule_Count[task] = 0;
#endif
}
除非已配置“運行到完成”計劃程序,否則將初始化任務的上下文塊–NUSE_Task_context[Task][]。大多數條目沒有設置為值,因為它們表示通用機器寄存器,當任務啟動時,這些寄存器被假定具有不確定的值。在Nucleus SE的示例(Freescale ColdFire)實現中(這對于任何處理器都是類似的),最后三個條目是顯式設置的:
NUSE_Task_Context[Task][15]保存狀態(tài)寄存器(SR),并設置為#define
symbol NUSE_status_寄存器中的值。
NUSE_Task_Context[Task][16]保存程序計數器(PC),并設置為任務代碼入口點的地址:NUSE_Task_Start_address[Task]。
NUSE_Task_Context[Task][17]保存堆棧指針(SP),該指針初始化為通過將任務的堆?;刂?#xff08;NUSE_Task_stack_base[Task])的地址添加到任務的堆棧大小(NUSE_Task_stack_size[Task])計算出來的值。
如果啟用信號支持,任務的信號標志(NUSE_task_signal_flags[task])設為零。
如果啟用了任務休眠(即API調用NUSE_task_sleep()),則任務的超時計數器(NUSE_task_timeout_counter[task])設置為零。
如果啟用任務掛起,則任務的狀態(tài)(NUSE_task_status[task])將初始化。如果啟用了任務初始任務狀態(tài)支持,則此初始值由用戶指定(在NUSE_Task_initial_State[Task])。否則,狀態(tài)設置為NUSE_READY。
如果啟用任務計劃計數,則任務的計數器(NUSE_task_schedule_Count[task])設置為零。
初始化分區(qū)池
以下是NUSE_Init_Partition_Pool()的完整代碼:
void
NUSE_Init_Partition_Pool(NUSE_PARTITION_POOL pool)
{
NUSE_Partition_Pool_Partition_Used[pool] = 0;#if NUSE_BLOCKING_ENABLE
NUSE_Partition_Pool_Blocking_Count[pool] = 0;
#endif
}
分區(qū)池的“used”計數器(NUSE_partition_pool_partition_used[pool])設置為零。
如果啟用了任務阻塞,則分區(qū)池的阻塞任務計數器(NUSE_partition_pool_blocking_Count[pool])設置為零。
初始化郵箱
以下是NUSE_Init_Mailbox()的完整代碼:
void NUSE_Init_Mailbox(NUSE_MAILBOX mailbox)
{
NUSE_Mailbox_Data[mailbox] = 0;NUSE_Mailbox_Status[mailbox] = 0;#if NUSE_BLOCKING_ENABLENUSE_Mailbox_Blocking_Count[mailbox] = 0;#endif
}
郵箱的數據存儲(NUSE_mailbox_data[mailbox])設置為零,其狀態(tài)(NUSE_mailbox_status[mailbox])設置為“未使用”(即零)。
如果啟用任務阻止,則郵箱的阻止任務計數器(NUSE_mailbox_blocking_Count[mailbox])設置為零。
初始化隊列
以下是NUSE_Init_Queue()的完整代碼:
void NUSE_Init_Queue(NUSE_QUEUE queue)
{
NUSE_Queue_Head[queue] = 0;NUSE_Queue_Tail[queue] = 0;NUSE_Queue_Items[queue] = 0;#if NUSE_BLOCKING_ENABLE
NUSE_Queue_Blocking_Count[queue] = 0;
#endif
}
隊列的head和tail指針(實際上,它們是索引–NUSE_queue_head[queue]和NUSE_queue_tail[queue])被設置為指向隊列數據區(qū)域的開始(即給定值0)。隊列的項目計數器(NUSE_queue_Items[queue])也設置為零。
如果啟用任務阻塞,隊列的阻塞任務計數器(NUSE_queue_blocking_Count[queue])設置為零。
初始化管道
以下是NUSE_Init_Pipe()的完整代碼:
void NUSE_Init_Pipe(NUSE_PIPE pipe)
{
NUSE_Pipe_Head[pipe] = 0;NUSE_Pipe_Tail[pipe] = 0;NUSE_Pipe_Items[pipe] = 0;#if NUSE_BLOCKING_ENABLE
NUSE_Pipe_Blocking_Count[pipe] = 0;
#endif
}
管道的頭和尾指針(實際上,它們是索引–NUSE_pipe_head[pipe]和NUSE_pipe_tail[pipe])被設置為指向管道數據區(qū)域的開始(即給定值0)。管道的項目計數器(NUSE_pipe_Items[pipe])也設置為零。
如果啟用任務阻塞,則管道的阻塞任務計數器(NUSE_pipe_blocking_Count[pipe])設置為零。
初始化信號量
以下是NUSE_Init_Semaphore()的完整代碼:
void NUSE_Init_Semaphore(NUSE_SEMAPHORE
semaphore)
{
NUSE_Semaphore_Counter[semaphore]
=
NUSE_Semaphore_Initial_Value[semaphore];
#if NUSE_BLOCKING_ENABLE
NUSE_Semaphore_Blocking_Count[semaphore] = 0;
#endif
}
信號量的計數器(NUSE_semaphore_counter[semaphore])初始化為用戶指定的值(NUSE_semaphore_Initial_value[semaphore])。
如果啟用任務阻塞,則信號量的阻塞任務計數器(NUSE_semaphore_blocking_Count[信號量])設置為零。
初始化事件組
以下是NUSE_Init_Event_Group()的完整代碼:
void NUSE_Init_Event_Group(NUSE_EVENT_GROUP group)
{
NUSE_Event_Group_Data[group] = 0;
#if NUSE_BLOCKING_ENABLE
NUSE_Event_Group_Blocking_Count[group] = 0;
#endif
}
事件組的標志被清除;即NUSE_event_group_Data[group]設置為零。
如果啟用任務阻止,則事件組的阻止任務計數器(NUSE_event_group_blocking_Count[group])設置為零。
初始化計時器
以下是NUSE_Init_Timer()的完整代碼:
void NUSE_Init_Timer(NUSE_TIMER timer)
{
NUSE_Timer_Status[timer] = FALSE;
NUSE_Timer_Value[timer] = NUSE_Timer_Initial_Time[timer];
NUSE_Timer_Expirations_Counter[timer] = 0;
}
計時器的狀態(tài)(NUSE_timer_status[timer])設置為“未使用”;即FALSE。
其倒計時值(NUSE_Timer_value[Timer])初始化為用戶指定的值(NUSE_Timer_Initial_Time[Timer])。
其過期計數器(NUSE_Timer_Expirations_counter[Timer])設置為零。
應用程序代碼初始化
一旦Nucleus SE數據結構被初始化,就有機會在執(zhí)行任務之前執(zhí)行應用程序初始化的代碼。此功能有許多可能的用途:
初始化應用程序數據結構。顯式賦值比允許靜態(tài)變量的自動初始化更容易理解和調試。
內核對象分配。假設所有內核對象都是在構建時靜態(tài)創(chuàng)建的,并由索引值標識,那么分配“所有權”或定義這些對象的用法可能很有用。這可以使用#define符號來完成,但是,如果存在多個任務實例,則最好通過全局數組(按任務的ID編制索引)來分配對象索引。
設備初始化。這可能是安裝任何外圍設備的好機會。
顯然,在執(zhí)行Nucleus SE初始化之前,很多事情都可以實現,但是在這里定位應用程序初始化代碼的好處是現在可以使用內核服務(API調用)。例如,隊列或郵箱可能預加載了任務啟動時要處理的數據。
允許API調用有一個限制:不能采取通常會導致調用調度程序的操作,例如任務掛起/阻塞。全局變量NUSE_Task_State已設置為NUSE_STARTUP_CONTEXT以反映此限制。
啟動計劃程序
初始化完成后,只剩下啟動調度程序來開始執(zhí)行應用程序代碼-任務。紹了調度程序的選項和各種類型的調度程序的操作,因此這里只需要簡要總結一下。
順序中的關鍵點是:
將全局變量NUSE_Task_State設置為NUSE_Task_CONTEXT。
選擇要運行的第一個任務的索引。如果啟用了對初始任務狀態(tài)的支持,將對第一個就緒任務執(zhí)行搜索;否則將使用值0。
調用調度程序–NUSE_scheduler()。
在最后一步中到底發(fā)生了什么取決于選擇了哪種調度程序類型。對于Run-to-Completion,進入調度循環(huán)并按順序調用任務。對于其他調度程序類型,將加載第一個任務的上下文并將控制權傳遞給該任務。
總結
以上是生活随笔為你收集整理的Nucleus SE RTOS初始化和启动的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Nucleus 实时操作系统中断(下)
- 下一篇: RTOS诊断和错误检查