# 實驗一 內部RAM與Flash空間定址與存取 ## 基礎題 請利用Keil c51的各種功能,找出問題的答案。 ### 位元定址法(Bit addressing) ```x86asm= ORG 0 ; code start from 0 MOV 20H, #00H ; address 20H = 00H SETB 20H.1 ; address 20H bit 1 = 1 MOV A, 20H ; A = ? SETB 03H ; bit address 03H = 1 MOV A, 20H ; A = ? SJMP $ ; infinite loop END ``` 1. A = 0x02 2. A = 0x0a ### 立即定址法(Bit addressing) ```x86asm= ORG 0 ;code start from 0 MOV R0, #30H ;R0 = 30H MOV R1, #10H ;R1 = 10H MOV 30H, #55H ;address 30H = 55H MOV 31H, #24H ;address 31H = 24H MOV A, #31H ;A = ? SJMP $ ;infinite loop END ``` A = 0x31 ### 直接定址法(Direct addressing) ```x86asm= ORG 0 ;code start from 0 MOV R0, #30H ;R0 = 30H MOV R1, #10H ;R1 = 10H MOV 30H, #55H ;address 30H = 55H MOV 31H, #24H ;address 31H = 24H MOV A, 30H ;A = ? SJMP $ ;infinite loop END ``` A = 0x55 ### 間接定址法(Indirect addressing) ```x86asm= ORG 0 ;code start from 0 MOV R0, #30H ;R0 = 30H MOV R1, #10H ;R1 = 10H MOV 30H, #55H ;address 30H = 55H MOV 31H, #24H ;address 31H = 24H MOV A, @R0 ;A = ? SJMP $ ;infinite loop END ``` A = 0x55 ### 暫存器定址法(Register addressing) ```x86asm= ORG 0 ;code start from 0 MOV R0, #30H ;R0 = 30H MOV R1, #10H ;R1 = 10H MOV 30H, #55H ;address 30H = 55H MOV 31H, #24H ;address 31H = 24H MOV A, R0 ;A = ? SJMP $ ;infinite loop END ``` A = 0x30 ### 索引定址法(Index addressing) ```x86asm= ORG 0 ;code start from 0 MOV DPTR, #TABLE ;DPTR = 'TABLE' label address MOV A, #01H ;A = 01H MOVC A, @A+DPTR ;A = ? SJMP $ ;infinite loop TABLE: DB 01H ;save 01H at 'TABLE'address DB 02H ;save 02H at 'TABLE' label + 1 address END ``` A = 0x02 ## 進階題 請利用Keil C51的各種功能,找出問題的答案,總共12題。 ### A. ```x86asm= ORG 0 START: MOV R0, #01H MOV R1, #0FFH SETB RS0 ; switch from RB0 to RB1 MOV R0, 00H MOV A, 01H MOV A, R0 MOV A, 08H MOV A, @R0 CLR RS0 ; switch from RB1 to RB0 MOV A, R0 SJMP $ END ``` 1. 第6行,A = ? Hint: 01H為暫存器庫0 R1的位址。 A=0xff 3. 第7行,A = ? Hint: R0此時為暫存器庫1的R0。 A=0x01 5. 第8行,A = ? Hint: 08H為暫存器庫1 R0的位址。 A=0x01 7. 第9行,A = ? Hint: R0內數值位址所儲存的資料。 A=0xff 9. 第11行,A = ? Hint: R0此時為暫存器庫0的R0。 A=0x01 Hint:暫存器庫 0 的 R0~R7 對應的內部 RAM 位址為 00H~07H,暫存器庫 1 的 R0~R7 對應的內部 RAM 位址為 08H~0FH。 ### B. 6. 第2行,A=? A=0x11 8. 承(6),該指令長度為多少 bytes? 佔 2 bytes 10. 第3行,A=? A=0x22 12. 承(8),該指令長度為多少 bytes? 佔 3 bytes 14. 承 (7)、(9),為何此兩種指令達成的功能相同,但指令長度卻不一致? A) 因為兩者根本是不同指令,只是兩種指令達成同樣的效果。 B) 因為兩指令在 Flash 空間的位址不同。 C) 因為 MOV 0E0H, #22H 的字數比 MOV A, #11H 還多。 A Hint:指令長度可以觀察 disassembly window 將前後位址相減,或者是查詢 8051 指令集獲得,由於 8051 為複雜指令集,代表著每個指令的長度可能不同,通常 要用到直接位址的指令會比用暫存器的指令還長,assembler 會將指令轉換後長 度不一的 machine code 從 ORG 指定的位址依序填入至 Flash 當中。 ### C. ```x86asm= ORG 0 MOV 20H, #10H SETB 20H.2 MOV 30H, #20H SETB 30H.1 SJMP $ END ``` 11. 為何此段程式碼無法成功編譯? A) 因為不存在 SETB 指令。 B) 因為 SETB XXH.X 的位址表示形式不合乎語法。 C) 因為 30H 位址不是 bit addressable area。 C Hint:觀察錯誤的行數以及 Build Output 視窗的內容。 ### D. ```x86asm= ORG 0 MOV DPTR, #TABLE START: MOV R7, #04H LOOP: MOV A, R7 MOVC A, @A+DPTR DJNZ R7, LOOP SJMP START TABLE: DB 5BH DB 7CH DB 1CH DB 1CH DB 3FH END ``` 12. 請依照程式碼順序,寫出 A+DPTR 位址在程式記憶體所儲存的數值。 3F -> 1C -> 1C -> 7C Hint:該數值將會被儲存至 A 暫存器,只須按照順序寫一次循環即可,可能出現的數值有 5BH、7CH、1CH、3FH。 # 實驗二 GPIO輸出驅動LED及七段顯示器 ## 基礎題 ### LED 跑馬燈實驗 程式碼 ```x86asm= ORG 00H ;start address 00H MOV A, #11111110B ;set A to 11111110B LOOP: MOV P1, A ;move A to P1 CALL DELAY ;delay 0.5 sec RL A ;rotate left SJMP LOOP ;main loop DELAY: MOV R0, #4 ;loop 4 times LOOP1: MOV R1, #250 ;loop 250 times LOOP2: MOV R2, #250 ;loop 250 times DJNZ R2, $ ;decrement R2 until 0 DJNZ R1, LOOP2 ;decrement R1 until 0 DJNZ R0, LOOP1 ;decrement R0 until 0 RET ;return from subrountine END ;end of program ``` #### 實驗結果影片 {%youtube TFnz0IX4AiQ %} ### 四合一七段顯示器實驗 程式碼 ```x86asm= ORG 00H ;start address is 00H MOV DPTR, #TABLE ;DPTR point to TABLE START: MOV R0, #4 ;4 LED MOV R1, #0 ;table index MOV R2, #0FEH ;LED drive pin(c0~c3) LOOP: MOV P2, R2 ;select LED MOV A, R1 ;move R1 to A MOVC A, @A+DPTR ;get value from table MOV P1, A ;move value to P1 CALL DELAY ;delay 0.5 ms INC R1 ;increase R1 MOV A, R2 ;move R2 to A RL A ;rotate A MOV R2, A ;move A back to R2 DJNZ R0,LOOP ;decrement R0 until 0 SJMP START ;jump to start DELAY: MOV R3, #250 ;loop 250 times DJNZ R3, $ ;decrement R3 until 0 RET ;return from subroutine TABLE: DB 0C0H ;0 DB 0F9H ;1 DB 0A4H ;2 DB 0B0H ;3 END ;end of program ``` #### 實驗結果影片 ## 進階題 修改四合一七段顯示器實驗之程式,使四合一七段顯示器顯示出自己的學號後四碼。 學號:N9612**1503** ```x86asm= ORG 00H ;start address is 00H MOV DPTR, #TABLE ;DPTR point to TABLE START: MOV R0, #4 ;4 LED MOV R1, #0 ;table index MOV R2, #0FEH ;LED drive pin(c0~c3) LOOP: MOV P2, R2 ;select LED MOV A, R1 ;move R1 to A MOVC A, @A+DPTR ;get value from table MOV P1, A ;move value to P1 CALL DELAY ;delay 0.5 ms INC R1 ;increase R1 MOV A, R2 ;move R2 to A RL A ;rotate A MOV R2, A ;move A back to R2 DJNZ R0,LOOP ;decrement R0 until 0 SJMP START ;jump to start DELAY: MOV R3, #250 ;loop 250 times DJNZ R3, $ ;decrement R3 until 0 RET ;return from subroutine TABLE: DB 0F9H ;1 DB 092H ;5 DB 0C0H ;0 DB 0B0H ;3 END ;end of program ``` ### 實驗結果照片 # 實驗三 GPIO輸入 ## 基礎題 程式碼 ```x86asm= ORG 0 ; start from 0000H MOV DPTR, #TABLE ; DPTR point to TABLE START: MOV R0, #0 ; initialize typed signal MOV R1, #3 ; set 3 row MOV R2, #11110111B ; set first row SCAN: MOV A, R2 ; move R2 to A MOV P2, A ; input scanned signal JNB P2.7, KEYIN ; scan column 1 INC R0 ; add #01 into R0 JNB P2.6, KEYIN ; scan column 2 INC R0 ; add #01 into R0 JNB P2.5, KEYIN ; scan column 3 RR A ; next row MOV R2, A ; move A to R2 DJNZ R1, SCAN ; scan 3 rows SJMP START ; jump to scan keypad again KEYIN: CALL DEBOUNCE ; call debounce function MOV A, R0 ; get typed signal MOVC A, @ A+DPTR ; according A, get signal from TABLE MOV P1, A ; output displayed signal SJMP START ; jump to scan keypad again DEBOUNCE: MOV R4, #50 ; move 50 times into R4 DE_500_us: MOV R3, #250 ; move 250 times into R3 DJNZ R3, $ ; jump to itself R3 times DJNZ R4, DE_500_us ; delay 25ms RET ; return to main code TABLE: DB 11111001B ; display “1” DB 10110100B ; display “2” DB 10110000B ; display “3” DB 10011001B ; display “4” DB 10100100B ; display “5” DB 10000010B ; display “6” DB 11011000B ; display “7” DB 10000000B ; display “8” DB 10010000B ; display “9” END ``` ### 實驗結果影片 {%youtube lKSrgAGyhQo%} ## 進階題 掃描讀取 44 鍵盤上每㇐個按壓訊號。除了基礎題中的 1~9,還有鍵盤上其他的符號(0、A、b、c、d、E、F,注意大小寫)。 若 4*4 鍵盤有「」和「#」符號,其中「」顯示 E,「#」顯示 F。 程式碼 ```x86asm= ORG 0 ; start from 0000H MOV DPTR, #TABLE ; DPTR point to TABLE START: MOV R0, #0 ; initialize typed signal MOV R1, #4 ; set 4 row MOV R2, #11110111B ; set first row SCAN: MOV A, R2 ; move R2 to A MOV P2, A ; input scanned signal JNB P2.7, KEYIN ; scan column 1 INC R0 ; add #01 into R0 JNB P2.6, KEYIN ; scan column 2 INC R0 ; add #01 into R0 JNB P2.5, KEYIN ; scan column 3 INC R0 ; add #01 into R0 JNB P2.4, KEYIN ; scan column 4 RR A ; next row MOV R2, A ; move A to R2 DJNZ R1, SCAN ; scan 4 rows SJMP START ; jump to scan keypad again KEYIN: CALL DEBOUNCE ; call debounce function MOV A, R0 ; get typed signal MOVC A, @ A+DPTR ; according A, get signal from TABLE MOV P1, A ; output displayed signal SJMP START ; jump to scan keypad again DEBOUNCE: MOV R4, #50 ; move 50 times into R4 DE_500_us: MOV R3, #250 ; move 250 times into R3 DJNZ R3, $ ; jump to itself R3 times DJNZ R4, DE_500_us ; delay 25ms RET ; return to main code TABLE: DB 11111001B ; display “1” DB 10100100B ; display “2” DB 10110000B ; display “3” DB 10001000B ; display “A” DB 10011001B ; display “4” DB 10010010B ; display “5” DB 10000010B ; display “6” DB 10000011B ; display “b” DB 11011000B ; display “7” DB 10000000B ; display “8” DB 10010000B ; display “9” DB 10100111B ; display “c” DB 10001110B ; display “F” DB 11000000B ; display “0” DB 10000110B ; display “E” DB 10100001B ; display “d” END ``` ### 實驗結果影片 {%youtube 0vTI97Aj35E%} # 實驗四 算術及邏輯運算指令 ## 基礎題 ### 算術運算實驗 (32H − 6H) × 7H ```x86asm= ORG 0 MOV A, #032H MOV B, #006H SUBB A, B MOV B, #007H MUL AB END ``` 1. A = 0x34 2. B = 0x01 (64H + DH) ÷ 6H ```x86asm= ORG 0 MOV A, #064H MOV B, #00DH ADD A, B MOV B, #006H DIV AB END ``` 1. A = 0x12 2. B = 0x05 ### 邏輯運算實驗 程式碼 ```x86asm= ORG 0 ; start from 0000H MOV A, #10111101B ; move 10111101B into ACC ANL A, #11101111B ; use AND Gate to turn Bit 4 into 0 MOV B, A ; move the value into B MOV A, #01000010B ; move 01000010B into ACC ORL A, #00010000B ; use OR Gate to turn Bit 4 into 1 CPL A ; use NOT Gate END ``` ## 進階題 掃描讀取 4*4 鍵盤之按壓輸入訊號,並在單顆七段顯示器上顯示四則運算的結果。鍵盤上 1~9表示要計算的數字,先從中隨機按下2鍵,再按下鍵盤 A、B、C、D 代表加、 減、乘、除。只有除法有先後順序差異,且只需考慮正整數運算。本實驗器材中的單顆七段顯示器為共陽極,其腳位如圖。 範例: • 先按下鍵盤上的9,再按下7。 • 按下鍵盤上的A,則執行 9 + 7 = 16 的運算,先將和 16 的十位數1顯示在七段顯示器上,延遲 250 毫秒後,顯示個位數 6。 • 按下鍵盤上的B,則執行 9 − 7 = 2 的運算,並將差2顯示在七段顯示器上。 • 按下鍵盤上的 C,則執行 9 × 7 = 63 的運算,先將積 63 的十位數6顯示在七段顯示器上,延遲 250 毫秒後,顯示個位數 3。 • 按下鍵盤上的 D,則執行 9 ÷ 7 = 1 … 2 的運算,先將商 1 顯示在七段顯示器上,延遲 250 毫秒後,顯示餘數 2。 Hint : 若先按下7再按下9,按下鍵盤上的 D後,則執行7 ÷ 9 = 0 … 7。 Hint:請使用加減乘除的指令,並利用查表法找到要顯示的數字,不要直接把答案移入七段顯示器。 ### 實驗結果影片 {%youtube %} # 實驗五 程式計數器與堆疊 ## 基礎題 程式碼 ```x86asm= ORG 0000h JMP LOOP ;jump into loop ORG 0030h LOOP: MOV SP, #32H ;SP = #32H MOV A, #0xfe ;A = #0xfe MOV P2, A ;P2 = A SETB P1.0 ;set p1.0 to high MOV R0, #8d ;set the execution times of marquee JNB P1.0, MARQUEE ;jump into marquee when p1.0 is low JMP LOOP ;infinite loop MARQUEE: PUSH 00h ;push the value of R0 into stack MOV R0, #250d ;set the execution times of DELAY1 CALL DELAY1 ;call DELAY1 POP 00h;pop out the value of R0 which is pushed in line 9 RL A ;left rotate A(left shift) MOV P2, A ;set the value of A into P2 DJNZ R0, MARQUEE;loop back until MARQUEE execute 8 times JMP LOOP ;end of MARQUEE, back to LOOP DELAY1: PUSH 00h;push the remain times of DELAY1 into stack MOV R0, #250d ;set the execution times of DELAY2 CALL DELAY2 ;call DELAY2 POP 00h;pop the remain times of DELAY1 back to R0 DJNZ R0, DELAY1 ;loop until R0 is 0 RET ;return to MARQUEE DELAY2: DJNZ R0, DELAY2 ;loop untie R0 is 0 RET ;return to DELAY1 END ``` ### 實驗結果影片 {%youtube 3lrC8niMt2w%} ## 進階題 請達成以下要求: - 當8051通電後,連接於P2的8個LED開始以跑馬燈的方式依序點亮,一次亮兩顆。 範例:O表示亮的LED,X表示暗的。 第零秒 XXXXXXOO 第一秒 XXXXXOOX 第二秒 XXXXOOXX 第七秒 OXXXXXXO 第八秒 XXXXXXOO - 於P1連接上兩顆按鈕,一顆為PUSH功能鈕,另一顆則為POP功能鈕。PUSH鈕的功能為將目前的跑馬燈狀態儲存進堆疊中;POP鈕的功能則為將儲存在堆疊的跑馬燈狀態依序展現出來。 範例:我在以下的燈號狀態下按下PUSH鈕 XXXXOOXX XXXXXXOO XOOXXXXX OXXXXXXO XOOXXXXX 當我按下POP鈕後,跑馬燈會停止,且LED會以以下順序點亮 XOOXXXXX OXXXXXXO XOOXXXXX XXXXXXOO XXXXOOXX Hint: - 本題沒有要求要檢查堆疊的東西是否POP完,因此按下POP鈕後除非RESET 8051,否則LED不會再次進入跑馬燈狀態。 - 因為是使用按鈕,請記得程式中要有DEBOUNCE的功能,以免按下PUSH鈕後多次儲存重複的資料到堆疊。 - 跑馬燈的部分不建議沿用基礎題的程式,除非對基礎題的程式流程十分了解,否則最好自己重寫一個。 - P2的記憶體位址是A0H。 ### 實驗結果影片 {%youtube iy5ZN5JzvPQ %} # 實驗六 中斷(Interrupt) ## 基礎題 利用一顆外接按鈕實現外部中斷,並藉由 LED 之閃爍呈現結果。 程式碼 ```x86asm= ORG 00H ; code start from 000h SJMP MAIN ; jump to MAIN ORG 03H ; vector address for INT0 SJMP INT0_ISR ; jump to INT0_ISR ORG 030H ; after vector table space MAIN: MOV IE, #10000001B ; enable EA and EX0 MOV SP, #30H ; stack start from #30H SETB IT0 ; falling edge-triggered MOV A, #00000000B ; set ACC as 0000000B LOOP: MOV P2, A ; P1 = A(LED output) CALL DELAY ; call delay function CPL A ; reverse A SJMP LOOP ; infinite loop INT0_ISR: PUSH PSW ; push PSW into stack PUSH ACC ; push ACC into stack SETB RS0 ; switch to RB1 MOV A, #11111110B ; set ACC as 11111110B MOV R0,#24 ; loop counter = 24 ROTATE_L: MOV P2, A ; P1 = A(LED output) CALL DELAY ; call delay function RL A ; rotate left DJNZ R0, ROTATE_L ; loop until R0 is 0 POP ACC ; pop out ACC from stack POP PSW ; pop out PSW from stack RETI ; return from ISR DELAY: MOV R7, #200 D1: MOV R6, #250 DJNZ R6, $ DJNZ R7, D1 RET ; return END ; end the code ``` ### 實驗結果影片 {%youtube BQvmuMty5kY%} ## 進階題 請達成以下要求: - 平時為8顆LED同時閃爍。 - 按下INT0的按鈕會執行三圈四顆LED跑馬燈,按下INT1的按鈕會執行兩圈單顆LED跑馬燈。 - 當四顆LED跑馬燈在跑的時候按下INT1需要先去跑完兩圈單顆LED跑馬燈,再回去繼續跑完四顆LED跑馬燈,而跑完四顆LED跑馬燈會八顆LED同時閃爍。 - 同時按下INT0和INT1會執行兩圈單顆LED跑馬燈。 - INT0與INT1皆使用低準位觸發。 範例: 單顆LED跑馬燈示意圖: XXXXXXXO XXXXXXOX XXXXXOXX XXXXOXXX XXXOXXXX XXOXXXXX XOXXXXXX OXXXXXXX 四顆LED跑馬燈示意圖: XXXXOOOO XXXOOOOX XXOOOOXX XOOOOXXX OOOOXXXX OOOXXXXO ### 實驗結果影片 {%youtube u4EYDV-qKpQ %} # 實驗七 計時/計數器 ## 基礎題 連接於 P2.0 的 LED 使用 delay function 作為閃爍的時間間隔;連接於 P2.1的 LED 使用 Timer 0 的interrupt 作為閃爍的時間間隔,調整參數,使兩個 LED 的閃爍時間間隔接近一致。 程式碼 ```x86asm= #include <regx52.h> ;// include header file for 8051 #define TH0_init 0x06 ;//TH0 = 256 - 250 #define TL0_init 0x06 ;//TL0 = 256 - 250 #define Timer0_int_exe_time 1000 ;//the parameter can be changed void delay(int t); int counter; void main( ) { TMOD = 0x02; //set timer0 to mode 2(8-bits and auto-reload) IP = 0x02; //timer0 interrupt has high priority IE = 0x82; //enable timer0 interrupt P2 = 0xff; TL0 = TL0_init; TH0 = TH0_init; //set TH0 & TL0 TCON = 0x10; //enable timer0 while (1) { delay(84000); // adjust this parameter to match timer P2_0 = ~P2_0; //inverse P2.0 } } void timer0_interrupt(void) interrupt 1 // ‘interrupt 1’ is int vector of INT0 { counter++; if (counter == Timer0_int_exe_time) //250clock cycle * 1000 = 0.25 second { P2_1 = ~P2_1; //inverse P2.0 counter = 0; } } //delay function void delay(int t) { for (; t>0; t--); } ``` ### 實驗結果影片 {%youtube k0b-PZ-oMVk%} ## 進階題 使8051通電後點亮P2.0的LED,並設計一顆按鈕,當按住它之後,每0.5秒鐘會使點亮的LED向左平移,如同實驗五的跑馬燈,只是間隔時間變成0.5秒,且只有按住按鈕時會跑動。 Hint:Timer請使用mode 1,並設定其為外部啟動(GATE = 1)。 # 實驗八 UART傳輸 ## 基礎題 8051 透過 URAT 並使用輪詢和中斷兩種方式接收來自 PC 的資料,8051 再自動回傳字元資料和數值資料至 PC 終端機顯示結果並比較差別。 ### 中斷法 程式碼 ```x86asm= #include <regx52.h> void init_uart( ); //declare UART function char recivevalue; //receivedata void main( ) { init_uart( ); while (1); } void init_uart( ) { SCON = 0x50; //Mode1 TMOD = 0x20; //Timer1 Mode2 TH1 = 0xe6; //set Baud=1200 TR1 = 1; //enable TCON Timer1 IE = 0x90; //enable UART } void UART_Isr(void) interrupt 4 //UART { while (RI == 0); //wait recieve flag == 1 RI = 0; //clear flag recivevalue = SBUF; P2 = recivevalue; //LED display data if (recivevalue == 'q') { SBUF = recivevalue; //load data while (TI == 0); //wait transmit flag == 1 TI=0; //clear flag } } ``` ### 輪詢法 程式碼 ```x86asm= #include <regx52.h> void init_uart( ); //declare UART function char recivevalue; //receivedata void main( ) { init_uart( ); while (1) { while (RI == 0); //wait recieve flag == 1 RI = 0; //clear flag recivevalue = SBUF; P2 = recivevalue; //LED displaydata if (recivevalue == 'q') { SBUF = recivevalue; //loaddata while (TI == 0); //wait transmit flag TI=0; //clear flag } } } void init_uart( ) { SCON=0x50; //Mode1 TMOD=0x20; //Timer1 Mode2 TH1=0xe6; //set BAUD=1200 TR1=1; //enable TCON Timer1 } ``` 實驗結果影片 {%youtube XYZbwUGQfZc %} ## 進階題 利用終端機軟體輸入學號並透過UART傳送給8051,8051接收資料後判斷學號是否為正確,最後傳送判斷結果給PUTTY終端機呈現結果,正確回傳correct,錯誤則回傳wrong,且每次判定後回傳的字串要換行並從第一個字開始輸出。 HINT:可用傳送數值方法傳送ASCII。 ### 實驗結果影片 {%youtube G4OgH09uNmM %} # 實驗九 ADC的轉換應用 ## 基礎題 利用可變電阻輸入電壓分壓給ADC0804轉為數位訊號,8051接收後判定是否超過電壓門檻(設為輸入最大輸入類比電壓的一半),若超過則LED燈全亮。 ### 連續轉換 輪詢法使用第一種連續轉換模式,接電路時須將INTR和WR接腳對接且把RD腳接地。 程式碼 ```x86asm= #include <regx52.h> void main( ) { while (1) { if (P1 > 256 / 2) //if P1 greater than 256/2 P2 = 0; //LED on else P2 = 255; //LED off } } ``` 實驗結果影片 {%youtube QwTHQtDW-FM%} ### 交握式 中斷法使用第二種交握式模式。 程式碼 ```x86asm= #include <regx52.h> void main( ) { IT0 = 0; //enable a low-level signal on external interrupt EX0 = 1; //enable INT0 EA = 1; //enable interrupt P3_4 = 0; //WR=0,clean the data P3_4 =1; //WR=1,analog convert to digital while(1); //infinit loop } void int_0(void) interrupt 0 //INT0 interrupt function { P3_5 = 0; //RD=0, enable read if (P1 > 256 / 2) { P2 = 0; } else { P2 = 255; } P3_5 = 1; //RD=1, stop read P3_4= 0; //WR=0,clean the data P3_4 = 1; //WR=1,analog convert to digital } ``` 實驗結果影片 {%youtube zmFdbXjSLkY %} ## 進階題 使用中斷方法,利用可變電阻輸入電壓分壓給ADC0804轉為數位訊號,並由8051接收後控制8顆LED燈在可變電阻旋轉時從全不亮漸變至全亮(漸亮的時間間隔要平均)。 ### 實驗結果影片 {%youtube hMadkaGmceg %} # 實驗十 LCD顯示器 ## 基礎題 在文字型 LCD 上顯示文字。 程式碼 ```x86asm= #include <regx52.h> #include <string.h> void print_msg(char[]); void write(char, int); void delay(unsigned int); void main( ) { write(0x38, 0); // use 2 lines and 5x7 matrix write(0x0F, 0); // LCD ON, cursor ON, cursor blinking ON write(0x06, 0); // increment cursor write(0x01, 0); // clear screen write(0x80, 0); // DDRAM 1st row 1st column (00H) print_msg("Hello, world!"); // display ‘Hello world!’ while (1); } void print_msg(char msg[]) { int i; for (i=0; i<strlen(msg); i++) // for_every_character write(msg[i], 1); // monitor_displays_character } void write(char cmd, int rs_value) { P1 = cmd; // P1=cmd P3_0 = rs_value; // RS=rs (1or0) P3_1 = 1; // ENABLE high delay(10000); P3_1 = 0; // ENABLE low } void delay(unsigned int i) { while (i--); } ``` ### 實驗結果影片 {%youtube mMXd2Ans-ew %} ## 進階題 LCD第一行中顯示學號,並於第二行中從以下挑選一個喜歡的圖示顯示。 ![](https://hackmd.io/_uploads/rkLHxZ9s3.png) ### 實驗結果照片 ![](https://hackmd.io/_uploads/ByO-2aehn.png) # 實驗十一 步進馬達 ## 基礎題 程式碼 ```x86asm= #include <regx52.h> void delay(int); void turn( ); code char one_phase[ ] = {0x01,0x02,0x04,0x08}; code char two_phase[ ] = {0x0c,0x06,0x03,0x09}; int dir , delay_time; void main( ) { delay_time = 3000; dir = 1; while (1) { turn( ); } } void turn( ) { int x; if (dir == 1) //one phase, turn right { for (x = 0; x < 4; x++) { P1 = one_phase[x]; delay(delay_time); } } else //two phase, turn left { for(x = 0; x < 4; x++) { P1 = two_phase[x]; delay(delay_time); } } } void delay(int t) { while(t--); } ``` **使用的步進馬達有震動但沒有轉動角度** ## 進階題 新增兩個外部中斷按鈕,一個功能為切換馬達轉動方向(順變逆、逆變順);另一個為切換馬達轉速快慢,馬達使用1.5相驅動。 # 實驗十二 繼電器與光耦合器 ## 基礎題 程式碼 ```x86asm= #include <regx52.h> void delay(unsigned int); void main( ) { while(1) { P2_0 = 0; //relay & LED switch on delay(50000); P2_0 = 1; //relay & LED switch off delay(50000); } } void delay(unsigned int t) { while (t--); } ``` ### 實驗結果影片 {%youtube igBzUZTebgM%}