前言: 此文章適合對51單片機感興趣,也想利用51實現簡單的實時系統的程序管理,適合學習交流用,望謹慎吐槽。
準備一:
所謂實時呢,這個概念三言兩語不好解釋, 在這里就不涉及太多的官方及專業名詞和術語。就像你用電飯煲做飯一樣,當你啟動后,是希望它一直工作,直到把
飯做好,而不希望它會中途出現故障什么的。然后在中斷,你希望想煮粥,那就停了電,再加水,調成煮粥模式,然后繼續工作,直到完成。這個,就有點實時的解
釋吧,如果這個解釋不夠形象,要不再舉例另一個,請私下聯系再作舉例。
所謂多任務呢,這個應該不難解釋了,不過,還是再羅嗦一下。51單片機,就只有一個CPU,就像你在廚房做飯,只有你一個人,你又想煮飯,又想燒菜,還
想打掃衛生,但你一個人,不可能同時做的。所以,一個重要的概念就在這里。一個CPU不可能同時做這么多做事的。所以,它不得不像人一樣,一會做這個,一
會做那個。比如10分鐘掃一下地,然后10分鐘燒一個菜,然后10分鐘 去上個廁所,然后10分鐘再回來掃一下地。就這樣把時間分配好,但以一個小時來
看,就感覺你這一個小時是幾個工作同時在做,而且這幾個事互不影響的。 好了,有了這兩個概念就差不多了。 51單片機靠什么來得到這個確定的時間,并把它分割成一段一段的呢。
下面就以一個多任務系統來說,因為51單片機的儲存空間不夠,一般不適合做大的系統移植。玩過STM32的人應該清楚uC/OS系統的移植,把uC
/OS移植到51單片機,也不無不可,只是光是把一個系統移植上去,單片機就沒有多少空間做其它事了。就好比說,你一個硬盤是40G的,你裝了個win
7系統后都差不多占了20G了,那你硬盤就沒有多少空間再做其它事了。 uC/OS不好移植了,就更別說把linux系統移植給51單片機了。那,就沒有合適給51單片機的嗎?答案是有的。 如果你使用增強型的51單片機,還是可以考慮對uC/OS裁剪移植的。 不過,對于增強型,還是普通型,還是基本51內核的單片機,都有一個比較適合做簡單的多任務系統開發的。
在KEIL里,開發了一個適合對51使用的多任務實時系統的開發的。就是RTX51系統。 RTX51分有兩個,一個是RTX51FULL,一個是RTX51TINY。 在這里介紹RTX51TINY。 對系統的移植,也不算移植吧, 從硬件的角度來說,就是對CPU的時間調度做出設置,用來管理內存,時間等。越大的系統管理的越多,管理的也越周到,但操作也越復雜。
從軟件的角度來說,就是添加把一系列的庫文件添加在程序里,然后,在程序里對庫里的子程序調用就可了。越大的系統里,提供的庫函數就越多,庫函數越多,調
用越來考慮的就越多。庫函數越少呢,程序應該會清晰一些,當然實現的功能及管理就跟不上去了。但對于51來說,RTX51TINY還是可以的。
好,下面就一步一步來使用RTX51TINY來實現51的多任務的吧。
一,庫文件的添加及KEIL的設置。 #include <rtx51tny.h> //rtx51tny.h這個文件就是RTX51TINY的庫文件,只有一個文件,因為這個文件是keil里包含有的,不必去哪里找。 #include <reg51.h> 然后就是打開工程的設置,如圖: 在Target這個選項里,找到如圖中那個,選擇RTX51 Tiny這個選項,這樣,在編譯的時候,就不用提示頭文件不存在了。 這便是第一步在完成的。 二,對rtx51tny.h的解讀。 從軟件的角度,也只是把這個頭文件添加進來,然后對庫函數做出調用。  這個庫文件的函數就這幾個,所以,實現多任務管理,只要恰當的使用這個函數就可以了。 其中,常用的幾個函數是 os_create_task(),os_send_signal(),os_wait().。由于os_wait1和os_wait2的這兩個函數是差不多的, 。 三,以一個簡單的程序分析: #include <rtx51tny.h> #include <reg51.h> void job() _task_ 0
{ os_create_task (1); os_create_task (2); os_create_task (3); while(1) { P1=~P1; os_wait(K_TMO,50,0); } }
void num1() _task_ 1 { while(1) { P0=~P0; os_wait(K_TMO,100,0); } }
void num2 () _task_ 2 { while (1) { P2=~P2; os_wait1(K_SIG); } }
void num4 () _task_ 3 { while (1) { P3=~P3; } } 以上便是一個完整的程序。首先,這個程序有一個特點,就是沒有main函數了。 下面,就對這個程序作出分析并對rtx51加深學習和理解。 void job() _task_ 0 {}, 這個,便是一個任務,所謂任務,就是披著嫁衣的函數。void,這個,名義上可有可無,但,有為好,一般不需要做什么返回。job()這個,就是函數名了。這個job可以隨便一個名字,自己可以隨便起。 然后_task_是一個關鍵字,必須要有,表示你建的這個函數就是一個任務。 然后 0呢, 表示這個任務的優先級是0。擁有最高優先級。 因為,使用rtx51這個,并沒有main函數,所以,程序是從任務0開始的,然后,做任務0開始執行的后,程序該干嘛就干嘛了。那現在看一下任務0干了嘛。 os_create_task (1); ,這個,就是任務0做的事。就是創建了任務1。因為 void num1() _task_ 1 {}只是定義了任務1的函數,或者只是定義了任務1該干什么的。但,程序沒有調用到它,它就還不能正常工作。所以os_create_task (1);的工作就是調用了任務1,讓任務1可以正常工作。然后,把任務1創建后,就和任務0無關了。同理,也可以os_delete_task(1);來刪除任務1,這樣,刪除了任務1后,任務1里的內容就不再工作了。 rtx51tiny這個可以定義16個任務。16個任務,對于用51實現的系統,基本就可以了。有一些初學者有點困惑的是,以為程序只是定義16個函數,這里只是說最大支持16個任務,而你要定義各種函數呢,定義多少個都可以的。 然后,現在就是每個任務的作用,聯系及區別了。 以上程序定義了0,1,2,3共四個任務。 任務0所做的是:
while(1) { P1=~P1; os_wait(K_TMO,50,0); } 就是在一定的時間間隔里,對P1的值取反。os_wait(K_TMO,50,0);這個函數就是等待時間溢出,具體參考os_wait()這個函數。K_TMO表示是對時間溢出的方式做出等待,K_SIG,這個表示對信號作出等待。如果用到了K_SIG,就要用到os_send_signal
這個函數,表示對某個任務發送信號。然后,那個函數接收到了另一個任務接收到的信號,就跳出等待,作出下一步的指令。這個的50呢,表示的是表示50個時
間間隔。就像剛才在廚房里的時間間隔為10分鐘,那這里就等了50個分鐘。在rtx51默認的時間間隔是0.01s,也就是10ms,100Hz.,那
50個時間間隔,就是間隔了0.5s,那任務0的功能就是每隔0.5來對P1的狀態取反。
同理,分析任務1就不難了。 也有一些人疑惑了,每個任務里都有一個while()循環,程序都進入了死循環,怎么再執行其它的指令呢。 所
以,在這里需要接受的概念就是,每一個任務被建立之后,就不再管其它任務了,就自己在做自己的事了。每個程序就相當于一個main函數一樣了。或者這么
說,一個12Mhz的晶振,你定義了12個任務,然后,這個CPU就被分成12個CPU,每一個CPU的時鐘頻率為1Mhz,然后,每個CPU就在做自己
的事,和其它CPU無關,只是兩個CPU之間是可以通信管理了。這樣的解釋雖然不恰當,不過,還是很形象地讓不少同學接受了這個概念。
以上便是一個簡單的多任務管理了。它有什么優點,這個, 就看你程序的用途了。舉個簡單的說法吧,比如你要用51單片機實現鍵盤的掃描,又要實現數碼管
的動態掃描顯示,還要實現通信,管理,控制等信息。其中一點,要做到鍵盤的掃描,就必須讓程序至少在每10ms內或者更嚴格的時間里,對鍵盤作出掃描,那
么,這個掃描程序如果用中斷來實現的話,還是可以接受的,但如果不是用中斷,而是在非中斷程序里實現的話,但,你的程序還是要做其它事的,而且程序在做其
它的事的情況下,還要照顧的鍵盤的掃描。還有數碼管的動態掃描,如果用定時中斷來說才能保證程序不因其它指令的執行而影響了數碼管顯示的延遲或不穩定。但
要保證這些都要照顧到,程序寫起來就畢竟麻煩了。但如果采用了這種多任務的方式的話,就免去了這個麻煩了。比如,鍵盤掃描就定義成一個任務,這樣,這個任
務的工作就是鍵盤的掃描,其它事也不做,這樣,就不受其它程序段的影響,而且這個任務也可以方便的移植到相同的系統中去。數碼管的顯示也定義一個任務來實
現。 比如你還想添加一個超聲波的顯示上去,那么,你只要再定義多一個任務用來作超聲波測距的就可以了。對原有程序幾乎不需要作修改,而且對原來的程序結構也不改變。
以上的程序只是一個簡單的例子,而且很多人搜索的時候,也一般很容易搜索到這個類似的簡單的例子。 下面獻上自己編寫的,根據超聲波測距控制小車的前進后退的程序: #include <rtx51tny.h> #include <reg51.h>
sbit left0=P1^0; sbit left1=P1^1; sbit right0=P1^2; sbit right1=P1^3; sbit echo=P3^3; sbit trig=P3^5; typedef unsigned char uchar; typedef unsigned int uint; long numecho=0; uint num; job0 () _task_ 0
{ os_create_task (1); os_create_task (2); os_create_task (3); os_create_task (4); os_create_task (5); TMOD=0x91; while(1) { } }
void back () _task_ 2 { while(1) { os_wait1(K_SIG); left0=1; left1=0; right0=0; right1=1; } } void go () _task_ 1 { while (1)
{ os_wait1(K_SIG);
left0=0; left1=1; right0=1; right1=0; }
}
void Echo_test () _task_ 3 { while (1)
{ trig=0; trig=1; TL1=0; TH1=0; os_wait(K_TMO,1,0); trig=0; TR1=1; os_wait(K_TMO,2,0); numecho=TH1*256+TL1; num=numecho*346/1000/2; }
} void panduan(void) _task_ 4 { while(1) { if(num>500) { os_send_signal(1); } if(num<500) { os_send_signal(2); } if(num<200) { os_send_signal(5); } } }
void stop() _task_ 5 { while (1) { os_wait1(K_SIG); left0=1; left1=1; right0=1; right1=1; } }
在這個程序里,任務3就是作的是超聲波的測距,這個任務是不停在做,也就是超聲波在不斷的測距,把測到的距離存到num這個全局變量里,然后任務4就是根據距離作出判斷,根據不作的距離,讓小車前進,后退或停止。
最后,對這個系統再作一些解釋。這個系統呢,肯定是用到了51單片機的資源的。首先,它需要做cpu作時間分割,那么就要用到定時器。這里就用了定時器0
來作定時用的。采用了工作方式1,并采用中斷方式。所以,在使用的時候,一定要注意,不能改變了TMOD里對定時器0的設置,也就是低四位的數值。也就是
說TMOD的值為0x01的,如果需要定時器1的時候,需要注意的。還有中斷,已經開戶了全局中斷和定時器0的中斷,所以,要用到中斷的時候,一定要注
意,不要在設置中斷的時候,把IE的值對應的中斷值改變了,這是注意一點的。
而
且,學會了51的簡單的多任務實時系統的開發,那么,再去學STM32的嵌入式操作系統就不難了。只不過,嵌入式操作系統要操作和管理調用的庫函數比較
多,需要修改的參數也比較多,而且,也要對內存管理,時鐘,系統等作進一步了解。這樣,從51向嵌入式的轉型就更容易了。 寥寥數語,不能把RTX51解釋清楚,更多的功能,還得靠讀者自己去發揮,出現的問題 也待大家去發現并解決了。
|