برمجة المتحكّمات بلغة مايكروبايثون · الفصل 6 — القراءة التماثليةקלט אנלוגי · תכנות מיקרו-בקרים בשפת MicroPython⌂📚🏠
الفصل 6

القراءة التماثلية

קלט אנלוגי – Analog Input (ADC)
6 ساعات تعليمية

المدخل الرقمي يعرف حالتين فقط: 0 أو 1. لكن العالم لا يتكلم هكذا: الحرارة والضوء وموضع المقبض مقادير تتغيّر باستمرار، والحسّاس يترجمها إلى جهد. في هذا الفصل نقرأ هذا الجهد على ثلاث لوحات، ونكتشف أن الفرق بينها أكبر مما يبدو: واحدة تحتاج إلى ضبط، والثانية لا تحتاج، والثالثة لا تملك محوّلا أصلا.

في هذا الفصل:
  • نحسب التمييز q من عدد البتّات وجهد المرجع.
  • نقرأ مقياس جهد على ESP32 وعلى Pico W، ونفهم لماذا يخطئ البرنامج نفسه إذا نقلناه كما هو.
  • نكتب برنامجا واحدا يعمل على اللوحتين بواسطة الملف board.py.
  • نحوّل القراءة إلى جهد ونسبة مئوية ومقاومة، ونخفّف الضجيج بالمتوسّط.
  • نوصل المحوّل الخارجي ADS1115، وهو الطريق الوحيد إلى قراءة تماثلية على Raspberry Pi 4/5.

6.1 من جهد إلى عدد – ממיר A/D (Analog to Digital)

البرنامج لا يفهم إلا الأعداد. لذلك يوجد داخل المتحكّم مُحوّل يقيس الجهد على الطرف ويضع النتيجة في مسجّل، ومن هناك يقرؤها البرنامج.

المحوّل التماثلي الرقمي (ממיר אנלוגי לספרתי, ממיר A/D): دائرة تحوّل جهدا يقع بين الصفر وجهد المرجع إلى عدد صحيح. واسمه بالإنجليزية ADC.
حسّاسجهد متغيّرالمحوّلجهد ← عددمسجّلعدد صحيحالبرنامجread_u16()
الطريق من الحسّاس إلى البرنامج.

العدد الناتج صحيح دائما، ولذلك لا يرى المحوّل كل الجهود: يقسّم المجال إلى خطوات متساوية، وكل جهد داخل الخطوة نفسها يعطي العدد نفسه. الرسم التالي يبيّن ذلك لمحوّل صغير من 3 بتّات، له 8 خطوات فقط. الخط المتقطّع هو الجواب الذي نتمنّاه، والدرج هو ما نحصل عليه. والفرق بينهما اسمه خطأ التكميم (שגיאת קוונטיזציה).

123456781234567VinN
محوّل من 3 بتّات: المحور الأفقي بوحدات الخطوة q، والمحور العمودي هو العدد N.

6.2 التمييز وجهد المرجع وزمن التحويل – רזולוציה, מתח ייחוס וזמן המרה (Resolution, Reference, Conversion Time)

ثلاثة مقادير تصف أي محوّل:

التمييز (רזולוציה): حجم الخطوة الواحدة
q = Vref2n
الرمزالمعنىوحدة القياس
qالتمييز، أصغر تغيّر في الجهد يراه المحوّل V
Vrefجهد المرجع V
nعدد بتّات المحوّل—
مثال 6.1
محوّل اللوحتين في هذا الكتاب من 12 بتّا، وجهد المرجع 3.3 V. ما هو التمييز؟
الحل:
q = Vref2n
q = 3.3212 = 3.34096
q = 0.806 mV
q = 0.806 mV

والآن نقطة مهمّة في مايكروبايثون. المحوّل من 12 بتّا، لكن الأمر read_u16() يعطي دائما عددا بين 0 و65535، أي على مجال 16 بتّا، في كل اللوحات. هكذا يبقى البرنامج صحيحا إذا انتقل إلى لوحة بمحوّل مختلف. ثمن ذلك أن العدد يقفز بخطوات حجم كل منها نحو 16، وليس 1:

نرفع الجهد بمقدار 0.4 mV في كل ثانية. الأعداد لا تتحرك إلا حين نعبر خطوة كاملة.
import board
import time

pot = board.analog_in(board.POT)

for i in range(6):
    print(pot.read_u16())
    time.sleep(1)
