<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > DIY:給單片機(jī)寫個(gè)實(shí)時(shí)操作系統(tǒng)內(nèi)核!

          DIY:給單片機(jī)寫個(gè)實(shí)時(shí)操作系統(tǒng)內(nèi)核!

          作者: 時(shí)間:2016-11-29 來源:網(wǎng)絡(luò) 收藏

           
          調(diào)度策略:實(shí)現(xiàn)了調(diào)度,還要繼續(xù)考慮調(diào)度策略,就是什么情況下需要調(diào)度哪些任務(wù)。調(diào)度策略分很多種,有興趣的可以去看那本《操作系統(tǒng)原理》,在我的源代碼里面使用了”搶占式優(yōu)先級(jí)調(diào)度+同一優(yōu)先級(jí)下時(shí)間片輪詢調(diào)度“的方法。
          所謂搶占式優(yōu)先級(jí)調(diào)度是一種實(shí)時(shí)調(diào)度的方法,在實(shí)時(shí)操作系統(tǒng)中常用,這種方法的原理就是:操作系統(tǒng)在任何時(shí)候都要保證擁有最高優(yōu)先級(jí)的那個(gè)任務(wù)處于運(yùn)行態(tài),比如此記在運(yùn)行著優(yōu)先級(jí)為2的任務(wù),因?yàn)橐恍┬盘?hào)到達(dá),優(yōu)先級(jí)為1的那個(gè)任務(wù)解除了阻塞,處于就緒態(tài),這時(shí)操作系統(tǒng)就必須馬上停止任務(wù)2,切換到任務(wù)1,切換的這段時(shí)間需要越短越好。
          而時(shí)間片輪詢即是讓每個(gè)任務(wù)都處于平等地位,然后給每個(gè)任務(wù)相同的時(shí)間片,當(dāng)一個(gè)任務(wù)的運(yùn)行時(shí)間用完了,操作系統(tǒng)就馬上切換給下一個(gè)需要執(zhí)行的任務(wù),這種方法的實(shí)時(shí)性不高,但它確保了每個(gè)任務(wù)都有相同的執(zhí)行時(shí)間。
          我把這兩種方法結(jié)合起來,首先設(shè)定了8個(gè)優(yōu)先級(jí)組,每個(gè)優(yōu)先級(jí)組下面都用單向鏈表把具有相同優(yōu)先級(jí)的任務(wù)連接起來。這樣的話首先操作系統(tǒng)會(huì)查找最高優(yōu)先級(jí)的那組,然后在組里面輪流執(zhí)行所有任務(wù)(和UCOS II相比這種做法更具有靈活性,因?yàn)閁COS II只有搶占式調(diào)度,這是UCOS II的硬傷。。)。我聲明了一個(gè)任務(wù)結(jié)構(gòu)體稱為線程控制塊,把關(guān)于該任務(wù)的所有狀態(tài)都放在一起:
          /**
          * @結(jié)構(gòu)體聲明
          * @名稱 : OS_TCB , *pOS_TCB
          * @成員 : 1. OS_DataType_ThreadStack *ThreadStackTop
          * 線程人工堆棧棧頂指針
          * 2. OS_DataType_ThreadStack *ThreadStackBottom
          * 線程人工堆棧棧底指針
          * 3. OS_DataType_ThreadStackSize ThreadStackSize
          * 線程人工堆棧大小
          * 4. OS_DataType_ThreadID ThreadID
          * 線程ID號(hào)
          * 5. OS_DataType_ThreadStatus ThreadStatus
          * 線程運(yùn)行狀態(tài)
          * 6. OS_DataType_PSW PSW
          * 記錄線程的程序狀態(tài)寄存器
          * 7. struct _OS_TCB *Front
          * 指向上一個(gè)線程控制塊的指針
          * 8. struct _OS_TCB *Next
          * 指向下一人線程控制塊的指針
          * 9.struct _OS_TCB *CommWaitNext ;
          * 指向線程通信控制塊的指針
          * 10.struct _OS_TCB *TimeWaitNext ;
          * 指向延時(shí)等待鏈表的指針
          * 11.OS_DataType_PreemptionPriority Priority ;
          * 任務(wù)優(yōu)先級(jí)
          * 12.OS_DataType_TimeDelay TimeDelay ;
          * 任務(wù)延時(shí)時(shí)間
          * @描述 : 定義線程控制塊的成員
          * @建立時(shí)間 : 2011-11-15
          * @最近修改時(shí)間: 2011-11-17
          */
          typedef struct _OS_TCB{
          OS_DataType_ThreadStack *ThreadStackTop ;
          OS_DataType_ThreadStack *ThreadStackBottom ;
          OS_DataType_ThreadStackSize ThreadStackSize;
          OS_DataType_ThreadID ThreadID ;
          OS_DataType_ThreadStatus ThreadStatus ;
          OS_DataType_PSW PSW ;
          struct _OS_TCB *Front ;
          struct _OS_TCB *Next ;
          #if OS_COMMUNICATION_EN == ON
          struct _OS_TCB *CommWaitNext ;
          #endif

           
          struct _OS_TCB *TimeWaitNext ;
          OS_DataType_PreemptionPriority Priority ;

           
          OS_DataType_TimeDelay TimeDelay ;
          }OS_TCB,*pOS_TCB;

           
          首先啟動(dòng)系統(tǒng)的時(shí)候需要先創(chuàng)建任務(wù),任務(wù)被創(chuàng)建之后才可以得到執(zhí)行,使用如下函數(shù):
          /**
          * @名稱:線程創(chuàng)建函數(shù)
          * @輸入?yún)?shù):1.pOS_TCB ThreadControlBlock 線程控制塊結(jié)構(gòu)體指針
          * 2.void (*Thread)(void*) 線程函數(shù)入口地址,接受一個(gè)空指針形式的輸入?yún)?shù),無返回參數(shù)
          * 3.void *Argument 需要傳遞給線程的參數(shù),空指針形式
          * @建立時(shí)間 : 2011-11-18
          * @最近修改時(shí)間: 2011-11-18
          */
          void OS_ThreadCreate(pOS_TCB ThreadControlBlock,void (*Thread)(void *),void *Argument)
          關(guān)于創(chuàng)建任務(wù)的大致描述就是:填定線程控制塊,把線程控制塊鏈到單向鏈表中,設(shè)置人工堆棧,細(xì)節(jié)很多,就不一一贅述了。

           
          當(dāng)前版本只實(shí)現(xiàn)了輪詢調(diào)度,還沒加上搶占調(diào)度,使用下面的函數(shù)就可以啟動(dòng)操作系統(tǒng)開始多線程任務(wù)!
          /**
          * @名稱 : 實(shí)時(shí)內(nèi)核引發(fā)函數(shù)
          * @版本 : V 0.0
          * @輸入?yún)?shù) : 無
          * @輸出參數(shù) : 無
          * @描述 : 在主函數(shù)中用于啟動(dòng),調(diào)用該函數(shù)后不會(huì)返回,直接切換到最高優(yōu)先級(jí)任務(wù)開始執(zhí)行
          * @建立時(shí)間 : 2011-11-15
          * @最近修改時(shí)間: 2011-11-15
          */
          void OS_KernelStart(void)
          {
          OS_Status = OS_RUNNING ; //把內(nèi)核狀態(tài)設(shè)置為運(yùn)行態(tài)

           
          //取得第一個(gè)需要運(yùn)行的任務(wù)
          OS_CurrentThread = OS_TCB_PriorityGroup[pgm_read_byte(ThreadSearchTab + OS_PreemptionPriority)].OS_TCB_Current;
          OS_LastThread = NULL ;
          //SP指針指向該任務(wù)的棧頂
          SP = (uint16_t)OS_CurrentThread->ThreadStackTop ;
          //使用出棧操作
          POP_REG();
          //調(diào)用RET,調(diào)用之后開始執(zhí)行任務(wù),不會(huì)再返回到這里
          _asm("RET");
          }

           
          怎樣實(shí)現(xiàn)時(shí)間片?答案是用定時(shí)器定時(shí),每次定時(shí)器產(chǎn)生中斷的時(shí)候就轉(zhuǎn)換一次任務(wù),時(shí)基可以自己確定,一般來說時(shí)基越小的話會(huì)讓CPU花很多時(shí)間在切換任務(wù)上,降低了效率,時(shí)基大的話又使時(shí)間粒度變粗,會(huì)使一些程序得不到及時(shí)的執(zhí)行。我設(shè)定了每10MS中斷一次,就是說每一輪中每個(gè)線程都有10MS的執(zhí)行時(shí)間。具體算法不再贅述。

           
          內(nèi)存管理策略
          接下來要考慮怎樣管理內(nèi)存了!在PC里面編程的時(shí)候,如果需要開辟一個(gè)內(nèi)存空間,我們可以很容易地調(diào)用malloc()和free()來完成,但是在單片機(jī)里面卻行不通,因?yàn)橐獙?shí)現(xiàn)這兩個(gè)函數(shù)背后需要完成很多算法支持,從速度和空間上單片機(jī)都做不到。
          在單片機(jī)里面如果你需要開辟內(nèi)存空間,你只有在編譯的時(shí)候就先定義好變量,無法動(dòng)態(tài)申請(qǐng),但是我們可以設(shè)計(jì)一個(gè)簡(jiǎn)單的內(nèi)存管理策略來實(shí)現(xiàn)這種動(dòng)態(tài)申請(qǐng)!原理就是在編譯的時(shí)候先向編譯器要一塊足夠大的內(nèi)存并且聲明為靜態(tài),然后把這塊空間交給內(nèi)存管理模塊來調(diào)用,內(nèi)存管理模塊負(fù)責(zé)分配這塊內(nèi)存,當(dāng)有任務(wù)要向它申請(qǐng)內(nèi)存的時(shí)候它就從里面拿出一塊交給任務(wù),而任務(wù)要釋放的時(shí)候就把該內(nèi)存空間交給內(nèi)存管理模塊來實(shí)現(xiàn)。
          關(guān)于內(nèi)存管理也有很多種策略,在這里就不一一述說了,我在源代碼里面使用了一種簡(jiǎn)單的隨機(jī)分配的方法,即有線程申請(qǐng)的時(shí)候就從當(dāng)前內(nèi)存塊的可用空間里拿出一塊來,然后在內(nèi)存頭加上一個(gè)專用的結(jié)構(gòu)體,把每個(gè)內(nèi)存塊都鏈接起來,這樣便于管理。當(dāng)線程釋放內(nèi)存的時(shí)候,就把內(nèi)存返回到內(nèi)存空間并跟其他空間的內(nèi)存塊合并起來等待線程再次調(diào)用。
          /**
          * @名稱 : 內(nèi)存塊申請(qǐng)函數(shù)
          * @版本 : V 0.0
          * @輸入?yún)?shù) : 1. OS_DataType_MemorySize MemorySize
          需要申請(qǐng)內(nèi)存塊的大小
          * @輸出參數(shù) : 1. void *
          若申請(qǐng)成功,則返回可使用內(nèi)存塊首地址,否則返回NULL
          * @描述 :
          * @建立時(shí)間 : 2011-11-16
          * @最近修改時(shí)間: 2011-11-16
          */
          #if OS_MEMORY_EN
          void *OS_MemoryMalloc(OS_DataType_MemorySize MemorySize)
          {
          pOS_MCB pmcb = OS_MCB_Head ;
          pOS_MCB pmcb2 ;
          MemorySize+=OS_MEMORY_BLOCK_SIZE ;
          //進(jìn)入內(nèi)存搜索算法
          while(1)
          {
          //檢測(cè)該內(nèi)存塊是否存在
          if(pmcb==NULL)
          {
          return NULL ;
          }
          //如果存在則檢測(cè)該內(nèi)存塊的使用狀態(tài)
          else if( (pmcb->Status==OS_MEMORY_STATUS_IDLE) && (pmcb->Size >= MemorySize) )
          {
          //如果可用內(nèi)存塊大小剛好等于需要申請(qǐng)的大小
          //則立即分配
          if(pmcb->Size == MemorySize)
          {
          pmcb->Status=OS_MEMORY_STATUS_USING ;
          OS_MemoryIdleCount -= MemorySize ;
          return (OS_DataType_Memory *)pmcb + OS_MEMORY_SIZE ;
          }
          //若可用內(nèi)存塊大小大于需要申請(qǐng)的大小
          //則進(jìn)行分割操作
          else
          {
          pmcb2=(pOS_MCB)( (OS_DataType_Memory *)pmcb + MemorySize );
          pmcb2->Front=pmcb ;
          pmcb2->Next=pmcb->Next ;
          pmcb2->Status=OS_MEMORY_STATUS_IDLE ;
          pmcb2->Size = pmcb->Size - MemorySize ;
          pmcb->Status = OS_MEMORY_STATUS_USING ;
          pmcb->Size = MemorySize ;
          pmcb->Next=pmcb2;
          OS_MemoryIdleCount -= MemorySize ;
          return (OS_DataType_Memory *)pmcb+OS_MEMORY_BLOCK_SIZE ;
          }
          }
          else
          {
          pmcb=pmcb->Next;
          }
          }
          }
          #endif

           
          內(nèi)存釋放函數(shù):
          /**
          * @名稱 : 內(nèi)存塊釋放函數(shù)
          * @版本 : V 0.0
          * @輸入?yún)?shù) : 1. OS_DataType_MemorySize MemorySize
          需要申請(qǐng)內(nèi)存塊的大小
          * @輸出參數(shù) : 1. void *
          若申請(qǐng)成功,則返回可使用內(nèi)存塊首地址,否則返回NULL
          * @描述 :
          * @建立時(shí)間 : 2011-11-16
          * @最近修改時(shí)間: 2011-11-16
          */
          #if OS_MEMORY_EN
          void OS_MemoryFree(void *MCB)
          {
          pOS_MCB pmcb = (pOS_MCB)( (OS_DataType_Memory *)MCB - OS_MEMORY_BLOCK_SIZE );
          //將當(dāng)前內(nèi)存塊設(shè)置為空閑狀態(tài)
          pmcb->Status=OS_MEMORY_STATUS_IDLE ;
          OS_MemoryIdleCount += pmcb->Size ;
          //如果存在上一塊內(nèi)存塊,則進(jìn)入判斷
          if(pmcb->Front!=NULL)
          {
          //如果上一塊內(nèi)存塊處于空閑狀態(tài),則進(jìn)行合并操作
          if(pmcb->Front->Status == OS_MEMORY_STATUS_IDLE)
          {
          pmcb->Front->Size += pmcb->Size ;
          pmcb->Front->Next = pmcb->Next ;
          pmcb=pmcb->Front ;
          OS_MemoryIdleCount += pmcb->Size ;
          }
          }
          //如果存在下一塊內(nèi)存塊,則進(jìn)入判斷
          if(pmcb->Next!=NULL)
          {
          //如果下一塊內(nèi)存塊處于空閑狀態(tài),則進(jìn)行合并操作
          if(pmcb->Next->Status==OS_MEMORY_STATUS_IDLE)
          {
          pmcb->Size += pmcb->Next->Size ;
          pmcb->Next = pmcb->Next->Next ;
          OS_MemoryIdleCount += pmcb->Size ;
          }
          }
          }
          #endif

           
          這種分配策略雖然實(shí)現(xiàn)簡(jiǎn)單,但是缺點(diǎn)就是容易產(chǎn)生內(nèi)存碎片,即隨著時(shí)間推移,可用內(nèi)存會(huì)越來越碎片化,最后導(dǎo)致想要申請(qǐng)足夠大的內(nèi)存塊都沒辦法。。。

           
          /********************************************************************************/
          至此,一個(gè)簡(jiǎn)單的單片機(jī)使用的操作系統(tǒng)模型就算完成了,應(yīng)用在AVR單片機(jī)中,下面進(jìn)入測(cè)試階段:
          因?yàn)檫€沒有完成線程通信模塊還搶占式算法,所以目前只能執(zhí)行輪詢多任務(wù)操作。我寫了一個(gè)測(cè)試程序,就是創(chuàng)建三個(gè)流水燈程序(是不是覺得寫個(gè)操作系統(tǒng)就用來跑流水燈太浪費(fèi)了,哈哈),讓它們同時(shí)閃,在PROTEUS中仿真查看
          在AVR STUDIO5開發(fā)環(huán)境中編寫,代碼如下:
          #include "includes.h"
          #include "OS_core.h"

           
          #define STACK_SIZE 80 //定義每個(gè)任務(wù)的人工堆棧大小

           
          //定義三個(gè)任務(wù)各自的人工堆棧
          uint8_t Test1Stack[STACK_SIZE];
          uint8_t Test2Stack[STACK_SIZE];
          uint8_t Test3Stack[STACK_SIZE];

           
          //定義三個(gè)任務(wù)各自的線程控制塊
          OS_TCB Task1;
          OS_TCB Task2;
          OS_TCB Task3;

           
          //線程1讓PB口閃爍
          void Test1(void *p)
          {
          uint8_t i;
          DDRB=0XFF;
          PORTB=0xff;
          SREG|=0X80;
          while(1)
          {
          for(i=0;i<8;i++)PORTB=1<}

          評(píng)論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();