חיישנים ומפעילים למיקרו-בקרים · פרק 3 — מבואות ספרתיים: חיבור חיישן ספרתי לבקרסמלי מקצוע 33.102 ו-33.002⌂📚🏠
פרק 3

מבואות ספרתיים: חיבור חיישן ספרתי לבקר

כיתות י״א–י״ב · 4 שעות לימוד

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

בסוף הפרק הזה נדע:
  • נקבע את רמות המתח ש-Arduino Uno ו-ESP32 קוראים כ-HIGH וכ-LOW, ואת התחום הלא מוגדר שביניהן.
  • נסביר את בעיית המבוא הצף ונפתור אותה בנגד משיכה למעלה או למטה, חיצוני או פנימי, ונחשב את הזרם בו.
  • נספור לחיצות בעזרת זיהוי קצה, ונסנן קפיצות מגע בתוכנה בעזרת הפונקציה millis().
  • נתכנן מחלק מתח כדי לקרוא חיישן שעובד ב-5 V בבקר שעובד ב-3.3 V.
  • נבודד אות תעשייתי של 24 V בעזרת מצמד אופטי ונחשב את שני הנגדים במעגל.
  • נספור פולסים מהירים בעזרת פסיקה חיצונית attachInterrupt().

רמות לוגיות במבוא ספרתי (Logic Levels)

מבוא ספרתי אינו מודד מתח כמו וולטמטר. הוא משווה את מתח ההדק לשני ספים, ומחזיר לתוכנית אחד משני ערכים: HIGH (1) או LOW (0).

רמה לוגית: תחום המתחים שהמבוא הספרתי קורא כערך לוגי אחד. סף הרמה הגבוהה VIH הוא המתח הקטן ביותר שמובטח שייקרא 1, וסף הרמה הנמוכה VIL הוא המתח הגדול ביותר שמובטח שייקרא 0. מתח שבין שני הספים נמצא בתחום הלא מוגדר: הוא עשוי להיקרא 0 ועשוי להיקרא 1.

בבקר ATmega328P שבלוח Arduino Uno, גיליון הנתונים נותן את שני הספים כשבר של מתח ההזנה VCC, וב-ESP32 כשבר של VDD:

ספי מבוא ספרתי
VIH = 0.6·VCC
VIL = 0.3·VCC
סימןמשמעותיחידת מידה
VIHהמתח הקטן ביותר שנקרא בוודאות HIGH[V]
VILהמתח הגדול ביותר שנקרא בוודאות LOW[V]
VCCמתח ההזנה של הבקר, 5 V ב-Uno[V]

וב-ESP32 (לפי גיליון הנתונים): VIH = 0.75·VDD ו-VIL = 0.25·VDD, כאשר VDD = 3.3 V.

LOW (0)לא מוגדרHIGH (1)0 V1.5 V3 V5 VArduino Uno · 5 VLOW (0)לא מוגדרHIGH (1)אסור!0 V0.825 V2.475 V3.3 V5 VESP32 · 3.3 V
רמות המתח במבוא ספרתי: Arduino Uno ב-5 V ו-ESP32 ב-3.3 V (ערכים לפי גיליון הנתונים). מתח מעל 3.3 V אסור בהדק של ESP32
דוגמה 3.1
נחשב את שני ספי המבוא ב-Arduino Uno, ונקבע איך ההדק קורא כל אחד מהמתחים: 0.8 V, 2.2 V, 4.1 V.
פתרון:
VIH = 0.6·VCC = 0.6·5 = 3 V
VIL = 0.3·VCC = 0.3·5 = 1.5 V

0.8 V < 1.5 V, לכן נקרא LOW. המתח 4.1 V > 3 V, לכן נקרא HIGH.

המתח 2.2 V נמצא בין 1.5 V ל-3 V: בתחום הלא מוגדר. הקריאה אינה מובטחת, והיא עשויה להשתנות מפעם לפעם.

VIH = 3 V , VIL = 1.5 V
שאלה 3.1: חיישן נותן במוצא שלו 2.6 V. מה קורא Arduino Uno? ומה קורא ESP32? (חשבו קודם את שני הספים של ESP32.)
3.3 V לעומת 5 V: ההדקים של ESP32 עובדים ב-3.3 V ואינם עומדים ב-5 V. חיבור ישיר של מוצא חיישן שנותן 5 V להדק של ESP32 עלול להרוס את ההדק או את הבקר כולו. לכן קודם נוריד את המתח בעזרת מחלק מתח, כפי שנראה בפרק זה.
הערה: ומה עם רספברי פיי? גם ההדקים שלו עובדים ב-3.3 V ואינם עומדים ב-5 V, ולכן כל מה שנאמר כאן על ESP32 — שני הספים ומחלק המתח — נכון גם לגביו בדיוק. ההבדל הוא רק בתכנות: ברספברי פיי קוראים את ההדק בפייתון (בספרייה gpiozero) ולא בשפת ארדואינו.

