آردوینو آموزش آردوینو

آموزش آردوینو بخش ۱۲ – استفاده از وقفه Interrupt در برنامه نویسی آردوینو

arduino-basic-tutorial-part-twelve-interrupt-digispark
نوشته شده توسط پریسا پوربلورچیان

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

 


معرفی وقفه Interrupt در برنامه نویسی


در برنامه‌نویسی، “وقفه” (Interrupt) یک روش است که به واسطه آن یک سیستم می‌تواند فعالیت عادی خود را متوقف کرده و به یک رویداد خاص یا به اجرای یک برنامه زمان‌بندی شده واکنش نشان دهد. وقفه‌ها به عنوان مکانیزمی برای پردازش رویدادهای فوری و بیرونی، مانند دریافت داده‌ها از دستگاه‌های ورودی، بررسی وضعیت‌های خطا، تایمرها و سایر رویدادها عمل می‌کنند.زمانی که وقفه‌ای رخ دهد، اجرای برنامه اصلی متوقف شده و کنترل به یک برنامه زمان‌بندی شده خاص که به عنوان وقفه (Interrupt Handler) شناخته می‌شود، منتقل می‌شود. وقفه مراحلی را انجام می‌دهد که به  سیستم اصلی برمی‌گرداند و به آن امکان می‌دهد تا به صورت مناسب به وقوع رویداد واکنش نشان دهد. بعد از اتمام وقفه، کنترل به برنامه اصلی بازگردانده می‌شود و اجرای آن از جایی که قبل از وقفه متوقف شده بود، ادامه پیدا می‌کند.

 

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

وقفه‌ها در سیستم‌های چند وظیفه‌ای (multitasking) و همچنین در سیستم‌های عامل نقش مهمی دارند. در سیستم‌های چند وظیفه‌ای، وقفه‌ها به سیستم‌عامل امکان می‌دهند تا منابع سیستم را به طور هوشمند تخصیص دهد و برنامه‌ها را بر اساس اولویت‌ها و اهمیت‌هایشان اجرا کند. برای مثال، یک وقفه مرتبط با تایمر می‌تواند به سیستم‌عامل اطلاع دهد که زمان اجرای یک برنامه به پایان رسیده است و برنامه‌ای که باید در اولویت بالاتری قرار داشته باشد را اجرا کند.با استفاده از وقفه‌ها، برنامه‌نویس قادر خواهند بود به طور همزمان با اجرای برنامه اصلی، رویدادها و وقایع مهم را پیگیری کنند. به عنوان مثال، یک برنامه کاربردی می‌تواند وقفه‌ای در نظر بگیرد که زمانی که یک پیام جدید دریافت می‌شود، اعلان را نمایش دهد و در عین حال ادامه فعالیت‌های خود را انجام دهد.


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


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

 

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

 

با استفاده از وقفه‌ها، می‌توان منابع سیستم را بهینه استفاده کرد. به عنوان مثال، شما می‌توانید با تنظیم یک وقفه زمانبندی شده، برنامه خود را در زمان مشخصی اجرا کنید و در بقیه زمان‌ها سیستم را در حالت خواب قرار دهید. این کار باعث صرفه‌جویی در مصرف انرژی و افزایش عمر باتری میکروکنترلر می‌شود.با استفاده از وقفه‌ها، می‌توانید توابعی را با زمانبندی دقیق پیاده سازی کنید. به عنوان مثال، می‌توانید با تنظیم یک وقفه زمانبندی شده، توابعی را در فواصل زمانی معین و با دقت بالا اجرا کنید. این کار برای برنامه‌هایی که نیاز به کنترل زمان دقیق دارند، بسیار مفید است. استفاده نادرست از وقفه‌ها ممکن است منابع سیستم را به طور نامناسب استفاده کند و باعث بروز مشکلاتی مانند تاخیر در اجرای برنامه یا قفل شدن سیستم شود. بنابراین، برنامه‌نویسان باید با دقت منابع مورد نیاز برای هر وقفه را تعیین کنند و از منابع بیش از حد استفاده نکنند.

 


تفاوت روش interrupt و pulling در وقفه


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

Interrupt (وقفه):

در روش Interrupt، سیستم به صورت فعال منتظر رویدادها و وقایع خارجی است. هنگامی که رویدادی رخ دهد (مانند فشار دکمه، تغییر وضعیت سنسور و غیره)، یک سیگنال وقفه به میکروکنترلر ارسال می‌شود و اجرای برنامه اصلی متوقف می‌شود.
سیستم سپس به روتین وقفه (Interrupt Service Routine) منتقل می‌شود که در آن برنامه‌نویس می‌تواند واکنش مناسبی را به وقوع رویداد نشان دهد و دستورات مربوطه را اجرا کند.
بعد از اجرای روتین وقفه، سیستم به اجرای برنامه اصلی خود بازمی‌گردد و عملکرد متوازنی بین واکنش به وقایع و اجرای برنامه اصلی را تحقق می‌دهد.

Polling (چک کردن):

