👉 Table of Contents
Giới thiệu về nút nhấn
Nút nhấn là một phần cứng khá cơ bản và dễ dàng sử dụng. Nút nhấn cơ học
là loại mình sẽ giới thiệu trong bài viết này. Hầu hết những bạn mới bắt đầu làm quen với điện tử thì đều quen với việc sử dụng nút nhấn.
Phân loại
Các loại nút nhấn cơ học phổ biến bao gồm:
Cơ chế lò xo
bên trong vận hành hai trạng thái
này (nhấn
và thả
) của một nút nhấn. Các nút bấm
được phân loại
chủ yếu thành
các nút ấn thường mở
, thường đóng
và nhấn kép
.
Repo có chứa Source code
mình ví dụ trong bài viết này: SmartClock
Nay mình sử dụng nút nhấn tự nhả - thường mở (dùng theo kiểu trở kéo lên nhé - pull up resistor
) để giới thiệu về cách sử dụng
khá hay ho khi chúng ta bị giới hạn PIN GPIO
trên MCU.
Các bạn có thể tìm hiểu của nút nhấn sài trở kéo lên hoặc trở kéo xuống ở bài viết này:
Note: Xác định trạng thái của một nút nhấn
Mục đích của bài viết này là giải quyết vấn đề bị giới hạn phần cứng và PIN GPIO ở MCU.
Nhìn vào hình ảnh trên chúng ta thấy hàm CheckButton_ndb()
được đặt trong loop()
của MCU.
Chống dội phím cho phím nhấn cơ học
Vì là nút nhấn cơ học nên khi thực hiện thao tác bấm (hoặc k bấm mà do yếu tố môi trường độ ẩm, nhiễm nước nhẹ) sẽ xảy ra hiện tượng dội phím gây nhiễn tín hiệu INPUT có thể làm chương trình chạy sai ý đồ. (#debounce button)
Như trong hình thì khoảng thời gian Fluctuations
chính là lúc xảy ra dội phím làm cho tín hiệu nhảy mức logic 1-0 loạn xạ và làm MCU xử lý sai lệch nếu đọc tín hiệu này.
Để khắc phục tình trạng trên thì khi coding chúng ta sử dụng phương pháp chống dội bằng delay()
(có nhiều phương pháp, mỗi phương pháp có ưu điểm, nhược điểm khác nhau. Với #tag mình để phía trên các bạn có thể tìm kiếm google
để tìm hiểu thêm).
Source code mẫu:
if (digitalRead(Button_Mode) == HIGH) // nếu nút bấm ở mức cao
{
delay(500); //check chac chan la do ng nhan nut
if (digitalRead(Button_Mode) == HIGH)
{
// thực hiện lệnh
}
}
Nhìn vào Source code mẫu:
Lệnh #line1 sẽ check xem nút nhấn có đang được nhấn hay không - HIGH
?
Nếu sau khoảng thời gian delay(500);
mà tín hiệu vẫn đang ỏ mức HIGH
thì chính xác là do con người cố ý tác động việc nhấn nút.
Còn sau 500ms
- #line3 mà việc check ở #line4 không thỏa mãn - LOW
thì đó chỉ là xung nhiễu hoặc nút nhấn vô tình bị cấn nhẹ gì đấy.
Tìm hiểu hàm millis
millis()
có nhiệm vụ trả về một số - là thời gian (tính theo mili giây) kể từ lúc mạch Arduino bắt đầu chương trình của bạn. Nó sẽ tràn số và quay số 0 (sau đó tiếp tục tăng) sau 50 ngày
(tức là cho phép đo đạc binh thường khoảng 50 ngày).
Trả về
một số nguyên kiểu unsigned long
là thời gian kể từ lúc thương trình Arduino được khởi động
Tham khảo thêm: Tìm hiểu hàm millis - diễn đàn ArduinoVN
Áp dụng thực tiễn
Nguyên lí làm việc
của nút nhấn là khi chúng ta nhấn giữ nút ở khoảng bao lâu thời gian thì mỗi khoảng thời gian sẽ là một mode lựa chọn khác nhau. Khi chúng ta nhả nút ra thì sẽ chọn mode đó. (Giống mấy nút nhấn trên màn hình LCD xe máy Winner, Exceter, Raider…)
Nhìn vào #line324, chúng ta khởi tạo một biến startTime
và nó được gán bằng millis()
. Điều này có nghĩa là ngay khi chương trình chạy tới lệnh này thì giá trị truyền về của hàm millis()
sẽ được gán cho biến startTime
.
Ví dụ, ngay khi chương trình hoạt động
thì hàm millis() được chạy từ 0ms
, chạy tới khi nó đụng cái lệnh #line324 thì hết 50 mili giây
. Thì ngay lúc đó biến startTime = 50
.
Sài kiểu biến phạm vi local vì mỗi lần chúng ta nhấn nút thì muốn nó lấy giá trị thời gian tại thời điểm đó.
Tại thời điểm #line327, lệnh while (digitalRead(Button_Mode) == HIGH)
sẽ check việc chúng ta có ĐANG GIỮ NÚT NHẤN
hay không? Nếu không còn giữ nút thì thoát While, ngược lại nếu đang còn giữ nút ở mức logic HIGH
thì check lệnh bên trong.
- Lúc này nếu còn giữ đè nút thì ở #line330,
couter_Mode = (millis() - startTime) / 1000;
có nghĩa là nếu chúng tagiữ nút nhấn trong 1000ms
(ứng với 1 giây) thì sau khoảng 1000ms thìhàm millis() lúc này bằng 50ms trước đó cộng với 1000ms giữ phím là 1050ms
. - Nên biến
couter_Mode
sẽ luu trữ khoảng thời gian chúng ta nhấn giữ nút chính thúc bằng cáchlấy giá trị trả về của hàm millis()
tại thời điểm buông nút
trừ đi
mốc thời gian trước khi vào việc
check giữ nút (startTime
).
Các bạn nhìn hình mô phỏng khoảng thời gian ở trên cho dễ hiểu nhé.
Source code:
long startTime = millis(); // giá trị ban đầu được gán bằng giá trị hiện tại của millis
// Serial.printf("digitalRead(Button_Mode): ");
// Serial.println(digitalRead(Button_Mode));
while (digitalRead(Button_Mode) == HIGH) // đợi cho nút bấm được giữ
{
Serial.println(millis() - startTime);
couter_Mode = (millis() - startTime) / 1000;
couter_Mode = couter_Mode / 1 % 10;
lcd.setCursor(0, 0);
lcd.print("Couter: ");
lcd.print(couter_Mode);
lcd.print(" seconds ");
if (couter_Mode < 1)
{
lcd.setCursor(0, 1);
lcd.print("Mode Selection ");
}
/* vao mode Message */
else if (couter_Mode >= 7)
{
lcd.setCursor(0, 1);
lcd.print("Mode: >> Message ");
lcd.setCursor(0, 2);
lcd.print(" ");
lcd.setCursor(0, 3);
lcd.print(" ");
}
/* vao mode setup wifi */
else if (couter_Mode >= 5)
{
lcd.setCursor(0, 1);
lcd.print("Mode: >> Wifi Change");
lcd.setCursor(0, 2);
lcd.print(" Message ");
lcd.setCursor(0, 3);
lcd.print(" ");
}
/* vao mode setup vi tri thoi tiet */
else if (couter_Mode >= 3)
{
lcd.setCursor(0, 1);
lcd.print("Mode: >> Location ");
lcd.setCursor(0, 2);
lcd.print(" Wifi Change");
lcd.setCursor(0, 3);
lcd.print(" Message ");
}
/* vao mode setup bao thuc */
else if (couter_Mode >= 1)
{
lcd.setCursor(0, 1);
lcd.print("Mode: >> Alarm ");
lcd.setCursor(0, 2);
lcd.print(" Location ");
lcd.setCursor(0, 3);
lcd.print(" Wifi Change");
}
yield(); // disble Soft WDT reset - NodeMCU
};
Kết quả trả về của couter_Mode
vẫn là mili giây nên chúng ta chia cho 1000 để đổi sang giây tiện việc theo dõi số liệu.
Tiếp đến, chương trình của mình có 4 Mode, mỗi Mode khoảng 2 giây
.
Nếu đè nút nhỏ hơn 1 giây
thì có thể do cấn nút hay gì đó nên mình chỉ cho hiển thị chuẩn bị vào chế độ chọn mode
và nếu buông nút thì thoát khỏi chọn Mode
.
Sau đó lần lượt các Mode từ 1 đến 3 giây
, 3 đến 5 giây
, 5 đến 7 giây
, 7 đến 9 giây
. Khi đến 10 mình quay lại 0
( tức là < 1) bằng cách chỉ lấy số hàng đơn vị.
Tương tự như vậy các bạn có thể triển khai thêm cho từng Mode. Khi vào mỗi mode riêng biệt
chúng ta tạo thêm biến local để đong đếm khoảng thời gian
đè nút để vào các mode con
của Mode cha mẹ
.
Chúc mọi người thành công!
Bài viết có sử dụng tư liệu từ diễn đàn Arduino Việt Nam