পাঠ ২৪ · ৫৭-এর মধ্যে · মডিউল ৬
Home / Courses / Microprocessors, Embedded Systems & IoT / I2C প্রোটোকল

I2C প্রোটোকল

I2C protocol
১০ মিনিট পড়া মধ্যম · Intermediate Python সিমুলেশনসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • I2C-এর শেয়ার্ড দুই-তার বাস টপোলজি -- SPI-এর তুলনায় পিন-সাশ্রয়ী কেন
  • START/অ্যাড্রেস+R-W/ACK-বা-NACK/ডেটা/STOP -- সম্পূর্ণ ট্রানজ্যাকশন সিকোয়েন্স
  • ACK/NACK আসলে কীভাবে নির্ধারিত হয় -- সেই অ্যাড্রেসে কোনো ডিভাইস আছে কিনা তার উপর ভিত্তি করে
  • একটি সত্যিকারের সিমুলেশন -- একটি সফল ট্রানজ্যাকশন এবং একটি NACK-এ বাতিল হওয়া ট্রানজ্যাকশন

১ · একটি শেয়ার্ড বাসে একাধিক ডিভাইস

আগের দুই পাঠে UART (পয়েন্ট-টু-পয়েন্ট) ও SPI (প্রতি স্লেভে আলাদা CS তার) দেখা গেছে। I2CI2CInter-Integrated Circuit -- মাত্র দুটো শেয়ার্ড তার (SDA, SCL) দিয়ে একাধিক ডিভাইসকে অ্যাড্রেসিং-ভিত্তিক একই বাসে চালানোর প্রোটোকল। এই সমস্যার একটি ভিন্ন সমাধান দেয় -- মাত্র দুটো তার:

  • SDA (Serial Data) -- ডেটা লাইন, দুই দিকেই ব্যবহৃত হয়
  • SCL (Serial Clock) -- ক্লক লাইন, মাস্টার চালায়

উভয় লাইন open-drain ধরনের এবং একটি পুল-আপ রেজিস্টরের মাধ্যমে সবসময় HIGH-এ টানা থাকে (L14-এর পুল-আপ ধারণার সরাসরি প্রয়োগ) -- যেকোনো ডিভাইস (মাস্টার বা স্লেভ) লাইনটিকে LOW-এ টেনে নামাতে পারে। বাসে থাকা প্রতিটি স্লেভ ডিভাইসের একটি অনন্য ৭-বিট অ্যাড্রেস থাকে, আর মাস্টার সেই অ্যাড্রেস পাঠিয়েই ঠিক করে কার সাথে কথা বলছে -- SPI-এর মতো আলাদা তারের বদলে অ্যাড্রেসিং দিয়ে ডিভাইস নির্বাচন।

I2C মাস্টার (MCU) SDA SCL ডিভাইস 0x48 (Temp সেন্সর) ডিভাইস 0x50 (EEPROM)
মাস্টার ও সব স্লেভ ডিভাইস একই SDA/SCL তারে যুক্ত থাকে -- মাস্টার অ্যাড্রেস পাঠিয়ে ঠিক করে কার সাথে কথা বলছে, আলাদা তারের দরকার হয় না।

২ · সম্পূর্ণ ট্রানজ্যাকশন সিকোয়েন্স

একটি I2C ট্রানজ্যাকশনের ধাপগুলো:

  1. START condition -- মাস্টার SDA-কে HIGH থেকে LOW-এ নামায় (SCL HIGH থাকা অবস্থায়) -- এটাই বাসকে জানায় একটি ট্রানজ্যাকশন শুরু হচ্ছে
  2. ৭-বিট অ্যাড্রেস + R/W বিট -- মাস্টার MSB-first (I2C UART-এর বিপরীতে MSB আগে পাঠায়) ৮ বিট পাঠায়: ৭ বিট অ্যাড্রেস + শেষে ১ বিট (0 = write, 1 = read)
  3. ACK/NACK -- সেই অ্যাড্রেসে থাকা ডিভাইস SDA-কে LOW টেনে ACK দেয়; কোনো ডিভাইস না থাকলে লাইন পুল-আপের কারণে HIGH-ই থেকে যায়, যা NACK হিসেবে গণ্য হয়
  4. ডেটা বাইট(গুলো) -- ACK পেলে প্রতিটি ডেটা বাইটের পর আবার একটি ACK/NACK আসে
  5. STOP condition -- মাস্টার SDA-কে LOW থেকে HIGH-এ তোলে (SCL HIGH থাকা অবস্থায়) -- ট্রানজ্যাকশন শেষ

