




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
第4章匯編語言程序設計實訓4信號燈的控制24.1概述4.2簡單程序設計4.3分支程序設計4.4循環程序設計4.5查表程序4.6子程序設計與堆棧技術本章小結習題4實訓4信號燈的控制2
1.實訓目的
(1)掌握匯編語言程序的基本結構。
(2)了解匯編語言程序設計的基本方法和思路。
2.實訓設備與器件
(1)實訓設備:單片機開發系統、微機等。
(2)實訓器件與電路:參見實訓1電路圖。
3.實訓步驟與要求
(1)運行程序1,觀察8個發光二極管的亮滅狀態。
(2)在單片機開發調試環境中,將內部RAM的20H單元內容修改為00H,運行程序2,觀察8個發光二極管的亮滅狀態;重新將內部RAM的20H單元內容修改為80H,再次運行程序2,觀察8個發光二極管的亮滅狀態。
(3)運行程序3,觀察8個發光二極管的亮滅狀態。程序1:所有發光二極管不停地閃動。
ORG
0000H ;程序從地址0000H開始存放
START:MOV
P1,#00H ;把立即數00H送P1口,點亮;所有發光二極管
ACALL
DELAY ;調用延時子程序
MOV
P1,#0FFH ;滅掉所有發光二極管
ACALL DELAY ;調用延時子程序
AJMP START ;重復閃動
DELAY:MOV R3,#7FH ;延時子程序
DEL2:MOV R4,#0FFH
DEL1:NOP
DJNZ R4,DEL1
DJNZ R3,DEL2
RET
END ;匯編程序結束程序2:用位狀態控制發光二極管的顯示方式。
ORG 0000H
MOV A,20H ;A←(20H),20H單元的內容;傳送到累加器A
RLC A ;累加器A的內容帶CY循環左移,;CY←ACC.7
JC NEXT ;判斷CY是否為1,若是,跳轉;到NEXT執行
MOV P1,#00H;否則,CY=0,點亮所有發光;二極管
SJMP $
NEXT: MOV P1,#55H;CY=1,發光二極管交替亮滅
SJMP $ END程序3:使8個發光二極管順序點亮。
ORG 0000H
START:MOVR2,#08H ;設置循環次數
MOV A,#0FEH ;送顯示模式字
NEXT:MOV
P1,A;點亮連接P1.0的發光二極管
ACALL
DELAY
RL A ;左移一位,改變顯示模式字
DJNZ
R2,NEXT;循環次數減1,若不為零,則繼續;點亮下面一個二極管
SJMP START
DELAY:MOV R3,#0FFH;延時子程序開始
DEL2:MOV R4,#0FFH
DEL1:NOP
DJNZ R4,DEL1
DJNZ R3,DEL2
RET
END
4.實訓分析與總結
(1)程序1的運行結果是:8個發光二極管同時閃動。該程序的運行過程用流程圖表示如圖4.1所示。程序1的執行過程是按照指令的排列順序逐條執行的。這種按照指令的排列順序逐條執行的程序結構稱為順序結構程序。
(2)程序2的運行結果是:若內部RAM20H單元的內容為00H,則8個發光二極管全部處于點亮狀態;若內部RAM20H單元的內容為80H,則8個發光二極管處于“亮滅亮滅亮滅亮滅”狀態。程序2的流程圖如圖4.2所示。圖4.1程序1流程圖圖4.2程序2流程圖程序2的特點是:程序不按照指令的排列順序執行,而是根據20H單元中的數據的第7位的狀態,分別執行不同的內容,即程序有兩個分支,執行時根據給定的條件選擇其中一個分支。這樣的程序結構稱為分支結構程序。分支結構程序的關鍵問題是如何根據條件選擇正確的分支。
(3)程序3的運行結果是:順序點亮8個發光二極管。該程序的流程圖如圖4.3所示。程序3的特點是:“點亮—延時—移位”這一程序段重復執行了8次。重復執行某一程序段的程序結構稱為循環結構程序。該程序的設計過程見例4.6。關于循環程序結構的詳細介紹參見4.4節。圖4.3程序3流程圖
(4)在程序1和程序3中都使用了一段相同的延時子程序DELAY,這種供其它程序反復使用的程序或程序段稱為子程序。關于子程序的詳細介紹參見4.6節。
5.思考
(1)在程序1和程序3中,如果去掉程序中的ACALLDELAY指令,程序運行結果是否有變化,為什么?如果想改變8個發光二極管的閃動或點亮速度,如何修改程序?
(2)在程序2中,判斷累加器A中數據最高位是否為1的方法有很多,試看下面的指令是否能夠實現。①?JBACC.7NEXT②?MOVC,ACC.7
JCNEXT4.1概述機器語言(MachineLanguage)是指直接用機器碼編寫程序、能夠為計算機直接執行的機器級語言。機器碼是一串由二進制代碼“0”和“1”組成的二進制數據,其執行速度快,但是可讀性極差。機器語言一般只在簡單的開發裝置中使用,程序的設計、輸入、修改和調試都很麻煩。在實訓1和實訓3中直接固化或輸入的程序都是機器語言程序。匯編語言(AssemblyLanguage)是指用指令助記符代替機器碼的編程語言。匯編語言程序結構簡單,執行速度快,程序易優化,編譯后占用存儲空間小,是單片機應用系統開發中最常用的程序設計語言。匯編語言的缺點是可讀性比較差,只有熟悉單片機指令系統并具有一定的程序設計經驗的人員,才能研制出功能復雜的應用程序。實訓4中的3個程序都是用匯編語言編寫的。高級語言(High-LevelLanguage)是在匯編語言的基礎上用自然語言的語句來編寫程序的,例如PL/M-51、FranklinC51、MBASIC51等。使用高級語言編寫的程序可讀性強,通用性好,適用于不熟悉單片機指令系統的的用戶。用高級語言編寫程序的缺點是實時性不高,結構不緊湊,編譯后占用存儲空間比較大,這一點在存儲器有限的單片機應用系統中沒有優勢。單片機匯編語言程序設計的基本步驟如下:
(1)題意分析。熟悉并了解匯編語言指令的基本格式和主要特點,明確被控對象對軟件的要求,設計出算法等。
(2)畫出程序流程圖。編寫較復雜的程序時,畫出程序流程圖是十分必要的。程序流程圖也稱為程序框圖,是根據控制流程設計的,它可以使程序清晰,結構合理,便于調試。在實訓4中,我們給出了3個實訓程序的流程圖。
(3)分配內存工作區及有關端口地址。分配內存工作區時,要根據程序區、數據區、暫存區、堆棧區等預計所占空間大小,對片內外存儲區進行合理分配并確定每個區域的首地址,便于編程使用。
(4)編制匯編源程序。
(5)仿真、調試程序。
(6)固化程序。4.2簡單程序設計
例4.14字節(雙字)加法。將內部RAM中從30H開始的4個單元中存放的4字節十六進制數和內部RAM中從40H單元開始的4個單元中存放的4字節十六進制數相加,結果存放到以40H開始的單元中。
(1)題意分析。題目的要求如圖4.4所示。圖4.4例4.1題意分析示意圖
(2)匯編語言源程序。按照雙字節加法的思路,編寫實現4字節加法的源程序如下:
ORG
0000H MOV
A,30H ADD
A,40H MOV
40H,A ;最低字節加法并送結果
MOV
A,31H ADDC
A,41H MOV
41H,A ;第二字節加法并送結果
MOV
A,32H ADDC
A,42H
MOV
42H,A ;第三字節加法并送結果 MOV
A,33H ADDC
A,43H MOV
43H,A ;第四字節加法并送結果,進位;位在CY中
END顯然,上面程序中每一步加法的步驟很相似,因此我們可以采用循環的方法來編程,使得源程序更加簡潔,結構更加緊湊。用循環方法編制的源程序見習題4.3題。
例4.2
數據拼拆程序。將內部RAM30H單元中存放的BCD碼十進制數拆開并變成相應的ASCII碼,分別存放到31H和32H單元中。
(1)題意分析。題目要求如圖4.5所示。本題中,首先必須將兩個數拆開,然后再拼裝成兩個ASCII碼。數字與ASCII碼之間的關系是:高4位為0011H,低4位即為該數字的8421碼。圖4.5例4.2題意分析示意圖
(2)匯編語言源程序如下:
ORG 0000H
MOV R0,#30H
MOV A,#30H
XCHD A,@R0 ;A的低4位與30H單元的低4位交換
MOV 32H,A ;A中的數值為低位的ASCII碼
MOV A,@R0
SWAP A ;將高位數據換到低位
ANL A,#0FHORL A,#30H ;與30H拼裝成ASCII碼
MOV 31H,AEND4.3分支程序設計4.3.1分支程序實例
1.兩分支程序設計
例4.3
兩個無符號數的比較(兩分支)。內部RAM的20H單元和30H單元各存放了一個8位無符號數,請比較這兩個數的大小,并將比較結果顯示在實訓的實驗板上:若(20H)≥(30H),則P1.0管腳連接的LED發光;若(20H)<(30H),則P1.1管腳連接的LED發光。
(1)題意分析。本例是典型的分支程序,根據兩個無符號數的比較結果(判斷條件),程序可以選擇兩個流向之中的某一個,分別點亮相應的LED。比較兩個無符號數常用的方法是將兩個數相減,然后判斷有否借位CY。若CY=0,無借位,則X≥Y;若CY=1,有借位,則X<Y。程序的流程圖如圖4.6所示。圖4.6兩數比較流程圖
(2)匯編語言源程序如下:
X
DATA 20H;數據地址賦值偽指令DATA
Y
DATA 30H
ORG
0000H
MOV
A,X ;(X)→A
CLR
C ;CY=0
SUBBA,Y ;帶借位減法,A-(Y)-CY→A
JC
L1 ;CY=1,轉移到
L1
CLR
P1.0 ;CY=0,(20H)≥(30H),點亮;P1.0連接的LED
SJMP
FINISH ;直接跳轉到結束等待
L1:CLR
P1.1 ;(20H)<(30H),點亮P1.1連接的LED
FINISH:SJMP $ END
(3)執行結果。執行該程序之前,利用單片機開發系統先往內部RAM的20H和30H單元存放兩個無符號數(可以任意設定),執行后觀察點亮的LED是否和存放的數據大小相一致。
2.三分支程序設計例4.4兩個有符號數的比較(三分支程序)。內部RAM的20H單元和30H單元各存放了一個8位有符號數,請比較這兩個數的大小,并將比較結果顯示在實訓實驗板上:若(20H)=(30H),則P1.0管腳連接的LED發光;若(20H)>(30H),則P1.1管腳連接的LED發光;若(20H)<(30H),則P1.2管腳連接的LED發光。
(1)題意分析。有符號數在計算機中的表示方式與無符號數是不相同的:正數以原碼形式表示,負數以補碼形式表示,8位二進制數的補碼所能表示的數值范圍為+127~-128。計算機本身無法區分一串二進制碼組成的數字是有符號數或無符號數,也無法區分它是程序指令還是一個數據。編程員必須對程序中出現的每一個數據的含義非常清楚,并按此選擇相應的操作。例如,數據FEH看做無符號數時其值為254,看做有符號數時為-2。比較兩個有符號數X和Y的大小要比比較無符號數麻煩得多。這里提供一種比較思路:先判別兩個有符號數X和Y的符號,如果X、Y兩數符號相反,則非負數大;如果X、Y兩數符號相同,將兩數相減,然后根據借位標志CY進行判斷。這一比較過程如圖4.7所示。圖4.7比較兩個有符號數X、Y的流程圖 X DATA20H Y DATA30H ORG 0000H MOV A,X XRL A,Y ;(X)與(Y)進行異或操作
JB ACC.7,NEXT1 ;累加器A的第7位為1,兩數符號不;同,轉移到NEXT1 MOV A,X CJNE A,Y,NEQUAL;(X)≠(Y),轉移到NEQUAL CLR P1.0 ;(X)=(Y),點亮P1.0連接的LED SJMP FINISH
NEQUAL: JC XXY ;(X)<(Y),轉移到XXY
SJMP XDY ;否則,(X)>(Y),轉移到XDY
NEXT1: MOV A,X
JNB ACC.7,XDY ;判斷(X)的最高位D7,;以確定其正負
XXY: CLR P1.2 ;(X)<(Y),點亮P1.2連接的LED
SJMP FINISH
XDY: CLR P1.1 ;(X)>(Y),點亮P1.1連接的LED
FINISH: SJMP $
END
(3)程序說明。①判斷兩個有符號數符號異同的方法。本例中使用邏輯異或指令,將(X)與(Y)進行異或操作,那么,(X)的符號位(X)7與(Y)的符號位(Y)7異或的結果如下:若(X)7與(Y)7相同,則(X)7
(Y)7=0;若(X)7與(Y)7不相同,則(X)7
(Y)7=1。本例中,(X)與(Y)的異或結果存放在累加器A中,因此判斷ACC.7是否為零即可知道兩個數的符號相同與否。②比較兩個有符號數的其它方法。除了本例中使用的比較兩個有符號數的方法之外,我們還可以利用溢出標志OV的狀態來判斷兩個有符號數的大小。具體算法如下:若X-Y為正數,則OV=0時X>Y;OV=1時X<Y。若X-Y為負數,則OV=0時X<Y;OV=1時X>Y。采用這種比較方式的匯編語言源程序見習題4.10。
3.散轉程序散轉程序是指經過某個條件判斷之后,程序有多個流向(三個以上)。在后面的鍵盤接口程序設計中經常會用到散轉功能——根據不同的鍵碼跳轉到相應的程序段。
例4.5
設計兩個開關,使CPU可以察知兩個開關組合出的4種不同狀態。然后對應每種狀態,使8個LED顯示出不同的亮滅模式。
(1)硬件設計。在實訓1的電路中,我們使用單片機的并行口P1的輸出功能來控制8個LED的顯示。現在我們使用其P3口的輸入功能來設計兩個輸入開關,硬件原理圖如圖4.8所示。如圖4.8所示,當開關S0接通2時,P3.4管腳接地,P3.4=0;當S0接通1時,P3.4接+5V,P3.4=1。同樣,當開關S1接通2時,P3.5管腳接地,P3.5=0;當S1接通1時,P3.5接+5V,P3.5=1。假設要求P3口的開關狀態對應的P1口的8個LED的顯示方式如下:
P3.5 P3.4 顯示方式
0 0 全亮
0 1交叉亮
1 0 低4位連接的燈滅,高4位亮
1 1 低4位連接的燈亮,高4位滅圖4.8在實訓1原理圖基礎之上的例4.5硬件原理圖
(2)軟件設計。①程序設計思想。散轉程序的特點是利用散轉指令實現向各分支程序的轉移,其程序流程圖如圖4.9所示。②匯編語言源程序。
ORG 0000H MOV P3,#00110000B;使P3口鎖存器相應位置位
MOV A,P3 ;讀P3口相應引腳線信號
ANL A,#00110000B ;“邏輯與”操作,屏蔽掉無關位
SWAP A ;將相應位移位到低位
RL A ;循環左移一位,A×2→A
MOV DPTR,#TABLE;將轉移指令表的基地址送數據;指針DPTR JMP @A+DPTR ;散轉指令ONE: MOV P1,#00H;第一種顯示方式,S0接地,S1接地
SJMP$TWO: MOV P1,#55H;第二種顯示方式,S0接+5V,S1接地
SJMP $THREE:MOV P1,#0FH;第三種顯示方式,S0接地,S1接+5V SJMP $FOUR:MOV P1,#0F0H;第四種顯示方式,S0接+5V,S1接地
SJMP $TABLE:AJMP ONE ;轉移指令表
AJMP TWO AJMP THREE AJMP FOUR END圖4.9散轉程序流程圖
(3)程序說明。①讀P3口的管腳狀態。MCS-51的4個I/O端口共有三種操作方式:輸出數據方式、讀端口數據方式和讀端口引腳方式。輸出數據方式舉例:
MOVP1,#00H;輸出數據00H→P1端口鎖存器→P1引腳
讀端口數據方式舉例:
MOVA,P3 ;A←P3端口鎖存器
讀端口引腳方式舉例:
MOVP3,#0FFH ;P3端口鎖存器各位置1
MOVA,P3 ;A←P3端口引腳狀態②散轉指令。散轉指令是單片機指令系統中專為散轉操作提供的無條件轉移指令,其指令格式如下:
JMP @A+DPTR ;PC←DPTR+A一般情況下,數據指針DPTR固定,根據累加器A的內容,程序轉入相應的分支程序中去。本例采用最常用的轉移指令表法,就是先用無條件轉移指令按一定的順序組成一個轉移表,再將轉移表首地址裝入數據指針DPTR中,然后將控制轉移方向的數值裝入累加器A中作變址,最后執行散轉指令,實現散轉。指令轉移表的存儲格式如圖4.10所示。圖4.10指令轉移表的存儲格式由于無條件轉移指令AJMP是兩字節指令,因此控制轉移方向的A中的數值為
A=0轉向 AJMP ONE
A=2轉向 AJMP TWO
A=4轉向 AJMP THREE
A=6轉向 AJMPFOUR程序中,從P3口讀入的數據分別為0、1、2、3,因此必須乘以2來修正A的值。如果A=2,散轉過程如下:
JMP @A+DPTR→PC=TABLE+2→AJMPTWO③三種無條件轉移指令LJMP、AJMP和SJMP的比較。三種無條件轉移指令在應用上的區別有以下三點:一是轉移距離不同。LJMP可在64KB范圍內轉移,AJMP指令可以在本指令取出后的2KB范圍內轉移,SJMP可在以本指令為核心的-126~+129B范圍內轉移;二是匯編后機器碼的字節數不同。LJMP是三字節指令,AJMP和SJMP都是兩字節指令。三是LJMP和AJMP都是絕對轉移指令,可以直接得到轉移目的地址,而SJMP是相對轉移指令,只能通過轉移偏移量來得到轉移目的地址。選擇無條件轉移指令的原則是根據跳轉的遠近,盡可能選擇占用字節數少的指令。例如,動態暫停指令一般都選用SJMP$,而不用LJMP$。4.3.2分支程序結構
1.單分支結構程序的形式單分支結構在程序設計中應用最廣,擁有的指令也最多。單分支結構一般為:一個入口,兩個出口。如圖4.11所示,單分支結構程序有以下兩種典型形式:圖4.11(a)表示當條件滿足時執行分支程序1,否則執行分支程序2,例4.3就是這樣的一種結構。圖4.11(b)表示當條件滿足時跳過程序段2,從程序段3往下執行,否則順序執行程序段2和3。圖4.11單分支結構程序的典型形式
2.散轉程序在實際程序中,常常需要從兩個以上的出口中選一個,稱為多分支程序或散轉程序。MCS-51單片機指令系統中專門提供了散轉指令,使得散轉程序的編制更加簡潔。例4.5中采用轉移指令表法實現散轉程序。轉移表是由雙字節短轉移指令“AJMP”組成的,各轉移指令地址依次相差兩個字節,所以累加器A中的變址值必須作乘2修正。若轉移表是由三字節長轉移指令“LJMP”組成的,則累加器A中的變址值必須乘3。當修正值有進位時,則應將進位先加在數據指針高位字節(DPH)上。此外,轉移表中使用了“AJMP”指令,這就限制了轉移的入口地址ONE、TWO、THREE、FOUR必須和散轉表首地址TABLE位于同一個2KB范圍內。為了克服上述局限性,除了可以使用“LJMP”指令組成跳轉表外,還可采用雙字節的寄存器存放散轉值,并利用對DPTR進行加法運算的方法,直接修改DPTR,然后再用散轉指令實現散轉。散轉程序除了轉移指令表法之外,還可以采用地址偏移量表法、轉向地址表法及利用“RET”指令(子程序返回指令)等實現散轉程序,具體實現參見習題4.11題。
3.轉移條件的形成分支程序中的轉移條件一般都是程序狀態字(PSW)中標志位的狀態,因此,保證分支程序正確流向的關鍵如下:
(1)在判斷之前,應執行對有關標志位有影響的指令,使該標志位能夠適應問題的要求,這就要求編程員要十分了解指令對標志位的影響情況。
(2)當某一標志位處于某一狀態時,在未執行下一條影響此標志位的指令前,它一直保持原狀態不變。
(3)正確理解PSW中各標志位的含義及變化情況,才能正確地判斷轉移。4.4循環程序設計4.4.1循環程序實例
1.單重循環程序設計例4.6實訓4的程序3設計。按照從P1.0到P1.7的順序,依次點亮P1口連接的LED。
(1)題意分析。這種顯示方式是一種動態顯示方式,逐一點亮一個燈,使人們感覺到點亮燈的位置在移動。根據點亮燈的位置,我們要向P1口依次送入如下的立即數:
FEH——點亮P1.0連接的LED MOV P1,#0FEH FDH——點亮P1.1連接的LED MOV P1,#0FDH
FBH——點亮P1.2連接的LED
MOV
P1,#0FBH
7FH——點亮P1.7連接的LED
MOV
P1,#7FH以上完全重復地執行往P1口傳送立即數的操作,會使程序結構松散。我們看到,控制LED點亮的立即數0FEH、0FDH、0FBH…7FH之間存在著每次左移一位的規律,因此我們可以試用循環程序來實現。初步設想的程序流程圖如圖4.12所示。用匯編語言實現的程序如下:
ORG 0000H
START:MOV R2,#08H ;設置循環次數
MOV A,#0FEH ;從P1.0→P1.7使LED逐;個亮過去
NEXT: MOV P1,A ;點亮LED
RL A ;左移一位
DJNZ R2,NEXT ;次數減1,不為零,繼;續點亮下一個LED
SJMP START ;反復點亮
END圖4.12例4.6初步設想的程序流程圖
(2)匯編語言源程序。本例完整的匯編語言源程序見實訓4中的程序3。由于程序設計中經常會出現如圖4.13所示的次數控制循環程序結構,為了編程方便,單片機指令系統中專門提供了循環指令DJNZ,以適用于上述結構的編程:
DJNZ R2,NEXT ;R2中存放控制次數,R2-1→R2。若;R2≠0,則轉移到NEXT繼續循環,;否則執行下面的指令圖4.13常見循環程序結構
2.雙重循環程序設計——延時程序設計在上例中使用了延時程序段之后,我們才能看到正確的顯示結果。延時程序在單片機匯編語言程序設計中使用得非常廣泛。例如,鍵盤接口程序設計中的軟件消除抖動、動態LED顯示程序設計、LCD接口程序設計、串行通信接口程序設計等都運用了延時程序。所謂延時,就是讓CPU做一些與主程序功能無關的操作(例如將一個數字逐次減1直到0為止)來消耗掉CPU的時間。由于我們知道CPU執行每條指令的準確時間,因此執行整個延時程序的時間也可以精確計算出來。也就是說,我們可以寫出延時長度任意而且精度相當高的延時程序。
例4.7
設計一個延時1s的程序,設單片機時鐘晶振頻率為fosc=6MHz。
(1)題意分析。設計延時程序的關鍵是計算延時時間。延時程序一般采用循環程序結構編程,通過確定循環程序中的循環次數和循環程序段這兩個因素來確定延時時間。對于循環程序段來講,必須知道每一條指令的執行時間,這里涉及到幾個非常重要的概念——時鐘周期、機器周期和指令周期。時鐘周期T時鐘是計算機的基本時間單位,同單片機使用的晶振頻率有關。題目給定fosc=6MHz,那么T時鐘=1/fosc=1/6M=166.7ns。機器周期T機器是指CPU完成一個基本操作如取指操作、讀數據操作等所需要的時間。機器周期的計算方法:T機器=12T時鐘=166.7ns×12=2μs。指令周期是指執行一條指令所需要的時間。由于指令匯編后有單字節指令、雙字節指令和三字節指令,因此指令周期沒有確定值,一般為1~4個T機器。在附錄2的指令表中給出了每條指令所需的機器周期數,可以計算每一條指令的指令周期。現在,我們可以來計算一下實訓4的程序3中延時程序段的延時時間。延時程序段如下:
DELAY1:MOV R3,#0FFH
DEL2: MOV R4,#0FFH
DEL1: NOP
DJNZ R4,DEL1
DJNZ R3,DEL2經查指令表得到:指令MOVR4,#0FFH、NOP、DJNZ的執行時間分別為2μs、2μs和4μs。
NOP為空操作指令,其功能是取指、譯碼,然后不進行任何操作便進入下一條指令,經常用于產生一個機器周期的延遲。延時程序段為雙重循環,下面分別計算內循環和外循環的延時時間。
內循環的循環次數為255(0FFH)次,循環內容為以下兩條指令:
NOP ;2μs
DJNZ R4,DEL1 ;4μs所以內循環的延時時間為:255×(2+4)=1530μs。
外循環的循環次數為255(0FFH)次,循環內容如下:
MOV R4,#0FFH ;2μs
1530μs內循環 ;1530μs
DJNZR3,DEL2 ;4μs外循環循環一次的時間為1530μs+2μs+4μs=1536μs,循環255次,另外加上第一條指令
MOV R3,#0FFH ;2μs的循環時間2μs,因此外循環總的循環時間為
2?μs+(1530?μs+2?μs+4?μs)×255=391682?μs≈392ms以上是比較精確的計算方法,一般情況下,在外循環的計算中,經常忽略比較小的時間段,例如將上面的外循環計算公式簡化為
1530μs×255=390150μs≈390ms
與精確計算值相比,誤差為2ms,在要求不是十分精確的情況下,這個誤差是完全可以接受的。了解了延時時間的計算方法后,本例我們使用三重循環結構。程序流程圖如圖4.14所示。內循環選擇為1ms,第二層循環達到延時10ms(循環次數為10),第三層循環延時到1s(循環次數為100)。圖4.14延時1s的程序流程圖
(2)匯編語言源程序段。一般情況下,只把延時程序作為一個子程序段使用,不會獨立運行它,因為單純的延時沒有實際意義。源程序如下:
DELAY:MOV R0,#100 ;延時1s的循環次數
DEL2: MOV R1,#10 ;延時10ms的循環次數
DEL1: MOV R2,#7DH ;延時1ms的循環次數
DEL0: NOP NOP DJNZ R2,DEL0 DJNZ R1,DEL1 DJNZ R0,DEL2
(3)程序說明。本例中,第二層循環和外循環都采用了簡化計算方法,編程關鍵是延時1ms的內循環程序如何編制。首先確定循環程序段的內容如下:
NOP ;2μs
NOP ;2μs
DJNZ R2,DEL0 ;4μs內循環次數設為count,計算方法如下:(一次循環時間)×count=1ms從而得到count=1ms/(2μs+2μs+4μs)=125=7DH本例提供了一種延時程序的基本編制方法,若需要延時更長或更短時間,只要用同樣的方法采用更多重或更少重的循環即可。
3.數據傳送程序例4.8不同存儲區域之間的數據傳輸。將內部RAM中從30H單元開始的內容依次傳送到外部RAM中從0100H單元開始的區域,直到遇到傳送的內容是0為止。
(1)題意分析。本例要解決的關鍵問題是:數據塊的傳送和不同存儲區域之間的數據傳送。前者采用循環程序結構,以條件控制結束;后者采用間接尋址方式,以累加器A作為中間變量實現數據傳輸。程序流程圖如圖4.15所示。圖4.15例4.8程序流程圖
(2)匯編語言源程序。
ORG
0000H
MOV
R0,#30H;R0指向內部RAM數據區首地址
MOV
DPTR,#0100H;DPTR指向外部RAM數據區首;地址
TRANS:MOV
A,@R0 ;A←(R0)
MOVX @DPTR,A ;(DPTR)←A
CJNE A,#00H,NEXT
SJMP FINISH ;A=0,傳送完成
NEXT:INC R0 ;修改地址指針
INC DPTR
AJMP TRANS ;繼續傳送
FINISH:SJMP $
END
(3)程序說明。①間接尋址指令。在單片機指令系統中,對內部RAM讀/寫數據有兩種方式:直接尋址方式和間接尋址方式。例如:
直接方式:MOV
A,30H ;內部RAM(30H)→累加器A間接方式:MOV R0,#30H;30H→R0 MOV A,@R0;內部RAM(R0)→累加器A對外部RAM的讀/寫數據只有間接尋址方式,間接尋址寄存器有R0、R1(尋址范圍是00H~FFH)和DPTR(尋址范圍是0000H~FFFFH,即整個外部RAM區)。②不同存儲空間之間的數據傳輸。MCS-51系列單片機存儲器結構的特點之一是存在著4種物理存儲空間,即片內RAM、片外RAM、片內ROM和片外ROM。不同的物理存儲空間之間的數據傳送一般以累加器A作為數據傳輸的中心,如圖4.16所示。不同的存儲空間是獨立編址的,在傳送指令中的區別在于不同的指令助記符,例如:
MOV R0,#30H
MOV A,@R0 ;內部RAM(30H)→A
MOVX A,@R0 ;外部RAM(30H)→A圖4.16以累加器A為中心的不同存儲空間的數據傳送示意圖4.4.2循環程序結構
1.循環程序組成以上循環程序實例中,我們看到循環程序的特點是程序中含有可以重復執行的程序段。循環程序由以下4部分組成:
(1)初始化部分。程序在進入循環處理之前必須先設立初值,例如循環次數計數器、工作寄存器以及其它變量的初始值等,為進入循環做準備。
(2)循環體。循環體也稱為循環處理部分,是循環程序的核心。循環體用于處理實際的數據,是重復執行部分。
(3)循環控制。在重復執行循環體的過程中,不斷修改和判別循環變量,直到符合循環結束條件。一般情況下,循環控制有以下幾種方式:①計數循環——如果循環次數已知,則可以用計數器計數來控制循環次數,這種控制方式用得比較多。循環次數要在初始化部分預置,在控制部分修改,每循環一次,計數器內容減1。例4.6、例4.7都屬于計數循環控制方式。②條件控制循環——在循環次數未知的情況下,一般通過設立結束條件來控制循環的結束,例4.8就是用條件A=0來控制循環結束的。③開關量與邏輯尺控制循環——這種方法經常用在過程控制程序設計中,這里不再詳述。
(4)循環結束處理。這部分程序用于存放執行循環程序所得結果以及恢復各工作單元的初值等。
2.循環程序的基本結構循環程序通常有兩種基本結構:一種是先處理再判斷,另一種是先判斷后處理,如圖4.17所示。圖4.17循環程序的兩種基本結構(a)先執行后判斷;(b)先判斷后執行
3.多重循環結構程序有些復雜問題,必須采用多重循環的程序結構。循環程序中包含循環程序或一個大循環中包含多個小循環程序,這種結構稱為多重循環程序結構,又稱循環嵌套。多重循環程序必須注意的是各重循環不能交叉,不能從外循環跳入內循環。例4.7的延時程序就是一個典型的三重循環結構。
4.循環程序與分支程序的比較循環程序本質上是分支程序的一種特殊形式,凡是分支程序可以使用的轉移指令,循環程序一般都可以使用,并且由于循環程序在程序設計中的重要性,單片機指令系統還專門提供了循環控制指令,如DJNZ等。4.5查表程序在單片機匯編語言程序設計中,查表程序的應用非常廣泛,在LED顯示程序和鍵盤接口程序設計中都用到了查表程序段。
例4.9
在程序中定義一個0~9的平方表,利用查表指令找出累加器A=05H的平方值。
(1)題意分析。所謂表格是指在程序中定義的一串有序的常數,如平方表、字型碼表、鍵碼表等。因為程序一般都是固化在程序存儲器(通常是只讀存儲器)中,因此可以說表格是預先定義在程序的數據區中,然后和程序一起固化在ROM中的一串常數。查表程序的關鍵是表格的定義和如何實現查表。
(2)匯編語言源程序。
ORG
0000H
MOV
DPTR,#TABLE;表首地址→DPTR(數據指針)
MOV
A,#05 ;05→A
MOVCA,@A+DPTR;查表指令,25→A,A=19H
SJMP
$ ;程序暫停
TABLE:DB
0,1,4,9,16,25,36,49,64,81;定義0~9平方表
END
(3)程序說明。從程序存儲器中讀數據時,只能先讀到累加器A中,然后再送到題目要求的地方。單片機提供了兩條專門用于查表操作的查表指令:
MOVC A,@A+DPTR ;(A+DPTR)→A MOVC A,@A+PC ;PC+1→PC,(A+PC)→A其中,DPTR為數據指針,一般用于存放表首地址。用指令MOVCA,@A+PC實現查找平方表的源程序如下:
ORG
0000H
MOV
A,#05 ;05→A
ADD
A,#02 ;修正累加器A的值,修正值為查表指令;距離表首地址的字節數減去1
MOVC
A,@A+PC;25→A
SJMP
$
TABLE:DB0,1,4,9,16,25,36,49,64,81 ;定義0~9平方表
END4.6子程序設計與堆棧技術4.6.1子程序實例
例4.10
延時子程序。編程使P1口連接的8個LED按下列方式顯示:從P1.0連接的LED開始,每個LED閃爍10次,再移向下一個LED,讓其同樣閃爍10次,循環不止。
(1)題意分析。在前面的例子中,我們已經編了一些LED模擬霓虹燈的程序。按照題目要求畫出本例的程序流程圖如圖4.18所示。在圖4.18中,兩次使用延時程序段,因此我們把延時程序編成子程序。圖4.18例4.10程序流程圖
(2)匯編語言源程序。
ORG 0000H
MAIN:MOV A,#0FEH ;送顯示初值
LP:MOV R0,#10 ;送閃爍次數
LP0:MOV P1,A ;點亮LED
LCALL DELAY ;延時
MOV P1,#0FFH ;熄滅燈
LCALL DELAY ;延時
DJNZ R0,LP0 ;閃爍次數不夠10次,繼續
RL A ;否則A左移,下一個燈閃爍
SJMP LP ;循環不止
DELAY: MOV R3,#0FFH ;延時子程序
DEL2: MOV R4,#0FFH
DEL1: NOP DJNZ R4,DEL1 DJNZ R3,DEL2 RET
(3)程序說明。①子程序調用和返回過程。在本例中,MAIN為主程序,DELAY為延時子程序。當主程序MAIN需要延時功能時,就用一條調用指令ACALL(或LCALL)DELAY即可。子程序DELAY的編制方法與一般程序遵循的規則相同,同時也有它的特殊性。子程序的第一條語句必須有一個標號,如DELAY,代表該子程序第一個語句的地址,也稱為子程序入口地址,供主程序調用;子程序的最后一條語句必須是子程序返回指令RET。子程序一般緊接著主程序存放。例4.10的主程序和子程序在存儲器中的存儲格式如下:主程序:
地址 機器碼 指令
0005 12**LCALLDELAY ;第一次調用子程序
0008 ******
MOV
P1,#0FFH;LCALL指令的下一;條指令首址0008H;稱為斷點地址子程序:
0013 ******
MOV
R3,#0FFH;子程序開始
001C 22
RET ;子程序返回主程序兩次調用子程序及子程序返回過程如圖4.19所示。圖4.19子程序兩次被調用、返回過程示意圖子程序只需書寫一次,主程序可以反復調用它。CPU執行LCALL指令所進行的具體操作(以第一次調用為例)是:
(a)?PC的自動加1功能使PC=0008H,指向下一條指令MOVP1,#0FFH的首址,PC中即為斷點地址;
(b)保存PC中的斷點地址0008H;
(c)將子程序DELAY的入口地址0013H賦給PC,PC=0013H;
(d)程序轉向DELAY子程序運行。
CPU執行RET指令的具體操作(以第一次調用為例)是:
(a)取出執行調用指令時保存的斷點地址0008H,并將它賦給PC,PC=0008H;
(b)程序轉向斷點處繼續執行主程序。從以上分析來看,在子程序調用過程中,斷點地址0008H是自動保存和取出的,那么斷點地址究竟存放在什么地方呢?這里引出了一個新的存儲區域概念——堆棧,它是一個存放臨時數據(例如斷點地址)的內存區域。堆棧的巧妙設計使程序員不必操心數據的具體存放地址。②子程序嵌套。修改上面的程序,將一個燈的閃爍過程也編成子程序形式。修改后的源程序如下:
ORG 0000H
MAIN: MOV A,#0FEH ;送顯示初值
COUN: ACALL FLASH ;調閃爍子程序
RL A ;A左移,下一個燈閃爍
SJMP COUN;循環不止
FLASH: MOV R0,#10 ;送閃爍次數
FLASH1:MOV P1,A ;點亮LED LCALL DELAY ;延時
MOV P1,#0FFH ;熄滅燈
LCALL DELAY ;延時
DJNZ R0,FLASH1 ;閃爍次數不夠10次,繼續
RET
DELAY:MOV R3,#0FFH ;延時子程序
DEL2: MOV R4,#0FFH
DEL1: NOP DJNZ R4,DEL1 DJNZ R3,DEL2 RET END上面的程序中,主程序調用了閃爍子程序FLASH,閃爍子程序又調用了延時子程序DELAY,這種主程序調用子程序,子程序又調用另外的子程序的程序結構,稱為子程序的嵌套。一般來說,子程序嵌套的層數理論上是無限的,但實際上,受堆棧深度的影響,嵌套層數是有限的。與子程序的多次調用不同,嵌套子程序的調用過程如圖4.20所示。思考:圖4.20中嵌套子程序執行過程中的標號①~⑨的具體操作是什么?圖4.20例4.10程序中嵌套子程序的執行過程
例4.11
查表子程序。假設a、b均小于10,計算c=a2+b2,其中a事先存在內部RAM的31H單元,b事先存在32H單元,請把c存入33H單元。
(1)題意分析。本例兩次使用平方的計算,在前面的例4.9中已經編過查平方表得到平方值的程序,在此我們采用把求平方編為子程序的方法。
(2)匯編語言源程序。
ORG 0000H ;主程序
MOV SP,#3FH ;設置棧底
MOV A,31H ;取數a存放到累加器A中作為入口參數
LCALL SQR
MOV R1,A ;出口參數——平方值存放在A中
MOV A,32H
LCALLSQR
ADD A,R1
MOV 33H,A
SJMP
$
;子程序:SQR
;功能:通過查表求出平方值y=x2
;入口參數:x存放在累加器A中 ;出口參數:求得的平方值y存放在A中 ;占用資源:累加器A,數據指針DPTR
SQR:PUSH
DPH ;保護現場,將主程序中DPTR的高8位;放入堆棧
PUSH
DPL ;保護現場,將主程序中DPTR;的低8位放入堆棧
MOV DPTR,#TABLE;在子程序中重新使用DPTR,;表首地址→DPTR
MOVC A,@A+DPTR ;查表
POP DPL ;恢復現場,將主程序中DPTR;的低8位從堆棧中彈出
POP
DPH ;恢復現場,將主程序中DPTR;的高8位從堆棧中彈出
RET
TABLE:DB 0,1,4,9,16,25,36,49,64,81
(3)執行程序。在運行程序之前,利用單片機開發系統先在內部RAM的31H、32H單元存放兩個小于10的數,執行完之后,結果存放在33H單元。
(4)程序說明。①參數傳遞。主程序調用查表子程序時,子程序需要從主程序中得到一個參數——已知數x,這個參數稱為子程序的入口參數。查表子程序執行完以后,必須將結果傳送給主程序,這個子程序向主程序傳遞的參數稱為子程序的出口參數。本例中入口參數和出口參數都是通過累加器A來傳送的。②現場保護和現場恢復。子程序在編制過程中經常會用到一些通用單元,如工作寄存器、累加器、數據指針DPTR以及PSW等。而這些工作單元在調用它的主程序中也會用到,為此,需要將子程序用到的這些通用編程資源加以保護,稱為保護現場。在子程序執行完后需恢復這些單元的內容,稱為恢復現場。③子程序的說明。在查表子程序前,以程序注釋的形式對子程序進行了說明,說明內容如下:
(a)子程序名:提供給主程序調用的名字。
(b)子程序功能:簡要說明子程序能完成的主要功能。
(c)入口參數:主程序需要向子程序提供的參數。
(d)出口參數:子程序執行完之后向主程序返回的參數。
(e)占用資源:該子程序中使用了哪些存儲單元、寄存器等。這些說明是寫給程序員看的,供以后使用子程序時參考。4.6.2堆棧結構
1.堆棧概念堆棧實際上是內部RAM的一部分,堆棧的具體位置由堆棧指針SP確定。SP是一個8位寄存器,用于存放堆棧的棧底(初始化)地址和棧頂地址。單片機復位或上電時,SP的初值是07H,表示堆棧棧底為07H,存入數據后,地址增1,SP中的地址值隨著加1。SP的值總是指向最后放進堆棧的一個數,此時,SP中的地址稱為棧頂地址。堆棧結構示意圖如圖4.21所示。圖4.21堆棧結構示意圖
2.堆棧操作堆棧有兩種最基本操作:向堆棧存入數據,這稱為“入棧”或“壓入堆棧”(PUSH);從堆棧取出數據,這稱為“出棧”或“彈出堆棧”(POP)。堆棧中數據的存取采用后進先出方式,即后入棧的數據先彈出,類似貨棧堆放貨物的存取方式,“堆棧”一詞因此而得名。由于單片機初始化的堆棧區域同第1組工作寄存器區重合,也就是說,當把堆棧棧底設在07H處時,就不能使用第1組工作寄存器,如果存入堆棧的數據量比較大的話,甚至第2組和第3組工作寄存器也不能使用了。因此,在匯編語言程序設計中,通常總是把堆棧區的位置設在用戶RAM區。例如:
MOV SP,#60H ;將堆棧棧底設在內部RAM的60H處
3.堆棧的功能最初,堆棧是為了子程序調用和返回而設計的,執行調用指令(LCALL、ACALL)時,CPU自動把斷點地址壓棧;執行返回指令RET時,自動從堆棧中彈出斷點地址。由于堆棧操作簡單,程序員也經常用堆棧暫存中間結果或數據。只是使用時需要注意堆棧先進后出的特點。例如,在例4.11的子程序SQR中,恢復現場的順序就不能弄反,先保護的DPH后恢復出來。另外,在子程序調用時,CPU會自動利用堆棧進行保護現場和恢復現場。
4.堆棧操作與RAM操作的比較堆棧作為內部RAM的一個特殊區域,又有其獨特性,為匯編語言程序設計提供了更多的方便。同內部RAM的操作相比較,使用堆棧有以下優點:
(1)使用內部RAM必須知道單元的具體地址,而堆棧只需設置好棧底地址,就可放心使用,無需再記住單元具體地址。
(2)當我們需要重新分配內存工作單元時,程序中使用內部RAM的地方,都要修改單元地址,而堆棧只需修改棧底地址就行了。
(3)堆棧所特有的先進后出特點,使得數據彈出之后,存儲單元自動回收、再次使用,充分提高了內存的利用率;而內部RAM的操作是不可能實現自動回收再利用的,必須通過編程員的重新分配,才能再次使用。4.6.3子程序結構
1.子程序的編程原則在實際的單片機應用系統軟件設計中,為了程序結構更加清晰,更加易于設計和修改,增強程序的可讀性,基本上都要使用子程序結構。子程序作為一個具有獨立功能的程序段,編程時需遵循以下原則:
(1)子程序的第一條指令必須有標號,以明確子程序入口地址。
(2)以返回指令RET結束子程序。
(3)簡明扼要的子程序說明部分。
(4)較強的通用性和可浮動性,盡可能避免使用具體的內存單元和絕對轉移地址等。
(5)注意保護現場和恢復現場。
2.參數傳遞的方法主程序調用子程序時,主程序和子程序之間存在著參數互相傳遞的問題。參數傳遞一般有以下三種方法。
1)寄存器傳遞參數如例4.11那樣,通過寄存器A傳遞入口參數和出口參數。
2)利用堆棧傳遞參數修改例4.11,利用堆棧來傳遞參數,源程序如下:
ORG 0000H ;主程序
MOV SP,#3FH ;設置棧底
PUSH 31H ;將數a存放到堆棧中,作為入口參數
LCALL SQR POP ACC
MOV R1,A ;出口參數——平方值存放在A中
PUSH
ACC
LCALL
SQR
POP
ACC
ADD
A,R1
MOV
33H,A
SJMP
$
;子程序:SQR
;功能:通過查表求出平方值y=x2
;入口參數:x存放在堆棧中 ;出口參數:求得的平方值y存放在堆棧中 ;占用資源:累加器A,數據指針DPTR
SQR:MOV
R0,SP ;R0作為參數指針
DEC
R0 ;堆棧指針退回子程序調用前的地址
DEC
R0
XCH
A,@R0 ;保護ACC,取出參數
MOV
DPTR,#TABLE ;表首地址→DPTR
MOVCA,@A+DPTR ;查表
XCH
A,@R0 ;查表結果放回堆棧中
RET
TABLE:DB
0,1,4,9,16,25,36,49,64,81
3)利用地址傳遞參數將要傳遞的參數存放在數據存儲器中,將其地址通過間接尋址寄存器傳遞,供子程序讀取參數。
3.子程序調用中應注意的問題由于子程序調用過程中CPU自動使用了堆棧,因此,容易出現以下幾種錯誤:
(1)忘記給堆棧指針SP賦棧底初值,堆棧初始化位置與第1組工作寄存器重合,如果以不同的方式使用了同一個內存區域,則可能會導致程序混亂。
(2)程序中的PUSH和POP沒有配對使用,使RET指令執行時不能彈出正確的斷點地址,造成返回錯誤。
(3)若堆棧設置太小,棧操作數據太多,會使棧區與其它內存單元重合。本章小結單片機匯編語言程序設計是單片機應用系統設計的重要組成部分。匯編語言程序基本結構包括順序結構、分支結構、循環結構和子程序結構等。程序設計的關鍵是掌握解題思路。程序設計的步驟一般分為:題意分析、畫流程圖、分配寄存器和內存單元、源程序設計、程序調試等。程序設計中還要注意單片機軟件資源的分配,內部RAM、工作寄存器、堆棧、位尋址區等資源的合理分配對程序的優化、可讀性和可移植性等起著非常重要的作用。習題4
4.1利用單片機來計算10-7,并在實訓1的實驗板上用P1口連接的8個LED顯示計算結果(注意:減法操作只有一條帶借位減法指令SUBB,減法之前先清CY)。
4.2將內部RAM中從30H開始的4個單元中存放的四字節十六進制數和內部RAM中從40H單元開始的4個單元中存放的四字節十六進制數相減,結果存放到從40H開始的單元中。
4.3下面是例4.1的另外一種實現方法,分析程序并畫出程序流程圖。
ORG
0000H
MOV
R0,#30H
MOV
R1,#40H
MOV
R2,#04H
CLR
C
LOOP: MOV
A,@R0
ADDC
A,@R1
MOV
@R1,A
INC
R0
INC
R1
DJNZ
R2,LOOP SJMP
$ END
4.4數據拼拆程序1:將一個字節內的兩個BCD碼十進制數拆開并變成相應的ASCII碼的程序段如下:
MOV R0,#32H
MOV A,@R0
ANL A,#0FH
ORL A,#30H
MOV 31H,A
MOV A,@R0
SWAP A
ANL A,#0FH
ORL A,#30H
MOV 32H,A分析上面的程序段,給每一條指令加上注釋,并說明BCD碼和拆開后的ASCII碼各自存放在內部RAM的什么地方。
4.5數據拼拆程序2:分析下面的程序,已知(20H)=
85H,(21H)=F9H,說明執行該程序段后,30H單元的內容是什么。
MOV 30H,20H
ANL 30H,#00011111B
MOV A,21H
SWAP A
RL A
ANL A,#11100000B
ORL 30H,A
4.6已知共陰極8段LED數碼管顯示數字的字形碼如下:
0
12?3456?7?8?9
3FH06H5BH4FH66H6DH7DH07H7FH6
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 數據庫性能分析與評估試題及答案
- 催收團隊現場管理制度
- 小區室內衛生管理制度
- 員工無故曠工管理制度
- 考前總結2025年計算機數據庫試題及答案
- 工廠廢棄食堂管理制度
- 宿舍廚房安全管理制度
- 基于云環境的測試自動化實施詳解試題及答案
- 計算機三級嵌入式真實場景試題及答案
- 中醫學試題及答案
- 05J927-1汽車庫建筑構造圖集
- 工業自動化與人工智能融合
- 回歸課標+重視教材+從容備考(章建躍)
- 電大專科【計算機平面設計(二)】網絡課形考任務2答案
- 消防工程火災自動報警及聯動控制系統施工
- 藝術鑒賞學習通超星期末考試答案章節答案2024年
- 2024年廣東省中考歷史真題(含解析)
- 自治區面向社會公開招聘中小學教師 政治思想審查表
- NB-T+10110-2018風力發電場技術監督導則
- JT-T-913-2014危險貨物道路運輸企業安全生產責任制編寫要求
- 事業單位專業測試項目管理試題庫
評論
0/150
提交評論