← FreeRTOS Development on STM32 Microcontrollers

#2 (2025-08-20)

טוען נגן…

תמלול

אז אם אנחנו כבר מסתכלים על המבנה של ה-Git, אז יש לנו את class 0 שהוא ה-main, יש לנו את ה-branch שהוא ה-main, שעליו אני אדחוף ברגע שהכל יעבוד.

ולכל שיעור, למרות שזה אמור להיות class 1, אז יהיה branch משלו, ויתווסף לנו כאן sub module. מה זה sub module? אם אתם לא יודעים זה בסדר, זה קצת קונספט טיפה יותר מתקדם, זה בעצם שימוש שלנו ב-repository בתוך ה-repository.

מה ה-repository שאנחנו משתמשים בו? FreeRTOS, אוקיי? וזה כשבשיעור הבא נוריד את ה-source code בצורה ידנית ונקנפג את זה, אז נעשה את זה בצורה של sub module, כי יש להם באג באתר שלהם, בסדר?

- זה באג באתר שלהם?

- של האתר. אני אסביר גם מה, כי כשעושים ככה, נכנסים ל-repository, עכשיו אנחנו בתוך ה-repository של FreeRTOS, אוקיי? נכון. אז גם פה יכולים להיות עוד sub modules.

אז זה ה-repository הראשי. פה יש הסבר על המערכת הפעלה ששייכת לאמזון והכל, מעולה. ואם אנחנו נכנסים לתוך תיקיית FreeRTOS, אז יש לנו עוד sub repositories, בסדר? וכאשר מורידים מהאתר, אז כל התיקיות האלה מגיעות ריקות.

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

- סליחה, פה איך אני יכול להיכנס ל-GitHub? אני צריך להיכנס ל-GitHub שלך?

- זה דרך אחת, כן. דרך אחת, דרך שנייה - זה פשוט ללכת לאתר, אני עוד שנייה מראה, בסדר? אפשר להיכנס לאתר של freertos.org ו-GitHub. יש להם פה שניים, זה הראשי, זה ה-kernel, תכף נדבר על זה.

טוב, בואו נדבר על מה שאנחנו הולכים לעשות היום.

## נושאי השיעור

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

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

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

ונעשה Real Time Clock setup. איזה Real Time Clock? אל דאגה, Real Time Clock פנימי, אני אראה גם איך אנחנו עובדים איתו.

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

ובסוף נעבור על ה-assignments שיש לכם, ונסביר גם איך מגישים. מי שרוצה להגיש, שוב, זה לא חובה, זה בעיקר בשבילכם.

## סיכום השיעור הקודם

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

אז מה עברנו בשיעור הקודם? עשינו את ההתקנה של STM32Cube וגדרנו קצת את הקונפיגורציה שלו, השתמשנו ב-Git Bash ל-Windows ו-Basic Git Commands. ארי רשם בצ'אט שאפשר גם להשתמש ב-PowerShell.

נכון, האמת היא ש... אני מזמין אתכם לנסות, אגלה שאני ניסיתי את זה פעם ראשונה לא מזמן, כי ידעתי שאי אפשר לעשות את זה בעולם של פעם, והיום כבר אפשר. אז PowerShell של Git, אתם יכולים להשתמש באותן פקודות Git ב-PowerShell, זה לא יראה יפה כמו שאנחנו עושים כאן Git Bash במקום הזה, אבל זה עובד, אוקיי? אז אצלי אותו דבר, git status, באנגלית כמובן, נראה פשוט אפור. בסדר.

אוקיי, אחרי זה דיברנו על... טוב, עוד לא דיברנו על branch management, זה היה בתכנון בשיעור הקודם, אנחנו נעשה את זה בסוף השיעור הזה.

ודיברנו קצת על בורדים של STM32, נרחיב עוד קצת היום.

והתחלנו Operating System Fundamentals - מה נותנת לנו מערכת הפעלה, בגדול, software שעוזרת לנו לנהל את האינטראקציה בין hardware ל-software עצמו.

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

אחר כך דיברנו על סוגים של מערכות הפעלה: single, multi-user, single task, multi-task, ודיברנו קצת על real-time, מה ההבדל בין hard real-time ל-soft real-time, ומה אנחנו משתמשים בשביל מה.

ובסוף דיברנו קצת, והיום אנחנו אפילו עוד קצת נרחיב, על מתי אנחנו משתמשים ב-bare-metal, בלי מערכת הפעלה, ומתי אנחנו משתמשים במערכת הפעלה.

מה היתרונות של מערכת הפעלה: שיש לנו אבסטרקציה של החומרה, כמה שכבות, ותמיכה של המפתחים, במקרה שלנו של FreeRTOS. אבל בגדול FreeRTOS נכנס תחת עולם ה-bare-metal operating system, שזה קצת דבר והיפוכו, אבל אני לא המצאתי את השמות האלה, לא המצאתי את ההגדרה הזאת.

אז FreeRTOS נכנס תחת bare-metal operating system, ואני מקווה שבקורס הזה אתם לא תקבלו רק FreeRTOS, תוכלו להשתמש בכולן. יש לנו עוד כל מיני... μC/OS...

- μC/OS.

- μC/OS, לא זוכר את השם. כן, μC/OS.

- האמת שאני לא השתמשתי בזה, אבל בחברה הקודמת שעבדתי אז כן השתמשו ב-μC/OS ב-Analog Devices.

- כן, מה עוד יש לנו? Mbed, של... שכחתי את השם, מבוסס של ST, ועוד כל מיני מערכות של bare-metal.

ומתי אנחנו בוחרים כל דבר? כאשר יש לנו בעיה של resources, אנחנו נרצה לבחור יותר bare-metal, מה שחוסך לא מעט ב-FreeRTOS. וכאשר יש לנו בעיות של complexity שאנחנו... או בעיה שאנחנו רוצים לפתור ורוצים להכניס הרבה מאוד קוד בזמן מהיר, אנחנו משתמשים במערכת הפעלה מוכנה שנותנת לנו את כל ה-services שלה. אז זה בעצם ה-recap של השיעור הקודם.

- אתה יכול רק במילה להזכיר... אמרת real-time: hard real-time ו-soft real-time?

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

- באפר?

- זהו, יש לנו בעצם איזשהו slack time, בעצם איזשהו זמן שאנחנו יכולים להיות בתוכו.

הדיבור על מערכות הפעלה, מערכות הפעלה שהן soft, לדוגמה זה ה-kernel של Linux, למרות שיש לנו כל מיני scheduler-ים... טוב, זה כבר הקורס של Linux, למרות שיש לנו כל מיני scheduler-ים שנותנים לנו deadlining ונותנים לנו הבטחות, זה עדיין נחשב soft real-time.

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

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

## OS מול Bare-Metal Programming

נמשיך בערך מאותה נקודה, תתקנו אותי אם אני טועה, אנחנו סיימנו פה ב-Operating System מול Bare-Metal Programming, אוקיי? מתי אנחנו, איך אנחנו משתמשים בכל דבר, ומתי זה bare-metal.

Bare-metal זה כאשר אנחנו ניגשים ישירות לחומרה, כותבים ממש לתוך הרגיסטרים. אנחנו נראה קצת את ה-reference manual היום, אבל ה-reference manual היום הוא יותר לדעת מה ה-features, פחות לכתוב את הספריות, זה קורס אחר.

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

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

זה מה שה-MCU נותן לנו, וההתנהגות היא מאוד דטרמיניסטית, זאת אומרת שאנחנו יכולים לצפות מתי יקרה משהו, אוקיי? אנחנו לא יכולים להניח שיהיה לנו איזשהו preemption, אוקיי? אנחנו יכולים להניח שהזמן שביצוע הפעולה יקרה הוא משהו שאפשר לחזות, אוקיי? והדטרמיניסטיות הזאת היא משהו שקובע hard real-time.

יש לנו גם אפליקציה אחת, לרוב אנחנו נעשה משהו אחד ואנחנו נריץ אותו ב-main loop, אוקיי? וגם שם יש לנו אינטראקציה בין שני קונטקסטים אם אנחנו לא מימשנו שום scheduler - נדבר בלי ממש scheduler - יש לנו שני קונטקסטים: יש לנו את הקונטקסט של interrupt, ויש לנו את הקונטקסט של ה-thread mode, אוקיי? זה נקרא thread mode.

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

- אור, שאלה. אם יש לנו interrupt handler, זה לא בעצם אומר ש... יש לנו מערכת הפעלה? שכאילו...

- שאלה טובה, שאלה טובה מאוד. בגדול, כשאנחנו נכנסים לתוך ה-interrupt handler, זה אומר שמשהו הכניס אותנו לשם. מי זה הדבר הזה שמכניס אותנו? זה ה-NVIC, Nested Vectored Interrupt Controller. ה-NVIC...

- כאילו אם שאלתי קדימה או משהו, אז זה בסדר, אל תתייחס לזה, יותר מאוחר...

- תוכל להראות לי את זה כשאני אדבר על המסמכים, אני באמת ארצה להראות לכם איך מורידים מסמכים ואיך מסתכלים. אבל מה שמכניס אותנו לתוך ה-interrupt handler זה הרכיב שנמצא קרוב ל-core שלנו, שהוא מקפיץ אותנו לתוך הקוד של ה-interrupt-ים. בעצם מה שהוא עושה, הוא עוצר את ה-execution של ה-CPU שלנו שנמצא בתוך המיקרו-קונטרולר, ומקפיץ את ה-execution לתוך ה-interrupt handler, אוקיי?

- אבל הקוד נמצא... הקוד לא נמצא באותו מקום? כלומר, אם אני כותב את הקוד ואני ממפה אותו לזיכרון של הקוד, זה לא באותו מקום?

- כן, אבל מה שמקפיץ אותך לשם זה event, event חומרתי.

- כן, זהו. אבל אני...

כאילו מההיכרות שלי, אירוע חומרתי, אה כן, כאילו בוא נקח PC, זה מקפיץ את הקרנל. הקרנל אומר "אה קרה ככה וככה", ואז הוא מקפיץ את ה-interrupt handler המתאים לאירוע.

אוקיי.

אז אתה אומר זה לא... אין פה קרנל, ואין פה... זה בעצם, מה מקפיץ?

רגע. מה שמקפיץ את הקרנל זה ה-interrupt controller. ה-NVIC זה interrupt controller.

אוקיי, גם ב-PC שלנו, גם ב... מה שלא... כאילו גם של ARM, גם של Intel, יש interrupt controller-ים. ו-interrupt controller-ים הם מחוברים לתוך איזשהו event, זה יכול להיות מקלדת, כאילו כתיבה של מקלדת, או זה, והוא מקפיץ אותך לתוך ה-interrupt handler, אבל זה נעשה חומרתי. זה לא... זה שיש מיפוי בתוך הטבלה ב-Linux, זה מיפוי שהוא מכניס אותך אחרי שהיית בתוך ה-interrupt handler. אתה לא קופץ ישירות לשם. לעומת זאת, ARM במיקרו-קונטרולרים שה-NVIC מכניס אותך ספציפית לתוך ה-interrupt handler. בסדר? זה... זה יהיה ברור יותר מאוחר.

אוקיי, כאילו זה עובר פחות ידיים אם אני מבין אותך נכון.

לא.

לא? אוקיי.

אני אסביר את זה ממש... אנחנו נעשה שנייה השוואה בין שני... בין... בין אמ... איך קוראים לזה? APU, לא APU... Microprocessing Unit, MPU, לבין micro-controller. Microprocessing Unit, ה-interrupt handler שלו הוא... ה-interrupt handler שלו הוא לרוב לא יהיה ה-NVIC, למרות שיש אם אנחנו מדברים על ARM, הוא יהיה איזשהו interrupt controller. או interrupt controller של X86, או interrupt controller של הארכיטקטורה שאנחנו משתמשים בה.

ה-interrupt controller הזה, כל מה שהוא עושה, יש לו בעצם רגלי interrupt שמחוברים בגדול לאיזשהו... בגדול זה MUX, שיכול להתחבר לעוד כמה MUX-ים, אבל בגדול זה MUX, שכל אחד מה-event-ים שמגיע, אז הוא מקפיץ איזושהי רגל חומרתית, וברגע שזה קורה, ה-interrupt controller מגיע לתוך ה-CPU, כי במקרה של MPU-ים זה כן CPU, מגיע לתוך ה-Microprocessing Unit או ה-processing unit, וה-processing unit קופץ לתוך הנקודה שממנה אנחנו מעבדים את ה-interrupt. במקרה שאמרנו Linux, אז ה-Linux יודע שאם קפצה הרגל הזאתי, היא קיבלה את הקוד הזה, אז הוא מקפיץ אותך ל-interrupt handler, שזה ה-interrupt handler הוא כבר תוכנתי של קוד מסוים בתוך טבלת ה-interrupt-ים שלו.

הטבלת interrupt-ים הזאתי היא דינמית, היא מקומפלת כשאתה בונה את ה-Linux. בתוך המיקרו-קונטרולרים שלנו הטבלת interrupt-ים היא סטטית, והיא נקבעת על ידי ה-manufacturer, אוקיי? כבר הכל baked in לפנים, לדוגמה interrupt של USART data, אוקיי? interrupt של... של timer כלשהו, הם כבר baked in וכבר יש להם אינדקס קבוע, האינדקס הוא לא נקבע בטבלה בקומפילציה, הוא כן קיים בקומפילציה. זה קצת שונה, אני יכול להראות את זה. זה פשוט יותר בקורס של ה-Bare-Metal, אז... אז זה לא מערכת הפעלה. בסדר?

