3.12. การอ่านค่าแอนะล็อกด้วย ADC

จนถึงตอนนี้ กล้องอ่านสัญญาณดิจิทัล -- พินอยู่ในสถานะ 0 หรือ 1 สวิตช์เปิดหรือปิด สัญญาณส่วนใหญ่ที่มาจากเซนเซอร์ในโลกจริงเป็นแอนะล็อก: แรงดันไฟฟ้าต่อเนื่องที่แปรผันอย่างราบเรียบในช่วงค่าหนึ่ง โฟโตรีซิสเตอร์กวาดผ่านทุกแรงดันระหว่างราง 0 V และ Vcc ตามความสว่างของสภาพแวดล้อม เอาต์พุตของเซนเซอร์อุณหภูมิเปลี่ยนแปลงเป็นมิลลิโวลต์ตามการเปลี่ยนแปลงของอุณหภูมิห้อง เอาต์พุตของไมโครโฟนเพิ่มและลดตามเสียงรอบข้าง

ตัวแปลงแอนะล็อกเป็นดิจิทัล (ADC) คือสะพานเชื่อม มันสุ่มตัวอย่างแรงดันบนพินและส่งคืนค่าจำนวนเต็มที่ Python อ่านได้เหมือนค่าอื่น ๆ

3.12.1. การหาปริมาณ

ค่าดิจิทัลไม่สามารถแทนแรงดันไฟฟ้าต่อเนื่องได้อย่างแม่นยำ หน้าที่ของ ADC คือ หาปริมาณ -- ปัดค่าแต่ละตัวอย่างให้ใกล้เคียงที่สุดจากชุดระดับที่กำหนดไว้ ADC N บิตมีระดับ 2^N ระดับ โดยตัวแปลง 12 บิตมี 4096 ระดับกระจายอยู่ทั่วช่วงอินพุต

A smooth analog curve plotted against time, overlaid with a stepped digital approximation. Dashed horizontal lines mark the quantization levels; the stepped curve snaps to whichever level is nearest the analog signal at each sample point.

การหาปริมาณ: แต่ละตัวอย่างของสัญญาณแอนะล็อก (เส้นทึบ) จะถูกปัดให้เป็นหนึ่งในชุดระดับดิจิทัลจำกัด (เส้นประขั้นบันได)

แรงดันระหว่างสองระดับที่อยู่ติดกันคือ ขนาดก้าว ของ ADC สิ่งใดที่เล็กกว่านั้นจะหายไปในการปัดเศษ ADC 12 บิตในช่วง 3.3 V มีขนาดก้าวประมาณ 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() ส่งคืนจำนวนเต็มไม่มีเครื่องหมาย 16 บิตระหว่าง 0 และ 65535 เสมอ ความละเอียด ADC ดั้งเดิมแตกต่างตามบอร์ด (12 บิตบน STM32, เฉพาะพอร์ตอื่น ๆ) ผลลัพธ์จัดชิดซ้ายใน 16 บิตเพื่อให้รายละเอียดฮาร์ดแวร์ไม่รั่วไหลสู่ Python -- ค่า 65535 คือสเกลเต็มไม่ว่าชิปจะเป็นอะไร

แรงดันอ้างอิง -- อินพุตที่ตรงกับสเกลเต็ม -- ขึ้นอยู่กับบอร์ด ตรวจสอบ บอร์ด OpenMV สำหรับค่าบน cam ของคุณ ค่าใดที่สูงกว่าอ้างอิงจะอ่านเป็นสเกลเต็ม (และอาจทำลายพินหากเกินแรงดันอินพุตสูงสุดสัมบูรณ์)

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. ตัวแบ่งแรงดัน

ตัวต้านทานสองตัวต่ออนุกรมระหว่างรางแรงดันและกราวด์จะสร้าง ตัวแบ่งแรงดัน จุดต่อระหว่างทั้งสองอยู่ที่แรงดันที่กำหนดโดยอัตราส่วนของตัวต้านทานสองตัว:

A voltage divider. Vin at the top connects through R1 to a node tapped off as V_out, which then connects through R2 to ground.

ตัวแบ่งแรงดัน: R1 และ R2 อนุกรมกันเพื่อลด Vin ลงเป็น V_out

V_out = Vin × R2 / (R1 + R2)

ตัวต้านทานที่เท่ากันให้แรงดันครึ่งหนึ่งของราง R2 ที่เล็กกว่า R1 มากทำให้แตะใกล้กราวด์ R2 ที่ใหญ่กว่ามากทำให้แตะใกล้ราง

สูตรนี้ถือว่าไม่มีสิ่งใดดึงกระแสที่สำคัญจาก V_out พิน ADC มีอิมพีแดนซ์สูง (เมกะโอห์ม นาโนแอมป์) และตอบสนองเงื่อนไขนี้ได้ง่าย ดังนั้นตัวแบ่งที่ป้อน ADC จะทำงานตามที่สูตรทำนาย

3.12.4. โพเทนชิโอมิเตอร์

โพเทนชิโอมิเตอร์ คือชิ้นส่วนกายภาพชิ้นเดียวที่เป็นตัวแบ่งแรงดันอย่างแท้จริง มีหน้าสัมผัสเลื่อนที่เคลื่อนที่แตะระหว่างปลายทั้งสอง การหมุนลูกบิดจะเปลี่ยน R1 และ R2 พร้อมกันในขณะที่รักษาผลรวม (ความต้านทานรวมของโพท) คงที่

A potentiometer wired between 3.3 V and ground. The wiper is tapped off to an ADC pin.

โพเทนชิโอมิเตอร์ต่อสายเป็นแหล่งแรงดันแบบแมนนวลสำหรับ 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 -- ลดสเกลด้วยตัวแบ่งแรงดันคงที่ก่อนถึงพิน:

A voltage divider scaling a high V_in down to an ADC pin. R1 runs from V_in down to a junction, which is tapped off horizontally to the ADC pin; R2 continues from the junction down to ground.

การลดสเกลแหล่งแรงดันสูงให้พอดีกับ 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 , R2 = 10 อัตราส่วนคือ 10 / 43 0.233 ดังนั้น V_adc สูงสุดประมาณ 12 × 0.233 2.79 V -- ต่ำกว่า Vref อย่างปลอดภัย

ในการกู้คืน V_in เดิมจากการอ่าน ADC ให้กลับสูตรตัวแบ่ง:

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%) ส่งผลโดยตรงต่อความแม่นยำในการวัด ตัวต้านทาน ±5% สองตัวอาจให้ค่า V_in ที่กู้คืนมาผิดพลาดในกรณีเลวร้ายสุดประมาณ ±10%

  • อิมพีแดนซ์ต้นทางของตัวแบ่งรวมกับความจุปรสิตใด ๆ จะกรองผ่านต่ำอินพุต สำหรับสัญญาณที่เปลี่ยนเร็ว เรื่องนี้สำคัญ แต่สำหรับการตรวจสอบแรงดันแบตเตอรี่ไม่มีนัยสำคัญ