בחינה של האותות שמתקבלים ממקודד סיבובי פשוט (Rotary encoder) הראתה שכאשר פיני הפלט שלו עוברים בין מצבים, קפיצות המתח האקראיות (Bounce) יכולות להיות קצרות מאוד או ארוכות מאוד, בהתאם למהירות הסיבוב של המקודד – עד כדי אי יכולת להבדיל בין קפיצה כזו לבין אות לגיטימי. האם אפשר ליצור קוד שיתגבר על כך וייתן לנו קריאה מדויקת של הסיבובים על פני כל טווח המהירויות?
מבוא
המקודד הסיבובי הוא רכיב שמתרגם סיבוב של ציר מכני למיתוג של אותות חשמליים, שמאפשרים להבחין בסיבוב ולדעת מה הכיוון שלו. המקודדים הסיבוביים בנויים במגוון טכנולוגיות, גדלים ורזולוציות, ויש גם מקודדים "אבסולוטיים" שמספקים מידע על זווית הציר. בפוסט זה נעסוק רק במקודדים המכניים הפשוטים.
למקודדים כאלה יש בדרך כלל שלוש "רגליים" שמוקדשות לעניין הסיבוב: לאחת אנחנו מחברים את מתח הייחוס (למשל GND או 5V), והשתיים האחרות, שנקראות לרוב A ו-B, מתחברות ומתנתקות מהייחוס הזה בסדר ידוע מראש כאשר הציר של המקודד מסתובב. אם נחבר לקווי הפלט נגדי pull-up או pull-down מתאימים (בהתאם למתח הייחוס), נסובב את הציר ונתבונן בהם בסקופ או בלוג'יק אנלייזר, נראה שני גלים ריבועיים בהפרש פאזה של 90 מעלות:
כבר במבט ראשון אפשר להבחין בדפוס. נניח שהגל העליון הוא A והתחתון הוא B. בכל פעם ש-A עולה מ-LOW ל-HIGH, אם B הוא בעצמו HIGH אז זה סימן שהמקודד מסתובב עם כיוון השעון. נהפוך בדמיון את ציר הזמן ונראה שבאותו אופן, אם B הוא LOW בזמן העלייה של A אז המקודד מסתובב נגד כיוון השעון. מעולה! אנחנו יודעים לקרוא את המידע מהמקודד!
קפיצות בלתי פתירות
אבל כמו בכל מתג מכני, יש לנו בעיה של Bounce. הנה תקריב של גל זהה לזה שבתמונה הקודמת:
אנחנו רואים שהמתח ב-A קפץ פעמיים לפני שהוא התייצב על HIGH – וזה עוד מקרה רגוע יחסית. הפרשי הזמנים בין הקפיצות (בתמונה, 2.25 מיקרושניות בין הקפיצה הראשונה לשניה) ארוכים מספיק כדי שמיקרו-בקר מודרני יתפוס את הקפיצות כעליות מתח נפרדות, והוא יספור שלושה צעדים במקום צעד יחיד.
אז מה הבעיה, אתם שואלים – הרי כל תינוק יודע לעשות Debounce, לא? אפשר להוסיף קבל קטן לכל קו פלט, או להוסיף איזושהי השהייה קטנה בתוכנה, והכול יסתדר, נכון?
בואו נסתכל על תקריב של פלט נוסף, מאותו מקודד בדיוק:
שימו לב לפרק הזמן הממושך שבין הקפיצות: 0.96 אלפיות שניה, פי כמה מאות מההפרשים שראינו קודם (וכן, זה Bounce אמתי, לא גלים לגיטימיים נפרדים). הסיבה להבדל היא שכאן סובבתי את המקודד בכוונה באיטיות רבה, ואילו בתמונה הקודמת סובבתי אותו בכוונה מהר ככל האפשר. אם תסתכלו עוד יותר למעלה תראו שבסיבוב מהיר, האורך של פולס לגיטימי יכול להיות בסביבות 1.6 אלפיות השניה – אותו סדר גודל כמו ה-Bounce של סיבוב איטי.
המשמעות של זה ברורה: אם קפיצות מתח ופולסים לגיטימיים יכולים להיות באותו אורך, שום Debounce בעולם לא יעזור: או שהוא יסנן בטעות סיגנלים לגיטימיים, או שהוא יעביר בטעות קפיצות ורעש. ותאמינו או לא, כמעט כל מי שראיתי שעובד עם מקודד כזה נופל במלכודת. ככל שהמקודד איכותי יותר כך הבעיה חריפה פחות, ובמצבי שימוש רגילים משכי ה-Bounce תואמים פחות או יותר את ה-Debounce כך שהעניין לא בא לידי ביטוי. אבל קחו פרויקטים כאלה וסובבו את המקודד בהם מהר או לאט במיוחד, ויש סיכוי טוב שתוכלו לראות בעצמכם את הפספוסים.
אז מה עושים?
ראשית, בודדתי את רצף הפלט המינימלי של המקודד. בדגם שבו השתמשתי אפשר להרגיש באצבעות כל צעד, דרך שינויים בהתנגדות הפיזית. ככה נראה הפלט של צעד יחיד:
כל מה שייצא מהמקודד יורכב מרצפים כאלה בדיוק, מהירים יותר או פחות (ובכיוון הפוך אם הסיבוב הוא בכיוון ההפוך). אם אצליח לזהות אחד, אצליח לזהות את כולם. דמיינתי קפיצות סביב כל אחד משינויי המתח. איזה דפוס אפשר בכל זאת לראות?
הדבר הראשון שקפץ לי לעין היה זה: בין כל שני שינויי מתח לגיטימיים בערוץ מסוים, חייב לבוא לפחות שינוי מתח אחד בערוץ האחר. מכאן נובע כלל פשוט – נתייחס לשינוי ב-A רק אם B השתנה מאז הפעם האחרונה בה התייחסנו לשינוי ב-A. הכלל הזה קל לניסוח אבל קצת יותר מורכב למימוש, ונדרשו לי כמה וכמה ניסיונות עד שהתגברתי על כל המכשולים והבאגים הקטנים. מאותו רגע זה עבד מעולה – פרט למצב אחד שלא חשבתי עליו: הצעד הראשון אחרי היפוך של כיוון הסיבוב. הפלטים במצב כזה נראים ככה:
בניגוד לתצפיות ולהשערות שלי מקודם, יש לנו כאן שני שינויי מתח לגיטימיים בערוץ התחתון, שאין ביניהם שום שינוי מתח בערוץ העליון. זה אמנם גורם לפספוס של צעד אחד בלבד, אך זהו דווקא צעד קריטי כשמנסים לכוונן במדויק ערך בעזרת המקודד. המשתמש ירגיש את הפספוס. ניסיתי למצוא טריק שיאפשר לי לזהות את המצב החריג הזה במסגרת השיטה הקיימת ולהתגבר עליו, אך זה היה בלתי אפשרי. חזרתי למשבצת הראשונה.
שאלה: הרי Rotary encoder נמצא איתנו לא מהיום, ונעשה בו שימוש גם במכשירים מדוייקים כמו אוסילוסקופים וציוד רפואי, והכל עובד די בסדר (עד שהחומר הסמיך באנקודר מתחיל ליזול פנימה ולשבש את פעולתו..) אז אולי מצאו פתרונות כלשהם לבעיה?
בציוד יקר כמו סקופ איכותי או מכשיר רפואי, היצרן כנראה יכול להרשות לעצמו מקודדים איכותיים הרבה יותר, עם bounce מזערי שאפשר לנטרל לגמרי עם קבלים – וגם אז, סביר להניח שאף משתמש לא מסובב אותם בפראות כמו שעשיתי כאן.
מעבר לזה, אני לא חושב שגיליתי את אמריקה – רוב הסיכויים שהשיטה שלי (שאתאר בפוסט הבא) כבר הומצאה ויושמה על ידי אחרים. היא חדשנית רק ביחס למה שמוצאים בחיפוש רגיל בגוגל 🙂