## OS vs Bare-Metal Programming

סבבה. אני גם אראה את זה בכיף. מה אנחנו מקבלים ממערכת הפעלה? אנחנו מקבלים אבסטרקציה על ה-hardware, בעצם אנחנו יכולים לפתח הרבה יותר מהר כי אנחנו משתמשים ב-API שנותנת לנו מערכת ההפעלה. עבודה עם כמה אפליקציות, זאת אומרת שאנחנו לא מריצים רק תוכנית אחת, אנחנו יכולים להריץ כמה אפליקציות במקביל או כמה תוכניות במקביל. שיתוף בין משאבים, בעצם יש לנו כל מיני מנגנונים של שיתוף מידע ושיתוף של device-ים שאנחנו בעצם משתפים גם בצורה בטוחה יותר, אני אזרוק קדימה, אבל לדוגמה לוקים, וגם שיתוף בין מידע, שזה... שכחתי את הראשי תיבות של זה, אבל זה בעצם מנגנונים שאנחנו מקבלים כשיתוף מידע כמו באפרים, קיוים מתוזמנים וכולי.

איסולציה של קוד, בעצם כל אפליקציה לא מקריסה את כל המערכת, היא יכולה להקריס רק את עצמה. ואפשרות לפתח מהר יותר, productivity של development. בעצם אנחנו לא מפתחים כל דבר מאפס, ולא לכל דבר צריך להיות לו דרייבר, אנחנו מפתחים קוד בצורה הרבה יותר מהירה. תחשבו על זה קצת כמו ההשוואה, ואני יודע שזו לא אותה השוואה, כמו לפתח קוד ב-C, ולדוגמה לפתח קוד ב-Python. ב-Python אני יכול פשוט להשתמש בכל מיני ספריות שפשוט מוכנות, ב-C לרוב אני מפתח מאפס, אלא אם כן כבר קיבלתי, אבל מישהו צריך לפתח מאפס. לדוגמה ב-Python אנחנו פשוט משתמשים ב-linked list, ב-C הרבה פעמים מפתחים linked list מאפס. אז זה רק בעצם ההשוואה, שבהשוואה הזאתי Bare-Metal זה C, ו-Operating System זה Python, אבל את הכל אנחנו עושים ב-C בקורס.

## When to Choose Each Approach

מתי אנחנו בוחרים בכל דבר? אז אני רואה שכן הכנסתי את זה כאן, בסדר. כאשר יש לנו בעצם אין לנו הרבה משאבים, יש לנו CPU שהוא חלש, או CPU שהוא מתאים בדיוק, לא בדיוק חלש, מתאים בדיוק למקרה, ואין לנו הרבה זיכרון. הזיכרון שלנו ב-Flash וב-SRAM הוא יחסית מוגבל, ואנחנו לא רוצים לבזבז אותו על איזשהו overhead של מערכת הפעלה, בעצם עוד קוד שירוץ. כאשר אנחנו בעצם ברמה שהיא מאוד קריטית, שאנחנו בעצם צריכים hard timing, ואנחנו לא רוצים לפחד שבטעות נפספס את המועד, מה שמערכת הפעלה יכולה לגרום אם היא לא מקונפגת כמו שצריך, ואם כן, אז לא. ואפליקציות בסיסיות יותר, כאשר יש לנו purpose אחד, כבר אמרנו את זה, ו-Bare-Metal יותר טוב לנו ל-battery-powered devices.

אני אסביר למה זה נכון, כי בגדול, כל פעולה, כל assembly instruction או machine code instruction שאני מבצע שלא לצורך, זה מבזבז לי אנרגיה. אם אני ברמה הזאתי, אז זה מאוד רלוונטי למערכות שהן מאוד battery-based. אבל אם אני מדבר בהכללה, אז עצם זה שאני שולח בייט והולך לישון, או שאני עכשיו קורא לשכבה שקוראת לעוד משהו שבודק כל מיני דברים ואז עושה את זה, אז יש לי בעצם פה איזשהו overhead שמבזבז לי סוללה, תכלס. CPU שווה הרבה פעמים סוללה.

מתי אנחנו רוצים להשתמש במערכת הפעלה? כאשר המערכות שלנו הן יותר קומפלקסיביות, יותר מסובכות. זאת אומרת שאנחנו צריכים כמה task-ים שרצים במקביל, לדוגמה יש לנו task של logger שרץ במקביל, או יש לנו שני task-ים: task אחד צריך למשוך מחיישן, וה-task השני צריך להדפיס דאטה למסך, אז אנחנו עושים דברים כאלה במקביל, ואז אנחנו צריכים משהו יותר מעניין. ואז אנחנו צריכים אינטראקציה קצת יותר מסובכת מה-user, זאת אומרת לא כפתור, לא LED, אלא אנחנו פתאום צריכים לקבל capacitive touch או whatever, אז אנחנו מקבלים interface קצת יותר מסובך מה-user, שה-interface מצריך task משלו. זה לדוגמה multi-tasking של user. וכאשר אנחנו רוצים לעבוד ב-network connectivity, זאת אומרת שיש לנו איזשהו stack של network שעכשיו רץ ומעביר לי פקודות, זאת אומרת שהוא לא חייב לרוץ ב-context הראשי, באפליקציה, הוא יכול בעצם להכניס פקודות לתוך buffer ואני יכול למשוך אותם, כמו לדוגמה socket, אפשרות של socket, ופיתוח מהר יותר, כרגיל.

## What is General Purpose OS (GPOS)?

אז מה זה GPOS? General Purpose Operating System. בעצם זו מערכת הפעלה שהיא נועדה לרוב האפליקציות שקיימות בעולם, אוקיי? לדוגמה, IDE - זו סביבת פיתוח, זו אפליקציה גנרית, היא לא צריכה לרוץ ספציפית על מיקרו-קונטרולר או ספציפית על חומרה ייעודית בשבילה, והיא לא צריכה עכשיו לעשות דברים ב-real-time. עוד לדוגמה: browser, אפליקציה כזאתי, או אם אנחנו כבר מדברים על דברים טיפה פחות, אז מסופון של אשראי, זו דוגמה שאפשר להשתמש בה. בגדול, זה מצריך מאיתנו מערכת הפעלה גנרית, שהיא פשוט נותנת לנו תמיכה בכל מיני מאפיינים שאנחנו יכולים להשתמש בהם.

כמו multi-user support, זאת אומרת שאנחנו כבר יכולים לעבוד עם כמה יוזרים, סיסמה וזה, resource sharing, זאת אומרת שאנחנו יכולים לעבוד עם שיתוף של ה-CPU גם על הזיכרון וגם על כמה device-ים, זיכרון וירטואלי, אם אנחנו רוצים להגן על process-ים או תוכניות מהתנגשויות בין תוכניות אחרות או malicious code, לא עלינו, file system, בעצם אפשרות לעבוד עם קבצים ולשמור דברים, בעצם אלו פעולות גדולות יותר, למרות שיש הרבה מערכות real-time שהן גם חזקות, אבל בעצם לעשות פעולות יותר מתקדמות. device driver-ים, כל מיני דרייברים שאנחנו צריכים תמיכה אליהם, לדוגמה מקלדת, לא בטוח שאני צריך מקלדת כשאני עובד כשיש לי כרית רכב, בוא נקרא לזה ככה, ו-user interface שהוא טיפה יותר זה. לדוגמה למערכות כאלה אז Windows, macOS, וכל מיני טעמים של Linux שהם לא...

כל מיני טעמים של Linux, ש-Linux משחק פה כמה תפקידים, אז לדוגמה טעמים של Linux אז Ubuntu, שהוא לא Ubuntu למערכות real-time, כי יש להם גם מערכות Ubuntu ל-real-time. עוד מערכות כאלה, אז Android או iOS, זה General Purpose Operating System. ול-server-ים, אז יש לנו את Windows Server או distribution של Linux Server, שוב, Ubuntu.

מה המטרה של המערכות האלה? Fairness, בעצם כל ה-process-ים מקבלים איזשהו CFS, Completely Fair Scheduler, הוא מחלק להם את ה-CPU בצורה שווה ושווה, אין לנו העדפות, אין לנו preemption, אין לנו כל מיני דברים כאלה. throughput, אנחנו מנסים להגדיל את ה-throughput של המערכת ולא לפגוע בזמן, אנחנו רוצים להגדיל את ה-productivity. ו-user interface, בעצם יש לנו CPU-ים בתוך המערכת שהם ספציפיים ל-user interface, נותנים לנו בעצם multi-tasking יותר נוח. ו-compatibility, הם עובדים על הרבה מאוד מכונות, על הרבה מאוד ברזלים, לא רק על ברזל ספציפי או משפחה ספציפית, על הרבה מאוד ברזלים בלי יותר מדי hardware constraint, בעצם בלי להיות ספציפיים לחומרה כלשהי.

## What is Real Time Operating System (RTOS)?

מה זה בעצם מערכת הפעלה שהיא real-time? מערכת הפעלה real-time היא בעצם נועדה לתת לנו אפליקציות real-time שה-data וה-responsiveness שלהם צריכים להיות strict-יים, צריכים להיות בזמן, מה שנקרא.

מה ה-feature-ים שאנחנו מקבלים? אז preemptive multi-tasking. מה זה preemptive multi-tasking? זה אומר ש-task עם priority גבוה יותר עוצר task-ים עם priority נמוך יותר כדי לרוץ בעצמו, ככה שאנחנו לא מאבדים את זמן המטרה. scheduling deterministics, זאת אומרת שה-task slicing, שנדבר עליו כנגיע אליו עוד רגע, הוא באיזשהו זמן שהוא פרדיקטיבילי, וזה זמן שאני יכול לחיות איתו כשאני בעצם יכול לעבור בין task-ים. context switching מהיר, בעצם אני עובר בין task-ים בצורה שהיא מהירה ושניתנת לחזות בזמן. priority-based scheduling, זה מתקשר ל-preemptive, אבל לא רק, כי יש לנו גם לוקים, אבל נדבר על זה גם. task שהוא בעצם רץ לפי ה-priority, זאת אומרת ש-task עם priority גבוה יותר ירוץ לפני task עם priority נמוך יותר, וה-scheduler מחליף לי את ה-task-ים, אז הוא קובע מה ה-task שירץ.

interrupt handlers, זאת אומרת שה-interrupt-ים הם נעשים ב-interrupt latency שהוא נמוך, בעצם הזמן מאז שקרה ה-event החומרתי עד שאני בעצם מבצע את הקוד שלו, אוקיי? כי אמרנו שוב, אמרנו שה-NVIC, ה-Nested Vectored Interrupt Controller, הוא זה חומרה שפשוט מקפיצה את הקוד בזמן שהוא ידוע מראש. פשוט מקפיצה לנו את ה-execution לפי ה-priority שאנחנו מכניסים לה. memory management, בעצם ניהול זיכרון דטרמיניסטי, זאת אומרת שאני לא יכול... שאני מקבל את ההקצאות שאני רוצה בזמן ליניארי, ולא בזמן שהוא לא ידוע מראש, וגם השחרור שלי הוא ליניארי, וכשנדבר קצת יותר מאוחר אנחנו נמשיך את זה.

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

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

Soft real-time בעצם כשאנחנו נפספס את ה-deadline, אנחנו רק נפגע באיכות של המערכת, אבל זה לא קטסטרופה כמו לדוגמה מולטימדיה. זאת אומרת שעכשיו אנחנו מסטרימים משהו עם FFmpeg או מסטרימים משהו בכללי, וההזרמה הזאתי שאנחנו עושים, אז פתאום יש לנו איזשהו task שצריך לרשום הרבה, או שמילא לנו איזשהו זיכרון, והזיכרון הזה צריך לעשות flashing שלוקח לנו קצת... סתם נותן דוגמאות לאירועים שיכולים לקרות, והזיכרון הזה פתאום עושה flashing שלוקח לנו עיבוד, אז איכות האודיו והווידאו קצת נפגעת. זה לא כזה נורא, אבל אנחנו עדיין משדרים כמעט יחסית בזמן אמת.

ו-firm real-time, זאת אומרת שהוא יכול לפספס מדי פעם וזה בסדר, אבל הרבה פספוסים זה כבר קטסטרופלי. זאת אומרת שהפספוס אחד הוא לא קטסטרופלי, אבל n פספוסים זה כבר קטסטרופלי, ולרוב במקרה כזה אנחנו עושים reset או מה שאנחנו עושים.

שאלות?

## What is a Task?

אז מה זה task?

Task, שבקונטקסטים מסוימים יכול להיקרא גם thread וגם process, זה בעצם יחידת execution עצמאית שמריצה או מייצגת פונקציונליות ספציפית או איזשהי activity בתוך המערכת. לדוגמה, אנחנו הולכים לעשות task כזה היום, logger task או led task, שהוא כל המשמעות שלו זה להבהב לנו לדים. בסדר, זה ה-tasks.

מה המאפיינים שלו?

Independent execution: כל task רץ בקונטקסט שלו, אוקיי? הוא רץ בלי קשר לקונטקסטים אחרים, אלא אם כן יש איזשהו מנגנון של IPC, אוקיי, זה מה ששכחתי, Inter-process Communication. אז IPC זה גם משהו שמספקת לנו המערכת הפעלה, שזה אחת השיטות של ה-IPC.

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

