久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 5551|回復: 5
打印 上一主題 下一主題
收起左側

簡單的多任務操作系統程序源碼

[復制鏈接]
跳轉到指定樓層
樓主
ID:20672 發表于 2018-7-22 09:38 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
這是一個可以運行的多任務程序,參考了網上的教程,十分感謝,自己添加了兩個led的任務,可以運行的,程序也不大。
/*

簡單的多任務操作系統

其實只有個任務調度切換,把說它是OS有點牽強,但它對于一些簡單的開發應用來說,
簡單也許就是最好的.盡情的擴展它吧.別忘了把你的成果分享給大家.

這是一個最簡單的OS,一切以運行效率為重,經測試,切換一次任務僅20個機器周期,
也就是在標準51(工作于12M晶振)上20uS.
而為速度作出的犧牲是,為了給每個任務都分配一個私有堆棧,而占用了較多的內存.
作為補償,多任務更容易安排程序邏輯,從而可以節省一些用于控制的變量.
任務槽越多,占用內存越多,但任務也越好安排,以實際需求合理安排任務數目.
一般來說,4個已足夠.況且可以拿一個槽出來作為活動槽,換入換入一些臨時任務.

task_load(函數名,任務槽號)
裝載任務

os_start(任務槽號)
啟動任務表.參數必須指向一個裝載了的任務,否則系統會崩潰.

task_switch()
切換到其它任務


.編寫任務函數注意事項:
KEIL C編譯器是假定用戶使用單任務環境,所以在變量的使用上都未對多任務進行處理,
編寫任務時應注意變量覆蓋和代碼重入問題.

1.覆蓋:編譯器為了節省內存,會給兩個沒用調用關系的函數分配同一內存地址作為變量空間.
這在單任務下是很合理的,但對于多任務來說,兩個進程會互相干擾對方.
解決的方法是:凡作用域內會跨越task_switch()的變量,都使用static前輟,
保證其地址空間分配時的唯一性.

2.重入:重入并不是多任務下獨有的問題,在單任務時,函數遞歸同樣會導致重入,
即,一個函數的不同實例(或者叫作"復本")之間的變量覆蓋問題.
解決的方法是:使用reentrant函數后輟(例如:void function1() reentrant{...}).當然,根本的辦法還是避免重入,因為重入會帶來巨大的目標代碼量,
并極大降低運行效率.

3.額外提醒一句,在本例中,任務函數必須為一個死循環.退出函數會導致系統崩潰.


.任務函數如果是用匯編寫成或內嵌匯編,切換任務時應該注意什么問題?

由于KEIL C編譯器在處理函數調用時的約定規則為"子函數有可能修改任務寄存器",
因此編譯器在調用前已釋放所有寄存器,子函數無需考慮保護任何寄存器.
這對于寫慣匯編的人來說有點不習慣: 匯編習慣于在子程序中保護寄存器.
請注意一條原則:凡是需要跨越task_switch()的寄存器,全部需要保護(例如入棧).
根本解決辦法還是,不要讓寄存器跨越任務切換函數task_switch()
事實上這里要補充一下,正如前所說,由于編譯器存在變量地址覆蓋優化,
因此凡是非靜態變量都不得跨越task_switch().


任務函數的書寫:
void 函數名(void)
{        //任務函數必須定義為無參數型
        while(1)
        {
                //任務函數不得返回,必須為死循環
                //....這里寫任務處理代碼

                task_switch();//每執行一段時間任務,就釋放CPU一下,
                讓別的任務有機會運行.
        }
}


任務裝載:
task_load(函數名,任務槽號)

裝載函數的動作可發生在任意時候,但通常是在main()中.要注意的是,
在本例中由于沒考慮任務換出,
所以在執行os_start()前必須將所有任務槽裝滿.之后可以隨意更換任務槽中的任務.

啟動任務調度器:
os_start(任務槽號)

調用該宏后,將從參數指定的任務槽開始執行任務調度.
本例為每切換一次任務需額外開銷20個機器周期,用于遷移堆棧.
*/


#include <reg52.h>

sbit LED1 = P2^0;
sbit LED2 = P2^1;
void func1();
void func2();

