PIC בהפתעה #3: איזה מתח

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


נתחיל בווידוי אישי: בפעם הראשונה ששמעתי על מיקרו-בקר שמודד את המתח של עצמו (בהקשר של משפחת AVR דווקא), תפסתי פתאום כמה דברים מעניינים יכולים להתחבא מתחת לפני השטח של הארדואינו הרגיל, וזה היה אחד הגורמים לתחילת ההתעניינות שלי במיקרו-בקרים "נטו". את הטריק הספציפי הזה, אגב, לא כל המיקרו-בקרים יודעים לעשות. למשל, ל-PIC פשוטים כמו PIC12F675 אין יכולת כזו. לעומת זאת, ל-PIC16F616 שמככב בסדרת הפוסטים הזו יש.

התיאוריה

בכל המקרים בהם נתקלתי, קריאה של מתח ההפעלה מתבססת על מודול ה-ADC הפנימי (ממיר אנלוגי לדיגיטלי, Analog to Digital Converter). המודול הזה מקבל מתח עליון כלשהו (Reference), ומודד מתח אחר ביחס אליו. התוצאה ניתנת כמספר שלם, שהטווח שלו תלוי ברזולוציה של ה-ADC. למשל, ברזולוציה הנפוצה של 10 ביט, הערכים האפשריים הם 0-1023. בתיאור פשטני, ה-ADC מחלק את הטווח שבין 0 לבין ה-Reference ל-1023 חלקים, ואומר לנו איפה על הסקאלה הזו נמצא המתח הנמדד.

נשמע מוכר? נכון מאד, זה בדיוק מה שעושה פקודת analogRead של ארדואינו מאחורי הקלעים. יש לארדואינו מתח Reference כלשהו – 5V כברירת מחדל אבל אפשר לשנות זאת עם הפקודה analogReference – ו-analogRead משווה אליו את המתח שמתקבל באחד מפיני הקלט האנלוגי.

אפרופו, זו הזדמנות טובה לחזור ולהבהיר – הפינים האנלוגיים של הארדואינו הם פינים רגילים, ואפשר להשתמש בהם לקלט/פלט דיגיטלי בלי שום חשש. הם פשוט מסוגלים, בנוסף, "להתחבר" למודול ה-ADC.

הפיסה החסרה בפאזל היא מודול, או מעגל קטן כלשהו בתוך המיקרו-בקר, שמספק לנו מתח קבוע ונמוך יחסית שאינו תלוי במתח ההפעלה. לדוגמה, ב-PIC16F616 יש מקור מתח פנימי של 0.6V ושל 1.2V, והמתחים האלה נשמרים בין אם המיקרו-בקר עצמו מקבל 3.3V, או 5V או כל מתח הפעלה לגיטימי אחר.

הטריק, אם כן, הוא לומר למודול ה-ADC להשתמש במתח ההפעלה בתור Reference, ובמתח הפנימי הקבוע בתור המתח הנמדד. מכיוון שהמתח הקבוע הוא… קבוע, ברור שככל שמתח ההפעלה גבוה יותר, כך התוצאה שייתן לנו ה-ADC תהיה נמוכה יותר (מחלקים ערך קבוע בערך גדול יותר). למשל, אם נבדוק את המתח הפנימי 1.2V לעומת מתחי הפעלה 3.3V ו-5V נקבל (תוצאות מעוגלות):

1.2 / 3.3 * 1023 = 372
1.2 / 5 * 1023 = 246

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

התכל'ס

הצעד הראשון הוא לכתוב 1 לביט FVREN, שנמצא ברגיסטר VRCON. זה מבטיח שהמתח הפנימי הקבוע יופעל.

FVREN = 1;

הרגיסטר הזה עושה עוד כמה דברים מעניינים, אך נעזוב אותם בינתיים. הצעד השני הוא לקבוע את מהירות השעון עבור המרות ADC. המרה היא פעולה שדורשת זמן, ואם ננסה לעשות אותה מהר מדי, הדיוק ייפגע (פשרה שלפעמים דווקא כן נרצה לעשות). אפשר לקבוע מהירות שהיא מהירות השעון הראשי חלקי 2,4,8,16,32 או 64, או לעבוד עם שעון פנימי ייעודי וקבוע להמרות ADC. הבחירה בין כל אלה נעשית באמצעות ביטים 4-6 ברגיסטר ADCON1. כדי לבחור בשעון הייעודי, ביטים 4 ו-5 צריכים להיות 1, וביט 6 לא משנה.

