Trong bài này chúng ta sẽ cùng học lập trình ESP32 ngắt ngoài hay EXTI (External Interrupts). Hiểu cách hoạt động của một ngắt trong chương trình nhúng, cách lập trình để tránh sự xung đột khi dùng ngắt.
Bài 5 trong Serie Học lập trình ESP32 từ A tới Z
Ngắt ngoài là gì?
Đầu tiên chúng ta phải hiểu về khái niệm ngắt và tuần tự trước đã.
- Tuần tự: Một trương trình C,Cpp sẽ được xử lý tuần tự theo chiều từ trên xuống dưới và sẽ bị ảnh hưởng bởi các câu lệnh rẽ nhánh và loop
- Ngắt: Chương trình sẽ bị thoát ra khỏi quá trình tuần tự, xử lý xong các lệnh trong ngắt, sau đó mới quay lại xử lý tiếp
Nói một cách đơn giản, Ngắt là một công việc được ưu tiên làm trước, khi xử lý xong mới được làm các việc khác. Các sự kiện ngắt có thể đến từ nhiều nguồn khác nhau như: ADC, IO, UART. TIMER…
Ngắt ngoài EXTI chính là một sự kiện ngắt được sinh ra với nguồn từ các chân IO của ESP32.
Ngắt ngoài trong ESP32
Khác với các chip Arduino truyền thống, trên ESP32 mọi chân Input đều có thể được cài đặt để sử dụng với ngắt ngoài. Nó không bị giới hạn như Arduino Uno hoặc các loại tương đương là chỉ có 2 chân có thể được sử dụng làm ngắt ngoài
Ngắt ngoài có các kiểu ngắt khác nhau tương ứng cho tín hiệu xung trên chân GPIO đó, cụ thể:
LOW | Interrupt được kích hoạt khi chân ở mức LOW |
HIGH | Interrupt được kích hoạt khi chân ở mức HIGH |
CHANGE | Interrupt được kích hoạt khi chân thay đổi mức, từ HIGH sang LOW, hoặc LOW sang HIGH |
FALLING | Interrupt được kích hoạt khi chân thay đổi mức, từ HIGH sang LOW |
RISING | Interrupt được kích hoạt khi chân thay đổi mức, từ LOW sang HIGH |
Để lập trình ngắt ngoài trên ESP32 chúng ta sử dụng 2 hàm chính đó là:
attachInterrupt(GPIOPin, ISR, Mode);
Đây là hàm chức năng được sử dụng để cài đặt ngắt ngoài cho 1 chân cụ thể.
Hàm này có ba đối số đầu vào:
GPIOPin – Là chân GPIO được chỉ định interrupt.
ISR – Là tên của hàm sẽ được gọi khi có interrupt
Mode – Khai báo chế độ interrupt được sử dụng, các chê độ tương ứng với các kiểu ngắt bên trên đã nêu.
detachInterrupt(GPIOPin);
Đây là hàm chức năng để xóa các cấu hình ngắt trên chân đó đi. Đối số truyền vào là chân được chỉ định ngắt.
void IRAM_ATTR ISR() { Statements; } //example void IRAM_ATTR NgatNgoai(){ //do some things }
Đây là hàm được gọi khi có một sự kiên ngắt sảy ra.
ISR
: Interrupts Service Routine hay định tuyến dịch vụ, là tên hàm sẽ được gọi khi ngắt sảy ra
IRAM_ATTR
khai báo cho trình biên dịch biết rằng hàm sẽ được biên dịch và đặt trong RAM (IRAM) của ESP32. Ngược lại hàm sẽ được đặt trong FLASH.
Một hàm khi được trên FLASH sẽ phải chờ Load xong mới có thể thực hiện gây tốn thời gian, do đó hàm ISR không nên được đặt trên FLASH.
Lập trình ESP32 ngắt ngoài điều khiển led bằng nút nhấn
Với ví dụ này chúng ta sẽ điều khiển led bằng nút nhấn, nhưng không cần đọc giá trị nút nhấn như bài 1.
Chuẩn bị:
- Breadboard
- Kit ESP32 dev
- Trở 200R
- Led cắm
- Nút nhấn
Sơ đồ nguyên lý
Chúng ta sử dụng sơ đồ nguyên lý như bài 1.
Code và giải thích code
Full code
#include <Arduino.h> const int led = 17; const int button = 16; bool ledstates = 0; portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; void IRAM_ATTR buttonPush() { portENTER_CRITICAL_ISR(&mux);//Phần quan trọng cần được bảo vệ khỏi mọi truy cập đồng thời để thay đổi nó Serial.println("Button Pushed!!!"); digitalWrite(led, ~ledstates); portEXIT_CRITICAL_ISR(&mux);//Cho phép tiếp tục chạy các task khác } void setup() { pinMode(button, INPUT_PULLUP); // cài đặt ngắt vào chân button, kiểu ngắt là falling (xung xuống), hàm gọi khi có sự kiện ngắt là button push attachInterrupt(digitalPinToInterrupt(button), buttonPush, FALLING); // chọn led là đầu ra pinMode(led, OUTPUT); //ghi giá trị ban đầu digitalWrite(led, ledstates); } void loop() { // do nothing }
Giải thích code:
Chúng ta khởi tạo các chân led và button, một biến để lưu trữ giá trị của Led
const int led = 17; const int button = 16; bool ledstates = 0;
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
Khai báo một biến mux với kiểu portMUX_TYPE biến này có tác dụng chống sự xung đột giữa các CPU và chương trình chính khi hoạt động.
Tiếp tới khơi tạo 1 hàm được gọi khi ngắt xảy ra:
void IRAM_ATTR buttonPush() { portENTER_CRITICAL_ISR(&mux);//Phần quan trọng cần được bảo vệ khỏi mọi truy cập đồng thời để thay đổi nó Serial.println("Button Pushed!!!"); digitalWrite(led, ~ledstates); portEXIT_CRITICAL_ISR(&mux);//Cho phép tiếp tục chạy các task khác }
Trong hàm này, chúng ta sẽ đảo trạng thái led, và in ra màn hình dòng chữ nút đã được bấm
Trong setup chúng ta khởi tạo Ngắt, chân Led và ghi giá trị ban đầu cho Led
Serial.begin(9600); pinMode(button, INPUT_PULLUP); // cài đặt ngắt vào chân button, kiểu ngắt là falling (xung xuống), hàm gọi khi có sự kiện ngắt là button push attachInterrupt(digitalPinToInterrupt(button), buttonPush, FALLING); // chọn led là đầu ra pinMode(led, OUTPUT); //ghi giá trị ban đầu digitalWrite(led, ledstates);
Trong loop không cần làm gì cả.
Nạp code và kết quả
Nhấn nút upload trên thanh công cụ nạp, sau đó nhấn nút để thay đổi trạng thái led nhé
Kết
Lập trình ESP32 ngắt ngoài rất quan trọng trong quá trình code một dự án nhúng, ngắt rất tiện lợi cho việc bắt các sự kiện tức thời, không cần CPU phải gọi lệnh kiểm tra, thế nên rất được ưa chuộng
Nếu thấy hay hãy chia sẻ bài viết này tới bạn bè nhé. Đừng quên tham gia nhóm Nghiện lập trình để kết nối với những người cùng đam mê.
Vì sao khi nhấn button thì điện áp ở trên chân GPIO lại chuyển tử HIGH sang LOW?
(Mình thấy trong code đang setup interrupt kiểu là FAILING)
Vì lúc set nút nhấn là INPUT_PULLUP, nghĩa là lúc rảnh rỗi sẽ được kéo lên, có điện áp là 3.3V – HIGH
bạn có tài liệu hướng dẫn ngắt uart trên esp32 ko ạ