新聞中心

        EEPW首頁(yè) > 嵌入式系統(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)閉
        主站蜘蛛池模板: 启东市| 南江县| 应城市| 北辰区| 彭山县| 永福县| 钟山县| 宁蒗| 堆龙德庆县| 禄丰县| 内乡县| 清河县| 临海市| 突泉县| 依兰县| 香格里拉县| 株洲县| 临泽县| 繁昌县| 沐川县| 晋中市| 建阳市| 万山特区| 新巴尔虎右旗| 西畴县| 安平县| 柳州市| 中方县| 青海省| 永春县| 郧西县| 廉江市| 上林县| 大余县| 阿尔山市| 绵阳市| 昌图县| 清水县| 黄梅县| 揭阳市| 法库县|