Chuyển đến nội dung chính

Chapter 3: Interrupt management (quản lý ngắt).

Bài trước: Chapter 2: Queue Management (Quản lý hàng đợi).
Nối tiếp chương 2 về quản lý hàng đợi, hôm nay chúng ta sẽ tìm hiểu về quản lý ngắt. Ngắt là 1 thành phần cực kỳ quan trọng trong lập trình nhúng, nếu bạn đã học qua về vi điều khiển thì chắc không phải nói nhiều về cái này làm gì. Còn nếu chưa thì hiểu nôm na Ngắt là 1 sự kiện mà nó luôn được ưu tiên cao nhất, tức là khi sự kiện ngắt xảy ra thì tất cả mọi sự kiên khác đều phải dừng lại để ngắt thực hiện công việc của mình.
Nhưng có 1 sự khác nhau ở ngắt trong FreeRTOS và ngắt thông thường ở chỗ:
Khi ngắt thông thường xảy ra nó sẽ nhảy vào hàm ngắt và các công việc cần phải làm đều đặt trong hàm ngắt. Còn ở FreeRTOS thì khi ngắt xảy ra nó sẽ nhảy vào hàm thủ tục ngắt, hàm thủ tục ngắt lại phát đi 1 "tín hiệu" để hàm thực hiện chức năng của ngắt Unblocking và hàm này có Priority cao hơn tất cả các task khác nên nó sẽ thực hiện ngay lập tức.


Tín hiệu ở đây chính là Semaphore, có 2 loại Semaphore là Binary Semaphore và Counting Semaphore.

1. A binary Semaphore (Example012).
Đây giống như kiểu là 1 Queue nhưng mà chỉ có 1 phần tử nên trạng thái của nó chỉ có là Full hoặc Empty.
Có 2 tác động chính vào Semaphore là "Take" và "Give", "Take" là dùng cho hàm Handler tức là hàm thực hiện chức năng của ngắt, khi mà hàm Handler gọi thủ tục này thì nó sẽ rơi vào trạng thái Block và chờ đợi sự kiện ngắt xảy ra. Tác động "Give" được thực hiện trong hàm thủ tục ngắt, nó sẽ phát ra tín hiệu là có ngắt xảy ra (Semaphore Full) và khi đó hàm đang đợi tín hiệu (cái mà đã gọi "Take" trước đó) sẽ ngay lập tức được Unblocking và thực hiện (Semaphore Empty). Sau khi thực hiện xong nó lại trở lại trạng thái block và chờ đợi cho sự kiện ngắt tiếp theo xảy ra.

1.1 Các hàm cần thiết :
      a, khởi tạo Semaphore:
          void vSemaphoreCreateBinary (xSemaphoreHandle xSemaphore);
          xSemaphore: là tên (handler) của semaphore.

      b, hàm nhận Semaphore "take" để unblocking:
          portBASE_TYPE xSemaphoreTake ( xSemaphoreHandle xSemaphore, portTickType xTicksToWait );
          Hàm này được đặt trong Handler, và giá trị trả về của hàm là pdPASS hoặc pdFALSE.
          xTicksToWait: là thời gian đợi cho phép, thường là để thời gian đợi mãi mãi nên giá trị này sẽ là portMAX_DELAY. Và nhớ trong file FreeRTOSConfig.h thì INCLUDE_vTaskSuspend Set lên 1.
          Lưu ý: không dùng hàm này trong hàm ngắt.

      c, hàm phát Semaphore "give":
          portBASE_TYPE xSemaphoreGiveFromISR ( xSemaphoreHandle xSemaphore, portBASE_TYPE *pxHigherPriorityTaskWoken);
          Hàm này được đặt trong thủ tục ngắt, giá trị trả về là pdPASS hoặc pdFAIL.
          pxHigherPriorityTaskWoken là 1 cái giá trị đảm bảo là nó Semaphore sẽ Unblocking cái Task có Priority cao hơn cái đang chạy. Khi ở trong 1 hàm (ví dụ main chẳng hạn) phải định nghĩa biến này trước với kiểu là portBASE_TYPE.
         Nên set giá trị này là pdFALSE trước, sau đó gọi hàm "give", kiểm tra nó đã về pdTRUE chưa, nếu rồi thì thực hiện chuyển trạng thái?? Hic. cái này khó hiểu quá, có lẽ phải làm mới hiểu hết.

