- حذف مهمة في FreeRTOS Arduino
- ما هي قائمة الانتظار في FreeRTOS؟
- إنشاء قائمة انتظار في FreeRTOS
- مخطط الرسم البياني
- تنفيذ FreeRTOS Queue في Arduino IDE
في البرنامج التعليمي السابق ، قدمنا FreeRTOS في Arduino Uno وقمنا بإنشاء مهمة لمصباح LED الوامض. الآن ، في هذا البرنامج التعليمي ، سوف نتعمق أكثر في المفاهيم المتقدمة لواجهات برمجة تطبيقات RTOS والتعرف على التواصل بين المهام المختلفة. هنا نتعرف أيضًا على قائمة الانتظار لنقل البيانات من مهمة إلى أخرى وإثبات عمل واجهات برمجة تطبيقات قائمة الانتظار من خلال توصيل شاشة LCD مقاس 16 × 2 و LDR مع Arduino Uno.
قبل مناقشة قوائم الانتظار ، دعنا نرى واجهة FreeRTOS API أخرى مفيدة في حذف المهام عند الانتهاء من العمل المعين. في بعض الأحيان تحتاج المهمة إلى حذفها لتحرير الذاكرة المخصصة. استمرارًا للدرس السابق ، سنستخدم وظيفة vTaskDelete () API في نفس الرمز لحذف إحدى المهام. يمكن للمهمة استخدام وظيفة واجهة برمجة التطبيقات vTaskDelete () لحذف نفسها أو أي مهمة أخرى.
لاستخدام واجهة برمجة التطبيقات هذه ، يجب عليك تكوين ملف FreeRTOSConfig.h . يُستخدم هذا الملف لتخصيص FreeRTOS وفقًا للتطبيق. يتم استخدامه لتغيير خوارزميات الجدولة والعديد من المعلمات الأخرى. يمكن العثور على الملف في دليل Arduino المتاح بشكل عام في مجلد المستندات بجهاز الكمبيوتر الخاص بك. في حالتي ، يتوفر في \ Documents \ Arduino \ libraries \ FreeRTOS \ src كما هو موضح أدناه.
الآن، فتح هذا الملف باستخدام أي محرر نصوص والبحث عن و رقم تعريف INCLUDE_vTaskDelete والتأكد من قيمتها "1" (1 يعني تمكين وسائل 0 تعطيل). إنه 1 افتراضيًا ولكنه يتحقق من ذلك.
سنستخدم ملف التكوين هذا بشكل متكرر في دروسنا التالية لتعيين المعلمات.
الآن ، دعنا نرى كيفية حذف مهمة.
حذف مهمة في FreeRTOS Arduino
لحذف مهمة ، يتعين علينا استخدام وظيفة واجهة برمجة التطبيقات vTaskDelete (). يتطلب الأمر حجة واحدة فقط.
vTaskDelete (TaskHandle_t pxTaskToDelete) ؛
pxTaskToDelete: هو مقبض المهمة الذي سيتم حذفه. إنها نفس الوسيطة السادسة من xTaskCreate () API. في البرنامج التعليمي السابق ، تم تعيين هذه الوسيطة على أنها NULL ولكن يمكنك تمرير عنوان محتويات المهمة باستخدام أي اسم. دعنا نقول ما إذا كنت تريد تعيين مقبض المهمة لـ Task2 الذي تم الإعلان عنه كـ
TaskHandle_t any_name ؛ مثال: TaskHandle_t xTask2Handle ؛
الآن ، في vTaskCreate () تعيين API الوسيطة السادسة على أنها
xTaskCreate (TaskBlink2 ، "Task2" ، 128 ، NULL ، 1 ، & xTask2Handle) ؛
يمكن الآن الوصول إلى محتوى هذه المهمة باستخدام المقبض الذي قدمته.
أيضًا ، يمكن للمهمة حذف نفسها بتمرير NULL بدلاً من مؤشر مهمة صالح.
إذا أردنا حذف المهمة 3 من المهمة 3 نفسها ، فأنت بحاجة إلى كتابة vTaskDelete (NULL) ؛ داخل وظيفة Task3 ولكن إذا كنت تريد حذف المهمة 3 من المهمة 2 ، فاكتب vTaskDelete (xTask3Handle) ؛ داخل وظيفة Task2.
في التعليمات البرمجية السابقة ، لحذف Task2 من Task2 نفسها ، فقط أضف vTaskDelete (NULL) ؛ في دالة TaskBlink2 (باطلة * pvParameters) . ثم ستبدو الوظيفة المذكورة أعلاه على هذا النحو
باطل TaskBlink2 (void * pvParameters) { Serial.println ("Task2 قيد التشغيل وعلى وشك الحذف") ؛ vTaskDelete (NULL) ، pinMode (7 ، الإخراج) ؛ while (1) { digitalWrite (7، HIGH) ؛ vTaskDelay (300 / portTICK_PERIOD_MS) ، digitalWrite (7، LOW)؛ vTaskDelay (300 / portTICK_PERIOD_MS)؛ } }
الآن ، قم بتحميل الكود ولاحظ مؤشرات LED والشاشة التسلسلية. سترى أن مؤشر LED الثاني لا يومض الآن ويتم حذف المهمة 2 بعد مواجهة واجهة برمجة التطبيقات للحذف.
لذلك يمكن استخدام واجهة برمجة التطبيقات هذه لإيقاف تنفيذ مهمة معينة.
الآن ، لنبدأ بقائمة الانتظار.
ما هي قائمة الانتظار في FreeRTOS؟
قائمة الانتظار هي بنية البيانات التي يمكن أن تحتوي على عدد محدود من العناصر ذات الحجم الثابت ويتم تشغيلها في مخطط FIFO (يدخل أولاً يخرج أولاً). توفر قوائم الانتظار آلية اتصال مهمة إلى مهمة ومهمة إلى مقاطعة ومقاطعة إلى مهمة.
يسمى الحد الأقصى لعدد العناصر التي يمكن لقائمة الانتظار الاحتفاظ بها "طولها". يتم تعيين طول وحجم كل عنصر عند إنشاء قائمة الانتظار.
تم توضيح مثال على كيفية استخدام قائمة الانتظار لنقل البيانات بشكل جيد في وثائق FreeRTOS التي يمكن العثور عليها هنا. يمكنك بسهولة فهم المثال المعطى.
بعد فهم قوائم الانتظار ، دعنا نحاول فهم عملية إنشاء قائمة انتظار ومحاولة تنفيذها في كود FreeRTOS الخاص بنا.
إنشاء قائمة انتظار في FreeRTOS
أولاً ، صف بيان المشكلة الذي سيتم تنفيذه بمساعدة قائمة انتظار FreeRTOS و Arduino Uno.
نريد طباعة قيمة مستشعر LDR على شاشة LCD مقاس 16 * 2. إذن هناك مهمتان الآن
- يحصل Task1 على القيم التناظرية لـ LDR.
- تقوم Task2 بطباعة القيمة التناظرية على شاشة LCD.
لذلك ، هنا تلعب قائمة الانتظار دورها لأنها ترسل البيانات التي تم إنشاؤها بواسطة المهمة 1 إلى المهمة 2. في المهمة 1 ، سنرسل قيمة تناظرية إلى قائمة الانتظار وفي المهمة 2 ، سنستلمها من قائمة الانتظار.
هناك ثلاث وظائف للعمل مع قوائم الانتظار
- إنشاء قائمة انتظار
- إرسال البيانات إلى قائمة الانتظار
- تلقي البيانات من قائمة الانتظار
لإنشاء قائمة انتظار ، استخدم واجهة برمجة تطبيقات دالة xQueueCreate (). يتطلب حجتين.
xQueueCreate (UBaseType_t uxQueueLength ، UBaseType_t uxItemSize) ،
uxQueueLength: الحد الأقصى لعدد العناصر التي يمكن أن تحتويها قائمة الانتظار التي يتم إنشاؤها في أي وقت.
uxItemSize: الحجم بالبايت لكل عنصر بيانات يمكن تخزينه في قائمة الانتظار.
إذا قامت هذه الدالة بإرجاع NULL ، فلن يتم إنشاء قائمة الانتظار بسبب عدم كفاية الذاكرة وإذا قامت بإرجاع قيمة غير NULL ، يتم إنشاء قائمة الانتظار بنجاح. قم بتخزين قيمة الإرجاع هذه في متغير لاستخدامها كمعامل للوصول إلى قائمة الانتظار كما هو موضح أدناه.
QueueHandle_t queue1؛ queue1 = xQueueCreate (4، sizeof (int)) ؛
سيؤدي هذا إلى إنشاء قائمة انتظار مكونة من 4 عناصر في ذاكرة الكومة بحجم int (2 بايت من كل كتلة) وتخزين قيمة الإرجاع إلى متغير مقبض queue1 .
2. إرسال البيانات إلى قائمة الانتظار في FreeRTOS
لإرسال القيم إلى قائمة الانتظار ، يحتوي FreeRTOS على متغيرين من API لهذا الغرض.
- xQueueSendToBack (): يُستخدم لإرسال البيانات إلى الجزء الخلفي (الذيل) من قائمة الانتظار.
- xQueueSendToFront (): يُستخدم لإرسال البيانات إلى مقدمة (رأس) قائمة الانتظار.
الآن ، xQueueSend () يكافئ ويماثل تمامًا xQueueSendToBack ().
كل واجهات برمجة التطبيقات هذه تأخذ 3 حجج.
xQueueSendToBack (QueueHandle_t xQueue، const void * pvItemToQueue ، TickType_t xTicksToWait) ؛
xQueue: مقبض قائمة الانتظار التي يتم إرسال البيانات إليها (كتابة). هذا المتغير هو نفسه المستخدم لتخزين القيمة المرجعة لـ xQueueCreate API.
pvItemToQueue: مؤشر للبيانات المراد نسخها في قائمة الانتظار.
xTicksToWait: الحد الأقصى من الوقت الذي يجب أن تظل فيه المهمة في حالة الحظر لانتظار توفر مساحة في قائمة الانتظار.
سيؤدي تعيين xTicksToWait إلى portMAX_DELAY إلى انتظار المهمة إلى أجل غير مسمى (بدون انتهاء المهلة) ، بشرط تعيين INCLUDE_vTaskSuspend على 1 في FreeRTOSConfig.h وإلا يمكنك استخدام الماكرو pdMS_TO_TICKS () لتحويل الوقت المحدد بالمللي ثانية إلى وقت محدد بعلامات التجزئة.
3. استلام البيانات من قائمة الانتظار في FreeRTOS
لتلقي (قراءة) عنصر من قائمة انتظار ، يتم استخدام xQueueReceive (). يتم إزالة العنصر الذي تم استلامه من قائمة الانتظار.
تأخذ واجهة برمجة التطبيقات هذه أيضًا ثلاث حجج.
xQueueReceive (QueueHandle_t xQueue، void * const pvBuffer، TickType_t xTicksToWait) ؛
الوسيطات الأولى والثالثة هي نفسها إرسال API. فقط الحجة الثانية مختلفة.
const pvBuffer: مؤشر إلى الذاكرة التي سيتم نسخ البيانات المستلمة فيها.
آمل أن تكون قد فهمت واجهات برمجة التطبيقات الثلاثة. الآن ، سنقوم بتنفيذ واجهات برمجة التطبيقات هذه في Arduino IDE ونحاول حل بيان المشكلة الذي وصفناه أعلاه.
مخطط الرسم البياني
هكذا تبدو على اللوح:
تنفيذ FreeRTOS Queue في Arduino IDE
لنبدأ في كتابة التعليمات البرمجية لتطبيقنا.
1. أولاً ، افتح Arduino IDE وقم بتضمين ملف الرأس Arduino_FreeRTOS.h . الآن ، إذا تم استخدام أي كائن kernel مثل قائمة الانتظار ، فقم بتضمين ملف الرأس الخاص به. نظرًا لأننا نستخدم شاشة LCD مقاس 16 * 2 ، فقم بتضمين المكتبة لها أيضًا.
# تضمين # تضمين
2. تهيئة مقبض قائمة الانتظار لتخزين محتويات قائمة الانتظار. أيضًا ، قم بتهيئة أرقام دبوس LCD.
QueueHandle_t queue_1؛ LiquidCrystal LCD (7 ، 8 ، 9 ، 10 ، 11 ، 12) ؛
3. في الإعداد باطل () ، قم بتهيئة شاشة LCD والشاشة التسلسلية بمعدل باود 9600. قم بإنشاء قائمة انتظار ومهمتين باستخدام واجهات برمجة التطبيقات ذات الصلة. هنا سننشئ قائمة انتظار بحجم 4 بنوع عدد صحيح. أنشئ مهمة ذات أولويات متساوية ثم حاول لاحقًا اللعب بهذا الرقم. أخيرًا ، ابدأ المجدول كما هو موضح أدناه.
إعداد باطل () { Serial.begin (9600) ؛ lcd.begin (16 ، 2) ؛ queue_1 = xQueueCreate (4، sizeof (int)) ؛ if (queue_1 == NULL) { Serial.println ("لا يمكن إنشاء قائمة الانتظار")؛ } xTaskCreate (TaskDisplay، "Display_task" ، 128 ، NULL ، 1 ، NULL) ؛ xTaskCreate (TaskLDR، "LDR_task"، 128، NULL، 1، NULL) ؛ vTaskStartScheduler () ، }
4. الآن ، قم بعمل وظيفتين TaskDisplay و TaskLDR . في وظيفة TaskLDR ، اقرأ الدبوس التمثيلي A0 في متغير لأن لدينا LDR متصل بالدبوس A0 في Arduino UNO. أرسل الآن القيمة المخزنة في المتغير عن طريق تمريرها في واجهة برمجة تطبيقات xQueueSend وإرسال المهمة لحالة الحظر بعد ثانية واحدة باستخدام واجهة برمجة تطبيقات vTaskDelay () كما هو موضح أدناه.
باطل TaskLDR (باطل * pvParameters) { int current_intensity ؛ بينما (1) { Serial.println ("Task1") ؛ Current_intensity = analogRead (A0) ؛ Serial.println (كثافة_حالية) ؛ xQueueSend (queue_1، & current_intensity، portMAX_DELAY) ؛ vTaskDelay (1000 / منفذ TICK_PERIOD_MS) ؛ } }
5. وبالمثل ، قم بإنشاء دالة لـ TaskDisplay واستقبل القيم في متغير يتم تمريره إلى الدالة xQueueReceive . أيضًا ، تقوم xQueueReceive () بإرجاع pdPASS إذا كان من الممكن تلقي البيانات بنجاح من قائمة الانتظار وإرجاع errQUEUE_EMPTY إذا كانت قائمة الانتظار فارغة.
الآن ، اعرض القيم على شاشة LCD باستخدام وظيفة lcd.print () .
TaskDisplay باطلة (void * pvParameters) { كثافة العمليات = 0 ؛ بينما (1) { Serial.println ("Task2") ؛ if (xQueueReceive (queue_1، &ensity، portMAX_DELAY) == pdPASS) { lcd.clear ()؛ lcd.setCursor (0 ، 0) ؛ lcd.print ("كثافة:") ؛ lcd.setCursor (11 ، 0) ؛ lcd.print (شدة) ؛ } } }
هذا هو. لقد انتهينا من جزء الترميز من تنفيذ قائمة الانتظار. يمكن العثور على الكود الكامل مع فيديو العمل في النهاية.
الآن ، قم بتوصيل LCD و LDR بـ Arduino UNO وفقًا لمخطط الدائرة ، قم بتحميل الكود. افتح الشاشة التسلسلية ولاحظ المهام. ستلاحظ أن المهام تتغير وقيم LDR تتغير وفقًا لشدة الضوء.
ملاحظة: لا تدعم نواة FreeRTOS معظم المكتبات المصممة لأجهزة استشعار مختلفة بسبب تأخر تنفيذ الوظيفة داخل المكتبات. يؤدي التأخير إلى توقف وحدة المعالجة المركزية تمامًا ، وبالتالي ، يتوقف FreeRTOS kernel أيضًا عن العمل ولن يتم تنفيذ الكود أكثر ويبدأ في سوء التصرف. لذلك ، علينا أن نجعل المكتبات خالية من التأخير للعمل مع FreeRTOS.