版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
第5章匯編語言程序設計
5.1STVD開發(fā)環(huán)境與STM8匯編語言程序結構5.2STM8匯編程序結構5.3程序基本結構5.4并行多任務程序結構及實現5.5程序仿真與調試 5.1STVD開發(fā)環(huán)境與STM8匯編語言程序結構5.1.1STVD開發(fā)環(huán)境中創(chuàng)建工作站文件工作站文件的創(chuàng)建過程如下:
(1)執(zhí)行“File”菜單下的“NewWorkspace…”,選擇“createworkspaceandproject”,創(chuàng)建新的ST工作站文件(擴展名為?.STW)。
(2)在圖5-1所示的“Workspace”文本框內指定工作站文件的存放目錄路徑;在“Workspacefilename”文本框內輸入工作站文件名(不用擴展名)。圖5-1創(chuàng)建工作站文件
(3)在圖5-2所示的“Projectfilename”文本框內輸入“項目文件名”;根據選定的開發(fā)語言(ST匯編還是某特定的C語言),在“Toolchain”文本框內選定相應的連接程序,如“STAssemblerLinker”。
(4)在“MCU”窗口內選擇相應的MCU型號,如STM8S208MB等,單擊“OK”按鈕后,就可以觀察到如圖5-3所示的文件夾。5.1.2STVD自動創(chuàng)建項目文件內容利用STVD創(chuàng)建STM8S匯編項目文件時,在“SourceFiles”文件夾下自動生成了mapping.asm源文件(段定義匯編文件)、main.asm文件(用戶應用程序主模塊框架匯編文件);在“IncludeFiles”包含文件夾內自動生成了mapping.inc文件(段定義匯編文件中涉及的符號常量定義說明文件)。其中,mapping.asm、mapping.inc的內容簡單明了,容易理解。下面主要分析main.asm文件組成和關鍵指令功能。
stm8/ ;?[注1]指定CPU類型匯編格式的偽指令,不能缺省且頂格書寫
#include"mapping.inc" ;?[注2]包含文件說明偽指令,凡指令、偽指令行至少退一個以上字符位
segment'rom' ;在rom段內存放指令碼main.l ;?[注3]主程序開始標號,凡標號一律頂格書寫
;?initializeSP
ldwX,#stack_end ;?[注4]初始化堆棧指針SPldwSP,X#ifdefRAM0 ;?[注5]RAM存儲區(qū)00H~FFH單元清0;?clearRAM0ram0_start.bEQU$ram0_segment_startram0_end.bEQU$ram0_segment_endldwX,#ram0_startclear_ram0.l
clr(X) incwX cpwX,#ram0_end
jruleclear_ram0 #endif #ifdefRAM1 ;?[注6]RAM存儲區(qū)0100H及以上單元清0 ;?clearRAM1ram1_start.wEQU$ram1_segment_startram1_end.wEQU$ram1_segment_end ldwX,#ram1_startclear_ram1.l clr(X) incwX cpwX,#ram1_end jruleclear_ram1
#endif ;
clearstack ;?[注7]堆棧區(qū)單元清0stack_start.wEQU$stack_segment_startstack_end.wEQU$stack_segment_end ldwX,#stack_startclear_stack.l clr(X) incwX cpwX,#stack_end jruleclear_stackinfinite_loop.l ;虛擬的主程序
jrainfinite_loop interruptNonHandledInterrupt ;?[注8]中斷服務程序定義偽指令NonHandledInterrupt.l iret;中斷入口地址表
segment'vectit' dc.l{$82000000+main} ;?reset[注9]中斷入口地址表
dc.l{$82000000+NonHandledInterrupt} ;?trap dc.l{$82000000+NonHandledInterrupt} ;?irq0 dc.l{$82000000+NonHandledInterrupt} ;?irq1 dc.l{$82000000+NonHandledInterrupt} ;?irq2 dc.l{$82000000+NonHandledInterrupt} ;?irq3 dc.l{$82000000+NonHandledInterrupt} ;?irq4 dc.l{$82000000+NonHandledInterrupt} ;?irq5 dc.l{$82000000+NonHandledInterrupt} ;?irq6 dc.l{$82000000+NonHandledInterrupt} ;?irq7 dc.l{$82000000+NonHandledInterrupt} ;?irq8
dc.l{$82000000+NonHandledInterrupt} ;?irq9dc.l{$82000000+NonHandledInterrupt} ;?irq10dc.l{$82000000+NonHandledInterrupt} ;?irq11dc.l{$82000000+NonHandledInterrupt} ;?irq12dc.l{$82000000+NonHandledInterrupt} ;?irq13dc.l{$82000000+NonHandledInterrupt} ;?irq14dc.l{$82000000+NonHandledInterrupt} ;?irq15dc.l{$82000000+NonHandledInterrupt} ;?irq16dc.l{$82000000+NonHandledInterrupt} ;?irq17dc.l{$82000000+NonHandledInterrupt} ;?irq18dc.l{$82000000+NonHandledInterrupt} ;?irq19dc.l{$82000000+NonHandledInterrupt} ;?irq20dc.l{$82000000+NonHandledInterrupt} ;?irq21dc.l{$82000000+NonHandledInterrupt} ;?irq22dc.l{$82000000+NonHandledInterrupt} ;?irq23dc.l{$82000000+NonHandledInterrupt} ;?irq24dc.l{$82000000+NonHandledInterrupt} ;?irq25dc.l{$82000000+NonHandledInterrupt} ;?irq26dc.l{$82000000+NonHandledInterrupt} ;?irq27dc.l{$82000000+NonHandledInterrupt} ;?irq28dc.l{$82000000+NonHandledInterrupt} ;?irq29end ;匯編程序結束偽指令,不能缺省
[注1]在ST匯編中,匯編程序源文件開始處必須用“st7/(或ST7/)”或“stm8(或STM8/)”偽指令指定隨后的指令、偽指令按哪一種類MCU芯片指令格式進行匯編,不可缺省且必須頂格書寫。
[注2]由于自動創(chuàng)建的段定義文件使用了符號常量作為段起始地址、終了地址,因此STVD自動創(chuàng)建了mapping.inc文件。該文件對段定義中涉及的符號常量進行了定義,插入了#includemapping.inc偽指令。
[注3]在ROM段中開始定義了一個main.l標號,標號必須頂格書寫。這樣,只要在中斷入口地址表中用“dc.l{$82000000+main}”偽指令填充復位中斷向量存儲單元,就可以保證將復位中斷邏輯指向復位后要執(zhí)行的第一條指令所在存儲單元的地址,如[注9]所示。在STM8S中,復位后將從復位中斷邏輯指示的地址單元(可以是ROM、EEPROM,甚至是RAM)取出并執(zhí)行第一條指令。第一條指令在ROM存儲區(qū)中的存放位置并沒有限制,將第一條指令所在存儲單元的地址填入復位中斷入口地址表中。
[注4]復位后初始化堆棧指針。盡管復位后堆棧指針SP也指向RAM的最后一個單元,但復位后,用指令初始化有關寄存器是一個良好的習慣,避免了用缺省值造成的不確定性。
[注5]~[注7]分別清除了RAM存儲區(qū)、堆棧區(qū)。如果復位后沒有保留RAM單元信息的必要,則復位后對RAM單元進行集中清0非常必要。
[注8]在ST匯編中,必須通過“interrupt”偽指令定義相應中斷服務程序入口地址標號,然后在中斷入口地址表中填入對應的中斷服務程序入口地址標號。
[注9]中斷入口地址表。例如,PortA外部中斷EXTI0的中斷號為IRQ03,因此PortA口外中斷服務程序結構如下:
InterruptEXTI0
EXTI0.L ;外中斷EXTI0服務程序指令系列
IRET然后將中斷向量表內的IRQ03改為
dc.l{$82000000+NonHandledInterrupt} ;
irq2,未定義中斷入口地址表依然保留
dc.l{$82000000+EXTI0}
;?irq3,即PortA口外中斷入口地址表
dc.l{$82000000+NonHandledInterrupt} ;?irq4可見在STM8S中,中斷服務程序入口地址不固定,只需將對應中斷號服務程序第一條指令所在存儲單元地址,實際上是把82000000H與中斷服務程序第一條指令前標號相加,填入對應中斷向量表內。5.1.3完善STVD自動創(chuàng)建的項目文件內容由STVD創(chuàng)建的項目匯編文件尚不十分完善,沒有把相應型號MCU的外設寄存器定義文件加入到“SourceFiles”(源文件夾)中,在用戶程序中尚不能直接引用外設寄存器名,如“LDPA_DDR,A”等;也沒有定義外設控制寄存器與外設狀態(tài)寄存器中的位,如“FLASH_IAPSR”的DUL位。為此須按下列步驟完善STVD創(chuàng)建的項目文件。
1.加入相應MCU的外設寄存器定義匯編文件(.asm)及其外部標號說明文件(.inc)將光標移到“SourceFiles”源程序文件夾上,單擊右鍵,選擇“AddFilestoForlder…”將ST_toolset\asm\include文件夾中對應型號芯片的外設寄存器定義匯編文件,如STM8S208MB.asm,加入到源程序文件中。將光標移到“IncludeFiles”包含文件夾上,單擊右鍵,選擇“AddFilestoForlder…”將ST_toolset\asm\include文件夾中對應芯片的外設端口寄存器標號說明文件,如STM8S208MB.inc,加入到該文件夾中。
2.在main.asm文件中插入相應MCU的.inc文件在main.asm文件中插入#include
"×××××××.inc",其中×××××××代表芯片型號,即上一步添加的包含文件名,以便能在應用程序模塊中直接引用外設寄存器名,如PA_DDR、PA_CR1、FLASH_IAPSR等。到此基本完成了匯編環(huán)境的創(chuàng)建過程,可以在main.asm文件內插入用戶指令系列,并進行編譯、模擬仿真、聯機調試等操作。
3.在main.asm文件頭插入通用變量定義偽指令為便于模塊化應用程序的編寫、調試,可將00H~3FH之間的RAM存儲單元劃分為四個區(qū),其中,00H~0FH作為主程序通用變量區(qū),10H~1FH作為優(yōu)先級為1的中斷服務程序的變量區(qū),20H~2FH作為優(yōu)先級為2的中斷服務程序的變量區(qū),30H~3FH作為優(yōu)先級為3的中斷服務程序的變量區(qū);分別用R00~R3F變量名對這64字節(jié)RAM單元進行命名,具體如下: segment'ram0' BYTES ;?ram0段內標號為字節(jié)標號。如果不用BYTES偽指令或后綴.B指定為字
;節(jié)標號,將默認為字標號,采用16位地址格式;?00H~0FH單元定義為字節(jié)變量,供主程序使用.R00ds.b1 ;最好定義為公共變量,即用前綴“.”進行聲明.R01ds.b1.R0Fds.b1;?10H~1FH單元定義為字節(jié)變量,供優(yōu)先級1中斷服務程序使用.R10ds.b1.R11ds.b1.R1Fds.b1;?20H~2FH單元定義為字節(jié)變量,供優(yōu)先級2中斷服務程序使用.R20ds.b1.R21ds.b1.R2Fds.b1;?30H~3FH單元定義為字節(jié)變量,供優(yōu)先級3中斷服務程序使用.R30ds.b1.R31ds.b1.R3Fds.b1
segment'ram1'WORDS ;?ram1段后定義的標號為字標號。當在ram0段中,把變量標號定義為bytes時,
;該語句不能少在模塊化程序結構中,最好將R00~R3F公共變量定義偽指令放在一個特定的源文件中,如User_register.asm,同時創(chuàng)建相應外部變量說明文件,如User_register.inc。然后分別添加到“SourceFiles”、“IncludeFiles”文件夾內,供不同的模塊引用。這兩個文件的內容如下:
;?User_register.asm文件內容:stm8/
;用戶定義公共變量
segment'ram0'
;?00H~0FH單元定義為字節(jié)變量(供主程序使用)
.R00.Bds.b1
.R01.Bds.b1
.R02.Bds.b1;省略R03~R0F變量定義偽指令行;?10H~1FH單元定義為字節(jié)變量,供優(yōu)先級為1中斷服務程序使用.R10.Bds.b1.R11.Bds.b1.R12.Bds.b1;省略R13~R1F變量定義偽指令行;?20H~2FH單元定義為字節(jié)變量,供優(yōu)先級為2中斷服務程序使用.R20.Bds.b1.R21.Bds.b1.R22.Bds.b1;省略R23~R2F變量定義偽指令行;?30H~3FH單元定義為字節(jié)變量,供優(yōu)先級為3中斷服務程序使用.R30.Bds.b1.R31.Bds.b1.R32.Bds.b1;省略R33~R3F變量定義偽指令行BYTESend;?User_register.inc文件內容:;用戶定義公共變量屬性說明;?00H~0FH單元定義為字節(jié)變量,供主程序使用
EXTERNR00.B ;用戶定義的變量
EXTERNR01.B ;用戶定義的變量
EXTERNR02.B ;用戶定義的變量;省略R03~R0F變量定義屬性說明偽指令行
EXTERNR10.B ;用戶定義的變量
EXTERNR11.B ;用戶定義的變量
EXTERNR12.B ;用戶定義的變量
;省略R13~R1F變量定義屬性說明偽指令行
EXTERNR20.B ;用戶定義的變量
EXTERNR21.B ;用戶定義的變量
EXTERNR22.B ;用戶定義的變量;省略R23~R2F變量定義屬性說明偽指令行
EXTERNR30.B ;用戶定義的變量
EXTERNR31.B ;用戶定義的變量
EXTERNR32.B ;用戶定義的變量;省略R33~R3F變量定義屬性說明偽指令行在具體應用程序中,可根據需要靈活裁剪,如某應用系統(tǒng)的中斷源只有兩個優(yōu)先級,則無須保留30H~3FH單元作為中斷優(yōu)先級3的通用變量區(qū)。需要注意的是:非屏蔽中斷TRAP、頂級中斷TLI(PD7引腳外中斷)可中斷優(yōu)先級為3的可屏蔽中斷,這兩類中斷服務程序不宜共用R30~R3F變量,否則可能出現資源沖突現象。
4.更換匯編語言數制表示方式如果程序員熟悉Intel格式匯編語言數制表示方式,則可按下列步驟改造STVD自動創(chuàng)建的main.asm文件:
(1)在虛擬主程序段前插入“Intel”偽指令。
(2)在segment'vectit'(中斷向量段)前插入“Motorola”偽指令;在segment'vectit'(中斷向量段)后插入“Intel”偽指令。當然也可以將堆棧段內中斷入口地址表常數中全部的“$82000000”修改為“82000000H”,但遠不如直接插入“Motorola”、“Intel”等偽指令方便。
5.創(chuàng)建外設寄存器位定義說明文件(.inc)創(chuàng)建外設寄存器位定義說明文件不是必需的,只是為了增加源程序的可讀性。未用?#Define偽指令定義外設寄存器位前,ST匯編語言源程序中的位操作指令只能采用“寄存器名,#位編號”形式作為位操作數。例如:
BTJTFLASH_IAPSR,#3,EEPROM_Write_Next1 ;DUL(EEPROM寫保護標志位)為1,則跳轉該指令如果不加注釋,則指令的可讀性很差,需要查閱用戶指南才能確定FLASH_IAPSR[3]位是DUL標志。此外,當程序中多處出現“FLASH_IAPSR,#3”時,容易出錯。例如,將“#3”誤寫成“#2”,則編譯時不給出任何提示信息。為此最好創(chuàng)建一個通用的外設寄存器位定義說明文件(periph_bit_define.INC),以便在程序中用“寄存器名_位名”形式作為位操作數,以提高源程序的可讀性。該文件格式如下:;------FLASH_IAPSR寄存器位定義------; 位名稱 所在位編號 讀寫特性說明#defineFLASH_IAPSR_HVOFF FLASH_IAPSR,#6 ;r#defineFLASH_IAPSR_DUL FLASH_IAPSR,#3 ;rc_w0#defineFLASH_IAPSR_EOP FLASH_IAPSR,#2 ;rc_r#defineFLASH_IAPSR_PUL FLASH_IAPSR,#1 ;rc_w0#defineFLASH_IAPSR_WR_PG_DIS FLASH_IAPSR,#0 ;rc_r將periph_bit_define.inc文件添加到IncludeFiles文件夾內,并在應用程序文件頭部分插入?#include“periph_bit_define.inc”?偽指令,如圖5-4所示。圖5-4插入并引用外設寄存器位定義名為防止編譯時聯接程序找不到指定的文件,一個簡單的辦法是將User_register.asm、User_register.inc、periph_bit_define.inc,甚至相應型號MCU外設寄存器定義文件,如STM8S207R8.asm、STM8S207R8.inc等,復制到指定工作站目錄下,然后再分類逐一添加到SourceFiles、IncludeFiles文件夾內。這樣,在匯編語言源程序中就可以直接使用位定義名代替“寄存器名,#位編號”形式位操作數,原因是“BTJTFLASH_IAPSR_DUL,EEPROM_Write_Next1”與“BTJTFLASH_IAPSR,#3,EEPROM_Write_Next1”等效。顯然,用寄存器位定義名后提高了源程序的可讀性,且只要保證位定義文件中寄存器名、位編號正確,就不會出錯。5.1.4在項目文件中添加其他文件
STVD開發(fā)環(huán)境支持多模塊匯編。因此,可創(chuàng)建多個源程序,并將它添加到“SourceFiles”文件夾內,匯編后將根據模塊內段定義特征連接成一個完整的應用程序。在項目內添加程序源文件時,需要注意以下幾點:
(1)任意一個匯編源程序文件的第一條指令必須是“ST7/(或st7/)”或“STM8/(或stm8/)”偽指令;最后一條指令一般為“END(或end)”偽指令,且每一指令行必須帶有“回車符”。
(2)對于變量定義偽指令,必須通過“Segment”指定變量存放在哪一段內;對于代碼,也必須通過“Segment”指定存放匯編后代碼存放在哪一段內。
(3)在多模塊結構程序中,模塊內的公共變量、標號必須指定為Public類型,或用前綴“.”定義,否則匯編時將視為局部變量、標號,僅在本模塊內有效。模塊內引用來自其他模塊定義的變量、標號時必須用“EXTERN”偽指令說明。
(4)在main.asm主程序中無須加入“#include匯編源程序名.asm”指令。對于包含文件(.inc),則必須通過“#include文件名.inc”語句聲明。
(5)模塊順序決定了連接后的定位順序。在STVD中,“SourceFiles”匯編程序源文件順序依次為
Mapping.asm ;段定義源文件應排在最前面
STM8S208MB.asm ;外設寄存器定義源程序文件,具體文件名與MCU型號有關
Main.asm ;用戶主程序或其他模塊程序5.2STM8匯編程序結構5.2.1子程序與中斷服務程序在主模塊內
采用子程序與中斷服務程序在主模塊內的結構時,工作站文件夾中除了相應型號芯片的頭文件外,幾乎沒有其他模塊文件,形成了單一主模塊程序結構,如圖5-5所示。該結構中所有的子程序、中斷服務程序均位于主應用程序main.asm模塊內,變量、子程序入口地址標號、中斷服務入口地址等屬于局部標號與局部變量,無須指定標號類型,也無須用EXTERN偽指令聲明其來源,但其缺點是程序結構不夠清晰,查找某一個子程序時效率較低。
圖5-5單一主模塊程序結構在這種結構程序中,主應用程序main.asm模塊大致包含了如下內容:
;按st7還是stm8代碼格式匯編源程序
stm8/ ;代碼格式偽指令
;程序頭(由#define、equ、cequ定義的符號常量、標號)#defineVAR1$50
;主程序引用的外部標號(變量)說明區(qū)(EXTERN)
segment'rom' ;指定了代碼存放在哪一段中
mani.l ;主程序開始標號
;初始化I/O引腳的輸入/輸出方式,并用#define指令對I/O引腳重定義
;初始化堆棧指針
;?RAM0段存儲單元清0
;?RAM1段存儲單元清0
;堆棧段存儲單元清0
;初始化主時鐘及CPU時鐘頻率
;硬件初始化(設置外設部件工作方式)
;復位中斷優(yōu)先級(開中斷);主程序實體指令系列;子程序;中斷服務程序;常數表(由dc.b、dc.w、dc.l定義的常數表);中斷向量表由于STVD開發(fā)環(huán)境不支持過程匯編,位于同一個匯編文件內不同子程序中的標號必須唯一。因此,在STVD開發(fā)環(huán)境中最好取長標號,可按“模塊名_模塊內標號”形式給標號取名。例如,在“EEPROM_Write.L”模塊中可用“EEPROM_Write_Lab1”、“EEPROM_Write_Next1”、“EEPROM_Write_Last1”、“EEPROM_Write_Loop1”等作為該模塊的標號。盡管在理論上,子程序與中斷服務程序可存放在FlashROM存儲區(qū)中的任意位置,但在STVD開發(fā)環(huán)境中,中斷服務程序必須位于子程序后,否則在仿真調試時,調試速度會很慢,單步執(zhí)行一條指令(包括子程序調用指令)所需的時間可能很長,甚至不能接受。5.2.2子程序與中斷服務程序在各自模塊內把子程序,尤其指令較多的子程序、中斷服務程序安排在各自模塊內,形成多模塊結構程序,如圖5-6所示。這種程序結構清晰,除了指定為“Public”的公共變量、標號外,均屬于局部變量、局部標號,這意味著不同模塊內的局部標號可重復使用。這有利于程序的維護,以及多人協作完成同一項目控制程序的設計。圖5-6多模塊程序結構為便于程序的維護和升級,在程序中應盡量避免直接使用存儲單元地址。因此良好的程序習慣如下:
(1)對于常數,可在程序頭中用#define、EQU偽指令定義。例如:
#definePlus_width50H或
Plus_widthEQU50H
(2)對于RAM、EEPROM中的存儲單元,最好用ds.b、ds.w、ds.l偽指令定義,使變量對應的存儲單元地址處于浮動狀態(tài),變量實際地址待編譯后才能確定。例如:
Segment'ram0'
ds.bTRK11
ds.bTRK21
(3)位于FlashROM中的常數表,用dc.B、dc.W、dc.L偽指令定義。5.2.3子程序結構所謂子程序就是供其他程序模塊通過CALL或CALLF指令調用的指令系列。當子程序中存在改寫CPU內某一寄存器(包括索引寄存器X、Y,累加器A,條件碼寄存器CC)時,如果返回后需要用到調用前該寄存器的值,則必須將其壓入堆棧保護,這容易理解。但容易忽略的是寄存器CC中的標志位,由于STM8內核CPU許多指令均影響標志位狀態(tài),因此返回后還需使用調用前的標志位狀態(tài),為防止錯誤,在子程序中一律將CC寄存器壓入堆棧將是一個良好的習慣。其實,在模塊化程序設計中,把子程序中改寫的CPU寄存器一律壓入堆棧也是一個良好策略,這是因為STM8內核MCU堆棧深度較大,如果子程序嵌套層數不太多,則遇到堆棧溢出的可能性很小。在STM8系統(tǒng)中,子程序入口地址標號可以是Word類型,即16位地址形式,對應的返回指令為RET;也可以是Long類型,即24位地址形式,對應的返回指令為RETF。采用L類型地址標號還是W類型地址標號,與子程序存放的位置有關。
(1)當子程序位于00段內時,可定義為W類型,也可以定義為L類型。當定義為W類型時,調用(CALL指令)與返回(RET指令)代碼短、執(zhí)行速度快,子程序中所有標號均定義為W類型。為方便程序維護,最好在地址標號后加“:”(冒號)。這樣當需要將該子程序放到01段以上時,只要將“:”(冒號)用“.L”替換;將JP絕對跳轉指令中的操作碼助記符“JP”用“JPF”替換;將返回指令RET用“RETF”替換即可。因此,00段內入口地址為W類型的子程序結構如下:
Sub_xxx: ;子程序入口地址標號Sub_xxx定義為W(Word)類型
PUSHWX ;保護索引寄存器X(子程序用到寄存器X) PUSHWY ;保護索引寄存器Y(子程序用到寄存器Y) PUSHCC ;保護寄存器CC PUSHA ;保護累加器A ;子程序實體
JPSub_xxx_NEXT1 ;子程序實體Sub_xxx_NEXT1: ;子程序內的地址標號定義為W類型
POPA POPCC POPWY POPWX RET ;子程序返回指令
RET ;子程序返回冗余指令數目為1~4條,參閱第11章這種結構子程序,只能在00段內通過CALL指令調用。為此可將00段內子程序入口地址定義為L類型,以便在任何位置都可以通過CALLF指令調用。因此,00段內推薦的子程序結構如下:
Sub_xxx.L ;子程序入口地址標號Sub_xxx定義為L(Long)類型
PUSHWX ;保護索引寄存器X(子程序用到寄存器X)
PUSHWY ;保護索引寄存器Y(子程序用到寄存器Y) PUSHCC ;保護寄存器CC PUSHA ;保護累加器A ;子程序實體
JPSub_xxx_NEXT1 ;為減少指令碼長度、提高運行速度,仍采用JP絕對跳轉指令
;子程序實體Sub_xxx_NEXT1: ;子程序內的地址標號定義為W類型
POPA POPCC POPWY POPWX RETF ;子程序返回指令
RETF ;子程序返回冗余指令
(2)當子程序位于01段以上時,入口地址標號必須定義為L類型,同時,子程序內所有地址標號均定義為L類型,如下所示:
Sub_xxx.L ;子程序入口地址標號Sub_xxx定義為L(Lord)類型
PUSHWX ;保護索引寄存器X(子程序用到寄存器X) PUSHWY ;保護索引寄存器Y(子程序用到寄存器Y) ;子程序實體
JPFSub_xxx_NEXT1 ;采用遠跳轉指令JPF ;子程序實體Sub_xxx_NEXT1.L ;子程序內所有地址標號必須定義為L類型
POPWY POPWX RETF ;子程序返回指令
RETF ;子程序返回冗余指令值得注意的是:中斷服務程序入口地址標號一定為L類型,原因是STM8內核CPU響應中斷請求時,將入口地址標號對應的三個字節(jié)壓入堆棧;在中斷服務程序中,無須將CPU內核寄存器壓入堆棧,這是因為STM8內核CPU響應中斷請求時已自動將CPU內各寄存器壓入堆棧。5.3程序基本結構5.3.1順序結構所謂順序程序結構,是指程序段中沒有轉移指令,執(zhí)行時CPU逐條執(zhí)行。例5-1查表程序。假設共陽LED數碼管數碼0~F的筆段碼存放在以LED_Data為標號的存儲單元中,如下所示:
LED_Data:
;
0,
1,2,
3,4,
5,
6,7,
8,9,
A,B,
C,D,
E,
F
dc.B
0C0H,0F9H,0A4H,0B0H,099H,092H,082H,0F8H,080H,090H,088H,083H,0C6H,0A1H,086H,08EH顯示數據在累加器A中,試編寫一個程序段將顯示數據對應的筆段碼取出。
參考程序段如下:
CLRWX ;寄存器X清0 LDXL,A ;顯示數碼送寄存器XL ADDWX,#LED_Data ;加上數據首地址LDA,(X) ;取出對應的筆段碼或
CLRWX ;寄存器X清0 LDXL,A ;顯示數碼送寄存器XL LDA,(LED_Data,X) ;以“基址?+?變址尋址”方式取出對應的筆段碼例5-2
將存放在R01單元中壓縮形式的BCD碼轉換為二進制數。參考程序如下:
;功能:把存放在R01單元中壓縮形式的BCD碼轉換為二進制數
;算法:
;入口參數:待轉換的BCD碼存放在R01單元中
;出口參數:結果回送R01單元,假定R01物理地址在RAM存儲區(qū)中
;使用資源:寄存器A、X及R01單元S_BCD_BI.W ;單字節(jié)BCD碼轉二進制
LDA,R01 ;取BCD碼
ANDA,#0F0H ;保留高4位(十位) SWAPA LDXL,A LDA,#10 MULX,A ;?X←XL?×?A,十位乘10,最大為90,高8位XH為0 LDA,R01 ;取BCD碼
ANDA,#0FH ;保留BCD碼個位
LDR01,A ;回送R01暫存
LDA,XL ADDA,R01 LDR01,A RET例5-3把存放在R02、R03單元中壓縮形式的BCD碼轉換為二進制數,結果回送到R02、R03單元中。參考程序如下:
;功能:把存放在R02、R03單元中壓縮形式的BCD碼轉換為二進制數
;算法:a3?×?103?+?a2?×?102?+?a1?×?10?+?a0?=?(a3?×?10?+?a2)?×?100?+?(a1?×?10?+?a0)
;入口參數:待轉換的BCD碼存放在寄存器R02、R03單元中
;出口參數:結果回送R02、R03單元,假定兩單元相鄰,可按字節(jié)訪問,也可以按字訪問
;使用資源:寄存器A、X以及R01單元D_BCD_BI.W MOVR01,R03 ;十位、個位送R01 CALLS_BCD_BI ;調用單字節(jié)BCD碼轉二進制子程序,計算a1?×?10?+?a0 MOVR03,R01 ;結果暫時存放在R03中
MOVR01,R02 ;千位、百位送R01 CALLS_BCD_BI ;調用單字節(jié)BCD碼轉二進制子程序,計算a3?×?10?+?a2 LDA,R01 LDXL,A ;結果送寄存器XL中
LDA,#100 MULX,A ;?(a3?×?10?+?a2)?×?100,結果在寄存器X中
CLRR02 ;清除R02單元
ADDWX,R02 ;按字相加
LDWR02,X ;結果回送R02、R03單元
RET例5-4將存放在R01單元中的二進制數轉換為壓縮形式的BCD碼,結果存放在R02(百位)、R03(十位及個位)單元。參考程序如下:
;算法:待轉換的二進制數除以100,所得的商是百位,余數再除以10所得的商是十位,余數為個位
;入口參數:待轉換的二進制存放在R01單元中
;出口參數:百位存放在R02單元中,十位及個位存放在R03單元中
;使用資源:寄存器A、X及R02、R03單元
S_BI_BCD: ;單字節(jié)二進制轉化為壓縮形式的BCD碼
CLRWX LDA,R01 LDXL,A LDA,#100 DIVX,A ;商(百位碼)在XL中,余數在A中
EXGA,XL ;商與余數交換
LDR02,A ;保存百位碼
LDA,#10 DIVX,A ;商(十位碼)在XL中,余數(個位碼)在A中
LDR03,A ;個位碼先放R03LDA,XL ;取十位碼SWAPA ;十位碼轉移到高4位ORA,R03 ;合并壓縮形式的BCD碼LDR03,ARET5.3.2循環(huán)結構循環(huán)程序結構由初始化、循環(huán)體、包含條件跳轉指令的循環(huán)控制等三部分組成。例5-5
將ram0段(即00H~FFH)存儲單元清0。由于需要清除256個單元,因此,不能再用例5-1~例5-4所示的順序程序結構,否則程序會很長,須用循環(huán)結構程序實現。參考程序如下:;初始化LDWX,#ram0_start ;初始化索引寄存器X ;循環(huán)體Ram0_CLR_LOOP: CLR(X) ;把寄存器X內容對應單元清0 INCWX ;?X加1,指向下一個存儲單元
;循環(huán)控制指令CPWX,#ram0_end ;與ram0段的終了地址比較JRULERam0_CLR_LOOP ;當X不大于終了地址時,跳轉到標號為LOOP處繼續(xù)執(zhí)行5.3.3分支程序結構分支程序也是一種常見的程序結構,常需要根據運算結果、某一個輸入引腳的狀態(tài),決定是否執(zhí)行相應的操作。根據分支多少,可將分支程序結構分為簡單分支(即兩分支)結構和多路分支結構。
1.簡單分支簡單分支常用條件轉移指令實現,例如位測試轉移指令,如下所示:
BTJTR00,#3,NEXT1
NEXT1:
2.多路分支在STM8指令系統(tǒng)中,00段內的多路分支可用無條件跳轉指令JP(TAB_ADR,X)實現,為菜單、并行多任務程序結構中任務與作業(yè)切換操作等提供了方便。由于變址寄存器X為16位,因此可以實現超過256分支的散轉。例5-6
編寫一段程序完成32位除以16位的運算。假設32位被除數存放在R04、R05、R06、R07單元中,16位除數存放在R00、R01單元中。
STM8內核CPU沒有32位除以16位運算指令,只能通過類似多項式除法完成。根據運算規(guī)則可知商為32位,余數為16位,因此需要把除數擴展為16?+?32?=?48位,然后通過移位相減獲得對應位的商。參考程序如下:
DIV3216: ;?32位除以16位運算子程序(沒有檢查除數為0)
;?32bit/16bit運算程序
;入口參數:32位被除數存放在R04~R07單元中,高位放在低地址
; :16位除數存放在R00~R01單元中,高位放在低地址
;出口參數:32位商放在R04、R05、R06、R07單元中
; :余數放在R02、R03寄存器中
;算法:通過移位相減類似多項式除法實現,先將被除數擴展為32?+?16位,即48位
;執(zhí)行時間約為584個機器周期;使用資源:X,ACLRR02CLRR03 ;擴展被除數為(32?+?16)位LDA,#32 ;定義移位相減次數DIV3216_LOOP1: SLLR07 ;左邏輯移位(C←b7,b7←b6,b0←0)RLCR06RLCR05RLCR04RLCR03RLCR02BCCMR07,#0 ;?C移到R07的b0位LDWX,R02 ;取擴展被除數最高位SUBWX,R00JRNCDIV3216_NEXT1;
C
=
1,即有借位BTJFR07,#0,DIV3216_NEXT2 ;商已為0,無須再設置;移出位為1,說明也夠減DIV3216_NEXT1:;夠減,用差替換被減數BSETR07,#0 ;?b0位置1,用差替換被減數LDWR02,XDIV3216_NEXT2:DECAJRNEDIV3216_LOOP1RET可見,實現32位除以16位運算程序段指令代碼不長,執(zhí)行時間大約為584個機器周期。用多項式除法運算規(guī)則實現除法運算時,一般情況下需要擴展被除數,但在特殊情況下,也可以不用擴展被除數。例如,當除數不小于8000H,即除數最高位b15為1,可不用擴展被除數,而是直接用被除數高位減除數,完成時間將極大地縮短,僅需300個機器周期,如例5-7所示。例5-7已知32位被除數存放在R04~R07單元中,不小于8000H的16位除數存放在R00~R01單元中,編寫出相應的除法計算指令系列。參考程序如下:
DIV3216A: ;特殊的32位除以16位運算子程序
;?32?bit?/?16?bit運算程序
;入口參數:32位被除數存放在R04~R07中,高位放在低地址
;不小于8000H的16位除數存放在R00~R01中,高位放在低地址
;出口參數:32位商放在R05(只有b0,即商的b16位有效)、R06、R07中;余數放在寄存器X中;算法:通過移位相減實現,由于除數不小于8000H,因此無須擴展被除數為48位;執(zhí)行時間為300個機器周期;使用資源:寄存器X,ALDA,#16BRESR02,#0 ;開始時移出位為0LOOP1:LDWX,R04 ;取被除數最高位SUBWX,R00 ;減除數JRNCNEXT1;有借位BTJTR02,#0,NEXT1;有借位,且移出位為0,不夠減,應保留原來的被減數 RCF ;商為0 JRTNEXT2NEXT1: LDWR04,X ;用差替換被除數高16位
SCF ;商為1NEXT2: RLCR07 ;移位
RLCR06 RLCR05 RLCR04 BCCMR02,#0 ;?C送R02的b0位
DECA ;循環(huán)次數減1 JRNELOOP1 ;最后一次減
LDWX,R04 ;取被除數最高位
SUBWX,R00 JRNCNEXT3
BTJTR02,#0,NEXT3 ;有借位,且移出位為0,說明不夠減,保留原來的差
LDWX,R04 RCF ;商為0 JRTNEXT4NEXT3: ;最后一次移位,形成商
SCF ;商為1NEXT4:
CLRR05 ;清除商的最高位
RLCR07 ;把最后運算結果移到商字節(jié)
RLCR06 RLCR05 RET在單片機應用系統(tǒng)中,有時用查表方式代替分支程序結構可能更簡單,不僅代碼短、運行速度快,程序維護、修改也非常方便,如例5-8所示。例5-8在某工程設計中,3個觸發(fā)器Q2、Q1、Q0狀態(tài)及其6個輸出量y5~y0變化規(guī)律如表5-1所示,表中未列出的狀態(tài)均屬無效態(tài)。表5-1轉換表如果采用條件跳轉指令的分支程序結構完成狀態(tài)判別及計算輸出量y5~y0的值將很復雜,而采用查表方式完成就非常簡單。根據表5-1狀態(tài)轉換對應的輸出量,不難得出二維數表,即b5~b0對應y5~y0,b6未使用規(guī)定為0,b7為“1”時表示無效態(tài),如下所示:Y5_Y0_TAB:;列編號為新狀態(tài)(n+1),行編號為當前狀態(tài)(n);01234567 dc.b01H,10H,02H,05H,05H,01H,06H,80H ;當前狀態(tài)(0) dc.b80H,00H,20H,80H,25H,21H,24H,80H ;當前狀態(tài)(1) dc.b80H,10H,00H,80H,05H,01H,04H,80H ;當前狀態(tài)(2) dc.b09H,80H,80H,01H,80H,80H,80H,80H ;當前狀態(tài)(3) dc.b80H,10H,0AH,80H,00H,08H,02H,80H ;當前狀態(tài)(4) dc.b80H,10H,02H,80H,04H,00H,06H,80H ;當前狀態(tài)(5) dc.b80H,10H,08H,80H,01H,09H,00H,80H ;當前狀態(tài)(6) dc.b80H,80H,80H,80H,80H,80H,80H,80H ;當前狀態(tài)(7)參考程序段如下:LDA,stu_Q2_0 ;取Q2~Q0當前狀態(tài)ANDA,#07HSLLASLLASLLA ;每個舊狀態(tài)對應8個新狀態(tài),因此要乘以8ADDA,R00 ;假設新狀態(tài)存放在R00單元中CLRWXLDXL,A ;舊—新狀態(tài)編號存放在寄存器X中LDA,(Y5_Y0_TAB,X) ;查表獲得舊—新狀態(tài)對應的輸出量CPA,#80HJRNENEXT1;遇到非法狀態(tài),可根據需要強制系統(tǒng)復位或不處理dc.b05Hdc.b05Hdc.b05Hdc.b05Hdc.b05H ;本例強制執(zhí)行非法指令使系統(tǒng)復位NEXT1:MOVstu_Q2_0,R00 ;更新狀態(tài)變量stu_Q2_0LDR00,A ;把輸出量送R00單元,以便通過位測試判別BTJFR00,#0,NEXT21;?R00的b0位即y0為1
;執(zhí)行y0為1的指令系列NEXT21:BTJFR00,#1,NEXT22;
R00的b1位即y1為1 ;執(zhí)行y1為1的指令系列NEXT22: ;省略5.4并行多任務程序結構及實現5.4.1串行多任務程序結構與并行多任務程序結構在串行多任務程序結構中,按預先設定的順序執(zhí)行各任務(即模塊),任何時候只執(zhí)行其中的一個任務,如圖5-7所示。
串行多任務程序結構簡單、清晰,編寫、調試比較容易,是單片機應用系統(tǒng)中最常用的程序結構之一。但在串行多任務程序結構中,只能通過查詢(如果滿足條件,則通過CALL及CALLF指令)和中斷方式執(zhí)行某些需要實時處理的事件,不適應于具有多個需要實時處理事件的應用系統(tǒng)。例如,在無線防盜報警器中,某防區(qū)報警時,第一要通過電話線將報警信息以DTMF方式發(fā)送到接警中心,或以語音方式通知用戶;第二要監(jiān)控其他防區(qū)有無被觸發(fā),即無線接收、解碼不能停頓;第三要監(jiān)視電話線狀態(tài),如忙音、回鈴音、被叫方提機、斷線等;第四是控制內置警笛的音量及音調。為此,在單片機應用系統(tǒng)中,有時需要用“實時(或稱為并行)多任務”程序結構。由于單片機系統(tǒng)內嵌RAM存儲器容量小,如STM8系列芯片的只有幾KB,沒有更多空間存放任務切換時需要保護的數據——斷點(即PC指針)、現場(CPU內寄存器,如累加器A、通用寄存器、標志寄存器等)和中間結果,因此決定了單片機應用系統(tǒng)并行多任務程序結構與一般微機、小型機并行多任務操作系統(tǒng)程序結構有所不同。圖5-7串行多任務程序結構5.4.2并行多任務程序結構并行多任務程序結構如圖5-8所示。圖5-8并行多任務程序結構把需要實時處理的多個任務排成一個隊列,通過隊列指針(也稱為任務號),借助散轉指令,如STM8的“JP(TAB_ADR,X)”指令(或條件轉移指令)實現任務間的切換。每個任務執(zhí)行時間長短不同,需將每一個任務細分為若干作業(yè)(或稱為子過程),不同任務的作業(yè)量不盡相同,即作業(yè)量與任務本身的復雜程度有關。例如,在圖5-8中的A任務,就分成A0、A1、A2、…、An,即n個作業(yè)。為此還需給每一個任務設置一個作業(yè)指針(或稱為作業(yè)號),切換到某一個任務后,執(zhí)行其中的哪一個作業(yè)由任務內的作業(yè)指針確定。在并行多任務程序結構中,各任務地位相同,每個任務內的作業(yè)地位也相同。并行多任務程序結構模塊清晰,能方便地增減其中任一個模塊。任務調度也很靈活,可根據當前作業(yè)的執(zhí)行結果選擇下一步將要執(zhí)行的任務號,非常適合于需要實時處理的多任務控制系統(tǒng),實用價值較高。在單片機并行多任務程序結構設計中,需要考慮的問題及注意事項如下:
(1)作業(yè)劃分原則。為減少任務、作業(yè)切換時需要保護的數據量,任務內的每一個作業(yè)必須是一個完整的子過程。對于執(zhí)行時間較長的任務,通過設置若干標志后,細分為多個作業(yè),使每個作業(yè)執(zhí)行時間不超出系統(tǒng)基本定時器的溢出時間。按上述原則劃分作業(yè)后,在作業(yè)處理過程中,除中斷外不被其他任務所中斷,作業(yè)執(zhí)行結束后,只需將處理結果保存到相應的RAM存儲單元中,對于初始化處理類作業(yè)根本無須保存結果;無須保護現場,即CPU各寄存器的值,如STM8的寄存器CC、A、X、Y等。
(2)任務切換方式。在微機、小型機實時多任務操作系統(tǒng)中,一般按設定順序執(zhí)行各任務,即每一個作業(yè)執(zhí)行結束后任務指針加1,當執(zhí)行到最后一個任務時將指針切換到第一個任務。在單片機控制系統(tǒng)中,一般不宜采用“定時時間到切換”規(guī)則,如果系統(tǒng)中沒有需要精確定時的事件,根本不需要定時器。原因是單片機系統(tǒng)時鐘頻率低,CPU響應速度慢,內嵌RAM容量有限,沒有更多空間存放任務切換時需要保存的大量數據。另外,在單片機應用系統(tǒng)中,控制對象屬性、控制對象所要執(zhí)行的操作又非常明確,完全可根據當前作業(yè)的執(zhí)行結果和系統(tǒng)的當前狀態(tài),直接切換到某個任務,以提高系統(tǒng)的實時性。這與十字路口交通燈切換時間最好由當前的車流量決定問題類似。例如,在報警器設計中,報警器上傳接警中心信息可分為三大類:布防、撤防及報警信息(包括旁路信息及系統(tǒng)狀態(tài)信息)。為減輕用戶話費負擔,這三類信息須分別撥打不同的電話號碼。對于布防、撤防信息來說,一般只需了解該信息來自哪一個用戶及其時間,完全可借助“來電顯示”功能感知信息來源與發(fā)生的時間(用FSK來電信息幀內嵌時間或接警中心內部時鐘),無須提機。對于布防、撤防以外的信息,如報警、旁路信息等,還需要進一步了解是哪個防區(qū)報警、旁路,什么樣的警情等,需要提機接收報警器上傳的全部信息。為此,在撥號前除了先根據緩沖區(qū)內信息類型確定撥打的電話號碼外,在撥號過程中還必須根據緩沖區(qū)內出現的新信息,決定是繼續(xù)撥號,還是掛機后撥打另一個號碼。為進一步提高系統(tǒng)的即時處理能力,除了在每個作業(yè)執(zhí)行結束后根據當前作業(yè)的執(zhí)行結果、系統(tǒng)當前狀態(tài)設置任務號外,還可在中斷服務程序中重新設置任務號。
(3)通過中斷方式響應需要實時處理的事件。在作業(yè)處理過程中,只能通過中斷方式(包括定時器溢出中斷、外部中斷等)響應需要實時處理的事件。為避免中斷服務程序執(zhí)行時間過長,降低系統(tǒng)對同級中斷請求響應的實時性,需要在中斷服務程序中引入事件驅動方式:響應某一個中斷請求后,僅設置事件發(fā)生標志或進行簡單處理后就退出,而事件執(zhí)行由事件處理程序完成,也就是說把中斷響應與事件處理過程分離,以提高系統(tǒng)的實時響應速度。例如,DTMF解碼芯片8870解碼有效DV信號接STM8SMCU的外中斷PD口的某一個引腳,則PD口中斷服務程序如下:InterruptPD_Interrupt_procPD_Interrupt_proc.L BSETPA_ODR,#6 ;解碼輸出允許OE接PA6引腳
LDA,PI_IDR ;解碼輸出引腳D3~D0分別接MCU的PI3~PI0 ANDA,#0FH ;屏蔽與解碼輸入無關位
ORA,#80H ;將DTMF輸入寄存器b7位置1,作為解碼輸入有效標志
LDDTMF_IN,A ;輸入數據放入DTMF_IN變量后就退出,至于輸入什么數據
;由解碼輸入處理程序判別和處理
BRESPA_ODR,#6 ;將8870解碼輸出置為高阻態(tài)
IRET系統(tǒng)中由主定時器控制的各定時時間必須呈整數倍關系,主定時器的溢出時間就是最小的定時時間。對于需要精確定時的事件,可放在系統(tǒng)主定時器中斷服務程序中計時,定時時間到,相應標志位置1,處理程序檢查相應標志來確定是否需要處理相應事件。例如,當主定時器溢出時間為10ms,而鍵盤定時掃描間隔為20ms時,那么,主定時器中斷服務程序內與鍵盤掃描定時有關的指令為
DECKEY_TIMEJRNEEXIT_K_T
;鍵盤掃描定時器減1,不為0跳轉BSETR_S_KEY,#0
;置位鍵盤掃描執(zhí)行標志R_S_KEY[0]MOVKEY_TIME,#2
;重置鍵盤掃描定時時間EXIT_K_T:
(4)子程序調用原則。各任務內任意一個作業(yè)均可調用同一個子程序,但不允許中斷服務程序調用,以免產生混亂。主定時器中斷服務程序應盡量短小,當遇到處理時間較長的事件時,可通過設置執(zhí)行標志后返回,在主程序任務調度處理過程中執(zhí)行。下面是具有4個任務的“實時多任務”程序結構:Segment'ram0'BytesTASKPds.b1 ;任務指針TASK0P ds.b1 ;任務0作業(yè)指針;TASK0P_TIMEds.b1 ;任務0執(zhí)行控制時間。對于有時間限制的任務,可設置
;任務執(zhí)行時間TASK1Pds.b1 ;任務1作業(yè)指針;TASK1P_TIMEds.b1 ;任務1執(zhí)行控制時間TASK2P ds.b1 ;任務2作業(yè)指針;TASK2P_TIMEds.b1 ;任務2執(zhí)行控制時間TASK3P ds.b1 ;任務3作業(yè)指針;TASK3P_TIMEds.b1 ;任務3執(zhí)行控制時間WordsSegment'rom'main.L;初始化部分MOVTASKP,#0 ;從任務0開始執(zhí)行MOVTASK0P,#0 ;任務0從作業(yè)0開始MOVTASK1P,#0 ;任務1從作業(yè)0開始
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024招投標企業(yè)誠信評價與廉潔承諾書3篇
- 證券信息化管理操作手冊
- 智能證券弱電綜合布線施工合同
- 實驗室租賃合同模板科研機構
- 飼料生產招投標實施細則
- 農村社區(qū)安全合同
- 咖啡館清潔施工合同范本
- 水泥生產項目招投標總結
- 通訊工程招標誠信承諾書模板
- 機械設備聯合體招投標合作協議
- 快樂讀書吧《愛的教育》復習小結(知識點)-統(tǒng)編版語文六年級上冊
- 2024年人教版初一生物(上冊)期末考卷及答案(各版本)
- 2024至2030年中國無糖壓縮餅干行業(yè)市場全景監(jiān)測及投資策略研究報告
- 食品安全追溯管理體系制度
- 作業(yè)治療學智慧樹知到答案2024年山東現代學院
- 律所之間轉委托合同范本
- 2023年湖南省普通高等學校對口招生考試機電類專業(yè)綜合知識試題附答題卡
- 2024年人教版初一信息技術上冊期末考試卷(附答案)
- DL-T-298-2011發(fā)電機定子繞組端部電暈檢測與評定導則
- 高密度聚乙烯外保護管聚氨酯發(fā)泡塑料預制直埋保溫管標準
- 水性聚氨酯生產加工項目規(guī)劃設計方案
評論
0/150
提交評論