3.12. 使用 ADC 讀取類比訊號¶
到目前為止,相機讀取的都是數位訊號——接腳不是 0 就是 1,開關不是斷開就是閉合。但實際感測器輸出的大多數訊號都是類比的:一種在某個範圍內平滑變化的連續電壓。當環境亮度改變時,光敏電阻會掃過電源軌之間的每一個電壓值。當房間升溫時,溫度感測器的輸出會漂移數毫伏特。麥克風的輸出則會隨著周圍的聲音上下起伏。
類比數位轉換器(analog-to-digital converter,ADC)就是兩者之間的橋樑。它會對接腳上的電壓取樣,並回傳一個整數,讓 Python 能像讀取其他數值一樣讀取它。
3.12.1. 量化¶
數位值無法精確表示連續電壓。ADC 的工作就是量化——把每個取樣值貼齊到一組固定電平中最接近的那一個。N 位元的 ADC 有 2^N 個電平;12 位元的轉換器在其輸入範圍內分布著 4096 個電平。
量化:類比訊號(實線)的每個取樣值都被捨入到有限的一組數位電平之一(階梯狀虛線)。¶
兩個相鄰電平之間的電壓就是 ADC 的步階大小;任何比它更小的變化都會在捨入中消失。在 3.3 V 範圍上的 12 位元 ADC,其步階大小約為 3.3 / 4096 ≈ 0.8 mV——精細到足以讓大多數訊號在軟體中看起來實際上是連續的。
3.12.2. machine.ADC 類別¶
machine.ADC 封裝了一個類比輸入通道。用你想讀取的接腳來建構它,然後呼叫 read_u16():
from machine import ADC
adc = ADC("P6")
value = adc.read_u16()
print(value)
read_u16() 永遠回傳一個介於 0 與 65535 之間的無號 16 位元整數。原生的 ADC 解析度會因開發板而異(STM32 上為 12 位元,其他平台則視埠而定);結果會被左對齊到 16 位元,因此硬體細節不會洩漏到 Python 中——不論是哪顆晶片,65535 都代表滿刻度。
參考電壓——也就是對應到滿刻度的輸入——取決於開發板。請查閱 OpenMV 開發板 以取得你的相機上的數值。任何高於參考電壓的輸入都會被讀成滿刻度(若超過絕對最大輸入電壓,還可能損壞接腳)。
3.12.2.1. 將計數值轉換為電壓¶
從計數值到電壓的對應是線性的,滿刻度計數值正好對應到 Vref:
voltage = counts × Vref / 65535
在程式碼中:
VREF = 3.3 # cam-dependent; see the quickref
counts = adc.read_u16()
voltage = counts * VREF / 65535
print(voltage, "V")
3.12.3. 分壓器¶
在一條電壓軌與接地之間串聯兩個電阻,就形成了一個分壓器。它們之間的節點所處的電壓由兩個電阻的比值決定:
分壓器:串聯的 R1 與 R2 將 Vin 按比例降至 V_out。¶
V_out = Vin × R2 / (R1 + R2)
相等的電阻會得到電源軌電壓的一半;R2 遠小於 R1 會讓引出點接近接地;R2 遠大於則會讓它接近電源軌。
這個公式假設沒有其他元件從 V_out 抽取明顯的電流。ADC 接腳是高阻抗的(百萬歐姆等級、奈安培等級的電流),輕易就能滿足這項條件,因此饋入 ADC 的分壓器其行為會與公式所預測的一致。
3.12.4. 電位器¶
電位器是一個單一的實體元件,本身就是一個分壓器,它有一個滑動的滑臂,可在兩端之間移動引出點。轉動旋鈕會同時改變 R1 與 R2,同時保持它們的總和(電位器的總電阻)不變。
電位器作為 ADC 的手動電壓源接線:一端接 3.3 V,另一端接地,滑臂接到接腳。¶
電位器是試用 ADC 的標準輸入裝置。將一端接到 3.3 V,另一端接地,滑臂接到一支支援 ADC 的接腳;轉動旋鈕就會讓滑臂掃過電源軌之間的每一個電壓值。
import time
from machine import ADC
pot = ADC("P6")
VREF = 3.3
while True:
counts = pot.read_u16()
voltage = counts * VREF / 65535
print(voltage, "V")
time.sleep_ms(100)
3.12.5. 用分壓器讀取較高的電壓¶
高於 Vref 的電壓會把 ADC 釘在滿刻度,若超過絕對最大額定值還可能損壞輸入。若要讀取較高的訊號源——例如電池,或範圍超出 Vref 的感測器輸出——請在它抵達接腳之前,用一個固定分壓器將它按比例降低:
將高電壓源縮放至符合 ADC 範圍:R1 與 R2 組成一個固定分壓器,其引出點饋入 ADC 接腳。¶
選擇 R1 與 R2,使得在你預期的最高輸入電壓下,分壓後的電壓仍保持在 ADC 的範圍之內:
V_adc = V_in × R2 / (R1 + R2)
對於最大 V_in = 12 V 與 3.3 V 的參考電壓,比值 R2 / (R1 + R2) 必須至多為 3.3 / 12 ≈ 0.275。一個帶有少許餘裕的常見選擇是 R1 = 33 kΩ、R2 = 10 kΩ。比值為 10 / 43 ≈ 0.233,因此 V_adc 最高約為 12 × 0.233 ≈ 2.79 V——安全地低於 Vref。
若要從 ADC 讀值還原出原始的 V_in,將分壓公式反推即可:
V_in = V_adc × (R1 + R2) / R2
在程式碼中:
from machine import ADC
R1 = 33_000
R2 = 10_000
VREF = 3.3
adc = ADC("P6")
counts = adc.read_u16()
v_adc = counts * VREF / 65535
v_in = v_adc * (R1 + R2) / R2
print(v_in, "V")
幾點實務注意事項:
分壓器會持續抽取
V_in / (R1 + R2)的電流。當R1 + R2 = 43 kΩ且V_in = 12 V時,約為 280 µA——通常可忽略不計,但如果訊號源是電池供電,可考慮使用較大的電阻(100 kΩ 至 1 MΩ)以降低待機耗電。電阻的容差(通常為 ±1 % 或 ±5 %)會直接影響量測精度。兩個 ±5 % 的電阻在最壞情況下可能使還原出的
V_in產生約 ±10 % 的誤差。分壓器的源阻抗會與任何雜散電容結合,對輸入進行低通濾波。對於快速變化的訊號這很重要;但對於電池電壓的檢查則無關緊要。