Nucleus 實時作業系統中斷(上)
Interrupts in the Nucleus SE RTOS
所有現代微處理器和微控制器都有某種中斷設施。這種能力對于提供許多應用程式所需的響應能力是必不可少的。當然,響應性和可預測性也是使用實時作業系統背後的一個關鍵目标,是以這兩個主題确實存在輕微的沖突。使用中斷可能會損害作業系統的實時完整性。這一主題,以及沖突的解決方法,目前不講。在這裡,我們将了解Nucleus SE使用的中斷處理政策。
在所有情況下,中斷都不是由Nucleus SE控制的,它們是在中斷發生時根據優先級進行處理的,并以通常的方式進行矢量化。它們的執行時間隻是從運作主線應用程式代碼和排程程式的可用時間中“偷走”的。顯然,這意味着所有的中斷服務程式都應該簡單、簡短和快速。
本機中斷和托管中斷
Nucleus SE确實提供了兩種進行中斷的方法:“本機”中斷服務例程沒有什麼特别的,并且與作業系統互動的機會有限(至少在選擇優先級排程器時是這樣);“托管”中斷服務例程可以進行更廣泛的API調用。
通過一些進入/退出宏,與Nucleus SE應用程式一起使用的中斷服務例程可以被指定為本機或托管的。
本機中斷
Nucleus SE本機中斷是标準的中斷服務例程,您可以将其視為“非托管的”。當am中斷可能以很高的頻率發生并且需要以非常低的開銷進行服務時,通常使用它們。由于許多現代嵌入式編譯器都支援通過interrupt關鍵字編寫中斷服務例程,是以這個例程很可能是用C編寫的。唯一儲存的上下文資訊是編譯器認為必要的資訊。這導緻本機中斷例程可以執行的操作有很大的限制,我們将很快看到。
要構造Nucleus SE本機中斷服務例程,隻需按通常的方式編寫ISR,包括在開始時調用NUSE_NISR_Enter()宏,在末尾調用NUSE_NISR_Exit()。這些宏在nuse_types.h中定義,隻需将全局變量nuse_Task_State設定為nuse_NISR_CONTEXT。
托管中斷
如果您需要在ISR可以執行的操作方面有更大的靈活性,Nucleus SE管理的中斷可能是解決方案。與本機中斷的關鍵差別是上下文儲存。托管中斷不僅僅允許編譯器堆疊幾個寄存器,而是在輸入時儲存完整的任務上下文(在上下文塊中)。然後從目前任務的上下文塊中加載目前任務的上下文。這考慮到了目前任務可能被ISR代碼的操作改變的可能性;當優先級排程器正在使用時,這是完全可能的。包含了對Nucleus SE上下文儲存和恢複的完整描述。
顯然,完整的上下文儲存比由本機中斷執行的幾個寄存器的堆棧開銷更高。這是額外靈活性的代價,也是為什麼可以選擇進行中斷的方法的原因。
托管中斷是使用NUSE_managed_ISR()宏構造的,該宏在NUSE_types.h中定義。此宏構造包含以下序列的函數:
- task context save
- set NUSE_Task_State to NUSE_MISR_CONTEXT
- call user-supplied ISR code function
- restore NUSE_Task_State to previous setting
- task context restore
宏接受兩個參數:中斷的名稱,用作構造的例程的函數名;包含使用者提供的ISR邏輯的函數名。
本文後面将介紹Nucleus SE實時時鐘ISR,它是受管ISR的一個示例。
中斷服務例程的API調用
可以從本機或托管ISR調用的API函數的範圍取決于選擇了哪個排程程式。一般來說,優先級排程程式的使用為在API函數調用的結果下調用排程程式提供了許多機會,這在本機ISR中是一個問題。
使用優先級排程程式從本機ISR調用API
允許使用優先級排程程式從本機ISR調用有限範圍的API函數。這一限制是由于Nucleus SE API的靈活性造成的–許多調用都會導緻任務準備就緒,并且本地ISR不可能調用排程器(因為任務上下文未被儲存)。如果不啟用任務阻塞,則具有更大的靈活性。
始終允許以下API調用:
NUSE_Task_Current()
NUSE_Task_Check_Stack()
NUSE_Task_Information()
NUSE_Task_Count()
NUSE_Partition_Pool_Information()
NUSE_Partition_Pool_Count()
NUSE_Mailbox_Information()
NUSE_Mailbox_Count()
NUSE_Queue_Information()
NUSE_Queue_Count()
NUSE_Pipe_Information()
NUSE_Pipe_Count()
NUSE_Semaphore_Information()
NUSE_Semaphore_Count()
NUSE_Event_Group_Information()
NUSE_Event_Group_Count()
NUSE_Signals_Send()
NUSE_Timer_Control()
NUSE_Timer_Get_Remaining()
NUSE_Timer_Reset()
NUSE_Timer_Information()
NUSE_Timer_Count()
NUSE_Clock_Set()
NUSE_Clock_Retrieve()
NUSE_Release_Information()
但是,唯一真正有用的是NUSE_Signals_Send(),因為這提供了一個很好的方法來訓示任務需要一些工作。
如果禁用了阻塞(這意味着許多API調用可能無法使任務就緒),則可以使用許多其他API函數:
NUSE_Partition_Allocate()
NUSE_Partition_Deallocate()
NUSE_Mailbox_Send()
NUSE_Mailbox_Receive()
NUSE_Mailbox_Reset()
NUSE_Queue_Send()
NUSE_Queue_Receive()
NUSE_Queue_Jam()
NUSE_Queue_Reset()
NUSE_Pipe_Send()
NUSE_Pipe_Receive()
NUSE_Pipe_Jam()
NUSE_Pipe_Reset()
NUSE_Semaphore_Obtain()
NUSE_Semaphore_Release()
NUSE_Semaphore_Reset()
NUSE_Event_Group_Set()
NUSE_Event_Group_Retrieve()
NUSE_Task_Suspend()
NUSE_Task_Resume()
NUSE_Task_Sleep()
NUSE_Task_Relinquish()
NUSE_Task_Reset()
NUSE_Signals_Receive()
來自托管ISR或具有非優先級排程程式的本機ISR的API調用
當使用run-to-completion、round-robin或time-sliced排程器時,可以從ISR調用範圍更廣的API函數。如果使用類似的ISR優先級,則受管排程器有助于提高ISR的優先級。這是因為允許調用可能導緻排程不同的任務。NUSE_Reschedule()中的代碼有助于此功能,該代碼檢測調用的上下文是ISR,并禁止上下文切換(允許它在ISR結束時發生)。排程程式操作的完整細節在前面的一篇文章中介紹過。
一個關鍵的要求是ISR中的API調用不能導緻目前任務的挂起,例如等待一個資源。換句話說,這種調用應該在suspend選項設定為NUSE_NO_suspend的情況下進行。
鑒于此規定,可使用以下API調用:
一些API調用是不允許的,因為它們與目前任務相關:
實時時鐘ISR
實時時鐘(RTC)ISR是Nucleus SE提供的唯一完整的中斷服務例程。除了為Nucleus SE提供所有所需的定時功能外,它還可以作為如何編寫受管中斷的示例。
RTC ISR操作
RTC ISR提供的設施已在前面的一篇文章中概述,其中涵蓋了Nucleus SE中的系統時間這一廣泛主題。根據應用程式的配置方式,所有功能都是可選的。這是RTC ISR的完整代碼。
#if NUSE_TIMER_NUMBER != 0
{
U8 timer;
for (timer=0; timer
<nuse_timer_number; timer++)="">
if
(NUSE_Timer_Status[timer])
{
if (–NUSE_Timer_Value[timer] == 0)
NUSE_Timer_Expirations_Counter[timer]++;
#if NUSE_TIMER_EXPIRATION_ROUTINE_SUPPORT ||
NUSE_INCLUDE_EVERYTHING
if (NUSE_Timer_Expiration_Routine_Address[timer]
!= NULL)
((PF1)NUSE_Timer_Expiration_Routine_Address[timer])
NUSE_Timer_Expiration_Routine_Parameter[timer]);
}
#endif
/* reschedule? */
if
(NUSE_Timer_Reschedule_Time[timer] != 0)
{
/* yes: set up time */
NUSE_Timer_Value[timer] =
NUSE_Timer_Reschedule_Time[timer];
else
{
/* no: disable */
NUSE_Timer_Status[timer] = FALSE;
}
}
}
#if NUSE_SYSTEM_TIME_SUPPORT || NUSE_INCLUDE_EVERYTHING
NUSE_Tick_Clock++;
#if NUSE_TASK_SLEEP || NUSE_INCLUDE_EVERYTHING
U8 task;
for (task=0; task
<nuse_task_number; task++)=""> {
if (NUSE_Task_Timeout_Counter[task]
!= 0)
NUSE_Task_Timeout_Counter[task]–;
if (NUSE_Task_Timeout_Counter[task] == 0)
NUSE_Wake_Task(task);
}
}
#if NUSE_SCHEDULER_TYPE == NUSE_TIME_SLICE_SCHEDULER
if (–NUSE_Time_Slice_Ticks == 0)
{
NUSE_Reschedule();
}
我們将研究RTC ISR的四個功能領域:
計時器
如果配置了任何應用程式計時器,ISR将通過遞減其計數器值來循環為每個計時器提供服務。如果計時器過期(即計數器達到零),則兩個操作将生效:
如果配置了計時器過期例程,并且計時器具有指向函數的有效(非空)指針(在NUSE_timer_expiration_Routine_Address[]),則執行例程,并從NUSE_timer_expiration_Routine_parameter[]接收參數。
如果計時器有一個重定時時間(即NUSE_timer_reschedule_time[]中的非零值),則計時器将重新加載該值。
應用程式計時器在上一篇文章中有更詳細的描述。
系統時鐘
如果配置了系統時鐘,那麼NUSE_Tick_clock的值隻會遞增。關于系統時間的進一步讨論可以在上一篇文章中找到。
任務睡眠
如果啟用了任務休眠(即配置了API調用NUSE_task_sleep()),則會檢查每個任務的逾時計數器(NUSE_task_timeout_counter[]中的條目),如果不是零,則遞減。如果任何計數器達到零,則相應的任務将被喚醒。
時間片排程
如果正在使用時間片排程程式,則時間片計數器(NUSE_time_slice_Ticks)将遞減。如果達到零,則調用排程程式。對NUSE_Reschedule()的調用負責重置計數器。
有管理的中斷
對RTC ISR是受管中斷的原因進行一些解釋可能是有用的,因為在正确的情況下,使用者可能希望将其重新編碼為本機中斷,以減少開銷。例如,如果隻使用系統時間工具(即沒有應用程式計時器、沒有任務睡眠和時間片排程器),則本機中斷就可以了。管理中斷的需求如下:
人工智能晶片與自動駕駛