משאבים משותפים: אנחנו יכולים לשתף כמה משאבים ל-tasks, לדוגמה זיכרון או עוד משאבים שאנחנו יכולים לשתף בין tasks, יכולים להיות חומרה. לדוגמה, ששני tasks יוכלו לשלוח הודעות ב-UART, אוקיי? זה דוגמה מסוימת.

איזה tasks לדוגמה אנחנו מכירים במערכות embedded? אז task שקורא סנסורים בצורה פריודית, communication task, task שבעצם שולח דאטה בין מערכות, display update, בעצם מרפרש לנו את ה-LCD אם אנחנו עובדים עם LCD, ו-button handler task, זאת אומרת שאם הכפתור שלנו לא הגדרנו אותו חומרתית, אז אנחנו יכולים לעשות עליו polling בתוך task.

אוקיי, זה ברור מה זה polling?

- לא.

Polling זה כאשר אני מתשאל איזשהו resource באופן קבוע. כמו לדוגמה, אני רוצה לתשאל איזשהו pin האם ה-state שלו הוא 1, אז אני הולך כל X זמן ומתשאל אותו. אני יכול לעשות את זה בתוך context של task.

Task נוסף שהוא task מאוד חשוב, watchdog. Watchdog זה כלב השמירה של המערכת. ה-watchdog הוא בא כל כמה זמן לבקש אוכל. אם אנחנו מאכילים אותו, הוא לא מרסט לנו את המערכת. אם לא הכלנו אותו, אז הוא יודע שה-CPU שלנו עמוס והוא מרסט אותנו. אז watchdog זה task שאנחנו משתמשים בו, ונתתי את האימפלמנטציה הבסיסית, אבל אפשר גם להרחיב אותו. ה-watchdog יכול לבדוק כמה זיכרון נשאר לנו, איפה אנחנו ב-stack, האם היה לנו stack overflow בנקודה מסוימת, ויכול לעשות הרבה דברים כאלה מתוך ה-MCU או מתוך ה-CPU עצמו, מתוך היחידה הלוגית שלנו, ה-Cortex-M4 במקרה של הבורד שלי היום, ה-F411RE.

## What is a Scheduler?

מה זה scheduler?

Scheduler זה בעצם קומפוננטה של מערכת ההפעלה שכל המשמעות שלה זה להחליט איזה task ירוץ ומתי להחליף בין tasks, אוקיי?

מה הוא צריך לעשות? הוא צריך להחליט איזה task רץ ברגע זה ואיזה task ירוץ הבא, הוא אחראי על context switching. מה זה context switching? Context switching זה בעצם שמירה של המצב של ה-task שהופסק, שנעצר, השמירה של המצב שלו כדי שנוכל לחזור כשיהיה תורו שוב, ומעבר ל-context של ה-task הבא. אוקיי, בעצם אנחנו עושים save ל-task הנוכחי ו-load ל-task הבא, ואז כשה-task הבא ירוץ, הוא יוכל לרוץ מאותה נקודה שהוא הפסיק. ואז אנחנו לא מאבדים את ה-context.

Priority management: אז אם יש לנו באמת scheduler שהוא לא CFS-י, שהוא לא completely fair, אז אנחנו צריכים לנהל priority. אז הוא מבטיח לנו ש-task או process עם priority גבוה יותר יקבל את ה-CPU קודם.

חלק נוסף, יש לנו time management, שהוא בעצם מנהל את החיתוך של הזמן של ה-scheduling הפריודי. זאת אומרת שהוא מחליט כל כמה זמן אנחנו מחליפים task, או כל כמה זמן אנחנו בודקים אם צריך לרוץ task אחר. Slicing, עוד שנייה נדבר גם על זה.

אלוקציה של משאבים: בעצם הוא מחליט כמה משאבים יקבל כל task אם החלוקה שלנו היא דינמית.

שימו לב שמה שציינתי, לא כל scheduler חייב שיהיה לו את זה. יש לנו schedulers שהם רק של משאבים, יש לנו schedulers שהם רק של task selection, ויש לנו schedulers של הכול. אני אתן דוגמאות לסוגים של schedulers.

ה-scheduler של ה-tasks שלנו, הוא מחליף לנו בין tasks, אבל לא חייב שיהיה לו priority.

Scheduler של לדוגמה אנטנה סלולרית, הוא מחליט כמה רוחב פס נקבל בכל רגע. אז הוא מחליט שכל user מקבל את זה, הוא לא מחליט איזה task רץ. אוקיי, אז יש כמה סוגים של schedulers.

שאלה על schedulers?

- מה ההבדל בין הראשון, ה-preemptive ל-priority-based?

Preemptive ו-priority-based הם כמעט אותו דבר. Priority-based זאת אומרת שבזמן ה-slice, בזמן הבדיקה, אנחנו בודקים אם יש task עם priority גבוה יותר והוא רץ, אוקיי? או ש-task עם priority גבוה יותר יכול לעצור task עם priority נמוך יותר. זה preemption בכללי.

מתי עוד אנחנו יכולים לעצור אם יש לנו את אותו priority? זאת אומרת שאין לנו priority במערכת, אם יש לנו נעילות על המערכת. אנחנו בעצם נעלנו tasks מסוימים או השאנו tasks מסוימים לפי sleep, ואז יש לנו עדיין preemption, פשוט הוא לא מבוסס priority.

אז preemption זה פשוט עצירה של task שרץ ברגע זה, priority זה הקביעה...

- זה תמיד על ידי interrupt?

לא, זה לא חייב להיות על ידי interrupt, זה יכול להיות גם שחרור voluntary של ה...

- ה-task משחרר את עצמו?

כן.

- כלומר ה-task משחרר את עצמו ואז זה חוזר ל-scheduler והוא זה שמבוחר...

נכון.

- אוקיי.

הוא גם יכול לשחרר את עצמו לזמן מסוים כמו sleep.

- אוקיי.

עוד שאלות על סוגים של schedulers? כי זה די חשוב להבין. הבנו שיש לנו scheduler של bandwidth לדוגמה, ו-scheduler של process, נכון? של execution. יש לנו כמה סוגים של schedulers. אנחנו יכולים לעשות scheduler לכל דבר.

- אפשר להסביר בקצרה את כל הסוגים?

אפשר לעשות scheduler לכל דבר. אם אנחנו מחלקים כל דבר לפי זמן ואנחנו יכולים לחלק אותו, זה בגדול מה שעושה scheduler.

לרוב כשמדברים על scheduler, מדברים על scheduler של tasks, אוקיי? שזה מה שמכריע איזה task רץ כרגע. מה שאמרתי, שיש גם scheduler של משאבים, אוקיי? הוא קובע לדוגמה, אני חוזר לאותה דוגמה כי היא הכי טובה, איזה task יקבל bandwidth של רשת, לדוגמה כמה פקטות כל task יקבל אם אנחנו בעצם מעבדים פקטות של רשת.

- אוקיי.

Scheduler כזה לדוגמה, זה scheduler של אנטנה סלולרית. אנטנה סלולרית מחליטה איזה טלפון יקבל כמה גלי רדיו, נקרא לזה ככה, לפי ה-scheduler שלו. בסדר?

- מובן.

סבבה.

אז על סמך מה ה-scheduler מחליט מי ירוץ כרגע?

לפי tasks עם priority, לפי task state, ובעצם רק tasks עם state של running כרגע יכולים לרוץ. לפי system events, interrupts ספציפיים מעלים priority של task לזמן רגעי, או שמבקשים לעשות scheduling כרגע, בעצם קוראים לעשות scheduling לא ב-time slice, לדוגמה.

קיבלנו עכשיו datagram ב-UART, זאת אומרת קיבלנו byte ב-UART וה-interrupt handler שלנו קפץ, אז קפצנו לתוך ה-interrupt של ה-UART, וכל מה שאנחנו עושים מתוך ה-interrupt handler של ה-UART זה רק עושים schedule ל-scheduler שלנו. מה זה אומר? זאת אומרת שה-scheduler יעלה, יחליט מי ה-task הבא שירץ, וכשנחזור מה-interrupt אנחנו כבר נרוץ משם.

אוקיי? אם אתם מאוד סקרנים לגבי schedulers, אנחנו עשינו באחד הקורסים, גם אמור להיפתח בהמשך, בקורס של ה-bare metal, אנחנו מימשנו scheduler. אם אתם רוצים קצת לדעת איך זה, אז תדברו עם שחף והוא יקשר אתכם ויגיד לכם מתי נפתח הקורס הזה שוב. בסופו של דבר אנחנו באמת מממשים scheduler.

ועוד סוגים של schedulers מבוססי זמן. דיברתי על ה-scheduler deadline של Linux, שה-scheduler deadline של Linux בגדול, כשאני קובע איזשהו task, אני אומר בעוד כמה זמן או כל כמה זמן הוא צריך לרוץ, וה-task הזה כל X זמן או בעוד X זמן יובטח לנו שיהיה רץ. אז זאת אומרת שאנחנו לפי ה-scheduling עצמו.

איזה סוגים של schedulers אנחנו מכירים? יש לנו כל מיני ערבובים.

Preemptive: זאת אומרת ש-task שרץ כרגע יכול להיעצר בשביל tasks עם priority גבוה יותר.

Non-preemptive: זה task שהוא רץ עד שהוא משחרר את ה-CPU שלו, אוקיי? זאת אומרת שהוא נכנס ל-sleep או שהוא נכנס לתוצאה של schedule, עד שהוא לא ויתר על ה-CPU אף אחד לא עוצר אותו.

Round Robin: לפי זמן קבוע אנחנו עושים slicing בין ה-tasks.

ו-Priority-based: שזה בעצם מה שאמרנו, שתמיד ה-task עם ה-priority הגבוה ביותר הוא זה שרץ.

## What is a Context?

מה זה context?

אם אני לא עוצר ולא הגעתי להפסקה ואתם מרגישים שאתם גמורים, תעצרו אותי, כי אני לא תמיד מסתכל על השעון, אוקיי?

מה זה context?

Context זה בעצם state של task שממנו אני יכול להמשיך לרוץ בזמן מסוים, אוקיי? לדוגמה, הייתי בשורה מסוימת בקוד, זאת אומרת שה-program counter שלי... אנחנו עוד שנייה נסביר מה זה program counter... ה-program counter שלי ביצע נקודה ספציפית, וכל הפרמטרים שהשתמשתי בהם, כל ה-registers עזר שלי היה בהם מידע מסוים, והפונקציה שאליה אני אמור לחזור נמצאת ב-link register, שזה ה-register שממנו אני חוזר לפונקציה. כל אלה נמצאים במצב מסוים. אוקיי? זה ה-context שלי.

מה יעשה ה-scheduler ל-context הזה? הוא ישמור אותו במקום מסוים, אני בכוונה לא אומר עדיין איפה, הוא ישמור אותו במקום מסוים ויביא את ה-context של task אחר. זאת אומרת שה-task יכול לרוץ בדיוק מהנקודה שהפסיק בתצורה. זה לא רק להיות במקום, זה גם להיות באותו state of mind, תחשבו על זה ככה. אוקיי? אז זה context.

איך נראה ה-context ברמת ה-core שלנו, ברמת ה-CPU?

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

התוכנית לתוך הרג'יסטרים האלה. לדוגמה, משתנים לוקאליים שאנחנו רוצים להשתמש בהם. אנחנו רוצים לחבר שני... אנחנו רוצים לחבר את A ואת B. אז אני אטען את A ל-R1, ואת B ל-R0. ואני אעשה פקודת assembly שלוקחת את שניהם לתוך אחד.

אוקיי?

אז בשביל זה אני צריך את ה-general purpose registers.

יש לנו את ה-Stack Pointer שהוא אומר באיזה נקודה בתוך ה-stack, באיזה נקודה בתוך ה-call stack אני נמצא, אוקיי? כדי שאני אוכל לחזור לשם.

יש לנו את ה-Link Register שהוא מכיל את הכתובת של הפונקציה שממנה נקראתי. זאת אומרת שכאשר אני אסיים את הפונקציה אני אוכל לחזור לשם.

יש לנו את ה-Program Counter, שה-Program Counter הוא מצביע ל-instruction בזיכרון שאותו אני מבצע כרגע. אוקיי? זה ה-Program Counter.

את ה-Status Register ששומר מה קורה ברגע זה, האם אני עושה פעולת חיבור, האם יש לי overflow וכולי, ו-Floating Point Registers שהם מבצעים לי פעולות של floating point. זה בעצם יחידה שמבצעת פעולות של floating point, של נקודה עשרונית.

הם ברצף. R0 עד R12 זה ה-general purpose registers.

תזכירו לי, אני אראה לכם את הכל. אם מישהו רושם, תנסו לרשום את זה, אני אראה לכם את הכל, איפה משיגים את המסמך של כל דבר.

Memory-mapped registers... לא ב-Cortex-M. אולי... אולי אנחנו מדברים על דברים ספציפיים. לא, זה בהקשר של הקור, זה של הקור עצמו.

אוקיי?

מה ה-Context Execution? יש לנו:

Active Context - זאת אומרת שה-context של ה-task כרגע רץ.

Saved Context - זאת אומרת שהוא נשמר לתוך הזיכרון. בגדול הוא נשמר לתוך stack ספציפי של ה-task, לתוך כל task יש stack משלו.

Context Integrity - בעצם זה מה ששומר לנו את כל הדאטה ומבטיח שהם נשמרים כמו שצריך.

