תקשורת UART: הסבר מפורט

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

רקע והגדרות כלליים

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

פרוטוקול UART הוא דיגיטלי, טורי, דו-סטרי וא-סינכרוני:

  • דיגיטלי, כיוון שיחידת המידע הבסיסית ביותר בו היא ביט, שערכו ("1" או "0") נקבע על ידי מתח חשמלי גבוה או נמוך בלבד. אין משמעות לערכי ביניים.
  • טורי, כיוון שהביטים מגיעים בזה אחר זה לפי התור, ואין מידע נוסף שעובר בו-זמנית באותו כיוון שידור. עם זאת, הוא-
  • דו-סטרי – הוא כולל שני קווי מידע, אחד לכל כיוון שידור, ובכיוונים שונים כן יכול לעבור מידע בו-זמנית. התקשורת לא חייבת להיות דו-סטרית (אפשר למשל רק לשלוח פקודות מרכיב א' לרכיב ב'), אך היכולת קיימת תמיד בחומרה.
  • ולסיום, הוא א-סינכרוני, שזה אומר שני דברים. ראשית, לפי ההגדרה הפורמלית, אין שעון אוניברסלי שקובע עבור שני הצדדים מתי יעבור מידע (בסגנון "א' ישלח ל-ב' בייט אחד כל שעה עגולה"). שנית, בהגדרה מצומצמת יותר, הצד המשדר אפילו לא "אומר" לצד הקולט מתי כל ביט חדש מגיע, או – במילים אחרות – הוא לא מוציא שום אות שעון ושום מידע של תזמון חוץ מהביטים עצמם. הוא פשוט מתחיל את השידור, ומאותו רגע זו האחריות של הצד הקולט לבדוק מה קורה בקו ולהסיק מסקנות.

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

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

מימוש חשמלי

איך תקשורת UART מתבצעת בפועל? אנחנו מתחילים מ"קו", בעצם מוליך חשמלי כלשהו שמחבר את הצד המשדר לצד הקולט, והמשדר (TX) אחראי להחזיק אותו במתח גבוה. כמה זה גבוה? תלוי במערכת עצמה, במיקרו-בקרים מודרניים זה יהיה בדרך כלל 5 או 3.3 וולט. אנחנו נקרא לזה פשוט HIGH. תיאורטית, המתח הזה מאפשר לנו לדעת, גם בזמן שאין שום שידור, שהקו עצמו תקין ולא כורסם על ידי עכבר או משהו. אבל הבדיקה הזו לא מובנית בפרוטוקול או בחומרה.

כיוון ש"ברירת המחדל" של הקו היא HIGH, והתקשורת היא כאמור דיגיטלית, הדרך היחידה להעביר מידע היא להוריד את הקו זמנית ל-LOW, מתח 0V (אדמה). אבל כמו שציינתי קודם, מתח LOW מציין ביט שערכו "0". מה לעשות אם המידע שאני רוצה להעביר מתחיל דווקא ב-"1"?

מימוש לוגי – מסגרת וזמנים

כדי לפתור את הבעיה הזו המציאו מה שנקרא "ביט התחלה" (Start bit), שהוא תמיד "0", אבל הוא לא חלק מהמידע עצמו אלא חלק מהמסגרת החיצונית (frame) שבתוכה המידע נשלח.

משך הזמן של הביט הזה, ושל כל הביטים האחרים בשידור, חייב להיות ידוע ומוסכם על שני הצדדים. לדוגמה, אם צריך לתקשר ב-UART עם מודול שעובד ב-9600 באוד (baud, שזה אומר פחות או יותר "ביטים לשנייה"), אז כל ביט צריך להימשך 104.17 מיליוניות השנייה (מיליון לחלק ל-9600). הדיוק לא חייב להיות מושלם, אך כמובן, ככל שהסטייה תהיה גדולה יותר כך יגדל גם הסיכוי לטעויות. אגב, במיקרו-בקרים מסוימים יש אופציה לזיהוי אוטומטי של קצב השידור הנכנס, אך כדי שזה יעבוד, צריך לשלוח אליהם קודם כל רצף ביטים אחד ספציפי – וזה לא תמיד מתאפשר.

אחרי ביט ההתחלה מגיעים הביטים של הנתונים. כמה ביטים? חמישה עד תשעה, וגם זה צריך להיות מוסכם מראש על שני הצדדים. ברוב המערכות המודרניות משדרים בכל מסגרת שמונה ביטים = בייט אחד. הביט הראשון שמשודר הוא ה-LSB, הביט "הכי פחות חשוב", והבאים אחריו כמובן בסדר חשיבות עולה.

אחרי הביטים של הנתונים, יש אופציה להוסיף ביט זוגיות (Parity). זהו ביט שעוזר לזהות שגיאות בשידור. החומרה סופרת כמה ביטים של נתונים במסגרת הנוכחית היו "1", ומוסיפה את הערך של ביט הזוגיות. לביט זוגיות זוגי (Even) הסכום הכולל אמור להיות מספר זוגי, ולביט זוגיות אי-זוגי (Odd) הסכום אמור להיות אי-זוגי. אם הוא לא, סימן שמשהו בדרך השתבש ואי אפשר לסמוך על הביטים האלה. גם כאן, השימוש בביט הזוגיות והאופי שלו צריכים להיות מוסכמים מראש.

