1. Giới Thiệu Chung
Ngắt (Interrupt) là một thành phần cốt lõi trong hầu hết hệ thống nhúng, cho phép xử lý các sự kiện thời gian thực một cách kịp thời. Khi kết hợp với hệ điều hành thời gian thực như FreeRTOS, cách thức xử lý ngắt trở nên linh hoạt hơn nhưng cũng phức tạp hơn.
Hiểu được cơ chế xử lý ngắt trong FreeRTOS sẽ giúp lập trình viên đảm bảo độ trễ thấp, an toàn tài nguyên và chuyển đổi tác vụ hiệu quả.
2. Ngắt Trong FreeRTOS Hoạt Động Như Thế Nào?
2.1. Ngắt Là Gì?
Ngắt (Interrupt) là một cơ chế cho phép một sự kiện ngoại lệ (từ ngoại vi hoặc lỗi) tạm thời làm gián đoạn chương trình chính để xử lý sự kiện đó. Khi một ngắt xảy ra:
- Bộ vi điều khiển lưu trạng thái hiện tại.
- Nhảy đến Interrupt Service Routine (ISR).
- Sau khi xử lý xong, chương trình tiếp tục tại điểm đã bị gián đoạn.
2.2. ISR Trong FreeRTOS
Trong FreeRTOS, ISR có thể tương tác với hệ điều hành bằng các API đặc biệt dành cho ISR, như:
xQueueSendFromISR()
xSemaphoreGiveFromISR()
xTaskNotifyFromISR()
Những hàm này cho phép ISR kích hoạt các tác vụ (task) từ ngắt, nhưng KHÔNG trực tiếp chuyển ngữ cảnh ngay lập tức. Thay vào đó, chúng gợi ý rằng một tác vụ ưu tiên cao hơn nên được chạy, và FreeRTOS sẽ thực hiện việc này sau khi ISR kết thúc.
3. Scheduler Hoạt Động Ra Sao Khi Có Ngắt?
FreeRTOS sử dụng một preemptive scheduler (lập lịch ưu tiên tiền nhiệm), nghĩa là tác vụ có độ ưu tiên cao hơn có thể giành quyền CPU ngay khi có điều kiện.
3.1. Trường Hợp Có Ngắt
Khi một ngắt xảy ra và ISR gọi các hàm FromISR()
, FreeRTOS sẽ kiểm tra xem liệu tác vụ nào đó đang chờ dữ liệu/tín hiệu đã sẵn sàng hay chưa.
Nếu một tác vụ có độ ưu tiên cao hơn task hiện tại đang chạy được "giải phóng" trong ISR, thì FreeRTOS yêu cầu chuyển ngữ cảnh ngay sau khi ISR kết thúc.
3.2. Sơ Đồ Luồng Xử Lý Ngắt Trong FreeRTOS
4. Giải Thích Kỹ Hơn Qua Ví Dụ
Giả sử có hai tác vụ:
- Task A (ưu tiên thấp): Đọc sensor mỗi 100ms.
- Task B (ưu tiên cao): Xử lý dữ liệu khi có ngắt UART.
ISR của UART sẽ nhận byte từ UART, sau đó gọi xSemaphoreGiveFromISR()
để kích hoạt Task B.
Trình tự diễn ra:
- Task A đang chạy (độ ưu tiên thấp hơn).
- Ngắt UART xảy ra.
- ISR nhận dữ liệu và gọi
xSemaphoreGiveFromISR()
, truyềnxHigherPriorityTaskWoken
. - Biến
xHigherPriorityTaskWoken
được set làpdTRUE
, nghĩa là có một task cao hơn (Task B) đang chờ semaphore này. - ISR gọi
portYIELD_FROM_ISR(xHigherPriorityTaskWoken)
. - Sau khi ISR kết thúc, scheduler thực hiện chuyển ngữ cảnh: Task B được chạy ngay lập tức.
5. Các Hàm API ISR Cần Biết
API Tên | Chức Năng |
---|---|
xQueueSendFromISR() |
Gửi dữ liệu vào hàng đợi |
xSemaphoreGiveFromISR() |
Nhả semaphore từ ISR |
xTaskNotifyFromISR() |
Gửi thông báo đến task |
portYIELD_FROM_ISR(x) |
Kích hoạt chuyển ngữ cảnh sau ISR nếu cần |
6. Lưu Ý Khi Viết ISR Trong FreeRTOS
- Không gọi API FreeRTOS bình thường từ ISR – phải dùng bản
FromISR
. - ISR nên rất ngắn gọn, tránh xử lý phức tạp.
- ISR không được sử dụng malloc(), không truy cập biến toàn cục mà không đồng bộ.
- Tránh tạo ra race condition – hãy sử dụng
volatile
,critical section
, hoặcsemaphore
.
7. So Sánh Chuyển Ngữ Cảnh Với Và Không Có Ngắt
Trạng thái hệ thống | Khi không có ngắt | Khi có ngắt |
---|---|---|
Quản lý tác vụ | Theo timer hoặc blocking | ISR có thể đánh thức task ngay |
Độ trễ kích hoạt | Có thể cao | Rất thấp (thời gian thực) |
Kiểm soát CPU | Thuần task | Tương tác giữa ISR và task |
8. Cơ Chế Nested Interrupt (Ngắt lồng nhau)
FreeRTOS cho phép cấu hình để xử lý ngắt lồng nhau thông qua cấu hình:
configMAX_SYSCALL_INTERRUPT_PRIORITY
Ngắt có độ ưu tiên thấp hơn hoặc bằng cấu hình trên có thể gọi FromISR()
, còn ngắt có ưu tiên cao hơn không được phép gọi các API FreeRTOS.
9. Tổng Kết
Xử lý ngắt trong FreeRTOS giúp tối ưu thời gian phản hồi và kích hoạt tác vụ nhanh chóng, nhưng cũng đòi hỏi quy ước nghiêm ngặt và hiểu rõ cơ chế hoạt động. Sự phối hợp giữa ISR + Scheduler + FromISR API chính là yếu tố then chốt giúp hệ thống hoạt động đúng logic thời gian thực.