2. Counting Semaphores (Example013):
Có 1 vấn đề xảy ra với Binary Semaphore là nếu mà tần suất ngắt xảy ra cao thì khi ngắt đầu tiên đang thực hiện, các ngắt tiếp theo xảy đến thì Semaphore cũng chỉ lưu lại được 1 tín hiệu do đó số lượng hàm phục vụ ngắt sẽ không đúng như mong muốn. Do đó Counting Semaphore ra đời để giải quyết vấn đề này, Semaphore này có thể chứa nhiều tín hiệu ngắt 1 lúc.

2.2 Các hàm.
      a, hàm khởi tạo:
         xSemaphoreHandle xSemaphoreCreateCounting ( unsigned portBASE_TYPE uxMaxCount,
                                                                                      unsigned portBASE_TYPE uxInitialCount );
         Giá trị trả về của hàm là NULL (nếu khởi tạo không thành công, non-NULL nếu thành công )
         uxMaxCount: số Semaphore tối đa có thể lưu.
         uxInitialCount: số đếm khởi tạo.
         ví dụ:
         xCountingSemaphore = xSemaphoreCreateCounting( 10, 0);
     b, các hàm khác thì tương tự như Binary Semaphore.

3. Sử dụng Queue trong thủ tục ngắt (Example014).
Nhớ lại 1 tý: QueueSent là gửi và hàng đợi, QueueReceive là lấy dữ liệu từ hàng đợi.
Vậy sự khác nhau khi sử dụng Counting Semaphore với Queue là gì? đó là  Semaphore thì dùng để kết nối với sự kiện, còn hàng đợi thì vừa kết nối sự kiện vừa truyền dữ liệu.
Khi sử dụng hàng đợi ở trong ngắt thì phải sử dụng các hàm .....FromISR(). Cụ thể:
portBASE_TYPE xQueueSendToFrontFromISR ( xQueueHandle xQueue, void *pvItemToQueue,
                                                                              portBASE_TYPE *pxHigherPriorityTaskWoken );
portBASE_TYPE xQueueSendToBackFromISR ( xQueueHandle xQueue, void *pvItemToQueue,
                                                                              portBASE_TYPE *pxHigherPriorityTaskWoken );

Giá trị trả về của các hàm này là pdPASS or pdQUEUE_FULL;
Cái pvHigherPriorityTaskWoken dùng giống như trong Semaphore.
Người ta thường sử dụng cái này trong hàm UART để nhận và gửi tín hiệu ??
LƯU Ý: Trong hàm thực hiện mà các công việc ngắt thì hàm nhận dữ liệu nên để thời gian chờ tối đa (không có time out), khi đó thì nếu mà không co dữ liệu thì hàm này sẽ bị Block.

4. Interrupt Nesting (tổ ngắt :)) chả  biết dịch thế nào cả) :
có 2 thông số cần chú ý ở file FreeRTOSConfig.h
configKERNEL_INTERRUPT_PRIORITY:   Giá trị Priority mà các hàm ngắt mang. (gt1)
configMAX_SYSCALL_INTERRUPT_PRIORITY:    Giá trị ưu tiên cao nhất mà hàm ngắt có thể có (gt2).
giá trị của thông số này phải cao hơn giá trị của thông số phía trên.
2 thông số này có ý nghĩa là những ngắt mà có ưu tiên bé hơn hoặc bằng (gt2) thì có thể bị cản trờ bởi 1 công việc đang khẩn cấp nào đó khác của Kernel, còn những ngắt có ưu tiên cao hơn sẽ được thực hiện 1 cách ngay lập tức và không bị vản trở bởi cái gì cả.