ESP32 + Pico W · MicroPython 1.29 · simulated board
19861
19861
19877
19877
19893
19893
من القراءة إلى الجهد
V = N · Vref65535
الرمزالمعنىوحدة القياس
Nقيمة read_u16()، من 0 إلى 65535—
Vالجهد على الطرف V
مثال 6.2
على الطرف جهد 1.2 V. فأعطى read_u16() العدد 23829. ما الجهد المحسوب؟
الحل:
V = N · Vref65535
V = 23829 · 3.365535
V = 1.1999 V

الفرق عن الجهد الحقيقي أقل من خطوة واحدة، وهذا هو خطأ التكميم.

V = 1.1999 V
سؤال 6.1: محوّل من 10 بتّات وجهد مرجعه 5 V: ما هو تمييزه؟ وهل هو أدق من محوّل اللوحتين أم أقل دقة؟

6.3 ماذا نجد على كل لوحة؟ – מה יש בכל לוח

ESP32Pico W / Pico 2 WPi 4 / Pi 5
اللغةمايكروبايثونمايكروبايثونبايثون على لينكس
محوّل داخلي12 بتّا12 بتّالا يوجد
الأطراف التماثلية32–39؛ 0, 2, 4, 12–15, 25–27GP26, GP27, GP28—
ضبط المجالواجب: attenلا يوجد: دائما حتى 3.3 V—
القراءةread_u16()، read_uv()read_u16()محوّل خارجي عبر I2C
قيد مع WiFiالأطراف غير 32–39 لا تعمللا قيد—
تحذير: لا نوصل بأي طرف جهدا أعلى من جهد تغذية المتحكّم. الحد الأقصى المطلق لطرف ESP32 هو 3.6 V حسب توثيق مايكروبايثون. وحسّاس خرجه 5 V يحتاج أولا إلى مجزّئ جهد.
ملاحظة: على ESP32 كتلتان من المحوّلات، والكتلة الثانية يستعملها WiFi. إذا كان برنامجنا يستعمل الشبكة، فكل قراءة من طرف خارج المجال 32–39 تنتهي بخطأ. لذلك نختار من البداية طرفا من هذا المجال.

6.4 البرنامج الأول: قراءة مقياس جهد – קריאת פוטנציומטר

مقياس الجهد (פוטנציומטר) هو أبسط حسّاس: نوصل طرفيه الخارجيين بـ3V3 وبـGND، والطرف الأوسط يعطي جهدا حسب موضع المقبض.

3V334 (ESP32)GNDGP26 (Pico W)10 kΩV
توصيل مقياس الجهد: الطرف الأوسط إلى الطرف التماثلي في اللوحة.
على ESP32: الطرف 34، مع ضبط المجال.
from machine import ADC, Pin
import time

pot = ADC(Pin(34), atten=ADC.ATTN_11DB)

while True:
    raw = pot.read_u16()
    print(raw)
    time.sleep(1)
ESP32 · MicroPython 1.29 · simulated board
0
16388
32776
49147
65535
...

نقرأ البرنامج سطرا سطرا:

الأمرعمله
from machine import ADC, Pinنستورد صنفي المحوّل والطرف من المكتبة المبنية في المتحكّم.
ADC(Pin(34), atten=ADC.ATTN_11DB)ننشئ محوّلا على الطرف 34، بمجال يصل إلى جهد التغذية.
pot.read_u16()نأخذ قراءة واحدة، عددا بين 0 و65535.
time.sleep(1)ننتظر ثانية، حتى نلحق بقراءة الشاشة.
على Pico W: سطر واحد يختلف، والمُخرَج نفسه.
from machine import ADC, Pin
import time

pot = ADC(Pin(26))

while True:
    raw = pot.read_u16()
    print(raw)
    time.sleep(1)
Pico W · MicroPython 1.29 · simulated board
0
16388
32776
49147
65535
...

لو نقلنا برنامج ESP32 كما هو إلى Pico W لتوقف عند السطر الأول، لأن الطرف 34 غير موجود هناك. والخطأ في الاتجاه المعاكس أخبث، فهو لا يوقف البرنامج:

البرنامج نفسه على ESP32 بلا atten: الجهد يرتفع من 1.65 V إلى 3.3 V، والعدد لا يتغيّر.
from machine import ADC, Pin
import time

pot = ADC(Pin(34))

while True:
    print(pot.read_u16())
    time.sleep(1)