در روش Polling، برنامه‌نویس باید به طور مداوم وقایع خارجی را بررسی کند. این بدان معناست که برنامه به صورت مداوم منابع سیستم را درگیر می‌کند، حتی در صورتی که وقوع رویداد‌ها بسیار کمتر از زمان بررسی باشد. به دلیل بررسی مداوم وضعیت وقایع، مصرف انرژی سیستم در روش Polling بیشتر است. زیرا برنامه باید به طور مداوم فعال باشد و بررسی کند که آیا وقایعی رخ داده است یا خیر.
در روش Polling، وقتی که برنامه در حال بررسی وضعیت وقایع است، از اجرای دستورات دیگر ممانعت می‌شود. این به این معناست که اگر برنامه در حال بررسی باشد، قادر به اجرای دستورات دیگری نخواهد بود و عملکرد سیستم ممکن است متوقف شود.در مقابل، در روش Interrupt، برنامه تنها در هنگام وقوع رویداد‌ها و وقایع خارجی متوقف می‌شود و بقیه زمان می‌تواند به اجرای برنامه اصلی بپردازد. این باعث صرفه‌جویی در منابع سیستم و کاهش مصرف انرژی می‌شود.همچنین، روش Interrupt به صورت سریع‌تر واکنش نشان می‌دهد زیرا به طور فوری به وقوع رویداد‌ها پاسخ می‌دهد و روتین وقفه را اجرا می‌کند.

 

 

 

 

 


PULLING


در این روش طبق بازه‌های زمانی  خاص که برنامه نویس آن‌ها را مشخص می‌کند میکروکنترلر یا آردوینو به سنسور رجوع می‌کند و اطلاعات مربوطه را دریافت می‌کند. در روش pooling میکروکنترلر باید مرتبا به سیستم خارجی رجوع کند و از سیستم مورد نظر درخواست اطلاعات کند. همانطور که مشاهده می‌کنید در روش pooling  سیستم خارجی متصل به میکروکنترلر ، یک سیستم مجزا محسوب نمی‌شود و وابسته به میکروکنترلر می‌باشد. اشکال این روش در این است که بار پردازشی اضافه برای چک کردن دائم سیستم خارجی می‌باشد که باعث مصرف انرژی و منابع میکروکنترلر می‌شود.بطور کلی، روش pulling در برنامه‌نویسی آردوینو به این معناست که در هر مرحله از برنامه اصلی، وضعیت ورودی‌ها (مانند دکمه‌ها) را بررسی کنیم. بر اساس وضعیت ورودی، می‌توانیم به دستورات مشخصی واکنش نشان دهیم. در زیر، یک مثال ساده از روش pulling برای کنترل دکمه در آردوینو را بررسی می‌کنیم:

 

const int buttonPin = 2;  // پین متصل به دکمه
int buttonState = 0;      // وضعیت دکمه (فشرده یا رها شده)

void setup() {
pinMode(buttonPin, INPUT);  // تنظیم پین دکمه به عنوان ورودی
Serial.begin(9600);         // آغاز ارتباط با پورت سریال
}

void loop() {
// بررسی وضعیت دکمه به صورت مداوم
buttonState = digitalRead(buttonPin);

// واکنش به وضعیت دکمه
if (buttonState == HIGH) {
Serial.println(“دکمه فشرده شد!”);
} else {
Serial.println(“دکمه رها شد!”);
}

delay(100);  // تاخیر بین بررسی‌های متوالی
}

 

بطور کلی، روش pulling در برنامه‌نویسی آردوینو به این معناست که در هر مرحله از برنامه اصلی، وضعیت ورودی‌ها (مانند دکمه‌ها) را بررسی کنیم. بر اساس وضعیت ورودی، می‌توانیم به دستورات مشخصی واکنش نشان دهیم. در زیر، یک مثال ساده از روش pulling برای کنترل دکمه در آردوینو را بررسی می‌کنیم:

cpp
Copy code
const int buttonPin = 2;  // پین متصل به دکمه
int buttonState = 0;      // وضعیت دکمه (فشرده یا رها شده)

void setup() {
pinMode(buttonPin, INPUT);  // تنظیم پین دکمه به عنوان ورودی
Serial.begin(9600);         // آغاز ارتباط با پورت سریال
}

void loop() {
// بررسی وضعیت دکمه به صورت مداوم
buttonState = digitalRead(buttonPin);

// واکنش به وضعیت دکمه
if (buttonState == HIGH) {
Serial.println(“دکمه فشرده شد!”);
} else {
Serial.println(“دکمه رها شد!”);
}

delay(100);  // تاخیر بین بررسی‌های متوالی
}

 

