طراحی مدار

I2C چیست و چه کاربردی دارد؟

I2C یکی از پرکاربردترین پروتکل های ارتباطی در پروژه های Arduino و ESP32 است که امکان ارتباط چندین دستگاه را با استفاده از دو خط SDA و SCL فراهم می کند. در این مقاله با ساختار I2C، آدرس دهی، Master و Slave، مقاومت های Pull-up، سطح ولتاژ، سرعت، طول سیم و نکات مهم طراحی و عیب یابی I2C آشنا می شویم.

I2C چیست و چه کاربردی دارد؟

در بسیاری از پروژه های Arduino و ESP32 لازم است میکروکنترلر با قطعات مختلفی ارتباط برقرار کند؛ مثلاً:

  • سنسور دما و رطوبت
  • نمایشگر OLED
  • RTC یا ساعت واقعی
  • سنسورهای فشار و شتاب
  • حافظه های EEPROM
  • مبدل های ADC و DAC
  • سنسورهای نور و فاصله
  • آی سی های مختلف

اگر برای هر قطعه چندین سیم جداگانه به میکروکنترلر وصل کنیم، مدار خیلی زود شلوغ و پیچیده می شود.

اینجاست که I2C وارد می شود.

I2C یکی از پرکاربردترین روش های ارتباطی در مدارهای الکترونیکی است که اجازه می دهد چندین قطعه با استفاده از تعداد بسیار کمی سیم با یکدیگر ارتباط برقرار کنند.

در این مقاله از پایه بررسی می کنیم:

  • I2C چیست؟
  • چرا به وجود آمده است؟
  • SDA و SCL چه هستند؟
  • Master و Slave یعنی چه؟
  • آدرس I2C چیست؟
  • چگونه چند دستگاه روی یک باس کار می کنند؟
  • چرا I2C به Pull-up نیاز دارد؟
  • چه محدودیت هایی دارد؟
  • هنگام طراحی مدار I2C به چه نکاتی باید توجه کنیم؟
  • چرا گاهی یک ماژول I2C کار نمی کند؟

I2C چیست؟

I2C مخفف:

Inter-Integrated Circuit

است.

این پروتکل برای ارتباط بین مدارهای مجتمع و قطعات الکترونیکی روی یک برد یا در فاصله های نسبتاً کوتاه طراحی شده است.

مهم ترین ویژگی I2C این است که برای ارتباط اصلی فقط به دو خط سیگنال نیاز دارد:

SDA
SCL

بنابراین اگر یک ESP32 داشته باشیم و بخواهیم چند سنسور و نمایشگر را به آن متصل کنیم، می توانیم بسیاری از آنها را روی همین دو خط قرار دهیم.

SDA چیست؟

SDA مخفف:

Serial Data

است.

این خط برای انتقال داده استفاده می شود.

به زبان ساده:

اطلاعاتی که بین میکروکنترلر و دستگاه I2C رد و بدل می شود، از طریق SDA منتقل می شود.

SCL چیست؟

SCL مخفف:

Serial Clock

است.

این خط سیگنال ساعت ارتباط را منتقل می کند.

میکروکنترلر با تغییر وضعیت SCL مشخص می کند که داده روی SDA در چه زمانی باید خوانده یا تغییر داده شود.

بنابراین به صورت ساده:

SDA → داده
SCL → زمان بندی انتقال داده

یک نکته مهم: I2C فقط دو سیم سیگنال دارد

وقتی می گوییم I2C به دو سیم نیاز دارد، منظور دو خط ارتباطی است.

البته در یک مدار واقعی معمولاً به سیم تغذیه نیز نیاز داریم:

VCC
GND
SDA
SCL

پس یک ماژول I2C معمولاً حداقل چهار اتصال دارد.

Master و Slave چیست؟

در معماری سنتی I2C، یک دستگاه نقش Master و دستگاه های دیگر نقش Slave دارند.

