تابع yield وصف Printable ومصفوفات التقابل : مزايا مهمة وغير معروفة في نواة الأردوينو
إن نواة الأردوينو Arduino core، وهي الملفات المصدرية لتوابع وصفوف classes الأردوينو، تملك بعض المزايا التي يمكن استخدامها بكفاءة. وعلى اعتبار أن التوثيق الرسمي للأردوينو لم يذكرها (على الأقل حتى وقت نشر هذه المدونة المصغّرة) فإن هذه المزايا ليست معروفة بشكل كبير لدى مطوري الأردوينو. دعونا نناقش كل ميزة على حدى.
Printable Class : كيف يمكن أن تجعل أي غرض object قابل للطبع
بدايةً، إن التوابع print() وprintln() والمستخدمة في غرض object مثل Serial من الصف class HardwareSerial هي موروثة inherited من صف يسمى Print (بالواقع ليست كوراثة مباشرة وإنما وراثة عبر صف آخر يدعى stream والذي يرث بدوره الصف Print). لجعل هذين التابعين قابلين على طباعة أي غرض من صف جديد فإن الصف Print يرث صف آخر يدعى Printable والذي يتألف من تابع واحد ووحيد عام public method ويسمى printTo. هذا التابع من النوع virtual وبالتالي يمكن لأي صف جديد يرث هذا الصف أن يكون لديه تعريفه الخاص لهذا التابع. يمكن مراجعة مفهوم الوراثة والتوابع الافتراضية من دروس موقع GeeksforGeeks .
class Printable { public: virtual size_t printTo(Print& p) const = 0; };
على الجانب الآخر، فإن الصف Print لديه نسخة محمّلة overloaded من التابعين print و println والتي تستخدم printTo.
size_t Print::print(const Printable& x) { return x.printTo(*this); } size_t Print::println(const Printable& x) { size_t n = print(x); n += println(); return n; }
كتجميع لكل ما سبق سنكتب مثال توضيحي لكيفية الاستفادة من هذه الميزة.
class foo : public Printable{ public: foo::foo(char f1,char f2) { __bar += f1; __bar += f2; } size_t printTo(Print& p) const{ return p.print(__bar); } private: String __bar; }; void setup() { // put your setup code here, to run once: foo chrlnk('A','T'); Serial.begin(9600); Serial.println(chrlnk); } void loop() { }
من المفيد أيضاً معرفة أن هذه الخدعة مستخدمة أيضاً في مكتبة IPAddress.h من أجل طباعة العنوان وذلك في الملفIPAddress.cpp.
Yield Function: تابع يستمر بالتنفيذ عندما يكون الأردوينو في حالة انتظار
أثناء تصفح الملفات المصدريّة للأردوينو وجدت ملف باسم غريب يسمى hooks.c ويحوي أسطر قليلة جداً بداخلة.
static void __empty() { // Empty } void yield(void) __attribute__ ((weak, alias("__empty")));
من التعليقات التي في الملف، إن التابع الضعيف yield (أي الذي يمكن إعادة تعريفه في ملف مصدري آخر) يبدو مهيأ ليتم استخدامه في الجدولة scheduling. ولكن من أجل دارات الأردوينو المعتمدة على متحكمات AVR، فإن هذا التابع غير مستخدم سوى في مكان وحيد ضمن تابع delay في ملف wiring.c.
void delay(unsigned long ms) { uint32_t start = micros(); while (ms > 0) { yield(); while ( ms > 0 && (micros() - start) >= 1000) { ms--; start += 1000; } } }
أحد الاستخدامات المفيدة لهذا التابع yield هي من خلال إعادة تعريفه في الكود الرئيسي بحيث يحتوي عمليات حساسة يجب على الأردوينو تنفيذها خلال عملية الانتظار delay أو حتى توصيف مجدول بسيط simple scheduler.
Mapping Arrays: مصفوفات مقابلة لأرجل الأردوينو مع نظيرها الفيزيائي في المتحكم
بسّط فريق الأردوينو الطريقة التي يتم النفاذ فيها على مسجلات وبوابات المتحكم من خلال تعريف مجموعة من المصفوفات في ذاكرة الفلاش (PROGMEM) لتخزين وبشكل دائم مصفوفات تقابل رقم الرجل حسب الأردوينو مع رقمها وعنوانها حسب المتحكم بشكل فيزيائي. هذه المصفوفات توجد في الملف pins_arduino.h.
const uint16_t PROGMEM port_to_input_PGM[] = { NOT_A_PORT, NOT_A_PORT, (uint16_t) &PINB, (uint16_t) &PINC, (uint16_t) &PIND, }; const uint8_t PROGMEM digital_pin_to_port_PGM[] = { PD, /* 0 */ PD, PD, PD, PD, PD, PD, PD, PB, /* 8 */ PB, PB, PB, PB, PB, PC, /* 14 */ PC, PC, PC, PC, PC, };
إن الملف Arduino.h يعرف مجموعة من الماكرو للنفاذ إلى هذه المصفوفات في ذاكرة الفلاش عبر pgm_read_word.
#define digitalPinToPort(P) ( pgm_read_byte( digital_pin_to_port_PGM + (P) ) ) #define portInputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_input_PGM + (P))) )
و الآن، كل تابع على حاجة للتعامل مع هذه السجلات registers يمكن ببساطة أن يستدعي هذه الماكرو macros للنفاذ للمصفوفات. مثل: pinMode و digitalWrite و (سمّ التابع الخاص بك) و ….إلخ.