אז כאשר אנחנו קופצים לתוך interrupt, נניח שיש לנו context אחד יחיד שאנחנו עובדים ב-bare metal, אנחנו רצים ב-context ראשי, אוקיי? ה-context הראשי הזה יש לו את כל הרג'יסטרים שכאן מוגדרים, אוקיי? ואז... ואז זה קורה חומרתית, זה לא מערכת הפעלה ולא כלום. ואז ה-NVIC מזהה שיש לנו interrupt, אוקיי? אז ברגע שה-NVIC מסמן ל-CPU, ל-core שלנו, ה-ARM Cortex-M משהו שלנו, אז הוא שומר את כל הרג'יסטרים האלה לתוך ה-stack שהוא רץ איתו כרגע, והוא קופץ ל-stack אחר, יש לו שני stack-ים, יש לו MSP ו-PSP. ה-stack האחר הזה הוא stack שהוא יכול להשתמש בו כדי להריץ את ה-context של ה-interrupt. בגלל זה זה קצת מבלבל, אבל interrupt זה איזשהו context אחר.

אוקיי? אודי, תגיד לי אם זה עזר לך קצת יותר לשאלה שלך, אם לא יש לנו עוד קלריפיקציה לעשות.

אני מדבר על ארכיטקטורה של מיקרו-קונטרולרים. בגדול כל הארכיטקטורות... כל הארכיטקטורות של מיקרו-קונטרולרים עובדות ככה.

אם אנחנו בלי מערכת הפעלה יש לנו שניים, נכון. אם אנחנו עם מערכת הפעלה אז לכל task יש באמת stack משלו.

לפי מה שנתקדם.

איתן, אתה פחות ברור, תתחיל שוב.

תרגיש חופשי לקטוע, הכל בסדר.

מה הכוונה? זאת אומרת שיש לנו שני context-ים, נכון? יש לנו context אחד שהוא ה-context הראשי שלנו, שזה ה-context של האפליקציה, כל הלוגיקה שלנו נמצאת שם. כאשר אנחנו בתוך interrupt, לדוגמה GPIO הרגיש אחד, אוקיי? הרגיש אחד לוגי, אז אנחנו קופצים לתוך ה-context של ה-interrupt handler, בעצם ה-NVIC מקפיץ אותנו לתוך קוד, מקום ספציפי, בעצם הוא לוקח את ה-Program Counter, סליחה, הוא שומר את כל המידע הזה בתוך מבנה נתונים שנקרא stack, אוקיי? הוא דוחף אותו לביפנים, ואז הוא הולך ומחליף את ה-Program Counter להיות ה... זה קצת אפסטרקציה, הוא לוקח את ה-Program Counter, ועל ה-Program Counter הזה הוא שם עליו את הערך של ה-instruction הבא, שה-instruction הבא הולך להיות ה-interrupt handler.

מעביר את הכתובת לנקודה הספציפית בקוד, אוקיי?

אחת ראשית. אחת ראשית ואחת ל-interrupt-ים.

לא, לא. אז תחשוב שלפני שהיה interrupt הייתי באמצע לעשות משהו, אוקיי? הייתי באמצע לחבר שני פרמטרים, להזיז זיכרון, הייתי באמצע לעשות משהו כזה, נכון? עכשיו בא interrupt, הוא עצר אותי והוא מעביר אותי ל-מקום אחר. אני אצטרך לחזור...

לא לאפליקציה, ל-interrupt handler, אוקיי?

## Context Switching

אני... אולי אני אראה לכם שניה קוד.

אני מסתייג כי הוא לא, אבל אני שניה אני אראה פה איזשהו interrupt handler כדי שיהיה ברור מה זה interrupt handler. Interrupt handler זה איזושהי פונקציה שנגמרת במילה Handler. היא בעצם כאשר אנחנו מזהים איזשהו event ספציפי, לדוגמה במקרה של HardFault, המערכת לא... יש לה שגיאה. אז ברגע שהמערכת מזהה שיש לה שגיאה, היא תקפוץ לכאן, אוקיי? לא משנה מה קרה. אז עכשיו היא רצה ב-context הזה.

עכשיו HardFault זה לא דוגמה טובה כי מ-HardFault לא חוזרים, נכון? אבל אם אנחנו לדוגמה ב-SysTick, אוקיי? שזה interrupt handler שקורה כל זמן קבוע, אוקיי? כל X ticks אנחנו קופצים לכאן. סיימנו לעשות את מה שקרה כאן, אנחנו... זו לא פונקציה רגילה, אנחנו צריכים לחזור, להחזיר את ה-execution לנקודה שהיינו בה. הנקודה שהיינו בה זה מאיפה שעצרנו את ה-CPU ועברנו לכאן. זה ה-context הקודם, זה context חדש.

כל task שרץ הוא context, ויש לנו context ספציפי לכל interrupt handler.

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

בתוך הזיכרון, זו שאלה טובה.

אוקיי, אם היינו מממשים בעצמנו את המערכת הפעלה, היינו עושים את זה איך שנו נרצה. היינו מגדירים גודל של stack, גודל מקסימלי של stack, אמרנו ש-stack הוא גדל וקטן, היינו מגדירים את הגודל הזה ואז מחלקים לפי מה שבא לנו. אוקיי? אם אנחנו משתמשים ב-FreeRTOS, אנחנו גם עושים את זה בצורה הזאת. אנחנו מגדירים את הגודל של ה-stack, הוא הולך לזיכרון, אומר, שומר, הגודל הזה זה הגודל של ה-stack. זיכרון נוסף הוא זיכרון שהוא יהיה heap, נדבר עליו. אז יש לו בעצם זיכרון שהוא מוגדר, אוקיי? ואם אני עובד רק ב-process של אפליקטיבי אחד ויחיד, אז הגודל שלי זה כל הזיכרון. כל הזיכרון שנשאר אחרי שיש לי את ה-קוד, יש לי את ה-גלובליים שהם לא מאותחלים, את הגלובליים שמאותחלים, ואז כל שאר הזיכרון אני יכול לשים בתוכו את ה-stack. ה-stack יכול לגדול בתוכו.

נכון.

נכון.

את ה-pointer שלו, את השורה האחרונה שלו, ואז אני פשוט מוציא, כן.

המערכת הפעלה.

ה-scheduler משתמש בה, אבל הוא לא מחזיק אותה. כאילו, נמצא בזיכרון. מה הכוונה מחזיק?

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

כשנגדיר task אנחנו נראה את זה. בסדר? זה תלוי מערכת הפעלה. במקרה של FreeRTOS זה מוגדר ככה. אתה יכול להגדיר גם מערכת הפעלה שיש לך stack דינמי, אוקיי? לא יודע איך תעשה את זה, אתה מוזמן לעשות את זה, יש כל מיני מערכות הפעלה כאלה, Linux לדוגמה, אני מניח שגם Windows. אמת שאולי Linux... תלוי Linux באיזה...

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

המצב הדיפולטיבי אתה פשוט יש לך system call שאתה נותן לך את הגודל של המקסימלי של ה-stack שלך, ואתה יכול לשנות אותו בזמן ריצה.

אוקיי?

## Task Stack

טוב.

מה זה... בדיוק הגענו לכאן. מה זה ה-task stack? בעצם זאת אומרת שזה stack ש... זו מחסנית של זיכרון שאנחנו משתמשים בה כדי לשמור את ה-local variables, את ה-function parameters, זאת אומרת מאיפה קפצתי לפונקציה ולאן אני חוזר, כתובות לחזרה, ואת ה-context כאשר אנחנו מסיימים לרוץ. זאת אומרת שבתוך ה-stack של ה-task נשמר כל המידע הזה.

בסדר? אז מה, שוב, מה המידע? Local variables, זאת אומרת כל מיני משתנים... כל מיני משתנים... טוב, אני כבר קופץ פה להראות קוד...

כל מיני משתנים לוקאליים, אם אנחנו מתוך ה-main ואחרי ה-זה, אז אם אני שמתי כאן `int i;`, אז זה משתנה לוקאלי, זאת אומרת שכאשר אנחנו מגיעים לכאן, ה-instruction שה-compiler בנה לנו, אומר תכניס לתוך ה-stack שלך את `i`.

ה-main הוא ה-task הראשי שלנו.

ה-main הוא ה-task הראשי.

אני מדבר כרגע בלי מערכת הפעלה. אנחנו נבין את זה כשיש מערכת הפעלה, אבל תמיד יש את התכנית הראשית, תמיד, כן.

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

או שאני מעביר אותו בתוך אחד מהרישומים שראינו, ה-General Purpose Registers, ה-R0, R1, או שאני מעביר אותו בתוך ה-Stack. אני דחפתי אותו ל-Stack, קראתי לפונקציה, כאשר הפונקציה נקראת, אז היא מוציאה אותו מה-Stack ומשתמשת בו, אוקיי?

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

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

סבבה. Return Addresses - בעצם כשאני קופץ לתוך הפונקציה, אני יכול להכניס את ה-Return Address ל-Stack, וב-Link Register אני יכול לשים את ה-Return Address הנוכחי או את ה-Status. אז זה תלוי, אוקיי?

ה-Status יכול להגיד מאיפה באתי בתוך ה-Link Register, שם אנחנו שמים את ה-Status, ובתוך ה-Stack עצמו את ה-Return Address, בעצם ה-Instruction שאליו אני אחזור כאשר אסיים לרוץ את הפונקציה. בסדר? זאת אומרת שהוא יהיה בתוך ה-Stack ואז אני אגיע אליו ואמשיך אותו. שוב, זה בתוך... הקומפיילר עושה לנו את זה אוטומטית. אנחנו רק צריכים להכיר שזה נעשה, אלא אם כן אנחנו רוצים לכתוב Scheduler משלנו ואז אנחנו נעשה את זה בצורה ידנית. אבל שוב, הקומפיילר עושה לנו את זה.

Processor Registers - לדוגמה כאשר נכנסתי לתוך פונקציה, אז היה לי Context ספציפי. הקשר מסוים בתוך פונקציה. היה לי Context ספציפי. ה-Context הזה, כל ה-R0 עד R16, כל ה-Context, כל זה נדחף לתוך Stack, וכאשר אני אצא מהפונקציה, אני אוכל להחזיר אותו ולהמשיך מאותה נקודה. אחרת, איך הייתי יודע מה ה-Context שאליו נכנסתי? זה פשוט נמצא בתוך ה-Stack.

ובאותו דבר כאשר עושים Nested Function Calls, זה בעצם כל פונקציה כאשר היא נקראת, אז ה-Context הקודם נכתב לתוך ה-Stack. וכאשר אני יוצא מהפונקציה, אני מוציא אותו וממשיך לרוץ. כאשר זה Nested, אז נכתב לתוך ה-Stack, נכתב לתוך ה-Stack, נכתב לתוך ה-Stack, וכאשר יוצאים מפונקציה אז מוציאים אותם החוצה. זה נקרא Pushing ו-Popping. כאשר אני דוחף לתוך ה-Stack, אני עושה Push, אני דוחף את המידע לשם. וכאשר אני יוצא, אני עושה Pop.

אני אגלה לכם שזה לא משהו שאנחנו עושים בצורה ידנית, יש אפילו Assembly Instruction ספציפי לזה, אוקיי? אם אתם מאוד סקרנים, אחרי שנחזור מההפסקה אנחנו נעשה את זה.

## Stack Memory Management

אז ה-Memory Management שאנחנו עושים, יש לנו את ה-Stack Pointer שהוא מצביע על המקום הנוכחי שבו נמצא ה-Stack.

ה-Stack Growth - לאן הוא גדל, למעלה או למטה? זה מבלבל וזה מבלבל עוד יותר עכשיו. אני יכול להגדיר שה-Stack שלי בכתובות הפיזיות של הזיכרון עולה למעלה, מתחיל מכתובת 2000, עולה ל-2004, 2008, 200C ועד 2010. זה דוגמה אחת. ואני יכול להגדיר שהוא יורד למטה, זאת אומרת שהוא מתחיל ב-3000 ואז הוא הולך ל-299C, 2998, 2994, 2990, אוקיי? אני בכוונה קפצתי ברביעיות כי אנחנו עובדים על מעבדים שהם 32-bit.

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

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

זה נקבע ספציפית לארכיטקטורה, אבל אם היית רוצה לממש Dynamic Stack, אוקיי? שבו אתה לוקח שני Tasks והם חולקים את ה-Stack הזה, איך אתה עושה את זה? אחד גדל למעלה, אחד גדל למטה והם נפגשים באמצע. אז בשביל זה אתה עושה את זה בצורה הזאת.

אז כן, זה נקבע בארכיטקטורה. שלנו הוא Descending, כל ה-Cortex-M הם Descending, זאת אומרת שהם יורדים למטה, אוקיי? אבל יש גם Ascending, ויש גם את העניין של Full ו-Empty, אבל זה לא משנה לנו כרגע, אוקיי?

את ה-Frame, שזה הזיכרון שכל Function משתמש.

Overflow - כאשר ה-Stack עובר את הגודל המקסימלי שהוקצה לו, אוקיי? Stack Overflow. מה קורה במקרה כזה? לרוב או שמערכת ההפעלה תגדיר לנו איזשהו Fault ואז אנחנו נקפוץ ל-Fault הזה ונדע, או שמערכת ההפעלה תג'נרט Fault בעצמה כדי שנדע. אבל זה בעצם אומר שעברנו את הגודל המקסימלי של ה-Stack.

