欧美极品高清xxxxhd,国产日产欧美最新,无码AV国产东京热AV无码,国产精品人与动性XXX,国产传媒亚洲综合一区二区,四库影院永久国产精品,毛片免费免费高清视频,福利所导航夜趣136

標(biāo)題: 51單片機(jī)的分時(shí)任務(wù)管理系統(tǒng)設(shè)計(jì)_coos [打印本頁(yè)]

作者: niuniu    時(shí)間: 2015-6-9 03:48
標(biāo)題: 51單片機(jī)的分時(shí)任務(wù)管理系統(tǒng)設(shè)計(jì)_coos
Coos簡(jiǎn)介

   最近開始為51寫一個(gè)分時(shí)任務(wù)管理,取名coos,終于也差不多把功能完善了,趁這幾天還有點(diǎn)沖動(dòng)發(fā)布上來(lái)吧。Coos最大的特色:完全用C語(yǔ)言實(shí)現(xiàn)。讓我不禁原創(chuàng)一句:敲一敲鍵盤,不留下一句匯編。呵呵~~~接下來(lái)吐槽一下先。
  
吐槽
51上實(shí)現(xiàn)任務(wù)管理?省省吧,有時(shí)間不如去搞armLinux?那才是高深技術(shù)!如果你是這樣想的,那這篇文章你沒(méi)必要再看下去了…….如果你覺(jué)得不把51玩?zhèn)透徹,使盡腦汁就是要把它發(fā)揮到極致,就像PC上很多高手,聽(tīng)說(shuō)有不少人拿CPU來(lái)超頻,玩得可以聞到燒焦味,偶爾主板冒起煙來(lái),甚至?xí)r不時(shí)地噴出火花(那種感覺(jué)是帥啊~~)。就算不是發(fā)燒友,但至少溫度稍高一點(diǎn)點(diǎn)吧。如果是這樣的話,那么,兄弟………………..知音吶!嗚嗚嗚,海內(nèi)難道真的存知己?

還是轉(zhuǎn)入正題吧。貌似已經(jīng)有不少高手為51寫系統(tǒng)了,ucos也算是可以移植到51上(我沒(méi)測(cè)試過(guò)),嵌入式實(shí)時(shí)內(nèi)核,但是畢竟占用資源太多,而且讓我感覺(jué)學(xué)術(shù)性大于使用性,好像很少人用?(至少我沒(méi)用過(guò))。俺的分時(shí)任務(wù)管理,算不上系統(tǒng),估計(jì)也經(jīng)不起什么考驗(yàn),不過(guò)俺還是寫了出來(lái),畢竟自己寫的東西自己用著才舒服嘛。老是用別人提供的玩久了也無(wú)聊。慢慢完善俺的coos吧,下次在把它移植到stm32上,嘿嘿~~

為什么寫這個(gè)分時(shí)任務(wù)管理?嗯,這個(gè)問(wèn)題問(wèn)得好!(口氣來(lái)自9527…)。這學(xué)期上了一門課,微型計(jì)算機(jī)原理與接口技術(shù),加上看了ucos一點(diǎn)點(diǎn)任務(wù)管理代碼,心里那個(gè)是不淡定啊。感覺(jué)任務(wù)管理也不難,何不嘗試自己寫一個(gè)任務(wù)管理?再加上本人實(shí)在無(wú)聊,就決定寫出這個(gè)無(wú)聊的任務(wù)管理了。
  
Coos的命名
先說(shuō)一下命名,本人網(wǎng)名coolweedman,操作系統(tǒng)簡(jiǎn)寫為OScoolweedman+OS讓我只能想到coos,所以就命名coos了,譯音“褲子”,在51上跑coos,我就想到了給51穿一條褲子呵呵,習(xí)慣裸跑,偶爾加條褲子感覺(jué)還行。
  
Coos的任務(wù)管理原理
說(shuō)一下任務(wù)管理的原理。學(xué)過(guò)微機(jī)的同學(xué)就知道,無(wú)非是直接或間接去修改PC指針。沒(méi)錯(cuò)。我這里是間接修改PC指針的。怎么個(gè)間接法?利用出棧來(lái)修改!如果入棧的時(shí)候PC指針入棧,那么出棧的時(shí)候就是對(duì)PC指針的修改了。只要控制出棧時(shí)PC指針的值,就可以實(shí)現(xiàn)任務(wù)調(diào)度了。具體做法是:
1.      定時(shí)器產(chǎn)生中斷(分時(shí)的依據(jù))
2.      中斷引起入棧,(PC指針入棧,其他寄存器也有)
3.      定時(shí)器響應(yīng)函數(shù)判斷需要執(zhí)行哪個(gè)任務(wù)
4.      修改SP指針為相應(yīng)任務(wù)的SP,(注意不是PC指針)
5.      定時(shí)器按照相應(yīng)的SP出棧,實(shí)現(xiàn)間接修改PC指針(任務(wù)調(diào)度)
大概思路就是這樣,算是偽代碼吧。然后把相應(yīng)的偽代碼用C語(yǔ)言實(shí)現(xiàn),就完成了(當(dāng)然其中還有很多細(xì)節(jié)部分,編寫代碼時(shí)才會(huì)發(fā)現(xiàn))。


Coos的任務(wù)管理機(jī)制
再說(shuō)一下任務(wù)管理機(jī)制吧。分時(shí)任務(wù)管理,一個(gè)周期20個(gè)時(shí)間片(多少個(gè)可配置,時(shí)間片具體時(shí)間也可配置),在這20個(gè)時(shí)間片上,運(yùn)行任務(wù)。coos最多管理8個(gè)任務(wù)(多少個(gè)可配置),每個(gè)任務(wù)時(shí)間片在創(chuàng)建時(shí)候指定。比如有5個(gè)任務(wù),時(shí)間片分別為2310116。那么coos執(zhí)行任務(wù)是這樣的。

第一次:
任務(wù)1執(zhí)行1個(gè)時(shí)間片
任務(wù)2執(zhí)行1個(gè)時(shí)間片
任務(wù)3執(zhí)行1個(gè)時(shí)間片
任務(wù)4執(zhí)行1個(gè)時(shí)間片
任務(wù)5執(zhí)行1個(gè)時(shí)間片
(已占用5個(gè)時(shí)間片)

第二次:
任務(wù)1執(zhí)行1個(gè)時(shí)間片
任務(wù)2執(zhí)行1個(gè)時(shí)間片
任務(wù)3執(zhí)行1個(gè)時(shí)間片
任務(wù)4執(zhí)行1個(gè)時(shí)間片
任務(wù)5執(zhí)行1個(gè)時(shí)間片
(已占用10個(gè)時(shí)間片)

第三次:(任務(wù)12個(gè)時(shí)間片執(zhí)行完,不執(zhí)行任務(wù)1
任務(wù)2執(zhí)行1個(gè)時(shí)間片
任務(wù)3執(zhí)行1個(gè)時(shí)間片
任務(wù)4執(zhí)行1個(gè)時(shí)間片
任務(wù)5執(zhí)行1個(gè)時(shí)間片
(已占用14個(gè)時(shí)間片)

第四次:(任務(wù)23個(gè)時(shí)間片執(zhí)行完,不執(zhí)行任務(wù)2
任務(wù)3執(zhí)行1個(gè)時(shí)間片
任務(wù)4執(zhí)行1個(gè)時(shí)間片
任務(wù)5執(zhí)行1個(gè)時(shí)間片
(已占用17個(gè)時(shí)間片)

第五次:
任務(wù)3執(zhí)行1個(gè)時(shí)間片
任務(wù)4執(zhí)行1個(gè)時(shí)間片
任務(wù)5執(zhí)行1個(gè)時(shí)間片
(已占用20個(gè)時(shí)間片)

一次系統(tǒng)周期結(jié)束(一次任務(wù)循環(huán)),coos重新刷新時(shí)間片。也就是5個(gè)任務(wù),時(shí)間片分別為2310116。然后按照任務(wù)時(shí)間片去執(zhí)行。
這樣就實(shí)現(xiàn)了所謂的分時(shí)任務(wù)管理了。因?yàn)槭欠謺r(shí),所以如果有多個(gè)任務(wù),沒(méi)有一個(gè)任務(wù)可以占用全部CPU,各個(gè)任務(wù)都是平等的,呵呵。
這樣的效果是,如果有多個(gè)任務(wù),那么,每個(gè)任務(wù)占用CPU必然減小,就像平時(shí)Windows開了太多任務(wù),就會(huì)覺(jué)得有點(diǎn)卡。哈哈~~~

  
Coos的使用
最后說(shuō)一次怎么使用吧。coos包含文件:(6個(gè))
Coos_typedef.h      類型定義
Coos_conf.h        coos配置,相當(dāng)于裁剪
Coos.c             coos實(shí)現(xiàn)的源文件
Coos.h             coos實(shí)現(xiàn)的頭文件
Coos_hook.h        coos鉤子函數(shù)頭文件
Coos_hook.c        coos鉤子函數(shù)源文件

包含這些文件之后,主函數(shù)只要調(diào)用幾個(gè)coos函數(shù)就可以實(shí)現(xiàn)分時(shí)任務(wù)管理了。舉個(gè)例子:

void main(void)
{
OS_Init();  //coos初始化


OS_TaskCreate(Task_One, Stk_TaskOne, 5,0);

OS_TaskCreate(Task_Two Stk_TaskTwo, 2, 1);

OS_TaskCreate(Task_Thr, Stk_TaskThr, 7, 2);

OS_Start();  //coos開始運(yùn)行, 任務(wù)交給coos管理
}
   
結(jié)束語(yǔ)
簡(jiǎn)介到此結(jié)束,再慢慢為coos寫一份說(shuō)明書吧,呵呵,源碼開發(fā)下載,上傳在我的百度網(wǎng)盤上。
歡迎同學(xué)們下載,當(dāng)然coos會(huì)有bug,但是我還沒(méi)有發(fā)現(xiàn),還請(qǐng)同學(xué)們多多支持多多反饋啊!!



目錄




第一章前言
第二章coos簡(jiǎn)介
第三章任務(wù)管理詳解
第四章任務(wù)管理的幕后操作
第五章任務(wù)管理的幕后操作
第六章coos條件編譯
第七章coos的配置 裁剪 類型定義





前言
(以下所說(shuō)的51特指STC89C52RC,晶振12M

  經(jīng)過(guò)了七七四十九天的閉關(guān)修煉,終于把51的分時(shí)任務(wù)管理實(shí)現(xiàn)了?

有點(diǎn)夸張。從產(chǎn)生為51寫任務(wù)管理的念頭,到開始著手寫代碼,應(yīng)該有34天的時(shí)間。然后接下來(lái)就一直寫個(gè)不停了。本來(lái)是從開始寫到第二天,功能就實(shí)現(xiàn)了,剩下的就是慢慢完善。但是,事情總是沒(méi)那么順利,發(fā)現(xiàn)了一個(gè)又一個(gè)的bug,修改完一個(gè)又有另一個(gè),再修改一個(gè)。這樣持續(xù)了好幾天。有時(shí)候一整天沒(méi)解決一個(gè)bug

修改bug就是每天晚上睡不好,老是想著coosbug,失眠是必須的。到最后發(fā)現(xiàn)了一個(gè)致命的bug,發(fā)現(xiàn)不得不用匯編來(lái)解決,然后就嘗試著C語(yǔ)言嵌入?yún)R編,但卻偏偏一個(gè)C語(yǔ)言函數(shù)匯編出來(lái)的代碼執(zhí)行有錯(cuò)…讓我只能懷疑編譯器的問(wèn)題的,因?yàn)楸緛?lái)沒(méi)嵌入?yún)R編那個(gè)函數(shù)是正常的,嵌入?yún)R編后,函數(shù)的返回值傳遞出錯(cuò)了(編譯器把返回值放到R3還是哪個(gè)忘記了,然后卻從R4取返回值?問(wèn)題我至今仍未能理解),我只能崩潰了。最后終于還是上火了,感冒發(fā)燒頭痛…到校醫(yī)院看病額。

  把coos擱一邊一兩個(gè)星期了,心里那個(gè)還真是不爽。把代碼全部重寫,并用回了最初的設(shè)計(jì)思想,任務(wù)調(diào)度完全在中斷里面實(shí)現(xiàn),讓中斷負(fù)責(zé)任務(wù)切換所有工作,參考著自己上次寫的代碼,老樣子,功能是一兩天就實(shí)現(xiàn)了,幸運(yùn)的是這次調(diào)試沒(méi)發(fā)現(xiàn)什么bug。然后,這樣一擱又是好多天。

