পাঠ ১৭ · ৫৭-এর মধ্যে · মডিউল ৪
Home / Courses / Microprocessors, Embedded Systems & IoT / ইন্টারাপ্ট-ড্রিভেন GPIO

ইন্টারাপ্ট-ড্রিভেন GPIO ও ISR ডিজাইন

Interrupt-driven GPIO & ISR design
৯ মিনিট পড়া মধ্যম · Intermediate Python সিমুলেশনসহ সম্পূর্ণ বাংলায়

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

  • পোলিং ও ইন্টারাপ্ট-ড্রিভেন GPIO-র মধ্যে মৌলিক পার্থক্য
  • একটি সত্যিকারের সিমুলেশন — একই ঘটনার জন্য পোলিং বনাম ইন্টারাপ্টের ডিটেকশন লেটেন্সি সরাসরি গণনা ও তুলনা
  • ISR ডিজাইনের মূল নিয়ম — কেন ISR সংক্ষিপ্ত রাখতে হয়, এবং কীভাবে কাজ স্থগিত করতে হয়
  • একটি ভুল (bad) বনাম সঠিক (good) ISR ডিজাইনের উদাহরণ

১ · পোলিং বনাম ইন্টারাপ্ট

Computer Architecture কোর্সে সাধারণ ইন্টারাপ্ট হ্যান্ডলিং মেকানিজম কভার হয়েছে; এই পাঠ সেই ভিত্তির ওপর দাঁড়িয়ে GPIO-স্পেসিফিক প্রয়োগ দেখায়। L14-L16 পর্যন্ত আমাদের কোড read() কল করে সরাসরি পিনের অবস্থা পড়েছে — এটিই পোলিংPollingসফটওয়্যার বারবার, পর্যায়ক্রমে একটি পিন/ফ্ল্যাগের অবস্থা চেক করে — হার্ডওয়্যার নিজে থেকে কিছু জানায় না।। মূল লুপ অন্য কাজে ব্যস্ত থাকলে পিন কখন চেক হবে তা নির্ভর করে লুপ কতবার সেই চেক-পয়েন্টে ফিরে আসছে তার ওপর — দুই চেকের মাঝামাঝি ঘটা একটি পরিবর্তন পরবর্তী চেক না আসা পর্যন্ত ধরা পড়ে না।

ইন্টারাপ্ট-ড্রিভেন GPIOInterrupt-driven GPIOহার্ডওয়্যার নিজেই পিন পরিবর্তনের মুহূর্তে CPU-কে "থামিয়ে" একটি নির্ধারিত ISR ফাংশন চালানো শুরু করায় — সফটওয়্যারকে বারবার জিজ্ঞেস করতে হয় না।-তে হার্ডওয়্যার নিজেই একটি পিন-পরিবর্তন শনাক্ত করার সাথে সাথে CPU-কে যা করছিল তা সাময়িকভাবে থামিয়ে একটি নির্ধারিত ISR ফাংশন চালানো শুরু করে দেয় — সফটওয়্যারকে বারবার জিজ্ঞেস করার প্রয়োজন হয় না। এতে দুটো সুবিধা: (১) CPU অন্য কাজে ব্যস্ত থাকতে পারে যতক্ষণ না সত্যিই কিছু ঘটে, (২) পরিবর্তন প্রায় তাৎক্ষণিকভাবে ধরা পড়ে।

GPIO পিন অবস্থা বদলালো পোলিং লুপ পরের চেক পর্যন্ত অপেক্ষা দেরিতে শনাক্ত লেটেন্সি = পোল ইন্টারভাল পর্যন্ত হার্ডওয়্যার ইন্টারাপ্ট সাথে সাথে ISR কল দ্রুত শনাক্ত লেটেন্সি = ছোট, ফিক্সড ISR ওভারহেড
একই পিন-পরিবর্তন ঘটনার জন্য পোলিং-এর ডিটেকশন লেটেন্সি পোল ইন্টারভালের ওপর নির্ভরশীল, কিন্তু ইন্টারাপ্টের লেটেন্সি ছোট ও প্রায় ফিক্সড।

