stm32超輕量操作系統(tǒng)之任務(wù)調(diào)度

這是簡單STM32 OS的第一章stm32超輕量操作系統(tǒng)之搶占式內(nèi)核

在這個最簡程序中只有兩個任務(wù)交替執(zhí)行盐碱,任務(wù)一和任務(wù)二瓮顽,兩個任務(wù)分別控制兩個LED燈的亮滅。只是完成了最簡單的任務(wù)切換功能聘惦。

這一版的程序中沒有加入像一般的OS中調(diào)用延時函數(shù)時會發(fā)生任務(wù)調(diào)度的功能儒恋,也沒有優(yōu)先級,沒有時間片禀酱,只是兩個任務(wù)不斷交替執(zhí)行牧嫉。在第二章中會加入搶占式內(nèi)核和延時功能减途。

STM32的任務(wù)調(diào)度可以有兩種方式

1.通過systick_handler定時器調(diào)度

2.執(zhí)行一個系統(tǒng)調(diào)用

cortexM3的寄存器只有16個鳍置,cortexM4除了這16個還有很多浮點運算和MPU單元送淆,如果不用這些單元它和M4沒有區(qū)別,我是用的cortexM4內(nèi)核的STM32F407辟拷,因為沒有用浮點運算和MPU保護單元因此OS也和M3內(nèi)核兼容衫冻。


?圖一.16個寄存器示意??

因此在任務(wù)調(diào)度的過程中谒出,也是不斷的保存現(xiàn)在任務(wù)的這16個寄存器,彈出下一個任務(wù)的16個寄存器考赛。

任務(wù)調(diào)度的步驟總結(jié)為如下:

1.保存程序的上下文即當前任務(wù)的寄存器莉测,保存存儲寄存器的任務(wù)堆棧的地址捣卤。

2.根據(jù)下一個任務(wù)的任務(wù)堆棧地址依次彈出下一個任務(wù)的16個寄存器八孝。

堆棧中的寄存器保存順序如下,

XPSR

SP(代表MSP或PSP,在任務(wù)調(diào)度完成后子姜,根據(jù)PSP的值定位了是哪個任務(wù))

LR(存儲函數(shù)的返回)

R12

R3

R2

R1

R0

R11

R10

R9

R8

R7

R6

R5

R4

具體寄存器的功能可以查看這位博主的文章 https://blog.csdn.net/sagitta_zl/article/details/51318507

接下來根據(jù)程序執(zhí)行的順序解釋程序


圖二.程序流程圖

首先介紹幾個定義的變量

1)TCB程序控制塊哥捕,程序控制塊是一個結(jié)構(gòu)體嘉熊,其中存儲了每一個任務(wù)的堆棧的地址指針。

2)taskTCB[2]阐肤,最多兩個任務(wù)TCB讲坎。程序中定義了最大的同時執(zhí)行的任務(wù)數(shù)量為2晨炕,也即只有兩個任務(wù)互相交替厚满,為了簡化也沒有加入IdleTask。

3)uint32_t stack[100]遵馆,任務(wù)堆棧的大小為丰榴。也即是100*4個字節(jié)大小四濒。當任務(wù)的嵌套層數(shù)很多,或很長有很多局部變量時要增大任務(wù)的堆棧戈二。局部變量保存在了堆棧中喳资。

4)TCB *currTCB,*nextTCB分別存儲了當前任務(wù)和下一個任務(wù)的TCB,在任務(wù)切換的時候使用

1. OSInit()

OSInit中執(zhí)行了對TCB的初始化鲜滩,后面根據(jù)TCB初始化的值可以判定哪個TCB還處于空閑狀態(tài)可以放入新的任務(wù)节值。很簡單,棧頂指針初始化為了NULL嗓蘑,后面就通過判斷是不是為NULL來判定這個TCB還能不能用匿乃。

void OSInit()

{

int i = 0;

for(i = 0;i<MAX_TASK_NUMBER;i++)

{

taskTCB[i].topStackPtr = NULL;

}

currTCB = &taskTCB[0];

nextTCB = &taskTCB[0];

}

2. 新建任務(wù)堆棧

新建任務(wù)堆棧是通過申請了一個靜態(tài)的數(shù)組扳埂,也即是uint32_t stack[100]當作堆棧,如果采用動態(tài)分配的話要涉及到內(nèi)存管理梅尤,現(xiàn)階段簡單的任務(wù)沒有必要加上,一切從簡赡盘。

3. 新建任務(wù)

根據(jù)任務(wù)的地址缰揪,任務(wù)的堆棧钝腺,就可以新建任務(wù)了

void OSCreateNewTask(void (*fun)(void),uint32_t *stackAddress)

