RTOS چیست ؟
هر وقت کلمهی “سیستمعامل” را میشنویم ناخودآگاه به یاد سیستمعاملهای لینوکس، مک، ویندوز، بیاسدی و دیگر سیستمعاملهای آشنا میافتیم! کار اصلی یک سیستمعامل مدیریت منابع و ایجاد هماهنگی بین اجزاء یک سیستم و برنامهها است. در سیستمعاملهای رایج، مدیریت منابع سیستم بین برنامههای در حال اجرا تقسیم میشود و تمرکز بر ایجاد تعادل بین برنامهها است. این دسته از سیستمعاملها برای اجراشدن نیاز به سختافزار قوی و میزان حافظه زیادی دارند که پیادهسازی آنها را بر روی سیستمهای مبتنی بر میکروکنترلر غیرممکن میکند.
اما نگران نباشید! سیستمعاملهای دیگری وجود دارند که برای کار بر روی میکروکنترلرها طراحی و ساختهشدهاند. ممکن است فکر کنید که “با محدودیت موجود در ساختار یک میکروکنترلر، چه منابعی وجود دارد که به مدیریت نیاز دارد؟ حجم برنامهها و تعداد سختافزارهای در دسترس اینقدر محدود است که نیاز به مدیریت خاصی ندارد.”
در پاسخ به این سؤال باید عنوان کنم که یکی از اساسیترین و کلیدیترین منابع موجود در هر سیستمی، زمان است!
شما در طراحی هر سیستمی نیاز دارید که بهصورت صحیح زمان را مدیریت کنید. اگر بعد از اعمال ورودیها، سیستم در زمان مناسب قادر به ارائه خروجی نباشد، به این سیستم نمیتوان به چشم یک سیستم کاربردی نگاه کرد. بهعنوانمثال خودرویی تمام هوشمند را فرض کنید که با سرعت 60 کیلومتر بر ساعت در حال حرکت است و مانعی را شناسایی میکند. اگر نتواند بهموقع بعد از شناسایی مانع عکسالعمل نشان داده و خودرو را متوقف کند، خودرو با مانع برخورد کرده و باعث ایجاد خسارت خواهد شد. پس همانطور که مشاهده میکنید مدیریت زمان، مخصوصاً در سیستمهای کنترلی از اهمیت بسیار بالایی برخوردار است.
مدیریت زمان با اتکا به سیستم سنتی Super Loop شاید برای برنامههای ساده و کوچک عملی باشد؛ ولی عملاً در برنامههای پیچیده غیرممکن خواهد بود. مثلاً برای پیادهسازی یک خلبان خودکار یا سیستمهای پزشکی که زمان اهمیت بالایی دارد نمیتوان از Super Loop برای کنترل برنامه استفاده کرد و سیستم بهسرعت دچار خطا خواهد شد.
برای رفع این مشکل، سیستمعاملهایی خاص طراحیشدهاند که مدیریت زمان را به بهترین نحو ممکن انجام میدهند. این دستهی خاص از سیستمعاملها real-time operating system نام دارند که بهاختصار RTOS خوانده میشوند. همانطور که از نام این سیستمعاملها بر میآید، تخصص آنها در مدیریت زمان است.
RTOS چگونه زمان را مدیریت میکند؟
در سیستمهای مبتنی بر RTOS، هر وظیفه (Task) به شکل یک Super Loop مجزا و ایزوله (به لحاظ فضای حافظه) از بقیه وظیفهها در نظر گرفته میشود و میتوان با توجه به اهمیت وظیفهی مربوطه، آنها را دستهبندی کرد. بهعنوانمثال فرض کنید قصد داریم سیستم راننده خودکار را پیادهسازی کنیم. برای شروع سیستم ما سه وظیفه اساسی خواهد داشت: تشخیص مانع، رانندگی کردن و پخش موزیک!
اگر همهی وظایف محوله به سیستم در یک اولویت قرار گیرند، قطعاً سیستم بهدرستی کار نخواهد کرد؛ چراکه هنگام پخش موزیک فراموش میکند که جاده را اسکن کرده و موانع را تشخیص دهد. اما بهراحتی میتوان اولویتها را تغییر داد: یعنی تشخیص موانع بالاترین اولویت را داشته باشد. بعدازآن رانندگی و در آخر پخش موزیک! قطعاً همه ترجیح میدهند که سالم از ماشین خود پیاده شوند تا اینکه یک موزیک را بدون اختلال گوش کرده باشند!
با توجه به تصویر فوق، وظیفه قرمز (Task 1) بالاترین اولویت را دارا خواهد بود؛ یعنی تشخیص موانع. بعدازآن رانندگی است که بارنگ آبی (Task 2) مشخصشده و در آخر پخش موزیک که بارنگ سبز (Task 3) نشان دادهشده است. همانطور که در تصویر مشاهده میکنید در طول زمان اول لازم است جاده برای تشخیص موانع اسکن شود و بعدازآن سیستم به رانندگی بپردازد و اگر زمان باقی ماند، موزیک پخش کند. برای همین است که Task3 دارای زمان یکسانی برای اجرا نیست!
از کدام RTOS استفاده کنم؟
امروزه انواع بسیار زیادی از RTOS ها در دسترس هستند؛ از RTOS های تجاری که توسط شرکتهای بزرگ، طراحی و پیادهسازی شدهاند تا RTOS های رایگان و متنبازی که توسط هزاران برنامهنویس در سراسر جهان توسعه یافتهاند. با توجه به این میزان تنوع در انتخاب، قطعاً باید معیارهای زیر را برای انتخاب درست در نظر گرفت که در ادامه دچار مشکل نشویم:
- محدودیتهای سختافزار
- نوع کار تعریفشده
- میزان حساسیت آن
در خصوص معیار اول، قطعاً شما نمیتوانید RTOS ای که برای هستههای ARM پایهریزی شده است را بر روی AVR استفاده کنید و یا برعکس. همچنین هر سیستمعاملی برای راهاندازی نیاز به یک حداقل Ram و حافظه برنامه خواهد داشت که باید آن را در نظر گرفت. بهعنوان نمونه شما نمیتوانید از ucLinux بر روی AVR بهره ببرید چراکه برای اجرای این سیستمعامل نیاز به چند ده مگابایت رم دارید؛ درصورتیکه بالاترین میزان حافظه در AVR چند ده کیلوبایت است.
مسئله مهم بعد میزان حساسیت کار است. میزان حساسیت کار سیستمی که خط تولید یک کارخانه را کنترل کند، چندان بالا نیست و میتوان از یک RTOS معمولی هم استفاده کرد. ولی اگر قصد طراحی سیستمی داشته باشید که خلبان خودکار یک وسیله پرنده باشد، قطعاً باید از RTOS خاص، با مدیریت دقیق زمان و ضریب اطمینان بسیار بالا استفاده کنید.
برای مشاهده لیست RTOS های موجود میتوانید به سایت osrtos مراجعه کنید.
ما در این مقاله قصد داریم که نحوه راهاندازی و کانفیگ یکی از پرطرفدارترین RTOS های موجود را آموزش دهیم: FREE RTOS ؛ که علاوه بر رایگان بودن متنباز هم هست و بهصورت خیلی ساده طراحیشده که قابلیت پشتیبانی از 31 نوع میکروکنترلر متفاوت را دارا باشد. پس بهترین گزینه برای خیلی از پروژهها، این RTOS است.
نصب FREE RTOS بر روی آردوینو:
برای شروع کار با FreeRTOS باید کتابخانههای لازم را بر روی ادیتور آردوینو نصب کنیم. جهت نصب ابتدا به منوی Sketch رفته و از زیر منوی Include Library گزینه Library Manager را انتخاب کنید.
بعدازآن از پنجره بازشده، واژه Free Rtos را جستجو کرده و سپس با فشردن کلید Install آن را نصب کنید.
چگونه با FREE RTOS یک پروژه ایجاد کنیم؟
ابتدا به منوی Sketch رفته و پس از آن از زیرمنوی Include Library گزینه FreeRtos را انتخاب میکنیم.
پس از انتخاب گزینه مربوطه، هدر Arduino_FreeRTOS.h به پروژه اضافه خواهد شد. دقت کنید که نصب سیستمعامل نیاز به فضای کافی دارد و در صورت نبود فضای کافی، با خطای کامپایلر هنگام کامپایل مواجه خواهید شد.
با توجه به جدول زیر میتوانید فضای موردنیاز جهت FreeRTOS و مقدار حافظه موردنیاز وقتیکه از FreeRTOS استفاده نمیکنید را در بردهای مختلف مشاهده کنید.
1 2 3 4 5 6 | // Device: loop() -> FreeRTOS | Additional Program Storage // Uno: 444 -> 7340 | 21% // Goldilocks: 502 -> 7408 | 6% // Leonardo: 3624 -> 10508 | 24% // Yun: 3618 -> 10502 | 24% // Mega: 656 -> 24108 | 9% |
همانطور که مشخص است بر روی آردوینو Uno در حالت بدون سیستمعامل، بدون هیچ برنامهای 444 بایت از حافظه کد اشغال خواهد شد و بعد از نصب سیستمعامل 7340 بایت که یعنی سیستمعامل فقط 21 درصد از فضای برنامه را اشغال کرده است.
چگونگی شروع برنامه نویسی با Free Rtos:
در برنامهی زیر ما سعی کردهایم که سادهترین برنامه ممکن را با استفاده از Free RTOS بنویسیم . درواقع کار برنامه این است که دو LED متصل به پورت های 12 و 13 را با سرعت متفاوتی به چشمک زدن وادار کند. برای این کار ما دو تسک ایجاد کردیم که یکی LED متصل به پورت 12 و دیگری LED متصل به پورت 13 را کنترل میکند.
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 66 67 68 69 | #include <Arduino_FreeRTOS.h> // define two tasks for Blink void TaskBlink_One( void *pvParameters ); void TaskBlink_Two( void *pvParameters ); // the setup function runs once when you press reset or power the board void setup() { // Now set up two tasks to run independently. xTaskCreate( TaskBlink_One , (const portCHAR *)"Blink One" // A name just for humans , 128 // Stack size , NULL , 2 // priority , NULL ); xTaskCreate( TaskBlink_Two , (const portCHAR *) "Blink Two" , 128 // This stack size can be checked & adjusted by reading Highwater , NULL , 1 // priority , NULL ); // Now the task scheduler, which takes over control of scheduling individual tasks, is automatically started. } void loop() { // Empty. Things are done in Tasks. } /*--------------------------------------------------*/ /*---------------------- Tasks ---------------------*/ /*--------------------------------------------------*/ void TaskBlink_One(void *pvParameters) // This is a task. { (void) pvParameters; // initialize digital pin 13 as an output. pinMode(13, OUTPUT); for (;;) // A Task shall never return or exit. { digitalWrite(13, HIGH); // turn the LED on (HIGH is the voltage level) vTaskDelay( 150 / portTICK_PERIOD_MS ); // wait for 150 Mili second digitalWrite(13, LOW); // turn the LED off by making the voltage LOW vTaskDelay( 150 / portTICK_PERIOD_MS ); // wait for 150 Mili second } } void TaskBlink_Two(void *pvParameters) // This is a task. { (void) pvParameters; // initialize digital pin 13 as an output. pinMode(12, OUTPUT); for (;;) // A Task shall never return or exit. { digitalWrite(12, HIGH); // turn the LED on (HIGH is the voltage level) vTaskDelay( 1000/ portTICK_PERIOD_MS ); // wait for one second digitalWrite(12, LOW); // turn the LED off by making the voltage LOW vTaskDelay( 1000/ portTICK_PERIOD_MS ); // wait for one second } } |
نحوه عملکرد Free RTOS بر روی برد آردوینو:
ممنون بابت اموزشتون من تازه شروع به تولید محتوا کردم واقعا زحمت می کشید خیلی ممنون
چطور میتونم مستقیم و بدون استفاده از آردوینو یک free rtos رو روی یک میکرو آرم نصب کنم؟
اگه ممکنه راهنمایی کنید
میتونید اموزش زیر را مشاهده کنید
https://sisoog.com/2020/07/freertos-%DB%8C%D8%A7%D8%AF-%D8%A8%DA%AF%DB%8C%D8%B1%DB%8C%D8%AF-%D9%88-%D8%A8%D9%87-%D9%85%D8%AD%DA%A9-%DA%A9%D9%85%DA%A9-%DA%A9%D9%86%DB%8C%D8%AF/
سلام استاد
سوال دارم میشه منبع کتاب رو بگید ویا کمکم کنید؟
در مورد متغیر هست متغیری که در تابع دوم استفاده میکنم بر روی تابع اول تاثیر نداره
تعیرف متغیر رو هم بالا کنار فرخوانی کتابخانه ها هم نوشتم ولی تاثیر نداشت
سلام و درود دوست عزیز
بهترین رفرنس کتابی هست که free-rtos منتشر شده و البته ظاهرا رایگان نیست ولی خوب فکر کنم برای دانلود بتونید توی اینترنت پیداش کنید.
سلام. قبلا داخل loop قسمتی داشتم که هر 60 ثانیه یک بار یه سری اطلاعات رو چک می کرد(اتصال همه سنسورها در یک پروتکل ارتباطی ) و این کار رو داخل یک while انجام داده بودم که یعنی توقف برنامه تا انتهای این حلقه، سپس از نتایج این بررسی در ادامه استفاده می کردم. سوال اینجاست چطور میشه یه task ایجاد کرد که با همون زمان بندی (60 ثانیه یا هرچی) اجرا بشه؟
و سوال دوم: آیا از نتایج داخل این task میشه در لوپ اصلی استفاده کرد؟ یعنی متغیرهای داخل این task رو میشه در loop استفاده کرد؟ و اگر در لوپ دوباره بخواهیم این متغیرهارو تغییر بدیم و مجددا در task استفاده کنیم قابل اجراست؟
سلام دوست عزیز
سوال اولتون رو درست متوجه نشدم – شما میخواید ریت اسکن ثابت باشه ؟ خوب بعد از اتمام یک دور اسکن به اندازه کافی تاخیر ایجاد کنید.
در خصوص سوال دوم – بله میشه – یک راه غیر اصولی تعریف متغیر ها به شکل گلوبال هست که مشکلات خیلی زیادی ایجاد میکنه ولی راه اصولی هم وجود داره و اون هم استفاده از mailbox هست.
یعنی اطلاعاتی که باید در تسک دیگه ای قابل دسترسی باشند رو برای اون تسک با تعریف یک میل باکس ارسال کنید.
سلام خسته نباشید
من میتونم از این کتابخونه برای esp8266 هم استفاده کنم؟؟؟
تا اونجایی که من اطلاع دارم پلتفرم آردوینو برای esp8266 از سیستم عامل استفاده نمیکنه !
و شما باید با sdk سیستم عامل دار خود شرکت استفاده کنید.
با سلام. یکی از بهترین، مفیدترین و کاربردی ترین مقالاتی بود که تا امروز خوندم. ازتون واقعا ممنونم.
خواهش میکنم دوست عزیز 🙂
با سلام.بابت مطلب مفیدتون ممنونم.فقط کمی در رابطه با stack size و اگر میشه یه کم برنامرو تحلیل کنید.کتشکرم
مفهوم استک ، همیشه مقداری گیج کننده بوده ، اما استک چست ؟ بذارید با یه مثال ساده مساله رو بازش کنم
اعمال افزودن و حذف در Stack را میتوان مشابه وضعیت ظروف کثیفی در نظر گرفت که به منظور شسته شدن در آشپزخانه روی یکدیگر انباشته شدهاند، عنصر (ظرف کثیف) جدید بر روی عناصر قبلی قرار میگیرد و هنگام حذف یک عنصر (برداشتن ظرف برای شسته شدن)، جدیدترین عنصری که به پشته اضافه شده است زودتر حذف میشود. از این رو به پشته، ساختمان دادهی LIFO نیز گفته میشود. دو کاربرد رایج از پشتهها در برنامه عبارتند از ارزیابی عبارات و فراخوانی توابع. به عنوان نمونهای ملموس و ساده میتوان گفت هنگام فراخوانی تابع، نام تابع همراه با پارامترهای آن به عنوان عنصری جدید وارد یک پشته میشوند. در صورتی که در این تابع، تابعی دیگر فراخوانی شود تابع دوم نیز به همین صورت وارد پشته میشود. به محض اینکه اجرای دستورات تابع دوم به پایان میرسد عمل Pop از پشته انجام میشود و تابع دوم از آن خارج میشود. به این ترتیب تابع کنونی که در حال اجراست همیشه در بالای این پشته قرار میگیرد.
میزان Stack هر تسک در هنگام تعریف آن قابل تنظیم است. ما در مثال ارائه شده به هر تسک 128 ورد (128*4 بایت) حافظه Stack اختصاص دادیم. که البته این مقادی با توجه به میزان فراخوانی توابع تو در تو و میزان حافظه مورد استفاده هر تابع متغیر باشد.
البته نباید محدودیت حافظه میکروکنترلر را فراموش کرد.
سوال اول اینکه هرچی مقدار استک رو برای یه تسک بیشتر کنیم از حافظه ی فلش میکرومون بیشتر مصرف میشه؟
دوم اینکه من تو برنامم چنتا تسک مشخص کردم ک هرکدوم ب ترتیب کار
خوندن کلید های ورودی
نمایش روی ال سی دی
محاسبات زمانی (ثانیه رو کم کن صفر شد دقیقه رو کم کن و…)
خوندن مقدار سنسور رطوبت و دما
رو انجام میدن
حالا وقتی میخوام تسک جدید اضافه کنم برنامه اصلا ران نمیشه یعنی با تست های مختلف متوجه شدم حتی وارد ووید لوپ هم نمیشه چ برسه ب تسک ها
حال با تغیر استک میتونم بهترش کنم؟چطور؟
لطفا راجب استک مثال بزنید و سعی کنبد کمتر کلمات تصصی بکار ببرید تا دوستان بتونن بهتر استفاده کنن
اول – نه استک از حافظه RAM استفاده میکنه
دوم – میکروی شما چی هست و چند تا تسک دارید ؟ ؛ میدونید که تعریف هر تسک از حافظه رم مقداری رو بر میداره و اگر تسک ها تعدادشون بقدری زیاد بشه که از رم میکرو ؛ رم بیشتری لازم داشته بشه ؛ سیستم عامل اجرا نمیشه چون استک آورفلو اتفاق می افته.
میکروی من اتمگا 328 هست (آردوینو یونو)
با اردوینو 2560 امتحان کردم مشکلی پیش نمیاد
چطور میشه محاسبه کرد چه مقدار فضا اشغال میکنه تا بشه فهمید چه میکرویی مناسبه؟
چطور میشه یه برنامه شبیه همین سیستم عاما فری ار تی او اس نوشت که کاراهارو موازی انجام بده؟
خوب قطعا مشکل شما مشکل مقدار RAM هست ؛ این که مقدار مناسب رو برای استک پیدا کنید ؛ کاملا تجربی است ؛ البته خود FreeRTOS امکانی داره که پر شدن استک رو گزارش میده و بر اون اساس میتونید مقدار استک رو افزایش بدید !
آموزش سیستم عامل رو توی دستور کار داریم و به زودی آموزشش رو شروع خواهیم کرد.
والا این تسک های شما رو من روی STM32F103ZET6 می خواستم تست کنم، خطا داد چه برسه به AVR بد بخت.
این کد برای آردوینو مدل های Nano ؛ Uno تست شده و به خوبی کار میکنند ؛ شاید مشکل جای دیگه ای باشه !
شما از آردوینو استفاده میکنید ؟
میشه توضیح بفرمایید که اولویت هارو چطور تغیر بدیم و ب چ نحوی کار میکنه؟من این کتابخونه free rtos رو نصب کردم چارتا task مشخص کردم میخوام بدونم چ فرقی داره اگه priority هارو دستکاری کنم؟
تو همون قسمت Setup که تسک ها تعریف میشن میشه الویت ها رو تعیین کرد. که الان مقدار 1 و 2 رو دارن ، هرچی عدد بیشتر بشه اولویت اون تسک هم بیشتر میشه
اگر منظورتون تغییر در حین اجراست میتونید با تابع vTaskPrioritySet اولویت تسک مورد نظرتون رو تغییر بدید.