آردوینومتوسط۸ دقیقه مطالعهArduino IDE 2.x / Uno R3

پروتکل I2C

اصول گذرگاه دو سیمی I2C؛ سیم‌کشی SDA و SCL روی Uno، مفهوم آدرس دستگاه‌ها، کتابخانه Wire، اسکنر I2C و اتصال هم‌زمان چند ماژول روی همان دو سیم.

آنچه در این درس می‌آموزید

  • ساختار گذرگاه I2C با دو خط SDA و SCL و نقش master و slave را توضیح دهید
  • سیم‌کشی I2C روی Uno را انجام دهید و نقش مقاومت‌های pull-up را بدانید
  • با کتابخانه Wire داده بفرستید و بخوانید و گذرگاه را اسکن کنید
  • چند دستگاه را با آدرس‌های مختلف روی یک گذرگاه مشترک راه‌اندازی کنید
نویسنده: تیم داکنامه · بازبینی: ۲۰۲۶-۱۰-۳۱
فهرست مطالب

هر ماژولی که تا حالا وصل کرده‌اید خطوط خودش را می‌خواست: نمایشگر چند پین، سنسور چند پین. حالا تصور کنید پنج ماژول باشد — پین‌های Uno تمام می‌شوند. «گذرگاه I2C» (Inter-Integrated Circuit) پاسخ صنعت به همین مشکل است: با فقط دو سیم مشترک، ده‌ها دستگاه را به یک میکروکنترلر وصل می‌کند. نمایشگرها، ساعت‌های واقعی، سنسورهای دما و شتاب و ده‌ها ماژول دیگر از همین دو سیم حرف می‌زنند. در این درس اصول I2C را می‌آموزید، گذرگاه را اسکن می‌کنید و دو دستگاه را هم‌زمان روی همان دو سیم راه می‌اندازید.

I2C چطور کار می‌کند؟

I2C یک گذرگاه دو سیمی است:

خط نام کامل نقش
SDA Serial Data داده روی همین خط رد و بدل می‌شود
SCL Serial Clock ساعت؛ ریتم بیت‌ها را master ضرب می‌کند

روی گذرگاه دو نقش وجود دارد: «مستر» (Master) که ساعت را می‌سازد و مکالمه را هدایت می‌کند — در مدارهای شما همان آردوینو — و «اسلیو» (Slave) که گوش می‌دهد یا پاسخ می‌دهد؛ هر ماژول یک اسلیو است.

نکتهٔ کلیدی I2C «آدرس» (Address) است: هر اسلیو یک شمارهٔ یکتا دارد، مثلاً 0x27 برای بسیاری از نمایشگرهای LCD با مبدل I2C و 0x68 برای DS3231. مستر ابتدای هر مکالمه آدرس مقصد را روی خط می‌فرستد و فقط دستگاه صاحبِ آن آدرس پاسخ می‌دهد. به همین دلیل همهٔ دستگاه‌ها می‌توانند روی همان دو سیم وصل باشند بدون آنکه حرف‌های هم را قاطی کنند.

سیم‌کشی روی Uno

پین Uno خط I2C
A4 SDA
A5 SCL

پین‌های A4 و A5 روی Uno رزرو سخت‌افزاری I2C هستند و جدا از کار آنالوگ‌بودنشان، در مدارهای I2C نقش SDA و SCL دارند. خطوط I2C «open-drain»‌اند: خودشان صفر می‌دهند اما یک را؛ پس به مقاومت «pull-up» به 5V نیاز دارند. خبر خوب: تقریباً همهٔ ماژول‌های آماده (LCD با مبدل، DS3231 و…) مقاومت‌های pull-up را روی برد خودشان دارند و شما چیزی اضافه نمی‌بندید.

کتابخانه Wire

کتابخانهٔ Wire همراه هسته است و فقط include می‌خواهد. توابع پرکاربردش:

  • Wire.begin() — شروع کار به‌عنوان مستر.
  • Wire.beginTransmission(address) — شروع مکالمه با آدرس مشخص.
  • Wire.write(value) — گذاشتن بایت روی خط.
  • Wire.endTransmission() — پایان ارسال و آزادسازی گذرگاه.
  • Wire.requestFrom(address, count) — درخواست count بایت از یک اسلیو؛ پاسخ با Wire.read() خوانده می‌شود.

اسکن گذرگاه: اسکچ لازم برای هر پروژهٔ I2C

قبل از هر کاری باید بدانید دستگاه‌هایتان واقعاً روی خط هستند و با چه آدرسی. اسکنر زیر تمام آدرس‌های بازهٔ مجاز ۸ تا ۱۱۹ را یکی‌یکی صدا می‌زند و هر دستگاهی که جواب دهد را چاپ می‌کند. مدار آزمایش: یک ماژول LCD با مبدل I2C (آدرس 0x27) روی SDA و SCL با تغذیه 5V:

i2c-scanner.ino
#include <Wire.h>

void setup() {
  Wire.begin();
  Serial.begin(9600);
  Serial.println("I2C scanner");
}

void loop() {
  byte count = 0;
  for (byte addr = 8; addr < 120; addr++) {
    Wire.beginTransmission(addr);
    if (Wire.endTransmission() == 0) {  // 0 means the device answered
      Serial.print("Found device at 0x");
      Serial.println(addr, HEX);
      count++;
    }
  }
  Serial.print("Done, ");
  Serial.print(count);
  Serial.println(" device(s)");
  delay(5000);
}

