
اردوينو لقد أحدثت ثورة في عالم الإلكترونيات بفضل السهولة التي يمكن بها إنشاء النماذج الأولية والمشاريع الوظيفية. ومع ذلك، بالنسبة لأولئك الذين يريدون أخذ برمجتهم إلى خطوة أبعد وتحسين الموارد والحفاظ على نظافة الكود والحصول على الكفاءة، وحدات الماكرو تصبح أداة رئيسية.
في هذه المقالة سوف نتعمق في استخدام وحدات الماكرو في اردوينو:ما هي، وكيف يتم استخدامها، ومزاياها وقيودها. وسوف نقوم بذلك من خلال جمع المعلومات الأكثر شمولاً وفائدة من أفضل المصادر المتاحة عبر الإنترنت، وإعادة كتابتها بطريقة واضحة وحديثة لتكون عملية حقًا.
ما هي وحدات الماكرو في اردوينو؟
الماكرو هي توجيهات المعالج المسبق في C/C++ التي تسمح لك باستبدال النص قبل تجميع الكود. بدلاً من تنفيذ التعليمات مثل الوظيفة التقليدية، يعمل الماكرو عن طريق استبدال أجزاء من النص المصدر، الذي يحتوي على تأثير مباشر على كيفية إنشاء الكود الثنائي النهائي.
المعالج المسبق يتم تشغيله قبل التجميع الفعلي، وهو المسؤول عن تطبيق هذه الاستبدالات. في Arduino، هذا يسمح من تعريف الثوابت، قم بتضمين الملفات بشكل مشروط أو حتى قم بإنشاء ملفات صغيرة ميزات على الانترنت التي توفر الوقت والذاكرة.
مثال أساسي: تعريف مثل #define LED_PIN 13 يؤدي إلى استبدال كافة التعليمات البرمجية تلقائيًا LED_PIN بواسطة 13 قبل التجميع.
قد يبدو هذا تافهاً، لكنه يقدم طريقة قوية لكتابة أكواد أكثر مرونة وقابلية للصيانة.
مزايا استخدام وحدات الماكرو
إن تنفيذ وحدات الماكرو في مشاريع Arduino قد يوفر عددًا من الفوائد المحددة:
- تحسين قابلية قراءة الكود: من خلال إعادة استخدام الأسماء الرمزية، يصبح من الأسهل فهم غرض كل عنصر.
- تحسين الأداء: من خلال عدم إنشاء مكالمات وظيفية، يمكن للماكرو تنفيذ العمليات بشكل أسرع.
- تقليل استخدام ذاكرة الوصول العشوائي: مفيد بشكل خاص على اللوحات ذات الموارد المحدودة، مثل Arduino UNO.
- يسمح بالتكيفات الشرطية: من الممكن تجميع أجزاء مختلفة من التعليمات البرمجية اعتمادًا على نوع لوحة Arduino المستخدمة.
وحدات الماكرو الأساسية: استخدام #define
التوجيه # تعريف هو الأكثر استخداما. يتم استخدامه لكلا من تحديد القيم الثابتة كما لو أن إنشاء وظائف تلقائية محقونة في وقت ما قبل التجميع.
المثال 1: تعريف الدبوس
#define PINLED 13
void setup() {
pinMode(PINLED, OUTPUT);
}
void loop() {
digitalWrite(PINLED, HIGH);
delay(500);
digitalWrite(PINLED, LOW);
delay(500);
}
المثال 2: الماكرو كدالة مضمنة
int itemCounter = 0;
#define COUNT_ITEM() do { itemCounter++; } while(0)
void setup() {
Serial.begin(9600);
COUNT_ITEM();
COUNT_ITEM();
}
void loop() {
Serial.println(itemCounter);
}
كما ترون، استخدام النمط افعل { … } بينما(0) يضمن أن الماكرو يتصرف بشكل آمن حتى إذا تم استخدامه داخل الهياكل الشرطية.
## تسلسل المشغل والماكرو
يعد عامل ## أداة معالجة مسبقة قوية. الذي يسمح بربط المعرفات. يعد هذا مفيدًا جدًا عندما تريد إنشاء أسماء المتغيرات بشكل ديناميكي.
مثال عملي:
#define GENERAR_VARIABLE(no) \
int var##no = no;
void setup() {
GENERAR_VARIABLE(3); // crea int var3 = 3
}
ملاحظة هامة: هذا المشغل غير متوافق مع جميع نماذج لوحة Arduino على حد سواء. على سبيل المثال، قد يعمل بشكل جيد على Uno أو Esplora، لكنه قد يفشل على Mega. بالإضافة إلى ذلك، لا يمكنك تعشيش إنشاء الماكرو داخل وحدات الماكرو الأخرى باستخدام ## بشكل مباشر.
وحدات الماكرو وتوفير الذاكرة
أحد المزايا الرئيسية لاستخدام وحدات الماكرو في Arduino هو توفير ذاكرة الوصول العشوائي (RAM). تتمتع Arduino بسعة محدودة، لذا فإن تحميل سلاسل النصوص مباشرة إلى ذاكرة الوصول العشوائي (RAM) يمكن أن يصبح مشكلة كبيرة.
تتضمن إحدى التقنيات المتقدمة لتجنب ذلك استخدام القوة_المضمنة وتحميل السلاسل من ذاكرة البرنامج (PROGMEM):
#include <HardwareSerial.h>
#define MYSERIAL Serial
#define FORCE_INLINE __attribute__((always_inline)) inline
FORCE_INLINE void printFromFlash(const char *str) {
char ch = pgm_read_byte(str);
while (ch) {
MYSERIAL.write(ch);
ch = pgm_read_byte(++str);
}
}
#define SERIAL_LOG(x) (MYSERIAL.print(x))
#define SERIAL_LOGLN(x) (MYSERIAL.println(x))
إن استخدام هذه الماكروات قد يحدث فرقًا بين نجاح المشروع أو فشله، وخاصةً في التطبيقات التي تحتوي على شاشات أو أجهزة استشعار متعددة.
وحدات الماكرو المدمجة مع الوظائف
يمكن للماكرو أيضًا تسهيل استدعاء الوظائف بشكل ديناميكي، استنادًا إلى النوع الذي تم تمريره كمعلمة. ومن الأمثلة الواضحة والمباشرة:
#define FUNC_LENTA(tipo) \
{ funcion_##tipo##_lenta(); }
#define FUNC_RAPIDA(tipo) \
{ funcion_##tipo##_rapida(); }
void funcion_caminar_lenta() {
Serial.println("Andando despacio");
}
void funcion_caminar_rapida() {
Serial.println("Andando rápido");
}
void setup() {
Serial.begin(9600);
FUNC_LENTA(caminar);
}
void loop() {
FUNC_RAPIDA(caminar);
}
بفضل عامل ## والماكرو، يمكننا تجنب تكرار الهياكل وتركيز المنطق الديناميكي..
وحدات الماكرو مع معلمات الإخراج
من الممكن أيضًا استخدام وحدات الماكرو لتغليف الكائنات الصغيرة أو التحويلات:
#define BOOL_OUT() (bool){false}
#define NUM_OUT(a,b) (float){a+b}
#define STR_OUT(msg) (String){msg}
void loop() {
Serial.println(BOOL_OUT());
Serial.println(NUM_OUT(1.2, 3.4));
Serial.println(STR_OUT("Mensaje"));
}
الممارسات الجيدة والاحتياطات مع وحدات الماكرو
يمكن أن يؤدي استخدام وحدات الماكرو بشكل مفرط أو بلا مبالاة إلى أخطاء يصعب تصحيحها. على سبيل المثال، عن طريق إجراء استبدالات غير صحيحة أو تعريف أسماء تتعارض مع أسماء في المكتبات الخارجية.
بعض القواعد الأساسية لتجنب المشاكل:
- تجنب المسافات غير الضرورية أو فواصل الأسطر داخل الماكرو.
- لا تقم بإدراج التعليقات داخل وحدات الماكرو المعقدة التي تستخدم أسطرًا متعددة.
- استخدم أسماء فريدة أو مع البادئات (مثل اسم المشروع) لتجنب التعارضات.
- استبدال وحدات الماكرو بثوابت أو وظائف حقيقية كلما كان ذلك ممكنا. تتيح لغة C++ الحديثة بدائل أنظف وأكثر أمانًا.
من ناحية أخرى، قد يؤدي الإفراط في استخدام وحدات الماكرو إلى تقليل وضوح التعليمات البرمجية. ينبغي أن يكون الهدف هو تحسين الكفاءة والوحدات النمطية دون المساس بإمكانية الصيانة.
التوجيهات الشرطية والتجميع التكيفي
أحد أكثر الوظائف العملية في المشاريع القابلة للتطوير هو استخدام وحدات الماكرو إنشاء الكود بشكل مشروط، وهو أمر مفيد للغاية عندما تريد أن يعمل نفس الرسم التخطيطي على لوحات مختلفة.
مثال نموذجي:
#ifdef ARDUINO_MEGA
#define LEDPIN 53
#else
#define LEDPIN 13
#endif
كما أنه مفيد أيضًا للتحكم في تصحيح الأخطاء أو عرض رسائل المترجم باستخدام #رسالة براغما أو حتى توليد أخطاء في ظل ظروف معينة مع #خطأ.
وحدات الماكرو الداخلية للمترجم
يتضمن معالج GCC المسبق لـ AVR (المستخدم في Arduino) العديد من وحدات ماكرو خاصة توفر معلومات النظام، مفيدة جدًا أثناء التطوير:
- __خط__: رقم السطر الحالي.
- __ملف__: اسم الملف الحالي.
- __الوقت__ و __التاريخ__:وقت وتاريخ التجميع.
- __وظيفة__: اسم الوظيفة الحالية.
إنها تسمح بالتحكم في الإصدارات وهياكل السجل وتسهل الصيانة وتتبع الأخطاء دون انتهاك الكود الرئيسي.
توفر وحدات الماكرو طريقة قوية ومرنة لتنظيم مشاريع Arduino. إنها تسمح لك بتحديد الثوابت، حفظ الذاكرة, تكييف الكود اعتمادًا على بيئة التنفيذ وإنشاء كتل قابلة لإعادة الاستخدام دون تكرار الأسطر. وبطبيعة الحال، فإنها تتطلب الانضباط والوضوح والمعرفة لتجنب الأخطاء الدقيقة أو فقدان القابلية للقراءة. عند تطبيقها بشكل صحيح، فهي تشكل أصلًا لا يقدر بثمن للمطورين المتوسطين والمتقدمين.