Learning Objectives
- 說明 ADC 如何把電壓換成數字,以及 12 位元代表 0~4095
- 把可變電阻、光敏電阻接成分壓電路,用 machine.ADC 讀取
- 分辨 read()、read_u16()、read_uv() 的範圍,並說明衰減設定的作用
- 用讀值控制 LED 亮度,並用兩個門檻(遲滯)做出不會一直閃的夜燈
世界不是只有 0 與 1
第 04 課的按鈕只有按下與放開兩種狀態,這是數位訊號。但很多感測器輸出的是連續變化的電壓: 旋鈕轉到一半是 1.65V、光線越亮電壓越高、溫度升高電壓跟著變,這是類比訊號。
微控制器的 CPU 只會處理數字,所以需要類比數位轉換器(ADC,Analog-to-Digital Converter): 量一次腳位的電壓,換成一個整數。
12 位元:把電壓切成 4096 格
ESP32 的 ADC 是 12 位元,讀值範圍是 0~4095(2¹² = 4096 種值)。可以想成把量測範圍切成 4096 格:
讀值 ≈ 電壓 ÷ 量測上限 × 4095
假設量測上限是 3.3V:
0V → 0
1.65V → 約 2047
3.3V → 4095
每一格約 3.3V ÷ 4096 ≈ 0.8 mV
格子越細,量得越精細,這叫解析度。電壓介於兩格之間時只能取其中一格,這個誤差叫量化誤差。 數字的二進位表示可以對照「計算機概論」軌的數字系統。
可變電阻與光敏電阻怎麼接
可變電阻(電位器)有三支腳:兩端接 3.3V 與 GND,中間的腳(滑動端)接到 ADC 腳位。 轉動旋鈕時,中間腳的位置在電阻上移動,電壓就在 0V 到 3.3V 之間變化。這種接法叫分壓,「自造者入門」軌第 04 課會算給你看。
3.3V ──┐
╪ 可變電阻 ←── 中間腳接 GPIO34
GND ──┘
光敏電阻(LDR)只有兩支腳,電阻值隨光線改變。和一顆固定電阻(例如 10kΩ)串聯成分壓電路, 中間點接 ADC,光線改變時中間點的電壓就跟著變。本課的接法是「越亮讀值越大」,接法反過來,讀值也會反過來。
接到 ADC 腳位的電壓不可以超過 3.3V。可變電阻的兩端要接 3.3V,不要接 5V。
ESP32 有兩組 ADC
| 組別 | 腳位 | 注意 |
|---|---|---|
| ADC1 | GPIO32~39 | 隨時都能用 |
| ADC2 | GPIO0、2、4、12~15、25~27 | Wi-Fi 開啟時不能用,讀取會出現例外 |
之後的課要連 Wi-Fi,所以類比感測器一律接 ADC1。GPIO34~39 只能當輸入,剛好適合接感測器。
machine.ADC:三種讀法與衰減
from machine import Pin, ADC
pot = ADC(Pin(34))
pot.atten(ADC.ATTN_11DB) # 設定衰減
pot.read() # 0~4095:原始讀值(依解析度)
pot.read_u16() # 0~65535:換成 16 位元的範圍,不同開發板寫法一致
pot.read_uv() # 校正過的電壓,單位是微伏(μV)
衰減(attenuation)決定能量多高的電壓。不衰減時 ADC 只能量到大約 0.95V,衰減會先把輸入的電壓縮小再量,所以能量的範圍變大。 MicroPython 官方文件列出 ESP32 大約的線性範圍:
| 設定 | 大約可量的範圍 |
|---|---|
ADC.ATTN_0DB |
100 mV~950 mV |
ADC.ATTN_2_5DB |
100 mV~1250 mV |
ADC.ATTN_6DB |
150 mV~1750 mV |
ADC.ATTN_11DB |
150 mV~2450 mV |
接 0~3.3V 的感測器通常設 ATTN_11DB。注意兩端:大約 100 mV 以下會讀成 0,接近 3.3V 時讀值也不準。
需要準確的電壓時用 read_uv(),它用晶片裡的校正資料換算,但也只在線性範圍內準確。
本站的模擬器把 0~3.3V 簡單地對應到 0~4095,方便看懂數字;真的 ESP32 讀值在兩端會偏離直線,而且每次讀都會有一點雜訊。
逐步看:讀可變電阻
旋鈕從 0 轉到底(上方的可變電阻顯示目前的比例)。每一圈用三種方法讀同一個電壓,比較它們的範圍。
逐步看:旋鈕調光
輸入接到輸出:讀旋鈕的位置,直接當作 LED 的工作週期。旋鈕先轉大、再轉回來,LED 跟著變亮又變暗。
夜燈:一個門檻不夠
做一個天黑自動開燈的夜燈,最直覺的寫法是一個門檻:
if ldr.read() < 1400:
lamp.value(1)
else:
lamp.value(0)
問題是黃昏時光線在門檻附近,加上讀值本身有雜訊,讀值會在 1390、1410 之間跳來跳去,燈就一直閃。
解法是用兩個門檻:低於 DARK 才開燈,高於 BRIGHT 才關燈,兩者之間維持原本的狀態。
兩個門檻中間的這段緩衝叫遲滯(hysteresis),冷氣的溫控、水塔的抽水馬達都用同樣的想法。
逐步看:有遲滯的夜燈
光線先變暗、再慢慢變亮。注意讀值在 1200~1600 之間時,燈維持上一次的狀態:變暗的途中維持暗,變亮的途中維持亮。
小測驗:解析度與範圍
ESP32 的 ADC 是 12 位元,read() 最大會回傳多少?
假設 0~3.3V 線性對應 0~4095,下面的程式會印出什麼?
raw = 1241
print(round(raw / 4095 * 3.3, 2))
要把 0~3.3V 的感測器電壓盡量量完整,衰減應該設成哪一個?
小測驗:讀法與門檻
下面模擬有遲滯的夜燈:readings 是依序讀到的光線值,會印出什麼?
readings = [1500, 1100, 1300, 1700, 1400]
lamp = 0
for v in readings:
if v < 1200:
lamp = 1
elif v > 1600:
lamp = 0
print(lamp, end=" ")
想用旋鈕直接控制 PWM 的工作週期,哪一種讀法最方便?
重點整理
- ADC 把電壓換成整數;ESP32 是 12 位元,
read()範圍 0~4095。 - 可變電阻與光敏電阻用分壓接法接到 ADC 腳位;輸入電壓不可超過 3.3V。
read()原始讀值、read_u16()0~65535、read_uv()校正後的微伏。atten()設定衰減:ATTN_11DB可量的範圍最大,兩端(約 100 mV 以下、接近 3.3V)讀值不準。- ADC1(GPIO32~39)隨時可用;ADC2 在 Wi-Fi 開啟時不能用。
- 用兩個門檻做遲滯,讀值在門檻附近時輸出才不會一直跳動。
下一課處理時間:程式在 sleep 的時候什麼都不能做,要怎麼同時閃燈、讀按鈕、讀感測器?