با استفاده از این روش pulling، وضعیت دکمه به صورت مداوم بررسی می‌شود و برنامه به طور مداوم منابع سیستم را درگیر می‌کند و برای بررسی واقعیت رخدادها باید به طور مکرر این عملیات انجام شود. این روش به صورت فعال منابع سیستم را استفاده می‌کند، حتی در صورتی که وقوع رویدادها نادر باشد. از طرفی، برای سیستم‌های ساده و بدون نیاز به واکنش فوری، استفاده از روش pulling ممکن است مناسب باشد زیرا کمترین هزینه منابع سیستم را دارد و به سادگی قابل پیاده‌سازی است.در مقابل، استفاده از وقفه (Interrupt) در برنامه‌نویسی آردوینو به شما امکان می‌دهد به صورت فعال منتظر وقوع رویدادها باشید و برنامه را متوقف کنید تا بلافاصله به وقوع رویداد واکنش نشان دهید. این به شما امکان می‌دهد برنامه را به صورت موازی و چند وظیفه‌ای طراحی کنید و برای رویدادهای مهم و فوری بلافاصله واکنش نشان دهید. با استفاده از وقفه، میزان استفاده از منابع سیستم نیز بهینه‌تر می‌شود زیرا برنامه فقط در هنگام وقوع رویداد مورد نظر فعال می‌شود و در بقیه زمان‌ها به اجرای برنامه اصلی خود باز می‌گردد.در کل، استفاده از روش Interrupt برای وقوع رویدادهای فوری و نیازمند واکنش سریع مناسب است، در حالی که روش pulling برای سیستم‌های ساده و بدون نیاز به واکنش فوری مناسب است و صرفه‌جویی در منابع سیستم را فراهم می‌کند. انتخاب روش مناسب وابسته به نوع سیستم و نیازهای خاص شما است.

 

 


INTERRUPT


وقفه یا همان interrupt یک سیگنال ریز پردازنده است که به توجه و پاسخ سریع CPU نیاز دارد. هنگامی که یک وقفه رخ میدهد، پردازنده عملیات جاری خود را متوقف میکند تا به درخواست وقفه ( کدهای خاصی که در برنامه نوشته شده است) رسیدگی کند. پس از رسیدگی به درخواست مورد نظر و انجام آن برنامه از همان جایی که متوقف شده است شروع به اجرا میکند. استفاده از وقفه باعث مصرف کمتر انرژی، بهینه تر شدن کد و استفاده کمتر از منابع میکروکنترلر می‌شود.به عنوان یک مثال از روش Interrupt در آردوینو، فرض کنید یک دکمه را به پین ۲ متصل کرده‌ایم و هر بار که دکمه فشار داده می‌شود، می‌خواهیم یک پیام را از طریق پورت سریال چاپ کنیم. برای این کار، از وقفه (Interrupt) استفاده می‌کنیم. در زیر، مثالی از روش Interrupt را مشاهده می‌کنید:

const int buttonPin = 2;  // پین متصل به دکمه

void setup() {
pinMode(buttonPin, INPUT);  // تنظیم پین دکمه به عنوان ورودی
attachInterrupt(digitalPinToInterrupt(buttonPin), buttonPressed, RISING);  // اتصال وقفه به پین دکمه
Serial.begin(9600);  // آغاز ارتباط با پورت سریال
}

void loop() {
// ادامه اجرای برنامه اصلی
// …
}

void buttonPressed() {
Serial.println(“دکمه فشرده شد!”);  // چاپ پیام از طریق پورت سریال
}

 

در این مثال، با استفاده از تابع attachInterrupt، وقفه را به پین دکمه اتصال می‌دهیم. در این حالت، وقفه به صورت RISING فعال می‌شود، یعنی هر بار که سیگنال ورودی از LOW به HIGH تغییر کند، وقفه فعال شده و تابع buttonPressed اجرا می‌شود. در تابع buttonPressed، ما پیام “دکمه فشرده شد!” را از طریق پورت سریال چاپ می‌کنیم.با استفاده از روش Interrupt، برنامه به طور معمولی اجرا می‌شود و منابع سیستم را درگیر نمی‌کند، تا زمانی که دکمه فشار داده شود و وقوع وقفه رخ دهد. در این صورت، تابع buttonPressed فوراً اجرا می‌شود و پیام مورد نظر را چاپ می‌کند. سپس برنامه به ادامه اجرای برنامه اصلی برمی‌گردد.تحت عنوان تابع وقفه (Interrupt Service Routine) استفاده کنیم. در مثال قبلی، تابع buttonPressed به عنوان تابع وقفه (Interrupt Service Routine) عمل می‌کند. در این تابع، می‌توانید به هر کدی که می‌خواهید برای واکنش به رویداد فشار دادن دکمه انجام دهید، از جمله تغییر وضعیت سایر پین‌ها، تولید صدا، ارسال داده‌ها و غیره.

 


مقایسه روش Pulling و Interrupt


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

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

 


مثال حقیقی


فرض کنید قصد داریدبرنامه‌ای بنویسید که با هر بار فشار دادن یک کلید یک عدد LED روشن شود. اگر از pooling  استفاده کنید باید مدام برنامه وضعیت کلید را چک کند و در صورت یک منطقی کلید LED  روشن می‌شود. ولی در روش interrupt  بدون چک کردن دائم وضعیت کلید ، هنگام فعال شدن کلید یک سیگنال به cpu   فرستاده می‌شود و سپس LED روشن می‌شود.

 

 


