tài liệu này nói về chế độ hoạt động time slice và preemption trong hệ điều hành Free RTOS của stm32f103c8t6. Tài liệu này còn nhiều thiếu sót, rất mong mọi người thông cảm và đóng góp ý kiển. Việc upload tài liệu này không vì bất kỳ mục đích thương mại nào, mà chỉ để mọi người học hỏi trao đổi lẫn nhau. Mình rất cảm ơn Thầy Cường đã chỉ dạy cho mình thời gian qua, nếu có gì sai thì đó là do mình hiểu chưa tới chứ không phải lỗi do thầy giảng sai. "luật nhân quả là công bằng tuyệt đối".
powerpoint mẫu họp phụ huynh cuối kì 2 học sinh lớp 7 bgs
Rtos 2: time slice vs preeption (cùng mức ưu tiên vs có chiếm quyền
1. 2.1 RTOS
RAM gồm vùng nhớ hệ thống và phần còn lại là vùng nhớ heap dành cho các task,
mỗi task tương ứng với (stack, control block)
heap_4 giúp chống phân mảnh, tận dụng vùng nhớ khi 1 task hoàn thành (giải phóng
bộ nhớ).
mỗi task tương ứng với 1 hàm thực thi, tham số task,
bộ hàm của API của stm32 khác bộ hàm free RTOS, bộ hàm API CMSIS-v2 )có thể
dùng cho nhiều nhiều hệ điều hành thời thực khác. link api
2.2 tổ chức chương trình theo chế độ chạy liên tục
link video bài giảng của thầy về hàm printf, time slice, preemption
2.2.1 lý thuyết về chế độ chạy liên tục :
yêu cầu: các task có mức ưu tiên bằng nhau, hàm thực thi là vòng lặp vô hạn.
các task chỉ có 2 trang thái running-chạy và ready-'sẵn sàng để chạy' (task ở trong
hàng đợi-theo cơ chế FIFO,
ban đầu các task cùng ready, do cùng mức ưu tiên nên task nào được khởi tạo trc sẽ
thoát chế độ ready và running trước (tại 1 thời điểm chỉ có tối đa 1 task running).
task sẽ running trong thời gian là 1 time slice (1 ms), khi hết time slice thì task bị
ngắt, nó sẽ chuyển từ running → ready (xếp vào cuối hàng đợi).
tại mốc thời gian time slice sẽ có ngắt hệ thống của kernel (bộ lập lịch) lưu trạng thái
của task trước và nạp trạng thái của task tiếp theo, task ready ở đầu hàng đợi sẽ
được running.
cứ như vậy thay các task thay phiên nhau running trong time slice của mình rồi lại
ready.
2. 2.2.2 hàm, thư viện sử dụng
// thư viện
#include "cmsis_os.h"
//vd tham số, thuộc tính của task
/* Definitions for NORMAL_TASK */
osThreadId_t NORMAL_TASKHandle;
const osThreadAttr_t NORMAL_TASK_attributes = {
.name = "NORMAL_TASK",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
//khởi tạo bộ lập lịch kernel
osKernelInitialize();
//vd khởi tạo các task (tên hàm, thuộc tính...)
/* creation of defaultTask */
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL,
&defaultTask_attributes);
// bộ lập lịch bắt dầu chạy
osKernelStart();
3. 2.2.3 cấu hình CUBMX
phần RCC, clock, GPIO làm như bình thường.
nên chọn time base khác system tick link giải thích
chọn FREERTOS, middware: CMSISV2 (api v1 hay v2, PREEMPTION (chiếm quyền),
thời gian mỗi time_slice (TICK_RATE, minimal stack size (kích thước stack nhỏ nhất),
queue registry size (kích thước hàng đợi), kích cỡ bộ nhớ heap, heap_4, ....
//vd hàm thực thi của task
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN 5 */
uint16_t u1;
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(GPIOA, LED1_Pin);
for(u1=0; u1<10000; u1++){ }
//osDelay(1);
}
/* USER CODE END 5 */
}
4. tạo task (tên task, mức ưu tiên, kích thước, tên hàm...)
5. 2.2.4 VD chương trình:
các task cùng mức ưu tiên, ko dùng osDelay trong vòng lặp vô hạn, thực hiện nháy
led.
khi đó task sẽ lần lượt vào các trạng thái: ready3→ready2→ready1→running-
>ready3...
các vấn để: thời gian chuyển task tạm coi là nhỏ. thời gian thực hiện task lớn hơn
time slice nên task bị ngắt khi tới time slice nhưng thời gian ready rồi trở lại running
trong khoảng thời gian đủ nhỏ . sử dụng 3 led khác nhau nên ko xung đột tài nguyên.
link ví dụ
2.3 tổ chức chương trình theo sự kiện (task có mức
ưu tiên).
link video bài giảng của thầy về hàm printf, time slice, preemption
6. 2.3.1 lý thuyết
ban đầu khi các task ở trạng thái ready (trong hàng đợi), do các task có mức ưu tiên
khác nhau nên task nào có mức ưu tiên cao nhất thì sẽ running trước. khi đến time
slice thì task đó trở lại trang thái ready. Nhưng khi so sánh mức ưu tiên của các task
đang ready thì nó vẫn là task có mức ưu tiên cao nhất nên tiếp tục running. Cứ như
vậy task có mức ưu tiên cao nhất chiếm hết thời gian hệ thống.
muốn tránh task có mức ưu tiên cao nhất chiếm hết thời gian hệ thống, thì ta có thể
dùng hàm osDelay. khi đó task có mức ưu tiên cao sẽ rơi vào trang thái block, task
có mức ưu tiên thấp hơn khác có cơ hội được thực hiện. sau 1 khoảng thời gian
osDelay thì task bị block sẽ chuyền về ready và có thể running.
7. một cách delay khác đó là delay theo mốc thời gian:
1 task có thể cầu hình lại mức ưu tiên của task khác
__IO uint32_t tick2 = osKernelGetTichCount();
tick2+=500;
//do something
osDelayUntil(tich2);
8. 1 task tạo ra 1 task khác, rồi task thậm chí xoá chính nó,
9. một task có thể suspended task khác.
tổ chức chương trình vừa có các task theo time slice chạy liên tục vừa tổ chức
chương trình theo sự kiện có task ở trang thái block.
2.3.2 cấu hình cubemx
phần RCC, clock, uart (normal)
nên chọn time base khác system tick link giải thích
chọn freeRTOS, API CMSIS V2, và các thông số khác:
10. thêm task, thay đổi tên task, hàm, dung lượng, mức ưu tiên...
14. 3 task có mức ưu tiên khác nhau, cùng sử dụng chung uart, có dùng osDelayUntil
link vd chương trình
chu trình mỗi task: ready ↔ running→block→ready...
các vấn để: thời gian chuyển task tạm coi là nhỏ ko xét đến. thời gian thực hiện task
lớn hơn time slice nên bị ngắt task nhưng thời gian ready rồi trở lại running đủ nhỏ
nên vẫn ok. task sử dụng chung tài nguyên uart nhưng quản lý thời gian hợp lý nên
ko có xung đột tài nguyên.
2.4 Các vd để hiểu rõ hơn về time slice và preemption.
link video bài giảng của thầy
2.4.1 chương trình 1 (phút 3200
osdelay, osdelayUntil: thì sẽ có trạng thái block.
task cùng ưu tiên, thời gian thực thi trc khi block (osDelay) dài hơn 1 time slice⟶có
chuyển trạng thái giữa các time slice⟶ mỗi task chỉ thực thi 1 phần thì phải chuyển sang
cái khác (xung đột tài nguyên).
ví này mình không làm được, muốn in ra linh tinh nhưng nó ko in ra linh tinh (^ ^).
2.4.2 chương trình 2 (phút 45 00 đổ lại)
muốn cho công việc thực hiện đầy đủ
cần set lại mức ưu tiên cho các task, task sẽ chạy liên tục nhiều time slice, đến cuối vòng
for vô hạn gặp osdelay thị bị block, hết thời gian osdelay thì task lại ready trở lại, trong
thời gian block thì task khác thực hiện.
link project , chương trình main đc copy vào file chương trình 2.txt
nếu dùng osDelay:
high task 08; 10101018; 20202028; 30303038
normal task:1018; 10201028; 20302038; 30403048
low task 2048; 10501078; 20802108; 31103138
thời gian thực hiện low task nhiều hơn high task, ta thấy khoảng thời gian giữa kết thức
low task và bắt đầu high task ngày càng thu hẹp→ đến lúc nào đo sẽ có sự chồng lấn.
2.4.4 chương trình 4 (phút 5800 đổ lại)
muốn mốc thời gian ready→running fix cứng ko chồng nhau⟶ sử dụng delay until
high task 08; 10001008; 20002008; 30003008
15. normal task:1018; 10101018; 20102018; 30103018
low task 2048; 10201048; 20202048; 30203048
link project , chương trình main đc copy vào file chương trình 4.txt
2.4.5 chương trình 5
osthreadresume cho task vào ready
osthreadsuspend cho task vào suspend (treo-không hoạt động, dù task đó running hay
ready)
link project , chương trình main đc copy vào file chương trình 5.txt
chú ý chương trình của task chia làm 2 phần giống ardruino (setup thực hiện 1 lần và vòng
loop vô hạn). low task khi đc gọi lại thì hàm osUntilDelay so sánh giá trị trọng biến
tick=3000 với thời gian của HT hiện tại thì đã qua nên task ko bị block mà thực hiện tiếp
vòng for vô hạn, tick=4000..., tick=5000...→ thực hiện3 lần.
size của task nhỏ → tràn stack lẫn vào task→ chạy 1 lần→ không khôi phục đc.