Table of Contents
نوشتن کد قابل حمل C برای دستگاه های IoT یک مهارت اساسی برای توسعه دهندگان جاسازی شده است که نیاز به استقرار برنامه ها در سیستم عامل های مختلف سخت افزاری دارند. اکوسیستم IoT شامل میکروکنترلرهای با ARM Cortex-M، RISC-V، AVR و معماری اختصاصی است، هر کدام با نقشه های منحصر به فرد، ثبت های محیطی و بدون طراحی برای پورت عمدی، کد که اغلب در یک راه حل استراتژیک دیگر کار می کند، و هدایت این مقاله و رمز گذاری های خاص، ارائه می دهد.
درک قابلیت پورتینگ در توسعه IoT
قابلیت پورتینگ به این معنی است که کد منبع را می توان در معماری های مختلف سخت افزاری با تغییرات کوچک یا بدون تغییر در جهان IoT کامپایل و اجرا کرد، پورتینگ فقط یک راحتی نیست - این یک نیاز تجاری است که چرخه عمر محصول طولانی است، انتقال زنجیره تامین و سیلیکون جدید به طور مداوم به نظر می رسد.
قابلیت پورتینگ در یک طیف وجود دارد.در یک انتها، کدی که کاملاً مستقل از پلتفرم است (به عنوان مثال الگوریتم های مرتب سازی عمومی) در هر نقطه جمع می شوند، کدی که به طور مستقیم ثبت نام های سخت افزاری را دستکاری می کند، ذاتا غیر قابل حمل است.هدف از قابل حمل C برای IoT این است که جزئیات غیر قابل حمل را در پشت لایه های انتزاعی جدا کند تا منطق اصلی کسب و الگوریتم کد قابل استفاده مجدد باقی بماند.
چالش های مشترک برای قابلیت دسترسی Code Portability
چندین تفاوت سطح پایین شامل قابلیت حمل C می شود:
- ARM Cortex-M و AVR کمی به پایان می رسند؛ برخی از معماری های قدیمی (به عنوان مثال، Freescale HC12) دارای نقطه عطف بزرگ هستند.
- اندازه و تعاریف نوع ممکن است 16 بیت در یک AVR 8 بیتی، 32 بیت در Cortex-M0، و 64 بیت در یک پردازنده RISC-V 64 بیتی باشد.
- تغییر نقشه تفاوت حتی دو MCU از همان فروشنده اغلب آدرس های مختلف پایه محیطی، زمینه های کوچک و توالی های پیکربندی دارند.
- افزونه های Compiler و pragmas GCC، IAR، ARM Compiler 6 و Keil هر کدام دارای ترکیب (FLT:2) و گویش های مونتاژ خط هستند.
- طرح و تراز حافظه برخی از سیستم عامل ها نیاز به هماهنگی دقیق برای دسترسی 32 بیتی دارند؛ برخی دیگر دسترسی های ناسازگار را با یک کارگزار خطا اداره می کنند.
- استفاده از وافر و استفاده از پشته ، بردارهای Interrupt، مدل های اولویت، و رفتار لانه سازی به طور گسترده ای متفاوت است.
استراتژی های کلیدی برای نوشتن کد C قابل حمل
لایه های انتزاع سخت افزار (HAL)
این ابزار قدرتمند در زرادخانه قابل حمل است لایه انتزاعی سخت افزار (FLT:1) در حالی که پنهان کردن یک HAL طراحی شده است، یک API یکنواخت برای توابع جانبی مشترک (GPIO، UART، I2C، اشپیگل، تایمر) را در حالی که پنهان کردن رابط کاربری زیر زمینی باید در یک تابع هدر (F3) تعریف شود.
یک الگوی پیاده سازی معمولی HAL به نظر می رسد این است:
// hal_gpio.h (common)
typedef uint8_t gpio_pin_t;
typedef uint8_t gpio_port_t;
void hal_gpio_set_output(gpio_port_t port, gpio_pin_t pin);
void hal_gpio_set_high(gpio_port_t port, gpio_pin_t pin);
void hal_gpio_set_low(gpio_port_t port, gpio_pin_t pin);
فایل های مخصوص پلتفرم (به عنوان مثال، حاوی ثبت نام واقعی هستند، هنگامی که به یک MCU جدید حرکت می کنند، فقط منابع HAL سطح پایین نیاز به بازنویسی دارند، در حالی که تمام لایه های بالاتر دست نخورده باقی می مانند.
اتخاذ کتابخانه های استاندارد
کتابخانه استاندارد C پایه قابل حمل برای بسیاری از عملیات های مشترک فراهم می کند. توابع مانند ، ، خدمات رشته ای و توابع ریاضی در هر کامپایلر C مطابقت دارند. اجتناب از مفروضات در مورد داخلی کتابخانه حیاتی است - هرگز بازنویسی برای عملکرد مگر اینکه شما تایید کرده اید که اجرای کامپایلر شما کافی نیست.
برای سیستم های IoT با حافظه محدود، استفاده از زیرمجموعه ای از کتابخانه استاندارد (مانند Newlib-nano را در اکوسیستم GCC در نظر بگیرید، به جای اینکه روال رشته خود را به طور مشابه، [FLT 11: ماکرو و به طور جهانی در دسترس است.
استفاده از انواع داده های ثابت
و از انواع [[FLT13]] و [[FLT 14|([[ویرایش] استفاده کنید تا متغیرهای صحیح را با عرض های صریح اعلام کنید: [FLT: 15] [FLT16]، ، و غیره از [FLT 19] ساده (FLT 19: 22، یا [F:21 برای هر چیزی که باید اندازه شناخته شده باشد، استفاده نمی کنند.
هنگامی که شما نیاز به جمع آوری اطلاعات در سراسر حمل و نقل های مبتنی بر بایت دارید، انواع پهنای باند ثابت را با توابع تبدیل صریح (، یا معادل قابل حمل خود ترکیب کنید. هرگز به سادگی یک [FLT 26] را به یک [FLT 27] تقسیم نکنید و آن را بیش از یک شبکه ارسال کنید.
تکمیل وضعیت
دستورالعمل های پیش پردازنده یک ابزار قانونی برای کد خاص پلت فرم هستند، اما باید به طور منظم از آنها استفاده شود.شکل کوچکی از ماکروهای پیکربندی را در یک هدر مرکزی (به عنوان مثال، به جای پراکنده کردن [FLT 29 از طریق هر فایل مثال:
// platform_config.h
#if defined(STM32L4)
#define PLATFORM_STM32L4
#elif defined(EFM32GG)
#define PLATFORM_EFM32GG
#else
#error "Unsupported platform"
#endif
سپس در کد، از کد عمومی (FLT 31) استفاده کنید، فقط در صورتی که کاملاً لازم باشد، به یاد داشته باشید که بیش از حد [FLT] (3) کد را سخت می کند تا بخواند و حفظ کند.
کاهش وابستگی های خارجی
هر کتابخانه شخص ثالثی که شامل آن هستید، خطر بالقوه قابل حمل بودن است، قبل از اضافه کردن وابستگی، تأیید کنید که از تمام معماری های هدف شما پشتیبانی می کند و در فرضیات غیر قابل حمل، کتابخانه هایی که به طور کامل در C قابل حمل نوشته شده اند (به عنوان مثال، (FLT:0FatFS یا [F:2Free [F] حتی می توانید آن را با استفاده از کد مخصوص یا بسته بندی کنید.
لینک: مقاله ای در مورد کد C قابل حمل در دنیای واقعی ارائه می دهد دیدگاه اضافی در مدیریت وابستگی ها ارائه می دهد.
نکات عملی برای قابلیت دسترسی Enhancing Portability
کد های استاندارد
سیستم عامل خود را به ماژول های مستقل با رابط های به خوبی تعریف شده تقسیم کنید.هر ماژول باید عملکرد خود را از طریق یک فایل هدر افشا کند و جزئیات داخلی آن را پنهان کند.این جدایی از نگرانی ها باعث می شود که جایگزین ماژول با یک نسخه قابل حمل در هنگام انتقال به یک پلت فرم جدید شود.برای مثال، یک ماژول کنترل موتور باید با یک HAL برای خروجی PWM صحبت کند، نه به طور مستقیم به ثبت نام محیط زیست.
دانلود مستند سخت افزار The Hardware
واضح است که هر کدی را که رفتار سخت افزاری خاصی را فرض می کند، یادداشت کنید تا توضیح دهید که چرا یک رویکرد غیر قابل حمل خاص انتخاب شده است، چه پلتفرم هایی روی آن کار می کند و چه چیزی باید برای یک هدف متفاوت تغییر کند، این اسناد زمانی ارزشمند است که توسعه دهنده اصلی در دسترس نیست و یک مهندس جدید باید کد را پورت کند.
استفاده از Cross-Platform Build Tools
ساخت سیستم هایی مانند CMake یا Meson می تواند چندین پیکربندی هدف را از یک ساختار پروژه واحد مدیریت کند. [برای مثال، به شما اجازه می دهد فایل های ابزار را برای هر پلت فرم مشخص کنید و تعاریف را بر اساس هدف تنظیم کنید.این نیاز به صورت دستی برای حفظ فایل های جداگانه برای تنظیم فایل های IFAR فراهم می کند.
استفاده از تکنیک های قابل حمل Bit-Manipulation
هنگام تنظیم یا پاک کردن بیت ها در ثبت نام، از نوشتن ماسک های مطلق که مکان های کوچک میدان را فرض می کنند، اجتناب کنید، از ثابت های نمادین تعریف شده در HAL استفاده کنید و از ماکرو یا توابع خط برای عملیات های کوچک ایمن استفاده کنید:
#define BIT_SET(reg, bit) ((reg) |= (1u << (bit)))
#define BIT_CLEAR(reg, bit) ((reg) &= ~(1u << (bit)))
تعریف (FLT:34) به عنوان یک پارامتر انتزاعی به جای یک صحیح واقعی، به این ترتیب، اگر موقعیت کمی در یک MCU مختلف تغییر کند، تنها تعریف ثابت باید تغییر کند، نه استفاده در سراسر کد پایه.
تست و اعتبار در سراسر پلتفرم
ادعاهای قابل اطمینان باید تأیید شود.استفاده از ادغام مداوم (CI) که پروژه شما را برای همه پلتفرم های پشتیبانی شده ایجاد می کند.در CI، اجرای ابزارهای تجزیه و تحلیل استاتیک مانند -lint و یا Coverity برای تشخیص سوء استفاده از ساختارهای غیر قابل حمل و نقل و نقل و یا دستگاه های کوچک برای استفاده از RIS.
تست های بازگشتی باید تمام API های HAL را در هر پلتفرم برای گرفتن قابلیت های درون محیطی در اوایل اجرا کنند.یک تست مانند “نوشتن یک بایت به یک UART، خواندن آن در یک حلقه”، تفاوت های زمان بندی یا پیکربندی بین پیاده سازی های UART را افشا می کند.
نتیجه گیری
نوشتن کد قابل حمل C برای دستگاه های IoT یک پس از تفکر نیست - این یک نظم است که باید از روز به معماری تبدیل شود، با سرمایه گذاری در یک لایه انتزاعی سخت افزار، جذب انواع استاندارد و کتابخانه ها، با استفاده از جمع آوری مشروط به کم کردن کم کردن، و آزمایش دقیق در سراسر اهداف، شما ایجاد سیستم عامل که می تواند زنده ماندن تغییرات اجتناب ناپذیر در سخت افزار زنجیره ای سخت افزار تلاش برای تقسیم سریع تر از پروژه های تعمیر و انعطاف پذیری جدید، و استفاده از طریق حمل و تجزیه و نگهداری پورت های جدید.