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

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 4653|回復(fù): 3
打印 上一主題 下一主題
收起左側(cè)

分時(shí)操作系統(tǒng)的設(shè)計(jì)-51單片機(jī)實(shí)踐

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:160871 發(fā)表于 2017-1-9 02:59 | 只看該作者 回帖獎勵 |倒序?yàn)g覽 |閱讀模式
轉(zhuǎn)自:http://www.zg4o1577.cn/bbs/dpj-59966-1.html
作者:  朱有田


本文僅討論筆者在實(shí)踐分時(shí)系統(tǒng)過程中的方法和思想,不拘泥于編程語言工具及其技巧,不用程序語言描述,全文采用敘事的方法,引出工程實(shí)踐過程中解決一些問題的過程和思路。
第一部分:  單CPU計(jì)算機(jī)運(yùn)行的模型
計(jì)算機(jī)最基本模型是圖靈模型(相關(guān)知識自行腦補(bǔ)),計(jì)算機(jī)將所有要處理的復(fù)雜任務(wù)分解到有限的基本的操作(指令),這個操作的集合就是指令集,指令集被設(shè)計(jì)固化到硬件(CPU或處理器)中。程序是為了解決特定問題而編制的一個指令序列,計(jì)算機(jī)的運(yùn)行就是一個在時(shí)間上串行的一個指令流。
如果有2個程序需要在一臺計(jì)算機(jī)上運(yùn)行,常見的場景是先運(yùn)行其中一個程序,運(yùn)行結(jié)束后,再運(yùn)行第二個程序。
第二部分:  單CPU計(jì)算機(jī)中多個程序并發(fā)執(zhí)行
多個程序并行的概念出現(xiàn)的很早,早期為了共享昂貴的計(jì)算機(jī)資源,人們試圖使一個計(jì)算機(jī)為多個用戶同時(shí)提供服務(wù)。多個程序并發(fā)執(zhí)行,采取分時(shí)的方法來實(shí)現(xiàn),稱分時(shí)系統(tǒng)。
分時(shí)即將時(shí)間視為資源進(jìn)行分配,將時(shí)間切分為人們感知上較小的一個單位,比如20毫秒(0.02秒),稱為一個時(shí)間片。若程序1和程序2都要被執(zhí)行,每個程序輪流被執(zhí)行一個時(shí)間片。從宏觀(感官)上來看,程序1和程序2是同時(shí)一起執(zhí)行的,就像兩臺計(jì)算機(jī)在同步工作一樣。
若程序1和程序2同步運(yùn)行,計(jì)算機(jī)是這樣進(jìn)行的:先運(yùn)行程序1,20毫秒后,切換到程序2,20毫秒后又切換到程序1,20毫秒后再次切換到程序2,如此反復(fù)… 一秒鐘切換了50次,程序1和程序2都在運(yùn)行、暫停、運(yùn)行、暫停這樣的狀態(tài)中進(jìn)行,由于切換時(shí)間夠快,人的感官認(rèn)為程序1和程序2是同步運(yùn)行的。
第三部分:  中斷和定時(shí)器的作用
要實(shí)現(xiàn)分時(shí)的機(jī)制,離不開定時(shí)器和中斷機(jī)制。定時(shí)器就是定時(shí)發(fā)出中斷信號讓計(jì)算機(jī)能夠進(jìn)入切換程序;中斷機(jī)制是指計(jì)算機(jī)的硬件要能夠支持在執(zhí)行過程中被中斷,跳轉(zhuǎn)到指定的中斷程序中去運(yùn)行,并在運(yùn)行結(jié)束后能夠返回到被中斷的點(diǎn)繼續(xù)運(yùn)行原來的程序。如果計(jì)算機(jī)硬件沒有中斷機(jī)制,則無法實(shí)現(xiàn)分時(shí)。
對于信號的輸入處理,程序總是以順序的方式進(jìn)行的。比如按下一個按鍵,程序并不是立即就知道有按鍵被按下,而是要等到程序指針運(yùn)行到按鍵處理程序時(shí)才會被發(fā)現(xiàn)。按鍵響應(yīng)的速度取決于程序指針本身的運(yùn)行速度和按鍵處理程序的長短及間隔距離,前者依賴于硬件主頻,后者依賴于程序設(shè)計(jì)的水平。這種依賴程序主動去發(fā)現(xiàn)的信號輸入方式一般稱查詢方式,與查詢方式相對應(yīng)的是中斷方式。
中斷方式是設(shè)計(jì)在硬件級別的,假如按下一個按鍵采用中斷的方式處理,那么當(dāng)按鍵按下時(shí)觸發(fā)中斷,系統(tǒng)執(zhí)行完當(dāng)前指令后保存當(dāng)前的程序指針位置(返回時(shí)候用),然后將程序指針跳轉(zhuǎn)到指定的點(diǎn),程序設(shè)計(jì)時(shí)在這個點(diǎn)編寫好按鍵處理程序,程序結(jié)束后返回到被中斷的點(diǎn)繼續(xù)運(yùn)行。很顯然,中斷方式的響應(yīng)時(shí)間很小且容易估算,程序也不需要“定時(shí)的去查詢”。中斷機(jī)制依賴于硬件提供的中斷資源(可接受的中斷源數(shù)量),往往比較有限;其次當(dāng)多個中斷信號發(fā)生時(shí),要考慮優(yōu)先次序和中斷嵌套(中斷過程中再響應(yīng)中斷)的問題。
事實(shí)上,中斷的實(shí)現(xiàn)也是一種查詢方式,當(dāng)中斷信號發(fā)生時(shí),計(jì)算機(jī)并不是真正的立即響應(yīng),而是要等待當(dāng)前的指令被執(zhí)行完,硬件才會去查詢是否有中斷信號存在。中斷也可以理解為在微指令級別(一個指令由一段微指令組成)下進(jìn)行查詢處理的,其顆粒度是一條指令。
定時(shí)器是一種二進(jìn)制計(jì)數(shù)器,其硬件上原理非常簡單:用一些邊沿觸發(fā)器串接起來就能構(gòu)成一個二進(jìn)制計(jì)數(shù)器,只要輸入一個方波,就能使計(jì)數(shù)器的數(shù)值加一。當(dāng)計(jì)數(shù)器加到溢出時(shí),將溢出信號引出到中斷信號,就能起到定時(shí)的作用。我們通過給定時(shí)器設(shè)置一個初始值來設(shè)置定時(shí)器時(shí)間的長短。
比如在程序中要等待一秒鐘的延時(shí),如不用定時(shí)器,則必須讓程序指針在一個循環(huán)體中空轉(zhuǎn)N次,達(dá)到延時(shí)的作用。這種方式有兩個弊端:一是程序空轉(zhuǎn)浪費(fèi)計(jì)算機(jī)資源,在這個延時(shí)過程中,程序啥也干不了,無法響應(yīng)按鍵,無法刷新顯示,如同死機(jī)一樣;二是延時(shí)精度難控制,由于程序指令本身執(zhí)行時(shí)間有長短之分,要達(dá)到精確的延時(shí),程序設(shè)計(jì)要反復(fù)調(diào)教循環(huán)體空轉(zhuǎn)的次數(shù)。
如采用定時(shí)器進(jìn)行一秒延時(shí),只要在定時(shí)器內(nèi)設(shè)置好初始值(使初始值到溢出的時(shí)間剛好為1秒),開啟定時(shí)器后,程序可以繼續(xù)執(zhí)行其他工作,如按鍵掃描等。當(dāng)1秒時(shí)間到達(dá),定時(shí)器溢出發(fā)出中斷信號,使程序指針跳轉(zhuǎn)到中斷響應(yīng)程序,程序在此進(jìn)行延時(shí)后的處理。由此可以發(fā)現(xiàn),定時(shí)器相當(dāng)于硬件級別并行的設(shè)備,程序指針的跑動和定時(shí)器內(nèi)計(jì)數(shù)值的增加是并行的。
言歸正傳,定時(shí)器和中斷是實(shí)現(xiàn)分時(shí)系統(tǒng)的基礎(chǔ),其中中斷機(jī)制尤為重要,70年代流行的PDP系列小型機(jī)上,分時(shí)系統(tǒng)的定時(shí)中斷是由一個從工頻電源上獲取的50-60Hz的定時(shí)中斷外設(shè)產(chǎn)生,稱之為電源時(shí)鐘,令人腦洞大開。現(xiàn)代操作系統(tǒng)的祖宗UNIX就是在這種機(jī)型上產(chǎn)生的一種分時(shí)操作系統(tǒng),UNIX最初的用戶是貝爾實(shí)驗(yàn)室的文員們,他們用它來處理專利文書,由于多個終端上可以同步進(jìn)行操作,又能方便進(jìn)行共享和調(diào)用,得到當(dāng)時(shí)女職員們的好評。
摘要:UNIX是用于DEC PDP-11及Interdata 8/32計(jì)算機(jī)的一個通用的交互作用的分時(shí)操作系統(tǒng)。從1971年開始以來,使用日趨廣泛。UNIX所論及的內(nèi)容有以下領(lǐng)域: 1.文件結(jié)構(gòu):一個統(tǒng)一的、可隨機(jī)尋址的字節(jié)序列。取消“記錄”的概念。文件尋址的效率。2.文件系統(tǒng)設(shè)備的結(jié)構(gòu):目錄與文件。3.I/O設(shè)備與文件系統(tǒng)的一體化。4.用戶接口:外殼程序原理,I/O重新定向以及管道。5.進(jìn)程環(huán)境:系統(tǒng)調(diào)用、信號、以及地址空間。6.可靠性:癱瘓,文件的丟失。7.安全性:損壞與檢查時(shí)數(shù)據(jù)保護(hù);阻塞情況下的系統(tǒng)保護(hù)。8.一個高級語言的使用—收益與代價(jià)。9.UNIX系統(tǒng)沒有一般意義上的“實(shí)時(shí)”、進(jìn)程間通訊、異步的I/O等功能。
UNIX內(nèi)核由10,000行左右的C語言代碼和1,000行左右的匯編語言代碼所組成。匯編語言代碼又可進(jìn)一步分成兩部份:一部份為200行,包括為提高系統(tǒng)效率而設(shè)計(jì)的那些代碼(可以用C語言來寫);另一部份為800行,包括不能用C語言寫的、執(zhí)行硬件功能的那些代碼。
第四部分:  分時(shí)的意義
從程序設(shè)計(jì)角度,程序是一個單線順序的指令流(語句流),在程序里,指令(語句)一個接一個被執(zhí)行,從開始到結(jié)束,或條件轉(zhuǎn)移,或反復(fù)循環(huán)。程序員安排好這一切前后處理的次序。在應(yīng)用角度,用戶打開程序,運(yùn)行程序,直到結(jié)束退出,再打開另一個程序。在DOS時(shí)代,人們就是這么做的,那真是一個單純的時(shí)代。
在分時(shí)系統(tǒng)下,多個程序并行,程序員設(shè)計(jì)程序時(shí)可以設(shè)計(jì)多個線程,比如一個線程負(fù)責(zé)顯示,一個線程負(fù)責(zé)鍵盤輸入,一個線程負(fù)責(zé)數(shù)據(jù)處理。每一個線程都是單獨(dú)的程序,最終他們將同步運(yùn)行,程序員除了要考慮單個線程的運(yùn)行,還要考慮線程之間的同步、通信和互斥。這改變了傳統(tǒng)的程序設(shè)計(jì)思維,使軟件的表現(xiàn)和可能性更加豐富多彩,程序設(shè)計(jì)手段也更加多樣。
在應(yīng)用角度,當(dāng)你在編輯文檔時(shí),同時(shí)又在下載電影,耳機(jī)里又能播放著音樂。這些當(dāng)下看起來稀松平常的事情,在計(jì)算機(jī)還是單任務(wù)時(shí)代真是難以想象的。


第五部分:  在80c52單片機(jī)上實(shí)踐分時(shí)系統(tǒng)
分時(shí)系統(tǒng)并不高不可攀,了解以上的概念后,任何有經(jīng)驗(yàn)的程序員都能對它進(jìn)行實(shí)踐,筆者就是在最便宜和古老的51單片機(jī)上實(shí)踐了分時(shí)系統(tǒng)。
80c52是intel的一款增強(qiáng)型mcs-51單片機(jī),51系列8位單片機(jī)自80年代開始至今仍被廣泛使用在各種場景,這種芯片很便宜,也很容易就能搞到。后來Intel將mcs-51授權(quán)給了多個芯片制造商。以ATMEL生產(chǎn)的AT89S52為例,它支持總計(jì)111條的51指令集,256字節(jié)RAM,8k字節(jié)程序閃存(可反復(fù)刷寫),3個16位計(jì)時(shí)器,5個中斷源,1個串口,4個8位IO口。要使她運(yùn)行起來非常的方便:只需在XTAL管腳之間加一個晶振和2個27pF電容(晶振的頻率決定運(yùn)行速度),在VCC腳加5V電源,然后把RESET管腳對地短路一下(復(fù)位),計(jì)算機(jī)便開始從程序閃存的0地址開始取指令執(zhí)行..
沒玩過51單片機(jī)的讀者一定好奇程序是怎么編制到芯片的閃存里面去的。單片機(jī)程序的開發(fā)主要依賴個人電腦,首先通過文本編輯器編寫源程序,然后通過51的編譯器程序編譯成目標(biāo)文件,最后通過芯片燒錄器將目標(biāo)文件復(fù)制(燒入)到51芯片內(nèi)。然后你的程序就可以在單片機(jī)跑動了。
事實(shí)上,無論是偉福,還是uvsion,都提供了包括編輯器、編譯器、仿真和調(diào)試為一體的開發(fā)環(huán)境,程序在編制完成后,直接在開發(fā)環(huán)境里面仿真調(diào)試,經(jīng)過排錯后,將問題降到最低再燒入到單片機(jī)運(yùn)行,這比反復(fù)燒寫芯片驗(yàn)證程序效率要高的多。
最要緊的事情是:AT89S52具備實(shí)現(xiàn)分時(shí)系統(tǒng)的必要條件:1支持中斷,2具備計(jì)時(shí)器,3勉強(qiáng)夠用的RAM空間。
第六部分:  時(shí)間片的分配
實(shí)現(xiàn)分時(shí)系統(tǒng)的第一要考慮怎么進(jìn)行時(shí)間片的分配,也就是程序之間的切換問題。這個很簡單,可利用一個定時(shí)器產(chǎn)生一個固定的延時(shí),當(dāng)延時(shí)到達(dá),進(jìn)入定時(shí)器中斷,將這個中斷的入口跳轉(zhuǎn)到切換程序。注意:程序的切換不是簡單的跳轉(zhuǎn),而是要先保存當(dāng)前程序的執(zhí)行狀態(tài)、被中斷的地址,不至于下次回來繼續(xù)當(dāng)前程序時(shí)變量、狀態(tài)都被改變,那就會亂套了。然后再選擇下一個要運(yùn)行的程序,將被選中程序的狀態(tài)恢復(fù)到當(dāng)前的各狀態(tài)寄存器當(dāng)中,恢復(fù)到這個程序上次被打斷的點(diǎn)繼續(xù)運(yùn)行,等待下一個時(shí)間片用完,進(jìn)中斷,重復(fù)以上過程。
既然每個程序都要保存和恢復(fù)它的運(yùn)行狀態(tài),那么問題來了。一是要保存哪些信息,稱之為保護(hù)的范圍;二是需要多少內(nèi)存,能夠支撐多少個程序進(jìn)行分時(shí)。
第一個問題,保護(hù)范圍取決于用戶程序的斷點(diǎn)地址、狀態(tài)字、寄存器組,以及堆棧空間。第二個問題,支撐多少個程序進(jìn)行分時(shí)取決于內(nèi)存的大小,89C52的256字節(jié)支撐8個線程后,留給應(yīng)用程序的內(nèi)存只有36個字節(jié),因此80c51的128個字節(jié)RAM是不夠的。
在此,為了區(qū)別于系統(tǒng)程序,改稱以上所謂被分時(shí)的程序?yàn)槿蝿?wù)或線程。

