טריקים חשמליים עם נוריות LED, חלק ב’

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

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

כדי להבין את התשובה, צריך קודם כל לזכור שלפינים הדיגיטליים של הארדואינו (ושל רוב המיקרו-בקרים האחרים) יש שלושה מצבים: פלט נמוך, פלט גבוה וקלט. במצב קלט, הפין הוא בעכבה גבוהה (High Impedance), שלצורך העניין זה כמעט כמו נתק פיזי. לכן, לא משנה בכמה פינים אנחנו משתמשים, אנחנו תמיד יכולים לשים את כולם במצב קלט חוץ משניים, לתת לאחד מהשניים האלה ערך פלט נמוך ולשני גבוה, וכך לשלוט בשתי נוריות LED שמחוברות לשני החוטים הללו – בדיוק כמו שעשינו בחלק א'.

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

N * (N - 1)

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

איך עושים את זה בתכל'ס? פשוט מאד: עושים רשימה של כל הזוגות האפשריים מתוך כלל הפינים שהקדשנו למשימה, ולכל זוג כזה מחברים שתי נוריות LED – אחת עם הפלוס לכיוון החוט הראשון, ואחת עם הפלוס לכיוון השני. כדי להאיר נורית מסוימת, מגדירים את כל הפינים שלא מחוברים אליה כקלט, את הפין שמחובר לפלוס שלה כפלט HIGH ואת הפין שמחובר למינוס/Ground כפלט LOW. כך זה נראה בשרטוט עבור שלושה פינים:

SixLEDWiring
חיבור להפעלת שש נוריות לד באמצעות שלושה פינים, לא כולל נגדים

כדי להדליק את נורית 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();
  }   

}

ולסיום, הנה הקדמה קטנה לפוסט הבא בסדרה:

להרשמה
הודע לי על
8 Comments
מהכי חדשה
מהכי ישנה לפי הצבעות
Inline Feedbacks
הראה את כל התגובות

אז מה אתה אומר על זה?
http://www.youtube.com/watch?v=PD7DzTIFJdU

לדעתי להציג את זה פה זה אכזרי קצת, אבל יאללה 🙂

תשמע, קרא לי [מילה הוסרה בעריכה – ע.ג.] אבל זה לא כזה פשוט להבין את החיבור של הלדים, אבל אחרי שהבנתי ועברתי לקוד – שמה לא הבנתי כלום…אפילו לא את השורה הראשונה..
הבנתי רק את ה if/for

הבנתי, אז עוד מוקדם בשבילי לקפוץ ולנסות להבין את הקוד הזה..
תודה שאמרת חשבתי שכולם פה (המתחילים) לפחות הרוב מבינים את הקוד
אז אסתפק במידע על החיבור לדים, ולשאר המתחילים – יש כאן חוברת של תרגילים והסברים שאני אהבתי (זה באנגלית) דפדפו קצת ותעיינו בדברים http://www.earthshineelectronics.com/files/ASKManualRev5.pdf

תודה עידו על הפוסט 🙂

היי,
בהמשך לפוסט הקודם, (ורק לידע כללי), השיטה הנ"ל נקראת: Charlieplexing
אפשר לקרוא עוד ב-wiki

BenB>