Stack Underflow - זה כמו משתנה שאנחנו עושים לו Underflow בעצמו, אנחנו יורדים מתחת לנקודת ההתחלה של ה-Stack. מה קורה שם? לא מוגדר, לכן זה גם שגיאה.

## Stack Size Considerations

Stack Size Considerations - מה המינימום של ה-Stack, שזה הגודל שאליו אני משתמש? זה Configurable, אוקיי? זה שזה Configurable, זה לא אומר שכל האפשרויות עובדות, אוקיי? אנחנו נסתכל על הקונפיגורציה.

נכון אמרת עכשיו Overflow ל-Stack? אז אני סתם מנסה להבין, נגיד יש לי תהליך שקורה... סתם, נכון יש לי, לא יודע, סתם דוגמה, כל 5 שניות אני עושה טריגר, אוקיי? אז הטריגר מה הוא עושה? הוא עושה Print. סתם מדפיס. השאלה שלי אליך, כאילו במקרה הזה, אחרי 50,000 פעמים של כל פעם הוא קורא לעצמו, מדפיס והולך לישון, אז יהיה... זה Overflow או שבמצב הזה זה לא?

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

בכיף. איפה היינו?

Maximum Interrupt Nesting - זאת אומרת הכמות המקסימלית של Interrupt-ים שיכולים לקרות בתוך Interrupt, אוקיי? זאת אומרת שאם אני עכשיו בתוך ה-SysTick, אוקיי? וה-SysTick עושה Pending, בעצם הוא קורא ל-Interrupt הבא, שה-Interrupt הבא הוא זה שיחליט מה ה-Context הבא שאני רץ, אוקיי? זה בגדול איך עובד ה-Scheduler. ה-SysTick יגדיר מה ה-Context הבא שרץ, אבל ברגע זה הגיע Interrupt עם Priority גבוה יותר, לדוגמה Pin State Change, אוקיי? השתנה לנו ה-State של ה-Pin, וגם הגיע UART2 שגם הוא די חשוב, והגיעו עוד כמה מקרים. בעצם כל המערכת שלנו מפוצצת ב-Interrupt-ים. אז ה-Maximum Interrupt Nesting זה כמות הזיכרון שאליו אנחנו יכולים לעשות Interrupt Nesting. זאת אומרת שכל פעם שקורה Event כזה של Interrupt, אנחנו דוחפים את ה-Context לתוך ה-Stack. אז זה גם גודל מסוים שאנחנו צריכים לקבוע, וזה נקבע בתוך הפרמטר הזה.

Local Variable Usage - זאת אומרת כמה מערכים וכמה Structure-ים יכולים להשתמש בתוך ה-Stack, אם לא - הם יכולים להשתמש בזיכרון העזר שלנו, שזה ה-Heap. אם זה מוגדר בצורה הזאת, שימו לב שבשפות שהן שפות יותר עליונות, אנחנו לא יכולים לקבוע מה נעשה בתוך ה-Heap ומה נעשה בתוך ה-Stack, אוקיי?

ה-Heap זה לא ל-Dynamic Allocation?

נכון, נכון. ה-Heap זה לאלוקציה דינמית. אבל אתה יכול להגדיר שממערך מסוים, מגודל של מערך מסוים, הוא מוקצה בתוך ה-Heap ואז הוא דינמי והוא לא אוכל לך את ה-Stack.

זה משהו per פלטפורמה?

per מערכת הפעלה, אתה יכול לעשות. או שפה, יש שפות שעושות את זה אוטומטית. אני חושב שפייתון עושה את זה.

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

אתה נכנס למימוש. הוא פשוט יביא לך את זה מה-Heap מגודל מסוים.

Function Call Depth - כמה רקורסיה אנחנו יכולים לעשות, בעצם כמה קריאות לפונקציה אנחנו יכולים לעשות.

ו-Safety Margin - בעצם כמה קרוב לסוף של ה-Stack אני יכול להגיע, כדי שאני אוכל לפחות לדבג מה קרה לי לפני ה-Overflow.

טוב, בואו נעשה הפסקה, אוקיי? נעשה הפסקה, אנחנו קצת מאחורי ה-Scheduling שלנו, אז נעשה הפסקה של 4 דקות, ב-46:40 ניפגש שוב. בסדר? ואז אני אמשיך לענות על שאלות ונסיים את החלק הזה, וכבר אני רוצה שנתחיל להגיע לפלטפורמה עצמה. סבבה, אז 4 דקות.

## RTOS vs GPOS: Task Scheduling

RTOS מול GPOS, אוקיי? מה קורה מבחינת scheduling?

כשאנחנו עובדים עם GPOS, ה-scheduling שלנו הוא time-sharing. בעצם ה-scheduler עושה את ה-slicing שלו לפי זמן, והוא מבטיח לנו fairness, זאת אומרת שכל ה-processes מקבלים את אותו CPU time.

מי שקצת נגע ב-schedulers אחרים – יצא לכם לגעת בעוד schedulers? או שאני סתם מנפח לכם את הראש?

אוקיי. אז מי שנגע קצת ב-schedulers אחרים, אני אגיד את זה במאמר מוסגר: לא כל ה-CFS-ים נולדו שווים, אוקיי? CFS-ים מסוימים כן יש להם priority, אבל זה לא priority בצורה שאתם חושבים. זה כן איזשהי priority של יחס של מי ירוץ יותר ופחות. עזבו את זה.

Throughput – בעצם המקסום של GPOS זה לדחוף כמה שיותר throughput, אוקיי? לדוגמה, throughput יכול להיות קומפילציה, אוקיי? לקמפל כמה שיותר דברים. זה איזשהו scheduler של קומפיילר, או איזשהו CFS.

Throughput של דאטה – לדוגמה, QoS. מכירים QoS מתקשורת? אז QoS זה איזשהו scheduler שעושה לנו Quality of Service לפי הדרישות של מי שהגדיר את ה-QoS, עזבו כרגע. דוגמה של QoS: אז יש לנו QoS ספציפי לדאטה, יש לנו QoS לשיחות וידאו, שאיזשהו scheduling שלו וכו'. דוגמאות ל-QoS-ים.

Dynamic priorities – זאת אומרת שה-scheduler של GPOS יכול לשנות את ה-priority לפי ה-system load, או לפי בדיקה של starvation / deprivation – אם יש לנו איזשהו task שלא קיבל execution הרבה זמן והוא מחכה כבר הרבה זמן, אז יש מה שנקרא priority boost לזמן ספציפי, רק כדי שהוא יוכל לרוץ.

וה-scheduling נעשה על best effort. אף אחד לא מובטח לו כלום, אוקיי? לא אף אחד לא מובטח execution, ובטח שלא execution לפי זמן. אז אין לנו שום guarantee. וזה בגדול איך עובד scheduling של GPOS.

RTOS – אז הוא priority-based, high priority tasks תמיד ירוצו קודם, אוקיי?

Preemption – high tasks יעצרו tasks עם lower priority.

Determinism – אנחנו יכולים לחזות מתי ירוץ לנו ה-task.

ה-priorities הם קבועים, אוקיי? זאת אומרת שהם לא משתנים. במאמר מוסגר – אפשר לשנות את זה בזמן ריצה, אם אני זוכר נכון גם ב-FreeRTOS, אבל ב-RTOS-ים ספציפיים אתה לא רוצה לשנות את זה, אלא אם כן אתה רוצה לטפל באיזשהו מקרה קצה שנגיע אליו בהמשך הקורס.

ו-real-time guarantee – זאת אומרת שאנחנו יכולים להבטיח איזשהו time constraint שבו אנחנו כן נבצע את הפעולה.

שאלות על task scheduling? נראה לי נגענו בהכל, זה קצת איזשהי חזרה.

## RTOS vs GPOS: Latency

Latency. יש לנו כמה סוגים של latency:

Interrupt latency – מה זה interrupt latency? מהזמן שבו ה-interrupt controller שלנו זיהה את ה-interrupt עד שאנחנו מריצים את הקוד בתוך ה-interrupt service routine, מה שנקרא ה-handler, אוקיי?

ה-handler כאן, מהזמן שבו... טוב, SysTick הוא פנימי. SysTick הוא interrupt פנימי, הוא לא interrupt של NVIC. יש לנו שני סוגים של interrupt-ים. יש לנו interrupt-ים שהם פנימיים מתוך ה-core עצמו, זאת אומרת מתוך ה-CPU. בתוך CPU ARM Cortex-M whatever, זה הארכיטקטורה של ה-CPU. לארכיטקטורה הזאתי יש לה interrupt-ים פנימיים, לדוגמה SysTick, דוגמה נוספת זה UsageFault ועוד כל מיני דוגמאות. ויש גם interrupt-ים...

- **תלמיד:** אלה interrupt-ים שהם כבר קבועים? כלומר זה חלק מהמערכת הפעלה?

- **מרצה:** זה חלק מה-core עצמו. חלק מה-CPU.

- **תלמיד:** אבל ה-interrupt עצמו או ה-handler?

- **מרצה:** ה-interrupt. ה-handler זה פשוט פונקציה. המימוש פה שאתה רואה, זה מימוש שבא מתוך המערכת הפעלה.

- **תלמיד:** כלומר זה לא משהו שאתה כתבת. אני רואה נגיד יש הערת user code begin, user code end, כלומר אם אתה רוצה...

- **מרצה:** עוד שנייה נגיע למה זה ההערות האלה, אוקיי?

- **תלמיד:** לא, אין בעיה, אבל הקוד הזה זה קוד של ה-core, אתה יכול לשנות אותו?

- **מרצה:** ספציפית SysTick, אוקיי? SysTick, באתחול של המערכת אני יכול לקבוע, או באיזשהי נקודה, אני יכול לקבוע כל X טיקים אני קופץ לתוך ה-SysTick, נכון?

- **תלמיד:** ועושה מה שאתה רוצה.

- **מרצה:** אז שנייה, סליחה שאני מחרבש פה, זה יכול להיראות ככה גם. ובכל פעם אני אבוא לפה ולא אעשה כלום, ואצא מפה. זה ספציפית של המערכת הפעלה, של FreeRTOS. וזה ספציפית של ה-STM, אוקיי? ל-STM יש את הספריות שלו, אז זה ספציפית משהו גנרי. למה זה נמצא בתוך האזורים האלה? אני מניח שאתה יודע, אבל אנחנו עוד שנייה גם נסביר הכל.

General-purpose latency – זאת אומרת שזה latency שהוא משתנה, זה לא latency קבוע. זה יכול להיות בין 1 ל-5 milliseconds, תלוי ב-system, תלוי במערכת הפעלה, תלוי בהרבה דברים, אבל זה בערך איזשהו...

- **תלמיד:** מה זה כולל latency?

- **מרצה:** מהזמן שקרה ה-event בעולם האמיתי, עד שאנחנו מקבלים חיווי. אני אתן דוגמה ל-latency כללי, אוקיי? Latency שאנחנו מכירים: אוזניות Bluetooth, אוקיי? מהזמן שבו אני... מיקרופון Bluetooth, אוקיי? מהזמן שבו אני מדבר, עד הזמן שבו זה מגיע לבנאדם שבצד השני, זה ה-latency של הדיבור, אוקיי? Latency של interrupt זה מהרגע שקרתה ה-detection של ה-interrupt עד שקפצתי לתוך הקוד. זה interrupt latency.

- **תלמיד:** כולל או לא כולל המימוש כאילו?

- **מרצה:** לא כולל המימוש. עד שהגעתי לכאן.

- **תלמיד:** אתה מדבר, אז ה-interrupt, הצליל נשמע, הוא מועבר לצד השני, זה הקוד, לא?

- **מרצה:** אז כן. עד הנקודה הזאת. אם אנחנו באמת ממשיכים באותה דוגמה, אז מאז שדיברתי, ה-CPU שבתוך האוזניות שלי זיהה את הקול, אוקיי? ויש לו interrupt handler שמוגדר, שה-interrupt handler הזה, ברגע שיש קול, הוא קופץ לתוכו. אז הוא זיהה את הקול, עזוב, הוא לא יודע מה הקול, הוא פשוט קפץ לנקודה הזאתי, תניח שיש פה שם אחר של handler, אוקיי? אז הוא קפץ לנקודה הזאתי. מה שהוא עושה איתו אחר כך, זה כבר ה-interrupt execution.

- **תלמיד:** עד הנקודה הזאת אתה אומר.

- **מרצה:** מהנקודה של הזיהוי עד הנקודה של התחלת הפעולה. לא הפעולה עצמה. זה latency.

אז שוב, latency הוא לא קבוע בזמן, שזה יכול להיות בעייתי מבחינת real-time. הוא לא דטרמיניסטי, אין לנו שום הבטחה של מתי זה יקרה, אם בכלל יכול להיות שזה יתפספס. הוא יכול להיות מושפע מכל מיני דברים: מזיכרון וירטואלי, מכתיבה וקריאה מהדיסק, שזה דברים שאנחנו לא רוצים לעשות ברוב המקרים, אלא אם כן אנחנו מודול שכותב לדיסק. ו-system calls במערכת.

Priority – ה-throughput הוא יותר חשוב מה-responsiveness, אוקיי? שזה GPOS כללי.

RTOS – latency מאוד נמוך, כמה microseconds, עד שזה קורה. ה-interrupt latency קבוע, הוא לפי ה-MCU. ה-latency של נגיד עד פתיחת כרית אוויר, זה יכול להיות בקוד, אוקיי? Latency של עד שלחצתי על הכפתור, יכול להיות חלק מהקוד, אלא אם כן זה מוגדר כ-interrupt. אוקיי, אם אני ב-polling אז ה-latency זה הזמן שבו אני מתשאל את ה-resource, דיברנו על polling. אם אני ב-interrupt, אז זה הזמן עד שקפצתי לתוך ה-interrupt handler.