২ · সিমুলেশন — লেটেন্সি সরাসরি গণনা

নিচের কোডে একটি পিন-পরিবর্তন একটি এলোমেলো (কিন্তু ফিক্সড সিডের কারণে রিপ্রোডিউসিবল) টিকে ঘটে। পোলিং লুপ শুধু প্রতি POLL_INTERVAL টিকে একবার চেক করে (কারণ বাস্তবে CPU অন্য কাজেও ব্যস্ত থাকে), আর ইন্টারাপ্ট একটি ছোট, ফিক্সড হার্ডওয়্যার-এন্ট্রি ওভারহেড (কনটেক্সট সেভ করা ইত্যাদির জন্য) বাদে প্রায় তাৎক্ষণিকভাবে সাড়া দেয়।

Python
import random

TOTAL_TICKS = 50
POLL_INTERVAL = 6      # মূল লুপ অন্য কাজে ব্যস্ত থাকে, প্রতি ৬ টিকে একবার পিন চেক করে
ISR_ENTRY_LATENCY = 1  # ফিক্সড হার্ডওয়্যার ওভারহেড (কনটেক্সট সেভ ইত্যাদি) ISR শুরু হওয়ার আগে

rng = random.Random(7)
change_tick = rng.randint(5, TOTAL_TICKS - 5)   # পিনটি ঠিক কোন টিকে বদলাবে -- ফিক্সড সিড, রিপ্রোডিউসিবল

# --- পোলিং সিমুলেশন: পিন শুধু নির্দিষ্ট পোল-টিকগুলোতেই চেক হয় ---
poll_ticks = [t for t in range(0, TOTAL_TICKS + 1) if t % POLL_INTERVAL == 0]
detected_tick_polling = next(t for t in poll_ticks if t >= change_tick)
polling_latency = detected_tick_polling - change_tick

# --- ইন্টারাপ্ট সিমুলেশন: হার্ডওয়্যার একই টিকে ISR কল করে, শুধু ফিক্সড এন্ট্রি ওভারহেডসহ ---
detected_tick_interrupt = change_tick + ISR_ENTRY_LATENCY
interrupt_latency = detected_tick_interrupt - change_tick

print(f"পিনের অবস্থা সত্যিই বদলালো টিক: {change_tick}")
print(f"পোলিং চেক-পয়েন্টগুলো: {poll_ticks}")
print(f"পোলিং শনাক্ত করলো টিক {detected_tick_polling}-এ -> লেটেন্সি = {polling_latency} টিক")
print(f"ইন্টারাপ্ট শনাক্ত করলো টিক {detected_tick_interrupt}-এ -> লেটেন্সি = {interrupt_latency} টিক")
print(f"পোলিং ইন্টারাপ্টের চেয়ে {polling_latency - interrupt_latency} টিক ধীর")

    
এই ফিক্সড সিডে পিন বদলায় টিক ২৫-এ। পোলিং (প্রতি ৬ টিকে চেক করে) সেটা টিক ৩০-এ ধরে — ৫ টিক লেটেন্সি। ইন্টারাপ্ট সেটা টিক ২৬-এ ধরে (শুধু ১-টিক ফিক্সড ওভারহেড) — ১ টিক লেটেন্সি। অর্থাৎ এই নির্দিষ্ট উদাহরণে পোলিং ইন্টারাপ্টের চেয়ে ৪ টিক ধীর — এবং POLL_INTERVAL যত বড় হবে, পোলিং-এর সর্বোচ্চ সম্ভাব্য লেটেন্সি তত বাড়বে, কিন্তু ইন্টারাপ্টের লেটেন্সি প্রায় স্থির থাকবে।

৩ · ISR ডিজাইনের মূল নিয়ম