Master معمولاً میکروکنترلر است.

مثلاً:

ESP32
  |
  +---- OLED
  |
  +---- BME280
  |
  +---- DS3231

در این مثال:

ESP32 → Master
OLED  → Slave
BME280 → Slave
DS3231 → Slave

Master ارتباط را آغاز می کند و دستگاه مورد نظر را انتخاب می کند.

البته در نسخه ها و پیاده سازی های پیشرفته تر I2C امکان وجود چند Master نیز وجود دارد، اما در پروژه های معمول Arduino و ESP32 بیشتر با یک Master و چند دستگاه جانبی مواجه هستیم.

آدرس I2C چیست؟

اگر چند دستگاه را روی یک باس مشترک قرار دهیم، یک سؤال مهم ایجاد می شود:

میکروکنترلر از کجا بفهمد داده را برای کدام دستگاه ارسال کند؟

پاسخ، آدرس I2C است.

هر دستگاه روی باس باید آدرس مخصوص خود را داشته باشد.

برای مثال ممکن است داشته باشیم:

OLED  → 0x3C
BME280 → 0x76
RTC   → 0x68

بنابراین وقتی ESP32 می خواهد با RTC صحبت کند، آدرس 0x68 را روی باس ارسال می کند.

دستگاه دارای این آدرس پاسخ می دهد و سایر دستگاه ها ارتباط را دنبال نمی کنند.

چرا چند دستگاه می توانند از یک SDA و SCL استفاده کنند؟

یکی از جذاب ترین ویژگی های I2C همین است.

فرض کنید سه دستگاه داریم:

             +---- OLED
             |
ESP32 -------+---- BME280
             |
             +---- RTC

همه دستگاه ها به:

SDA
SCL

مشترک متصل هستند.

اما هر دستگاه آدرس مخصوص خودش را دارد.

مثلاً:

0x3C → OLED
0x76 → BME280
0x68 → RTC

پس نیازی نیست برای هر دستگاه دو GPIO جداگانه داشته باشیم.

ساختار ساده باس I2C

یک مدار ساده را می توان این طور تصور کرد:

                 VCC
                  |
             +----+----+
             |         |
            R1        R2
             |         |
SDA ---------+---------+---------+
             |         |         |
           ESP32      OLED      Sensor

SCL ---------+---------+---------+
             |         |         |
           ESP32      OLED      Sensor

GND ---------+---------+---------+

R1 و R2 همان مقاومت های Pull-up هستند.

این دو مقاومت یکی از مهم ترین بخش های مدار I2C هستند.

چرا I2C به Pull-up نیاز دارد؟

این موضوع بسیار مهم است.

در I2C خطوط SDA و SCL معمولاً با ساختار Open-Drain / Open-Collector کار می کنند.

دستگاه ها خط را به سمت LOW می کشند، اما برای ایجاد HIGH از مقاومت Pull-up استفاده می شود.

به شکل ساده:

VCC
 |
[Pull-up]
 |
SDA
 |
دستگاه های I2C

وقتی هیچ دستگاهی خط را به زمین نکشد، مقاومت آن را به HIGH می برد.

وقتی یک دستگاه بخواهد LOW ایجاد کند، خط را به GND می کشد.

بنابراین:

Pull-up → HIGH
دستگاه → LOW

به همین دلیل اگر Pull-up مناسب وجود نداشته باشد، I2C ممکن است اصلاً درست کار نکند.

آیا همه ماژول های I2C مقاومت Pull-up دارند؟

خیر، اما بسیاری از ماژول های آماده این مقاومت را روی خود دارند.

مثلاً ممکن است یک ماژول سنسور، دو مقاومت Pull-up برای SDA و SCL داشته باشد.

اگر چند ماژول را روی یک باس قرار دهیم، ممکن است هرکدام مقاومت Pull-up مخصوص خودشان را داشته باشند.

در این حالت مقاومت ها موازی می شوند.

