Servo Shield

ה-Servo Shield מניע עד שמונה מנועי הגה (servo) של חובבים במקביל מתוך ה-OpenMV Cam דרך I2C, באמצעות בקר servo / PWM מסוג PCA9685.

Servo Shield

ל-datasheet המלא, תמונות והזמנה ראו את עמוד המוצר של Servo Shield.

עיקרי הדברים

  • בקר servo / PWM מסוג PCA9685

  • שמונה ערוצי servo עצמאיים דרך I2C

  • ניתן להערים יחד עם ה-Motor Shield וה-Pan and Tilt Shield

Pinout

Servo Shield Pinout

טבלת פינים

פין

תפקיד

P4

I²C SCL — שעון אל ה-PCA9685

P5

I²C SDA — נתונים אל ה-PCA9685

מסילת VIN

מזין את מנועי ההגה (מפין ה-VIN של המצלמה)

מסילת 3.3V

מזין את הלוגיקה של ה-PCA9685

מסילת GND

הארקה משותפת של ה-servo והמצלמה

כתובת ה-I²C ברירת המחדל היא 0x40. חברו את גשר ההלחמה המובנה כדי להעביר את הכתובת אל 0x60.

הערה

ה-shield שואב את הזנת ה-servo ישירות מפין ה-VIN של המצלמה. USB אינו מזין את VIN באף OpenMV Cam, ולכן יש לספק את VIN חיצונית (סוללה, ספק שולחני או דומה) — בחרו מקור המדורג לזרם העצירה (stall) המשולב של כל מנוע הגה שבכוונתכם להניע.

שימוש

הניעו את שמונת ערוצי ה-servo דרך ה-PCA9685 על I²C. טווח רוחב-הדופק משתנה בין מנועי הגה, לכן כווננו את MIN_US ו-MAX_US כך שיתאימו לשלכם — ערכים אופייניים נעים סביב 1000–2000 µs:

import time
from machine import SoftI2C, Pin


class PCA9685:
    """Minimal PCA9685 driver — 12-bit PWM on any of 8 channels."""

    def __init__(self, bus, address=0x40, freq=50):
        self._bus = bus
        self._addr = address
        bus.writeto_mem(address, 0x00, b"\x00")            # reset Mode1
        prescale = round(25_000_000 / (4096 * freq)) - 1
        bus.writeto_mem(address, 0x00, b"\x10")            # sleep
        bus.writeto_mem(address, 0xFE, bytes([prescale]))  # prescale
        bus.writeto_mem(address, 0x00, b"\x00")            # wake
        time.sleep_us(5)
        bus.writeto_mem(address, 0x00, b"\xA1")            # restart + AI + allcall
        self._period_us = 1_000_000 // freq

    def set_duty(self, channel, duty):
        duty &= 0xFFF                                      # 12-bit
        if duty == 0:
            on, off = 0, 0x1000                            # FULL_OFF
        elif duty == 0xFFF:
            on, off = 0x1000, 0                            # FULL_ON
        else:
            on, off = 0, duty
        self._bus.writeto_mem(
            self._addr, 0x06 + 4 * channel,
            bytes([on & 0xFF, on >> 8, off & 0xFF, off >> 8]))

    def set_us(self, channel, pulse_us):
        self.set_duty(channel, (pulse_us * 4096) // self._period_us)


MIN_US = 1000  # full-left pulse width (microseconds)
MAX_US = 2000  # full-right pulse width

bus = SoftI2C(scl=Pin("P4"), sda=Pin("P5"))
pca = PCA9685(bus, address=0x40, freq=50)


def angle(channel, deg):
    pca.set_us(channel, MIN_US + (deg * (MAX_US - MIN_US)) // 180)


while True:
    for ch in range(8):
        angle(ch, 0)
    time.sleep_ms(2000)
    for ch in range(8):
        angle(ch, 180)
    time.sleep_ms(2000)

ה-PCA9685 מטפל גם ב-PWM כללי של 12 סיביות בכל תדר — השתמשו שוב באותה מחלקה עם set_duty (0–4095) כדי, למשל, לעמעם LED בערוץ 0 בתדר 1 kHz. הפונקציה המסייעת למטה ממירה מספר עשרוני של 0.0–100.0% אל טווח ה-duty של השבב 0–4095:

import time
from machine import SoftI2C, Pin

bus = SoftI2C(scl=Pin("P4"), sda=Pin("P5"))
pca = PCA9685(bus, address=0x40, freq=1000)


def brightness(channel, pct):
    pca.set_duty(channel, int(pct * 4095 / 100))


while True:
    # Ramp up 0 → 100%.
    for pct in range(101):
        brightness(0, float(pct))
        time.sleep_ms(20)
    # Ramp down 100 → 0%.
    for pct in reversed(range(101)):
        brightness(0, float(pct))
        time.sleep_ms(20)