第七部分:  內(nèi)存組織的方法
我們能設(shè)想到最原始的方法是通過數(shù)組(數(shù)據(jù)表)的方式,將每一個線程的保護(hù)內(nèi)容固定的保存起來。這種方法的缺點(diǎn)是不靈活,要增補(bǔ)被保護(hù)的內(nèi)容就要重新定義數(shù)組,改動操作數(shù)據(jù)的代碼。當(dāng)然還有更值得推薦的方法:用堆棧來組織內(nèi)存。
堆棧是一種數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)如被壓入沖鋒步槍AK47彈匣的子彈,最先被壓進(jìn)去的子彈,最后才被彈出到槍膛。在計(jì)算機(jī)內(nèi)一般設(shè)有一個SP寄存器,存放堆棧指針,如果執(zhí)行PUSH指令,SP先向前推一格,數(shù)據(jù)被寫入SP所指向的內(nèi)存;如果執(zhí)行POP指令,數(shù)據(jù)從SP所指向的內(nèi)存讀出,SP再向后退一格。這種先入后出的結(jié)構(gòu)能夠很好的支撐子程序嵌套調(diào)用時(shí)的數(shù)據(jù)存儲。舉一個栗子:
主程序運(yùn)行時(shí)調(diào)用子程序A,當(dāng)前的程序指針PC被壓入堆棧(必須留下跳轉(zhuǎn)點(diǎn)的腳印,否則子程序完成后返回不到原來被跳過去的點(diǎn)),接著PC跳轉(zhuǎn)到子程序A中開始運(yùn)行,在子程序A里面又有調(diào)用子程序B,此刻的程序指針PC也被壓入堆棧,接著跳到子程序B中,子程序B完成后返回,將堆棧中最近保存的PC彈出到PC寄存器,程序返回到子程序A,子程序A完成后返回,從堆棧彈出最早壓入的PC,程序返回到主程序。
系統(tǒng)在線程之間切換時(shí),將當(dāng)前線程的所有需要保護(hù)的內(nèi)容全部壓入堆棧,最后僅需要保存好它的堆棧指針(就像把東西全扔箱子里,只需保管好鑰匙)。將下一個線程的堆棧指針取出來,從堆棧彈出所有要恢復(fù)的數(shù)據(jù),然后返回到新線程運(yùn)行下一個時(shí)間片。
第八部分:  關(guān)于線程調(diào)度
當(dāng)一個時(shí)間片用完,定時(shí)器發(fā)生中斷后,程序指針跳轉(zhuǎn)到線程切換程序,首先是保護(hù)現(xiàn)場。然后是選擇下一個線程。那么問題又來了,我們打算怎么來選擇下一個線程?這就是所謂的線程調(diào)度,操作系統(tǒng)的教課書里叫進(jìn)程調(diào)度。
最原始粗暴的方法就是順序循環(huán)調(diào)度,所有線程先來后到排個隊(duì),挨個輪著,實(shí)行平均主義的制度。在一些實(shí)時(shí)系統(tǒng)里面,比如μCOS,是按優(yōu)先級來確定調(diào)度權(quán)的:永遠(yuǎn)只選擇優(yōu)先級最高的線程,也就是說,如果線程一直處于最高優(yōu)先級,那么它就一直占著CPU不放,就不會被搶占。如果要讓線程立即得到執(zhí)行,就必須設(shè)置最高優(yōu)先級。這種方法能夠提供手段讓用戶設(shè)計(jì)的任務(wù)滿足實(shí)時(shí)的要求(所謂實(shí)時(shí),就是對響應(yīng)時(shí)間可評估,可控制,不是字面理解上的實(shí)時(shí)。)
除了優(yōu)先級方式,還可以設(shè)定一種搶占的機(jī)制,比如在順序調(diào)度的過程中來一個半道插隊(duì)方式的搶占,也是一種可以滿足實(shí)時(shí)要求的方法。半路殺出個陳咬金,需要提前設(shè)置一個信號通知給調(diào)度程序,表示當(dāng)前有某個線程需要插隊(duì)搶占。這里要考慮到原有的順序調(diào)度不能被打亂,或者被搶占的線程正好又是被輪到的線程,那么下次調(diào)度應(yīng)繼續(xù)往后調(diào)度。
調(diào)度算法有很多,應(yīng)按工程實(shí)際需求來設(shè)計(jì),讀者不妨也可以設(shè)想更有創(chuàng)新意義的方法。


第九部分:  系統(tǒng)效率和延遲
分時(shí)總是要付出一些代價(jià)的,假如一顆CPU全力運(yùn)行一個單程序,那么CPU利用率是100%。如果這顆CPU要分時(shí)運(yùn)行兩個程序,那么每個程序?qū)PU的利用率都不足50%,因?yàn)榍袚Q要耗費(fèi)CPU時(shí)間,切換次數(shù)越多越耗費(fèi),“切換可是要上稅的”。切換次數(shù)和時(shí)間片的設(shè)置相關(guān),一般來說時(shí)間片設(shè)置范圍在1ms到20ms之間,時(shí)間片太小,浪費(fèi)了大量CPU時(shí)間在上下文切換上,時(shí)間片太大,線程被輪到的時(shí)間就長,響應(yīng)變差。一個線程的運(yùn)行延遲在分時(shí)系統(tǒng)里很難被準(zhǔn)確的估算,它與已就緒的線程數(shù)量、CPU主頻、時(shí)間片長短、中斷響應(yīng)等各種因素相關(guān),這些因素動態(tài)影響著單個線程的運(yùn)行延遲。
分時(shí)系統(tǒng)中單個線程性能的一種簡單的估算方法:假如一個CPU工作在30MHz的主頻下,有3個已就緒的線程被平均調(diào)度,時(shí)間片相等,那么單個線程的運(yùn)行性能相當(dāng)于它獨(dú)占了一個10MHz不到的CPU的效果,相當(dāng)于這個CPU被拆分成了3個10MHz不到的CPU在同步運(yùn)行。由于現(xiàn)場就緒與否是動態(tài)變化的,因此很難完全準(zhǔn)確估算。
總而言之,分時(shí)盡管耗費(fèi)了一些CPU時(shí)間,但相對它所帶來驚奇效果和并行思維給程序設(shè)計(jì)帶來更多的可能性而言,微不足道。分時(shí)就像變魔術(shù)一樣,能把一顆CPU拆分成N個弱小的CPU同步運(yùn)行不同的程序。從宏觀上,分時(shí)能夠充分挖掘CPU資源,當(dāng)一個線程需要等待或有目的的延時(shí),完全可以把CPU時(shí)間讓給其他線程,而不是白白的空等。

第十部分:  線程的休眠和就緒
當(dāng)一個線程需要等待或延時(shí),或者索性暫停掉,可以通過狀態(tài)標(biāo)記,使調(diào)度程序跳過對它的調(diào)度,那么這個線程就是被休眠了,或稱該線程處于休眠態(tài)。反過來,如果線程等著被調(diào)度,稱該線程處于就緒態(tài)。這個功能在調(diào)度程序內(nèi)做一個判斷不難實(shí)現(xiàn)。
如果要使一個線程休眠,系統(tǒng)可以提供一個函數(shù)(子程序)來修改線程的休眠標(biāo)志(變量)。任何線程,包括線程自己都可以進(jìn)行休眠操作。所謂想睡就睡,但不是想醒就醒,喚醒一定是其他線程或時(shí)鐘服務(wù)來幫助喚醒你。
除了休眠,還可以設(shè)置一個殺死的操作,所謂殺死線程,就是讓線程的狀態(tài)信息恢復(fù)初始化,下次再喚醒該線程,相當(dāng)于要從頭開始執(zhí)行。殺死線程,或線程自殺,可以理解為線程的復(fù)位并休眠。
可以設(shè)想一種簡單的設(shè)計(jì)場景:用線程1來處理用戶輸入,線程2、3、4為不同功能的獨(dú)立任務(wù),線程1可通過用戶輸入選擇喚醒或休眠線程2、3、4來調(diào)取不同的功能。對于任務(wù)的開發(fā)來說,確實(shí)提供了與往常不同的手段,用的好,絕對順手。
第十一部分:    線程之間通信和互斥
感官上,線程是獨(dú)立運(yùn)行的,微觀上,線程跑著跑著不知道什么時(shí)候就咔的被喊停,CPU被強(qiáng)行切換到別的線程去運(yùn)行。也就是說,線程的運(yùn)行是隨時(shí)隨刻隨地都可能被中斷的。
線程和線程之間要通信,一般通過全局變量、全局的數(shù)據(jù)結(jié)構(gòu)(內(nèi)存的一塊空間)來共享信息。比如線程A往全局變量內(nèi)寫信息,線程B從該變量讀信息。以達(dá)到讓線程A的信息傳達(dá)給線程B的目的。那么問題又來了,如果線程A對全局變量寫了一半,咔,時(shí)間片到了,輪到線程B運(yùn)行,這時(shí)候線程B從該變量讀到的信息就是一個錯誤的信息。
先來分析這個問題:
中斷是在指令和指令之間響應(yīng)的,也就是其最小顆粒度是單條指令,如果寫變量是一條指令搞定的,由于指令在執(zhí)行過程中不會被中斷,所以不會發(fā)生以上情況。單條指令的操作也稱為原子操作。
可惜的是,一條指令至多可寫一個字。對于沒有接觸過匯編的程序員來說,一條高級語言的語句很容易被認(rèn)為是一個原子操作,其實(shí)不然,經(jīng)過編譯后,一條高級語言的語句往往都由若干條指令組成。
思考解決問題:
要解決寫變量時(shí)候不被破壞的問題,就要排斥其他線程對這塊變量的操作,或防止在寫的過程中被中斷。方法有幾種:
最簡單粗暴的方法是開始寫共享變量之前直接把中斷給關(guān)了,寫完以后再開。還有更流氓的做法,就是在寫共享變量之前直接讓計(jì)時(shí)器停擺,定住,結(jié)束后再繼續(xù)往前計(jì)數(shù)。方法雖然簡單,但是這么做會讓整個系統(tǒng)短暫停滯,明顯有副作用。
推薦的方法是讓調(diào)度程序帶上鎖功能,如果處于上鎖狀態(tài),調(diào)度程序不執(zhí)行切換。線程在寫共享變量之前先加鎖,結(jié)束后再解鎖。在加鎖期間,雖然中斷和計(jì)時(shí)都不受影響,但是其他所有線程都被禁止了,還是有些缺憾。
更好的辦法是采用信號量,相當(dāng)于給共享變量加一個紅綠燈。設(shè)置紅綠燈的操作必須是原子操作,線程在操作共享變量時(shí)先查一下是不是綠燈,如果是綠燈就把它變?yōu)榧t燈,然后操作變量,結(jié)束后再設(shè)為綠燈;如果是紅燈就排隊(duì)等待,直到綠燈來了再進(jìn)行操作。
名詞摘要:在教材中,把對需要互斥的共享變量和設(shè)備進(jìn)行獨(dú)占操作,叫臨界區(qū)操作,結(jié)束后要退出臨界區(qū)。
第十二部分:    掛鐘和鬧鈴叫醒服務(wù)
線程在運(yùn)行過程中經(jīng)常會用到等待相對精確的延時(shí)、以及超時(shí)判斷等和時(shí)間相關(guān)的操作。傳統(tǒng)的單線程程序,經(jīng)常用最粗暴的空操作指令循環(huán)來獲得一定的延時(shí),這無可厚非,單線程中一段程序的運(yùn)行時(shí)間能夠被準(zhǔn)確的估算出來。多線程環(huán)境就不同了,一段程序的運(yùn)行時(shí)間長短是不可準(zhǔn)確估算的,取決于就緒線程的數(shù)量和時(shí)間片的長短,這些都是變化的。
需求場景1:線程需要就地等待1分鐘。
需求場景2:線程進(jìn)入一個循環(huán)等待某個信號,當(dāng)時(shí)間超過5秒鐘判斷為超時(shí),退出循環(huán)。
以下是一種設(shè)計(jì)方案:
設(shè)計(jì)一個系統(tǒng)掛鐘,讓他一直以單位時(shí)間往前走。好比在墻上掛一個鐘頭,讓所有線程都能看時(shí)間。為每個線程提供一個鬧鈴指針和喚醒服務(wù)標(biāo)志,如果某個線程需要喚醒服務(wù),就把鬧鈴指針撥到目標(biāo)時(shí)間,然后開啟喚醒服務(wù)。系統(tǒng)掛鐘每走一個刻度都要判斷當(dāng)前時(shí)間和需要喚醒服務(wù)線程的鬧鈴時(shí)間是否一致,一致的話就喚醒這個線程。
當(dāng)某個線程需要就地等待1分鐘就可以這樣做:線程先看一下當(dāng)前的時(shí)間,然后把時(shí)間往前推1分鐘設(shè)置一個鬧鈴,然后告訴系統(tǒng)時(shí)鐘:鬧鈴到了記得叫醒我!接著線程就自我休眠了,等著1分鐘后系統(tǒng)時(shí)鐘的喚醒并繼續(xù)運(yùn)行。
實(shí)踐的栗子:啟用硬件另一個計(jì)時(shí)器,每10ms發(fā)生一次中斷。系統(tǒng)中設(shè)置一個16位的字,每過10ms將這個字加一,這個16位字相當(dāng)于一個量程為65536的掛鐘。設(shè)置一個數(shù)組用于存儲每個線程的鬧鈴時(shí)間,設(shè)置一個狀態(tài)字,用于標(biāo)志線程的喚醒服務(wù)。當(dāng)時(shí)鐘往前走一個刻度時(shí),同時(shí)進(jìn)行一個喚醒的判斷,將當(dāng)前時(shí)間和鬧鈴時(shí)間一致的線程喚醒。
時(shí)鐘的最小刻度設(shè)定為10ms,也就意味著延時(shí)設(shè)定的最小單位是10ms,那么問題又來了,若需要延時(shí)1ms或更小的時(shí)間刻度該怎么解決?
如果把時(shí)鐘刻度直接調(diào)至1ms,會導(dǎo)致時(shí)鐘中斷次數(shù)增加了10倍,時(shí)鐘和鬧鈴的程序段的執(zhí)行次數(shù)也就增加了10倍,這嚴(yán)重消耗了CPU時(shí)間,降低了整個系統(tǒng)的性能。如何解決這個問題留給讀者去思考。
第十三部分:    公用的函數(shù)(或子程序)
如果一個函數(shù)僅僅使用堆棧和局部變量,那么這個函數(shù)就能天然的被多個線程同時(shí)調(diào)用。因?yàn)槎褩:途植孔兞浚ɑ駽PU寄存器)都是受保護(hù)的,假使2個線程同時(shí)調(diào)用一個函數(shù),在線程切換的情況下,雖然都是運(yùn)行在相同的函數(shù)代碼段,但各自線程里的局部變量和堆棧內(nèi)容不同,所以運(yùn)行結(jié)果也是各自分開的。這個函數(shù)就像使了分身之術(shù)一樣,在不同的線程里同時(shí)運(yùn)行著。這種函數(shù)也叫作可重入函數(shù)。
如果一個函數(shù)使用了全局變量,那么假使2個線程同時(shí)調(diào)用了它,那么在線程A里面所定義的全局變量和線程B里面所定義的全局變量指向同一個位置,就會發(fā)生問題。這種不能被多個線程同時(shí)調(diào)用的函數(shù),叫不可重入函數(shù)。
第十四部分:    后記
事實(shí)上,分時(shí)系統(tǒng)是現(xiàn)代操作系統(tǒng)的核心技術(shù)內(nèi)容,它并不過時(shí)。本文所描述的內(nèi)容涉及到操作系統(tǒng)的部分原理知識,當(dāng)然描述不夠?qū)I(yè),沒有準(zhǔn)確引用教材中的各種名詞和術(shù)語,不能完整的討論操作系統(tǒng)。但筆者希望本文能夠成為讀者對操作系統(tǒng)知識的一種指引,如果覺得意猶未盡,可以去翻一翻《操作系統(tǒng)》相關(guān)教材,看看專業(yè)的角度這些概念是怎么被描述的。很多程序員總是以為軟件能夠解決一切問題,其實(shí)不然,有些想法或算法不得不依賴硬件的支持。事實(shí)上,操作系統(tǒng)的各種思想也深刻影響了硬件的設(shè)計(jì),CPU設(shè)計(jì)者在設(shè)計(jì)新的CPU時(shí),充分考慮并納入了好多為操作系統(tǒng)考慮的事情。
現(xiàn)代操作系統(tǒng)往往鑒于UNIX這樣的通用操作系統(tǒng)來描述的,其內(nèi)容除了講分時(shí)調(diào)度以外,還包括內(nèi)存管理,外存管理(也就是文件系統(tǒng)),系統(tǒng)調(diào)用,設(shè)備驅(qū)動等內(nèi)容。硬件往往基于個人計(jì)算機(jī)(intel的架構(gòu))來描述,頭緒非常多,讓學(xué)習(xí)者覺得課程就像是空中閣樓,可遠(yuǎn)觀而不可觸碰。
用單片機(jī)做實(shí)踐是最能摸到計(jì)算機(jī)本質(zhì)的路徑,試想,在一臺裸機(jī)上讓你的程序最接近硬件的跑動起來,是多么踏實(shí)的趕腳(好吧,牽強(qiáng)了)。通用計(jì)算機(jī)和單片機(jī)原理是一樣的,只不過通用計(jì)算機(jī)規(guī)模更大,主頻更高,為了達(dá)到更大的規(guī)模,架構(gòu)起來就要用更多的手段和技巧。
比如在通用計(jì)算機(jī)內(nèi),程序指令并不是固化在ROM或閃存內(nèi)的,而是存放在低速容量更大的外存(硬盤、軟盤、光驅(qū)、U盤)中,需要執(zhí)行程序時(shí),把程序文件成塊的復(fù)制到內(nèi)存中,CPU從內(nèi)存中取指令執(zhí)行。那么問題來了,程序文件必須被裝載在指定內(nèi)存地址段,(跳轉(zhuǎn)指令中包含了具體的地址),如果地址和指令錯位,那么程序跳轉(zhuǎn)將找不到北。要解決這個問題,CPU的設(shè)計(jì)者從早期的內(nèi)存分段管理,發(fā)展到現(xiàn)在的虛擬地址空間轉(zhuǎn)換,可謂費(fèi)盡心思。隨著程序規(guī)模越做越大,當(dāng)單個程序的長度超出內(nèi)存的總?cè)萘繒r(shí),問題又來了。為了解決這個問題,又引入了分頁和虛擬內(nèi)存的方法,分頁允許程序不用一次性全部調(diào)入內(nèi)存,而是將要被執(zhí)行部分的頁面調(diào)入內(nèi)存,剩余的還是存在硬盤里,當(dāng)程序執(zhí)行的指令已經(jīng)不在內(nèi)存時(shí),CPU將會發(fā)生一個缺頁中斷,將硬盤中的目標(biāo)頁調(diào)入內(nèi)存,將不用的頁調(diào)回到硬盤。這些都是內(nèi)存管理單元MMU做的事情,而MMU基本是硬件實(shí)現(xiàn)(做在CPU內(nèi)部),但它需要軟件配合才能用起來。所有的這些機(jī)制都是為了解決過程中碰到的問題而提出的解決方案,要搞清楚源頭。  
討論操作系統(tǒng)離不開信息安全的話題,黑客和病毒總是存在。理論上,用戶程序如果要想搞破壞是非常容易做到的,單靠軟件防止黑客或病毒的攻擊理論上是做不到的,因?yàn)槔硐霠顟B(tài)下所有指令都是公平對等的,你不能用程序指令去阻止用于惡意破壞的程序指令,比如故意改寫數(shù)據(jù)(破壞正確的數(shù)據(jù))。所以,計(jì)算機(jī)安全必須依靠硬件提供的機(jī)制,例如CPU設(shè)計(jì)者將處理器的運(yùn)行分成多個狀態(tài),低權(quán)級狀態(tài)下只能執(zhí)行部分安全的指令,高權(quán)級狀態(tài)下才可以執(zhí)行全部指令,用戶程序只能運(yùn)行在低權(quán)級,需要進(jìn)行讀寫操作時(shí)須通過調(diào)用系統(tǒng)提供的功能調(diào)用來實(shí)現(xiàn),系統(tǒng)是工作在特權(quán)級下的,這樣就為計(jì)算機(jī)安全提供了可控手段。
性能是計(jì)算機(jī)永恒的話題。分時(shí)系統(tǒng)上就緒的線程數(shù)量越多,單個線程的性能就越低(可視為CPU的性能被線程瓜分了,線程越多,單個線程瓜分到的性能就少)。為了提升計(jì)算機(jī)性能,除了設(shè)置流水線、多級緩存等方式外,單個CPU主要靠提升主頻來實(shí)現(xiàn)性能的數(shù)量級提升。除此之外,引入多個CPU并行工作,通過增加系統(tǒng)的并行度也是提高性能的主要手段。多個CPU使得線程從物理上真正的并行了,一個多核的CPU在同一時(shí)刻可以同時(shí)運(yùn)行多個線程,多CPU在硬件組織上又是一件費(fèi)盡心機(jī)的活(不要簡單的理解成多插幾個CPU就完事那么簡單)。超級計(jì)算機(jī)(用于氣象、原子能、航天等領(lǐng)域的測算)往往以集群方式組織起來的高度并行的結(jié)構(gòu),這也要求軟件設(shè)計(jì)必須要被分割為高度的并行化才能發(fā)揮超級計(jì)算機(jī)的性能。如果一個問題的算法必須至始至終前后串行,無法被分割成多個同步運(yùn)行的部分,那么這種程序放在超級計(jì)算機(jī)上運(yùn)行,縱使這臺超算有千萬個CPU,程序也只能在其中一個CPU上跑完,剩下的CPU等于閑著沒事干。同樣的道理,多核CPU體現(xiàn)的也是并行性能,只有在線程數(shù)量多的分時(shí)系統(tǒng)內(nèi)才能體現(xiàn)其性能。如果用一個8核的CPU僅僅只運(yùn)行一個線程,那么剩下7個核心就閑著沒事做。
備注:本文一直采用線程來描述并發(fā)執(zhí)行的程序,其實(shí)線程是專有名詞,有更清晰的概念。操作系統(tǒng)中還有進(jìn)程的概念,進(jìn)程也是并發(fā)的程序,一個進(jìn)程還能再分多個線程。在實(shí)時(shí)操作系統(tǒng)中,線程又被稱為任務(wù)。
寫在最后
通過以上敘述,我想讀者一定對分時(shí)、多線程、并發(fā)執(zhí)行有了概念,同時(shí)也一定留下了一堆的疑問和不解,這可是好現(xiàn)象,進(jìn)步都是在疑問和不解中開始的。



