[C20] Interrupt

Ngô Văn Tuân

Gà con
Staff member
Nếu CPU yêu cầu module thực hiện một công việc nào đó. Để biết rằng công việc đó đã được thực hiện xong chưa, CPU có thể liên tục đọc một bit flag (cờ) trên một SFR nào đó (module sẽ set bit này lên 1 nếu công việc hoàn thành).

Tuy nhiên, cách này có một số vấn đề sau:
  • Nếu CPU còn việc khác để làm thì câu hỏi đặt ra sẽ là khi nào ta nên đọc bit flag đó.
    • Nếu đọc quá nhiều thì sẽ ảnh hưởng đến năng suất công việc hiện tại (đang làm việc này mà cứ phải để ý việc khác).
    • Nếu đọc quá chậm thì lại chậm đáp ứng nhu cầu của module, ảnh hưởng đến việc lấy kết quả của công việc. Một ví dụ cụ thể là nếu ta dùng module UART để giao tiếp với các module bên ngoài VĐK. Khi module UART nhận được dữ liệu, nó sẽ ghi dữ liệu đó vào một thành ghi buffer (cũng là một SFR) và bật cờ nhận. Nếu CPU không đọc cờ nhận kịp thời và lấy dữ liệu đó ra xử lý thì khi dữ liệu tiếp theo đến. Module UART sẽ ghi đè vào thanh ghi buffer khiến dữ liệu cũ bị mất. Điều này ảnh hưởng đến quá trình giao tiếp.
  • Một trường hợp ít xảy ra hơn là, nếu CPU không còn việc gì để làm. Chỉ ngồi đọc cái cờ và chờ cho đến khi nó bật để làm việc gì đó thì việc chờ này gây lãng phí năng lượng. CPU nên đi vào chế độ tiết kiệm năng lượng và chỉ chạy lại khi có kết quả từ module.
--> Cách giải quyết là dùng interrupt (ngắt) hay nói đúng hơn là hardware inturrupt (ngắt phần cứng).

1. Định nghĩa ngắt
Ngắt là cơ chế giúp CPU phát hiện event từ module để thực hiện chương trình đáp ứng ISR (Interrupt Service Routine) của event đó.

Interrupt-and-ISR-in-LPC2148-ARM7-Microcontroller.jpg
Từ hình trên ta có thể thấy rằng, trong khi chương trình của chúng ta đang chạy, nếu có interrupt xảy ra thì CPU sẽ chuyển qua thực hiện chương trình ISR, sau khi thực hiện ISR xong, CPU sẽ quay lại thực hiện tiếp chương trình chính từ chỗ mà CPU đã bỏ đi.

2. Phân loại ngắt
Tùy theo tiêu chí, ta có thể phân loại ngắt như sau:
  • Dựa theo khả năng điều khiển
    • Reset: được tạo ra bằng cách kích reset bằng phần mềm hoặc bằng phần cứng (nhấn nút Reset), đây là interrupt có độ ưu tiền cao nhất. CPU chắc chắn sẽ reset khi có ngắt này xảy ra.​
    • Non-maskable interrupt (NMI): là loại hardware interrupt không có bit để disable nó đi, CPU bắt buộc phải thực hiện ngắt này và gần như phải thực hiện với độ ưu tiên cao nhất. Ngắt này có thể đến từ SRAM parity error, flash double ECC error hoặc là clock failure. Các lỗi này xảy ra không phải do lỗi của chương trình viết sai mà có thể do điều kiện môi trường khiến cho VĐK chạy sai và VĐK có thể phát hiện ra điều đó.​
    • Hard Fault: Ta cũng không thể có disable ngắt này được. Ngắt này xảy ra do phần mềm làm những điều không được phép làm. Ví dụ như ghi hoặc đọc một ô nhớ không tồn tại trong phần cứng.​
    • Maskable interrupt: là loại hardware interrupt có bit điều khiển enable-disable. Nếu enable, CPU bắt buộc phải thực hiện ISR của interrupt này nhưng với độ ưu tiên có thể cấu hình được.​
  • Dựa theo nguồn gốc của ngắt
    • Internal Interrupt: Ngắt trong được tạo ra bởi các module bên trong VĐK​
    • External Interrupt: Được tạo ra từ bên ngoài, đưa vào VĐK thông qua các chân của VĐK​
  • Dựa theo cách gọi ngắt
    • Hardware interrupt: Là loại ngắt được tạo ra bởi phần cứng. Muốn bật tắt loại ngắt này nhất định phải thay đổi giá trị của biến điều khiển ngắt.​
    • Sofware interrupt: Là loại ngắt được tạo ra bởi phần mềm. Bắt buộc phải có nguồn gốc từ hardware interrupt. Ví dụ: ta có sự kiện ngắt phần mềm: nếu nhận được chữ a từ module UART thì sẽ gửi lại chữ a qua module này, các chữ khác bỏ qua. Sự kiện này bắt nguồn từ sự kiện phần cứng: nhận được ký tự, nhưng việc có gọi đến service để truyền lại không lại do CPU (phần mềm) quyết định.​
