attachInterrupt()

Giới thiệu

Ngắt (interrupt) là những lời gọi hàm tự động khi hệ thống sinh ra một sự kiện. Những sự kiện này được nhà sản xuất vi điều khiển thiết lập bằng phần cứng và được cấu hình trong phần mềm bằng những tên gọi cố định.

Vì ngắt hoạt động độc lập và tự sinh ra khi được cấu hình nên chương trình chính sẽ đơn giản hơn. Một ví dụ điển hình về ngắt là hàm millis(). Hàm này tự động chạy cùng với chương trình và trả về 1 con số tăng dần theo thời gian mặc dù chúng ta không cài đặt nó. Việc cài đặt hàm millis() sử dụng đến ngắt và được cấu hình tự động bên trong mã chương trình Arduino.

Vì sao cần phải dùng đến ngắt?

Ngắt giúp chương trình gọn nhẹ và xử lý nhanh hơn. Chẳng hạn, khi kiểm tra 1 nút nhấn có được nhấn hay không, thông thường bạn cần kiểm tra trạng thái nút nhấn bằng hàm digitalRead() trong đoạn chương trình loop(). Với việc sử dụng ngắt, bạn chỉ cần nối nút nhấn đến đúng chân có hỗ trợ ngắt, sau đó cài đặt ngắt sẽ sinh ra khi trạng thái nút chuyển từ HIGH->LOW. Thêm 1 tên hàm sẽ gọi khi ngắt sinh ra. Vậy là xong, biến trong đoạn chương trình ngắt sẽ cho ta biết trạng thái nút nhấn.

Số lượng các ngắt phụ thuộc vào từng dòng vi điều khiển. Với Arduino Uno bạn chỉ có 2 ngắt, Mega 2560 có 6 ngắt và Leonardo có 5 ngắt.

Board int.0 int.1 int.2 int.3 int.4 int.5
Uno, Ethernet 2 3        
Mega2560 2 3 21 20 19 18
Leonardo 3 2 0 1 7

Cú pháp

attachInterrupt(interrupt, ISR, mode);

Thông số

interrupt: Số thứ tự của ngắt. Trên Arduino Uno, bạn có 2 ngắt với số thứ tự là 0 và 1. Ngắt số 0 nối với chân digital số 2 và ngắt số 1 nối với chân digital số 3. Muốn dùng ngắt bạn phải gắn nút nhấn hoặc cảm biến vào đúng các chân này thì mới sinh ra sự kiện ngắt. Nếu dùng ngắt số 0 mà gắn nút nhấn ở chân digital 4 thì không chạy được rồi.

ISR: tên hàm sẽ gọi khi có sự kiện ngắt được sinh ra.

mode: kiểu kích hoạt ngắt, bao gồm

  • LOW: kích hoạt liên tục khi trạng thái chân digital có mức thấp
  • HIGH: kích hoạt liên tục khi trạng thái chân digital có mức cao.
  • RISING: kích hoạt khi trạng thái của chân digital chuyển từ mức điện áp thấp sang mức điện áp cao.
  • FALLING: kích hoạt khi trạng thái của chân digital chuyển từ mức điện áp cao sang mức điện áp thấp.

Lưu ý: với mode LOW và HIGH, chương trình ngắt sẽ được gọi liên tục khi chân digital còn giữ mức điện áp tương ứng.

Trả về

không

Ví dụ

Đoạn chương trình dưới đây sẽ làm sáng đèn led khi không nhấn nút và làm đèn led tắt đi khi người dùng nhấn nút, nếu vẫn giữ nút nhấn thì đèn led vẫn còn tắt. Sau khi thả nút nhấn, đèn led sẽ sáng trở lại.

int ledPin = 13;

void tatled()
{
    digitalWrite(ledPin, LOW); // tắt đèn led
}

void setup()
{
   pinMode(ledPin, OUTPUT);
   pinMode(2, INPUT_PULLUP); // sử dụng điện trở kéo lên cho chân số 2, ngắt 0
   attachInterrupt(0, tatled, LOW); // gọi hàm tatled liên tục khi còn nhấn nút
}

void loop()
{
  digitalWrite(ledPin, HIGH);   // bật đèn led
}

Một ví dụ khác khi sử dụng ngắt, các bạn có thể thoát khỏi các hàm delay để xử lý 1 đoạn chương trình khác

int ledPin = 13; 

void tatled()
{
    // tắt đèn led khi nhấn nút, nhả ra led nhấp nháy trở lại
    digitalWrite(ledPin, LOW);
}