還有2個應(yīng)用例程可以從這里下載:http://www.zg4o1577.cn/bbs/dpj-59966-1.html


 豆豆的打開關(guān)上
需求:假設(shè)有一個指針,可以作圓周運(yùn)動,通過在鍵盤上輸入具體的角度,使指針指向該角度。
輸入:數(shù)字鍵盤,按3位數(shù)字后回車生效。范圍0-999,表示圓周角度,對360度取模。
顯示:3位段式液晶數(shù)碼顯示。
執(zhí)行:減速步進(jìn)電機(jī),按輸入的角度作圓周運(yùn)動。
結(jié)構(gòu):木結(jié)構(gòu)為主,在電機(jī)上安裝臂桿,做成類停車桿的結(jié)構(gòu)。可360度自由轉(zhuǎn)動。

技術(shù)上要考慮的問題:
1、液晶驅(qū)動的問題,擬采用HT1621芯片驅(qū)動;
2、歸零問題,擬采用12V電池+5V模塊的供電方式,設(shè)置斷電繼電器和斷電歸零程序;
3、轉(zhuǎn)動角度問題,單步判斷逼近方法,or距離計(jì)算一次到位方法;擬采用前者;

操作模型:
1、打開開關(guān),液晶顯示---;臂桿角度為0;(開關(guān)處并聯(lián)的繼電器吸合,開關(guān)的觸點(diǎn)留一組去cpu)
2、輸入0-360之間的一個整數(shù)表示圓周的絕對角度,每輸入一個數(shù)字,顯示到液晶屏;其他輸入作無效處理;
3、回車后,液晶屏內(nèi)的數(shù)字被作為目標(biāo)值,執(zhí)行電機(jī)轉(zhuǎn)到該角度;
4、轉(zhuǎn)動過程中,可以按暫停鍵,暫停或繼續(xù);
5、轉(zhuǎn)動過程中,可以繼續(xù)輸入新的角度值,按回車后變更新的目標(biāo)值執(zhí)行。
6、執(zhí)行完成后,電機(jī)去電待命。
7、開關(guān)關(guān)閉,cpu檢測到關(guān)機(jī)指令,執(zhí)行歸零程序,完成后,繼電器斷開。(歸零程序不可被中斷,歸零后須再確認(rèn)關(guān)機(jī)指令,若還是關(guān)機(jī),則繼電器斷開。)


實(shí)施步驟:

1、整體方案構(gòu)思;資料消化;
2、硬件草圖;
3、電機(jī)驅(qū)動試驗(yàn),正反向,速度和角度;
4、液晶顯示試驗(yàn);
5、按鍵掃描模擬和試驗(yàn);
6、軟件構(gòu)思;
7、編碼和模塊測試;
8、最終調(diào)試。

全局變量:當(dāng)前值,目標(biāo)值,暫停標(biāo)志,鍵值,顯示數(shù)字,ram區(qū),8拍數(shù)組;

