پروتکل 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:
#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);
}توضیح نکتهها:
- بازهٔ اسکن ۸ تا ۱۱۹ است؛ آدرسهای پایینتر و بالاتر این بازه رزرو پروتکلاند.
beginTransmission+endTransmissionیک «سلام» خالی است؛ اگر کسی آن آدرس نشسته باشد کد بازگشتی صفر میشود.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 و ساعت، سطری با هر دو آدرس چاپ شود و شمارش هم حفظ شود.
نمایش راهحل
آدرسها را هنگام اسکن در یک رشته جمع میکنیم و آخر هر دور یک سطر کامل چاپ میشود:
#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 آشنا میشوید، پروتکل چهار سیمیای که وقتی سرعت مهم است برنده است.