قائمة طعام
مجانا
تسجيل
بيت  /  التثبيت والإعداد/ Arduino المقاطعات مع attachInterrupt. كيفية تشغيل المهام المتوازية (الخيوط) في برنامج Arduino

اردوينو يقاطع مع attachInterrupt. كيفية تشغيل المهام المتوازية (الخيوط) في برنامج Arduino


مقاطعات الأجهزة

لم أتمكن من العثور على صورة مضحكة لهذا الدرس ، لقد وجدت فقط بعض المحاضرات حول البرمجة ، وبداية هذه المحاضرة تشرح لنا تمامًا ما يقطع. يمكن وصف المقاطعة في Arduino بنفس الطريقة تمامًا: المتحكم الدقيق "يسقط كل شيء" ، ينتقل إلى تنفيذ مجموعة من الوظائف في معالج المقاطعة ، وينفذها ، ثم يعود بالضبط إلى المكان في الكود الرئيسي حيث توقف.

تختلف المقاطعات ، أي ليست المقاطعات نفسها ، ولكن أسبابها: يمكن أن يتسبب المقاطعة في محول تناظري إلى رقمي ، أو عداد مؤقت ، أو حرفياً دبوس متحكم دقيق. تسمى هذه المقاطعات الخارجية. المعداتوهذا ما نتحدث عنه اليوم.

مقاطعة الأجهزة الخارجية- هذه مقاطعة ناتجة عن تغيير في الجهد على دبوس المتحكم الدقيق. النقطة الأساسية هي أن المتحكم الدقيق (جوهر الحوسبة) لا يستطلع دبوسو لا تضيعوا الوقت في ذلك، "قطعة حديد" أخرى تعمل في التثبيت. بمجرد أن يتغير الجهد على الدبوس (بمعنى الإشارات الرقمية، +5 مطبق / + 5 إزالة) - يستقبل المتحكم الدقيق إشارة ، وينهي كل شيء ، ويعالج المقاطعة ، ويعود إلى العمل. لماذا هذا مطلوب؟ في أغلب الأحيان ، تُستخدم المقاطعات لاكتشاف الأحداث القصيرة - النبضات ، أو حتى لحساب عددها ، دون تحميل الكود الرئيسي. يمكن أن تضغط مقاطعة الأجهزة على ضغطة قصيرة على زر أو مشغل مستشعر أثناء العمليات الحسابية الطويلة المعقدة أو التأخير في الكود ، أي تقريبًا - يتم استقصاء الدبوس بالتوازي مع الكود الرئيسي. أيضًا ، يمكن أن تستيقظ المقاطعات المتحكم الدقيق من أوضاع توفير الطاقة ، عند إيقاف تشغيل جميع الأجهزة الطرفية تقريبًا. دعونا نرى كيفية العمل مع مقاطعات الأجهزة في Arduino IDE.

المقاطعات في اردوينو

لنبدأ بحقيقة أنه ليس كل الدبابيس "يمكن" المقاطعة. نعم ، هناك شيء مثل المقاطعاتولكن سنتحدث عنها في دروس متقدمة. نحتاج الآن إلى فهم أن مقاطعات الأجهزة يمكنها فقط إنشاء دبابيس معينة:

MK / رقم المقاطعة ذكاء 0 ذكاء 1 ذكاء 2 ذكاء 3 الذكاء 4 الذكاء 5
ATmega 328/168 (نانو ، أونو ، ميني) د 2 د 3
ATmega 32U4 (ليوناردو ، مايكرو) د 3 د 2 د 0 D1 د 7
ATmega 2560 (ميجا) د 2 د 3 D21 D20 D19 D18

كما فهمت من الجدول ، فإن المقاطعات لها أرقامها الخاصة ، والتي تختلف عن الرقم السري. بالمناسبة ميزة يدوية digitalPinToInterrupt (دبوس)، الذي يأخذ رقمًا سريًا ويعيد رقم المقاطعة. من خلال تغذية هذه الوظيفة بالرقم 3 على Arduino nano ، نحصل على 1. كل شيء وفقًا للجدول أعلاه ، وظيفة للكسول.

تم توصيل المقاطعة باستخدام الوظيفة attachInterrupt (دبوس ، معالج ، وضع):

  • دبوس- رقم المقاطعة
  • معالج- اسم وظيفة معالج المقاطعة (تحتاج إلى إنشائها بنفسك)
  • وضع- مقاطعة "طريقة" التشغيل:
    • قليل(منخفض) - يتم تشغيله بواسطة إشارة قليلعلى دبوس
    • ارتفاع(النمو) - يتم تشغيله عندما تتغير الإشارة الموجودة على الدبوس من قليلعلى عالي
    • هبوط(إسقاط) - يتم تشغيله عندما تتغير الإشارة الموجودة على الدبوس من عاليعلى قليل
    • يتغير(تغيير) - يتم تشغيله عندما تتغير الإشارة (مع قليلعلى عاليوالعكس بالعكس)

يمكن أيضًا تعطيل المقاطعة باستخدام الوظيفة فصل المقاطعة (دبوس)حيث يوجد رقم التعريف الشخصي مرة أخرى رقم المقاطعة.

يمكنك أيضًا تعطيل المقاطعات بشكل عام مع الوظيفة لا المقاطعات ()وحلها مرة أخرى باستخدام المقاطعات (). كن حذرا معهم! لا المقاطعات ()كما أنه سيوقف مقاطعات المؤقت ، وستتعطل جميع وظائف الوقت وتوليد PWM من أجلك.

لنلقِ نظرة على مثال يتم فيه حساب ضغطات الأزرار في المقاطعة ، وفي الحلقة الرئيسية يتم إخراجها بتأخير قدره ثانية واحدة. العمل مع الزر الوضع العادي، من المستحيل دمج مثل هذا الناتج التقريبي مع التأخير:

عداد int المتطاير = 0 ؛ // إعداد الفراغ المتغير المضاد () (Serial.begin (9600) ؛ // منفذ مفتوح للاتصال // زر متصل على D2 و GND pinMode (2 ، INPUT_PULLUP) ؛ \ // D2 is interrupt 0 // handler - function buttonTick // FALLING - عند النقر فوق الزر ، ستكون الإشارة 0 ، وسنلتقطها نعلق المقاطعة (0، buttonTick، FALLING)؛) زر باطلة () (counter ++؛ // + press) void loop () (Serial. println) (عداد) ؛ // تأخير الإخراج (1000) ؛ // انتظر)