按鍵掃描獲取鍵值(任務(wù)2)
鍵值改變數(shù)字or啟動執(zhí)行(轉(zhuǎn)換為目標(biāo)值)or暫停標(biāo)志變更,無效值;新的數(shù)字來了,要標(biāo)志,用于判斷回車鍵的作用;數(shù)字 轉(zhuǎn)換 成ram區(qū);ram區(qū)寫入HT1621的ram;(任務(wù)3)
暫停標(biāo)志沒有時(shí)and當(dāng)前值!=目標(biāo)值時(shí)執(zhí)行:目標(biāo)值在左側(cè)還是在右側(cè):前進(jìn)一步或后退一步,延時(shí),返回;(任務(wù)4)
[一個8拍數(shù)組;前進(jìn)一步,當(dāng)前值(0-4096變更);后退一步,當(dāng)前值(0-4096變更);]
不停的查開關(guān)標(biāo)志,若關(guān)機(jī),改變目標(biāo)為0,執(zhí)行,等待執(zhí)行完成,再判斷是否關(guān)機(jī),關(guān)斷電源繼電器。否則返回。(任務(wù)0)

  1. ;kernel: sys51 r0.99
  2. ;project name:    doudou's up & down   
  3. ;designer: ut
  4. ;version: 1.0
  5. ;date: 2016/11/1


  6. ;=============================================================================================
  7. ;                            TIME-SHARING SYSTEM FOR MCS51 RELEASE 0.99
  8. ;                                         UT.ZUZU
  9. ;                               COPYRIGHT(2012/5/10-2016/11/--)
  10. ;=============================================================================================
  11. ;詳細(xì)請查看手冊

  12. ;硬件要求
  13. ;1、52系列兼容的51單片機(jī),內(nèi)存256字節(jié)或以上。本程序在AT89S52運(yùn)行,24.576MHZ晶振,改變晶振需調(diào)整計(jì)數(shù)器值。晶振頻率越高,控制器性能越好。
  14. ;2、256字節(jié)內(nèi)存中,系統(tǒng)使用了大部分高地址部分,0-47由用戶支配,具體請看內(nèi)存分配說明。
  15. ;3、一共8個線程:TASK0到TASK2為3個主線程,其余為次線程;主線程對9個寄存器和PSW、AB、DPTR進(jìn)行保護(hù),并預(yù)留堆棧最大嵌套調(diào)用為7級;次線程僅保護(hù)PSW,AB,R0-R3,最大嵌套調(diào)用為2級。
  16. ;4、系統(tǒng)可以在調(diào)度程序中喂看門狗,時(shí)間片不可過大,超過4MS不喂狗看門狗發(fā)出系統(tǒng)復(fù)位信號。看門狗功能可以在配置定義中取消。
  17. ;5、分時(shí)過程通過定時(shí)器0進(jìn)行,其初值定義在T0_VALUE中,目前設(shè)置的是5MS時(shí)間片。
  18. ;6、系統(tǒng)時(shí)間記錄在SYS_TIME變量中,通過定時(shí)器2進(jìn)行,目前設(shè)置是10MS加1.

  19. ;任務(wù)操作說明
  20. ;0、任務(wù)的邊界應(yīng)該是循環(huán)。不建議跳出邊界。盡可能的使用系統(tǒng)提供的調(diào)用。
  21. ;1、不同任務(wù)可以調(diào)用同一個子程序,注意子程序內(nèi)受保護(hù)的范圍。
  22. ;2、主任務(wù)擁有獨(dú)立的R0-R7、ACC、B、PSW寄存器、DPTR指針。次任務(wù)僅保護(hù)7個寄存器。
  23. ;3、任務(wù)之間可以通過內(nèi)存變量來傳遞信息,注意在寫內(nèi)存時(shí)必須占用系統(tǒng),寫完后再釋放系統(tǒng),建議使用加鎖和解鎖調(diào)用。
  24. ;4、系統(tǒng)初始化后所有任務(wù)都是睡眠的,系統(tǒng)會喚醒任務(wù)0和任務(wù)7,其他任務(wù)的喚醒由用戶操作。任務(wù)7為伺服任務(wù),不建議休眠它或在該任務(wù)中使用系統(tǒng)延時(shí)調(diào)用。(有一種風(fēng)險(xiǎn):所有任務(wù)處于休眠態(tài),會進(jìn)入待機(jī))
  25. ;5、系統(tǒng)的性能與晶振頻率、喚醒的任務(wù)數(shù)量、任務(wù)占用的時(shí)間片有關(guān)系。
  26. ;6、任務(wù)有權(quán)殺死或休眠任何任務(wù),如果系統(tǒng)所有任務(wù)都被殺死或休眠,系統(tǒng)會進(jìn)入節(jié)電POWER_DOWN模式,等待復(fù)位激活。
  27. ;7、系統(tǒng)提供10MS刻度的16位系統(tǒng)時(shí)間,由TIMER2來完成。任務(wù)可以根據(jù)自己需要來完成延時(shí)功能,其性能優(yōu)于普通的空等待DELAY子程序。
  28. ;8、任務(wù)不可以操作TIMER0和TIMER2這兩個定時(shí)器,需要時(shí),可以使用TIMER1. 建議不要設(shè)置為高優(yōu)先級,可能導(dǎo)致系統(tǒng)時(shí)間停走。

  29. ;注:殺死和休眠的區(qū)別:任務(wù)被殺死后再次喚醒從頭開始運(yùn)行,任務(wù)被休眠后再次喚醒是從原來休眠的地方繼續(xù)運(yùn)行(就像暫停)。

  30. ;用戶使用注意:
  31. ;1.總計(jì)8個任務(wù),單個線程是死循環(huán),所有線程并發(fā)執(zhí)行,可以有限調(diào)整每個線程的時(shí)間片,默認(rèn)5MS時(shí)間片,合理使用可以滿足實(shí)時(shí)要求。
  32. ;2.任務(wù)0到2是主線程,線程內(nèi)寄存器A和B,R0-R7,DPTR都受保護(hù),子程序嵌套調(diào)用最大達(dá)8級。
  33. ;3.任務(wù)3到7是次線程,線程內(nèi)寄存器A和B,R0-R3受保護(hù),子程序嵌套調(diào)用最大2級。注意這個限制條件。嵌套調(diào)用超限將導(dǎo)致堆棧過界破壞,使系統(tǒng)崩潰。
  34. ;4.用戶只能使用0-59之間的內(nèi)存空間。
  35. ;5.用戶無需考慮堆棧的分配,禁止任務(wù)程序修改堆棧指針SP。
  36. ;6.中斷響應(yīng)程序中要注意保護(hù)現(xiàn)場和恢復(fù)現(xiàn)場。                  

  37. ;2012-5-22 R0.91 占用系統(tǒng)和釋放系統(tǒng)改用停止和開啟計(jì)時(shí)器的方式實(shí)現(xiàn)。UNDEBUG
  38. ;2012-5-23 R0.92 喂狗簡化到CPL指令 UNDEBUG
  39. ;使用時(shí)注意:所有中斷程序內(nèi)要用到PSW,A,B,R0~R7,DPTR,必須事先暫存,返回前恢復(fù),注意它們不受保護(hù)
  40. ;2016-11-08 確認(rèn)BUG和注釋。可以作為穩(wěn)定版。

  41. ;2016-11-08 R0.99
  42. ;為了增強(qiáng)實(shí)用性,擬重新布局內(nèi)存,改用堆棧方式保護(hù)現(xiàn)場,保證3個主線程,每個線程分配29字節(jié),增加對DPTR的保護(hù),;增加對PSW的保護(hù)
  43. ;可以最大嵌套29-2(PC)-2(DPTR)-10(AB,RG)-1(PSW)=7個CALL;閹割剩余5個次線程,每個線程分配13字節(jié):AB,R0-R3,PC,PSW,最大嵌套2個CALL。
  44. ;總體256字節(jié)的內(nèi)存:3個主線程:29*3=87;5個次線程:13*5=65;8個線程狀態(tài)(優(yōu)先級、SP、鬧鈴H、鬧鈴L)=32;
  45. ;系統(tǒng)變量:10;系統(tǒng)堆棧:14;R0-R7:8個除去。 剩余用戶可用的內(nèi)存區(qū):40字節(jié)
  46. ;真可謂:螺螄殼里做道場。  

  47. ;20161110
  48. ;備忘:調(diào)度程序要增加加鎖功能(不切換,好處是總是能進(jìn)系統(tǒng)區(qū)做一些系統(tǒng)要做的事,比如喂狗)
  49. ;延時(shí)誤差太大,最大誤差是一個單位(不累計(jì)),考慮系統(tǒng)(放在時(shí)鐘程序內(nèi))來負(fù)責(zé)高精度大跨度的計(jì)時(shí),和喚醒服務(wù),需要額外16字節(jié)用于8個線程的鬧鐘記錄。
  50. ;看門狗使用指南:時(shí)間片調(diào)節(jié)的太大就會觸發(fā)看門狗,應(yīng)能根據(jù)需要關(guān)閉看門狗。13位,每一個機(jī)器周期+1
  51. ;時(shí)鐘要方便配置
  52. ;注意中斷嵌套的影響 ;注意測量 系統(tǒng)服務(wù)的時(shí)間,及其與中斷時(shí)間的比重,比重和效率成正比 ;中斷響應(yīng)前后次序的關(guān)系分析,用戶怎么用中斷
  53. ;喚醒服務(wù)要注意的是:必須留一個伺服線程,該線程始終保持就緒(不能使用系統(tǒng)延時(shí))。否則有一種風(fēng)險(xiǎn):所有任務(wù)同時(shí)調(diào)用系統(tǒng)延時(shí)而休眠,調(diào)度程序?qū)⑥D(zhuǎn)入節(jié)電模式。要復(fù)位或外部中斷才能恢復(fù)。

  54. ;20161111 r0.99基本調(diào)試完成
  55. ;0.99版本比較0.92版本特點(diǎn)如下:
  56. ;1、充分利用堆棧的特點(diǎn)布局內(nèi)存,使得保護(hù)內(nèi)容的調(diào)整變的靈活。
  57. ;2、不改變8個任務(wù)的總數(shù),但集中資源到3個主任務(wù)上,增加對psw、dptr寄存器的保護(hù)(原來沒考慮周全,如psw是必須保護(hù)的)。使主任務(wù)不再有束縛。
  58. ;3、取消原有的延時(shí)服務(wù),增加系統(tǒng)時(shí)鐘的定時(shí)喚醒服務(wù)功能,每個任務(wù)可以設(shè)置自己的延時(shí)時(shí)間,然后進(jìn)入休眠態(tài)等待,時(shí)間到了系統(tǒng)時(shí)鐘會喚醒你。
  59. ;4、改變了殺、休眠、喚醒的方式,采用位表示殺死信號、就緒態(tài)、喚醒服務(wù),可以用邏輯的方法快速操作。
  60. ;5、增加了調(diào)度程序的加鎖功能,加鎖狀態(tài)下,調(diào)度程序不進(jìn)行任務(wù)切換,但繼續(xù)執(zhí)行其他系統(tǒng)功能。
  61. ;6、看門狗、初始時(shí)間片可配置。
  62. ;7、任務(wù)7作為伺服線程,可以做一些簡單的脈搏動作。伺服線程必須始終就緒,否則有任務(wù)全部休眠的風(fēng)險(xiǎn)。

  63. ;0.99的篇幅反而比0.92下降了7%,除了更加實(shí)用以外,顯得更加優(yōu)美。
  64. ;實(shí)際應(yīng)用達(dá)到3個以上時(shí),修復(fù)一些潛在的bug之后,可以升為r1.0版本,并出一份《51多任務(wù)內(nèi)核的應(yīng)用手冊》

  65. ;內(nèi)存地圖規(guī)劃
  66. ;0-47 用戶
  67. ;48-63 鬧鐘數(shù)組-每個任務(wù)2個字節(jié),用于指示鬧鐘時(shí)間 /30H
  68. ;64-73 系統(tǒng)變量 /40H
  69. ;74-87 系統(tǒng)堆棧 7個CALL 包括中斷 SP_SYS:73 /49H                   再壓縮至6個call
  70. ;88-95 任務(wù)優(yōu)先狀態(tài)字節(jié) /58H
  71. ;96-103 任務(wù)SP指針 /60H
  72. ;104-132 任務(wù)0堆棧 SP0:103 /67H
  73. ;133-161 任務(wù)1堆棧 SP1:132 /84H
  74. ;162-190 任務(wù)2堆棧 SP2:161 /A1H
  75. ;191-203 任務(wù)3堆棧 SP3:190 /BEH
  76. ;204-216 任務(wù)4堆棧 SP4:203 /CBH
  77. ;217-229 任務(wù)5堆棧 SP5:216 /D8H
  78. ;230-242 任務(wù)6堆棧 SP6:229 /E5H
  79. ;243-255 任務(wù)7堆棧 SP7:242 /F2H
  80. ;NOTE:OPRATING SFR OR RAM WHERE HAVE THE SAME ADDRESS WITH EACH OTHER WILL BE ATTENTED!  CARE <DATASHEET OF AT89S52>
  81. ;-------------------------------------------------------------------------------
  82. ;標(biāo)號定義
  83. PRI_BYTE                EQU        0D8H        ;INIT PRIORITY OF EVERY TASK ;時(shí)間片;0.5MS:0FCH,1MS:0F8H,2MS:0F0H,5MS:D8H,10MS:B0H,20MS:60H           這里是參考值,初始化時(shí)間片請定義在PRI_BYTE
  84. SYS_SP                        EQU        4bH        ;SYSTEM STACK HEAD
  85. START_TASK_SP                EQU        67H
  86. TAB_PRI                        EQU        58H        ;基址 見內(nèi)存分配規(guī)劃
  87. TAB_SP                        EQU        60H        ;基址
  88. TAB_CLK                        EQU        30H        ;BASE
  89. WDT_PIE                        EQU        00H        ;設(shè)置為1E,看門狗開啟,其他值則關(guān)閉看門狗 NO TEST  13位計(jì)時(shí)器1FFF復(fù)位,合計(jì)4MS :意味著啟用看門狗時(shí),時(shí)間片必須小于4MS,占用系統(tǒng)時(shí)也要注意這個問題,建議用加鎖功能代替占用系統(tǒng)

  90. ;系統(tǒng)全局變量定義
  91. sys_bit_byte                equ        2fh        ;留給系統(tǒng)的8個標(biāo)志位 位地址78-7fh
  92. TMP_A                        EQU        40H
  93. TASK_CURT_P                EQU        41H        ;當(dāng)前的任務(wù)指針
  94. task_sch_p                equ 4ah        ;調(diào)度任務(wù)指針
  95. CLK_ALARM                EQU        42H        ;鬧鐘字節(jié) 從左到右每一位依次標(biāo)志任務(wù)0到7的鬧鈴請求,1為有鬧鈴請求
  96. DEAD_SIG                EQU        43H        ;從左到右每一位依次標(biāo)志任務(wù)0到7的殺死請求,1為有殺死請求
  97. READY_BYTE                EQU        44H        ;從左到右每一位依次標(biāo)志任務(wù)0到7的就緒狀態(tài),1為就緒
  98. LOCK_BYTE                EQU        45H        ;5A表示加鎖,其他值表示解鎖                               
  99. TMP_SP                        EQU        46H       
  100. WDT_BYTE                 EQU        47H        ;狗盆子
  101. SYS_TIME_H                EQU        48H        ;系統(tǒng)時(shí)鐘高8位
  102. SYS_TIME_L                EQU        49H        ;系統(tǒng)時(shí)鐘低8位
  103. nouse                        equ 4bh ;預(yù)留

  104. preempt_bit                 bit        78h        ;是否搶占
  105. delay_sv_bit                bit        79h        ;定時(shí)器1中斷服務(wù) 標(biāo)志 用于小刻度的延時(shí)需求
  106. preempt_task                EQU        2eh        ;搶占任務(wù)號 僅0-7有效,搶占后作廢,用于調(diào)度程序切換到指定的任務(wù)去。
  107. delay_times                equ        2dh        ;用于timer1計(jì)時(shí)刻度的次數(shù)

  108. ;系統(tǒng)晶振:24.576MHZ
  109. T0_VALUE_H                EQU        0D8H        ;時(shí)間片;0.5MS:0FCH,1MS:0F8H,2MS:0F0H,5MS:D8H,10MS:B0H,20MS:60H           這里是初始賦值,初始化時(shí)間片請定義在PRI_BYTE
  110. T0_VALUE_L                EQU        00H                                                  
  111. T2_VALUE_H                EQU        0B0H        ;時(shí)鐘刻度 參考上面
  112. T2_VALUE_L                EQU        00H
  113. T1_VALUE_H                EQU        0fcH        ;時(shí)鐘刻度 參考上面 500us
  114. T1_VALUE_L                EQU        00h        ;       

  115. ;------------------------------規(guī)劃程序入口
  116. ORG 00H
  117.         JMP SYS_START
  118. ORG 03H
  119.         ;LJMP INT_INT0  ;(INT0)
  120.         RETI
  121. ORG 0BH
  122.         ;LJMP INT_T0        ;(IF0)
  123.         LJMP SHARE_SYS
  124.         RETI
  125. ORG 13H
  126.         ;LJMP INT_INT1        ;(INT1)
  127.         RETI
  128. ORG 1BH
  129.         ;LJMP INT_T1        ;(IF1)
  130.         JMP sys_ms_svrs
  131.         RETI
  132. ORG 23H
  133.         ;LJMP INT_RTX        ;(RI,TI)
  134.         RETI
  135. ORG 2BH
  136.         ;LJMP INT_T2        ;(IF2)
  137.         JMP SYS_TIME_RUN
  138.         RETI

  139. ;標(biāo)記中斷返回:如果意外中斷,直接返回,不至于跳飛;-)

  140. ;以下是任務(wù)的入口,應(yīng)和表格中定義一致
  141. ORG 30H
  142.         LJMP TASK_0
  143. ORG 38H
  144.         LJMP TASK_1
  145. ORG 40H
  146.         LJMP TASK_2
  147. ORG 48H
  148.         LJMP TASK_3
  149. ORG 50H
  150.         LJMP TASK_4
  151. ORG 58H
  152.         LJMP TASK_5
  153. ORG 60H
  154.         LJMP TASK_6
  155. ORG 68H
  156.         LJMP TASK_7

  157. ;;開機(jī),從00H跳過來*******************************************
  158. SYS_START:
  159.         MOV SP,#SYS_SP                ;SYSTEM STACK
  160.         MOV WDT_BYTE,#WDT_PIE        ;準(zhǔn)備好狗糧
  161.         clr preempt_bit       
  162.         clr delay_sv_bit
  163.         mov preempt_task,#0

  164.         CALL INIT_RAM                ;初始化系統(tǒng)內(nèi)存
  165.         CALL INIT_TIMER                ;初始化定時(shí)器
  166.         CALL USER_INIT                ;用戶初始化程序
  167.         CALL SYS_TIMER_START        ;啟動系統(tǒng)定時(shí)器
  168.        
  169.         MOV DEAD_SIG,#0                                ;清空殺手信號
  170.         MOV R1,#0F8H;
  171.         MOV R0,#7;
  172.         CALL SET_PRIBYTE        ;任務(wù)7時(shí)間片設(shè)置為1MS
  173.         MOV READY_BYTE,#10000001B        ;任務(wù)0就緒,任務(wù)7當(dāng)作伺服線程,如果沒有一個線程就緒,會進(jìn)待機(jī)
  174.         MOV TASK_CURT_P,#0       
  175.         MOV TASK_sch_P,#0       
  176.         MOV TAB_PRI,#PRI_BYTE                ;任務(wù)正常運(yùn)行的要素:不被殺,就緒,優(yōu)先級(時(shí)間片)不要太長(看門狗會叫),SP狀態(tài)
  177.         MOV SP,#START_TASK_SP         
  178.         LJMP TASK_0                        ;進(jìn)入任務(wù)0,啟動分時(shí),START SHARE

  179. ;;上面用到的子程序:任務(wù)1到7依次初始化各自內(nèi)存空間-----------------------------
  180. INIT_RAM:
  181.         MOV R0,#7 ;以此對各任務(wù)進(jìn)行內(nèi)存初始化賦值
  182. ITR0:
  183.         CALL TASKRAM_INIT
  184.         DJNZ R0,ITR0     ;TASK0任務(wù)作為系統(tǒng)啟動的入口,可以不用初始,其內(nèi)容會在第一個時(shí)間片中斷后調(diào)度程序會給予。
  185.         ;CALL TASKRAM_INIT          ;JUST FOR TEST        TASK0 RAM INIT
  186. RET

  187. ;以下表格用于初始化內(nèi)存用
  188. TAB_1:
  189.         DB 067H,084H,0A1H,0BEH,0CBH,0D8H,0E5H,0F2H,00H        ;任務(wù)棧頂?shù)刂?br />
  190. TAB_2:
  191.         DB 030H,038H,040H,048H,050H,058H,060H,068H,00H        ;任務(wù)入口地址 和ORG 30H.. 對應(yīng)         

  192. ;上面用到的子程序:開機(jī)初始化任務(wù)內(nèi)存操作:1、根據(jù)任務(wù)號查表得棧頂位置、入口位置;2、在棧頂壓入:入口、現(xiàn)場;3、將SP存到SP_I; 4、清就緒態(tài)
  193. ;初始化任務(wù)內(nèi)存分主次 ;任務(wù)號先存R0       
  194. TASKRAM_INIT:  
  195.         MOV A,R0
  196.         MOV DPTR,#TAB_1       
  197.         MOVC A,@A+DPTR        ;查表得初始SP

  198.         MOV TMP_SP,SP
  199.         MOV SP,A        ;開始壓棧

  200.         MOV A,R0
  201.         MOV DPTR,#TAB_2       
  202.         MOVC A,@A+DPTR        ;查表得初始PC
  203.        
  204.         MOV 02H,A
  205.         PUSH 02H                ;PUSH PC_L
  206.         MOV 02H,#0
  207.         PUSH 02H                ;PUSH PC_H  PC是16位的

  208.         PUSH 02H ;PSW,AB,R0-R7,DPTR
  209.         PUSH 02H
  210.         PUSH 02H
  211.         PUSH 02H
  212.         PUSH 02H
  213.         PUSH 02H
  214.         PUSH 02H

  215. ;區(qū)分主次任務(wù)
  216. ;任務(wù)號大于2則跳過以下步驟
  217.         CLR C
  218.         MOV A,#2
  219.         SUBB A,R0
  220.         JC TKI00

  221.         PUSH 02H ;R4 R5 R6 R7 DPL DPH
  222.         PUSH 02H
  223.         PUSH 02H
  224.         PUSH 02H
  225.         PUSH 02H
  226.         PUSH 02H
  227. TKI00:
  228. ;保存SP到數(shù)組,SP--> SP_I
  229.         MOV A,#TAB_SP
  230.         ADD A,R0
  231.         MOV R1,A        ;這個是指針變量,指向當(dāng)前SP的存放地址
  232.         MOV @R1,SP        ;記錄SP

  233.         MOV SP,TMP_SP ;壓棧完成,恢復(fù)SP

  234. ;優(yōu)先級字節(jié)賦值初始值
  235.         MOV A,#TAB_PRI
  236.         ADD A,R0
  237.         MOV R1,A       
  238.         MOV @R1,#PRI_BYTE

  239.         ;清就緒態(tài)
  240.         CALL CLR_READY_BIT         
  241. RET                 

  242. ;子程序:以下初始化系統(tǒng)定時(shí)器 TIMER2 DEBUGED 120516 --------------------------
  243. INIT_TIMER:       
  244.                 ;TIMER2 SETUP       
  245.         MOV 0C8H,#00H        ;MOV T2CON,#00H
  246.         MOV 0C9H,#00H        ;MOV T2MOD,#00H
  247.         MOV 0CCH,#T2_VALUE_L        ;MOV TL2,#T2_VALUE_L
  248.         MOV 0CDH,#T2_VALUE_H        ;MOV TH2,#T2_VALUE_H
  249.         MOV 0CAH,0CCH         ;MOV RCAP2L,TL2
  250.         MOV 0CBH,0CDH        ;MOV RCAP2H,TH2
  251.                 ;TIMER0 SETUP
  252.         ANL 88H,#11101111B;TCON CLR TR0 : STOP TIMER0
  253.         ANL 89H,#11110000B ;TMOD(SET TIMER0)
  254.         ORL 89H,#00000001B ;TMOD(SET TIMER0) MODE:01 16BIT COUNT UP
  255.         MOV 8AH,#T0_VALUE_L        ;TL0
  256.         MOV 8CH,#T0_VALUE_H        ;TH0

  257.         ;TIMER1 SETUP
  258.         ANL 88H,#10111111B;TCON CLR TR0 : STOP TIMER1
  259.         ANL 89H,#00001111B ;TMOD(SET TIMER0)
  260.         ORL 89H,#00010000B ;TMOD(SET TIMER0)MODE:01 16BIT COUNT UP MODE:02 8BIT autoCOUNT UP
  261.         MOV 8bH,#T1_VALUE_L        ;TL0
  262.         MOV 8dH,#T1_VALUE_H        ;TH0
  263. RET          

  264. ;子程序:啟動TIMER0和TIMER2   ;DEBUGED 120516---------------------------------
  265. SYS_TIMER_START:
  266.         MOV SYS_TIME_H,#00
  267.         MOV SYS_TIME_L,#00
  268.         MOV IP,#00000000B        ;SET PRIORITY
  269.         MOV IE,#10101010B        ;SETB EA ;SETB ET2 ;SETB ET0 ET1  TO ENABLE INTERUPT OF TIMER2 AND TIMER0 AND TIMER1
  270.         ORL 88H,#01010000B           ;TCON SETB TR0,TR1 START TIMER0 TIMER1
  271.         ORL 0C8H,#00000100B           ;ORL T2CON,#00000100B        ;SETB TR2 TO START TIMER2
  272. RET

  273. ;;系統(tǒng)時(shí)間處理,在TIMER2中斷后跳進(jìn)來
  274. ;系統(tǒng)時(shí)間處理有2大內(nèi)容:1、比較各鬧鐘的目標(biāo)時(shí)間是否到達(dá),到達(dá)并且該任務(wù)有喚醒服務(wù),就執(zhí)行喚醒;2、時(shí)鐘刻度加一。
  275. SYS_TIME_RUN:
  276.         CLR EA
  277.         MOV TMP_SP,SP                ;保存A        保護(hù)現(xiàn)場
  278.         MOV SP,#SYS_SP  ;--------------------------------界面,以下系統(tǒng)區(qū)
  279.         MOV TMP_A,PSW
  280.         PUSH TMP_A
  281.         MOV TMP_A,A ;PUSH A
  282.         PUSH TMP_A
  283.         MOV TMP_A,B        ;PUSH B
  284.         PUSH TMP_A
  285.         PUSH 00H        ;PUSH R0
  286.         PUSH 01H        ;PUSH R1
  287.         PUSH 02H        ;PUSH R2
  288.         PUSH 03H        ;PUSH R3  

  289. ;處理CLK_ALARM字節(jié)、TAB_CLK數(shù)組
  290.         MOV A,CLK_ALARM
  291.         JZ STR00              ;沒有服務(wù)時(shí)跳過
  292.         MOV R0,#TAB_CLK
  293.         MOV R3,SYS_TIME_L
  294.         CALL PROC_CMP_BYTE ;低8位比較
  295.         MOV R1,A

  296.         MOV R0,#TAB_CLK
  297.         DEC R0
  298.         MOV R3,SYS_TIME_H
  299.         CALL PROC_CMP_BYTE ;高8位比較,對比結(jié)果保存到A 1表示相等 0表示不等

  300.         ANL A,R1 ;H和L的比較結(jié)果合并

  301.         MOV R1,A
  302.         MOV A,CLK_ALARM       
  303.         ANL A,R1 ;與喚醒服務(wù)合并

  304.         ORL READY_BYTE,A ;執(zhí)行喚醒

  305.         MOV A,R1
  306.         CPL A
  307.         ANL CLK_ALARM,A ;清喚醒標(biāo)志,表示完成喚醒

  308. STR00:
  309. ;16位系統(tǒng)時(shí)鐘+1                   放在后面處理,延時(shí)00時(shí)可立即生效
  310.         MOV A,SYS_TIME_L
  311.         INC SYS_TIME_L
  312.         INC A
  313.         JNZ $+4
  314.         INC SYS_TIME_H
  315.         ANL 0C8H,#01111111B   ;ANL T2CON,#01111111B        ;CLEAR TF2 清TIMER2中斷標(biāo)志       

  316.         POP 03H        ;POP R3
  317.         POP 02H        ;POP R2
  318.         POP 01H        ;POP R1
  319.         POP 00H        ;POP R0
  320.         POP TMP_A
  321.         MOV B,TMP_A         ;POP B
  322.         POP TMP_A
  323.         MOV A,TMP_A        ;POP A                ;恢復(fù)現(xiàn)場
  324.         POP TMP_A
  325.         MOV PSW,TMP_A

  326.         MOV SP,TMP_SP ;---------------------------------------------界面,以上系統(tǒng)區(qū)
  327.         SETB EA
  328. RETI
  329. ;;;;;;;中斷返回

  330. ;用于刻度為500us,次數(shù)255的等待服務(wù)。只提供一個線程使用,出于系統(tǒng)消耗的考慮,500us中斷必須篇幅足夠小。
  331. ;定時(shí)器1中斷服務(wù):500us中斷一次,無服務(wù)直接返回。有服務(wù):次數(shù)(time_us字節(jié))為0則讓waiting_task_p任務(wù)搶占(標(biāo)志完成)。不為0時(shí),減一。
  332. ;系統(tǒng)需要用一個字節(jié)的標(biāo)志位2fh,用戶要避開。
  333. ;preempt_bit                bit        78h        ;是否搶占
  334. ;delay_sv_bit                bit        79h        ;定時(shí)器1中斷服務(wù) 標(biāo)志 用于小刻度的延時(shí)需求
  335. ;preempt_task                EQU        3fh        ;搶占任務(wù)號 僅0-7有效,搶占后作廢,用于調(diào)度程序切換到指定的任務(wù)去。
  336. ;delay_times                equ        3eh        ;用于timer1計(jì)時(shí)刻度的次數(shù)

  337. sys_ms_svrs:
  338.         jb delay_sv_bit,smsv0 ;無服務(wù)直接返回
  339.         MOV 8bH,#T1_VALUE_L        ;TL1
  340.         MOV 8dH,#T1_VALUE_H        ;TH1
  341.         reti
  342. smsv0:
  343.         ;保護(hù)現(xiàn)場
  344.         mov tmp_a,a
  345.        
  346.         ;查delay_times次數(shù):等于0時(shí),置搶占任務(wù)preempt_bit
  347.         mov a,delay_times
  348.         jnz smsv1
  349.         setb preempt_bit ;置搶占位
  350.         clr delay_sv_bit        ;清服務(wù)位
  351.         ORL 88H,#00100000B        ;SETB TF0 ;SOFT INTERUPT TIMER0  TCON-->:TF1:TR1:TF0:TR0:IE1:IT1:IE0:IT0:
  352.         ;中斷不嵌套,本次中斷返回后進(jìn)入系統(tǒng)中斷
  353.         MOV 8bH,#T1_VALUE_L        ;TL1
  354.         MOV 8dH,#T1_VALUE_H        ;TH1
  355.         mov tmp_a,a
  356.         reti
  357.         ;次數(shù)減一
  358. smsv1:        dec delay_times

  359.         ;恢復(fù)現(xiàn)場
  360.         mov a,tmp_a
  361.         MOV 8bH,#T1_VALUE_L        ;TL1
  362.         MOV 8dH,#T1_VALUE_H        ;TH1
  363. reti

  364. ;;上面要用到
  365. ;;子程序:基地址存R0(間隔1個字節(jié)的8個數(shù)組),與系統(tǒng)時(shí)鐘(H或L字節(jié))R3進(jìn)行比較,8次,結(jié)果存放在ACC對應(yīng)的位里面,1表示相等
  366. PROC_CMP_BYTE:
  367.         MOV B,#0
  368.         MOV R2,#8
  369.         MOV A,#15
  370.         ADD A,R0
  371.         MOV R0,A
  372. PCB00:
  373.         MOV A,@R0
  374.         CJNE A,03H,PCB01
  375.         MOV A,B
  376.         SETB C
  377.         RRC A
  378.         JMP PCB02
  379. PCB01:
  380.         MOV A,B
  381.         CLR C
  382.         RRC A
  383. PCB02:        MOV B,A

  384.         DEC R0
  385.         DEC R0    ;間隔1字節(jié)的指針,從右到左
  386.        
  387.         DJNZ R2,PCB00
  388.         MOV A,B
  389. RET

  390. ;;調(diào)度程序,在timer0中斷后跳過來。
  391. ;;調(diào)度程序的內(nèi)容:1、保護(hù)現(xiàn)場;2、存sp;3、喂狗、執(zhí)行任務(wù)死刑、判斷加鎖;4、切換下一個就緒的任務(wù)指針;5、調(diào)取新任務(wù)的時(shí)間片設(shè)置到定時(shí)器;6、調(diào)取新sp;7、恢復(fù)現(xiàn)場;8、返回到新任務(wù)。
  392. SHARE_SYS:  ;保護(hù)現(xiàn)場先
  393.         MOV TMP_A,PSW ;PUSH PSW
  394.         PUSH TMP_A
  395.         MOV TMP_A,A ;PUSH A
  396.         PUSH TMP_A
  397.         MOV TMP_A,B        ;PUSH B
  398.         PUSH TMP_A
  399.         PUSH 00H        ;PUSH R0
  400.         PUSH 01H        ;PUSH R1
  401.         PUSH 02H        ;PUSH R2
  402.         PUSH 03H        ;PUSH R3
  403. ;區(qū)分主次任務(wù)
  404. ;TASK_CURT_P 大于2則跳過以下步驟
  405.         CLR C
  406.         MOV A,#2
  407.         SUBB A,TASK_CURT_P
  408.         JC SS00

  409.         PUSH 04H        ;PUSH R4
  410.         PUSH 05H        ;PUSH R5
  411.         PUSH 06H        ;PUSH R6
  412.         PUSH 07H        ;PUSH R7
  413.         PUSH DPL
  414.         PUSH DPH

  415. SS00:                              ;存SP到數(shù)組SP
  416.         MOV A,#TAB_SP
  417.         ADD A,TASK_CURT_P
  418.         MOV R0,A        ;這個是指針變量,指向當(dāng)前SP的存放地址
  419.         MOV @R0,SP        ;記錄SP       
  420.        
  421. ;切換SP,以下進(jìn)入系統(tǒng)區(qū)----------------------------------------------------------------INTERFACE
  422.         MOV SP,#SYS_SP                ;SP指向系統(tǒng)SP
  423.         CALL WDT            ;喂狗
  424.         CALL KILL_TASK ;根據(jù)DEAD_SIG字節(jié),執(zhí)行任務(wù)的死刑 ;-*

  425. ;是否上鎖,如果上鎖 LOCK_BYTE= 5AH 則不執(zhí)行任務(wù)切換
  426.         MOV A,LOCK_BYTE
  427.         CJNE A,#5AH,SS04
  428.         JMP SS05
  429. SS04:
  430.         mov r1,task_sch_p        ;暫存
  431.         MOV R6,#10
  432. SELECT_P:                        ;選擇下一個任務(wù)
  433.         DJNZ R6,SS01                ;選擇次數(shù)計(jì)時(shí),如果連續(xù)選擇超10次就得進(jìn)節(jié)電模式了
  434.         MOV P1,#0FFH
  435.         ORL 87H,#02H                ;INTO POWER-DOWN MODE
  436.         LJMP SYS_START                ;醒來的話就重新開機(jī)咯

  437. ;切換任務(wù)指針(0-7) 全局變量TASK_sch_P 任務(wù)指針,僅此進(jìn)行寫操作
  438. SS01:
  439.         INC TASK_sch_P
  440.         MOV R0,TASK_sch_P
  441.         CJNE R0,#8,SS02 ;超限
  442.         MOV TASK_sch_P,#0
  443. ;判就緒位,不在就緒態(tài)就跳回 SELECT_P,重復(fù)以上步驟
  444. SS02:       
  445.         MOV R0,TASK_sch_P
  446.         CALL GET_READY_BIT
  447.         JNC SELECT_P
  448.                                            ;調(diào)度結(jié)束,新的指針在task_sch_p
  449.         ;是否有搶占信號
  450.         jnb preempt_bit,ss06
  451.         mov a,preempt_task
  452.         clr c
  453.         subb a,#8
  454.         jnc ss06 ;搶占任務(wù)號無效(大于7)

  455.         mov a,preempt_task
  456.         cjne a,task_sch_p,ss07 ;如果搶占任務(wù)和本次應(yīng)該調(diào)度的任務(wù)相同,則下一次不要再調(diào)這個任務(wù)了。(本次調(diào)度生效,否則退回上一次調(diào)度指針)。
  457.         jmp ss08
  458. ss07:       
  459.         mov task_sch_p,r1 ;恢復(fù)調(diào)度指針
  460. ss08:       
  461.         mov r0,preempt_task
  462.         call set_ready_bit ;搶占任務(wù)就緒位
  463.         mov task_curt_p,preempt_task  ;直接指定任務(wù)號,切換
  464.         clr preempt_bit
  465.         jmp ss05

  466. ss06:        mov task_curt_p,task_sch_p ;調(diào)度盤指針 確定調(diào)度指針和實(shí)際任務(wù)指針分離,解決搶占后調(diào)度不公平問題

  467. SS05:
  468. ;取優(yōu)先字節(jié)地址
  469.         MOV A,#TAB_PRI
  470.         ADD A,TASK_CURT_P
  471.         MOV R0,A

  472. ;時(shí)間片賦值 ;RESET THE TIMER0       
  473.         MOV 8AH,#T0_VALUE_L        ;TL0
  474.         MOV 8CH,@R0                ;TH0  ;MOV TH0,@R0;選中后,優(yōu)先級設(shè)置到時(shí)間片

  475. ;取SP_I --> SP
  476.         MOV A,#TAB_SP
  477.         ADD A,TASK_CURT_P
  478.         MOV R0,A
  479.         MOV SP,@R0

  480. ;以下退出系統(tǒng)態(tài),回到新的任務(wù)態(tài),恢復(fù)現(xiàn)場-------------------------------------------INTERFACE
  481. ;區(qū)分主次任務(wù)
  482. ;TASK_CURT_P 大于2則跳過以下步驟
  483.         CLR C
  484.         MOV A,#2
  485.         SUBB A,TASK_CURT_P
  486.         JC SS03

  487.         POP DPH
  488.         POP DPL
  489.         POP 07H                ;POP R7
  490.         POP 06H                ;POP R6
  491.         POP 05H        ;POP R5
  492.         POP 04H        ;POP R4

  493. SS03:
  494.         POP        03H        ;POP R3
  495.         POP 02H        ;POP R2
  496.         POP 01H        ;POP R1
  497.         POP 00H        ;POP R0
  498.         POP TMP_A
  499.         MOV B,TMP_A         ;POP B
  500.         POP TMP_A
  501.         MOV A,TMP_A        ;POP A
  502.         POP TMP_A
  503.         MOV PSW,TMP_A
  504. ;此時(shí)堆棧內(nèi)當(dāng)前應(yīng)是中斷返回時(shí)的PC值,RETI可以返回。
  505.         ;ANL 88H,#11011111B        ;CLR TF0 ;SOFT INTERUPT TIMER0  TCON-->:TF1:TR1:TF0:TR0:IE1:IT1:IE0:IT0:
  506. RETI

  507. ;;子程序:根據(jù)被殺任務(wù)信號字節(jié)8位,從左到右每一位代表任務(wù)0-7是否要?dú)⒌簦?為殺死,0為不殺 來執(zhí)行死刑
  508. ;執(zhí)行內(nèi)容:將該任務(wù)的內(nèi)存區(qū)重新初始化(初始化后為休眠態(tài)),下次再輪到時(shí),從頭開始。
  509. KILL_TASK:
  510.         MOV R3,#8
  511. KTA00:       
  512.         MOV A,DEAD_SIG
  513.         RRC A
  514.         MOV DEAD_SIG,A

  515.            JNC KTA01
  516.         MOV a,R3
  517.         DEC a       
  518.         mov r0,a
  519.         CALL TASKRAM_INIT                          
  520. KTA01:
  521.         DJNZ R3,KTA00
  522.         MOV DEAD_SIG,#0 ;清掉所有DEAD信息
  523. RET         

  524. ;;喂狗子程序
  525. WDT:
  526.         MOV 0A6H,WDT_BYTE        ;MOV WDTRST,WDT_BYTE  WDT_BYTE= 1EH OR E1H
  527.         MOV A,WDT_BYTE
  528.         CPL A                        ;取反
  529.         MOV WDT_BYTE,A
  530. RET

  531. ;;獲取就緒位:在調(diào)度程序中用到
  532. GET_READY_BIT:  ;任務(wù)號R0, 執(zhí)行結(jié)束后,結(jié)果的位在C
  533.         MOV B,R0
  534.         INC B
  535.         MOV A,READY_BYTE
  536. GRB00:        RLC A
  537.         DJNZ B,GRB00       
  538. RET         

  539. ;提供的系統(tǒng)調(diào)用
  540. ;-----------------------------------------------------------------------------------------------

  541. ;子程序:修改任務(wù)的時(shí)間片,任務(wù)號在R0,優(yōu)先字節(jié)(時(shí)間片)在R1,將優(yōu)先字節(jié)寫入到數(shù)組
  542. SET_PRIBYTE:
  543.         MOV A,#TAB_PRI
  544.         ADD A,R0
  545.         MOV R0,A
  546.         MOV A,R1
  547.         MOV @R0,A
  548. RET

  549. ;子程序:回到調(diào)度程序        DEBUGED 120516
  550. WAITING:
  551.         NOP                        ;留給中斷響應(yīng)的間隙
  552.         ORL 88H,#00100000B        ;SETB TF0 ;SOFT INTERUPT TIMER0  TCON-->:TF1:TR1:TF0:TR0:IE1:IT1:IE0:IT0:
  553. RET


  554. ;子程序:占用系統(tǒng),任務(wù)在讀寫的時(shí)候不允許系統(tǒng)中斷,和frees配套使用
  555. OCCUPY:
  556.         ;ORL IE,#00000010B        ;ENABLE INTERRUPT OF TIMER0    方法1:關(guān)閉timer0的中斷
  557.         ANL 88H,#11101111B           ;TCON CLR TR0, STOP TIMER0  方法2:關(guān)閉timer0的計(jì)時(shí)
  558. RET


  559. ;子程序:釋放系統(tǒng) 和occupy配套使用,任務(wù)占用系統(tǒng)后應(yīng)及時(shí)釋放
  560. FREES:
  561.         ;ANL IE,#11111101B        ;DISABLE INTERUPT OF TIMER0
  562.         ORL 88H,#00010000B           ;TCON SETB TR0, START TIMER0
  563. RET

  564. ;注意:occupy和free要配套用,他們之間就是臨界區(qū),然而occupy會導(dǎo)致不進(jìn)調(diào)度程序,不建議使用。建議用加鎖和解鎖來實(shí)現(xiàn)臨界區(qū)的操作。
  565. LOCK_SYS:
  566.         MOV LOCK_BYTE,#5AH
  567. RET

  568. UNLOCK_SYS:
  569.         MOV LOCK_BYTE,#0EEH
  570. RET


  571. ;精確的系統(tǒng)延時(shí)-將16位的延時(shí)數(shù),每一位為一個時(shí)刻,存放在DPTR,計(jì)算目標(biāo)時(shí)間,設(shè)置喚醒任務(wù),休眠自己。等待系統(tǒng)時(shí)鐘在時(shí)間到了再喚醒你,誤差為一個調(diào)度周期。
  572. ;任務(wù)號為全局變量指針TASK_CURT_P
  573. ;延時(shí)步驟:1、將dptr個刻度和當(dāng)前時(shí)間相加得到目標(biāo)時(shí)間,存入到鬧鈴數(shù)組當(dāng)前任務(wù)位置;2、設(shè)置本任務(wù)的喚醒服務(wù)位,當(dāng)目標(biāo)時(shí)間到達(dá),系統(tǒng)時(shí)鐘會喚醒你;3、進(jìn)入休眠態(tài);
  574. DELAY_SYS:
  575. ;計(jì)算目標(biāo)16位目標(biāo)值,存放在TAB_CLK對應(yīng)的位置
  576.         MOV A,SYS_TIME_L
  577.         ADD A,DPL
  578.         MOV DPL,A
  579.         MOV A,SYS_TIME_H
  580.         ADDC A,DPH                ;帶進(jìn)位
  581.         MOV DPH,A

  582.         MOV A,#TAB_CLK
  583.         MOV R0,TASK_CURT_P
  584.        
  585.         ADD A,R0
  586.         ADD A,R0
  587.                         ;雙字節(jié)指針
  588.         MOV R0,A
  589.         MOV @R0,DPH
  590.         INC R0
  591.         MOV @R0,DPL

  592. ;設(shè)置喚醒位,在CLK_ALARM字節(jié),8個位標(biāo)志8個任務(wù)的喚醒服務(wù),1為有服務(wù)。
  593.         MOV R0,TASK_CURT_P
  594.         CALL SET_ALARM_BIT

  595. ;清就緒位,在READY_BYTE
  596.         MOV R0,TASK_CURT_P
  597.         CALL CLR_READY_BIT

  598. ;回調(diào)度
  599.         ORL 88H,#00100000B       
  600. RET

  601. ;上面用到的子程序:設(shè)置喚醒服務(wù)的位,任務(wù)號預(yù)先放在R0
  602. SET_ALARM_BIT:
  603.         MOV B,R0
  604.         MOV A,#10000000B
  605.         INC B                        ;最小任務(wù)號為1

  606. SAB00:        DJNZ B,SAB01 ;循環(huán)左移
  607.         ORL CLK_ALARM,A
  608.         JMP SAB02

  609. SAB01:        RR A
  610.         JMP SAB00
  611. SAB02:
  612. RET


  613. ;子程序:任務(wù)自殺
  614. KILL_SELF:
  615.         MOV B,TASK_CURT_P
  616.         MOV A,#10000000B
  617.         INC B                        ;最小任務(wù)號為1

  618. KSF00:        DJNZ B,KSF01 ;循環(huán)左移
  619.         ORL DEAD_SIG,A
  620.         JMP KSF02

  621. KSF01:        RR A
  622.         JMP KSF00
  623. KSF02:          
  624. RET

  625. ;子程序:殺死,任務(wù)號存R0
  626. KILL_TASK_CALL:
  627.         MOV B,R0
  628.         MOV A,#10000000B
  629.         INC B                        ;最小任務(wù)號為1

  630. KTSK00:        DJNZ B,KTSK01 ;循環(huán)左移
  631.         ORL DEAD_SIG,A
  632.         JMP KTSK02

  633. KTSK01:        RR A
  634.         JMP KTSK00
  635. KTSK02:          
  636. RET

  637. ;子程序:清就緒位,就緒態(tài)字節(jié) 8位  從左到右每一位分別代表任務(wù)0-7是否就緒,1為就緒,0為休眠
  638. ;任務(wù)號存在R0               
  639. CLR_READY_BIT:
  640.         MOV B,R0
  641.         MOV A,#01111111B
  642.         INC B                        ;最小任務(wù)號為1

  643. CRB00:        DJNZ B,CRB01 ;循環(huán)左移
  644.         ANL READY_BYTE,A
  645.         JMP CRB02

  646. CRB01:        RR A
  647.         JMP CRB00
  648. CRB02:

  649. RET

  650. ;子程序:置就緒位,上面的相反操作 ;任務(wù)號存在R0
  651. SET_READY_BIT:
  652.         MOV B,R0
  653.         MOV A,#10000000B
  654.         INC B                        ;最小任務(wù)號為1

  655. SRB00:        DJNZ B,SRB01 ;循環(huán)左移
  656.         ORL READY_BYTE,A
  657.         JMP SRB02

  658. SRB01:        RR A
  659.         JMP SRB00
  660. SRB02:
  661. RET

  662. ;子程序:小刻度的延時(shí)功能(通過定時(shí)器1和搶占機(jī)制完成),次數(shù)放在r0
  663. delay_sys_us:       
  664.         mov delay_times,r0
  665.         mov preempt_task,task_curt_p ;占用的任務(wù)號預(yù)存
  666.         mov r0,task_curt_p
  667.         call clr_ready_bit ;延時(shí)期間要休眠
  668.         setb delay_sv_bit ;開啟延時(shí)服務(wù)       
  669.         ORL 88H,#00100000B        ;SETB TF0 ;SOFT INTERUPT TIMER0  TCON-->:TF1:TR1:TF0:TR0:IE1:IT1:IE0:IT0:
  670.         nop
  671.         nop ;中斷響應(yīng)                  
  672. ret

  673. ;;;;;;;;;;;;;;;;;;伺服線程:任務(wù)7
  674. task_7:
  675.         mov r2,#77h
  676.         mov r3,#77h
  677. tk700:
  678.         mov r0,#01h
  679.         mov r1,#0eh
  680.         call delay16b
  681.         setb p1.7                        ;led 熄滅100ms       

  682.         mov r0,#0dh
  683.         mov r1,#0bdh
  684.         call delay16b

  685.         clr p1.7                        ;led 點(diǎn)亮900ms
  686.         jmp tk700
  687. jmp task_7

  688. ;r0:h r1:l  16位數(shù)的nop延時(shí) 一個周期為10.25us(全速) ,高8位放在r0,低8位放在r1
  689. delay16b:
  690. dll00:
  691.         mov a,r1
  692.         clr c
  693.         subb a,#1
  694.         mov r1,a
  695.         mov a,r0
  696.         subb a,#0 ;進(jìn)位                16位數(shù)減一
  697.         mov r0,a

  698.         div ab ;純粹為了延時(shí)
  699.         div ab
  700.         nop
  701.         nop

  702.         mov a,r0
  703.         orl a,r1
  704.         jnz dll00
  705. ret

  706. ;注意:以上僅做了關(guān)于殺死、休眠、喚醒任務(wù)的調(diào)用,僅為了使用方便,實(shí)際使用時(shí)推薦使用更高效的邏輯方法:
  707. ;比如:要?dú)⑷蝿?wù)3和6,可以將dead_sig ORL 00010010 即可
  708. ;要休眠任務(wù)2和4,可以將ready_byte ANL 11010111 即可
  709. ;要喚醒任務(wù)1和7,可以將ready_byte ORL 10000001 即可
  710. ;SYSTEM END==============================================================line number of r0.92 is 750


  711. ;User's code  
  712. ;*********************************************************************
  713. ;project name: 360度指示器 豆豆的打開關(guān)上  
  714. ;designer: ut
  715. ;version: 1.0
  716. ;date: 16-11-16
  717. ;*********************************************************************       
  718. ;用戶在此定義自己的變量地址 及 標(biāo)號 0-46d 0-2cH :一共45個字節(jié),除去0-7,可以用8-2cH這37個字節(jié)
  719. ;需求:
  720. ;1\ 16位數(shù)字鍵,0-9 abcd * #
  721. ; 2\ 3位段碼LCD顯示
  722. ;  3\ 按下數(shù)字,插入到LCD的左側(cè)。
  723. ;   4\ 按下D(回車),LCD的數(shù)字作為角度值,電機(jī)轉(zhuǎn)動到指定角度,三位數(shù)字范圍0-999,對360取模,執(zhí)行完畢后再輸入數(shù)字時(shí)LCD清零再插入。
  724. ;    5\ 按下A電機(jī)向上微調(diào),按下B電機(jī)向下微調(diào)
  725. ;     6\ 按下C,LCD清零。
  726. ;      7\ 關(guān)機(jī)時(shí),壁板歸位至270度位置,再切斷電源
  727. ;        8\ 開機(jī)時(shí),壁板開啟到0度位置。

  728. ;task0: 主線程,開機(jī)初始化,啟動其他任務(wù),主循環(huán)是不停的取鍵、取鍵值成功后處理鍵值。
  729. ;task1: 電機(jī)移動,始終試圖將當(dāng)前位置靠近目標(biāo)位置,直到達(dá)到為止。
  730. ;task2: 按鍵掃描,轉(zhuǎn)換為鍵值存入到緩沖區(qū)。

  731. ;處理鍵值:0-9,執(zhí)行循環(huán)插入 BCD 數(shù)組,如果有清屏標(biāo)志,則先清屏再插入。
  732. ;處理鍵值:A-B, 執(zhí)行電機(jī)走12拍,約1度,A為正方向,B為反方向。
  733. ;處理鍵值:C, 將BCD全部設(shè)置為0
  734. ;處理鍵值:D,將BCD轉(zhuǎn)成一個16位數(shù),再mod360運(yùn)算,將結(jié)果寫到電機(jī)目標(biāo)值。設(shè)置清屏標(biāo)志。

  735. ;關(guān)于顯示:在主線程的循環(huán)中,涉及到BCD變化時(shí),才會觸發(fā)顯示,顯示過程:將BCD碼轉(zhuǎn)換成段碼,將段碼輸出到HT1621驅(qū)動器。

  736. WR_1621                 BIT P3.6
  737. ;RD_1621                BIT P3.7
  738. DATA_1621                BIT P3.5
  739. CS_1621                 BIT P3.7

  740. BIAS         EQU 52H; //0B1000 0101 0010 1/3DUTY 4COM
  741. SYSDIS         EQU 0; //0B1000 0000 0000 關(guān)振系統(tǒng)蕩器和LCD偏壓發(fā)生器
  742. SYSEN         EQU 02H; //0B1000 0000 0010 打開系統(tǒng)振蕩器
  743. LCDOFF        EQU 04H; //0B1000 0000 0100 關(guān)LCD偏壓
  744. LCDON        EQU 06H; //0B1000 0000 0110 打開LCD偏壓
  745. XTAL         EQU 28H; //0B1000 0010 1000 外部接時(shí)鐘
  746. RC256         EQU 30H; //0B1000 0011 0000 內(nèi)部時(shí)鐘
  747. TONEON         EQU 12H; //0B1000 0001 0010 打開聲音輸出
  748. TONEOFF         EQU 10H; //0B1000 0001 0000 關(guān)閉聲音輸出
  749. WDTDIS         EQU 0AH; //0B1000 0000 1010 禁止看門狗

  750. ;;;內(nèi)存變量,范圍(8-2cH)
  751. nouse1                equ 20h ;預(yù)留給可尋址的位
  752. nouse2                equ 21h
  753. pool_key        equ 21h ;22,23,24;類似堆棧指針,前推一位
  754. p_key                equ 25h
  755. pool_bcd        equ 26h ;26,27,28;存放bcd碼 循環(huán)覆蓋
  756. p_bcd                equ 29h ;存放bcd指針,0-2 循環(huán)
  757. pool_print        equ 8h ;8 9 10 存放3位數(shù)碼管的段碼
  758. key_value        equ 0bh
  759. p_moto_H        equ 0ch
  760. p_moto_L        equ 0dh
  761. targ_moto_H        equ 0eh
  762. targ_moto_L        equ 0fh       
  763. p_step                equ 10h        ;表的指針
  764. p_deg                equ 11h


  765. ;定義位
  766. key_catched        bit 00h        ;獲取到一個按鍵后置位
  767. bcd_ready        bit 01h        ;bcd插入新值時(shí)置位
  768. moto_dir        bit 02h ;電機(jī)方向
  769. tmp_dir                bit 03h
  770. reset_bcd         bit 04h           ;重置bcd


  771. ;;用戶在這里寫初始化程序,在系統(tǒng)開機(jī)初始化時(shí),被調(diào)用,注意:此處不可進(jìn)行系統(tǒng)功能的調(diào)用
  772. user_init:
  773.         mov p1,#0f0h ;關(guān)電機(jī)
  774.         mov 08h,#0           ;段碼區(qū)
  775.         mov 09h,#0
  776.         mov 0ah,#0

  777.         mov 26h,#0        ;bcd區(qū)
  778.         mov 27h,#0
  779.         mov 28h,#0
  780.        
  781.         mov p_key,#0          ;表示無按鍵
  782.         mov p_bcd,#0
  783.         mov p_step,#0
  784.         mov p_deg,#0
  785.         mov p_moto_h,#0
  786.         mov p_moto_l,#0
  787.         mov targ_moto_h,#0
  788.         mov targ_moto_l,#0

  789.         clr reset_BCD
  790.         clr p2.2 ;開啟關(guān)機(jī)繼電器
  791.        
  792. ret



  793. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  794. ;;;;;;;;任務(wù)0   主線程                     ;
  795. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  796. ;主線程,1、從鍵盤緩沖池取一個按鍵; 2、處理該按鍵(數(shù)字鍵插入bcd區(qū))其他鍵(步數(shù)增減、清零、回車)3、bcd轉(zhuǎn)換為段碼,隱去尾部0
  797. ;4、段碼輸出
  798. ;;;其他按鍵處理:步數(shù)增加一個幅度,不改變參數(shù),步數(shù)減少一個幅度,不改變參數(shù),bcd歸零,bcd轉(zhuǎn)換為目標(biāo)值;;;;;;;;;;
  799. ;;-----------------------------------任務(wù)0
  800. task_0:
  801.         CALL Ht1621_Init;()          ; 上電初始化LCD驅(qū)動芯片
  802.         mov dptr,#tab_ht1621  
  803.         mov r3,#0
  804.         mov r5,#16
  805.         call Ht1621WrAllData;(0,Ht1621Tab,16)      ;清除1621寄存器數(shù)據(jù),清屏
  806.         mov dptr,#tab_ht1621_dou
  807.         mov r3,#0
  808.         mov r5,#3
  809.         call Ht1621WrAllData;(0,Ht1621Tab,3);顯示    ;logo
  810.         ;LCD的掃描是不需要延時(shí)的

  811.         orl ready_byte,#01100000b ;開啟任務(wù)2:鍵盤掃描程序 ;開啟任務(wù)1:電機(jī)驅(qū)動:實(shí)際值逼近目標(biāo)值
  812.        
  813.         ;臂板垂直向下270度為初始態(tài)(壓縮狀態(tài),方便包裝和移動)在電氣驅(qū)動里面初始化。

  814. tsk0tv0:
  815.         jb p2.1,tsk04 ;關(guān)機(jī)信號判斷
  816.         clr key_catched
  817.         call catch_a_key
  818.         jnb key_catched,tsk0tv0 ;沒有獲取到鍵值

  819.         clr bcd_ready
  820.         call key_proc
  821.         jnb bcd_ready,tsk0tv0 ;不涉及到bcd變化
  822.        
  823.         call bcd2print        ;bcd 轉(zhuǎn)換為段碼
  824.         call hide_zero        ;消隱尾部的0


  825.         mov r3,#0
  826.         mov r4,#pool_print
  827.         mov r5,#3
  828. ;        mov lock_byte,#5ah                         ;加鎖
  829.         call print        ;輸出到LCD
  830. ;        mov lock_byte,#11h

  831.        
  832.         jmp tsk0tv0

  833. ;;關(guān)機(jī)流程,回到270度位置
  834. tsk04:       
  835.         mov dptr,#tab_ht1621_off
  836.         mov r3,#0
  837.         mov r5,#3
  838.         call Ht1621WrAllData;(0,Ht1621Tab,3);顯示off

  839.         mov targ_moto_l,#0eh
  840.         mov targ_moto_h,#01h ;電機(jī)目標(biāo)為270

  841.         ;等待電機(jī)到點(diǎn)
  842. tsk040:        mov a,P_moto_l
  843.         cjne a,targ_moto_l,tsk040
  844.         mov a,p_moto_h
  845.         cjne a,targ_moto_h,tsk040


  846.         jnb p2.1,tsk0tv0 ;最后確認(rèn)是否關(guān)機(jī)
  847.         setb p2.2            ;關(guān)機(jī)
  848.         ORL 87H,#02H                ;INTO POWER-DOWN MODE
  849.         LJMP SYS_START                ;醒來的話就重新開機(jī)咯


  850. ;步驟:1、指針為0則無按鍵值,2、取鍵值,指針-1,(臨界區(qū));3處理鍵值;
  851. ;print_LCD: 函數(shù) 將3個字節(jié)的內(nèi)容顯示到LCD  0-f 都能顯示 步奏:1、字節(jié)數(shù)轉(zhuǎn)換到 段碼字節(jié) 2發(fā)送給ht1621
  852. ;如何循環(huán)顯示,三個字節(jié)要構(gòu)成單向環(huán),abcabcabc,始終顯示指針后3位數(shù)字,加入新的字節(jié)時(shí)指針往前推。
  853. ;將上面的三個字節(jié),轉(zhuǎn)為一個整數(shù)<=999,占2個字節(jié)。
  854. ;設(shè)為目標(biāo)值
  855. ;當(dāng)前值與目標(biāo)值比較,不等于則靠近,等于則關(guān)閉。
  856. ;關(guān)機(jī)線程
  857. ;正或反,步數(shù)n個。函數(shù)

  858. jmp task_0       
  859. ;--------task0 end----------



  860. ;任務(wù)0子程序:從pool_key,p_key取一個鍵值,存放在key_value,并置位key_catched  
  861. catch_a_key:
  862.         ;clr key_catched
  863.         mov a,p_key
  864.         jnz cak00
  865.         ret                ;p_key 為0表示鍵值池空
  866. cak00:
  867.         ;臨界區(qū):取一個鍵值
  868.         mov lock_byte,#5ah
  869.         mov a,#pool_key
  870.         add a,p_key
  871.         mov r0,a
  872.         mov a,@r0
  873.         dec p_key
  874.         mov lock_byte,#11;臨界區(qū)

  875.         mov key_value,a ;鍵值
  876.         setb key_catched
  877. ret

  878. ;任務(wù)0子程序:處理當(dāng)前鍵值,小于10放到循環(huán)的bcd池里(pool_bcd,p_bcd),大于10則調(diào)用相關(guān)功能
  879. key_proc:
  880.         mov a,key_value
  881.         clr c
  882.         subb a,#10
  883.         jc kpc00

  884.         mov a,key_value
  885.         cjne a,#0ah,kpc01
  886.         ;0a鍵功能
  887.         call up_a_bit

  888. kpc01:        cjne a,#0bh,kpc02
  889.         ;0b鍵功能
  890.         call down_a_bit

  891. kpc02:        cjne a,#0ch,kpc03
  892.         ;0c鍵功能
  893.         call clr_bcd

  894. kpc03:        cjne a,#0dh,kpc04
  895.         ;0d鍵功能
  896.         setb reset_BCD ;回車后前面的數(shù)據(jù)在下次按鍵輸入后,清掉
  897.         call set_target
  898.         ret

  899. kpc00:        call keyv2bcd
  900. kpc04:       
  901. ret

  902. ;;;;;;第二層子程序
  903. clr_bcd:
  904.         mov r0,#pool_bcd
  905.         mov @r0,#0
  906.         inc r0
  907.         mov @r0,#0
  908.         inc r0
  909.         mov @r0,#0

  910.         setb bcd_ready       
  911. ret

  912. set_target: ;將bcd里的3位數(shù)字轉(zhuǎn)換16位數(shù)字,并存入到targ_moto_L, targ_moto_H 中
  913.        
  914.         mov r1,p_bcd ;0-2范圍
  915.         mov r3,#0
  916.         mov r4,#0

  917. ;;個位數(shù)
  918.         mov a,#pool_bcd
  919.         add a,r1
  920.        
  921.         mov r0,a
  922.         mov a,@r0 ; 取到bcd值 從個位數(shù)查起

  923.         mov r4,a  ;r3存H,r4存L  100* + 10*  + L
  924. ;;;重復(fù)
  925.         dec r1  
  926.         mov a,r1   
  927.         cjne a,#0ffh,ste00;
  928.         mov r1,#2 ;過界處理
  929. ste00:
  930.         mov a,#pool_bcd
  931.         add a,r1

  932.            mov r0,a
  933.         mov a,@r0
  934.         mov b,#10
  935.         mul ab                        ;十位數(shù)

  936.         clr c        ;16位加法
  937.         addc a,r4
  938.         mov r4,a
  939.         mov a,b
  940.         addc a,r3
  941.         mov r3,a
  942. ;;;重復(fù)以上
  943.         dec r1   
  944.         mov a,r1  
  945.         cjne a,#0ffh,ste01;
  946.         mov r1,#2 ;過界處理
  947. ste01:
  948.         mov a,#pool_bcd
  949.         add a,r1

  950.         mov r0,a
  951.         mov a,@r0
  952.         mov b,#100
  953.         mul ab                        ;百位數(shù)

  954.         clr c        ;16位加法
  955.         addc a,r4
  956.         mov r4,a
  957.         mov a,b
  958.         addc a,r3
  959.         mov r3,a

  960.         call targ_mod360

  961.    mov lock_byte,#5ah
  962.         mov targ_moto_L,r4
  963.         mov targ_moto_H,r3       
  964.    mov lock_byte,#11h
  965. ret

  966. ;目標(biāo)值在r3,r4(HL),結(jié)果調(diào)整后還是在R3 R4
  967. targ_mod360:   ;輸入的bcd值(0-999)轉(zhuǎn)換為0-360度范圍,與360取模:求余
  968.                 mov dph,r3                ;暫存
  969.                 mov dpl,r4

  970.                 mov a,r4                            ;360d=0168h
  971.                 clr c
  972.                 subb a,#68h
  973.                 mov r4,a
  974.                 mov a,r3
  975.                 subb a,#01h
  976.                 mov r3,a

  977.                 jc tmd00;表示過頭了       
  978.                 jmp targ_mod360
  979. tmd00:
  980.                 mov r3,dph
  981.                 mov r4,dpl
  982. ret


  983. up_a_bit: ;電機(jī)向上微調(diào)一個距離 ;臨界區(qū)處理,禁止其他控制電機(jī)的操作
  984.         anl ready_byte,#10111111b ;休眠電機(jī)任務(wù)(task1)

  985.         mov r3,#12 ;走12拍
  986.        
  987. uab00:       
  988.         dec p_step
  989.         mov a,p_step
  990.         cpl a
  991.         jnz uab03 ;過0則回到7
  992.         mov p_step,#7
  993. uab03:
  994.         mov a,p_step
  995.         mov dptr,#tab_step
  996.         MOVC A,@a+dptr
  997.         anl P1,#11110000b        ;驅(qū)動電機(jī)
  998.         orl P1,a

  999.         call waiting
  1000.        
  1001.         djnz r3,uab00

  1002.         anl p1,#11110000b ;關(guān)電機(jī)
  1003.         orl ready_byte,#01000000b ;喚醒電機(jī)任務(wù)
  1004. ret

  1005. down_a_bit: ;電機(jī)向下微調(diào)一個距離 ;臨界區(qū)處理,禁止其他控制電機(jī)的操作
  1006.         anl ready_byte,#10111111b ;休眠電機(jī)任務(wù)(task1)

  1007.         mov r3,#12 ;走12拍
  1008. dab00:       
  1009.         inc p_step
  1010.         mov a,p_step
  1011.         cjne a,#8,dab03 ;過7則回到0
  1012.         mov p_step,#0
  1013. dab03:
  1014.         mov a,p_step
  1015.         mov dptr,#tab_step
  1016.         MOVC A,@a+dptr
  1017.         anl P1,#11110000b        ;驅(qū)動電機(jī)
  1018.         orl P1,a
  1019.        
  1020.         call waiting

  1021.         djnz r3,dab00

  1022.         anl p1,#11110000b ;關(guān)電機(jī)
  1023.         orl ready_byte,#01000000b ;喚醒電機(jī)任務(wù)
  1024. ret

  1025. ;任務(wù)0子程序:鍵值key_value放到循環(huán)的bcd池里(pool_bcd,p_bcd),置位bcd_ready *****orig
  1026. keyv2bcd:
  1027.         jnb reset_bcd,k2b10
  1028.         call clr_bcd       
  1029.         clr reset_bcd

  1030. k2b10:                mov b,key_value ;鍵值

  1031.         ;存放在pool_bcd
  1032.         mov a,p_bcd  ;容錯處理:p_bcd只能0-2,超范圍就置0
  1033.         clr c
  1034.         subb a,#3
  1035.         jnc k2b01

  1036.         ;先推指針
  1037.         inc p_bcd
  1038.         mov a,p_bcd
  1039.         cjne a,#3,k2b02 ;0-2循環(huán)處理
  1040. k2b01:        mov p_bcd,#0
  1041. k2b02:
  1042.         mov a,#pool_bcd
  1043.         add a,p_bcd
  1044.         mov r0,a
  1045.         mov @r0,b  ;再存鍵值       

  1046.         setb bcd_ready
  1047. ret

  1048. ;任務(wù)0子程序:從循環(huán)的bcd池里取最近3個值(pool_bcd,p_bcd),查表轉(zhuǎn)換成段碼,存放到段碼數(shù)組(pool_print);
  1049. bcd2print:
  1050.         mov r1,p_bcd    ;0-2范圍
  1051.         mov r2,#3        ;依次取3個數(shù)
  1052. b2p00:        mov a,#pool_bcd
  1053.         add a,r1
  1054.         mov r0,a
  1055.         mov a,@r0 ; 取到bcd值然后 查表獲取段碼

  1056.         mov dptr,#tab_ht1621_seg
  1057.         movc a,@a+dptr
  1058.         mov r3,a

  1059.         mov a,r2
  1060.         dec a
  1061.         add a,#pool_print
  1062.         mov r0,a
  1063.         mov a,r3
  1064.         mov @r0,a


  1065.         dec r1
  1066.         mov a,r1
  1067.         cpl a
  1068.         jnz b2p01
  1069.         mov r1,#2

  1070. b2p01:
  1071.         djnz r2,b2p00
  1072. ret

  1073. ;任務(wù)0子程序:將段碼數(shù)組pool_print的3個字節(jié)前面的0隱去
  1074. hide_zero:
  1075.         mov r3,#2 ;2次,個位數(shù)不管
  1076.         mov r0,#pool_print
  1077. hzo00:
  1078.         mov a,@r0
  1079.         cjne a,#5fh,hzo01  ; 5f 為0的段碼
  1080.         mov @r0,#0
  1081.         inc r0
  1082.         djnz r3,hzo00
  1083. hzo01:
  1084. ret

  1085. ;任務(wù)0子程序:將段碼數(shù)組pool_print的3個字節(jié)輸出到ht1621
  1086. print:   ;(uchar Addr,uchar *p,uchar cnt)  R3:ADDR r4:P R5:CNT

  1087.         CLR CS_1621;
  1088.         MOV A,#0A0H
  1089.         MOV R0,#3
  1090.         CALL Ht1621Wr_Data ;(0xa0,3); // - - 寫入數(shù)據(jù)標(biāo)志101
  1091.         MOV A,R3
  1092.         RLC A
  1093.         RLC A ;Ht1621Wr_Data(Addr<<2,6); // - - 寫入地址數(shù)據(jù)
  1094.         MOV R0,#6
  1095.         CALL Ht1621Wr_Data

  1096. prt00:
  1097.         mov a,r4
  1098.         mov r0,a
  1099.         MOV A,@r0 ;取段碼
  1100.         MOV R0,#8
  1101.         CALL Ht1621Wr_Data         ;Ht1621Wr_Data(*p,8); // - - 寫入數(shù)據(jù)
  1102.         INC r4
  1103.         DJNZ R5,prt00       

  1104.         SETB CS_1621
  1105.         CALL delay_a_while

  1106. RET

  1107. ;任務(wù)0清零表
  1108. TAB_HT1621:
  1109.         DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0;
  1110. TAB_HT1621_off:
  1111.         DB 033h,078h,078h,055h,055h,055h,000h,05h,05h,05h,0,0,0,0,0,0;
  1112. TAB_HT1621_dou:
  1113.         DB 0b7h,0b3h,093h,055h,055h,055h,000h,05h,05h,05h,0,0,0,0,0,0;

  1114. ;任務(wù)0段碼表,依據(jù)硬件線序確定
  1115. tab_ht1621_seg:
  1116. db 5fh; 0
  1117. db 06h; 1
  1118. db 3dh; 2
  1119. db 2fh; 3
  1120. db 66h; 4
  1121. db 6bh; 5
  1122. db 7bh; 6
  1123. db 0eh; 7
  1124. db 7fh; 8
  1125. db 6fh; 9
  1126. db 7eh; A
  1127. db 73h; b
  1128. db 31h; c
  1129. db 37h; d
  1130. db 79h; E
  1131. db 78h; F
  1132. db 33h; o




  1133. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1134. ;;;;;任務(wù)1:電機(jī)驅(qū)動,實(shí)際值逼近目標(biāo)值      ;
  1135. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1136. ;實(shí)際值:p_moto_L、p_moto_H、目標(biāo)值targ_moto_L、targ_moto_H 最大360度
  1137. ;步驟:目標(biāo)值-實(shí)際值,記錄符號(正負(fù))到moto_dir, 結(jié)果等于0時(shí),關(guān)閉電機(jī),返回;結(jié)果小于180時(shí),moto_dir反置(尋找最短路徑)
  1138. ;讓電機(jī)逼近一步,實(shí)際值+1或-1。返回。
  1139. ;;------------------------------------------任務(wù)1
  1140. task_1:
  1141.         mov p_moto_L,#0eh
  1142.         mov p_moto_H,#01h ; 0-359范圍
  1143.         mov targ_moto_L,#0
  1144.         mov targ_moto_H,#0 ; 0-999范圍 ;要mod360處理,變?yōu)?-359范圍
  1145.                 
  1146. ;臂板垂直向下270度為初始態(tài)(壓縮狀態(tài),方便包裝和移動)在電氣驅(qū)動里面初始化。

  1147. tsk100:         
  1148.                 ;16位減法 目標(biāo)值-當(dāng)前值,默認(rèn)為+
  1149.         clr moto_dir ;默認(rèn)電機(jī)方向
  1150.         clr c
  1151.         mov a,targ_moto_L
  1152.         subb a,P_moto_L
  1153.         mov b,a
  1154.         mov a,targ_moto_H
  1155.         subb a,P_moto_H ;結(jié)果高位在a,低位在b

  1156.         jnc tsk105 ;結(jié)果為負(fù)數(shù)的話  被減數(shù)+360,再減
  1157.         clr c
  1158.         mov a,targ_moto_L
  1159.         addc a,#68h
  1160.         mov r0,a
  1161.         mov a,#01h
  1162.         addc a,targ_moto_h
  1163.         mov r1,a

  1164.         clr c                           ;重新算一次
  1165.         mov a,r0
  1166.         subb a,P_moto_L
  1167.         mov b,a
  1168.         mov a,r1
  1169.         subb a,P_moto_H ;結(jié)果高位在a,低位在b

  1170. tsk105:
  1171.           mov r1,a  ;H
  1172.         mov r0,b  ;L  暫存結(jié)果(正偏差:0-359)

  1173.         jnz tsk104
  1174.         mov a,b
  1175.         jnz tsk104
  1176.         ;結(jié)果為0 關(guān)閉電機(jī) 并返回
  1177.         anl p1,#11110000b         ;驅(qū)動電機(jī)       
  1178.         orl ready_byte,#11100001b           ;開啟其他任務(wù)
  1179.         jmp tsk100       

  1180. tsk104:         ;偏差如果大于180,電機(jī)方向反向  
  1181.         anl ready_byte,#11011111b         ;暫停鍵盤線程
  1182.         clr c
  1183.         mov a,r0
  1184.         subb a,#180
  1185.         mov b,a
  1186.         mov a,r1
  1187.         subb a,#0   ;16位減去180

  1188.         jc tsk101 ;  
  1189.         cpl moto_dir

  1190. tsk101:                
  1191.         call moto_move

  1192. ;實(shí)際位置指針調(diào)整一位
  1193.         jb moto_dir,tsk102
  1194.         clr c
  1195.         mov a,p_moto_L
  1196.         addc a,#1
  1197.         mov p_moto_L,a

  1198.         clr a
  1199.         addc a,p_moto_H
  1200.         mov p_moto_H,a

  1201.         cjne a,#01h,tsk100 ;如果等于360則歸零
  1202.         mov a,p_moto_L
  1203.         cjne a,#68h,tsk100
  1204.         mov p_moto_L,#0
  1205.         mov p_moto_H,#0
  1206.         jmp tsk100

  1207. tsk102:
  1208.         clr c
  1209.         mov a,p_moto_L
  1210.         subb a,#1
  1211.         mov p_moto_L,a
  1212.         mov a,p_moto_H
  1213.         subb a,#0
  1214.         mov p_moto_H,a

  1215.         cpl a  ;如果等于ffff,則改為359
  1216.         jnz tsk100
  1217.         mov a,p_moto_L
  1218.         cpl a
  1219.         jnz tsk100

  1220.         mov p_moto_L,#67h
  1221.         mov p_moto_H,#01h

  1222. jmp tsk100


  1223. jmp task_1
  1224. ;----------task1 end-------}}}}--

  1225. ;任務(wù)1子程序:電機(jī)走一度,方向在moto_dir,
  1226. ;涉及2張表:表1,45度折合512拍表,tab_deg,p_deg(0-44),  表2,8拍表tab_step,p_step(0-7)
  1227. ;步驟:1根據(jù)方向調(diào)整度數(shù)指針,取一個度數(shù)拍數(shù) 2根據(jù)方向走N拍并更新拍數(shù)指針;
  1228. moto_move:
  1229.         mov c,moto_dir ;防止過程中改變方向
  1230.         mov tmp_dir,c

  1231. mmv00:
  1232.         jb tmp_dir,mmv01 ;正方向
  1233.         dec p_deg
  1234.         mov a,p_deg
  1235.         cpl a
  1236.         jnz mmv03 ;過0則回到44
  1237.         mov p_deg,#44
  1238. mmv03:
  1239.         mov a,p_deg
  1240.         mov dptr,#tab_deg
  1241.         movc a,@a+dptr
  1242.         jmp mmv02
  1243. mmv01:
  1244.         inc p_deg        ;反方向
  1245.         mov a,p_deg
  1246.         cjne a,#45,mmv04 ;過44則回到0
  1247.         mov p_deg,#0
  1248. mmv04:
  1249.         mov a,p_deg
  1250.         mov dptr,#tab_deg
  1251.         movc a,@a+dptr
  1252. mmv02:
  1253.         mov r3,a
  1254.         call move_n_step

  1255. ret


  1256. move_n_step:;方向在tmp_dir,步數(shù)在r3,  表2,8拍表tab_step,p_step(0-7)

  1257. mns00:
  1258.         jb tmp_dir,msn01 ;正方向
  1259.         dec p_step
  1260.         mov a,p_step
  1261.         cpl a
  1262.         jnz msn03 ;過0則回到7
  1263.         mov p_step,#7
  1264. msn03:
  1265.         mov a,p_step
  1266.         mov dptr,#tab_step
  1267.         MOVC A,@a+dptr
  1268.         jmp msn02
  1269. msn01:
  1270.         inc p_step        ;反方向
  1271.         mov a,p_step
  1272.         cjne a,#8,msn04 ;過7則回到0
  1273.         mov p_step,#0
  1274. msn04:
  1275.         mov a,p_step
  1276.         mov dptr,#tab_step
  1277.         MOVC A,@a+dptr
  1278.        
  1279. msn02:       
  1280.         anl p1,#11110000b         ;驅(qū)動電機(jī)
  1281.         orl p1,a

  1282.         ;至此,電機(jī)走動了一拍,下面是延時(shí):需要2ms,采用不可重入的delay_sys_us完成
  1283.         ;CALL delay_a_step
  1284.         ;call waiting
  1285.         ;mov dptr,#10
  1286.         ;call delay_sys
  1287.        
  1288.         mov r0,#3
  1289.         call delay_sys_us
  1290.        
  1291.         djnz r3,mns00 ;走r3步數(shù)
  1292. ret

  1293. tab_step: ;步進(jìn)電機(jī)8拍表,循環(huán)使用
  1294.         DB 1001B,0001B,0011B,0010B,0110B,0100B,1100B,1000B

  1295. tab_deg: ;45度折合512拍表,循環(huán)使用
  1296.         db 11,11,12,11,11,12,11,11,12,11,12,12,11,11,12
  1297.         db 11,11,12,11,11,12,11,11,12,11,11,12,11,11,12
  1298.         db 11,11,12,12,11,12,11,11,12,11,11,12,11,11,12



  1299. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1300. ;                            ;
  1301. ;;;;;任務(wù)2:按鍵掃描         ;
  1302. ;;標(biāo)準(zhǔn)的4*4按鍵掃描程序,鍵值為0-fh,設(shè)置3個字節(jié)的緩沖pool_key,設(shè)置一個緩沖指針p_key(0-3),當(dāng)緩沖區(qū)滿,丟棄新的按鍵
  1303. task_2:  
  1304. ;初始化
  1305.         mov p_key,#0        ;0表示緩沖區(qū)空,3表示滿了,類似堆棧指針,注意定義時(shí)往前推一格

  1306. scan_key:
  1307.         mov a,p_key
  1308.         cjne a,#3,tk301  ;緩沖區(qū)滿了
  1309.         jmp scan_key
  1310. tk301:
  1311.         anl p0,#00001111b           ;p0.7 p0.6 p0.5 p0.4 為豎線 從左到右
  1312.         mov a,p2                ;p2.4 p2.5 p2.6 p2.7 為橫線 從上到下
  1313.         orl a,#00001111b

  1314.         cpl a
  1315.         jz scan_key  ;快速判斷,無任何按鍵時(shí)不要去挨個掃了,這樣響應(yīng)更快

  1316.         mov r3,#4          ;豎線循環(huán)4次
  1317.         mov a,#01111111b
  1318. tk300:
  1319.         orl p0,#11110000b
  1320.         anl p0,a
  1321.         mov r2,a ;暫存

  1322.         jb p2.4,tk303
  1323.         mov r0,#0
  1324.         call take_keyv

  1325. tk303:       
  1326.         jb p2.5,tk304
  1327.         mov r0,#1
  1328.         call take_keyv

  1329. tk304:       
  1330.         jb p2.6,tk305
  1331.         mov r0,#2
  1332.         call take_keyv

  1333. tk305:       
  1334.         jb p2.7,tk302
  1335.         mov r0,#3
  1336.         call take_keyv

  1337. tk302:       
  1338.         mov a,r2
  1339.         rr a         ;下一個豎線
  1340.         djnz r3,tk300

  1341.         jmp scan_key
  1342.        

  1343. jmp task_2
  1344. ;----------------------------task2 end---}}}}}-----
  1345. ;
  1346. tab_key16:          ;二位數(shù)組的4*4鍵值表
  1347. db 0ah,0bh,0ch,0dh
  1348. db 03,06,09,0fh
  1349. db 02,05,08,0
  1350. db 01,04,07,0eh

  1351. ;子程序:查表取鍵值,r3:行號,r0:列號
  1352. take_keyv:
  1353.         mov a,r3 ;1-4 轉(zhuǎn)為 0-3
  1354.         dec a
  1355.        
  1356.         mov dptr,#tab_key16
  1357.         mov b,#4
  1358.         mul ab ;調(diào)整基地址
  1359.         add a,dpl
  1360.         mov dpl,a
  1361.         clr a
  1362.         addc a,dph  ;進(jìn)位考慮
  1363.         mov dph,a

  1364.         mov a,r0
  1365.         movc a,@a+dptr ;查表取到對應(yīng)的鍵值 在b
  1366.         mov b,a

  1367.         ;存到緩沖池
  1368.         mov a,p_key
  1369.         cjne a,#3,tkv00  ;緩沖區(qū)滿了
  1370.         ret
  1371. tkv00:
  1372.        
  1373.         mov lock_byte,#5ah  ;;臨界區(qū),加鎖
  1374.         inc p_key
  1375.         mov a,p_key
  1376.         add a,#pool_key
  1377.         mov r1,a
  1378.         mov @r1,b        ;存緩沖
  1379.         mov lock_byte,#11h        ;;;;退臨界區(qū),解鎖

  1380.         mov a,#30
  1381.         add a,sys_time_l ;設(shè)置300ms時(shí)限
  1382.         mov r1,a
  1383. tkv01:                          ;按鍵釋放時(shí)立即返回,連續(xù)按住時(shí)要間隔延時(shí)

  1384.         ;超時(shí)退出
  1385.         mov a,sys_time_l  
  1386.         clr c
  1387.         subb a,r1
  1388.         clr c
  1389.         subb a,#5                ;時(shí)間模糊處理,只要接近目標(biāo)時(shí)間50ms以內(nèi),就算超時(shí),擔(dān)心有錯過時(shí)鐘刻度的考慮
  1390.         jc tkv02       

  1391.     ;anl p0,#00001111b           ;p0.7 p0.6 p0.5 p0.4 為豎線 改變p0可能會擾亂豎線掃描
  1392.         mov a,p2                ;p2.4 p2.5 p2.6 p2.7 為橫線 從上到下
  1393.         orl a,#00001111b         
  1394.         cpl a
  1395.         jnz tkv01  ;判斷是否釋放(范圍為本行)


  1396. tkv02:       
  1397. ret





  1398. ;;--------------任務(wù)3-----次任務(wù),注意保護(hù)范圍:psw\a\b\r0-r3  以及最大嵌套2個call       
  1399. ;;-----------------------------------任務(wù)3
  1400. ;;步進(jìn)電機(jī)每走一步的延時(shí)喚醒線程
  1401. task_3:       

  1402.         jmp task_3         
  1403. ;-----------------------------------------------task3 end--------------
  1404. ;

  1405. ;;-----------------------------------任務(wù)4
  1406. task_4:         
  1407.         jmp task_4         
  1408. ;-----------------------------------------------task5 end--------------
  1409. ;

  1410. ;;-----------------------------------任務(wù)5
  1411. task_5:
  1412.         jmp task_5

  1413. ;-----------------------------------------------task5 end--------------
  1414. ;


  1415. ;;-----------------------------------任務(wù)6
  1416. task_6:

  1417.         jmp task_6
  1418. ;-----------------------------------------------task6 end--------------
  1419. ;




  1420. ;
  1421. ;--------------------------------------------------------------------------------------
  1422. ;以下用戶子程序區(qū)          
  1423. ;;ht1621b driver  RD WR DATA CS
  1424. ;/********************************************************
  1425. ;函數(shù)名稱:void Ht1621_Init(void)
  1426. ;功能描述: HT1621初始化

  1427. Ht1621_Init:

  1428. SETB CS_1621;
  1429. SETB WR_1621;
  1430. SETB DATA_1621;
  1431. MOV dptr,#5;
  1432. CALL delay_sys; // - - 延時(shí)使LCD工作電壓穩(wěn)定
  1433. MOV R1,#BIAS
  1434. CALL Ht1621WrCmd;
  1435. MOV R1,#RC256
  1436. CALL Ht1621WrCmd; // - - 使用內(nèi)部振蕩器
  1437. MOV R1,#SYSDIS
  1438. CALL Ht1621WrCmd; // - - 關(guān)振系統(tǒng)蕩器和LCD偏壓發(fā)生器
  1439. MOV R1,#WDTDIS
  1440. CALL Ht1621WrCmd;; // - - 禁止看門狗
  1441. MOV R1,#SYSEN; // - - 打開系統(tǒng)振蕩器
  1442. CALL Ht1621WrCmd;
  1443. MOV R1,#LCDON; // - - 打開聲音輸出
  1444. CALL Ht1621WrCmd;

  1445. ret

  1446. ;**寫數(shù)據(jù)到ht1621,數(shù)據(jù)存A,發(fā)送位數(shù)存R0*****************************************************/
  1447. Ht1621Wr_Data:;(uchar Data,uchar cnt) A:DATA R0:number of send-bit

  1448. CLR WR_1621;
  1449. CALL delay_a_while
  1450. RLC A;
  1451. MOV DATA_1621,C;
  1452. CALL delay_a_while
  1453. SETB WR_1621;
  1454. CALL delay_a_while
  1455. DJNZ R0,Ht1621Wr_Data

  1456. ret

  1457. ;****寫命令給HT1621****************************************************
  1458. Ht1621WrCmd: ;(uchar Cmd) cmd byte store in R1
  1459. CLR CS_1621
  1460. CALL delay_a_while
  1461. MOV A,#80H
  1462. MOV R0,#4
  1463. CALL Ht1621Wr_Data; // - - 寫入命令標(biāo)志1000
  1464. MOV A,R1
  1465. MOV R0,#8
  1466. CALL Ht1621Wr_Data; // - - 寫入命令數(shù)據(jù)
  1467. SETB CS_1621
  1468. CALL delay_a_while

  1469. RET

  1470. ;*******************************************************
  1471. ;函數(shù)名稱:void Ht1621WrOneData(uchar Addr,uchar Data)
  1472. ;功能描述: HT1621在指定地址寫入數(shù)據(jù)函數(shù)
  1473. ;全局變量:無
  1474. ;參數(shù)說明:Addr為寫入初始地址,Data為寫入數(shù)據(jù)
  1475. ;返回說明:無
  1476. ;說 明:因?yàn)镠T1621的數(shù)據(jù)位4位,所以實(shí)際寫入數(shù)據(jù)為參數(shù)的后4位
  1477. ;********************************************************/
  1478. Ht1621WrOneData:;(uchar Addr,uchar Data)        R2,R3

  1479. CLR CS_1621;
  1480. MOV A,#0A0H
  1481. MOV R0,#3
  1482. CALL Ht1621Wr_Data;(0xa0,3); // - - 寫入數(shù)據(jù)標(biāo)志101
  1483. MOV A,R2
  1484. RLC A
  1485. RLC A
  1486. MOV R0,#6
  1487. CALL Ht1621Wr_Data;(Addr<<2,6); // - - 寫入地址數(shù)據(jù)

  1488. MOV A,R3
  1489. RLC A
  1490. RLC A
  1491. RLC A
  1492. RLC A
  1493. MOV R0,#4
  1494. CALL Ht1621Wr_Data;(Data<<4,4); // - - 寫入數(shù)據(jù)
  1495. SETB CS_1621
  1496. CALL delay_a_while

  1497. RET

  1498. ;*********函數(shù)名稱:void Ht1621WrAllData(uchar Addr,uchar *p,uchar cnt)
  1499. ;功能描述: HT1621連續(xù)寫入方式函數(shù)
  1500. ;參數(shù)說明:Addr為寫入初始地址,*p為連續(xù)寫入數(shù)據(jù)指針,
  1501. ;cnt為寫入數(shù)據(jù)總數(shù)
  1502. ;返回說明:無
  1503. ;說 明:HT1621的數(shù)據(jù)位4位,此處每次數(shù)據(jù)為8位,寫入數(shù)據(jù)
  1504. ;總數(shù)按8位計(jì)算
  1505. ;********************************************************/
  1506. Ht1621WrAllData:;(uchar Addr,uchar *p,uchar cnt)  R3:ADDR DPTR:P R5:CNT

  1507. CLR CS_1621;
  1508. MOV A,#0A0H
  1509. MOV R0,#3
  1510. CALL Ht1621Wr_Data ;(0xa0,3); // - - 寫入數(shù)據(jù)標(biāo)志101
  1511. MOV A,R3
  1512. RLC A
  1513. RLC A ;Ht1621Wr_Data(Addr<<2,6); // - - 寫入地址數(shù)據(jù)
  1514. MOV R0,#6
  1515. CALL Ht1621Wr_Data

  1516. hwd00:
  1517. CLR A
  1518. MOVC A,@A+DPTR
  1519. MOV R0,#8
  1520. CALL Ht1621Wr_Data         ;Ht1621Wr_Data(*p,8); // - - 寫入數(shù)據(jù)
  1521. INC DPTR
  1522. DJNZ R5,hwd00       

  1523. SETB CS_1621
  1524. CALL delay_a_while

  1525. RET

  1526. ;;;----------------

  1527. delay_a_while:
  1528.         mov r3,#50
  1529. daw00:
  1530.         nop
  1531.         djnz r3,daw00
  1532. ret

  1533. delay_a_step:
  1534.         mov r6,#0ffh
  1535. dast00:
  1536.         nop
  1537.         nop
  1538.         nop
  1539.         djnz r6,dast00
  1540. ret
  1541. ;---------------------------------------以下用戶數(shù)據(jù)表區(qū)------------------
  1542. ;用戶可以在此定義所需要的數(shù)據(jù)表



  1543. end
  1544. ;*******************************************the end**********************
