תקשורת UART: שלושה (ויותר) במחיר אחד

ספריית SoftwareSerial הכושלת מוכרת לכל משתמש ארדואינו שהיה צריך אי-פעם לעבוד בתקשורת UART מול שני רכיבים (או מול רכיב ומחשב). לעתים קרובות, מצב כזה מחייב בסופו של דבר מעבר למיקרו-בקר גדול ויקר בהרבה, שתומך ביותר מ-UART אחד בחומרה; אבל בתנאים מסוימים, מיקרו-בקרים חדשים ממשפחת PIC מציעים פתרון אחר, מתוחכם ואלגנטי.

בדיקה בשטח: ארבעה פיני TX ממודול UART יחיד
בדיקה בשטח: ארבעה פיני TX ממודול UART יחיד

למעשה, הבעיה עם UART מורכבת משני אספקטים. ראשית, יש את החומרה המוגבלת של המיקרו-בקרים. שנית, בניגוד לפרוטוקולים כמו I2C, ב-UART לא מוגדרות כתובות. כל רכיב מתנהג כאילו הוא היחיד במערכת, כך שאי אפשר לחבר בקלות מספר רכיבים לאותו פין TX או RX. פתרונות שידור וקליטה בתוכנה הם, כפי שרמזתי למעלה, בעייתיים – במיוחד אם אנחנו זקוקים לקצב תקשורת גבוה יחסית.

לא הקדשתי לעניין הזה מחשבה מרובה, עד שיום אחד מצאתי את עצמי מול פרויקט שאמור להיות מבוסס על מיקרו-בקר PIC16F18346 (בעל מודול UART יחיד בחומרה), ולכלול לא פחות משלושה רכיבים שונים שצריכים לתקשר איתו ב-UART.

לפעמים, מציאת פתרונות במערכות Embedded מזכירה את סדרות הטלוויזיה המשפטיות, שבהן עורך הדין הממולח מוצא איזושהי פרצה נסתרת בחוק ומצליח לחלץ בעזרתה את הנאשם ממצבים שנראים אבודים. כך לפחות הרגשתי כשהבנתי פתאום, שאמנם יש שלושה רכיבים שצריכים תקשורת UART, אבל הם לא צריכים אותה בו-זמנית. כלומר, אני לא באמת צריך שלושה ערוצי UART, אלא רק דרך לנתב את ה-UART היחיד לכל אחד משלושת הרכיבים!

הכיוון הראשוני שלי היה להיעזר בג'וק Multiplexer, שהומצא בעבר הרחוק בדיוק למטרות כאלה, אך לפני שהספקתי לפתח את הכיוון הזה נזכרתי בעוד משהו, שכתבתי עליו לא מזמן כאן. בדגם PIC אחר יש רגיסטר עם ביטים, שמאפשרים לנתב פונקציות פריפריאליות (כגון TX או RX) אל אחד משני פינים. האם ב-PIC של הפרויקט הנוכחי יש משהו דומה?

ניגשתי ל-Datasheet ומצאתי משהו טוב עוד יותר: מודול PPS (ראשי תיבות של Peripheral Pin Select). מודול פנימי זה מאפשר לנתב כמעט כל פונקציה פריפריאלית, לכמעט כל פין GPIO – ולעשות זאת, אם נרצה, בזמן אמת ובלי הגבלה. כל עוד התקשורת לא צריכה להיות בו-זמנית, אפשר לחבר שניים, שלושה, אפילו שמונה רכיבי UART לפינים שונים!

כבדיקה ראשונית, כתבתי קוד קצר שמשדר במחזוריות, באמצעות מודול ה-UART המובנה, את התווים "A" עד "D" בפינים 11 עד 14 של הג'וק (בהתאמה). את הפלט בדקתי על ידי כך שהחזקתי לוח ארדואינו במצב Reset (חוט מפין GND ל-RESET), שיתפתי אדמות וחיברתי חוט מפין TX של הארדואינו אל הג'וק המשדר, כל פעם לפין אחר. זה הפך את הארדואינו למעשה למתאם UART-to-USB, ובסריאל מוניטור אכן ראיתי את התווים הצפויים בתזמון הנכון. זה עובד!

הניתוב של פלט מפונקציה פריפריאלית לפין מתבצע מצד הפין, כלומר, אני מגדיר לפין ה-GPIO לאיזה פונקציה להתחבר, ולא להיפך. זה עורר מיד שאלה מעניינת: מה יקרה אם אגיד לכל ארבעת הפינים להתחבר ל-TX? לשמחתי, כולם פשוט התחילו לשדר את אותו המידע. אז אפילו זה אפשרי. הבעיה בפרויקט שלי נפתרה בלי ג'וק יקר יותר, במספר שורות קוד פשוטות – ראו למטה – ובלי שום רכיבים חיצוניים.

void UARTSetTXChannel(const uint8_t ch) {
    
    RB6PPS = (0 == ch) ? 0x14 : 0x00;
    RB5PPS = (1 == ch) ? 0x14 : 0x00; 
    RB4PPS = (2 == ch) ? 0x14 : 0x00;
    RC2PPS = (3 == ch) ? 0x14 : 0x00;
    
}

כשכתבתי את הפוסט ההוא על הקצאת פינים דינמית, עדיין לא הבנתי עד כמה התכונה הזו שימושית. עכשיו, כמאמר הקלישאה, קשה לי להבין איך הסתדרתי עד היום בלעדיה. יחד עם זאת, חשוב לזכור שזו תכונה חדשה ונדירה יחסית, במיקרו-בקרים חדשים (ה-Datasheet הראשון של ה-PIC16F18346 הוא מאפריל 2016), וזה אומר סיכוי גדול יותר לבאגים ותקלות בסיליקון באופן כללי. סתם לדוגמה, בדף ה-Errata של המיקרו-בקר החדש ורב העוצמה PIC18F27K40 מצוין שזיכרון ה-Flash שלו לא מחזיק מעמד 10K מחזורי כתיבה כפי שמובטח ב-Datasheet, אלא רק 1000….

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *