בין התפזרות מיותרת לפיזור סיכונים: ניסיון ראשון להתקדם ברצינות עם המיקרו-בקרים של Silicon Labs, בעזרת לוח הערכה חדש וזול.
תוך כדי שאני משחק עם הלוחות החדשים והמגניבים של PIC ו-AVR, יש ערמה לא קטנה של לוחות ישנים שבקושי נגעתי בהם ושמסתכלים עליי במבט מאשים. הפעם האחרונה שעשיתי משהו עם EFM8, למשל, הייתה בסוף 2016. וזה חבל, כי בניגוד לכמה מיקרו-בקרים אחרים שצוברים אבק אצלי, המשפחה הזו מציעה מאזן טוב של יכולות ומחיר – ומהמעט שראיתי בינתיים, גם קלוּת פיתוח.
לפני כשמונה חודשים, חברת Silicon Labs הוציאה לשוק לוח הערכה בשם EFM8BB1LCK, שמנגיש את המיקרו-בקר הבסיסי EFM8BB10F8G למי שעדיין לא מכיר ולא עובד עם הכלים שלה – אם כי חשוב לציין שזה לא איזה מין ארדואינו חדש, והוא לגמרי לא מתאים למי שרק נכנס לתחום בלי הכוונה מסודרת. הלוח כולל צורב ודיבאגר מובנה, כך שמבחינת חומרה, כדי להתחיל לעבוד איתו צריך רק להתקין את סביבת הפיתוח החינמית Simplicity Studio, לחבר כבל מיקרו-USB, ואולי להלחים header או שניים בשביל חוטי גישור.
כברירת מחדל, אין תקשורת UART בין הלוח הזה למחשב. אם מלחימים את הגשרים הזעירים J7 ו-J9 על הלוח ומתקינים את תוכנת העזר ToolStick Terminal (קישור להורדה בדף הזה) אפשר לדבר איתו, אם כי זה עדיין לא פורט רגיל – לשם כך יידרש מתאם USB-to-UART נפרד. אגב, בהתקנה של התוכנה הנ"ל בחרתי לא לכלול את ה-ToolStick Development Kits, הם לא רלוונטיים ללוח הזה וסתם תופסים מגהבייטים מיותרים על הדיסק.
אוקיי, לעבודה. כאמור, כבר עשיתי משהו עם EFM8, אבל חלף מספיק זמן כדי להצדיק רענון. מה שננסה ליצור הפעם, במקום הבלינק הפשוט, זו מערכת ששולחת ב-UART (קצב 9600 באוד, כמובן) את המחרוזת "Hello world!" ואת מספר השניות שעברו מאז האתחול, בכל פעם שלוחצים על הכפתור המובנה שעל הלוח. המסמכים שנרצה מול העיניים הם:
- ה-Datasheet הכללי של דגמי EFM8BB1
- ה-Errata שלהם (לא לשכוח בשום מיקרו-בקר!)
- ה-Reference manual, שבו נמצאים כל הפרטים הטכניים הקטנים והחשובים באמת.
אולי בגלל הזמן שעבר מאז הניסיון הקודם, ואולי בגלל הלוח השונה, כשיצרתי הפעם פרויקט חדש הופיע בו אוטומטית, פרט לקובץ קוד המקור ב-C, קובץ בשם SILAB_STARTUP.A51 . הסתבר שזהו קוד אסמבלי שרץ מיד אחרי האתחול, מאפס את המשתנים הגלובליים וכיוצא בזה. הוא גם מחפש, לפני הכול, בקוד שלנו פונקציה בשם SiLabs_Startup ומריץ אותה אם היא קיימת. מי צריך את זה? ובכן, הייתי מעדיף להסתדר בלי הסיבוך הנוסף, אך אם אתם זוכרים מהפוסט ההוא מ-2016, למיקרו-בקרים האלה יש Watchdog אוטומטי שחייבים לנטרל בתוכנה ומהר. לדברי התיעוד, עלול להיווצר מצב של קוד אתחול ארוך כל כך, שה-Watchdog יקפוץ עוד לפני שהקוד שלנו יתחיל לרוץ, וזה יגרום ללולאת אתחולים אינסופית. כדי להבטיח שזה לא יקרה, אפשר לכתוב את פקודות הניטרול – ואולי עוד כמה דברים, על הדרך – בפונקציה SiLabs_Startup המיוחדת.
הלאה. הארכיטקטורה של המיקרו-בקר הזה מבוססת על 8051, וזו ארכיטקטורה עתיקה. כמה עתיקה? ראיתם פעם קומודור 64? בערך ככה. אז הוא כולל כל מיני מוזרויות קטנות וגדולות, אם כי אני לא בקיא מספיק בחומר כדי לדעת בדיוק מי מהן היא תוצר ישיר של ה-8051 ומי המצאה חדשה. למשל, לשני הטיימרים 0 ו-1 יש prescaler אחד משותף, והוא יכול לחלק את השעון ב-4,8,12 או 48 בלבד. למודול ה-UART אין שעון משלו שיקבע את ה-baud rate; הוא מסתמך על אירוע ה-Overflow של טיימר 1, ומסיבות עלומות כלשהן, צריך לכוון את הטיימר כך שהאירועים האלה יקרו בקצב כפול מה-baud rate הרצוי.
כברירת מחדל, המיקרו-בקר מתבסס על המתנד הפנימי (תדר 24.5MHz) ומחלק אותו ב-8, כך ששעון המערכת הוא 3.0625MHz. ברירת המחדל של טיימר 1 היא prescaler מופעל עם חלוקה ב-12, שנותנת 255.208KHz. נחלק את זה ב-baud rate הרצוי-כפול-2 ונקבל 13.292. המספר העגול הקרוב ביותר הוא 13 ואנחנו מאבדים ככה אחוזי דיוק, אבל זה עדיין בגבולות הסביר. מה שצריך לזכור זה שהאירוע הרלוונטי הוא Overflow, וזה קורה כשהטיימר (במצב 8-ביט) עובר מ-255 ל-0, לא כשהוא מגיע ל-13… אז צריך להגדיר לו ערך reload של 256 פחות 13, כלומר 243. בהחלט חישוב מורכב יותר מאשר ב-AVR החדשים!
עם זאת, החישובים המרגיזים האלה נעשים רק על הנייר. אם לא נוגעים בברירות המחדל שהוזכרו, אז בקוד התוכנה אפשר לסכם את כל ההגדרה הדרושה של טיימר 1 בשלוש פקודות:
TMOD |= TMOD_T1M__MODE2; // Set Timer1 mode to 8-bit TH1 = 243; // Set reload value to OVF @19.2KHz
TCON_TR1 = 1; // Enable timer1
עבור ספירת השניות ניצור פונקציית millis כמו בארדואינו, שתתבסס על טיימר 0. בקצב שעון (אחרי prescaler למיניהם) של 255.208KHz, כל 255 "תקתוקים" של השעון הם בקירוב אלפית שנייה, וזה מספיק טוב בשבילי כרגע. בשביל התרגיל, אגדיר את טיימר 0 למצב 16-ביט. הפסיקה שלו תתעורר כשהמונה יגיע לערך העליון (65536) ויהפוך שוב ל-0, אבל כאן (בניגוד למה שעשינו עם טיימר 1) אין reload אוטומטי – צריך לתת למונה את הערך ההתחלתי הנכון (65536 פחות 255) כל סיבוב מחדש. אז ההגדרה של הטיימר, פלוס הפעלת הפסיקות, נראית כך:
TMOD |= TMOD_T0M__MODE1; // Timer0 mode to 16-bit
TH0 = 0xFF; // counter to overflow after 255 "ticks" TL0 = 0x01;
TCON_TR0 = 1; // Enable timer0
IE_ET0 = 1; // Enable timer0 interrupt
IE_EA = 1; // Enable interrupts globally
ופונקציית הפסיקה שלו נראית ככה:
הביטוי SI_INTERRUPT אומר לקומפיילר (Keil 8051, אם רציתם לדעת) שזו פונקציית פסיקה. הפרמטר הראשון בסוגריים (TIMER0_OVF_ISR) הוא סתם שם שאני בוחר עבור הפונקציה, והפרמטר השני (TIMER0_IRQn) הוא קבוע מוגדר-מראש שמציין איזו פסיקה הפונקציה הזו משרתת. את המונה של הטיימר מעדכנים בשני חלקים – בייט גבוה ובייט נמוך – ואם הדיוק היה חשוב, הייתי צריך להתחשב גם בזמן שעבר בין הקריאה לפסיקה לבין העדכון. כאן, כדי לא להסתבך יותר מדי וגם כיוון שהרזולוציה נמוכה ממילא, התעלמתי באלגנטיות מהעניין. המשתנה בשורה האחרונה הוא כמובן משהו גלובלי שאני הגדרתי.
את המימוש של קריאת הלחצן המובנה, כולל debounce (שמסתמך על ה-millis שלי), ביצעתי ללא פסיקות אלא בלולאה הראשית. את השידור ב-UART, לעומת זאת, מימשתי באופן שהוא non-blocking, כלומר פונקציית הפסיקה של "סיימתי לשדר תו אחד ב-UART" היא זו שמתחילה את שידור התו הבא, אם יש כזה. בין לבין, לולאת הקוד הראשית יכולה להמשיך לפעול כרגיל ללא עיכובים. מעניין לציין שלמודול UART0 יש רק פסיקה אחת, משותפת לשידור של תו ולקליטה של תו. אם התקשורת היא דו-סטרית ושני האירועים חשובים לנו, ניאלץ לבדוק בתוך פונקציית הפסיקה מי מהם העיר אותה – קצת כמו ב-PIC הפשוטים.
הקוד המלא והערוך של תוכנת ההתנסות הזו שלי נמצא כאן. הקוד המקומפל תופס 47 בייטים בזיכרון SRAM ו-781 בייטים ב-FLASH, ובמקרה הספציפי הזה שינוי הגדרות האופטימיזציה של הקומפיילר לא משפר את התוצאות.
מסקנות
ציינתי בהתחלה שהמיקרו-בקר הוא בסיסי, מהפשוטים במשפחת EFM8. בנוסף, כאן השתמשתי בפיצ'רים הכי בסיסיים שלו, שכמו Timer0 במיקרו-בקרי PIC, הם פחות או יותר מאובנים חיים, עם כל מיני שגעונות ומגבלות שכבר לא רואים במודולי החומרה המודרניים. כלומר, זו תהיה טעות לשפוט את EFM8 לפי הפוסט הזה בלבד. בדגמים המשוכללים יש מודולים מתקדמים ושימושיים, שעל פי השמועות גם התפעול שלהם הגיוני יותר. אבל EEPROM אין להם – את זה צריך לדמות על ה-FLASH בקוד נוסף.
העבודה עם EFM8 לא מובנת מאליה – זה תמיד ככה כשמתחילים ללמוד לבד משהו חדש – אך למי שכבר התעסק עם מיקרו-בקרים וסביבות פיתוח אחרים, לא נדרש מאמץ מיוחד כדי להסתדר עם Simplicity Studio (יש גם גרסה ללינוקס ומק), וה-datasheet והמסמכים האחרים תמציתיים וברורים יחסית. מבחינת המחיר, דגמים ברי-השוואה של Microchip (למשל) נהיו זולים יותר עם הזמן, וההפרש בינם לבין דגמי EFM8 כבר לא משמעותי כפי שהיה לפני שנים ספורות.
לסיכום, על סמך ההתרשמות הראשונית-מאוד שלי, EFM8 הוא אופציה נוספת לפיתוח. לא טובה יותר או גרועה יותר משמעותית מהאופציות האחרות, פשוט נוספת. ותמיד טוב שיש עוד אופציות.
נחמד מאוד 😀
מה חשבת על simplicity studio? אומרים שהיא מוצלחת למדי.
עשיתי כל כך מעט שם בינתיים, שאני לא יכול לומר שום דבר כרגע חוץ מ"נראה בסדר"… כן הייתי רוצה למצוא את הדרך להעלות קוד למיקרו-בקר ולהריץ אותו בבת אחת, במקום שני הכפתורים הנפרדים של "דיבוג" ו"המשך לרוץ". [עריכה: מצאתי – בתפריט Run->Debug configurations, בלשונית configuration, מבטלים את הסימון של "Break at" ולוחצים Apply]