Go والأجهزة: لماذا ليست مزحة؟
\nعندما تسمع عبارة "تطوير الأنظمة المدمجة"، عادة ما تتخيل لغة C، ولغة التجميع، وتصحيح الأخطاء المؤلم عبر مصباح LED. لكن العالم لا يقف ساكنًا. اليوم، Go (أو Golang) تطرق باب عالم المتحكمات الدقيقة بثقة متزايدة. وهي لا تطرق عبثًا، بل مع goroutines، وجامع القمامة، والواجهات المدمجة.
\nفي هذه المقالة، سنفهم كيف يبدو تطوير الأنظمة المدمجة باستخدام Go، وسنكتب كودًا حقيقيًا لمتحكم دقيق افتراضي، وسنعرف ما إذا كان الأمر يستحق العناء. تلميح: نعم، إذا كنت قد سئمت من تسرب الذاكرة وتحب الكود القابل للقراءة.
\n\nلماذا Go وليس C؟ ثلاثة أركان
\nالأنظمة المدمجة التقليدية هي C. لكن Go تمتلك ثلاث ميزات قاتلة تجعل المهندسين يعيدون النظر في عاداتهم:
\n- \n
- أمان الذاكرة: لا مؤشرات جامحة يمكن أن تطير إلى الفضاء. مترجم Go أكثر صرامة من مترجم C. \n
- Goroutines: خيوط خفيفة الوزن تتناسب تمامًا مع البنية الموجهة بالأحداث للمتحكمات الدقيقة. \n
- التوافق عبر المنصات: تكتب الكود على Mac، وتجمعه لـ ARM Cortex-M. بدون طقوس معقدة. \n
بالطبع، هناك فارق بسيط: Go الأصلي (gc) لا يمكنه التجميع لوحدات MCU الصغيرة. لكن هناك مشروع TinyGo — مترجم يكيّف Go للأجهزة ذات الكيلوبايتات من الذاكرة. عليه سنركز.
\n\nإعداد البيئة: TinyGo ولوحتك
\nقبل كتابة الكود، دعنا نجهز مساحة العمل. لـ تطوير الأنظمة المدمجة باستخدام Go ستحتاج:
\n- \n
- تثبيت TinyGo:
brew install tinygo(أو التحميل من الموقع الرسمي). \n - تثبيت سلسلة الأدوات للوحتك (مثل ARM GCC لـ STM32). \n
- توصيل اللوحة عبر USB. \n
نتحقق من أن كل شيء يعمل:
\ntinygo version\ntinygo: 0.31.0 (linux/amd64)\n\nالآن يمكنك تجميع الكود لأي لوحة مدعومة: tinygo build -target=arduino-nano -o firmware.hex main.go.
أول وميض: LED على Go
\nكلاسيكيات المجال. نتصل بالدبوس، نحرك القدم. هكذا يبدو برمجة المتحكمات الدقيقة باستخدام Go:
\npackage main\n\nimport (\n "machine"\n "time"\n)\n\nfunc main() {\n // LED المدمج عادة على الدبوس 13 (أو LED_BUILTIN)\n led := machine.LED\n led.Configure(machine.PinConfig{Mode: machine.PinOutput})\n\n for {\n led.High()\n time.Sleep(500 * time.Millisecond)\n led.Low()\n time.Sleep(500 * time.Millisecond)\n }\n}\n\nنجمعه ونحمله على اللوحة:
\ntinygo flash -target=arduino-nano -port=/dev/ttyUSB0 main.go\n\nها هي ذا! الـ LED يومض. لاحظ: نستخدم حزمة machine — وهي طبقة تجريد الأجهزة في TinyGo. تخفي وراءها العمل مع السجلات، لكنها تعطي واجهة Go نظيفة.
العمل مع GPIO والمقاطعات
\nفي المشاريع الحقيقية، مجرد الوميض لا يكفي. تحتاج لقراءة الأزرار، وأجهزة الاستشعار، والاستجابة للأحداث. في Go للأنظمة المضمنة، تُنفذ المقاطعات عبر القنوات و goroutines. مثال: نعلق معالجًا لضغطة زر.
\npackage main\n\nimport (\n "machine"\n "fmt"\n)\n\nfunc main() {\n button := machine.D2\n button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})\n\n // ننتظر الضغط (pin == LOW، بسبب السحب إلى مصدر الطاقة)\n for {\n if !button.Get() {\n fmt.Println("تم الضغط على الزر!")\n }\n }\n}\n\nلكن هذه حلقة انتظار مشغولة. تريد كفاءة أعلى؟ استخدم مقاطعة عبر قناة:
\nfunc main() {\n button := machine.D2\n button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})\n\n // قناة للأحداث\n ch := make(chan bool, 1)\n button.SetInterrupt(machine.PinFalling,