כמה מהבאגים המרגיזים ביותר נובעים מאלמנטים, שאמורים דווקא להגן עלינו ועל הרכיבים שלנו. בפוסט זה נסתכל ספציפית על דיודות הגנה פנימיות של פיני קלט/פלט במיקרו-בקרים, ובאפשרויות להתמודדות איתן.
רקע: הדיודות הסמויות מן העין
לפני כעשר שנים התפרסם פוסט מעניין, של בחור שניסה לבדוק כמה זמן זיכרון ה-SRAM של מיקרו-בקר שורד ללא חשמל. הזמנים שמדד היו ארוכים מעבר לכל היגיון, עד שהבין שנורת LED קטנה וכבויה, שהייתה מחוברת לפין GPIO של המיקרו-בקר, שימשה מעין "תא סולרי" כשהחדר היה מואר והעבירה מספיק חשמל כדי שהנתונים בזיכרון ישתמרו. הסיפור הזה חשף את הצד האפל – סליחה על משחק המילים – של דיודות ההגנה הפנימיות של מיקרו-בקרים.
הדיודות האלה לא תמיד מופיעות ב-datasheets הרשמיים, אבל אם תחפשו טוב במסמכים טכניים תמצאו בדרך כלל שרטוט בסגנון הבא, שמציג (בין השאר) שתי דיודות שצמודות לכל פין GPIO ושנמצאות בתוך המיקרו-בקר:
לפעמים הן נקראות Clamping diodes ולפעמים ESD protection diodes, בכל מקרה תפקידן זהה: להגן על המעגלים הפנימיים העדינים מפני מתחים שחורגים ממתחי ההפעלה של המיקרו-בקר. כאשר המתח על הפין נמוך משמעותית מה-GND של המיקרו-בקר, הדיודה התחתונה בשרטוט "נפתחת", וכאשר המתח גבוה משמעותית ממתח המערכת (Vdd), הדיודה העליונה נכנסת לפעולה. זהו פתרון נפוץ ביותר, אבל ממש לא מושלם. קודם כל, מיקרו-בקרים ממשיכים להיפגע כל הזמן מפריקות אלקטרוסטטיות (ESD) וממתחים חריגים, ובנוסף הדיודות עצמן יוצרות בעיות חדשות. הנה דוגמה.
חשמל משום-מקום
לפני זמן מה פנה אליי לקוח והציג תקלה מוזרה. הפרויקט שלו כלל לוח Arduino Nano Every, ששלט בפס לדים Addressable RGB. בדרך כלל זה עבד, אבל כאשר הוא חיבר קווי פלט נתונים של מודול חיצוני מסוים לארדואינו (בזמן שהארדואינו עצמו טרם קיבל חשמל!) הלדים נדלקו פתאום – כולם באדום – והארדואינו נתקע במצב לא ברור שממנו הוא לא הצליח להיחלץ, אפילו כשחובר לאחר מכן לאספקת החשמל הרגילה שלו. מקור החשמל ה"רשמי" ללדים היה מייצב המתח שעל הארדואינו עצמו, אז כשהוא היה כבוי, מאיפה הגיע החשמל בשביל שהלדים יאירו באדום? ולמה הארדואינו השתגע ככה?
חקירה קצרה העלתה שהמודול החיצוני קיבל אספקת חשמל ממקור נפרד מזה של הארדואינו (עם אדמה משותפת), ושהפלט שלו היה מסוג Idle HIGH – כלומר, כמו בקווי TX של UART, מצב ברירת המחדל כשאין שידור הוא מתח חיובי. מכאן לא נדרש לי זמן רב כדי להיזכר בדיודות ההגנה הפנימיות ולהבין מה קרה:
כשהארדואינו ללא חשמל, פין הקלט שלו מקבל HIGH (נניח 5V) מפיני הפלט של המודול; זה הרבה יותר גבוה מ-Vdd באותו זמן, שהוא כמובן 0V, ולכן דיודת ההגנה של הפין מתחילה להוליך, ובגללה Vdd עצמו קופץ לערך קרוב ל-5V ומתחיל להזין את המיקרו-בקר וגם את הלדים. המיליאמפרים הספורים שהמודול מסוגל להעביר בקווי הנתונים שלו מספיקים בשביל שיקרה משהו, אבל לא לפעילות תקינה.
על סמך התובנה הזו הצעתי פתרון זריז: לשים נגדים בעלי ערך גבוה (נגיד 10K) בטור, בין קווי הפלט של המודול לפיני הקלט של הארדואינו. הנגדים מאפשרים למידע לעבור, אבל מגבילים את הזרם במידה כזו שהוא לא כבר לא מסוגל להפעיל דברים ולגרום נזקים. הפתרון הזה עבד, ועם זאת נותרה תעלומה: למה הארדואינו לא הצליח להתאושש כשחיברו אותו לאספקת החשמל הרגילה?
מחדל ברירת המחדל
קווי נתונים אינם בנויים לאספקה של זרם משמעותי, וכאשר הלדים והמיקרו-בקר של הארדואינו לוקחים מהם את כל הזרם שאפשר, המתח בקווים יורד. בדיוק אותו דבר קורה, אגב, כשמנסים לקחת מפין GPIO של מיקרו-בקר יותר מדי זרם: הוא לא סתם מגיע למקסימום שמצוין ב-Datasheet ואז נשרף, אלא המתח יורד ככל שהזרם עולה. זו תופעה אמפירית שהצילה וממשיכה להציל אינסוף מייקרים מתחילים מנזק בלתי-הפיך לארדואינו שלהם, בלי שהם ידעו בכלל!
לכל מיקרו-בקר שמכבד את עצמו יש מנגנון מובנה להגנה מפני מתח מערכת נמוך מדי. זה נקרא BOD, ראשי תיבות של Brown Out Detection (טרמינולוגיה שמקורה ברשת החשמל). ה-BOD מנטר את מתח המערכת, וכאשר המתח נמוך מדי, המנגנון מכניס את המיקרו-בקר למצב אתחול. בצורה כזו, כאשר המתח חוזר לרמות מתאימות, המיקרו-בקר עובר Reset מסודר ולא נתקע בגלל שיבושים אקראיים שעלולים היו להתחולל בזמן שהמתח היה בעייתי. ההיתקעות של הארדואינו אצל הלקוח תואמת מצב שבו ה-BOD לא פועל – אז למה הוא לא פועל?
למיקרו-בקר שבארדואינו הזה (ATmega4809) יש BOD, ויש גם "פיוז" שקובע אם הוא יפעל כברירת מחדל או לא. למה שמישהו ירצה לבטל את הפעולה של ה-BOD? כי כמו כל מודול אחר הוא צורך קצת זרם, ובמצבים מסוימים אפילו הצריכה הזו תיחשב בזבזנית. חיפוש בגוגל, ובירור עם ChatGPT, לא הצליחו לומר לי מהן הגדרות ברירת המחדל של הפיוזים בארדואינו Nano Every; אפילו בקבצים של סביבת הפיתוח על המחשב שלי לא הצלחתי לאתר אותן. אז כתבתי קוד ארדואינו קצר שכותב ל-Serial Monitor את ערך הפיוז של עצמו, והסתבר שאכן הוא נשאר במצב ברירת המחדל מהמפעל – BOD כבוי!
אפרופו, זה לא ששאר הפיוזים נשארים עם ברירות המחדל! הרי אחד הממצאים הראשונים שלי כשחקרתי את דגם הארדואינו הזה, כשהוא היה חדש, היה שהגביש הפנימי הועבר לתדר 16MHz במקום 20MHz. אז אם כבר נגעו בפיוזים, למה לא הדליקו את ה-BOD? פעולת הארדואינו הייתה יכולה להיות אמינה יותר, ולמייקר הממוצע ממילא לא אכפת מצריכת הזרם של המודול – הוא בכלל לא ירגיש בהבדל. אבל זה מה יש.
הבעיה החמורה יותר היא שקשה מאוד, מתוך סביבת הפיתוח של ארדואינו, לשנות את הפיוזים. קוד שאמור היה להתאים אמנם התקמפל, אבל תוכנת הצריבה (AVRDude) התעקשה שאסור לה לגעת באזור הזה בזכרון. אולי אפשר לשנות איפשהו את הפרמטרים שנשלחים אליה, אבל גם אם זה אפשרי מדובר בפעולה מסורבלת.
אז מה האפשרויות?
הדרך הטובה ביותר לפתור את הבעיה היא למנוע אותה מלכתחילה: לדאוג שמערכת כבויה לא תקבל קלט ממודולים חיצוניים פעילים.
אם זה בלתי אפשרי, ובכל מקרה ליתר ביטחון, כדאי לשים נגדים על קווי הנתונים. פה צריך לשים לב לאינטראקציה שלהם עם שאר האלמנטים במערכת (כגון נגדי pull-up או pull-down במיקרו-בקר).
אגב, למיטב ידיעתי אין פתרון קל עם רכיב חוצץ כזה או אחר. חישבו למשל על מבודד אופטי – הרי הוא מבוסס על פוטוטרנזיסטור, שעלול ליצור זרם משל עצמו בדיוק כמו הלד בסיפור שבתחילת הפוסט! ורכיבים אקטיביים אחרים זקוקים גם הם לאספקת חשמל.
ולסיום, חשוב להגן על המיקרו-בקר ולהפעיל את ה-BOD שלו. כפי שראינו, זה לא תמיד אפשרי – ובלוחות ארדואינו, אפילו אם נצליח להגן על השבב הראשי, עדיין יש את שבב התקשורת שלפעמים צריך ושעלול לחוות תקלות משל עצמו.
כן, זה קטע. אני זוכר את הפעם הראשונה שזה קרא לי והייתי מבולבל לגמרי…
עוד אפשרויות שיש לטפל בנושא הזה הן:
אם לרכיבי משנה יש פין EN אז להפעיל אותו בהתאם כדי לכבות את רכיבי המשנה כאשר לא צריך. ככה גם חוסכים באנרגיה.
אפשר לשים מוספט על הקו ולפעיל אותו ככה שהחשמל יעבור רק כאשר ה VCC בבקר הראשי HIGH.
בנוסף ישנם IC ייעודיים שמתנהגים כמו מפסק. לדוגמא, Nordic מאוד אוהבים את ה NX3DV2567 בלוחות פיתוח שלהם. זה ישר תופר 4 קוים שצריך למתג.
כמו שכתבתי, לא בטוח שפתרונות אקטיביים יעזרו, כל עוד המעגל לא מקבל חשמל משל עצמו (במקרה כזה, כנראה רכיבים החוצצים הם אלה שייפגעו, במקום המיקרו-בקר).
עם MOSFET… אולי לשים את הסיגנל החיצוני ב-Gate של N-Channel? צריך לחשוב על זה כל מקרה לגופו, אחרת אני לא חושב שיהיה לזה יתרון לעומת נגד פשוט.