Nội Dung Bài Viết
Phương pháp Finite State Machine (FSM) giúp mô hình hóa nút nhấn thành các trạng thái rõ ràng: IDLE → PRESSING → PRESSED → RELEASING → RELEASED, mỗi trạng thái có thời gian debounce riêng (thường 20ms) để xác nhận tín hiệu ổn định.
Trong lập trình vi điều khiển STM32F103 bằng thanh ghi, xử lý hiện tượng dội nút nhấn (button bounce) là một bước quan trọng để đảm bảo hệ thống nhận đúng sự kiện. Dao động cơ học khi nhấn hoặc thả nút có thể gây ra nhiều lần nhấn giả, dẫn đến lỗi như đếm sai hoặc LED nhấp nháy liên tục.
Phương pháp 4: Máy trạng thái hữu hạn (Finite State Machine – FSM)
Dao động từ nút nhấn có thể gây nhầm lẫn giữa các lần nhấn thực sự và nhiễu. State Machine giúp hệ thống phân biệt rõ ràng từng giai đoạn (nhấn, giữ, thả), tránh lỗi khi xử lý các sự kiện liên quan đến nút.
Nguyên lý hoạt động và sơ đồ chống dội nút nhấn bằng Finite State Machine
Nguyên lý
Dưới đây là các trạng thái máy trong phương pháp chống dội nút nhấn dùng State Machine:
- STATE_IDLE: Nút chưa được nhấn. Khi tín hiệu = 1 → chuyển sang PRESSING.
- STATE_PRESSING: Nếu tín hiệu vẫn = 1 → giảm bộ đếm. Khi hết thời gian → chuyển sang PRESSED.
- STATE_PRESSED: Khi tín hiệu = 0 → chuyển sang RELEASING.
- STATE_RELEASING: Nếu tín hiệu vẫn = 0 → giảm bộ đếm. Khi hết thời gian → chuyển sang RELEASED.
- STATE_RELEASED: Quay lại IDLE, sẵn sàng chu kỳ mới.
Mỗi trạng thái có thời gian debounce riêng (20 ms) để xác nhận tín hiệu.
Sơ đồ (flow chart) phương pháp chống dội nút nhấn bằng Finite State Machine

