وقفه در سیستم های Embedded نقش بسیار مهمی دارند. سیستمهای بلادرنگ باید سریعا پاسخ وقفههای گوناگونی که از محیط خارج و داخل میکروکنترلر نشأت میگیرند را فراهم کند. برای مثال سیستم آسانسور را در نظر بگیرید. سیستم باز شدن کیسه هوای خودرو را در نظر بگیرید که حتی اندکی تاخیر میتواند منجر به تلفات جبران ناپذیری شود. در این بخش نحوه مدیریت و کنترل وقفه در با استفاده از سیستم عامل FreeRTOS بررسی شده است.
سیستم عامل FreeRTOS هیچگونه محدودیتی جهت کنترل و مدیریت وقفه ها به طراح سیستم تحمیل نمیکند. در عوض امکاناتی را فراهم میکند که طراح با استفاده از آن براحتی طرح خود را پیاده سازی کند.
فهرست مطالب
سخت افزار مورد نیاز
- هر یک از میکروکنترلرهای 32 بیتی شرکت ST. در این پروژه از بورد STM32F4DISCOVERY استفاده شده است.
نرم افزار مورد نیاز
- stm32cubemx
- یک نرم افزار برای کامپایل و پروگرم کردن میکروکنترلر. در این پروژه از بورد IAR استفاده شده است.
تفاوت اولویت تسک با وقفه
قبل از هر چیز ضروریست تا تفاوت اولویت تسک با اولویت وقفه بیان شود. تسک کلا ماهیت نرم افزاری دارد و ارتباطی به سخت افزار ندارد و اولویت آن بصورت نرم افزاری تنظیم میشود. ولی وقفه ماهیت سخت افزاری دارد. تسکها فقط زمانی اجرا میشوند که هیچ روتین وقفهای در حال اجرا نباشد. بعبارت دیگر وقفه حتی با پایین ترین اولویت بر تسک با بالاترین اولویت مقدم است و هیچ تسکی نمیتواند یک وقفه را Preempt کند.
استفاده از توابع سیستم عامل FreeRTOS در روتین وقفه
برخی از توابعی که FreeRTOS فراهم کرده است در روتین وقفه معتبر نیستند. از جمله این توابع میتوان به توابعی که تسک را به حالت کاری میبرند، اشاره نمود. چرا که بلافاصله پس از روی دادن وقفه، کلیه تسکها متوقف شده و روتین وقفه اجرا میشود. FreeRTOS برای جلوگیری از خطاهای ممکن، یک API دیگر که فقط در داخل روتین وقفه قابل استفاده هستند فراهم نموده است. نام همه این توابع “FromISR” خاتمه مییابد. بعنوان مثال برای نوشتن داده در Queue در روتین وقفه از تابع xQueueSendFromISR باید استفاده شود.
پارامتر xHigherPriorityTaskWoken
با مقایسه توابع مختص ISR با توابع متناظر آن مشاهده میشود که توابع مخصوص ISR دارای پارامتر خاصی به نام xHigherPriorityTaskWoken هستند. در این قسمت به بررسی این پارامتر پرداخته شده است.
برخی مواقع ممکن است که پس از رخداد یک وقفه نیاز به Context Switching باشد. در این حالت تسکی که بعد از اتمام روتین وقفه اجرا میشود با تسکی که قبل از رخداد یکسان نیست.
با فراخوانی برخی از توابع FreeRTOS ممکن است یک تسک از حالت Block به حالت کاری Ready برود. بعنوان مثال با فراخوانی تابع xQueueSendToBack تسک دیگری که منتظر دریافت داده بوده است به حالت کاری Ready میرود. در صورتی که تابع موردنظر در داخل یک تسک فراخوانی شود و مقدار configUSE_PREEMPTION یک تنظیم شده باشد، بلافاصله تسک Context Switching اجرا میشود و تسک با اولویت از حالت کاری Ready به Active میرود. ولی اگر تابع موردنظر در روتین وقفه فراخوانی شود، Context Switching بصورت خودکار انجام نمیشود. در این حالت میتوان یک متغیر را استفاده کرد تا در روتین وقفه تنظیم شود و بعدا در برنامه این متغیر بررسی شود و در صورت نیاز Context Switching انجام شود. پارامتر xHigherPriorityTaskWoken برای این منظور در نظر گرفته شده است. لذا در صورتیکه نیاز به Context Switching باشد باید مقدار xHigherPriorityTaskWoken را pdTRUE تنظیم کرد.
ماکرو portYIELD_FROM_ISR
ماکرو portYIELD_FROM_ISR |
از این ماکرو برای درخواست Context Switching در روتین وقفه استفاده میشود. الگوی این ماکرو بصورت زیر است:
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
در صورتی که پارامتر xHigherPriorityTaskWoken را pdTRUE تنظیم شود، درخواست Context Switching داده میشود و ممکن است تسک در حال اجرا عوض شود. ولی اگر مقدار آن pdFALSE باشد، درخواستی داده نمیشود.
Deferred Interrupt Processing
در سیستمهای Embedded همواره تلاش میشود تا روتین وقفه تا حد ممکن کوتاه باشد. از جمله دلایل این امر میتوان به موارد زیر اشاره نمود:
- کلیه تسکها تنها زمانی اجرا میشوند که هیچ روتین وقفهای در حال اجرا نباشد. لذا طولانی بودن روتین وقفه ممکن است موجب اختلال در عملکرد سیستم شود. روتین وقفه با تحت تاثیر قرار دادن زمان شروع تسک و مدت زمان اجرای آن موجب اضافه نمودن Jitter و اختلال در سیستم میشود.
- طولانی بودن روتین وقفه موجب از عدم دریافت وقفه و پاسخگویی مناسب به آن میشود.
- مکانیزم وقفه در اکثر میکروکنترلرها و پردازندههای امروزی بصورت nested است. بطوریکه وقفه با اولویت بالاتر میتواند روتین وقفه با اولویت پایین تر را متوقف کند که موجب پیچیده تر شدن سیستم میشود.
لذا باید در روتین وقفه تنها علت وقفه ذخیره شود و Flag وقفه پاک شود و پردازش موردنیاز در داخل یک تسک دیگر انجام شود. به این فرآیند Deferred interrupt processing گفته میشود. Deferred interrupt processing این امکان را فراهم میکند تا طراح بین تسک مربوط به وقفه و سایر تسکهای سیستم اولویت بندی کند. در صورتیکه اولویت تسک مربوط به وقفه بسیار بالاتر از سایر تسک باشد میتوان با استفاده از ماکروی portYIELD_FROM_ISR در خواست Context Switching داد و بلافاصله خارج از روتین وقفه پردازش موردنظر را انجام داد.
استفاده از Queue در داخل روتین وقفه
استفاده از Queue در داخل روتین وقفه |
در بخش قبل نحوه استفاده از Binary Semaphore برای همزمانسازی و اطلاع رسانی بین وقفه و تسک در حال اجرا بررسی شد. برای انتقال داده بین روتین وقفه و تسک از Queue استفاده میشود. در بخشهای قبل نحوه استفاده از Queue بین تسکها بررسی شد. در این قسمت نحوه تبادل داده از روتین وقفه به تسک و برعکس بررسی شده است.
برای ارسال داده در روتین وقفه باید از توابع xQueueSendToFrontFromISR و xQueueSendToBackFromISR بجای xQueueSendToFront و xQueueSendToBack استفاده کرد. همچنین برای دریافت داده از تابع xQueueReceiveFromISR بجای تابع xQueueReceive باید استفاده کرد
مثال نمونه
در این مثال نحوه استفاده از Queue در داخل روتین وقفه نشان و نحوه به عقب انداختن پردازش مودنیاز وقفه (Deferred Interrupt Processing) نشان داده شده است. در این مثال میکروکنترلر داده را با گذرگاه Uart از کامپیوتر دریافت می کند و با توجه به مقدار آن یک LED را روشن و یا خاموش میکند. در صورتیکه داده سریال “A” باشد LED روشن شده و در صورتیکه “B” باشد LED خاموش میشود.
داده سریال در روتین وقفه در داخل یک Queue ریخته میشود و سپس در تسک دیگری داده از Queue بازیابی شده و با توجه به مقدار آن وضعیت LED را تنظیم میکند.
در قسمتهای قبل نحوه اضافه نمودن تسک و Queue بیان شده است. لذا در این بخش از بیان مجدد آن خودداری شده است.
این روش نسبت به روش RTC انعطاف پذیری بالاتری دارد ولی در عوض پیچیدگیهای بیشتری به سیستم تحمیل میکند. چرا که هنگامی که تسک قبل از به اتمام رسیدن متوقف میشود نیازمند Context Switching است.
نحوه تنظیم GPIO
برای روشن و خاموش کردن LED ابتدا باید پایه موردنظر بصورت خروجی تعیین شود. این LED در بورد استفاده شده به پایه PD.13 متصل شده است. برای تنظیم پایه بعنوان خروجی در نرم افزار STM32CubeMX در پنجره Pinout View بر روی پایه مورد نظر کلیک کرده و گزینه GPIO_Output را انتخاب کنید
نحوه تنظیم پورت UART
در این پروژه از پورت UART1 برای ارتباط با کامپیوتر استفاده شده است. نحوه تنظیم پورت UART در شکل 2 نشان داده شده است.
همچنین برای فعال نمودن وقفه UART در سربرگ NVIC Settings تیک Enabled را فعال نمایید
نرم افزار
در کد زیر تابع main نشان داده شده است. در تابع main پس از انجام تنظیمات GPIO و UART یک تسک به نام myTask و یک Queue بنام UartQueue تعریف شده است. پس از آن Scheduler فعال شده است.
روتین وقفه UART
در روتین وقفه دریافت UART داده در Queue نوشته شده است. همانطور که ملاحظه میکنید پارامتر pxHigherPriorityTaskWoken نیز برابر pdTRUE تنظیم شده است تا پس از اتمام روتین وقفه Scheduler سریعا تسک myTask را اجرا نماید. کد روتین وقفه بصورت زیر است:
برنامه تسک مرتبط با وقفه UART
سیستم عاملهای بلادرنگ معمولا دارای API های متعددی برای پشتیبانی از درایورهای ادوات مختلف هستند.
سلام
در انتهای توضیحات نمونه کدی دیده نمیشود