برای توسعه تعبیه شده – جامعه dev

عجیب است ، زیرا به نظر می رسد توسعه وب و میکروکنترلرهای برنامه نویسی بسیار متفاوت از یکدیگر هستند. یکی از همه در مورد ساخت رابط های کاربر نرم و صاف در راحتی یک مرورگر وب ، با فضای زیادی برای تنفس-فکر می کنید حافظه پویا ، انتزاع های سطح بالا و منابعی که عملاً نامتناهی هستند. یکی دیگر؟ در این دنیای محدودیت های سخت ، شما در سطح سخت افزار در حال ضرب و شتم هستید ، مهلت های زمان واقعی یک قاعده است و هر بایت حافظه شمارش می شود.
در اصل آنها ، برنامه نویسی وب و MCU با یک مشکل اساسی روبرو هستند: چیزها خیلی پیچیده می شوند.
اگر تا به حال برای یک میکروکنترلر روی سیستم عامل کار کرده اید ، احتمالاً درد را احساس کرده اید. با رشد پایگاه کد شما ، شروع می شود مانند یک بشقاب اسپاگتی که کسی روی زمین افتاده است. بخش های منطق برای کنترل لوازم جانبی در تمام نقاط پراکنده می شوند. دستگیره ها و تغییرات ایالتی را در بعید ترین مکان های کد شما نشان می دهند. چگونه ما دوست داریم یک آشفتگی ایجاد کنیم ، نه؟ به اندازه کافی برای یادآوری کابوس های خود در مورد اشکال زدایی و تلاش برای ادغام یک ویژگی جدید در سیستم؟ کشیدن یک نخ بدون پاره کردن کل ژاکت می تواند اینگونه باشد.
اما اگر – دیوانه به نظر می رسد – ما برخی از ایده های زیبا از React ، آن کتابخانه پیشگامانه را برای توسعه وب گرفتیم و آنها را به برنامه نویسی MCU آوردیم؟ React در آغوش مدولار بودن ، فکر کردن در مورد اینکه چه کاری باید انجام شود ، نه چگونه این کار را انجام دهیم ، و به روشی صریح و قابل پیش بینی رسیدگی می کند.
در این مقاله فقط به این شرح داده شده است: چگونه این الگوهای معماری طراحی مبتنی بر مؤلفه ، به روزرسانی های دولتی محور و مدیریت چرخه عمر ممکن است در دنیای دقیق و محدود از منابع میکروکنترلرها به نظر برسند. چگونه می توان چنین ایده هایی را با استفاده از ترفندهای هوشمندانه مانند ماکروهای C و بهینه سازی های کامپایل ترجمه کرد تا برای سخت افزار در توسعه سیستم عامل حس کند.
چرا برای MCU واکنش نشان می دهد؟
جای تعجب ندارد که اصول اصلی آن بسیاری از مشکلات جهانی در معماری نرم افزار را برطرف کرده است و از این رو می تواند برنامه هایی را به دور از حوزه مبدا خود پیدا کند. هسته واکنش همه چیز در مورد سازماندهی پیچیدگی است. در این دنیای هرج و مرج از میکروکنترلرها که همه چیز ، از LED های LED تا مدیریت ارتباطات UART ، به نظر می رسد که دستکاری های سخت رو به روسری با متغیرهای دولتی پراکنده در اینجا و آنجا را شامل می شود ، این تأکید بر سادگی جذابیت زیادی دارد.
این بدان معناست که ، اساساً ، ما مجبور نیستیم خود را واکنش نشان دهیم اما می توانیم با استفاده از وسایل بومی توسعه جاسازی شده ، اصول اصلی واکنش را بررسی کنیم. با استفاده از M Macros به همراه تولید کد کامپایل با دقت ، می توان یک معماری مؤلفه React مانند را بدون سربار اجرا کرد. این فقط یک فکر نظری نیست ؛ من موفق شده ام آن را در سیستم های تولید واقعی ، از کنترل کننده های ساده LED گرفته تا تجهیزات پیچیده اتوماسیون صنعتی اعمال کنم.
تطبیق مفاهیم اصلی
حافظه محدود ، بدون جمع آوری زباله ، الزامات در زمان واقعی ، ارتباط مستقیم با سخت افزار برخی از ویژگی هایی است که محیط محدود شده از منابع MCU را تعریف می کند. ایده های اساسی React – مؤلفه سازی ، مدیریت دولت و جریان داده های یک طرفه – با وجود این محدودیت ها می توانند با موفقیت دوباره استفاده شوند.
طراحی مبتنی بر مؤلفه
اجزای قابل استفاده مجدد جنبه ای پاداش دهنده استفاده از React برای ایجاد رابط های کاربر است. آنها موجودات جداگانه ای با حالات ، منطق و رفتارهای مجزا هستند. حال استفاده از این مفهوم را در طراحی MCUS در نظر بگیرید. چه می شود اگر اجزای سخت افزاری مانند موتورها ، تایمرها ، دکمه ها و LED ها را به عنوان “قطعات” جداگانه دیدیم؟ عناصر پیچیده سطح پایین را حذف کنید تا مدولار بودن و قابلیت استفاده مجدد در پروژه ها امکان پذیر باشد.
در React ، مؤلفه ها پویا و تعاملی هستند ، به طور معمول از طریق DOM مجازی به اقدامات کاربر پاسخ می دهند. از طرف دیگر ، از طرف دیگر ، مؤلفه های ما استاتیک و کاملاً قطعی هستند و می توانند با یک بلوک ساختاری در C مدل سازی شوند که می تواند حالت و غرفه ها را ذخیره کند ، جایی که غرفه ها نشان دهنده رابط های تعریف شده برای تعامل هستند.
/* Button component */
typedef struct {
eer_gpio_handler_t *io;
void * pin;
enum {
BUTTON_PUSH, /* Button pressed only when pushed */
BUTTON_TOGGLE /* Pressed state change after every push */
} type;
struct Clock_time *clock; /* Timestamp for bounce filtering */
int bounce_delay_ms; /* Debounce delay in milliseconds */
struct {
void (*press)(eer_t *instance);
void (*release)(eer_t *instance);
void (*toggle)(eer_t *instance); /* When pressed state changes */
} on;
} Button_props_t;
typedef struct {
union {
struct {
bool level : 1;
bool pressed : 1; /* Is button pressed */
};
uint8_t flags;
};
uint16_t tick; /* Timestamp when pin state changed */
} Button_state_t;
به عنوان مثال ، اجرای اجزای IO و دکمه ساده وجود دارد.
با فکر کردن در مورد لوازم جانبی تعبیه شده از طریق لنزهای “مؤلفه ها” ، توسعه MCU نه تنها قابل کنترل تر می شود ، بلکه ظریف تر می شود – لمس ظرافت مهندسی نرم افزار به دنیای سخت افزاری اغلب دلهره آور.
مدیریت و هماهنگ سازی دولت
React بر یک منبع واحد از حقیقت برای دولت تأکید می کند ، و اطمینان می دهد که به روزرسانی ها به طور پیش بینی شده و از طریق سیستم به طور اعلامیه ای جریان می یابند. در MCU ، حالت می تواند پیکربندی یا رفتار لوازم جانبی سخت افزار را نشان دهد – مانند اینکه آیا یک موتور در حال کار است ، یک LED روشن است یا یک سنسور دما به آستانه رسیده است. با سازماندهی این حالتها در “ظروف دولتی” مرکزی یا محلی ، می توانیم حالتهای محیطی را بطور منظم ردیابی کنیم و از خطاهای ناشی از تغییرات حالت پراکنده و غیر هماهنگ جلوگیری کنیم.
تخصیص دولت جهانی: برخلاف اشیاء پویا حالت React ، ما دولت را در زمان کامپایل در سطح جهانی اختصاص می دهیم. این تضمین کنترل کامل بر استفاده از حافظه را دارد.
به روزرسانی های تعیین کننده: چرخه های بروزرسانی به خوبی طراحی شده ، تغییرات حالت را مدیریت می کنند. این تضمین می کند که مؤلفه ها با سخت افزار اساسی خود سازگار هستند و از مشکلاتی مانند شرایط مسابقه یا حالتهای متناقض جلوگیری می کنند.
برنامه نویسی اعلانی
برنامه نویسی تعبیه شده سنتی به شدت به کد “چگونه به” متکی است: صریح نوشتن هر پیکربندی پین ، به روزرسانی حالت و تعامل محیطی. یک رویکرد با الهام از واکنش به توسعه دهندگان این امکان را می دهد تا روی “آنچه” سیستم باید انجام دهد ، تمرکز کنند و “چگونه” را به انتزاع می اندازند.
Clock(clk, &hw(timer), TIMESTAMP);
/* Button component with full lifecycle */
pin_t enter_pin = hw_pin(ENTER_PORT, ENTER_PIN);
Button(enter, _({
.io = &hw(gpio),
.pin = &enter_pin,
.clock = &clk.state.time,
.type = BUTTON_PUSH_PULLUP,
.bounce_delay_ms = 100,
.on = {
.press = handle_press,
.release = handle_release
}
}));
/* Main application loop */
/* Components automatically manages its lifecycle */
loop(clk, enter) {
/* React to state changes */
if (Button_is_pressed(enter)) {
/* Handle pressed state */
}
}
مثال پیشرفته تر اجرای ساده پوسته.
جریان داده های یک طرفه
React یک جریان داده یک طرفه را اجرا می کند ، و اطمینان می دهد که تغییرات به طور قابل پیش بینی از منبع حقیقت (حالت) به رابط ارائه شده پخش می شود. در MCU ، این اصل می تواند تعامل بین لوازم جانبی ورودی (به عنوان مثال ، داده های سنسور) و لوازم جانبی خروجی (به عنوان مثال ، موتورها یا نمایشگرها) را مدیریت کند ، و از حلقه های بازخورد هرج و مرج یا شرایط مسابقه جلوگیری می کند.
چرخه عمر مؤلفه
لوازم جانبی سخت افزار ، سنسورها یا واحدهای منطقی به طور مشابه با واکنش اجزای موجود در این روش عمل می کنند. هر یک از طول عمر مشخصی دارند ، از ابتدایی (نصب) گرفته تا تغییرات حالت (به روزرسانی) و پاکسازی (عدم انعطاف). ما ضمن حفظ کارایی کد و حداقل وزن ، که دقیقاً همان چیزی است که سیستم های تعبیه شده به آن نیاز دارند ، با استفاده از ماکروهای C برای ساخت یک API اعلانی ، ظرافت React را تکرار می کنیم.
#include "MyComponent.h"
WILL_MOUNT(MyComponent) {
state->initialized = true;
}
SHOULD_UPDATE(MyComponent) {
return props->value != next_props->value;
}
WILL_UPDATE(MyComponent) {
state->updated = false;
}
RELEASE(MyComponent) {
state->value = props->value;
}
DID_UPDATE(MyComponent) {
state->updated = true;
printf("MyComponent updated with value %d\n", state->value);
}
DID_MOUNT(MyComponent) {
printf("MyComponent mounted with value %d\n", state->value);
}
این الگوی کد را سازمان یافته ، قابل استفاده مجدد و اشکال زدایی با حالتهای روشن مانند DEFINED → RELEASED → PREPARED → RELEASED → …
و قلاب برای هر مرحله.
مرحله اولیه سازی
- تعریف شده: مؤلفه اعلام شده با خصوصیات اولیه
- will_mount: برای اولین بار استفاده از مؤلفه را تهیه کنید (به عنوان مثال ، اولیه سازی سخت افزار)
- رهایی: حالت اولیه را اعمال کنید
- _: مؤلفه برای بهره برداری آماده است
مرحله بروزرسانی
- عاری از: مؤلفه برای چرخه عمر جدید به روزرسانی آماده است
- cust_update: تعیین کنید که آیا مؤلفه نیاز به بروزرسانی دارد
- will_mount: برای تغییرات دولت/املاک آماده شوید
- آماده: مؤلفه آماده برای اعمال تغییرات
- رهایی: تغییرات را اعمال کنید
- did_update: عملیات پس از به روزرسانی
این بدان معنی است که مدیریت حالت های مؤلفه و به روزرسانی در دستگاه های تعبیه شده می تواند با الگوهای چرخه عمر React برای برنامه نویسی MCU واقعاً ساده باشد. فلسفه زیبای React ، آمیخته با چالش های منحصر به فرد برنامه نویسی تعبیه شده ، منجر به کدی خواهد شد که حفظ آن آسان تر است اما مهمتر از همه ، بسیار مطمئن تر.
این اجرای نشان می دهد که روشهای پیچیده توسعه وب می تواند ضمن تمیز نگه داشتن کد و سیستم قابل اعتماد ، با موفقیت برای محیط های محدود شده از منابع سازگار شود.
منابع
- React مبتنی بر رویداد تعبیه شده: eer