👉 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ấnthả) 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 đóngnhấn kép.

Các loại nút nhấn.

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.

Nội dung code trong bài giới thiệu.

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)

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

Hàm CheckButton_ndb() cho ví dụ bên dưới.

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 ta giữ 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ách lấ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).
Mô phỏng tiến trình thời gian khi MCU hoạt động.

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


Hi, I'm Bang