Tìm hiểu về hệ điều hành thời gian thực µC/OS
µC/OS là một hệ điều hành thời gian thực được áp dụng cho rất nhiều dự án có yêu cầu cao về đáp ứng thời gian (hàng không, y tế...). µC/OS không phải là một hệ điều hành hoàn chỉnh mà chỉ là nhân thời gian thực dùng để cung cấp cho các ứng dụng. Và hệ điều hành µC/OS hỗ trợ các tính năng sau: ...
µC/OS là một hệ điều hành thời gian thực được áp dụng cho rất nhiều dự án có yêu cầu cao về đáp ứng thời gian (hàng không, y tế...). µC/OS không phải là một hệ điều hành hoàn chỉnh mà chỉ là nhân thời gian thực dùng để cung cấp cho các ứng dụng. Và hệ điều hành µC/OS hỗ trợ các tính năng sau:
- Quản lý truy cập tới miền găng của µC/OS
- Cấu trúc và trạng thái của các tác vụ
- Hàm xử lý ngắt
- Quản lý nhịp đồng hồ
- Khởi động hệ thống µC/OS, hệ thống đa nhiệm
I. Quản lý miền găng
Miền găng trong µC/OS chính là vũng mà mã lệnh xảy ra tranh chấp tài nguyên dùng chung khi có nhiều tiến trình cùng thực hiện miền găng.
VD: Vào cùng một thời điểm có 2 request tới server để cập nhật một đối tượng nào đó mà giá trị của request này lại ảnh hưởng tới request khác. Và những đoạn code đó được gọi là miền găng.
Với mỗi hệ điều hành hay hệ thống mà có nhiều tiến trình chạy thì phải đảm bảo trong một thời điểm chỉ có một tiến trình được thực hiện trong đoạn găng đó để đảm bảo toàn vẹn dữ liệu. Có rất nhiều giải pháp để xử ý trường hợp này như: Sermaphore, Test&Set, cấm ngắt... µC/OS sử dụng giải pháp vô hiệu hóa các ngắt khi tiến trình thực thi miền găng. OS_ENTER_CRITICAL() và OS_EXIT_CRITICAL() là 2 hàm để vô hiệu hay hữu hiệu các ngắt đó. Các hàm này có thể được định nghĩa khác nhau tùy vào vi xử lý nên được đặt trong file cấu hình (OS_CPU.H).
II. Cấu trúc và các trạng thái của các tác vụ
Một tác vụ là một hàm lặp vô hạn. Nó giống như các hàm khác trong C VD:
void Tesk(void *data) { For (; ;) { /* CODE */ Call one of µC/OS's services: OSMboxPend(); OSSemPend(); OSTaskDel(OS_PROI_SELF); ... /* CODE */ } }
µC/OS có thể quản lý tối đa 64 tác vụ: trong đó có 2 tác vụ hệ thống có độ ưu tiên OS_LOWEST_PRIO-0 tới OS_LOWEST_PRIO-3 => Người dùng chỉ quản lý được 56 tác vụ với mỗi tác vụ có 1 độ ưu tiên duy nhất từ OS_LOWEST_PRIO-0 tới OS_LOWEST_PRIO-2 và µC/OS luôn chạy tác vụ có độ ưu tiên cao nhất trước.
OSTaskCreate() và OSTaskCreateEx() là 2 hàm trong µC/OS dùng để tạo các tác vụ.
Mỗi tác vụ trong µC/OS có thể ở trong các trạng thái sau:
- DORMANT: Tác vụ nằm trong bộ nhớ (ROM hoặc RAM) nhưng chưa được khởi tạo.
- READY: Đây là trạng thái khi tác vụ được tạo ra.
- RUNNING: Là trạng thái khi tác vụ đó được thực thi.
- WAITING (WAITING FOR AN EVENT): Đang chờ sự kiện nào đó.
- INTERRUPTED: Ngắt xuất hiện khi đang thực thi tác vụ và CPU đang thực thi thủ tục ngắt.
Trạng thái DORMANT tương ứng với tác vụ đã nằm trong chương trình nhưng chưa nằm trong sự quản lý của µC/OS. Khi µC/OS gọi hàm tạo (OSTaskCreate() hoặc OSTaskCreateEx()) thì tác vụ trên mới được quản lý bởi hệ điều hành, và nó được chuyển sang trạng thái READY. Khi tác vụ đó bị xóa khỏi µC/OS bằng lệnh OSTaskDel() thì tác vụ đó bị chuyển về trạng thái DORMANT. Các tác vụ có thể được tạo ra bất cứ lúc nào, khi tạo mới mà được gán quyền cao hơn thì nó lập tức chiếm quyền điều khiển của CPU.
Quá trình đa nhiệm được bắt đầu khi gọi OSStart(). Sau đó OSStart() sẽ chọn tác vụ có trạng thái READY và có độ ưu tiên cao nhất để chuyển sang trạng thái RUNNING.
Một tác vụ đang chạy có thể tự trì hoãn chính nó bằng cách gọi OSTimeDly(). Trong khi đó thì các tác vụ khác có quyền ưu tiên cao hơn vẫn có thể chiếm quyền CPU. OSTimeTick() sẽ khôi phục lại trạng thái RUNNING cho tác vụ khi thời gian trì hoãn kết thúc.
Tác vụ đang chạy cũng có thể đợi sự xuất hiện của các sự kiện khác bằng việc gọi OSMboxPend(), OSSemPend() hoặc OSQPend(), khi gọi các hàm này thì tác vụ sẽ chuyển sang trạng thái WAITTING. Khi tác vụ này đang ở trạng WAITTING thì tác vụ có độ ưu tiên cao nhất tiếp theo được quyền sử dụng CPU. Khi sự kiện xuất hiện thì tác vụ lại được chuyển sang trạng thái READY và được báo bới một tác vụ khác hoặc bằng ISR. Tác vụ đang chạy luôn có thể bị ngắt trừ khi µC/OS vô hiệu hóa các ngắt (thường là khi trong miền găng) và tác vụ được chuyển sang trạng thái INTERRUPTED. Khi ngắt xuất hiện, tác vụ bị tạm dừng, ISR chiếm quyền điều khiển CPU. Khi tất cả các tác vụ đều đợi sự kiện thì µC/OS sẽ thực hiện tác vụ OSTaskIdle().
III. Lập lịch tác vụ
µC/OS sự dụng bộ lập lịch tác vụ được thực hiện bởi hàm OSSched(), nó luôn chọn tác vụ có độ ưu tiên cao nhất để thực thi. Lập lịch mức ISR sẽ được quản lý bởi hàm OSIntExit() (lập lịch sau khi thoát khỏi ngắt).
Thời gian để lập lịch các tác vụ luôn là hằng số và không phụ thuộc vào số tác vụ. Nếu ứng dụng gọi OSSchedLock() hoặc xuất hiện ngắt (OSIntNessting>0), tác vụ lập lịch sẽ kết thúc.
IV. Ngắt trong µC/OS
µC/OS yêu cầu dịch vụ ngắt ISR viết bằng hợp ngữ.
ISR: Save all CPU registers; Call OSIntEnter() or increment OSIntNesting directly; Excute user code to service ISR; Call OSIntExit(); Restore all CPU registers; Exucute a return from interrup instructions;
Khi tăng trực tiếp OSIntNesting thì sẽ ngắt nhanh hơn do việc gọi hàm OSIntEnter() sẽ phải thực hiện vô hiệu hóa các ngắt khác.
V. Nhịp đồng hồ
µC/OS yêu cầu cung cấp một đồng hồ thời gian để thực thi các chức năng liên quan đến đo thời gian mà các dịch vụ cần đến. Trong µC/OS, người phát triển cho phép ngắt thời gian sau khi khởi động hệ thống đa nhiệm (OSTart()) nhưng không cho phép ngắt thời gian xảy ra giữa OSInit() và OSStart() vì khi đó µC/OS sẽ trong trạng thái không được định nghĩa và sẽ xảy ra lỗi.
Nhịp đồng hồ trong µC/OS được sử dụng qua hàm OSTimeTick() từ một ngắt đồng hồ. Mã ngắt cho đồng hồ phải được viết bằng hợp ngữ hoặc gọi hàm OSTimeTick() từ một tác vụ có độ ưu tiên cao hơn tác vụ hiện có. Tác vụ này được gọi là TickTask.
VI. Khởi động hệ thống µC/OS
Như đã viết ở trên thì khởi tạo hệ thống µC/OS bằng các gọi hàm OSInit() và hàm này phải gọi trước khi gọi bất cứ dịch vụ nào. OSInit() sẽ khởi tạo mọi biến và cấu trúc dữ liệu của µC/OS và khởi tạo tác vụ OSTaskIdle() với độ ưu tiên ko đổi là OS_LOWEST_PRIO. Khởi động hệ thống đa nhiệm bằng hàm OSStart() và trước khi khởi tạo hàm này ta phải tạo ít nhất 1 tác vụ cho chương trình ứng dung.