בסיום המסגרת, המתח על הקו חייב לחזור ל-HIGH, ולשם כך יש Stop bit חובה שערכו "1". הוא יכול להימשך פרק זמן של ביט רגיל, או פי 1.5 או פי 2 – כרגיל, איך ששני הצדדים מחליטים וקובעים מראש.

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

תיאור מקוצר

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

9600,8,N,1

המשמעות היא שהפרמטרים של הפרוטוקול בשני הצדדים צריכים להיות:

  • מהירות = 9600 באוד
  • מספר ביטים של נתונים בכל מסגרת = 8
  • ביט זוגיות = ללא (None)
  • אורך ביט עצירה = 1 ביט רגיל
פירוט התיאור המקוצר של הפרוטוקול
פירוט התיאור המקוצר של הפרוטוקול (לחצו להגדלה)

לפעמים יש מקפים במקום הפסיקים בתיאור המקוצר, או שהכול מופיע כרצף אותיות אחד, אבל זה עניין של העדפה.

דוגמאות

מודול החומרה של UART במיקרו-בקר ATmega328P (שנמצא כידוע בלוחות הארדואינו הפשוטים) תומך כמעט בכל האופציות של הפרוטוקול. התקשורת דרך אובייקט Serial של ארדואינו בתוכנה מגבילה אותנו ל-8 ביטים, ללא ביט זוגיות, ועם ביט עצירה באורך 1, אך עבודה ישירה עם הרגיסטרים פותחת את כל שאר האפשרויות. בהמשך מוצג הפלט, כפי שנקלט בסקופ, של שלוש תוכניות קצרות. כולן שולחות ברצף את התווים A ו-B (מספר 65 ו-66 בקוד ASCII).

התוכנית הראשונה פועלת בקצב שידור 9600, משדרת 8 ביטים של נתונים בכל מסגרת, ללא ביט זוגיות, ועם ביט עצירה באורך 1 (אותן הגדרות כמו בארדואינו):

פלט האותיות AB ב-UART "רגיל"
פלט האותיות AB ב-UART "רגיל" (לחצו להגדלה)

הנה אותו צילום מסך בדיוק, אך הפעם עם גרפיקה שהוספתי לצורך זיהוי של המסגרות וחלקיהן:

אותו פלט, עם הבהרות
אותו פלט, עם הבהרות (לחצו להגדלה)

בתמונה הבאה שידור דומה, באותו קצב, אך עם ביט עצירה באורך 2 במקום 1, ועם ביט זוגיות זוגי. כיוון שבכל מסגרת כאן יש שני ביטים שערכם "1" (מספר שהוא בעצמו זוגי), הערך של ביט הזוגיות בשני המקרים הוא אפס:

שידור "AB" ב-UART עם ביט זוגיות זוגי וביט עצירה באורך 2
שידור "AB" ב-UART עם ביט זוגיות זוגי וביט עצירה באורך 2 (לחצו להגדלה)

התמונה הבאה היא בקצב שידור נמוך פי שניים (4800 באוד, שימו לב למשך של כל ביט), ללא ביט זוגיות, עם ביט עצירה באורך 1, אבל עם חמישה ביטים בלבד של נתונים בכל מסגרת, מה שלמעשה מקצץ לנו כאן חלק מהמידע:

שידור בקצב נמוך, עם 5 ביטים של נתונים בכל מסגרת
שידור בקצב נמוך, עם 5 ביטים של נתונים בכל מסגרת (לחצו להגדלה)

שגיאות ואיתורן

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

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

לרכיבים אין שום דרך לדעת בוודאות מי מהתנאים האלה התקיים, אם בכלל. לרוב הם יכולים לתת לנו רק שתי אינדיקציות:

  • אם הם מצפים לביט Parity, ורואים ערך שלא מסתדר עם שאר הביטים שנקלטו, הם יכולים להפיק שגיאת זוגיות (Parity error)
  • אם הם לא רואים מתח HIGH במקום שבו אמור להופיע ביט העצירה, הם יפיקו שגיאת מסגרת (Frame error)

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

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

הנה הדגמה נוספת של UART, עם מימוש שידור על מיקרו-בקר ללא מודול UART בחומרה.

להרשמה
הודע לי על
4 תגובות
מהכי חדשה
מהכי ישנה לפי הצבעות
Inline Feedbacks
הראה את כל התגובות

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

אגלה לך סוד; מימוש החלק הסינכרוני היה די מסובך ולא הצליח לפעול בשום קצב. מה שעשיתי היה לוותר עליו, ולכתוב משפט דומה למה שכתבת כעת: "כיוון שמעולם לא השתמשו בתקשורת הסינכרונית של ה8251 בחיבור בין מחשבים, האופציה הזו לא ניתנת להפעלה" 🙂