Nút Nhấn (Push Button) & Tín Hiệu Digital Input
- Nút nhấn tact switch tiêu chuẩn có 4 chân được chia thành 2 nhóm (cặp chân đối diện nhau được nối thông ngầm bên trong).
- Khi nút ở trạng thái nhả (không nhấn): Hai nhóm chân bị cách ly hoàn toàn, mạch hở.
- Khi nút được nhấn xuống: Cơ cấu kim loại đàn hồi bên trong nối chập 2 nhóm chân lại với nhau, cho phép dòng điện chạy qua.
Nút nhấn là thiết bị INPUT dạng số (Digital Input). Trạng thái chỉ có 2 giá trị nhị phân: LOW (0V / mức 0) và HIGH (5V / mức 1).
Dùng hàm digitalRead(pin) để đọc điện áp tại chân Digital được chỉ định, trả về giá trị HIGH hoặc LOW.
Giải Thích Cơ Chế Điện & Mạch Điện Trở Kéo Xuống (Pull-Down)
Trong mạch điện tử số, nếu chân vi điều khiển không được nối cố định với nguồn hoặc mass, chân đó sẽ rơi vào trạng thái "Floating" (trôi nổi/thả nổi). Lúc này chân như một ăng-ten thu nhận sóng điện từ xung quanh, làm hàm digitalRead() nhảy loạn xạ giữa HIGH và LOW. Điện trở kéo xuống (Pull-down resistor ~10kΩ) giải quyết triệt để vấn đề này:
Nút nhấn đang hở, dòng điện từ nguồn 5V không thể đi qua. Chân đọc tín hiệu (ví dụ chân D12) được nối thông qua điện trở 10kΩ về cực âm GND. Do đó, điện áp trên chân D12 bị kéo thẳng xuống 0V → Hàm digitalRead(12) luôn đọc được mức LOW ổn định và chính xác.
Nút nhấn đóng tiếp điểm, dòng điện từ 5V đi qua nút nhấn:
- Nhánh 1: Đi thẳng vào chân vi điều khiển D12, nâng điện thế chân này lên mức 5V → Hàm
digitalRead(12)ghi nhận mứcHIGH(Button is pressed). - Nhánh 2: Dòng điện chạy qua điện trở 10kΩ xuống GND. Do điện trở có giá trị lớn (10kΩ = 10,000Ω), cường độ dòng chỉ là
I = 5V / 10,000Ω = 0.5mA(rất nhỏ, an toàn tuyệt đối, không gây ngắn mạch nguồn 5V và GND).
Ngay khi nút được thả ra, nguồn 5V bị ngắt. Toàn bộ lượng điện tích còn tích tụ trên đường dây chân D12 sẽ phóng qua điện trở 10kΩ để trở về GND. Hiện tượng này giúp triệt tiêu hoàn toàn điện tích dư, đưa chân D12 trở về trạng thái 0V ngay lập tức mà không bị trễ tín hiệu.
Mã Nguồn Căn Bản & Bài Tập Nút Nhấn Điều Khiển LED
1. Đoạn mã cơ bản: Bắt sự kiện nhấn nút & In Serial Monitor
int button = 12;
void setup()
{
pinMode(button, INPUT); // Cấu hình chân D12 là cổng vào (INPUT)
Serial.begin(9600); // Khởi động cổng Serial với baud rate 9600
}
void loop()
{
int buttonState = digitalRead(button); // Đọc trạng thái logic chân D12
if (buttonState == HIGH)
{
Serial.println("Button is pressed"); // Nút được nhấn -> in ra màn hình
}
}
pinMode(button, INPUT): Khai báo chân D12 là chân nhận dữ liệu từ cảm biến / nút nhấn ngoại vi.digitalRead(button): Lấy mẫu điện áp. Nếu nối mạch Pull-down, khi bấm nút sẽ có 5V đi vào D12 → trả vềHIGH.
2. Bài tập thực hành: Đèn luôn sáng, nhấn nút thì tắt đèn
Bài tập 1
Hình 1: Đấu nối điện trở kéo xuống 10kΩ cho nút nhấn ở chân D12 và điện trở 220Ω bảo vệ LED ở chân D8.
| Linh kiện | Chân linh kiện | Kết nối Arduino UNO / Breadboard | Mục đích |
|---|---|---|---|
| Nút nhấn (Button) | Chân 1 (Cặp trên) | Nối thanh nguồn 5V | Cấp nguồn khi bấm nút |
| Nút nhấn (Button) | Chân 2 (Cặp dưới) | Nối vào chân Digital 12 & 1 đầu trở 10kΩ | Đường tín hiệu đọc |
| Điện trở kéo 10kΩ | Đầu còn lại | Nối thanh mass GND | Kéo điện thế về 0V khi thả nút |
| Đèn LED đơn | Anode (Chân dài) | Nối qua điện trở 220Ω vào Digital 8 | Tín hiệu điều khiển sáng/tắt |
| Đèn LED đơn | Cathode (Chân ngắn) | Nối thanh mass GND | Hoàn thành mạch khép kín |
int button = 12; // Chân nút nhấn
int led = 8; // Chân đèn LED
void setup()
{
pinMode(button, INPUT); // Cấu hình nút nhấn là cổng vào
pinMode(led, OUTPUT); // Cấu hình chân đèn LED là cổng xuất
Serial.begin(9600);
}
void loop()
{
int buttonState = digitalRead(button);
// Logic nghịch: Bình thường chưa nhấn (LOW) thì BẬT đèn
if (buttonState == LOW)
{
digitalWrite(led, HIGH);
Serial.println("Button is not pressed -> LED ON");
delay(50); // Chống rung phím (Debounce) nhẹ
}
else
{
// Khi nhấn nút (HIGH) thì TẮT đèn
digitalWrite(led, LOW);
}
}
- Trạng thái nghỉ (Không bấm nút):
buttonState == LOW, chương trình thực thi khốiif→ GọidigitalWrite(led, HIGH)→ Đèn LED sáng liên tục. - Trạng thái nhấn nút:
buttonState == HIGH, điều kiện kiểm tra sai → Nhảy sang khốielse→ GọidigitalWrite(led, LOW)→ Đèn LED tắt ngay tức khắc. - Lệnh
delay(50)hỗ trợ lọc nhiễu cơ học (chống rung nảy phím - debounce) trong quá trình tiếp điểm kim loại đóng ngắt.
Điều Chế Độ Rộng Xung - Pulse Width Modulation (PWM)
Vi điều khiển ATmega328P trên bo mạch Arduino UNO không tích hợp bộ chuyển đổi Số sang Tương tự thực sự (DAC - Digital to Analog Converter). Để điều khiển độ sáng của bóng đèn LED hoặc tốc độ quay của động cơ, Arduino sử dụng phương pháp PWM (Điều chế độ rộng xung).
PWM bật tắt chân ngõ ra với tần số rất cao (~490 Hz hoặc 980 Hz). Bằng cách thay đổi tỷ lệ thời gian chân ở mức HIGH so với tổng chu kỳ (gọi là Duty Cycle), mắt người hoặc tải điện tử sẽ cảm nhận được một điện áp tương đương (trung bình) từ 0V đến 5V.
- Duty Cycle 0% (Giá trị 0): Tín hiệu luôn ở mức 0V → Đèn tắt hoàn toàn.
- Duty Cycle 50% (Giá trị 127): Nửa chu kỳ 5V, nửa chu kỳ 0V → Điện áp tương đương 2.5V → Đèn sáng trung bình.
- Duty Cycle 100% (Giá trị 255): Tín hiệu luôn ở mức 5V → Đèn sáng cực đại.
analogWrite(pin, value);
- pin: Chỉ dùng trên các chân có ký hiệu dấu ngã
~trên bo mạch: 3, 5, 6, 9, 10, 11. - value: Nhận giá trị từ 0 đến 255 (độ phân giải 8-bit, tương ứng 256 mức độ rộng xung).
Biến Trở (Potentiometer) & Tín Hiệu Analog Input
Hình 2: Biến trở 3 chân: 2 chân ngoài cùng tạo cầu điện trở, chân giữa (Wiper) di chuyển trích xuất điện áp chia áp.
Biến trở là thiết bị INPUT dạng ANALOG. Giá trị điện áp chân giữa biến thiên liên tục trong khoảng từ 0V đến 5V tùy theo góc vặn của núm xoay.
Chân tín hiệu phải được nối về 1 trong 6 chân Analog Input (A0 đến A5) trên bo mạch Arduino UNO. Hàm lập trình là analogRead(pin).
Bo mạch Arduino UNO tích hợp bộ biến đổi Tương tự - Số (ADC) độ phân giải 10-bit. Khi đọc điện áp từ 0V đến 5V, hàm analogRead() sẽ chia dải điện áp này thành 2^10 = 1024 mức, tương đương từ 0 đến 1023 (Mỗi đơn vị tương ứng 5V / 1024 ≈ 4.88 mV).
Lưu ý: Đây là khoảng giá trị chung cho toàn bộ các cảm biến Analog trên Arduino (cảm biến ánh sáng LDR, cảm biến nhiệt độ LM35, biến trở...).
- 2 chân bìa (Chân 1 & Chân 3): Nối vào nguồn cấp 5V và GND để tạo thành một thang đo điện thế toàn dải trên thanh điện trở cố định.
- Chân ở giữa (Chân 2 - Wiper/Chổi gạt): Trượt dọc trên thanh trở khi xoay núm, lấy ra điện áp chia áp (0V..5V) nối trực tiếp về chân A0 → A5.
- • Điều gì xảy ra khi đảo 2 cực âm và dương của biến trở? → Giá trị đọc bị đảo chiều: Nếu trước đó vặn theo chiều kim đồng hồ thì giá trị tăng từ 0 lên 1023, sau khi đảo cực thì vặn theo chiều kim đồng hồ giá trị sẽ giảm từ 1023 về 0.
Đọc giá trị analog từ A0 (0..1023), sau đó dùng hàm map() để ánh xạ dải giá trị sang chuẩn PWM (0..255) và xuất ra chân LED (Chân ~9):
int potPin = A0; // Chân giữa biến trở nối A0
int ledPin = 9; // Chân LED hỗ trợ PWM (~9)
void setup() {
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
}
void loop() {
int potValue = analogRead(potPin); // Đọc giá trị 0..1023
int brightness = map(potValue, 0, 1023, 0, 255); // Ánh xạ sang dải PWM 0..255
analogWrite(ledPin, brightness); // Điều khiển độ sáng đèn LED
Serial.print("Pot: ");
Serial.print(potValue);
Serial.print(" -> Brightness: ");
Serial.println(brightness);
delay(10);
}
Game Roulette 3 Bóng Đèn Xoay Vòng (Stop-and-Win)
- Hệ thống gồm 3 bóng đèn LED (Đỏ, Vàng, Xanh) sáng luân phiên theo vòng tuần hoàn.
- Dùng biến trở (Potentiometer) để thay đổi tốc độ chuyển vòng xoay của bóng đèn (vặn biến trở để xoay nhanh hoặc chậm tùy ý).
- Dùng nút nhấn (Button) để dừng vòng xoay đèn lại.
- Luật xét thắng / thua: Nếu người chơi bấm dừng đúng lúc đèn màu Xanh đang sáng → CHIẾN THẮNG (WIN)! Nếu dừng ở màu Đỏ hoặc màu Vàng → THUA (LOSE)!
Sơ đồ đấu dây đề xuất:
| Linh kiện | Chân cắm | Chân Arduino UNO | Ghi chú |
|---|---|---|---|
| LED Đỏ | Anode | Digital Pin 10 | Nối tiếp điện trở 220Ω, Cathode về GND |
| LED Vàng | Anode | Digital Pin 9 | Nối tiếp điện trở 220Ω, Cathode về GND |
| LED Xanh (Thắng) | Anode | Digital Pin 8 | Nối tiếp điện trở 220Ω, Cathode về GND |
| Biến trở | Chân giữa (Wiper) | Analog In A0 | Chân 1 nối 5V, Chân 3 nối GND |
| Nút nhấn (Stop) | Tín hiệu | Digital Pin 12 | Mạch Pull-down kèm trở 10kΩ về GND |
// Định nghĩa chân cắm
const int ledRed = 10;
const int ledYellow = 9;
const int ledGreen = 8;
const int potPin = A0;
const int buttonPin = 12;
int currentLed = 0; // 0: Đỏ, 1: Vàng, 2: Xanh
bool isStopped = false; // Cờ báo trạng thái dừng game
int lastButtonState = LOW;
void setup() {
pinMode(ledRed, OUTPUT);
pinMode(ledYellow, OUTPUT);
pinMode(ledGreen, OUTPUT);
pinMode(buttonPin, INPUT);
Serial.begin(9600);
Serial.println("=== TRO CHOI ROULETTE 3 DEN BAT DAU ===");
Serial.println("Van bien tro de doi toc do, bam nut de DUNG!");
}
void setLeds(int activeIndex) {
digitalWrite(ledRed, (activeIndex == 0) ? HIGH : LOW);
digitalWrite(ledYellow, (activeIndex == 1) ? HIGH : LOW);
digitalWrite(ledGreen, (activeIndex == 2) ? HIGH : LOW);
}
void loop() {
// Đọc trạng thái nút nhấn
int reading = digitalRead(buttonPin);
// Bắt sườn lên khi bấm nút (Edge Detection & Debounce)
if (reading == HIGH && lastButtonState == LOW) {
isStopped = !isStopped; // Đổi trạng thái: Chạy <-> Dừng
if (isStopped) {
Serial.println("\n--- DA DUNG VONG XOAY! ---");
if (currentLed == 2) {
Serial.println(">>> CHUC MUNG! DUNG DUNG DEN XANH: BAN DA THANG! (WIN) <<<");
// Hiệu ứng nhấp nháy đèn xanh chúc mừng
for (int i = 0; i < 5; i++) {
digitalWrite(ledGreen, LOW); delay(100);
digitalWrite(ledGreen, HIGH); delay(100);
}
} else {
Serial.print(">>> RAT TIEC! Ban dung o den ");
Serial.print((currentLed == 0) ? "DO" : "VANG");
Serial.println(": BAN DA THUA! (LOSE) <<<");
}
} else {
Serial.println(">>> TIEP TUC TRO CHOI! <<<");
}
delay(200); // Chống dội nút
}
lastButtonState = reading;
// Nếu game chưa dừng, tiếp tục xoay đèn
if (!isStopped) {
setLeds(currentLed);
// Đọc biến trở để lấy tốc độ xoay (từ 50ms - siêu nhanh đến 600ms - chậm)
int potVal = analogRead(potPin);
int delayTime = map(potVal, 0, 1023, 50, 600);
delay(delayTime);
// Chuyển sang đèn tiếp theo theo vòng tuần hoàn: 0 -> 1 -> 2 -> 0
currentLed = (currentLed + 1) % 3;
}
}
9 Câu Hỏi Ôn Tập Cuối Buổi & Lời Giải Chi Tiết
Đáp án: Phải sử dụng các chân có tính năng PWM (ký hiệu dấu ngã ~ trên bo mạch). Trên Arduino UNO R3 gồm 6 chân: 3, 5, 6, 9, 10, 11.
Đáp án: Khoảng giá trị từ 0 đến 255 (độ phân giải 8-bit, 2^8 = 256 mức độ).
Đáp án: Sử dụng hàm analogWrite(pin, value); với pin là chân PWM và value nằm trong đoạn [0, 255].
Đáp án: Sẽ xảy ra hiện tượng tràn số kiểu byte (Byte Overflow). Vì tham số chu kỳ xung của thanh ghi timer trên vi điều khiển AVR chỉ có 8-bit, hàm sẽ chỉ lấy 8 bit thấp nhất (tương đương phép toán value % 256). Ví dụ: truyền vào 256 sẽ bị quay vòng về 0 (đèn tắt), truyền 257 thành 1, truyền 300 thành 44.
Đáp án: Biến trở gồm 3 chân:
• 2 chân ở hai mép ngoài: Là hai đầu của dải điện trở cố định, một chân nối vào nguồn dương 5V, chân còn lại nối về cực âm GND.
• Chân ở giữa (Wiper): Là tiếp điểm trượt (con chạy), dùng để trích xuất điện áp chia áp biến thiên liên tục (từ 0V đến 5V) đưa về chân Analog In (A0..A5).
Đáp án: Khoảng giá trị từ 0 đến 1023 (do bộ chuyển đổi ADC trên Arduino UNO có độ phân giải 10-bit: 2^10 = 1024 mức).
Đáp án: Giá trị đọc được bị đảo chiều hoàn toàn. Chiều vặn núm trước đây làm tăng giá trị sẽ chuyển thành làm giảm giá trị (đặc tuyến điện áp chia áp bị đảo chiều ngược lại).
Sơ đồ nguyên lý mạch Pull-down: Chân 5V → Nút nhấn → Chân Digital D12. Từ chân D12 có thêm 1 đường qua điện trở 10kΩ về GND.
int buttonPin = 12;
void setup() {
pinMode(buttonPin, INPUT);
Serial.begin(9600);
}
void loop() {
if (digitalRead(buttonPin) == HIGH) {
Serial.println("Phat hien su kien: Nut dang duoc nhan!");
delay(50); // Loc nhieu
}
}
Phân tích thuật toán:
• Biến trở nối chân A0, đèn LED nối chân PWM ~9.
• Đọc val = analogRead(A0);
• Nếu val < 100: Tắt đèn → analogWrite(9, 0);
• Ngược lại (khi val ≥ 100): "Biến trở càng lớn thì đèn càng tối" → Nghịch biến! Khi val = 100 đèn sáng nhất (PWM = 255), khi val = 1023 đèn tối nhất (PWM = 0). Dùng hàm ánh xạ nghịch: map(val, 100, 1023, 255, 0);
const int potPin = A0;
const int ledPin = 9; // Chân hỗ trợ PWM
void setup() {
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
}
void loop() {
int potValue = analogRead(potPin);
if (potValue < 100) {
// Nếu giá trị bé hơn 100: Tắt đèn
analogWrite(ledPin, 0);
} else {
// Ngược lại: Biến trở càng lớn đèn càng tối (Ánh xạ nghịch)
int pwmValue = map(potValue, 100, 1023, 255, 0);
analogWrite(ledPin, pwmValue);
}
delay(15);
}