ESP32 · MicroPython 1.29 · simulated board
32776
65535
65535
65535
65535
...
خطأ شائع: دون atten يعمل محوّل ESP32 على أصغر مجال، نحو 1 V. كل جهد أعلى يعطي 65535. البرنامج لا يتوقف ولا يعطي رسالة خطأ، فنظن أن مقياس الجهد معطوب.
الثابتالمجال الخطّي حسب التوثيق
ADC.ATTN_0DB100 mV – 950 mV
ADC.ATTN_2_5DB100 mV – 1250 mV
ADC.ATTN_6DB150 mV – 1750 mV
ADC.ATTN_11DB150 mV – 2450 mV

انتبهوا إلى العمود الثاني: حتى بأكبر مجال، القراءة موثوقة بين 0.15 V و2.45 V فقط. خارج ذلك تقرأ اللوحة الحقيقية أقل من الجهد الفعلي. والمُحاكاة في هذا الكتاب مثالية ولا تُظهر هذا. ولقياس دقيق نستعمل read_uv()، وهو خاص بـESP32 ويطبّق معايرة الشريحة.

ملاحظة: في مواد كثيرة على الإنترنت نجد adc.width() وadc.read() وadc.atten(). هذه أوامر قديمة تعمل على ESP32 وحده، ولا وجود لها على Pico W. لذلك نستعمل في الكتاب read_u16()، وهو الأمر المشترك.
سؤال 6.2: ما الذي يطبعه البرنامج الأخير تقريبا لو كان الجهد 0.275 V؟ اعتبروا المجال 1.1 V.

6.5 برنامج واحد لكل اللوحات – תוכנית אחת לכל הלוחות

عرفنا في الفصل 3 الملف board.py، وفيه كل ما يخص اللوحة. والفرق الذي رأيناه الآن مكانه هناك: الدالة analog_in تخفي ضبط المجال، والبرنامج لا يذكر أرقاما بل أسماء فقط.

على ESP32
# board.py - ESP32 DevKit
from machine import ADC, I2C, PWM, SPI, UART, Pin, Timer

POT = 34
LDR = 35


def analog_in(pin):
    # 11 dB attenuation: the input range reaches the 3.3 V supply.
    return ADC(Pin(pin), atten=ADC.ATTN_11DB)
على Pico W
# board.py - Raspberry Pi Pico W / Pico 2 W
from machine import ADC, I2C, PWM, SPI, UART, Pin, Timer

POT = 26
LDR = 27


def analog_in(pin):
    # The range of the Pico is fixed at 0 to 3.3 V: nothing to configure.
    return ADC(Pin(pin))

ونضيف مكتبة صغيرة ثانية، analog.py، فيها حسابات لا تتعلق باللوحة:

الملف analog.py، على كل اللوحات
# analog.py - helpers for analog readings, the same on every board.
# (c) 2026 Fahed Ghanem - MIT License - fahed-lab.com

VREF = 3.3


def to_volts(raw):
    return raw * VREF / 65535


def scale(x, in_min, in_max, out_min, out_max):
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min


def average(adc, n=16):
    total = 0
    for _ in range(n):
        total += adc.read_u16()
    return total // n
البرنامج نفسه حرفيا على اللوحتين. شغّلناه عليهما عند بناء الكتاب، وتحقّقنا من تطابق المُخرَج.
import board
import time
from analog import to_volts, scale

pot = board.analog_in(board.POT)

while True:
    raw = pot.read_u16()
    volts = to_volts(raw)
    percent = scale(raw, 0, 65535, 0, 100)
    print("raw = %5d   V = %.3f   %3.0f%%" % (raw, volts, percent))
    time.sleep(1)
ESP32 + Pico W · MicroPython 1.29 · simulated board
raw =     0   V = 0.000     0%
raw = 16388   V = 0.825    25%
raw = 32776   V = 1.650    50%
raw = 49147   V = 2.475    75%
raw = 65535   V = 3.300   100%
...

الدالة scale تنقل عددا من مجال إلى مجال، وهي تعطي النسبة المئوية هنا. وسنحتاج إليها مرارا.