چند مدل وقفه داریم؟


در برنامه‌نویسی میکروکنترلرها مانند آردوینو، مدل‌های مختلفی برای وقفه وجود دارد. چند مدل معمول وقفه عبارتند از:

وقفه خارجی (External Interrupts): این نوع وقفه با وقوع رویدادهای خارجی از طریق پین‌های مشخصی رخ می‌دهد. میکروکنترلرها معمولاً پین‌هایی را برای وقفه خارجی دارند که توانایی ایجاد وقفه با تغییر وضعیت خروجی را دارند.

وقفه تایمر (Timer Interrupts): تایمرهای سخت‌افزاری در میکروکنترلرها می‌توانند وقفه‌ها را براساس زمانبندی مشخص شده فراخوانی کنند. این نوع وقفه بر اساس توقیت و تنظیمات تایمر اجرا می‌شود.

وقفه سریال (Serial Interrupt): در برخی میکروکنترلرها، می‌توان وقفه را برای دریافت داده‌ها از پورت سریال تنظیم کرد. وقفه سریال معمولاً بر اساس وقوع رویدادهایی مانند دریافت بایت جدید یا پر شدن بفر استفاده می‌شود.

وقفه نرم‌افزاری (Software Interrupt): این نوع وقفه توسط خود برنامه‌نویس در هر زمانی که نیاز دارد فراخوانی می‌شود. این وقفه معمولاً برای اجرای تسک‌های مهم یا پردازش‌های طولانی مدت در برنامه استفاده می‌شود.

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

 

 


مثال آردوینو برای ایجاد وقفه خارجی


برای ایجاد وقفه در برنامه نویسی آردوینو، می‌توانید از توابع attachInterrupt() یا attachInterruptArgs() استفاده کنید. این توابع به شما اجازه می‌دهند تا یک تابع وقفه (Interrupt Service Routine) را به یک سیگنال خارجی یا تایمر مرتبط با میکروکنترلر متصل کنید.

 

const int interruptPin = 2;

void setup() {
pinMode(interruptPin, INPUT);
attachInterrupt(digitalPinToInterrupt(interruptPin), isr, CHANGE);
Serial.begin(9600);
}

void loop() {
// کد اصلی برنامه
// …
}

void isr() {
// تابع وقفه (Interrupt Service Routine)
Serial.println(“وقوع وقفه خارجی!”);
}

 

در این مثال، یک وقفه خارجی به پین ۲ متصل شده است. تابع attachInterrupt() برای اتصال وقفه به پین استفاده می‌شود. ما از تابع digitalPinToInterrupt() برای تبدیل شماره پین به شماره وقفه استفاده کرده‌ایم. در این مثال، تابع وقفه isr() برای اجرا هر بار که وضعیت ورودی پین تغییر کند (CHANGE) فراخوانی می‌شود. در این تابع، ما یک پیام را از طریق پورت سریال چاپ می‌کنیم.

مثال آردوینو برای ایجاد وقفه تایمر


در این مثال، از کتابخانه TimerOne استفاده می‌شود. ابتدا باید کتابخانه TimerOne را به برنامه آردوینو خود اضافه کنید. برای این کار، به قسمت “Sketch” در نوار منو بروید، سپس “Include Library” را انتخاب کنید و در ادامه “TimerOne” را جستجو کنید و نصب کنید.در مثال از تایمر سخت‌افزاری Timer1 برای تنظیم یک وقفه تایمر استفاده شده است. تابع initialize() برای تنظیم فرکانس تایمر (در این مثال ۱ مگاهرتز) استفاده می‌شود. سپس تابع attachInterrupt() به شماره وقفه تایمر و تابع timerIsr() متصل می‌شود. تابع timerIsr() تابع وقفه تایمر را پیاده‌سازی می‌کند و در آن یک پیام را از طریق پورت سریال چاپ می‌کند.از طریق توابع attachInterrupt() و attachInterruptArgs() شما می‌توانید تابع وقفه خود را به وقایع خارجی یا تایمرها متصل کنید. این توابع به شما امکان می‌دهند تا برنامه خود را به صورت رخداد محور طراحی کنید و به صورت فوری واکنش نشان دهید.

 

const int interruptPin = 2;

void setup() {
Serial.begin(9600);
attachInterrupt(digitalPinToInterrupt(interruptPin), isr, FALLING);
Timer1.initialize(1000000);  // زمانبندی برای ۱ ثانیه
Timer1.attachInterrupt(timerIsr);  // اتصال تابع وقفه تایمر
}

void loop() {
// کد اصلی برنامه
// …
}

void isr() {
// تابع وقفه خارجی
Serial.println(“وقوع وقفه خارجی!”);
}

void timerIsr() {
// تابع وقفه تایمر
Serial.println(“وقوع وقفه تایمر!”);
}

 