דטרמיניסטיות – אני חייב להיות מוגבל בזמן. קונסיסטנטיות – ה-consistency נקבעת מראש ברמת ה-RTOS שלי, אבל גם וריאציה מאוד קטנה ב-response time, לא זמן שהוא לא מקובל. ו-priority – responsiveness על throughput, אוקיי? זה העדפה שלי.

אז task switch latency בכל type של operating system:

ב-GPOS (General Purpose) – כמה microseconds, תלוי ב-memory management וכל מיני דברים כמו caching. לא מדבר על זה כרגע.

ב-FreeRTOS – כמה microseconds קטנים, שזה context מינימלי, ואין לנו בעצם זיכרון וירטואלי או כל מיני דברים כאלה. אנחנו בעצם לא תלויים בעוד רכיבים. אוקיי, כשאני מדבר על זיכרון וירטואלי, זאת אומרת שיש עוד רכיב, שזה הזיכרון וירטואלי, שאני תלוי בו. פה לא תלויים בו, במקרה של RTOS.

## Scheduler Time Slice Latency

מה זה בעצם slice time? Slice time זה בעצם כל X זמן שה-scheduler שלי בודק מי הבא שאמור לרוץ ומחליף אם צריך. אוקיי, אז זה בעצם הזמן שמוגדר להחלפת זמן.

מה ה-GPOS latency שלי? הוא בין 10 ל-100 milliseconds per task, אוקיי?

אם זה round-robin, זה אומר שאני עובר על ה-tasks בסיבוב, אז כל slice time אני מחליף, קבוע.

אם זה משהו דינמי, אז ה-slice time הוא זז, אוקיי? אז הוא יכול להיות כל זמן קצר, או כל זמן גדול, אבל הוא דינמי.

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

ה-context switching overhead הוא קצת גדול יותר כי... סליחה, ה-context switching overhead, שזה בעצם הבזבוז של זמן רק על context switching – context switching זה פעולות, פעולות לוקחות זמן – אז הוא עיקרי כי בעצם אנחנו כל X זמן עושים החלפה של context, אוקיי? כל X זמן אנחנו עושים החלפה של context.

ב-RTOS – אז זה priority-based, זאת אומרת ש-tasks עם priority תמיד ירוצו מהר יותר, בלי קשר ל-time slicing. אוקיי, preemptive switching – כאשר task צריך לרוץ... כאשר task...

אז הוא בעצם, כאשר task עם priority נמוך צריך להיעצר, אז ה-scheduler עוצר אותו כדי ש-task עם priority גבוה יותר ירוץ. ה-overhead מינימלי, ה-context switching הוא מאוד קטן. בעצם ה-context switching הוא בדיוק החלפה של context, לא מעבר לזה. לא עכשיו יש לי כל מיני... מאוד מינימלי, לא עכשיו החלפה של context של process, שזה משהו שהוא כבר יותר גדול, וזה כבר במערכות שהם GPOS. אנחנו לא עושים את זה.

אנחנו עושים context שכל ההחלפה של ה-context זה רק להגיד: ה-CPU רץ מכאן לכאן וזה ה-context שלו. לא דברים שהם overhead, לא דברים שהם מעבר לזה.

אנחנו יכולים להניח כמה זמן יקח לנו להחליף את ה-context, אוקיי? להכניס ולהוציא. וגם יש לנו guarantee, ש-guarantee זה בעצם ההבטחה של המערכת הפעלה של מתי היא מחליפה ל-higher priority task.

## What is Multitasking?

מה זה multitasking? multitasking זה היכולת של מערכת הפעלה להריץ כמה tasks במקביל, אוקיי? ולהחליף את ה-context ביניהם.

איזה סוגים של multitasking יש לנו? יש לנו cooperative multitasking, זאת אומרת שכל task משחרר בצורה אלטרואיסטית, באופן וולנטרי, את ה-CPU ל-tasks אחרים. זאת אומרת שה-multitasking או ה-scheduling נעשה בצורה שהיא לא preemptive. זאת אומרת שה-task ממשיך לרוץ עד שהוא משחרר את ה-CPU. יתרונות: אימפלמנטציה פשוטה ולא משהו מסובך, ואין לנו פחד של race condition, שנדבר עליו בשיעור הבא. החסרונות: זאת אומרת שאם task לא מתנהג כמו שצריך, הוא תוקע את המערכת.

preemptive multitasking: זאת אומרת שה-operating system עוצר בצורה by force, בכוח, execution של task לפי priority או לפי time slicing, אוקיי? interrupt driven, אז higher priority task, שזה interrupt, יכול לעצור... interrupt driven higher priority task יכול לעצור lower priority task. יתרונות: responsiveness הרבה יותר גדולה, ואנחנו בעצם הרבה יותר מהירים, ואנחנו בעצם יכולים להימנע מ-faults של אחד של השני. חסרונות: אימפלמנטציה טיפה יותר מסובכת, ומצריכה קצת סנכרון.

סוג שלישי של multitasking: לפי task state. לכל task יש איזה שהוא state שהוא נמצא בו כרגע, ולפי ה-state הזה – ופה יש איזה שהיא דוגמה של states – לפי ה-states האלה ה-scheduler עוצר או מריץ את ה-tasks.

## FreeRTOS Overview

אז מה תהיה מערכת ההפעלה הספציפית שאנחנו נריץ אותה הקורס הזה? FreeRTOS. FreeRTOS זה open source, אוקיי? FreeRTOS הוא חינמי. קצת מהשם. FreeRTOS הוא open source שהוא מיועד ל-microcontrollers ול-microprocessors קטנים, MPUs (Microprocessing Unit).

הוא משתמש ב-preemptive multitasking, ויש לו kernel, שזה החלק, השכבה של הקוד ששם נמצא ה-scheduler. וה-kernel עצמו עושה לנו priority-based scheduling. הזיכרון שלו הוא מאוד קטן, הוא אמור להיות מאוד קטן כי אנחנו במערכות בלי הרבה זיכרון, אוקיי? לדוגמה, לי יש 128 קילובייט, שזה מאוד קטן, ואי אפשר לעשות עם זה דברים מסובכים מדי.

והוא פורטבילי, זאת אומרת שלכל ארכיטקטורה, או לכמה ארכיטקטורות שונות, יש לו קובץ port, שהקובץ הזה אחראי על הפורטביליות בין הארכיטקטורות השונות. דוגמאות לארכיטקטורות שונות: ARM Cortex-M4, שזו ארכיטקטורת הקורס, RISC-V, x86 שהוא לא... האמת שלא נתקלתי אף פעם ביכולת להריץ x86, אם תרצו שאני אסתכל אחר כך, הוסיפו את זה לרשימת החובות, וכולי. ותמיכה על ידי AWS, שרכשו את FreeRTOS, רכשו את ה-open source הזה.

מה ה-features העיקריים שאנחנו גם נעבור עליהם במהלך הקורס הזה?

Tasks - בעצם threads שיכולים להריץ לנו tasks עם stackים נפרדים משלהם. זאת אומרת, לכל task יש stack משלו.

Queues - IPC (Inter-Process) או ITC (Inter-Task Communication) - בעצם מנגנונים, כל מיני שיטות לסנכרן מידע בין tasks.

Semaphores ו-Mutexes - בעצם מערכות נעילה וסנכרון על resources שיש לנו.

Software timers - בעצם טיימרים תוכנתיים. זאת אומרת שאני יכול להשתמש בעוד טיימרים מעבר לטיימרים החומראתיים שיש לי ב-MCU שלי או ב-MPU שלי.

ו-low power support - בעצם אנחנו יכולים לרוץ בצורה שהיא בלי tick. אני אסביר גם מה זה אומר בלי tick.

דיברנו על ה-SysTick הרבה, וה-SysTick כל מה שהוא בעצם מתעורר ומעדכן את ה-tick, מעדכן את ה-pacer, בעצם את הקבוע זמן שהוא רץ. מה קורה כאשר אני נכנס ל-low power mode? חלק מהשעונים נעצרים, אוקיי? למה הם נעצרים? לחסוך באנרגיה, זה מובן. חלק מהשעונים נעצרים, ואז אנחנו בעצם במצב שהוא tickless, כי אם ה-SysTick עוצר, וה-SysTick הוא אחד מהשעונים שנעצרים, אז השעון הזה בעצם עוצר ואנחנו לא יכולים לדעת מה ה-tick הבא, לא יכולים לדעת כמה ticks עברו מאז שעצרנו עד שאנחנו חוזרים לרוץ.

אז FreeRTOS נותן לנו תמיכה ב-tickless idle mode, זאת אומרת שכאשר אנחנו נתעורר הוא יידע כמה זמן עבר, אוקיי? איך הוא עושה את זה? לא בדקתי. שימו לב שיש גם טיימרים שהם רצים כאשר ה-CPU לא, LPTIMER אם אתם מכירים, Low Power Timers.

למה אנחנו נשתמש ב-FreeRTOS על STM32 או על כל microcontroller?

תמיכה ב-STM, בעצם STM32CubeMX נותן לנו תמיכה במערכת הפעלה out of the box, אנחנו לא צריכים להתקין אותה בצורה ידנית, למרות שאנחנו נלמד איך עושים את זה. פשוט לוחצים על כמה כפתורים וזה עובד.

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

ותמיכה - בעצם גם STM32Cube תומכים בדרייברים שלהם, תומכים באינטגרציה עם גרסה חדשה של FreeRTOS, ככה שאנחנו ממשיכים להיות up to date, אם יש עדכונים של security... לא זוכר, כל מיני עדכונים של גרסאות שאנחנו צריכים עקב חולשות שנמצאו במערכת הפעלה, אז אנחנו יכולים לעדכן את זה בצורה יותר פשוטה, וכבר האינטגרציה עצמה נמצאת בתוך סביבת הפיתוח שלנו, החיים שלנו יפים.

ו-debugging tools - יכולת לדבג את המערכת הפעלה. Debugging tools - או שמדובר ב-JTAG, או שיש לנו ST-LINK, שזה רכיב ה-debugging שנמצא על ה-boards. אם יש לכם board, אז כנראה שיש לכם ST-LINK שתוכלו לדבג באמצעותה, אם יש לכם NUCLEO או DISCOVERY, אז כנראה שיש לכם את הרכיב הזה. אם אין לכם, אז debugger. JTAG debugger או debugger כללי של STM32.

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

## STM32CubeIDE Deep Dive

אוקיי, קצת ללחוץ על כפתורים.

בואו נראה איך אנחנו פותחים את ה-STM32Cube. אז אם אתם ב-Windows אז כפתור חלונות, אם אתם במערכת הפעלה אחרת, אז כפתור שפשוט לא נקרא חלונות.

פה, אנחנו בוחרים ב-STM32Cube בזה שהורדנו, אם הורדנו 1.18 כאן, או 19, לא לפתוח אותם בו זמנית.

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

אז יש לכם את שני אלה, אנחנו לא נכנסים לאף אחד מהם, מהתיקייה הזאתי, מהקונטקסט הזה, אנחנו נלחץ Select Folder. זאת אומרת אנחנו בוחרים ב-class0, לא בוחרים באף אחד מהם. Select Folder.

ואנחנו עושים Launch.

בזמן שזה עולה... כרגע יש לנו את זה, ולא מופיע לנו פה כלום. מה אנחנו צריכים לעשות? אנחנו צריכים לעשות Import, אוקיי? File -> Import.

ומתוך General אנחנו נבחר Existing Projects into Workspace. שוב, Browse לאותו מקום, סבבה? class0.

Select Folder, הוא יראה לי פה כמה.

את זה הוא לא אמור להראות לכם, ופה הוא גם לא אמור להראות לכם את זה. אז יהיה לכם שניים, יהיה לכם את 00_logger ו-00_logger_FreeRTOS. שימו לב שכתוב פה מבחוץ 01.

01... לא manual, 01_logger_FreeRTOS. הוא לא משנה את שמו, הוא נשאר עם מה שקבעת בהתחלה, אז סורי. אז 00 ו-01, לכם אמורים להופיע רק שני אלה.

עכשיו אנחנו נעשה...

- רשום 01_logger_FreeRTOS, נכון?

- אפילו יותר טוב.

- אוקיי. אני מפספס כי אין לי את החבר'ה אלה. אתה ב-Import project.

- כן, האם עשית pull בתחילת השיעור? git pull?

- עשיתי, כן.

- תסתכל באיזה... אה, רגע, אתה צריך להיות ב-branch class0.

- כן.

- בוא נראה מה אתה אומר. טוב, הוא כבר נותן לי שני warnings על include path לא נמצא.

- אוקיי.

עוד רגע, אני לא יודע מה הוא נתן, אבל כרגע אנחנו לא עושים עדיין כלום.

בסדר, אז מה יש לנו בתוך STM32Cube? יש לנו IDE מבוסס Eclipse, דיברנו עליו, הוא מכיל את ה-configuration tool שאנחנו נדבר, debugger, ותמיכה ב-FreeRTOS.

יש לנו STM32Cube graphical user interface, שזה ה-ioc, ואתם יכולים גם ללחוץ עליו כרגע.

- על מה ללחוץ סליחה?

- על הקובץ מתוך 00_logger, על הקובץ שנקרא ioc. אה, מה שכתוב עם MX בהתחלה?

