OpenMV AE3

بُنيت OpenMV AE3 حول Alif Ensemble E3 — وهو نظام على شريحة (SoC) ثنائي النواة من نوع ARM Cortex‑M55 (نواة عالية الأداء بتردد 400 MHz + نواة عالية الكفاءة بتردد 160 MHz) مع وحدتي معالجة عصبية (NPU) على الشريحة (وحدة NPU عالية الأداء بتردد 400 MHz / 204 GOPS + وحدة NPU عالية الكفاءة بتردد 160 MHz / 46 GOPS). تجمع اللوحة بين وحدات NPU والمستشعر PAG7936 ذي الـ 1 ميجابكسل والغالق الشامل، ومنفذ USB‑C عالي السرعة، وWi‑Fi، وBluetooth 5.1، ووحدة قياس بالقصور الذاتي LSM6DSM، وميكروفون، ومقياس مدى بزمن الطيران VL53L8CX بدقة 8×8، وكل ذلك على لوحة قياسها 30 × 30 مم.

OpenMV AE3

للاطلاع على ورقة البيانات الكاملة والصور والأبعاد، راجع صفحة منتج OpenMV AE3.

أبرز الميزات

  • Alif Ensemble E3 — معالج ثنائي ARM Cortex‑M55 مع تقنية Helium وحدة SIMD بعرض 128 بت، نواة عالية الأداء بتردد 400 MHz + نواة عالية الكفاءة بتردد 160 MHz (نحو 640 / 256 DMIPS، وCoreMark 1748 / 752).

  • وحدتا NPU: وحدة NPU عالية الأداء بتردد 400 MHz / 204 GOPS + وحدة NPU عالية الكفاءة بتردد 160 MHz / 46 GOPS للذكاء الاصطناعي / تعلم الآلة — تشغّل كشف الأجسام YOLO جنبًا إلى جنب مع أعباء عمل أخرى.

  • وحدة معالجة رسومات (GPU) ثنائية الأبعاد عتادية لتغيير الحجم.

  • ذاكرة SRAM داخلية بسعة 13.5 ميجابايت بالإضافة إلى ذاكرة MRAM على الشريحة بسعة 5.5 ميجابايت وذاكرة فلاش ثُمانية خارجية بسعة 32 ميجابايت (تردد 100 MHz بعرض 8 بت من نوع DDR، قراءة بسرعة 200 ميجابايت/ث).

  • ذاكرة احتياطية RAM بسعة 4 كيلوبايت مع ساعة الوقت الحقيقي (RTC) على الشريحة.

  • مستشعر ملوّن بغالق شامل بدقة 1 ميجابكسل من نوع PAG7936.

  • وحدة قياس بالقصور الذاتي مدمجة (مقياس تسارع + جيروسكوب من نوع LSM6DSM)، وميكروفون، ومستشعر زمن الطيران VL53L8CX بدقة 8×8 (حتى 4 أمتار).

  • منفذ USB‑C عالي السرعة (480 ميجابت/ث) مع ترشيح للتداخل الكهرومغناطيسي وحماية TVS، وWi‑Fi من نوع a/b/g/n + Bluetooth 5.1 (هوائي على الشريحة أو خيار U.FL).

  • 10 دبابيس إدخال/إخراج للمستخدم — P0–P3 على الموصلات الجانبية، وP4–P5 على موصل Qwiic، وP6–P9 على موصل B2B في الخلف. كما توجّه خطوط إضافية للتنقيح والاسترداد إلى موصل B2B.

  • جميع الدبابيس إخراج 3.3 فولت / متحملة 3.3 فولت، 25 ملّي أمبير لكل دبوس، وقادرة على المقاطعة. مدخلات ADC مرجعها 1.8 فولت.

  • LED من نوع RGB للمستخدم، وزر للمستخدم، ومفتاح استرداد، وموصل Qwiic.

  • سُبات عميق بسحب 80 ميكرو أمبير عند 3.3 فولت (24 ملّي أمبير في الخمول، 50–60 ملّي أمبير عند النشاط).

تحذير

دبابيس الإدخال/الإخراج في AE3 غير متحملة لجهد 5 فولت. لا توصّل الجهاز مباشرة بوحدة تحكم دقيقة تعمل بجهد 5 فولت مثل Arduino Mega — استخدم محوّل مستوى لأي إشارة بجهد 5 فولت.

مخطط الدبابيس

OpenMV AE3 PAG7936 Pinout

مرجع الدبابيس

تكشف AE3 عن 10 دبابيس للمستخدم على الموصلات الجانبية (P0–P9). تُوجَّه إشارات إضافية — بما في ذلك JTAG وخط الاسترداد — إلى موصل B2B (لوحة إلى لوحة) في الخلف من اللوحة من أجل الدروع واللوحات الحاملة.

اسم الدبوس

المرجع

الوظيفة

P0

3.3 فولت

SPI0 MOSI / I2C2 SCL / UART4 TX / TIM0 T1 / PDM D3

