1. 程式人生 > >8086_i386試驗箱綜合設計實驗

8086_i386試驗箱綜合設計實驗

設計要求

  1. 利用實驗臺上的開關(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對應)位的資料識別為步進電機的轉速控制。
  2. 利用實驗臺上的8*8點陣顯示電路實現“小人跑步”的模擬。具體要求如下:

    • 利用8255的PA7-PA0與8*8LED點陣顯示模組的“行線Ri”連線,控制著每一列的8個LED的顯示資料。
    • 利用8255的PB7-PB0與8*8LED點陣顯示模組的“列線Ri”連線,列線訊號即為“位掃描”訊號,決定著8255的PA資料顯示在哪一列LED上。
    • 利用8*8LED點陣顯示一個小人在運動的動態畫面,即顯示一個小人四肢在擺動。
    • 實現小人運動的速度與電機轉速保持一致。即步進電機轉速快,則小人運動快,轉速慢,小人運動慢。
  3. 利用實驗臺上的六位數碼管鎖存器驅動動態顯示具體檔位。具體要求如下:

    • 利用六位數碼管模組的CPU模式實現功能。
    • 利用鎖存器(74LS73)電路實現一位共陰極數碼管動態顯示檔位。
    • 實現六位數碼管鎖存器驅動動態顯示具體檔位,即由滑動開關確定檔位後,即時的顯示對應的檔位數字。
  4. 利用試驗檯的8253完成一個定時提示器。具體要求如下:

    • 在CNT1的基礎上,利用8253的CNT2做負脈衝發生器(0.1HZ)。
    • 指定計數初值,實現定時使邏輯筆閃爍,實現提示功能。

設計分析及系統方案設計

  1. 程式結構
    主要程式組成如下結構圖。

    結構圖

  2. 演算法描述

    • 開關控制步進電機的轉速、轉向

      步進電機驅動原理是通過對電機每相線圈施加某一順序電壓(電流)來使電機作步進式旋轉。驅動電路由脈衝訊號來控制,所以調節脈衝的頻率便可改變步進電機的轉速。由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的工作方式。實現負脈衝發生器。

  3. 硬體電路圖

    • 8255:用於配合CPU實現對8*8LED點陣,步進電機以及滑動開關的資料的相關讀寫操作。初始化命令字為10001000b,PA,PB口連結行列介面,控制8*8LED點陣,PC3~PC0連線BJ_IN4~BJ_IN1以及LED燈,用於控制步進電機和顯示相序,PC7~PC4接K7~K0,用於讀取開關資料。

      8255連線方式示意圖

    • 8253:用於實現對於基準時鐘1MHZ的分頻輸出2s脈衝引發中斷以及指定時間輸出到邏輯筆。

      8253連線方式示意圖

    • 數碼管:用於顯示對應的檔位數字。將地址接到230h即可

      數碼管示意圖

  4. 程式流程圖

    主程式以及中斷服務程式流程圖

    8*8LED顯示以及七段數碼管顯示子程式

  5. 程式清單

    ; 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               ;程式結束
  6. 實驗結果與分析

    • 系統執行環境:
      • 硬體:Windows XP 32位、386EX試驗箱
      • 軟體:HQFC整合開發環境
    • 設計語言:彙編
    • 除錯遇到的問題
      • 由於使用了8*8點陣,導致只能將8255的介面PC分高低位用於不同的工作方式。在按此,高四位接好滑動開關,低四位接好步進電機後,執行程式,發現顯示結果不正常。
      • 在最後,準備利用8253分頻配合邏輯筆,實現定時提示功能,程式碼寫好,介面連線好後,發現邏輯筆始終顯示低電平。
    • 對應的解決辦法
      • 因為在程式中的讀寫PC口的時候,沒有考慮到每次的寫操作,是會影響到另外的四個口的資料的。所以在原來向PC口寫資料的基礎上,進行了進一步的處理,即“讀改寫”操作,先讀回來原來的資料,只修改低四位,改為想要傳送的資料,再寫回去。這樣就避免了互相干擾的問題,最終恢復正常了。
      • 仔細檢查程式碼後,發現,原來是CNT2相應的命令字寫錯了。對於2號計數器的指定出了錯誤,應該是0b4h,結果寫成了0e4h。這裡主要是因為對於16進位制數字不熟悉的結果,導致資訊錯亂,沒有輸到想要的位置上。
    • 系統執行結果

      • 由下圖可見,最終實現了檔位指示,小人跑步模擬,步進電機的轉動以及定時提示。

      實驗結果圖

  7. 結論及設計體會

    最終,完成了自己的設計,實現了“跑步機”的模擬。

    最終完成的功能有:8*8LED點陣動態顯示小人運動,一位七段數碼管顯示對應的速度檔位,而利用滑動開關,實現跑步機(步進電機)的正反轉和檔位的控制,同時,利用分頻實現定時功能,可以在指定時間輸出負脈衝,將邏輯筆的指示燈切換,最終實現了定時提示的功能。

    這個實驗持續了兩三週的時間,之前由於有考試,主要的精力沒有放在這個上面,只是先做好了步進電機的基本功能,還未拓展點陣和數碼管的功能。不過還好,最後還是趕上來了。

    這個思路很好,在一個小的,課本的例子的基礎上,不斷擴充套件,不斷增加更多的功能,這樣也讓我在不斷地學習新的模組的知識,同時不同模組之間的配合,也在考驗著我的能力,因為並不是把模組拿來接好就可以用的,總是會遇到各種各樣的問題,需要我們不斷地取尋找,解決。

    這時,漸近式的程式設計就顯得尤為重要。保留好原始碼,先修改一部分,測試,再修改,再測試,不斷地反覆,直到效果滿足預期要求,否則就不能拿來用,只能使用原來的未修改的原始碼。這裡也提示了我們,及時存檔的重要性。

    總的看來,磕磕絆絆,但還是走到了終點。

    本次試驗最遺憾的事情就是12864沒用上。為了它我花費了半天時間,查資料,諮詢老師,研究了半天,但最終也還是也沒有用上12864。不過還是感謝老師的指導。

    這次實驗學到了很多,包括各種模組的使用,介面操作,中斷操作,這都是在程式設計中使用到的,以及對於各種問題的除錯解決方法,甚至對於程式設計也有了進一步的認識。彙編這門語言也因此而熟練了許多。感覺獲益匪淺。