כולם יודעים של-ATmega328, המיקרו-בקר שבלב לוחות הארדואינו הנפוצים, יש 32 קילובייט של זכרון Flash לשמירת הקוד המקומפל. כולם יודעים גם שיש לו 2 קילובייט של זכרון RAM, וליתר דיוק SRAM, לאחסון זמני של משתנים ומידע אחר (עד שהארדואינו מנותק מזרם החשמל). אבל אם תסתכלו היטב במפרט של הג'וק הזה תראו שם גם קילובייט אחד של EEPROM. מה זה? מה זה נותן? ואיך מגיעים לשם? כל זאת ועוד, אחרי התמונה…
ובכן, יש חדשות טובות וחדשות רעות. החדשות הטובות הן שה-EEPROM הוא אכן שטח זכרון של קילובייט שלם שעומד לרשותכם, ובניגוד לזכרון ה-SRAM המוכר, ה-EEPROM לא יימחק אם וכאשר הארדואינו ינותק מהחשמל.
החדשות הרעות הן, קודם כל, שהכתיבה ל-EEPROM איטית להחריד (מדדתי ממוצע של כ-3.3 אלפיות השניה לכתיבה של בייט יחיד – בדיוק כמו שמצוין במפרט), מה שגם הופך אותה לפגיעה לכל מיני תהליכים כמו פסיקות. דבר שני, כל פעולת כתיבה מקרבת את הקץ של ה-EEPROM (המפרט מתחייב לאמינות מלאה רק עד 100,000 פעולות כתיבה – בהחלט לא משהו שאתם רוצים לשים בלולאה הראשית). שלישית, הפניה ל-EEPROM נעשית לא באמצעות משתנים, אלא ישירות לבייטים על פי כתובות מספריות.
אז מתי בכל זאת משתמשים ב-EEPROM של הארדואינו?
הדוגמה הקלאסית ביותר, תרתי משמע, שהצלחתי לחשוב עליה היא טבלת השיאים (Highscore) במשחקי וידאו ומחשב: כל שחקן שמגיע לתוצאה בין עשר הטובות ביותר זוכה לרשום את שמו בטבלה, והשם יישאר שם תמיד – לפחות עד ששחקנים אחרים ידחקו אותו החוצה. הכתיבה לטבלה הזו מתבצעת לעתים נדירות מאד, והמידע חייב לשרוד גם אם המחשב/קונסולה מנותקים מהחשמל.
הנה דוגמה נוספת: אתם רוצים לבדוק את מהירות התגובה הממוצעת של כל החברים שלכם. אתם כותבים קוד שמודד את הזמן בין הידלקות נורה לבין לחיצה על כפתור, ויוצאים לסבב ביקורים אצל החבר'ה עם המערכת בתיק. אם תשתמשו בזכרון הרגיל בלבד תהיו חייבים לדאוג לאספקת חשמל שוטפת לארדואינו, ומספיק שמישהו מושך בטעות איזה חוט וכל המידע הלך. בעזרת ה-EEPROM, לעומת זאת, אפשר "לצרוב" כל מדידה חדשה שמתקבלת, לנתק את המכשיר בין לבין, ולשפוך את כל הנתונים למחשב דרך החיבור הסריאלי כשחוזרים הביתה. בהנחה שאין לכם אלפי חברים, לא תצטרכו שום התקן אחסון חיצוני!
ואיך עושים את זה בפועל?
הדבר הראשון שצריך, כבר בתחילת הקוד, הוא השורה שמוסיפה לתוכנית את הספריה שמטפלת בגישה ל-EEPROM:
#include "EEPROM.h"
בספריה הזו יש שתי פקודות בלבד: EEPROM.read, שמקבלת כתובת (במשתנה מסוג int) ומחזירה ערך byte (ליתר דיוק, uint8_t) והפקודה EEPROM.write שמקבלת גם כתובת int וגם ערך byte לכתיבה. מכיוון שיש לנו קילובייט יחיד של EEPROM, טווח הכתובות הוא 0-1023. מה יקרה אם ניתן כתובת גבוהה או נמוכה יותר? הפקודות יתעלמו מהביטים המיותרים, והמידע ייכתב או ייקרא מכתובת חוקית כלשהי. לדוגמה, קריאה מכתובת 1024 תחזיר לנו את המידע שנמצא, בפועל, בכתובת 0.
... EEPROM.write(765, 19); ... byte b; b = EEPROM.read(765); ...
עוד דברים שכדאי לדעת
אם אף פעם לא נגעתם ב-EEPROM של הארדואינו שלכם, הוא מלא כעת בערכי 255. מרגע ששיניתם משהו, השינוי יישאר שם גם אם תטענו לארדואינו תוכנה חדשה! זה יכול להיות יתרון (למשל, לטעון פעם אחת טבלה של ערכי סינוסים וקוסינוסים ולהשתמש בה בכל תוכנית שתרצו), וזה יכול להיות חיסרון (למשל, אם תוכנית אחת משתמשת ב-EEPROM לאחסון מידע בפורמט שיכול לשגע את התוכנית הבאה, או שקיבלתם ארדואינו ממישהו אחר שלכו תדעו מה הוא עשה איתו קודם). לכן, אלא אם אתם ממש מסודרים, הנחת היסוד שלכם צריכה להיות שיש ב-EEPROM נתונים אקראיים לגמרי, והתוכנה שלכם צריכה לדעת להתמודד עם זה – או שתהיה לכם תוכנת עזר קטנה, שמורה באיזו פינה בדיסק, למחיקה גורפת של ה-EEPROM. זמן הקריאה של בייט יחיד מה-EEPROM, אם תהיתם, הוא כ-3 מיקרו שניות.
איך אני יכול לכתוב תוכנה שמודדת עוצמת אור?
קודם כל תשיג חיישן שמאפשר מדידה של עוצמת אור, ואז תכתוב תוכנה שקוראת ממנו את הנתונים 🙂
מעניין… אני מצטרף לבקשה של עדי.
אגב, טבלה של ערכי סינוס וקוסינוס היא לא דבר יעיל במיוחד, כי באמצעות פונקציה פשותה של טור טיילור אפשר להגיע לתוצאה מדוייקת מספיק עבור כל ערך כמעט (אם אתה לא מתכנן להשתמש בארדואינו שלך לשליטה בלייזר לחיתוך או משהו). אבל תקן אותי אם אני טועה.
אגב, הארדואינו ננו מתחבר ישירות למטריצה? ואם כן, זה לא עדיף על האונו הרגיל מבחינת חיסכון בחוטים וכו'?
להרחבות זכרון גדולות יותר עוד אגיע – יש פתרונות לכרטיסי SD, ויש ג'וקים של זכרון EEPROM (דומים לג'וקים של ה-RAM שהזכרתי בבלוג בעבר) שאולי אצליח להשיג בהזדמנות.
סינוס וקוסינוס אפשר לחשב בכל פעם מחדש, השאלה כמה זמן לוקח החישוב וכמה זמן אתה יכול להקצות לו ביישום שלך…
הנאנו אכן מתלבש ישירות על המטריצה, אבל היתרונות והחסרונות לעומת הלוחות ה"רגילים" תלויים לגמרי ביישום שלך, באילו רכיבים אתה משתמש וכו'.
אני לאחרונה החלפתי אונו בננו על מנת לחסוך מקום במארז – מדובר בשני שליש מבחינת הגודל, כולל הbreadboard הקטן – וזה עשה הבדל משמעותי ביותר ביכולתי להכניס שתי סוללות 9V, ארדואינו, מעגל סוויצ'ים, מעגל לדים, פוטנציומטר ומנחת זרם עבור סרבו לתוך קופסא יותר קטנה ונוחה לאחיזה.
אם הכל יעבוד כמו שצריך השלב הבא יהיה לצרוב את הקוד על AVR ובכלל כל העניין יקטן עוד יותר (אם אני מבין נכון…)
גם לי יש תוכניות לצרוב מיקרוקונטרולרים בשלב כזה או אחר (שמתי עין על ATtiny). אם תקדים אותי, אני אתעלק עליך שתפתור לי את כל הגליצ'ים של המתחילים 🙂
זה יהיה הישג משמעותי ביותר מבחינתי!
🙂
השאלה על איזה יעילות אתה מדבר, יעילות במקום בזכרון או יעילות בזמן ריצה.
אל תשכח שזה מעבד 8 ביט פשוט, בקושי יש לו יכולת של הכפלה בחומרה (של מספרי 8 ביט), להעלות בחזקה זה דבר מאוד לא טריויאלי בשביל בקר שכזה.
הצעד הטבעי הבא הוא להסביר איך להרחיב את הזיכרון/להשתמש בSD או כל פיתרון אחר שיאפשר לנו לשמור הרבה יותר ערכים…
תודה!