مثلاً اگر سه ماژول هرکدام یک مقاومت 4.7kΩ داشته باشند، مقاومت معادل حدوداً:

4.7kΩ / 3 ≈ 1.57kΩ

خواهد شد.

این موضوع می تواند مهم باشد.

بنابراین:

فقط به این دلیل که هر ماژول به تنهایی درست کار می کند، نمی توانیم بدون بررسی همه آنها را روی یک باس قرار دهیم.

آیا باید برای هر ماژول یک Pull-up جداگانه قرار دهیم؟

خیر.

Pull-up مربوط به باس است، نه الزاماً هر دستگاه.

معمولاً یک مجموعه Pull-up مناسب برای SDA و SCL کافی است.

اگر ماژول های مختلف خودشان Pull-up داشته باشند، باید مجموع آنها را نیز در نظر گرفت.

مقدار Pull-up چقدر باشد؟

مقدار مقاومت Pull-up به عوامل مختلفی بستگی دارد، از جمله:

  • ولتاژ تغذیه
  • سرعت I2C
  • ظرفیت خازنی باس
  • طول سیم
  • تعداد دستگاه ها
  • مشخصات ورودی و خروجی دستگاه ها

مقادیر رایجی مانند:

10kΩ
4.7kΩ
2.2kΩ

در پروژه های مختلف دیده می شوند.

4.7kΩ یکی از مقادیر متداول برای بسیاری از مدارهای I2C است، اما نباید آن را یک قانون ثابت بدانیم.

گاهی 10kΩ مناسب است و در شرایط دیگر ممکن است مقاومت کوچک تری لازم باشد.

مقاومت Pull-up خیلی بزرگ چه مشکلی ایجاد می کند؟

فرض کنید مقاومت بسیار بزرگی استفاده کنیم.

در این حالت خط با جریان بسیار کمی به سمت HIGH کشیده می شود.

در نتیجه بالا رفتن ولتاژ خط کندتر می شود.

این موضوع در سرعت های بالاتر و باس هایی با ظرفیت خازنی بیشتر می تواند مشکل ایجاد کند.

در نتیجه ممکن است شکل موج SDA و SCL مناسب نباشد و ارتباط دچار خطا شود.

مقاومت Pull-up خیلی کوچک چه مشکلی ایجاد می کند؟

اگر مقاومت بیش از حد کوچک باشد، وقتی دستگاه خط را به LOW می کشد، جریان بیشتری عبور می کند.

مثلاً در 3.3V:

R = 1kΩ

I = 3.3 / 1000
I ≈ 3.3mA

در حالی که با 10kΩ:

I = 3.3 / 10000
I ≈ 0.33mA

بنابراین مقاومت کوچک تر باعث افزایش جریان LOW می شود.

پس انتخاب مقاومت یک مصالحه بین سرعت بالا رفتن خط و جریان مصرفی است.

مهم ترین نکته: سطح ولتاژ

یکی از خطرناک ترین اشتباهات در I2C، بی توجهی به ولتاژ Pull-up است.

فرض کنید یک ESP32 با منطق 3.3V داریم.

اگر یک ماژول I2C، SDA و SCL را با Pull-up به 5V متصل کند، ممکن است ولتاژ 5V مستقیماً روی GPIO میکروکنترلر قرار گیرد.

این موضوع می تواند برای GPIO مشکل ایجاد کند.

پس همیشه باید بررسی کنیم:

Pull-up های SDA و SCL به چه ولتاژی متصل هستند؟

به خصوص هنگام ترکیب:

ESP32 + ماژول 5V

یا:

Arduino 5V + سنسور 3.3V

باید سطح منطقی هر دو طرف بررسی شود.

I2C در ESP32

یکی از مزیت های ESP32 انعطاف نسبتاً خوب در انتخاب GPIO برای I2C است.

در بسیاری از پروژه ها از پایه های متداولی مانند:

