UCosII移植之OS_CPU.H詳解
一資料型別
/******************************************************************************
* 定義資料型別
******************************************************************************/
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U; /*Unsigned 8 bit quantity */
typedef signed char INT8S; /*Signed 8 bit quantity */
typedef unsigned short INT16U; /* Unsigned 16 bitquantity */
typedef signed short INT16S; /* Signed 16bit quantity */
typedef unsigned int INT32U; /*Unsigned 32 bit quantity */
typedef signed int INT32S; /*Signed 32 bit quantity */
typedef float FP32; /*Single precision floating point*/
typedef double FP64; /*Double precision floating point*/
typedef unsigned int OS_STK; /*Each stack entry is 32-bit wide*/
typedef unsigned int OS_CPU_SR; /*Define size of CPU status register*/
解析:因為CM3是32位寬的,所有OS_STK被型別重定義成32位的unsigned int,CM3的狀態暫存器(xPSR)也是32位寬的,OS_CPU_SR被型別重定義為unsigned int型別重定義是為了方便移植,不同處理器編譯不一樣,比如ARM7 處理器字長為32位,半字長為16位,位元組為8位,因此在移植時涉及到變數型別時只需要修改型別重定義即可。
二臨界區
/*******************************************************************************OS_CRITICAL_METHOD=1直接使用處理器的開關中斷指令來實線巨集
*OS_CRITICAL_METHOD=2 利用堆疊保護和恢復CPU狀態
*OS_CRITICAL_METHOD=3 利用編譯器擴充套件功能獲得的程式狀態字,儲存在區域性變數cpu_sr中
******************************************************************************/
#define OS_CRITICAL_METHOD 3
/********選擇第三種方法********************/
#if OS_CRITICAL_METHOD == 3
/********進入臨界程式碼區********************/
#define OS_ENTER_CRITICAL() {cpu_sr =OS_CPU_SR_Save();}
/*********退出臨界程式碼區*****************/
#define OS_EXIT_CRITICAL() {OS_CPU_SR_Restore(cpu_sr);}
#endif
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR OS_CPU_SR_Save(void);
void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);
解析:一般喜歡使用第三種方法來實現臨界區。
第一種直接開關中斷的實現臨界區的方法很少採用,因為這種方法可能將原本關閉了的中斷意外的開啟。
(簡單的如51微控制器的開關中斷指令EA=1與EA=0,即實現直接使用處理器的開關中斷,但問題是呼叫了OS_EXIT_CRITICAL後,處理器的中斷一定為開啟,而無論在進入臨界程式碼之前的處理器的中斷是開啟或者關閉。但最理想的狀態應該是,在用了OS_EXIT_CRITICAL後,處理器的中斷開啟與否狀態,應該跟進入臨界程式碼段之前處理器的中斷狀態一致)
第二種方法是最高效的實現方法,但是這種方法調整了堆疊指標,對於需要利用堆疊指標間接定址區域性變數的系統並不適用。(x86通常採用第二種方法,因為它有單獨的暫存器來做區域性變數的定址)
(即在關閉中斷之前,通過堆疊快取CPU的中斷暫存器狀態,即記住進入臨界程式碼之前CPU的中斷開啟與否狀態,然後在推出臨界程式碼的時候,只需將堆疊中的CPU中斷狀態暫存器彈出堆疊,這樣即能實現用了巨集OS_EXIT_CRITICAL後,處理器的中斷開啟與否狀態,跟進入臨界程式碼段之前處理器的中斷狀態一致)
第三種方法最保險,雖然效率比第二種方法略低一點。
(一些處理器可以讀取PSW暫存器的值,將包含中斷開關狀態的PSW暫存器值保持在C語言變數中沒在退出臨界程式碼段時,再將區域性變數中的值回寫到PSW暫存器中。STM32可以通過MRS與MSR指令讀寫特殊暫存器,因此選用了第三種方式)
OS_CPU_SR_Save()和OS_CPU_SR_Restore(cpu_sr)在os_cpu_a.asm中實現
其實也可以通過C程式碼(OS_CPU_C.C)中插入彙編的方式來實現:
1. __asm OS_CPU_SR OS_CPU_SR_Save(void)
2. {
3. MRS R0, PRIMASK ; Set prio int mask to mask all (except faults)
4. CPSID I
5. BX LR
6. }
7. __asm void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr)
8. {
9. MSR PRIMASK, R0
10. BX LR
11. }
三堆疊
#define OS_STK_GROWTH 1
CM3中,棧是由高地址向低地址生長的,因此OS_STK_GROWTH設為1
四任務切換
#define OS_TASK_SW() OSCtxSw()
任務切換巨集,由彙編實現,在os_cpu_a.asm中
五函式原型宣告
未作修改應該是這樣
void OSCtxSw(void);
void OSIntCtxSw(void);
void OSStartHighRdy(void);
void OS_CPU_PendSVHandler(void);
void OS_CPU_SysTickHandler(void);
void OS_CPU_SysTickInit(INT32U cnts);
修改:
A:將OS_CPU_PendSVHandler(void)改為PendSV_Handler(void)
B:註釋掉
OS_CPU_SysTickHandler(void)和OS_CPU_SysTickInit(INT32U cnts)
詳細分析:
A:修改為PendSV_Handler
UCOSII在stm32中的上下文切換是通過觸發一個PendSV的異常(優先順序較低),然後再異常服務程式裡實現的。os_cpu_a.asm中標號為OS_CPU_PendSVHandler和OS_CPU_PendSVHandler_nosave的內容即是。
但是Micrium官方沒有ST公司官方的啟動程式碼,用的是自己的啟動程式碼,在startup_stm32f10x_cl.s中看到在啟動設定中斷向量表時,PendSV的異常標號為PendSV_Handler,因此兩者有矛盾,可選擇不改變ST官方的檔案,即不改變啟動程式碼,修改ucosII的介面檔案中的程式碼
修改PendSV
(1)在os_cpu.a.asm中
(2)在os_cpu.h中
(3)在stm32f10x_it.h中
B:註釋掉OS_CPU_SysTickHandler(void)
OS_CPU_SysTickHandler(void)定義在os_cpu_c.c中,是systick的中斷處理函式,而在stm32f10x_it.c中已經有該函式的定義
C:註釋掉OS_CPU_SysTickInit(INT32U cnts)
OS_CPU_SysTickInit(INT32U cnts)定義在os_cpu_c.c中,用以初始化systick定時器,此函式我們自己實現,故註釋掉