復(fù)制代碼



評分

參與人數(shù) 1黑幣 +5 收起 理由
zhangli019 + 5 共享資料的黑幣獎勵!

查看全部評分

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

相關(guān)帖子

回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:160895 發(fā)表于 2017-1-9 10:38 | 只看該作者
巨大的篇幅,看完不容易。
回復(fù)

使用道具 舉報(bào)

板凳
ID:160895 發(fā)表于 2017-1-9 16:21 | 只看該作者
長篇大論,還不錯,
回復(fù)

使用道具 舉報(bào)

地板
ID:346965 發(fā)表于 2018-6-8 09:49 | 只看該作者
匯編果然看不懂
回復(fù)

使用道具 舉報(bào)

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

本版積分規(guī)則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表
主站蜘蛛池模板: 国产精品久久久久久久久久久久 | 国产九九九 | 亚洲成人一区 | 久久av一区| 中文二区 | 日本不卡免费新一二三区 | 午夜www | 精品国产免费一区二区三区演员表 | 亚洲一区在线播放 | 国产高清在线精品 | 日韩在线视频免费观看 | 久久精品网| 国产1区2区3区| 高清不卡毛片 | 综合色站导航 | 欧美国产精品久久久 | 精品欧美| 精品免费 | 福利视频一区二区 | 国产精品成人一区二区 | 一区二区不卡 | 午夜在线视频一区二区三区 | 亚洲精品一区二区在线观看 | 亚洲三级在线观看 | 毛片久久久| 国产高清精品在线 | 男女搞网站 | 丝袜 亚洲 另类 欧美 综合 | 午夜影院 | 成人在线视频免费看 | 国产精品色婷婷久久58 | 亚洲精品一区中文字幕乱码 | 日韩精品一区二区三区在线观看 | 国产欧美一区二区三区久久人妖 | 亚洲一区 中文字幕 | 久久久久久成人 | 国产日韩精品视频 | 夜夜草天天草 | 在线观看亚洲专区 | 黄网站在线播放 | 中文字幕在线看 |