Trang Tổng Quan / Tuần 3: Button, PWM & Biến Trở
Tuần 3 Note chính thức: w3-03-10.md • 03/10/2026

NÚT NHẤN (BUTTON), ĐIỀU CHẾ XUNG PWM & BIẾN TRỞ (POTENTIOMETER)

Nội dung bài học chuyên sâu Tuần 3: Phân tích cơ chế nút nhấn 4 chân, nguyên lý mạch điện trở kéo xuống (Pull-down resistor), điều khiển tín hiệu Digital Input, kỹ thuật điều chế xung PWM (analogWrite), đọc tín hiệu Analog từ biến trở xoay (analogRead), mini project Roulette 3 đèn xoay và 9 câu hỏi ôn tập có lời giải chi tiết.

Mục 1.3

Nút Nhấn (Push Button) & Tín Hiệu Digital Input

Digital Input
• Cấu trúc phần cứng của nút nhấn 4 chân:
  • 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.
Dạng tín hiệu: Digital

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).

Hàm lập trình: digitalRead()

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.

Nguyên Lý Mạch

Giải Thích Cơ Chế Điện & Mạch Điện Trở Kéo Xuống (Pull-Down)

Điện trở kéo 10kΩ

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:

1. Khi KHÔNG nhấn nút (Mạch hở):

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.

2. Khi NHẤN nút (Mạch đóng):

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ức HIGH (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).
3. Sau khi thả nút nhấn (Triệt tiêu điện tích):

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ục 3

Mã Nguồn Căn Bản & Bài Tập Nút Nhấn Điều Khiển LED

Mã nguồn C++

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
  }
}
- Giải thích logic:
  • 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
Sơ đồ lắp ráp mạch nút nhấn kết hợp LED trên Breadboard (Chân D12 nút nhấn, Chân D8 LED)
Sơ đồ mạch nút nhấn và LED trên breadboard

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 5VCấ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ạiNối thanh mass GNDKéo điện thế về 0V khi thả nút
Đèn LED đơnAnode (Chân dài)Nối qua điện trở 220Ω vào Digital 8Tín hiệu điều khiển sáng/tắt
Đèn LED đơnCathode (Chân ngắn)Nối thanh mass GNDHoà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);
  }
}
- Cơ chế hoạt động của thuật toán:
  • Trạng thái nghỉ (Không bấm nút): buttonState == LOW, chương trình thực thi khối if → Gọi digitalWrite(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ối else → Gọi digitalWrite(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.
Mục 4

Điều Chế Độ Rộng Xung - Pulse Width Modulation (PWM)

analogWrite()

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).

• Khái niệm & Nguyên lý Duty Cycle (Chu kỳ 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.
Cú pháp hàm: 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).
Mục 1.4

Biến Trở (Potentiometer) & Tín Hiệu Analog Input

Analog Input A0..A5
Hình ảnh linh kiện: Biến trở xoay (Potentiometer) 3 chân
Linh kiện biến trở xoay Potentiometer 3 chân

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.

• Bản chất & Dạng tín hiệu:

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 cắm & Hàm đọc:

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).

• Khoảng giá trị Analog Input: Từ 0 → 1023 (10-bit ADC)

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ở...).

• Ý nghĩa và cách đấu nối 3 chân của 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.
Bài tập: Dùng biến trở điều chỉnh cường độ sáng của đèn LED Bài tập 2

Đọ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);
}
Mini Project Thực Hành

Game Roulette 3 Bóng Đèn Xoay Vòng (Stop-and-Win)

Dự án trọng tâm
• Yêu cầu kịch bản trò chơi:
  • 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 ĐỏAnodeDigital Pin 10Nối tiếp điện trở 220Ω, Cathode về GND
LED VàngAnodeDigital Pin 9Nối tiếp điện trở 220Ω, Cathode về GND
LED Xanh (Thắng)AnodeDigital Pin 8Nối tiếp điện trở 220Ω, Cathode về GND
Biến trởChân giữa (Wiper)Analog In A0Chân 1 nối 5V, Chân 3 nối GND
Nút nhấn (Stop)Tín hiệuDigital Pin 12Mạch Pull-down kèm trở 10kΩ về GND
Mã nguồn C++ hoàn chỉnh cho Game: Kiến trúc chống trễ (Non-blocking / Debounced)
// Đị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;
  }
}
Mục 7

9 Câu Hỏi Ôn Tập Cuối Buổi & Lời Giải Chi Tiết

9 Câu hỏi kiểm tra
Câu 1: Có thể dùng những chân nào trên Arduino để điều khiển cường độ sáng của bóng đèn LED?

Đá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.

Câu 2: Hãy cho biết khoảng giá trị output của analog (PWM)?

Đáp án: Khoảng giá trị từ 0 đến 255 (độ phân giải 8-bit, 2^8 = 256 mức độ).

Câu 3: Sử dụng hàm gì để có thể điều khiển cường độ sáng của đèn?

Đá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].

Câu 4: Chuyện gì xảy ra nếu truyền vào hàm analogWrite giá trị lớn hơn 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.

Câu 5: Hãy cho biết ý nghĩa các chân của biến trở.

Đá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).

Câu 6: Khoảng giá trị analog input là bao nhiêu?

Đá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).

Câu 7: Điều gì xảy ra khi đảo 2 cực âm và dương của biến trở?

Đá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).

Câu 8: Vẽ mạch nút nhấn và viết đoạn code chính để bắt sự kiện nhấn nút.

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
  }
}
Câu 9: Lắp mạch và lập trình đầy đủ theo yêu cầu: Dùng biến trở để điều khiển 1 bóng đèn LED: nếu giá trị biến trở bé hơn 100 tắt đèn, ngược lại giá trị điện trở càng lớn đèn càng tối.

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);
}
Tổng quan Tuần 1 Tuần 2 Tuần 3 Slide PDF Tra cứu