ملاحظة: على اللوحة الحقيقية نطبع الأعداد العشرية دائما بصيغة محدّدة مثل "%.3f". الأعداد العشرية في ESP32 وPico W أقل دقة منها في الحاسوب، ومن دون صيغة ستظهر خانات أخيرة مختلفة.
▶تجربة حية — مختبر القراءة التماثليةنكتب البرنامج في مايكروبايثون الحقيقي داخل المتصفّح، نختار اللوحة، ونحرّك مقياس الجهد بالفأرة.
⚠
المختبر غير متاح الآن. المادة في هذا الفصل كاملة من دونه — نواصل القراءة، ونعود إلى التجربة لاحقا أو نفتحها مباشرة من الرابط الذي تحت الإطار.
https://fahed-lab.com/books/micropython/lab/mpy_lab?prog=shared
نفتح المختبر في صفحة مستقلة ↗ fahed-lab.com/books/micropython/lab/mpy_lab?prog=shared

6.6 حسّاس حقيقي: المقاومة الضوئية – נגד תלוי אור (LDR)

المقاومة الضوئية (נגד תלוי אור) لا تعطي جهدا، بل تتغيّر مقاومتها: كلما زاد الضوء قلّت. لذلك نضعها مع مقاومة ثابتة RF في مجزّئ جهد، والمحوّل يقرأ الجهد بينهما.

3V335 (ESP32)GNDGP27 (Pico W)RLDRRF10 kΩV
مجزّئ الجهد: كلما زاد الضوء ارتفع V.
مقاومة الحسّاس من الجهد المقيس
V = Vref · RFRLDR + RF
RLDR = RF · (VrefV − 1)
الرمزالمعنىوحدة القياس
RLDRمقاومة الحسّاس Ω
RFالمقاومة الثابتة Ω
Vالجهد المقيس V
مثال 6.3
قرأنا V = 2.2 V، وRF = 10 kΩ. ما مقاومة الحسّاس؟
الحل:
RLDR = RF · (VrefV − 1)
RLDR = 10000 · (3.32.2 − 1)
RLDR = 5000 Ω
RLDR = 5 kΩ
نغطي الحسّاس تدريجيا. حين يقترب الجهد من الصفر تنتفخ النتيجة، ولذلك نفحص قبل القسمة.
import board
import time
from analog import to_volts

R_FIXED = 10000

ldr = board.analog_in(board.LDR)

while True:
    v = to_volts(ldr.read_u16())
    if v < 0.05:
        print("V = %.2f   too dark to measure" % v)
    else:
        r = R_FIXED * (3.3 / v - 1)
        print("V = %.2f   R = %6.0f ohm" % (v, r))
    time.sleep(1)
ESP32 + Pico W · MicroPython 1.29 · simulated board
V = 3.00   R =    999 ohm
V = 2.20   R =   5000 ohm
V = 1.10   R =  20000 ohm
V = 0.40   R =  72559 ohm
V = 0.00   too dark to measure
...
خطأ شائع: لو حذفنا الشرط if v < 0.05 وأصبح V = 0، لتوقف البرنامج عند القسمة على صفر. كل قسمة على قراءة من حسّاس تحتاج إلى فحص.

6.7 الضجيج: لماذا ترتجف الأعداد؟ – רעש ומיצוע (Noise)

اتركوا مقياس الجهد ساكنا واقرؤوا: ستجدون الأعداد ترتجف. هذا الضجيج طبيعي، مصدره الدائرة نفسها. وعلاجه الأبسط أن نأخذ عدة قراءات ونحسب متوسّطها، فالارتفاعات تلغي الانخفاضات.

أضفنا إلى المحاكاة ضجيجا عشوائيا مقداره ±20 mV، ونقيس الفرق بين أعلى قراءة وأدناها.
import board
from analog import average

pot = board.analog_in(board.POT)


def single():
    return pot.read_u16()


def smooth():
    return average(pot, 16)


def spread(read):
    low = high = read()
    for _ in range(49):
        x = read()
        low = min(low, x)
        high = max(high, x)
    return high - low


print("single reads, spread  :", spread(single))
print("average of 16, spread :", spread(smooth))
ESP32 + Pico W · MicroPython 1.29 · simulated board
single reads, spread  : 785
average of 16, spread : 266

ثمن المتوسّط هو الزمن: 16 قراءة تستغرق 16 ضعفا. في بطء مثل الحرارة لا يهم ذلك، أما في إشارة سريعة فالمتوسّط يطمس ما نريد قياسه.

نشاط: على لوحة حقيقية: شغّلوا البرنامج ومقياس الجهد ساكن. ثم غيّروا 16 إلى 4 وإلى 64، وسجّلوا الفرق في جدول.