{

int i = 0;

//進入臨界區(qū),關(guān)中斷定硝,也就是在臨界區(qū)之內(nèi)不發(fā)生中斷

EnterCriticalRegion();

//尋找空閑的任務(wù)塊

while(taskTCB[i].topStackPtr!=NULL)

{

i++;

}

//初始化任務(wù)的棧毫目,棧存儲的寄存器順序不能錯,順序見圖一

*stackAddress? ? = (uint32_t)0x01000000uL;? //xPSR的值箱蟆,有個1代表是thumb模式

*(--stackAddress) = (uint32_t)fun;? ? ? ? ? //存儲了要執(zhí)行的任務(wù)的地址

*(--stackAddress) = (uint32_t)0xffffffffuL;? //R14(LR)因為程序是個無限的大循環(huán)空猜,因此不返回

*(--stackAddress) = (uint32_t)0x12121212uL;? //R12

*(--stackAddress) = (uint32_t)0x03030303uL;? //R3

*(--stackAddress) = (uint32_t)0x02020202uL;? //R2

*(--stackAddress) = (uint32_t)0x01010101uL;? //R1

*(--stackAddress) = (uint32_t)0x00000000uL;? //R0

*(--stackAddress) = (uint32_t)0x11111111uL;? //R11

*(--stackAddress) = (uint32_t)0x10101010uL;? //R10

*(--stackAddress) = (uint32_t)0x09090909uL;? //R9

*(--stackAddress) = (uint32_t)0x08080808uL;? //R8

*(--stackAddress) = (uint32_t)0x07070707uL;? //R7

*(--stackAddress) = (uint32_t)0x06060606uL;? //R6

*(--stackAddress) = (uint32_t)0x05050505uL;? //R5

*(--stackAddress) = (uint32_t)0x04040404uL;? //R4

//TCB棧頂指針的初始化

taskTCB[i].topStackPtr = stackAddress;

//離開臨界區(qū)诺核,代表可以進行任務(wù)的切換

ExitCriticalRegion();

}

4. OSStart()

在這個部分中完成的任務(wù)比較重要窖杀,首先我們要知道PendSV中斷的作用裙士。前面提到了執(zhí)行任務(wù)切換的兩種方式,其中systick_handler就是通過調(diào)用PendSV來完成的任務(wù)切換桌硫。


圖二.任務(wù)調(diào)度實例

個中事件的流水賬記錄如下:

1)? 任務(wù) A 呼叫 SVC 來請求任務(wù)切換(例如铆隘,等待某些工作完成)

2)? OS 接收到請求南用,做好上下文切換的準備掏湾,并且 pend 一個 PendSV 異常融击。

3)? 當 CPU 退出 SVC 后雳窟,它立即進入 PendSV,從而執(zhí)行上下文切換拇涤。

4)? 當 PendSV 執(zhí)行完畢后誉结,將返回到任務(wù) B搓彻,同時進入線程模式。

5)? 發(fā)生了一個中斷怔接,并且中斷服務(wù)程序開始執(zhí)行

6)? 在 ISR 執(zhí)行過程中稀轨,發(fā)生 SysTick 異常,并且搶占了該 ISR奋刽。

7)? OS 執(zhí)行必要的操作,然后 pend 起 PendSV 異常以作好上下文切換的準備肚吏。

8)? 當 SysTick 退出后狭魂,回到先前被搶占的 ISR 中雌澄,ISR 繼續(xù)執(zhí)行

9)? ISR 執(zhí)行完畢并退出后,PendSV 服務(wù)例程開始執(zhí)行镐牺,并且在里面執(zhí)行上下文切換

10) 當 PendSV 執(zhí)行完畢后睬涧,回到任務(wù) A沛厨,同時系統(tǒng)再次進入線程模式逆皮。

可以看到PendSV的優(yōu)先級是最低的参袱,這樣才能夠不影響其他中斷的執(zhí)行,影響了實時性剿牺。在Systick中可以把PendSV掛起环壤,在ISR執(zhí)行完成后再執(zhí)行這個優(yōu)先級最低的中斷郑现。

__ASM void OSStart()

{

PRESERVE8

//關(guān)中斷

CPSID I

//設(shè)置PendSV的優(yōu)先級為最低

LDR R0,=NVIC_SYSPRI14 //R0 = NVIC_SYSPRI14

LDR R1,=NVIC_PENDSV_PRI //R1 = NVIC_PENDSV_PRI

STRB R1,[R0] //R0 = *R1

//賦PSP=0,代表是第一次執(zhí)行攒读,作用見下文

LDR R4,=0x0 //R4 = 0

MSR PSP,R4 //PSP = R4

//LDR R4,=0x3

//MSR CONTROL,R4

//掛起PendSV中斷辛友,通過直接寫寄存器的方式可以掛起

? LDR? R4, =NVIC_INT_CTRL? ? ? ? ? ? ?

? LDR? R5, =NVIC_PENDSVSET? ? ? ? ? ?

? STR? R5, [R4]? ? ? ? ? ? ? ? ? ? ? ?

//開中斷

CPSIE I

BX LR

nop //對齊

}

5. PendSV

