# 實驗一 內部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第一行中顯示學號,並於第二行中從以下挑選一個喜歡的圖示顯示。

### 實驗結果照片

# 實驗十一 步進馬達
## 基礎題
程式碼
```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%}