6.8 من قراءة إلى قرار: تحكّم بحالتين – בקרה דו-מצבית (On/Off Control)

نريد مصباحا يضيء عند العتمة. البرنامج البسيط يقارن بعتبة واحدة، لكن قرب العتبة يكفي الضجيج ليشعل المصباح ويطفئه مرارا. الحل هو عتبتان متباعدتان: نشعل تحت الأولى ونطفئ فوق الثانية، وبينهما لا نغيّر شيئا. هذا هو التخلّف (היסטרזיס).

الجهد يتذبذب حول 1 V في الثواني الوسطى، والمصباح لا يرتجف.
import board
import time
from machine import Pin
from analog import to_volts

DARK = 1.0
BRIGHT = 1.4

led = Pin(board.LED, Pin.OUT)
ldr = board.analog_in(board.LDR)

while True:
    v = to_volts(ldr.read_u16())
    if v < DARK:
        led.on()
    elif v > BRIGHT:
        led.off()
    print("V = %.2f   LED = %d" % (v, led.value()))
    time.sleep(1)
ESP32 + Pico W · MicroPython 1.29 · simulated board
V = 1.60   LED = 0
V = 1.20   LED = 0
V = 0.95   LED = 1
V = 1.05   LED = 1
V = 0.95   LED = 1
V = 1.20   LED = 1
V = 1.30   LED = 1
V = 1.50   LED = 0
...

لاحظوا سطر الطرف: Pin(board.LED, Pin.OUT). على ESP32 قيمته 2 وعلى Pico W هو الاسم "LED"، لأن مصباح اللوحة هناك موصول بشريحة اللاسلكي. والبرنامج لا يحتاج إلى معرفة ذلك.

سؤال 6.3: في المُخرَج السابق: في أي ثانية انطفأ المصباح؟ ولماذا لم ينطفئ عند 1.05 V؟

6.9 محوّل خارجي: ADS1115 – ממיר חיצוני (External ADC)

حين لا يكفي المحوّل الداخلي، أو لا يوجد أصلا، نوصل شريحة محوّل خارجية. ADS1115 شريحة شائعة ورخيصة، لها 4 مداخل ودقة 16 بتّا، وتتحدّث بناقل I2C الذي نتعلمه في الفصل 9. وهنا نستعملها جاهزة.

A0ADDRVDDSCLSDAGNDADS11153V3SCLSDAGND
توصيل ADS1115: طرف ADDR إلى GND يعطي العنوان 0x48.

أول ما نفعله مع أي شريحة I2C هو مسح الناقل، حتى نتأكد من التوصيل والعنوان:

إذا طُبعت قائمة فارغة، فالمشكلة في التوصيل وليست في البرنامج.
import board

i2c = board.i2c()
print([hex(a) for a in i2c.scan()])
ESP32 + Pico W · MicroPython 1.29 · simulated board
['0x48']

والآن المكتبة. كتبناها بأنفسنا لأنها قصيرة، ولأن قراءتها تعلّمنا كيف نكلّم شريحة:

الملف ads1115.py
# ads1115.py - driver for the ADS1115 16-bit converter.
# ESP32 and Pico: pass a machine.I2C. Raspberry Pi 4/5: pass a pi_i2c.PiI2C.
# (c) 2026 Fahed Ghanem - MIT License - fahed-lab.com
import time

REG_CONV = 0
REG_CONFIG = 1
START = 0x8000
SINGLE_ENDED = 4
RANGE_4V = 0x0200
ONE_SHOT = 0x0100
RATE_128 = 0x0080
NO_COMPARATOR = 0x0003
LSB = 4.096 / 32768


class ADS1115:
    def __init__(self, i2c, addr=0x48):
        self.i2c = i2c
        self.addr = addr

    def _write(self, reg, value):
        self.i2c.writeto(self.addr, bytes([reg, value >> 8, value & 0xFF]))

    def _read(self, reg):
        self.i2c.writeto(self.addr, bytes([reg]))
        data = self.i2c.readfrom(self.addr, 2)
        return (data[0] << 8) | data[1]

    def read_raw(self, channel):
        mux = (SINGLE_ENDED + channel) << 12
        config = START | mux | RANGE_4V | ONE_SHOT | RATE_128 | NO_COMPARATOR
        self._write(REG_CONFIG, config)
        while not self._read(REG_CONFIG) & START:
            time.sleep(0.001)
        value = self._read(REG_CONV)
        return value - 65536 if value & 0x8000 else value

    def read_volts(self, channel):
        return self.read_raw(channel) * LSB
