כשמיקרו-בקרים ושבבים "מדברים" ביניהם דרך חוטים או קווי נחושת, התקשורת היא בדרך כלל הודעות בכיוון אחד כל פעם (Half-Duplex) על גבי אותו קו, כפי שנעשה למשל על SDA בפרוטוקול I2C, או הודעות בשני הכיוונים בו-זמנית (Full-Duplex) על גבי שני קווים נפרדים, כגון TX ו-RX בפרוטוקול UART. מבחינה חשמלית, אלה הפתרונות הקלים. עם זאת, יש דרכים להעביר מידע בשני הכיוונים בו-זמנית גם על גבי קו יחיד. הנה הדגמה מאולתרת.

רקע
הפוסט הזה התחיל בשיחה מקרית על עניינים טכניים: בלי להיכנס ליותר מדי פרטים, בחברה מסוימת החליטו שבמקום ארבעה חוטים בכבלי תקשורת (שני זוגות של Twisted-pair, אחד להודעות יוצאות ואחד לנכנסות), הם ישתמשו בשני חוטים בלבד. "קהל היעד" הוא שרתים שמתקשרים ביניהם במהירויות אדירות של מאות Gb/s, כך שבכל מקרה מדובר במעגלים מתקדמים ומסובכים, אבל השאלה היסודית נשארת זהה גם אם מדובר בביט אחד ויחיד של מידע מכל צד: אם ערך של ביט מוגדר כמתח מסוים על הקו, איך אפשר לייצג שני ביטים בו-זמנית בלי שיתנגשו זה בזה?
כשהתחלתי לחשוב על הנושא קצת יותר לעומק, נזכרתי שבעצם הבעיה נפתרה כבר לפני מאה שנים, פלוס-מינוס: הרי הטלפון הקווי, שבו השיחה היא כמובן Full-Duplex לגמרי, מבוסס על זוג חוטים בלבד. המעגל שאחראי לקסם הזה נקרא Telephone Hybrid, והוא נעזר בשני טריקים: הראשון הוא ייצוג המידע על ידי מתח אנלוגי, לא רק High/Low, והשני הוא סינון, שבעזרתו המעגל מפריד בזמן אמת בין התרומה שלו-עצמו למתח שעל הקו לבין התרומה של הצד השני. להמחשה קצת פרימיטיבית, נניח ששני אנשים, א' ו-ב', רוצים למסור זה לזה מספרים, אבל הדרך היחידה להעביר מידע כלשהו היא לשים תפוזים בתוך ארגז משותף. אם א' שם 41 תפוזים, ואז סופר ורואה שיש בארגז 60 תפוזים, הוא צריך רק לזכור כמה הוא עצמו שם כדי להבין ש-ב' שם 19, ולהיפך.
העיקרון אולי נשמע פשוט, אבל המימוש לא: הוא דורש גם הבנה טובה באלקטרוניקה אנלוגית, וגם כלים/רכיבים מסוימים, שאת כולם אין לי כרגע. האם ניתן בכל זאת לממש תקשורת Full-Duplex על קו יחיד, בחומרה בלבד, באמצעים שיש לכל מייקר בהישג יד? ליתר דיוק, אני רוצה ששני מיקרו-בקרים "ידברו" ביניהם ב-UART רגיל לגמרי – דרך הפינים TX ו-RX – אבל שהמידע יעבור בפועל על גבי חוט יחיד (בהנחה שיש לי, בנפרד, אדמה משותפת).
שלב א': שליטה דו-צדדית במתח
המכשול הראשון שצריך לעבור הוא מציאת דרך שבה שני המיקרו-בקרים יוכלו לשנות, כל אחד באופן עצמאי, את המתח על הקו – מבלי לאבד מידע. מה זאת אומרת? היזכרו למשל בקו SDA בפרוטוקול I2C. הוא מוחזק במצב HIGH כברירת מחדל על ידי נגד Pull-up יחיד, ושני הצדדים (ה-Master וה-Slave) יכולים "למשוך" אותו לאדמה. אם צד אחד יודע שהוא עצמו לא מושך את הקו, אבל הקו בכל זאת ירד לאדמה, זה מוכיח שהצד השני כן מושך; ולהיפך, אם הקו נשאר HIGH, כל צד יכול לדעת שגם השני לא משך אותו לאדמה. לעומת זאת, אם צד אחד מושך לאדמה, אין לו שום דרך לדעת מה הצד השני עושה, כי הקו יישאר אדמה בכל מקרה. המידע הזה הלך לאיבוד.
המגבלות שהגדרתי לעצמי למעלה לא השאירו הרבה אופציות: הדרך היחידה שיכולתי לחשוב עליה להשפיע על המתח בלי לאבד מידע היא לעשות מניפולציה כזו או אחרת על מחלקי מתח. עברתי, בראש ועם נייר ועיפרון, על כל מיני דרכים להפעיל טרנזיסטורים כדי להוסיף או לנטרל נגדים שונים, ובסוף הבנתי שהפתרון הקל הוא דווקא מחלק המתח הפשוט ביותר: שני נגדים בטור, בעלי ערך שווה, אחד ליד כל משדר.
כזכור, כל משדר יכול להוציא מתח HIGH (נניח 5V) או LOW (אדמה, 0V). זה אומר שבאמצע הקו יכולים להתקבל שלושה מתחים שונים: 0, 5 או 2.5. אם הפלט של שני המשדרים זהה, המתח באמצע יהיה זהה לפלט. אם הפלט שונה, המתח באמצע יהיה 2.5. בכל קומבינציה אפשרית, כל צד עדיין יוכל לדעת מה הצד השני משדר! אבל איך קוראים את המידע הזה והופכים אותו לערך דיגיטלי עבור המקלט?
שלב ב': פענוח חד-משמעי
הדרך השגרתית לבדוק מי גדול או קטן יותר מבין שני מתחים היא באמצעות מגבר שרת (OpAmp), אך יש לנו בעיה עם המקרים שבהם המתחים בשני הצדדים זהים. מגבר שרת אינו יכול לזהות מצב "שווה", וכל הפרש זעיר ביותר בין המתחים, שמקורו ברעש אקראי או בכל סיבה אחרת, פשוט יוגבר וייתן לנו תוצאה שגויה. איזו השוואה תיתן לנו תוצאה וודאית יותר?
נדבר רגע ביחידות של וולט: נניח שאני משדר 5. המתח על הקו יכול להיות 5 או 2.5, בהתאם למה שהצד השני משדר. הסף האופטימלי להבדלה בין השניים באמצעות מגבר שרת הוא ממוצע של שני הערכים – 3.75. לעומת זאת, אם אני משדר 0, המתח על הקו יכול להיות 2.5 או 0, והסף האופטימלי הוא 1.25. מאיפה אפשר "להביא" את הספים האלה להשוואה עבור מגבר השרת?
גם כאן, הפתרון שקופץ לראש הוא מחלק מתח. כדי להגיע ליחס הרצוי (1.25 זה 25% מ-5, ו-3.75 זה 75%), הערך של אחד מהנגדים במחלק המתח צריך להיות גדול פי שלושה מזה של השני. האם זה אומר שצריך שני מחלקי מתח, אחד שנותן יחס של 25% ואחד שנותן 75%, ודרך כלשהי למתג ביניהם לפי הפלט הנוכחי של המשדר?
כיוון שחשבתי כל כל הרבה על מחלקי מתח ומיתוגים למיניהם, התשובה קפצה לי לראש די מהר. עוד קצת משחק עם מחשבוני נגדים אונליין ואקסל נתן לי ערכי נגדים אופטימליים (גם מבחינת המספרים וגם מבחינת הזמינות אצלי במלאי):
איך זה עובד? כאשר שמים במקביל נגד 20K ונגד 10K, ההתנגדות המשולבת היא 6.667K אוהם – שליש של 20K. הפלט של TX (שיכול להיות 5V או 0V) קובע אם הנגד 10K יהיה מקביל מבחינה חשמלית ל-20K העליון או ל-20K התחתון, ולפיכך, אם המתח ביציאה של מחלק המתח (הנקודה האדומה במרכז) יהיה 1.25 או 3.75 וולט. בדיוק הערכים האופטימליים שחיפשתי, ואפשר להזין אותם למגבר שרת יחיד לצורך השוואה עם המתח על קו התקשורת! אם המתח על הקו גדול מהמתח להשוואה, סימן שהצד השני משדר HIGH, ואם המתח על הקו קטן מהמתח להשוואה, סימן שהצד השני משדר LOW; אין אופציה אחרת.
שלב ג': הרכבה
חיפשתי במגירות ומצאתי שני מגברי שרת ישנים ופשוטים מדגם LMC6462BIN. הרכיב הזה כולל למעשה שני מגברים במעגל אחד – אני אשתמש רק באחד מהם בכל צד של המערכת. הרכבתי מגבר שרת ואת כל הנגדים הדרושים על מטריצה, ובעזרת ספק כוח, חוטי גישור וסקופ וידאתי שהמעגל אכן עובד ומגיב לפי התכנון.
לאחר מכן בניתי מעגל נוסף, זהה, עבור הצד השני, וחיברתי את שניהם ללוחות ארדואינו. לשני הלוחות העליתי תוכנה זהה, שמשדרת דרך UART רגיל את הערכים 0-255 במחזוריות, ומפעילה לד מקומי עם אות PWM בהתאם לערכים שהיא מקבלת.
חשוב לזכור שבספריית Serial של ארדואינו, הבייטים היוצאים והנכנסים מצטברים בחוצצים (buffers), וכאשר השידור איטי יותר מקצב ההצטברות, יכולים להיווצר כל מיני עיכובים. המשמעות, במקרה זה, היא שלא כל השידורים משני הצדדים יהיו באמת בו-זמנית, ויכולים בהחלט להיות זמנים שבהם רק צד אחד משדר.
תוצאות
המערכת השלמה עבדה בדיוק כמו שתכננתי – אבל רק בקצבי שידור נמוכים, כמו 1200 או 2400 באוד. בגלל ערכי הנגדים הגדולים-יחסית שבחרתי, הקיבוליות של המטריצה ואולי גם גורמים נוספים, זמני העלייה והירידה של האות היו ארוכים יחסית, ובקצב של 9600 באוד זה כבר הפריע לקריאת הביטים.
הנה צילום מסך מהסקופ, של המתח על קו התקשורת בקצב שידור 1200 באוד. קל מאוד לראות את שלושת המתחים שעליהם דיברתי – 0, 2.5, 5 – שנובעים משידור דו-סטרי.
האם כל הסיפור הזה מעשי לנו, כמייקרים? קשה לדמיין סיטואציה שבה באמת נצטרך להשתמש בטריק הזה, מצב שבו חיסכון בחוט או ב-Trace יחיד יהיה חשוב מספיק כדי להצדיק את הרכיבים הנוספים. זהו כן תרגיל מחשבתי טוב, אולי לאימון מתקדם בחשיבה על מחלקי מתח… האם אתם יכולים לחשוב על שימושים נוספים?
יש מערכות אינטרקום לבית של מגוון חברות, שמשתמשות (כנראה כדי להתאים לתשתית אנלוגית ישנה) בשני גידים בלבד. הם משמשים גם להזנת מתח ליחידה החיצונית, וגם מעבירים על זה וידאו, סאונד דו כיווני, ואף TCP/IP סטנדרטי.
לאחרונה שמעתי (לגמרי במקרה, אחרי שהתחלתי לכתוב את הפוסט הזה) על משהו שנקרא Single Pair Ethernet שדומה מאוד למה שתיארת, אבל עד כמה שהבנתי זו המצאה חדשה יחסית – משהו כמו עשר שנים. אין לי מושג איך זה עובד.