নিচের কোড সেলে এই পুরো সিকোয়েন্সটি একটি Python ফাংশন হিসেবে বাস্তবায়ন করা হয়েছে, যেখানে ACK/NACK সত্যিই একটি devices ডিকশনারিতে সেই অ্যাড্রেস আছে কিনা তার উপর নির্ভর করে নির্ধারিত হয় -- হার্ডকোড করা নয়।

Python
devices = {
    0x48: "তাপমাত্রা সেন্সর (Temp Sensor)",
    0x50: "EEPROM",
}


def i2c_transaction(address, rw, data_byte=None, devices=devices):
    # rw: 0 = write, 1 = read
    events = []
    events.append("START condition পাঠানো হলো")

    addr_byte = (address << 1) | rw
    addr_bits = [(addr_byte >> i) & 1 for i in reversed(range(8))]  # MSB প্রথমে
    rw_label = "READ" if rw else "WRITE"
    events.append(
        f"অ্যাড্রেস+R/W বাইট পাঠানো হলো (MSB প্রথমে): {addr_bits} "
        f"= 0x{addr_byte:02X}  (address=0x{address:02X}, R/W={rw_label})")

    if address not in devices:
        events.append(f"NACK: 0x{address:02X} ঠিকানায় কোনো ডিভাইস সাড়া দেয়নি (SDA HIGH-ই থেকে গেছে)")
        events.append("ট্রানজ্যাকশন বাতিল -- মাস্টার সাথে সাথে STOP condition পাঠাল")
        return False, events

    events.append(f"ACK: ডিভাইস '{devices[address]}' (0x{address:02X}) সাড়া দিয়েছে")

    if rw == 0 and data_byte is not None:
        events.append(f"ডেটা বাইট পাঠানো হলো: {format(data_byte, '08b')}b (0x{data_byte:02X})")
        events.append("ACK: ডিভাইস ডেটা বাইট গ্রহণ করেছে")
    elif rw == 1:
        simulated_reply = 0x1B
        events.append(f"ডিভাইস থেকে ডেটা বাইট গৃহীত: {format(simulated_reply, '08b')}b (0x{simulated_reply:02X})")
        events.append("মাস্টার NACK পাঠাল (এটাই শেষ/একমাত্র বাইট, তাই আর পড়া হবে না)")

    events.append("STOP condition পাঠানো হলো")
    return True, events


print("=== দৃশ্য ১: সফল ট্রানজ্যাকশন (ডিভাইস আছে -- ACK) ===")
success, log = i2c_transaction(0x48, rw=0, data_byte=0x2A)
for line in log:
    print(" ", line)
print(f"ফলাফল: {'সফল' if success else 'ব্যর্থ'}\n")

print("=== দৃশ্য ২: ব্যর্থ ট্রানজ্যাকশন (ডিভাইস নেই -- NACK) ===")
failed, log2 = i2c_transaction(0x60, rw=0, data_byte=0x10)
for line in log2:
    print(" ", line)
print(f"ফলাফল: {'সফল' if failed else 'ব্যর্থ (বাতিল হয়েছে)'}")

    
লক্ষ্য করুন -- i2c_transaction(0x60, ...) কল করার সাথে সাথেই ফাংশন address not in devices শর্তে থেমে যায় এবং False রিটার্ন করে -- ডেটা বাইট পাঠানোর চেষ্টাই করা হয় না। বাস্তব I2C হার্ডওয়্যারেও ঠিক এভাবেই কাজ করে: NACK পেলে মাস্টার সাথে সাথে ট্রানজ্যাকশন বাতিল করে দেয়, কোনো ডেটা পাঠানোর চেষ্টা চালিয়ে যায় না।
মূল কথা · Key takeaway

I2C = দুটো শেয়ার্ড তার (SDA/SCL) + ৭-বিট অ্যাড্রেসিং + START/ACK-বা-NACK/STOP হ্যান্ডশেক। একাধিক ডিভাইস যুক্ত করতে অতিরিক্ত তার লাগে না -- শুধু নতুন অ্যাড্রেস। এই কারণেই সেন্সর-ঘন ডিজাইনে (M9) I2C প্রায়ই পছন্দের প্রোটোকল। তবে গতিতে এটি সাধারণত SPI-এর চেয়ে ধীর -- L26-এ এই ট্রেডঅফগুলো একসাথে তুলনা করা হবে।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন -- তারপর "→ উত্তর" চাপুন।

প্র ০১ একটি ৭-বিট অ্যাড্রেস স্পেসে সর্বোচ্চ কতগুলো ভিন্ন ডিভাইস অ্যাড্রেস থাকতে পারে?