Lưu ý: Trong bài viết này, khi nhắc đến ngắt, ta mặc định hiểu là maskable và hardware interrupt.

3. Nguyên lý của ngắt
Nói một cách đơn giản, các ngắt đều được định nghĩa trước (không phải ta muốn ngắt nào là có ngắt đó). Khi xảy ra cho một sự kiện ngắt, CPU sẽ nhảy đến một vị trí xác định trong vùng nhớ để chạy chương trình ở đó.
Điều ta cần làm là cấu hình có cho phép ngắt nếu sự kiện xảy ra hay không và ghi chương tình ngắt của chúng ta vào vị trí xác định trong bộ nhớ chương trình.

Vector-Table.png
Hình trên là Interrupt Vector Table của STM8. Khi xảy ra loại ngắt nào, nếu ngắt đó được enable thì CPU sẽ nhảy đến vị trí tương ứng trong bảng để thực hiện chương trình ở đó.

Ví dụ: Ở module UART1, nếu ta cho phép ngắt TX complete thì khi module UART1 gửi xong dữ liệu trong TX buffer, module UART sẽ bật cờ TX complete trong một ISR nào đó lên 1 đồng thời đưa tín hiệu đến CPU, CPU sẽ nhảy đến địa chỉ 0x00804C để thực hiện chương trình ở đó.

Trước khi chạy hàm main, chương trình của chúng ta tự động sẽ ghi vào Interrupt Vector Table địa chỉ của các hàm phục vụ ngắt (ISR - Interrupt Service Routine). Nhờ đó, các chương trình phục vụ ngắt có thể được đặt ở các vị trí phân tán trong bộ nhớ chương trình. Đây cũng là lý do tại sao người ta gọi vùng nhớ này là Interrupt Vector Table: bảng chứa các con trỏ hàm trỏ tới các vị trí của các chương trình ngắt.

4. Độ ưu tiên ngắt (Interrupt priority)
Khi sử dụng ngắt nảy sinh ra các vấn đề sau:
  • Có 2 hay nhiều ngắt cùng xảy ra một lúc
  • Một ngắt đang được thực hiện thì có ngắt khác xảy ra.
Để giải quyết vấn đề trên, người ta sinh ra khái niệm độ ưu tiên ngắt (Interrupt priority).
Trong đa số VĐK STM32 sẽ có 16 mức ưu tiên 0->163 mức ưu tiên đặc biệt, giá trị ưu tiên càng nhỏ thì độ ưu tiên càng lớn.
Ba mức ưu tiên đặc biệt đó là:
InterruptPriority
Reset
-3​
Non-Maskable Interrupt (NMI)
-2​
Hard Fault
-1​

Priority.PNG
Trong khi chương trình chính Main đang chạy thì RQ_B (Request B) xảy ra. CPU sẽ lưu lại context (context store), context ở đây có thể là địa chỉ của câu lệnh tiếp theo trong hàm Main mà CPU phải chạy khi thực hiện xong ISR, là giá trị các thanh ghi riêng của CPU. Sau đó, CPU chạy IRQ_B, trong khi chạy IRQ_B thì có RQ_A xảy ra. Vì priority của RQ_A cao hơn priority của RQ_B (thấp hơn về giá trị) nên CPU sẽ lưu lại context của IRQ_B và chuyển qua chay IRQ_A. Sau khi chạy IRQ_A xong thì CPU sẽ khôi phục lại context (Context restore) của IRQ_B và chạy tiếp IRQ_B. Sau khi chạy IRQ_B xong thì CPU sẽ khôi phục lại context của Main và tiếp tục chạy Main.

