3.12. ADC로 아날로그 읽기

지금까지 카메라는 디지털 신호를 읽었습니다. 핀은 0 또는 1 이고, 스위치는 열려 있거나 닫혀 있습니다. 그러나 실제 세계의 센서에서 나오는 대부분의 신호는 아날로그입니다. 즉, 어떤 범위에 걸쳐 부드럽게 변하는 연속적인 전압입니다. 광저항(포토레지스터)은 주변 밝기가 변함에 따라 전원 레일 사이의 모든 전압을 거쳐 갑니다. 온도 센서의 출력은 방이 더워지면서 몇 밀리볼트씩 변합니다. 마이크의 출력은 주변 소리에 따라 오르내립니다.

아날로그-디지털 변환기(ADC)가 그 다리 역할을 합니다. 핀의 전압을 샘플링하여 Python이 다른 값과 마찬가지로 읽을 수 있는 정수를 반환합니다.

3.12.1. 양자화

디지털 값은 연속적인 전압을 정확하게 표현할 수 없습니다. ADC의 역할은 양자화입니다. 즉, 각 샘플을 고정된 레벨 집합 중 가장 가까운 값으로 스냅합니다. N 비트 ADC는 2^N 개의 레벨을 가지며, 12비트 변환기는 입력 범위 전체에 걸쳐 4096개의 레벨을 가집니다.

시간에 대해 그려진 부드러운 아날로그 곡선 위에 계단형 디지털 근사가 겹쳐져 있습니다. 점선으로 된 수평선이 양자화 레벨을 나타내며, 계단형 곡선은 각 샘플 지점에서 아날로그 신호에 가장 가까운 레벨로 스냅됩니다.

양자화: 아날로그 신호의 각 샘플(실선)은 유한한 디지털 레벨 집합 중 하나로 반올림됩니다(계단형 점선).

두 인접한 레벨 사이의 전압이 ADC의 스텝 크기입니다. 그보다 작은 것은 반올림으로 사라집니다. 3.3V 범위의 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() 는 항상 065535 사이의 부호 없는 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. 전압 분배기

전압 레일과 접지 사이에 직렬로 연결된 두 저항은 전압 분배기를 형성합니다. 그 사이의 노드는 두 저항의 비율에 의해 결정되는 전압에 놓입니다:

전압 분배기. 맨 위의 Vin이 R1을 통해 V_out으로 분기되는 노드에 연결되고, 그 노드는 R2를 통해 접지로 연결됩니다.

전압 분배기: 직렬로 연결된 R1R2VinV_out 으로 낮춥니다.

V_out = Vin × R2 / (R1 + R2)

동일한 저항은 레일 전압의 절반을 줍니다. R2R1 보다 훨씬 작으면 분기점이 접지에 가까워지고, R2 가 훨씬 크면 레일에 가까워집니다.

이 공식은 V_out 에서 다른 곳으로 의미 있는 전류가 빠져나가지 않는다고 가정합니다. ADC 핀은 고임피던스(메가옴, 나노암페어)여서 이를 쉽게 만족하므로, ADC에 연결된 분배기는 공식이 예측하는 대로 동작합니다.

3.12.4. 포텐셔미터

포텐셔미터는 그 자체가 정확히 전압 분배기인 단일 물리 부품으로, 양 끝 사이에서 분기점을 움직이는 슬라이딩 와이퍼를 가지고 있습니다. 노브를 돌리면 R1R2 가 함께 변하지만 그 합(포텐셔미터의 전체 저항)은 일정하게 유지됩니다.

3.3V와 접지 사이에 연결된 포텐셔미터. 와이퍼는 ADC 핀으로 분기됩니다.

ADC를 위한 수동 전압원으로 연결된 포텐셔미터: 한쪽 끝은 3.3V, 다른 쪽은 접지, 와이퍼는 핀에 연결됩니다.

포텐셔미터는 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.3V 기준 전압의 경우, 비율 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로, 보통은 무시할 수 있지만 전원이 배터리라면 대기 소모를 줄이기 위해 더 큰 저항(100kΩ에서 1MΩ)을 고려하세요.

  • 저항 공차(일반적으로 ±1% 또는 ±5%)는 측정 정확도에 직접 영향을 줍니다. ±5% 저항 두 개는 복원된 V_in 에 최악의 경우 약 ±10%의 오차를 줄 수 있습니다.

  • 분배기의 소스 임피던스는 부유 정전용량과 결합하여 입력을 저역 통과 필터링합니다. 빠르게 변하는 신호에서는 이것이 중요하지만, 배터리 전압 확인에는 그렇지 않습니다.