Firmware نرمافزاری است که مستقیماً روی میکروکنترلر یا پردازنده تعبیهشده (Embedded Processor) اجرا میشود و وظیفه کنترل سختافزار را بر عهده دارد. برخلاف نرمافزارهای کامپیوتری، Firmware معمولاً به صورت Real-Time عمل میکند و باید با کمترین تأخیر به رویدادهای سیستم پاسخ دهد.
در صنایع مختلف از جمله خطوط نورد سرد، ماشینآلات CNC، تجهیزات هیدرولیک، رباتهای صنعتی، سیستمهای PLC، اینورترها و تجهیزات اندازهگیری، Firmware قلب سیستم کنترل محسوب میشود. کوچکترین خطا در طراحی آن میتواند باعث توقف خط تولید، کاهش کیفیت محصول یا حتی آسیب به تجهیزات شود.
در بسیاری از پروژههای کوچک مشاهده میشود که تمام برنامه در فایل main.cpp نوشته میشود. این روش شاید در ابتدای پروژه مناسب به نظر برسد، اما با افزایش تعداد سنسورها، موتورها، پروتکلهای ارتباطی و امکانات نرمافزار، مدیریت کد بسیار دشوار خواهد شد.
به عنوان مثال تصور کنید یک کنترلر صنعتی دارای تجهیزات زیر باشد:
اگر تمام این بخشها داخل فایل اصلی برنامه قرار گیرند، کد به سرعت غیرقابل نگهداری خواهد شد و اضافه کردن قابلیتهای جدید یا رفع اشکال زمان بسیار زیادی نیاز خواهد داشت.
به همین دلیل در پروژههای حرفهای از معماری چندلایه (Layered Architecture) استفاده میشود تا هر بخش از سیستم مسئولیت مشخصی داشته باشد و وابستگی بین قسمتهای مختلف به حداقل برسد.
یکی از رایجترین ساختارهای Firmware در تجهیزات صنعتی، معماری لایهای است.
Application │ ├── API │ ├── Drivers │ ├── HAL (Hardware Abstraction Layer) │ └── Hardware
در این ساختار، هر لایه تنها با لایه مجاور خود ارتباط دارد و از جزئیات سایر لایهها بیاطلاع است. این موضوع باعث میشود تغییر سختافزار یا توسعه نرمافزار با کمترین تغییر در سایر قسمتها انجام شود.
این بخش شامل منطق اصلی دستگاه است. تصمیمگیری، مدیریت حالتهای کاری، کنترل فرآیند، الگوریتمهای کنترلی و ارتباط با کاربر در این لایه انجام میشود.
این لایه رابطی استاندارد برای استفاده از تجهیزات مختلف فراهم میکند. به عنوان مثال، برنامه اصلی به جای کار مستقیم با پایههای میکروکنترلر، تنها از توابع API مانند StartMotor() یا ReadTemperature() استفاده میکند.
در این قسمت، راهاندازی تجهیزات مانند موتور، نمایشگر، EEPROM، سنسورها، انکودرها، UART، SPI، I2C و سایر تجهیزات انجام میشود.
HAL ارتباط مستقیم با سختافزار را برقرار میکند. در صورت تغییر میکروکنترلر از STM32 به ESP32 یا بالعکس، معمولاً تنها این لایه نیاز به تغییر خواهد داشت.
اصول SOLID مجموعهای از پنج اصل مهم در مهندسی نرمافزار است که با هدف افزایش قابلیت نگهداری، توسعه و تست نرمافزار ارائه شدهاند. اگرچه این اصول ابتدا برای برنامههای شیگرا مطرح شدند، اما امروزه در توسعه Firmware سیستمهای Embedded نیز بهطور گسترده مورد استفاده قرار میگیرند.
در پروژههای صنعتی که ممکن است سالها توسعه پیدا کنند، رعایت این اصول باعث میشود اضافه کردن قابلیتهای جدید یا تغییر سختافزار با حداقل تغییر در کد انجام شود.
هر کلاس یا ماژول باید تنها یک مسئولیت داشته باشد.
برای مثال، اگر یک کلاس هم وظیفه کنترل موتور، هم ارتباط بلوتوث، هم ذخیره اطلاعات در EEPROM و هم نمایش اطلاعات روی LCD را بر عهده داشته باشد، با کوچکترین تغییر در یکی از این بخشها احتمال ایجاد خطا در کل سیستم افزایش پیدا میکند.
طراحی صحیح به صورت زیر است:
هر کدام تنها یک وظیفه مشخص دارند.
کد باید برای توسعه باز باشد اما برای تغییر بسته باشد.
فرض کنید امروز دستگاه از موتور DC استفاده میکند اما سال آینده قرار است Servo Motor یا Stepper Motor به آن اضافه شود.
اگر تمام برنامه بر اساس Motor Interface نوشته شده باشد، تنها کافی است Driver موتور جدید نوشته شود و نیازی به تغییر منطق اصلی برنامه نخواهد بود.
Motor ├── DcMotor ├── ServoMotor └── StepperMotor
این روش باعث میشود توسعه پروژه بدون ایجاد خطا در قسمتهای قبلی انجام شود.
هر کلاس فرزند باید بتواند بدون ایجاد مشکل جایگزین کلاس والد شود.
برای مثال اگر برنامه با کلاس Motor نوشته شده باشد، باید بتوان به جای آن از ServoMotor یا HydraulicMotor استفاده کرد بدون اینکه بخشهای دیگر برنامه تغییر کنند.
این اصل باعث افزایش انعطافپذیری Firmware خواهد شد.
این اصل بیان میکند که هیچ کلاس یا ماژولی نباید مجبور شود متدهایی را پیادهسازی کند که به آنها نیازی ندارد.
به عنوان مثال، فرض کنید یک اینترفیس بزرگ برای تمامی تجهیزات صنعتی تعریف شده باشد:
IMachine Start() Stop() ReadEncoder() Bluetooth() WiFi() ReadTemperature() ReadPressure() Display() SaveEEPROM()
در حالی که ممکن است یک سنسور دما فقط به تابع ReadTemperature() نیاز داشته باشد. در چنین شرایطی بهتر است Interfaceها کوچک و تخصصی طراحی شوند.
نمونهای از این طراحی:
آخرین اصل SOLID شاید مهمترین اصل در Firmwareهای صنعتی باشد.
برنامه اصلی نباید مستقیماً با سختافزار کار کند.
اشتباه:
digitalWrite(LED,HIGH); analogWrite(PWM,120); HAL_GPIO_WritePin(...);
در این روش، اگر از Arduino به STM32 یا ESP32 مهاجرت کنید، تقریباً کل برنامه باید بازنویسی شود.
روش صحیح:
Motor.Start(); Led.On(); Display.ShowSpeed(); Temperature.Read();
در این حالت فقط Driver هر سختافزار تغییر میکند و منطق اصلی برنامه بدون تغییر باقی میماند.
یک پروژه حرفهای Firmware معمولاً دارای ساختاری مشابه زیر است:
Firmware/ │ ├── Application/ ├── API/ ├── Drivers/ ├── HAL/ ├── Middleware/ ├── Config/ ├── Utilities/ ├── Tests/ └── main.cpp
این ساختار باعث میشود هر قسمت از پروژه مسئولیت مشخصی داشته باشد و توسعه سیستم در آینده بسیار سادهتر انجام شود.