Trong bài này chúng ta cùng nhau tìm hiểu cách lập trình STM32 với nút nhấn 3 chế độ click, double click và long click, cũng như cách lập trình với các trạng thái của nút nhấn.
Bài này nằm trong Serie Học STM32 từ A tới Z
Ý tưởng lập trình nút nhấn nhiều trạng thái
Chắc hẳn các bạn ai cũng đã sử dụng máy tính. Trên chuột máy tính có các nút left click và right click, và ai cũng biết rằng chúng có 3 kiểu thao tác: click, double click và hold,
tương ứng với ấn 1 lần, ấn 2 lần liên tiếp và nhấn giữ.
Vậy làm thế nào chúng ta có thể lập trình được như vậy?
Ý tưởng như sau:
Khi nút nhấn không được bấm, nó ở trạng thái rảnh IDLE.
Khi nhấn nút lần đầu tiên, nó sẽ ở trạng thái click. Lúc này có 2 trường hợp xảy ra:
- Nút nhấn được nhả ra
- Nút nhấn được giữ (nút nhấn đang ở trạng thái hold)
Nếu nút nhấn được nhả ra, lại có 2 trường hợp xảy ra:
- Nút nhấn được bấm tiếp: lúc này chúng ta xác định được đây là double click
- Nút nhấn không được bấm tiếp: chúng ta xác định được đây là single click
Nếu nút nhấn được giữ, chúng ta sẽ bắt đầu đếm xem giữ được bao lâu. Nếu lớn hơn hoặc bằng thời gian đã định nghĩa (vd: 3 giây) thì chúng ta xác định được đây là long click.
Để biểu diễn các trạng thái nút nhấn một cách dễ hiểu, người ta sử dụng State machine.
States machine là gì
State machine (máy trạng thái) là một cách lập trình theo mô hình quản lý quá trình chuyển đổi trạng thái của thiết bị. Với máy trạng thái, chúng ta tiếp cận vấn đề bằng cách chia quá trình vận hành của thiết bị thành các trạng thái. Mỗi trạng thái gồm: các hành động được thực hiện khi thiết bị ở trạng thái này, các trạng thái có thể chuyển sang, và điều kiện kích hoạt việc chuyển trạng thái.
Giống như đèn giao thông vậy: đèn chỉ có thể ở 1 trong 3 trạng thái xanh, vàng, đỏ, và chỉ chuyển sang trạng thái kế tiếp khi hết thời gian của trạng thái hiện tại.
Mình sẽ có 1 bài nói chi tiết về State Machine trong lập trình nhúng.

Trong ví dụ về nút nhấn, chúng ta sẽ định nghĩa các trạng thái của nút nhấn như sau:
- IDLE: chưa được nhấn
- Click: nhấn lần đầu
- Release: nhả nút nhấn
- 2nd Click: click lần tiếp theo
Các sự kiện để chuyển trạng thái như sau:
- IDLE – Click: nhấn nút nhấn
- Click – Long Click: so sánh với thời gian 3s, nếu giữ trên 3s thì trả về long click
- Click – Release: nhả nút nhấn
- Release – Single click: so sánh với thời gian chờ click (nghĩa là trong thời gian này nếu không được nhấn lần tiếp theo sẽ trả về kết quả Single Click)
- Release – 2nd Click: nút được nhấn tiếp
- 2nd Click – Release: nút nhấn được nhả ra, trả về kết quả Double click.
Cứ như vậy bạn cũng có thể nhận biết được các trạng thái triple click,…
Lập trình STM32 với nút nhấn
Khởi tạo nút nhấn ở PA0 (Input Pull Up, nút nối xuống GND) và LED ở PC13 (LED có sẵn trên KIT Blue Pill). Làm như bài 3 STM32 GPIO led và nút nhấn nhé các bạn.
Định nghĩa khoảng thời gian
Trong button.h mình sẽ định nghĩa các khoảng thời gian để chuyển trạng thái:
DEBOUND_TIME: thời gian chống rung phím (debounce)SINGLE_Click_Time: mốc thời gian phân biệt single click và long clickDouble_Click_Time: thời gian tối đa giữa 2 lần nhấn nút để được tính là double click.
Sau đó định nghĩa kiểu enum các trạng thái của nút nhấn.

