⑤這兩個函數都用于任務切換,它們的本質都是觸發PendSV中斷,具體切換過程在PendSV的中斷函數中進行,其中OSCtxSw是任務級切換,OSIntCtxSw是中斷級切換,是從中斷退出時切換到一個任務中,從中斷切換到任務的過程中,CPU的寄存器入棧工作已經完成。
OSCtxSw
PUSH {R4, R5}
LDR R4, =NVIC_INT_CTRL ;觸發PendSV異常
LDR R5, =NVIC_PENDSVSET
STR R5, [R4] ;向NVIC_INT_CTRL寫入NVIC_PENDSVSET觸發PendSV中斷
POP {R4, R5}
BX LR
OSIntCtxSw
PUSH {R4, R5}
LDR R4, =NVIC_INT_CTRL ;觸發PendSV異常
LDR R5, =NVIC_PENDSVSET
STR R5, [R4] ;向NVIC_INT_CTRL寫入NVIC_PENDSVSET觸發PendSV中斷
POP {R4, R5}
BX LR
NOP
⑥這部分代碼才是真正的任務切換函數,通過觸發PendSV中斷來進入該函數內進行任務切換
PendSV_Handler
CPSID I ;任務切換過程中必須關閉所有中斷
MRS R0, PSP ;如果在用PSP堆棧,則可以忽略保存寄存器
CBZ R0, PendSV_Handler_Nosave ;如果PSP為0就轉移到PendSV_Handler_Nosave
SUBS R0, R0, #0x20 ;R0-=20H
STM R0, {R4-R11}
LDR R1, =OSTCBCur
LDR R1, [R1]
STR R0, [R1]
PendSV_Handler_Nosave
PUSH {R14} ;保存R14的值
LDR R0, =OSTaskSwHook ;調用OSTaskSwHook()
BLX R0
POP {R14}
LDR R0, =OSPrioCur
LDR R1, =OSPrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
LDR R0, =OSTCBCur
LDR R1, =OSTCBHighRdy
LDR R2, [R1]
STR R2, [R0]
LDR R0, [R2] ;R0作為新任務的SP
LDM R0, {R4-R11} ;從堆棧中恢復R4-R11
ADDS R0, R0, #0x20
MSR PSP, R0 ;用新任務的SP加載PSP
ORR LR, LR, #0x04 ;確保LR的bit2為1,返回后使用進程堆棧
CPSIE I ;開啟所有中斷
BX LR ;中斷返回
end
(2)os_cpu.h文件詳解
①這部分主要用于定義一些數據類型,其中重點關注OS_STK這個數據類型,我們在定義任務堆棧的時候就是該類型數據,這是一個32位的數據類型,按字節算的話實際堆棧大小是我們定義的4倍。
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U;
typedef signed char INT8S;
typedef unsigned short INT16U;
typedef signed short INT16S;
typedef unsigned int INT32U;
typedef signed int INT32S;
typedef float FP32;
typedef double FP64;
typedef unsigned int OS_STK;
typedef unsigned int OS_CPU_SR;
②這部分代碼定義了堆棧的增長方向,任務機切換的宏定義OS_TASK_SW,如果OS_CRITICAL_METHOD被定義為3的話那么進出臨界段的宏定義分別為OS_ENTER_CRITICAL和OS_EXIT_CRITICAL,這兩個函數都是用匯編語言編寫的
//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
void OSCtxSw(void);
void OSIntCtxSw(void);
void OSStartHighRdy(void);
void OSPendSV(void);
#if OS_CRITICAL_METHOD == 3u
OS_CPU_SR OS_CPU_SR_Save(void);
void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);
#endif
OS_CPU_EXT INT32U OSInterrputSum;
(3)sys.h文件修改
添加關于條件編譯的定義,在文件中添加以下代碼即可。
#define SYSTEM_SUPPORT_OS 1
當宏定義為1的時候,編譯器在編譯的時候會只編譯滿足條件的代碼,當為0時,這部分代碼不會被編譯。
(4)delay.c文件修改
①添加Sys_Tick中斷服務函數與函數定義
#include "includes.h"
//支持UCOSII
#ifdef OS_CRITICAL_METHOD
#define delay_osrunning OSRunning //OS是否運行標記,0,不運行;1,在運行
#define delay_ostickspersec OS_TICKS_PER_SEC //OS時鐘節拍,即每秒調度次數
#define delay_osintnesting OSIntNesting //中斷嵌套級別,即中斷嵌套次數
#endif
//systick中斷服務函數,使用OS時用到
void SysTick_Handler()
{
//OS開始跑了,才執行正常的調度處理
if( delay_osrunning==1 )
{
OSIntEnter() ; //進入中斷
OSTimeTick() ; //調用ucos的時鐘服務程序
OSIntExit() ; //觸發任務切換軟中斷
}
}
②時鐘初始化函數修改
void SysTick_Init( u8 SYSCLK )
{
#if SYSTEM_SUPPORT_OS
u32 reload;
#endif
SysTick->CTRL &= ~( 1<<2 ) ; //SYSTICK使用外部時鐘源
fac_us = SYSCLK/8 ; //fac_us都需要使用
#if SYSTEM_SUPPORT_OS
reload = SYSCLK/8 ; //每秒鐘的計數次數,單位為K
reload *= 1000000/delay_ostickspersec ; //根據delay_ostickspersec設定溢出時間
fac_ms = 1000/delay_ostickspersec ; //代表OS可以延時的最少單位
SysTick->CTRL |= 1<<1 ; //開啟SYSTICK中斷
SysTick->LOAD = reload ; //每1/delay_ostickspersec秒中斷一次
SysTick->CTRL |= 1<<0 ; //開啟SYSTICK
#else
fac_ms = ( u16 )fac_us*1000 ; //代表每個ms需要的systick時鐘數
#endif
}
-
單片機
+關注
關注
6035文章
44554瀏覽量
634673 -
操作系統
+關注
關注
37文章
6803瀏覽量
123285 -
uCOS-Ⅱ
+關注
關注
0文章
9瀏覽量
8585
發布評論請先 登錄
相關推薦
評論