در این کد، یک وقفه خارجی به پین ۲ متصل شده است و تابع isr به آن متصل شده است. همچنین، یک وقفه تایمر با استفاده از Timer1 سخت‌افزاری ایجاد شده است و تابع timerIsr به آن متصل شده است. هر بار که وقفه خارجی رخ دهد، تابع isr اجرا می‌شود و پیام “وقوع وقفه خارجی!” را از طریق پورت سریال چاپ می‌کند. همچنین، هر بار که وقفه تایمر رخ دهد، تابع timerIsr اجرا می‌شود و پیام “وقوع وقفه تایمر!” را چاپ می‌کند.قبل از اجرای کد، مطمئن شوید که کتابخانه TimerOne به درستی نصب شده است. همچنین، در صورت استفاده از وقفه خارجی، اتصال یک سیگنال خارجی به پین ۲ را بررسی کنید.

 


مثال آردوینو برای ایجاد وقفه سریال


به عنوان یک مثال برای وقفه سریال در آردوینو، می‌توانید از تابع attachInterrupt() به همراه توابع Serial.available() و Serial.read() استفاده کنید.

void setup() {
Serial.begin(9600);
attachInterrupt(digitalPinToInterrupt(0), serialIsr, FALLING);
}

void loop() {
// کد اصلی برنامه
// …
}

void serialIsr() {
// تابع وقفه سریال
while (Serial.available()) {
char data = Serial.read();
Serial.print(“دریافت شد: “);
Serial.println(data);
}
}

در این مثال، وقفه سریال به پین ۰ متصل شده است و تابع serialIsr به آن متصل شده است. هر بار که رویداد FALLING در پین ۰ رخ دهد (مانند دریافت بایت جدید از پورت سریال)، تابع serialIsr فراخوانی می‌شود. درون تابع serialIsr، با استفاده از تابع Serial.available() بررسی می‌شود که آیا داده‌ای دریافت شده است یا خیر. اگر داده دریافت شده باشد، با استفاده از تابع Serial.read() آن را می‌خوانیم و سپس آن را در سریال چاپ می‌کنیم.در این مثال، پین ۰ معمولاً به عنوان پین RX در آردوینو استفاده می‌شود و با اتصال کابل سریال به پورت سریال آردوینو، وقفه سریال رخ می‌دهد. لطفاً مطمئن شوید که پین ۰ به درستی با کابل سریال متصل شده است و برنامه Serial Monitor آردوینو را برای مشاهده خروجی استفاده کنید.

 


مثال آردوینو برای ایجاد وقفه نرم افزاری


در آردوینو، برای ایجاد وقفه نرم‌افزاری، می‌توانید از تابع interrupts() و noInterrupts() استفاده کنید. در ادامه یک مثال ساده برای ایجاد وقفه نرم‌افزاری در آردوینو آورده شده است:

volatile bool flag = false;  // پرچمی برای نشان دادن وقوع وقفه

void setup() {
Serial.begin(9600);
attachInterrupt(digitalPinToInterrupt(2), isr, FALLING);  // اتصال وقفه خارجی به پین ۲
interrupts();  // فعال کردن وقفه‌ها
}

void loop() {
// کد اصلی برنامه
// …

if (flag) {
noInterrupts();  // غیرفعال کردن وقفه‌ها
Serial.println(“وقوع وقفه نرم‌افزاری!”);
flag = false;
interrupts();  // فعال کردن وقفه‌ها
}
}

void isr() {
// تابع وقفه خارجی
flag = true;
}

در این مثال، وقفه خارجی به پین ۲ متصل شده است و تابع isr به آن متصل شده است. هر بار که رویداد FALLING در پین ۲ رخ دهد، تابع isr فراخوانی می‌شود و پرچم flag برابر با true قرار می‌گیرد. در حلقه loop()، قسمت اصلی برنامه اجرا می‌شود. اگر پرچم flag برابر با true باشد، وقفه نرم‌افزاری رخ داده است. در این حالت، با استفاده از noInterrupts() و interrupts() وقفه‌ها را به صورت موقت غیرفعال می‌کنیم، پیام مربوط به وقوع وقفه را چاپ می‌کنیم و سپس وقفه‌ها را دوباره فعال می‌کنیم.مطمئن شوید که پین ۲ به درستی با رویدادهای خارجی مورد نظر متصل شده است. همچنین، می‌توانید این کد را با توجه به نیازهای خود تغییر دهید.

 


مزیت استفاده از وقفه


با استفاده از وقفه دیگر نیازی به بررسی مداوم شرایط حلقه وقفه در برنامه نیست. پردازنده هنگام وقفه به طور خودکار هر کاری را که انجام می دهد متوقف می کند و با کنترل کننده وقفه ارتباط میگیرد. شما فقط باید کد بنویسید تا زمانیکه وقفه رخ دهد در برنامه پاسخ دهد. استفاده از وقفه به صورت سخت افزاری بسیار ساده است و با تریگر کردن پایه دیجیتال وقفه ایجاد می‌شود.

