3.12. ADC でアナログ信号を読み取る

これまでカメラはデジタル信号を読み取ってきました。ピンは 01 のいずれか、スイッチは開いているか閉じているかのどちらかです。しかし、実世界のセンサーから得られる信号のほとんどはアナログです。つまり、ある範囲を滑らかに変化する連続的な電圧です。光抵抗(フォトレジスタ)は周囲の明るさが変化するにつれて電源電圧の間のあらゆる電圧を取り得ます。温度センサーの出力は部屋が暖まるにつれて数ミリボルトずつ変動します。マイクの出力は周囲の音に応じて上下します。

アナログ-デジタル変換器(ADC)はその橋渡しをします。ピン上の電圧をサンプリングし、Python が他の値と同じように読み取れる整数を返します。

3.12.1. 量子化

デジタル値は連続的な電圧を正確に表すことができません。ADC の役割は量子化すること、つまり各サンプルを固定された一連のレベルのうち最も近いものに丸めることです。N ビットの ADC は 2^N 段階のレベルを持ち、12 ビットの変換器は入力範囲全体にわたって 4096 段階を持ちます。

時間に対してプロットされた滑らかなアナログ曲線に、 階段状のデジタル近似が重ね合わされている。破線の 水平線が量子化レベルを示し、階段状の曲線は各サンプル 点でアナログ信号に最も近いレベルに丸められる。

量子化: アナログ信号(実線)の各サンプルは、有限個のデジタルレベル(階段状の破線)のいずれかに丸められます。

隣り合う 2 つのレベル間の電圧が ADC のステップサイズです。それより小さいものは丸めによって消えてしまいます。3.3 V 範囲にわたる 12 ビット ADC のステップサイズは約 3.3 / 4096 0.8 mV で、ほとんどの信号がソフトウェア上では実質的に連続して見えるほど細かい値です。

3.12.2. machine.ADC クラス

machine.ADC は 1 つのアナログ入力チャネルをラップします。読み取りたいピンを指定して生成し、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. 分圧回路

電圧レールとグランドの間に直列に接続された 2 つの抵抗は分圧回路を形成します。その間のノードは、2 つの抵抗の比によって決まる電圧になります:

分圧回路。上部の Vin は R1 を通じて V_out として 取り出されるノードに接続され、そのノードはさらに R2 を通じてグランドに接続される。

分圧回路: R1R2 を直列に接続し、VinV_out へと減衰させます。

V_out = Vin × R2 / (R1 + R2)

等しい抵抗ではレール電圧の半分になります。R2R1 よりはるかに小さいと取り出し点はグランドに近くなり、R2 がはるかに大きいとレールに近くなります。

この式は V_out から他に目立った電流を引き出すものがないことを前提としています。ADC ピンは高インピーダンス(メガオーム単位、ナノアンペア単位)で、容易にその条件を満たすため、ADC に接続された分圧回路は式どおりに動作します。

3.12.4. ポテンショメータ

ポテンショメータは、まさに分圧回路そのものである単一の物理部品で、両端の間で取り出し点を移動させる摺動ワイパーを備えています。つまみを回すと、合計(ポット全体の抵抗値)を一定に保ちながら R1R2 が同時に変化します。

3.3 V とグランドの間に配線されたポテンショメータ。 ワイパーが ADC ピンへ取り出されている。

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 を超える範囲を持つセンサー出力を読み取るには、ピンに到達する前に固定の分圧回路で電圧を下げます:

高い V_in を ADC ピンに合わせて減衰させる分圧回路。 R1 は V_in から接続点まで下り、そこから水平に ADC ピンへ取り出される。R2 は接続点から グランドまで続く。

高電圧源を ADC に合わせて縮小する: R1R2 が固定の分圧回路を形成し、その取り出し点が ADC ピンに供給します。

想定される最大入力電圧において、分圧された電圧が ADC の範囲内に収まるように R1R2 を選びます:

V_adc = V_in × R2 / (R1 + R2)

最大 V_in = 12 V と 3.3 V の基準電圧の場合、比 R2 / (R1 + R2) は最大でも 3.3 / 12 0.275 でなければなりません。少し余裕を持たせた一般的な選択肢は R1 = 33 R2 = 10 です。この比は 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 V_in = 12 V の場合、これは約 280 µA で、通常は無視できる程度ですが、電源がバッテリーの場合はアイドル時の消費を減らすためにより大きな抵抗(100 kΩ から 1 MΩ)を検討してください。

  • 抵抗の許容差(通常 ±1 % または ±5 %)は測定精度に直接影響します。2 つの ±5 % 抵抗では、復元された V_in に最悪の場合およそ ±10 % の誤差が生じることがあります。

  • 分圧回路の出力インピーダンスは浮遊容量と組み合わさって入力をローパスフィルタリングします。高速に変化する信号ではこれが問題になりますが、バッテリー電圧のチェック程度であれば問題になりません。