Tiếp theo mình định nghĩa 1 struct để lưu port và pin của nút nhấn, trạng thái nút nhấn và các biến thời gian để so sánh.

Lập trình STM32 tại các trạng thái
Trong hàm read chúng ta sẽ lần lượt kiểm tra từ trạng thái IDLE.
Nếu được nhấn, chương trình sẽ chạy xuống kiểm tra nút được nhả ra hay vẫn đang giữ.
Nếu nút được nhả ra, chúng ta kiểm tra tiếp trong khoảng thời gian Double_Click_Time có được nhấn tiếp không?
- Nếu được nhấn: trả về double click
- Nếu không được nhấn: chúng ta kiểm tra tiếp thời gian đã giữ nút. Nếu nhỏ hơn
SINGLE_Click_Timethì trả về single click, nếu lớn hơn thì trả về long click

Lưu ý: Để nhận biết được double click, chương trình bắt buộc phải chờ hết khoảng Double_Click_Time sau lần nhả nút đầu tiên rồi mới dám kết luận đó là single click. Vì vậy phản hồi của single click luôn bị trễ đúng bằng khoảng thời gian này. Đặt quá dài (vd 1s) thì người dùng thấy nút “ì”, đặt quá ngắn (dưới 200ms) thì rất khó bấm được double click, thường chọn khoảng 250 – 400ms.
Để tải full code các bạn đọc bài viết: Download tài liệu STM32 và tải thư viện cùng ví dụ (lib example) về nhé.
Trong main các bạn khai báo nút nhấn, khởi tạo nút nhấn và LED.

Trong while chúng ta đọc trạng thái nút nhấn, sau đó lập trình nháy LED tương ứng với mỗi trạng thái của nút.
Các bạn có thể dùng switch case thay cho if else nếu nút có nhiều trạng thái.

Kết quả
Kết
Lập trình STM32 với nút nhấn có thể sử dụng nhiều phương pháp khác nhau. Đơn giản nhất là chúng ta dùng while để chờ ở từng state, nhược điểm là chương trình có thể bị kẹt trong while và làm chậm các tác vụ khác nếu không tối ưu code (khi đó nên dùng HAL_GetTick() để kiểm tra thời gian theo kiểu non-blocking).
Nhưng cách này cũng rất đơn giản để các bạn hiểu thêm về khái niệm State Machine trong lập trình nhúng.
Nếu thấy bài viết này hay, hãy chia sẻ tới những người bạn học hay đồng nghiệp của mình. Và nếu thắc mắc điều gì, hãy để lại bình luận và cùng gia nhập những người anh em nghiện lập trình tại đây nhé: Hội anh em nghiện lập trình



Hi anh,
cảm ơn bài viết rất hay của anh. em đã làm theo và chạy đc, nhưng có 1 lỗi nhỏ, đó là trong hàm main em có sử dụng if status button giống anh thì sau khi vào rẽ nhánh double click thì sau đó nó vẫn tiếp tục vào rẽ nhánh single click. em debug thì lại ko bị hiện tượng đó. ko biết anh có bị hiện tượng này ko, mong anh chỉ giúp em lỗi sai.
thanks anh.
phần BUTTON_Init(&Button1,BUTTON_GPIO_Port ,BUTTON_Pin ); em nhâp vào toàn báo lỗi chố &Button1 em mông a chỉ cách sửa ạ
anh ơi a ra thêm nhiều bài hơn được không ạ. Đây là nguồn tư liệu quý báu cho bọn em học ạ tài vì nhiều kiến thức trường không đi sâu phải tự mò anh ak. Cảm ơn a nhiều
A sẽ cố gắng nhé