Bạn đang muốn tạo một dự án Arduino có thể nhận diện các thao tác khác nhau trên nút nhấn, như nhấn một lần, nhấn đúp hoặc giữ đồng thời hai nút? Bài viết này sẽ cung cấp cho bạn một hướng dẫn chi tiết kèm theo code mẫu đã được tối ưu, giúp bạn dễ dàng tích hợp vào dự án của mình. Chúng ta sẽ đi sâu vào cách debounce nút nhấn hiệu quả, xử lý các sự kiện một cách chính xác và đưa ra những ví dụ thực tế để bạn có thể áp dụng ngay.
Trong nhiều dự án, việc chỉ nhận biết một thao tác nhấn nút đơn thuần là chưa đủ. Ví dụ, bạn có thể muốn:
Bằng cách phân biệt các thao tác này, bạn có thể tạo ra các giao diện điều khiển trực quan và hiệu quả hơn cho người dùng.
Một trong những thách thức lớn nhất khi làm việc với nút nhấn là hiện tượng "debounce". Do cấu tạo cơ học, khi bạn nhấn một nút, nó không chỉ đóng mạch một lần duy nhất mà sẽ nảy lên xuống (bật/tắt liên tục) trong một khoảng thời gian rất ngắn (vài mili giây). Nếu không xử lý, Arduino có thể hiểu nhầm một lần nhấn thành nhiều lần nhấn.
Trước đây, thư viện `SwitchPack.h` là một lựa chọn phổ biến để debounce nút nhấn và phát hiện nhấn đơn/đúp. Tuy nhiên, thư viện này hiện không còn được phát triển và người tạo ra nó đã qua đời. Dù vậy, chúng ta vẫn có thể học hỏi một số ý tưởng từ cách nó hoạt động.
Một đoạn code ví dụ (sử dụng thư viện này) có thể trông như sau:
#define KEY1_PIN A2
#define KEY2_PIN A3
#define LEDA_PIN 8
#define LEDB_PIN 13
#define LEDC_PIN 10
#define BLINK_TIME 300
#define REACTION_TIME 500
#include "SwitchPack.h"
DoubleClick key1(KEY1_PIN, PULLUP, REACTION_TIME);
DoubleClick key2(KEY2_PIN, PULLUP, REACTION_TIME);
void setup() {
key1.begin();
key1.setSensitivity(16);
key2.begin();
key2.setSensitivity(16);
pinMode(LEDA_PIN, OUTPUT);
pinMode(LEDB_PIN, OUTPUT);
pinMode(LEDC_PIN, OUTPUT);
}
void loop() {
int event = key1.clickCount();
if (key1.closed() && key2.closed()) {
event = 3;
}
switch (event) {
case 1:
clickEvent1();
break;
case 2:
doubleClickEvent1();
break;
case 3:
doubleHoldEvent();
break;
}
}
void clickEvent1() {
digitalWrite(LEDA_PIN, HIGH);
delay(250);
digitalWrite(LEDA_PIN, LOW);
}
void doubleClickEvent1() {
digitalWrite(LEDB_PIN, HIGH);
delay(250);
digitalWrite(LEDB_PIN, LOW);
}
void doubleHoldEvent() {
digitalWrite(LEDC_PIN, HIGH);
delay(250);
digitalWrite(LEDC_PIN, LOW);
}
Tuy nhiên, việc sử dụng thư viện đã ngừng phát triển có thể gây ra một số vấn đề về bảo trì và tương thích trong tương lai.
Một cách tiếp cận tốt hơn là tự viết code để xử lý debounce và phát hiện các sự kiện nhấn nút. Điều này cho phép bạn kiểm soát hoàn toàn logic và dễ dàng tùy chỉnh theo nhu cầu của dự án.
Dưới đây là một ví dụ về cấu trúc dữ liệu và hàm cơ bản để theo dõi trạng thái nút nhấn:
struct button {
int pin;
int value;
int previous;
unsigned long onTime;
unsigned long debounceStart;
char pressType;
boolean doublePress;
};
Giải thích:
Dưới đây là một ví dụ code hoàn chỉnh, minh họa cách sử dụng cấu trúc `button` và các hàm liên quan để phát hiện nhấn đơn, nhấn giữ và nhấn đồng thời hai nút. Lưu ý: Code này cần được điều chỉnh cho phù hợp với phần cứng và yêu cầu cụ thể của dự án của bạn.
#define DOUBLE_PRESS 3000
#define LONG_PRESS 1000
#define DEBOUNCE 100
struct button {
int pin;
int value;
int previous;
unsigned long onTime;
unsigned long debounceStart;
char pressType;
boolean doublePress;
};
button button1 = {A1, HIGH, HIGH, 0, 0, ' ', false};
button button2 = {A2, HIGH, HIGH, 0, 0, ' ', false};
unsigned long currMillis;
void setup() {
Serial.begin(115200);
pinMode(button1.pin, INPUT_PULLUP);
pinMode(button2.pin, INPUT_PULLUP);
Serial.println("Started");
}
void loop() {
currMillis = millis();
checkButton(button1);
checkButton(button2);
if (button1.value == LOW && currMillis - button1.onTime > DOUBLE_PRESS &&
button2.value == LOW && currMillis - button2.onTime > DOUBLE_PRESS &&
!button1.doublePress && !button2.doublePress) {
Serial.println("Both buttons held");
button1.doublePress = true;
button2.doublePress = true;
}
if (button1.pressType != ' ') {
Serial.print("Button 1 ");
Serial.print(button1.pressType);
Serial.print(" ");
Serial.println(currMillis - button1.onTime);
button1.pressType = ' ';
}
if (button2.pressType != ' ') {
Serial.print("Button 2 ");
Serial.print(button2.pressType);
Serial.print(" ");
Serial.println(currMillis - button2.onTime);
button2.pressType = ' ';
}
}
void checkButton(button &passedButton) {
passedButton.value = digitalRead(passedButton.pin);
if (currMillis - passedButton.debounceStart > DEBOUNCE) {
if (passedButton.value == LOW) {
if (passedButton.previous == HIGH) {
passedButton.onTime = currMillis;
passedButton.debounceStart = currMillis;
}
} else {
if (passedButton.previous == LOW) {
if (passedButton.doublePress)
passedButton.doublePress = false;
else if (currMillis - passedButton.onTime > LONG_PRESS)
passedButton.pressType = 'L';
else
passedButton.pressType = 'S';
passedButton.debounceStart = currMillis;
}
}
passedButton.previous = passedButton.value;
}
}
Trong đoạn code này:
Việc phát hiện và xử lý các sự kiện nhấn nút khác nhau trên Arduino mở ra nhiều khả năng sáng tạo cho các dự án của bạn. Bằng cách hiểu rõ về hiện tượng debounce và áp dụng các kỹ thuật lập trình phù hợp, bạn có thể tạo ra các giao diện điều khiển trực quan và đáp ứng tốt hơn. Hy vọng bài viết này đã cung cấp cho bạn một nền tảng vững chắc để bắt đầu.
Bài viết liên quan