SDA → GPIO21
SCL → GPIO22

استفاده می شود.

اما این به معنی آن نیست که همیشه مجبور به استفاده از این دو پایه هستیم.

بسته به برد و طراحی برنامه می توان I2C را روی GPIO های دیگری نیز تنظیم کرد، البته به شرطی که آن پایه ها برای این کار مناسب باشند.

مثلاً در Arduino-ESP32 می توان چنین کاری انجام داد:

Wire.begin(SDA_PIN, SCL_PIN);

بنابراین هنگام طراحی PCB بهتر است ابتدا محدودیت های GPIO های برد را بررسی کنیم و سپس پایه های I2C را انتخاب کنیم.

سرعت I2C

I2C چند حالت سرعت دارد.

سرعت های متداول عبارتند از:

حالتسرعت تقریبی
Standard Mode100 kHz
Fast Mode400 kHz
Fast Mode Plus1 MHz

بسته به سخت افزار و کتابخانه مورد استفاده، حالت های مختلف ممکن است قابل استفاده باشند.

برای بسیاری از پروژه های معمول:

100 kHz

کاملاً کافی است.

اگر به سرعت بیشتری نیاز داشته باشیم، می توان از:

400 kHz

استفاده کرد؛ البته به شرط اینکه همه قطعات موجود روی باس از آن سرعت پشتیبانی کنند.

آیا هر دستگاه I2C را می توان با هر سرعتی استفاده کرد؟

خیر.

اگر چند دستگاه روی یک باس داریم، باید محدودیت های آنها را در نظر بگیریم.

مثلاً اگر یک دستگاه فقط تا 100kHz را پشتیبانی کند، افزایش باس به 400kHz می تواند باعث مشکل شود.

بنابراین:

سرعت باس را باید بر اساس ضعیف ترین یا محدودترین دستگاه موجود روی باس انتخاب کرد.

طول سیم در I2C

I2C برای ارتباطات کوتاه داخل برد و بین ماژول های نزدیک طراحی شده است.

به طور کلی:

هرچه سیم طولانی تر شود، ظرفیت خازنی باس بیشتر می شود.

این موضوع باعث می شود بالا رفتن ولتاژ SDA و SCL کندتر شود.

در نتیجه ممکن است ارتباط در سیم های بلند ناپایدار شود.

بنابراین اگر پروژه ای دارید که در آن سنسور I2C چندین متر از میکروکنترلر فاصله دارد، I2C معمولی ممکن است انتخاب مناسبی نباشد.

برای فاصله های بیشتر بهتر است سراغ راهکارهای مناسب تر برویم.

تعداد دستگاه های I2C

از نظر تئوری می توان دستگاه های زیادی روی یک باس قرار داد، اما محدودیت های واقعی وجود دارند.

یکی از محدودیت های مهم، آدرس ها هستند.

اگر دو دستگاه دقیقاً یک آدرس داشته باشند، ممکن است هر دو همزمان به درخواست Master پاسخ دهند.

مثلاً:

Sensor A → 0x68
Sensor B → 0x68

این وضعیت می تواند مشکل ساز شود.

اگر دو دستگاه آدرس یکسان داشته باشند چه کنیم؟

این یکی از مشکلات رایج در پروژه های I2C است.

راه حل به نوع دستگاه بستگی دارد.

گاهی می توان آدرس دستگاه را با پایه های سخت افزاری تغییر داد.

مثلاً یک سنسور ممکن است پایه ای مانند:

ADDR
A0
A1
A2

داشته باشد.

با تغییر وضعیت این پایه می توان آدرس دستگاه را تغییر داد.

اما اگر امکان تغییر آدرس وجود نداشته باشد، ممکن است از یک I2C Multiplexer استفاده کنیم.

Scanner برای پیدا کردن دستگاه های I2C

یکی از بهترین ابزارها برای عیب یابی I2C، برنامه I2C Scanner است.

