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

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

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

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

 


وقفه Interrupt


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

 


سوال خیلی مهم


چگونه یک پردازنده اطلاعات مورد نیاز را از سیستم‌های خارجی متصل به خود دریافت می‌کند؟

 

 

 

 

 


PULLING


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

 


INTERRUPT


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

 


مثال حقیقی


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

 

 


تولید وقفه


وقفه از چندین منبع می‌تواند تولید شود.

  •  وقفه تایمر در آردوینو
  • وقفه های خارجی از پایه های وقفه خارجی
  • وقفه تغییر پین

 


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


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

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

  • تابع 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 بنویسیم و سپس خلاف آن را در حلقه برنامه تعریف کنیم.
      بله وقفه به صورت داخلی و خارجی وجود دارد.

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

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

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

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

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

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

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

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

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

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

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

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

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

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