370 likes | 480 Views
Real Time Systems. Chapter 11 תכנות במערכות הפעלה לזמן אמת. תכנות במעה"פ RT. מעה"פ לזמן אמת מוגבלות בד"כ בזיכרון ומצד שני מרכיב הזמן חשוב ביותר קיימים מנגנונים בשפות תכנות שרצוי לא להשתמש בהם במערכות משובצות תהליך בניית מערכת RT דורש שמירה על כללים מיוחדים. Real Time Kernel.
E N D
Real Time Systems Chapter 11 תכנות במערכות הפעלה לזמן אמת
תכנות במעה"פ RT • מעה"פ לזמן אמת מוגבלות בד"כ בזיכרון ומצד שני מרכיב הזמן חשוב ביותר • קיימים מנגנונים בשפות תכנות שרצוי לא להשתמש בהם במערכות משובצות • תהליך בניית מערכת RT דורש שמירה על כללים מיוחדים
Real Time Kernel • לא תמיד יש צורך להשתמש ב RTK • דוגמא: במערכת קיימים 3 מנועים ו-3 מפסקים דו מצביים. • יש לדגום כל מפסק 10 פעמים בשניה ובמידת הצורך להפעיל או להפסיק את המנוע
מימוש void main(void) { int i; while(1) { for (i= 0; i < 3; i++ ) { if (switchChanged(i)) changeMotor(i); } } }
הערות • לא מובטח לנו שכל מפסק ידגם 10 פעמים בשניה אולם גם מעבד איטי יבצע הרבה יותר מ- 10 דגימות בשניה לכל מפסק • מה נעשה אם צריך לדגום כל מפסק בדיוק 10 פעמים בשניה (לא יותר)?
while(1) { if (OneTenthSecondIsUp) { for (i= 0; i < 3; i++ ) { if (switchChanged(i)) changeMotor(i); } OneTenthSecondIsUp = 0; } } המשתנה הגלובלי יאותחל ל-1 ע"י Timer ISR
הוספת מרכיב למערכת • נניח שצריך לדגום מד לחץ וכאשר הלחץ עולה מעל 100 יש לפתוח שסתום. • ברגע שפתחנו את השסתום אם הלחץ ירד מתחת ל-90 יש לסגור אותו • יש צורך לדגום את הלחץ כל 50ms
if (FiftyMsIsUp) { switch (valveState) { case CLOSED: if (pressure() > 100) { openValve(); valveState = OPEN; } break; case OPEN: if (pressure() < 90) { closeValve(); valveState = CLOSED; } } FiftyMsIsUp = 0; }
בצורה כזו ניתן להוסיף גם מרכיב שמקבל מהרשת כל פרק זמן מסויים מידע • הקוד מסורבל ולכן נחלק לפונקציות: while(1) { checkMotorSwitches(); checkPressure(); checkDatagrams(); } • הבעיה – לא בטוח שאנו עדיין עומדים בזמנים.
מימוש ב- RTOS • דרישות: • מקביליות – ע"י 3 תהליכים • זמנים • תור לקליטת הנתונים מהרשת • כל RTK מספק את הדרישות הנ"ל והמימוש יהיה ע"י 3 תהליכים עצמאים
void checkMotorSwitches(void) { while (TRUE) { pause(100L); for (i = 0; i < 3; i++) { if (switchChanged(i)) changeMotor(i); } } }
void checkPressure(void) { while (TRUE) { pause(50L); if (pressure() > 100) { closeValve(); while (TRUE) { pause(50L); if (pressure < 90) { openValve(); break; } }}}}
void checkDatagrams(void) { typeMsg * msg; while (TRUE) { msg = waitMsg(DATA_GRAM); processDataGram(msg); freeMsg(msg); } }
טיפול בבעיות מכניות • כאשר אנו מטפלים במנועים, מנופים, חיישנים וכד' צריך להתחשב בבעיות פיזיקליות • כאשר נפסיק פעולת מנוע הוא לא יעצור באופן מיידי. • כאשר חיישן יזהה רכיב בפס יצור צריך להמתין עד שהרכיב יהיה במנוחה לפני שליחת הוראה לביצוע.
פתרון – השהייה מקסימלית • נמתין זמן ארוך יותר ממקסימום הזמן שנדרש לרכיב להתייצב if(closeEng()) { delay(1000); closed=TRUE; }
הערות • הפתרון בזבזני בזמן ואם יהיו הרבה מרכיבים מהסוג הזה המערכת תעבוד בצורה איטית • שיטה טובה יותר היא להגדיר מצב מנוחה כפרק זמן מינימלי שבו העצם נמצא באותו מקום. • לדוגמא: מעגל משולב (IC) נע על פס יצור, כאשר הוא מזוהה ע"י חיישן הוא נעצר. נגדיר לדוגמא שאם בפרק זמן של 50ms הוא באותו מקום אזי הרכיב במנוחה
void stampPartTask(void) { while (TRUE) { if (partIsAtPost()) { pause(50L); if (partIsAtPost()) stampAndReleasePart(); } yield(); } }
מימוש מערכת באמצעות תהליכים • נניח שהמערכת שלנו צריכה לבצע 3 דברים במקביל: • קריאת נתון כל 100ms • הדפסת ממוצע הנתונים כל שניה • קריאת תו מהמקלדת (אם הוקש) כל 100ms
void doAllTask(void) { int i; for (i = 0; ; i++) { pause(100L); if (kbhit()) processKey(); readAndAverageData(); if (i == 9) { displayAverageData(); i = 0; } } }
void dataAcquisitionTask(void) { while (TRUE) { pause(100L); readAndAverageData(); }} void keyboardTask(void) { while (TRUE) { pause(100L); if (kbhit()) processKey(); }} void displayTask(void) { while (TRUE) { pause(1000L); displayAverageData(); } }
הערות • שיטה זו עדיפה ממספר סיבות: • 3 תהליכים לא יעמיסו על המערכת • הקוד יותר קריא • שינויים יכולים להתבצע בקלות • כל תהליך ניתן לבדיקה בנפרד • הוספת תהליכים יכולה גם לגרום בעיות כאשר לדוגמא כל תהליך משתמש ב- timer ומספרם מוגבל
Timing Mechanisms • Pause function • Periodic timer – כל פרק זמן קצוב נשלחת הודעה • One-shot timer – אחרי פרק זמן קצוב נשלחת הודעה אחת • Time-slicing - לולאות busy wait
Pause Function void taskA(void) { while (TRUE) { pause(10L); acquireAndProcessData (); } } • לא ניתן לקבוע שהלולאה תתבצע 100 פעמים בשניה – לא מספיק מדוייק
The Periodic Timer void taskA(void) { typeData data; typeMsg * timer; timer = makeTimer(5000L); timer->flags |= PERIODIC;startTimer(timer); while (TRUE) { freeMsg(waitMsg(TIMEOUT)); acquireData(&data); processData(&data); } } הודעת Timer תגיע כל 5 שניות במדוייק
The One-shot Timer void taskA(void) { long timerNum; typeMsg * timer; while (TRUE) { timer = makeTimer(5000L); timerNum = timer->seqNum; startTimer(timer); sendRs232("Are you there?"); msg = waitMsg(ANY_MSG); switch (msg->msgNum) { case TIMEOUT: printf("RS232 is not present\n"); break; case I_AM_HERE: printf("RS232 is present\n"); cancelTimer(timer, timerNum); } } }
שימוש במערכות קיימות • גם אם המערכת שאנו צריכים לפתח נראית שונה ממערכות שפותחו בעבר ניתן כמעט תמיד למצוא חלקים זהים • אלגוריתמים • תזמון • ניהול משאבים
Delay • אסור לממש delay כלולאה ריקה • זמן ההשהייה לא יהיה מדוייק • יעבוד שונה אם נחליף מעבד • לכל RTOS קיימת פונקצית השהייה שעובדת עם שעון המערכת ומספקת דיוק טוב יותר • השהייה מכניסה את התהליך למצב חסום ומפנה את המעבד לתהליכים אחרים
תנאים מורכבים • יש להימנע משימוש בבלוקים גדולים שלIf-else • ההבדל בזמן הביצוע בין המקרה הטוב ביותר למקרה הגרוע ביותר גדול • כאשר צריך להקפיד על זמנים כלומר בכל מקרה לבצע פעולה בזמן קבוע יש צורך להשתמש באלגוריתם אחר
דוגמא • נתונים 3 תהליכים: • T1 – 3 מצבים (ON, OFF, SB) • T2 – 4 מצבים (RUN, BLKD, WAIT, END) • T3 – 2 מצבים (RED, GREEN) • נתון תהליך נוסף בעל 4 מצבים שנקבע ע"פ 3 התהליכים הנ"ל
מימוש רגיל if ((ts1==ON) && (ts2==RUN) && (ts3==RED)) ts4=FIRST; else if ((ts1==ON) && (ts2==WAIT) && (ts3==RED)) ts4=SEC; else if (…….) ts==TH; סה"כ יהיו 4*3*2 = 24 תנאים. הבדל גדול בין זמן הריצה כאשר התנאי הראשון מתקיים לזמן ריצה כאשר התנאי האחרון מתקיים
כל מצב מקבל ערך מספרי • נגדיר מערך: • int result[2][4][3]={1,2,1,3,1,4,2,1,3,….}; • חישוב המצב של T4: • t4=result[ts3][ts2][ts1]; • מתבצע בצעד חישוב יחיד • חסרון: זכרון נוסף (24 תאים במערך) • הקוד יהיה קצר יותר ולכן הזיכרון שדרוש לתוכנית יהיה קטן יותר • דרכים נוספות לחיסכון בתנאים: • x=!x; x=1-x;
תיעוד • התיעוד חייב להתבצע לפני ובזמן כתיבת הקוד • תיעוד שנכתב אחרי שלב הקידוד אינו אפקטיבי כיוון שאם מתגלות בעיות (מה שבד"כ קורה כאשר מתעדים) התיקון יהיה יקר • רמות תיעוד: • חיצוני • פנימי • מדריך למשתמש
תוכניות בדיקה • על מנת לבדוק את רכיבי המערכת בנפרד יש לכתוב תוכניות בדיקה • אם תהליך הבדיקה אינטרקטיבי, כלומר דורש קלט מהמשתמש, כדאי להכין תוכנית בדיקה שתקבל את הקלט מקובץ
בחירת החומרה • התוכניתן חייב להיות מעורב בתהליך בחירת החומרה למערכת. • דוגמא: מהנדס חומרה רוצה להשתמש במעבד 32 סיביות – נניח במחיר של 10$ • התוכניתן יודע ש- 40% מהזמן התוכנית נמצאת בהמתנה לרכיב I/O • שימוש בשני מעבדים זול יותר: 16 סיביות עבור התוכנית (4$) , 8 סיביות עבור I/O (1$)
שימוש בסימולטור • סימולטור ל – Target יקצר את תהליך הפיתוח בצורה משמעותית. • תהליך פיתוח ללא סימולטור: • edit -> comiple -> download -> execute -> test • גם אם יש רק רכיב חומרה יחיד מספר תוכניתנים יכולים לעבוד במקביל • עדיין יהיו רכיבים שלא ניתנים להרצה בסימולטור (גישה לחומרה לדוגמא)