رحلة داخل نواة الأردوينو: الملفات المصدرية – كيفية بناء نواة أردوينو جديدة – خطوات الترجمة في الأردوينو
مقدمة
إن استكشاف الملفات المصدرية والتصاميم لمشروع مفتوح المصدر يجلب الكثير من الفوائد على المستكشف وخاصة إذا كان المشروع هذا تشاركي وعالمي وهذا يعني أن المستكشف سيتعّرض لعقليات مختلفة وحيل ذكية في الكود. بالتأكيد إن الأردوينو أحد هذه المشاريع والذي يعطيه جانب مميز إضافي أن الأردوينو قدّم طريقة جديدة في التجريد في الإلكترونيات القابلة للبرمجة وتحديداً المتحكمات والذي لم يكن شائعاً في الماضي. أردوينو تريد من عيون المطوّر أن تركز على حل المشاكل عوضاً عن إعادة اختراع العجلة بقيامه بالبرمجة في السويات المنخفضة والتي تتكرر في كل مشروع وكود خاص بالمتحكمات الصغرية.
إن الكثير من المطورين يكرهون طريقة الأردوينو ويكرهون كيف لمبتدئ لا يعرف حتى كيفية التعامل مع السجلات أن يقوم ببرمجة المتحكم. أردوينو يجعل من السهل برمجة متحكم كامل دون التعامل حتى مع سجل واحد. لكن من أجل المطورين الذين يرغبون أن يصبحوا خبراء ومن أجل الطلاب ذوي الخلفية الهندسية فليس من الجيد عدم الخوض في التفاصيل لأنه لا حل المشكلات التقنية ولا كتابة كود التطبيق يمكن أن يكون احترافياً دون معرفة واحتراف التفاصيل.
شخصياً، إن رحلتي القديمة مع الأردوينو بدأت من دون أن أعرف التفصيلات في المستوى المنخفض ولكن كان لدي الحماس لمعرفة السحر الداخلي خلف الأضواء. بينما كانت خبرة الإلكترونيات تنمو استطعت فهم التصميم الإلكتروني للدارة، والتعامل المباشر مع المتحكمات بلغة السي والتعامل من أكثر من عائلة جعل فهم الجانب البرمجي من الأردوينو (نواة الأردوينو) ممكناً.
إن نواة الأردوينو Arduino core هو المكان الذي يوجد فيه تعريف التوابع الداخلية للأردوينو مثل: pinMode(), digitalWrite(), analogRead(), Serial.begin() …إلخ. هذه المقالة هي محاولة لفهم بنية نواة الأردوينو وحتى بناء نواة جديدة ، كما سنناقش خطوات بناء وترجمة الكود في الأردوينو Building process.
رحلة داخل نواة الأردوينو
كما قلنا، فإن نواة الأردوينو هي المكان الذي يوجد فيه تعريف التوابع الداخلية للأردوينو. إن الجداول التالية هي لذكر أهم الأجزاء في الأكواد للملفات المصدرية لنواة الأردوينو والمقال يجمّعها تبعاً لغرضها.
الملفات المصدريّة العامّة
الملف المصدري | ماذا بداخله؟ |
---|---|
Arduino.h | 1- تصريح declaration لمعظم التوابع الداخلية في الأردوينو مثل pinMode() و digitalWrite() ... إلخ 2- ماكرو لبعض الثوابت مثل HIGH, LOW, INPUT, OUTPUT, .. إلخ 3- توابع ماكرو لبعض العمليات الخاصة بمعالجة البتات bitwise وبعض العمليات العامة الأخرى مثل min() و rand() … إلخ. 4- تصريح declaration لمصفوفات التقابل بين أرقام الأرجل الخاصة بالأردوينو ورقمها وسجلها الحقيقي في المتحكم الصغري (اقرأ التدوينة المصغرة المنشورة في عتادياتهذا الموضوع). إن قيم هذه المصفوفات موجودة في الملف التالي: hardware\arduino\avr\variants\standard\pins_arduino.h |
main.cpp | هنا يوجد التقسيم الحقيقي للبرنامج والكود الرئيسي للمتحكم الصغري (استدعاء setup و loop): int main(void){ init(); initVariant(); setup(); for (;;) {loop(); return 0;} |
الملفات المصدريّة المتعلقة بالدخل والخرج الرقمي والتماثلي
الملف المصدري | ماذا بداخله؟ |
---|---|
wiring_digital.c | تعريف define التوابع digitalWrite() و digitalRead() و pinMode(), ..إلخ. |
wiring_analog.c | تعريف define التوابع analogRead() و analogWrite() ..إلخ. |
wiring_shift.c | تابعين لقراءة وتخريج لتسلسل من البتات bits-streams |
wiring_pulse.c | تابعين لقراءة الدخل من نوع التعديل بعرض النبضة PWM. |
WInterrupts.c | تعريف بعض التوابع للتعامل مع المقاطعة الخارجية external interrupts. |
الملفات المصدريّة المتعلقة بالتوابع الزمنية والمؤقتات Timers
الملف المصدري | ماذا بداخله؟ |
---|---|
wiring.c | 1- تعريف توابع زمنية مثل millis() و delay() ..إلخ. 2- تعريف Timer0 overflow ISR 3- تابع تهيئة Initialization function يستدعى لتهيئة بعض الطرفيات peripherals قبل استدعاء التابع setup في main.cpp. |
الملفات المصدريّة المتعلقة بالاتصال Hardware Serial (بالمجمل)
الملف المصدري | ماذا بداخله؟ |
---|---|
Stream.h, Stream.cpp | تعريف الصف Stream class والذي يحوي إجرائيات methods والتي تستخدم (بالوراثة) في صف HardwareSerial class مثل readUntil() و find() … إلخ |
Print.cpp, Print.h | تعريف الصف Print class والذي يحوي إجرائيات methods لطباعة الرسائل (print و println). هذا الصف يتم وراثته من الصف Stream class. |
Printable.h | هذا الملف يعرف صف Printable class والذي يحوي على إجرائية واحدة ويتم وراثة هذا الصف من قبل أي صف جديد لكي يصبح بالإمكان طباعة الأغراض من هذا الصف الجديد عبر تابعي الطباعة print() و println(). لمزيد من المعلومات عن هذا التابع يمكن مراجعة التدوينة المصغرة المنشورة في عتاديات. |
HardwareSerial.h, HardwareSerial.cpp, HardwareSerial_private.h | تعريف الصف HardwareSerial class. إن الإجرائيات methods مثل print() و find() ..إلخ هي موروثة من صف Stream class. |
HardwareSerialx.cpp | 1- تعريف UARTx ISR. 2- التصريح عن class oject HardwareSerial Serialx. |
رحلة في نظام الملفات Files System
هذه الرحلة ستركز على الأجزاء الخاصة بملفات نواة الأردوينو و الملفات التي تدخل بعملية الترجمة والبناء للكود. بداية، كل الملفات المصدرية توضع في المكان التالي: \Arduino\hardware\arduino\{the MCU architecture}\cores\arduino
وكمكان آخر يمكن أن يوجد فيه أنوية الأردوينو للأطراف الثالثة أو الأنواع الأخرى (مثل ESP8266) \Users\{username}\AppData\Local\Arduino15\packages هذه الأنوية يتم تحميلها طبعاً من board manager (Tools>Boards>Board Manager). سلسلة الأدوات Tool-chain (وحتى ملفات الترويس الخاصة بـ avr-gcc ) توجد هنا Arduino\hardware\tools\{the MCU architecture} و هنا أيضاًUsers\{username}\AppData\Local\Arduino15\packages\arduino\tools.
كيفية بناء نواة أردوينو جديدة
كمثال سنقوم ببناء نواة للأردوينو من أجل متحكمات AVR. لأجل بناء نواة جديدة إننا نحتاج لأمرين:
1- ملف JSON يصف النواة الجديدة: اسمها ورقم النسخة والأدوات المتعلقة tools dependencies ورابط تحميل الملفات .. إلخ
2- الملفات المصدرية الخاصة بالنواة مضغوطة في ملف بلاحقة .zip أو .tar.bz2 أو .tar.gz
بدايةً، إن ملف JSON يجب أن يحمل اسم شبيه لهذا الشكل package_x_index.json أو اسم معبر آخر ويجب أن يحوي النموذج التالي:
{ "packages": [ { "name": "aaa", "maintainer": "bbb", "websiteURL": "http://bbb.com/", "email": "aaa@bbb.com", "help": { "online": "http://bbb.com/" }, "platforms": [ { "name": "aaa Boards", "architecture": "avr", "version": "1.0.0", "category": "aaa", "url": "http://bbb.com/aaa.zip", "archiveFileName": "aaa.zip", "checksum": "SHA-256:x", "size": "x", "help": { "online": "http://bbb.com/" }, "boards": [ { "name": "Arduino/Genuino Uno" } ], "toolsDependencies": [ { "packager": "aaa", "name": "avr-gcc", "version": "4.9.2-atmel3.5.3-arduino2" }, { "packager": "aaa", "name": "avrdude", "version": "6.3.0-arduino6" } ] } ], "tools": [] } ] }
والآن يجب تعديل محتوى الملف بما يتواءم مع المعلومات الجديدة للنواة. هناك العديد من الأدوات على الانترنت من أجل حساب توقيع الملف SHA-256 checksum خاص بالملف المضغوط للنواة مثل هذا المواقع. حاول أن تطلع على ملفات JSON من أجل أنوية أخرى لتفهم كيفية بناءها وتعديلها مثل esp8266 core JSON.
إن الخطوة القادمة هي إنشاء الملف المضغوط. يجب أن يحوي مجلد رئيسي واحد باسم النواة ويحوي بداخله نموذج template الملفات للنواة (bootloaders – cores – doc – libraries – tools & variants folders – boards.txt – platform.txt – programmers.txt).
Boards.txt و platform.txt و programmers.txt هي ملفات يجب تعديلها أيضاً تبعاً للنواة الجديدة. بما أن هذه المقالة لوضع نظرة عامة وليس تفصيليّة سيترك مهمة معرفة محتويات هذه الملفات للقارئ وللدليل الرسمي.
لتلخيص ما سبق، سوف ننشئ نموذج باسم “atadiat” بالخطوات التالية:
1- تعديل في platform.txt
name=Atadiat AVR Boards version=1.0
2- تعديل في ملف boards.txt للإبقاء على دارة واحدة فقط في النواة الجديدة (Arduino UNO)
# See: http://code.google.com/p/arduino/wiki/Platforms menu.cpu=Processor ############################################################## uno.name=Arduino/Genuino Uno uno.vid.0=0x2341 uno.pid.0=0x0043 uno.vid.1=0x2341 uno.pid.1=0x0001 uno.vid.2=0x2A03 uno.pid.2=0x0043 uno.vid.3=0x2341 uno.pid.3=0x0243 uno.upload.tool=avrdude uno.upload.protocol=arduino uno.upload.maximum_size=32256 uno.upload.maximum_data_size=2048 uno.upload.speed=115200 uno.bootloader.tool=avrdude uno.bootloader.low_fuses=0xFF uno.bootloader.high_fuses=0xDE uno.bootloader.extended_fuses=0xFD uno.bootloader.unlock_bits=0x3F uno.bootloader.lock_bits=0x0F uno.bootloader.file=optiboot/optiboot_atmega328.hex uno.build.mcu=atmega328p uno.build.f_cpu=16000000L uno.build.board=AVR_UNO uno.build.core=arduino uno.build.variant=standard ##############################################################
3- إنشاء الملف المضغوط atadiat.zip وحساب توقيع الملف باستخدام SHA online generator.
4- إنشاء ملف JSON تبعاً للنموذج المذكور أعلاه.
5- لتجربة هذه النواة يمكنك إضافة رابط ملف JSON إلى بيئة الأردوينو Arduino IDE
(https://atadiat.com/wp-content/uploads/package_atadiat_index.json)
ثم لتحميل النواة الجديدة نذهب إلى board manager .
إضافة نواة جديدة من دون ملف JSON
تعتبر الطريقة السابقة هي الطريقة المعتمدة للمطورين ليتمكن المستخدمين لاحقاً من استخدام الأنوية cores الجديدة. ولكن قد يكون هناك اضرار لسبب ما لإضافة نواة إلى Arduino IDE من غير استضافة النواة على الانترنت أو حتى إنشاء ملف JSON واستضافته أيضاً. الطريقة الأسهل في هذه الحالة أن يتم إنشاء ملف باسم النواة في Documents\Arduino\hardware وداخله توضع النواة. يصبح المسار الكامل
Documents\Arduino\hardware \{core name }\{the MCU architecture}
عملية البناء والترجمة في الأردوينو
إن بيئة الأردوينو الأساسية تحوي مجموعة من العمليات للبناء والترجمة غير ظاهرة جزئياً للمطور. وبالتالي لا يمكن العثورة على ملف makefile يحدد خطوات البناء والترجمة. عوضاً عن ذلك تتيح الأردوينو للمطور إضافة بعض التعديلات لعملية البناء عبر ملف platform.txt ومع ذلك تبقى بعض الخطوات خارج تحكم المطوّر. يجب أن نبقي في الذهن أن بعض الثوابت يتم أخذها بناءً على الدارة المحددة حسب الملف board.txt وهذه الثوابت تستخدم لاحقاً في عملية البناء والترجمة.
إن عملية البناء في الأردوينو (لمتحكمات AVR على سبيل المثال) تتألف من الخطوات التالية وبالترتيب (للتأكد يمكن تفعيل خيار إخراج رسائل أثناء البناء):
- يتعرف الـIDE على الدارة المستخدمة والنواة المستخدمة ويستدعي بالتالي معلومات من ملف board.txt.
- يتم إنشاء ملف مؤقت في المسار التالي Users\{username}\AppData\Local\Temp\arduino_build_x والذي سيستخدم من أجل وضع object files من أجل جميع الملفات المصدرية في النواة وأيضاً المكاتب المستخدمة إلى جانب ملفات نتائج عملية البناء مثل HEX file والذي يرفع لاحقاً على المتحكم عبر المبرمجة (راجع مقال كيفية برمجة المتحكمات).
- ترجمة السكيتش Compiling sketch.
- ترجمة الملفات المصدرية للمكاتب Compiling the libraries source files.
- ترجمة الملفات المصدرية لنواة الأردوينو بالخطوات التالية:
- ترجمة ملفات السي.
- ترجمة ملفات السي بلس بلس.
- ترجمة ملفات الأسيمبلي.
- تجميع الملفات object files في ملف أرشيفي (.a) archive file. ماهو الملف الأرشيفي؟ تحقق من الإجابة على Stackoverflow .
- ربط كل الملفات linking لإنتاج ملف HEX النهائي.
مالذي يمكن ملف platform.txt المطور من فعله:
- تعريف متحولات خاصة compilation/building flags/variables لإضافتهم إلى الأوامر لاحقاً.
- تغيير نمط pattern كل مرحلة من المراحل المذكورة سابقاً في عملية البناء.
- تغيير تعليمات البناء.
- إضافة توسيعات لعملية البناء.
أنماط البناء في الأردوينو building patterns تبدو كالتالي:
## Compile c files recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.c.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" ## Compile c++ files recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpp.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" ## Compile S files recipe.S.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.S.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.S.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}"
هذه الأنماط تتحول إلى مجموعة من التعليمات المتتابعة ضمن النمط الواحد
أردوينو أضافت منذ النسخة 1.6.5 من البيئة طريقة تمكن المطور من إضافة توسيعات لعملية البناء في مختلف المراحل وتسمى هذه التوسيعات pre and post build hooks.
كمثال لتوسيع عملية البناء بعد ترجمة النواة core compilation. أولاً، سوف نضيف بعض المتحولات (يمكن تغيير التسميات كما تريد):
compiler.hook.cmd = avr-gcc-nm compiler.hook.cmd.flag = -g
إن التوسعة هنا ستستخدم الأمر nm من أدوات GCC من أجل طباعة الرموز symbols داخل أحد ملفات object file.
recipe.hooks.core.postbuild.1.pattern = "{compiler.path}{compiler.hook.cmd}" {compiler.hook.cmd.flag} "{build.path}/core/wiring_digital.c.o"
ملاحظة: يمكن إضافة أكثر من توسعة في نفس الفئة وذلك بزيادة الرقم.
ختاماً إذا كان المطور بحاجة إلى مزيد من التحكم في عملية البناء فيجب إعطاء الاهتمام لمشاريع مثل Arduino-makefile التي قامت ببناء makefile فيه تحكم كامل وقابل لتخصيص وتعديل العملية بشكل كامل.
اقرأ أكثر
- لمعرفة البنية الداخلية وتعريف كثير من التوابع الداخلية في الأردوينو، يمكن قراءة فصول bits & pieces من هذا الكتاب الرائع Embedded Controllers Using C and Arduino by James M. Fiore.
- Arduino 3rd party hardware specification
- Arduino core package json format specification
- Arduino Build-Process