الأمرعمله
config = START | mux | ...نبني سجل الإعداد من حقوله: ابدأ تحويلا، أي مدخل، أي مجال، أي سرعة.
self._write(REG_CONFIG, config)نرسل السجل فيبدأ التحويل.
while not ... & STARTننتظر حتى تُعلن الشريحة انتهاء التحويل.
value - 65536 if value & 0x8000العدد بإشارة: البتّ الأعلى يعني عددا سالبا.
مثال 6.4
ما التمييز عند المجال ±4.096 V؟ وما زمن التحويل عند 128 عيّنة في الثانية؟
الحل:
q = 4.096215 = 4.09632768
q = 125 µV
t = 1128
t = 7.81 ms

الأس هنا 15 وليس 16، لأن بتّا واحدا مخصص للإشارة.

q = 125 µV, t = 7.81 ms

أصغر بنحو 6 مرات من خطوة المحوّل الداخلي، لكن كل قراءة تستغرق بضع ميلي ثوانٍ. لا يوجد محوّل أفضل في كل شيء.

على ESP32 وعلى Pico W.
import board
import time
from ads1115 import ADS1115

adc = ADS1115(board.i2c())

while True:
    print("A0 = %.4f V" % adc.read_volts(0))
    time.sleep(1)
ESP32 + Pico W · MicroPython 1.29 · simulated board
A0 = 0.5000 V
A0 = 1.2500 V
A0 = 2.5000 V
...

6.10 على Raspberry Pi 4/5

لا يوجد في Raspberry Pi 4/5 محوّل تماثلي رقمي، ولا يعمل عليه machine: هو حاسوب عليه لينكس، ونكتب له بايثون العادي. لكننا نستطيع استعمال المكتبة نفسها ads1115.py. كل ما تطلبه من الناقل أمران: writeto وreadfrom، فنكتب ملفا صغيرا يعطي الناقل هذين الأمرين بواسطة المكتبة smbus2.

الملف pi_i2c.py، على الباي فقط
# pi_i2c.py - gives the Pi 4/5 I2C bus the two methods of machine.I2C
# that the book's drivers use, so the same driver runs on all boards.
# Needs: pip install smbus2, and I2C enabled in raspi-config.
# (c) 2026 Fahed Ghanem - MIT License - fahed-lab.com
from smbus2 import SMBus, i2c_msg


class PiI2C:
    def __init__(self, bus=1):
        self.bus = SMBus(bus)

    def writeto(self, addr, data):
        self.bus.i2c_rdwr(i2c_msg.write(addr, data))

    def readfrom(self, addr, n):
        msg = i2c_msg.read(addr, n)
        self.bus.i2c_rdwr(msg)
        return bytes(msg)

التحضير لمرة واحدة، في نافذة الأوامر: نفعّل I2C من sudo raspi-config (قسم Interface Options)، ثم نثبّت المكتبة في بيئة خاصة:

python3 -m venv ~/lab
~/lab/bin/pip install smbus2
~/lab/bin/python ads_read_pi.py
سطران فقط يختلفان عن برنامج المتحكّم. والأعداد مطابقة.
import time
from ads1115 import ADS1115
from pi_i2c import PiI2C

adc = ADS1115(PiI2C())

while True:
    print("A0 = %.4f V" % adc.read_volts(0))
    time.sleep(1)
Raspberry Pi 4/5 · Python 3 · simulated bus
A0 = 0.5000 V
A0 = 1.2500 V
A0 = 2.5000 V
...
ملاحظة: التوصيل: SDA إلى GPIO2، SCL إلى GPIO3، وVDD إلى 3V3 وليس إلى 5V. ولمن يفضّل ناقل SPI، في المكتبة gpiozero صنف جاهز للمحوّل MCP3008.

أهمّ أفكار الفصل:

ملخّص قوانين الفصل:

القانونالصيغة
التمييزq = Vref2n
من القراءة إلى الجهدV = N · Vref65535
مقاومة حسّاس في مجزّئRLDR = RF · (VrefV − 1)

أوامر الفصل:

الأمرعمله
ADC(Pin(n), atten=ADC.ATTN_11DB)محوّل على طرف في ESP32.
ADC(Pin(n))محوّل على طرف في Pico W.
adc.read_u16()قراءة بين 0 و65535، في كل اللوحات.
adc.read_uv()جهد معايَر بالميكروفولط، في ESP32 فقط.
i2c.scan()قائمة عناوين الشرائح الموصولة بالناقل.