- 00_logger.ioc.

הוא אומר: "Do you want to open this perspective now?"

- כן.

- הוא לא כותב. Invalid input: must be project active ioc file.

- ניסיתם לעשות import נוסף? בואו נראה. מעבר לזה, שימו לב שהפרויקט הזה הוא ספציפי לארכיטקטורה הזאתי, ל-MCU הספציפי הזה, אוקיי? אז אנחנו חייבים ליצור פרויקט חדש, בסדר? ככה שגם כל אחד יכנס ל-board הספציפי שלו.

## יצירת פרויקט חדש ב-STM32CubeIDE

אז הימני כאן, New, STM project, אוקיי?

אורל: רגע, אה סליחה, New STM32 project.

כן, אני אעשה שוב. ימני, New, STM32 project, אפשר גם מה-File, אבל בסדר. ומפה יש לנו כמה אפשרויות, אוקיי? רגע, רק שאני זה. בסדר.

מפה יש לנו כמה אפשרויות. האפשרות הראשונה היא MCU/MPU Selector, אם אנחנו רוצים לבחור MCU ספציפי שהוא לא חלק מ-Board, אלא רק MCU, אנחנו נבחר בזה. ויש לנו גם Board Selector, אוקיי? בתוך ה-Board Selector יש לי פה כל מיני פרמטרים, ואני יכול לפלטר, ותכלס גם לקנות מכאן, כאילו אם אנחנו ממש רוצים. ומפה אני יכול לבחור, ומפה אני בעצם מפלטר את ה-Board שלי. סבבה? אז כל אחד והשם של ה-Board שלו.

אורל: איפה אני יכול, נגיד אם אני רואה את ה-Board מולי, איפה אני כותב את זה? ב-Commercial Part Number או...

כן, לדוגמה יש לי פה NUCLEO-WBA65RI. אז NUCLEO, לא ב-Commercial, בחיפוש.

אורל: איפה? אה, בחיפוש.

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

אז לדוגמה, NUCLEO, NUCLEO מקף, בדוגמה שלי אז זה WBA65RI. אז זה הדוגמה הזאתי. שימו לב שברגע שאני אעשה Enter, הוא ייתן לי אותו. זה ה-Board הזה.

אורל: אני לא מספיק... אתה עושה NUCLEO...

בוא, בוא נחפש את ה-Board שלך. נכון, אמרת זה...

אורל: אצלי זה כתוב NUCLEO-F411...

NUCLEO-F411?

אורל: כן, אז אני כתבתי F411, אז הוא נותן לי פשוט מלא אפשרויות. איך אני יודע מה...

אז יש שם עוד זה. אם אני זוכר את ה-Board שהראית לי, אז 411RE או 401, מה...

אורל: יש איפה אני יכול לראות אותו, על הכרטיס אולי?

היה לי פה בשלוף. בדיוק NUCLEO כמו שלך.

אורל: אה RE, כן כן כן. NUCLEO-F411RE.

כן, זה אחד, אוקיי. אז כדי ש...

אורל: אבל כתוב לי גם RET6, RET6TR, RET7.

שימו לב, מה שמופיע פה, זה ה-Mounted Device. RET6. אוקיי? עכשיו, במקרה שלך, כן, Mounted Device זה ה-MCU שלך.

אורל: כן.

אוקיי? כאן רשום לך את כל מה שאמרתי. שימו לב שזה ה-MCU שלך הספציפי.

אורל: איפה אני יכול לראות את זה? איך הגעת לציור הזה? ב-Features?

ברגע שפילטרתי, אז יש לי פה את ה-Board הספציפי. אוקיי? Board list.

אורל: לא, אבל איך אתה מגיע לציור הזה של ה...

לוחץ עליו. הוא מופיע.

אורל: על מה אתה לוחץ?

לחצתי כאן, על השורה הזאתי. נגיד אני אעשה ככה, לא הכנסתי את הכל, רק 4.

אורל: כן.

נגיד 401, לחצתי עליו, בסדר? כל אחד וה-Board שלו. אם יש לכם NUCLEO-144, אתה לוחץ עליו. אז...

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

אורל: ואז Next?

מה? לא לא, רגע שנייה.

אורל: אה, אוקיי, סליחה.

סבבה. ברגע שלחצנו על אחד הספציפי, אני שוב ממשיך עם ה-DISCO, כל אחד שיקח את שלו, אז נפתח לנו החלון הזה, ובואו נסתכל קצת מה יש כאן. יש לנו את ה-Features של ה-MCU שלנו, מה הוא תומך, Microcontroller שמגיע עם 512KB של Flash, 128 RAM, זה ה-Package שלו, יש לו ארבעה LEDs, בגלל זה הוא נקרא DISCO... לא באמת, Discovery. אבל DISCO גם מתאים. יש לו עוד שני LEDs כאלה, יש לו LED של Power, יש לו LED של זה, יש לו כפתורים. זה כל התיאור של ה-Board עצמו, אוקיי?

## תיעוד וסכמות חשמליות

בנוסף לזה יש לנו את המסמכים האלה, אוקיי? והמסמכים האלה הם מאוד מעניינים אותנו. מסמך אחד שהוא מאוד רלוונטי: Schematic Pack, אוקיי? פה, בתוך ה-Schematic Pack, אז אנחנו נראה את הסכמה הספציפית של ה-Board שלנו, שאם אנחנו נצטרך לחווט זה יעזור לנו. אוקיי?

פתחתי אחד שגוי, שזה טעות, אבל לא, פשוט נלחץ על אחד מהם עד שנראה את הסכמה עצמה. אז הסכמה נפתחה, טוב, זה גם היה הראשון במקרה שלי. יש כאלה עם יותר מאחד, יש כאלה עם אחד. שימו לב שאם אתם משתמשים ב-Board עם Daughter board, כמו לדוגמה החבר שלנו פה, אז יהיה לנו סכמות לשניהם. אוקיי? שני Boards, הם בסנדוויץ', מה שנקרא.

אז אם יש לנו סנדוויץ', אז יהיה לנו סכמה לשניהם אם כאילו שניהם מגיעים. זה המסמך הראשון, הוא יפתח לכם אותו כאן. אוקיי, אז זה הסכמה של ה-Board שלי, מה שנמצא עליו. ה-Pinout של ה-Board עצמו, איזשהו אחד ה-Headers, P2, Header הימני, אם אתם משתמשים באותו Board. עוד כניסה של ה-MCU, אז הנה, יש לנו את ה-Oscillator, אוקיי? Oscillator In, Oscillator Out. מה יוצא לפה, כל מיני דברים. אם זה מעניין אתכם, נגיד מה משפיע על LED2 שידלוק, Communication של ה-LED ST-LINK, מה שמפעיל אותו, כל מיני דברים, יחידת USB.

אוקיי, עוד כל מיני חיווטים, אז שוב אנחנו מסתכלים שוב על אותו דבר, אז שוב ה-Oscillator נמצא כאן. ה-Pull-ups שיש לנו, אם אנחנו משתמשים ב-PB6 ו-PB9, אז שנדע שיש להם Pull-ups. מה זה Pull-up? זה לא בסקופ של הקורס הזה, אבל זה בעצם איזשהו נגד שממושך אותנו ל-State, 1 או 0. ועוד כניסה, ורק כדי לסבר את העין, ה-LEDs. אותם LEDs שהופכים אותו ל-DISCO. סליחה שוב על הבדיחה, אני מתנצל. אותם LEDs שהופכים אותו ל... יש ארבעה LEDs בכל מיני צבעים. אם לא הייתי מחובר הייתי מראה לכם איך הם מהבהבים, אבל לא נורא. ושני כפתורים. יש לנו פה כפתור אחד, שהכפתור הזה הוא משוך למעלה, זה הכפתור של ה-Reset, ויש לנו כפתור שני, שזה הכפתור שמחובר ל-PA0, שהוא משוך למטה. אם אתם לא הבנתם לאן נמשך כל אחד, זה בסדר, זה אלקטרוניקה, זה לא הסקופ של הקורס שלנו.

מסמך אחד, אוקיי. יש לנו פה BOM, אם אנחנו רוצים לדעת את כל הרכיבים אם נרצה להרכיב. יש לנו כל מיני דברים כאן, Product specification, Manuals.

אבל מה שיותר מעניין אותנו כרגע, זה ה-Reference Manual. מה זה ה-Reference Manual, ואיך מגיעים אליו? שתי אפשרויות: אפשרות אחת לחפש באינטרנט ולמצוא. פחות נוח, הוא לא תמיד מעודכן, לא ספציפית מה שאחנו רוצים. אפשרות שנייה: בשורה הזאתי, אופס, לחצתי מהר מדי, סליחה. בשורה הזאתי, שזו השורה שפילטרנו, או השורה שסימנו בכוכב, אז יש לנו פה Mounted Device. ברגע שאני אלחץ על ה-Device, ראיתם שהוא קופץ? הוא הקפיץ אותנו לכאן. הוא הקפיץ אותנו ל-Context של MCU Selector. לחצתי, קפצתי. מכאן אנחנו רואים את ה-Chip הספציפי, אוקיי?

High-performance foundation line, Arm Cortex-M4 with DSP with FPU, 512, בלה בלה בלה, זה המהירות, עם ART Accelerator. זה ה-MCU שאחנו רוצים אותו. Features של ה-MCU, שימו לב שה-Features הם קצת שונים, Features של ה-MCU עצמו. DMA, 11 טיימרים, אין לו LP Timer, אבל הוא יכול להוציא PWM, כל מיני דברים. כל הדברים שאתם תראו כאן, הם Features. לא אומר שאפשר להשתמש בכולם במקביל, אנחנו נסתכל על זה עוד רגע. Block Diagram, כל מיני דברים שאתם יכולים להסתכל בצורה של בלוקים, ושוב יש לנו את המסמכים.

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

## מנגנון ה-Interrupts וה-Reference Manual

ה-Reference Manual זה מסמך שהוא מסביר את כל הפריפריאלים, את כל מה שאנחנו עושים. בתוך ה-Reference Manual יש לנו...

אורל: גם את הרגיסטרים, כן?

כן, כן, כן. גם את הרגיסטרים, אבל לפני הרגיסטרים, לפני הרגיסטרים, אז יש לנו את כל הפריפריאלים שאנחנו משתמשים בהם: ה-ADC (Analog-to-Digital Converter), טיימרים, RTC פנימי, ששימו לב שהוא שומר את הזמן גם כאשר אנחנו עצרנו את ה- את ה-Core שלנו, שה-Core שלנו זה לא ה-זה. ורגיסטרים, נכון? אז הטבלת כתובות שלנו נמצאת כאן. כל הטבלת כתובות שמה נמצא באיזה כתובת. זה יותר כשאנחנו עושים Bare metal, אוקיי? ומה שמעניין אותנו כרגע, זה ה- איפה זה, איפה זה, איפה זה, איפה זה... Interrupts and events.

יש לנו הסבר על ה-NVIC, Nested Vectored Interrupt Controller. כל פעם רצחתי את זה, אבל זה באמת מה שזה אומר. זה ה-NVIC, זה ה-Interrupt Controller הספציפי שלנו. Interrupt Controller סטנדרטי, אוקיי? יש לו תמיכה של 52 Interrupts. בגדול, בארכיטקטורה אפשר עד 255, פחות ה-Interrupts הפנימיים של ה-MCU עצמו. נגיע אליהם, זה... אז פחות אני חושב 16, אז עד 240 או עד 249. אוקיי, לא כולל... אני אומר את זה וזה רשום. לא כולל אלה. ושיש לנו את טבלת ה-Interrupts.

אז אמרנו מה קורה. עוד שנייה אנחנו נראה את ה-Datasheet ושם יש איזושהי סכמה שמסבירה את זה קצת. מה קורה, מה זה אלה? אלה Interrupts שמהנדסים מתוך ה-Core עצמו. שימו לב שחלק מה-Priorities שלהם הם קבועים. אז ה-Priority זה מה שקובע איזה Interrupt ירוץ קודם. אז יש לנו Reset, שזה ה-Interrupt הראשון כשעולה ה-MCU, וממנו בעצם מתחיל ה-Bootloader, מה שמעלה את הקוד, אוקיי? יש לנו HardFault, זה בגדול Interrupt שאם לא קונפגו Interrupts... Faults, סליחה, זה ה-Fault Exception שאם לא קונפגו Exceptions אחרים, אנחנו תמיד נגיע אליו, אוקיי? Memory manage, BusFault, UsageFault, כל אלה.

יש לנו עוד אחד שהוא מעניין אותנו, שזה ה-SVC, שזה Supervisor Call. הוא עוזר לנו להשתמש ב-System calls. אם עשינו אימפלמנטציה, שימו לב, אם עשינו אימפלמנטציה, כי זה... כי אנחנו פשוט יכולים לקרוא לו עם Instruction של SVC ו-Value מסוים. ואם עשינו אימפלמנטציה אז זה לא ככה. מי שעבד על Linux, אז באמצעות ה-SVC ככה אנחנו קוראים ל-System calls. כל System call ב-כל System call, לדוגמה fork, יש לו ID מסוים, כשאנחנו קוראים ל-SVC עם ה-ID הזה, אנחנו קוראים ל-glibc, glibc עוטף לנו את זה, בסופו של דבר, לא ממש לקורס הזה, סליחה. אנחנו קוראים ל-glibc, glibc עוטף לנו, בסופו של דבר הוא קורא ל-SVC עם ה-ID שהוא יודע שנמצא ב-fork. ה-SVC, הקריאה הזאתי מגלגלת לנו Interrupt לתוך ה-SVC handler. ה-SVC handler יודע לקחת מהטבלה ולהקפיץ אותנו לנקודה לקוד הספציפי. זה ה-SVC handler.

