مقدمهای بر معماری FreeRTOS
FreeRTOS یک کرنل سبکوزن (Lightweight Kernel) است که بهطور خاص برای میکروکنترلرها و سیستمهای توکار طراحی شده است.
هدف آن فراهم کردن یک هستهی قابل پیشبینی برای مدیریت چندین Task، وقفه، صف، Semaphore، Mutex، Timer و سایر مکانیزمهای همزمانی است.
FreeRTOS معمولاً بین سختافزار (Hardware) و برنامهی کاربردی (Application Code) قرار میگیرد و مانند یک مدیر مرکزی وظایف را بین منابع سیستم (CPU، حافظه و زمان) توزیع میکند.
ساختار لایهای FreeRTOS
برای درک بهتر، FreeRTOS را میتوان به سه لایهی اصلی تقسیم کرد:
لایهی Kernel Core
شامل هستهی زمانبندی (Scheduler)، مدیریت Task، صف، Semaphore و حافظه است.
این بخش از FreeRTOS برای تمام پلتفرمها مشترک بوده و مستقل از سختافزار است. این لایه در واقع قلب اصلی FreeRTOS محسوب میشود و تمام مکانیزمهای اساسی سیستم مانند ایجاد، حذف و زمانبندی تسکها، مدیریت صفها، Semaphoreها و تایمرهای نرمافزاری در آن پیادهسازی شده است.
کد این بخش به زبان C نوشته شده و بدون نیاز به تغییر، روی تمام معماریها قابل استفاده است.
تمام تعاملات برنامهنویس با RTOS از طریق توابع API این لایه انجام میشود.
Kernel Core همچنین مسئول مدیریت context switching بین تسکها و حفظ پایداری سیستم در هنگام وقفهها و عملیات همزمان است.
در واقع، اگر سایر بخشها مانند Port یا Application حذف شوند، این لایه همچنان قادر است منطق اصلی RTOS را شبیهسازی کند.
لایهی Port Layer
وظیفهی ارتباط Kernel با سختافزار خاص میکروکنترلر را دارد.
در STM32، این بخش در فایلهای port.c و portmacro.h قرار دارد و شامل:
- تعریف وقفهی سیستم (SysTick)
- نحوهی سوئیچ بین Taskها (Context Switch)
- تنظیم Stack برای هر Task می باشد
این لایه بهنوعی پلی میان کرنل نرمافزاری و سختافزار واقعی است و جزئیات وابسته به معماری پردازنده (مثل ARM Cortex-M) را پیادهسازی میکند.
به عنوان مثال، در STM32 وظیفه دارد وقفه SysTick را پیکربندی کند تا زمانبندی تسکها با دقت بالا انجام شود و همچنین عملیات ذخیره و بازیابی رجیسترها هنگام Context Switch را کنترل نماید.
بدون این لایه، کرنل FreeRTOS نمیتواند با CPU ارتباط برقرار کند یا زمان را اندازه بگیرد.
هر خانواده از میکروکنترلرها نسخهی مخصوص به خود از این فایلها را دارند که به آنها Port Files گفته میشود و معمولاً در مسیر portable/GCC/ARM_CMx/ ذخیره میشوند.
به همین دلیل، در زمان مهاجرت FreeRTOS به سختافزار جدید، بیشترین تغییرات معمولاً در همین لایه انجام میگیرد.
لایهی Application Layer
برنامهنویس در این بخش وظایف خود را تعریف میکند:
void SensorTask(void *argument);
void DisplayTask(void *argument);
و با استفاده از توابع API مانند xTaskCreate()، xQueueSend() یا vTaskDelay() با Kernel تعامل دارد. در این لایه، تمام منطق و رفتار برنامه توسط کاربر طراحی و پیادهسازی میشود.
هر وظیفه (Task) معمولاً برای انجام یک کار مشخص مانند خواندن سنسور، پردازش داده یا ارسال اطلاعات از طریق UART تعریف میشود.
برنامهنویس میتواند با استفاده از توابع API کرنل (مثل xTaskCreate(), vTaskDelay(), xQueueSend()) به سادگی رفتار همزمان و زمانبندیشده را در سیستم ایجاد کند.
در این بخش همچنین نحوهی ارتباط میان وظایف از طریق Queue، Semaphore، Mutex یا Event Group تعیین میشود تا هماهنگی بین ماژولهای مختلف حفظ شود.
در واقع، Application Layer همان بخشی است که ایدهی طراح سیستم بهصورت عملی در قالب وظایف RTOS اجرا میشود.
اجزای اصلی FreeRTOS
FreeRTOS از چند ماژول کلیدی تشکیل شده که هرکدام نقش خاصی در عملکرد سیستم دارند:
| جزء | وظیفه | فایل مرتبط |
| Tasks | واحد اجرایی مستقل | tasks.c, task.h |
| Scheduler | زمانبندی و مدیریت اولویتها | بخشی از tasks.c |
| Queues | تبادل داده بین Taskها | queue.c, queue.h |
| Semaphores / Mutexes | همگامسازی وظایف و جلوگیری از تداخل | semphr.h |
| Software Timers | اجرای وظایف دورهای | timers.c, timers.h |
| Event Groups | اعلان همزمان چند رویداد | event_groups.c |
| Memory Management | تخصیص حافظه برای Stack و اشیاء RTOS | heap_1.c تا heap_5.c |
نقش Scheduler در FreeRTOS
Scheduler قلب تپندهی RTOS است. وظیفهی آن تصمیمگیری دربارهی این است که در هر لحظه کدام Task باید اجرا شود. Scheduler. در واقع مدیر اجرای تسکها در FreeRTOS است و با بررسی وضعیت و اولویت هر Task تصمیم میگیرد کدامیک باید کنترل CPU را در اختیار بگیرد.
این بخش بهطور مداوم در حال بررسی تسکهای آماده، معلق یا در حال انتظار است و بر اساس سیاست زمانبندی انتخابشده (Preemptive یا Cooperative) عمل میکند.
هر بار که وقفهی سیستم (SysTick) فعال میشود، Scheduler میتواند تصمیم بگیرد که آیا باید Context Switch انجام دهد یا خیر.
در صورت وقوع این تغییر، وضعیت کامل تسک فعلی ذخیره و وضعیت تسک جدید بازیابی میشود تا اجرای آن دقیقاً از نقطهی قبلی ادامه یابد.
بهطور خلاصه، Scheduler تضمین میکند که تسکهای با اولویت بالاتر همیشه در زمان مناسب و بدون تأخیر غیرقابل پیشبینی اجرا شوند.
انواع زمانبندی در FreeRTOS:
- Preemptive Scheduling پیشدستانه:
اگر Task با اولویت بالاتر آماده شود، Task فعلی متوقف و Task جدید اجرا میشود.
تنظیم با #define configUSE_PREEMPTION 1
در این روش، سیستم همیشه در حال بررسی اولویتها است و به محض آماده شدن یک تسک با اولویت بالاتر، اجرای تسک فعلی را قطع میکند.
این مدل مناسب سیستمهای بیدرنگ است، زیرا تضمین میکند وظایف حیاتی همیشه سریعتر از سایر وظایف اجرا شوند و زمان پاسخ بسیار دقیق باشد.
- Cooperative Scheduling تعاونی:
تسکها داوطلبانه CPU را آزاد میکنند (vTaskYield()).در این مدل، تسکها خودشان باید در زمان مناسب CPU را آزاد کنند، در غیر این صورت سایر وظایف نمیتوانند اجرا شوند.
این روش سادهتر و کمهزینهتر از نظر Context Switch است، اما در پروژههای بزرگ نیاز به برنامهریزی دقیق دارد تا هیچ تسکی باعث قفل شدن سیستم نشود. - Round Robin برای وظایف هماولویت:
اگر چند Task با اولویت یکسان آماده باشند، هر کدام بهنوبت و به مدت زمان تعیینشده اجرا میشوند. در این حالت، وظایف با اولویت یکسان بهصورت نوبتی و با سهم زمانی (Time Slice) مساوی اجرا میشوند. این روش باعث میشود عدالت در استفاده از CPU برقرار شود و هیچ تسک هماولویت بهصورت مداوم از اجرای دیگران جلوگیری نکند.
نحوهی پورت FreeRTOS روی STM32
پورت به معنای سازگار کردن هستهی FreeRTOS با سختافزار هدف است.
در STM32، این کار توسط ST در محیط STM32Cube انجام شده و کاربر نیازی به تغییر در فایلهای پورت ندارد.
- فرآیند پورت در STM32CubeMX:
- انتخاب میکروکنترلر یا برد Nucleo/Discovery
- فعالسازی FreeRTOS Middleware در CubeMX
- تنظیم اولویت وقفه SysTick
- تولید کد (Generate Code)
- مشاهده ساختار پروژه در IDE:
/Middlewares/Third_Party/FreeRTOS/
├── CMSIS_RTOS/
├── Source/
├── include/
├── portable/
│ ├── MemMang/
│ └── GCC/ARM_CM4F/
در مسیر portable/GCC/ARM_CM4F فایلهای port.c و portmacro.h مخصوص هسته Cortex-M4 قرار دارند.
نقش CMSIS-OS در پروژههای STM32
CMSIS-RTOS یک لایهی استاندارد است که توسط ARM ارائه شده تا برنامهنویس بدون وابستگی به RTOS خاصی، از یک رابط واحد برای کار با Taskها، Semaphoreها و Queueها استفاده کند. این لایه در واقع نقش یک واسط ترجمه (Abstraction Layer) را دارد تا کدهای کاربر از جزئیات داخلی FreeRTOS بینیاز شوند و قابل حملتر باشند.
با استفاده از CMSIS-OS، اگر در آینده بخواهید RTOS سیستم را تغییر دهید (مثلاً از FreeRTOS به RTX)، فقط لازم است لایهی واسط را عوض کنید و نیازی به بازنویسی کل برنامه نیست.
این استاندارد همچنین باعث میشود تا سازگاری کامل با HAL و درایورهای STM32Cube حفظ شود، چون اغلب این درایورها برای کار در محیط CMSIS طراحی شدهاند.
در نتیجه، CMSIS-OS ارتباط بین بخشهای سطح پایین سختافزاری (HAL) و بخشهای سطح بالای نرمافزاری Taskهای کاربر را هماهنگ و یکپارچه میسازد.
به بیان ساده، CMSIS-OS باعث میشود توسعهدهنده بتواند با تمرکز بر منطق برنامه، از پیچیدگیهای درونی RTOS دور بماند.
در پروژههای STM32:
- FreeRTOS بهعنوان کرنل اصلی استفاده میشود.
- CMSIS-OS بهعنوان واسط بین HAL و FreeRTOS عمل میکند.
مثال:
// لایهی CMSIS-OS
osThreadNew(Task1, NULL, &Task1_attributes);
// لایهی FreeRTOS معادل
xTaskCreate(Task1, "Task1", 128, NULL, 1, NULL);
این استانداردسازی باعث میشود در آینده بتوان RTOS را بدون تغییر اساسی در کد، جایگزین کرد مثلاً RTX یا Zephyr.
فایلهای مهم در پروژهی FreeRTOS
| فایل | توضیح |
| FreeRTOSConfig.h | تنظیمات سیستم: اندازه Stack، اولویتها، فعالسازی Timer و غیره |
| tasks.c / task.h | مدیریت وظایف و Scheduler |
| queue.c / queue.h | مدیریت صفها |
| port.c / portmacro.h | وابسته به سختافزار، شامل Context Switch |
| heap_x.c | انتخاب روش تخصیص حافظه (۵ نوع مختلف) |
| timers.c / timers.h | اجرای تایمر نرمافزاری |
| cmsis_os2.c / cmsis_os2.h | واسط استاندارد بین HAL و FreeRTOS |
مکانیزم زمان در FreeRTOS
FreeRTOS از یک وقفهی سختافزاری به نام SysTick برای شمارش زمان استفاده میکند.
هر بار که این وقفه فعال میشود (مثلاً هر ۱ میلیثانیه)، RTOS:
- شمارنده زمان (Tick Count) را افزایش میدهد.
- بررسی میکند آیا تسکی باید از حالت Blocked به Ready تغییر کند یا نه.
- در صورت لزوم Context Switch انجام میدهد.
با تابع vTaskDelay() میتوان تسکی را برای چند Tick متوقف کرد:
vTaskDelay(pdMS_TO_TICKS(1000)); // توقف برای ۱ ثانیه
در واقع SysTick Timer یکی از تایمرهای داخلی هستهی Cortex-M است که در تمام میکروکنترلرهای STM32 وجود دارد و برای تولید سیگنالهای زمانی دقیق به کار میرود.
FreeRTOS از این وقفه برای اندازهگیری گذر زمان، کنترل تأخیرها (vTaskDelay) و فعال کردن تسکهایی که زمان انتظارشان به پایان رسیده استفاده میکند.
فرکانس SysTick معمولاً طوری تنظیم میشود که هر tick معادل ۱ میلیثانیه باشد، ولی میتوان بسته به نیاز پروژه آن را تغییر داد.
دقت و پایداری این تایمر نقش بسیار مهمی در عملکرد قابل پیشبینی RTOS دارد، زیرا هرگونه خطا یا تغییر در نرخ SysTick میتواند کل زمانبندی تسکها را دچار انحراف کند.
به همین دلیل توصیه میشود هنگام پیکربندی، اولویت وقفهی SysTick از سایر وقفهها بالاتر باشد تا زمانبندی سیستم دچار تأخیر نشود.
نحوهی تخصیص حافظه (Memory Management)
| فایل | ویژگی | توضیح |
| heap_1.c | سادهترین حالت | فقط تخصیص، بدون آزادسازی |
| heap_2.c | آزادسازی ممکن است | مناسب سیستمهای متوسط |
| heap_3.c | استفاده از malloc/free استاندارد C | |
| heap_4.c | تخصیص با بلوکهای مختلف، کارایی بالا | |
| heap_5.c | امکان استفاده از چند ناحیه حافظه |
در پروژههای STM32 معمولاً heap_4.c بهدلیل انعطاف و بازده بالا استفاده میشود.
فلوچارت عملکرد FreeRTOS
+------------------+
| MCU Startup |
+------------------+
|
v
+------------------+
| HAL & Drivers |
+------------------+
|
v
+------------------+
| FreeRTOS Init |
| - Create Tasks |
| - Init Queue |
| - Init Timer |
+------------------+
|
v
+------------------+
| vTaskStartScheduler() |
+------------------+
|
v
|<---- Scheduler Loop ---->|
مراحل اجرای یک پروژه ساده در CubeMX
هدف: ایجاد یک پروژه با دو Task مستقل
گامها:
- باز کردن STM32CubeMX و انتخاب برد یا MCU
- فعالسازی سیستمعامل FreeRTOS از تب Middleware
- تنظیم دو Task با نامهای Task1 و Task2
- انتخاب گزینه Generate Code و باز کردن پروژه در STM32CubeIDE
- ویرایش فایل freertos.c:
مثال 1 :
/* USER CODE BEGIN Header_StartGreenLED */
/**
* @brief Function implementing the GreenLED thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartGreenLED */
void StartGreenLED(void *argument)
{
/* USER CODE BEGIN StartGreenLED */
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(GREEN_LED_GPIO_Port, GREEN_LED_Pin);
osDelay(200);
}
/* USER CODE END StartGreenLED */
}
/* USER CODE BEGIN Header_StartBlueLED */
/**
* @brief Function implementing the BlueLED thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartBlueLED */
void StartBlueLED(void *argument)
{
/* USER CODE BEGIN StartBlueLED */
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(BLUE_LED_GPIO_Port, BLUE_LED_Pin);
osDelay(500);
}
/* USER CODE END StartBlueLED */
}
/* USER CODE BEGIN Header_StartRedLED */
/**
* @brief Function implementing the RedLED thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartRedLED */
void StartRedLED(void *argument)
{
/* USER CODE BEGIN StartRedLED */
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(RED_LED_GPIO_Port, RED_LED_Pin);
osDelay(1000);
}
/* USER CODE END StartRedLED */
}
6.اجرای برنامه → LEDها با 3 سرعت متفاوت چشمک میزنند.
نکات کلیدی
- در پروژههای FreeRTOS، هیچ حلقهی while(1) در main.c وجود ندارد؛ پس از osKernelStart()، Scheduler کنترل را در دست میگیرد.
- هر Task باید درون حلقهی بینهایت خودش باشد تا پس از اجرا از بین نرود.
- تابع vTaskDelay() یا osDelay() جایگزین HAL_Delay() میشود.
- تمام توابع HAL باید thread-safe باشند یا با Mutex محافظت شوند.
جمعبندی
- FreeRTOS شامل سه بخش اصلی است:
هستهی مستقل از سختافزار، لایهی پورت مخصوص MCU، و لایهی برنامهی کاربر. - Scheduler تصمیم میگیرد که در هر لحظه کدام Task اجرا شود.
- در STM32، ارتباط FreeRTOS و HAL از طریق CMSIS-OS برقرار میشود.
- با CubeMX میتوان بدون نیاز به تنظیمات دستی، پروژهی RTOS را سریع ساخت.
علاوه بر این، FreeRTOS بهدلیل ماژولار بودن ساختار خود، بهراحتی قابل گسترش و سفارشیسازی برای پروژههای کوچک تا صنعتی است.پشتیبانی از ابزارهای STM مانند STM32CubeIDE و SystemView نیز امکان تحلیل عملکرد تسکها و اشکالزدایی دقیق را فراهم میکند.همچنین طراحی استاندارد و شفاف آن باعث شده FreeRTOS بهعنوان پایهی بسیاری از سیستمهای تجاری و IoT مورد استفاده قرار گیرد.در مجموع، آشنایی کامل با معماری FreeRTOS به توسعهدهندگان کمک میکند تا نرمافزارهایی قابل پیشبینی، پایدار و زمانسنجیشده برای سیستمهای نهفته طراحی کنند.
منابع
FreeRTOS Reference Manual
UM1722 – Developing Applications on STM32Cube with RTOS (STMicroelectronics)