ADCON1 = 0x70; // Binary 0111 0000

עכשיו נעבור לרגיסטר ADCON0, שגדוש בביטים חשובים. הביט הראשון משמאל (ביט 7, ה-MSB) אומר אם ליישר את התוצאות לימין או לשמאל. כזכור, אנחנו עובדים בסקאלה של 0-1023, כלומר 10 ביטים, והם משתרעים על פני שני רגיסטרים שנקראים ADRESL (הנמוך) ו-ADRESH (הגבוה). בעזרת הביט הזה, שנקרא ADFM, אנחנו יכולים לבחור את צורת ההשתרעות:

בחירת סידור הביטים של תוצאת ההמרה (מתוך ה-Datasheet)
בחירת סידור הביטים של תוצאת ההמרה (מתוך ה-Datasheet)

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

הביט הבא משמאל ברגיסטר ADCON0 הוא VCFG, שקובע מה יהיה מתח ה-Reference שלנו. אם הביט הוא 0 זה יהיה מתח האספקה הראשי, ואם 1 זה יהיה המתח שמתקבל בפין RA1 שנקרא גם Vref. אנחנו רוצים כמובן את המתח הראשי.

ארבעת הביטים הבאים קובעים מאיזה מקור ניקח את המתח שאותו צריך למדוד ביחס ל-Reference. רוב האפשרויות מייצגות את הפינים השונים של המיקרו-בקר; האפשרות 1110 נותנת לנו את המתח הפנימי הקבוע 1.2V.

הביט האחרון – כן, דילגתי על אחד – נקרא ADON והוא מפעיל או מכבה את מודול ה-ADC.

הביט הלפני-אחרון, שנקרא GO/DONE, הוא זה שאיתו אנחנו מורים לג'וק להתחיל את ההמרה, ובעזרתו בודקים מתי היא הסתיימה. הערך 1 פירושו "להתחיל המרה" או "המרה מתבצעת כעת", ואילו הערך 0 אומר "המרה הסתיימה" או "לא מתבצעת כרגע המרה".

וככה זה נראה

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

הנה הקוד ל-MPLAB X, לא כולל הגדרות הפיוזים הסטנדרטיות שלא רלוונטיות לנו כרגע:

#include <xc.h>

void main() {

    // LEDs are in negative logic
    // RA0 -> Green LED, RA1 -> Red LED

    ANSEL = 0; // No analog stuff of pins
    TRISA = 0; // All pins output
    PORTA = 3; // both LEDs off

    // ADC Setup
    FVREN = 1; // Enable Fixed Voltage Ref.
    ADCON1 = 0x70; // Internal dedicated osc.
    ADCON0 = 0xB9; 
    /* 1= Right justified
       0= Voltage reference is VDD
       1110= Channel is 1.2V fixed
       0= status flag
       1= ADC Enabled */

    int res, x;
    
    while (1) {

       GO = 1; // Start ADC
       while (GO) ; // Wait for it to finish
       res = ((int)ADRESH << 8) + ADRESL;
       PORTA = res < 256 ? 1 : 2;
       for (x = 0; x < 1000; x++);

    } // while
    
} // main

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

עלה לי רעיון כיצד ליישם את זה באמצעות ארדואינו.
ניקח את מתח VIN שנכנס לארדואינו ונחבר לו שני נגדים מאוד גדולים שערכם שווה בטור.
בהנחה שמתח הכניסה הוא 9V, אז קיבלנו מחלק מתח יפה של 4.5V על כל נגד.כלומר, בדיוק חצי. את המתח הזה שנופל על אחד הנגדים נדגום לתוך רגל אנלוגית, ובתוכנה נעשה את ההמרה בחזרה, נכפיל פי 2 ונחלק אותו ב9V.
כמובן שכל המעגל הזה גורם לבזבוז של יותר זרם, אבל אני מניח שזה יהיה זניח.

תכלס צודק 🙂
תודה.

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

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