3.12. קריאת אות אנלוגי באמצעות ה-ADC¶
עד כה המצלמה קראה אותות דיגיטליים – פין הוא 0 או 1, מתג פתוח או סגור. רוב האותות שמגיעים מחיישנים בעולם האמיתי הם אנלוגיים: מתח רציף שמשתנה בצורה חלקה על פני טווח כלשהו. נגד-אור (photoresistor) עובר דרך כל מתח שבין הפסים כאשר הבהירות הסביבתית משתנה. מוצא של חיישן טמפרטורה נע במספר מיליוולטים כאשר חדר מתחמם. מוצא של מיקרופון עולה ויורד בהתאם לקול שסביבו.
ממיר אנלוגי-לדיגיטלי (ADC) הוא הגשר. הוא דוגם את המתח על פין ומחזיר מספר שלם ש-Python יכולה לקרוא כמו כל ערך אחר.
3.12.1. קוונטיזציה¶
ערך דיגיטלי אינו יכול לייצג מתח רציף בדיוק מוחלט. תפקיד ה-ADC הוא לקוונטֵט – להצמיד כל דגימה לרמה הקרובה ביותר מתוך מערכה קבועה של רמות. ל-ADC בעל N סיביות יש 2^N רמות; לממיר בן 12 סיביות יש 4096 רמות פרושות על פני טווח הכניסה שלו.
קוונטיזציה: כל דגימה של האות האנלוגי (קו מלא) מעוגלת לאחת ממערכה סופית של רמות דיגיטליות (קו מדורג מקווקו).¶
המתח שבין שתי רמות סמוכות הוא גודל הצעד של ה-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, תלויית-port במקומות אחרים); התוצאה מיושרת לשמאל לתוך 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. פוטנציומטרים¶
פוטנציומטר הוא רכיב פיזי יחיד שהוא בדיוק מחלק מתח, עם מגרד (wiper) הזזה שמזיז את נקודת ההקשה בין שני הקצוות. סיבוב הכפתור משנה את 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.
כדי לשחזר את 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 kΩו-V_in = 12 V, זהו בערך 280 µA – בדרך כלל זניח, אך אם המקור מופעל בסוללה שקלו נגדים גדולים יותר (100 kΩ עד 1 MΩ) כדי לצמצם את צריכת המנוחה.סבילות הנגד (בדרך כלל ±1 % או ±5 %) מזינה ישירות אל תוך דיוק המדידה. שני נגדים בסבילות ±5 % יכולים לתת ל-
V_inהמשוחזר שגיאה במקרה הגרוע ביותר של בערך ±10 %.עכבת המקור של המחלק משתלבת עם כל קיבול טפילי כדי לסנן את הכניסה בסינון מעביר-נמוכים. עבור אותות המשתנים במהירות זה משמעותי; עבור בדיקת מתח סוללה זה לא.