- מהי סמפור?
- כיצד להשתמש בסמפור ב- FreeRTOS?
- הסבר לקוד סמפור
- תרשים מעגל
- מה זה Mutex?
- כיצד להשתמש ב- Mutex ב- FreeRTOS?
- הסבר על קוד Mutex
במדריכים קודמים סקרנו את יסודות FreeRTOS עם Arduino ואת אובייקט הליבה של Queue ב- FreeRTOS Arduino. כעת, במדריך השלישי של FreeRTOS, נלמד עוד על FreeRTOS ועל ממשקי ה- API המתקדמים שלו, מה שיכול לגרום לך להבין את פלטפורמת ריבוי המשימות לעומק.
Semaphore ו- Mutex (אי הכללה הדדית) הם אובייקטים גרעיניים המשמשים לסינכרון, ניהול משאבים והגנה על משאבים מפני שחיתות. במחצית הראשונה של הדרכה זו, נראה את הרעיון שמאחורי סמפור, כיצד ואיפה להשתמש בו. במחצית השנייה נמשיך עם מוטקס.
מהי סמפור?
בהדרכות קודמות, דנו אודות סדרי עדיפויות למשימה וגם נוכח לדעת כי מטלה בעדיפות גבוהה מקדימה משימה בעדיפות נמוכה יותר, כך שלמרות ביצוע משימת עדיפות גבוהה יתכן שיש אפשרות שהשחתת נתונים יכולה לקרות במשימה בעדיפות נמוכה יותר מכיוון שהיא עדיין לא מבוצע והנתונים מגיעים ברציפות למשימה זו מחיישן הגורם לאובדן נתונים ולתפקוד לקוי של היישום כולו.
לכן, יש צורך להגן על משאבים מפני אובדן נתונים וכאן לסמפור תפקיד חשוב.
סמפור הוא מנגנון איתות שבו משימה במצב המתנה מסומנת על ידי משימה אחרת לביצוע. במילים אחרות, כאשר משימה 1 סיימה את עבודתה, היא תציג דגל או תגדיל דגל ב -1 ואז דגל זה יתקבל על ידי משימה אחרת (משימה 2) שתראה שהוא יכול לבצע את עבודתו כעת. כאשר task2 סיימה את עבודתה, הדגל יופחת ב -1.
אז, בעצם, מדובר במנגנון "תן" ו"קח "וסמפור הוא משתנה שלם המשמש לסינכרון גישה למשאבים.
סוגי סמפור ב- FreeRTOS:
סמפור הוא משני סוגים.
- סמפור בינארי
- ספירת סמפור
1. סמפור בינארי: יש לו שני ערכים שלמים 0 ו- 1. זה דומה במקצת לתור באורך 1. לדוגמא, יש לנו שתי משימות, task1 ו- task2. משימה 1 שולחת נתונים למשימה 2 ולכן משימה 2 בודקת ברצף את פריט התור אם יש 1, ואז הוא יכול לקרוא את הנתונים שהוא צריך להמתין עד שיהפוך ל- 1. לאחר לקיחת הנתונים, משימה 2 מקטינה את התור והופכת אותה ל 0 כלומר המשמעות היא משימה 1 יכול לשלוח את הנתונים למשימה 2.
מהדוגמא לעיל ניתן לומר כי סמפור בינארי משמש לסינכרון בין משימות או בין משימות להפרעה.
2. ספירת סמפור: יש לו ערכים הגדולים מ- 0 וניתן לחשוב על תור שאורכו עולה על 1. סמפור זה משמש לספירת אירועים. בתרחיש שימוש זה, מטפל באירועים 'יתן' סמפור בכל פעם שאירוע מתרחש (מגדיל את ערך ספירת הסמפור), ומשימת מטפל 'תיקח' סמפור בכל פעם שהוא מעבד אירוע (מקטין את ערך ספירת הסמפור).
ערך הספירה הוא, אם כן, ההפרש בין מספר האירועים שהתרחשו למספר שעובד.
עכשיו, בואו נראה איך להשתמש בסמפור בקוד FreeRTOS שלנו.
כיצד להשתמש בסמפור ב- FreeRTOS?
FreeRTOS תומך בממשקי API שונים ליצירת סמפור, לקיחת סמפור ולמתן סמפור.
כעת, יכולים להיות שני סוגים של ממשקי API לאותו אובייקט גרעין. אם עלינו לתת סמפור מ- ISR, לא ניתן להשתמש ב- API של semaphore רגיל. עליכם להשתמש בממשקי API מוגנים להפסיק.
במדריך זה נשתמש בסמפור בינארי מכיוון שהוא קל להבנה וליישום. מאחר שמשתמשים כאן בפונקציונליות הפסקה, עליכם להשתמש בממשקי API מוגנים להפרעה בפונקציית ISR. כשאנחנו אומרים סנכרון משימה עם הפסקה, פירוש הדבר להכניס את המשימה למצב הריצה מיד אחרי ה- ISR.
יצירת סמפור:
כדי להשתמש בכל אובייקט גרעין, עלינו ליצור אותו תחילה. ליצירת סמפור בינארי, השתמש ב- vSemaphoreCreateBinary ().
ממשק API זה אינו לוקח אף פרמטר ומחזיר משתנה מסוג SemaphoreHandle_t. נוצר שם משתנה גלובלי sema_v לאחסון הסמפור.
SemaphoreHandle_t sema_v; sema_v = xSemaphoreCreateBinary ();
מתן סמפור:
למתן סמפור, ישנן שתי גרסאות - אחת להפסקה ואחת למשימה הרגילה.
- xSemaphoreGive (): API זה לוקח רק ארגומנט אחד שהוא שם המשתנה של סמפור כמו sema_v כפי שניתן לעיל בעת יצירת סמפור. ניתן לקרוא לזה מכל משימה רגילה שתרצו לסנכרן.
- xSemaphoreGiveFromISR (): זוהי גרסת ה- API המוגנת על ידי הפרעה של xSemaphoreGive (). כאשר עלינו לסנכרן משימה ISR ומשימה רגילה, יש להשתמש ב- xSemaphoreGiveFromISR () מהפונקציה ISR.
לקחת סמפור:
כדי לקחת סמפור, השתמש בפונקציית API xSemaphoreTake (). API זה לוקח שני פרמטרים.
xSemaphoreTake (SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
xSemaphore: שם הסמפור שיש לקחת במקרה שלנו sema_v.
xTicksToWait: זהו משך הזמן המרבי שהמשימה תמתין במצב חסום עד שהסמפור יהיה זמין. בפרויקט שלנו, נגדיר את xTicksToWait ל portMAX_DELAY כדי שהמשימה_1 תמתין ללא הגבלת זמן במצב חסום עד ש- sema_v יהיה זמין.
כעת, בואו נשתמש בממשקי ה- API הללו ונכתוב קוד לביצוע משימות מסוימות.
הנה ממשק לחצן אחד ושני נוריות. כפתור הלחיצה ישמש ככפתור הפסקה אשר מחובר לסיכה 2 של Arduino Uno. כשלוחצים על כפתור זה תיווצר הפרעה ונורית שמחוברת לסיכה 8 תופעל וכשתלחצו עליה שוב היא תהיה כבויה.
לכן, כאשר לוחצים על כפתור xSemaphoreGiveFromISR () ייקרא מפונקציית ISR ופונקציה xSemaphoreTake () תיקרא מהפונקציה TaskLED.
כדי לגרום למערכת להראות ריבוי משימות, חבר נוריות LED אחרות עם סיכה 7 אשר יהיו במצב מהבהב תמיד.
הסבר לקוד סמפור
נתחיל לכתוב קוד על ידי פתיחת ה- IDE של ארדואינו
1. ראשית, כלול את קובץ הכותרת Arduino_FreeRTOS.h . כעת, אם נעשה שימוש באובייקט גרעין כלשהו כמו סמפור לתורים, יש לכלול עבורו גם קובץ כותרת.
# כלול # כלול
2. הכריז על משתנה מסוג SemaphoreHandle_t לאחסון ערכי הסמפור.
SemaphoreHandle_t interruptSemaphore;
3. בהתקנה הריקה (), צור שתי משימות (TaskLED ו- TaskBlink) באמצעות ה- API של xTaskCreate () ואז צור סמפור באמצעות xSemaphoreCreateBinary (). צור משימה עם עדיפות שווה ובהמשך נסה לשחק עם המספר הזה. כמו כן, הגדירו את סיכה 2 ככניסה ואפשרו את הנגרר הנגרר הפנימי וחברו את סיכת ההפסקה. לבסוף, התחל את מתזמן כפי שמוצג להלן.
הגדרת חלל () { pinMode (2, INPUT_PULLUP); xTaskCreate (TaskLed, "Led", 128, NULL, 0, NULL); xTaskCreate (TaskBlink, "LedBlink", 128, NULL, 0, NULL); interruptSemaphore = xSemaphoreCreateBinary (); אם (interruptSemaphore! = NULL) { attachInterrupt (digitalPinToInterrupt (2), debounceInterrupt, LOW); } }
4. כעת, הטמיע את פונקציית ה- ISR. בצע פונקציה ושמה זהה לארגומנט השני של פונקציית attachInterrupt () . כדי לגרום להפרעה לעבוד כראוי, עליך להסיר את בעיית ההפחתה של כפתור הלחיצה באמצעות פונקציית מילי או מיקרו ועל ידי התאמת זמן ההפצה. מהפונקציה הזו, קרא לפונקציה interruptHandler () כמוצג להלן.
debouncing_time ארוך = 150; נדיפים לא חתומים אחרונים_מיקרו; בטל debounceInterrupt () { if ((long) (micros () - last_micros)> = debouncing_time * 1000) { interruptHandler (); last_micros = מיקרו (); } }
בשנת שגרת טיפול בפסיקה () פונקציה, קוראים xSemaphoreGiveFromISR () API.
void interruptHandler () { xSemaphoreGiveFromISR (interruptSemaphore, NULL); }
פונקציה זו תתן סמפור ל- TaskLed להדליק את ה- LED.
5. יצירת TaskLed פונקציה ובתוך בעוד הלולאה, קורא xSemaphoreTake () API ולבדוק אם סמפור נלקח בהצלחה או לא. אם זה שווה ל- pdPASS (כלומר 1), הפוך את נורית ה- LED למוצג להלן.
void TaskLed (void * pvParameters) { (void) pvParameters; pinMode (8, OUTPUT); בעוד (1) { if (xSemaphoreTake (interruptSemaphore, portMAX_DELAY) == pdPASS) { digitalWrite (8,! digitalRead (8)); } } }
6. כמו כן, צור פונקציה כדי להבהב נורית אחרת המחוברת לסיכה 7.
void TaskLed1 (void * pvParameters) { (void) pvParameters; pinMode (7, OUTPUT); בעוד (1) { digitalWrite (7, HIGH); vTaskDelay (200 / portTICK_PERIOD_MS); digitalWrite (7, LOW); vTaskDelay (200 / portTICK_PERIOD_MS); } }
7. פונקציית הלולאה הריקנית תישאר ריקה. אל תשכח את זה.
לולאה בטל () {}
זהו, קוד מלא ניתן למצוא בסוף הדרכה זו. כעת העלה קוד זה וחבר את נוריות הלחצן והלחצן עם ה- Arduino UNO על פי תרשים המעגל.
תרשים מעגל
לאחר העלאת הקוד, תראו נורית LED מהבהבת לאחר 200 ms וכאשר לוחצים על הכפתור, מיד הנורית השנייה תידלק כפי שמוצג בסרטון שניתן בסוף.
באופן זה, ניתן להשתמש בסמפורות ב- FreeRTOS עם Arduino שם הוא צריך להעביר את הנתונים ממשימה אחת לאחרת ללא הפסד.
עכשיו, בואו נראה מה זה Mutex וכיצד להשתמש בו FreeRTOS.
מה זה Mutex?
כפי שהוסבר לעיל הסמפור הוא מנגנון איתות, באופן דומה, Mutex הוא מנגנון נעילה שלא כמו הסמפור שיש לו פונקציות נפרדות להגדלה ולירידה, אך ב- Mutex, הפונקציה לוקחת ונותנת בפני עצמה. זו טכניקה להימנע משחיתות של משאבים משותפים.
כדי להגן על המשאב המשותף, מקצים משאב כרטיס אסימון (mutex). מי שיש לו כרטיס זה יכול לגשת למשאב האחר. אחרים צריכים לחכות עד שהכרטיס יחזור. באופן זה, רק משאב אחד יכול לגשת למשימה ואחרים ממתינים להזדמנותם.
בואו ונבין את Mutex ב- FreeRTOS בעזרת דוגמה.
כאן יש לנו שלוש משימות, אחת להדפסת נתונים על LCD, שנייה למשלוח נתוני LDR למשימה LCD ומשימה אחרונה לשליחת נתוני טמפרטורה על LCD. אז הנה שתי משימות חולקות את אותו משאב, כלומר LCD. אם משימת ה- LDR ומשימת הטמפרטורה שולחות נתונים בו זמנית אז אחד מהנתונים עלול להיפגם או לאבד.
אז כדי להגן על אובדן הנתונים, עלינו לנעול את משאב ה- LCD עבור task1 עד שיסיים את משימת התצוגה. אז משימת ה- LCD תיפתח ואז task2 יכולה לבצע את עבודתה.
אתה יכול לראות את העבודה של Mutex וסמפורות בתרשים שלהלן.
כיצד להשתמש ב- Mutex ב- FreeRTOS?
Mutexs משמשים גם באותו אופן כמו סמפורות. ראשית, צור אותו, ואז נתן וקח באמצעות ממשקי API בהתאמה.
יצירת Mutex:
כדי ליצור Mutex , השתמש ב- xSemaphoreCreateMutex () API . כשמו כן הוא כי Mutex הוא סוג של סמפור בינארי. הם משמשים בהקשרים ומטרות שונות. סמפור בינארי מיועד לסנכרון משימות ואילו Mutex משמש להגנה על משאב משותף.
ממשק API זה אינו לוקח שום טיעון ומחזיר משתנה מסוג SemaphoreHandle_t . אם לא ניתן ליצור את mutex, xSemaphoreCreateMutex () יחזיר את NULL.
SemaphoreHandle_t mutex_v; mutex_v = xSemaphoreCreateMutex ();
לוקח Mutex:
כאשר משימה מעוניינת לגשת למשאב, היא תדרוש Mutex באמצעות ממשק ה- API של xSemaphoreTake () . זהה לסמפור בינארי. זה לוקח גם שני פרמטרים.
xSemaphore: שם ה- Mutex שיש לקחת במקרה שלנו mutex_v .
xTicksToWait: זהו משך הזמן המרבי שהמשימה תמתין במצב חסום עד שה- Mutex יהיה זמין. בפרויקט שלנו, נגדיר את xTicksToWait ל- portMAX_DELAY כדי שהמשימה_1 תמתין ללא הגבלת זמן במצב חסום עד ש- mutex_v יהיה זמין.
מתן Mutex:
לאחר גישה למשאב המשותף, על המשימה להחזיר את Mutex כך שמשימות אחרות יוכלו לגשת אליו. ה- API של xSemaphoreGive () משמש להחזרת ה- Mutex .
הפונקציה xSemaphoreGive () לוקחת רק טיעון אחד שהוא ה- Mutex שיינתן במקרה שלנו mutex_v.
באמצעות ממשקי ה- API שלעיל, בוא ניישם את Mutex בקוד FreeRTOS באמצעות Arduino IDE.
הסבר על קוד Mutex
כאן המטרה של חלק זה היא להשתמש בצג סידורי כמשאב משותף ושתי משימות שונות כדי לגשת לצג הסדרתי להדפסת הודעה כלשהי.
1. קבצי הכותרת יישארו זהים לסמפור.
# כלול # כלול
2. הכריז על משתנה מסוג SemaphoreHandle_t לאחסון הערכים של Mutex.
SemaphoreHandle_t mutex_v;
3. בהתקנת החלל (), אתחל את המסך הטורי עם קצב שידור 9600 וצור שתי משימות (Task1 ו- Task2) באמצעות ה- API של xTaskCreate () . לאחר מכן צור Mutex באמצעות xSemaphoreCreateMutex (). צור משימה עם סדרי עדיפויות שווים ובהמשך נסה לשחק עם המספר הזה.
הגדרת חלל () { Serial.begin (9600); mutex_v = xSemaphoreCreateMutex (); אם (mutex_v == NULL) { Serial.println ("לא ניתן ליצור Mutex"); } xTaskCreate (משימה 1, "משימה 1", 128, NULL, 1, NULL); xTaskCreate (משימה 2, "משימה 2", 128, NULL, 1, NULL); }
4. כעת, הפוך פונקציות משימה עבור משימה 1 ו משימה 2. בתוך זמן מה של פונקציית המשימה, לפני שמדפיסים הודעה על הצג הסדרתי עלינו לקחת Mutex באמצעות xSemaphoreTake () ואז להדפיס את ההודעה ואז להחזיר את Mutex באמצעות xSemaphoreGive (). ואז תן קצת עיכוב.
בטל משימה 1 (בטל * pvParameters) { תוך (1) { xSemaphoreTake (mutex_v, portMAX_DELAY); Serial.println ("היי ממשימה 1"); xSemaphoreGive (mutex_v); vTaskDelay (pdMS_TO_TICKS (1000)); } }
באופן דומה, יישם את פונקציית Task2 עם עיכוב של 500ms.
5. לולאה בטלה () תישאר ריקה.
כעת העלה קוד זה ל- Arduino UNO ופתח את המסך הטורי.
תראה הודעות שמודפסות מ- task1 ו- task2.
כדי לבדוק את פעולתו של Mutex , פשוט הגיבו על xSemaphoreGive (mutex_v); מכל משימה. אתה יכול לראות שהתוכנית תלויה בהודעת ההדפסה האחרונה .
כך ניתן ליישם את Semaphore ו- Mutex ב- FreeRTOS עם Arduino. למידע נוסף על סמפור ומוטקס, תוכלו לבקר בתיעוד הרשמי של FreeRTOS.
הקודים המלאים והסרטון לסמפור והשתק מובאים להלן.