在電腦上安裝win8來(lái)玩玩,挺新鮮的,界面也挺好看。期間曾一度因?yàn)樵?/font>win8上運(yùn)行不了RVMDK而讓我再度崩潰多次。最終放棄win8,用回win7

  重新在win7上把coos完善。也就是多寫了一些函數(shù)。像延時(shí),任務(wù)刪除,條件編譯等等,也是搞了好多天,每個(gè)函數(shù)都需要調(diào)試看看正不正確啊…

  當(dāng)然最終功能也差不多,而且發(fā)現(xiàn)暫時(shí)已經(jīng)不想再寫下去了,就發(fā)布一下新浪博客。雖然我知道沒(méi)人看,但是因?yàn)橐郧霸诰W(wǎng)上看到一句話,原話我忘記了。意思就差不多是:經(jīng)歷了一段時(shí)間的學(xué)習(xí),最好最后把學(xué)到的東西記錄下來(lái),走的彎路也記下來(lái),不然留不下什么東西,最終忘記了也就像一無(wú)所獲。學(xué)到的東西最好是發(fā)到網(wǎng)上分享。這些話我是接受了,因?yàn)槲乙彩墙?jīng)常在網(wǎng)上下載別人的資料學(xué)習(xí),所以這次我就發(fā)了出來(lái)…雖然沒(méi)用。就當(dāng)留作紀(jì)念吧呵呵。



第二章    coos簡(jiǎn)介

  coos是一個(gè)專門為51寫的分時(shí)任務(wù)管理,完全由C語(yǔ)言實(shí)現(xiàn),傳統(tǒng)的任務(wù)管理總會(huì)涉及到匯編,因?yàn)橛行┎僮魇?/font>C語(yǔ)言實(shí)現(xiàn)不了的,例如出棧入棧,現(xiàn)場(chǎng)保護(hù)等。設(shè)計(jì)coos是我就試著盡量減少匯編代碼,到最后居然真的實(shí)現(xiàn)了零匯編,是最理想的結(jié)果。不過(guò)我得先說(shuō)明的是,任務(wù)管理的話,我也覺(jué)得從匯編的角度去理解效果會(huì)好一點(diǎn)。

coos是利用51的定時(shí)器0中斷作為分時(shí)依據(jù),進(jìn)入中斷后判斷需要運(yùn)行哪個(gè)任務(wù),中斷退出便切換到相應(yīng)的任務(wù)去執(zhí)行,實(shí)現(xiàn)了任務(wù)調(diào)度。這種管理機(jī)制類似于我們熟悉Windows,例如我們平時(shí)在Windows上運(yùn)行多個(gè)任務(wù)時(shí),有時(shí)會(huì)覺(jué)得卡。coos也一樣,如果任務(wù)多了,每個(gè)任務(wù)占用的CPU必然就減少,給我們的感覺(jué)也是有點(diǎn)卡。

  分時(shí)籠統(tǒng)的講就是把一秒鐘(比較容易理解的單位)劃分周期,我這里是劃分成10個(gè)周期,每個(gè)周期100ms,然后每個(gè)周期劃分成20個(gè)時(shí)間片,每個(gè)時(shí)間片為5ms,也即是每個(gè)任務(wù)運(yùn)行時(shí)間的最小單位是5ms(一個(gè)時(shí)間片)。一個(gè)周期有20個(gè)時(shí)間片,當(dāng)有多個(gè)任務(wù)時(shí),每個(gè)任務(wù)輪流運(yùn)行一個(gè)時(shí)間片。還需要說(shuō)明的是一個(gè)周期有20個(gè)時(shí)間片,每個(gè)任務(wù)一個(gè)周期運(yùn)行多少個(gè)時(shí)間片需要設(shè)置。例如有2個(gè)任務(wù),其中一個(gè)任務(wù)設(shè)置運(yùn)行時(shí)間片為4,另一個(gè)設(shè)置運(yùn)行時(shí)間片為50,那么在coos管理的一個(gè)周期中,任務(wù)一運(yùn)行4個(gè)時(shí)間片,任務(wù)二運(yùn)行16個(gè)時(shí)間片。如果沒(méi)創(chuàng)建新任務(wù)或者刪除任務(wù)的話,下個(gè)周期也是這樣執(zhí)行。

  當(dāng)然為了讓coos更靈活,我也用了不少的條件編譯。實(shí)現(xiàn)可配置,可裁剪。例如一個(gè)時(shí)間片的時(shí)間是5ms還是其他,用戶可自行配置。一個(gè)周期有多少個(gè)時(shí)間片也可配置。當(dāng)然還提供了不少函數(shù),例如任務(wù)刪除,任務(wù)主動(dòng)放棄CPU,系統(tǒng)延時(shí)等。因?yàn)橛行⿲?shí)在不需要用到,所以用條件編譯實(shí)現(xiàn)可裁剪。嘗試了裁剪掉除任務(wù)管理以外的功能,coos占用RAM 44Byte + 1bitcode692

  還需要說(shuō)的是coos的包含的文件。
coos.c             任務(wù)管理實(shí)現(xiàn)源文件
coos.h             任務(wù)管理實(shí)現(xiàn)頭文件
coos_hook.c        鉤子函數(shù)源文件
coos_hook.h        鉤子函數(shù)頭文件
coos_conf.h        任務(wù)管理配置頭文件
coos_typedef.h      類型定義頭文件

coos的實(shí)現(xiàn)由coos.c完成,是coos最核心的文件。
coos_typedef.h是為了coos的移植性需要加入的,定義coos使用的變量類型,如uint8_t
coos_conf.hcoos的配置文件,實(shí)現(xiàn)的可裁剪功能都在這個(gè)文件設(shè)置,也就是條件編譯的開關(guān)。
coos_hook.c鉤子函數(shù)源文件是由用戶實(shí)現(xiàn)的,類似于ucos的鉤子函數(shù),例如選擇啟用任務(wù)調(diào)度的鉤子函數(shù),用戶將自己的鉤子函數(shù)寫好后每次任務(wù)調(diào)度都會(huì)調(diào)用任務(wù)調(diào)度的鉤子函數(shù)。



第三章 任務(wù)管理原理

  在這里最重要的是要知道PC指針是什么?翻譯成中文是程序計(jì)數(shù)器,它保存的是CPU下一個(gè)指令的地址。下一個(gè)指令是什么?PC指針不知道,但是它知道下一條指令放在哪里,就是PC指針指向的那個(gè)地址的內(nèi)容。修改了PC指針,就修改了下一條指令的位置,PC指針指向哪里,CPU就運(yùn)行哪個(gè)指令。任務(wù)調(diào)度就是通過(guò)修改PC指針實(shí)現(xiàn)的,我這里是間接修改PC指針,當(dāng)然修改前要將它保存,也就是入棧操作。修改它就是通過(guò)出棧操作完成的。還有51PC指針是16位的。入棧占用了兩個(gè)字節(jié)。下面再詳細(xì)介紹:先看任務(wù)管理函數(shù)的截圖。







功能看截圖就大概可以理解了。我再說(shuō)明一下大概思路:
606行之前:51產(chǎn)生定時(shí)中斷,C語(yǔ)言看不到的入棧操作:包含R0~R7ACCBDP0LDP0HPSW,還有最重要的PC指針,也就是現(xiàn)場(chǎng)保護(hù)。
606行:一個(gè)賦值語(yǔ)句,保存的是當(dāng)前的SP(棧指針),保存在當(dāng)前任務(wù)的任務(wù)控制塊里面
607行:也是賦值語(yǔ)句,修改當(dāng)前的SP,將當(dāng)前棧指針修改為定時(shí)器函數(shù)專用的棧
609~627行:先別管
629行:賦值語(yǔ)句,修改SP指針為下一個(gè)任務(wù)的SP指針,中斷退出將按下一個(gè)任務(wù)的任務(wù)棧出棧,出棧時(shí)修改了PC指針,實(shí)現(xiàn)了現(xiàn)場(chǎng)還原和任務(wù)調(diào)度

至于為什么要這么做我也說(shuō)明一下。任務(wù)調(diào)度最重要的就是現(xiàn)場(chǎng)保護(hù)和現(xiàn)場(chǎng)還原了。現(xiàn)場(chǎng)保護(hù)利用的是51產(chǎn)生中斷時(shí)的入棧操作,這是C語(yǔ)言不用管也管不了的。現(xiàn)場(chǎng)還原是利用51退出中斷的出棧操作,也是C語(yǔ)言所管不了的。雖然說(shuō)不用管,但是大部分時(shí)候現(xiàn)場(chǎng)保護(hù)并不完全。我們需要將每個(gè)任務(wù)用到的寄存器都入棧。經(jīng)調(diào)試發(fā)現(xiàn),51入棧機(jī)制是,在中斷服務(wù)程序有用到的寄存器就一定會(huì)入棧,PSWPC是一定會(huì)入棧的。