P1

3.3 فولت

SPI0 MISO / I2C2 SDA / UART4 RX / TIM0 T0

P2

3.3 فولت

SPI0 SCLK / LPI2C SDA / UART5 TX / TIM1 T1

P3

3.3 فولت

SPI0 SS / LPI2C SCL / UART5 RX / TIM1 T0 / PDM C3

P4

3.3 فولت

I2C1 SCL / UART1 TX / TIM2 T1 / PDM C0 / CAN TX

P5

3.3 فولت

I2C1 SDA / UART1 RX / TIM2 T0 / PDM D0 / CAN RX

P6

1.8 فولت

I2C1 SDA / UART3 CTS / TIM9 T0 (B2B فقط)

P7

1.8 فولت

I2C1 SCL / UART3 RTS / TIM9 T1 (B2B فقط)

P8

1.8 فولت

I3C SDA / UART3 RX / TIM5 T0 / ADC ch S10 (B2B فقط)

P9

1.8 فولت

I3C SCL / UART3 TX / TIM5 T1 / ADC ch S11 (B2B فقط)

P10

1.8 فولت

GPIO / JTAG TCK (B2B فقط)

P11

1.8 فولت

GPIO / JTAG TDO (B2B فقط)

P13

1.8 فولت

GPIO / JTAG TMS (B2B فقط)

P14

1.8 فولت

GPIO / JTAG TDI (B2B فقط)

RESET

3.3 فولت

اسحبه إلى GND لإعادة ضبط اللوحة

SW

3.3 فولت

زر المستخدم (نشط عند المستوى المنخفض)

LED_RED

3.3 فولت

قناة الأحمر في LED من نوع RGB (نشطة عند المستوى المنخفض)

LED_GREEN

3.3 فولت

قناة الأخضر في LED من نوع RGB (نشطة عند المستوى المنخفض)

LED_BLUE

3.3 فولت

قناة الأزرق في LED من نوع RGB (نشطة عند المستوى المنخفض)

ملاحظة

الدبابيس P0–P5 موجودة على الموصلات الجانبية (مرجعها 3.3 فولت)؛ أما P6–P9 فتُكشف فقط على موصل B2B في خلف اللوحة ومرجعها 1.8 فولت. سيؤدي دفع 3.3 فولت إلى دبوس مرجعه 1.8 فولت إلى تلف نظام SoC — تأكد من أن أي إشارة متصلة بموصل B2B تكون عند 1.8 فولت.

دبابيس الطاقة

  • 3.3V — مسار الطاقة الرئيسي في AE3. يُكشف مسار 3.3 فولت نفسه على وسادات لحام موصل GPIO، وموصل Qwiic، وموصل B2B في خلف اللوحة.

  • 1.8V — يُكشف على موصل B2B بوصفه إخراجًا فقط. استخدمه لتزويد الطرفيات ذات منطق 1.8 فولت بالطاقة على لوحة حاملة B2B؛ لا تدفعه من خارج اللوحة.

  • GND — الأرضي المشترك.

لا تملك AE3 دبوس VIN ولا شاحن LiPo. ويمكن تزويدها بالطاقة عبر أي من ثلاثة مسارات:

  • USB‑C — يخفض المنظّم الموجود على اللوحة الجهد من 5 فولت في USB إلى 3.3 فولت ويحقنه في مسار 3.3 فولت.

  • موصل Qwiic — ادفع مصدر تغذية منظّمًا بجهد 3.3 فولت إلى موصل Qwiic لتزويد اللوحة بالطاقة من وحدة Qwiic.

  • موصل GPIO / وسادات 3.3 فولت في B2B — ادفع مصدر تغذية منظّمًا بجهد 3.3 فولت إلى أي من وسادات 3.3 فولت على موصل الإدخال/الإخراج أو موصل B2B.

يغذّي منظّم USB المسار عبر صمام ثنائي مثالي، لذا تستطيع مصادر التغذية الخارجية بجهد 3.3 فولت على جانب Qwiic / GPIO / B2B تزويد اللوحة بالطاقة حتى وإن كان USB ما يزال موصولًا، دون دفع عكسي لمنظّم USB.

نصيحة

استخدم أداة تقدير عمر البطارية لنمذجة المدة التي ستعمل خلالها AE3 على بطارية وفق دورة عمل معطاة بين النشاط والسُبات العميق.

دبابيس الاسترداد والتنقيح

  • RESET — اسحبه إلى GND لإعادة ضبط اللوحة. وعند تحريره يبدأ نظام SoC في التشغيل بشكل طبيعي.

يوجد مفتاح استرداد على الوجه الأمامي (جانب الكاميرا) من اللوحة، في الزاوية السفلية اليسرى. وعند تمكينه يُجبر منفذ SE UART في AE3 على الخروج عبر USB كي يتمكن OpenMV IDE من إعادة وميض محمّل الإقلاع الموجود على اللوحة. ويمكن تشغيل وضع الاسترداد نفسه عن بُعد عبر سحب دبوس RECOVERY على موصل B2B إلى المستوى المنخفض.

