ארדואינו: סוד האתחול הנעלם

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

ceci n'est pas un feu de circulation
ceci n'est pas un feu de circulation

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

לסרטון השני חשבתי על נושא – לא אגלה בינתיים מהו, בשביל לשמור על המתח – שכדי להדגים אותו הייתי יכול להיעזר בהבדלים שבין סוגי האתחולים (Reset) השונים של הארדואינו. אגב, יש תמיד ויכוח לגבי המינוחים המדויקים אתחול, איפוס, Reset ו-Restart; לדעתי במיקרו-בקרים כאלה הוויכוח די מיותר. בכל אופן, בהרבה מיקרו-בקרים וגם ב-ATmega328P שבארדואינו הנפוצים מסתתר איזשהו רגיסטר, שאומר לנו מה הייתה הסיבה לאתחול. האם הרכיב פשוט חובר לאספקת החשמל והתחיל לעבוד? או שאולי באמצע העבודה מישהו חיבר את פין ה-Reset שלו ל-GND? או שהמתח במערכת צנח לרגע והכניס לפעולה את מנגנון ה-Brown-out detector? או שה-Watchdog נכנס לפעולה כי הקוד הרגיל לא איפס אותו בזמן? לפעמים קריטי שנדע מי מהגורמים האלה אחראי לאתחול, ולשם כך יש לנו את הרגיסטר MCUSR:

הרגיסטר MCUSR של ATmega328P
הרגיסטר MCUSR של ATmega328P (לחצו להגדלה)

ארבעת הביטים העליונים של הרגיסטר אינם בשימוש. כל אחד מארבעת הביטים הנמוכים יכול להיות 1 אם התחולל אתחול מהסוג המתאים (למשל, WDRF הוא "דגל" שמציין אתחול בגלל Watchdog). פרט חשוב נוסף שמצוין בהמשך ה-Datasheet הוא שהדגלים בביטים 1, 2 ו-3 מתאפסים רק במקרה של אתחול מלא (Power On), או על ידי פקודה מפורשת בתוכנה. כלומר, אם לדוגמה היה אתחול Power On ואחריו אתחול Brown-out, שני הדגלים PORF ו-BORF יהיו 1 בו-זמנית.

תפסתי את לוח הארדואינו הקרוב ביותר, שהיה במקרה מדגם Duemillanove הוותיק, חיברתי לו שלושה לדים צבעוניים וכתבתי קוד בסיסי בתוך הפונקציה setup שבודק את הרגיסטר הנ"ל ומדליק לד לפי סוג האתחול: לד אדום ל-Power On, צהוב ל-Brown-out וירוק ל-External (זה שמתרחש כשלוחצים על לחצן ה-Reset בלוח). את ה-Watchdog עזבתי בינתיים. בקוד שכתבתי בדקתי את הביטים השונים אחד כל פעם בשרשרת של פקודות if…else, ודאגתי גם לאפס כל דגל אחרי שהשתמשתי בו. חיברתי את הארדואינו למתח ישירות מספק כוח שולחני (כדי שאוכל ליצור מצב של Brown-out), והכל עבד יפה… פרט לדבר מוזר אחד: אחרי החיבור הראשוני לחשמל, שבו הלד האדום דלק כצפוי, לחצתי על הלחצן Reset ודווקא הלד הצהוב נדלק. רק בלחיצה הבאה ובאלה שאחריה הירוק האיר. מאיפה הגיע ה-Brown-out הזה? הדבר הטבעי לעשות היה למדוד אם אכן היה איזשהו Brown-out אמיתי למיקרו-בקר, אך מתוך עצלנותראיית הנולד עשיתי משהו אחר: הושטתי יד קצת יותר רחוק, תפסתי ארדואינו Uno והחלפתי איתו את ה-Duemilanove שבמעגל. העליתי אליו את אותו קוד בדיוק ו… שום לד לא נדלק בשום מצב.

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