為了現(xiàn)場(chǎng)保護(hù)完全,我在中斷服務(wù)函數(shù)里面,用C語(yǔ)言把每個(gè)通用寄存器都訪問(wèn)了一遍(因?yàn)橛幸恍┩ㄓ眉拇嫫鳑](méi)入棧保護(hù)),所以每個(gè)通用寄存器都入棧。其他像ACCB寄存器等也入棧了。這些操作在606行之前完成的。要看匯編出來(lái)的代碼才知道,所以說(shuō)任務(wù)管理還是從匯編的角度去理解好一點(diǎn)。
然后現(xiàn)場(chǎng)還原就好辦了。只要在中斷服務(wù)函數(shù)退出前修改SP指針(629行),SP指針在中斷服務(wù)函數(shù)就保存在相應(yīng)任務(wù)的任務(wù)控制塊了(606行),629行一個(gè)賦值語(yǔ)句的作用就是,中斷會(huì)按照相應(yīng)的任務(wù)棧出棧(在606行保存的SP的值),之后會(huì)還原寄存器和PC指針,實(shí)現(xiàn)了任務(wù)調(diào)度。只要修改了PC指針,程序就跳轉(zhuǎn)到相應(yīng)的位置執(zhí)行,而PC指針在中斷服務(wù)函數(shù)里面已將入棧保存起來(lái)了,出棧后就切換到上次該任務(wù)執(zhí)行的斷點(diǎn)位置,實(shí)現(xiàn)任務(wù)的繼續(xù)。

  能理解這些操作就好辦了,簡(jiǎn)單的說(shuō)就是定時(shí)器產(chǎn)生中斷會(huì)引起入棧,退出中斷會(huì)出棧,每個(gè)任務(wù)的任務(wù)棧不同,入棧由51中斷的現(xiàn)場(chǎng)保護(hù)完成,出棧由51中斷的現(xiàn)場(chǎng)還原完成。任務(wù)切換需要做的就僅僅是在退出中斷前修改出棧的SP指針(棧指針),629行就完成這個(gè)功能。剩下的就是51的現(xiàn)場(chǎng)還原了。還原后就回去上次被中斷的任務(wù)的斷點(diǎn)處所,繼續(xù)執(zhí)行上次被中斷的任務(wù)。

  詳細(xì)的任務(wù)管理將在下一章繼續(xù)介紹。







第四章 任務(wù)管理詳解

  有了上一章的任務(wù)管理原理的基礎(chǔ),來(lái)理解整個(gè)中斷響應(yīng)函數(shù)的功能就容易多了。繼續(xù)上一章的截圖。(大概瀏覽一下代碼,不到20行)

609~612行,624~627行。這兩個(gè)實(shí)現(xiàn)相同的功能,為定時(shí)器賦初值,條件編譯的條件為是否使能精確延時(shí),條件編譯結(jié)果是選擇其中的一個(gè)。
coos_conf.h文件里有:
#define    OS_ACRT_TIM_EN     0
也就是默認(rèn)沒(méi)有使能精確延時(shí),如果需要使能精確延時(shí)的話只要將0改為1就行了。
說(shuō)明一下65535 - OS_TICK_CNT + OS_ETR_TICK_TIM
OS_TICK_CNT       是定時(shí)器計(jì)數(shù)個(gè)數(shù),可配置(coos_conf.h),默認(rèn)5000,分時(shí)時(shí)間片
OS_ETR_TICK_TIM   是定時(shí)器響應(yīng)周期,也就是入棧所用時(shí)間,默認(rèn)0x2A

使能精確延時(shí)的結(jié)果是,coos提供的時(shí)鐘對(duì)外是比較精確的,例如做一個(gè)定時(shí)器時(shí)鐘,可以比較精確的運(yùn)行一段時(shí)間。但是每個(gè)任務(wù)執(zhí)行的時(shí)間卻減少了,因?yàn)橹袛囗憫?yīng)函數(shù)的執(zhí)行也是需要時(shí)間的,這對(duì)于分時(shí)任務(wù)管理來(lái)說(shuō)是不公平的,因?yàn)槿蝿?wù)調(diào)度的時(shí)間是不一定相同的,導(dǎo)致任務(wù)執(zhí)行的時(shí)間也不一定相同。
沒(méi)有使能精確延時(shí),那么coos提供的延時(shí)默認(rèn)是5ms + 任務(wù)調(diào)度的時(shí)間。分時(shí)任務(wù)管理的話,每個(gè)任務(wù)是公平的,執(zhí)行的時(shí)間相同。但是coos提供的延時(shí)對(duì)外是不精確的。

  接下來(lái)看一下619~621行。條件編譯,條件是如果使能定時(shí)器鉤子函數(shù),在配置文件里是否使能(coos_conf.h)。
函數(shù)OS_Hook_Tick()在coos_hook.c文件,默認(rèn)函數(shù)體為空,該函數(shù)由用戶實(shí)現(xiàn)。

  最后就剩下614~617行,三個(gè)函數(shù)了,一個(gè)一個(gè)來(lái),都是比較好理解的。
先看最簡(jiǎn)單的617行,OS_ResSav();該函數(shù)訪問(wèn)通用寄存器,引起定時(shí)器中斷函數(shù)執(zhí)行前,通用寄存器入棧(中斷服務(wù)函數(shù)訪問(wèn)到哪些通用寄存器,那些通用寄存器就會(huì)入棧),具體代碼是:
540行:讓res_ptr指向0x00,也就是51的工作寄存器組0R0,這個(gè)還是從匯編的角度去看會(huì)好理解一點(diǎn)。51256ByteRAM,前8Byte * 4是四個(gè)工作寄存器組。
542~549行:訪問(wèn)通用寄存器,用下標(biāo)的形式訪問(wèn),自己給自己賦值,分別訪問(wèn)R0R1R7,實(shí)際寄存器值沒(méi)改變,目的是要在定時(shí)器函數(shù)執(zhí)行前將通用寄存器入棧。完善現(xiàn)場(chǎng)保護(hù)工作。

接下來(lái)看614行,OS_TickTimDeal();
代碼也是相當(dāng)簡(jiǎn)單的,看截圖
該函數(shù)處理coos的時(shí)間,由于是分時(shí)任務(wù)管理,當(dāng)然要知道當(dāng)前運(yùn)行到哪個(gè)時(shí)間片,并且每個(gè)任務(wù)一旦運(yùn)行了一個(gè)時(shí)間片,也要將時(shí)間片減一。
499行:當(dāng)前任務(wù)時(shí)間片減一,也就是當(dāng)前任務(wù)運(yùn)行了一個(gè)時(shí)間片
501行:系統(tǒng)時(shí)間片加一,分時(shí)管理任務(wù),當(dāng)然要有一個(gè)變量(OS_Tick)記錄當(dāng)前時(shí)間片

502行:OS_TICK_PERIOD系統(tǒng)時(shí)間片周期,默認(rèn)為20,判斷是否滿一個(gè)周期
504行:滿一個(gè)時(shí)間片周期,時(shí)間片清零
505行:滿一個(gè)時(shí)間片周期,置位系統(tǒng)周期標(biāo)志位

508行:判斷是否滿一個(gè)系統(tǒng)周期,標(biāo)志位是在505行置位的
510行:滿一個(gè)系統(tǒng)周期,標(biāo)志位清零
512~518行:條件編譯,如果使能系統(tǒng)提供參考時(shí)間,也就是延時(shí)時(shí)間才編譯這幾行
  513行:系統(tǒng)時(shí)間加一
  514行:如果時(shí)間滿一個(gè)時(shí)間周期
  516行:時(shí)間清零
520行:調(diào)用任務(wù)時(shí)間片初始化函數(shù),重新刷新任務(wù)時(shí)間片,具體代碼如下截圖
477行:一個(gè)for循環(huán),循環(huán)次數(shù)為任務(wù)個(gè)數(shù),其中OS_TASK_NUM可配置,最大為8
479行:for循環(huán)的循環(huán)體,OS_TaskTimGrp數(shù)組記錄任務(wù)時(shí)間片,這里是按照任務(wù)創(chuàng)建時(shí)的時(shí)間片去刷新
481~483行:條件編譯,如果使能系統(tǒng)空閑任務(wù)。系統(tǒng)空閑任務(wù)是指在一個(gè)系統(tǒng)周期內(nèi)(例如20個(gè)時(shí)間片內(nèi)),如果沒(méi)有可運(yùn)行任務(wù)(只需要執(zhí)行不到20個(gè)時(shí)間片的任務(wù)),也就是創(chuàng)建的各個(gè)任務(wù)時(shí)間片已經(jīng)執(zhí)行完,但是一個(gè)周期還沒(méi)到,就執(zhí)行空閑任務(wù)。coos默認(rèn)提供的空閑任務(wù)是讓51進(jìn)入低功耗模式,省電。當(dāng)然可配置,用戶可以自行創(chuàng)建空閑任務(wù)。
482行:刷新空閑任務(wù)時(shí)間片

任務(wù)調(diào)度還剩下最后一個(gè)函數(shù),繼續(xù)任務(wù)調(diào)度那張截圖:
615行最后一個(gè)函數(shù):目的是獲取下一個(gè)需要執(zhí)行的任務(wù),為切換任務(wù)做準(zhǔn)備
具體函數(shù)代碼如下截圖:



該函數(shù)的思路是:在任務(wù)狀態(tài)表上查找下一個(gè)任務(wù)。任務(wù)狀態(tài)表是579行的OS_TaskStatGrp,是一個(gè)8bit的變量,每一位為0或者為1代表是否創(chuàng)建了任務(wù),1表示創(chuàng)建了任務(wù),當(dāng)然最多支持8個(gè)任務(wù)。
查找到有創(chuàng)建的任務(wù),然后判斷該任務(wù)時(shí)間片是否為0,如果不為0,那么下一個(gè)任務(wù)就是被查找到的任務(wù),如果為0,那么繼續(xù)查找下一個(gè)任務(wù)。如果所有被創(chuàng)建的任務(wù)時(shí)間片都為0,那么返回OS_TASK_NUM,代表著下一個(gè)任務(wù)執(zhí)行的是空閑任務(wù)。
567行:為id賦初值,為當(dāng)前執(zhí)行任務(wù)的id
568行:i初值為0,循環(huán)控制用
570行:進(jìn)入循環(huán),循環(huán)次數(shù)為coos管理任務(wù)的個(gè)數(shù),由OS_TASK_NUM決定,可配置
  572行:進(jìn)入了循環(huán),循環(huán)次數(shù)加一
  573行:id加一,表示下一個(gè)任務(wù)
  574行:判斷id是否溢出,超出coos管理任務(wù)的個(gè)數(shù)
  676行:id溢出則清零
579行:重點(diǎn)!!判斷該id的任務(wù)是否需要執(zhí)行,判斷的條件有兩個(gè),分別是:
1、在該id上創(chuàng)建了任務(wù)
2、該任務(wù)時(shí)間片不為0

