說明:除非特別註明,8051 題目以標準 Intel MCS-51 指令集與 12 MHz 晶振計算;Arduino 題目以 Arduino UNO R3 / Arduino Sketch 語法回答。
Section A - Multiple Choice
A1
題目
Which of the following correctly enables only the Serial and Timer 1 interrupts while ensuring all other interrupts are disabled?
- A)
MOV IE, #00011010B- B)
MOV IE, #10011010B- C)
MOV IE, #00000101B- D)
MOV IE, #10010101B
答案:題目選項有誤;正確設定應為 MOV IE, #10011000B
解答過程
8051 IE (Interrupt Enable) register 是一個 SFR,位元結構如下(bit 7 在最左、bit 0 在最右):
| Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| 名稱 | EA |
- | ET2 |
ES |
ET1 |
EX1 |
ET0 |
EX0 |
| 意義 | Global enable | (reserved) | Timer 2 (8052) | Serial | Timer 1 | Ext. INT1 | Timer 0 | Ext. INT0 |
要「only enable Serial and Timer 1」:
- 必須打開 global enable:
EA = 1(bit 7)。沒有EA = 1,所有個別 enable bit 都不會生效。 - 打開 Serial:
ES = 1(bit 4)。 - 打開 Timer 1:
ET1 = 1(bit 3)。 - 其他全部 = 0。
把每一位填好:
Bit 7 6 5 4 3 2 1 0
1 0 0 1 1 0 0 0 = 10011000B = 98H
各選項分析
| 選項 | 二進位 | bit 7 EA | bit 4 ES | bit 3 ET1 | bit 1 ET0 | bit 0 EX0 | 結果 |
|---|---|---|---|---|---|---|---|
| A | 00011010B |
0 | 1 | 1 | 1 | 0 | EA = 0,全部 interrupt 都關閉 |
| B | 10011010B |
1 | 1 | 1 | 1 | 0 | 多開了 ET0,不符題目「only」 |
| C | 00000101B |
0 | 0 | 0 | 0 | 1 | EA = 0,且開啟的是 EX0 |
| D | 10010101B |
1 | 1 | 0 | 0 | 1 | 沒開 ET1,反而開 EX0 |
補充說明
嚴格而言四個選項皆不正確;正確值為 10011000B = 98H。考試時若被迫從選項中挑一個,B 最接近(只多打開 ET0)。建議在答題時加上一行附註說明此情況。
A2
題目
Which of the following statements best describes the difference between interrupt-driven I/O and polling?
- A) In polling, the device sends a signal to the CPU when it needs attention; while using interrupts, the CPU repeatedly checks the device status.
- B) Polling continuously checks the device status in a loop, while interrupts allow the device to signal the CPU only when service is required.
- C) Polling and interrupts both require the CPU to constantly monitor device status registers.
- D) Interrupts and polling both stop the CPU until the device finishes its task.
答案:B
解答過程
需要分清楚 polling 與 interrupt-driven I/O 在「誰主動」這件事上的差別:
| 機制 | 主動方 | CPU 行為 | 典型用途 |
|---|---|---|---|
| Polling | CPU 主動 | 在 loop 內不斷讀 flag/status register | 簡單系統、I/O 速度可預測 |
| Interrupt-driven | 裝置主動 | 平時做主程式工作,被打斷時才服務 | 不可預測事件、低 latency |
各選項分析
- A:把 polling 與 interrupt 的主動方寫顛倒了,錯誤。
- B:正確描述兩者的本質差異。
- C:interrupt 不需要 CPU 不斷監控 status register,錯誤。
- D:polling 期間 CPU 確實「在等」,但 interrupt 並不會 stop CPU;CPU 會一直執行其他工作直到 interrupt 進來,錯誤。
A3
題目
Which of the following situations is most suitable for using interrupts instead of polling?
- A) Monitoring a very fast-changing signal that must be checked every clock cycle
- B) Handling a keyboard input that occurs unpredictably
- C) Continuously reading data from a high-speed ADC
- D) Running a delay loop in embedded firmware
答案:B
解答過程
判斷要不要用 interrupt 的關鍵:
- 事件是否可預測 — 不可預測 → interrupt
- 事件頻率高還是低 — 低或不規則 → interrupt;極高速且固定 → polling/DMA 反而較佳
- 是否需要 CPU 同時做其他工作 — 是 → interrupt
各選項分析
- A:每個 clock cycle 都要檢查的訊號,interrupt 進入/離開 ISR 的 overhead 反而會跟不上速度,應用 polling 或硬體電路。
- B:鍵盤輸入時間不可預測,且大部分時間沒有事件,這是 interrupt 最典型的應用場景。正確。
- C:高速 ADC 通常用 DMA 或 polling,interrupt 太頻繁會把 CPU 全部塞滿。
- D:delay loop 是純 CPU dummy 操作,與 I/O 無關,不需要 interrupt。
A4
題目
In an MCS-51 microcontroller, both
RI(Receive Interrupt flag) andTI(Transmit Interrupt flag) become set at the same time. How is the priority between the receive and transmit operations determined?
- A) RI always has higher priority than TI in hardware
- B) TI always has higher priority than RI in hardware
- C) The priority depends on the order of checking the flags in the interrupt service routine code
- D) The interrupt priority register (IP) automatically determines which one is handled first
答案:C
解答過程
8051 的硬體把 RI 與 TI OR 在一起共用同一個 Serial interrupt vector(0023H)。也就是說:
Serial Interrupt Source = RI OR TI
當 ISR 被觸發進入 0023H 之後,CPU 並不知道是 receive 還是 transmit 引起的;必須由 ISR 內部的程式碼自行判斷:
SERIAL_ISR:
JBC RI, HANDLE_RX ; if RI=1, clear it and handle receive
JBC TI, HANDLE_TX ; if TI=1, clear it and handle transmit
RETI
因此「先處理 RX 還是 TX」完全由程式設計者決定 → C 正確。
各選項分析
- A、B:硬體並沒有規定 RI/TI 之間的優先次序。
- D:
IPregister 控制的是「五個 interrupt sources 之間」的優先級(high/low 兩級),不能在同一個 source 內部再分。
A5
題目
What is the theoretical maximum baud rate that can be generated by Timer 1 in the classic Intel 8051 when using an 11.0592 MHz crystal oscillator (Modes 1 or 3)?
- A) 19200 bps
- B) 38400 bps
- C) 57600 bps
- D) 115200 bps
答案:C,57600 bps
解答過程
步驟 1:列出 baud rate 公式
8051 在 serial mode 1 或 mode 3,baud rate 由 Timer 1 提供:
\text{Baud rate} = \frac{2^{\text{SMOD}}}{32} \times \text{Timer 1 overflow rate}
其中 SMOD 是 PCON register 的 bit 7,可為 0 或 1。為了得到「最大」baud rate,取 SMOD = 1,所以乘數變成 2/32 = 1/16。
步驟 2:算 Timer 1 最大 overflow rate
Timer 1 通常設成 mode 2(8-bit auto-reload)做 baud rate generator:
\text{Overflow rate} = \frac{f_{osc}/12}{256 - \text{TH1}}
最大 overflow 發生在 TH1 = 255,此時分母 (256 - 255) = 1:
\text{Max overflow rate} = \frac{f_{osc}}{12} = \frac{11{,}059{,}200}{12} = 921{,}600 \text{ Hz}
步驟 3:算最大 baud rate
\text{Baud rate}_{max} = \frac{1}{16} \times 921{,}600 = 57{,}600 \text{ bps}
補充說明
11.0592 MHz 是專為 serial communication 挑選的晶振:12 分頻後得到 921,600 Hz,可整除常見 baud rate(9600、19200、38400、57600 …),故誤差極低。115,200 bps 在 classic 8051 + 11.0592 MHz 是無法達到的(必須換 22.1184 MHz 或使用 enhanced 8052 的 Timer 2)。
A6
題目
When an 8-bit MCU performs a 32-bit multiplication using software routines, which of the following is the most significant tradeoff compared with performing the same operation on a 32-bit MCU?
- A) Increased power consumption but identical execution time
- B) Increased instruction count and longer execution time
- C) Reduced memory usage but higher clock frequency requirement
- D) Loss of arithmetic accuracy
答案:B
解答過程
8-bit MCU 的 ALU 一次只能處理 8-bit 資料,32-bit 乘法必須拆成 4×4 個 byte-level 乘法 + 中間累加 + 進位處理:
A3 A2 A1 A0
x B3 B2 B1 B0
-----------------
多個 8x8 partial product 與 16-bit 累加
8051 的 MUL AB 一次只能做 8-bit × 8-bit → 16-bit。一個 32×32 → 64-bit 軟體乘法 routine 通常需要十幾條到幾十條指令,比 32-bit MCU 的單一硬體乘法慢得多。
各選項分析
- A:與 32-bit 比,執行時間絕對不會相同,反而更長。
- B:增加指令數、延長執行時間是最主要的 tradeoff。正確。
- C:軟體 routine 還會佔更多 program memory,「降低 memory」描述錯。
- D:只要 routine 正確處理進位,數學結果完全一致,不會 loss of accuracy。
A7
題目
In the Intel 8051 MCU, suppose two interrupts occur simultaneously, and both are configured with the same priority level in the IP register. Which interrupt will be serviced first?
- A) Both interrupts will be ignored
- B) The interrupt with the lower vector address will be serviced first
- C) The interrupt with the higher vector address will be serviced first
- D) The order is undetermined
答案:B
解答過程
8051 解決 interrupt 衝突的機制有兩層:
**IPregister 設定的兩級優先權**(high / low)- Hardware polling sequence(同一 priority level 內的固定順序)
當兩個 interrupt 同 priority 同時發生時,硬體 polling 順序如下(由先到後 = vector address 由低到高):
| 順序 | Source | Vector |
|---|---|---|
| 1 | External Interrupt 0 (INT0) |
0003H |
| 2 | Timer 0 | 000BH |
| 3 | External Interrupt 1 (INT1) |
0013H |
| 4 | Timer 1 | 001BH |
| 5 | Serial port | 0023H |
可見 vector address 越低的 interrupt 優先度越高,所以 B 正確。
各選項分析
- A:interrupt 不會被「ignored」。
- C:方向相反。
- D:硬體 polling 順序是固定的,不是 undetermined。
A8
題目
For the Intel 8051 MCU, in serial communication mode 1, how many bits are transmitted per character (including framing)?
- A) 8
- B) 9
- C) 10
- D) None of the above
答案:C,10 bits
解答過程
8051 serial 共有四種 mode:
| Mode | 說明 | Frame |
|---|---|---|
| 0 | Shift register, 同步 | 8 data bits(無 start/stop) |
| 1 | 8-bit UART, baud rate 可變 | 1 start + 8 data + 1 stop = 10 bits |
| 2 | 9-bit UART, baud rate 固定 | 1 start + 9 data + 1 stop = 11 bits |
| 3 | 9-bit UART, baud rate 可變 | 1 start + 9 data + 1 stop = 11 bits |
題目問的是 mode 1(標準 UART),所以 framing 為:
1 start bit + 8 data bits + 1 stop bit = 10 bits
A9
題目
A student observes that ADC readings on an Arduino UNO R4 Minima appear inaccurate. The error becomes more severe when the board is powered via the USB-C port, whereas it is smaller when powered via the barrel jack. What is the most likely reason for this behaviour?
- A) The ADC automatically switches to a lower resolution when the board is powered by USB-C.
- B) USB-C power disables the analog reference circuitry, so the ADC uses an internal random reference voltage.
- C) USB-C power on the UNO R4 Minima is reduced by a Schottky diode, while barrel input is regulated to 5 V; therefore, ADC readings can differ if the reference depends on the supply rail.
- D) VIN power increases the MCU clock speed, which improves ADC accuracy.
答案:C
解答過程
ADC 結果是相對於 reference voltage 的比值:
\text{ADC reading} = \frac{V_{in}}{V_{ref}} \times (2^N - 1)
如果 V_{ref} 依賴 supply rail(即 AVCC 直接接 5V),那麼 supply 的變動 = ADC 結果的誤差。
UNO R4 Minima 的供電路徑:
| 來源 | 路徑 | 結果到 5V rail |
|---|---|---|
| USB-C | 5V → Schottky diode (~0.3 V drop) → 5V rail | 約 4.7 V,會隨 USB 線材波動 |
| Barrel jack (DC in) | 7-12 V → on-board regulator | 穩定 5.0 V |
當供電變成 ~4.7 V,若 ADC reference 又跟 supply rail 相連,則所有 ADC reading 都被「壓縮」,造成系統性誤差。
各選項分析
- A:硬體不會因供電方式自動切換 resolution。
- B:USB-C 不會 disable analog reference 電路。
- C:Schottky diode 壓降是 R4 Minima 的設計事實,且 ADC 受 supply rail 影響,邏輯成立。正確。
- D:MCU clock 與供電源無關,且 clock 更快也不會改善 ADC accuracy(反而可能更差)。
A10
題目
Continue from Question A9, suppose the same analog-input circuit is migrated from an Arduino UNO R4 Minima to an Arduino UNO R3 without changing the sensor or software approach. Which observation and tradeoff are most likely?
- A) The power-source-related ADC error would become larger, but the UNO R3 would provide higher ADC precision.
- B) The ADC readings would become independent of the power source, but the UNO R3 would run at a lower clock speed.
- C) The power-source-related ADC error would likely be smaller, but the tradeoff is lower ADC resolution on the UNO R3.
- D) The ADC behaviour would remain the same, but the UNO R3 would only have less memory available.
答案:C
解答過程
UNO R3 vs UNO R4 Minima 對 ADC 的關鍵差異:
| 項目 | UNO R3 (ATmega328P) | UNO R4 Minima (RA4M1) |
|---|---|---|
| ADC resolution | 10-bit (0~1023) | 12-bit (0~4095) |
| 預設 ADC reference | AVCC ≈ 5V(透過 LC filter,較穩) |
受 5V rail 直接影響 |
| USB 供電壓降 | 通常 ≤ 0.1 V(無 Schottky diode) | ~0.3 V(Schottky diode) |
因此把同一電路搬到 UNO R3:
- USB 供電壓降較小,ADC 對 supply 的敏感度也較弱 → 誤差變小
- 但 ADC resolution 從 12-bit 降為 10-bit → 解析度較差
兩者形成「誤差較小 / 解析度較低」的 tradeoff,符合 C。
各選項分析
- A:誤差變大 + 精度更高皆不正確。
- B:完全 independent 是過度宣稱;R3 的 ADC reading 仍然依賴 reference。
- D:ADC 行為會改(resolution 不同),不能說「remain the same」。
Section B1 - 8051 Timer 0 Interrupt LED Blinking
題目背景
Referring to the "8051 Microcontroller Assembly Language Program for Blinking LEDs" shown in Chapters 5 and 6 (& Supplementary Notes), the program uses nested
DJNZdummy loops to generate delay. In this question, you are required to redesign the program so that LED blinking is controlled more efficiently using Timer 0 and its interrupt on the standard Intel 8051 MCU. Assume a 12 MHz crystal oscillator is used throughout unless otherwise stated.
原本課堂的程式用「兩層 / 三層 DJNZ」軟體 dummy loop 製造約 0.8 s 的 delay;缺點是 CPU 在 delay 期間完全被佔用,無法做其他工作。本題要求改用 Timer 0 interrupt 達到相同的閃爍效果,讓 CPU 可在 delay 期間執行其他工作(雖然本範例 main loop 只是 SJMP HERE)。
B1(a)
題目
For the standard Intel 8051 MCU, answer the following about the timer operated in timer mode:
- What is the largest timer size available for a single timer overflow?
- Which timer mode provides this largest single-overflow delay?
- What is the maximum count value for that mode?
- What is the maximum time delay obtainable from one overflow in that mode when the crystal frequency is 12 MHz?
解答過程
Step 1:4 種 timer mode 的位寬比較
標準 Intel 8051 Timer 0 / Timer 1 由 TMOD 設定,共四種模式:
| Mode | M1 M0 | Counter 寬度 | 最大 count |
|---|---|---|---|
| 0 | 0 0 | 13-bit | 8192 |
| 1 | 0 1 | 16-bit | 65536 |
| 2 | 1 0 | 8-bit auto-reload | 256 |
| 3 | 1 1 | Split timer (Timer 0 only) | 256 each |
「單次 overflow 最長」必然要選 counter 位寬最大的 → Mode 1, 16-bit。
Step 2:最大 count 值
16-bit counter 可從 0000H 數到 FFFFH,再加 1 才 overflow:
\text{Max counts per overflow} = 2^{16} = 65536
Step 3:12 MHz 下每個 timer count 的時間
8051 將 f_{osc} 除以 12 得到 machine cycle:
T (mc) = 12/f_osc = 12/ 12 MHz = 1 / mu s
Timer mode 下,每個 machine cycle 計數加 1,所以 1 count = 1 μs。
Step 4:最大 single-overflow delay
T = 65536 times (1 / mu s = 65,536 / mus = 65.536 ms
答案整理
| 項目 | 答案 |
|---|---|
| Largest timer size | 16-bit |
| Mode | Timer Mode 1 |
| Maximum count | 65,536 counts(從 0000H 數到 FFFFH 再 +1) |
| Maximum one-overflow delay @12 MHz | 65.536 ms |
B1(b)
題目
Can one direct Timer 0 overflow in the mode identified in part (a) to replace the nested
DJNZ-based delay directly? Briefly explain your answer.
答案:不能直接用一次 Timer 0 overflow 取代
解答過程
原 program 的 LED ON / OFF time 約為 0.8 s,但 (a) 算出 Timer 0 Mode 1 單次 overflow 最大只能:
65.536 ms -> 0.0655 s
兩者差距:
800 ms / 65.536 ms -> approx 12.2
也就是說一次 overflow 連 0.1 秒都不到,根本無法直接代替 0.8 s delay。
解決方法
採用「short overflow + software counter」:
- 設 timer reload 值,使每次 overflow 約 50 ms。
- 用一個工作暫存器(例如
R7)做 software counter,每次 overflow 把R7減 1。 - 累計 \lceil 800 / 50 \rceil = 16 次 overflow 後 toggle LED,重新 reload 16。
如此即可組合出任意長度的 delay,而且每兩次 overflow 之間 CPU 都是空閒的,可以做其他事。
B1(c)
題目
Suppose a timer interrupt period of approximately 50 ms per overflow is chosen.
- Using Timer 0 in Mode 1, determine the initial values to be loaded into TH0 and TL0.
- How many such overflows are required to obtain an LED ON time or OFF time close to the original 0.8 s delay?
解答過程
Step 1:50 ms 需要多少 counts
12 MHz 下 1 count = 1 μs:
N = 50 \text{ ms} = 50{,}000 \mu s = 50{,}000 \text{ counts}
Step 2:從 reload value 倒推
Timer 0 Mode 1 是 16-bit up counter;overflow 發生在 FFFFH → 0000H 那一刻。要讓「再經過 50,000 counts 就 overflow」,必須預先載入:
Reload = 65,536 - 50,000 = 15,536
Step 3:轉成 hex 並拆成 high / low byte
15,536_10 = 3CB0H
拆成 16-bit timer 的高 / 低位元組:
| Register | Value |
|---|---|
TH0 |
3CH(高 8 bits) |
TL0 |
B0H(低 8 bits,組譯時須寫 0B0H) |
Step 4:累計多少次 overflow ≈ 0.8 s
\frac{0.8 \text{ s}}{50 \text{ ms}} = \frac{800}{50} = 16
因此 16 次 overflow 後才 toggle LED。
答案整理
MOV TH0, #3CH ; high byte of 0x3CB0
MOV TL0, #0B0H ; low byte; leading 0 因為是 hex 開頭為字母
| 項目 | 答案 |
|---|---|
TH0 |
3CH |
TL0 |
0B0H |
| Reload 16-bit value | 3CB0H |
| Overflows required for ~0.8 s | 16 次 |
補充說明:為什麼 hex 數字要寫 0B0H?
8051 assembler 規定 immediate constant 不能以字母開頭,否則會被誤判為 label。B0H 字母開頭,必須寫成 0B0H;同理 FFH 寫成 0FFH。3CH 因為以數字開頭可直接寫。
B1(d)
題目
Before writing the full program, answer the following:
- What is the interrupt vector address for Timer 0?
- State the IE register setting required to enable Timer 0 interrupt.
解答過程
Step 1:Timer 0 vector
8051 的 5 個 interrupt vector address:
| Source | Vector |
|---|---|
| External Interrupt 0 | 0003H |
| Timer 0 | **000BH** |
| External Interrupt 1 | 0013H |
| Timer 1 | 001BH |
| Serial port | 0023H |
所以 Timer 0 的 vector 是 **000BH**。
Step 2:IE 設定
要 enable Timer 0 interrupt,需要:
EA = 1(bit 7,global enable)ET0 = 1(bit 1,Timer 0 enable)- 其他 bit = 0
對應位元圖:
| Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| 名稱 | EA | - | ET2 | ES | ET1 | EX1 | ET0 | EX0 |
| 值 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
\text{IE} = 10000010_2 = 82\text{H}
答案整理
| 項目 | 答案 |
|---|---|
| Timer 0 vector | **000BH** |
IE register |
**10000010B = 82H** |
兩種等價寫法:
MOV IE, #82H ; 一次設好
或:
SETB EA ; 開 global enable
SETB ET0 ; 開 Timer 0 enable
B1(e)
題目
題目給出半完成 skeleton(包含 ORG、註解,但缺少實際指令),要求補完。完整 skeleton 為:
;First line of the program
;bypass interrupt vector table
;-----------------------------------
; INTERRUPT VECTOR ADDRESS
;-----------------------------------
;Timer 0 interrupt vector
;-----------------------------------
; MAIN PROGRAM
;-----------------------------------
ORG 0030H
MAIN: ;Timer 0, Mode 1 (16-bit timer)
MOV P1, #0FFH ;Initial LED pattern
;initialize software counter
;Reload value for about 50 ms
;enable Timer 0 interrupt
;Start Timer 0
HERE: SJMP HERE ;wait for interrupts
;-----------------------------------
; TIMER 0 ISR
; Every overflow ≈ 50 ms
; After 16 overflows, toggle P1
;-----------------------------------
T0_ISR: ;Stop timer before reloading
;Reload Timer 0
;Decrement software counter
;Toggle all bits of P1
;Reload software counter
EXIT_ISR: ;Restart Timer 0
;Return from interrupt
;Last line of the program
設計思路
**ORG 0000H**後立即LJMP MAIN:reset vector 只有 3 個 byte,必須跳過 interrupt vector table。**ORG 000BH**放LJMP T0_ISR:Timer 0 vector 也只有 8 byte 空間,習慣上LJMP出去。**ORG 0030H**放主程式:避開所有 interrupt vector area(最後一個0023H之後)。- ISR 內:先
CLR TR0停止 timer,再 reload TH0/TL0;最後SETB TR0重啟,RETI結束(注意是RETI不是RET,否則 interrupt logic 不會解鎖)。 - 因為 ISR 用了
MOV A, P1 / CPL A,要在 ISR 開頭/結尾PUSH ACC / POP ACC保護 main program 的 ACC。
完整 Timer 0 interrupt-driven rewrite
; First line of the program
ORG 0000H
; bypass interrupt vector table
LJMP MAIN
;-----------------------------------
; INTERRUPT VECTOR ADDRESS
;-----------------------------------
; Timer 0 interrupt vector
ORG 000BH
LJMP T0_ISR
;-----------------------------------
; MAIN PROGRAM
;-----------------------------------
ORG 0030H
MAIN:
; Timer 0, Mode 1 (16-bit timer)
MOV TMOD, #00000001B
MOV P1, #0FFH ; Initial LED pattern
; initialize software counter
MOV R7, #16
; Reload value for about 50 ms
MOV TH0, #3CH
MOV TL0, #0B0H
; enable Timer 0 interrupt
MOV IE, #10000010B
; Start Timer 0
SETB TR0
HERE:
SJMP HERE ; wait for interrupts
;-----------------------------------
; TIMER 0 ISR
; Every overflow ≈ 50 ms
; After 16 overflows, toggle P1
;-----------------------------------
T0_ISR:
; Stop timer before reloading
CLR TR0
PUSH ACC
; Reload Timer 0
MOV TH0, #3CH
MOV TL0, #0B0H
; Decrement software counter
DJNZ R7, EXIT_ISR
; Toggle all bits of P1
MOV A, P1
CPL A
MOV P1, A
; Reload software counter
MOV R7, #16
EXIT_ISR:
POP ACC
; Restart Timer 0
SETB TR0
; Return from interrupt
RETI
; Last line of the program
END
程式逐段解釋
| 區段 | 作用 |
|---|---|
ORG 0000H + LJMP MAIN |
Reset vector,跳過下方 interrupt vector table |
ORG 000BH + LJMP T0_ISR |
Timer 0 interrupt 進入點,跳到實際 ISR |
ORG 0030H |
把主程式擺在所有 vector 之後的安全區 |
MOV TMOD, #00000001B |
M1M0 = 01 設 Timer 0 為 Mode 1 (16-bit timer);高 4 bit = 0 表示不影響 Timer 1 |
MOV R7, #16 |
software counter,計 16 次 overflow 才 toggle |
MOV TH0/TL0, #3CH/#0B0H |
預載 3CB0H 使下一次 overflow 出現在 50 ms 後 |
MOV IE, #82H |
同時 set EA, ET0 |
SETB TR0 |
啟動 Timer 0 |
SJMP HERE |
主程式無事可做,等 interrupt |
CLR TR0 (ISR 開頭) |
reload 期間先停 timer,避免 reload 兩個 byte 時 timer 仍在跑導致誤差 |
PUSH ACC / POP ACC |
保護 ACC,否則 main 中的 ACC 內容會被 ISR 改掉 |
DJNZ R7, EXIT_ISR |
若 R7 還沒到 0,直接跳到 EXIT;只有 R7 減到 0 那次才 toggle P1 |
MOV A, P1 / CPL A / MOV P1, A |
把 P1 全部位元反相,達成 LED 切換 |
RETI |
interrupt return,與 RET 不同,會清除 interrupt-in-service flag |
補充說明:精度
由於 ISR 本身也要執行時間(PUSH/POP、reload 寫兩次 MOV),「停止 → reload → 重啟」之間 timer 會少數個 machine cycles,實際週期會比 50 ms 稍長一點。要更精確可:
- 不停 timer,直接寫
TH0/TL0(但有 race condition 風險)。 - 或在 reload 時加上補償,例如
MOV TL0, #(0B0H + correction)。
以本題要求「approximately 50 ms」而言,3CB0H 已足夠。
Section B2 - HC-SR04 與四個 LED 的 Arduino 題目
題目背景
You are provided with a breadboard diagram showing the connections of an HC-SR04 ultrasonic sensor and four LEDs to an Arduino UNO R3.
題目給一張麵包板配線圖(Arduino UNO R3、HC-SR04 距離感測器、四顆 LED + 四顆限流電阻),要做:
- (a) 把 breadboard 圖轉成電路 schematic。
- (b) 寫 Arduino 程式:依測得距離分區控制四顆 LED(含閃爍頻率隨距離變化)。
B2(a)
題目
Based on the breadboard view provided, draw the circuit schematic using the electronic symbols below. (3M)
解答過程
從麵包板配線可整理出 pin assignment(與 (b) 提供的主程式變數一致):
HC-SR04 ultrasonic sensor
| HC-SR04 pin | Arduino UNO R3 connection | 備註 |
|---|---|---|
VCC |
5V |
必須是 5V,3.3V 不夠驅動 |
TRIG |
D3 |
output:發送 trigger pulse |
ECHO |
D2 |
input:量測 echo pulse 寬度 |
GND |
GND |
共地 |
LED connections
四顆 LED 都是 active HIGH:digital output 經 current-limiting resistor 接到 LED anode(長腳),cathode(短腳)接 GND。
| LED | Arduino pin | 連接 |
|---|---|---|
| LED 1 | D13 |
D13 → R1 → LED1(A) → LED1(K) → GND |
| LED 2 | D12 |
D12 → R2 → LED2(A) → LED2(K) → GND |
| LED 3 | D8 |
D8 → R3 → LED3(A) → LED3(K) → GND |
| LED 4 | D5 |
D5 → R4 → LED4(A) → LED4(K) → GND |
文字版 schematic(提交時請改用題目要求的電子元件符號)
Arduino UNO R3
+-------------+
5V o--------------------+ | 5V |
| | GND o-+----+
+---[R1]--|>|--+ | | D2 (ECHO) o-+--+ |
| | | | D3 (TRIG) o-+-+| |
| +--+ | D5 o-+|||--[R4]--|>|---+
| | | D8 o-+||+--[R3]--|>|---+
| | | D12 o-+|+--[R2]--|>|----+
| | | D13 o-+|------------+ |
| | +-------------+ | | |
| | | |
+-----------------------------------+--+ | |
| | |
v v v
GND GND GND
HC-SR04
+--------+
5V o--+ VCC |
D3 o--+ TRIG |
D2 o--+ ECHO |
GND o--+ GND |
+--------+
更直覺的 per-LED schematic:
Arduino D13 ----[R1]----|>|---- GND LED1
Arduino D12 ----[R2]----|>|---- GND LED2
Arduino D8 ----[R3]----|>|---- GND LED3
Arduino D5 ----[R4]----|>|---- GND LED4
Arduino 5V ------------------ VCC HC-SR04
Arduino D3 ------------------ TRIG HC-SR04
Arduino D2 ------------------ ECHO HC-SR04
Arduino GND ------------------ GND HC-SR04
補充說明
- 限流電阻典型值:5V 接 220 Ω 或 330 Ω。
- 在答案紙上請使用題目所給的標準 schematic symbol:電阻用 zigzag 或長方形、LED 用三角形+橫線+兩條箭頭、Arduino 用 IC block;確保 anode/cathode 方向正確。
- 重要原則:schematic 的 pin label 必須與 (b) 程式
const byte trigPin = 3等一致;否則扣分。
B2(b)
題目
Write an Arduino program to implement the following functionality: – Continuously measure distance using the HC-SR04 ultrasonic sensor. – Control the four LEDs according to the measured distance:
0 ≤ d < 50 cm:only LED1 active;blink rate increases with distance, slowest at 0 cm → continuously ON at 50 cm.50 ≤ d < 100 cm:LED1 always ON, LED2 blinks (slowest at 50 → ON at 100).100 ≤ d < 150 cm:LED1, LED2 ON, LED3 blinks (slowest at 100 → ON at 150).150 ≤ d ≤ 200 cm:LED1–LED3 ON, LED4 blinks (slowest at 150 → ON at 200).d > 200 cm:all LEDs ON.- invalid reading:all LEDs OFF. The main program loop is provided below. Complete the missing parts. (7M)
題目已給 loop() 完整實作;要補的是:
- Pin 變數宣告
readUltrasonicDistance()controlBlinkingLED()setup()
設計思路
(1) HC-SR04 測距時序
HC-SR04 觸發流程:
TRIG ___|‾‾‾‾‾‾|________________ (10 μs HIGH pulse)
ECHO __________|‾‾‾‾‾‾‾‾‾‾|_____ (HIGH 寬度 = 來回飛行時間)
聲音來回,所以單程距離為:
d \text{ (cm)} = \frac{\text{duration (μs)} \times 0.0343 \text{ cm/μs}}{2}
加上 timeout 防止裝置沒接或超過量測範圍時 pulseIn() 永遠等下去(典型 timeout 30,000 μs ≈ 5 m)。
(2) LED 行為總表
| 距離 | LED1 | LED2 | LED3 | LED4 | 對應「閃爍中」LED |
|---|---|---|---|---|---|
| invalid | OFF | OFF | OFF | OFF | — |
[0, 50) |
blink | OFF | OFF | OFF | LED1 |
[50, 100) |
ON | blink | OFF | OFF | LED2 |
[100, 150) |
ON | ON | blink | OFF | LED3 |
[150, 200] |
ON | ON | ON | blink | LED4 |
> 200 |
ON | ON | ON | ON | — |
(3) 閃爍頻率隨距離增加
依題目註解:「0 cm → 1000 ms,50 cm → 100 ms」對該區間(每 50 cm 一段)內線性內插:
\text{interval} = T_{max} - \frac{d - \text{start}}{\text{end} - \text{start}} \times (T_{max} - T_{min})
其中 T_{max} = 1000 \text{ ms}(區間起點,最慢),T_{min} = 100 \text{ ms}(區間終點,最快)。距離越接近區間上限,interval 越短,閃爍越快;到上限即進入下一個 range,變成 continuously ON。
(4) 為何使用 millis() 不用 delay()
每個 loop() 必須持續測距,若用 delay(interval) 會阻塞 1 秒等不到下一筆距離。改用 non-blocking timing:紀錄上次 toggle 時刻 previousMillis,每次進 loop 檢查是否已過 interval ms。
if (millis() - previousMillis >= interval) {
previousMillis = millis();
ledState = !ledState;
}
(5) 切換 range 時的 reset
當距離跨入新 range,blinking 的 LED 就換了一顆。要避免上一顆 LED 殘留 HIGH,並避免新 LED 一開始就 high/low 不同步,故在 controlBlinkingLED() 內以 static byte previousLedPin 偵測切換、重置 ledState 與 previousMillis。
完整 Arduino program
// Define pin assignments
const byte trigPin = 3;
const byte echoPin = 2;
const byte led1Pin = 13;
const byte led2Pin = 12;
const byte led3Pin = 8;
const byte led4Pin = 5;
const unsigned long maxBlinkInterval = 1000; // slowest blink
const unsigned long minBlinkInterval = 100; // fastest blink before continuously ON
// Function to read distance from the ultrasonic sensor
long readUltrasonicDistance(byte trig, byte echo) {
digitalWrite(trig, LOW);
delayMicroseconds(2);
digitalWrite(trig, HIGH);
delayMicroseconds(10);
digitalWrite(trig, LOW);
// Timeout prevents the program from waiting forever.
long duration = pulseIn(echo, HIGH, 30000UL);
return duration; // 0 means timeout / invalid reading
}
// Function to control one blinking LED
void controlBlinkingLED(byte ledPin, float distance, float startCm, float endCm) {
static unsigned long previousMillis = 0;
static bool ledState = LOW;
static byte previousLedPin = 255;
// If the active blinking LED changes, restart the blink state.
if (previousLedPin != ledPin) {
previousLedPin = ledPin;
previousMillis = millis();
ledState = LOW;
}
// Blink interval decreases as distance increases.
// Example: startCm => 1000 ms, endCm => 100 ms.
float ratio = (distance - startCm) / (endCm - startCm);
ratio = constrain(ratio, 0.0, 1.0);
unsigned long interval =
(unsigned long)(maxBlinkInterval -
ratio * (maxBlinkInterval - minBlinkInterval));
// Non-blocking blinking using millis()
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
ledState = !ledState;
}
digitalWrite(ledPin, ledState);
}
void setup() {
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(led1Pin, OUTPUT);
pinMode(led2Pin, OUTPUT);
pinMode(led3Pin, OUTPUT);
pinMode(led4Pin, OUTPUT);
digitalWrite(led1Pin, LOW);
digitalWrite(led2Pin, LOW);
digitalWrite(led3Pin, LOW);
digitalWrite(led4Pin, LOW);
}
void loop() {
long duration = readUltrasonicDistance(trigPin, echoPin);
// If invalid reading, turn all LEDs OFF
if (duration == 0) {
digitalWrite(led1Pin, LOW);
digitalWrite(led2Pin, LOW);
digitalWrite(led3Pin, LOW);
digitalWrite(led4Pin, LOW);
delay(50);
return;
}
// Convert duration to distance in cm
float distance = duration * 0.0343 / 2.0;
// Default all LEDs OFF first
digitalWrite(led1Pin, LOW);
digitalWrite(led2Pin, LOW);
digitalWrite(led3Pin, LOW);
digitalWrite(led4Pin, LOW);
if (distance < 50) {
// 0-50 cm: only LED1 active
controlBlinkingLED(led1Pin, distance, 0, 50);
}
else if (distance < 100) {
// 50-100 cm: LED1 always ON, LED2 blinking
digitalWrite(led1Pin, HIGH);
controlBlinkingLED(led2Pin, distance, 50, 100);
}
else if (distance < 150) {
// 100-150 cm: LED1 and LED2 always ON, LED3 blinking
digitalWrite(led1Pin, HIGH);
digitalWrite(led2Pin, HIGH);
controlBlinkingLED(led3Pin, distance, 100, 150);
}
else if (distance <= 200) {
// 150-200 cm: LED1 to LED3 always ON, LED4 blinking
digitalWrite(led1Pin, HIGH);
digitalWrite(led2Pin, HIGH);
digitalWrite(led3Pin, HIGH);
controlBlinkingLED(led4Pin, distance, 150, 200);
}
else {
// Above 200 cm: all LEDs ON
digitalWrite(led1Pin, HIGH);
digitalWrite(led2Pin, HIGH);
digitalWrite(led3Pin, HIGH);
digitalWrite(led4Pin, HIGH);
}
delay(50);
}
程式重點解釋
readUltrasonicDistance()
- 回傳的是 echo pulse duration(μs),而不是直接回傳距離 → 因為題目給的 main loop 自己會做
duration * 0.0343 / 2.0,函式必須匹配介面。 digitalWrite(trig, LOW); delayMicroseconds(2);確保 trigger 從乾淨的 LOW 開始。delayMicroseconds(10)給 10 μs HIGH,之後拉 LOW 觸發感測器內部 8 個 40 kHz 脈衝。pulseIn(echo, HIGH, 30000UL):等 echo 變 HIGH 後計時,timeout 設 30 ms(理論上對應 ~5 m,超過 HC-SR04 的最大測程 4 m),避免無限等待。- timeout 時
pulseIn()回傳0,正好對應主 loop 中的「invalid reading」分支。
controlBlinkingLED()
- 三個
static變數讓函式能跨多次呼叫保留狀態(local 但非 stack):previousMillis(上次 toggle 時間)、ledState(當前 LED 狀態)、previousLedPin(上一輪 blinking 的 pin)。 if (previousLedPin != ledPin) { ... }偵測 range 切換時 reset 計時器。constrain(ratio, 0.0, 1.0)防止距離小幅超出[startCm, endCm]時 ratio 跑掉。- 例如在
0 ≤ d < 50(startCm=0, endCm=50):
| 距離 | ratio | interval (ms) |
|---|---|---|
| 0 cm | 0.0 | 1000 |
| 25 cm | 0.5 | 550 |
| 49 cm | 0.98 | 118 |
| 50 cm | 進入下一 range | LED1 = continuously ON |
setup()
- 將 pin 設為 OUTPUT/INPUT。
- 把所有 LED 預設拉 LOW,避免 power on 時殘留亮起。
- HC-SR04 的 trigger pin 在第一次測距前先處於 LOW 狀態。
主 loop()(題目給定,不需修改)
- 每次 loop 量一次距離 → 換成 cm → 把所有 LED 先關 → 依距離區間決定哪些 LED ON、哪一顆呼叫
controlBlinkingLED()閃爍 →delay(50)控制 sample rate(約 20 Hz)。 - 注意
delay(50)不會干擾 blinking 的 ms 計時,因為millis()在 delay 期間仍持續累計。
補充說明:常見扣分點
- 忘記
pinMode(echoPin, INPUT)→pulseIn()讀不到 pulse。 - 在
loop()中誤用delay(interval)來閃爍 → 主程式期間無法測距,距離 update 緩慢。 pulseIn()沒有 timeout 參數 → 沒有物體時程式會 hang 1 秒(預設 timeout 1,000,000 μs)。controlBlinkingLED()內未處理 LED 切換 → 換 range 時前一顆 LED 仍亮著。- 把 distance 算進函式裡(讓函式回傳 cm)→ 與題目已給 main loop 的呼叫方式不符,會編譯錯誤。
最終答案總表
Section A
| Question | Answer |
|---|---|
| A1 | No exact option; correct instruction is MOV IE, #10011000B |
| A2 | B |
| A3 | B |
| A4 | C |
| A5 | C |
| A6 | B |
| A7 | B |
| A8 | C |
| A9 | C |
| A10 | C |
Section B
| Part | Key answer |
|---|---|
| B1(a) | 16-bit, Mode 1, 65536 counts, 65.536 ms |
| B1(b) | Cannot directly replace 0.8 s delay with one overflow |
| B1(c) | TH0 = 3CH, TL0 = B0H, 16 overflows |
| B1(d) | Timer 0 vector 000BH, IE = 82H |
| B1(e) | Use Timer 0 ISR, reload 3CB0H, decrement software counter, toggle P1 every 16 overflows |
| B2(a) | HC-SR04: VCC-5V, TRIG-D3, ECHO-D2, GND-GND; LEDs via resistors to digital pins and GND |
| B2(b) | Complete Arduino sketch shown above |