8086_i386試驗箱綜合設計實驗
設計要求
利用實驗臺上的開關(K7-K4),實現步進電機的轉速、轉向控制。具體要求如下:
- 利用8255的PC3-PC0做輸出,輸出步進電機的相序、驅動步進電機工作(同時使用四個LED監視步進電機的相序訊號),相序之間的時間決定著步進電機的轉速,而間隔時間由延時程式中的CX暫存器的初值決定。
- 利用8255的PC7-PC4做輸入,與K7-K4連線。其中K7做步進電機的轉向控制,其餘位做步進電機的轉速控制。程式執行時通過K7-K4對步進電機實施動態控制。
- 利用8253做秒脈衝發生器,產生約2秒的週期性方波訊號。其中CNT0做分頻器:將1MHZ訊號分頻為100HZ;CNT1做秒脈衝輸出(0.5HZ)。
- 利用386模組的主8259的MIR5做中斷請求輸入,將CNT1的OUT1秒訊號方波作為中斷請求訊號,引發中斷服務ISR。
- 在中斷服務程式中實現對步進電機的轉速、轉向實時記錄儲存。方法如下:在ISR中,對D8255的PC7-PC4口進行一次輸入操作,並根據輸入的資料:
- 對D7(與K7對應)位的資料識別為步進電機的轉向控制;
- 對D6-D4(與K6-K4對應)位的資料識別為步進電機的轉速控制。
利用實驗臺上的8*8點陣顯示電路實現“小人跑步”的模擬。具體要求如下:
- 利用8255的PA7-PA0與8*8LED點陣顯示模組的“行線Ri”連線,控制著每一列的8個LED的顯示資料。
- 利用8255的PB7-PB0與8*8LED點陣顯示模組的“列線Ri”連線,列線訊號即為“位掃描”訊號,決定著8255的PA資料顯示在哪一列LED上。
- 利用8*8LED點陣顯示一個小人在運動的動態畫面,即顯示一個小人四肢在擺動。
- 實現小人運動的速度與電機轉速保持一致。即步進電機轉速快,則小人運動快,轉速慢,小人運動慢。
利用實驗臺上的六位數碼管鎖存器驅動動態顯示具體檔位。具體要求如下:
- 利用六位數碼管模組的CPU模式實現功能。
- 利用鎖存器(74LS73)電路實現一位共陰極數碼管動態顯示檔位。
- 實現六位數碼管鎖存器驅動動態顯示具體檔位,即由滑動開關確定檔位後,即時的顯示對應的檔位數字。
利用試驗檯的8253完成一個定時提示器。具體要求如下:
- 在CNT1的基礎上,利用8253的CNT2做負脈衝發生器(0.1HZ)。
- 指定計數初值,實現定時使邏輯筆閃爍,實現提示功能。
設計分析及系統方案設計
程式結構
主要程式組成如下結構圖。演算法描述
開關控制步進電機的轉速、轉向
步進電機驅動原理是通過對電機每相線圈施加某一順序電壓(電流)來使電機作步進式旋轉。驅動電路由脈衝訊號來控制,所以調節脈衝的頻率便可改變步進電機的轉速。由ULN2803八路達靈頓反向驅動器實現電機的電流驅動,只要在ULN2803的輸入端(BJ_IN1~BJ_IN4)提供相序訊號。經ULN2803反相後實現電機的驅動。ULN2803與步進電機的連結在實驗臺上已經完成。利用8255的低四位PC埠連線到ULN2803的輸入端,輸出脈衝序列,從而控制步進電機的旋轉。
程式設計中,為了實現更為便捷的操作,使用“雙四拍”相序訊號。於是將初始相序存放在一個暫存器中(原始相序資料位33H),然後利用對該暫存器“移位”的方式產生下一個相序。這種方式下,輸出脈衝的頻率決定著步進電機的旋轉速度,而對暫存器中的資料移位方向則決定著電機旋轉方向。
所以在程式設計中,通過讀取8255的PC口的高四位,以PC7作為方向,將其存放在方向資料變數direc,進而控制步進電機旋轉方向。而PC6~PC4中資料作為速度引數,這裡的速度引數存在程式中的速度資料變數speed中,而它又與步進電機相序輸出的延時有關。以這樣的方式,實現了對於步進電機的速度的調控。
在這裡需要指定8255的工作方式,向8255的控制口輸出10001000b,設定PA,PB口為輸出,PC高位輸入,低位輸出。
實際中,還需要利用8253來實現定時引發自定義的中斷。對於1MHZ時鐘進行兩次分頻,最終獲得週期為2s的輸出訊號,將這個訊號接到MIR5上,定時引發中斷。在中斷程式中實現對於開關訊號的讀取和轉存到對應的變數direc和speed內的功能。在編制與中斷相關的程式時,需要設定中斷遮蔽字、中斷向量表以及開中斷的操作。
在利用PC口進行讀寫操作時,這裡需要注意,由於高四位為輸入,低四位為輸出,這導致讀寫資料時會修改了其他埠的資料,這對於程式的正常執行是不利的。所以,需要使用“讀改寫”的操作,在寫資料的時候,先讀入資料,再保留高四位的基礎上,只寫入低四位資料,再利用OUT指令輸出。這樣就保證了資料的穩定性和程式的健壯性。
8*8點陣顯示
這裡要實現對於8*8點陣的操作,在連線好8255的PA與行介面,PB與列介面後,通過對PA與PB的寫資料,可以實現對具體LED的操作。為了實現小人跑步的模擬,需要先在資料段中準備好至少兩份不同的小人動作的圖形字元碼,即程式中的LED1與LED2。通過利用speed來控制8*8點陣的掃描速度,來實現小人的運動速度的控制。這裡利用speed來控制,也有利於點陣變化與步進電機變化的同步,更為合理的實現了“小人跑步”的概念設定。
8*8點陣圖案的繪製,首先需要設定最高位掃描碼,再通過對8255的PB口輸出0,來關閉掃描訊號,熄滅led,接下來通過向PA送列碼,向PB送對應的列上的行的資料,不斷地左移列碼,顯示行資料,這就是實現了圖案從左向右掃描不斷地顯示。而多次呼叫,顯示不同的圖案,從表面上來看,就是實現了小人的運動狀態的“模擬”。
六位數碼管顯示
程式使用的是CPU模式來操作一個七段數碼管,來顯示對應的資料的檔位。對於數碼管的操作,需要指定它的地址,這裡使用220h,這裡實際指定的是字型碼鎖存器,而掃描碼鎖存器地址為221h。實際的使用需要向220h中寫入對應的字形碼,指定顯示的數字,而向221h中寫入對應的掃描碼,指定使用的數碼管的位置。
程式設計中,先在資料段中指定0-3的字型碼,對應四個檔位。利用分支結構,實現了對應的字型碼的選擇。再對220h與221h中寫資料,進而實現對於不同檔位的顯示。
定時指示
為了實現對於“運動者”定時的提示,利用8253的CNT2手動指定初值,以及工作於方式2,來實現對於CNT1輸出的2s的訊號定時引發負脈衝的功能,將輸出接到邏輯筆上,進而實現了指示功能。
對8253的控制口輸出控制字0b4h,指定cnt2的工作方式。實現負脈衝發生器。
硬體電路圖
8255:用於配合CPU實現對8*8LED點陣,步進電機以及滑動開關的資料的相關讀寫操作。初始化命令字為10001000b,PA,PB口連結行列介面,控制8*8LED點陣,PC3~PC0連線BJ_IN4~BJ_IN1以及LED燈,用於控制步進電機和顯示相序,PC7~PC4接K7~K0,用於讀取開關資料。
8253:用於實現對於基準時鐘1MHZ的分頻輸出2s脈衝引發中斷以及指定時間輸出到邏輯筆。
數碼管:用於顯示對應的檔位數字。將地址接到230h即可
程式流程圖
程式清單
; 2018-3-27 22:57:16 ; BY: Lart Pang .model small .386 ;begin for variable of address io8253_0 equ 200h io8253_1 equ 201h io8253_2 equ 202h io8253_k equ 203h p8255_a equ 210h p8255_b equ 211h p8255_c equ 212h p8255_ctl equ 213h SMG equ 220h ;end for variable of address data segment ;begin for motor buf db 0 ;相序儲存 speed db 0 ;轉速控制 direc db 0 ;轉向控制 ;end for motor ;begin for 7-seg led ledcode db 3fh, 06h, 5bh, 4fh ;0-3的對應字形碼 ;end for 7-seg led ;begin for 8*8 led led1 db 88h, 44h, 24h, 1fh, 1fh, 24h, 44h, 88h led2 db 02h, 04h, 84h, 7fh, 7fh, 84h, 04h, 02h count dw 00h ;查表計數器 bz dw ? ;定義一個位掃描碼字的空間 ;end for 8*8 led data ends ss_seg segment stack dw 100 dup(0) ss_seg ends code segment assume cs:code, ds:data start: cli ;關中斷 mov ax, data ;指定資料段 mov ds, ax ;begin 8259 for interruption in al, 21h ;讀IMR暫存器 and al, 11011111b ;開放主片IR5中斷 out 21h, al ;裝載中斷到IMR push ds ;壓棧儲存資料 mov ax, 0 ;設定中斷向量 mov ds, ax lea ax, cs:int_proc ;AX指向中斷程式入口地址 mov si, 35h ;中斷型別碼35H add si, si ;35H*4H找到相應中斷向量表中的位置 add si, si mov ds:[si], ax ;裝入自定義中斷服務程式IP push cs ;將CS壓棧,用以獲取CS地址存到AX中 pop ax mov ds:[si+2], ax ;裝入自定義中斷服務程式CS:int_proc pop ds ;恢復資料 ;end 8259 for interruption ;begin 8255 for initialization mov dx, p8255_ctl ;設定A口方式0輸出,B口方式0輸出 mov al, 10001000b ;高C口方式0輸入,低C口方式0輸出 out dx, al ;end 8255 for initialization ;begin for setting the initial phase sequence mov buf, 33h ;初始相序33h = 0011 0011,這裡是用"雙四拍" ;相序驅動電機,故實際使用33h ;end for setting the initial phase sequence ;begin 8253 for frequency division ;第一次分頻 1M-->1K mov dx, io8253_k ;cnt0 雙數 方式3 二進位制 mov al, 36h ;00 11 011 0 out dx, al ;方波發生器,方式3 mov dx, io8253_0 ;cnt0產生1000Hz(1ms)的時鐘 mov ax, 1000 ;分高低位寫入1000 out dx, al mov al, ah out dx, al ;第二次分頻 1K-->0.5 mov dx, io8253_k ;cnt1 雙數 方式3 二進位制 mov al, 76h ;01 11 011 0 out dx, al mov dx, io8253_1 ;cnt1產生0.5Hz的時鐘 mov ax, 2000 ;分高低位寫入2000 out dx, al mov al, ah out dx, al ;計時 2s->10s mov dx, io8253_k ;cnt2 雙數 方式2 二進位制 mov al, 0b4h ;10 11 010 0 out dx, al mov dx, io8253_2 ;cnt2產生指定時間的負脈衝 mov ax, 5 ;分高低位寫入指定資料 out dx, al ;這裡指定為5 mov al, ah out dx, al ;end 8253 for frequency division ;begin main program mov dx, p8255_c ;讀取8255C口內容 in al, dx ;由於C口既有輸入又有輸出,所以為了不會干擾 and al, 11110000b ;在寫資料的時候,要保證不會改動輸入的口的資料 mov bl, buf and bl, 00001111b ;只寫buf的低四位到al裡 add al, bl ;這裡需要在低四位寫資料,所以保留高四位。 out dx, al ;將資料傳送 loop_main: cmp direc, 0 ;比較direc,決定相序正向輸出還是反向輸出 je direc_0 ;這裡的方向控制實際上就是通過改變相序的迴圈方向 ;進而改變的 mov al, buf ;載入相序 ror al, 1 ;迴圈右移一位 jmp direc_1 direc_0: mov al, buf rol al, 1 ;迴圈左移一位 direc_1: ;移動完成後,進行後續操作 mov buf, al mov dx, p8255_c ;讀取8255C口內容 in al, dx ;由於C口既有輸入又有輸出,所以為了不會干擾 and al, 11110000b ;在寫資料的時候,要保證不會改動輸入的口的資料 mov bl, buf and bl, 00001111b ;只寫buf的低四位到al裡 add al, bl ;這裡需要在低四位寫資料,所以保留高四位。 out dx, al ;將資料傳送 sti ;開中斷 ;begin 8*8 led & 7-seg digital tube mov di, offset led1 ;設di為字型碼緩衝區(指向首地址) call display mov di, offset led2 ;設di為字型碼緩衝區(指向首地址) call display call disp_shumaguan ;end 8*8 led & 7-seg digital tube jmp loop_main ;end main program ;begin for interrupt service program int_proc proc far push ax ;保護現場 push cx push dx mov dx, p8255_c ;讀8255C口,修改方向 in al, dx ;讀取埠資訊 and al, 80h ;1000 0000保留方向位 mov direc, al ;將方向存入方向儲存單元 mov dx, p8255_c ;再讀8255C口,修改速度 in al, dx ;讀取埠資訊 and al, 01110000b ;0111 0000保留速度檔位 mov speed, al ;將速度檔位資訊存入儲存單元 mov al, 20h ;發EOI結束命令 out 20h, al pop dx ;恢復現場 pop cx pop ax sti ;開中斷 iret int_proc endp ;end for interrupt service program ;begin for 8*8 led display display proc push bx ;保護現場 push ax push dx mov count, 0000h ;計數器初始值0 mov bh, 01h ;產生最高位掃描碼 mov al, 0 ;掃描碼消失(熄滅) mov dx, p8255_b ;指向b口 out dx, al ;輸出掃描碼熄滅數碼管 again: mov byte ptr bz, bh ;將位掃描碼位元組送儲存空間bz push di ;儲存變數指標 add di, count ;修改變數資料指標(基地址+偏移量) mov bl, byte ptr [di] ;查表得到變數資料送bl pop di ;恢復變數指標 mov al, bl mov dx, p8255_a ;指向a口 out dx, al ;輸出掃描碼 mov al, byte ptr bz ;使相應的數碼管亮 mov dx, p8255_b out dx, al ;顯示字型 inc count push cx ;保護CX mov dh, speed ;設定延時 dh_0_disp: mov cx, 30h ;設定迴圈延時次數 loop_disp: loop loop_disp dec dh jnz dh_0_disp pop cx ;恢復CX mov al, 0 ;掃描碼消失(熄滅) mov dx, p8255_b ;指向b口 out dx, al ;輸出掃描碼熄滅數碼管 mov bh, byte ptr bz ;取出掃描碼 shl bh, 1 ;將掃描碼右移一位 jnz again ;如果不等於0就跳轉again pop dx ;恢復現場 pop ax pop bx ret display endp ;end for 8*8 led display ;begin for 7-seg digital tube disp_shumaguan proc push bx ;保護現場 push ax push dx mov al, speed ;讀取已經儲存好的速度資訊 cmp al, 01000000b ;第一檔 jz al_1 cmp al, 00100000b ;第二檔 jz al_2 cmp al, 00010000b ;第三檔 jz al_3 ;al=0 mov si, 0 ;第0檔:最慢,因為其間隔時間最長 jmp next_0 al_1: mov si, 1 jmp next_0 al_2: mov si, 2 jmp next_0 al_3: mov si, 3 next_0: lea bx, ledcode ;將數碼管的字形碼地址賦給BX裡 mov bl, [bx+si] ;將對應的字形碼存放到BL裡 mov al, bl mov dx, SMG ;將數碼管的地址存到DX裡 out dx, al mov al, 01h ;設定數碼管的位碼,這裡使用第一個數碼管 inc dx ;數碼管的位碼,在CPU模式下 ;要存在數碼管地址的下一地址 out dx, al pop dx ;恢復現場 pop ax pop bx ret disp_shumaguan endp ;end for 7-seg digital tube code ends end start ;程式結束
實驗結果與分析
- 系統執行環境:
- 硬體:Windows XP 32位、386EX試驗箱
- 軟體:HQFC整合開發環境
- 設計語言:彙編
- 除錯遇到的問題
- 由於使用了8*8點陣,導致只能將8255的介面PC分高低位用於不同的工作方式。在按此,高四位接好滑動開關,低四位接好步進電機後,執行程式,發現顯示結果不正常。
- 在最後,準備利用8253分頻配合邏輯筆,實現定時提示功能,程式碼寫好,介面連線好後,發現邏輯筆始終顯示低電平。
- 對應的解決辦法
- 因為在程式中的讀寫PC口的時候,沒有考慮到每次的寫操作,是會影響到另外的四個口的資料的。所以在原來向PC口寫資料的基礎上,進行了進一步的處理,即“讀改寫”操作,先讀回來原來的資料,只修改低四位,改為想要傳送的資料,再寫回去。這樣就避免了互相干擾的問題,最終恢復正常了。
- 仔細檢查程式碼後,發現,原來是CNT2相應的命令字寫錯了。對於2號計數器的指定出了錯誤,應該是0b4h,結果寫成了0e4h。這裡主要是因為對於16進位制數字不熟悉的結果,導致資訊錯亂,沒有輸到想要的位置上。
系統執行結果
- 由下圖可見,最終實現了檔位指示,小人跑步模擬,步進電機的轉動以及定時提示。
- 系統執行環境:
結論及設計體會
最終,完成了自己的設計,實現了“跑步機”的模擬。
最終完成的功能有:8*8LED點陣動態顯示小人運動,一位七段數碼管顯示對應的速度檔位,而利用滑動開關,實現跑步機(步進電機)的正反轉和檔位的控制,同時,利用分頻實現定時功能,可以在指定時間輸出負脈衝,將邏輯筆的指示燈切換,最終實現了定時提示的功能。
這個實驗持續了兩三週的時間,之前由於有考試,主要的精力沒有放在這個上面,只是先做好了步進電機的基本功能,還未拓展點陣和數碼管的功能。不過還好,最後還是趕上來了。
這個思路很好,在一個小的,課本的例子的基礎上,不斷擴充套件,不斷增加更多的功能,這樣也讓我在不斷地學習新的模組的知識,同時不同模組之間的配合,也在考驗著我的能力,因為並不是把模組拿來接好就可以用的,總是會遇到各種各樣的問題,需要我們不斷地取尋找,解決。
這時,漸近式的程式設計就顯得尤為重要。保留好原始碼,先修改一部分,測試,再修改,再測試,不斷地反覆,直到效果滿足預期要求,否則就不能拿來用,只能使用原來的未修改的原始碼。這裡也提示了我們,及時存檔的重要性。
總的看來,磕磕絆絆,但還是走到了終點。
本次試驗最遺憾的事情就是12864沒用上。為了它我花費了半天時間,查資料,諮詢老師,研究了半天,但最終也還是也沒有用上12864。不過還是感謝老師的指導。
這次實驗學到了很多,包括各種模組的使用,介面操作,中斷操作,這都是在程式設計中使用到的,以及對於各種問題的除錯解決方法,甚至對於程式設計也有了進一步的認識。彙編這門語言也因此而熟練了許多。感覺獲益匪淺。