آموزش AVR قسمت ششم
سلام به همه.
تو آموزش قبلی با مفهوم مقاومت های پول آپ و پول دان آشنا شدیم ، توی اون آموزش ها از روش polling یا سرکشی استفاده کردیم که زیاد خوشایند نیست و تمام CPU رو درگیر خودش میکنه.
در این قسمت از روش interrupt استفاده میکنیم.خیلی کم و مفید در مورد اینتراپت یا وقفه:
وقتی CPU داره کار خودش رو انجام میده ، اگر وقفه ای اتفاق بیافته CPU کار خودش رو رها میکنه و به کاری که ما تعریف کردیم توی تابع وقفه میپردازه .
وقتی کار روی تابع وقفه تموم شد ، دوباره برنامه رو از همون جایی که ول کرده بود ادامه میده.
توی این پست نحوه کار با وقفه خارجی رو بررسی میکنیم.
(توی octacore.ir زیاد در مورد مفاهیم پایه توضیح نمیدم چون اینا توی سایت های داخلی خیلی خیلی زیاد هستند ، هدف فقط کدنویسی توی Atmel Studio است. اگر نیاز به توضیح اضافه دارید توی نظرات بگید.)
خب ، برای آشنایی با نحوه کار با وقفه یه پروژه به این صورت داریم :
یه برنامه مینویسیم که هر 4 ثانیه یکبار یک LED رو روشن و خاموش کنه ، وقتی که کلید زده شد و وقفه اتفاق افتاد ، یه دونه به شماره counter اضافه کنه و با فرمولی که توی ماکرو نوشته شده ، مقدار counter رو تبدیل به میلی ثانیه کنه و میزان تاخیر در روشن و خاموش شدن led رو کنترل کنه.
برای تنظیم رجیسترهای اینتراپت باید به دیتاشیت مراجعه کنیم.
اینجا با رجیسترهای Mega8 کار داریم ، ممکنه این رجیسترها برای میکروهای دیگه فرق داشته باشه (فقط اسمشون ) از تو دیتاشیت میتونید پیدا کنید و اونا رو بنویسید.
بعد از اضافه کردن کتابخونه avr/interrupt.h میریم سراغ رجیستر اول ، GICR :
این رجیستر اینتراپت خارجی رو فعال میکنه ، من اینتراپت خارجی روی پایه PORTD3 رو فعال کردم که اسمش INT1 هست (PINOUT میکرو هم همینو نوشته).
با این کد اینتراپت این پایه فعال میشه:
1 |
GICR |=(1<<INT1); |
رجیستر دوم که مربوط به نحوه تولید وقفه است MCUCR :
با فرض اینکه کلید PULL-UP شده:
اولین گزینه از جدول ، وقتی که کلید پایین نگه داشته شده وقفه تولید میکنه.(سطح منطقی 0 )
دومین گزینه:هر گونه تغییر در کلید ، وقفه تولید میکنه.
گزینه سوم ، گزینه مورد علاقه من : وقتی که کلید پایین زده شد ، وقفه تولید میکنه (لبه پایین رونده تغییرات ولتاژ ) ، (یعنی فقط در لحظه خیلی کوتاهی که کلید به سمت پایین حرکت میکنه وقفه تولید میشه)
گزینه آخر که قابل حدس زدن هست و نمیگم !!!
من کدهای مربوط به اینتراپت خارجی رو داخل این تابع ریختم:
1 2 3 4 5 6 |
void init_int1() { GICR |=(1<<INT1); //فعال کردن وقفه 1 خارجی MCUCR|=(1<<ISC11); //حساس به لبه پایین رونده sei(); // فعال کردن کلی اینتراپت } |
نکته : حتما اینتراپت کلی رو فعال کنید.
خب ما تونستیم اینتراپت خارجی رو تنظیم کنیم.
برای اینکه اینتراپت رخ داد و یک سری کار های مد نظر ما انجام بشه از Interrupt Service Routine استفاده میکنیم .
این تابع با توابع معمولی فرق میکنه و هر موقع اینتراپت رخ داد اجرا میشه.
1 2 3 4 5 6 7 8 9 10 11 |
ISR (INT1_vect) { _delay_ms(1); //تاخیر برای نادیده گرفتن نویز کلید if (bit_is_clear(PIND , 3)) // اگر همچنان کلید پایین نگه داشته شده { counter++; // اضافه کردن یک واحد به شمارنده counter &=0b00000111; // محدود کردن شمارنده از 0 تا 7 Delay=LED_DELAY(counter); // عوض کردن مقدار تاخیر روشن و خاموش شدن ال ای دی } } |
توضیحات داخل برنامه هست و فقط در مورد INT1_vect توضیح میدم:
این دستور به تابع اینتراپت میگه که هر وقت اینتراپت 1 اتفاق افتاد ، اجرا بشه.
برای اینتراپت 0 از INT0_vect استفاده میکنیم.
توی avr بخش ها مختلفی اینتراپت تولید میکنن که در بخش های دیگه به اونا اشاره میکنیم.
اجرای برنامه:
وقتی برنامه اجرا شد ، در ابتدا LED هر 4 ثانیه یکبار روشن و خاموش میشه.وقتی که کلید زده شد ، وقفه رخ میده و مقدار کانتر 1 میشه ، و مقدار Delay 1000 میلی ثانیه میشه.
اگر کلید همینطور زده بشه و مقدار counter به نهایت خودش که 7 هست ، برسه ، مقدار تاخیر به 4000 میلی ثانیه میرسه و اگر یکبار دیگه کلید زده بشه ، مقدار کانتر 0 شده و مقدار تاخیر 500 میلی ثانیه میشه.
برنامه کلی:
نکته: این برنامه تست شده و هیچ ایرادی نداره!!! سخت افزار من ایراد داشت و خوب کار نمیکرد که با درست کردن سخت افزار ، کل برنامه به خوبی کار میکنه.(دیباگ کردنش 3 ساعت طول کشید!!!)
تمام توضیحات داخل برنامه هست.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
/* * avr-tutorial6.c * * Created: 7/31/2018 9:10:29 AM * Author : Mahdi OCTACORE.IR ADMIN */ #ifndef F_CPU #define F_CPU 8000000UL #endif #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #include <avr/sfr_defs.h> // پین 0 از پرت بی ، برای روشن و خاموش کردن ال ای دی #define LED_DDR DDRB #define LED_PORT PORTB #define LED PORTB0 //تعریف ماکرو برای تبدیل عدد به میلی ثانیه #define LED_DELAY(A) ((A+1)*500) void init_int1(); void delay_ms(double milisec); volatile uint16_t Delay=4000; // تاخیر بر حسب میلی ثانیه volatile uint8_t counter; int main(void) { PORTD |= (1<<3); //فعال کردن پول آپ پین 3 از پرت دی ، برای اتصال کلید LED_DDR |= (1<<LED); init_int1(); while (1) { LED_PORT ^= (1<<LED); delay_ms(Delay); } } void init_int1() { GICR |=(1<<INT1); //فعال کردن وقفه 1 خارجی MCUCR|=(1<<ISC11); //حساس به لبه پایین رونده sei(); // فعال کردن کلی اینتراپت } ISR (INT1_vect) { _delay_ms(1); //تاخیر برای نادیده گرفتن نویز کلید if (bit_is_clear(PIND , 3)) // اگر همچنان کلید پایین نگه داشته شده { counter++; // اضافه کردن یک واحد به شمارنده counter &=0b00000111; // محدود کردن شمارنده از 0 تا 7 Delay=LED_DELAY(counter); // عوض کردن مقدار تاخیر روشن و خاموش شدن ال ای دی } } void delay_ms(double milisec) { while (milisec--) { _delay_ms(1); } } |
چند نکته :
برای تاخیر (تابع Delay ) از متغیر فرار استفاده شده ، برای اینکه کامپایلر و CPU متوجه بشن که در هر زمان امکان تغییر کردن این متغییر وجود داره و مقدار تغییر داده شده رو توی بخش های مد نظر استفاده کنن.
برای تبدیل مقدار کانتر به میلی ثانیه از ماکرو استفاده شده که حجم برنامه کمتر بشه و سریعتر اجرا بشه.
حتما مقاومت پول آپ پایه مربوط به اینتراپت رو فعال کنید.
چون توابع کتابخوانه util/delay.h مقدارهای متغیر برای تاخیر رو قبول نمیکنن مجبوریم خودمون تابع تاخیر رو بنویسیم.
کل برنامه نوشته شده رو میتونید از جعبه دانلود دریافت کنید.
خدا قوت…
این آموزش هم تموم شد و اگر فکر میکنید که نکته ای جا افتاده بگید تا اضافه کنم .
موفق باشید…

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