این برنامه آدرس های مختلف را بررسی می کند و نشان می دهد چه دستگاه هایی روی باس پاسخ می دهند.

یک نمونه ساده:

#include <Wire.h>

void setup() {
    Wire.begin();
    Serial.begin(115200);

    Serial.println("I2C Scanner");
}

void loop() {
    byte error;
    int count = 0;

    for (byte address = 1; address < 127; address++) {

        Wire.beginTransmission(address);
        error = Wire.endTransmission();

        if (error == 0) {
            Serial.print("I2C device found at 0x");
            if (address < 16)
                Serial.print("0");

            Serial.println(address, HEX);
            count++;
        }
    }

    if (count == 0)
        Serial.println("No I2C devices found.");

    delay(3000);
}

اگر مثلاً در Serial Monitor ببینیم:

I2C device found at 0x3C
I2C device found at 0x68

می فهمیم که دو دستگاه روی باس پاسخ می دهند.

اگر I2C Scanner هیچ دستگاهی پیدا نکرد چه کنیم؟

این قسمت بسیار مهم است.

اگر Scanner چیزی پیدا نکرد، فوراً نتیجه نگیریم که سنسور خراب است.

این موارد را مرحله به مرحله بررسی کنیم:

1. تغذیه

اول بررسی کنیم:

VCC
GND

درست متصل باشند.

2. SDA و SCL

جای SDA و SCL را بررسی کنیم.

جابجا شدن این دو خط یکی از خطاهای بسیار رایج است.

3. آدرس دستگاه

آدرس واقعی ماژول را بررسی کنیم.

اطلاعات نوشته شده روی فروشگاه یا مثال برنامه ممکن است مربوط به نسخه دیگری از ماژول باشد.

4. Pull-up

بررسی کنیم SDA و SCL Pull-up مناسب داشته باشند.

5. ولتاژ منطقی

بررسی کنیم Pull-up به ولتاژ مناسبی متصل باشد.

6. پایه های صحیح ESP32

بررسی کنیم برنامه از همان GPIO هایی استفاده کند که در مدار به SDA و SCL متصل کرده ایم.

7. اتصال زمین

اگر چند ماژول داریم، معمولاً باید زمین آنها مشترک باشد:

ESP32 GND
   |
   +---- Sensor GND
   |
   +---- OLED GND

یک اشتباه مهم: فکر کردن به SDA و SCL به عنوان TX و RX

در UART معمولاً با:

TX
RX

کار می کنیم.

اما I2C متفاوت است.

در I2C:

SDA → Data
SCL → Clock

بنابراین نباید I2C را صرفاً یک UART با دو سیم بدانیم.

ساختار و نحوه مدیریت ارتباط در این دو پروتکل متفاوت است.

I2C در برابر UART و SPI

سه پروتکل بسیار رایج در پروژه های میکروکنترلری عبارتند از:

ویژگیI2CSPIUART
خطوط اصلی2حداقل 3 یا 42
Clockداردداردندارد
چند دستگاهبلهبلهمعمولاً محدودتر
آدرس دهیداردمعمولاً با CSندارد
سرعتمتوسطبالامتوسط
سیم کشیکمبیشترکم
کاربرد رایجسنسورها، RTC، OLEDنمایشگر، حافظه، SDارتباط سریال

هیچ کدام همیشه بهترین گزینه نیستند.

انتخاب پروتکل به نوع قطعه و نیاز پروژه بستگی دارد.

چه زمانی I2C انتخاب خوبی است؟

I2C انتخاب بسیار خوبی است وقتی:

  • چند سنسور داریم.
  • تعداد GPIO های ما محدود است.
  • سرعت بسیار بالا لازم نیست.
  • فاصله دستگاه ها کم است.
  • دستگاه ها I2C را پشتیبانی می کنند.
  • می خواهیم چند دستگاه را با دو خط ارتباطی مدیریت کنیم.

