وقفه چیست؟

فرض کنید در منزل به امور روزمره تان می پردازید. اگر کسی پشت در باشد چطور متوجه می شوید که باید بروید و در را باز کنید؟ جواب ساده است. آن شخص زنگ در را می زند، شما متوجه می شوید که شخصی پشت در است و می روید و در را باز می کنید. قطعا شما برای اینکه متوجه شوید آیا کسی پشت در منتظر هست یا نه هر 5 دقیقه در را باز نمی کنید تا این مساله را بررسی کنید!

در این مثال زنگ در نقش وقفه یا Interrupt را بازی می کند.  یعنی شما به کارهای معمول می پردازید تا زمانی که یک وقفه شما را از انجام این کارها باز دارد و شما به وقفه ای که دریافت کردید رسیدگی کنید.  اما به روشی که به طور مرتب در را باز کنید تا ببینید کسی پشت در هست یا خیر polling می گویند.

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

وقفه ها وظایف جانبی هستند که CPU باید به محض دریافت آنها را انجام دهد و انجام این کار نباید خیلی طول بکشد. در غیر این صورت، اجرای برنامه اصلی بسیار کند می شود و پاسخگویی کل سیستم نیز به هم می ریزد.

ساز و کار رسیدگی به وقفه ها در آردوینو

هنگامی که CPU میکروکنترلر یک سیگنال وقفه دریافت می کند، اجرای برنامه اصلی را متوقف می کند و وضعیت فعلی آن را ذخیره می کند.

سپس CPU به بردار وقفه (آدرس) که تابع کنترل کننده ISR مربوط به آن قرار دارد می پرد و شروع به اجرای عملکرد کنترل کننده ISR تا تکمیل شود.

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

ذخیره و بازیابی وضعیت برنامه فرآیندی است که CPU باید انجام دهد تا به راحتی بین اجرای برنامه اصلی و کنترل کننده های ISR جابجا شود. برای این کار باید رجیسترهای فعلی CPU، آدرس شمارنده برنامه را ذخیره کند. این تنها راهی است که CPU می تواند بداند قبل از پریدن به بردار وقفه برای اجرای تابع ISR در کجا کار خود را متوقف کرده است.

پس از اتمام اجرای تابع کنترل کننده ISR، CPU باید به خاطر بیاورد که برنامه اصلی را کجا متوقف کرده است تا از آن نقطه ادامه دهد. بنابراین تمام آدرس‌ها و رجیسترهای ذخیره‌شده را برمی گرداند  و به همان جایی که برنامه اصلی را متوقف کرده است، باز می‌گردد.

هنگامی که دوباره یک وقفه جدید فعال شود، بلافاصله به CPU اطلاع داده می شود و تمام مراحل قبلی دوباره تکرار می گردد. در غیر این صورت، CPU به اجرای برنامه اصلی ادامه خواهد داد.

روال سرویس وقفه (ISR)

ISR (روال سرویس وقفه) یک تابع اختصاصی است که CPU در پاسخ به یک وقفه اجرا می کند. این روال مسوول رسیدگی به وظایف خاص مرتبط با سیگنال وقفه است. هنگامی که یک وقفه رخ می دهد، میکروکنترلر به تابع کنترل کننده ISR می پرد، آن را اجرا می کند و به جایی که برنامه اصلی را متوقف کرده است، باز می گردد.

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

میکروکنترلر Atmega328p که در آردوینو UNO به کار رفته دارای سیستم وقفه برداری است. این بدان معنی است که ما باید یک تابع کنترل کننده ISR برای هر سیگنال وقفه ای که در سیستم استفاده می شود تعریف کنیم.

وقفه خارجی آردوینو

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

وقفه های خارجی از سوی دیگر توسط سیگنال های خارجی اعمال شده به پین های خاص میکروکنترلر فعال می شوند. این پین ها معمولاً به عنوان پین های IRQ (پین های درخواست وقفه) نامیده می شوند. هنگامی که یک رویداد خارجی خاص رخ می دهد، مستقیماً سیگنال های وقفه به CPU ارسال می گردد، به همین دلیل است که این وقفه ها به عنوان وقفه های خارجی شناخته می شوند.

پین های وقفه خارجی در آردوینو UNO پین شماره 2 و 3 هستند و نام آنها به ترتیب INT0 و INT1 است. البته این شماره پایه ها در هر آردوینو متفاوت است و باید بسته به نوع آردوینو به دیتاشیت آن مراجعه نمایید.

پین های وقفه خارجی آردوینو زمانی که وضعیت دیجیتال پین مرتبط تغییر کرده است، وقفه ایجاد می کنند. رویداد تغییر پین که باعث ایجاد وقفه می شود را می توان طوری پیکربندی کرد که یکی از حالت های زیر را داشته باشد:

