- كيف يعمل نظام RTOS؟
- المصطلحات المستخدمة بكثرة في RTOS
- تثبيت مكتبة Arduino FreeRTOS
- مخطط الرسم البياني
- مثال Arduino FreeRTOS- إنشاء مهام FreeRTOS في Arduino IDE
- تنفيذ مهام FreeRTOS في Arduino IDE
يُطلق على نظام التشغيل الموجود داخل الأجهزة المضمنة اسم RTOS (نظام التشغيل في الوقت الفعلي). في الأجهزة المضمنة ، تعتبر المهام في الوقت الفعلي بالغة الأهمية حيث يلعب التوقيت دورًا مهمًا للغاية. المهام في الوقت الفعلي هي مهام محددة للوقت ، مما يعني أن وقت الاستجابة لأي حدث ثابت دائمًا بحيث يمكن ضمان حدوث أي حدث معين في وقت محدد. صُمم نظام RTOS لتشغيل التطبيقات بتوقيت دقيق للغاية ودرجة عالية من الموثوقية. يساعد نظام RTOS أيضًا في تعدد المهام بنواة واحدة.
لقد قمنا بالفعل بتغطية برنامج تعليمي حول كيفية استخدام RTOS في الأنظمة المضمنة حيث يمكنك معرفة المزيد عن RTOS ، والفرق بين نظام التشغيل للأغراض العامة و RTOS ، وأنواع مختلفة من RTOS ، وما إلى ذلك.
في هذا البرنامج التعليمي ، سنبدأ بـ FreeRTOS. FreeRTOS هي فئة من RTOS للأجهزة المدمجة وهي صغيرة بما يكفي ليتم تشغيلها على متحكمات 8/16-bit ، على الرغم من أن استخدامها لا يقتصر على هذه المتحكمات الدقيقة. إنه مفتوح المصدر بالكامل وشفرته متاحة على جيثب. إذا كنا نعرف بعض المفاهيم الأساسية لـ RTOS ، فمن السهل جدًا استخدام FreeRTOS لأنه يحتوي على واجهات برمجة تطبيقات موثقة جيدًا والتي يمكن استخدامها مباشرة في الكود دون معرفة الجزء الخلفي من التشفير. يمكن العثور على وثائق FreeRTOS الكاملة هنا.
نظرًا لأنه يمكن تشغيل FreeRTOS على MCU 8 بت ، لذا يمكن تشغيله أيضًا على لوحة Arduino Uno. يتعين علينا فقط تنزيل مكتبة FreeRTOS ثم البدء في تنفيذ الكود باستخدام واجهات برمجة التطبيقات. هذا البرنامج التعليمي مخصص للمبتدئين بالكامل ، فيما يلي الموضوعات التي سنغطيها في هذا البرنامج التعليمي Arduino FreeRTOS:
- كيف يعمل نظام RTOS
- بعض المصطلحات المستخدمة بكثرة في RTOS
- تثبيت FreeRTOS في Arduino IDE
- كيفية إنشاء مهام FreeRTOS مع مثال
كيف يعمل نظام RTOS؟
قبل البدء في عمل RTOS ، دعونا نرى ما هي المهمة. المهمة هي جزء من التعليمات البرمجية التي يمكن جدولتها على وحدة المعالجة المركزية لتنفيذه. لذلك ، إذا كنت ترغب في تنفيذ بعض المهام ، فيجب جدولتها باستخدام تأخير kernel أو استخدام المقاطعات. يتم تنفيذ هذا العمل بواسطة "المجدول" الموجود في النواة. في معالج أحادي النواة ، يساعد المجدول المهام على التنفيذ في شريحة زمنية معينة ولكن يبدو أن المهام المختلفة يتم تنفيذها في وقت واحد. تعمل كل مهمة وفقًا للأولوية المعطاة لها.
الآن ، دعنا نرى ما يحدث في نواة RTOS إذا أردنا إنشاء مهمة لميض LED بفاصل زمني مدته ثانية واحدة ووضع هذه المهمة على رأس الأولويات.
بصرف النظر عن مهمة LED ، ستكون هناك مهمة أخرى يتم إنشاؤها بواسطة kernel ، وهي تُعرف بالمهمة الخاملة. يتم إنشاء المهمة الخاملة عند عدم توفر مهمة للتنفيذ. تعمل هذه المهمة دائمًا على أقل أولوية ، أي صفر أولوية. إذا قمنا بتحليل الرسم البياني للتوقيت الموضح أعلاه ، فيمكن ملاحظة أن التنفيذ يبدأ بمهمة LED ويتم تشغيله لفترة زمنية محددة ، ثم بالنسبة للوقت المتبقي ، يتم تشغيل المهمة الخاملة حتى تحدث مقاطعة التجزئة. ثم تقرر kernel المهمة التي يجب تنفيذها وفقًا لأولوية المهمة وإجمالي الوقت المنقضي لمهمة LED. عند اكتمال ثانية واحدة ، تختار kernel المهمة التي يقودها مرة أخرى لتنفيذها نظرًا لأن لها أولوية أعلى من المهمة الخاملة ، ويمكننا أيضًا أن نقول أن مهمة LED تستبق المهمة الخاملة. إذا كان هناك أكثر من مهمتين لهما نفس الأولوية ، فسيتم تشغيلهما بطريقة round-robin لفترة محددة.
أسفل مخطط الحالة حيث يُظهر تبديل المهمة غير قيد التشغيل إلى حالة التشغيل.
تنتقل كل مهمة تم إنشاؤها حديثًا إلى حالة الاستعداد (جزء من حالة عدم التشغيل). إذا كانت المهمة التي تم إنشاؤها (Task1) لها الأولوية القصوى من المهام الأخرى ، فسوف تنتقل إلى حالة التشغيل. إذا كانت مهمة التشغيل هذه تستبق المهمة الأخرى ، فستعود إلى حالة الاستعداد مرة أخرى. وإلا إذا تم حظر المهمة 1 باستخدام واجهة برمجة التطبيقات للحظر ، فلن تشارك وحدة المعالجة المركزية في هذه المهمة حتى انتهاء المهلة المحددة من قبل المستخدم.
إذا تم تعليق Task1 في حالة التشغيل باستخدام Suspend APIs ، فسوف تنتقل Task1 إلى الحالة Suspended ولن تكون متاحة للجدول مرة أخرى. إذا استأنفت Task1 في حالة التعليق ، فسوف تعود إلى حالة الاستعداد كما ترى في مخطط الكتلة.
هذه هي الفكرة الأساسية لكيفية تشغيل المهام وتغيير حالاتها. في هذا البرنامج التعليمي ، سنقوم بتنفيذ مهمتين في Arduino Uno باستخدام FreeRTOS API.
المصطلحات المستخدمة بكثرة في RTOS
1. المهمة: هي جزء من التعليمات البرمجية التي يمكن جدولة تنفيذها على وحدة المعالجة المركزية.
2. المجدول: وهو مسؤول عن اختيار مهمة من قائمة حالة الاستعداد إلى حالة التشغيل. غالبًا ما يتم تنفيذ أدوات الجدولة بحيث تبقي جميع موارد الكمبيوتر مشغولة (كما هو الحال في موازنة التحميل).
3. الشفعة: هو فعل المقاطعة المؤقتة لمهمة منفذة بالفعل بقصد إزالتها من حالة التشغيل دون تعاونها.
4. السياق التبديل: في الشفعة على أساس الأولوية، جدولة يقارن أولوية المهام قيد التشغيل مع إعطاء الأولوية لقائمة المهام جاهزة في كل systick المقاطعة. إذا كانت هناك أي مهمة في القائمة تكون أولويتها أعلى من مهمة قيد التشغيل ، فسيحدث تبديل السياق. في الأساس ، في هذه العملية يتم حفظ محتويات المهام المختلفة في ذاكرة المكدس الخاصة بكل منها.
5. أنواع سياسات الجدولة:
- الجدولة الوقائية: في هذا النوع من الجدولة ، يتم تشغيل المهام بشريحة زمنية متساوية دون مراعاة الأولويات.
- استباقية على أساس الأولوية: سيتم تشغيل المهمة ذات الأولوية العالية أولاً.
- الجدولة التعاونية: سيحدث تبديل السياق فقط مع تعاون المهام قيد التشغيل. سيتم تشغيل المهمة بشكل مستمر حتى يتم استدعاء عائد المهمة.
6. كائنات النواة: للإشارة إلى المهمة للقيام ببعض الأعمال ، يتم استخدام عملية المزامنة. لتنفيذ هذه العملية ، يتم استخدام كائنات Kernel. بعض كائنات Kernel هي Events ، Semaphores ، Queues ، Mutex ، Mailboxes ، إلخ. سنرى كيفية استخدام هذه الكائنات في البرامج التعليمية القادمة.
من المناقشة أعلاه ، لدينا بعض الأفكار الأساسية حول مفهوم RTOS والآن يمكننا تنفيذ مشروع FreeRTOS في Arduino. لذلك ، لنبدأ بتثبيت مكتبات FreeRTOS في Arduino IDE.
تثبيت مكتبة Arduino FreeRTOS
1. افتح Arduino IDE وانتقل إلى Sketch -> Include Library -> Manage Libraries . ابحث عن FreeRTOS وقم بتثبيت المكتبة كما هو موضح أدناه.
يمكنك تنزيل المكتبة من github وإضافة ملف.zip في Sketch-> Include Library -> Add.zip file.
الآن ، أعد تشغيل Arduino IDE. توفر هذه المكتبة بعض الأمثلة على الكود ، والتي يمكن العثور عليها أيضًا في ملف -> أمثلة -> FreeRTOS كما هو موضح أدناه.
هنا سنكتب الكود من البداية لفهم العمل ، لاحقًا يمكنك التحقق من أكواد الأمثلة واستخدامها.
مخطط الرسم البياني
يوجد أدناه مخطط الدائرة لإنشاء مهمة LED وامضة باستخدام FreeRTOS على Arduino:
مثال Arduino FreeRTOS- إنشاء مهام FreeRTOS في Arduino IDE
دعنا نرى الهيكل الأساسي لكتابة مشروع FreeRTOS.
1. أولاً ، قم بتضمين ملف رأس Arduino FreeRTOS كملف
#تضمن
2. أعط النموذج الأولي لجميع الوظائف التي تكتبها للتنفيذ والتي تتم كتابتها كـ
Task1 باطلة (void * pvParameters) ؛ Task2 باطلة (void * pvParameters) ؛ .. ….
3. الآن ، في وظيفة الإعداد الباطلة () ، قم بإنشاء المهام وابدأ برنامج جدولة المهام.
لإنشاء مهمة ، يتم استدعاء xTaskCreate () API في وظيفة الإعداد مع معلمات / وسيطات معينة.
xTaskCreate (TaskFunction_t pvTaskCode ، const char * const pcName ، uint16_t usStackDepth ، void * pvParameters ، UBaseType_t uxPriority ، TaskHandle_t * pxCreatedTask) ؛
هناك 6 وسيطات يجب تمريرها أثناء إنشاء أي مهمة. دعونا نرى ما هي هذه الحجج
- pvTaskCode: إنه ببساطة مؤشر للوظيفة التي تنفذ المهمة (في الواقع ، فقط اسم الوظيفة).
- pcName: اسم وصفي للمهمة. هذا لا تستخدمه FreeRTOS. تم تضمينه فقط لأغراض التصحيح.
- usStackDepth: لكل مهمة مكدس فريد خاص بها يتم تخصيصه بواسطة النواة للمهمة عند إنشاء المهمة. تحدد القيمة عدد الكلمات التي يمكن للمكدس الاحتفاظ بها ، وليس عدد البايت. على سبيل المثال ، إذا كان عرض المكدس 32 بت وتم تمرير usStackDepth كـ 100 ، فسيتم تخصيص 400 بايت من مساحة المكدس (100 * 4 بايت) في ذاكرة الوصول العشوائي. استخدم هذا بحكمة لأن Arduino Uno يحتوي على 2 كيلو بايت فقط من ذاكرة الوصول العشوائي.
- pvParameters: معلمة إدخال المهمة (يمكن أن تكون فارغة).
- uxPriority: أولوية المهمة (0 هي أدنى أولوية).
- pxCreatedTask: يمكن استخدامه لتمرير مقبض للمهمة التي يتم إنشاؤها. يمكن بعد ذلك استخدام هذا المقبض للإشارة إلى المهمة في استدعاءات API التي ، على سبيل المثال ، تغير أولوية المهمة أو تحذف المهمة (يمكن أن تكون فارغة).
مثال على إنشاء المهام
xTaskCreate (task1، "task1"، 128، NULL، 1، NULL) ؛ xTaskCreate (مهمة 2 ، "مهمة 2" ، 128 ، NULL ، 2 ، NULL) ؛
هنا ، يكون لـ Task2 أولوية أعلى وبالتالي يتم تنفيذه أولاً.
4. بعد إنشاء المهمة ، ابدأ المجدول في إعداد باطل باستخدام vTaskStartScheduler () ؛ API.
5. ستبقى وظيفة الحلقة الفارغة () فارغة لأننا لا نريد تشغيل أي مهمة يدويًا وبلا حدود. لأنه تتم الآن معالجة تنفيذ المهمة بواسطة المجدول.
6. الآن ، علينا تنفيذ وظائف المهام وكتابة المنطق الذي تريد تنفيذه داخل هذه الوظائف. يجب أن يكون اسم الوظيفة هو نفسه الوسيطة الأولى لـ xTaskCreate () API.
مهمة 1 باطلة (باطل * pvParameters) { while (1) { .. ..// منطقتك } }
7. يحتاج معظم الكود إلى وظيفة تأخير لإيقاف مهمة التشغيل ولكن في RTOS لا يُقترح استخدام وظيفة Delay () لأنها توقف وحدة المعالجة المركزية وبالتالي يتوقف RTOS أيضًا عن العمل. لذا فإن FreeRTOS لديها واجهة برمجة تطبيقات kernel لحظر المهمة لفترة محددة.
vTaskDelay (const TickType_t xTicksToDelay) ؛
يمكن استخدام واجهة برمجة التطبيقات هذه لأغراض التأخير. API هذا يؤخر مهمة لعدد معين من التكات. يعتمد الوقت الفعلي الذي تظل فيه المهمة محظورة على معدل التجزئة. يمكن استخدام المنفذ الثابت TICK_PERIOD_MS لحساب الوقت الفعلي من معدل التجزئة.
هذا يعني أنك إذا كنت تريد تأخيرًا بمقدار 200 مللي ثانية ، فاكتب هذا السطر
vTaskDelay (200 / portTICK_PERIOD_MS) ،
لذلك في هذا البرنامج التعليمي ، سنستخدم واجهات برمجة تطبيقات FreeRTOS لتنفيذ ثلاث مهام.
واجهات برمجة التطبيقات لاستخدامها:
- xTaskCreate () ،
- vTaskStartScheduler () ،
- vTaskDelay () ،
المهمة التي سيتم إنشاؤها لهذا البرنامج التعليمي:
- يومض LED عند الرقم الرقمي 8 بتردد 200 مللي ثانية
- يومض LED عند الرقم 7 بتردد 300 مللي ثانية
- طباعة الأرقام في الشاشة التسلسلية بتردد 500 مللي ثانية.
تنفيذ مهام FreeRTOS في Arduino IDE
1. من شرح الهيكل الأساسي أعلاه ، قم بتضمين ملف رأس Arduino FreeRTOS. ثم قم بعمل نماذج أولية للوظيفة. نظرًا لأن لدينا ثلاث مهام ، قم بعمل ثلاث وظائف ونماذج أولية.
# تضمين TaskBlink1 باطل (باطل * pvParameters) ؛ TaskBlink2 باطلة (باطل * pvParameters) ؛ باطل Taskprint (void * pvParameters) ؛
2. في وظيفة () الإعداد الفارغة ، قم بتهيئة الاتصال التسلسلي بمعدل 9600 بت في الثانية وإنشاء جميع المهام الثلاث باستخدام واجهة برمجة تطبيقات xTaskCreate () . في البداية ، حدد أولويات جميع المهام كـ "1" وابدأ المجدول.
إعداد باطل () { Serial.begin (9600) ؛ xTaskCreate (TaskBlink1 ، "Task1" ، 128 ، NULL ، 1 ، NULL) ؛ xTaskCreate (TaskBlink2 ، "Task2" ، 128 ، NULL ، 1 ، NULL) ؛ xTaskCreate (Taskprint، "Task3"، 128، NULL، 1، NULL) ؛ vTaskStartScheduler () ، }
3. الآن ، قم بتنفيذ جميع الوظائف الثلاث كما هو موضح أدناه لميض المهمة 1 LED.
TaskBlink1 باطلة (باطل * pvParameters) { pinMode (8، OUTPUT) ؛ while (1) { digitalWrite (8، HIGH) ؛ vTaskDelay (200 / portTICK_PERIOD_MS) ، digitalWrite (8 ، منخفض) ؛ vTaskDelay (200 / portTICK_PERIOD_MS) ، } }
وبالمثل ، قم بتنفيذ دالة TaskBlink2. ستتم كتابة دالة Task3 كـ
void Taskprint (void * pvParameters) { int counter = 0 ؛ بينما (1) { عداد ++ ؛ Serial.println (عداد) ؛ vTaskDelay (500 / منفذ TICK_PERIOD_MS) ، } }
هذا هو. لقد أكملنا بنجاح مشروع FreeRTOS Arduino لـ Arduino Uno. يمكنك العثور على رمز كامل مع مقطع فيديو في نهاية هذا البرنامج التعليمي.
أخيرًا ، قم بتوصيل اثنين من مصابيح LED في الدبوس الرقمي 7 و 8 وقم بتحميل الكود على لوحة Arduino وافتح الشاشة التسلسلية. سترى عدادًا يعمل مرة واحدة كل 500 مللي ثانية باسم المهمة كما هو موضح أدناه.
أيضًا ، راقب مصابيح LED ، فهي تومض على فترات زمنية مختلفة. حاول اللعب مع وسيطة الأولوية في وظيفة xTaskCreate . قم بتغيير الرقم ولاحظ السلوك على الشاشة التسلسلية ومصابيح LED.
الآن ، يمكنك فهم أول مثالين من الرموز التي يتم فيها إنشاء مهام القراءة التناظرية والقراءة الرقمية. بهذه الطريقة ، يمكنك إنشاء المزيد من المشاريع المتقدمة باستخدام واجهات برمجة تطبيقات Arduino Uno و FreeRTOS فقط.