חבורת מופרעים (או: איך מיקרו-בקרים מנהלים פסיקות)

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

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

מה זה פסיקה

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

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

מה בדיוק קורה כשיש פסיקה

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

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

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

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

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

אחת בשביל כולם

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

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

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

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

מה עדיף

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

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

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

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