כל מי שרוצה לתכנת מיקרו-בקרים חייב לשלוט, בין השאר, בייצוג בינארי (בסיס 2) והקסדצימלי (בסיס 16) של מספרים. הייצוג הבינארי חיוני כדי להבין את נושא הביטים, ואילו ההקסדצימלי מאפשר לנו להתמודד איתו בנוחות יחסית בזמן התכנות בפועל. הבעיה היא שאנחנו מחונכים מגיל אפס לחשוב ולהשתמש בבסיס העשרוני בלבד, וזה הרגל שקשה להיפטר ממנו.
הפרויקט שמתארח היום בבלוג, חוץ מזה שהוא יופי של תרגיל למתחילים, יכול לעזור ללומדי התכנות לחשוב בבינארי ובהקסדצימלי. הוא כולל ארבעה לחצנים, שמייצגים ארבעה ביטים במספר בינארי, ולידם תצוגת 7-Segment של ספרה יחידה, אשר מציגה את המספר הנ"ל בבסיס הקסדצימלי. כמה זה המספר הבינארי 1011 בהקסדצימלי? איך מייצגים בבינארי את המספר ההקסדצימלי 7? עם הממיר הבינארי-להקסדצימלי, גם אתם תוכלו לדעת!
לפרויקט הזה יש היסטוריה לא פשוטה – כפי שמספר היוצר, בני שפירא, בבלוג הטרי שלו:
"הרעיון לפרויקט עלה בראשי תוך כדי ניסיונות עם תצוגת 7-Segment ולחצן פשוט במטרה שעם כל הקלקה הספרה בלחצן תגדל ב-1 עד שתגיע ל-9 ומשם תקטן ב-1 עד ל-0. התוצאה היתה נחמדה אבל לא ממש שימושית (לא שמישהו באמת צריך ממיר בינארי אבל זה לא חשוב עכשיו) אז חיברתי עוד לחצן וכך אחד מחבר ואחד מחסר (עדיין לא ממש שימושי).
המשכתי להוסיף לחצנים במטרה להגיע ל-10 לחצנים, שכל אחד מהם ייתן מספר אחר (שימושי מאוד) ובלחצן הרביעי נזכרתי ש-4 ביטים מייצגים ספרה הקסדצימלית, ובמקום שכל לחצן מתוך 10 יציג ספרה אחרת, אפשר באמצעות 4 לחצנים שישמשו כ-0 ו-1 לייצג ספרת הקס. כך גם אוכל להרחיב את הספירה עד 16 בתצוגה של ספרה אחת."
הפרטים המדויקים אולי שונים בין מקרה למקרה, אך הקטע של רעיונות שצצים מתוך העשייה מוכר היטב לכל מפתח!
החומרה
כפי שרואים בתמונות, הפרויקט מומש באמצעות ארדואינו מגה – יש גם גרסה מעודכנת עם Duemilanove – ומטריצה. ארבעת הלחצנים הפשוטים מתווכים בין יציאת 5V לבין ארבע כניסות דיגיטליות בארדואינו, כשלכל כניסה כזו יש גם נגד Pull-down, שמבטיח שהיא תקרא 0 כאשר הלחצן אינו לחוץ ויש נתק מלא.
התצוגה גם היא סטנדרטית: פין Ground משותף, ושמונה כניסות – אחת לכל סגמנט של הספרה עצמה ועוד אחת לסגמנט ה"נקודה העשרונית". שמונה הכניסות האלה מתחברות לשמונה יציאות דיגיטליות של הארדואינו.
התוכנה, חלק ראשון
הקוד המקורי של בני המיר את הקלט הבינארי לפלט ההקסדצימלי בדרך הכי ישירה ובוטה שאפשר. ראשית, הוא הגדיר תבנית הדלקה של סגמנטים עבור כל אחד מהמספרים. כך זה מתחיל:
byte sevenSegDig[16][7] = { { 1,1,1,1,1,1,0 } , // = 0 { 0,1,1,0,0,0,0 } , // = 1
בפונקציה loop, הוא קרא את הקלט מהלחצנים לארבעה משתנים, וכתב שישה-עשר תנאים נפרדים לבדיקה של כל אחת מהאפשרויות ותגובה בהתאם. לדוגמה:
if((a==LOW) && (b==LOW)&&(c==LOW)&&(d==LOW)) { SevenWrite(0); Doti(0); } else if((a==LOW) && (b==LOW)&&(c==LOW)&&(d==HIGH)) { SevenWrite(1); Doti(0); } else if // …
הפונקציה SevenWrite לוקחת, בהתאם לפרמטר שנשלח אליה, אחת מהתבניות שהוגדרה קודם ו"כותבת" אותה אל הסגמנטים של התצוגה.
ומה עושה הפונקציה Doti? בבסיס ההקסדצימלי, המספרים העשרוניים 10-15 מיוצגים באמצעות האותיות A-F. אין בעיה להציג, למשל, F על תצוגת 7-Segment, אבל בני הבחין ש-B ו-D עלולות לגרום לבלבול עם 8 ו-0. לכן, הוא הוסיף את הפונקציה הקטנה הזו, שהאירה בנקודה העשרונית של התצוגה בכל פעם שהספרה המוצגת היתה גדולה מ-9.
התוכנה, Reloaded
הקוד המקורי עובד, ואם הוא היה מסתתר בתוך מוצר מדף, לא היינו מרגישים בשום בעיה. אך בתור בעל ניסיון מסוים בתכנות, כל המלל הזה הכאיב לי בעיניים ובלב. הצעתי דרך לשפר אותו. בני לא רק עשה זאת בצורה זריזה ומושלמת על סמך התיאור הכללי מאד שלי, אלא אף השאיר את הקוד המקורי לצד החדש בבלוג שלו, למען הדורות הבאים. על האומץ והכנות האלה לבדם מגיעה לו הצדעה!
אז מה התיקונים? קודם כל, סיפרתי לבני על הפתרון העתיק לבעיית ייצוג ה-B וה-D: אותיות קטנות! אפשר להציג b ו-d בקלות על תצוגת 7-Segment, ואין סכנה של בלבול עם משהו אחר – אחרי שזוכרים שהספרה 6 התקנית היא עם קו אופקי למעלה.
שנית, אם הלחצנים כבר מייצגים ביטים, למה לא להפוך אותם לביטים בפועל? במקום שש-עשרה בדיקות נפרדות, כל לחצן קבע את הערך של ביט תואם במשתנה אמיתי – וכך נוצר, מיידית, הפרמטר שאפשר לשלוח כמו שהוא אל SevenWrite. פונקציית loop הענקית מקודם הוחלפה בקוד החסכוני הבא:
void loop() { int sum=0; int a =digitalRead(msb); int b = digitalRead(m2sb); int c = digitalRead(l2sb); int d = digitalRead(lsb); if(d==HIGH){sum=sum+1;} if(c==HIGH){sum=sum+2;} if(b==HIGH){sum=sum+4;} if(a==HIGH){sum=sum+8;} SevenWrite(sum); }
(נגלה לכם בסוד שאפילו את זה אפשר לצמצם ולייעל… אבל את זה נשאיר לכם כתרגיל לבית).
תודה לבני על השיתוף – נקווה לראות עוד הרבה רשומות בבלוג שלו, ועוד הרבה פרויקטים שלו ושלכם כאן ב"בייט הלבן"!
בקוד ראו רק איך ממירים מבינארי להקסדצימלי אולי אפשר לעשות כפתור או מתג נוסף שבו יקבע אם ההמרה תהיה מבינארי להסדצימלי או להפך (ואז חלק מהכפתורים יהיו הוספה והפחתה במקום ביטים)
דבר שני: איך התוכנה יודעת מתי נגמר מספר אחד ומתי מתחיל מספר חדש?
פרויקט ממש נחמד.
דרך נוספת שחשבתי עליה .אני לא יודע אם זה יותר יעיל ממה שאתה רשמתה אבל ממה שהוא רשם בזה שאני בטוח 🙂 אם אני טועה אני אשמח ותתקנו אותי
(כל לולאת for היא "מציינת" byte אחד ו A,B,C,D כפתורים)
int count=0;
void loop(){
for(int i=0;i<2;i++){
for(int j=0;j<2;j++){
for(int g=0;g<2;g++){
for(int h=0;h<2;h++){
count++;
if(A==i && B==j &&C==g && D==h){
//לשלוח את count להדפסה ל 7-Segment//
}
}
}
}
}
Serial.println(count);
count=0;
}