מכיוון שלרוב המשתמשים אין Duemilanove, ולשחק עם ה-Bootloader זו פעולה בעייתית מכל מיני סיבות, נאלצתי לוותר על האספקט הזה של הסרטון שלי. עם זאת, חידת ה-Brown-out הלא-צפוי עדיין לא נפתרה. חיברתי בחזרה את ה-Duemilanove למעגל עם הלדים והתחלתי לשחק עם ספק הכוח. כשהמתח ירד ל-2.6V בערך המיקרו-בקר השתתק, וכשהמתח חזר לסביבות 2.8V הארדואינו חזר לחיים והדליק את הלד הצהוב כצפוי. המתחים האלה כמובן אינם אקראיים. זו ההגדרה שנקבעה בפיוזים של הארדואינו, כפי שאפשר לראות בטבלה הזו מה-Datasheet:

הגדרות סף ה-Brown-out השונות ב-ATmega328P
הגדרות סף ה-Brown-out השונות ב-ATmega328P (לחצו להגדלה)

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

המתח ב-Vcc בתחילת הפעולה של המערכת
המתח ב-Vcc בתחילת הפעולה של המערכת (לחצו להגדלה)

עוד לא בדקתי אם הסיבה נעוצה בספק הכוח או בלוח הארדואינו עצמו, אבל אם תסתכלו טוב על המספרים והפרמטרים תראו שרגע קצרצר אחרי החיבור לחשמל, למשך כמעט 400 מיליוניות השנייה, המתח יורד אל מתחת לסף ה-Brown-out. זה הרבה מאוד זמן, די והותר כדי לגרום לאתחול נוסף של הארדואינו. הקוד שלי בדק קודם כל את הביט של Power On, ראה שהוא 1, הדליק את הלד האדום ואיפס את הביט. אבל הביט של ה-Brown-out עדיין היה 1 בגלל האתחול הנוסף, ונשאר 1 גם אחרי שאתחלתי את הלוח בעזרת הלחצן. במילים אחרות, נוצר מצב שבו גם BORF וגם  EXTRF היו 1, ו-BORF היה הביט השני שהקוד שלי בדק אז הוא קיבל קדימות והתבטא בלד הצהוב. רק אז הוא התנקה ואיפשר לקוד להגיע, אחרי עוד אתחול עם הלחצן, ל-EXTRF ולהדליק את הלד הירוק.

לסיכום, לוחות ארדואינו Uno עם ה-Bootloader הנפוץ כיום מונעים בכוונה תחילה גישה למידע החשוב שנמצא ב-MCUSR, ובכך הם גם לא שומרים על עקביות עם מה שקורה בלוחות Duemilanove. דמיינו מפתח מערכות שבנה משהו עם Duemilanove לפני כמה שנים והסתמך על MCUSR – איזה בלגן ובעיות דיבוג נהיו לו כשהלקוח החליט לעבור ל-Uno! התחלתי את הסיפור הזה כדי להראות משהו לא מוכר ומעניין בארדואינו, ונפלתי למאורת ארנב עמוקה ואפלה של הגבלה והסתרת מידע שימושי מהמשתמש. יש סיבה יותר טובה מזו לרצות לעבור מארדואינו למיקרו-בקרים נטו?

תוספת מאוחרת: כדי להבין בדיוק למה ה-Bootloader של ה-Uno מוחק את המידע מה-MCUSR, חיפשתי ומצאתי את המקור הזה. מסתבר שכדי לזרז את פעולת ה-Bootloader, הוא עצמו בודק את סיבת ה-Reset האחרונה, וגם משתמש ב-Watchdog כדי להחליט מתי נמאס לו לחכות לקוד חדש ואפשר להריץ את הקוד הקיים. השיטה הזו משנה בעצמה את הביטים ב-MCUSR, כך שהם כבר לא משקפים את סיבת האתחול האמיתית ועדיף למחוק אותם. כך הרווחנו כמה שניות בהרצת הקוד, אבל הפסדנו נתונים מעניינים וחשובים.

להרשמה
הודע לי על
0 Comments
Inline Feedbacks
הראה את כל התגובות