نکاتی پیرامون تابع وقفه :

  • تابع millis برای شمردن متکی به وقفه ها می‌باشد و مقدار آن هیچوقت درون تابع وقفه (ISR) افزایش پیدا نمی‌کند.
  • تابع delay برای کار کردن نیاز به وقفه‌ها دارد از این رو دستور delay هیچگاه درون تابع وقفه (ISR) اجرا نمی‌شود.
  • تابع micros در ابتدای کار ، درون تابع وقفه (ISR) به درستی عمل می‌کند ولی بعد از گذشت ۱-۲ میلی‌ثانیه به طور نامنظم کار می‌کند.
  • تابع delayMicroseconds برای افزایش مقدار خود به هیچ‌شمارنده‌ای وابسته نمی‌باشد بنابراین دستورdelayMicroseconds درون تابع وقفه (ISR) به درستی عمل می‌کند.
  • توجه کنید هیچگاه در تابع وقفه (ISR) پردازش‌های سنگین قرار داده نشود.
  • هیچ گاه دو تابع وقفه همزمان اجرا نمی‌شوند و به ترتیب اولویت موجود در جدول بردار وقفه اجرا می‌شوند.
  • هنگامی که یک متغیر را به طور سراسری تعریف می‌کنید و تمایل دارید مقدار متغیر درون تابع وقفه نیز بتواند تغییر کند باید قبل از تعریف متغیر از کلمه کلیدی volatile استفاده شود. با انجام این کار به کامپایلر یادآوری می‌کنیم که مقدار متغیر را در حافظه RAM و نه در یک رجیستر ذخیره کند و مقدار این متغیر می تواند خارج از برنامه (زیربرنامه وقفه یا سایر زیربرنامه ها) تغییر نماید.

 


وقفه در آردوینو


اکثر میکروکنترلر ها دارای ارتباط SPI، I2C، آنالوگ و دیجیتال هستند. گاهی اوقات یک سیستم خارجی مثل سنسور برای انجام عملیاتی مشخص به میکروکنترلر یا آردوینو متصل می‌شوند واز طریق دستورالعمل‎هایی با آن‌ها ارتباط برقرار شده و کنترل می‌شوند به عنوان نمونه یک سیستم دماسنج دیجیتال، که دمای محیط را اندازه‌گیری می‌کند و پس از پایان اندازه‌گیری مقادیر را به میکروکنترلر ارسال می‌کند. در ساختار اکثر بردهای آردوینو از دو وقفه سخت افزاری interrupt0 و interrupt1 است. فانکشن های آماده برای فراخوانی وقفه در برنامه نویسی آردوینو است و به راحتی از آن ها میتوانید در کدنویسی استفاده کنید. همانطور که گفته شد، دو نوع وقفه در آردوینو وجود دارد.

  •  وقفه خارجی
  • وقفه تغییر پایه

وقفه خارجی External Interrupt

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

در برد آردوینو UNO و آردوینو MEGA2560، پین های وقفه های خارجی طبق جدول زیر است.

 

 

آردوینو uno دارای ۲ پایه اینتراپت خارجی است. با یک مثال روند ایجاد وقفه را با هم بررسی می‌کنیم. در این کد روش ایجاد وقفه برای حرکت سرو موتو را بررسی می‌کنیم.

 

 void reset()
  {
    pos = 0;
    servo.write(pos);
    increment = abs(increment);
  }

 

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

 


()attachInterrupt


اولین پارامتر برای attachInterrupt، عدد اینتراپت است. در واقعیت دستور digitalPinToInterrupt(pin) مستقیم پایه مورد نظر را به وقفه نسبت میدهد. به عنوان مثال اگر بخواهیم پایه شماره ۳ را به عنوان وقفه تعریف کنیم به صورت زیر است.

digitalPinToInterrupt(3)

 

 

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

 

روش فراخوانی وقفه در برنامه آردوینو 

attachInterrupt(digitalPinToInterrupt(pin), ISR, mode) (recommended)
attachInterrupt(interrupt, ISR, mode) (not recommended)
attachInterrupt(pin, ISR, mode) (Not recommended. Additionally, this syntax only works on Arduino SAMD Boards, Uno WiFi Rev2, Due, and 101.)

 

پارامترهای وقفه

  • Interrupt = عدد اختصاص داده شده به وقفه
  • Pin = پایه آردوینو
  • ISR = زمانی فراخوانی می‌شود که وقفه اتفاق بیفتد.
  • MODE = زمانی تعریف می‌شود که وقفه تریگر شده باشد .توسط این آرگمان نوع تحریک شدن وقفه مشخص می‌شود.دارای چهار حالت زیر می‌باشد.
  • LOW : هرگاه وضعیت پین interrupt در حالت LOW ( صفر منطقی) قرار داشته باشد ، تابع وقفه اجرا می‌شود.
  • HIGH : هر گاه وضعیت پین interrupt  در حالت HIGH ( یک منطقی ) قرار داشته باشد ، تابع وقفه اجرا می‌شود. درست عکس حالت فوق.
  • CHANGE : هر گاه وضعیت پین INTERRUPT  از حالت صفر منطقی به  یک منطقی تغییر کند یا هرگاه از حالت ۱ منطقی به صفر منطقی تغییر کند، تابع وقفه اجرا می‌شود.
  • RISING : هنگامی که وضعیت پین INTERRUPT از صفر منطقی به یک منطقی تغییر کند تابع وقفه اجرا می‌شود.
  • FALLING : هنگامی وضعیت پین INTERRUPT از یک مطقی به صفر منطقی تغییر کند ، تابع وقفه اجرا می‌شود.

