Trong phần này, luận văn sẽ trình bày những vấn đề chính trong cấu trúc nhân
thời gian thực µC/OS. Một hệ điều hành thời gian thực rất nhỏ (khoảng hơn 5000 dòng
mã lệnh) [17], nhưng hiện đã được áp dụng và triển khai thành công vào rất nhiều dự án có yêu cầu khắt khe về đáp ứng thời gian (như các hệ thống trong hàng không, y tế …). Nhằm nắm bắt được những vấn đề cơ bản trong việc thiết kế xây dựng hệ thống nhúng theo kiến trúc thời gian thực và thực hiện phần thực nghiệm trong quá trình nghiên cứu vì nhân của nó cũng khá nhỏ và miễn phí cho mục đích giáo dục. Ngoài ra trên thị trường còn có rất nhiều hệ điều hành thời gian thực nổi tiếng khác như: QNX, VxWorks, Embedded Linux, Window CE…Các phần trình bày chính là:
Cách 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 tác vụ
Cách viết hàm xử lý ngắt
Cách quản lý nhịp đồng hồ
Khởi động hệ thống µC/OS
Khởi động hệ thống đa nhiệm
a. Các miền găng
Cũng giống như các nhân thời gian thực khác, µC/OS cũng vô hiệu hoá các
ngắt khi chương trình thực thi miền găng. Do ảnh hưởng tới vấn đề đáp ứng các ngắt
nên thời gian vô hiệu hoá các ngắt là một vấn đề quan trọng. µC/OS duy trì thời gian
vô hiệu hoá ngắt đến mức tối thiểu, nhưng thời gian này còn tuỳ thuộc nhiều vào kiến trúc vi xử lý và chất lượng mã được sinh bởi chương trình dịch. Mọi vi xử lý đều cung cấp các chỉ thị để vô hiệu/hữu hiệu hoá các ngắt và chương trình dịch C phải có cơ chế thực thi các chỉ thị này bằng ngôn ngữ C hoặc Assembly (đối với một số trình biên
dịch). Trong µC/OS, hai macro được định nghĩa để vô hiệu/hữu hiệu các ngắt đó là
OS_ENTER_CRITICAL() và OS_EXIT_CRITICAL(). Các macro có thể khác nhau tuỳ theo từng vi xử lý. Chính vì thế, chúng được viết trong file cấu hình dành riêng cho vi xử lý trong OS_CPU.H (khi thực hiện porting).
b. Các tác vụ
Một tác vụ là một hàm lặp vô hạn. Nó giống như những hàm khác trong C chứa
đối số và một kiểu trả về nhưng không bao giờ trả về giá trị (hình 3.6). µC/OS có thể
quản lý tới 64 tác vụ (phiên bản µC/OS-II), trong đó có 2 tác vụ hệ thống, 4 tác vụ từ
OS_LOWEST_PRIO tới OS_LOWEST_PRIO-3. Người dùng có thể quản lý 56 tác vụ. Mỗi tác vụ được gán một mức ưu tiên duy nhất từ 0 tới OS_LOWEST_PRIO-2. Vì
nhân theo kiểu lập lịch ưu tiên nên µC/OS luôn chọn tác vụ có độ ưu tiên cao nhất để
Hình 3.6 Cấu trúc một tác vụ
Để µC/OS quản lý các tác vụ của ứng dụng, bước đầu tiên là phải tạo các tác
vụ. Trong µC/OS hàm OSTaskCreate() và OSTaskCreateEx() (phiên bản II) thực hiện
điều này.
c. Các trạng thái của tác vụ
Các tác vụ có thể nhận 1 trong 5 trạng thái: DORMANT, READY, RUNNING, WAITING FOR AN EVENT hoặc INTERRUPTED.
DORMANT: Tác vụ nằm trong bộ nhớ (ROM hoặc RAM) nhưng không sẵn
sàng cho µC/OS do chưa được tạo.
READY: Khi một tác vụ được tạo nó được đặt trạng thái là READY.
RUNNING: Tác vụ đang có sự phục vụ của CPU.
WAITING FOR AN EVENT: Đang chờ sự kiện.
INTERRUPTED: Ngắt xuất hiện khi đang thực hiện 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 quản lý của µC/OS. Khi µC/OS gọi hàm OSTaskCreate() hoặc
OSTaskCreateEx() thì tác vụ trên mới thực sự được quản lý bởi hệ điều hành và chuyển sang trạng thái READY. Tác vụ có thể được tạo bất cứ lúc nào, khi tạo tác vụ mới được gán quyền ưu tiên cao hơn thì nó sẽ chiểm ngay quyền điều khiển của CPU.
Tác vụ có thể trở về trạng thái DORMANT khi nó bị xoá khỏi µC/OS bằng
OSTaskDel().
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 trì hoãn, tác vụ khác nếu có quyền ưu tiên cao hơn vẫn có quyền chiếm CPU. Khi thời gian trì hoãn kết thúc, OSTimeTick() sẽ khôi phục trạng thái RUNNING cho tác vụ.
Tác vụ đang chạy cũng có thể đợi sự xuất hiện của sự kiện bằng việc gọi OSSemPend(), OSMBoxPend() hoặc OSQPend(), tác vụ chuyển sang trạng thái WAITING. Khi tác vụ đợi một sự kiện, 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, tác vụ lại chuyển sang trạng thái READY. Sự xuất hiện của sự kiện có thể đượ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 hoá các ngắt. Khi đó, tác vụ trong
trạng thái ISR. 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, µC/OS sẽ thực hiện tác vụ OSTaskIdle().
d. Khối điều khiển tác vụ
Khi tác vụ được tạo, nó được gán một khối điều khiển tác vụ (TCB). µC/OS sử
dụng TCB để duy trì trạng thái của tác vụ khi nó được thực thi theo kiểu lập lịch ưu tiên. Khi tác vụ được khôi phục quyền điều khiển CPU (sau khi kết thúc ngắt), trạng thái thực thi của tác vụ được khôi phục nhờ TCB. Tất cả TCB được lưu trú trong RAM.
e. 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. Đối với 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ố cho dù số tác vụ được tạo là bao nhiêu. Nếu ứng dụng gọi OSSchedLock() hoặc xuất hiện ngắt (điều kiện OSIntNesting>0), tác vụ lập lịch sẽ kết thúc. Khi bộ lập lịch tìm thấy tác vụ có độ ưu tiên cao nhất, OSSched() sẽ kiểm tra xem tác vụ có độ ưu tiên cao nhất có phải là tác vụ hiện thời hay không, điều này để tránh lãng phí một bước chuyển ngữ cảnh.
f. Các ngắt trong µC/OS
µC/OS yêu cầu dịch vụ ngắt ISR viết bằng hợp ngữ. Đỗi với những trình biên
dịch C hỗ trợ nhúng mã hợp ngữ inline thì có thể viết dịch vụ ngắt bằng hợp ngữ ngay trong mã C.
Giả mã cho ISR như sau:
Hình 3.8 Hàm dịch vụ ngắt trong µC/OS
Trong đoạn mã trên, việc tăng trực tiếp OSIntNesting nhanh hơn nhiều so với việc gọi hàm OSIntEnter() vì trong hàm OSIntEnter() hệ thống còn phải thực hiện việc vô hiệu hoá các ngắt khác.
g. 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. Một tích tắc cần xuất hiện 10 đến 100 lần trong một giây.
Trong µC/OS, người phát triển phải cho phép các ngắt thời gian xảy ra sau khi
khởi động hệ thống đa nhiệm (Sau khi gọi hàm OSStart()). Không được cho phép ngắt
thời gian xảy ra giữa hai lời gọi: OSInit() và OSStart() vì trong trường hợp đó µC/OS
sẽ trong trạng thái không được định nghĩa và hệ thống dễ gây ra lỗi.
Nhịp đồng hồ trong µC/OS được phục vụ bởi việc gọi hàm OSTimeTick() từ
một ngắt đồng hồ. Mã cho ngắt đồng hồ phải được viết bằng hợp ngữ vì nó phải truy cập trực tiếp đến các thanh ghi. Trong trường hợp không viết được ISR cho ngắt đồng hồ, người phát triển có thể gọi trực tiếp hàm OSTimeTick() từ mức tác vụ. Để làm điều này, trước tiên tạo một tác vụ có độ ưu tiên cao hơn các tác vụ hiện có trong ứng dụng, ngắt thời gian cần báo hiệu cho tác vụ này thông qua semaphore hoặc mailbox. Tác vụ này được gọi là Tick Task.
h. Khởi động hệ thống µC/OS
Khởi tạo hệ thống µC/OS bằng cách gọi hàm OSInit() 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. Nó cũng khởi tạo
tác vụ OSTaskIdle() với độ ưu tiên không đổi là OS_LOWEST_PRIO. Khởi động hệ
thống đa nhiệm µC/OS bằng cách gọi hàm OSStart(). Trước khi bắt đầu gọi hàm này
i. Porting µC/OS
Porting µC/OS có nghĩa là cấu hình µC/OS để gắn nó với một vi xử lý xác định.
Hầu hết µC/OS được viết bằng C chuẩn trong đó có sử dụng các kiểu dữ liệu có khả
năng chuyển giữa các môi trường khác nhau, các bộ dịch khác nhau. µC/OS được thiết
kế có tính khả chuyển cao nên việc porting trên các vi xử lý khác nhau là khá dễ dàng.
Một vi xử lý có thể chạy µC/OS nếu nó thoả mãn các yêu cầu sau:
Có trình biên dịch C cho loại vi xử lý này.
Cho phép vô hiệu/hữu hiệu hoá các ngắt từ C.
Vi xử lý phải hỗ trợ ngắt và cần cung cấp một ngắt xuất hiện trong khoảng thời
gian đều đặn (10 đến 100Hz).
Vi xử lý phải hỗ trợ một ngăn xếp phần cứng và cho phép lưu các thông tin trên
ngăn xếp đó (có thể tới nhiều Kbyte).
Vi xử lý phải có các chỉ thị để tải, lưu con trỏ ngăn xếp và các thanh ghi CPU
khác trên ngăn xếp hoặc trong bộ nhớ.
CHƢƠNG 4: CÁC CÁCH TIẾP CẬN VỚI BÀI TOÁN NHẬN DẠNG CHỮ NÔM