لذلك ، يحسب الكود الخاص بنا النقرات حتى أثناء التأخير! عظيم. لكن ما هو متقلب؟ لقد أعلنا عن متغير عالمي عداد، والذي سيخزن عدد النقرات على الزر. إذا كانت قيمة المتغير ستتغير في المقاطعة ، فأنت بحاجة إلى إبلاغ المتحكم الدقيق بهذا باستخدام المحدد متقلبوالتي تكتب قبل تحديد نوع بيانات المتغير وإلا فسيكون العمل غير صحيح. تحتاج فقط إلى تذكر هذا: إذا تغير متغير في مقاطعة ، فافعل ذلك متقلب.

بعض النقاط الأكثر أهمية:

  • يجب التصريح عن المتغيرات التي تم تعديلها في المقاطعة على أنها متقلب
  • المقاطعات ليس لها تأخيرات مثل تأخير()
  • لا يغير قيمته في مقاطعة ميلي ()و ميكرو ()
  • في المقاطعة ، لا يعمل الإخراج إلى المنفذ بشكل صحيح ( Serial.print ()) ، وكذلك لا تستخدمه هناك - فهو يقوم بتحميل النواة
  • أثناء المقاطعة ، تحتاج إلى محاولة إجراء أقل قدر ممكن من العمليات الحسابية وبصفة عامة الإجراءات "الطويلة" - سيؤدي ذلك إلى إبطاء عمل MC مع الانقطاعات المتكررة! ما يجب القيام به؟ اقرأ أدناه.

إذا اكتشفت المقاطعة حدثًا لا يلزم معالجته على الفور ، فمن الأفضل استخدام خوارزمية معالجة المقاطعة التالية:

  • في معالج المقاطعة ، ما عليك سوى رفع العلم
  • في الحلقة الرئيسية للبرنامج ، نتحقق من العلم ، إذا تم رفعه ، نقوم بإعادة ضبطه وتنفيذ الإجراءات اللازمة
intFlag منطقي متطاير = خطأ ؛ // إعداد الفراغ () (Serial.begin (9600) ؛ // منفذ مفتوح للاتصال // زر متصل على D2 و GND pinMode (2 ، INPUT_PULLUP) ؛ // D2 is interrupt 0 // handler - buttonTick function // FALLING - عند الضغط على الزر ، ستكون الإشارة 0 ، ونلحقها بإرفاق Interrupt (0، buttonTick، FALLING)؛) زر باطلة () (intFlag = true؛ // رفع علامة المقاطعة) حلقة فارغة () (إذا (intFlag) (intFlag = false ؛ // إعادة تعيين // افعل شيئًا Serial.println ("مقاطعة!") ؛))

هذا هو كل ما تحتاج لمعرفته حول المقاطعات ، وأكثر من ذلك حالات محددةسنقوم بتغطيتها في دروس متقدمة.

فيديو

وسنقدم مثالاً على استخدام وظيفة Arduino إرفاق المقاطعة ().

المقاطعة هي إشارة تخبر المعالج بحدوث حدث يتطلب اهتمامًا فوريًا. يجب أن يستجيب المعالج لهذه الإشارة عن طريق مقاطعة تنفيذ التعليمات الحالية ونقل التحكم إلى معالج المقاطعة (ISR ، Interrupt Service Routine). المعالج هو وظيفة عادية نكتبها بأنفسنا ونضع فيها الكود الذي يجب أن يستجيب للحدث.

بعد خدمة مقاطعة ISR ، تنهي الوظيفة عملها ويعود المعالج بسعادة إلى الأنشطة المتقطعة - يستمر في تنفيذ الكود من المكان الذي توقف فيه. كل هذا يحدث تلقائيًا ، لذا فإن مهمتنا هي فقط كتابة معالج مقاطعة دون كسر أي شيء ودون إجبار المعالج على تشتيت انتباهنا كثيرًا. ستحتاج إلى فهم الدائرة ومبادئ تشغيل الأجهزة المتصلة وفكرة عن عدد المرات التي يمكن فيها استدعاء المقاطعة وما هي ميزات حدوثها. كل هذا هو الصعوبة الرئيسية في العمل مع المقاطعات.

مقاطعات الأجهزة والبرامج

يمكن تقسيم المقاطعات في Arduino إلى عدة أنواع:

  • مقاطعات الأجهزة. الانقطاع على مستوى بنية المعالجات الدقيقة. يمكن أن يحدث الحدث نفسه في لحظة مثمرة من جهاز خارجي- على سبيل المثال ، الضغط على زر على لوحة المفاتيح تتحرك فأرة الحاسوبوما إلى ذلك وهلم جرا.
  • المقاطعات البرمجية. تشغيل داخل البرنامج باستخدام تعليمات خاصة. تستخدم لاستدعاء معالج المقاطعة.
  • المقاطعات الداخلية (المتزامنة). تحدث مقاطعة داخلية نتيجة لتغيير أو انتهاك في تنفيذ البرنامج (على سبيل المثال ، عند الوصول إلى عنوان غير صالح ، أو كود تشغيل غير صالح ، وما إلى ذلك).

لماذا نحتاج مقاطعات الأجهزة

تحدث مقاطعات الأجهزة استجابةً لحدث خارجي وتأتي من جهاز خارجي. هناك 4 أنواع من مقاطعات الأجهزة في Arduino. كلهم يختلفون في الإشارة الموجودة على دبوس المقاطعة:

  • يتم سحب جهة الاتصال على الأرض. يتم تنفيذ معالج المقاطعة طالما أن طرف المقاطعة منخفض.
  • تغيير الإشارة على جهة اتصال. في هذه الحالة ، يقوم Arduino بتنفيذ معالج المقاطعة عند حدوث تغيير في الإشارة على دبوس المقاطعة.
  • تغيير الإشارة من LOW إلى HIGH على دبوس - عند التغيير من منخفض إلى مرتفع ، سيتم تنفيذ معالج المقاطعة.
  • تغيير الإشارة من HIGH إلى LOW على دبوس - عندما تتغير الإشارة من عالية إلى منخفضة ، سيتم تنفيذ معالج المقاطعة.