تدعم AE3 كلا نوعي التنقيح SWD وJTAG الكامل:

  • موصل SWD بجهد 1.8 فولت على جانب اللوحة مخصص لكابل Tag-Connect ECV3-06-CTX ويُخرج إشارات SWD الأربع (TCK / TMS / TDO / RSTN) بالإضافة إلى GND.

  • يكشف موصل B2B في خلف اللوحة عن دبابيس التنقيح نفسها (P10 = TCK، وP11 = TDO، وP13 = TMS، وP14 = TDI) بالإضافة إلى RSTN النظام وJTAG RSTN منفصل. ويمكن استخدام هذه الدبابيس إما لـ SWD (TCK + TMS) أو لـ JTAG الكامل؛ ولا يُحتاج إلى خط JTAG RSTN إلا في وضع JTAG الكامل.

جميع إشارات التنقيح مرجعها 1.8 فولت — تأكد من ضبط محوّل التنقيح لديك على منطق 1.8 فولت قبل التوصيل.

الطرفيات الموجودة على اللوحة

مصابيح LED

تملك AE3 مصباح LED من نوع RGB واحدًا للمستخدم، يمكن التحكم فيه برمجيًا عبر machine.LED

from machine import LED

LED("LED_RED").on()
LED("LED_GREEN").on()
LED("LED_BLUE").on()

زر المستخدم

تملك AE3 زر مستخدم واحدًا (SW):

from machine import Pin

sw = Pin("SW", Pin.IN)
print(sw.value())

لوضع اللوحة في السُبات العميق وجعل SW يوقظها، ما عليك إلا استدعاء machine.deepsleep() — لا يلزم أي إعداد للإيقاظ، فالزر موصول مباشرة بمدخل إيقاظ:

import machine

machine.deepsleep()   # press SW to wake the board

يمكنك أيضًا توصيل SW بوصفه مفتاح طاقة برمجي. حفّزه عند الحافة الصاعدة — إذ يستقر الخط على المستوى المرتفع بعد أن يطلق المستخدم الزر، فيكون الضغط التالي بلا لبس حدث إيقاظ:

import machine
from machine import Pin

def power_off(_):
    machine.deepsleep()

Pin("SW", Pin.IN).irq(power_off, Pin.IRQ_RISING)

# ...rest of the application runs here. Press SW once to sleep,
# press it again to wake.

مستشعر الكاميرا

يُشغَّل المستشعر PAG7936 عبر وحدة csi --- مستشعرات الكاميرا

import csi

cam = csi.CSI()
cam.reset()
cam.pixformat(csi.RGB565)
cam.framesize(csi.HD)         # 1280×800
cam.snapshot(time=2000)       # let auto‑exposure settle

while True:
    img = cam.snapshot()

يدعم PAG7936 الوضع المُحفَّز — حيث تتزامن خطوط تكامل البكسلات بدقة مع كل استدعاء لـ csi.CSI.snapshot بدلًا من ساعة الإطارات ذاتية التشغيل، وهو مفيد لمزامنة الالتقاط مع حدث خارجي أو مع مستشعر آخر. مكّنه عبر csi.CSI.ioctl مع csi.IOCTL_SET_TRIGGERED_MODE. وينخفض معدل الإطارات إلى نحو نصف معدل الوضع ذاتي التشغيل لأن القراءة لم تعد تتداخل مع تكامل الإطار التالي:

cam.ioctl(csi.IOCTL_SET_TRIGGERED_MODE, True)

NPU

تُكشف وحدتا NPU على الشريحة في AE3 (وحدة NPU عالية الأداء بتردد 400 MHz / 204 GOPS + وحدة NPU عالية الكفاءة بتردد 160 MHz / 46 GOPS) عبر وحدة ml --- التعلم الآلي. وتُحمَّل النماذج المخزنة على نظام الملفات للقراءة فقط /rom مباشرة من ذاكرة الفلاش دون نسخها إلى RAM، لذا حتى الكواشف الكبيرة تتسع بأريحية إلى جانب مخزن الإطارات الحي. شغّل كاشف YOLOv8 على كل إطار وارسم التنبؤات فوق الصورة الحية:

import csi
import time
import ml
from ml.postprocessing.ultralytics import YoloV8

# Initialize the sensor.
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.VGA)

# Load YOLO V8 model from ROM FS.
model = ml.Model("/rom/yolov8n_192.tflite", postprocess=YoloV8(threshold=0.4))
print(model)