在步驟4中程序的最后掛起了PendSV废累,因此一旦開啟了中斷,程序?qū)M入PendSV執(zhí)行日缨。在這一步中殿遂,需要理解到進入函數(shù)跳轉(zhuǎn)即進入PendSV時乙各,硬件自動會完成在PSP指向的地址中存儲xPSR,LR,SP,R0-R3,R12這8個寄存器的工作幢竹。因此完成這個步驟之后PSP=PSP-0x20,之后再在PSP指向的地址中存儲R4-R11 8個寄存器蹲坷。之后的彈出過程與之相反,先彈出R4-R11寄存器级乐,跳出PendSV后县匠,硬件自動完成剩余的8個寄存器的彈出乞旦,PSP=PSP+0x20

__ASM void PendSV_Handler()

{

extern PendSVFirst;

extern currTCB;

extern nextTCB;

PRESERVE8

CPSID I

//判斷PSP是否為0,如果是則代表是第一次執(zhí)行程序故痊,那么就沒有了保存當前寄存器這個過程玖姑,直接跳轉(zhuǎn)到彈出寄存器

MRS R0,PSP

CBZ R0,PendSVPopData

STMDB R0!,{R4-R11} //在R0中依次保存R4-R11寄存器 完成后R0=R0-0x20

LDR R1,=currTCB

LDR R1,[R1] //R1=currTCB->StackTopPtr

STR R0,[R1] //currTCB->StackTopPtr=R0焰络,保存當前任務(wù)上下文的最后一步豫领,也即當前任務(wù)的TCB保存了其任務(wù)堆棧棧頂?shù)闹羔槪瓿闪吮4妗?/p>

nop

//因為沒有BX跳轉(zhuǎn)指令舔琅,執(zhí)行完這個函數(shù)后接著會執(zhí)行下面的PendSVPopData()

}

__ASM void PendSVPopData()

{

extern PendSVFirst;

extern currTCB;

extern nextTCB;

PRESERVE8

LDR R0,=currTCB //R0=currTCB?

LDR R1,=nextTCB //R1=nextTCB

LDR R1,[R1] //R1=*R1

STR R1,[R0] //*R0=R1 currTCB=nextTCB完成了指向新的任務(wù)TCB的工作

LDR R0,[R1] //R0=*R1 R0保存了TCB堆棧棧頂?shù)闹羔?/p>

LDMIA R0!,{R4-R11} //依次彈出R4-R11等恐,完成后R0=R0+0x20

MSR PSP,R0 //PSP=R0

ORR LR,LR,#0x04 //LR=LR|0x04,表示函數(shù)返回后使用PSP指針

CPSIE I

BX LR

nop

}

6. OSSwitch()

OSSwitch函數(shù)只是簡單的更新了nextTCB备蚓,之后完成了觸發(fā)PendSV课蔬,即設(shè)置PendSV相應(yīng)寄存器的位為1

void OSSwitch()

{

EnterCriticalRegion();

if(currTCB==&taskTCB[0])

nextTCB = &taskTCB[1];

else

nextTCB = &taskTCB[0];

OSTaskSchedule();

ExitCriticalRegion();

}

7. EnterCtiticalRegion和ExitCriticalRegion很簡單,內(nèi)容就是開關(guān)中斷

__ASM void EnterCriticalRegion()

{

PRESERVE8

CPSID I? //關(guān)中斷

BX? ? LR //LR跳回

}

__ASM void ExitCriticalRegion()

{

PRESERVE8

CPSIE I

BX LR

}

至此就完了最簡單的任務(wù)切換功能郊尝,下一章將加入搶占式內(nèi)核二跋、時間片和延時進行調(diào)度的功能。

程序鏈接如下扎即,實驗用的是STM32F407的開發(fā)板

鏈接:https://pan.baidu.com/s/1my2HPG6shXB7QiwbR47Dnw

提取碼:j5hw


stm32超輕量操作系統(tǒng)之搶占式內(nèi)核

stm32超輕量操作系統(tǒng)之信號量與互斥量

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市况凉,隨后出現(xiàn)的幾起案子谚鄙,更是在濱河造成了極大的恐慌,老刑警劉巖刁绒,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闷营,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機傻盟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門速蕊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人娘赴,你說我怎么就攤上這事规哲。” “怎么了诽表?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵媳叨,是天一觀的道長。 經(jīng)常有香客問我关顷,道長糊秆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任议双,我火速辦了婚禮痘番,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘平痰。我一直安慰自己汞舱,他們只是感情好,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布宗雇。 她就那樣靜靜地躺著昂芜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪赔蒲。 梳的紋絲不亂的頭發(fā)上泌神,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音舞虱,去河邊找鬼欢际。 笑死,一個胖子當著我的面吹牛矾兜,可吹牛的內(nèi)容都是我干的损趋。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼椅寺,長吁一口氣:“原來是場噩夢啊……” “哼浑槽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起返帕,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤桐玻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后溉旋,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體畸冲,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年观腊,在試婚紗的時候發(fā)現(xiàn)自己被綠了邑闲。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡梧油,死狀恐怖苫耸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情儡陨,我是刑警寧澤褪子,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站骗村,受9級特大地震影響嫌褪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜胚股,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一笼痛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧琅拌,春花似錦缨伊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至党晋,卻和暖如春谭胚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背未玻。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工漏益, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人深胳。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓绰疤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親舞终。 傳聞我的和親對象是個殘疾皇子轻庆,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容