المقاطعات مفيدة في برامج Arduino لأنها تساعد في حل مشاكل التوقيت. على سبيل المثال ، عند العمل مع UART ، تسمح لك المقاطعات بعدم تتبع وصول كل حرف. يصدر الجهاز الخارجي إشارة مقاطعة ، ويستدعي المعالج على الفور معالج المقاطعة ، والذي يلتقط الحرف في الوقت المناسب. هذا يوفر وقت وحدة المعالجة المركزية ، والذي سيتم إنفاقه بدون مقاطعات في التحقق من حالة UART ، بدلاً من ذلك ، يتم تنفيذ جميع الإجراءات الضرورية بواسطة معالج المقاطعة دون التأثير على البرنامج الرئيسي. لا يلزم وجود قدرات خاصة من الجهاز.

الأسباب الرئيسية للاتصال بالمقاطعة هي:

  • تحديد تغير حالة المخرجات ؛
  • الموقت يقطع
  • مقاطعة البيانات عبر SPI ، I2C ، USART ؛
  • التناظرية إلى التحويل الرقمي ؛
  • الاستعداد لاستخدام إيبروم ، ذاكرة فلاش.

كيف يتم تنفيذ المقاطعات في Arduino

عند تلقي إشارة مقاطعة ، يتم تعليق العملية. يبدأ تنفيذ الوظيفة التي تم الإعلان عن تنفيذها عند الانقطاع. لا يمكن للوظيفة المُعلنة قبول قيم الإدخال وقيم الإرجاع عند الانتهاء. لا تؤثر المقاطعة على الكود نفسه في حلقة البرنامج الرئيسية. للعمل مع المقاطعات في Arduino ، استخدم وظيفة قياسية إرفاق المقاطعة ().

الفرق بين تنفيذ المقاطعات في لوحات Arduino المختلفة

اعتمادا على تنفيذ الأجهزة نموذج محدديحتوي المتحكم الدقيق على عدة مقاطعات. تحتوي لوحة Arduino Uno على مقاطعتين على المسامير 2 و 3 ، ولكن إذا كانت هناك حاجة إلى أكثر من مخرجات ، فإن اللوحة تدعم الوضع الخاصتغيير دبوس. يعمل هذا الوضع عن طريق تغيير المدخلات لجميع المسامير. يتمثل الاختلاف في وضع مقاطعة تغيير الإدخال في أنه يمكن إنشاء المقاطعات على أي من المسامير الثمانية. ستكون المعالجة في هذه الحالة أكثر تعقيدًا وأطول ، حيث سيتعين عليك تتبع الحالة الأخيرة لكل جهة اتصال.

في اللوحات الأخرى ، يكون عدد المقاطعات أعلى. على سبيل المثال ، تحتوي اللوحة على 6 دبابيس يمكنها التعامل مع المقاطعات الخارجية. بالنسبة لجميع لوحات Arduino ، عند العمل مع وظيفة attachInterrupt (مقاطعة ، وظيفة ، وضع) ، ترتبط وسيطة Inerrupt 0 بالدبوس الرقمي 2.

المقاطعات في لغة الأردوينو

الآن دعنا ننتقل إلى التدريب والتحدث عن كيفية استخدام المقاطعات في مشاريعك.

AttachInterrupt () بناء الجملة

تُستخدم وظيفة attachInterrupt (المقاطعة) للعمل مع المقاطعات. إنه يعمل على توصيل مقاطعة خارجية بمعالج.

صيغة المكالمة: attachInterrupt (interrupt، function، mode)

وسيطات الوظيفة:

  • المقاطعة - رقم المقاطعة الذي يتم استدعاؤه (قياسي 0 - للدبوس الثاني ، للوحة Arduino Uno 1 - للدبوس الثالث) ،
  • الوظيفة - اسم الوظيفة التي تم استدعاؤها عند مقاطعتها (مهمة - يجب ألا تقبل الوظيفة أي قيم أو تعيدها) ،
  • الوضع هو شرط بدء المقاطعة.

يمكن ضبط شروط الزناد التالية:

  • منخفض - يتم إجراؤه على مستوى إشارة منخفض ، عندما يكون لجهة الاتصال قيمة صفرية. يمكن تكرار المقاطعة بشكل دوري - على سبيل المثال ، عند الضغط على زر.
  • التغيير - في المقدمة ، تحدث المقاطعة عندما تتغير الإشارة من عالية إلى منخفضة أو العكس. يتم تنفيذه مرة واحدة عند تغيير أي إشارة.
  • RISING - قم بتنفيذ مقاطعة مرة واحدة عندما تتغير الإشارة من LOW إلى HIGH.
  • FALLING - قم بتنفيذ مقاطعة مرة واحدة عندما تتغير الإشارة من HIGH إلى LOW.4

ملاحظات هامة

عند العمل مع المقاطعات ، يجب مراعاة القيود المهمة التالية:

  • يجب ألا يستغرق تنفيذ وظيفة المعالج وقتًا طويلاً. الشيء هو أن Arduino لا يمكنه التعامل مع المقاطعات المتعددة في نفس الوقت. أثناء تنفيذ وظيفة المعالج ، سيتم تجاهل جميع المقاطعات الأخرى وقد تفوتك أحداث مهمة. إذا كنت بحاجة إلى القيام بشيء كبير ، فما عليك سوى تمرير معالجة الحدث في الحلقة الرئيسية (). في المعالج ، يمكنك فقط تعيين علامة الحدث ، وفي الحلقة ، يمكنك التحقق من العلم ومعالجته.
  • عليك أن تكون حذرا جدا مع المتغيرات. يمكن لمترجم C ++ الذكي "إعادة تحسين" برنامجك عن طريق إزالة المتغيرات التي لا يحتاجها. لن يرى المترجم ببساطة أنك تقوم بتعيين بعض المتغيرات في جزء واحد واستخدامها في جزء آخر. للقضاء على هذا الاحتمال في حالة أنواع البيانات الأساسية ، يمكنك استخدام كلمة رئيسيةمتقلب ، مثل: الحالة المنطقية المتطايرة = 0. لكن هذه الطريقة لن تعمل مع هياكل البيانات المعقدة. لذلك عليك أن تكون دائمًا في حالة تأهب.
  • لا ينصح باستخدام عدد كبير من المقاطعات (حاول ألا تستخدم أكثر من 6-8). يتطلب عدد كبير من الأحداث المختلفة تعقيدًا خطيرًا للشفرة ، وبالتالي يؤدي إلى حدوث أخطاء. بالإضافة إلى ذلك ، يجب على المرء أن يفهم أنه لا توجد دقة زمنية للتنفيذ في الأنظمة ذات كمية كبيرةلا يمكن أن يكون هناك انقطاع في الكلام - لن تفهم بالضبط الفجوة بين مكالمات الأوامر التي تهمك.
  • يجب عدم استخدام Delay () في المعالجات. تستخدم آلية تحديد الفاصل الزمني للتأخير أجهزة ضبط الوقت ، كما أنها تعمل أيضًا على المقاطعات التي سيحظرها المعالج. نتيجة لذلك ، سينتظر الجميع الجميع وسيتوقف البرنامج. للسبب نفسه ، لا يمكن استخدام بروتوكولات الاتصال القائمة على المقاطعة (مثل i2c).