מבוא צף ונגדי משיכה (Floating Input, Pull-up, Pull-down)

מבוא צף: הדק מבוא שאינו מחובר למתח מוגדר: לא ל-5 V ולא ל-GND. התנגדות המבוא גדולה מאוד, ולכן הדק צף קולט רעש חשמלי מהחוטים ומיד קרובה, ועובר בין 0 ל-1 באופן אקראי.

נתבונן בלחצן שמחובר רק בין 5 V להדק D7 (הסרטוט הימני). בלחיצה ההדק מגיע ל-5 V ונקרא 1. אבל כשמשחררים את הלחצן, ההדק אינו מחובר לשום דבר: הוא צף, ולקריאה אין משמעות. הפתרון: נגד שמושך את ההדק לרמה קבועה כשהלחצן משוחרר.

Arduino Uno5VD7GNDS?ההדק צף
לחצן בלי נגד: בשחרור ההדק D7 נשאר צף
Arduino Uno5VD7GNDSRPD10 kΩ
נגד משיכה למטה 10 kΩ: בשחרור נקרא 0, ובלחיצה 1
נגד משיכה למטה: נגד (pull-down) בין הדק המבוא ל-GND. כשהלחצן פתוח הנגד מושך את ההדק ל-0 V; בלחיצה ההדק מגיע ל-5 V, ובנגד זורם זרם קטן I = VCCR.

בתוכנית נגדיר את ההדק כמבוא רגיל pinMode(7, INPUT), והפונקציה digitalRead(7) תחזיר 1 בלחיצה ו-0 בשחרור.

דוגמה 3.2
נחשב את הזרם ואת ההספק בנגד המשיכה למטה 10 kΩ בלחיצה על הלחצן, ונשווה לנגד של 220 Ω. אחר כך נוודא שזרם הזליגה במבוא (עד 1 µA לפי גיליון הנתונים) אינו מעלה את מתח ההדק המשוחרר מעל VIL.
פתרון:
I = VCCR = 510000 = 0.5 mA
P = VCC·I = 5·0.5·10−3 = 2.5 mW

עם 220 Ω הזרם הוא 22.7 mA: בזבוז ללא תועלת, כי המבוא כמעט אינו צורך זרם. וכשהלחצן משוחרר זורם בנגד רק זרם הזליגה:

V = Ileak·R = 1·10−6 · 10000 = 10 mV

וזה קטן בהרבה מ-VIL = 1.5 V, ולכן ההדק נקרא 0 בבטחה.

I = 0.5 mA , P = 2.5 mW

נגד משיכה למעלה ולוגיקה הפוכה:

נגד משיכה למעלה: נגד (pull-up) בין הדק המבוא ל-VCC, והלחצן בין ההדק ל-GND. במנוחה הנגד מושך את ההדק ל-5 V והוא נקרא 1, ובלחיצה הלחצן מחבר את ההדק ל-GND והוא נקרא 0.

זו לוגיקה הפוכה: לחיצה נותנת 0 ושחרור נותן 1. החיבור עם נגד משיכה למעלה הוא הנפוץ ביותר, כי הלחצן מתחבר רק ל-GND, ואין צורך למשוך חוט 5 V אל לוח הלחצנים. ב-ATmega328P יש נגד משיכה למעלה פנימי בכל הדק, ואנו מפעילים אותו בפקודה pinMode(pin, INPUT_PULLUP), כך שאין צורך כלל בנגד חיצוני. ערכו בין 20 kΩ ל-50 kΩ (ערך טיפוסי לפי גיליון הנתונים), כלומר אין זה ערך מדויק.