前半部分:(OS_TaskStatGrp & SET_BIT(id))
OS_TaskStatGrp是coos任務(wù)狀態(tài)表,8bit,每一位代表一個(gè)任務(wù),為1表示創(chuàng)建了任務(wù)
SET_BIT()是一個(gè)宏操作,代碼是
#define SET_BIT(i) (1 << i)
表示置位某個(gè)位,這里SET_BIT(id)是置位id那一位
操作(OS_TaskStatGrp & SET_BIT(id)表示判斷在任務(wù)狀態(tài)表上判斷id那一位是否創(chuàng)建了任務(wù)
后半部分:OS_TaskTimGrp[id] > 0
判斷任務(wù)時(shí)間片數(shù)組上該任務(wù)時(shí)間片是否為0
如果滿足前半部分和后半部分都為真,那么就需要切換到的任務(wù)就是臨時(shí)變量id了。
所以就有:
return id

當(dāng)然如果不能同時(shí)滿足兩個(gè)條件,就
return  OS_TASK_NUM
表示下一個(gè)任務(wù)是空閑任務(wù)。

至此,任務(wù)調(diào)度講解是完了,不過(guò)還是再總結(jié)一下:
1、 進(jìn)入任務(wù)調(diào)度函數(shù)前(定時(shí)器中斷服務(wù)函數(shù)),有C語(yǔ)言看不見(jiàn)的入棧操作,保存了寄存器和PC指針等
2、 入棧后就進(jìn)入任務(wù)調(diào)度函數(shù),第一步保存當(dāng)前任務(wù)的SP在相應(yīng)任務(wù)的任務(wù)控制塊里面
3、 將SP修改為任務(wù)調(diào)度專用的SP,任務(wù)調(diào)度函數(shù)專用的棧
4、 系統(tǒng)時(shí)間處理,分時(shí)管理需要處理時(shí)間
5、 獲取下一個(gè)需要執(zhí)行的任務(wù)
6、 訪問(wèn)通用寄存器,為了第一步入棧操作所有通用寄存器都入棧
7、 任務(wù)調(diào)度的鉤子函數(shù)(如果使能)
8、 定時(shí)器計(jì)時(shí)賦初值
9、 修改SP為下一個(gè)任務(wù)的SP,在相應(yīng)的任務(wù)控制塊里面讀取(第2步保存)
10、退出任務(wù)調(diào)度函數(shù),按照下一個(gè)任務(wù)的SP出棧,還原寄存器,PC指針等,實(shí)現(xiàn)了任務(wù)調(diào)度

到這里其實(shí)還遺留一個(gè)問(wèn)題:切換到的任務(wù)如果還沒(méi)被執(zhí)行過(guò)的話(定時(shí)器中斷入棧是入棧被中斷的任務(wù),說(shuō)明該任務(wù)已經(jīng)執(zhí)行了),那么相應(yīng)的PC指針,寄存器值是什么?下一章繼續(xù)介紹。


第五章 任務(wù)管理的幕后操作

來(lái)到這里,可以看到,要實(shí)現(xiàn)任務(wù)管理功能,還需要很多幕后操作的。這次我們實(shí)際來(lái)看任務(wù)管理從頭到尾怎么做的,要開始來(lái)弄清楚來(lái)龍去脈了。這一章,需要分幾個(gè)小節(jié)了。
51 coos的文件結(jié)構(gòu)和使用

看看coosC語(yǔ)言工程吧。截圖:

先看左邊的文件,兩個(gè)文件夾coosuser
coos里面兩個(gè)源文件coos.ccoos_hook.c。這兩個(gè)文件是任務(wù)管理文件,只要包含了這兩個(gè)文件和相應(yīng)的頭文件,再調(diào)用幾個(gè)函數(shù)就可以實(shí)現(xiàn)分時(shí)任務(wù)管理功能了。
user里面兩個(gè)源文件是用戶自己編寫的。main.ctask.c分別是主函數(shù)文件和被管理任務(wù)的文件。任務(wù)到底是什么,等一下再看。這里先介紹主函數(shù)應(yīng)該怎么樣寫,也就是前面啰嗦了那么多,那到底coos怎么用?

要先添加了coos的幾個(gè)文件到自己的工程,然后大概瀏覽一下那10來(lái)行代碼。可以看到主函數(shù)的操作分成三部分:
1、初始化coos
2、創(chuàng)建任務(wù)
3、開始運(yùn)行coos

具體再看代碼:

12~14行:定義三個(gè)數(shù)組,用來(lái)做任務(wù)棧。可以看到該主函數(shù)創(chuàng)建了三個(gè)任務(wù),因?yàn)槊總(gè)任務(wù)要有一個(gè)任務(wù)棧,所以定義了三個(gè)數(shù)組來(lái)當(dāng)作任務(wù)棧。
16行:主函數(shù)開始入口。
18行:coos初始化。調(diào)用coos的函數(shù),一些coos運(yùn)行必要的操作,等一下再說(shuō)。
20~22行:創(chuàng)建任務(wù)。也是調(diào)用coos的函數(shù),創(chuàng)建任務(wù)后準(zhǔn)備交給coos管理。
24行:coos開始運(yùn)行。同樣是coos的函數(shù),coos開始分時(shí)管理被創(chuàng)建的三個(gè)任務(wù)。

可以看到,要使用來(lái)管理任務(wù)是非常簡(jiǎn)單的,主要在主函數(shù)里面調(diào)用幾個(gè)coos提供的函數(shù)就行了。沒(méi)錯(cuò),但是這次我們要看coos是怎樣運(yùn)行的,了解到具體的每一步。只要理解coos幾個(gè)重要的函數(shù),就能很深入地理解coos了。

52 coos重要函數(shù)說(shuō)明

1void OS_Tick_Init();
coos定時(shí)器初始化函數(shù),跟普通的51定時(shí)器初始化函數(shù)沒(méi)什么不同,但也看一下具體代碼。
功能是配置定時(shí)器0,讓51能夠產(chǎn)生中斷,相當(dāng)好理解。
其中457行的TR0 = 1;被我注釋掉是因?yàn)槲覀儾⒉皇且谶@個(gè)時(shí)候讓coos開始運(yùn)行,等到創(chuàng)建好任務(wù)在讓它開始運(yùn)行。

2void  OS_TaskCreate(void Task(void), uint8_t *TaskStk, uint8_t TaskTim, uint8_t TaskID)
coos創(chuàng)建任務(wù)的函數(shù),非常重要!!

可以說(shuō)coos得以運(yùn)行都是這個(gè)函數(shù)和定時(shí)器中斷函數(shù)在幕后的操作了,這個(gè)函數(shù)有這跟定時(shí)器中斷函數(shù)同樣級(jí)別的重要性!!并且coos的很多概念都在這個(gè)函數(shù)里面使用到。

先介紹一下一些基本概念,再來(lái)說(shuō)明任務(wù)創(chuàng)建函數(shù)。

①在C語(yǔ)言中,函數(shù)名代表函數(shù)的地址。什么意思?舉個(gè)例子說(shuō)明一下:
比如有一個(gè)函數(shù):void  Func();
Func是函數(shù)名,
Func()是這個(gè)函數(shù)。
要知道它們是有區(qū)別的。
首先我們可以把Func當(dāng)成一個(gè)常量,這個(gè)常量是一個(gè)地址,也就是Func()在ROM存放的地址,ROM51上我們稱為code。這些屬于C語(yǔ)言的知識(shí),大家有興趣在繼續(xù)深入了解。我們這里要用到的只是要知道Func就是一個(gè)常量,這個(gè)常量是一個(gè)地址,這個(gè)地址是函數(shù)Func()在ROM上的地址。就行了。

但是它有什么用?如果跟PC指針聯(lián)系起來(lái)的話就知道了解這個(gè)是有作用了。
PC指針存放的是下一條指令的地址,如果把Func賦值給PC,那么CPU下一個(gè)執(zhí)行的指令就是Func()函數(shù)的指令了,類似于C語(yǔ)言的函數(shù)調(diào)用,調(diào)用的函數(shù)是Func()。其實(shí)調(diào)用函數(shù)的時(shí)候就是將PC指針修改成對(duì)應(yīng)函數(shù)的地址,CPU就跳轉(zhuǎn)去執(zhí)行相應(yīng)的函數(shù)了。但是調(diào)用函數(shù)的話還涉及到函數(shù)參數(shù)的傳遞和函數(shù)的返回值,與只修改PC指針也是有區(qū)別的。

②任務(wù)控制塊
先看代碼截圖:
要管理任務(wù),當(dāng)然要有任務(wù)的信息。任務(wù)控制塊就是用來(lái)保存任務(wù)信息的。因?yàn)?font face="Times New Roman">51RAM只有256Byte,所以任務(wù)控制塊我也盡量減小,只有任務(wù)棧和任務(wù)時(shí)間片是必要的。

任務(wù)棧地址:每個(gè)任務(wù)要有自己的棧,任務(wù)棧的地址用來(lái)記錄任務(wù)進(jìn)入中斷的SP的值(棧指針),方便中斷退出按該任務(wù)棧出棧實(shí)現(xiàn)現(xiàn)場(chǎng)還原。在第二章講任務(wù)管理原理時(shí),進(jìn)入中斷的第一件事就是保存任務(wù)的SP到任務(wù)控制塊的棧地址,也就是606行的賦值語(yǔ)句。
任務(wù)時(shí)間片:每個(gè)任務(wù)還要有時(shí)間片。也就是任務(wù)在一個(gè)周期內(nèi)最多執(zhí)行多少個(gè)時(shí)間片。因?yàn)槭欠謺r(shí)任務(wù)管理,所以不能讓某個(gè)任務(wù)占用所有CPU,要按照時(shí)間片去執(zhí)行。

接下來(lái)看任務(wù)創(chuàng)建函數(shù),截圖:

函數(shù)帶四個(gè)參數(shù),每個(gè)參數(shù)都相當(dāng)重要。
第一個(gè)參數(shù):
void  Taskvoid)  ---  這個(gè)參數(shù)是一個(gè)函數(shù),可能有點(diǎn)難以理解。但是函數(shù)的參數(shù)可以是一個(gè)函數(shù),這個(gè)參數(shù)函數(shù)不帶參數(shù),不帶返回值。這個(gè)參數(shù)函數(shù)就是我們用C語(yǔ)言寫的任務(wù)函數(shù)。也就是任務(wù)創(chuàng)建函數(shù)創(chuàng)建的任務(wù)就放在這個(gè)參數(shù)。
第二個(gè)參數(shù):
uint8_t  *TaskStk  ---  這個(gè)參數(shù)是任務(wù)棧首地址。如果我們定義一個(gè)數(shù)組,那么數(shù)組名就是數(shù)組的首地址,一般這個(gè)參數(shù)傳遞的是數(shù)組名(數(shù)組的首地址),然后數(shù)組就作為該任務(wù)的任務(wù)棧。(當(dāng)然動(dòng)態(tài)分配內(nèi)存也行)
第三個(gè)參數(shù):
uint8_t  TaskTim  --- 這個(gè)參數(shù)是任務(wù)的時(shí)間片。比較容易理解了,一個(gè)任務(wù)在一個(gè)周期內(nèi)最多運(yùn)行多少個(gè)時(shí)間片就是看創(chuàng)建任務(wù)時(shí)傳遞進(jìn)來(lái)的這個(gè)參數(shù)。
第四個(gè)參數(shù):
uint8_t  TaskID  ---  任務(wù)的idcoos管理任務(wù)是根據(jù)任務(wù)的id管理的,只有任務(wù)掛在coos任務(wù)狀態(tài)表(OS_TaskStatGrp)上coos才管理這個(gè)任務(wù),不然任務(wù)是不會(huì)被運(yùn)行的,哪怕有再多的時(shí)間片。并且coos的任務(wù)狀態(tài)表是一個(gè)8bit的變量,每一位代表一個(gè)任務(wù)id。任務(wù)的id分別是從低位的0到高位的7