أمثلة المقاطعة

دعنا نبدأ العمل ونلقي نظرة على أبسط مثالباستخدام المقاطعات. في المثال ، نحدد وظيفة المعالج التي ، عندما تتغير الإشارة الموجودة على الطرف 2 من Arduino Uno ، ستبدل حالة الدبوس 13 ، والتي سنقوم بتوصيل مؤشر LED بها تقليديًا.

#define PIN_LED 13 إجراء منطقي متغير الحالة = منخفض ؛ إعداد باطل () (pinMode (PIN_LED ، OUTPUT) ؛ // تعيين المقاطعة // سيتم استدعاء وظيفة myEventListener عندما تتغير الإشارة على الدبوس 2 (المقاطعة 0 متصلة بالدبوس 2) // تتغير الإشارة (لا المسألة في أي اتجاه) attachInterrupt (0، myEventListener، CHANGE)؛) حلقة فارغة () (// لا نفعل أي شيء في وظيفة الحلقة ، نظرًا لأن كل كود معالجة الحدث سيكون في وظيفة myEventListener) باطل myEventListener () (actionState! = actionState ؛ // / / قم بأشياء أخرى مثل تشغيل أو إيقاف تشغيل مؤشر LED للكتابة الرقمية (PIN_LED ، actionState) ؛)

لنلقِ نظرة على بعض الأمثلة على المقاطعات الأكثر تعقيدًا ومعالجاتها: للمؤقت والأزرار.

المقاطعات زر مكافحة الارتداد

عند حدوث مقاطعة - قبل أن تقوم جهات الاتصال بإجراء اتصال ثابت عند الضغط على الزر ، فإنها ستتذبذب ، مما يؤدي إلى توليد العديد من العمليات. هناك طريقتان للتعامل مع الثرثرة - الأجهزة ، أي عن طريق لحام مكثف بالزر ، والبرمجيات.

يمكنك التخلص من الثرثرة باستخدام الوظيفة - فهي تسمح لك باكتشاف الوقت المنقضي من أول تشغيل للزر.

If (digitalRead (2) == HIGH) (// عند الضغط على الزر // إذا مر أكثر من 100 مللي ثانية منذ الضغط السابق إذا (millis () - previousMillis> = 100) (// تذكر وقت العملية الأولى previousMillis = millis () ؛ إذا (led == oldled) (// يتحقق من أن حالة الزر لم تتغير led =! led ؛)

يسمح لك هذا الكود بإزالة الثرثرة ولا يمنع تنفيذ البرنامج كما هو الحال مع وظيفة التأخير التي لا يسمح بها في المقاطعات.

الموقت المقاطعات

الموقت هو عداد يحسب بتردد معين يتم الحصول عليه من المعالج 16 ميجاهرتز. يمكنك تكوين مقسم التردد للحصول عليه الوضع المطلوبحسابات. يمكنك أيضًا تكوين العداد لإنشاء المقاطعات عند الوصول إلى قيمة معينة.

وتسمح لك مقاطعة المؤقت بتنفيذ المقاطعة مرة واحدة لكل مللي ثانية. يحتوي Arduino على 3 مؤقتات - Timer0 و Timer1 و Timer2. يتم استخدام Timer0 لإنشاء المقاطعات مرة واحدة لكل مللي ثانية ، مما يؤدي إلى تحديث العداد الذي تم تمريره إلى وظيفة millis (). يتكون هذا المؤقت من ثماني بتات ويتراوح عددها من 0 إلى 255. يتم إنشاء مقاطعة عندما تكون القيمة 255. بشكل افتراضي ، يتم استخدام مقسم الساعة بمقدار 65 للحصول على تردد قريب من 1 كيلو هرتز.

تُستخدم سجلات المقارنة لمقارنة الحالة على المؤقت والبيانات المخزنة. في هذا المثال ، ستقوم الشفرة بإنشاء مقاطعة عندما يصل العداد إلى 0xAF.

TIMSK0 | = _BV (OCIE0A) ،

مطلوب تحديد معالج المقاطعة لمتجه المقاطعة المؤقت. متجه المقاطعة هو مؤشر إلى موقع التعليمات التي سيتم تنفيذها عند استدعاء المقاطعة. يتم دمج العديد من نواقل المقاطعة في جدول متجه للمقاطعة. سيكون للمؤقت في هذه الحالة اسم TIMER0_COMPA_vect. في هذا المعالج ، سيتم تنفيذ نفس الإجراءات كما في حلقة ().

SIGNAL (TIMER0_COMPA_vect) (تيار طويل غير موقعة Millis = millis () ؛ مكنسة 1.Update (currentMillis) ؛ if (digitalRead (2) == HIGH) (sweeper2.Update (currentMillis) ؛ led1.Update (currentMillis) ؛) led2.Update (currentMillis) ؛) currentMillis) ؛ led3.Update (currentMillis) ؛) // ستبقى وظيفة loop () فارغة. حلقة فارغة() ( )

تلخيص

يعد الانقطاع في Arduino موضوعًا معقدًا إلى حد ما ، لأنه عليك التفكير في بنية المشروع بالكامل في وقت واحد ، وتخيل كيفية تنفيذ الكود ، وما هي الأحداث المحتملة ، وما يحدث عند مقاطعة الكود الرئيسي. لم نهدف إلى الكشف عن جميع ميزات العمل مع بناء اللغة هذا ، كان الهدف الرئيسي هو تقديم حالات الاستخدام الرئيسية. في المقالات المستقبلية ، سنواصل الحديث عن المقاطعات بمزيد من التفصيل.

