Bài 4 · Toán · Công nghệ · Tin học
Đếm số lần nhấn nút
máy chỉ thấy trạng thái, người mới thấy sự kiện
Bài này nhìn qua tưởng dễ hơn Bài 2, nhưng thật ra khó hơn hẳn. Đếm số lần nhấn buộc chương trình phải nhớ được quá khứ — và đó là khái niệm mới quan trọng nhất của cả cấp độ 1.
01 Mục tiêu và yêu cầu cần đạt
Ở Bài 2, chương trình chỉ cần biết ngay lúc này nút có đang bị nhấn không. Bài này cần biết thêm một thứ hoàn toàn khác: vừa nãy nút có bị nhấn không. Chỉ thêm một chữ "vừa nãy" thôi, nhưng nó buộc chương trình phải có trí nhớ.
Sau bài này học sinh làm được
- Dùng biến để đếm và giải thích vì sao biến phải khai báo ngoài vòng lặp.
- Phân biệt trạng thái (đang nhấn) và sự kiện (vừa nhấn xuống).
- Viết được điều kiện phát hiện sườn:
đang nhấnvàchưa nhấn ở vòng trước. - Mô tả được hiện tượng dội tiếp điểm và cách xử lí bằng khoảng chờ.
- Dự đoán được số đếm sai khi bỏ phần nhớ trạng thái.
- Tin học 10 — biến, phép gán, biểu thức lôgic có phép "và" và "không".
- Toán 10 — đại lượng rời rạc, phép đếm.
- Công nghệ 12 — xử lí tín hiệu đầu vào từ nút nhấn.
Trích nguyên văn yêu cầu cần đạt từ Phụ lục Thông tư 32/2018 trước khi đưa vào kế hoạch bài dạy.
02 Lí thuyết
Máy không thấy sự kiện, chỉ thấy trạng thái
Ta nghĩ mình vừa tạo ra một cú bấm. Máy thì chạy vòng lặp rất nhanh và mỗi vòng chỉ đọc được một trạng thái: nhấn, hoặc không nhấn.
Ngón tay giữ nút chừng 0,2 giây. Vòng lặp nghỉ 20 ms thì máy đọc được khoảng mười vòng liên tiếp đều thấy "đang nhấn" — cứ thấy là cộng một thì một cú bấm bị đếm thành mười.
Sự kiện là chỗ trạng thái thay đổi
Đừng hỏi "nút có đang được nhấn không", hãy hỏi "nút có VỪA MỚI bị nhấn xuống không". Câu sau cần hai thông tin — bây giờ và vòng trước:
if dang_nhan and not truoc_do: Điều kiện này chỉ đúng ở một vòng duy nhất trong cả quãng ngón tay còn đè lên nút — đúng vòng mà trạng thái vừa lật. Gọi là phát hiện sườn.
Hai lá kim loại không chạm nhau dứt khoát một lần. Chúng nảy vài lần trong dưới 10 ms đầu, tín hiệu là 0-1-0-1 rồi mới yên — máy thấy vài sườn cho một cú bấm.
Chữa rẻ nhất: ghi nhận xong một lần nhấn thì nghỉ hẳn 200 ms mới đọc tiếp. Lúc đó tiếp điểm đã ổn định, mà ngón tay người cũng không bấm nổi hai cái trong 200 ms nên không mất cú nào.
Biến đếm phải sống lâu hơn một vòng lặp
so_lan = 0 nằm trước while True:.
Chuyển vào trong vòng lặp thì mỗi vòng biến lại bị đặt về 0 và số đếm mãi mãi
bằng 1. Lỗi kinh điển, cũng là thử thách ở cuối bài.
03 Linh kiện
| Linh kiện | Mô tả, lưu ý khi mua | Số lượng · giá |
|---|---|---|
| ESP32 DevKit V1 | Bo 30 chân, cổng micro-USB hoặc Type-C | 1 · 90–150k |
| Nút nhấn 4 chân | Dùng lại nút của Bài 2 | 1 · 1k |
| LED 5 mm và điện trở 220 Ω | Đèn báo mỗi lần ghi nhận được một cú bấm | 1 · 700đ |
| Breadboard và dây cắm | Loại 400 lỗ là đủ | 1 bộ · 30k |
04 Lắp mạch và sơ đồ nối dây
| Khối | Nối dây | Nguồn |
|---|---|---|
| Nút nhấn | GPIO4 → một chân nút · chân chéo góc → GND | Không cần nguồn riêng |
| LED | GPIO23 → điện trở 220 Ω → chân dài · chân ngắn → GND | Lấy từ chính chân GPIO |
Mạch giống hệt Bài 2 — GPIO4 đọc nút, GPIO23 điều khiển LED. Nếu lớp vừa học Bài 2 xong thì không phải rút một sợi dây nào, chỉ đổi chương trình.
Chính mạch ấy khi đã cắm xong: nút nhấn vắt qua rãnh giữa breadboard, LED và điện trở ở hàng bên. Số lần nhấn hiện ở khung Màn hình của bo phía dưới.
Giữ nguyên mạch Bài 2
Bài này dùng đúng mạch của Bài 2, không thêm bớt gì: nút nhấn ở
GPIO4 và LED ở GPIO23. Nếu lớp vừa học Bài 2 xong thì
không phải rút một sợi dây nào.
- Nút nhấn: một chân →
GPIO4, chân chéo góc →GND. - LED:
GPIO23→ điện trở 220 Ω → chân dài, chân ngắn →GND.
Nhắc lại vì đây là lỗi tốn thời gian nhất của cả hai bài: nút 4 chân có hai cặp đã thông sẵn bên trong. Dùng nhầm một cặp thì máy sẽ thấy nút luôn luôn được nhấn, và số đếm nhảy loạn ngay khi cấp nguồn.
05 Mã nguồn
Toàn bộ mã trong bài chạy trên MicroPython 1.28 cho ESP32. Chép vào bo rồi đổi tên thành main.py là chạy mỗi lần cấp nguồn.
Bước 0 — bản SAI, cho học sinh chạy trước# ------------------------------------------------------------
# Bai 4 - Ban DEM SAI, co tinh de hoc sinh tu tim ra loi
# ------------------------------------------------------------
from machine import Pin
import time
nut = Pin(4, Pin.IN, Pin.PULL_UP)
so_lan = 0
while True:
if nut.value() == 0:
so_lan = so_lan + 1
print("So lan nhan:", so_lan)
time.sleep_ms(20) Bấm đúng một cái, màn hình in ra:
So lan nhan: 1 So lan nhan: 2 So lan nhan: 3 ... So lan nhan: 47 <-- moi bam MOT cai
Đây là bản cố tình sai, và nó đáng giá hơn bản đúng. Hãy để học sinh chạy bản này trước, tự thấy con số nhảy vọt, rồi mới hỏi vì sao. Câu trả lời chính là toàn bộ nội dung của bài.
Chương trình chính — đếm đúng# ------------------------------------------------------------
# Bai 4 - Dem so lan nhan nut
# Phan cung: GPIO4 -> nut nhan -> GND
# GPIO23 -> dien tro 220 ohm -> LED -> GND
# ------------------------------------------------------------
from machine import Pin
import time
# ===== CẤU HÌNH =====
CHAN_NUT = 4 # nut nhan
CHAN_LED = 23 # den bao da ghi nhan
CHONG_DOI = 200 # ms - bo qua nhieu tiep diem sau moi lan nhan
# ====================
nut = Pin(CHAN_NUT, Pin.IN, Pin.PULL_UP)
led = Pin(CHAN_LED, Pin.OUT)
so_lan = 0
truoc_do = False # nut co dang duoc nhan o VONG TRUOC khong
while True:
dang_nhan = nut.value() == 0
# Chi lam viec o dung KHOANH KHAC nut chuyen tu nha sang nhan
if dang_nhan and not truoc_do:
so_lan = so_lan + 1
print("So lan nhan:", so_lan)
led.on()
time.sleep_ms(120)
led.off()
time.sleep_ms(CHONG_DOI)
truoc_do = dang_nhan
time.sleep_ms(10) Bấm ba cái, màn hình in ra đúng ba dòng:
So lan nhan: 1 So lan nhan: 2 So lan nhan: 3
So hai bản cạnh nhau, khác biệt chỉ nằm ở hai chỗ: biến truoc_do và
điều kiện and not truoc_do. Hai chỗ đó biến một chương trình sai
thành một chương trình đúng.
Em mô tả bằng tiếng Việt, hệ thống dựng thuật toán và chương trình, rồi nạp thẳng xuống ESP32 qua Wi-Fi.
06 Hiệu chuẩn và kiểm thử
Phép thử đếm đúng
- Nạp chương trình chính, để số đếm về 0 bằng cách cấp nguồn lại.
- Bấm chậm rãi đúng 10 lần, mỗi lần cách nhau khoảng một giây.
- Số cuối cùng in ra phải đúng bằng 10.
- Lặp lại nhưng bấm nhanh hết mức có thể trong 5 giây, rồi đếm lại bằng mắt xem có khớp không.
Phép thử phá chống dội
Hạ CHONG_DOI từ 200 xuống 5 ms rồi bấm nhanh. Số đếm sẽ nhảy hơn
số lần bấm thật. Đó không phải lỗi mới — đó chính là dội tiếp điểm lộ ra khi ta
gỡ bỏ tấm chắn.
Danh sách kiểm trước khi coi là xong
- Bản sai ở Bước 0 cho ra số nhảy vọt, học sinh giải thích được vì sao.
- Bản đúng đếm khớp 10 lần bấm chậm.
- Giữ nút thật lâu vẫn chỉ tính là một lần.
- Hạ
CHONG_DOIxuống 5 ms thì số đếm bắt đầu sai. - Học sinh chỉ ra được vì sao
so_lan = 0phải nằm ngoài vòng lặp.
07 Kinh nghiệm và cạm bẫy
Cám dỗ lớn nhất của bài này là đưa thẳng bản đúng rồi giảng về phát hiện sườn.
Làm thế thì học sinh chép được chương trình nhưng không hiểu vì sao lại cần
biến truoc_do — với các em nó chỉ là một dòng thừa khó hiểu.
Chạy bản sai trước mất thêm năm phút, nhưng sau đó cả lớp muốn biết
cách chữa. Từ chỗ đó, dòng and not truoc_do không còn là phép
thuật mà là câu trả lời cho một câu hỏi các em vừa tự đặt ra.
- Số đếm luôn bằng 1. Dòng tạo biến đếm bị đưa vào trong vòng lặp. Mỗi vòng nó lại về 0.
- Số đếm tự nhảy khi không ai chạm vào. Nút cắm nhầm cặp chân, hoặc dây nút bị lỏng làm chân GPIO thả nổi.
- Bấm nhanh thì mất cú bấm.
CHONG_DOIđặt quá lớn, ví dụ 1000 ms. Trong một giây đó mọi cú bấm đều bị bỏ qua.
Hai hộp trên viết từ quá trình dựng và chạy thử bài. Sau buổi dạy đầu tiên, xin thầy cô thay bằng tình huống đã thực sự xảy ra ở lớp mình.
08 Ứng dụng trong dạy học
Tin học — biến, phép gán và biểu thức lôgic gặp nhau
Ba khái niệm rời rạc trong sách bỗng phải làm việc cùng nhau ở đây: biến để
nhớ, phép gán để cập nhật, biểu thức lôgic để quyết định. Và có một dòng rất
đáng dừng lại: truoc_do = dang_nhan nằm ở cuối vòng lặp. Hỏi cả
lớp xem chuyện gì xảy ra nếu chuyển dòng đó lên đầu.
Toán — đếm và sai số của phép đếm
Cho học sinh bấm 50 lần rồi so số máy đếm với số các em tự đếm. Thảo luận: nếu hai con số lệch nhau, ai sai? Máy đếm thừa vì dội tiếp điểm, hay người đếm sót vì mất tập trung? Đây là cửa vào rất tự nhiên cho khái niệm sai số của phép đo.
Hoạt động 1 — Xác định vấn đề. Thư viện trường muốn biết mỗi buổi có bao nhiêu lượt người vào, mà không phải cử người ngồi đếm.
Tiêu chí sản phẩm: đếm đúng ít nhất 19 trên 20 lượt thử; có đèn báo mỗi lượt để người dùng biết máy đã ghi nhận; và số đếm không bị nhảy khi không có ai đi qua.
Hoạt động 2 — Kiến thức nền. Trạng thái và sự kiện; phát hiện sườn; dội tiếp điểm; biến đếm.
Hoạt động 3 — Đề xuất và chọn phương án. Nút nhấn ở đây thay cho cảm biến thật. Cho các nhóm bàn xem sau này thay bằng cảm biến gì, và cảm biến đó có bị "dội" như nút không.
Hoạt động 4 — Chế tạo và thử. Lập trình, chạy thử 20 lượt, ghi lại số máy đếm được.
Hoạt động 5 — Báo cáo và điều chỉnh. Nhóm khác thử phá: bấm thật nhanh, giữ thật lâu, bấm nửa vời. Nhóm nào bị phá thành công thì chỉnh lại con số chống dội.
- Thêm nút thứ hai để đếm lượt ra, rồi tính số người đang ở trong phòng.
- Ghép với Bài 35 để hiện số đếm lên màn hình OLED thay vì in ra máy tính.
- Bài 5 dùng lại đúng ý phát hiện sườn này để làm nút đặt lại đồng hồ.
- Hướng thi Khoa học kĩ thuật: máy đếm lượt mượn sách, gửi số liệu về máy chủ cuối ngày.