You are on page 1of 12

A.

FreeRTOS Thread Management


Note: Quản lý 1 Task thì cần 1 biến kiểu dữ liệu TaskHandle_t
1. Create Task: xTaskCreate(...)
BaseType_t xTaskCreate(
TaskFunction_t pvTaskCode, Tham số thứ nhất: Con trỏ đến hàm mà task sẽ
thực thi
const char * const pcName, Tham số thứ hai: Tên của task (chuỗi ký tự)
configSTACK_DEPTH_TYPE usStackDepth, Tham số thứ ba: Kích thước của stack
cho task (kiểu dữ liệu phụ thuộc vào kiến trúc)
void *pvParameters, Tham số thứ tư: Con trỏ đến các tham số mà bạn
muốn truyền vào task
UBaseType_t uxPriority, Tham số thứ năm: Ưu tiên của task (0 là ưu tiên
cao nhất)
TaskHandle_t *pxCreatedTask Tham số thứ sáu: Con trỏ đến biến để lưu lại
handle của task mới được tạo
);
```
1. `TaskFunction_t pvTaskCode`: Đây là tham số quan trọng nhất của hàm
`xTaskCreate`. Nó là con trỏ đến hàm mà task sẽ thực thi. Đây là nơi mà bạn sẽ
định nghĩa các hoạt động mà task sẽ thực hiện.
2. `const char * const pcName`: Đây là tên của task, được xác định bằng một chuỗi
ký tự. Tên này không làm ảnh hưởng đến hoạt động của task, nhưng nó giúp bạn dễ
dàng nhận ra task khi sử dụng các công cụ debug.
3. `configSTACK_DEPTH_TYPE usStackDepth`: Đây là kích thước của stack cho task,
được định nghĩa bằng số từ (word). Kích thước của stack phải đủ lớn để chứa tất
cả các biến cục bộ và giá trị trung gian của task.
4. `void *pvParameters`: Đây là con trỏ đến các tham số mà bạn muốn truyền vào
task. Thông thường, nếu task cần các tham số đầu vào, bạn có thể truyền chúng vào
qua tham số này.
5. `UBaseType_t uxPriority`: Đây là ưu tiên của task, quyết định việc lập lịch
thực hiện các task. Giá trị càng thấp thì ưu tiên càng cao. Ví dụ, 0 là ưu tiên
cao nhất, còn ở cuối danh sách là giá trị `configMAX_PRIORITIES - 1`.
6. `TaskHandle_t *pxCreatedTask`: Đây là con trỏ đến biến để lưu lại handle của
task mới được tạo. Handle này được sử dụng để thao tác với task sau khi nó đã
được tạo, chẳng hạn như để xóa task hoặc để gửi message cho task.

E.G:
xTaskCreate(vBlueLedControlerTask,
"Blue Led Controller",
100,
NULL,
1,
NULL;

void vBlueLedControlerTask(void* pvParameters)


{
while (1)
{
printf("Blue Led Controller");
}
}
2. Create Task from other Task
E.G:
void vBlueLedControlerTask(void* pvParameters)
{
xTaskCreate(vGreenLedControlerTask,
"Green Led Controller",
100,
NULL,
1,
NULL);
while (1)
{
printf("Blue Led Controller");
}
}

void vGreenLedControlerTask(void* pvParameters)


{
while (1)
{
printf("Blue Led Controller");
}
}

NOTE: Các task có nhiệm vụ giống nhau thì chỉ cần thay đổi cái
Parameter truyền vào khi gọi hàm xTaskCreate.
E.G:
void vLedControlerTask(void* pvParameters)
{
while (1)
{
.....
}
}
xTaskCreate(vLedControlerTask,
"Blue Led Controller",
100,
(void*)Blue,
1,
NULL;

xTaskCreate(vLedControlerTask,
"Green Led Controller",
100,
(void*)Green,
1,
NULL;
...

3. Changing a Task Priority in Runtime: vTaskPrioritySet(…, …)


NOTE: A lower priority task cannot interrupt a higher priority
task.
E.G:
TaskHandle_t blueHandle, greenHandle, ...
vTaskPrioritySet(greenHandle, 3);
Để thay đổi Priority cho chính Task đó thì Param đầu truyền vào NULL
→ vTaskPrioritySet(NULL, 1);

4. Reading the Priority of a Task: uxTaskPriorityGet(...)

5. Suspending a Task: vTaskSuspend(...)


- Tự đình chỉ thì truyền vào NULL

6. Resuming a suspended Task: vTaskResume(...)

7. Terminating a Task: vTaskDelete(...)


- Tự hủy thì truyển vào NULL
8. vTaskStartScheduler()
A. Task States

9. Using the Blocked State to create delays: vTaskDelay ( pdMS_TO_TICKS(100) )


- pdMS_TO_TICKS(): Convert a time specified milliseconds into a
time specified in tick
E.G:
const TickType_t _50ms_ = pdMS_TO_TICKS(50);
vTaskDelay(_50ms_);
10. Executing Tasks Periodically (Nhằm cho 1 task thưc hiện công việc sau 1 khoảng thời gian
định kỳ)
void vBlueLedControlerTask(void* pvParameters)
{
TickType_t xLastWakeTime;
const TickType_t xPeriod = pdMS_TO_TICKS(500);
xLastWakeTime = xTaskGetTickCount();

while (1)
{
countBlue++;
vTaskDelayUntil(&xLastWakeTime, xPeriod); /*xLastWakeTime
sẽ được cập nhật ở đây nên đảm bảo Task thực hiện định kỳ
đúng 500ms*/
}
}

NOTE: Another way to achieve that, of course, would be to create


a hardware interrupt using a timer. Because with a hardware
timer, you can come a time to arm, to create and interrupt every
500 times.
11. The Idle Task

- Idle là Task được tạo tự động, không cần tự tạo lại như các Task khác.

NOTE: Nhớ đặt cái Macro “ConfigUSE_IDLE_HOOK” trong thư viện


FreeRTOSConfig.h lên 1: #define configUSE_IDLE_HOOK 1

void vApplicationIdleHook(void)
{
while (1)
{
countIdle++; /*Code trong đây*/
}
}
B. Queue and Queueset Management (include “queue.h”)

Các APIs thường dùng:


Note:

- Quản lý 1 Queue cần 1 biến có kiểu dữ liệu QueueHandle_t


- Các Task có cùng mức độ ưu tiên khi gửi dữ liệu vào Queue sẽ gửi
đan xe nhau chứ không gửi theo kiểu Task này được gửi nhiều hơn
Task khác.

1. xQueueSend() : Ghi dữ liệu vào Queue

BaseType_t xQueueSend(QueueHandle_t xQueue,


const void * pvItemToQueue,
TickType_t xTicksToWait);
Trong đó:
 xQueue: là con trỏ đến hàng đợi mà bạn muốn gửi dữ liệu vào.
 pvItemToQueue: là con trỏ đến dữ liệu mà bạn muốn gửi vào hàng đợi.
 xTicksToWait: là thời gian chờ tối đa (trong tick) nếu hàng đợi đầy.
Bạn có thể sử dụng `portMAX_DELAY` để cho hàm chờ vô hạn, hoặc một giá
trị thời gian chờ cụ thể (được tính theo tick time của hệ thống).
Hàm `xQueueSend()` trả về:
 pdPASS: nếu gửi dữ liệu vào hàng đợi thành công.
 errQUEUE_FULL: nếu hàng đợi đã đầy và không thể gửi dữ liệu vào.

2. xQueueReceive() : Lấy dữ liệu từ Queue

BaseType_t xQueueReceive(QueueHandle_t xQueue,


void * pvBuffer,
TickType_t xTicksToWait);
Trong đó:
 xQueue: là con trỏ đến hàng đợi mà bạn muốn nhận dữ liệu từ.
 pvBuffer: là con trỏ đến vùng nhớ mà dữ liệu từ hàng đợi sẽ được sao
chép vào.
 xTicksToWait: là thời gian chờ tối đa (trong tick) nếu hàng đợi không
có dữ liệu để nhận. Bạn có thể sử dụng `portMAX_DELAY` để cho hàm chờ
vô hạn, hoặc một giá trị thời gian chờ cụ thể (được tính theo tick
time của hệ thống).

Hàm xQueueReceive() trả về:


 pdPASS nếu nhận dữ liệu từ hàng đợi thành công.
 errQUEUE_EMPTY nếu hàng đợi không có dữ liệu để nhận.

3. xQueueCreate() : Tạo Queue

QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength,


UBaseType_t uxItemSize);

Trong đó:
 uxQueueLength: là độ dài của hàng đợi, tức là số lượng phần tử mà hàng
đợi có thể chứa. Tham số này không phải là kích thước dữ liệu mà hàng
đợi sẽ chứa, mà là số lượng phần tử.
 uxItemSize: là kích thước của mỗi phần tử trong hàng đợi, tính bằng
byte. Tham số này xác định kích thước của dữ liệu mà mỗi phần tử trong
hàng đợi có thể chứa.
Hàm xQueueCreate() trả về một con trỏ kiểu `QueueHandle_t` đến hàng đợi
được tạo.
E.G:
QueueHandle_t xQueue;
xQueue = xQueueCreate(10, sizeof(uint32_t));

- Enable Queue Set: configUSE_QUEUE_SETS in FreeRTOSConfig.h


4. xQueueCreateSet()

QueueSetHandle_t xQueueCreateSet(UBaseType_t uxEventQueueLength);


Trong đó:
uxEventQueueLength: là số lượng hàng đợi sự kiện (event queue) mà tập
hợp hàng đợi này có thể chứa. Tổng số lượng phần tử của các Queue
Hàm xQueueCreateSet() trả về một con trỏ kiểu `QueueSetHandle_t` đến tập
hợp hàng đợi được tạo.
Tập hợp hàng đợi được tạo bằng hàm này có thể được sử dụng để giám sát
nhiều hàng đợi sự kiện cùng một lúc. Điều này hữu ích khi bạn muốn chờ đợi
cho một trong các sự kiện xảy ra từ nhiều nguồn khác nhau.
E.G:
QueueSetHandle_t xQueueSet = NULL;
/*Create a QueueSet to hold 2 Queues. Each Queue holds 1 element*/
xQueueSet=xQueueCreateSet(1*2);

5. xQueueAddToSet()

Hàm `xQueueAddToSet()` được sử dụng để thêm một hàng đợi hoặc semaphore vào
một tập hợp hàng đợi trước đó đã được tạo bằng cách gọi hàm
`xQueueCreateSet()`.
Nguyên mẫu:
BaseType_t xQueueAddToSet(QueueSetMemberHandle_t xQueueOrSemaphore,
QueueSetHandle_t xQueueSet);
Mô tả:
xQueueOrSemaphore: là handle của hàng đợi hoặc semaphore mà bạn muốn
thêm vào tập hợp hàng đợi. Đối số này được chuyển đổi sang kiểu
`QueueSetMemberHandle_t`.
xQueueSet: là handle của tập hợp hàng đợi mà bạn muốn thêm hàng đợi
hoặc semaphore vào.
Hàm `xQueueAddToSet()` trả về `pdPASS` nếu hàng đợi hoặc semaphore được
thêm vào tập hợp hàng đợi thành công. Trong trường hợp hàng đợi đã là thành
viên của một tập hợp hàng đợi khác, hàm trả về `pdFAIL`.
Lưu ý: Để sử dụng được hàm `xQueueAddToSet()`, cần phải thiết lập
`configUSE_QUEUE_SETS` thành 1 trong tệp `FreeRTOSConfig.h`.
E.G:
/*Add two queues to queue set*/
xQueueAddToSet (xQueue1,xQueueSet); /*Add xQueue1 to queue set*/
xQueueAddToSet (xQueue2,xQueueSet); /*Add xQueue2 to queue set*/

6. xQueueSelectFromSet()

Hàm `xQueueSelectFromSet()` trong FreeRTOS có tác dụng chờ đợi và lựa chọn
một hoặc nhiều queues hoặc semaphores từ một set (Queue Set) đã được chỉ
định.
Cụ thể, hàm này thường được sử dụng trong các tác vụ (tasks) để chờ đợi
trên một nhóm queues hoặc semaphores. Khi một trong các queues hoặc
semaphores trong set trở thành sẵn sàng (ví dụ như có dữ liệu mới được gửi
vào queue), hàm `xQueueSelectFromSet()` sẽ trả về một handle (tham chiếu)
tới queue hoặc semaphore đó, cho phép tác vụ tiếp tục xử lý dữ liệu từ đó.
Điểm quan trọng là hàm này cho phép tác vụ chờ đợi trên nhiều nguồn (queues
hoặc semaphores) một cách hiệu quả, không cần phải sử dụng nhiều hàm chờ
đợi riêng lẻ.
QueueHandle_t xQueueSelectFromSet(QueueSetHandle_t xQueueSet,
TickType_t xTicksToWait);
 xQueueSet: Đây là handle của Queue Set mà bạn muốn chờ đợi và lựa chọn
các queues hoặc semaphores từ đó.
 xTicksToWait: Đây là thời gian mà tác vụ sẽ chờ đợi trước khi hủy bỏ
hoạt động chờ đợi. Bạn có thể sử dụng `portMAX_DELAY` để chờ đợi vô
thời hạn.
Hàm này trả về handle của queue hoặc semaphore đầu tiên trong set mà đã sẵn
sàng, hoặc NULL nếu đã hết thời gian chờ đợi mà không có queue hoặc
semaphore nào sẵn sàng.
E.G:
QueueHandle_t xQueueThatContainsData;
xQueueThatContainsData = xQueueSelectFromSet(xQueueSet,portMAX_DELAY);
xQueueReceive(xQueueThatContainsData,&pcReceivedString,0);

C. SEMAPHONES (include “semphr.h”)


- Semaphone là 1 tín hiệu hoặc 1 key được gửi giữa các Tasks hoặc giữa
các Tasks và Interrupts. Nó không mang dữ liệu (data). Semaphore trong
FreeRTOS thường được sử dụng để đảm bảo an toàn khi truy cập vào các
tài nguyên chia sẻ, tránh tình trạng đua đòi giữa các task hoặc ISR,
và giữ cho các task thực hiện đồng bộ theo các luồng cụ thể.
- Tưởng tượng: có 1 căn phòng (tài nguyên của CPU), và để có thể vào
được căn phòng đó để thực hiện nhiệm vụ (các câu lệnh) thì các task
cần phải có chìa khóa (Semaphore). Và số lượng chìa khóa có thể đánh
ra được nhiều cái tùy người dùng config. Trước khi vào phòng, các task
sẽ “take” semaphore có sẵn, nếu không thì task sẽ “wait” cho tới khi
có semaphore, sau khi đã có được semarphore task sẽ thực thi nhiệm vụ
của mình và “Give” semaphore về lại “chỗ cũ”. Ngoài các task ra thì
các hàm xử lý ngắt cũng có thể “Give” semaphore.
- Phân loại:
o Binary Semaphone: Mang 2 giá trị ‘1’ or ‘0’, để biểu thị có tín
hiệu (signal) hay không. Một Task có key hay không.
o Counting Semaphone:
o Mutex: Viết tắt của loại trừ lẫn nhau (mutual exclusion). Cho phép
nhiều Tasks truy cập vào 1 tài nguyên được chia sẻ nhưng chỉ 1 tác
vụ tại 1 thời điểm.
https://www.geeksforgeeks.org/difference-between-counting-and-binary-semaphores/

Các APIs thường dùng:


- Quản lý 1 Semaphone cần 1 biến có kiểu dữ liệu SemaphoneHandle_t
BaseType_t xSemaphoreTake(
SemaphoreHandle_t xSemaphore,
TickType_t xTicksToWait sẵn
);
Trong đó:
- xSemaphore: Semaphore handle, được tạo và trả về bởi hàm
`xSemaphoreCreateBinary()` hoặc `xSemaphoreCreateCounting()`.
- xTicksToWait: Thời gian chờ (tính bằng ticks) trước khi quyết
định rằng Semaphore không có sẵn. Nếu Semaphore không có sẵn ngay lập
tức, task sẽ chờ cho đến khi Semaphore trở nên có sẵn hoặc thời gian
chờ đã hết.
Return:
- pdTRUE: Nếu Semaphore đã được lấy thành công.
- pdFALSE: Nếu Semaphore không thể được lấy trong thời gian chờ đã
cho.
Hàm `xSemaphoreTake` được sử dụng để lấy Semaphore từ một task. Nó sẽ
chờ cho đến khi Semaphore có sẵn nếu Semaphore hiện không có sẵn,
hoặc nếu đã vượt quá thời gian chờ cho phép. Hàm này thường được sử
dụng để truy cập vào các tài nguyên được bảo vệ bởi Semaphore.
I. Binary Semaphone
1. xSemaphoreCreateBinary

SemaphoreHandle_t xSemaphoreCreateBinary( void );

NOTE: Khi sử dụng hàm này, bạn cần kiểm tra kết quả trả về xem semaphore đã
được tạo thành công hay không, bằng cách kiểm tra xem giá trị trả về có là
NULL hay không.
2. xSemaphoreGive
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
- Hàm này được sử dụng để "give" (trả lại) một semaphore, tăng giá trị
của semaphore đó. Nếu có các task đang chờ semaphore, nó sẽ cho phép
task đầu tiên trong hàng đợi tiếp tục thực hiện, và semaphore sẽ được
giảm đi một.
- Trong ngữ cảnh của semaphore nhị phân, việc gọi hàm này sẽ đặt
semaphore về giá trị 1, cho phép một task khác đang chờ (nếu có) thực
hiện tiếp.
- Giá trị trả về của hàm là pdTRUE nếu semaphore đã được "give", và
pdFALSE nếu không thể "give" semaphore (thường xảy ra khi semaphore
không tồn tại hoặc không hợp lệ).
- Cần gọi hàm xSemaphoreGive() đầu tiên trong 1 Task nào đó (Viết ngoài
while) do mặc định ban đầu Semaphone có giá trị = 0.
3. xSemaphoreGiveFromISR

BaseType_t xSemaphoreGiveFromISR(
SemaphoreHandle_t xSemaphore,
BaseType_t *pxHigherPriorityTaskWoken
);

Trong đó:
- xSemaphore: Semaphore handle, được tạo và trả về bởi hàm
`xSemaphoreCreateBinary()` hoặc `xSemaphoreCreateCounting()`.
- pxHigherPriorityTaskWoken: Con trỏ tới một biến kiểu `BaseType_t` được
sử dụng để đánh dấu xem một task với mức độ ưu tiên cao hơn đã được thức
tỉnh trong ISR hay không. Nếu một task với mức độ ưu tiên cao hơn cần được
thức tỉnh, biến này sẽ được đặt thành `pdTRUE`; ngược lại, nếu không có
task nào cần được thức tỉnh, biến này sẽ được đặt thành `pdFALSE`.
Return:
- pdPASS: Nếu Semaphore đã được trả lại thành công từ một ISR.
- errQUEUE_FULL: Nếu Semaphore đã đạt đến giá trị tối đa và không thể
được trả lại.

Hàm `xSemaphoreGiveFromISR` được sử dụng trong ISR để trả lại Semaphore,


cho phép các task tiếp tục thực hiện sau khi một tài nguyên đã được giải
phóng bởi ISR. Nó thường được sử dụng trong trường hợp cần thực hiện một
hoạt động ngắn gọn và không thể gọi các hàm chờ đợi trong ISR. Sau khi gọi
`xSemaphoreGiveFromISR`, ISR thường cần kiểm tra biến
`pxHigherPriorityTaskWoken`. Nếu giá trị của biến này là `pdTRUE`, ISR cần
gọi hàm `portYIELD_FROM_ISR()` hoặc `portEND_SWITCHING_ISR()` để thông báo
cho kernel rằng một task với mức độ ưu tiên cao hơn đã được thức tỉnh và có
thể cần phải chuyển giao luồng thực thi.
E.G:
SemaphoreHandle_t xBinarySemaphore;
void redLedController(void *pvParameters)
{
xSemaphoreGive(xBinarySemaphore);
while(1)
{
xSemaphoreTake(xBinarySemaphore,portMAX_DELAY);
RedLEDProfiler++;
printf("this is red task\r\n");
xSemaphoreGive(xBinarySemaphore);
vTaskDelay(1); /*Cần delay 1 chút nếu không thì Task lại lấy luôn token*/
}
}
void yellowLedController(void *pvParameters)
{
while(1)
{
xSemaphoreTake(xBinarySemaphore,portMAX_DELAY);
YellowLEDProfiler++;
printf("this is yellow task\r\n");
xSemaphoreGive(xBinarySemaphore);
vTaskDelay(1);
}
}
II. Counting Semaphone
- Enable: configUSE_COUNTING_SEMAPHONES in FreeRTOSConfig.h
1. xSemaphoreCreateCounting
SemaphoreHandle_t xSemaphoreCreateCounting(
UBaseType_t uxMaxCount,
UBaseType_t uxInitialCount
);
Trong đó:
- uxMaxCount: Số lượng tối đa của Semaphore, nó chỉ định số lần mà Semaphore có
thể được lấy trước khi nó cần được trả lại hoặc giải phóng.
- uxInitialCount: Số lượng ban đầu của Semaphore, nó chỉ định số lượng
Semaphore ban đầu có sẵn.
Return:
- Semaphore handle: Thành công.
- NULL: Nếu không thể tạo Semaphore.
Hàm `xSemaphoreCreateCounting` được sử dụng để tạo một Semaphore kiểu đếm
(counting Semaphore) trong FreeRTOS. Semaphore kiểu đếm cho phép nhiều task lấy
Semaphore cùng một lúc, với giới hạn là `uxMaxCount`. Khi Semaphore đã đạt đến
giới hạn `uxMaxCount`, các lần gọi tiếp theo của `xSemaphoreTake` sẽ chờ cho đến
khi có Semaphore được trả lại hoặc giải phóng. `uxInitialCount` xác định số lượng
Semaphore ban đầu có sẵn khi Semaphore được tạo.

III. MUTEX
- Enable: configUSE_MUTEXES in FreeRTOSConfig.h
1. xSemaphoreCreateMutex
SemaphoreHandle_t xSemaphoreCreateMutex(void);
Return:
- Semaphore handle: Thành công.
- NULL: Nếu không thể tạo Semaphore.
Hàm `xSemaphoreCreateMutex` được sử dụng để tạo một Semaphore kiểu mutex
(mutex Semaphore) trong FreeRTOS. Mutex Semaphore là một loại Semaphore đặc
biệt được sử dụng để đảm bảo đồng bộ hóa truy cập vào tài nguyên chia sẻ
giữa các task. Semaphore kiểu mutex chỉ cho phép một task có quyền truy cập
vào tài nguyên vào một thời điểm. Nếu một task đã lấy Semaphore mutex, thì
task khác cần phải chờ đến khi Semaphore được trả lại hoặc giải phóng trước
khi nó có thể lấy Semaphore được.

You might also like