بشكل عام ، لا يدعم Arduino موازنة المهام الحقيقية أو تعدد مؤشرات الترابط. لكن هذا ممكن مع كل تكرار للدورة حلقة()قم بإرشاد المتحكم الدقيق للتحقق مما إذا كان الوقت قد حان لتنفيذ بعض المهام الإضافية في الخلفية. في هذه الحالة ، سيظهر للمستخدم أنه يتم تنفيذ العديد من المهام في وقت واحد.

على سبيل المثال ، دعنا نومض مؤشر LED بتردد معين ونصدر في نفس الوقت أصوات ارتفاع وهبوط مثل صفارات الإنذار من باعث بيزو. لقد قمنا بالفعل بتوصيل كل من LED وباعث بيزو بـ Arduino أكثر من مرة. دعونا نقوم بتجميع الدائرة ، كما هو موضح في الشكل.

إذا كنت تقوم بتوصيل LED بمسمار رقمي غير "13" ، فلا تنسى مقاومة التيار التي تصل إلى 220 أوم.

2 تحكم LED و Piezo Buzzerباستخدام عامل التأخير ()

لنكتب هذا الرسم التخطيطي ونقوم بتحميله على Arduino.

Const int soundPin = 3 ؛ / * أعلن عن متغير برقم الدبوس الذي يتصل به العنصر الكهرضغطية * / const int ledPin = 13 ؛ // أعلن عن متغير برقم دبوس LED الإعداد باطل()( pinMode (soundPin ، الإخراج) ؛ // أعلن الدبوس 3 كإخراج. pinMode (ledPin ، الإخراج) ؛ / / أعلن دبوس 13 كإخراج. } حلقة فارغة() (// التحكم في الصوت: النغمة (soundPin ، 700) ؛ // إصدار صوت بتردد 700 هرتز تأخير (200) ؛ نغمة (soundPin ، 500) ؛ // عند تأخير 500 هرتز (200) ؛ نغمة (soundPin ، 300) ؛ // عند تأخير 300 هرتز (200) ؛ نغمة (soundPin ، 200) ؛ // عند تأخير 200 هرتز (200) ؛ // تحكم LED: كتابة رقمية (ledPin ، عالية) ؛ // تأخير الحريق (200) ؛ digitalWrite (ledPin ، منخفض) ؛ // إطفاء تأخير (200) ؛ }

بعد تشغيله ، يمكن ملاحظة أن الرسم لا يتم تنفيذه تمامًا كما نحتاج: حتى يتم تشغيل صفارات الإنذار بالكامل ، لن يومض مؤشر LED ، ونود أن يومض مؤشر LED خلالصوت صفارات الانذار. ماهي المشكلة هنا؟

الحقيقة هي أن هذه المشكلة لا يمكن حلها بالطريقة المعتادة. يتم تنفيذ المهام بواسطة متحكم دقيق بشكل متسلسل. المشغل أو العامل تأخير()يؤخر تنفيذ البرنامج لفترة زمنية محددة ، وحتى انتهاء هذا الوقت ، لن يتم تنفيذ أوامر البرنامج التالية. لهذا السبب ، لا يمكننا تعيين مدة تنفيذ مختلفة لكل مهمة في الحلقة. حلقة()البرامج. لذلك ، تحتاج إلى محاكاة تعدد المهام بطريقة ما.

3 العمليات المتوازيةبدون عامل "تأخير ()"

اقترح مطورو Arduino الخيار الذي سيؤدي فيه Arduino المهام في شبه موازية. جوهر الطريقة هو أنه مع كل تكرار للدورة حلقة()نتحقق مما إذا كان وقت وميض LED (تنفيذ مهمة في الخلفية) أم لا. وإذا كان الأمر كذلك ، فإننا نعكس حالة LED. هذا نوع من عامل التجاوز تأخير().

Const int soundPin = 3 ؛ // متغير برقم دبوس عنصر كهرضغطية const int ledPin = 13 ؛ // متغير مع رقم دبوس LED const طويل ledInterval = 200 ؛ // الفاصل الزمني وامض LED ، مللي ثانية. int ledState = منخفض ؛ // الحالة الأوليةالمصابيح غير الموقعة منذ فترة طويلة ميليس = 0 ؛ // قم بتخزين وقت إطلاق LED السابق الإعداد باطل()( pinMode (soundPin ، الإخراج) ؛ // تعيين دبوس 3 كإخراج. pinMode (ledPin ، الإخراج) ؛ // تعيين دبوس 13 كإخراج. } حلقة فارغة() (// التحكم في الصوت: النغمة (soundPin ، 700) ؛ تأخير (200) ؛ نغمة (soundPin ، 500) ؛ تأخير (200) ؛ نغمة (soundPin ، 300) ؛ تأخير (200) ؛ نغمة (soundPin ، 200) ؛ تأخير (200) ؛ // Blink LED: // الوقت منذ تشغيل Arduino ، ms: تيار طويل غير موقعة Millis = millis () ؛ // إذا حان وقت الوميض ، إذا (currentMillis - previousMillis> = ledInterval) (previousMillis = currentMillis ؛ // ثم خزن الوقت الحالي إذا (ledState == LOW) (// وعكس حالة LED ledState = HIGH ؛) آخر (ledState = LOW ؛) digitalWrite (ledPin ، ledState) ؛ // تبديل حالة LED) }

عيب كبير هذه الطريقةهو أن قسم الكود قبل كتلة التحكم LED يجب أن يتم تنفيذه بشكل أسرع من الفاصل الزمني للوميض LED "ledInterval". خلاف ذلك ، سيحدث الوميض بشكل متكرر أقل من اللازم ، ولن نحصل على تأثير التنفيذ المتوازي للمهام. على وجه الخصوص ، في مخططنا ، تبلغ مدة التغيير في صوت صفارات الإنذار 200 + 200 + 200 + 200 = 800 مللي ثانية ، وقمنا بتعيين الفاصل الزمني الوامض لمصباح LED على 200 مللي ثانية. ولكن سيومض مؤشر LED في غضون 800 مللي ثانية ، وهو ما يعادل 4 أضعاف ما حددناه.

