基於STM32F1開發平臺,從頭開始完成一個基於時間片輪詢和優先順序搶占的實時任務調度內核 ...
今年大四,在準備自己的畢業設計。因為畢設題目是一個比較複雜的多感測器監控的嵌入式系統,然後最近自己有使用一些rtos,比方說freertos和ucos,感覺比起單純對單片機的裸機開發還是有很多好玩的地方。特別喜歡這種搶占式和時間片輪詢這兩種內核調度模式,所以最近在開始想自己嘗試去寫一個實時的操作系統的內核調度,看看用自己淺薄的技術,自己去實現會怎麼弄,純粹為了好玩哈哈哈。花了大概幾天左右的時間,現在已完成了一個時間片輪詢和優先順序搶占的實時任務調度內核了,可能有些地方還有些bug,後面有空再慢慢修改,希望通過這個博客記錄一下,為以後的開發養成記錄和保存的習慣,後面有時間慢慢添加內容。
先說一下硬體平臺,我使用的STMF1系列的單片機,F1系列採用的內核是ARM的Crotex M3內核,最高主頻 72MHz。使用的開發軟體是MDK4.0,參考的操作系統是freertos和ucos,重要參考書籍:《嵌入式操作系統內核調度:底層開發者手冊》,《CM3權威指南CnR2》。
一、關於Crotex M3內核的一些小知識
1.ARM的Crotex M3內核使用的事Thumb-2指令集。Thumb-2是16位Thumb 指令集的一個超集,在Thumb-2中,16位指令首次與32位指令並存,無需煩心地把處理器狀態在Thumb和ARM之間來回的切換。
2.Crotex-M3 處理器擁有 R0-R15 的寄存器組。其中 R13 作為堆棧指針 SP。SP 有兩個,但在同 一時刻只能有一個可以看到,這也就是所謂的“banked”寄存器。R0-R12是通用寄存器。R0-R12 都是 32 位通用寄存器,用於數據操作(註意:絕大多數 16 位 Thumb 指令只能訪 問 R0-R7,而 32 Thumb-2 指令可以訪問所有寄存器)。
3.R13寄存器(SP):Cortex-M3 擁有兩個堆棧指針,然而它們是 banked,因此任一時刻只能使用其中的一個。主堆棧指針(MSP):複位後預設使用的堆棧指針,用於操作系統內核以及異常處理常式(包 括中斷服務常式);進程堆棧指針(PSP):由用戶的應用程式代碼使用。堆棧指針的最低兩位永遠是 0,這意味著堆棧總是 4 位元組對齊的。
4.R14寄存器(LR):當呼叫一個子程式時,由 R14 存儲返回地址。
5.R15寄存器(PC):指向當前的程式地址。如果修改它的值,就能改變程式的執行流。
6.Cortex-M3 還在內核水平上搭載了若幹特殊功能寄存器,包括:程式狀態字寄存器組(PSRs)、中斷屏蔽寄存器組(PRIMASK, FAULTMASK, BASEPRI)、控制寄存器(CONTROL),具體功能請翻閱《CM3權威指南CnR2》第二章。
7.Cortex-M3 處理器支持兩種處理器的操作模式,還支持兩級特權操作。兩種操作模式分別為:處理者模式和線程模式。引入兩個模式的本意,是用於區別普通應用程式的代碼和異常服務常式的代碼——包括中斷服務常式的代碼。Cortex-M3 的另一個側面則是特權的分級——特權級和用戶級。這可以提供一種存儲器訪問的保護機制,使得普通的用戶程式代碼不能意外地,甚至是惡意地執行涉及到要害的操作。處理器 支持兩種特權級,這也是一個基本的安全模型。(引自《CM3權威指南CnR2》)
操作系統的內核通常都在特權級下執行,所有沒有被MPU禁掉的存儲器都可以訪問。在操作系統開啟了一個用戶程式後,通常都會讓它在用戶級下執行,從而使系統不會因某個程式的崩潰或惡意破壞而受損。這個是很多rtos需要用到SVC這個彙編指令觸發SVC軟中斷的原因,因為程式在用戶級的時候如果產生PendSV中斷會引發硬體異常,導致程式奔潰;但是程式進入中斷回擁有特權及許可權,所以可以通過觸發軟中斷,在軟中斷裡面出大PendSV中斷進行任務調度,保證實時任務的上下文切換。
8.Cortex-M3 在內核水平上搭載了一個異常響應系統, 支持為數眾多的系統異常和外部中斷。其中,編號為 1-15 的對應系統異常,大於等於 16 的則全是外部中斷。除了個別異常的優先順序被定死外,其它異常的優先順序都是可編程的。優先順序的數值越小,則優先順序越高。 CM3 支持中斷嵌套,使得高優先順序異常會搶占(preempt)低優先順序異常。有 3 個系統異常:複位, NMI 以及硬 fault,它們有固定的優先順序,並且它們的優先順序號是負數,從而高於所有其它異常。所有其它異常的優先順序則都是可編程的。
9.關於搶占優先順序與子優先順序。NVIC 中有一個寄存器是“應用程式中斷及複位控制寄存器”(內容見表 7.5),它裡面有一個位段名為“優先順序組”。該位段的值對每一個優先順序可配置的異常都有影響——把其優先順序分為 2 個位段: MSB 所在的位段(左邊的)對應搶占優先順序,而 LSB 所在的位段(右邊的) 對應子優先順序。如下表:
STM32 的中斷向量具有兩個屬性,一個為搶占屬性,另一個為響應屬性,搶占,是指打斷其他中斷的屬性,即因為具有這個屬性會出現嵌套中斷(在執行中斷服務函數 A 的過程中被中斷 B 打斷,執行完中斷服務函數 B 再繼續執行中斷服務函數A),搶占屬性由 NVIC_IRQChannelPreemptionPriority 的參數配置。而響應屬性則應用在搶占屬性相同的情況下,當 兩個中斷向量的搶占優先順序相同時,如果兩個中斷同時到達,則相應優先順序更高的中斷,由NVIC_IRQChannelSubPriority 參數配置。NVIC 只可配置 16 種中斷向量的優先順序,也就是說,搶占優先順序和響應優先順序的數量由一個 4 位的數字來決定,把這個4位數字的位數分配成搶占優先順序部分和響應優先順序部分。有 5 組分配方式,其中第 4 組:所有 4 位用來配置搶占優先順序,即 NVIC 配置的 24 =16 種中斷向量都是只有搶占屬性,沒有響應屬性。
所以,一個搶占式的實時操作系統,中斷優先順序分組應該配置位第4組。
10.SVC(系統服務調用,亦簡稱系統調用)和 PendSV(可懸起系統調用),它們多用在上了操作系統的軟體開發中。 SVC 用於產生系統函數的調用請求。例如,操作系統通常不讓用戶程式直接訪問硬體,而是通過提供一些系統服務函數,讓用戶程式使用 SVC 發出對系統服務函數的呼叫請求,以這種方法調用它們來間接訪問硬體。因此,當用戶程式想要控制特定的硬體時,它就要產生一個SVC 異常,然後操作系統提供的 SVC 異常服務常式得到執行,它再調用相關的操作系統函數,後者完成用戶程式請求的服務。
這種“提出要求——得到滿足”的方式,很好、很強大、很方便、很靈活、很能可持續發展。首先,它使用戶程式從控制硬體的繁文縟節中解脫出來,而是由 OS 負責控制具體的硬體。第二,OS 的代碼可以經過充分的測試,從而能使系統更加健壯和可靠。第三,它使用戶程式無需在特權級序變得與硬體無關,因此在開發應用程式時無需瞭解硬體的操作細節,從而簡化了開發的難度和繁瑣度,並且使應用程式跨硬體平臺移植成為可能。開發應用程式唯一需要知道的就是操作系統提供的應用編程介面( API),並且在瞭解了各個請求代號和參數表後,就可以使用 SVC 來提出要求了。SVC 異常通過執行”SVC”指令來產生。該指令需要一個立即數,充當系統調用代號。 SVC 異常服務常式稍後會提取出此代號,從而獲知本次調用的具體要求,再調用相應的服務函數。例如,
SVC 0x3 ; 調用 3 號系統服務
在 SVC 服務常式執行後,上次執行的 SVC 指令地址可以根據自動入棧的返回地址計算出。找到了 SVC 指令後,就可以讀取該 SVC 指令的機器碼,從機器碼中萃取出立即數,就獲知了請求執行的功能代號。如果用戶程式使用的是 PSP,服務常式還需要先執行 MRS Rn, PSP 指令來獲取應用程式的堆棧指針。通過分析 LR 的值,可以獲知在 SVC指令執行時,正在使用哪個堆棧。
11.另一個相關的異常是 PendSV(可懸起的系統調用),它和 SVC 協同使用。一方面, SVC 異常是必須在執行 SVC 指令後立即得到響應的(對於 SVC 異常來說,若因優先順序不比當前正處理的高,或是其它原因使之無法立即響應,將上訪成硬 fault),應用程式執行 SVC 時都是希望所需的請求立即得到響應。另一方面, PendSV 則不同,它是可以像普通的中斷一樣被懸起的(不像SVC 那樣會上訪)。 OS 可以利用它“緩期執行”一個異常——直到其它重要的任務完成後才執行動作。懸起 PendSV 的方法是:手工往 NVIC 的 PendSV 懸起寄存器中寫 1。懸起後,如果優先順序不夠高,則將緩期等待執行。PendSV 異常會自動延遲上下文切換的請求,直到其它的 ISR 都完成了處理後才放行。為實現這個機制,需要把 PendSV 編程為最低優先順序的異常。
以上內容為基於CM3內核開發一個實時操作系統我們需要知道的一些關於CM3的知識,建議去看《CM3權威指南CnR2》。
////////////暫時更新到這裡,後面再慢慢寫-2016-12-09//////////////