Cách triển khai trong STM32F103
Sử dụng struct để lưu trạng thái nút, thời gian debounce và trạng thái logic.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | typedef struct { uint32_t keyStatus; uint32_t keyState; uint32_t keyDebounceTime; } ProcessButton; enum { STATE_IDLE, STATE_PRESSING, STATE_PRESSED, STATE_RELEASING, STATE_RELEASED }; #define BUTTON_DEBOUNCING_TIME 1000*20 //20ms ProcessButton Btn = {0,0,0}; |
Hàm ProcesButton() xử lý chuyển trạng thái theo tín hiệu đầu vào.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | void ProcesButton(GPIO_TypeDef* port, uint32_t pin, ProcessButton* Button, uint32_t active) { uint32_t signal = ((port->IDR.reg >> pin) & 1U); if (active == 0) signal = !signal; switch (Button->keyStatus) { case STATE_IDLE: if (signal) { Button->keyStatus = STATE_PRESSING; Button->keyDebounceTime = BUTTON_DEBOUNCING_TIME; } break; case STATE_PRESSING: if (signal) { if (Button->keyDebounceTime > 0) { Button->keyDebounceTime--; if (Button->keyDebounceTime == 0) { Button->keyStatus = STATE_PRESSED; Button->keyState = 1; } } } else { Button->keyStatus = STATE_IDLE; } break; case STATE_PRESSED: if (!signal) { Button->keyStatus = STATE_RELEASING; Button->keyDebounceTime = BUTTON_DEBOUNCING_TIME; } break; case STATE_RELEASING: if (!signal) { if (Button->keyDebounceTime > 0) { Button->keyDebounceTime--; if (Button->keyDebounceTime == 0) { Button->keyStatus = STATE_RELEASED; Button->keyState = 0; } } } else { Button->keyStatus = STATE_PRESSED; } break; case STATE_RELEASED: Button->keyStatus = STATE_IDLE; break; }; } |
Chương trình main chống dội nút nhấn bằng Finite State machine
Trong main(), kiểm tra sự thay đổi trạng thái để xử lý hành động (ví dụ: LED_Toggle()).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | int main(void) { SystemClock_Config(); // Initialize system clock GPIO_LED_Config(); GPIO_Button_Config(); uint32_t lastKeyState = 0; while (1) { ProcesButton(GPIOC,1,&Btn,0); if ((Btn.keyState == 1) && (lastKeyState == 0)) { LED_Toggle(); count++; } lastKeyState = Btn.keyState; } } |
Chương trình chống dội nút nhấn bằng phương pháp State Machine hoạt động như sau:
- STATE_IDLE: Nút chưa được nhấn. Khi tín hiệu = 1, chuyển sang PRESSING và khởi động bộ đếm 20ms.
- STATE_PRESSING: Nếu tín hiệu vẫn = 1, giảm bộ đếm. Khi bộ đếm = 0, chuyển sang PRESSED và gán keyState = 1. Nếu tín hiệu = 0, quay lại IDLE.
- STATE_PRESSED: Khi tín hiệu = 0, chuyển sang RELEASING với bộ đếm 20ms.
- STATE_RELEASING: Nếu tín hiệu = 0 và bộ đếm = 0, chuyển sang RELEASED, gán keyState = 0. Nếu tín hiệu = 1, quay lại PRESSED.
- STATE_RELEASED: Quay lại IDLE, sẵn sàng chu kỳ mới.
- Ví dụ: Khi nhấn nút, hệ thống chờ 20ms để xác nhận PRESSED, sau đó chờ thêm 20ms khi thả để xác nhận RELEASED, bỏ qua dao động trong quá trình.
Link Github: Download chương trình chống dội nút nhấn bằng Finite State Machine
Ưu điểm phương pháp chống dội nút nhấn bằng Finite State machine
- Chính xác cao → loại bỏ nhiễu hiệu quả.
- Dễ mở rộng → hỗ trợ nhấn giữ, nhấn đôi, auto-repeat.
- Phù hợp hệ thống phức tạp → dễ tích hợp vào FSM tổng thể.
Nhược điểm phương pháp chống dội nút nhấn bằng Finite State machine
- Code phức tạp hơn → cần quản lý nhiều trạng thái.
- Tốn RAM và CPU hơn → do lưu biến trạng thái và bộ đếm.
So sánh: FSM vs. Đếm thời gian ổn định
| Tiêu chí | ⏱️ Đếm thời gian ổn định (Time-based) | 🔁 Máy trạng thái hữu hạn (FSM) |
|---|---|---|
| Nguyên lý hoạt động | Đếm thời gian tín hiệu không thay đổi | Mô hình hóa nút nhấn thành các trạng thái |
| Độ chính xác | Tốt | Rất cao |
| Khả năng mở rộng | Hạn chế (khó xử lý nhấn giữ, nhấn đôi) | Dễ dàng mở rộng nhiều kiểu nhấn |
| Phù hợp hệ thống phức tạp | Trung bình | Rất phù hợp |
| Tài nguyên sử dụng | Ít hơn | Nhiều hơn (RAM, CPU) |
| Độ phức tạp code | Trung bình | Cao hơn |
| Điều chỉnh thời gian debounce | Dễ dàng bằng giá trị đếm | Linh hoạt theo từng trạng thái |
| Ứng dụng phù hợp | Thiết bị đơn giản, không yêu cầu đa trạng thái | Thiết bị công nghiệp, giao diện người dùng |
Kết luận
Phương pháp chống dội nút nhấn bằng Finite State Machine (FSM) trong hướng dẫn lập trình STM32F103 thanh ghi là giải pháp mạnh mẽ và chính xác nhất trong các kỹ thuật phần mềm xử lý nút nhấn. Bằng cách mô hình hóa từng giai đoạn của nút nhấn thành các trạng thái riêng biệt, FSM giúp hệ thống phân biệt rõ ràng giữa nhấn, giữ và thả, đồng thời loại bỏ nhiễu một cách hiệu quả.
Dù có độ phức tạp cao hơn và tiêu tốn tài nguyên hơn so với các phương pháp đơn giản như delay hay đếm thời gian ổn định, FSM lại vượt trội về khả năng mở rộng và độ tin cậy. Đây là lựa chọn lý tưởng cho các hệ thống nhúng phức tạp, giao diện người dùng, hoặc ứng dụng công nghiệp yêu cầu xử lý đa trạng thái như nhấn giữ, nhấn đôi, auto-repeat.
👉 Nếu bạn đang phát triển một hệ thống có nhiều nút chức năng hoặc yêu cầu phản hồi chính xác, FSM là phương pháp chống dội đáng cân nhắc nhất.