Arduino Uno5VD2GNDRPU10 kΩS
נגד משיכה למעלה חיצוני 10 kΩ בהדק D2
Arduino UnoD2GND5V20–50 kΩINPUT_PULLUPS
נגד המשיכה למעלה הפנימי: אותו מעגל בלי נגד חיצוני
שאלה 3.2: חשבו את תחום הזרם בנגד המשיכה למעלה הפנימי כשהלחצן לחוץ (VCC = 5 V). ומה מחזירה digitalRead() בלחיצה ובשחרור?
לחצן עם נגד משיכה למעלה פנימי בהדק D2 Arduino Uno · button_pullup_internal.ino
const int BTN = 2;
const int LED = 13;             // on-board LED

void setup() {
  pinMode(BTN, INPUT_PULLUP);   // idle = HIGH
  pinMode(LED, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  int raw = digitalRead(BTN);
  bool pressed = (raw == LOW);  // inverted logic
  digitalWrite(LED, pressed ? HIGH : LOW);
  Serial.print("raw = ");
  Serial.print(raw);
  if (pressed) Serial.println("  pressed");
  else Serial.println("  released");
  delay(300);
}
Serial Monitor · קריאה לדוגמה
raw = 1  released
raw = 1  released
raw = 1  released
raw = 0  pressed
raw = 0  pressed
raw = 1  released
raw = 1  released
טעות נפוצה: שוכחים שהלוגיקה הפוכה וכותבים if (digitalRead(BTN) == HIGH) עבור לחיצה, והתוכנית פועלת הפוך. עם INPUT_PULLUP הלחיצה היא LOW. טעות נוספת: בלי נגד חיצוני כותבים pinMode(BTN, INPUT), וההדק נשאר צף.
אזהרה: מחברים לחצנים רק להדקים שהוגדרו כמבואות. אם ההדק הוגדר כמוצא OUTPUT, הלחיצה יוצרת קצר, והזרם עובר את הגבול המומלץ (20 mA) ואת הגבול המרבי המוחלט (40 mA) של ההדק.

קפיצות מגע, זיהוי קצה וסינון קפיצות (Contact Bounce and Debouncing)

tactile
קפיצות מגע, ריטוט: כשסוגרים או פותחים מפסק מכני, פיסת המגע המתכתית קופצת כמה פעמים לפני שהיא מתייצבת, והמפסק נפתח ונסגר במהירות בתוך זמן קצר, בסדר גודל של אלפיות שנייה. הבקר מהיר מאוד, ולכן הוא רואה כל קפיצה כלחיצה נפרדת.
12345671.535t [ms]VVIHVILקפיצות מגעלחוץ ויציב
מתח ההדק D2 (עם נגד משיכה למעלה) בלחיצה אחת: אות טיפוסי סינתטי להמחשה. בזמן הקפיצות המתח חוצה את שני הספים 7 פעמים לפני שהוא מתייצב על 0 V

זיהוי קצה:

אנו רוצים לספור כמה פעמים נלחץ הלחצן. הטעות הראשונה: להגדיל את המונה כל עוד הלחצן לחוץ:

שגוי: כך סופרים מחזורי לולאה ולא לחיצות
if (digitalRead(BTN) == LOW) {   // wrong: true in every loop while held
  count++;
}

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

זיהוי קצה: השוואה של הקריאה הנוכחית לקריאה הקודמת. קצה יורד הוא מעבר מ-1 ל-0 (תחילת הלחיצה עם נגד משיכה למעלה), וקצה עולה הוא מעבר מ-0 ל-1 (שחרור הלחצן).

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

סינון קפיצות בתוכנה:

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

ב-lastChange נשמור את זמן השינוי האחרון בקריאה. אם עברו מאז DEBOUNCE_MS אלפיות שנייה, והקריאה שונה מהמצב המקובל stableState, נקבל אותה. ואם המצב החדש הוא LOW, זהו קצה יורד ודאי: לחיצה אחת. איננו משתמשים ב-delay(), כי היא עוצרת את כל התוכנית, ואילו millis() נותנת את הזמן מאז ההפעלה בלי לעצור.

מונה לחיצות עם סינון קפיצות Arduino Uno · button_debounce_count.ino
const int BTN = 2;
const unsigned long DEBOUNCE_MS = 30;

int lastReading = HIGH;   // previous raw reading
int stableState = HIGH;   // accepted state
unsigned long lastChange = 0, presses = 0;

void setup() {
  pinMode(BTN, INPUT_PULLUP);
  Serial.begin(9600);
}

void loop() {
  unsigned long now = millis();
  int reading = digitalRead(BTN);
  if (reading != lastReading) {   // contact moved
    lastChange = now;             // restart timer
    lastReading = reading;
  }
  bool settled = (now - lastChange >= DEBOUNCE_MS);
  if (settled && reading != stableState) {
    stableState = reading;        // accept it
    if (stableState == LOW) {     // falling edge
      presses++;
      Serial.print("t = ");
      Serial.print(now);
      Serial.print(" ms  presses = ");
      Serial.println(presses);
    }
  }
}
Serial Monitor · קריאה לדוגמה
t = 1232 ms  presses = 1
t = 2482 ms  presses = 2
t = 3743 ms  presses = 3
DEBOUNCE_MS = 0 · קריאה לדוגמה
t = 1200 ms  presses = 1
t = 1200 ms  presses = 2
t = 1201 ms  presses = 3
t = 1202 ms  presses = 4
t = 1380 ms  presses = 5
t = 1381 ms  presses = 6

לחצנו על הלחצן 3 פעמים, והמסך הראשון מראה 3 לחיצות. במסך השני הגדרנו DEBOUNCE_MS = 0, כך שכל קצה מתקבל מיד, וזה זיהוי קצה לבדו. הלחיצה הראשונה לבדה נספרה 6 פעמים: 4 קצוות בלחיצה ו-2 בשחרור, כשהמתכת נגעה שוב לרגע. ואחרי שלוש הלחיצות הגיע המונה ל-17 במקום 3.

דוגמה 3.3
בקריאה הטיפוסית הלחצן נגע לראשונה ב-t = 1200 ms, והקפיצה האחרונה הייתה ב-t = 1202.6 ms. מדוע הדפיסה התוכנית את הלחיצה הראשונה ב-1232 ms?
פתרון:

השינוי האחרון בקריאה היה ב-1202.6 ms, והפונקציה millis() נותנת מספר שלם, ולכן נשמר lastChange = 1202.

המצב החדש מתקבל כשעוברים DEBOUNCE_MS (כלומר 30 ms) בלי שינוי:

t = 1202 + 30 = 1232 ms

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

t = 1232 ms
שאלה 3.3: בלחיצה השנייה הלחצן נגע ב-2450 ms והקפיצה האחרונה הייתה ב-2452.2 ms. שנו את DEBOUNCE_MS ל-50. מתי תודפס הלחיצה השנייה?
הערה: נבחר זמן סינון ארוך מהקפיצה הארוכה ביותר הצפויה במפסק, וקצר מהלחיצה המכוונת הקצרה ביותר. מוצאים של חיישנים אלקטרוניים (טרנזיסטור, מצמד אופטי) אינם קופצים, ולכן אינם צריכים סינון קפיצות; ואילו לחצנים, מפסקי גבול ומצופים הם מכניים, והם קופצים.
▶ניסוי חי — הלחצן ונגד המשיכה למעלהסימולציה חיה: נלחץ על הלחצן ונראה את מתח ההדק עם נגד המשיכה למעלה, את הקריאה הספרתית ואת השפעת הקפיצות על הספירה.
⚠
המעבדה אינה זמינה כרגע. החומר בפרק שלם גם בלעדיה — ממשיכים לקרוא, וחוזרים לניסוי מאוחר יותר או פותחים אותו ישירות בקישור שמתחת למסגרת.
https://fahed-lab.com/micro/sensors_guide#button
פתיחת המעבדה בדף נפרד ↗ fahed-lab.com/micro/sensors_guide#button

חיבור חיישן 5 V לבקר 3.3 V (Level Shifting with a Voltage Divider)

מודולי חיישנים רבים עובדים ב-5 V והמוצא הספרתי שלהם 5 V, למשל ההדק ECHO בחיישן העל-קולי HC-SR04 (פרק 7). לא נחבר מוצא כזה ישירות להדק של ESP32 או של רספברי פיי. הפתרון הפשוט ביותר לאותות איטיים יחסית הוא מחלק מתח:

מחלק מתח
Vout = Vin·R2R1 + R2
סימןמשמעותיחידת מידה
Vinמתח המוצא של החיישן[V]
Voutהמתח בהדק הבקר[V]
R1 , R2הנגד בטור, והנגד אל GND[Ω]

שני תנאים לתכנון: Vout ≤ 3.3 V גם כאשר Vin גבוה מעט מ-5 V, ו-Vout ≥ VIH = 2.475 V כדי שייקרא 1 בביטחון.

ESP325VGPIO27GND5V SENSORVCCOUTGNDR110 kΩR215 kΩ5 V3 V
חיישן ספרתי שעובד ב-5 V מחובר להדק GPIO27 של ESP32 דרך מחלק מתח. האדמה משותפת
דוגמה 3.4
נבחר R1 = 10 kΩ. נחשב את R2 הגדול ביותר שנותן בדיוק 3.3 V, אחר כך נבחר ערך תקני ונבדוק את שני התנאים (נניח שמתח החיישן עשוי להגיע ל-5 V + 5% = 5.25 V).
פתרון:
R2 = R1·VoutVin − Vout = 10·3.35 − 3.3 = 19.4 kΩ

הערך התקני 18 kΩ נותן ב-5.25 V מתח של 3.38 V, יותר מ-3.3 V. לכן נבחר R2 = 15 kΩ:

Vout = 5·1510 + 15 = 3 V
Vout,max = 5.25·1525 = 3.15 V
I = 525000 = 0.2 mA

3 V ≥ 2.475 V: נקרא HIGH, ו-3.15 V < 3.3 V: ההדק מוגן.

R2 = 15 kΩ , Vout = 3 V
ספירת עצמים בחיישן 5 V ב-ESP32 ESP32 · esp32_5v_sensor.ino
const int SENSOR = 27;    // via 10k / 15k divider

int lastState = LOW;
unsigned long objects = 0;

void setup() {
  pinMode(SENSOR, INPUT);
  Serial.begin(9600);
}

void loop() {
  int state = digitalRead(SENSOR);
  if (lastState == LOW && state == HIGH) {
    objects++;                  // rising edge
    Serial.print("objects = ");
    Serial.println(objects);
  }
  lastState = state;
}
Serial Monitor · קריאה לדוגמה
objects = 1
objects = 2
objects = 3
שאלה 3.4: משתמשים ב-R1 = 1 kΩ וב-R2 = 2 kΩ. חשבו את Vout כשהחיישן נותן 5 V. האם התכנון הזה בטוח לגמרי?
שימו לב: מחלק מתח פועל רק עם אדמה משותפת לחיישן ולבקר. הוא מתאים לכיוון אחד (מהחיישן אל הבקר). לאותות מהירים מאוד או דו-כיווניים משתמשים במודול מוכן להתאמת רמות.

בידוד אות תעשייתי 24 V במצמד אופטי (Optocoupler Isolation)

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

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

נשתמש במצמד האופטי הנפוץ PC817. הערכים (ערכים טיפוסיים לפי גיליון הנתונים): המתח הקדמי של ה-LED VF ≈ 1.2 V; יחס העברת זרם CTR = ICIF אינו קטן מ-50% ב-IF = 5 mA; ומתח הרוויה VCE,sat ≤ 0.2 V.

Arduino Uno5VD4GNDRPU10 kΩPC817+−24 Vמוצא החיישןRLED4.7 kΩמעגל24 Vמעגל5 V
אות 24 V מחיישן תעשייתי, מבודד במצמד האופטי PC817 לפני ההדק D4. לשני המעגלים אין אדמה משותפת

גם כאן הלוגיקה הפוכה: יש אות 24 V ← ה-LED מאיר ← הטרנזיסטור מוליך ← ההדק D4 ב-0 V (LOW). בלי אות הטרנזיסטור נתוק, ונגד המשיכה למעלה RPU מושך את ההדק ל-5 V (HIGH).

דוגמה 3.5
נתכנן את המעגל:
(א) נחשב את RLED לזרם IF = 5 mA, נבחר ערך תקני ונחשב את ההספק בו.
(ב) נבדוק שהטרנזיסטור נכנס לרוויה עם RPU = 10 kΩ.
פתרון:

(א) המתח על הנגד הוא מתח האות פחות המתח על ה-LED:

RLED = Vin − VFIF = 24 − 1.20.005 = 4560 Ω

נבחר את הערך התקני 4.7 kΩ ונחשב את הזרם ואת ההספק בפועל:

IF = 24 − 1.24700 = 4.85 mA
P = IF2·RLED = (4.85·10−3)2 · 4700 = 0.111 W

נבחר נגד בהספק של 0.25 W לפחות.

(ב) הזרם שנגד המשיכה למעלה צריך כדי שההדק ירד למתח הרוויה, והזרם שהמצמד מבטיח:

IC,need = VCC − VCE,satRPU = 5 − 0.210000 = 0.48 mA
IC,min = CTR·IF = 0.5 · 4.85 = 2.43 mA

2.43 mA > 0.48 mA: הטרנזיסטור ברוויה, ומתח ההדק כ-0.2 V, נמוך בהרבה מ-VIL = 1.5 V.

RLED = 4.7 kΩ , IF = 4.85 mA , RPU = 10 kΩ
ספירת פריטים דרך מצמד אופטי Arduino Uno · opto_24v_counter.ino
const int IN24 = 4;     // opto output, 10k pull-up

int lastState = HIGH;   // HIGH = no 24 V signal
unsigned long items = 0;

void setup() {
  pinMode(IN24, INPUT);
  Serial.begin(9600);
}

void loop() {
  int state = digitalRead(IN24);
  if (lastState == HIGH && state == LOW) {
    items++;                    // 24 V appeared
    Serial.print("items = ");
    Serial.println(items);
  }
  lastState = state;
}
Serial Monitor · קריאה לדוגמה
items = 1
items = 2
items = 3
שאלה 3.5: מהו מתח האות הקטן ביותר שמבטיח רוויה של הטרנזיסטור במעגל הקודם (RLED = 4.7 kΩ, RPU = 10 kΩ, CTR ≥ 50%)?
טעות נפוצה: מחברים את האדמה של מעגל ה-24 V ל-GND של Arduino, וכל הבידוד אובד. שני המעגלים נפרדים לגמרי, ורק המצמד האופטי מקשר ביניהם.
אזהרה: עובדים על מעגלי 24 V תעשייתיים רק בפיקוח המורה, וההזנה מנותקת בזמן החיבור. מצמד אופטי על לוח ניסויים אינו הופך עבודה עם מתח הרשת 230 V לבטוחה: אותות מהרשת נכנסים לבקר רק דרך מודול מוכן ומאושר.
מבט קדימה: בפרק 8 נכיר את המוצאים של חיישנים תעשייתיים: יציאת NPN (שוקעת) ויציאת PNP (מקורית), ואיך כל אחת מהן מתחברת למצמד אופטי.
▶ניסוי חי — חיישנים ספרתייםסימולציה חיה של חיישנים ספרתיים תעשייתיים: מפסקי גבול, חיישני קרבה והמוצאים שלהם.
⚠
המעבדה אינה זמינה כרגע. החומר בפרק שלם גם בלעדיה — ממשיכים לקרוא, וחוזרים לניסוי מאוחר יותר או פותחים אותו ישירות בקישור שמתחת למסגרת.
https://fahed-lab.com/hashmal/sensors#dsens
פתיחת המעבדה בדף נפרד ↗ fahed-lab.com/hashmal/sensors#dsens

פסיקות חומרה לספירת פולסים (Hardware Interrupts)

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

פסיקה: אות חשמלי בהדק שעוצר מיד את התוכנית הראשית ומבצע פונקציה קצרה שנקראת מטפל בפסיקה, ואחר כך התוכנית חוזרת לנקודה שבה נעצרה. ב-Arduino Uno הפסיקה החיצונית attachInterrupt() פועלת רק בהדקים 2 ו-3.

דוגמה: למאוורר מחשב יש הדק TACH שנותן שני פולסים בכל סיבוב (לפי המפרט המקובל של מאווררי מחשב). המוצא הוא מסוג "קולט פתוח": הטרנזיסטור מושך את הקו רק ל-GND, ולכן נדרש נגד משיכה למעלה ל-5 V. המאוורר מוזן ב-12 V, אבל המוצא שלו אינו נושא 12 V, ולכן מספיקה אדמה משותפת.

Arduino Uno5VD2GNDFAN 12V+12VTACHGND12VRPU10 kΩאדמה משותפת
מוצא TACH של מאוורר 12 V מחובר להדק D2 (פסיקה INT0) עם נגד משיכה למעלה ל-5 V
ספירת פולסי המאוורר בעזרת פסיקה Arduino Uno · fan_tach_interrupt.ino
const int TACH = 2;                 // INT0 on Uno
const unsigned long PULSES_PER_REV = 2;

volatile unsigned long pulses = 0;  // set in ISR
unsigned long lastPrint = 0;

void onPulse() { pulses++; }        // ISR: short

void setup() {
  pinMode(TACH, INPUT);       // 10k pull-up to 5V
  attachInterrupt(digitalPinToInterrupt(TACH),
                  onPulse, FALLING);
  Serial.begin(9600);
}

void loop() {
  if (millis() - lastPrint >= 1000) {
    lastPrint += 1000;
    noInterrupts();           // copy, then reset
    unsigned long n = pulses;
    pulses = 0;
    interrupts();
    Serial.print("pulses = ");
    Serial.print(n);
    Serial.print("  rpm = ");
    Serial.println(n * 60 / PULSES_PER_REV);
  }
}
Serial Monitor · קריאה לדוגמה
pulses = 0  rpm = 0
pulses = 30  rpm = 900
pulses = 46  rpm = 1380
pulses = 54  rpm = 1620
pulses = 58  rpm = 1740
pulses = 59  rpm = 1770
מהירות סיבוב
n = 60·Np
סימןמשמעותיחידת מידה
nמהירות הסיבוב[rpm]
Nמספר הפולסים בשנייה אחת—
pמספר הפולסים בכל סיבוב—
דוגמה 3.6
בשורה האחרונה במסך נספרו 59 פולסים בשנייה. נחשב את מהירות הסיבוב ואת הזמן בין שני פולסים.
פתרון:
n = 60·Np = 60·592 = 1770 rpm
T = 1N = 159 = 16.9 ms

פולס כל 16.9 ms בקירוב: תוכנית שיש בה, למשל, delay(100) הייתה מחמיצה את רובם, ואילו הפסיקה קולטת את כולם.

n = 1770 rpm
שאלה 3.6: מאוורר שנותן שני פולסים בסיבוב מסתובב ב-2400 rpm. כמה פולסים סופרת התוכנית בשנייה?
טעות נפוצה: משתמשים ב-attachInterrupt() בהדק אחר ב-Uno, למשל בהדק 4, ושום דבר אינו נספר: הפסיקה החיצונית פועלת רק בהדקים 2 ו-3. ב-ESP32, לעומת זאת, אפשר לקשר פסיקה כמעט לכל הדק; אבל ההדקים 34–39 בו הם מבואות בלבד, ואין בהם נגד משיכה למעלה פנימי.

סיכום נוסחאות הפרק:

החוקהנוסחה
סף הרמה הגבוהה ב-ATmega328P (Uno)VIH = 0.6·VCC
סף הרמה הנמוכה ב-ATmega328P (Uno)VIL = 0.3·VCC
סף הרמה הגבוהה ב-ESP32VIH = 0.75·VDD
סף הרמה הנמוכה ב-ESP32VIL = 0.25·VDD
הזרם בנגד משיכה למעלה או למטה בלחיצהI = VCCR
מחלק מתחVout = Vin·R2R1 + R2
נגד ה-LED במצמד האופטיRLED = Vin − VFIF
תנאי הרוויה במצמד האופטיCTR·IF ≥ VCC − VCE,satRPU
מהירות סיבוב מתוך הפולסיםn = 60·Np

מילון מונחים:

עבריתEnglishالعربية
מבוא ספרתיDigital inputالمدخل الرقمي
רמה לוגיתLogic levelالمستوى المنطقي
סף רמה גבוהה / נמוכהV_IH / V_IL thresholdعتبة المستوى العالي / المنخفض
תחום לא מוגדרUndefined regionالمنطقة غير المعرّفة
מבוא צףFloating inputالمدخل العائم
נגד משיכה למעלהPull-up resistorمقاومة الرفع (pull-up)
נגד משיכה למטהPull-down resistorمقاومة الخفض (pull-down)
לוגיקה הפוכהInverted (active-low) logicالمنطق المعكوس
קפיצות מגע (ריטוט)Contact bounceارتداد التلامس
סינון קפיצותDebouncingإزالة الارتداد
זיהוי קצהEdge detectionكشف الحافة
קצה עולה / יורדRising / falling edgeالحافة الصاعدة / الهابطة
מחלק מתחVoltage dividerمُجزِّء الجهد
מצמד אופטיOptocouplerالمقرن الضوئي
יחס העברת זרםCurrent transfer ratio (CTR)نسبة نقل التيار
תשאולPollingالاستطلاع
פסיקהInterruptالمقاطعة
מטפל בפסיקהInterrupt service routine (ISR)معالج المقاطعة

שאלות לתרגול:

1.
חשבו את VIH ואת VIL של הבקר ATmega328P כשהוא מוזן ב-3.3 V.
תשובות: VIH = 1.98 V, VIL = 0.99 V
2.
איך קורא הדק של ESP32 כל אחד מהמתחים:
(א) 0.5 V
(ב) 1.6 V
(ג) 2.9 V?
תשובות: (א) LOW (ב) לא מוגדר (ג) HIGH
3.
הסבירו בשני משפטים: מדוע הקריאה של הדק צף משתנה כשמקרבים יד אל החוט?
תשובות: התנגדות המבוא גדולה מאוד, ולכן ההדק קולט רעש ומטענים מהסביבה, ושום דבר אינו מושך אותו למתח קבוע
4.
לחצן עם נגד משיכה למטה 4.7 kΩ. חשבו את הזרם ואת ההספק בנגד בלחיצה (VCC = 5 V).
תשובות: I = 1.06 mA, P = 5.32 mW
5.
תלמיד השתמש בנגד משיכה למטה 1 MΩ. זרם הזליגה במבוא מגיע ל-1 µA. חשבו את מתח ההדק כשהלחצן משוחרר. האם הקריאה מובטחת?
תשובות: V = 1 V: נמוך מ-VIL = 1.5 V, אבל המרווח קטן, והנגד הגדול קולט רעש; לא מומלץ
6.
כתבו שתי שורות, את השורה ב-setup() ואת תנאי ה-if, ללחצן בהדק 3 עם נגד משיכה למעלה פנימי, כך שהתנאי יתקיים בלחיצה.
תשובות: pinMode(3, INPUT_PULLUP);, if (digitalRead(3) == LOW)
7.
לחצן נותן בכל לחיצה 3 קצוות יורדים בלחיצה וקצה יורד אחד בשחרור. לחצנו עליו 5 פעמים. כמה סופרת תוכנית זיהוי קצה בלי סינון קפיצות?
תשובות: 20
8.
בתוכנית סינון הקפיצות DEBOUNCE_MS = 30. הלחצן נגע ב-5000 ms והקפיצה האחרונה הייתה ב-5004 ms. מתי נספרת הלחיצה?
תשובות: t = 5034 ms
9.
מחברים חיישן שנותן 5 V להדק של ESP32 דרך מחלק שבו R1 = 2.2 kΩ. חשבו את R2 הגדול ביותר שנותן 3.3 V, בחרו מבין הערכים 3.3 kΩ, 3.9 kΩ, 4.7 kΩ את הגדול ביותר שאינו עובר 3.3 V, וחשבו את Vout.
תשובות: R2,max = 4.27 kΩ; נבחר 3.9 kΩ, Vout = 3.2 V
10.
אות תעשייתי של 12 V מדליק את ה-LED במצמד PC817 (VF = 1.2 V). חשבו את RLED לזרם 5 mA, ואחר כך את הזרם עם הערך התקני 2.2 kΩ.
תשובות: RLED = 2160 Ω; IF = 4.91 mA
11.
במעגל המצמד האופטי מחליפים את RPU בנגד 1 kΩ (IF = 4.85 mA, CTR ≥ 50%). האם הטרנזיסטור נכנס לרוויה?
תשובות: נדרש IC = 4.8 mA והמצמד מבטיח רק 2.43 mA: הרוויה אינה מובטחת, וייתכן שההדק לא ירד לרמה LOW
12.
מאוורר נותן שני פולסים בסיבוב. התוכנית ספרה 75 פולסים בשנייה. חשבו את מהירות הסיבוב ואת הזמן בין שני פולסים.
תשובות: n = 2250 rpm, T = 13.3 ms
13.
ציינו שלושה כללים לכתיבת מטפל בפסיקה, וציינו באילו הדקים של Uno פועלת attachInterrupt().
תשובות: מטפל קצר, בלי Serial ובלי delay(); משתנה volatile; העתקת המשתנה כשהפסיקות מושבתות. ההדקים 2 ו-3
תשובות סופיות לשאלות הפרק
3.1 הספים של ESP32 הם VIH = 2.475 V ו-VIL = 0.825 V, לכן ESP32 קורא את המתח 2.6 V כ-HIGH; ואילו ב-Uno המתח נמצא בתחום הלא מוגדר
3.2 0.1 mA ≤ I ≤ 0.25 mA; בלחיצה LOW (0), ובשחרור HIGH (1)
3.3 t = 2502 ms
3.4 Vout = 3.33 V: גבוה מעט מ-3.3 V, ואינו משאיר מרווח; עדיף R2 קטן יותר
3.5 Vin,min = 5.71 V
3.6 N = 80 פולסים
נוסחאון הספר: כל נוסחאות הפרק הזה מרוכזות, עם סרטוטיהן וטבלאות הסמלים שלהן, בפרק 15 — נוסחאון הספר.