הלו אסתי #3: צלילי המוזיקה

כשהתחלתי לעיין ב-Datasheet של המיקרו-בקר STM8S103F3P6, הבחנתי במקרה שפין PD4 נקרא גם BEEP. בירור נוסף העלה שהמיקרו-בקר אכן מכיל מנגנון מובנה להשמעת צלילים בסיסיים בעזרת מספר הגדרות פשוטות, ובפוסט זה נראה כיצד מבצעים זאת בפועל. לפני הפירוט, הנה התוצאה הסופית:


מה אומר היצרן

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

אז ראשית, צריך להגדיר שפין PD4 עובד ב"פונקציה החלופית" שלו, וזאת בעזרת הביט AFR7 שברגיסטר AFR (ראשי תיבות של Alternate Function Register). בדיעבד גיליתי שבמקרה זה לא צריך לשנות כלום, אבל אני משאיר את המידע בכל זאת כי למדתי ממנו משהו חשוב – ראו בהמשך.

שנית, כדי לקבל תדרי צליל מדויקים, המסמכים הרשמיים מציעים למדוד את המהירות המדויקת של השעון הפנימי האיטי (LSI) של המיקרו-בקר, ולכייל את רגיסטר הצלילים בהתאם. ברשותכם, אדלג בינתיים על הכיול המורכב-יחסית ואגש ישר לחלקים השונים של הרגיסטר, שנקרא BEEP_CSR:

חלק מתיאור הרגיסטר BEEP_CSR (מה-Reference Manual)
חלק מתיאור הרגיסטר BEEP_CSR (מה-Reference Manual)

הביט החמישי, BEEPEN, הוא זה שמכבה או מפעיל את הצליל. שני הביטים העליונים BEEPSEL הם שקובעים אם הצליל הוא נמוך, בינוני או גבוה. הדבר האחרון שנשאר לעשות הוא לתת ערך לחמשת הביטים BEEPDIV. הערך הזה יכול להגיע מפעולת הכיול שהזכרתי למעלה, או ערך אחר לבחירתנו, כל עוד זה לא 0x1F (בעשרוני 31) שהוא ברירת המחדל. כאשר הערך הוא 0x1F, הצליל לא יושמע.

בתמונה למעלה אפשר לראות, מתחת לכותרת BEEPSEL, את הנוסחאות לחישוב תדרי הצלילים על פי תדר ה-LSI והערך של BEEPDIV. היא נראית לא הגיונית, כי אם BEEPDIV יהיה אפס נקבל חלוקה באפס… אך אל חשש, בהמשך המסמך מוסבר שמאחורי הקלעים החומרה מוסיפה 2 לערך של BEEPDIV לפני החלוקה. לכן, אם כתבנו שם 0 אז הערך בפועל יהיה 2, אם כתבנו 1 זה יהיה 3 וכו'.

התדר המשוער של LSI הוא 128KHz. לפי הנוסחאות למעלה, אם נכתוב ל-BEEPDIV את הערך 30 (בפועל 32), נקבל צלילים של 500, 1000 ו-2000 הרץ לפי BEEPSEL. בואו נראה.

לידע כללי: שינוי Option Bytes

שמח וטוב לב ניגשתי לסביבת הפיתוח של IAR כדי לכתוב את הקוד, אבל כבר בבדיקה הראשונית צצה בעיה: השם של הרגיסטר AFR, שחשבתי בהתחלה שצריך לשנות, לא מוכר למערכת (אף על פי שכתבתי כמובן את פקודת ה-include שהזכרתי בפוסט הקודם בסדרה). מכיוון שיש כל כך מעט חומר על STM8S ברשת לקח לי זמן לגלות איפה הוא מסתתר. הסתבר שכמו ה"פיוזים" במיקרו-בקרים ממשפחת AVR, שאי אפשר לשנות בקוד אלא רק בצריבה ישירה, כך גם כאן יש "Option Bytes", ש-AFR הוא אחד מהם ושהגישה אליהם היא לא מהקוד אלא מסביבת הפיתוח. אחרי שהגדרנו לקומפיילר את דגם המיקרו-בקר הנכון (ראו בפוסט הקודם), וחיברנו את החומרה למחשב – להזכירכם, אנחנו עובדים עם צורב ST-Link – נבחר בתפריט ST-LINK->Option Bytes:

איך להגיע ל-Option Bytes
איך להגיע ל-Option Bytes

בחלון הארוך שנפתח מופיעים הפרמטרים השונים שמוגדרים כ-Option Bytes, ובלחיצה על הערכים השונים עם לחצן העכבר הימני אפשר לקבל תפריט אפשרויות לכל פרמטר ולבחור. אפשר גם לשמור ולטעון קונפיגורציות עם לחצני Save ו-Load בתחתית החלון. השינוי יישמר ברגע שנלחץ על OK.

בחירת הפונקציה החלופית עבור AFR7
בחירת הפונקציה החלופית עבור  AFR7 – לא דרוש במקרה שלנו!

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

הקוד

הנה תוכנית קטנה שמשמיעה במחזוריות את שלושת הצלילים עם הפסקה קצרה אחרי כל שלישיה:

#include <iostm8s103f3.h>

int main(void) {
  
  unsigned char state = 0;
  unsigned long dummy;
  
  for ( ; ; ) {
    
    BEEP_CSR = (state << 6) + 30;
    if (state ^ 3) BEEP_CSR |= 32;
    for (dummy = 0; dummy < 25000; dummy++) ;
    state = (state + 1) & 3;
    
  }   

}

בתוך לולאת ה-for האינסופית, השורה הראשונה נותנת ערך ל-BEEPSEL ול-BEEPDIV, ומאפסת על הדרך את BEEPEN. מיד לאחר מכן, השורה השניה מחליטה אם להפעיל בכל זאת את BEEPEN (מה שנותן לנו את ההפסקה הקצרה בכל פעם ש-state שווה 3).

בלולאת ההשהיה בשורה השלישית אני סופר רק עד 25,000, אבל טיפוס המשתנה הוא unsigned long (של 32 ביט). למה לא הסתפקתי ב-int (של 16 ביט)? יש כאן טריק קטן. מכיוון שהמיקרו-בקר עצמו הוא 8 ביט, ככל שהטיפוס של המשתנה גדול יותר, כך כל פעולה שמבוצעת עליו לוקחת יותר זמן – ולולאה ריקה של 25,000 ל-long דומה מבחינת זמן ההרצה ללולאה של 100,000 ל-int, שכמובן אי אפשר להריץ במציאות כי int לא מגיע למספרים כאלה.

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

איזה סיבוך! יפה שאתה נהנה כל כך ללמוד את הדברים האלה. אני הייתי בורח.