開始看具體的函數(shù)代碼,現(xiàn)在應(yīng)該很好理解了。
099行:目的是判斷該id上是否已經(jīng)創(chuàng)建了任務(wù)。判斷條件是任務(wù)狀態(tài)表該為是否為1。其中操作SET_BIT()是一個(gè)宏操作。在coos.h里面有:
#define  SET_BIT(i)    (1 << i)
目的是置位某個(gè)位,操作(SET_BIT(TaskID))是置位id那一位,然后跟OS_TaskStatGrp(任務(wù)狀態(tài)表)與一下是否為真。如果為真,該id上已經(jīng)創(chuàng)建了任務(wù);如果不為真,該id上未創(chuàng)建任務(wù)。

如果任務(wù)id已被占用存在,那么運(yùn)行101~104行。
101~103行:條件編譯,條件是是否使能系統(tǒng)錯(cuò)誤統(tǒng)計(jì),如果使能系統(tǒng)錯(cuò)誤統(tǒng)計(jì)則運(yùn)行OS_ErrCnt(); ,這個(gè)函數(shù)是當(dāng)coos出錯(cuò)時(shí)候統(tǒng)計(jì)錯(cuò)誤次數(shù)的,這里不是重點(diǎn)。
104行:return,函數(shù)返回。因?yàn)椴荒軇?chuàng)建任務(wù)(任務(wù)id被占用了)

如果任務(wù)id未被占用,則運(yùn)行108~118行:
108行:因?yàn)橐谠?/font>id上創(chuàng)建任務(wù),所以置位任務(wù)狀態(tài)表在該id上的那一位

109行:記錄任務(wù)的任務(wù)棧,也就是任務(wù)的SP。記錄在對(duì)應(yīng)任務(wù)的任務(wù)控制塊里面。任務(wù)控制塊是一個(gè)結(jié)構(gòu),任務(wù)控制塊在上面已經(jīng)說(shuō)明了。我這里是定義一個(gè)結(jié)構(gòu)數(shù)組,結(jié)構(gòu)就是任務(wù)控制塊結(jié)構(gòu),每個(gè)任務(wù)的任務(wù)控制塊就是在該結(jié)構(gòu)數(shù)組上的一個(gè)元素(結(jié)構(gòu)數(shù)組的元素是一個(gè)結(jié)構(gòu))。

右邊(uint8_t)TaskStk  +  OS_TASK_STK_SIZE_MIN有兩部分。
第一部分(uint8_t)TaskStk 是任務(wù)創(chuàng)建函數(shù)的第二個(gè)參數(shù),任務(wù)棧首地址,本來(lái)是一個(gè)指針,強(qiáng)制轉(zhuǎn)換成uint8_t,無(wú)符號(hào)八位的變量
第二部分 OS_TASK_STK_SIZE_MIN 是任務(wù)棧的大小,是一個(gè)常量,默認(rèn)值為13,這個(gè)有點(diǎn)難理解。因?yàn)榕c定時(shí)器中斷服務(wù)函數(shù)相關(guān),并且是從匯編的角度去看才能知道這個(gè)常量的大小。詳細(xì)說(shuō)明一下:中斷服務(wù)函數(shù)執(zhí)行前有入棧操作,但是具體入棧寄存器多少事先難以預(yù)知,最好的辦法是查看編譯后的匯編代碼。我就是查看匯編代碼的,默認(rèn)總共入棧的寄存器有13個(gè),占用14個(gè)字節(jié)(PC指針占用兩個(gè)字節(jié),其他寄存器占用一個(gè)字節(jié))。除了PC指針還有額外的12個(gè)字節(jié),分別是R0~R7ACCDP0HDP0LPSW。還需要了解的是51入棧機(jī)制,棧是向高地址增長(zhǎng)的,而且是先增長(zhǎng)再入棧(貌似專業(yè)一點(diǎn)講是一個(gè)是滿棧,UBUupBbefore,要入棧前先增長(zhǎng)棧地址)。
了解了這些之后就可以推斷出 OS_TASK_STK_SIZE_MIN 這個(gè)常量是13了,當(dāng)然我也順便說(shuō)一下。總共入棧14個(gè)字節(jié),數(shù)組名是首地址,數(shù)組名加上13就是棧頂了,總共是14個(gè)字節(jié),這樣就模仿得跟中斷入棧后的棧頂?shù)闹凳且粯拥摹?/font>

左邊OS_TaskTcbGrp[TaskID].TaskStk 是該任務(wù)的任務(wù)棧,接受右邊的棧頂?shù)刂贰?/div>
110行:右邊是任務(wù)創(chuàng)建函數(shù)的第三個(gè)參數(shù),任務(wù)時(shí)間片,在任務(wù)控制塊上記錄任務(wù)時(shí)間片。
112~113行也是重點(diǎn)和難點(diǎn)!!
賦值語(yǔ)句,模擬入棧。要先知道PC指針是16bit的,入棧占用兩個(gè)字節(jié)。但是究竟是高字節(jié)先入棧還是低字節(jié)先入棧?有辦法!進(jìn)入調(diào)試模式,然后觀察內(nèi)存。我是用這個(gè)辦法知道51入棧PC指針是低地址先入棧,然后高地址的。大家有興趣可以自行嘗試。
知道了先入棧低地址,下一步是模擬出入棧的是任務(wù)剛要運(yùn)行時(shí)的PC指針。上面已經(jīng)講到函數(shù)名就是函數(shù)的地址,與PC指針有聯(lián)系。
112行: TaskStk[0] = FUNC_ADDR_LOW(Task);
右邊是一個(gè)宏操作,在coos.h里面有:
#define   FUNC_ADDR_LOW(func)   (uint8_t)(((uint32_t)func) >> 0)
目的是獲取函數(shù)的低地址,宏操作的參數(shù)是func,具體應(yīng)用時(shí)候是一個(gè)函數(shù)名,也就是一個(gè)常量(函數(shù)的地址),操作先將函數(shù)的地址func強(qiáng)制轉(zhuǎn)換成一個(gè)32bit的變量,右移0是為了配合獲取函數(shù)高地址操作寫的,實(shí)際沒(méi)用,然后再?gòu)?qiáng)制轉(zhuǎn)換成8bit的變量。
左邊是任務(wù)棧,用下標(biāo)形式訪問(wèn)棧底,將函數(shù)的低地址賦值給棧底,模擬了PC指針入棧時(shí)的低八位地址入棧。
113行:TaskStk[1] = FUNC_ADDR_HIGH(Task)
理解了112行,這一行就好辦了。模擬入棧PC指針的高八位。
右邊一樣是一個(gè)宏操作,在coos.h里面有:
#define  FUNC_ADDR_HIGH(func)   (uint8_t)(((uint32_t)func) >> 8)
目的是獲取函數(shù)func的高八位地址。
左邊任務(wù)棧,因?yàn)閯偛湃霔:瘮?shù)的低八位地址,所以棧要向上增長(zhǎng),用下標(biāo)訪問(wèn)的形式就是
TaskStk[1]了,這一行模擬出PC指針入棧是的高八位地址入棧。

115~118行:模擬其它寄存器入棧,默認(rèn)都是0x00,其他寄存器包含有R0~R7ACCDP0HDP0LPSW具體要看匯編代碼才知道是這些寄存器的。

至此任務(wù)創(chuàng)建函數(shù)就完了。這里在總結(jié)一下,在coos里要?jiǎng)?chuàng)建一個(gè)任務(wù)就是要將任務(wù)掛在任務(wù)狀態(tài)表上,供coos管理,而且要填好任務(wù)的信息,包括任務(wù)棧頂,任務(wù)時(shí)間片。最后模擬任務(wù)被中斷服務(wù)函數(shù)中斷了的入棧操作。具體再分幾步說(shuō)明:
1、判斷任務(wù)id是否被占用
2、如果任務(wù)id被占用就不創(chuàng)建任務(wù),根據(jù)條件編譯是否運(yùn)行系統(tǒng)錯(cuò)誤統(tǒng)計(jì)函數(shù)

3、任務(wù)id沒(méi)有被占用,置位coos任務(wù)狀態(tài)表對(duì)應(yīng)id那一位,說(shuō)明要在該id創(chuàng)建任務(wù)
  4、為任務(wù)棧的棧頂初始值
  5、任務(wù)時(shí)間片初始化
  6、模擬入棧PC指針和其他寄存器。

3void  OS_TaskTim_Init(void)
因?yàn)?font face="Times New Roman">coos管理任務(wù)是按時(shí)間片管理的,任務(wù)一旦運(yùn)行一個(gè)時(shí)間片,時(shí)間片就被減一。所以每個(gè)周期要刷新一次任務(wù)時(shí)間片。這個(gè)函數(shù)就是每個(gè)周期調(diào)用一次的任務(wù)時(shí)間片初始化,重新刷新任務(wù)時(shí)間片共coos管理。
477行:for循環(huán),循環(huán)次數(shù)是coos管理的任務(wù)個(gè)數(shù)
479行:for的循環(huán)體,功能是根據(jù)任務(wù)時(shí)間片初始化任務(wù)時(shí)間片數(shù)組,就是記錄各個(gè)任務(wù)創(chuàng)建時(shí)的時(shí)間片,創(chuàng)建一個(gè)副本。任務(wù)運(yùn)行后的時(shí)間片減一操作在這個(gè)副本進(jìn)行的。
481~483行:條件編譯,條件是如果使能系統(tǒng)提供空閑任務(wù)。如果使能了系統(tǒng)提供空閑任務(wù),那么就會(huì)刷新空閑任務(wù)的時(shí)間片。其中常量OS_IDLE_TASK_TIM的值與coos一個(gè)周期的時(shí)間片個(gè)數(shù)相等。在coos_conf.h里面有:
#define  OS_IDLE_TASK_TIM  OS_TICK_PERIOD
其中后半部分OS_TICK_PERIOD是coos一個(gè)周期時(shí)間片的個(gè)數(shù)。

理解了這三個(gè)函數(shù)后就可以來(lái)看看coos具體的運(yùn)行過(guò)程了。現(xiàn)在開始要具體從main函數(shù)開始看coos的任務(wù)管理了。