مثلاً برای این مجموعه:

ESP32
 ├── OLED
 ├── BME280
 ├── DS3231
 └── BH1750

I2C می تواند انتخاب بسیار مناسبی باشد.

چه زمانی I2C انتخاب خوبی نیست؟

I2C همیشه بهترین گزینه نیست.

اگر:

  • فاصله زیاد است.
  • سرعت بسیار بالا نیاز داریم.
  • محیط بسیار نویزی است.
  • تعداد زیادی دستگاه با آدرس های تکراری داریم.
  • ظرفیت خازنی باس زیاد شده است.

ممکن است پروتکل یا سخت افزار دیگری انتخاب بهتری باشد.

چند نکته ضروری در طراحی PCB

اگر قرار است یک مدار I2C را روی PCB طراحی کنیم، چند نکته مهم را فراموش نکنیم.

مسیرهای SDA و SCL را بی دلیل طولانی نکنیم

هرچه مسیر طولانی تر باشد، ظرفیت انگلی بیشتری ایجاد می شود.

Pull-up را نزدیک باس قرار دهیم

مقاومت های Pull-up را در طراحی منطقی باس قرار دهیم و از پراکنده کردن مقاومت های اضافی روی هر ماژول تا حد امکان جلوگیری کنیم.

زمین مناسب داشته باشیم

مسیر GND مناسب برای سیگنال ها اهمیت دارد.

سطح ولتاژ را بررسی کنیم

قبل از اتصال ماژول های 5V و 3.3V، حتماً Pull-up و سطح منطقی را بررسی کنیم.

همه ماژول ها را کورکورانه به هم وصل نکنیم

ممکن است یک ماژول:

  • Pull-up داشته باشد.
  • آدرس خاصی داشته باشد.
  • فقط 100kHz را پشتیبانی کند.
  • با 3.3V کار کند.
  • یا حتی با 5V سازگار نباشد.

پس دیتاشیت یا شماتیک ماژول را بررسی کنیم.

مهم ترین نکته درباره I2C

اگر بخواهیم تمام این مقاله را در چند جمله خلاصه کنیم:

I2C = دو خط ارتباطی

SDA = Data
SCL = Clock

هر دستگاه = یک آدرس

SDA و SCL = معمولاً دارای Pull-up

چند دستگاه = می توانند یک باس مشترک داشته باشند

اما چهار نکته را همیشه به خاطر داشته باشید:

  1. SDA و SCL را درست وصل کنید.
  2. Pull-up مناسب داشته باشید.
  3. ولتاژ منطقی دستگاه ها را بررسی کنید.
  4. تداخل آدرس دستگاه ها را بررسی کنید.

جمع بندی

I2C یکی از کاربردی ترین پروتکل های ارتباطی در پروژه های میکروکنترلری است.

مزیت اصلی آن این است که می توانیم چندین دستگاه را با تنها دو خط اصلی:

SDA
SCL

به یک میکروکنترلر متصل کنیم.

هر دستگاه با یک آدرس مشخص روی باس شناسایی می شود و Master می تواند با انتخاب آن آدرس، با دستگاه مورد نظر ارتباط برقرار کند.

اما سادگی سیم کشی به معنی بی نیاز بودن از طراحی صحیح نیست.

Pull-up، سطح ولتاژ، سرعت، طول سیم، ظرفیت باس، آدرس دستگاه ها و تغذیه همگی می توانند روی عملکرد I2C تأثیر بگذارند.

اگر I2C را فقط به عنوان «دو سیم SDA و SCL» بشناسیم، در پروژه های واقعی خیلی زود با مشکل مواجه می شویم.

اما اگر منطق پشت آن را بفهمیم، I2C به یکی از ساده ترین و کاربردی ترین ابزارهای ارتباطی برای پروژه های Arduino و ESP32 تبدیل می شود.

→ بازگشت به وبلاگ