void setup()
{
    pinMode(ledPin, OUTPUT);
    pinMode(2, INPUT_PULLUP); // sử dụng điện trở kéo lên cho chân số 2, ngắt 0
    attachInterrupt(0, tatled, LOW);
}

void loop()
{
    // đoạn chương trình này nhấp nháy led sau 500ms
    digitalWrite(ledPin, HIGH);
    delay(500);
    digitalWrite(ledPin, LOW);
    delay(500);
}   

Đi xa hơn

Ngắt trên AVR. (Arduino sử dụng vi điều khiển AVR)

Interrupts, thường được gọi là ngắt, là một tín hiệu khẩn cấp gởi đến bộ xử lí, yêu cầu bộ xử lí tạm ngừng tức khắc các hoạt động hiện tại để “nhảy” đến một nơi khác thực hiện một nhiệm vụ khẩn cấp nào đó, nhiệm vụ này gọi là trình phục vụ ngắt – isr (interrupt service routine ). Sau khi kết thúc nhiệm vụ trong isr, bộ đếm chương trình sẽ được trả về giá trị trước đó để bộ xử lí quay về thực hiện tiếp các nhiệm vụ còn dang dở. Như vậy, ngắt có mức độ ưu tiên xử lí cao nhất, ngắt thường được dùng để xử lí các sự kiện bất ngờ nhưng không tốn quá nhiều thời gian. Các tín hiệu dẫn đến ngắt có thể xuất phát từ các thiết bị bên trong chip (ngắt báo bộ đếm timer/counter tràn, ngắt báo quá trình gởi dữ liệu bằng RS232 kết thúc…) hay do các tác nhân bên ngoài (ngắt báo có 1 button được nhấn, ngắt báo có 1 gói dữ liệu đã được nhận…).

Ngắt là một trong 2 kỹ thuật “bắt” sự kiện cơ bản là hỏi vòng (Polling) và ngắt. Hãy tưởng tượng bạn cần thiết kế một mạch điều khiển hoàn chỉnh thực hiện rất nhiều nhiệm vụ bao gồm nhận thông tin từ người dùng qua các button hay keypad (hoặc keyboard), nhận tín hiệu từ cảm biến, xử lí thông tin, xuất tín hiệu điều khiển, hiển thị thông tin trạng thái lên các LCD…(bạn hoàn toàn có thể làm được với AVR), rõ ràng trong các nhiệm vụ này việc nhận thông tin người dùng (start, stop, setup, change,…) rất hiếm xảy ra (so với các nhiệm vụ khác) nhưng lại rất “khẩn cấp”, được ưu tiên hàng đầu. Nếu dùng Polling nghĩa là bạn cần viết 1 đoạn chương trình chuyên thăm dò trạng thái của các button (tôi tạm gọi đoạn chương trình đó là Input()) và bạn phải chèn đoạn chương trình Input() này vào rất nhiều vị trí trong chương trình chính để tránh trường hợp bỏ sót lệnh từ người dùng, điều này thật lãng phí thời gian thực thi. Giải pháp cho vấn đề này là sử dụng ngắt, bằng cách kết nối các button với đường ngắt của chip và sử dụng chương trình Input() làm trình phục vụ ngắt - isr của ngắt đó, bạn không cần phải chèn Input() trong lúc đang thực thi và vì thế không tốn thời gian cho nó, Input() chỉ được gọi khi người dùng nhấn các button. Đó là ý tưởng sử dụng ngắt.

Đóng góp của bạn Phạm Hiếu

Reference Tags: 
lên
27 thành viên đã đánh giá bài viết này hữu ích.
Các bài viết cùng tác giả

noInterrupts()

Giới thiệu

Nếu bạn chưa biết Ngắt (interrupt) là gì, vui lòng tham khảo thêm tại bài attachInterrupt().

Khi cần chạy các đoạn chương trình yêu cầu chính xác về thời gian, bạn cần tắt các ngắt để Arduino chỉ tập trung vào xử lý các tác vụ cần thiết và chỉ duy nhất các tác vụ này. Các ngắt chạy nền sẽ không được thực thi sau khi gọi hàm noInterrupts().

lên
9 thành viên đã đánh giá bài viết này hữu ích.

detachInterrupt()

Hàm detachInterrupt() sẽ tắt các ngắt đã được kích hoạt tương ứng với thông số truyển vào. Giả sử sau khi nhấn nút bấm lần đầu tiên đèn led sẽ tắt nhưng nhấn lần thứ 2 đèn sẽ không tắt nữa. Lúc này cần dùng đến detachInterrupt() để tắt ngắt chúng ta đã tạo ra.

lên
8 thành viên đã đánh giá bài viết này hữu ích.