/*============================以下為任務管理器代碼============================*/

//任務槽個數.在本例中并未考慮任務換入換出,所以實際運行的任務有多少個,
//就定義多少個任務槽,不可多定義或少定義
#define MAX_TASKS 5

//任務的棧指針
unsigned char idata task_sp[MAX_TASKS];

//最大棧深.最低不得少于2個,保守值為12.
//預估方法:以2為基數,每增加一層函數調用,加2字節.
//如果其間可能發生中斷,則還要再加上中斷需要的棧深.
//減小棧深的方法:1.盡量少嵌套子程序 2.調子程序前關中斷.
#define MAX_TASK_DEP 12

unsigned char idata task_stack[MAX_TASKS][MAX_TASK_DEP];//任務堆棧.

unsigned char task_id;                //當前活動任務號


//任務切換函數(任務調度器)
void task_switch()
{
        task_sp[task_id] = SP;                //保存當前任務的棧指針

        if(++task_id == MAX_TASKS)        //任務號切換到下一個任務
                task_id = 0;

        SP = task_sp[task_id];                //將系統的棧指針指向下個任務的私棧
}




//任務裝入函數.將指定的函數(參數1)裝入指定(參數2)的任務槽中.
//如果該槽中原來就有任務,則原任務丟失,但系統本身不會發生錯誤.
//將各任務的函數地址的低字節和高字節分別入在
//task_stack[任務號][0]和task_stack[任務號][1]中
void task_load(unsigned int fn, unsigned char tid)
{
        task_sp[tid] = task_stack[tid] + 1;
        task_stack[tid][0] = (unsigned int)fn & 0xff;
        task_stack[tid][1] = (unsigned int)fn >> 8;
}

//從指定的任務開始運行任務調度.調用該宏后,將永不返回.
#define os_start(tid) {task_id = tid,SP = task_sp[tid];return;}




/*============================以下為測試代碼============================*/


unsigned char stra[3], strb[3];//用于內存塊復制測試的數組.


//測試任務:復制內存塊.每復制一個字節釋放CPU一次
void task1()
{
        //每復制一個字節釋放CPU一次,控制循環的變量必須考慮覆蓋
        static unsigned char i;//如果將這個變量前的static去掉,會發生什么事?
        i = 0;

        while(1) //任務必須為死循環,不得退出函數,否則系統會崩潰
        {
                stra[ i] = strb[ i];
                if(++i == sizeof(stra))
                        i = 0;

                //變量i在這里跨越了task_switch(),因此它必須定義為靜態(static),
                //否則它將會被其它進程修改,因為在另一個進程里也會用到該變量所占用的地址.
                task_switch();//釋放CPU一會兒,讓其它進程有機會運行.如果去掉該行,則別的進程永遠不會被調用到
        }
}

//測試任務:復制內存塊.每復制一個字節釋放CPU一次.
void task2()
{
        //每復制一個字節釋放CPU一次,控制循環的變量必須考慮覆蓋
        static unsigned char i;//如果將這個變量前的static去掉,將會發生覆蓋問題.
        //task1()和task2()會被編譯器分配到同一個內存地址上,當兩個任務同時運行時,i的值就會被兩個任務改來改去
        i = 0;

        while(1) //任務必須為死循環,不得退出函數,否則系統會崩潰
        {
                stra[ i] = strb[ i];
                if(++i == sizeof(stra))
                        i = 0;

                //變量i在這里跨越了task_switch(),因此它必須定義為靜態(static),
                //否則它將會被其它進程修改,因為在另一個進程里也會用到該變量所占用的地址.
                task_switch();//釋放CPU一會兒,讓其它進程有機會運行.如果去掉該行,則別的進程永遠不會被調用到
        }
}

//測試任務:復制內存塊.復制完所有字節后釋放CPU一次.
void task3()
{
        //復制全部字節后才釋放CPU,控制循環的變量不須考慮覆蓋
        unsigned char i;//這個變量前不需要加static,
        //因為在它的作用域內并沒有釋放過CPU

        while(1) //任務必須為死循環,不得退出函數,否則系統會崩潰
        {
                i = sizeof(stra);
                do
                {
                        stra[i-1] = strb[i-1];
                }
                while(--i);

                //變量i在這里已完成它的使命,所以無需定義為靜態.
                //你甚至可以定義為寄存器型(regiter)
                task_switch();//釋放CPU一會兒,讓其它進程有機會運行.如果去掉該行,
                //則別的進程永遠不會被調用到
        }
}