attachInterrupt(digitalPinToInterrupt(pin), ISR, mode)

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

attachInterrupt(0, ISR, FALLING);

 

ولی با استفاده از تابع جدیدی که هم اکنون معرفی کردیم باید مطابق کد زیر عمل کنیم :

attachInterrupt( digitalPinToInterrupt(2) , ISR, FALLING);

 

detachInterrupt()

این تابع وقفه مورد نظر را غیرفعال می‌کند و آن پایه که وقفه به آن نسبت داده شده بود به ورودی / خروجی تغییر می‌دهد. این تابع دارای  ساختارهایی به صورت زیر است.

detachInterrupt (interrupt);	   //شماره وقفه را وارد می کنیم

detachInterrupt (digitalPinToInterrupt (pin));	 //شماره پین وقفه را وارد میکنیم

detachInterrupt (pin);		  //می توان از این ساختار استفاده نمود Zero و  Due فقط در آردوینو

 

noInterrupts()

از این تابع برای غیر فعال کردن وقفه استفاده می‌شود.

 

interrupts()

بعد از غیر فعال کردن وقفه‌ می‌توان با این دستور ، وقفه را دوباره فعال نمود.

 


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


کد را کپی و به نرم افزار آردوینو انتقال دهید. در این کد هر با که کلید فشرده شود، وقفه تریگر شده و توسط ISR حالت ال ای دی را تعییر میدهد. ال ای دی به پایه شماره ۱۳ وصل شده و کلید به پایه ای از آردوینو وصل شده که از اینتراپت پشتیبانی می‌کند. برای تبدیل پایه دیجیتال به اینتراپت باید از دستور attachInterrupt استفاده کنیم. اولین آرگومان در این تابع آی دی اینتراپت است پس اگر از آردوینو uno استفاده کنیم، اینتراپت به همان پایه شماره ۲ است.

INT0=PIN2

آرگومان بعدی فانکشنی است که وقفه را تریگر می‌کند که در اینجا همان فشردن کلید است. برای این حالت یک اسم در دستور داریم که ISR NAME است. انتخاب اسم ISR دلخواه است. در نهایت آخرین آرگومان به آردوینو اینکه وضعیت تریگر به چه صورت بوده است را اعلام می‌کند. اگر از مود RISING استفاده شود، زمانی وقفه تریگر می‌شود که حالت پایه شماره ۲ از LOW به HIGH برسد. اگر مود FALLING تعریف شود، زمانی وقفه تریگر می‌شود، از HIGH به LOW می‌رسد. در این حالت وقفه فعال شده است. در این مرحله بایستی ISR بسازیم. در این کد ISR ساخته شده همان کلید ورودی است که با فشردن کلید وضعیت ال ای دی در دو حالت ON , OFF مداوم بررسی می‌شود.

 

 

const byte ledPin = 13;
const byte interruptPin = 2;
volatile byte state = LOW;

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

void loop() {
  digitalWrite(ledPin, state);
}

void blink() {
  state = !state;
}

 


شماره اینتراپت


با استفاده از دستور digitalPinToInterrupt(pin) به صورت اتوماتیک پایه های پین را به وقفه نسبت میدهد. در جدول زیر شماره ی پایه های هر آردوینو برای وقفه مشخص شده است.

 


جمع بندی لیدی پای


وقفه Interrupt یک سیگنال به ریزپردازنده است که به توجه و پاسخ سریع CPU نیاز دارد. هنگامی که یک وقفه رخ می‌دهد، پردازنده عملیات جاری خود را متوقف می‌کند تا به درخواست وقفه رسیدگی کند. پس از رسیدگی به درخواست مورد نظر و انجام آن ، برنامه از همان‌جایی که متوقف شده است شروع به اجرا می‌کند. استفاده از وقفه باعث مصرف کمتر انرژی ، بهینه تر شدن کد ، درک بهتر از کد و همچنین استفاده کمتر از منابع میکروکنترلر می‌شود. در این آموزش روش استفاده از وقفه با ذکر مثال توضیح داده شده است. بررسی و در صورت سوال از طریق بخش دیدگاه ها بپرسید.

 

درباره نویسنده

پریسا پوربلورچیان

تولید کننده محتوا / کارشناس IOT

زندگی یعنی پژوهش و فهمیدن چیزی جدید

تلاشم بر این است تجربیاتم در زمینه IOT‌ را به بهترین شکل با شما در میان بگذارم.

تبادل نظر و رفع عیب با ثبت دیدگاه

