Arduino/volatile
تصنَّف الكلمة volatile المفتاحية من «مقيدات المتغيرات» (variable qualifier)، وتُستعمَل عادةً قبل نوع المتغير عند تعريفه لتعديل الطريقة التي يعامل فيها المصرِّف والبرنامج اللاحق هذا المتغير.
تمثِّل الكلمة volatile المفتاحية عند استعمالها في تعريف متغير توجيهًا (directive) للمصرِّف نفسه. المصرِّف هو برنامج وظيفته تحويل الشيفرة المكتوبة بلغة ++C/C إلى شيفرة تنفيذية يفهمها العتاد؛ تحوي هذه الشيفرة التنفيذية على أوامر فعلية للمتحكم الموجود على لوحة أردينو تخبره بما يتوجب عليه فعله.
الأمر الذي تفعله الكلمة volatile
المفتاحية مع المتغير هو توجيه المصرِّف إلى تحميل المتغير من الذاكرة RAM وليس من سجلات التخزين للذاكرة المؤقتة (الذاكرة flash) حيث تُخزَّن جميع متغيرات البرنامج. في بعض الحالات، القيمة المخزَّنة في المسجلات لمتغيرٍ ما قد تكون غير دقيقة.
يجب أن يعرَّف المتغير مع الكلمة volatile المفتاحية متى ما كان بالإمكان تغيير قيمته عبر شيء يتجاوز تحكم جزء الشيفرة الذي يظهر هذا المتغير ضمنه، مثل تنفيذ خيط في وقت واحد. في أردوينو، المكان الوحيد الذي يحتمل أن يحدث فيه هذا الأمر هو أجزاء الشيفرة المرتبطة بالمقاطعات والتي تدعى ببرامج خدمة المقاطعة الفرعية (ISR).
الأعداد الصحيحة المتطايرة أم الطويل المتطايرة؟
إن استعملت الكلمة volatile مع متغير يزيد حجمه عن بايت واحد (2 بايت مثل العدد الصحيح، أو 4 بايت مثل العدد الطويل ...إلخ.)، فلن يتمكن المتحكم من قراءته في خطوة واحدة لأنَّ معمارية المتحكم هي 8 بت. هذا يعني أنَّه بينما يقرأ جزء الشيفرة (مثل الجزء loop()) أول ثمانية بتات من المتغير، قد تكون المقاطعة قد غيِّرت مسبقًا البتات الثمانية الأخرى (في حال كان حجم المتغير 16 بت). سيؤدي هذا إلى الحصول على قيمة عشوائية للمتغير.
حلُّ هذه المشكلة هو تعطيل المقاطعات أثناء قراءة قيم المتغيرات، وبذلك لا تتغير قيمة بتات المتغير آثناء قراءتها. يوجد عدة طرق لفعل ذلك منها:
- استعمال الدالة noInterrupts، أو
- استعمال ATOMIC_BLOCK، إذ العمليات الذرية (atomic operations) هي عمليات مفردة يجريها المتحكم (MCU) وتمثِّل أصغر وحدة محتملة.
أمثلة
تبديل حالة رجل متصلة مع ضوء ليد عند حدوث مقاطعة على الرجل 2:
int pin = 13;
volatile byte state = LOW;
void setup()
{
pinMode(pin, OUTPUT);
attachInterrupt(digitalPinToInterrupt(2), blink, CHANGE);
}
void loop()
{
digitalWrite(pin, state);
}
void blink()
{
state = !state;
}