در روزگاری که سیستم های دیجیتال زندگی ما را احاطه کرده اند، میکروکنترلرها به عنوان فرمانده زندگی دیجیتال شناخته می شوند. میکروکنترلرها که وظیفه دریافت ورودی، پردازش داده و ایجاد خروجی را بر عهده دارند، قابلیت های فراوانی را در خود جای می دهند. اما از آنجاییکه منابع پردازشی میکروکنترلرها محدود است، لذا برنامه نویسی این پردازنده ها بسیار حائز اهمیت است. در برنامه نویسی میکروکنترلرها استفاده بهینه از منابع حافظه، قدرت پردازنده و… بسیار کلیدی است. از طرف دیگر، از آنجاییکه اجرای برخی فرامین ممکن است نیاز به زمان زیادی داشته باشد، بنابراین جهت جلوگیری از تداخل این فرایندها با سایر قسمت ها، زمانبندی امری بسیار ضروری خواهد بود. به کمک زمانبندی اجرای فرامین می توانیم از تداخل بین اجرای دستورات جلوگیری کنیم. این مورد در رابطه با دستورات زمانبر بسیار مهم است. در این آموزش قصد داریم تا به کمک تایمرهای داخلی میکروکنترلر ATNEGA328P در برد آردوینو UNO، اجرای توابع را به صورت Interval در آوریم. در ادامه زمانبندی در آردوینو با مرجع تخصصی آردوینو به زبان فارسی، دیجی اسپارک همراه باشید.
سازوکار زمانبندی در آردوینو
همانطور که پیشتر هم گفته شد، در این پروژه قصد داریم تا به زمانبندی اجرای توابع بپردازیم. برای روشن تر شدن موضوع فرض کنید که یک تابع وظیفه چاپ عبارتی خاص را در خروجی دارد. این عبارت باید هر چند ثانیه یکبار، به عنوان مثال هر ۱۰ ثانیه یکبار در خروجی چاپ گردد. در نگاه اول شاید اجرای این پروژه ساده به نظر بیاید. چراکه به کمک تابع millis به سادگی می توان این فرایند را پیاده سازی نمود. اما اشکال این روش در اینست که تابع millis دائما باید در loop فراخوانی شود. اما در این آموزش به کمکی روشی که ارایه خواهیم داد، نیازی به فراخوانی هیچ تابعی در loop نخواهد بود. در این حالت ما تنها از تایمرهای داخلی میکروکنترلر استفاده می کنیم.
در این آموزش ما به صورت رجیستری، تایمر شماره ۰ برد آردوینو را فعال می کنیم. با فعال سازی این تایمر زمانبندی در آردوینو را در دست می گیریم. پس از این مورد فراخوانی تابع را در روال وقفه تایمر قرار داده و با سر رسیدن موعد مقرر، تابع اجرا می گردد. شاید این موضوع در ابتدا کمی گنگ به نظر برسد. در قسمت بعد به این موضوع خواهیم پرداخت.
تایمرهای داخلی Atmega328P
در قسمت پیشین به تایمرها پرداختیم. از آنجاییکه در این پروژه قصد داریم تا با برد آردوینو UNO کار کنیم، بنابراین در رابطه با پردازنده ATMEGA328P صحبت خواهیم نمود. این پردازنده که دارای ۳ تایمر داخلی است، تایمر شماره ۰ را به تابع millis اختصاص داده است. تایمرها درون میکروکنترلرها یک روال موازی را طی می کنند. زمانیکه شما یک تایمر را فعال می کنید، این واحد شروع به شمارش می کند. حال هرگاه که شمارش به انتها برسد، یک وقفه ایجاد خواهد شد. همانطور که اطلاع دارید تابع وقفه، تابعیست که در هنگام وقوع یک وقفه اجرا می شود. در مثال تایمر وقفه، مدت زمان شمارش توسط رجیسترهای تایمر تعیین می شود.
پس از اینکه وقفه تایمر اجرا شد، تابع مد نظر نیز به اجرا درخواهد آمد. بدین ترتیب هر N ثانیه یکبار می توان اجرای تابع را مشاهده نمود. در اینجا، ذکر این نکته ضروریست که روال اجرای تایمرها بر مبنای رزولوشن آن ها صورت می گیرد. در اینجا منظور از رزولوشن، توانایی شمارش تایمر از ۰ تا حد توان آن است. به عنوان مثال، میکروکنترلر ATMEGA328P که هسته پردازشی برد آردوینو محسوب می شود، دارای سه تایمر، دو تایمر ۱۶ بیتی و دیگری ۸ بیتی است. دو تایمر ۰ و ۲ هشت بیت و تایمر ۱ به عنوان ۱۶ بیتی شناخته می شود. در تایمر ۸ بیتی، شمارنده می تواند از ۰ الی ۲۵۵ و در نوع ۱۶ بیتی شمارش از ۰ الی ۶۵۵۳۵ خواهد بود. در این جا با تنظیم فرکانس شمارش و عدد شروع شمارش، می توان زمان را برای وقوع هر وقفه تنظیم نمود. بدین ترتیب می توان زمان Interval را تعیین کرد. در این جا با توجه به پارامترهای تنظیم شده، تابع وقفه تایمر هر یک میلی ثانیه یکبار اجرا می شود. این موضوع را در قسمت بعد که مربوط به کدهای پروژه است، خواهیم دید.
کدهای پروژه زمانبندی در آردوینو
پس از آشنایی با مکانیزم عملمکردی پروژه، نوبت به کدهای آن می رسد. در کد زیر هر ۱۰ ثانیه یکبار، تابع print_data اجرا می گردد. به کدهای این پروژه کمی بیشتر دقت کنید. درون تابع loop هیچ تابعی فراخوانی نشده است.
unsigned long timer_on = 0; //متغیر تنظیم زمان unsigned long set_time=10000; //متغیر جهت مقایسه زمان void setup() { pinMode(13, OUTPUT); Serial.begin(115200); set_timer(); //تابع تنظیمات رجیسترهای تایمر } void loop() { } void set_timer() { TCCR0A = (1 << WGM01); //تنظیم مد CTC تایمر OCR0A = 0xF9; //به کمک این مقدار، وقفه هر یک میلی ثانیه یکبار رخ می دهد. TIMSK0 |= (1 << OCIE0A); //تنظیم و فعال سازی تابع وقفه sei(); //فعال سازی وقفه ها TCCR0B |= (1 << CS01); //تنظیم تقسیم فرکانسی روی ۶۴ TCCR0B |= (1 << CS00); } ISR(TIMER0_COMPA_vect) { //تابع(روتین) وقفه تایمر timer_on++; //افزایش مقدار کنترلی فعال ماندن رله if(timer_on >= set_time){ timer_on=0; print_data(); } } void print_data(){ Serial.println("Interval run!"); Serial.flush(); }
اجرای پروژه Interval
پس از آپلود کد روی برد آردوینو خود، سریال مانیتور را باز کنید. در این قسمت خواهید دید که هر ده ثانیه یکبار تابع print_data اجرا می شود.
در این پروژه شما می توانید زمان اجرای پروژه را از مقدار پیشفرض هر ده ثانیه یکبار به مقدار دلخواه خود تغییر دهید. همچنین کدهای تابع print_data را می توان به کدهای مد نظر خود تبدیل کنید.
توجه: کدهای قرار گرفته در تابع print_data به هیچ عنوان نباید زمانبر باشند.
تحلیل کدهای پروژه زمانبندی در آردوینو
پس از اجرای پروژه نوبت به تحلیل کدهای آن می رسد. از آنجاییکه درون کدهای پروژه به صورت خط به خط توضیح(کامنت) درج شده، لذا جدول زیر شرح توابع پروژه را نمایش می دهد.
شرح | تابع |
تنظیم رجیسترهای تایمر | set_timer |
تابع روتین وقفه | ISR |
تابع اجرای Interval | print_data |
لوازم مورد نیاز
جمع بندی
اهمیت زمانبندی در پروژه های مختلف و حساس بر هیچ کسی پوشیده نیست. زمانی که برد شما فاقد سیستم عامل بوده و از طرف دیگر برنامه دارای Task های زمانبر است، تنظیم زمان اجرا امری کلیدی و کاربردی خواهد بود. در این آموزش به طراحی و پیاده سازی پروژه ای اقدام کردیم که در آن اجرای یک تابع به صورت Interval صورت می گیرد. جهت اجرای این پروژه کلیدی از برد آردوینو UNO با تراشه ATMEGA328P بهره برده ایم. از آنجاییکه میکروکنترلر ATMEGA328P دارای سه تایمر داخلی است، بنابراین ما از تایمر شماره ۰ جهت اجرای این پروژه استفاده نمودیم. به کمک این تایمر و در روتین وقفه، تابع مد نظر در بازه زمانی تعیین شده اجرا می گردد.
چنانچه در مراحل راه اندازی و انجام این پروژه با مشکل مواجه شدید، بدون هیچ نگرانی در انتهای همین پست، به صورت ثبت نظر سوالتان را مطرح کنید. من در سریعترین زمان ممکن پاسخ رفع مشکل شما را خواهم داد. همچنین اگر ایرادی در کدها و یا مراحل اجرایی وجود دارند میتوانید از همین طریق اطلاع رسانی کنید.