۳۸ دیدگاه

  • یه جوری گفتید اینتراپت گفتم حالا میشه از این دریچه پا به جهان هستی دیگری گذاشت
    تو برنامه میخواستم با فشار دادن یک کلید از برنامه خارج و دو تا متغیر روی نمایشگر بهم نشون بده
    یکی بیشتر نشون نمیداد اونم سام تامیز

    • اتفاقا با این قابلیت میتونی پا به جهانی دیگر بزاری، اگه برنامه نویسی بلد باشی. نه اینکه صرفا از سایت های مختلف کد رو کپی کنی.
      یکی از کاربردهاش میتونه برای ترمز اضطراری باشه. که به محض فشردن کلید، کل سیستم ریست یا خاموش بشه.
      شما احتمالا به جای اینکه یه متغیر رو داخل ISR تغییر بدی، کل کد lcd رو داخلش گذاشتی. این بخش در حد میکروثانیه تا نهایتا یکی دو میلی ثانیه کار میکنه و بعد از اون خطاهای متعددی میده.
      مثل کد ارائه شده توسط نویسنده وبسایت، شما هم یک state تعریف کن و بیرون از ISR یه if بزار که هر وقت کلید فشرده شده بود اون LCD متن رو نمایش بده.

      • با سلام
        کاربر گرامی از اینکه تجربه ی خود را با دیگر کاربران به اشتراک گذاشته اید، صمیمانه سپاسگزاریم.

  • خیلی عالی و آموزش کاربردی
    دوتا سوال:

    تو تابع blink
    منظور از اینکه state=!state چی هست؟
    دوتا متغیر یکسان برابرو هم نکنیم یعنی چه؟!!!

    سوال دوم: حتما باید یک مدار سخت افزاری به پایه های اینتراپت بدیم و میشه به صورت مجازی هم وقفه ایجاد کرد ؟

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

      • در مورد سوال اول ایشون احتمالا اشتباه کردن با شرط، بلکه فقط داریم میگیم state هر چی هست، برعکسش رو بریز داخل خود state
        شرطی نیست و فقط داره مقدار دهی به state اتفاق می‌افته…

  • سلام و خسته نباشید لطفا با استفاده از وقفه برناه ای بنویسد که بشه با چرخاندن شفت اینکدر در وقفه ۰ و۱ اردیونو uno بر روی ال سی دی عددی کم و زیاد شود

  • کاملترین مرجع معرفی وقفه به فارسی تا به امروز که دیدم و خوندم و یاد گرفتم
    موفق باشید

  • سلام
    به عنوان مثال اگر بخواهیم وقفه در فشردن کلید اتفاق بیفتد باید دقیقا چه تابعی را در وقفه بنویسیم؟

    • با سلام
      بایستی تابع ISR را به صورت سفارشی برای همان تسکی که مد نظرتان استف بنویسید.

    • با سلام
      از بردهایی مانند nodemcu استفاده کنید چون تمامی پایه ها میتوانند وقفه باشند.

  • سپاس از توضیحات کاملتون
    بنده برای راه اندازی پروژم با مشکل رو به رو شدم و دقت بالایی نداشت. برای وودی اینتراپت اضافه کردم و بدون باگ عمل میکنه

    • با سلام
      کاربر گرامی سپاس از همراهی شما
      از اینکه تجربه خود را با دیگر کاربران به اشتراک می‌گذارید، صمیمانه سپاسگزاریم.

  • سلام
    ممنون از توضیحات خوبتون.
    فقط یه سوال، من میتونم از همون پایه ای که به عنوان وقفه استفاده کردم در همون وقفه به عنوان ورودی هم استفاده کنم؟

  • سلام
    اینجور که من فهمیدم برای isr یک تابع سفارشی سازی شده باید بنویسیم که یه وقفه ای در اون اتفاق بیفته
    درسته؟

    • با سلام
      بله یک تابع وقفه ISR برای عملی که قرار است در وقفه رخ دهد به صورت جداگانه در برنامه تعریف می‌کنیم.

    • با سلام
      در این خصوص دقیقا مشابه تابع های سفارشی در کد برنامه با اسم مشخص اضافه می‌شود.
      void isrfunction()
      {
      }

    • با سلام
      زمانیکه سطح سیگنال از HIGH به LOW برسد همان لبه کاهنده یعنی FALLING EDGE خواهد بود.

    • با سلام
      با استفاده از وقفه دیگر نیازی به بررسی مداوم شرایط حلقه وقفه در برنامه نیست. پردازنده هنگام وقفه به طور خودکار هر کاری را که انجام می دهد متوقف می کند و با کنترل کننده وقفه ارتباط میگیرد. شما فقط باید کد بنویسید تا زمانیکه وقفه رخ دهد در برنامه پاسخ دهد. استفاده از وقفه به صورت سخت افزاری بسیار ساده است و با تریگر کردن پایه دیجیتال وقفه ایجاد می‌شود.

    • با سلام
      بله با توجه به نیاز پروژه این مورد پیشنهاد می‌شود.

  • سلام برنامه موقعیت یاب خودرو نوشتم که هر۳۰ ثانیه موقعیت به یک سایت ارسال میشه، میخوام با دریافت پیامک ارسال اطلاعات متوقف و ماشین در سرعت زیر ۲۰ کیلومتر خاموش بشه، چطور این کاررو انجام بدم