Lưu ý:
Khi nói priority cao thì mặc định hiểu là số biểu thị priority đó nhỏ.​
Khi nói priority thấp thì mặc định hiểu là số biểu thị priority đó lớn.​

5. Ngắt trong STM32F103C8T6
Trong các VĐK 8-bit, 16-bit, việc quản lý ngắt cho từng module được thực hiện thông qua các SFR của module đó.
Tuy nhiên, khi số lượng ngắt tăng lên (trong VĐK 32-bit) việc quản lý ngắt được thực hiện bởi một module riêng là NVIC (Nested Vector Interrupt Controller).
NVIC-STM32.PNG

Trong hình trên, ta đã cấu tình 5 tín hiệu ngắt với các độ ưu tiên khác nhau. Trong đó có 2 ngắt trong (từ UART1, Timer1) và 3 ngắt ngoài.
Configured InterruptPriotity
USART1 global interrupt
0​
TIM1 update interrupt
1​
EXTI line0 interrupt
2​
EXTI line1 interrupt
3​
EXTI line2 interrupt
4​
 
Last edited:

phamngocson

Thành Viên PIF
Đối với preemption priority càng nhỏ thì càng ưu tiên ngắt hơn. Còn sub priority có như vậy ko anh?
Nếu trong trường hợp 2 ngắt có cùng Preemption thì ngắt nào có sub priority thấp hơn sẽ thực hiện trước
 
Last edited:

Ngô Văn Tuân

Gà con
Staff member
Đối với preemption priority càng nhỏ thì càng ưu tiên ngắt hơn. Còn sub priority có như vậy ko anh?
1. Nói một cách đơn giản: The lower the number the higher the priority.
Do đó sub priority càng nhỏ thì độ ưu tiên càng cao.
Khi nói priority cao thì mặc định hiểu là số biểu thị priority đó nhỏ.
Khi nói priority thấp thì mặc định hiểu là số biểu thị priority đó lớn.

2. Mở rộng thêm một chút về sub priority:
Priority nhắc đến trong bài viết có tên gọi chính xác là Preemption Priority.
Nếu một interrupt có preemption priority cao hơn thì ISR của interrupt đó có quyền preempt (chiếm quyền CPU) của ISR của interrupt có preemtion priority thấp hơn khi nó đang chạy. Hình sau mô tả quá trình preempt:


Giả sử hai interrupt có cùng preemption priority, nếu một interrupt có subpriority cao hơn thì ISR của interrupt đó không có quyền preempt (chiếm quyền CPU) của ISR của interrupt có sub priority thấp hơn khi nó đang chạy.

Nếu hai interrupt có cùng preemption priority, sub priority dùng để quyết định ISR nào sẽ chạy trước nếu hai interrupt cùng đến một lúc.

Việc hai interrupt cùng đến một lúc gần như là không thể.

Ta sẽ dùng đến sub priority trong trường hợp một ISR có preemtion priority cao hơn đang chạy thì có hai interrupt có cùng preemtion priority thấp hơn xảy ra. Khi đó, ISR của hai interrupt này sẽ phải đợi, sau khi ISR có preemtion priority cao hơn chạy xong thì CPU sẽ xét đến sub priority để chọn ISR chạy trước.
 
Last edited:

Ngô Văn Tuân

Gà con
Staff member
Cao hơn hay thấp hơn thì thực hiện trc anh?
Ý @phamngocson nói là sub priority có giá trị số thấp hơn.
Nhưng mà phải mặc định như sau mới đúng:
Khi nói priority cao thì mặc định hiểu là số biểu thị priority đó nhỏ.
Khi nói priority thấp thì mặc định hiểu là số biểu thị priority đó lớn.
 
Top