قاموس المصطلحات – מילון מונחים:

بالعربيةبالعبريةEnglish
المحوّل التماثلي الرقميממיר אנלוגי לספרתיAnalog-to-Digital Converter
التمييزרזולוציהResolution
جهد المرجعמתח ייחוסReference Voltage
زمن التحويلזמן המרהConversion Time
خطأ التكميمשגיאת קוונטיזציהQuantization Error
مقياس الجهدפוטנציומטרPotentiometer
مقاومة ضوئيةנגד תלוי אורLight Dependent Resistor
مجزّئ الجهدמחלק מתחVoltage Divider
الضجيجרעשNoise
التخلّفהיסטרזיסHysteresis
الناقلאפיקBus
الإجابات النهائية لأسئلة الفصل
6.1 q = 4.88 mV، أي أقل دقة من 0.81 mV.
6.2 نحو 16400، أي ربع المجال.
6.3 انطفأ في السطر الأخير (V = 1.50) فقط، لأن 1.05 V تقع بين العتبتين، وهناك لا يتغيّر شيء.

أسئلة للتمرين:

المستوى الأول — أساسي
1.
محوّل من 8 بتّات وجهد مرجعه 3.3 V. ما هو التمييز؟
الإجابات: q = 12.89 mV.
2.
أعطى read_u16() العدد 20000. ما الجهد على الطرف؟
الإجابات: V = 1.007 V.
3.
أي الأطراف تصلح للقراءة التماثلية على Pico W: 26، 34، 27، 5؟ ولماذا؟
الإجابات: 26 و27 فقط: الطرفان 34 و5 ليسا من أطراف المحوّل، والطرف 34 غير موجود أصلا.
المستوى الثاني — تطبيق
4.
اكتبوا برنامجا يرسم مستوى مقياس الجهد بعلامات #: من صفر علامات حتى 10.
الإجابات:
import board
import time
from analog import scale

pot = board.analog_in(board.POT)

while True:
    level = scale(pot.read_u16(), 0, 65535, 0, 10)
    print("#" * int(level))
    time.sleep(1)
5.
قرأنا في مجزّئ المقاومة الضوئية V = 0.8 V، وRF = 10 kΩ. ما مقاومة الحسّاس؟
الإجابات: RLDR = 31.25 kΩ.
6.
الحسّاس LM35 يعطي 10 mV لكل درجة مئوية. اكتبوا برنامجا يطبع الحرارة، وأوجدوا ما يطبعه عند 25 درجة.
الإجابات:
import board
import time
from analog import to_volts

sensor = board.analog_in(board.POT)

while True:
    v = to_volts(sensor.read_u16())
    print("T = %.1f C" % (v * 100))
    time.sleep(1)
يطبع: T = 25.0 C
المستوى الثالث — تعمّق
7.
البرنامج السابق يقرأ 0.25 V على مجال 3.3 V.
(أ) كم درجة تساوي خطوة واحدة؟
(ب) لماذا نكسب من مجال أصغر على ESP32؟
الإجابات: (أ) 0.81 mV تساوي 0.08 درجة. (ب) مع ATTN_0DB يصغر المجال نحو 1 V، فتصغر الخطوة بالنسبة نفسها، ويبقى جهد الحسّاس داخل المجال الخطّي.
8.
يستعمل برنامجكم WiFi والطرف 25 للقراءة. ماذا يحدث؟ وكيف نصلحه؟
الإجابات: تنتهي القراءة بخطأ، لأن الطرف 25 في الكتلة الثانية التي يستعملها WiFi. ننقل الحسّاس إلى طرف بين 32 و39.
9.
(أ) لماذا تعمل المكتبة ads1115.py على الباي دون تغيير؟
(ب) أي أمر فيها كان سيفشل لو استعملنا time.sleep_ms؟
الإجابات: (أ) لأنها لا تطلب من الناقل سوى writeto وreadfrom، ولا تستورد machine. (ب) sleep_ms موجود في مايكروبايثون فقط، وليس في بايثون العادي.
لوحة القوانين: كل قوانين هذا الفصل مجموعة مع رسومها وجداول رموزها في الفصل 25 — لوحة القوانين الشاملة. (נוסחאון הספר)