/*
my first task
*/

void func1()
{
        static unsigned char data i;
        i = 0;

        while(1)
        {
                if(i<250)
                {
                        i++;
                }
                if(i>=250)
                {
                        LED1 = ~LED1;
                        i = 0;                        
                }
                task_switch();
        }
}
void func2()
{
        static unsigned int data j;
        j = 0;

        while(1)
        {
                if(j<654)
                {
                        j++;
                }
                if(j>=654)
                {
                        LED2 = ~LED2;
                        j = 0;                                
                }
                task_switch();
        }
}


/*
void func1()
{
    register char data i;
    while(1)
        {
        i = 5;
        do
                {
                sigl = !sigl;
        }while(--i);
        task_switch();
    }
}
void func2()
{
    register char data i;
    while(1)
        {
        i = 5;
        do
                {
                sigl = !sigl;
        }while(--i);
        task_switch();
    }
}
*/

void main()
{
        //在這個示例里并沒有考慮任務的換入換出,所以任務槽必須全部用完,否則系統會崩潰.
        //這里裝載了三個任務,因此在定義MAX_TASKS時也必須定義為5
        task_load(task1, 0);//將task1函數裝入0號槽
        task_load(task2, 1);//將task2函數裝入1號槽
        task_load(task3, 2);//將task3函數裝入2號槽
        task_load(func1, 3);//將task3函數裝入3號槽
        task_load(func2, 4);//將task3函數裝入4號槽

        os_start(0);//啟動任務調度,并從0號槽開始運行.參數改為1,則首先運行1號槽.
        //調用該宏后,程序流將永不再返回main(),也就是說,該語句行之后的所有語句都不被執行到.
}

評分

參與人數 1黑幣 +50 收起 理由
admin + 50 共享資料的黑幣獎勵!

查看全部評分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏3 分享淘帖 頂 踩
回復

使用道具 舉報

沙發
ID:394678 發表于 2018-9-9 18:04 | 只看該作者
好,我現在有個項目這好用到這個
回復

使用道具 舉報

板凳
ID:388870 發表于 2018-9-11 08:58 | 只看該作者
謝謝分享
回復

使用道具 舉報

地板
ID:66287 發表于 2018-9-14 09:09 | 只看該作者
剛編譯運行了一下,很好
回復

使用道具 舉報

5#
ID:364805 發表于 2019-6-22 09:59 | 只看該作者

謝謝分享
回復

使用道具 舉報

6#
ID:382454 發表于 2019-6-22 11:55 | 只看該作者
菜鳥看到這個,太深奧了。
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 日韩一区二区三区在线 | 国产精品视频一区二区三区四区国 | 日韩精品一区二区三区中文在线 | 免费成人高清在线视频 | 久久久久99 | 国产成人免费网站 | 欧美日韩综合视频 | 亚洲一区二区av | 国产精品不卡视频 | 中国91av| 中文字幕一区二区三区四区 | 欧美精品一区二区三区在线播放 | 午夜精品在线 | 久久免费看 | 日韩欧美一区二区三区四区 | 久久久久国产一区二区 | 日韩欧美亚洲 | 日韩在线观看中文字幕 | 久久精品国产一区二区电影 | 欧美无乱码久久久免费午夜一区 | 日韩成人精品一区二区三区 | 精品无码久久久久久国产 | 99精品国产一区二区三区 | 99国产精品久久久久 | 男女啪啪高潮无遮挡免费动态 | 男女视频免费 | 国产精彩视频一区 | 国产片侵犯亲女视频播放 | 欧美激情黄色 | 欧美日韩精品国产 | 放个毛片看看 | 97成人在线 | 色综合色综合 | 久久综合国产 | 青青草在线播放 | 久草福利 | 毛片免费视频 | 日韩国产高清在线观看 | 中文字幕一区二区三区在线乱码 | 偷派自拍| 99tv|