IoTLabs

Nghiên cứu, Sáng tạo và Thử nghiệm

Đọc Nút Nhấn — Digital Input, Pull-up và Xử Lý Debounce

Nút Nhấn — Đầu Vào Đơn Giản Nhất

Nút nhấn (push button) là thiết bị đầu vào cơ bản nhất: nhấn = nối mạch, thả = ngắt mạch. Tuy đơn giản nhưng để đọc chính xác, có vài kỹ thuật quan trọng cần nắm.

Hiểu Pull-up và Pull-down

Khi chân Arduino không được nối với gì (trạng thái “floating”), nó có thể đọc ngẫu nhiên HIGH hay LOW do nhiễu điện. Vì vậy cần điện trở kéo (pull resistor).

Pull-down (kéo xuống GND)

5V ──[nút]──┬──[10kΩ]── GND
            │
           (pin đọc)
  • Nút thả: pin = LOW (điện trở kéo về GND)
  • Nút nhấn: pin = HIGH (nối với 5V)

Pull-up (kéo lên 5V) — Phổ biến hơn

5V ──[10kΩ]──┬──[nút]── GND
             │
            (pin đọc)
  • Nút thả: pin = HIGH (điện trở kéo về 5V)
  • Nút nhấn: pin = LOW (nối với GND)

Logic đảo ngược, nhưng dùng phổ biến hơn vì Arduino có pull-up nội tích hợp — không cần điện trở ngoài!

📷 [Hình minh hoạ: Sơ đồ kết nối Arduino Uno với nút nhấn và LED — pull-up resistor, dây VCC đỏ, GND đen, signal xanh, breadboard trắng]

Kết Nối Phần Cứng

Dùng pull-up nội của Arduino (đơn giản nhất):

Arduino D2 ──┤ nút nhấn ├── GND

Chỉ 2 dây! Không cần điện trở, không cần 5V riêng.

Kết nối trên breadboard:

  • Chân 1 của nút → D2 của Arduino
  • Chân đối diện (chân 3) → GND

Lưu ý cấu tạo nút 4 chân: Nút nhấn 4 chân thường có 2 cặp chân đã nối sẵn trong. Dùng chân ở 2 góc đối diện theo chiều ngang.

Code Đọc Nút Nhấn

Cách 1: Đơn Giản Nhất

const int BTN_PIN = 2;
const int LED_PIN = 13;

void setup() {
  pinMode(BTN_PIN, INPUT_PULLUP); // Pull-up nội
  pinMode(LED_PIN, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  int btnState = digitalRead(BTN_PIN);

  if (btnState == LOW) {    // LOW = đang nhấn (pull-up đảo ngược)
    digitalWrite(LED_PIN, HIGH);
    Serial.println("Button pressed!");
  } else {
    digitalWrite(LED_PIN, LOW);
  }
}

INPUT_PULLUP bật điện trở kéo lên nội ~20–50 kΩ. Logic đảo ngược: nhấn = LOW, thả = HIGH.

Cách 2: Toggle LED (Bật/Tắt)

Vấn đề: nếu chỉ đọc trạng thái nút, LED bật khi giữ và tắt khi thả. Muốn toggle (nhấn 1 lần bật, nhấn lần nữa tắt) thì cần detect cạnh xuống (falling edge):

const int BTN_PIN = 2;
const int LED_PIN = 13;

bool ledState = false;
int lastBtnState = HIGH; // Pull-up → mặc định HIGH

void setup() {
  pinMode(BTN_PIN, INPUT_PULLUP);
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  int btnState = digitalRead(BTN_PIN);

  // Phát hiện cạnh xuống (HIGH → LOW = vừa nhấn)
  if (lastBtnState == HIGH && btnState == LOW) {
    ledState = !ledState;              // Đảo trạng thái LED
    digitalWrite(LED_PIN, ledState);
  }

  lastBtnState = btnState;
  delay(10); // Debounce cơ bản
}

Vấn Đề Debounce

Khi nhấn nút vật lý, tiếp điểm kim loại rung vài mili giây trước khi ổn định — gọi là contact bounce. Arduino đọc hàng triệu lần/giây, nên có thể đếm một lần nhấn thành 5–10 lần!

Thực tế nút nhấn:   _____|‾‾‾‾‾‾‾‾|____
Arduino thấy:       _____|‾|_|‾|_|‾‾|__  ← loạn!

Debounce với millis()

const int BTN_PIN = 2;
const int LED_PIN = 13;
const unsigned long DEBOUNCE_DELAY = 50; // 50ms là đủ

bool ledState = false;
int lastBtnState = HIGH;
int stableBtnState = HIGH;
unsigned long lastChangeTime = 0;

void setup() {
  pinMode(BTN_PIN, INPUT_PULLUP);
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  int currentRead = digitalRead(BTN_PIN);

  // Nếu trạng thái thay đổi, ghi lại thời điểm
  if (currentRead != lastBtnState) {
    lastChangeTime = millis();
    lastBtnState = currentRead;
  }

  // Chỉ nhận trạng thái mới nếu ổn định trong DEBOUNCE_DELAY ms
  if ((millis() - lastChangeTime) > DEBOUNCE_DELAY) {
    if (currentRead != stableBtnState) {
      stableBtnState = currentRead;

      // Toggle LED khi nút ổn định ở LOW (đang nhấn)
      if (stableBtnState == LOW) {
        ledState = !ledState;
        digitalWrite(LED_PIN, ledState);
      }
    }
  }
}

Kỹ thuật này dùng millis() (thời gian đã chạy tính bằng ms) thay vì delay() — board vẫn làm việc khác trong 50ms chờ debounce.

Đếm Số Lần Nhấn

const int BTN_PIN = 2;
int pressCount = 0;
int lastBtnState = HIGH;

void setup() {
  pinMode(BTN_PIN, INPUT_PULLUP);
  Serial.begin(9600);
}

void loop() {
  int btnState = digitalRead(BTN_PIN);

  if (lastBtnState == HIGH && btnState == LOW) {
    pressCount++;
    Serial.print("Đã nhấn: ");
    Serial.print(pressCount);
    Serial.println(" lần");
    delay(50); // Debounce đơn giản
  }

  lastBtnState = btnState;
}

Mở Serial Monitor (Ctrl+Shift+M) để xem kết quả.

Tổng Kết

Kỹ thuậtKhi nào dùng
INPUT + điện trở 10kΩ ngoàiPull-down chuyên nghiệp
INPUT_PULLUPĐơn giản nhất — không cần linh kiện thêm
delay(50) debounceProject nhỏ, không cần làm gì khác
millis() debounceProject thực tế — không bị block

Bài tiếp theo: PWM — Điều chỉnh độ sáng LED. Thay vì bật/tắt, chúng ta sẽ kiểm soát được mức độ sáng của LED từ 0% đến 100%. Chuẩn bị biến trở (potentiometer) nhé!