بشكل عام ، إذا كان الرمز يستخدم المشغل تأخير()، في هذه الحالة من الصعب محاكاة التوازي الزائف ، لذلك من المستحسن تجنبه.

في هذه الحالة ، سيكون من الضروري لوحدة التحكم في صوت صفارات الإنذار أيضًا التحقق مما إذا كان الوقت قد حان أم لا ، وعدم استخدامها تأخير(). لكن هذا من شأنه أن يزيد من كمية الكود ويزيد من قابلية قراءة البرنامج.

4 باستخدام مكتبة ArduinoThreadلإنشاء خيوط متوازية

لحل المشكلة ، سوف نستخدم مكتبة رائعة اردوينو الموضوع، والذي يسمح لك بإنشاء عمليات زائفة متوازية بسهولة. إنه يعمل بطريقة مماثلة ، لكنه يسمح لك بعدم كتابة رمز للتحقق من الوقت - تحتاج إلى إكمال المهمة في هذه الدورة أم لا. هذا يقلل من كمية الكود ويحسن قابلية قراءة الرسم التخطيطي. دعنا نتحقق من عمل المكتبة.


بادئ ذي بدء ، قم بتنزيل أرشيف المكتبة من الموقع الرسمي وفك ضغطه في الدليل مكتبات /بيئة تطوير Arduino IDE. ثم أعد تسمية المجلد اردوينو الموضوع الرئيسيالخامس اردوينو الموضوع.

سيبقى مخطط الأسلاك كما هو. فقط رمز البرنامج سوف يتغير.

#يشمل // توصيل مكتبة ArduinoThread const int soundPin = 3 ؛ // متغير برقم دبوس عنصر كهرضغطية const int ledPin = 13 ؛ // متغير مع رقم دبوس LED مؤشر ترابط LEDThread = Thread () ؛ // إنشاء مؤشر ترابط للتحكم في صوت مؤشر الترابط LED = Thread () ؛ // إنشاء تدفق تحكم لصفارات الإنذار الإعداد باطل()( pinMode (soundPin ، الإخراج) ؛ // أعلن الدبوس 3 كإخراج. pinMode (ledPin ، الإخراج) ؛ / / أعلن دبوس 13 كإخراج. ledThread.onRun (ledBlink) ؛ // تعيين مهمة إلى مؤشر الترابط ledThread.setInterval (1000) ؛ // ضبط فاصل الاستجابة ، ms soundThread.onRun (صوت) ؛ // إسناد مهمة إلى موضوع soundThread. setInterval (20) ؛ // تعيين فاصل الاستجابة ، مللي ثانية } حلقة فارغة() (// تحقق مما إذا كان الوقت قد حان لتبديل مؤشر LED: إذا (ledThread.shouldRun ()) ledThread.run () ؛ // بدء الخيط // تحقق مما إذا كان الوقت قد حان لتغيير نغمة صفارة الإنذار: إذا (soundThread.shouldRun ()) soundThread.run () ؛ // بدء الموضوع } // تدفق الصمام: ledBlink () باطل ()ثابت منطقي ledStatus = خطأ ؛ // حالة LED تشغيل / إيقاف ledStatus =! ledStatus ؛ // قلب الحالة الرقمية للكتابة (ledPin ، ledStatus) ؛ // تشغيل / إيقاف تشغيل LED } // تيار صفارة الإنذار: صوت باطل () (نغمة كثافة العمليات الثابتة = 100 ؛ // نغمة الصوت ، نغمة هرتز (soundPin ، طن) ؛ // قم بتشغيل صفارات الإنذار عند "طن" هرتز إذا (طن)

في البرنامج ، نقوم بإنشاء خيطين - ledThreadو soundThread، كل منها يقوم بعمله الخاص: أحدهما يومض مؤشر LED ، والثاني يتحكم في صوت صفارة الإنذار. في كل تكرار للحلقة لكل مؤشر ترابط ، نتحقق مما إذا كان وقت تنفيذه قد حان أم لا. إذا وصل ، يتم تشغيله للتنفيذ باستخدام الطريقة يجري(). الشيء الرئيسي هو عدم استخدام عامل التشغيل تأخير(). يتم تقديم تفسيرات أكثر تفصيلاً في الكود.


قم بتحميل الكود في ذاكرة Arduino ، وقم بتشغيله. الآن كل شيء يعمل تمامًا كما ينبغي!

بشكل عام ، لا يدعم Arduino موازنة المهام الحقيقية أو تعدد مؤشرات الترابط. لكن هذا ممكن مع كل تكرار للدورة حلقة()قم بإرشاد المتحكم الدقيق للتحقق مما إذا كان الوقت قد حان لتنفيذ بعض المهام الإضافية في الخلفية. في هذه الحالة ، سيظهر للمستخدم أنه يتم تنفيذ العديد من المهام في وقت واحد.

على سبيل المثال ، دعنا نومض مؤشر LED بتردد معين ونصدر في نفس الوقت أصوات ارتفاع وهبوط مثل صفارات الإنذار من باعث بيزو. لقد قمنا بالفعل بتوصيل كل من LED وباعث بيزو بـ Arduino أكثر من مرة. دعونا نقوم بتجميع الدائرة ، كما هو موضح في الشكل.

إذا كنت تقوم بتوصيل LED بمسمار رقمي غير "13" ، فلا تنسى مقاومة التيار التي تصل إلى 220 أوم.

2 تحكم LED و Piezo Buzzerباستخدام عامل التأخير ()

لنكتب هذا الرسم التخطيطي ونقوم بتحميله على Arduino.

Const int soundPin = 3 ؛ / * أعلن عن متغير برقم الدبوس الذي يتصل به العنصر الكهرضغطية * / const int ledPin = 13 ؛ // أعلن عن متغير برقم دبوس LED الإعداد باطل()( pinMode (soundPin ، الإخراج) ؛ // أعلن الدبوس 3 كإخراج. pinMode (ledPin ، الإخراج) ؛ / / أعلن دبوس 13 كإخراج. } حلقة فارغة() (// التحكم في الصوت: النغمة (soundPin ، 700) ؛ // إصدار صوت بتردد 700 هرتز تأخير (200) ؛ نغمة (soundPin ، 500) ؛ // عند تأخير 500 هرتز (200) ؛ نغمة (soundPin ، 300) ؛ // عند تأخير 300 هرتز (200) ؛ نغمة (soundPin ، 200) ؛ // عند تأخير 200 هرتز (200) ؛ // تحكم LED: كتابة رقمية (ledPin ، عالية) ؛ // تأخير الحريق (200) ؛ digitalWrite (ledPin ، منخفض) ؛ // إطفاء تأخير (200) ؛ }