53 main函數(shù)的執(zhí)行理解coos的運(yùn)行

再來(lái)main函數(shù)的截圖吧:
從現(xiàn)在開始一步一步分析main函數(shù)。

1OS_Init();

進(jìn)入這個(gè)函數(shù)繼續(xù)分析。看該函數(shù)截圖:
062行:OS_Tick_Init();代碼上節(jié)已經(jīng)分析過(guò),重溫一下,看截圖:
064行:變量OS_CurTaskID記錄當(dāng)前是執(zhí)行那個(gè)任務(wù),初始化將它初始化為空閑任務(wù)。
065行:變量OS_TaskStatGrpcoos任務(wù)狀態(tài)表,初始化為0表示沒(méi)有創(chuàng)建任務(wù)。
067行:for循環(huán),循環(huán)次數(shù)是coos管理任務(wù)的個(gè)數(shù),也就是常量OS_TASK_NUM,可配置
069~070行:for循環(huán)的循環(huán)體,將coos管理的任務(wù)的任務(wù)控制塊任務(wù)棧初始化為0x00,時(shí)間片初始化為0
073~075行:條件編譯,兩個(gè)條件。分別是
1、如果使能coos空閑任務(wù),可以不使能
2、如果使能coos提供的空閑任務(wù),當(dāng)然可以自行創(chuàng)建其他任務(wù)作為coos空閑任務(wù)
coos默認(rèn)提供的空閑任務(wù)是讓51進(jìn)入低功耗模式,省電。此時(shí)51等待中斷喚醒。具體可以查找51數(shù)據(jù)手冊(cè)繼續(xù)深入理解,代碼看截圖:
到了這里,主函數(shù)的第一個(gè)函數(shù)就完了,接下來(lái)繼續(xù)看主函數(shù)的其他操作。

2OS_TaskCreate(Task_One,  Stk_TaskOne,  5,  0);

重溫一下任務(wù)創(chuàng)建函數(shù),截圖:

這個(gè)函數(shù)在上一節(jié)也已經(jīng)說(shuō)明過(guò),再羅嗦一下。
該函數(shù)的操作是創(chuàng)建一個(gè)名字為“Task_One”的任務(wù),其中Task_One是一個(gè)函數(shù)名,也就是有一個(gè)Task_One()的函數(shù),具體任務(wù)做什么等一下再說(shuō)。
任務(wù) Task_One的任務(wù)棧是Stk_TaskOne,也就是主函數(shù)文件中012行定義的那個(gè)數(shù)組,將數(shù)組的首地址傳遞進(jìn)來(lái),這個(gè)數(shù)組的空間給Task_One做任務(wù)棧。
任務(wù)Task_One的時(shí)間片為5,也就是Task_One一個(gè)周期最多執(zhí)行5個(gè)時(shí)間片
任務(wù)Task_One的id0,也就是Task_One在任務(wù)狀態(tài)表OS_TaskStatGrp占用了第0

Task_One的具體代碼再看一下,相當(dāng)簡(jiǎn)單的一個(gè)任務(wù)。截圖:
說(shuō)明一下任務(wù)必須在一個(gè)死循環(huán)里面,即時(shí)任務(wù)只需執(zhí)行一次,也必須讓任務(wù)在死循環(huán)里面執(zhí)行,不能讓任務(wù)函數(shù)返回。因?yàn)榉祷氐脑挘邪l(fā)生不可預(yù)知的結(jié)果,一般就是程序跑飛了。因?yàn)榉祷貢r(shí)誰(shuí)也不知PC指針會(huì)指向哪里。
如果有只需執(zhí)行一次的任務(wù)的話,coos當(dāng)然考慮了這個(gè)情況。那就是在任務(wù)運(yùn)行完后調(diào)用coos的任務(wù)刪除函數(shù)void  OS_TaskDel(uint8_t  TaskID);將該任務(wù)刪除掉,coos就再也不會(huì)執(zhí)行該任務(wù)了。參數(shù)TaskID是待刪除任務(wù)的id。比如要?jiǎng)h除剛才創(chuàng)建的任務(wù)的話就調(diào)用:
OS_TaskDel(0);
就將任務(wù)刪除了。
3OS_TaskCreate(Task_Two, Stk_TaskTwo, 2, 1);和
OS_TaskCreate(Task_Thr, Stk_TaskThr, 7, 3);
再創(chuàng)建兩個(gè)任務(wù),類似與創(chuàng)建任務(wù)1
任務(wù)代碼也很簡(jiǎn)單,截圖:
功能類似與任務(wù)一。任務(wù)二中被注釋的內(nèi)容是我在寫coos時(shí)測(cè)試函數(shù)功能是否正常用的。
創(chuàng)建了任務(wù)之后,我們要讓coos開始運(yùn)行了。繼續(xù)看mian函數(shù)中的最后一個(gè)函數(shù)。

3OS_Start();
代碼在截圖:

135行:coos任務(wù)時(shí)間片初始化,該函數(shù)上面已經(jīng)上一節(jié)也已經(jīng)說(shuō)過(guò),重溫一下代碼:
因?yàn)閯?chuàng)建任務(wù)的時(shí)候在任務(wù)控制塊里面填寫了任務(wù)的時(shí)間片。然后我們想讓coos管理這些任務(wù),我就創(chuàng)建了一個(gè)數(shù)組,作為任務(wù)時(shí)間片的副本。因?yàn)槿蝿?wù)運(yùn)行后我們要記錄已經(jīng)運(yùn)行了,直接在任務(wù)控制塊里面操作會(huì)造成任務(wù)信息丟失,所以創(chuàng)建一個(gè)副本。在coos.c里面有:
static  uint8_t  idata  OS_TaskTimGrp[OS_TASK_NUM + 1];
用這個(gè)數(shù)組來(lái)記錄任務(wù)時(shí)間片,也就是運(yùn)行了任務(wù)就來(lái)一次任務(wù)時(shí)間片減一操作,滿一個(gè)周期后再重新刷新。任務(wù)時(shí)間片為0任務(wù)就不會(huì)被執(zhí)行。

138行:因?yàn)?/font>coos運(yùn)行前默認(rèn)coos是在運(yùn)行空閑任務(wù),第一次進(jìn)入中斷服務(wù)函數(shù)(任務(wù)調(diào)度函數(shù)),會(huì)將相應(yīng)任務(wù)的時(shí)間片減一(第一次進(jìn)入中斷空閑任務(wù)時(shí)間片會(huì)被減一),所以先加一,解決第一次進(jìn)行任務(wù)調(diào)度出現(xiàn)的bug
138行:因?yàn)榈谝淮芜M(jìn)入中斷,OS_Tick會(huì)加一(coos時(shí)間片記錄函數(shù)),表示中斷了一個(gè)任務(wù),coos運(yùn)行了一個(gè)時(shí)間片,但實(shí)際第一次進(jìn)入中斷并沒(méi)有運(yùn)行一個(gè)時(shí)間片的任務(wù),所以OS_Tick減一,解決第一次進(jìn)行任務(wù)調(diào)度的bug

140行:表示定時(shí)器開始計(jì)時(shí)。
141行:表示定時(shí)器產(chǎn)生溢出,實(shí)際沒(méi)溢出,只是為了立刻進(jìn)入中斷。
此時(shí)CPU的不會(huì)繼續(xù)執(zhí)行141行下面的代碼了,它會(huì)進(jìn)入定時(shí)器0中斷服務(wù)函數(shù),但是下面的代碼還是有可能被執(zhí)行的。
143~147行:條件編譯,如果使能空閑任務(wù)和coos提供的空閑任務(wù)
空閑任務(wù)放在這里,是因?yàn)楫?dāng)沒(méi)有可運(yùn)行任務(wù)的時(shí)候,coos任務(wù)調(diào)度返回,141行下面的代碼。為什么,可以自行思考一下。原因是141行是第一次被中斷的斷點(diǎn),然后進(jìn)行任務(wù)調(diào)度。任務(wù)調(diào)度函數(shù)會(huì)記錄斷點(diǎn),入棧保護(hù),并且coos初始化是默認(rèn)的任務(wù)是空閑任務(wù),所以入棧保護(hù)入的棧是空閑任務(wù)的棧。如果將空閑任務(wù)放在141行下面,那么就模仿的非常的像是第一次中斷斷點(diǎn)就是空閑任務(wù)了。

至此,coos任務(wù)調(diào)度講解完畢。當(dāng)然要看代碼,要調(diào)試,要看一下匯編代碼,要看51的說(shuō)明書,要懂一點(diǎn)微機(jī)原理,要有C語(yǔ)言基礎(chǔ),才能更深刻的了解coos
簡(jiǎn)單總結(jié):
1、初始化定時(shí)器,coos使用的變量。為coos分時(shí)管理任務(wù)做準(zhǔn)備
2、創(chuàng)建任務(wù),準(zhǔn)備讓coos管理(創(chuàng)建任務(wù)是記錄任務(wù)信息,并模擬任務(wù)被中斷函數(shù)打斷)
3、開始運(yùn)行coos,啟用定時(shí)器中斷根據(jù)任務(wù)信息調(diào)度任務(wù)

當(dāng)然為了讓coos更強(qiáng)大,并且作為嵌入式的東西,可裁剪是必不可少的。所以很多條件編譯默認(rèn)都是不編譯的,下一章主要說(shuō)一下條件編譯函數(shù)。

以下已經(jīng)不是重點(diǎn)了,可有可無(wú)。coos的主要目的是任務(wù)管理。并且發(fā)現(xiàn)分時(shí)任務(wù)管理比較簡(jiǎn)單實(shí)現(xiàn),所以就是分時(shí)任務(wù)管理的coos了。


第六章 coos條件編譯

數(shù)了一下是11個(gè)條件編譯函數(shù),都是相當(dāng)簡(jiǎn)單或者說(shuō)理所當(dāng)然會(huì)有這些函數(shù),沒(méi)有多少技術(shù)含量了這些。下面一一說(shuō)明:

1void OS_TaskDel(uint8_t TaskID)
任務(wù)刪除函數(shù),看截圖:
169行:傳遞進(jìn)來(lái)的參數(shù)是任務(wù)的id
171行:判斷任務(wù)是否創(chuàng)建了,因?yàn)閯?chuàng)建任務(wù)函數(shù)默認(rèn)已經(jīng)置位了任務(wù)狀態(tài)表(OS_TaskStatGrp)對(duì)應(yīng)的id那一位,所以如果對(duì)應(yīng)的為1則說(shuō)明確實(shí)創(chuàng)建了任務(wù),準(zhǔn)備刪除任務(wù)。
173行:目的是將任務(wù)狀態(tài)表(OS_TaskStatGrp)對(duì)應(yīng)任務(wù)id那一位清零。其中RESET_BIT()是一個(gè)宏操作,在coos.h文件里面有:
#define  RESET_BIT(i)  (~(1 << i))
目的是清零對(duì)應(yīng)i的那一位,最重要是這一步就把任務(wù)刪除了,任務(wù)就再也不會(huì)運(yùn)行了,下面的步驟只能說(shuō)是善后吧。
174~175行:任務(wù)棧置為0x00,時(shí)間片置零
176行:任務(wù)時(shí)間片數(shù)組(OS_TaskTimGrp)對(duì)應(yīng)任務(wù)id那個(gè)元素的時(shí)間片清零

