סיימונים לפי משקל, חלק ב'

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

לוח המשחק המוכן בפעולה
המשחק המוכן בפעולה

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

כיוון שהמיקרו-בקרים שלי היו כאמור "יד שנייה", היה לי חשוב לבדוק אותם בשלב כמה שיותר מוקדם. לכן, התחלתי כל לוח בהלחמה של קונקטור ה-USB (לאספקת חשמל), המיקרו-בקר, והקבל והנגד שדרושים לו. תכנון הלוח כלל גם מגעים לצריבת קוד למיקרו-בקר (באמצעות צורב PICKIT 3 עם מתאם pogo pins שהכנתי בעבר), אז כבר בשלב זה צרבתי על כל לוח קוד בסיסי לבדיקת פונקציונליות. אף מיקרו-בקר לא נכשל בשלב הצריבה. לאחר מכן השלמתי את הלחמת שאר הרכיבים – לדים, לחצנים, באזר – ובדקתי את התקינות שלהם ושל ה-GPIO של המיקרו-בקר בעזרת הקוד שכבר נצרב. פרט ללד אחד שהולחם הפוך בטעות, לא הייתה כל תקלה.

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

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

הלולאה שמציגה את רצף הצבעים בסיסית מאוד: מה שקובע את משך הצגת הצבע זו קריאה לפונקציית delay פשוטה שכתבתי לבד. פונקציה זו מסתמכת על מונה אלפיות שנייה, שמתעדכן בעזרת פונקציית פסיקה של טיימר – שגם אותה כתבתי כמובן לבד. את המונה הזה, שהוא בעצם משתנה בגודל 16 ביט (שני בייט), אני קורא רק אחרי שנטרלתי זמנית את הטיפול בפסיקות – כדי למנוע מצב בו בזמן שאני קורא את הבייט הראשון, פסיקה משנה את השני. בקיצור, קוד פשוט ולפי הספר. אז איפה הבעיה?

בקוד של פונקציית delay שכתבתי, אני מריץ לולאה שמחשבת שוב ושוב את ההפרש בין הערך הנוכחי של מונה אלפיות השנייה לבין הערך שהיה בו בעת הכניסה לפונקציה, ועוצרת כשההפרש הזה גדול או שווה למספר אלפיות השנייה המבוקש. זה דבר שביצעתי כל כך הרבה פעמים בארדואינו, שאפילו קראתי לפונקציות הרלוונטיות באותם שמות – millis, delay. וכמו בארדואינו, הייצוג של המספרים בזיכרון הוא כזה שגם אם מספר אלפיות השנייה גולש מעבר למקסימום שהמשתנה יכול להכיל (ב-16 ביט ללא סימן זה יוצא 65535), חישוב ההפרש עדיין ייתן תשובה הגיונית.

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

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

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

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

הצחקת אותי עם "הדרישות מוגדרות היטב מראש"…