بعد تشغيله ، يمكن ملاحظة أن الرسم لا يتم تنفيذه تمامًا كما نحتاج: حتى يتم تشغيل صفارات الإنذار بالكامل ، لن يومض مؤشر LED ، ونود أن يومض مؤشر LED خلالصوت صفارات الانذار. ماهي المشكلة هنا؟

الحقيقة هي أن هذه المشكلة لا يمكن حلها بالطريقة المعتادة. يتم تنفيذ المهام بواسطة متحكم دقيق بشكل متسلسل. المشغل أو العامل تأخير()يؤخر تنفيذ البرنامج لفترة زمنية محددة ، وحتى انتهاء هذا الوقت ، لن يتم تنفيذ أوامر البرنامج التالية. لهذا السبب ، لا يمكننا تعيين مدة تنفيذ مختلفة لكل مهمة في الحلقة. حلقة()البرامج. لذلك ، تحتاج إلى محاكاة تعدد المهام بطريقة ما.

3 العمليات المتوازيةبدون عامل "تأخير ()"

اقترح مطورو Arduino الخيار الذي سيؤدي فيه Arduino المهام في شبه موازية. جوهر الطريقة هو أنه مع كل تكرار للدورة حلقة()نتحقق مما إذا كان وقت وميض LED (تنفيذ مهمة في الخلفية) أم لا. وإذا كان الأمر كذلك ، فإننا نعكس حالة LED. هذا نوع من عامل التجاوز تأخير().

Const int soundPin = 3 ؛ // متغير برقم دبوس عنصر كهرضغطية const int ledPin = 13 ؛ // متغير مع رقم دبوس LED const طويل ledInterval = 200 ؛ // الفاصل الزمني وامض LED ، مللي ثانية. int ledState = منخفض ؛ // الحالة الأولية للـ LED غير الموقعة منذ فترة طويلة ميليس = 0 ؛ // قم بتخزين وقت إطلاق LED السابق الإعداد باطل()( pinMode (soundPin ، الإخراج) ؛ // تعيين دبوس 3 كإخراج. pinMode (ledPin ، الإخراج) ؛ // تعيين دبوس 13 كإخراج. } حلقة فارغة() (// التحكم في الصوت: النغمة (soundPin ، 700) ؛ تأخير (200) ؛ نغمة (soundPin ، 500) ؛ تأخير (200) ؛ نغمة (soundPin ، 300) ؛ تأخير (200) ؛ نغمة (soundPin ، 200) ؛ تأخير (200) ؛ // Blink LED: // الوقت منذ تشغيل Arduino ، ms: تيار طويل غير موقعة Millis = millis () ؛ // إذا حان وقت الوميض ، إذا (currentMillis - previousMillis> = ledInterval) (previousMillis = currentMillis ؛ // ثم خزن الوقت الحالي إذا (ledState == LOW) (// وعكس حالة LED ledState = HIGH ؛) آخر (ledState = LOW ؛) digitalWrite (ledPin ، ledState) ؛ // تبديل حالة LED) }

من العيوب المهمة لهذه الطريقة أن قسم الكود قبل كتلة التحكم في LED يجب أن يتم تنفيذه بشكل أسرع من الفاصل الزمني للوميض "ledInterval". خلاف ذلك ، سيحدث الوميض بشكل متكرر أقل من اللازم ، ولن نحصل على تأثير التنفيذ المتوازي للمهام. على وجه الخصوص ، في مخططنا ، تبلغ مدة التغيير في صوت صفارات الإنذار 200 + 200 + 200 + 200 = 800 مللي ثانية ، وقمنا بتعيين الفاصل الزمني الوامض لمصباح LED على 200 مللي ثانية. ولكن سيومض مؤشر LED في غضون 800 مللي ثانية ، وهو ما يعادل 4 أضعاف ما حددناه.

بشكل عام ، إذا كان الرمز يستخدم المشغل تأخير()، في هذه الحالة من الصعب محاكاة التوازي الزائف ، لذلك من المستحسن تجنبه.

في هذه الحالة ، سيكون من الضروري لوحدة التحكم في صوت صفارات الإنذار أيضًا التحقق مما إذا كان الوقت قد حان أم لا ، وعدم استخدامها تأخير(). لكن هذا من شأنه أن يزيد من كمية الكود ويزيد من قابلية قراءة البرنامج.

4 باستخدام مكتبة ArduinoThreadلإنشاء خيوط متوازية

لحل المشكلة ، سوف نستخدم مكتبة رائعة اردوينو الموضوع، والذي يسمح لك بإنشاء عمليات زائفة متوازية بسهولة. إنه يعمل بطريقة مماثلة ، لكنه يسمح لك بعدم كتابة رمز للتحقق من الوقت - تحتاج إلى إكمال المهمة في هذه الدورة أم لا. هذا يقلل من كمية الكود ويحسن قابلية قراءة الرسم التخطيطي. دعنا نتحقق من عمل المكتبة.


بادئ ذي بدء ، قم بتنزيل أرشيف المكتبة من الموقع الرسمي وفك ضغطه في الدليل مكتبات /بيئة تطوير Arduino IDE. ثم أعد تسمية المجلد اردوينو الموضوع الرئيسيالخامس اردوينو الموضوع.

سيبقى مخطط الأسلاك كما هو. فقط رمز البرنامج سوف يتغير.

