Cách FreeRTOS cấp phát bộ nhớ cho Task
Trong FreeRTOS, mỗi task cần hai vùng nhớ quan trọng:
- TCB (Task Control Block): chứa thông tin quản lý task.
- Stack: vùng ngăn xếp riêng của task.
FreeRTOS cung cấp hai cơ chế cấp phát bộ nhớ cho task
- Dynamic Allocation -
xTaskCreate(). - Static Allocation -
xTaskCreateStatic().
Hai cơ chế này khác nhau ở nơi cấp phát bộ nhớ và cách quản lí RAM.
1. Dynamic Allocation
Khi sử dụng xTaskCreate(), FreeRTOS sẽ tự động cấp phát bộ nhớ cho TCB và Stack của Task. Bộ nhớ được lấy từ heap của freeRTOS. Task được tạo tại runtime và bộ nhớ sẽ được giải phóng khi task bị delete (trừ khi dùng heap_1 không hỗ trợ vPortFree()).
Về bản chất của cấp phát động thì ta sử dụng hàm vPortMalloc(), hàm này sẽ gọi đến hàm malloc() trong c. Tuy nhiên vPortMalloc() được xây dựng để đảm bảo độ an toàn của cả hệ thống thay vì gọi trực tiếp malloc().
Cấu trúc của hàm xTaskCreate()
BaseType_t xTaskCreate(
TaskFunction_t pvTaskCode, // Con trỏ đến hàm thực hiện task
const char * const pcName, // Tên task (để debug)
uint16_t usStackDepth, // Độ sâu Stack (tính bằng "word", không phải byte)
void *pvParameters, // Tham số truyền vào task
UBaseType_t uxPriority, // Độ ưu tiên của task
TaskHandle_t *pxCreatedTask // Biến lưu trữ handle (định danh) của task
);
Với kiểu cấp phát này, TCB và Stack sẽ nằm trên heap.

Ưu điểm
- Dễ sử dụng, linh hoạt.
- Có thể tạo task tại runtime.
Nhược điểm
- Phân mảnh heap, cấp phát có thể bị fail do hết memory.
- Phụ thuộc vào loại heap (heap_1, heap_2, heap_3, heap_4, heap_5).
Ví dụ
#include <Arduino_FreeRTOS.h>
void TaskBlink(void *pvParameters) {
pinMode(LED_BUILTIN, OUTPUT);
for (;;) {
digitalWrite(LED_BUILTIN, HIGH);
vTaskDelay(500 / portTICK_PERIOD_MS); // Nghỉ 500ms
digitalWrite(LED_BUILTIN, LOW);
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
void setup() {
xTaskCreate(
TaskBlink, // Hàm thực thi
"BlinkTask", // Tên task
1024, // Stack size (word)
NULL, // Tham số
1, // Độ ưu tiên
NULL // Không cần lưu handle
);
}
void loop() {
// FreeRTOS quản lý task, loop có thể để trống hoặc chạy code khác
}
2. Static Allocation
Với Static Allocation, FreeRTOS không cấp phát bộ nhớ cho task, dev phải tự cấp phát sẵn cho TCB và Stack. FreeRTOS chỉ sử dụng vùng RAM đó để khởi tạo task tại runtime.
Điều kiện sử dụng: Trong FreeRTOSConfig.h phải bật
#define configSUPPORT_STATIC_ALLOCATION 1
Cấu trúc của hàm xTaskCreateStatic
TaskHandle_t xTaskCreateStatic(
TaskFunction_t pxTaskCode, // Hàm thực hiện tác vụ
const char * const pcName, // Tên tác vụ (để debug)
const uint32_t ulStackDepth, // Độ sâu Stack (số lượng word, không phải byte)
void *pvParameters, // Tham số truyền vào task
UBaseType_t uxPriority, // Độ ưu tiên
StackType_t *puxStackBuffer, // Mảng bộ nhớ cho Stack
StaticTask_t *pxTaskBuffer // Biến lưu trữ cấu trúc TCB của Task
);
Với Static Allocation:
- TCB và Stack nằm tại nơi bạn khai báo biến.
- Thường nằm trong
.bsshoặc.data.

Ưu điểm
- Không phân mảnh heap
- Deterministic (phù hợp real-time system)
- Không lo malloc fail
Nhược điểm
- RAM bị chiếm cố định
- Không linh hoạt
- Phải tính toán stack cẩn thận
Ví dụ
#include <Arduino_FreeRTOS.h>
// 1. Khai báo bộ nhớ tĩnh
#define STACK_SIZE 128
StaticTask_t xTaskBuffer; // TCB của Task
StackType_t xStack[STACK_SIZE]; // Stack của Task
// Hàm thực thi Task
void vMyTask(void *pvParameters) {
for (;;) {
Serial.println("Task đang chạy...");
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void setup() {
Serial.begin(9600);
// 2. Tạo task tĩnh
xTaskCreateStatic(
vMyTask, // Hàm xử lý
"StaticTask", // Tên task
STACK_SIZE, // Kích thước stack
NULL, // Tham số
1, // Độ ưu tiên
xStack, // Mảng stack đã khai báo
&xTaskBuffer // Biến TCB đã khai báo
);
}
void loop() {
}
3. Chủ đề tiếp theo
Trong bài viết tiếp theo, tôi sẽ giải thích rõ về các ưu và nhược điểm khi dùng từng loại bộ nhớ heap (heap_1, heap_2, heap_3, heap_4, heap_5) như nào ?
Câu hỏi
Có thể sử dụng song song 2 kiểu cấp phát không ?
Yes
Chỉ cần trong FreeRTOSConfig.h:
#define configSUPPORT_STATIC_ALLOCATION 1
#define configSUPPORT_DYNAMIC_ALLOCATION 1
Khi nào dùng static
- Idle task
- Timer task
- Task quan trọng (sensor, control loop, safety)
- Task chạy suốt vòng đời hệ thống
Khi nào dùng dynamic
- Task tạo theo trạng thái hệ thống
- Task tạm thời (communication session, OTA, CLI)
- Module plug-in
- Test/debug task
All rights reserved