Bài 11 · Công nghệ · Tin học · Toán
Cần điều khiển joystick hai trục
hai biến trở đặt vuông góc, và bài toán vùng chết
Joystick trông như một linh kiện mới, nhưng bên trong chỉ là hai biến trở của Bài 12 đặt vuông góc nhau cộng một nút nhấn của Bài 2. Cái mới nằm ở chỗ khác: cần điều khiển không bao giờ về đúng giữa, và chương trình phải sống chung với điều đó.
01 Mục tiêu và yêu cầu cần đạt
Bài 12 đọc một biến trở. Bài này đọc hai, cộng thêm một nút — nghe như chỉ là phép cộng. Nhưng có một thứ hoàn toàn mới: số đọc không bao giờ đứng yên, kể cả khi không ai chạm vào cần. Xử lí sự không đứng yên đó là nội dung chính.
Sau bài này học sinh làm được
- Giải thích joystick chỉ là hai biến trở đặt vuông góc cộng một nút nhấn.
- Đo được dao động tự nhiên của số đọc khi để yên cần.
- Chọn được vùng chết từ số liệu đo chứ không chép của nhóm khác.
- Viết hàm đổi một số đọc thành ba trạng thái −1, 0, 1.
- Nhận biết bốn hướng và trạng thái nhấn cần.
- Toán 10 — toạ độ trên mặt phẳng, khoảng giá trị, sai số ngẫu nhiên.
- Tin học 10 — hàm có giá trị trả về, cấu trúc rẽ nhánh nhiều nhánh.
- Công nghệ 10 — thiết bị nhập dạng tương tự.
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ở nắp ra chẳng có gì mới
Hai trục quay vuông góc, mỗi trục xoay con chạy của một biến trở. Đẩy cần đi đâu thì hình chiếu của chuyển động lên mỗi trục làm biến trở tương ứng đổi giá trị. Ấn cần xuống thì chạm một nút nhấn nằm dưới.
Tức joystick là Bài 12 nhân đôi cộng Bài 2 — phần lớn linh kiện "phức tạp" hoá ra chỉ là thứ đã biết ghép lại.
Vì sao số đọc không bao giờ đứng yên
Để yên cần mà in liên tục, số nhảy quanh một giá trị: 1890, 1902, 1887, 1895… Ba nguyên nhân chồng lên nhau — lò xo không đưa cần về đúng một điểm, tiếp xúc trong biến trở có điện trở thay đổi, và bản thân ADC cũng có nhiễu.
So sánh trực tiếp với một mốc thì kết quả chớp qua lại giữa "trái" và "giữa" hàng chục lần mỗi giây. Đó không phải lỗi lập trình mà là tính chất của mọi phép đo thật.
Dùng hai mốc thay vì một: dưới mốc thấp là một hướng, trên mốc cao là hướng ngược, ở giữa coi như đứng yên. Khoảng giữa ấy phải rộng hơn dao động tự nhiên của số đọc.
Cách chọn: để yên cần vài giây, ghi giá trị nhỏ nhất và lớn nhất, lấy nửa hiệu rồi cộng chút dự phòng. Đây là lần đầu học sinh đo để chọn tham số thay vì nhận một con số cho sẵn.
Chân module ghi +5V vì vốn dành cho bo 5 V. Cấp 5 V thì đầu ra
hai biến trở có thể lên 5 V, vượt mức chân ESP32 chịu được và làm hỏng chân.
Cấp 3V3 thì số đọc trải đúng 0–4095 mà không có rủi ro nào.
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 |
| Module joystick hai trục | Loại năm chân GND, +5V, VRx, VRy, SW | 1 · 12–18k |
| LED 5 mm và điện trở 220 Ω | Để thấy kết quả ngay trên mạch | 1 · 700đ |
| Dây nối cái–đực | Module joystick dùng chân cắm đực | 5 · 3k |
04 Lắp mạch và sơ đồ nối dây
| Khối | Nối dây | Nguồn |
|---|---|---|
| Trục X | VRx → GPIO34 | Bắt buộc nhóm ADC1 |
| Trục Y | VRy → GPIO35 | Bắt buộc nhóm ADC1 |
| Nút cần | SW → GPIO32 | Dùng điện trở kéo lên trong chip, nhấn đọc ra 0 |
| Nguồn module | +5V → 3V3 của ESP32 · GND → GND | Cấp 3V3 chứ không phải 5 V — xem hộp cảnh báo |
Module joystick cắm năm dây vào ESP32, kèm LED trên breadboard. Nhãn VRx, VRy, SW trên module đọc được rõ.
Khung giữ chỗ — thay bằng ảnh chụp thật khi cóMặt cắt joystick: hai biến trở đặt vuông góc, mỗi cái gắn với một trục quay của cần; bên dưới là một nút nhấn thường mở.
Khung giữ chỗ — thay bằng ảnh chụp thật khi cóMặt phẳng toạ độ 0–4095 theo cả hai trục, ở giữa vẽ một hình vuông tô nhạt ghi "vùng chết", bốn phía ngoài ghi TRÁI, PHẢI, LÊN, XUỐNG.
Khung giữ chỗ — thay bằng ảnh chụp thật khi cóNăm dây
GNDcủa module →GNDcủa ESP32.+5Vcủa module →3V3của ESP32.VRx→GPIO34.VRy→GPIO35.SW→GPIO32.- LED:
GPIO23→ điện trở 220 Ω → chân dài, chân ngắn →GND.
ESP32 có hai nhóm ADC. Nhóm thứ hai ngừng đọc được khi Wi-Fi bật,
mà runtime của lộ trình này giữ Wi-Fi bật suốt để nhận lệnh nạp mã. Vì vậy cả hai
trục đều phải nằm trong nhóm ADC1 — tức các chân từ GPIO32 đến
GPIO39. Cắm nhầm sang chân khác thì số đọc đứng im ở 0 và rất khó đoán ra lí do.
Hai chân này không có mạch ra và cũng không có điện trở kéo lên bên trong. Với joystick thì không sao vì ta chỉ đọc. Nhưng đừng thử dùng chúng cho LED ở bài khác — sẽ không sáng và không có thông báo lỗi nào.
05 Mã nguồn
Toàn bộ mã trong bài chạy trên MicroPython 1.28 cho ESP32. Số đọc giữa của mỗi module một khác — mọi con số trong bài chỉ là ví dụ, học sinh phải đo lại trên chính bo của mình.
Bước 0 — chỉ in hai số# ------------------------------------------------------------
# Bai 11 - Buoc 0: chi in hai so, chua ket luan gi
# ------------------------------------------------------------
from machine import ADC, Pin
import time
x = ADC(Pin(34)); x.atten(ADC.ATTN_11DB)
y = ADC(Pin(35)); y.atten(ADC.ATTN_11DB)
while True:
print(x.read(), y.read())
time.sleep_ms(200) Đẩy cần đi bốn phía và quan sát. Học sinh sẽ tự rút ra: một số đổi khi đẩy ngang, số kia đổi khi đẩy dọc, đẩy chéo thì cả hai cùng đổi. Cả lớp cũng sẽ nhận ra ngay chuyện số không đứng yên — và đó là câu hỏi mở đầu tự nhiên nhất cho mục Lí thuyết.
Chương trình đo — tự tìm vùng chết cho bo của mình# Do vung chet: de yen can 5 giay roi doc lai bang so lieu
from machine import ADC, Pin
import time
x = ADC(Pin(34)); x.atten(ADC.ATTN_11DB)
nho_nhat = 4095
lon_nhat = 0
moc = time.ticks_ms()
while time.ticks_diff(time.ticks_ms(), moc) < 5000:
v = x.read()
if v < nho_nhat:
nho_nhat = v
if v > lon_nhat:
lon_nhat = v
time.sleep_ms(10)
print("Nho nhat:", nho_nhat)
print("Lon nhat:", lon_nhat)
print("Giua nen dat:", (nho_nhat + lon_nhat) // 2)
print("Vung chet toi thieu:", (lon_nhat - nho_nhat) // 2 + 50) Năm giây để yên cần, máy tự tính hộ giá trị giữa và vùng chết tối thiểu. Ghi lại hai con số này rồi chép vào chương trình chính. Mỗi nhóm sẽ có một cặp số khác nhau — đó là điều bình thường và rất nên nói rõ.
Chương trình chính — bốn hướng có vùng chết# ------------------------------------------------------------
# Bai 11 - Bon huong co vung chet
# ------------------------------------------------------------
from machine import ADC, Pin
import time
# ===== CẤU HÌNH =====
CHAN_X = 34
CHAN_Y = 35
CHAN_NUT = 32
GIUA = 1900 # so doc khi de yen - DO LAI tren bo cua minh
VUNG_CHET = 600 # rong hon dao dong tu nhien luc de yen
NGHI = 150 # ms
# ====================
x = ADC(Pin(CHAN_X)); x.atten(ADC.ATTN_11DB)
y = ADC(Pin(CHAN_Y)); y.atten(ADC.ATTN_11DB)
nut = Pin(CHAN_NUT, Pin.IN, Pin.PULL_UP)
def huong(gia_tri):
if gia_tri < GIUA - VUNG_CHET:
return -1
if gia_tri > GIUA + VUNG_CHET:
return 1
return 0
while True:
hx = huong(x.read())
hy = huong(y.read())
if hx == -1:
print("TRAI")
elif hx == 1:
print("PHAI")
elif hy == -1:
print("XUONG")
elif hy == 1:
print("LEN")
if nut.value() == 0:
print("NHAN CAN")
time.sleep_ms(NGHI)
Hàm huong() nén cả một dải 4096 giá trị xuống còn ba trạng thái. Đây
là một ý tưởng quan trọng: phần lớn chương trình điều khiển không cần biết con số
chính xác, chỉ cần biết "về phía nào".
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ử
Đo dao động tự nhiên của hai trục
Chạy chương trình đo hai lần, một lần cho trục X và một lần cho trục Y (đổi
Pin(34) thành Pin(35)). Ghi vào bảng.
| Trục | Nhỏ nhất | Lớn nhất | Giá trị giữa | Vùng chết tối thiểu |
|---|---|---|---|---|
| X | … | … | … | … |
| Y | … | … | … | … |
Đo bốn cực trị
Đẩy cần hết cỡ về từng phía và ghi số đọc. Bảng này cho biết dải làm việc thật của module, và thường không đối xứng — một chi tiết hay để bàn về sai số của linh kiện rẻ.
| Vị trí cần | Số đọc X | Số đọc Y | Nhận xét |
|---|---|---|---|
| Hết cỡ trái | … | … | … |
| Hết cỡ phải | … | … | … |
| Hết cỡ lên | … | … | … |
| Hết cỡ xuống | … | … | … |
| Để yên | … | … | … |
Thử vùng chết quá hẹp
Đặt VUNG_CHET xuống 20 rồi chạy lại, để yên cần và quan sát. Máy sẽ
in ra hướng liên tục dù không ai chạm vào. Cho học sinh thấy hiện tượng này trước
khi trả về giá trị đo được — nó biến vùng chết từ một quy tắc thành một nhu cầu.
Danh sách kiểm trước khi coi là xong
- Để yên cần thì không có dòng hướng nào được in.
- Bốn hướng đều nhận đúng và không lẫn sang nhau.
- Nhấn cần xuống thì in ra thông báo, thả ra thì thôi.
- Hai bảng số liệu đã điền đủ bằng số đo thật.
- Học sinh giải thích được vì sao vùng chết của nhóm mình khác nhóm bên.
07 Kinh nghiệm và cạm bẫy
Đến đây là lần đầu học sinh phải đo trước, chọn tham số sau. Nếu cả lớp chép chung một con số vùng chết, bài mất hẳn giá trị. Cách giữ được ý nghĩa: yêu cầu mỗi nhóm dán số đo của mình lên bảng, rồi cùng nhìn xem các nhóm khác nhau bao nhiêu.
- Một trục luôn đọc ra 0. Cắm vào chân ngoài nhóm ADC1, hoặc
cắm nhầm
VRyvào chân không phải ADC. - Hai trục đổi vai. Cắm ngược
VRxvàVRy; đổi lại hai dây là xong, không cần sửa mã. - Hướng bị ngược. Module xoay 180° so với hình. Đảo dấu so
sánh trong hàm
huong(), hoặc đơn giản là xoay module lại. - Nút cần không nhận. Quên
Pin.PULL_UP— chânSWkhông có điện trở kéo lên trên module.
Các 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
Toán — toạ độ và sai số ngẫu nhiên gặp nhau
Hai số đọc là một cặp toạ độ thật, và tập hợp các điểm đo được khi để yên cần là một đám mây quanh tâm. Học sinh có thể vẽ đám mây ấy lên giấy kẻ ô, rồi thấy vùng chết chính là hình vuông bao lấy đám mây. Khái niệm "sai số ngẫu nhiên" trở nên nhìn thấy được.
Tin học — hàm trả về giá trị
huong() là ví dụ gọn cho một hàm nhận vào một số và trả ra một trong
ba khả năng. Học sinh thường viết đi viết lại cùng một chuỗi if cho
cả hai trục; đây là lúc chỉ ra rằng gói nó thành hàm vừa ngắn hơn vừa dễ sửa hơn.
Hoạt động 1 — Xác định vấn đề. Lớp muốn làm một trò chơi nhỏ hiển thị trên máy tính, nhưng gõ bàn phím thì không có cảm giác điều khiển.
Tiêu chí sản phẩm: nhận đủ bốn hướng và nút bấm; để yên tay thì nhân vật phải đứng im hoàn toàn trong 30 giây; độ trễ cảm nhận được phải nhỏ.
Hoạt động 2 — Kiến thức nền. ADC và nhóm ADC1; vùng chết; hàm trả về; nhịp lặp ảnh hưởng tới độ trễ.
Hoạt động 3 — Đề xuất và chọn phương án. Hai tiêu chí đối chọi nhau: vùng chết rộng thì đứng im tốt nhưng cần phải đẩy xa mới ăn; nhịp lặp nhanh thì đỡ trễ nhưng in ra quá nhiều. Các nhóm phải tìm điểm cân bằng và giải thích được.
Hoạt động 4 — Chế tạo và thử. Đo, chọn tham số, thử đúng 30 giây để yên tay.
Hoạt động 5 — Báo cáo và điều chỉnh. Đổi tay cầm giữa các nhóm; nhóm nào để yên mà vẫn trôi thì phải đo lại chứ không được đoán.
- Ghép servo của cấp độ 6 để cần điều khiển quay được một cánh tay thật.
- Dùng độ lệch khỏi tâm làm tốc độ chứ không chỉ làm hướng — một trục analog thật sự.
- Ghép ESP-NOW ở cấp độ 7 để tay cầm điều khiển xe không dây.
- Hướng thi Khoa học kĩ thuật: tay cầm hỗ trợ người khó thao tác chuột.