একটি ISR চলাকালীন সময় সাধারণত অন্যান্য (কম-প্রায়োরিটি) ইন্টারাপ্ট ব্লক থাকে এবং CPU তার স্বাভাবিক কাজ করতে পারে না — তাই একটি মৌলিক নিয়ম: ISR-কে যতটা সম্ভব সংক্ষিপ্ত রাখা। ভারী কাজ (যেমন একটি বড় হিসাব, একটি সেন্সর থেকে ধীর গতির ডেটা পড়া, বা নেটওয়ার্কে কিছু পাঠানো) সরাসরি ISR-এর ভেতরে করা উচিত নয় — বরং ISR শুধু একটি ফ্ল্যাগ সেট করে বা একটি কিউ-তে একটি ইভেন্ট যোগ করে সাথে সাথে ফিরে আসবে, আর মূল লুপ (বা একটি নিম্ন-প্রায়োরিটি টাস্ক) সেই ফ্ল্যাগ/কিউ দেখে আসল কাজটা পরে করবে।

নিচে একটি ইলাস্ট্রেটিভ (এই স্যান্ডবক্সে রান হয় না — বাস্তব এমবেডেড C-তে এমন দেখতে হয়) bad-বনাম-good ISR স্কেচ দেওয়া হলো:

/* খারাপ ISR -- ভেতরেই ভারী কাজ করছে */
void EXTI0_IRQHandler(void) {
    uint16_t raw = read_sensor_over_i2c();   /* ধীর, ব্লকিং I/O -- ISR অনেকক্ষণ আটকে থাকবে */
    float value = complex_filter(raw);       /* ভারী গণনা -- ISR-এর ভেতরে করা উচিত নয় */
    log_to_flash(value);                     /* ধীর রাইট অপারেশন -- আরও খারাপ */
    clear_interrupt_flag();
}

/* ভালো ISR -- শুধু একটি ফ্ল্যাগ/ইভেন্ট সেট করে সাথে সাথে ফিরে আসে */
volatile bool pin_change_pending = false;

void EXTI0_IRQHandler(void) {
    pin_change_pending = true;   /* ভারী কাজ মূল লুপে স্থগিত */
    clear_interrupt_flag();
}

/* মূল লুপ -- আসল, ভারী কাজ এখানে করা হয় */
while (1) {
    if (pin_change_pending) {
        pin_change_pending = false;
        uint16_t raw = read_sensor_over_i2c();
        float value = complex_filter(raw);
        log_to_flash(value);
    }
    /* অন্যান্য কাজ */
}
মূল কথা · Key takeaway

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

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

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

প্র ০১ উপরের কোড সেলে polling_latency-এর মান সবসময় ০ থেকে POLL_INTERVAL - 1-এর মধ্যে থাকে কেন?

কারণ detected_tick_polling সবসময় change_tick-এর পরের প্রথম পোল-চেকপয়েন্ট, আর পোল-চেকপয়েন্টগুলো প্রতি POLL_INTERVAL টিকে একবার আসে — তাই সবচেয়ে খারাপ ক্ষেত্রেও পরের চেকপয়েন্ট POLL_INTERVAL - 1 টিকের বেশি দূরে থাকতে পারে না (আর সবচেয়ে ভালো ক্ষেত্রে, পরিবর্তনটা ঠিক একটি চেকপয়েন্টেই ঘটলে, লেটেন্সি ০ হয়)।

প্র ০২ ISR_ENTRY_LATENCY-কে ০ না রেখে ১ রাখা হয়েছে কেন — বাস্তবে কি ইন্টারাপ্ট সত্যিই একদম শূন্য-লেটেন্সিতে সাড়া দেয়?

না। বাস্তব হার্ডওয়্যারে একটি ইন্টারাপ্ট ঘটলেও CPU-কে বর্তমান ইনস্ট্রাকশন শেষ করতে হয়, রেজিস্টার কনটেক্সট সেভ করতে হয়, এবং ISR-এর ঠিকানায় লাফ দিতে হয় — এই পুরো প্রক্রিয়ায় সবসময় কিছু (যদিও খুবই ছোট) সময় লাগে। ISR_ENTRY_LATENCY = 1 সেই বাস্তব, অপরিহার্য ওভারহেডটাকেই প্রতিনিধিত্ব করছে — ইন্টারাপ্ট দ্রুত, কিন্তু একেবারে শূন্য-লেটেন্সি নয়।