LƯU Ý: Những ngắt mà không sử dụng các hàm API của Kernal thì có thể đặt bất cứ giá trị ưu tiên bất kỳ.
Với ARM Cortex M3:
Thì ngắt được định nghĩa ưu tiên dựa vào 1 con số ảo, con số này càng LỚN thì mức ưu tiên càng BÉ, có nghĩa là mức ưu tiên lớn nhất khi con số này là 0, và bé nhất là 255. (Hi. dịch thế thôi chứ đã là ARM bao giờ đâu mà biết :D )

Bài tiếp: Chapter 4: Resource Management ( Quản lý tài nguyên).

Nhận xét

  1. bạn cho hỏi hàm thủ tục ngắt và hàm ngắt khác nhau như thế nào?

    Trả lờiXóa
    Trả lời
    1. Bạn có câu trả lời chưa ạ? Nếu có cho mình xin với ạ

      Xóa
  2. Cảm ơn bạn, bài viết rất hữu ích cho người mới tìm hiểu về freeRTOS. mục Interrupt Nesting mình nghĩ nên dịch là ngắt kép(ngắt trong ngắt)

    Trả lờiXóa

Đăng nhận xét

Bài đăng phổ biến từ blog này

Bắt đầu với FreeRTOS (Bài 1)

NOTE: 1 vài tài liệu mình sử dụng trong bài này là mình sưu tầm được, có thể không có bản quyền, nếu tác giả không muốn đăng lên thì mình sẽ xóa. Trước khi tìm hiểu mọi người có thể biên dịch thử 1 chương trình để lấy khí thế tại  đây. FreeRTOS là 1 hệ điều hành nhúng phù hợp cho các ứng dụng có yêu cầu về thời gian thực 1 cách chặt chẽ. Và vì nó miễn phí, nhỏ gọn và có thể nhúng vào các dòng vi điều khiển thông thường như PIC, AVR, MSP430, ARM...nên mình làm hướng dẫn này cho những ai muốn bắt đầu tìm hiểu về FreeRTOS. Trang chủ của nó  http://www.freertos.org/ Đầu tiên là phải tải FreeRTOS về đã: vào  http://sourceforge.net/projects/freertos/files/   Click vào  Download FreeRTOSV7.5.2.exe (9.3 MB) (thời điểm này bản mới nhất là 7.5.2) để tải về. Sau khi tải về bạn click đúp vào file .exe ấy để giải nén. Ta được 1 folder FreeRTOSV7.5.2. Xong Bước 1  Tiếp theo là nên đọc 1 tài liệu để hiểu sơ qua xem RTOS nó là cái gì và nó hoạt động như thế nào: Các bạn lấy tài l

Chap 1: Task management (Quản lý Task).

Bài trước: Bắt đầu với FreeRTOS (Bài 2) 1 tháng rồi bận bịu quá nà. Bài này sẽ tổng hợp lại những thứ cần chú ý ở trong Chap 1 cuốn "Using the freertos real time kernel" Câu 1: Task là gì? và được tạo ra thế nào? Task được hiểu nôm na như là 1 chương trình thực hiện 1 nhiệm vụ nào đó. 1 Task gồm có: tên, chức năng, giá trị ưu tiên, độ sâu stack, task handle (như kiểu cái móc để tác động ấy), biến tác động vào task. Để tạo task thì đầu tiên phải định nghĩa chức năng của nó trước, cấu trúc hàm cơ bản như sau: void ATaskFunction(void *pvParameters) { // Khởi tạo biến ở đây for( ; ; ) { // Chức năng thực hiện ở đây } } Ví dụ: void vTask1(void *pvParameters) {                const char *pcTaskName = "This is Task 1 \r\n";                for ( ; ; ) { vPrintString(pcTaskName); } } Sau đó phải dùng hàm xTaskCreate()