האם אפשר לנסח אלגוריתם לקריאה של מקודד סיבובי, שפותר לגמרי את בעיית ה-Bounce מצד אחד, ומצד שני לא נופל בפח של היפוך כיוון הסיבוב כפי שקרה לאלגוריתם הקודם שלי?
כשהסתכלתי מקרוב על המקרה ש"הפיל" את הניסיון הקודם שלי, הבנתי שהוא מייצג משהו רציני, כזה שאי אפשר לשים עליו טלאי קטן וזהו. בעיית ה-Bounce חזרה והרימה את ראשה המכוער, ונראה היה שהדרך היחידה להתגבר עליה עכשיו הייתה להיעזר בטיימר, מתוך הנחה שמשתמש אנושי לא יוכל להפוך את כיוון הסיבוב של המקודד תוך פחות ממספר אלפיות שניה. כלומר, אם תוך חמש (נניח) אלפיות שניה לא מגיע שום מידע חדש מהמקודד, אפשר להניח שהסיגנל הבא שיגיע ממנו יהיה לגיטימי, ולא שאריות Bounce מסיבוב קודם.
ייתכן שזה היה עובד, אבל נראה לי לא נכון "לבזבז" טיימר על כזה דבר, במיוחד מכיוון שאני עובד על פרויקט שמבוסס על ATtiny שאין לו יותר מדי טיימרים פנויים. לקחתי נייר ועפרון, ציירתי את כל דפוסי הפלט שיכולתי לחשוב עליהם וניסיתי לנסח כללים שיתאימו לכולם. ואז הבנתי פתאום דבר מעניין: הפתרונות הנפוצים לקריאת מקודד סיבובי, כמו גם הפתרון הקודם שלי, סובבים תמיד סביב העלייה של פלט A מהמקודד. ברגע שהפלט הזה עובר מ-LOW ל-HIGH, מיד אנחנו מנסים להחליט אם הוא שייך לסיבוב עם או נגד כיוון השעון. אבל למה חייבים לעשות את זה מיד? למה לא לחכות עם ההחלטה הזו עד לקבלת אישוש (או מידע סותר)?
כאן, אני מוכרח להודות, עשיתי טעות והנחתי ששינוי המתח הבא בתור יהווה קריטריון מספיק טוב להחלטה. זה עבד בצורה כמעט מושלמת, וכבר כמעט השלמתי ופרסמתי את הפוסט הזה, עד שהבחנתי בעוד מקרה קצה בעייתי: משתמש שהופך את כיוון הסיבוב אחרי שינוי המתח השני, אבל לפני השלמת המחזור של ארבעה שינויים. זה לא קורה הרבה, אבל זה אפשרי – והרסני.
הפתרון העקרוני, אם כן, הוא לא להשתמש באירוע נקודתי ספציפי כצומת החלטה, אלא להתמקד רק בשרשרת ההופעה של אירועים. הקוד צריך לעקוב אחרי סדר ההופעה של האירועים השונים, וכאשר הסדר הזה תואם את הסדר הצפוי במסגרת צעד של המקודד, לרשום זאת כצעד בפועל.
.הנה, שוב, תמונה של רצף לגיטימי:
אם נקרא לערוץ העליון A ולתחתון B, ונקרא למתח גבוה 1 ולנמוך 0, אפשר יהיה לתאר את הרצף כך:
A0B0 -> A1B0 -> A1B1 -> A0B1 -> A0B0
אפשר לחשוב על זה כעל מכונת מצבים עם ארבעה מצבים שונים (האחרון הרי זהה לראשון). תכונה שימושית של המקודד הסיבובי היא שכל פעם רק אחד מהערוצים יכול להשתנות, אף פעם לא שניהם ביחד; במילים אחרות, המעבר בין מצבים הוא תמיד בצעד יחיד קדימה או אחורה, לא יותר (ומה שהכי חשוב, זה כולל את שינויי המצבים שמקורם ב-Bounce!). בכל פעם שהפלט מהמקודד משתנה, אני יכול לבדוק באיזה כיוון הוא השתנה בשרשרת המצבים הזו, ולהגדיל או להקטין באחד מונה בתוכנה. ברגע שהמונה יגיע לארבע, או למינוס ארבע, סימן שהמקודד השלים תנועה אחת בכיוון השעון, או נגד כיוון השעון, בהתאמה – וזה לא משנה כמה קפיצות וכמה התלבטויות של המשתמש היו בדרך!
הדבר היחיד שנותר לעשות הוא לוודא, בתחילת התוכנית, שהמקודד נמצא במצב נייטרלי (LOW בשני הערוצים), ואם לא – להתאים את הפרמטרים בתוכנה. זה חשוב כי המשתמש מצפה להתאמה בין המשוב המכני-פיזי של צעדי המקודד לבין תגובת התוכנה. אם מתחילים לספור ממצב ביניים, ההתאמה הזו תשתבש.
הקוד שמבצע את כל זה הוא קצר ופשוט, יחסית. במקרה שלי, הוא נכתב לפונקציית פסיקה כללית של שינוי פינים, שעוקבת אחרי פיני הקלט PB0 ו-PB1 של ה-ATtiny44A.
הניסיון הראה שהקוד הנ"ל עובד בצורה מושלמת גם אם לא מחברים למקודד קבלים ל-Debounce. יחד עם זאת, לא כדאי לוותר עליהם לגמרי. עדיף לכלול במערכת קבלים קטנים לסינון של קפיצות המתח המהירות ביותר, משתי סיבות. ראשית, אם כל קפיצה זעירה תעורר לנו את הפסיקה, אז כל עוד ה-Bounce בעיצומו הפסיקה תרוץ שוב ושוב ו"תחנוק" את הקוד הראשי של המיקרו-בקר. מוטב שהחומרה תסנן את הקפיצות המהירות-מאד, ואילו התוכנה תדאג להבחין בין האות המשמעותי לבין הקפיצות הארוכות.
בנוסף, אם התוכנה שלנו כוללת הרבה פסיקות אחרות (למשל לתקשורת או לטיימרים), הפסיקות האחרות עלולות לעכב את פונקציית הפסיקה שקשורה למקודד, ולגרום לשיבושים בקריאת הפלטים ממנו.
האם מצאתם טעות בשיטה שלי? או שיש לכם שיטה יעילה יותר להגיע לאותן תוצאות? קדימה, בתגובות…
הנה הסבר מתוך פרוייקט של בודק טרנזיסטורים מבוסס AVR על קריאת rotary encoder בשיטה הנ\"ל (ללא קבלים):
https://github.com/svn2github/transistortester/blob/master/Doku/tags/english/ttester_eng112k.pdf
עמודים 14-15.
יש גם קוד ב-SVN הנ\"ל.