שימו לב שהוא בעצם... כאשר אנחנו נעשה את זה, אנחנו נקפוץ לתוך ה-SVC handler עם קוד מסוים. זאת אומרת שהוא יקח אותנו לנקודה הספציפית הזאתי. PendSV, שזה Interrupt שאנחנו יכולים לעשות לו pending, לבקש אותו, והוא בדרך כלל Interrupt עם Priority נמוך. אנחנו נשתמש בו, כל מערכת הפעלה משתמשת בו. וה-SysTick. SysTick דיברנו עליו, הוא סופר לנו את ה-Tick של המערכת. אחר כך, זה כבר Interrupts שהם ממומשים על ידי ה-Manufacturer, במקרה שלנו STM.

אז הוא בחר מה לשים איפה. הוא החליט שהראשון יהיה ה-Watchdog. אחריו יהיה EXTI16, שזה External Interrupt 16. בואו נלך להסתכל רגע על דברים שהם יותר ברורים. ADC, אוקיי? גלובלי ל- גלובלי ל-Analog-to-Digital Converter. ה-Priority שלו הוא אפשרי להגדיר אותו, והמספר שלו הוא 18. זאת אומרת שבקוד הוא יקפוץ בתוך טבלת ה-Interrupts ל-Interrupt מספר 18.

אוקיי, ויש פה עוד הרבה, אתם מוזמנים להיכנס ולחפור. זה מסמך מאוד חשוב. אם אתם עושים Bare metal זה התורה שלכם, אוקיי? רק מפה אתם קוראים ועושים דברים. אם אתם משתמשים בספריות, זה כדי להבין מה קורה קצת מאחורה ברקע, או כדי להבין מה קורה זה, ואם אתם צריכים Features ספציפיים ואתם צריכים לדעת במה אתם תומכים, אתם הולכים למסמך הזה, Reference Manual. אוקיי, אחר כך...

## Datasheet

שאלות על ה-Reference Manual? אשמח, מאוד חשוב.

מעולה. אחר כך, הדבר השני הכי חשוב שלנו שאנחנו רוצים זה ה-datasheet. ב-datasheet, אנסה לחפש, מאוחר, אני לא מצליח לראות, אם אתם רואים את ה-datasheet תגידו לי. ב-D, איפה ה-datasheet...

אפשר לשאול שאלה קטנה?

כן.

בגדול ה-Reference Manual מה... אוקיי, כאילו, אתה... אתה כדי לדעת איזה סוגי אינטראפטים יש לך, כאילו, מה... מה במיוחד אתה עושה עם זה?

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

אז אם אני ב-port, port מגדיר סט של פינים, אם אני ב-port מסוים ואני רוצה להגדיר mode מסוים כאן, אני יכול להגדיר אותו לכל אחד מהם לפי זה שאני אדחוף את המידע הזה לכאן. אוקיי? אם אני ארצה להגדיר את פין 0, פין A0, אז את PA אני אקבל מהאזור של הכתובות. את המקום הראשון, זה המקום הזה. ואם אני ארצה להגדיר אותו להיות output, אז אני צריך לשים פה את הערך 1. זאת אומרת שעל שני הביטים האלה ביחד יהיה את הערך 1. תבינו איך אתם עושים את זה bitwise.

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

כאילו, אתה הולך במסמך הזה בשביל לקנפג את הבורד שלך, את ה-I/O, כל מה שאתה צריך כאילו, נכון?

להגדיר את המיקרו-קונטרולר, להשתמש במיקרו-קונטרולר.

אוקיי, בסדר.

המסמך השני, שהוא ה-datasheet. טוב, אני לא רוצה שתסתכלו ב-datasheet של זה, אבל... אה, פשוט פה בגדול, סליחה.

אז זה ה-datasheet. בתוך ה-datasheet פה יש לנו איזושהי סכמה מאוד נחמדה, אני תמיד שוכח... איי, הנה היא פה. איבדתי אותה.

יש לנו את הסכמה הנחמדה הזאת שזה block diagram, אוקיי? מה יש ב-block diagram הזה? יש לנו את ה-core שלנו. ה-core של ה-CPU הזה. אתם רואים? זה core Arm Cortex-M4. על ה-core הזה, או בסמוך לו, יש לנו את ה-NVIC. ה-NVIC הזה הוא יודע להשתמש ב-bus הזה. bus זה איזשהו רכיב שמחבר הרבה מאוד חלקים ביחד במקביל.

מה יש לו? יש לנו פה את ה-NVIC שמחובר אליו, את ה-Instruction bus, את ה- לא זוכר... שכחתי את השמות שלהם כרגע, אבל זה איזשהם רכיבים מתוך ה-core שלנו. יש לנו פה איזשהו DMA, יש לנו פה עוד רכיבים, כל אלה מחוברים.

וכאשר אנחנו... לדוגמה, כאשר ב-Port A, ב-GPIO Port A, אנחנו משנים איזשהו משהו, ה-NVIC יכול על ידי החיבורים האלה לחוש את הדבר הזה, וכאשר הוא חוש את זה, אז הוא לוקח את ה-core הזה ואומר לו: בוא, אתה לא רץ פה, אתה עכשיו רץ כאן. ועל הדרך, מתוך ה-core הזה נעשה לנו ה-context switching. זה ה-context switching שדיברנו של interrupt מול application. אוקיי? בלי מערכת הפעלה בכלל.

תוכל לתת מילה על ה-NVIC?

ה-NVIC זה ה-Interrupt Controller שלנו. הוא מרגיש event-ים שמוגדרים ב-Interrupt Table שראינו, והוא מקפיץ את ה-MCU, מקפיץ את ה-core של ה-MCU, מה שמפעיל את ה-instruction, מקפיץ אותו לפונקציה הספציפית. הפונקציה הספציפית הזאתי, זה הפונקציה שתפעיל, זה ה-handler של ה-interrupt, אוקיי? אז ראינו handler-ים, ראינו את זה. כאן זה נראה בצורת בלוקים. יש לנו כל מיני interrupt controller-ים.

אוקיי, אלה ה-peripherals שיש לנו פה. יש לנו port-ים של פינים מ-A עד H עם איזשהו חור. יש לנו UART-ים, אוקיי? שנקראים USART-ים, שזה asynchronous synchronous, זה בעצם צורה לתקשר בצורה סינכרונית ואסינכרונית. timer-ים הם גם peripheral-ים. למה הכוונה peripheral-ים? זאת אומרת שגם הם לא נמצאים על ה-core עצמו, הם בפריפריה שלו.

אוקיי, SPI, temperature sensor, זה בעצם חיישן טמפרטורה שנמצא בתוך ה-CPU שלנו. עוד timer-ים, analog to digital converter, watchdog, DMA, שנדבר על זה בהמשך הקורס, והרבה מאוד רכיבים. כי זה בעצם הארכיטקטורה עצמה. סליחה, זה ה-block diagram עצמו.

## Programming Manual

והדבר האחרון, אני יודע שאני מושך אתכם, והדבר האחרון שמעניין אותנו מהמסמכייה הזאתי, אוקיי? זה ה-Core Reference Manual, שזה ה-Reference Manual שמגדיר לנו את ה-core עצמו.

זה נקרא גם Reference Manual, אם הייתם איתי ביחד, מצאנו פה את ה-Programming Manual, שזה משהו מקביל. רגע, כן, Programming Manual, אוקיי? שהוא מסמך בסופו של דבר שמכיל את ה-Reference Manual של ה-MCU שלנו, ומסביר לנו על כל מה שיש לנו. מה זה stack, אם קצת פספסתם מה זה.

והוא מסביר לנו מה ה-core registers, בעצם R0 עד R12 זה ה-General Purpose Registers, Stack Pointer שהזכרתי ומה הוא מורכב, אבל נתייחס אליו כ-Stack Pointer אחד, שהוא אומר לנו איפה בתוך ה-stack אנחנו נמצאים, ברמת ה-context. Link Register, Program Counter, ו-Status Register ספציפיים, שכל אלה ביחד הם נחשבים context. אוקיי? יש לכם פה מידע על כל אחד מהם, על ה-General Purpose Registers, מה הם אומרים, מה הם ה-Stack Pointers. אם זה ממש מעניין אתכם יש קורס שלם שעשינו בגדול על המסמך הזה ועל כל הארכיטקטורה עצמה.

דברים נוספים זה איך בעצם לקנפג את ה-SysTick. בוא נרשום פה שנייה SysTick, SysTick.

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

בסדר, אלה המסמכים.

## הנחיות לעבודה ומטלות

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

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

המסמך השני זה ה-Reference Manual. קצת תכירו את ה-Reference Manual של הבורד שלכם, מה אתם יכולים לעשות איתו, ומה קורה בכל מיני חלקים. לדוגמה, אם אתם רוצים משהו ספציפי לחפש, תראו איך הוא עובד עם input ו-output מ-GPIO, מה הרכיבים שיש לו שם, אוקיי? ומה ה-mode-ים של ה-GPIO שאתם יכולים לעבוד איתם. קונפיגורציית פינים, אוקיי.

החלק השני שאתם יכולים לעשות זה ה-datasheet, תעברו על סכמת הבלוקים הזאתי, תראו שאתם מבינים אותה. אם אתם לא מבינים, תשאלו בקבוצה, כולם ישמחו לעזור לכולם.

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

וכשתסיימו את כל זה, נמשיך כאילו לקנפג את ה-logger. אז אם אתם כן רוצים לעקוב תוך כדי, אז שיהיה לכם איזשהו ממיר של UART ל-USB, אוקיי? איזשהו ממיר UART ל-USB כדי שתוכלו להשתמש בו להריץ COM, ואני אראה לכם איזשהו סקריפט פייתון שבאמצעותו אנחנו מדפיסים. אם יש לכם FTDI סבבה, אבל FTDI סתם יקר, אתם יכולים להזמין דברים יותר זולים. מאליאקספרס או...

מה זה ה-UART שצריך, אתה יכול לפרט על... למה הכוונה ממיר UART ל-USB?

לא RTOS, ממיר UART. זה ממש חומרה, אוקיי, אני אסביר, UART to USB converter, אוקיי? יש מלא כאלה, סבבה? האחד שיותר מוכר זה אחד שנקרא FTDI, אבל הוא יקר, אתם רואים, סתם אבל. זה פשוט נותן לנו את האפשרות לשלוח לוגים, נותן לנו עוד אפשרות, אוקיי? נותן לנו עוד אפשרות לשלוח לוגים אל המחשב.

אורן, זה חיבור של הכרטיס בנוסף לחיבור USB הרגיל שלו?

כן, זה חיבור אופציונלי נוסף. למה אנחנו עושים אותו? כדי שיהיה לנו task. אם אתם לא רוצים לשלם את הדבר הזה, כאילו לא חייב את זה ספציפית, שימו לב שיש פה עוד מלא, שחלק מהם עולים גרושים. אם אתם לא חושבים שתספיקו, אני אראה בשיעור הבא דרך נוספת לעשות את זה מתוך ה-ST-LINK, אוקיי? אבל אז יהיה לנו יותר קשה לסנכרן את ה-RTC.

בסדר? אני משתמש ב-UART כדי לסנכרן את ה-Real-time Clock.

אור, אבל זה נגיד ב... מה שאתה מראה על המסך, נגיד ה-converter-ים האלה, הנה למשל זה, אז איך הוא... הוא כאילו אני צריך לחבר כבל מאריך ל-USB?

לא, לא, זה נכנס למחשב מצד אחד. מצד השני בפינים האלה, אנחנו מחברים אותם בפינים האחרים בבורד.

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

רגע, פשוט אראה, יש לי פה... אתם מצליחים לראות? אין לי עוד כבל...

תקרב... רגע, אולי נעשה אותך המסך הראשי.

רוצים לראות? לדוגמה, הנה יש לי גם כבל. הכבל הזה מגיע ומפעיל לי את ה... את ה...

תסיר את זה, הנה רק... כן. הנה, עכשיו רואים. תכתבו "Add pin" על המסך ואז הוא הופך להיות ה...

אני יכול להפסיק גם את ה-share.

הווידאו שלך נהיה ראשי.

אוקיי, אז אני רואה את ה-setup שלך.

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

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

כן, זה לא יגיע תוך שבוע.

יש את זה במקום אחר שאפשר לקנות כזה?

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

השאלה מה אנחנו הולכים לעשות עם זה? אנחנו פשוט רוצים ליצור קצת אינטראפטים במערכת, וכי כאילו...

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

מי שמייצר את הלוגים ל-UART זה ה-FreeRTOS או שזה הרכיב עצמו?

ה-MCU.

אורן, אני חייב לסיים. יש עוד משהו שאתם הולכים לדבר עליו ש...

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

ומה אמרת מבחינת שיעורי בית, מה שאפשר לעשות זה ה...

עדיין לא רלוונטי. רק לעבור על המסמכים. אה, המסמכים.

בסדר גמור.

בשמחה, אתה יכול ללכת להקלטה, פשוט לעשות Stop.

תודה, תודה רבה.

בכיף. ניפגש בשבוע הבא כרגיל. להתראות.

לילה טוב, ביי ביי.

ביי.