GIẢI THUẬT CHỐNG NHIỄU CHO NÚT NHẤN
Nút nhấn là 1 giao diện người dùng quen thuộc nhất đối với những tất cả chúng ta. Có những nút có những tính năng rất quan trọng như Phóng tên lửa hạt nhân, bật dây truyền sản xuất. Nhưng có những nút chỉ thêm vào để cho đẹp mạch
Đối với nút phóng tên lửa hạt nhân chúng ta không thể thiết kế 1 cách cẩu thả được, như vậy sẽ ảnh hưởng tới rất nhiều mạng người. Và tuyệt đối không thể để bị nhiễu được. Hãy xem hình 1 để thấy mô hình nút nhấn chúng ta hay dùng.
Hình 1. Mô hình nút nhấn
Đây là mô hình nút nhấn hay dùng cho những bạn mới học, và với mô hình này bạn sẽ gặp nhiễu như hình 2.
Hình 2. Nhiễu trên tín hiệu nút nhấn
Đối với nút nhấn treo trở thì sẽ rất dễ nhiễu, nếu giảm giá trị của trở thì sẽ giảm được nhiễu, nhưng sẽ dẫn đến tăng công suất tiêu thụ điện của mạch điện. Do đó ta có 1 cách chống nhiễu bằng phần cứng như Hình 3.
Hình 3. Chống nhiễu nút nhấn bằng tụ
Thường thì tụ 100nF sẽ được dùng để chống nhiễu nút nhấn. Đối với cường độ nhiễu nhỏ có thể dùng tụ 10nF, còn nhiễu nhiều quá có thể dùng tụ 10uF.
Sau khi dùng mô hình như hình 3 ta có tín hiệu nút nhấn như hình 4
Hình 4. Hết nhiễu sau khi lọc nút nhấn bằng tụ
Nhìn Hình 4 là kết quả sau khi lọc nhiễu bằng tụ. Có vẻ rất lý tưởng, nhưng thực tế trong 1 số môi trường khắc nghiệt, vẫn sẽ còn nhiễu, và do nút nhấn thực hiện công việc quá quan trọng nên ta sẽ phải thêm lọc nhiễu bằng phần mềm.
Bình thường khi nhấn 1 nút nhấn rồi nhả ra sẽ mất ít nhất khoảng 10ms, chậm thì khoảng 1s. Chậm nữa thì infinity luôn. Còn nhiễu nút nhấn thì chỉ diễn ra trong khoảng thời gian tính bằng ns. Do đó ta có thể dùng thời gian để xem 1 giá trị logic có phải nhiễu hay không. Xem lại Hình 2 ta biết ngay thời điểm nào là nhấn nút và thời điểm nào là nhiễu. Vậy thì tôi nghĩ ra 1 cách đơn giản là tôi sẽ tính giá trị logic trung bình của nút nhấn theo thời gian. Cứ khoảng 50ms thì tôi tổng trung bình 1 lần. Đối với mô hình nút nhấn như trên thì mức logic mặc định (logic không nhấn nút ) = 1. còn mức logic nhấn nút = 0. Mức 0 thì gần như không thể nhiễu, chỉ còn mức 1 hay bị nhiễu nhảy xuống 0. Nên tôi sẽ tính trung bình mức logic 1, cứ mỗi 1 vòng quét của hệ thống tôi sẽ lưu lại giá trị của nút nhấn, sau 64 lần lưu lại giá trị, tôi sẽ tính xem có bao nhiễu giá trị mức 1, nếu % số giá trị mức 1 > 70% thì sẽ kết luận nút nhấn đang ở mức 1. Còn không thì sẽ kết luận là đang ở mức 0. Cách đọc như hình 5.
Hình 5. Phân khoảng thời gian tính trung bình mức 1 logic nút nhấn
Như trong hình 5. Tôi phân khoảng thời gian thành rất nhiều khoảng bằng nhau, H1, H2,… H7. Trong mỗi khoảng đấy có 64 lần đọc nút nhấn được lưu lại kết quả để tính trung bình. Cứ mỗi lần đọc đủ 64 lần nút nhấn sẽ tính trung bình 1 lần, ta có thể ước tính ra giá trị trung bình logic 1 của từng khoảng như sau:
+ H1: >95% và < 100% Logic 1
+ H2: >95% và < 100% Logic 1
+ H3: < 40% Logic 1
+ H4: 0% Logic 1
+ H5: > 70% Logic 1
+ H6: > 95% và < 100% Logic 1
+ H7: > 95% và < 100% Logic 1
Cứ mỗi lần đọc dữ liệu nút nhấn mới, tôi sẽ xóa đi kết quả đọc nút nhấn cũ nhất, giữ lại 63 kết quả đọc nút nhấn gần nhất cộng với kết quả lần đọc này và tính ra trung bình. Tôi ước tính cường độ nhiễu lớn nhất trong ứng dụng tôi gặp có thể lên đến 30%. Do đó khi trung bình mức logic > 70% thì tôi sẽ kết luận là đang ở mức 1. Còn < 70% thì tôi kết luận mức 0.
Tại sao tôi lại chọn con số 64 lần đọc nút nhấn để tính trung bình. Vì để tiện cho thuật toán, Tôi dùng 1 biến 64bit để lưu dữ liệu của 64 lần đọc nút nhấn, cứ mỗi lần đọc nút nhấn mới tôi chỉ cần dịch bit sang trái 1 bit và thêm dữ liệu mới vào. Như thế rất tiện. Nếu chip của bạn không cho phép khai báo biến 64bit thì bạn có thể chọn biến 32bit và chỉ lấy trung bình 32 lần. Hoặc nếu bạn vẫn muốn lấy trung bình 64 lần hoặc 100 lần thì bạn có thể dùng mảng để lưu dữ liệu.
Code của tôi sẽ như sau:
uint16_t ReadCnt = 0; uint64_t Log = 0; uint8_t ButtonValue = 0; // Giá trị nút nhấn sau khi lọc bằng phần mềm void ReadGInputLoop(void) { char InputValue = ReadGInput(); // Lấy giá trị nút nhấn hiện tại Log = Log << 1;// Dịch trái biến log để xóa giá trị nút nhấn cũ nhất if (InputValue > 0) Log |= 1; if (ReadCnt < 64) ReadCnt ++; else { float Logic1Percent = 0; uint16_t Logic1Cnt = 0; for (int i = 0; i< 64; i++) if (((Log >> i)&&0x01) == 1) Logic1Cnt ++; Logic1Percent = (Logic1Cnt*100.0 / 64.0) ; if (Logic1Percent > 70 ) ButtonValue = 1; else ButtonValue = 0; } }
OK. Mình sẽ đặt hàm ReadGInputLoop() trong while(1) của hàm main. xong chỉ lúc cần chỉ lấy giá trị ButtonValue để dùng thôi. Cách này thì nó không làm trễ hệ thống, không có delay nên cũng rất phù hợp để dùng.
Ok. Chúc các bạn thành công !
BÀI VIẾT CÙNG CHỦ ĐỀ