প্র ০৩ ISR-এর ভেতরে read_sensor_over_i2c()-এর মতো একটি ধীর, ব্লকিং কল রাখা কেন বিপজ্জনক?

কারণ ISR চলাকালীন সাধারণত অন্য (সমান বা নিম্ন প্রায়োরিটির) ইন্টারাপ্ট ব্লক থাকে এবং CPU তার স্বাভাবিক মূল-লুপ কাজ থামিয়ে রাখে। একটি ধীর I2C ট্রানজ্যাকশন (যা মাইক্রোসেকেন্ড থেকে মিলিসেকেন্ড সময় নিতে পারে) ISR-এর ভেতরে থাকলে সেই পুরো সময় ধরে সিস্টেম অন্য জরুরি ইন্টারাপ্ট বা কাজে সাড়া দিতে পারবে না — এটি পুরো সিস্টেমের রেসপন্সিভনেস নষ্ট করে দিতে পারে, বিশেষ করে একাধিক টাইম-ক্রিটিক্যাল ইভেন্ট থাকা সিস্টেমে।

অনুশীলন

  1. চিন্তা করুন: উপরের কোড সেলে POLL_INTERVAL-এর মান ৬ থেকে ১২ করলে polling_latency-এর সর্বোচ্চ সম্ভাব্য মান কী হবে?

    সর্বোচ্চ সম্ভাব্য polling_latency হবে POLL_INTERVAL - 1 = 11 টিক (আগে যা ছিল 6 - 1 = 5)। অর্থাৎ পোল ইন্টারভাল দ্বিগুণ করলে সবচেয়ে খারাপ ক্ষেত্রে লেটেন্সিও প্রায় দ্বিগুণ হয়ে যেতে পারে — এটাই দেখায় পোলিং কেন ইন্টারাপ্টের তুলনায় লেটেন্সি-সংবেদনশীল অ্যাপ্লিকেশনে কম উপযোগী।

  2. পরীক্ষা করুন: উপরের কোড সেলে rng = random.Random(7)-এর সিড বদলে random.Random(1) করে Run চাপুন এবং লক্ষ্য করুন change_tick ও polling_latency কীভাবে বদলায়, কিন্তু interrupt_latency কেন অপরিবর্তিত থাকে।

    সিড বদলালে change_tick-এর মান বদলে যাবে (নতুন এলোমেলো ফলাফল), আর যেহেতু detected_tick_polling নির্ভর করে change_tick ঠিক কোথায় পড়ছে তার ওপর, polling_latency-ও বদলে যাবে। কিন্তু interrupt_latency সবসময় ISR_ENTRY_LATENCY-এর সমান থাকে (এখানে ১) — কারণ ইন্টারাপ্ট সবসময় ঠিক সেই টিকেই সাড়া দেয় যখন পরিবর্তন ঘটে, প্লাস একটি ফিক্সড অফসেট — এটি change_tick-এর নির্দিষ্ট মানের ওপর নির্ভর করে না।

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

  • কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ মাইক্রোপ্রসেসর আর্কিটেকচার, এমবেডেড C, GPIO, টাইমার/PWM/ADC, সিরিয়াল প্রোটোকল, RTOS, সেন্সর/অ্যাকচুয়েটর, IoT আর্কিটেকচার, ওয়্যারলেস প্রোটোকল, MQTT/CoAP ও IoT সিকিউরিটি — বাকি পাঠগুলো শীঘ্রই যুক্ত হবে।
  • ডিজিটাল আউটপুট — LED ও লোড ড্রাইভ করা আগের পাঠ GPIO আউটপুট দিয়ে বাস্তব লোড নিরাপদে চালানোর নিয়ম — এই পাঠে GPIO ইনপুটে দ্রুত সাড়া দেওয়ার কৌশল নিয়ে আলোচনা হলো।
  • সব 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 ও আরও অনেক কোর্স — সব এক জায়গায়।
আগের পাঠ
ডিজিটাল আউটপুট — LED ও লোড ড্রাইভ করা