#يشمل // توصيل مكتبة ArduinoThread const int soundPin = 3 ؛ // متغير برقم دبوس عنصر كهرضغطية const int ledPin = 13 ؛ // متغير مع رقم دبوس LED مؤشر ترابط LEDThread = Thread () ؛ // إنشاء مؤشر ترابط للتحكم في صوت مؤشر الترابط LED = Thread () ؛ // إنشاء تدفق تحكم لصفارات الإنذار الإعداد باطل()( pinMode (soundPin ، الإخراج) ؛ // أعلن الدبوس 3 كإخراج. pinMode (ledPin ، الإخراج) ؛ / / أعلن دبوس 13 كإخراج. ledThread.onRun (ledBlink) ؛ // تعيين مهمة إلى مؤشر الترابط ledThread.setInterval (1000) ؛ // ضبط فاصل الاستجابة ، ms soundThread.onRun (صوت) ؛ // إسناد مهمة إلى موضوع soundThread. setInterval (20) ؛ // تعيين فاصل الاستجابة ، مللي ثانية } حلقة فارغة() (// تحقق مما إذا كان الوقت قد حان لتبديل مؤشر LED: إذا (ledThread.shouldRun ()) ledThread.run () ؛ // بدء الخيط // تحقق مما إذا كان الوقت قد حان لتغيير نغمة صفارة الإنذار: إذا (soundThread.shouldRun ()) soundThread.run () ؛ // بدء الموضوع } // تدفق الصمام: ledBlink () باطل ()ثابت منطقي ledStatus = خطأ ؛ // حالة LED تشغيل / إيقاف ledStatus =! ledStatus ؛ // قلب الحالة الرقمية للكتابة (ledPin ، ledStatus) ؛ // تشغيل / إيقاف تشغيل LED } // تيار صفارة الإنذار: صوت باطل () (نغمة كثافة العمليات الثابتة = 100 ؛ // نغمة الصوت ، نغمة هرتز (soundPin ، طن) ؛ // قم بتشغيل صفارات الإنذار عند "طن" هرتز إذا (طن)

في البرنامج ، نقوم بإنشاء خيطين - ledThreadو soundThread، كل منها يقوم بعمله الخاص: أحدهما يومض مؤشر LED ، والثاني يتحكم في صوت صفارة الإنذار. في كل تكرار للحلقة لكل مؤشر ترابط ، نتحقق مما إذا كان وقت تنفيذه قد حان أم لا. إذا وصل ، يتم تشغيله للتنفيذ باستخدام الطريقة يجري(). الشيء الرئيسي هو عدم استخدام عامل التشغيل تأخير(). يتم تقديم تفسيرات أكثر تفصيلاً في الكود.


قم بتحميل الكود في ذاكرة Arduino ، وقم بتشغيله. الآن كل شيء يعمل تمامًا كما ينبغي!

قد تكون هناك حاجة إلى عدة مقاطعات أثناء تنفيذ المشروع ، ولكن إذا كان لكل منها الأولوية القصوى ، فلن يكون لها في الواقع أي من الوظائف. للسبب نفسه ، لا ينصح باستخدام أكثر من اثني عشر مقاطعة.

يجب تطبيق المعالجات فقط على تلك العمليات ذات الحساسية القصوى للفترات الزمنية. لا تنس أنه أثناء وجود البرنامج في معالج المقاطعة ، يتم تعطيل جميع المقاطعات الأخرى. يؤدي عدد كبير من الانقطاعات إلى تدهور استجابتها.

في الوقت الذي تكون فيه إحدى المقاطعات نشطة ، ويتم تعطيل المقاطعات الأخرى ، اثنان الفروق الدقيقة الهامةليأخذها مصمم الدوائر في الاعتبار. أولاً ، يجب أن يكون وقت الانقطاع قصيرًا قدر الإمكان.

سيضمن ذلك عدم تفويت جميع المقاطعات المجدولة الأخرى. ثانيًا ، عند التعامل مع المقاطعة كود البرمجةيجب ألا يتطلب نشاطًا من المقاطعات الأخرى. إذا لم يتم منع ذلك ، فسيتجمد البرنامج ببساطة.

لا تستخدم معالجة مطولة حلقة() ، من الأفضل تطوير رمز لمعالج المقاطعة مع ضبط المتغير على متغير. سيخبر البرنامج أنه لا حاجة إلى مزيد من المعالجة.

إذا كانت المكالمة وظيفة تحديث() ضروري ، ستحتاج أولاً إلى التحقق من متغير الحالة. سيحدد هذا ما إذا كانت المعالجة الإضافية ضرورية.

قبل أن تبدأ في تكوين المؤقت ، يجب عليك التحقق من الرمز. يجب أن تُعزى مؤقتات Anduino إلى موارد محدودة ، لأن هناك ثلاثة منها فقط ، ويتم استخدامها لأداء أكثر وظائف مختلفة. إذا كنت مرتبكًا مع استخدام أجهزة ضبط الوقت ، فقد يتوقف عدد من العمليات عن العمل.

ما هي الوظائف التي يعمل عليها مؤقت معين؟

بالنسبة إلى متحكم Arduino Uno ، كل من العدادات الثلاثة لها عملياتها الخاصة.

لذا الموقت 0 هو المسؤول عن PWM على وظائف الدبوس الخامس والسادس ميلي () , ميكرو () , تأخير() .

مؤقت آخر - جهاز ضبط الوقت 1 ، تستخدم مع PWM على الدبوس التاسع والعاشر ، مع المكتبات WaveHC و Servo.

الموقت 2 يعمل مع PWM على الدبابيس 11 و 13 ، وكذلك مع نغمة.

يجب أن يعتني مصمم الدائرة بالاستخدام الآمن للبيانات المشتركة. بعد كل شيء ، توقف المقاطعة جميع عمليات المعالج لمدة مللي ثانية ، وتوقف تبادل البيانات بين حلقة() ويجب أن تكون معالجات المقاطعة دائمة. قد تنشأ حالة عند المترجم ، من أجل تحقيق ما يخصه الاداء العاليسيبدأ في تحسين الكود.

ستكون نتيجة هذه العملية حفظ نسخة من متغيرات الكود الرئيسية في السجل ، مما يضمن أقصى سرعة للوصول إليها.

يمكن أن يكون الجانب السلبي لهذه العملية هو استبدال القيم الفعلية بالنسخ المحفوظة ، مما قد يؤدي إلى فقدان الوظائف.

لمنع حدوث ذلك ، تحتاج إلى استخدام متغير متقلبة , مما سيساعد في منع التحسينات غير الضرورية. عند استخدام المصفوفات الكبيرة التي تتطلب دورات للتحديثات ، تحتاج إلى تعطيل المقاطعات في وقت هذه التحديثات.