RISING: هنگامی که سیگنال از LOW به HIGH می رود، وقفه فعال شود.

FALLING: هنگامی که سیگنال از HIGH به LOW می رود، وقفه فعال شود.

CHANGE: هنگامی که سیگنال تغییر می کند (LOW -> HIGH یا HIGH -> LOW)، وقفه فعال شود .

LOW: هر زمان که سیگنال LOW شود، وقفه فعال می شود.

تابع attachInterrupt آردوینو برای فعال کردن پین های وقفه خارجی INT0 و INT1 استفاده می شود.

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

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

arduino interrupt

وقفه زمانی فعال می شود که لبه ای در حال سقوط روی یک پین خارجی وجود داشته باشد. کنترل کننده وقفه به سادگی یک متغیر پرچم را تغییر می دهد و وضعیت LED را متناسب با آن به روز می کند.

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

#define ledPin  8
#define interruptPin  2
byte state = LOW;

void blink() {
  state=!state;
  digitalWrite(ledPin, state);
}

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), blink, FALLING);
}

void loop() {
}


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

#define ledPin 3

این دستورالعمل یک ثابت به نام ledPin تعریف می کند و مقدار 8 را به آن اختصاص می دهد. سپس از این ثابت برای اشاره به پین دیجیتال شماره 8 در سراسر برنامه استفاده می شود.

#define interruptPin 2

به طور مشابه، این دستورالعمل یک ثابت به نام interruptPin تعریف می کند و مقدار 2 را به آن اختصاص می دهد. این ثابت برای اشاره به پین وقفه خارجی شماره 2 استفاده می شود.

خط بعدی یک متغیر بایت به نام state را اعلام می کند. این متغیر برای ذخیره وضعیت فعلی LED چه روشن یا خاموش استفاده می شود. مقدار متغیر state در ابتدا روی LOW تنظیم می شود که مربوط به خاموش بودن LED است.

تابع blink روال سرویس وقفه (ISR) است که هر زمان که یک لبه در حال سقوط در پین وقفه (پین 2) وجود داشته باشد فراخوانی می شود. ISR یک تابع ویژه است که برای کنترل یک وقفه فراخوانی می شود که سیگنالی است که نشان می دهد اتفاق مهمی رخ داده است. در این حالت، زمانی که مقدار interruptPin از HIGH به LOW می‌رود، وقفه ایجاد می‌شود.

تابع blink به سادگی متغیر state را تغییر می دهد که باعث می شود LED روشن یا خاموش شود. تابع digitalWrite برای تنظیم وضعیت ledPin (پین 8) به مقدار متغیر state استفاده می شود. اگر حالت 0 باشد، LED خاموش می شود. اگر حالت 1 باشد، LED روشن می شود.

هنگامی که برد آردوینو روشن یا ریست می شود، تابع setup یک بار فراخوانی می شود و برای مقداردهی اولیه سخت افزار و آماده سازی برد برای اجرای حلقه اصلی استفاده می شود.

pinMode (ledPin، OUTPUT);

این خط حالت ledPin (پایه 8) را روی OUTPUT تنظیم می کند. این بدان معنی است که می توان از  این پین برای هدایت جریان به LED استفاده کرد.

pinMode (Pin interrupt، INPUT_PULLUP);

این خط حالت interruptPin (پایه 2) را روی INPUT_PULLUP تنظیم می کند. این بدان معنی است که پین در داخل تا یک ولتاژ بالا کشیده می شود و می توان از آن به عنوان ورودی بدون نیاز به مقاومت کششی خارجی استفاده کرد.

attachInterrupt(digitalPinToInterrupt(interruptPin), blink, FALLING);

این خط تابع blink را به عنوان کنترل کننده وقفه برای interruptPin متصل می کند. تابع digitalPinToInterrupt عدد پین را به عدد وقفه تبدیل می کند. یا می توان به جای digitalPinToInterrupt(interruptPin) عدد 1 را قرار داد که به معنی اولین وقفه خارجی است که به پین شماره 2 مربوط است.

 و پارامتر FALLING مشخص می کند که وقفه باید در لبه های در حال سقوط سیگنال ایجاد شود. این بدان معنی است که هر زمان که مقدار interruptPin از HIGH به LOW برود، وقفه ایجاد می شود.

به جای FALLING یکی از 3 حالت دیگر را نیز می توان انتخاب کرد.

تابع loop حلقه اصلی برنامه است که به طور مداوم اجرا می شود تا زمانی که برد آردوینو خاموش یا تنظیم مجدد شود. در این حالت، تابع loop هیچ کاری انجام نمی دهد، زیرا LED توسط کنترل کننده وقفه کنترل می شود. کنترل کننده وقفه هر زمان که یک لبه سقوط در پین وقفه وجود داشته باشد فراخوانی می شود و وضعیت LED را تغییر می دهد.