מכונות סופיות ומיקרו-בקרים

הגשות סופיות בקורסי עיצוב פנים במכון הטכנולוגי חולון HIT (מאי 2019).

$config[ads_text] not found
Anonim

מכונות סופיות ומיקרו-בקרים


Finite State Machines: הדרך הטובה ביותר לקוד!

השימוש ב- Finite State Machines (FSM) הוא תרגול תעשייתי מקובל בקידוד, אך השימוש בהם הוא מעבר לתוכנה היומיומית. הם יכולים בקלות לשמש בפרויקטים, כדי למנוע באגים, להפסיק לולאות אינסוף hogging המעבד, כדי להקל על באגים.

הבעיה

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

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

הערה: מאמר זה יהיה שוקל מכונות המדינה הסופית ב C אבל הם אפשריים בכל שפה (כולל הרכבה).

מה הם סופיים מכונות המדינה "hljs"> STATE = "התחל"; חלל ראשי (חלל) {לעשות {אם (STATE == "התחל") {startCode (); } אלא אם (STATE == "MIDDLE") {midCode (); } אלא אם (STATE == "END") {endCode (); } אחר {STATE = "START"; }} בעוד (1); } חלל startCode () {/ כמה קוד כאן STATE = "MIDDLE"; } קוד קוד ריק () {/ קוד כלשהו כאן STATE = "END"; } קודקוד ריק () {/ קוד כלשהו כאן STATE = "START"; }

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

זה רק שימושי כמו קוד שיושב בתוך פונקציות המדינה (startCode, midCode, ו- endCode). אם הקוד בתוך פונקציות אלה אינו תואם FSM, לולאות אינסופיות עדיין ניתן נתקל ובכך להפוך את כל הקוד חסר טעם. הטריק האמיתי הוא עושה "קוד סופי המדינה תואם". אז איך עושים את זה? בואו נתחיל בכך שתסתכל על קוד הלולאה האינסופי הישן.

 sendI2CStartBit(); while(I2CSTARTWAIT); 

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

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

בואו נסתכל על הפריסה הגרפית של מכונת I2C סופיים המדינה כי היא להיכלל בתוכנית העיקרית שלנו.

תוכנית כמו FSM

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

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

 Void I2cupdate() { if(i2cState == “STARTBIT”) { sendI2CStartBit(); i2cState = “CHECKSTART”; } if(i2cState == “CHECKSTART”) { if(I2CSTARTWAIT == 0) { i2cState = “I2CIDLE” } } if(i2cState == “I2CIDLE”) { } } 

אז איך אנחנו מקבלים את המטפל I2C לשלוח קצת להתחיל? פָּשׁוּט. אם פיסת קוד צריכה להשתמש I2C, אתה הראשון לבדוק כי I2C הוא סרק אז אתה מגדיר את i2cState כדי "startbit".

 SomeFunction() { if(i2cState == “I2cIDLE”) { i2cState = “STARTBIT”; } } 

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

 // This is OK: SomeFunction() { if(i2cState == “I2CIDLE”) { next state for this function; } } // This is NOT ok: SomeFunction() { while(i2cState != “I2CIDLE”); } 

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

אז בואו נסתכל על קוד I2C שונה:

 SomeFunction() { if(internalState == “STATE1”) { if(i2cState == “I2cIDLE”) { i2cState = “STARTBIT”; internalState = “STATE2”; } } else if(internalState == “STATE2”) { if(i2cState == “I2cIDLE”) { if(i2cError == 1) { // Error handling code here } else { // Send data, read data or any old code internalState = “STATE1”; } } } } 

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

עיצוב FSM עם מיני פונקציות FSM

הפונקציה העיקרית היא מכונה סופית המדינה קורא לכל אחת מהמכונות משנה המדינה. כאשר מכונות אלה מכונות משנה נקראים, הם מבצעים בדיקות משלהם ושינויים פנימיים המדינה. כל אחת מהמכונות הללו יכולה לחקור את מצבם של מכונות משנה אחרות, כך שיוכלו לתקשר ולשלוט זה בזה. בדרך כלל הסדר שתקבל הוא כמה שכבות של מכונות המדינה עם השכבה העליונה להיות הפונקציה העיקרית ואחריו את שכבת התוכנית השנייה כי יבקש נתונים I2C, לשלוח בתים UART, ולבקש קלט המשתמש. השכבות האחרונות (מימין בתחתית כי בדרך כלל לא לתקשר עם שום דבר חוץ מאשר את עצמם) להתמודד עם IO והתקנים כגון I2C, SPI, וגישה IO.

תצוגת שכבות

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

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

הורד קוד