如果任務(wù)狀態(tài)表(OS_TaskStatGrp)對(duì)應(yīng)任務(wù)id那一位為零,也就是對(duì)應(yīng)id上沒(méi)有創(chuàng)建任務(wù),根據(jù)條件編譯是否運(yùn)行系統(tǒng)錯(cuò)誤統(tǒng)計(jì)函數(shù),這里不是重點(diǎn)。

2void OS_IdleTask(void)
coos提供的空閑任務(wù),老樣子,截圖:
跟普通任務(wù)一樣是進(jìn)入一個(gè)while死循環(huán),因?yàn)橐坏┖瘮?shù)返回,會(huì)有意想不到的結(jié)果反生,難以預(yù)測(cè)發(fā)生什么事,程序跑飛了那是不可避免的。
204行:根據(jù)51數(shù)據(jù)手冊(cè),置位PCON的第0位讓51進(jìn)入低功耗,具體可參考數(shù)據(jù)手冊(cè)

3uint8_t OS_GetCurTaskID(void)
獲取當(dāng)前運(yùn)行任務(wù)id的函數(shù),截圖:
直接將OS_CurTaskID返回,該變量記錄當(dāng)前被運(yùn)行的任務(wù)
來(lái)到這里相信同學(xué)們也覺(jué)得條件編譯函數(shù)是相當(dāng)?shù)摹^續(xù)

4uint8_t OS_GetCurTickTim(void)
獲取coos提供的參考時(shí)間,截圖:
類似與上一個(gè)函數(shù),不說(shuō)了。

5void OS_SetCurTickTim(uint8_t TickTim)
設(shè)置coos當(dāng)前參考時(shí)間,截圖:
對(duì)OS_Tim賦值,就是設(shè)置coos的參考時(shí)間了

6void OS_TaskExit(void)
例如當(dāng)前正在運(yùn)行某個(gè)任務(wù),我們已經(jīng)不想再讓任務(wù)運(yùn)行下去了,就可以調(diào)用這個(gè)函數(shù),進(jìn)入任務(wù)調(diào)度函數(shù),讓任務(wù)調(diào)度函數(shù)判斷下一個(gè)任務(wù)是什么而去執(zhí)行下一個(gè)任務(wù),當(dāng)前任務(wù)就沒(méi)有被執(zhí)行了。
這個(gè)時(shí)候任務(wù)這個(gè)函數(shù)的價(jià)值就體現(xiàn)出來(lái)了。因?yàn)槲覀兊娜蝿?wù)都必須設(shè)置成在一個(gè)死循環(huán)里面執(zhí)行,如果沒(méi)有這個(gè)函數(shù),那么coos必須也讓該任務(wù)運(yùn)行一個(gè)時(shí)間片,如果在這個(gè)時(shí)間片內(nèi)引腳的狀態(tài)都為0,那么可想而知,該任務(wù)是在浪費(fèi)CPU。如果我們改進(jìn)一下:
如果狀態(tài)為0,則一樣運(yùn)行一段代碼,如果引腳狀態(tài)為1,調(diào)用該函數(shù),讓任務(wù)放棄CPU
那么CPU的利用率就大大提高了,下面看代碼。截圖:
282行:判斷任務(wù)是否存在
284行:任務(wù)確實(shí)存在,置位TF0,定時(shí)器0溢出標(biāo)志,則進(jìn)入定時(shí)器中斷函數(shù)進(jìn)行任務(wù)調(diào)度。
289行:任務(wù)并不存在,根據(jù)條件編譯是否運(yùn)行coos錯(cuò)誤統(tǒng)計(jì)函數(shù)(這里不是重點(diǎn))

7void OS_TaskTimEdit(uint8_t TaskID, uint8_t TaskTim)
任務(wù)時(shí)間片修改函數(shù):截圖
因?yàn)閯?chuàng)建任務(wù)的時(shí)候我們指定了任務(wù)的時(shí)間片,coos管理任務(wù)后任務(wù)的CPU使用率是一定的,如果要增加或減少任務(wù)的CPU使用率可以試著用這個(gè)函數(shù)。
傳遞進(jìn)來(lái)的參數(shù):
1uint8_t  TaskID   任務(wù)的id
2uint8_t  TaskTim  任務(wù)的新時(shí)間片
當(dāng)然311行先判斷任務(wù)是否存在
任務(wù)存在則修改任務(wù)控制塊數(shù)組中該任務(wù)的任務(wù)控制塊的時(shí)間片為新設(shè)置的時(shí)間片
任務(wù)不存在根據(jù)條件是否編譯運(yùn)行coos錯(cuò)誤統(tǒng)計(jì)函數(shù)

8void OS_ErrCntClr(void)
coos錯(cuò)誤次數(shù)清零函數(shù)。截圖:
條件編譯有兩個(gè)條件:
1、使能coos的錯(cuò)誤統(tǒng)計(jì)
2、使能錯(cuò)誤統(tǒng)計(jì)清零
340行:簡(jiǎn)單的將OS_Err_Cnt賦值為0就清零了

9uint8_t OS_GetTick(void)
獲取coos當(dāng)前時(shí)間片,截圖:
360行:直接將OS_Tick返回。

10void OS_TimDly(uint8_t Tim)
coos提供延時(shí)函數(shù),截圖:
稍微有一點(diǎn)點(diǎn)復(fù)雜,不過(guò)說(shuō)一下基本思路應(yīng)該就很清晰了。
延時(shí)的話是根據(jù)coos提供的參考時(shí)間作為延時(shí)依據(jù)的,所以要根據(jù)延時(shí)的時(shí)間和coos提供的參考時(shí)間,計(jì)算出延時(shí)的終點(diǎn)(延時(shí)的結(jié)束條件),在延時(shí)的條件內(nèi)就等待。
384行:臨時(shí)保存coos參考時(shí)間變量OS_Tim
385行:Tim是傳遞進(jìn)來(lái)的參數(shù),加上OS_Tim是延時(shí)的終點(diǎn),但要判斷是否溢出
387行:判斷延時(shí)終點(diǎn)是否溢出,溢出條件是Tim OS_TIM_PERIODcoos參考時(shí)間的周期,可配置)
  389行:延時(shí)終點(diǎn)溢出了,用coos的參考時(shí)間周期取余Tim,得到新的延時(shí)終點(diǎn)。
          其實(shí)由這里也可以看出,coos提供的延時(shí)范圍實(shí)在一個(gè)coos參考時(shí)間周期內(nèi)的,因?yàn)樵浇绮糠秩∮嗪笠呀?jīng)沒(méi)了。
  390行:進(jìn)入while等待延時(shí)結(jié)束。因?yàn)檠訒r(shí)終點(diǎn)越界了,可知等待的終點(diǎn)coos提供的OS_Tim是小于當(dāng)前臨時(shí)保存的coos參考時(shí)間tim_tmp,所以等待條件第一個(gè)為:
OS_Tim >= tim_tmp
因?yàn)檠訒r(shí)終點(diǎn)溢出也可知延時(shí)終點(diǎn)coos的參考時(shí)間OS_Tim大于計(jì)算出的延時(shí)終點(diǎn)TimTim是計(jì)算出的延時(shí)終點(diǎn),第二個(gè)條件是只要coos參考時(shí)間大于該值則等待結(jié)束) ,所以有
OS_Tim < Tim

  394行:延時(shí)終點(diǎn)為溢出,那么延時(shí)就好理解了,只要coos提供的參考時(shí)間大于計(jì)算出的延時(shí)終點(diǎn),那么延時(shí)就結(jié)束了,有:
          while(OS_Tim <= Tim);

如果對(duì)延時(shí)函數(shù)不理解的話拿起筆和紙寫一下就懂了,我是用筆和紙計(jì)算出來(lái)的…(——#!)

11void OS_ErrCnt(void)
最后一個(gè)條件編譯函數(shù),coos錯(cuò)誤統(tǒng)計(jì)。雖然是個(gè)很簡(jiǎn)單的函數(shù),不過(guò)因?yàn)闆](méi)有更復(fù)雜的函數(shù)了,所以也只能拿它來(lái)壓壓軸,看截圖:
415行:判斷錯(cuò)誤次數(shù)是否溢出,未溢出進(jìn)入417~420
  417行:錯(cuò)誤次數(shù)未溢出,錯(cuò)誤次數(shù)加一
  418~420行:條件編譯,條件是是否使能coos錯(cuò)誤統(tǒng)計(jì)鉤子函數(shù)。什么是鉤子函數(shù),了解過(guò)ucos的同學(xué)們就知道,為了增強(qiáng)ucos的功能,ucos作者為ucos引入10個(gè)鉤子函數(shù),是在當(dāng)ucos發(fā)生某些情況時(shí)候運(yùn)行的函數(shù),由用戶實(shí)現(xiàn)的。我這里也是模仿ucos的,引入鉤子函數(shù)OS_Hook_Errr(),在coos_hook.c文件里面,默認(rèn)函數(shù)體為空,該函數(shù)又用戶實(shí)現(xiàn),目的是為了增強(qiáng)coos的功能,多一些自定義功能,其實(shí)也沒(méi)什么大不了的。

422~428行:條件編譯,條件是是否使能錯(cuò)誤次數(shù)溢出鉤子函數(shù),進(jìn)行錯(cuò)誤次數(shù)溢出處理。
  425行:錯(cuò)誤次數(shù)清零
  426行:錯(cuò)誤次數(shù)溢出鉤子函數(shù),同樣在coos_hook.c文件里面,默認(rèn)函數(shù)體為空,由用戶實(shí)現(xiàn),目的是增強(qiáng)coos功能。

條件編譯函數(shù)就完了,最后還剩下的就是coos的配置,裁剪了,下一章介紹。


第七章 coos的配置、裁剪、類型定義

當(dāng)然作為嵌入式的東西很將就可裁剪性的,配置和裁剪在coos_conf.h文件里面。
先看看配置部分,截圖:
一個(gè)一個(gè)說(shuō):
19行:coos定時(shí)器計(jì)數(shù)值,也就是coos一個(gè)時(shí)間片的長(zhǎng)度。該值越大,coos任務(wù)調(diào)度的頻率就越小,CPU的使用率就越高。但是太大的話任務(wù)被打算時(shí)間太長(zhǎng),所以要折衷配置。默認(rèn)5000,晶振12M的時(shí)候一個(gè)任務(wù)時(shí)間片是5ms

