p><!--StartFragment
تعریف وقفه
وقفه پیامی است که از طرف یک وسیله جانبی تولید می شود تا از CPU درخواست سرویس کند(به زبون ساده: به CPU بگه به منم توجه کن باهات کار دارم). CPU با در یافت یک سیگنال وقفه کارهای جاری را متوقف کرده و به وسیله مورد نظر سرویس می دهد. درست شبیه زمانی که فردی در حال انجام کارهای روزمره است و با زنگ خوردن تلفن کارها را رها کرده تا به تلفن پاسخ دهد. در این مثال فرد مورد نظر نقش CPU، کارهای روزمره نقش برنامه اصلی، تلفن نقش وسیله جانبی و صدای زنگ آن نقش سیگنال وقفه را دارند.
مزیت استفاده از وقفه
به دو روش می توان به وسایل جانبی سرویس دهی کرد:
۱) سرکشی دوره ای یا polling:
سرکشی دوره ای به این صورت است که در خلال برنامه بیت های پرچم وسیله جانبی جهت آگاهی از وضعیت آن، توسط cpu بررسی می شود و اگر آن وسیله نیاز به سرویس دهی داشت این کار توسط cpu انجام می شود. در این روش cpu باید به طور مداوم به این وسایل سرکشی کند که این کار باعث کندی برنامه و از دست رفتن زمان برای بررسی وسایل دیگر می شود.
۲) استفاده از وقفه یا interrupt:
در روش وقفه، cpu در حال اجرای روال عادی برنامه است. اما زمانی که یک وسیله جانبی نیاز به توجه cpu دارد با ارسال سیگنال وقفه باعث می شود تا cpu روال عادی برنامه را متوقف کرده و به آن توجه کند. این روش باعث می شود تا زمان cpu برای بررسی رخ دادن یک وضعیت تلف نشود.
انواع وقفه در AVR
معمولا در میکرو کنترلرها وقفهها به دوسته کلی تقسیم می شوند:
- وقفههای خارجی: وقفههایی هستند که منبع آنها خارج از میکرو قرار دارد و با ارسال سیگنالی به پایه های مخصوصی در میکروکنترلر، وقفه ایجاد می کنند.
- وقفههای داخلی: همانطور که از اسمش پیداست این وقفهها سیگنال هایی هستند که داخل میکرو ایجاد می شوند. اکثر این سیگنال ها از وسایل جانبی خود میکرو مانند تایمر/کانتر ها ، ADC و … تولید می شوند. این سیگنال ها همان درخواست سرویس از CPU می باشند.
بردار وقفه
هر وقفهای در AVR خانه ای را در ROM (حافظه فلش) به خود اختصاص می دهد. زمانی که وقفه ایجاد می شود بسته به نوع وقفه، CPU بعد از اجراکردن دستور جاری به آن خانه مراجعه می کند. این خانه محتوی آدرس برنامهای است که به وقفه سرویس می دهد. به مجموعه این خانه ها که هرکدام مربوط به یک نوع وقفه هستند جدول بردار وقفه و به برنامه ای که به وقفه سرویس می دهد روال سرویس وقفه(ISR) میگویند. در جدول زیر می توانید آدرس وقفههای مختلف در ATmega32 را مشاهده کنید:
p style="text-align: justify;"><!--StartFragmentimg class="aligncenter wp-image-6106 size-full tie-appear" src="http://microlearn.ir/wp-content/uploads/2015/10/int-table-w.png" alt="" width="541" height="1054" /><!--EndFragment p style="text-align: justify;"><!--StartFragment
فعال کردن پاسخ گویی به وقفهها
وقفهها در AVR به صورت پیش فرض غیر فعال هستند.یعنی با روشن شدن میکرو هیچ وقفهای کار نمی کند و باید به وسیله برنامه نویسی، وقفههایی را که میخواهیم ایجاد شوند فعال کنیم. در AVR معمولا هر وسیلهای به طور اختصاصی دارای بیتی به نام بیت فعال ساز وقفه می باشد. با یک شدن این بیت اجازه پاسخ گویی به وقفهی مربوط به آن وسیله فعال و با صفر شدن بیت مذکور اجازه پاسخ گویی به وقفه غیر فعال می شود. مکان این بیت برای هروقفهای در ثبات خاصی از ثبات های I/O می باشد. اگر نیاز به یادآوری رجیسترهای I/O دارید مقاله سازماندهی حافظه در AVR را مطالعه کنید.
اگر از مباحث مربوط به ثبات وضعیت به یاد داشته باشید یکی از بیت های این ثبات “I” یا فعال ساز وقفهی عمومی (Interrupt Enable) نام دارد. به وسیله ی این بیت می توان کلیه وقفهها را فعال ویا غیرفعال کرد. به این صورت که اگر این بیت را یک کنیم وقفهها فعال و اگر آن را صفر کنیم وقفهها غیرفعال می شوند. با صفر شدن این بیت حتی اگر بیت فعال ساز وقفه برای وسیله ای یک باشد، وقفه ایجاد نمی شود. پس برای رخ دادن یک وقفه لازم است بیت فعال ساز وقفهی عمومی و بیت فعال ساز وقفهی اختصاصی، همزمان یک باشند. درواقع بیت فعال ساز وقفهی عمومی با بیت های فعال ساز وقفههای اختصاصی AND شده است.
در کد زیر می توانید نحوه فعال و غیر فعال کردن بیت I در کدویژن را مشاهده کنید:
#asm("sei") /*فعال کردن وقفه عمومی*/
#asm("cli") /*غیر فعال کردن وقفه عمومی*/
p><!--StartFragment
مراحل اجرای یک وقفه
- هنگامی که یک وقفه اتفاق می افتد cpu دستور در حال اجرا را به پایان برده و آدرس دستور بعدی (این آدرس درون رجیستر pc یا program counter قرار دارد) را بر روی پشته ذخیره می کند.
- بسته به نوع وقفه، میکروکنترلر به خانه ثابتی از حافظه فلش واقع در جدول بردار وقفه پرش می کند.
- در این خانه، آدرس روال سرویس وقفه یا ISR وجود دارد و میکرو با پرش به این آدرس، شروع به اجرای دستورات موجود در روال سرویس وقفه می کند تا به دستور آخر روال یعنی RETI برسد.
- این دستور شمارنده برنامه یا pc را با آدرس دستوری که در مرحله یک در پشته ذخیره شده بود بارگذاری می کند. این کار با برداشتن دو بایت بالای پشته و قرار دادن آن در PC انجام می شود. سپس میکرو شروع به اجرای دستورات از این آدرس می کند.
این موضوع اهمیت مدیریت پشته را در حین نوشتن روال سرویس وقفه برای ما آشکار می کند. بهتر است در ابتدای هر روال سرویس وقفهای محتویات ثبات های همه منظوره ای را که می خواهیم در این روال استفاده کنیم به وسیله دستور push در پشته ذخیره شده و در آخر روال به وسیله دستور pop از پشته بازیابی شود. به این کار ذخیره سازی محتوا گویند. این مورد به طور کلی در مورد پورت ها و ثبات هایی که نیاز است محتوایشان ذخیره شود صدق می کند. می توان به جای پشته از هر فضایی در RAM برای این کار استفاده کرد. برای چگونگی استفاده از دستورات push و pop می توانید این مقاله را مطالعه کنید.
روال سرویس وقفه برنامه ای است که توسط برنامه نویس نوشته می شود تا عملیاتی را با ایجاد وقفه انجام دهد. اما چرا این برنامه را در آدرسی که مربوط به بردار وقفه است نمی نویسیم؟
آدرس های بردار وقفه فقط ۲ بایت فضا دارند. پس باید ISR را در محل دیگری نوشت و آدرس آن را در محل بردار وقفه قرار داد.
مواردی که در مورد پشته و دستورات push و pop و نوشتن ISR در آدرسی دیگر گفته شد مخصوص زبان اسمبلی می باشد. اما در زبان C کامپایلر این کارها را مدیریت می کند و نیازی به نگرانی برنامه نویس وجود ندارد. تنها کافی است یک تابع برای روال وقفه نوشته شود.
شاید بپرسید در صورتی که در طول پردازش روال سرویس وقفهای، وقفهی دیگری رخ دهد چه اتفاقی می افتد؟
در جواب این سوال باید بگویم که این بستگی به برنامه نویس و دستورات موجود در روال سرویس وقفه جاری دارد. همان طور که گفتم در صورت ایجاد وقفه، AVR به طور اتوماتیک بیت فعال ساز وقفهی عمومی را پاک می کند. اگر ما بخواهیم در حین سرویس دهی به یک وقفه، وقفهی دیگری در صورت ایجاد سرویس دهی شود باید در روال سرویس وقفهی قبلی بیت فعال ساز وقفه عمومی را یک کنیم. اما باید چنین کاری با احتیاط انجام شود چون ممکن است باعث گیر کردن برنامه درون یک وقفه شود. مثلا اگر یک وقفهی خارجی حساس به سطح فعال باشد و بیت I در حالی که هنوز پایه وقفه در حالت تحریک است، یک شود باعث می شود تا به طور بی نهایت وارد ISR شده و سرریز پشته و اتفاقات غیر قابل پیش بینی رخ دهد. در ادامه درباره وقفهی خارجی و حساس به سطح صحبت می کنیم.
لازم به ذکر است که بیت وقفهی اختصاصی یک وسیله جانبی در صورت درخواست وقفه توسط آن وسیله و پرش به جدول بردار وقفه برخلاف بیت فعال ساز وقفهی عمومی هیچ تغییری نمی کند.
اولویت وقفه
ممکن است این سوال در ذهن شما ایجاد شود که اگر دو وقفه به طور هم زمان رخ دهند به کدام وقفه پاسخ داده می شود؟ در جواب باید بگوییم که در جدول بردار وقفه، هر وقفهای که آدرس کمتری دارد(هر چه در جدول بالاتر باشد) اولویت بیشتری هم در پاسخگویی خواهد داشت. پس در صورت وقوع چنین حالتی آن وقفهای که اولویت بالاتری دارد پاسخ داده خواهد شد.
وقفههای خارجی
بعد از توضیح موارد کلی و اصول وقفهها حال وقفههای خارجی را به صورت خاص بررسی می کنیم.
یک وقفهی خارجی در اثر سیگنالی از یک وسیله جانبی خارج از میکرو که وارد پایه خاصی از میکرو می شود اتفاق می افتد. برای مثال در میکروکنترل ATMEGA32 سه پایه برای وقفهی خارجی با نام های INT0 ,INT1 ,INT2 وجود دارد که در شکل زیر مشخص شده است:
p><!--EndFragment <!--StartFragment-->img class="aligncenter wp-image-6108 size-full tie-appear" src="http://microlearn.ir/wp-content/uploads/2015/10/m32.gif" alt="" width="305" height="336" /><!--EndFragment
p><!--StartFragment
رجیستر GICR
نام این رجیستر از Global interrupt control register گرفته شده است. با استفاده از بیت های شماره ۵، ۶ و ۷ که در تصویر زیر قابل مشاهده است می تواند قابلیت پاسخ گویی به وقفه های خارجی ۰ تا ۲ را فعال کرد. INT0 برای وقفه خارجی صفر استفاده می شود و سایر وقفه های خارجی نیز به همین ترتیب فعال می شوند. البته باید توسط رجیستر SREG بیت فعال ساز وقفه عمومی فعال شود و توسط رجیستر MCUCR حساسیت به لبه یا سطح مشخص شود که در ادامه توضیح داده می شود.
بیت های ۲ تا ۴ رزرو هستند و از آن ها استفاده ای نمی شود. بیت ۰ و ۱ نیز برای تغییر مکان بردارهای وقفه مورد استفاده قرار می گیرد که خارج از بحث جلسه امروز است.
p><!--EndFragment
p><!--StartFragment
رجیستر MCUCR
نام این رجیستر از MCU Control register گرفته شده است.
همان طور که گفتیم، وقفه های سخت افزاری یا حساس به سطح هستند یا حساس به لبه. با تنظیم بیت های رجیستر MCUCR می توان نوع حساسیت را تعیین کرد. برای INT0 از بیت های ۰ , ۱ و برای INT1 از بیت های ۲ و ۳ استفاده می شود.
p><!--EndFragment
p><!--StartFragmentدرتصویر زیر نحوه تنظیم حساسیت به سطح یا لبه برای INT0 توضیح داده شده است:<!--EndFragment-->
p><!--StartFragmentimg class="aligncenter wp-image-6107 size-large tie-appear" src="http://microlearn.ir/wp-content/uploads/2015/10/isc-w-1024x513.png" alt="" width="618" height="310" /><!--EndFragment p><!--StartFragment
تمام موارد گفته شده درباره INT0 در مورد INT1 هم صدق می کند.برای تنظیم نوع حساسیت INT1کافیست در جدول بالا بیت های ISC10 و ISC11 را به همین شیوه برای INT1 اعمال کنید.
رجیستر MCUCSR
نام این رجیستر از MCU Control and Status Register گرفته شده است.
نحوه حساسیت وقفهی خارجی دو یا INT2 در ATMEGA32 توسط بیت ISC2 در ثبات MCUCSR تنظیم می شود. وقفهی خارجی دو فقط در دو حالت تنطیم می شود.
- اگر ISC2 صفر باشد INT2 حساس به لبه پایین رونده می شود.
- اگر ISC2 یک باشد INT2 حساس به لبه بالا رونده می شود.
p><!--StartFragment
رجیستر GIFR
نام این رجیستر از Global interrupt flag register گرفته شده است. این رجیستر پرچم وقوع وقفههای خارجی است. هر وقت یک وقفهی خارجی رخ دهد، پرچم متناظر با آن یک می شود. همان طور که در شکل زیر می بینید، پرچم متناظر با وقفهی خارجی صفر INTF0 است و برای وقفههای یک و دو نیز به همین ترتیب.
اما نکته ی مهمی که در رابطه با این پرچم ها وجود دارد صفر شدن آن ها است. اگر پاسخ گویی به وقفه را فعال کرده باشیم، با وقوع وقفه و پرش به آدرس بردار وقفه، پرچم متناظر هم صفر می شود. اما اگر به صورت نرم افزاری بخواهیم آن را صفر کنیم باید روی آن ۱ نوشته شود. این قانون برای تمامی پرچم های AVR صادق است.
p style="text-align: justify;"><!--StartFragment
مثال:
فرض کنید در میکروکنترلر ATmega32 پایه INT0 به یک کلید فشاری متصل است. برنامه ای بنویسید که با فشرده شدن کلید فشاری، LED متصل به پایه صفر PORTA روشن شود و در صورت رها شدن کلید فشاری LED خاموش شود.
زبان C در کدویژن:
p style="text-align: justify;"><!--StartFragment
#include
interrupt[EXT_INT0]voidext_int0_isr(void)//روال سرویس وقفه خارجی ?
{
PORTA.0=1;//روشن کردن ال ای دی
}
voidmain(void)
{
DDRA.0=1;//تنظیم پایه صفر پورت به عنوان خروجی
PORTA.0=0;
PORTD.2=1;//فعال کردن مقاومت بالا کش پایه وقفه خارجی صفر
GICR=(0<<INT1)|(1<<INT0)|(0<<INT2);//فعال کردن بیت اختصاصی وقفه خارجی ?
MCUCR=(0<<ISC11)|(0<<ISC10)|(0<<ISC01)|(0<<ISC00);//تنطیم در سطح پایین
#asm("sei");//فعال کردن وقفه عمومی
while(1)
{
PORTA.0=0;//خاموش کردن ال ای دی
}
}
div class="crayon-line" dir="rtl" style="text-align: right;">
<!--StartFragment
- در این برنامه از وقفهی خارجی کمک گرفته شده است.
- در خط یک فایل هدر ATmega32 به برنامه include شده است.
- در خط دو تابع وقفهی خارجی صفر تعریف شده است. دقت کنید که نوشتن کلمه کلیدی interrupt و شماره بردار وقفه در کدویژن الزامی است ولی نام تابع دلخواه است.
- شماره بردار وقفه، را می توان به صورت عدد یا کلمات کلیدی تعریف شده در کدویژن (EXT_INT0) نوشت. که برای خوانایی بیشتر برنامه استفاده از کلمات کلیدی توصیه می شود.
- درون این تابع فقط یک دستور برای روشن کردن پین صفر از پورت A وجود دارد.
- در خط هفت تابع main تعریف شده است.
- در خط ۹ تا ۱۱ پورت ها پیکربندی شده اند.
- در خط دوازده با استفاده از رجیستر GICR پاسخ گویی به وقفهی خارجی صفر فعال شده است.
- در خط سیزده این وقفه به سطح صفر حساس شده است.
- در خط چهارده وقفهی عمومی فعال شده است. دقت کنید که استفاده از asm# برای نوشتن دستورات اسمبلی است.
- دستور sei یک دستور اسمبلی برای یک کردن بیت I در SREG است.
- در خط پانزده حلقه بینهایت تعریف شده که شامل یک دستور برای خاموش کردن پین صفر از پورت A می باشد.
مطالب این بخش برگرفته از میکرولرن میباشد.
span class="crayon-sy"><!--EndFragment
p style="text-align: justify;"><!--EndFragment p style="text-align: justify;"><!--EndFragment p><!--EndFragment p><!--EndFragment