مفهوم معماری ماژولار
معماری ماژولار به معنای تقسیم سیستم به بخشهای کوچک، مستقل و قابل توسعه است که هر بخش یک وظیفهی مشخص دارد.
در این مدل، هر ماژول مانند یک بلوک جداگانه طراحی میشود که از طریق رابطهای استاندارد (Interface) با سایر بخشها ارتباط دارد.
در پروژههای RTOS، ماژولها معمولاً بهصورت Task یا گروهی از Taskها پیادهسازی میشوند.
مثلاً:
- Task 1 → جمعآوری داده از سنسور (Sensor Module)
- Task 2 → پردازش داده (Processing Module)
- Task 3 → ارسال داده از طریق UART (Communication Module)
مزیت اصلی این ساختار:
- افزایش قابلیت نگهداری (Maintainability)
- افزایش خوانایی و تستپذیری (Testability)
- امکان توسعه یا جایگزینی هر ماژول بدون تغییر کل سیستم
در معماری ماژولار، هر بخش از سیستم طوری طراحی میشود که بتواند بهصورت مستقل توسعه، آزمایش و حتی جایگزین شود، بدون آنکه نیاز به تغییر در سایر بخشها باشد.
این روش باعث کاهش وابستگی (Coupling) بین اجزای سیستم و افزایش انسجام داخلی (Cohesion) هر ماژول میشود.
همچنین در پروژههای تیمی، توسعهی موازی بین اعضا سادهتر میگردد، زیرا هر نفر میتواند روی ماژول خود کار کند بدون اینکه کد بقیه را مختل کند.
در نهایت، این ساختار امکان استفادهی مجدد (Reusability) از ماژولها را در پروژههای مشابه فراهم میکند و موجب کاهش زمان توسعه میشود.
لایهبندی (Layered Design)
در طراحی ماژولار برای میکروکنترلرها، معمولاً از سه لایه استفاده میشود:
| لایه | توضیح |
| Driver Layer (HAL Layer) | شامل درایورهای سختافزاری مثل ADC، UART، GPIO |
| Service Layer (RTOS Services) | شامل تسکها، سمافور، صفها و منابع اشتراکی |
| Application Layer (Logic Layer) | شامل منطق کنترل، پردازش داده و تصمیمگیری سیستم |
در این ساختار لایهای، هر لایه فقط از خدمات لایهی پایینتر استفاده میکند و هیچگاه مستقیماً با سختافزارهای غیرمرتبط ارتباط نمیگیرد.
این باعث میشود تغییر در سختافزار یا درایور، منطق نرمافزار اصلی را تحتتأثیر قرار ندهد و نگهداری پروژه بسیار آسانتر شود.
همچنین لایهها میتوانند بهصورت جداگانه آزمایش (Unit Test) و بهینهسازی شوند که این موضوع در پروژههای صنعتی و ایمنیمحور اهمیت زیادی دارد.
در پروژههای بزرگ RTOS، حتی گاهی یک Interface Layer بین Service و Application اضافه میشود تا وابستگیها کاملاً از هم جدا شده و کد بهصورت ماژولار و قابلگسترش باقی بماند.
مثال
در یک سیستم دمای محیط:
- ADC → دادهی خام را میگیرد (Driver Layer)
- RTOS → با Semaphore یا Queue بین تسکها ارتباط برقرار میکند (Service Layer)
- منطق کنترل → تصمیم میگیرد فن روشن شود یا خیر (Application Layer)
طراحی تسکها در معماری ماژولار
هر ماژول در RTOS معمولاً یک Task اختصاصی دارد.
هر Task فقط کار خودش را انجام میدهد و از طریق Queue یا Event Flags با سایر Taskها ارتباط دارد. این تفکیک تسکها باعث میشود هر بخش از سیستم بتواند بهصورت مستقل زمانبندی و کنترل شود و خطا در یک Task، عملکرد سایر بخشها را مختل نکند.
بهعنوان مثال، اگر تسک مربوط به سنسور دچار وقفه یا خطا شود، تسک کنترل یا ارتباط همچنان به کار خود ادامه میدهد.
این ویژگی باعث افزایش پایداری (Reliability) و انعطافپذیری (Flexibility) سیستم میشود.
همچنین به کمک اولویتدهی مناسب در Scheduler میتوان اطمینان حاصل کرد که تسکهای حیاتی همواره در زمان مناسب اجرا میشوند و تسکهای غیراولویتدار منابع اضافی را اشغال نکنند.
| Task | وظیفه | نوع ارتباط |
| SensorTask | خواندن داده از ADC (سنسور دما) | ارسال داده از طریق Queue |
| ControlTask | تحلیل داده و تصمیمگیری | دریافت از Queue، ارسال رویداد |
| CommTask | ارسال داده از طریق UART | دریافت از Event Flag |
ارتباط بین ماژولها (Inter-Task Communication)
برای اینکه ماژولها بهصورت ایمن و بدون تداخل با هم ارتباط برقرار کنند، از Queue و Event Flags استفاده میکنیم.
بههیچوجه نباید Taskها مستقیماً تابعهای یکدیگر را صدا بزنند (Coupling بالا).
در معماری ماژولار مبتنی بر RTOS، ارتباط بین تسکها باید غیرمستقیم و ایمن باشد تا از تداخل دادهها و شرایط رقابتی (Race Condition) جلوگیری شود.
استفاده از Queue و Event Flag باعث میشود هر ماژول فقط داده یا رویدادهای مورد نیاز خود را دریافت کند، بدون اینکه از جزئیات داخلی سایر ماژولها مطلع باشد.
این رویکرد علاوهبر افزایش ایمنی دادهها، موجب بهبود مقیاسپذیری و تستپذیری سیستم نیز میشود.
همچنین در صورت نیاز به همگامسازی دقیق بین چند Task، میتوان از Semaphore یا Mutex استفاده کرد تا دسترسی همزمان به منابع مشترک بهصورت کنترلشده انجام گیرد.
ارتباط امن:
- SensorTask → ControlTask → از طریق osMessageQueuePut و osMessageQueueGet
- ControlTask → CommTask → از طریق osEventFlagsSet برای اعلان وضعیت
مثال عملی: طراحی پروژهی سهماژوله در CubeMX (CMSIS-RTOS v2)
در این پروژه:
- ADC از طریق DMA داده را میخواند.
- Task کنترل دما تصمیم میگیرد فن روشن یا خاموش شود.
- Task ارتباط، دادهها را در UART ارسال میکند.
تنظیمات CubeMX
ADC1 → DMA Circular + Channel for Temp Sensor (مثلاً PA0 = IN1)
UART2 → 9600 baud, TX فعال
GPIO → خروجی برای فن (مثلاً PB5)
FreeRTOS → CMSIS-RTOS v2
- 3 تسک بساز: SensorTask, ControlTask, CommTask
- یک Queue (عمق 10، نوع uint16_t)
- یک Event Flag برای ارسال اعلان
ساختار کلی پروژه
SensorTask → Queue → ControlTask → EventFlag → CommTask
↑ DMA Callbackکدها
۱) SensorTask – خواندن دما و ارسال در Queue
void StartSensorTask(void *argument)
{
uint16_t adcVal = 0;
HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&adcVal, 1);
for(;;)
{
osDelay(500); // هر نیمثانیه یک بار
osMessageQueuePut(sensorQueueHandle, &adcVal, 0, 0);
}
}۲) ControlTask – پردازش داده و تصمیمگیری
#define TEMP_THRESHOLD 2000 // حد آستانهی دما (مثلاً 2.0V)
void StartControlTask(void *argument)
{
uint16_t val;
for(;;)
{
if (osMessageQueueGet(sensorQueueHandle, &val, NULL, osWaitForever) == osOK)
{
if (val > TEMP_THRESHOLD)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET); // فن روشن
osEventFlagsSet(commEventHandle, 0x01); // اعلان به تسک ارتباط
}
else
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET); // فن خاموش
}
}
}
}۳) CommTask – ارسال اطلاعات در UART
void StartCommTask(void *argument)
{
uint16_t val;
char msg[40];
for(;;)
{
uint32_t flags = osEventFlagsWait(commEventHandle, 0x01, osFlagsWaitAny, osWaitForever);
if (flags & 0x01)
{
sprintf(msg, "High Temp! ADC=%u\r\n", val);
HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), 100);
}
}
}نکات طراحی حرفهای
Loose Coupling:
هیچ Task نباید مستقیم تابع Task دیگر را صدا بزند. ارتباط فقط از طریق RTOS Objectها باشد.
Clear Interface:
برای هر ماژول یک فایل header تعریف کن (مثلاً sensor.h, control.h, comm.h) تا وابستگیها محدود شوند.
Error Isolation:
اگر یکی از تسکها خطا دهد (مثل Timeout)، بقیهی سیستم باید به کار خود ادامه دهند.
Reusability:
ماژولها باید طوری نوشته شوند که در پروژههای دیگر هم بتوان از آنها استفاده کرد.
Debugging:
در SystemView یا CubeIDE RTOS Viewer، رفتار هر ماژول جدا دیده میشود و میتوان زمان اجرای هر Task را تحلیل کرد.
مزایای طراحی ماژولار در RTOS
| مزیت | توضیح |
| پایداری بالا | خطا در یک Task بقیهی سیستم را از کار نمیاندازد. |
| قابلیت توسعه | افزودن ماژول جدید بدون تغییر در ساختار کلی. |
| افزایش خوانایی | هر بخش وظیفهی مشخصی دارد. |
| قابلیت اشکالزدایی آسان | رفتار هر ماژول را میتوان جدا بررسی کرد. |
| مناسب برای تیمهای بزرگ | توسعهی همزمان چند نفر بدون تداخل. |
جمعبندی
در این فصل یاد گرفتیم که در یک پروژهی RTOS، تقسیم منطقی سیستم به ماژولهای مستقل باعث میشود:
- کد ساختارمندتر، قابلتوسعهتر و پایدارتر باشد.
- عیبیابی و تست راحتتر انجام گیرد.
- ارتباط بین بخشها از طریق سازوکارهای امن RTOS (Queue, Event, Semaphore) صورت گیرد.
این روش در سیستمهای صنعتی و تجاری (مثلاً کنترل ربات، IoT، ابزار پزشکی یا مانیتورینگ صنعتی) اساس طراحی حرفهای محسوب میشود.
منابع
Mastering Embedded Systems with UML State Machines – Takehiko Nishimoto
Hands-On RTOS with Microcontrollers – Brian Amos UM1722 – Developing Applications on STM32Cube with RTOS – STMicroelectronics