20行:coos時(shí)間片周期,也就是多少個(gè)時(shí)間片為一個(gè)coos周期。一個(gè)周期后coos會(huì)重新刷新任務(wù)時(shí)間片。同樣該值越大,CPU利用率越高,但這個(gè)值的影響是不明顯的。

21行:coos參考時(shí)間的周期,也是coos提供延時(shí)的最大值。對(duì)CPU利用率幾乎沒(méi)影響。

22行:coos管理任務(wù)的個(gè)數(shù)。該值越小,CPU利用率越高,RAM占用越少。因?yàn)槿蝿?wù)調(diào)度的時(shí)候遍歷了每個(gè)任務(wù),需要時(shí)間。并且每個(gè)任務(wù)都要有任務(wù)控制塊(2Byte)和任務(wù)時(shí)間片副本(1Byte),占用RAM。綜合說(shuō)名一下就是,我們需要?jiǎng)?chuàng)建多少個(gè)任務(wù),就定義該值為多少,不用預(yù)留余量之類的。

23行:定時(shí)器專用棧的棧大小,默認(rèn)為8其實(shí)已經(jīng)夠用了,如果使能的定時(shí)器鉤子函數(shù),并且鉤子函數(shù)過(guò)于復(fù)雜(運(yùn)行起來(lái)占用棧較大),那么該值需要適當(dāng)加大,因?yàn)闂R绯隽顺绦蜻\(yùn)行是肯定會(huì)有問(wèn)題的。

24行:coos空閑任務(wù)默認(rèn)的時(shí)間片,該值與coos時(shí)間片周期大小一樣。目的是為了在一個(gè)時(shí)間片周期內(nèi),如果沒(méi)有一個(gè)可以運(yùn)行的任務(wù),那么所有時(shí)間片周期運(yùn)行空閑任務(wù)(不會(huì)因?yàn)榭臻e任務(wù)時(shí)間片不夠而發(fā)生意想不到的結(jié)果)


接下來(lái)是裁剪,老樣子,截圖:
裁剪也就是上一章的條件編譯函數(shù)的使能,后面記錄裁剪掉多少RAMcode
需要使用到上一章的條件編譯函數(shù)就使能,1是使能
可以看到coos默認(rèn)使用很少條件編譯函數(shù),因?yàn)槎际潜容^簡(jiǎn)單的(就是上一章的內(nèi)容),所以不說(shuō)了。

鉤子函數(shù)的配置,截圖:
相信大家一看就懂了的,1使能。
coos鉤子函數(shù)要用戶自己實(shí)現(xiàn)(自定義的功能了),在coos_hook.c文件里面,將自己的代碼寫進(jìn)去就行了,當(dāng)然要使能了才有用。看一下coos_hook.c這幾個(gè)函數(shù)吧。
截圖:

還有類型定義,看截圖:
uint8_t stm32的風(fēng)格,因?yàn)槲乙呀?jīng)習(xí)慣了這種風(fēng)格了,不該了,當(dāng)然也在coos用了這種風(fēng)格。



附錄
coos的說(shuō)明書也應(yīng)該結(jié)束了吧。順便提一下就是,實(shí)現(xiàn)coos任務(wù)管理功能也就那不到200行代碼(除去條件編譯這些可有可無(wú)的東西),但是也寫了我一個(gè)月左右的時(shí)間了。因?yàn)榇_實(shí)也走了一下彎路。例如我想讓任務(wù)調(diào)度獨(dú)立成為一個(gè)函數(shù),定時(shí)器中斷只是負(fù)責(zé)篡改返回地址,但是卻有寄存器沒(méi)入棧保存完善的bug。這個(gè)bug曾讓我一度想放棄,因?yàn)榘l(fā)現(xiàn)到不得不用匯編,我是大大的不想用匯編的。機(jī)器語(yǔ)言已經(jīng)跟機(jī)器密切相關(guān)了,移植性大大減少了,同時(shí)新鮮性也沒(méi)了。因?yàn)橛脜R編的話實(shí)現(xiàn)任務(wù)管理難度是不大的。
期間也上火,頭痛,牙痛,在校醫(yī)院看了兩次,差點(diǎn)要放棄。
裝上win8,安裝了RVMDK,運(yùn)行不了,用什么win7兼容啊,XP SP3兼容啊什么的,都不行。百度也找不到win8兼容的RVMDK,我崩潰了好幾次。因?yàn)檠b一次和卸載一次都是比較花時(shí)間的…
RVMDK是什么?接近keil增強(qiáng)版吧,我就是用RVMDKcoos的,keilRVMDK的前身,RVMDKstm32arm之類用的,當(dāng)然51也行,支持很多微處理器)



參考文獻(xiàn):
挺多的,可以說(shuō)看了不少書吧。能想到的就寫出來(lái)吧
1C和指針(C語(yǔ)言的書)
251單片機(jī)匯編實(shí)例(網(wǎng)上隨便下載看看的)
3c51中文書名keil c51(網(wǎng)上隨便下載的)
4、微型計(jì)算機(jī)原理與接口技術(shù)(上課的課本…)
5、《嵌入式實(shí)時(shí)操作系統(tǒng)uCOS-II(第二版)
6、其它的對(duì)coos貢獻(xiàn)不大,有C語(yǔ)言的,數(shù)據(jù)結(jié)構(gòu)的,算法的,stm32

coos_打包fix1.0.zip

6.13 MB, 下載次數(shù): 199, 下載積分: 黑幣 -5

coos打包舊版.zip

2.56 MB, 下載次數(shù): 72, 下載積分: 黑幣 -5


作者: chenguangyou    時(shí)間: 2015-6-9 10:47
樓主你收了我吧,如此牛,匯編厲害,C語(yǔ)言也厲害。我一直想學(xué)51任務(wù)管理來(lái)著,謝謝!
作者: chenguangyou    時(shí)間: 2015-6-9 10:48
牛的不行了
作者: keykay    時(shí)間: 2015-11-12 13:44
金幣不夠 發(fā)個(gè)貼子頂樓主
作者: 大平山    時(shí)間: 2015-12-5 11:44
牛人啊
作者: 永遠(yuǎn)的王同學(xué)    時(shí)間: 2015-12-22 17:26
51黑有你更精彩
作者: jwjjwj123    時(shí)間: 2016-7-4 10:57
要好好學(xué)習(xí)了,高深實(shí)用
作者: pl01665077    時(shí)間: 2016-10-6 20:04
好文章!!!這就是可搶占式時(shí)間片輪轉(zhuǎn)任務(wù)機(jī)制!!!可以算是一種線程。
作者: 邰夏留    時(shí)間: 2016-10-12 08:28
本菜只能寫一些相當(dāng)簡(jiǎn)單的程序。看到樓主的帖子,感覺(jué)真的很高大上啊。。
作者: aa294828045    時(shí)間: 2016-11-14 11:16
好!!!!!!!!!!
作者: utzuzu    時(shí)間: 2016-12-7 22:25
首先要給樓主贊一個(gè),有趣果然是貨真價(jià)實(shí)的春藥!
粗看了一遍有幾點(diǎn)疑問(wèn),請(qǐng)教一二:
1、假如系統(tǒng)周期是20個(gè)時(shí)間片,有5個(gè)任務(wù),分別都是1個(gè)時(shí)間片,那么一個(gè)系統(tǒng)周期里面還有15個(gè)時(shí)間片是浪費(fèi)了嗎?
2、關(guān)于延時(shí)函數(shù),如果目標(biāo)時(shí)間剛剛好是 FF或FFFF,那么是不是會(huì)永遠(yuǎn)在延時(shí)里面出不來(lái)。
3、任務(wù)之間的通信和互斥是怎么考慮的?
作者: utzuzu    時(shí)間: 2016-12-7 22:29
不錯(cuò),看了有疑問(wèn)請(qǐng)教
作者: utzuzu    時(shí)間: 2016-12-7 22:30
本帖最后由 utzuzu 于 2016-12-7 22:33 編輯

1、如果系統(tǒng)周期是20個(gè)時(shí)間片,有5個(gè)任務(wù)每個(gè)1個(gè)時(shí)間片,那么一個(gè)系統(tǒng)周期內(nèi)剩下的15個(gè)時(shí)間片是什么狀態(tài),浪費(fèi)了嗎2、在延時(shí)函數(shù)內(nèi),如果目標(biāo)時(shí)間剛好是FF或者FFFF,會(huì)不會(huì)永遠(yuǎn)在延時(shí)里出不來(lái)了。
3、有沒(méi)有提供任務(wù)之間的通信和互斥的機(jī)制。

作者: andy12345    時(shí)間: 2016-12-8 21:13
高深實(shí)用,很給力!
哈哈  又被多扣一次 要再多積一點(diǎn) 在下其他
作者: adls    時(shí)間: 2016-12-9 06:03
要好好學(xué)習(xí)了,高深實(shí)用
作者: luotechnically    時(shí)間: 2017-2-18 09:55
樓主思路太好了
作者: luotechnically    時(shí)間: 2017-2-18 09:56
學(xué)習(xí)學(xué)習(xí)樓主的思路和代碼風(fēng)格
作者: 846649188    時(shí)間: 2017-3-5 20:43
萬(wàn)分感謝
作者: bbxyzzj    時(shí)間: 2017-3-6 08:09
感謝分享!
作者: King_Dream    時(shí)間: 2017-3-28 11:25
頂一下
作者: 1127957008    時(shí)間: 2017-4-3 23:39
這個(gè)和rxt riny有區(qū)別嗎
作者: liuxiaohua719    時(shí)間: 2017-5-6 16:28
真的是很受用嗎
作者: jiacheng1    時(shí)間: 2017-8-7 14:49
很給力  很有用
作者: 李牧林    時(shí)間: 2017-12-9 18:40
非常牛
作者: 練氏    時(shí)間: 2017-12-9 20:13
雖然看不懂大佬說(shuō)的是啥,但是感覺(jué)很牛,確實(shí),積累到一定經(jīng)驗(yàn)的時(shí)候要開始搞自己的
作者: thisisyy    時(shí)間: 2018-4-20 17:33
牛逼,同樣是用單片機(jī)你玩出了境界
作者: 口口單禾術(shù)    時(shí)間: 2018-6-9 12:46
跪著看完,大佬大佬
作者: wdliming    時(shí)間: 2019-11-22 08:32
謝謝分分享

作者: jvsoft    時(shí)間: 2019-11-22 11:12
樓主牛。匯編厲害,C語(yǔ)言也厲害。我一直想學(xué)51任務(wù)管理來(lái)著,謝謝!
作者: wdliming    時(shí)間: 2019-11-23 22:06
謝謝分享~~學(xué)習(xí)os的好資料
作者: jjwangxu2008    時(shí)間: 2019-12-10 13:13
牛的不行了




歡迎光臨 (http://m.raoushi.com/bbs/) Powered by Discuz! X3.1