$2^7 = 128$টি সম্ভাব্য অ্যাড্রেস (0x00 থেকে 0x7F), যদিও কিছু অ্যাড্রেস (যেমন 0x00, 0x01-এর মতো কয়েকটি) I2C স্পেসিফিকেশনে বিশেষ/রিজার্ভড উদ্দেশ্যে সংরক্ষিত থাকে, তাই বাস্তবে ব্যবহারযোগ্য অ্যাড্রেস এর চেয়ে কিছুটা কম।

প্র ০২ উপরের কোডে দুটো আলাদা 0x48 অ্যাড্রেসের ডিভাইস একই বাসে যুক্ত থাকলে কী সমস্যা হতে পারে?

দুটো ডিভাইসই একই সাথে ACK দেবে (বা একে অপরের রেসপন্স নষ্ট করবে), মাস্টার বুঝতে পারবে না ঠিক কার সাথে কথা হচ্ছে -- একটি অ্যাড্রেস কনফ্লিক্ট। বাস্তবে এই সমস্যা এড়াতে অনেক I2C চিপে একাধিক পিন-সিলেক্টেবল অ্যাড্রেস অপশন থাকে, যাতে একই মডেলের একাধিক চিপ একই বাসে ভিন্ন অ্যাড্রেসে বসানো যায়।

প্র ০৩ I2C অ্যাড্রেস+R/W বাইট MSB প্রথমে পাঠায়, কিন্তু UART ডেটা বাইট LSB প্রথমে পাঠায় (L22)। এই পার্থক্য প্র্যাকটিক্যালি কোথায় গুরুত্বপূর্ণ হয়ে ওঠে?

লজিক অ্যানালাইজার দিয়ে কাঁচা বিট-স্ট্রিম দেখার সময়, বা কোনো প্রোটোকল হাতে ডিকোড করার সময় ভুল বিট-অর্ডার ধরে নিলে সম্পূর্ণ ভুল অ্যাড্রেস/মান বের হবে। প্রতিটি প্রোটোকলের ডেটাশিট/স্পেসিফিকেশনে বিট-অর্ডার স্পষ্টভাবে উল্লেখ থাকে -- এমবেডেড ডিবাগিংয়ে এটি যাচাই করে নেওয়া জরুরি।

অনুশীলন

  1. চিন্তা করুন: i2c_transaction(0x50, rw=1) কল করলে (0x50 = EEPROM, রিড মোড) লগে কী কী ইভেন্ট আসবে বলে আপনার ধারণা?

    START, অ্যাড্রেস+R/W বাইট (rw=1 তাই READ), ACK (0x50 ডিভাইসে আছে), তারপর একটি সিমুলেটেড ডেটা বাইট গৃহীত হওয়ার লগ ও মাস্টারের NACK পাঠানোর লগ, এবং সবশেষে STOP condition -- মোট ৬টি ইভেন্ট লাইন, এবং ফাংশন True রিটার্ন করবে (সফল ট্রানজ্যাকশন)।

  2. পরীক্ষা করুন: devices ডিকশনারিতে 0x60: "নতুন সেন্সর" যোগ করুন, তারপর দৃশ্য ২-এর কলটি (i2c_transaction(0x60, ...)) আবার Run করে দেখুন ফলাফল কীভাবে NACK থেকে ACK-এ বদলে যায়।

    এখন 0x60 in devices সত্যি হবে, তাই ফাংশন ACK পাথে যাবে, ডেটা বাইট পাঠাবে, এবং True রিটার্ন করবে -- এটাই দেখায় যে ACK/NACK লজিক আসলেই devices ডিকশনারির উপর নির্ভরশীল, কোনো হার্ডকোড করা ফলাফল নয়।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

  • কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ মাইক্রোপ্রসেসর আর্কিটেকচার, এমবেডেড C, GPIO, টাইমার/PWM/ADC, সিরিয়াল প্রোটোকল, RTOS, সেন্সর/অ্যাকচুয়েটর, IoT আর্কিটেকচার, ওয়্যারলেস প্রোটোকল, MQTT/CoAP ও IoT সিকিউরিটি -- বাকি পাঠগুলো শীঘ্রই যুক্ত হবে।
  • Computer Architecture & Digital Logic কোর্স সহোদর কোর্স সাধারণ বাস আর্কিটেকচারের ভিত্তি সেই কোর্সে -- এই পাঠ তার একধাপ নিচের ডিভাইস-লেভেল সিরিয়াল প্রোটোকলে যায়।
  • সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture, Design and Analysis of Algorithms ও আরও অনেক কোর্স -- সব এক জায়গায়।
আগের পাঠ
SPI প্রোটোকল