- ما هو تعدد المهام؟
- لماذا تخطي () في اردوينو؟
- لماذا تستخدم ميلي ()؟
- المكونات مطلوبة
- مخطط الرسم البياني
- برمجة اردوينو UNO لتعدد المهام
أدى تعدد المهام إلى ثورة أجهزة الكمبيوتر حيث يمكن تشغيل برنامج واحد أو أكثر في وقت واحد مما يزيد من الكفاءة والمرونة والقدرة على التكيف والإنتاجية. في الأنظمة المضمنة ، يمكن للميكروكونترولر أيضًا التعامل مع تعدد المهام وأداء مهمتين أو أكثر في وقت واحد دون إيقاف التعليمات الحالية.
هنا في هذا البرنامج التعليمي سوف نتعلم كيف يقوم Arduino بتنفيذ مهام متعددة باستخدام وظيفة Arduino millis. بشكل عام ، يتم استخدام وظيفة delay () في Arduino لمهمة دورية مثل وميض LED ولكن وظيفة التأخير () هذه توقف البرنامج لبعض الوقت المحدد ولا تسمح بإجراء عمليات أخرى. لذا تشرح هذه المقالة كيف يمكننا تجنب استخدام وظيفة delay () واستبدالها بـ millis () لأداء أكثر من مهمة في وقت واحد وجعل Arduino وحدة تحكم متعددة المهام. قبل الخوض في التفاصيل ، لنبدأ بتقليل تعدد المهام.
ما هو تعدد المهام؟
يعني تعدد المهام ببساطة تنفيذ أكثر من مهمة أو برنامج واحد في نفس الوقت. تتميز جميع أنظمة التشغيل تقريبًا بتعدد المهام. يُعرف هذا النوع من أنظمة التشغيل باسم MOS (نظام تشغيل متعدد المهام). يمكن أن يكون MOS عبارة عن نظام تشغيل محمول أو كمبيوتر مكتبي. المثال الجيد على تعدد المهام في أجهزة الكمبيوتر هو عندما يقوم المستخدمون بتشغيل تطبيق البريد الإلكتروني ، ومتصفح الإنترنت ، ومشغل الوسائط ، والألعاب ، في نفس الوقت ، وإذا لم يرغب المستخدمون في استخدام التطبيق ، فسيتم تشغيله في الخلفية إذا لم يكن مغلقًا. يستخدم المستخدم النهائي كل هذه التطبيقات في نفس الوقت ولكن نظام التشغيل يأخذ هذا المفهوم بشكل مختلف قليلاً. دعونا نناقش كيف يدير نظام التشغيل تعدد المهام.
كما هو موضح في الصورة ، تقسم وحدة المعالجة المركزية الوقت إلى ثلاثة أجزاء متساوية وتخصص كل جزء لكل مهمة / تطبيق. هذه هي الطريقة التي يتم بها تعدد المهام في معظم الأنظمة. سيكون المفهوم متماثلًا تقريبًا بالنسبة إلى Arduino Multitasking ، باستثناء أن توزيع الوقت سيكون مختلفًا بعض الشيء. نظرًا لأن Arduino يعمل بتردد منخفض وذاكرة الوصول العشوائي مقارنة بالكمبيوتر المحمول / المحمول / الكمبيوتر الشخصي ، فإن الوقت الممنوح لكل مهمة سيكون مختلفًا أيضًا. يحتوي Arduino أيضًا على وظيفة تأخير () تُستخدم على نطاق واسع. ولكن قبل البدء دعونا نناقش سبب عدم استخدام وظيفة delay () في أي مشروع.
لماذا تخطي () في اردوينو؟
إذا تم اعتبار التوثيق المرجعي لـ Arduino ، فهناك نوعان من وظائف التأخير ، الأول هو delay () والثاني هو delayMicroseconds (). كلتا الوظيفتين متطابقتين من حيث توليد التأخير. الاختلاف الوحيد هو أنه في وظيفة delay () ، يكون عدد المعلمات الذي تم تمريره بالمللي ثانية ، أي إذا كتبنا تأخيرًا (1000) ، فسيكون التأخير 1000 مللي ثانية ، أي ثانية واحدة. وبالمثل في وظيفة delayMicroseconds () ، فإن المعامل الذي تم تمريره يكون بالميكروثانية ، أي إذا كتبنا delayMicroseconds (1000) ، فسيكون التأخير 1000 ميكرو ثانية ، أي 1 مللي ثانية.
هنا تأتي النقطة ، كلتا الوظيفتين توقف البرنامج مؤقتًا لمقدار الوقت المنقضي في وظيفة التأخير. لذلك إذا أعطينا تأخيرًا لمدة ثانية واحدة ، فلن يتمكن المعالج من الانتقال إلى التعليمات التالية حتى مرور ثانية واحدة. وبالمثل ، إذا كان التأخير 10 ثوانٍ ، فسيتوقف البرنامج لمدة 10 ثوانٍ ولن يسمح المعالج بالانتقال إلى التعليمات التالية حتى مرور 10 ثوانٍ. هذا يعيق أداء الميكروكونترولر من حيث السرعة وتنفيذ التعليمات.
أفضل مثال لشرح عيب وظيفة التأخير هو استخدام زري ضغط. ضع في اعتبارك أننا نريد تبديل مصباحي LED باستخدام زرين ضغط. لذلك إذا تم الضغط على زر ضغط واحد ، فيجب أن يتوهج مؤشر LED المقابل لمدة ثانيتين ، وبالمثل إذا تم الضغط على الثانية ، فيجب أن يتوهج مؤشر LED لمدة 4 ثوانٍ. ولكن عند استخدام التأخير () ، إذا كان المستخدم يضغط على الزر الأول ، فسيتوقف البرنامج لمدة ثانيتين وإذا ضغط المستخدم على الزر الثاني قبل تأخير ثانيتين ، فلن يقبل المتحكم الدقيق الإدخال كما هو البرنامج في مرحلة التوقف.
تذكر الوثائق الرسمية لـ Arduino هذا بوضوح في وصف وظيفة Notes and Warnings of delay (). يمكنك القيام بذلك والتحقق من ذلك لجعله أكثر وضوحًا.
لماذا تستخدم ميلي ()؟
للتغلب على المشكلة الناتجة عن استخدام التأخير ، يجب على المطور استخدام وظيفة millis () التي يسهل استخدامها بمجرد أن تصبح معتادًا وسيستخدم أداء وحدة المعالجة المركزية بنسبة 100٪ دون إحداث أي تأخير في تنفيذ التعليمات. millis () هي وظيفة تُرجع فقط مقدار المللي ثانية التي انقضت منذ أن بدأت لوحة Arduino في تشغيل البرنامج الحالي دون تجميد البرنامج. سيتم تجاوز هذا الرقم الزمني (أي العودة إلى الصفر) ، بعد حوالي 50 يومًا.
تمامًا مثل Arduino لديه delayMicroseconds () ، فإنه يحتوي أيضًا على الإصدار الصغير من millis () مثل micros (). الفرق بين micros و millis هو أن الميكرو () سوف يفيض بعد 70 دقيقة تقريبًا ، مقارنة بالمللي () وهو 50 يومًا. بناءً على التطبيق ، يمكنك استخدام millis () أو micros ().
استخدام المللي () بدلاً من التأخير ():
لاستخدام الميلي () للتوقيت والتأخير ، تحتاج إلى تسجيل وتخزين الوقت الذي حدث فيه الإجراء لبدء الوقت ثم التحقق على فترات زمنية مما إذا كان الوقت المحدد قد مر. لذلك كما ذكرنا ، قم بتخزين الوقت الحالي في متغير.
تيار طويل بدون توقيع
نحتاج إلى متغيرين آخرين لمعرفة ما إذا كان الوقت المطلوب قد مر. لقد قمنا بتخزين الوقت الحالي في currentMillis متغير ولكننا نحتاج أيضًا إلى معرفة متى بدأت فترة التوقيت وكم هي الفترة. لذلك تم الإعلان عن الفاصل الزمني والميلي السابق . سيخبرنا الفاصل الزمني بالتأخير الزمني وسيقوم previosMillis بتخزين آخر مرة وقع فيها الحدث.
غير موقعة منذ فترة طويلة ميليس. فترة طويلة بدون توقيع = 1000 ؛
لفهم هذا ، دعنا نأخذ مثالاً على مصباح LED وامض بسيط. ستخبرنا الفترة = 1000 أن مؤشر LED سيومض لمدة ثانية واحدة أو 1000 مللي ثانية.
const int ledPin = 4 ؛ // رقم دبوس LED متصل int ledState = منخفض ؛ // تستخدم لضبط حالة LED غير الموقعة منذ فترة طويلة ميليس = 0 ؛ // سيتم تخزين آخر مرة وميض فيها مؤشر LED لفترة طويلة = 1000 ؛ // الفترة التي يومض فيها في إعداد ms void () { pinMode (ledPin ، OUTPUT) ؛ // set ledpin as output } void loop () { uncigned long currentMillis = millis ()؛ // تخزين الوقت الحالي إذا (currentMillis - previousMillis> = period) {// تحقق مما إذا كانت 1000ms قد مرت previousMillis = currentMillis؛ // حفظ آخر مرة تومض فيها مؤشر LED إذا (ledState == LOW) {// إذا كان مؤشر LED مطفأ ، فقم بتشغيله والعكس بالعكس ledState = HIGH ؛ } else { ledState = LOW ؛ } digitalWrite (ledPin ، ledState) ؛ // اضبط LED مع ledState على الوميض مرة أخرى } }
هنا البيان
تعمل المقاطعات في Arduino بنفس طريقة عمل المتحكمات الدقيقة الأخرى. تحتوي لوحة Arduino UNO على دبابيس منفصلة لإرفاق المقاطعات على GPIO pin 2 و 3. لقد قمنا بتغطيتها بالتفصيل في Arduino Interrupts Tutorial ، حيث يمكنك معرفة المزيد حول المقاطعات وكيفية استخدامها.
سنعرض هنا Arduino Multitasking من خلال التعامل مع مهمتين في نفس الوقت. ستشمل المهام وميض اثنين من مصابيح LED في تأخير زمني مختلف إلى جانب زر الضغط الذي سيتم استخدامه للتحكم في حالة تشغيل / إيقاف LED. لذلك سيتم تنفيذ ثلاث مهام في وقت واحد.
المكونات مطلوبة
- اردوينو UNO
- ثلاثة مصابيح LED (أي لون)
- المقاومات (470 ، 10 ك)
- صداري
- اللوح
مخطط الرسم البياني
مخطط الدائرة لإثبات استخدام وظيفة Arduino Millis () سهل للغاية ولا يحتوي على الكثير من المكونات لإرفاقها كما هو موضح أدناه.
برمجة اردوينو UNO لتعدد المهام
تتطلب برمجة Arduino UNO لتعدد المهام فقط المنطق وراء كيفية عمل millis () الموضح أعلاه. يوصى بممارسة وميض LED باستخدام المللي مرارًا وتكرارًا لتوضيح المنطق وجعل نفسك مرتاحًا مع المللي () قبل البدء في برمجة Arduino UNO لتعدد المهام. في هذا البرنامج التعليمي ، تُستخدم المقاطعة أيضًا مع مللي () في وقت واحد لتعدد المهام. سيكون الزر مقاطعة. لذلك عندما يتم إنشاء مقاطعة ، أي الضغط على زر الضغط ، سيتحول مؤشر LED إلى حالة التشغيل أو الإيقاف.تبدأ البرمجة بإعلان أرقام الدبوس حيث يتم توصيل مصابيح LED وزر الضغط.
int led1 = 6 ؛ int led2 = 7 ؛ int toggleLed = 5 ؛ int pushButton = 2 ؛
بعد ذلك نكتب متغيرًا لتخزين حالة مصابيح LED للاستخدام في المستقبل.
int ledState1 = منخفض ؛ int ledState2 = منخفض ؛
تمامًا كما هو موضح أعلاه في مثال الوميض ، تم الإعلان عن متغيرات الفترة والميل السابق للمقارنة وإنشاء تأخير لمصابيح LED. يومض مؤشر LED الأول بعد كل ثانية ويومض مؤشر LED آخر بعد 200 مللي ثانية.
ميلليس 1 = 0 ؛ فترة طويلة 1 = 1000 ؛ ميلليس 2 = 0 ؛ فترة طويلة 2 = 200 ؛
سيتم استخدام وظيفة أخرى بالمللي لإنشاء تأخير الإلغاء لتجنب الضغطات المتعددة على زر الضغط. سيكون هناك نهج مماثل على النحو الوارد أعلاه.
int debouncePeriod = 20 ؛ int debounceMillis = 0 ؛
و سوف تستخدم المتغيرات الثلاثة لتخزين وضع زر كما المقاطعة ، تبديل LED والدولة زر.
زر منطقي دفع = خطأ ؛ int ledChange = منخفض ؛ int lastState = عالية ؛
حدد عمل الدبوس الذي سيعمل الدبوس على أنه INPUT أو OUTPUT.
pinMode (led1 ، الإخراج) ؛ pinMode (led2 ، الإخراج) ؛ pinMode (toggleLed ، الإخراج) ؛ pinMode (زر الضغط ، الإدخال) ؛
الآن حدد دبوس المقاطعة عن طريق إرفاق المقاطعة بتعريف ISR ووضع المقاطعة. لاحظ أنه يوصى باستخدام digitalPinToInterrupt (pin_number) عند الإعلان عن وظيفة attachInterrupt () لترجمة الدبوس الرقمي الفعلي إلى رقم المقاطعة المحدد.
attachInterrupt (digitalPinToInterrupt (زر الضغط) ، pushButton_ISR ، أختر) ؛
تمت كتابة الروتين الفرعي للمقاطعة وسيؤدي فقط إلى تغيير علامة buttonPushed. لاحظ أن روتين المقاطعة يجب أن يكون قصيرًا قدر الإمكان ، لذا حاول كتابته وتقليل التعليمات الإضافية.
pushButton_ISR () باطل { buttonPushed = صحيح ؛ }
تبدأ الحلقة بتخزين قيمة ميلي في متغير ميليس الحالي الذي سيخزن قيمة الوقت المنقضي في كل مرة تتكرر فيها الحلقة.
تيار طويل بدون توقيع
هناك من إجمالي الوظائف الثلاث في تعدد المهام، وميض واحد الصمام في 1 ثانية، وميض الصمام الثانية في 200ms والتعليم إذا لم تضغط على زر ثم التبديل OFF / ON LED. لذلك سنكتب ثلاثة أجزاء للقيام بهذه المهمة.
ل أول مرة هو تبديل حالة الصمام بعد كل 1 ثانية عن طريق مقارنة بالمللي انقضت.
إذا (currentMillis - previousMillis1> = period1 ) { previousMillis1 = currentMillis ؛ إذا (ledState1 == منخفض) { ledState1 = مرتفع ؛ } else { ledState1 = LOW ؛ } digitalWrite (led1، ledState1)؛ }
وبالمثل ، ثانيًا ، يقوم بتبديل مؤشر LED بعد كل 200 مللي ثانية من خلال مقارنة المللي المنقضية. سبق شرح الشرح مسبقًا في هذه المقالة.
إذا (currentMillis - previousMillis2> = period2) { previousMillis2 = currentMillis ؛ إذا (ledState2 == منخفض) { ledState2 = مرتفع ؛ } else { ledState2 = LOW ؛ } digitalWrite (led2، ledState2)؛ }
أخيرًا ، تتم مراقبة علامة buttonPushed وبعد إنشاء تأخير في الارتداد يبلغ 20 مللي ثانية ، يتم تبديل حالة LED التي تتوافق مع زر الضغط المتصل كمقاطعة.
if (buttonPushed = true) // تحقق مما إذا كان ISR يسمى { if ((currentMillis - debounceMillis)> debouncePeriod && buttonPushed) // توليد تأخير 20 مللي ثانية لتفادي الضغطات المتعددة { debounceMillis = currentMillis؛ // حفظ آخر وقت تأخير للرد إذا (digitalRead (pushButton) == LOW && lastState == HIGH) // غيّر المؤشر بعد الضغط على زر الدفع { ledChange =! تغيير ؛ digitalWrite (toggleLed ، ledChange) ؛ lastState = منخفض ؛ } else if (digitalRead (pushButton) == HIGH && lastState == LOW) { lastState = HIGH؛ } buttonPushed = خطأ ؛ } }
ينتهي هذا البرنامج التعليمي Arduino millis (). لاحظ أنه من أجل التعود مع millis () ، ما عليك سوى التدرب على تنفيذ هذا المنطق في بعض التطبيقات الأخرى. يمكنك أيضًا توسيعه لاستخدام المحركات والمحركات المؤازرة وأجهزة الاستشعار والأجهزة الطرفية الأخرى. في حالة وجود أي شك ، يرجى الكتابة إلى منتدانا أو التعليق أدناه.
يتم توفير رمز وفيديو كاملين لتوضيح استخدام وظيفة المللي في Arduino أدناه.