בחלק א' הראיתי איך אפשר, באמצעות שני חוטי חשמל בלבד, לשלוט בשתי נוריות LED נפרדות (עם מגבלה על הארה בו-זמנית), ושאלתי בכמה נוריות אפשר לשלוט באמצעות שלושה, ארבעה או מקרה כללי כלשהו של N חוטים.
אחד המגיבים השקיע ומצא את התשובה הנכונה עבור שלושה חוטים – שש נוריות LED. ומה לגבי השאר? אם אתם עדיין רוצים לפתור את החידה לבד, אל תקראו את הפסקאות הבאות, כי בפוסט זה אגלה את התשובה המפורטת. בנוסף, נזכיר שאלה מעניינת נוספת שעלתה בתגובות לחלק הקודם. אך לפני שנתחיל, הנה הדגמה של שליטה בשש נוריות באמצעות שלושה חוטים:
כדי להבין את התשובה, צריך קודם כל לזכור שלפינים הדיגיטליים של הארדואינו (ושל רוב המיקרו-בקרים האחרים) יש שלושה מצבים: פלט נמוך, פלט גבוה וקלט. במצב קלט, הפין הוא בעכבה גבוהה (High Impedance), שלצורך העניין זה כמעט כמו נתק פיזי. לכן, לא משנה בכמה פינים אנחנו משתמשים, אנחנו תמיד יכולים לשים את כולם במצב קלט חוץ משניים, לתת לאחד מהשניים האלה ערך פלט נמוך ולשני גבוה, וכך לשלוט בשתי נוריות LED שמחוברות לשני החוטים הללו – בדיוק כמו שעשינו בחלק א'.
לכן, אפשר להסתכל על השאלה הכללית "בכמה נוריות אפשר לשלוט עם N חוטים" מהזווית של "כמה זוגות חוטים שונים אפשר ליצור מ-N חוטים?" זו שאלה מתחום הקומבינטוריקה, והתשובה הסופית מתבטאת בנוסחה פשוטה מאד:
N * (N - 1)
למה? ראשית, דמיינו שיש חמישה אנשים וצריך לבחור זוג מתוכם. כמה אפשרויות יש? אנחנו יכולים לבחור כל אחד מהחמישה בתור "עוגן", ולצרף אליו כל אחד מארבעת הנותרים, סה"כ 20 אפשרויות. במקרה של בני אדם, צריך לחלק את התוצאה בשניים כי "אברהם ומשה" זה הרי אותו זוג כמו "משה ואברהם", אבל מכיוון שכל זוג חוטים שולט בשתי נוריות LED ולא באחת, אנחנו חוזרים ומכפילים בשתיים. עם שלושה חוטים אנחנו מגיעים ל-6 נוריות, עם ארבעה ל-12, עם חמישה כאמור ל-20 וכן הלאה.
איך עושים את זה בתכל'ס? פשוט מאד: עושים רשימה של כל הזוגות האפשריים מתוך כלל הפינים שהקדשנו למשימה, ולכל זוג כזה מחברים שתי נוריות LED – אחת עם הפלוס לכיוון החוט הראשון, ואחת עם הפלוס לכיוון השני. כדי להאיר נורית מסוימת, מגדירים את כל הפינים שלא מחוברים אליה כקלט, את הפין שמחובר לפלוס שלה כפלט HIGH ואת הפין שמחובר למינוס/Ground כפלט LOW. כך זה נראה בשרטוט עבור שלושה פינים:
כדי להדליק את נורית A בשרטוט זה, ורק אותה, צריך לקבוע את פין 1 כ-HIGH, את פין 0 כ-LOW ואת פין 2 כ-Input. כדי להדליק את נורית F בלבד, צריך לקבוע את 2 כ-HIGH, את 0 כ-LOW ואת 1, ליתר ביטחון, כ-Input. אבל במקרה כזה, מה מונע מהנוריות C ו-A לדלוק גם כן? הרי גם הן מחוברות בעצם לאותו מעגל ובכיוון הנכון!
אם שמתם לב, לא הצגתי בשרטוט שום נגדים. ביישום אמיתי נזדקק להם, כמובן, כדי לא לשרוף את הנוריות בטעות, אבל המיקומים שלהם לא באמת קריטיים: בסרטון למעלה, לדוגמה, חיברתי נגד ישירות לכל פין, ולא כמו שמופיע בשרטוט בספר שממנו נלקח כל הרעיון, ועובדה שזה עובד. הנקודה הקריטית היא שביחס לפינים 2 ו-0, נורית F היא בודדה, ואילו C ו-A מחוברות בטור. לחשמל יותר קשה, כביכול, לעבור דרך שתי נוריות מאשר דרך אחת, ולכן הוא "מעדיף" את המסלול הקל עם הנורית הבודדה.
אף על פי כן, יש חשיבות מסוימת למיקומי הנגדים. אם נוסיף נגד מספיק גדול בטור לנורית F, המסלול שלה יהפוך לפחות אטרקטיבי מבחינת הולכה, ו-A ו-C הן אלה שיידלקו.
למי שמתעניין, הנה הקוד ששימש למעגל שבסרטון למעלה. כמובן שהוא מתאים רק לסידור מאד ספציפי של הנוריות, אם כי לא מסובך להמיר אותו לסידורים אחרים:
// Three-wire six-LED controller demo // by Ido Gendel, 2012 // Share and enjoy! // Bit patterns for pin state - 2 bits per pin const byte singleLEDState[6] = {52, 49, 13, 7, 19, 28}; #define MAX_DELAY 170 int delayVal = MAX_DELAY / 2; int delayStep = 1; //----------------------------------------------- void setup() { for (byte j=8; j < 11; j++) pinMode(j, OUTPUT); } //----------------------------------------------- void setState(byte s) { for (byte j=8; j < 11; j++) { // Isolate lowest 2 bits // 00 = LOW, 01 = HIGH, 10 or // 11 = INPUT (high impedance) switch (s & 3) { case 0 : pinMode(j, OUTPUT); digitalWrite(j, LOW); break; case 1 : pinMode(j, OUTPUT); digitalWrite(j, HIGH); break; default : pinMode(j, INPUT); } // Shift remaining bits down s = s >> 2; } } //----------------------------------------------- void stepDelay() { // Variable delay time handling delay(delayVal); delayVal += delayStep; if ((delayVal == 0) || (delayVal == MAX_DELAY)) delayStep = -delayStep; } //----------------------------------------------- void loop() { // Cycle through bit patterns // to light up LEDs in sequence for (byte b = 0; b < 6; b++) { setState(singleLEDState[b]); stepDelay(); } }
ולסיום, הנה הקדמה קטנה לפוסט הבא בסדרה:
אז מה אתה אומר על זה?
http://www.youtube.com/watch?v=PD7DzTIFJdU
לדעתי להציג את זה פה זה אכזרי קצת, אבל יאללה 🙂
היתה תחרות של כאלה ביוטיוב, לא? אכן אכזרי מאד. מצד שני, זה כמו המרצים האלה למתמטיקה בשנה א' שנותנים כשיעורי בית חידות שאין להן פיתרון… אולי מישהו פה יתלבש על הנושא וימצא פתרון אמיתי 🙂
תשמע, קרא לי [מילה הוסרה בעריכה – ע.ג.] אבל זה לא כזה פשוט להבין את החיבור של הלדים, אבל אחרי שהבנתי ועברתי לקוד – שמה לא הבנתי כלום…אפילו לא את השורה הראשונה..
הבנתי רק את ה if/for
אני לא מוכן לשמוע מילים כאלה בבלוג שלי 😉
גם "פשוט" זה עניין יחסי. יש לך ניסיון בבניית מעגלים בסיסיים? בתכנות בשפת C? אם רק התחלת לגעת בתחום, ברור שזה לא יהיה מובן מאליו. אני ממליץ לקרוא שוב את חלק א' של הסדרה הזו (יש קישור בהתחלת הפוסט), הוא יעזור לך להבין יותר טוב את עניין הנוריות. [תוספת מאוחרת: סליחה, אמרת שהבנת – אז לא חשוב :-)]
הקוד ששמתי פה מורכב יחסית, והוא לא לצורך לימוד – שמתי אותו רק במקרה שמישהו ירגיש שהוא ממש חייב לדעת…
הבנתי, אז עוד מוקדם בשבילי לקפוץ ולנסות להבין את הקוד הזה..
תודה שאמרת חשבתי שכולם פה (המתחילים) לפחות הרוב מבינים את הקוד
אז אסתפק במידע על החיבור לדים, ולשאר המתחילים – יש כאן חוברת של תרגילים והסברים שאני אהבתי (זה באנגלית) דפדפו קצת ותעיינו בדברים http://www.earthshineelectronics.com/files/ASKManualRev5.pdf
תודה עידו על הפוסט 🙂
נראה מדריך מעניין מאד, תודה!
היי,
בהמשך לפוסט הקודם, (ורק לידע כללי), השיטה הנ"ל נקראת: Charlieplexing
אפשר לקרוא עוד ב-wiki
BenB>
כן, כבר הזכרתי את זה בחלק ג'… 🙂 תודה בכל אופן!