توضیح نکته‌ها:

  1. بازهٔ اسکن ۸ تا ۱۱۹ است؛ آدرس‌های پایین‌تر و بالاتر این بازه رزرو پروتکل‌اند.
  2. beginTransmission + endTransmission یک «سلام» خالی است؛ اگر کسی آن آدرس نشسته باشد کد بازگشتی صفر می‌شود.
  3. Serial.println(addr, HEX) عدد آدرس را در مبنای شانزده چاپ می‌کند — همان قالبی که دیتاشیت‌ها می‌نویسند.

خروجی سریال مانیتور با LCD متصل چنین است:

خروجی کد

I2C scanner Found device at 0x27 Done, 1 device(s)

اگر اسکنر چیزی پیدا نکرد، تقریباً همیشه مشکل سیم‌کشی است، نه کد؛ جدول عیب‌یابی پایین درس را ببینید.

چند دستگاه روی یک گذرگاه

حالا امتیاز واقعی I2C: همان دو سیم، یک دستگاه دیگر. جدول سیم‌کشی دو دستگاه با هم:

ماژول SDA SCL VCC GND آدرس
LCD 16x2 با مبدل I2C A4 A5 5V GND 0x27
ماژول ساعت DS3231 A4 A5 5V GND 0x68

همهٔ پایه‌های SDA به هم و SCL به هم وصل‌اند؛ چون آدرس‌ها متفاوت‌اند، هیچ تداخلی پیش نمی‌آید. اسکنر بالا حالا چنین چاپ می‌کند:

خروجی کد

Found device at 0x27 Found device at 0x68 Done, 2 device(s)

اشتباهات رایج و عیب‌یابی

نشانه علت رایج راه‌حل
اسکنر هیچ دستگاهی پیدا نمی‌کند جابه‌جایی SDA و SCL، تغذیه‌نشدن ماژول یا سیم شل سیم‌کشی A4=SDA و A5=SCL را سطربه‌سطر چک کنید و LED پشت ماژول (در صورت وجود) را ببینید
دستگاه پیدا می‌شود اما با آدرس غلط حرف می‌زنید فرض‌کردن آدرس از روی آموزش‌های دیگران؛ مبدل‌ها 0x27 یا 0x3F دارند همیشه اول اسکن کنید و آدرس واقعی چاپ‌شده را در کد بگذارید
ماژول 3.3 ولتی جواب نمی‌دهد یا می‌سوزد تغذیهٔ 5 ولت یا سیگنال 5 ولتی بدون مبدل سطح تغذیه را به 3.3V ببرید و برای خطوط داده مبدل سطح بگذارید
گذرگاه یک‌باره قفل می‌کند و اسکنر هم آدرسی نمی‌بیند قفل‌شدن اسلیو وسط ارسال یا طول سیم‌ها تغذیه را یک لحظه قطع و وصل کنید؛ سیم‌های SDA و SCL را کوتاه (چند ده سانتی‌متر) نگه دارید
فقط گاهی پاسخ می‌آید اتصال ضعیف بردبورد یا نبود مقاومت pull-up روی ماژول دست‌ساز اتصال‌ها را محکم کنید؛ برای مدار دست‌ساز دو مقاومت 4.7 کیلواهم از SDA و SCL به VCC ببندید

تمرین‌ها

تمرین

تمرین ۱ — گزارش آدرس‌ها در یک سطر

اسکنر را طوری تغییر دهید که پس از پایان اسکن، همهٔ آدرس‌های پیدا‌شده را در یک سطر با کاما جدا کند؛ مثلاً برای LCD و ساعت، سطری با هر دو آدرس چاپ شود و شمارش هم حفظ شود.

نمایش راه‌حل

آدرس‌ها را هنگام اسکن در یک رشته جمع می‌کنیم و آخر هر دور یک سطر کامل چاپ می‌شود:

i2c-scanner-oneline.ino
#include <Wire.h>

void setup() {
  Wire.begin();
  Serial.begin(9600);
  Serial.println("I2C scanner");
}

void loop() {
  byte count = 0;
  String found = "";
  for (byte addr = 8; addr < 120; addr++) {
    Wire.beginTransmission(addr);
    if (Wire.endTransmission() == 0) {
      found += "0x";
      if (addr < 16) found += "0";
      found += String(addr, HEX);
      found += " ";
      count++;
    }
  }
  Serial.print("Devices (");
  Serial.print(count);
  Serial.print("): ");
  Serial.println(found);
  delay(5000);
}

خروجی با دو دستگاه متصل:

خروجی کد

Devices (2): 0x27 0x68

نکتهٔ حل تمرین: شرط if (addr < 16) صفر پیشوند برای آدرس‌های زیر 0x10 می‌گذارد تا قالب آدرس‌ها همیشه دو رقمی شانزده‌شانزده بماند. ::: :::

خلاصه

  • I2C با دو خط SDA و SCL کار می‌کند؛ آردوینو مستر است و هر ماژول اسلیوی با آدرس یکتا.
  • روی Uno، SDA به A4 و SCL به A5 می‌رود؛ مقاومت‌های pull-up معمولاً روی خود ماژول‌ها هستند.
  • کتابخانهٔ Wire با beginTransmission و write و endTransmission می‌نویسد و با requestFrom و read می‌خواند.
  • اسکنر I2C ابزار اول هر پروژه است: آدرس واقعی را حدس نزنید، اسکن کنید.
  • چند دستگاه با آدرس‌های متفاوت روی همان دو سیم، بدون تداخل، هم‌زمان کار می‌کنند.

I2C دو سیم و آدرس داشت؛ در درس بعدی با SPI آشنا می‌شوید، پروتکل چهار سیمی‌ای که وقتی سرعت مهم است برنده است.

منابع و ارجاع‌ها

درس‌های مرتبط