# Visualization parameters.
n = len(model.labels)
model_class_colors = [
    (int(255 * i // n), int(255 * (n - i - 1) // n), 255)
    for i in range(n)
]

clock = time.clock()
while True:
    clock.tick()
    img = csi0.snapshot()

    # boxes is a list of list per class of ((x, y, w, h), score) tuples
    boxes = model.predict([img])

    # Draw bounding boxes around the detected objects
    for i, class_detections in enumerate(boxes):
        rects = [r for r, score in class_detections]
        labels = [model.labels[i] for j in range(len(rects))]
        colors = [model_class_colors[i] for j in range(len(rects))]
        ml.utils.draw_predictions(img, rects, labels, colors, format=None)

    print(clock.fps(), "fps")

النواة عالية الكفاءة (HE)

تضم AE3 نواتي Cortex‑M55 في وحدة تحكم دقيقة واحدة: النواة عالية الأداء (HP) التي تشغّل نسخة MicroPython الرئيسية والكاميرا ووحدة NPU عالية الأداء وUSB وما إلى ذلك؛ والنواة عالية الكفاءة (HE) التي تعمل بطاقة أقل بكثير وتُقلع إلى نسخة MicroPython صغيرة خاصة بها. وتتشارك النواتان ناقل رسائل Open-AMP / RPMsg، فيمكن للنواة HP إرسال دوال Python إلى النواة HE واستلام النتائج وإبقاء النصفين منفصلين.

أبسط نقطة دخول هي المُزخرِف @openamp.async_remote. فهو يُسلسِل دالة Python ويرسلها إلى النواة HE، التي تشغّلها بوصفها مهمة asyncio. وبعد تسجيل المهام، أنشئ نسخة من openamp.RemoteProc بعنوان فلاش البرنامج الثابت للنواة HE واستدعِ rproc.start() لإقلاع النواة الثانية. وفي حال عدم وجود دالة رد نداء، يُمرَّر خرج print() للدالة المُزخرَفة عبر نقطة النهاية الافتراضية إلى مخرج stdout للنواة HP — وهو ملائم لمثال "hello world":

import time
import openamp

@openamp.async_remote
async def task1(ept):
    import asyncio
    while True:
        print("Hello from the HE core!")
        await asyncio.sleep(1)

# Boot the HE core. This runs the registered tasks.
rproc = openamp.RemoteProc(0x80320000)
rproc.start()

while True:
    print("Hello from the HP core!")
    time.sleep(1)

للمراسلة ثنائية الاتجاه، مرّر دالة رد نداء إلى المُزخرِف. وتُنفَّذ دالة رد النداء على النواة HP كلما استدعت مهمة HE الأمر ept.send()

import time
import openamp

def task_callback(src_addr, data):
    print("HP received:", data.decode())

@openamp.async_remote(task_callback)
async def task1(ept):
    import asyncio
    count = 0
    while True:
        ept.send(f"count = {count}")
        count += 1
        await asyncio.sleep(1)

rproc = openamp.RemoteProc(0x80320000)
rproc.start()

while True:
    time.sleep(1)

تملك النواة HE وحدة NPU عالية الكفاءة خاصة بها (160 MHz، 46 GOPS)، فتستطيع تشغيل نموذج تعلم آلة ثانٍ بالتوازي مع ما تنشغل به وحدة NPU عالية الأداء في النواة HP. ومن التقسيمات المفيدة وضع نموذج محفّز / مُصنِّف صغير دائم العمل على جانب HE وجعل النواة HP لا تتفاعل إلا عند وضع علامة على شيء مثير للاهتمام — والتعرف على الكلمات المفتاحية من الميكروفون الموجود على اللوحة مثال جيد لأنه مستمر ومنخفض النطاق الترددي وتبقى النواة HE بطاقة أقل بكثير من HP. ويتعرف المساعد المجمَّد ml.apps.MicroSpeech على "Yes" و"No" جاهزًا للاستعمال — انطق الكلمتين بصوت عالٍ وواضح في الميكروفون الموجود على اللوحة لتحفيز الكشف:

import time
import openamp

def task_callback(src_addr, data):
    print("Heard:", data.decode())

@openamp.async_remote(task_callback)
async def task1(ept):
    from ml.apps import MicroSpeech
    speech = MicroSpeech(gain_db=24)
    while True:
        label, scores = speech.listen(timeout=0, threshold=0.70)
        if label:
            ept.send(label)

rproc = openamp.RemoteProc(0x80320000)
rproc.start()

while True:
    time.sleep(1)

لتقسيم أغنى، شغّل BlazeFace على وحدة NPU عالية الأداء بينما تتولى النواة HE التعرف على الكلمات المفتاحية في الخلفية — تعرض حلقة HP أحدث كلمة مفتاحية مسموعة فوق إطار الكاميرا:

import csi
import time
import openamp
import ml
from ml.postprocessing.mediapipe import BlazeFace

label = None
label_ticks = 0
LABEL_HOLD_MS = 2000

def task_callback(src_addr, data):
    global label, label_ticks
    label = data.decode()
    label_ticks = time.ticks_ms()

@openamp.async_remote(task_callback)
async def task1(ept):
    from ml.apps import MicroSpeech
    speech = MicroSpeech(gain_db=24)
    while True:
        l, scores = speech.listen(timeout=0, threshold=0.70)
        if l:
            ept.send(l)

# Start the HE core before initializing the camera on the HP core.
rproc = openamp.RemoteProc(0x80320000)
rproc.start()

csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.VGA)
csi0.window((400, 400))

model = ml.Model("/rom/blazeface_front_128.tflite",
                 postprocess=BlazeFace(threshold=0.4))

clock = time.clock()
while True:
    clock.tick()
    img = csi0.snapshot()
    for r, score, keypoints in model.predict([img]):
        ml.utils.draw_predictions(img, [r], ("face",),
                                  ((0, 0, 255),), format=None)
        ml.utils.draw_keypoints(img, keypoints, color=(255, 0, 0))
    if label is not None:
        if time.ticks_diff(time.ticks_ms(), label_ticks) < LABEL_HOLD_MS:
            img.draw_string((4, 4), f"Heard: {label}",
                            color=(255, 0, 0), scale=2)
        else:
            label = None
    print(clock.fps(), "fps")

تلائم النواة HE أعباء العمل الدائمة العمل أو منخفضة المعدل التي لا تريد لها أن تنافس مسار الكاميرا / NPU على جانب HP — كالاستدلال بنماذج تعلم آلة صغيرة، ومعالجة الإشارات الرقمية الخفيفة على بيانات الميكروفون أو وحدة القصور الذاتي، وأعمال الخلفية المماثلة.

بعض القيود التي ينبغي وضعها في الحسبان:

  • اقتصر على الميكروفون ووحدة القصور الذاتي عند تشغيل الطرفيات من النواة HE — فهي ما صُمم جانب HE من أجله. ولا يمكن أن تملك كل طرفية إلا نواة واحدة في وقت معين، لذا اختر لها HP أو HE والتزم بذلك طوال عمر البرنامج النصي.

  • يجب أن يُسلسَل متن كل مهمة @openamp.async_remote إلى أقل من 500 بايت من بايتكود mpy — أبقِ الدالة صغيرة وفكّك المنطق الأثقل إلى وحدات مكتبية منفصلة تُجمَّد في البرنامج الثابت.

  • لا ترى عمليات الاستيراد داخل الدالة المُرسَلة إلا الوحدات الموجودة على نظام ملفات النواة HE. وتملك النواة HE نظام ROMFS /rom خاصًا بها — منفصلًا عن /rom الخاص بالنواة HP — لذا يجب خبز الوحدات ونماذج تعلم الآلة التي تريد إتاحتها على HE داخل صورة ROMFS الخاصة بجانب HE، لا تلك الخاصة بـ HP.

الميكروفون

يُلتقط الميكروفون الموجود على اللوحة عبر audio --- وحدة الصوت. ويصل كل مخزن مؤقت على هيئة bytearray بصيغة PCM ذي 16 بت بإشارة، مما يجعل من البسيط تمريره إلى ulab/numpy لمعالجة إشارات رقمية سريعة. وفيما يلي كاشف جهارة بسيط — يطبع كلما تجاوز حجم RMS عتبة معينة:

import audio
from ulab import numpy as np

def loudness(pcmbuf):
    samples = np.array(np.frombuffer(pcmbuf, dtype=np.int16), dtype=np.float)
    rms = np.sqrt(np.mean(samples ** 2))
    if rms > 10000:
        print("Loud!", int(rms))

audio.init(channels=1, frequency=16000, gain_db=24)
audio.start_streaming(loudness)

while True:
    pass

وحدة القصور الذاتي (IMU)

يُكشف مقياس التسارع + الجيروسكوب LSM6DSM الموجود على اللوحة عبر imu --- مستشعر imu

import imu
import time

while True:
    print(imu.acceleration_mg())   # (x, y, z) in milli‑g
    print(imu.angular_rate_mdps()) # (x, y, z) in milli‑deg/s
    time.sleep_ms(100)

مستشعر زمن الطيران

تحمل AE3 مستشعر زمن الطيران متعدد المناطق VL53L8CX بدقة 8×8 الذي يعيد حتى 64 قراءة مسافة لكل إطار، بمدى أقصى نحو 4 أمتار. ويُكشف عبر وحدة tof --- مشغل مستشعر زمن الرحلة — استدعِ tof.init() لبدء المستشعر وtof.read_depth() لالتقاط إطار عمق على هيئة قائمة مسطحة من قراءات بالمليمتر (قراءة واحدة لكل منطقة):

import tof

tof.init()
while True:
    depth, depth_min, depth_max = tof.read_depth()
    print("min:", depth_min, "mm  max:", depth_max, "mm")

يمكن أيضًا رسم مصفوفة العمق فوق إطار ملوّن من المستشعر الرئيسي — تطلي tof.draw_depth() المصفوفة على image.Image موجودة، بينما تعيد tof.snapshot() صورة عمق مُصيَّرة حديثًا:

import image
import tof
import csi

# Bring up the VL53L8CX time-of-flight sensor.
tof.init()

# Configure the main camera at VGA RGB565.
cam = csi.CSI()
cam.reset()
cam.pixformat(csi.RGB565)
cam.framesize(csi.VGA)

# Off-screen framebuffer used to compose the camera frame and the
# up-scaled depth heat-map side by side before pushing the result
# back to the live preview.
b = image.Image(640, 480, image.RGB565)

while True:
    # Grab a colour frame from the main camera.
    img = cam.snapshot()

    try:
        # Capture TOF data [depth map, min distance, max distance].
        # vflip / hmirror align the ToF orientation with the camera.
        depth, dmin, dmax = tof.read_depth(vflip=True, hmirror=True)

        # Zones with no return read back as 0.0 — clamp them to the
        # frame's max distance so the colour palette doesn't show
        # them as "closest".
        for i in range(0, len(depth)):
            if depth[i] == 0.0:
                depth[i] = dmax

    except RuntimeError:
        # The sensor occasionally faults on a frame; reset and skip.
        tof.reset()
        continue

    # Draw the camera frame into the left half of the framebuffer,
    # scaled to 60% so it leaves room for the depth heat-map on
    # the right.
    b.draw_image(img, x=0, y=64+8, x_scale=0.6, hint=image.BILINEAR)

    # Up-sample the 8x8 depth array 30x with bicubic smoothing and
    # blend it into the right half using the depth palette.
    # scale=(0, 400) maps 0-400 mm to the full palette range.
    tof.draw_depth(b, depth, x=320+64+16, y=64+8, alpha=255,
                   hint=image.BICUBIC, x_scale=30, y_scale=30,
                   scale=(0, 400), color_palette=image.PALETTE_DEPTH)

    # Copy the composed framebuffer back into the live preview so
    # OpenMV IDE shows both panels.
    img.set(b)

Wi‑Fi

تُكشف وحدة CYW43439 الموجودة على اللوحة عبر network --- تهيئة الشبكة بوصفها واجهة محطة. وبعد الاتصال، يعيد ipconfig("addr4") زوج (ip, netmask)

import network, time

wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect("ssid", "password")
while not wlan.isconnected():
    time.sleep(1)
print("Wi‑Fi IP:", wlan.ipconfig("addr4")[0])

Bluetooth

تكشف وحدة CYW43439 نفسها أيضًا عن Bluetooth 5.1. استخدم aioble --- BLE غير المتزامن للحصول على BLE ملائم لـ asyncio — على سبيل المثال، أعلن عن نفسك بوصفك طرفية وانتظر اتصال جهاز مركزي:

import asyncio
import aioble

async def run():
    while True:
        conn = await aioble.advertise(250_000, name="OpenMV-AE3")
        print("Connected:", conn.device)
        await conn.disconnected()

asyncio.run(run())

مرجع الناقل

GPIO

استخدم machine.Pin لقراءة أو تشغيل أي من الدبابيس المطبوعة على اللوحة. والمخارج من نوع 3.3 فولت CMOS ويمكنها سحب/إمداد حتى 25 ملّي أمبير لكل دبوس.

from machine import Pin

out = Pin("P0", Pin.OUT)
out.on()
out.off()
out.value(1)

inp = Pin("P1", Pin.IN, Pin.PULL_UP)
print(inp.value())

يمكن لأي دبوس إدخال أيضًا إطلاق مقاطعة عند انتقالات الحافة:

def handler(pin):
    print("triggered:", pin)

Pin("P1", Pin.IN, Pin.PULL_UP).irq(
    handler, Pin.IRQ_FALLING | Pin.IRQ_RISING,
)

UART

الناقل

TX

RX

RTS

CTS

UART1

P4

P5

UART3

P9

P8

P7

P6

UART4

P0

P1

UART5

P2

P3

from machine import UART

uart = UART(1, baudrate=115200)
uart.write("hello")
uart.read(5)

UART3 هو الناقل الوحيد المزود بتحكم تدفق عتادي. ولأن P6–P9 تقع على موصل B2B ومرجعها 1.8 فولت، فإن UART3 لا يعمل إلا عبر محوّل مستوى أو لوحة حاملة B2B — لا توصّل منطق 3.3 فولت به مباشرة.

I²C

الناقل

SCL

SDA

I2C1

P4

P5

I2C2

P0

P1

LPI2C

P3

P2

from machine import I2C

i2c = I2C(1, freq=400_000)
i2c.scan()
i2c.writeto(0x76, b"hi")

يُخرج موصل Qwiic الموجود على اللوحة الناقل I2C2 عند 3.3 فولت.

يمكن أيضًا استخدام I2C1 وI2C2 في وضع الهدف (التابع) عبر machine.I2CTarget لكشف منطقة ذاكرة لوحدة تحكم I²C أخرى:

from machine import I2CTarget

buf = bytearray(32)
target = I2CTarget(1, addr=0x42, mem=buf)

ملاحظة

الطرفية LPI2C غير مكشوفة في البرنامج الثابت. وهي لن تدعم سوى وضع الهدف (التابع) إن كُشفت، كما أن I2C1 وI2C2 يغطيان بالفعل تشغيل وحدة التحكم والهدف معًا.

SPI

الناقل

MOSI

MISO

SCK

CS

SPI0

P0

P1

P2

P3

from machine import SPI
from machine import Pin

spi = SPI(0, baudrate=10_000_000)
cs = Pin("P3", Pin.OUT, value=1)   # CS is not driven by the SPI peripheral

cs.value(0)
spi.write(b"hello")
cs.value(1)

ADC

يكشف Alif Ensemble E3 عن قناتي ADC بدقة 12 بت على P8 وP9 (موصل B2B فقط). ومرجع كلا المدخلين 1.8 فولت — يعيد read_u16 القيمة 0–65535 عبر المدى 0–1.8 فولت عند الدبوس:

from machine import ADC
import time

adc = ADC("P8")
while True:
    voltage = adc.read_u16() * 1.8 / 65535
    print(voltage)
    time.sleep_ms(100)

تحذير

مرجع مدخلات ADC في AE3 هو 1.8 فولت، لا 3.3 فولت. وسيؤدي دفع إشارة خام بجهد 3.3 فولت إلى إشباع المحوّل وقد يتلف الدبوس — قسّم الجهود الأعلى خارجيًا.

PWM

الدبوس

المؤقت / القناة

P0

TIM0 T1

P1

TIM0 T0

P2

TIM1 T1

P3

TIM1 T0

P4

TIM2 T1

P5

TIM2 T0

P6

TIM9 T0 (B2B فقط)

P7

TIM9 T1 (B2B فقط)

P8

TIM5 T0 (B2B فقط)

P9

TIM5 T1 (B2B فقط)

شغّل أيًا منها عبر machine.PWM

from machine import Pin, PWM

pwm = PWM(Pin("P0"), freq=1_000, duty_u16=32768)

النواقل البرمجية بطريقة bit‑banging

تعمل machine.SoftI2C وmachine.SoftSPI على أي دبوس GPIO إذا احتجت إلى ناقل إضافي.

المستشعر الحراري (خارج اللوحة)

يتضمن البرنامج الثابت مُشغّل fir --- مشغّل المستشعر الحراري (fir == far infrared) لمصوّر حراري AMG8833 بدقة 8 × 8 موصول خارجيًا. وصّل الوحدة بناقل I²C المذكور أدناه، ثم اقرأ الإطارات باستخدام fir.init() + fir.snapshot()

import time
import image
import fir

fir.init()                          # auto‑detects the sensor
clock = time.clock()

while True:
    clock.tick()
    try:
        img = fir.snapshot(x_scale=5, y_scale=5,
                           color_palette=image.PALETTE_IRONBOW,
                           hint=image.BICUBIC,
                           copy_to_fb=True)
    except OSError:
        continue
    print(clock.fps())

لا يتحدث مُشغّل fir مع المستشعر إلا عبر I²C 1 — وصّل الوحدة بـ P4 (SCL) وP5 (SDA).

التوقيت

time

تغطي وحدة time التأخيرات الحاجبة، والنبضات الرتيبة، وقياس الزمن المنقضي:

import time

time.sleep(1)              # seconds
time.sleep_ms(500)
time.sleep_us(10)

start = time.ticks_ms()
# ...do work...
elapsed = time.ticks_diff(time.ticks_ms(), start)

المؤقتات الافتراضية

تجدول machine.Timer دوال رد نداء دورية أو لمرة واحدة دون استهلاك فتحة مؤقت عتادي. مرّر -1 بوصفه المعرّف لاستخدام مؤقت افتراضي (برمجي):

from machine import Timer

one_shot = Timer(-1)
one_shot.init(period=5_000, mode=Timer.ONE_SHOT,
              callback=lambda t: print("once"))

periodic = Timer(-1)
periodic.init(period=2_000, mode=Timer.PERIODIC,
              callback=lambda t: print("tick"))

قيم الفترة بالمللي ثانية. استدعِ deinit() لإيقاف الفتحة وتحريرها.

ساعة الوقت الحقيقي

تحفظ machine.RTC وقت ساعة الحائط عبر عمليات إعادة الضبط، مدعومةً بـ 4 كيلوبايت من ذاكرة RAM الاحتياطية على الشريحة التي تنجو من السُبات العميق:

from machine import RTC

rtc = RTC()
rtc.datetime((2026, 4, 30, 4, 12, 0, 0, 0))   # Y, M, D, weekday, h, m, s, subsec
print(rtc.datetime())

تعمل RTC أيضًا خلال السُبات العميق، لذا يمكنك استخدامها بوصفها مصدر إيقاظ لـ machine.deepsleep().

معلومات الإقلاع ووقت التشغيل

نافذة محمّل الإقلاع عبر USB

عند كل تشغيل تعمل الكاميرا على محمّل إقلاع قصير (بضع ثوانٍ) يتيح لـ OpenMV IDE تحديث البرنامج الثابت دون اضطرار المستخدم إلى الدخول في وضع DFU. وبعد انتهاء النافذة يسلّم محمّل الإقلاع المهمة إلى boot.py ثم main.py.

يمكن لبرنامج نصي قيد التشغيل إعادة الدخول إلى محمّل الإقلاع عند الطلب باستدعاء machine.bootloader()

import machine

machine.bootloader()

نظام الملفات وترتيب الإقلاع

يركّب البرنامج الثابت في AE3 ما يصل إلى نظامي ملفات عند الإقلاع:

  • الفلاش الداخلي — مركّب دائمًا عند /flash. ويحتوي على main.py وREADME.txt افتراضيًا؛ ويُنشأ عند أول إقلاع على الإطلاق.

  • ROMFS — نظام ملفات للقراءة فقط ومُسقَط في الذاكرة عند /rom يُستخدم لشحن أصول بيانات كبيرة (مثل نماذج الذكاء الاصطناعي) تستفيد من الوصول بلا نسخ. يركّبه MicroPython تلقائيًا عند بدء التشغيل، قبل تشغيل أي شيفرة Python للمستخدم.

بعد التركيب، يُضبط دليل العمل على /flash. ثم يشغّل المفسّر البرامج النصية من ذلك الدليل:

  • يُنفَّذ boot.py عند كل إعادة ضبط ناعمة (إقلاع بارد، أو Ctrl‑D من REPL، أو كلما عاد البرنامج النصي قيد التشغيل).

  • يُنفَّذ main.py عند الإقلاع البارد فقط، فور boot.py مباشرة. وتعيد عمليات إعادة الضبط الناعمة اللاحقة تشغيل boot.py لكنها تنتقل مباشرة إلى REPL — ولإعادة تشغيل main.py عليك إعادة ضبط اللوحة بالكامل.

يكتفي main.py الافتراضي المشحون على لوحة طازجة الوميض بإيماض قناة الأزرق في LED من نوع RGB للمستخدم بوصفها نبض حياة (نبضتان قصيرتان، بفاصل قصير)، فيمكنك معرفة أن البرنامج الثابت أقلع بنجاح دون أي مضيف موصول.

يُوسَّع sys.path ليشمل نظامي الملفات كليهما وأدلتهما الفرعية lib/، فيمكن للوحدات القابلة للاستيراد أن تقيم في /flash/lib أو /rom/lib.

عند التوصيل عبر USB، يُعدَّد /flash أيضًا بوصفه قرص تخزين USB كبير السعة على المضيف، مما يتيح لك تحرير boot.py وmain.py وأي ملفات أخرى مباشرة. أخرج القرص قبل إعادة ضبط الكاميرا كي يُفرغ المضيف كتاباته المخزنة مؤقتًا.

ملاحظة

لأن نظام التشغيل يعامل القرص بوصفه جهاز كتل سلبيًا، فإن الملفات التي تنشئها أو تعدّلها شيفرة تعمل على OpenMV Cam لن تظهر حتى يعيد المضيف تركيب القرص. وإذا كتب كل من نظام التشغيل وOpenMV Cam على نظام الملفات نفسه في الوقت نفسه، فسيفوز نظام التشغيل ويكتب فوق التغييرات التي أجرتها الكاميرا.

ملاحظة

قد تضيء قناة الأحمر في LED من نوع RGB للمستخدم لفترة وجيزة أثناء قراءة المضيف من قرص تخزين USB كبير السعة أو الكتابة إليه — وهذا مؤشر نشاط يقوده البرنامج الثابت، وليس عطلًا.

أحجام التخزين

تأتي AE3 مع:

  • /flash — نظام ملفات FAT بسعة 8 ميجابايت، للقراءة/الكتابة.

  • /rom على النواة HP — نظام ROMFS للقراءة فقط ومُسقَط في الذاكرة بسعة 24 ميجابايت للبرامج النصية والبيانات التي تحمّلها النواة HP عند بدء التشغيل.

  • /rom على النواة HE — نظام ROMFS للقراءة فقط بسعة 1 ميجابايت تملكه النواة HE. ويجب خبز الوحدات ونماذج تعلم الآلة التي تريد إتاحتها لمهام @openamp.async_remote داخل هذه الصورة، لا تلك الخاصة بـ HP.

مؤشر العطل الفادح

إذا كان LED من نوع RGB للمستخدم يدور بسرعة عبر جميع الألوان — بسرعة كافية تجعله يبدو غالبًا كـ LED أبيض متلألئ بدلًا من ألوان متمايزة — فهذا يعني أن البرنامج الثابت واجه عطلًا فادحًا غير قابل للاسترداد. أعد وميض البرنامج الثابت للاسترداد؛ وإذا لم تساعد إعادة الوميض، فقد تكون اللوحة تالفة ماديًا.

المكتبات البرمجية

راجع فهرس المكتبات للحصول على القائمة الكاملة للوحدات — بما في ذلك تلك الفريدة لبناء AE3.