說明:除非特別註明,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」:

  1. 必須打開 global enable:EA = 1(bit 7)。沒有 EA = 1,所有個別 enable bit 都不會生效。
  2. 打開 Serial:ES = 1(bit 4)。
  3. 打開 Timer 1:ET1 = 1(bit 3)。
  4. 其他全部 = 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 的關鍵:

  1. 事件是否可預測 — 不可預測 → interrupt
  2. 事件頻率高還是低 — 低或不規則 → interrupt;極高速且固定 → polling/DMA 反而較佳
  3. 是否需要 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) and TI (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 的硬體把 RITI 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 之間的優先次序。
  • DIP register 控制的是「五個 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}

其中 SMODPCON 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 衝突的機制有兩層:

  1. **IP register 設定的兩級優先權**(high / low)
  2. 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:

  1. USB 供電壓降較小,ADC 對 supply 的敏感度也較弱 → 誤差變小
  2. 但 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 DJNZ dummy 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」:

  1. 設 timer reload 值,使每次 overflow 約 50 ms。
  2. 用一個工作暫存器(例如 R7)做 software counter,每次 overflow 把 R7 減 1。
  3. 累計 \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 寫成 0FFH3CH 因為以數字開頭可直接寫。


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

設計思路

  1. **ORG 0000H** 後立即 LJMP MAIN:reset vector 只有 3 個 byte,必須跳過 interrupt vector table。
  2. **ORG 000BH**LJMP T0_ISR:Timer 0 vector 也只有 8 byte 空間,習慣上 LJMP 出去。
  3. **ORG 0030H** 放主程式:避開所有 interrupt vector area(最後一個 0023H 之後)。
  4. ISR 內:先 CLR TR0 停止 timer,再 reload TH0/TL0;最後 SETB TR0 重啟,RETI 結束(注意是 RETI 不是 RET,否則 interrupt logic 不會解鎖)。
  5. 因為 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 稍長一點。要更精確可:

  1. 不停 timer,直接寫 TH0/TL0(但有 race condition 風險)。
  2. 或在 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 + 四顆限流電阻),要做:

  1. (a) 把 breadboard 圖轉成電路 schematic。
  2. (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() 完整實作;要補的是:

  1. Pin 變數宣告
  2. readUltrasonicDistance()
  3. controlBlinkingLED()
  4. 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 ms50 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 偵測切換、重置 ledStatepreviousMillis

完整 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 期間仍持續累計。

補充說明:常見扣分點

  1. 忘記 pinMode(echoPin, INPUT)pulseIn() 讀不到 pulse。
  2. loop() 中誤用 delay(interval) 來閃爍 → 主程式期間無法測距,距離 update 緩慢。
  3. pulseIn() 沒有 timeout 參數 → 沒有物體時程式會 hang 1 秒(預設 timeout 1,000,000 μs)。
  4. controlBlinkingLED() 內未處理 LED 切換 → 換 range 時前一顆 LED 仍亮著。
  5. 把 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