קוד לדוגמה של Arduino עבור אנקודרים אבסולוטיים SPI‏

מאת ‎Damon Tarry, Design Applications Engineer, Same Sky

מדריך קוד דוגמה זה של Arduino נועד לתת למשתמשים נקודת התחלה מוצקה להגדרת תצורה וקריאת נתונים מהאנקודרים האבסולוטיים AMT22‏ של Same Sky‏ עם תקשורת ממשק היקפי טורי (SPI). מדריך הלמידה יספק את החומרה והתוכנה הדרושים, דרישות הגדרה עיקריות, חבילות קוד לדוגמה והוראות עבור אפשרויות יציאה סיבוב-יחיד ורב-סיבובים כאחד. להלן רשימה של מה שנדרש כדי להתחיל:

סקירה כללית של אנקודר אבסולוטי AMT22‏

ה-AMT22 של Same Sky (לשעבר CUI Devices) הוא אנקודר אבסולוטי המוצע ברזולוציה של Bit‏-12‏ או Bit‏-14‏, כלומר מספק מספר מדויק של מיקומים ייחודיים בכל סיבוב. עבור גרסת Bit‏-12‏, זה מתורגם ל-4,096 מיקומים שונים, בעוד שדגם Bit‏-14‏ כולל 16,384 מיקומים בכל סיבוב. ללא קשר למספר הפעמים שההתקן מסתובב, הוא מדווח באופן רציף על מיקומו האבסולוטי, ומעניק למשתמשים משוב מדויק על הזווית המדויקת של ההתקן.

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

צעדים ראשונים

ודאו שההתקן הוא באופן RUN על ידי כוונון המתג הממוקם בגב האנקודר למצב המתאים (איור 1). כעת הרכיבו את אנקודר AMT22 למנוע או למכלל באמצעות הוראות הרכבת AMT‏ כדי להבטיח התקנה נכונה. ה-AMT22 תומך ב-9 גודלי ציר שונים הנעים בין 2 מ"מ ל-8 מ"מ.

תרשים של אנקודר AMT22 של Same Sky הממותג לאופן RUNאיור 1: העבירו את המתג בגב אנקודר AMT22 לאופן RUN. (מקור התמונה: Same Sky‏)

החיבורים המתוארים באיור 2 ובטבלה 1 מיועדים במיוחד עבור לוח Arduino Uno, אך הקוד המסופק אמור להיות תואם לרוב לוחות Arduino‏. עם זאת, זכרו שתצורות הפינים עשויות להיות שונות בין דגמי Arduino השונים. לפרטי חיבור מדויקים על לוחות אחרים, מומלץ לעיין בתיעוד המתאים של Arduino.

תרשים של חיבורי חיווט Arduino Uno עם אנקודר AMT22איור 2‏: חיבורי חיווט Arduino Uno עם אנקודר AMT22. (מקור התמונה: Same Sky‏)

פונקציה מספר פין אנקודר פין Arduino Uno‏ AMT-DBC-1-036
V‏ 5‏+ 1 V‏ 5‏ לבן/ירוק
SCLK‏ 2 13 כחול/לבן
MOSI‏ 3 11 לבן/כחול
GND 4 GND ירוק/לבן
MISO‏ 5 12 כתום/לבן
CS‏ 6 2 לבן/כתום

טבלה 1: הגדרה מורחבת של חיבורי חיווט Arduino Uno. (מקור התמונה: Same Sky‏)

אנקודר AMT22 מתחיל לשדר את נתוני המיקום האבסולוטי שלו מיד עם תחילת תקשורת SPI, ומבטל את הצורך במבנה פקודה-תגובה מסורתי. במהלך ה-Byte‏ הראשון של העברת ה-SPI, המארח שולח 0x00, וה-AMT22 מגיב בו-זמנית עם נתוני מיקום תקפים.

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

פקודה Byte‏ הערות
קבלת מיקום 0x00 0x00
הגדרת אפס 0x00 0x70 סיבוב-יחיד בלבד
קבלת מספר הסיבובים 0x00 0xA0 רב-סיבובים בלבד

טבלה 2‏: הגדרת פקודות AMT22‏. (מקור התמונה: Same Sky‏)

מדריך למידה קוד - כולל ומגדיר

מכיוון שאפיק SPI של Arduino משמש לממשק עם אנקודר AMT22, ספריית SPI צריכה להיכלל בקוד. כדי לשלוח את נתוני המיקום מה-Arduino למחשב, משתמשים בחיבור USB-טורי המובנה בתוך ה-Arduino IDE, המוגדר בקצב Baud‏ של 115,200.

בנוסף, יש להגדיר את הפקודות המשמשות את ה-AMT22. מאחר והאנקודר לא מעבד את תוכן ה-Byte‏ הראשון, פקודת NOP‏ (ללא-פעולה) מוקצית כדי לפשט את תהליך התקשורת (רשימה 1).

העתק
/* Include the SPI library for the arduino boards */
#include <SPI.h>
 
/* Serial rates for UART */
#define BAUDRATE      115200
 
/* SPI commands */
#define AMT22_NOP     0x00
#define AMT22_ZERO    0x70
#define AMT22_TURNS   0xA0

רשימה 1‏: הגדרת ממשק SPI‏.

אתחול

בפונקציה setup() (רשימה 2), התחילו באתחול כל פיני ה-SPI הנדרשים וקביעת התצורה של הממשקים הטוריים לתקשורת.

את נקודתה-חיבור הטורית יש לאתחל כדי לאפשר העברת נתונים למחשב המארח. זה נעשה על ידי העברת ה-BAUDRATE המוגדר לתוך הפונקציה ()Serial.begin‏.

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

בחרו קצב שעון עבור אפיק SPI כדי לתקשר עם ה-AMT22. למטרות בניית אב-טיפוס מתאים קצב שעון של kHz‏ 500‏, למרות שה-AMT22 תומך בקצבים של עד MHz‏ 2‏. השגת kHz‏ 500‏ יכולה להיעשות באמצעות הגדרת SPI_CLOCK_DIV32‏. בהתחשב בשעון MHz‏ 16‏ של Arduino Uno, חלוקה זו מביאה לקצב שעון SPI של kHz‏ 500‏. לפרטים נוספים על תצורת שעון SPI, עיינו בתיעוד של Arduino.

לאחר הגדרת הכל, ניתן לאתחל את אפיק SPI באמצעות ()SPI.begin‏, אשר יגדיר את שלושת פיני ה-SPI הייעודיים: MISO, MOSI ו-SCLK, המכינים את המערכת עבור תקשורת עם האנקודר.

העתק
void setup()
{
  uint8_t cs_pin = 2;
 
  //Set the modes for the SPI CS
  pinMode(cs_pin, OUTPUT);
  //Get the CS line high which is the default inactive state
  digitalWrite(cs_pin, HIGH);
 
  //Initialize the UART serial connection for debugging
  Serial.begin(BAUDRATE);
 
  //set the clockrate. Uno clock rate is 16Mhz, divider of 32 gives 500 kHz.
  //500 kHz is a good speed for our test environment
  //SPI.setClockDivider(SPI_CLOCK_DIV2);   // 8 MHz
  //SPI.setClockDivider(SPI_CLOCK_DIV4);   // 4 MHz
  //SPI.setClockDivider(SPI_CLOCK_DIV8);   // 2 MHz
  //SPI.setClockDivider(SPI_CLOCK_DIV16);  // 1 MHz
  SPI.setClockDivider(SPI_CLOCK_DIV32);    // 500 kHz
  //SPI.setClockDivider(SPI_CLOCK_DIV64);  // 250 kHz
  //SPI.setClockDivider(SPI_CLOCK_DIV128); // 125 kHz
 
  //start SPI bus
  SPI.begin();
}

רשימה 2: הפונקציה ()setup‏ מאתחלת את כל פיני ה-SPI.

תקשורת SPI‏

תקשורת SPI עם AMT22 מטופלת דרך ספריית ה-SPI של Arduino, בעוד שבקרת בחיראת השבב (CS‏) מנוהלת באמצעות הקוד באמצעות פיני I/O דיגיטליים. פונקציית ()digitalWrite‏ משמשת לאשר או לבטל אישור של קו CS‏ (רשימה 3).

ה-AMT22 מצפה ששני Bytes‏ של 0x00 יישלחו, והוא מחזיר נתונים מיד לאחר קולטת Bytes‏ אלה. עקב תגובה מהירה זו, יש לעמוד בדרישות תזמון מינימליות מסוימות, המפורטות בגיליון הנתונים של ה-AMT22.

לא משנה אם האנקודר הוא גרסת Bit‏-12‏ או Bit‏-14‏, הוא תמיד מגיב עם שני Bytes‏ (Bit‏-16‏) של נתונים. שני הביטים העליונים הם ביטי בדיקה המשמשים לוודא את שלמות הנתונים. עבור גרסת Bit‏-12‏, שני הביטים התחתונים הן שניהם 0, ויש להסיט את הערך המוחזר ימינה ב-2 ביטים (או לחלק ב-4) עבור שימוש נכון.

כדי לקבל נתוני מיקום, קוראים לפונקציה ()SPI.transfer‏, השולחת את פקודת AMT22_NOP. קו CS נשאר נמוך במהלך תהליך זה. ה-AMT22 שולח תחילה את ה-הגבוה Byte, כך שה-Byte הנקלט מוסט שמאלה ב-Bit‏ 8‏ כדי ליישר אותו בחצי העליון של משתנה uint16_t. ערך זה מוקצה למשתנה encoderPosition בפעולה אחת. לאחר שיהוי קצר כדי לעמוד בדרישות התזמון, מתבצעת קריאה שנייה של ()SPI.transfer‏ לשליחת פקודת AMT22_NOP נוספת. התוצאה עוברת פעולת OR עם הערך הנוכחי ב-encoderPosition, המשלבת למעשה את שני ה-Bytes‏ שנקלטו לתוך משתנה uint16_t יחיד. לבסוף, קו ה-CS משוחרר ומשלים את התקשורת.

העתק
uint8_t cs_pin = 2;
 
//set the CS signal to low
digitalWrite(cs_pin, LOW);
delayMicroseconds(3);
 
//read the two bytes for position from the encoder, starting with the high byte
uint16_t encoderPosition = SPI.transfer(AMT22_NOP) << 8; //shift up 8 bits because this is the high byte
delayMicroseconds(3);
encoderPosition |= SPI.transfer(AMT22_NOP); //we do not need a specific command to get the encoder position, just no-op
 
//set the CS signal to high
digitalWrite(cs_pin, HIGH);

רשימה 3‏:הגדרת תקשורת SPI‏.

וידוא בדיקת Checksum‏

לאחר השלמת העברת ה-SPI, חיוני לאמת את הנתונים שהתקבלו באמצעות בדיקת Checksum‏ (רשימה 4).

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

הפונקציה תבצע את השלבים הבאים:

  1. חישוב הזוגיות עבור הביטים האי-זוגיים (ביטים 1‏, 3, 5, 7, 9, 11‏, 13‏)
  2. חישוב הזוגיות עבור הביטים הזוגיים (ביטים 0, 2, 4, 6, 8, 10, 12, 14)
  3. השוואת הזוגיות המחושבת מול הערכים המצוינים על ידי הביטים של ה-Checksum‏

הפונקציה תחזיר True‏ אם סכום הבדיקה תקף, מה שמצביע על אישור שלמות הנתונים. אם בדיקת Checksum‏ אינה תקפה, הפונקציה תחזיר False‏, מה שמצביע על שגיאה אפשרית בנתונים שנקלטו.

העתק
/*
 * Using the equation on the datasheet we can calculate the checksums and then make sure they match what the encoder sent.
 */
bool verifyChecksumSPI(uint16_t message)
{
  //checksum is invert of XOR of bits, so start with 0b11, so things end up inverted
  uint16_t checksum = 0x3;
  for(int i = 0; i < 14; i += 2)
  {
    checksum ^= (message >> i) & 0x3;
  }
  return checksum == (message >> 14);
}

רשימה 4: אימות בדיקת Checksum‏.

פורמט הנתונים

אם אימות ה-Checksum‏ מאשר את שלמות הנתונים, השלב הבא הוא עדכון משתנה encoderPosition על ידי הסרת שני הביטים העליונים (רשימה 5). ניתן להשיג זאת על ידי הפעלת פעולת AND בשיטת ביטים עם 0x3FFF (או 0b0011111111111111), אשר למעשה שומר על כל 14 הביטים התחתונים של נתוני המיקום.

בנוסף, יש צורך להביא בחשבון את הרזולוציה של האנקודר - בין אם היא Bit‏-12‏ או Bit‏-14‏. אם הרזולוציה היא Bit‏-12‏, יש להסיט ימינה את 2‏ הביטים של ערך encoderPosition כדי להתאים עבור הרזולוציה הנמוכה יותר. זה מבטיח שנתוני המיקום מיוצגים במדויק במשתנה encoderPosition, המשקף את המיקום האמיתי של האנקודר על בסיס הרזולוציה שהוגדרה.

העתק
if (verifyChecksumSPI(encoderPosition)) //position was good
{
  encoderPosition &= 0x3FFF; //discard upper two checksum bits
  if (RESOLUTION == 12) encoderPosition = encoderPosition >> 2; //on a 12-bit encoder, the lower two bits will always be zero
 
  Serial.print(encoderPosition, DEC); //print the position in decimal format
  Serial.write('\n');
}
else //position is bad
{
  Serial.print("Encoder position error.\n");
}

רשימה 5: עדכון ה-encoderPosition.

הגדרת מיקום אפס (סיבוב-יחיד בלבד)

ואריאנטים מסוימים של אנקודר AMT22 מציעים מאפיין מיקום אפס ניתן-לתכנות. כדי להגדיר את מיקום אפס זה, יש לשלוח רצף פקודות ספציפי של שני-Byte‏. התהליך כולל שליחת פקודת AMT22_NOP תחילה, ולאחר מכן המתנה קצרה כדי לעמוד בדרישות התזמון המינימליות שהוגדרו על ידי ה-AMT22. לאחר המתנה זו, הפקודה AMT22_ZERO נשלחת תוך הבטחה שקו בחירת שבב (CS) משוחרר. ברגע שהאנקודר יקלוט פקודה זו, הוא יבצע פעולת איפוס (רשימה 6).

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

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

העתק
/*
 * The AMT22 bus allows for extended commands. The first byte is 0x00 like a normal position transfer,
 * but the second byte is the command.
 * This function takes the pin number of the desired device as an input
 */
void setZeroSPI(uint8_t cs_pin)
{
  //set CS to low
  digitalWrite(cs_pin, LOW);
  delayMicroseconds(3);
 
  //send the first byte of the command
  SPI.transfer(AMT22_NOP);
  delayMicroseconds(3);
 
  //send the second byte of the command
  SPI.transfer(AMT22_ZERO);
  delayMicroseconds(3);
 
  //set CS to high
  digitalWrite(cs_pin, HIGH);
 
  delay(250); //250 millisecond delay to allow the encoder to reset
}

רשימה 6‏: הגדרת מיקום אפס של אנקודר סיבוב-יחיד AMT22‏.

קריאת מונה סיבובים (רב-סיבובים בלבד)

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

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

העתק
uint8_t cs_pin = 2;
 
//set the CS signal to low
digitalWrite(cs_pin, LOW);
delayMicroseconds(3);
 
//read the two bytes for position from the encoder, starting with the high byte
uint16_t encoderPosition = SPI.transfer(AMT22_NOP) << 8; //shift up 8 bits because this is the high byte
delayMicroseconds(3);
encoderPosition |= SPI.transfer(AMT22_TURNS); //we send the turns command (0xA0) here, to tell the encoder to send us the turns count after the position
 
//wait 40us before reading the turns counter
delayMicroseconds(40);
 
//read the two bytes for turns from the encoder, starting with the high byte
uint16_t encoderTurns = SPI.transfer(AMT22_NOP) << 8; //shift up 8 bits because this is the high byte
delayMicroseconds(3);
encoderTurns |= SPI.transfer(AMT22_NOP);
delayMicroseconds(3);
 
//set the CS signal to high
digitalWrite(cs_pin, HIGH);

רשימה 7‏: קריאת encoderPosition‏ ומונה הסיבובים באנקודר AMT22‏ רב-סיבובים.

הרצת הקוד

כשהקוד נוצר בהצלחה, הגיע הזמן להעלות אותו ל-Arduino ולהקים תקשורת עם אנקודר AMT22.

כדי לנטר את היציאה, פתחו את הצג הטורי ב-Arduino IDE וודאו שקצב הנתונים מוגדר ל-Baud‏ 115,200‏. זה יאפשר למשתמשים לבחון את פעולת האנקודר ולצפות בנתוני המיקום המדווחים בזמן-אמת. לאחר שהצג הטורי פעיל, האנקודר צריך להתחיל לשדר את מידע המיקום שלו ולהדגים את הפונקציונליות שלו בתוך המערכת (איור 3).

תמונה של המיקום המדווח מהאנקודראיור 3: המיקום המדווח מהאנקודר, שנקלט על ידי ה-Arduino‏ (מקור התמונה: Same Sky‏)

מספר אנקודרים

יתרון משמעותי אחד בשימוש בהתקן SPI הוא היכולת לתקשר עם מספר אנקודרים עם אותו אפיק. כדי להקל על כך, יש להקצות פין I/O‏ דיגיטלי נוסף לכל אנקודר המאפשר בקרת בחירת שבב (CS‏) אינדיבידואלית.

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

העתק
uint8_t cs_pins[] = {2}; //only one encoder connected, using pin 2 on arduino for CS
//uint8_t cs_pins[] = {2, 3}; //two encoders connected, using pins 2 & 3 on arduino for CS

רשימה 8‏: הגדרת מערך עבור קריאת מספר אנקודרים.

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

העתק
void loop()
{
  for(int encoder = 0; encoder < sizeof(cs_pins); ++encoder)
  {
    uint8_t cs_pin = cs_pins[encoder];
 
    //set the CS signal to low
    digitalWrite(cs_pin, LOW);
    delayMicroseconds(3);
 
    //read the two bytes for position from the encoder, starting with the high byte
    uint16_t encoderPosition = SPI.transfer(AMT22_NOP) << 8; //shift up 8 bits because this is the high byte
    delayMicroseconds(3);
    encoderPosition |= SPI.transfer(AMT22_NOP); //we do not need a specific command to get the encoder position, just no-op
 
    //set the CS signal to high
    digitalWrite(cs_pin, HIGH);
 
    if (verifyChecksumSPI(encoderPosition)) //position was good, print to serial stream
    {
      encoderPosition &= 0x3FFF; //discard upper two checksum bits
      if (RESOLUTION == 12) encoderPosition = encoderPosition >> 2; //on a 12-bit encoder, the lower two bits will always be zero
 
      Serial.print("Encoder #");
      Serial.print(encoder, DEC);
      Serial.print(" position: ");
      Serial.print(encoderPosition, DEC); //print the position in decimal format
      Serial.write('\n');
    }
    else //position is bad, let the user know how many times we tried
    {
      Serial.print("Encoder #");
      Serial.print(encoder, DEC);
      Serial.print(" position error.\n");
    }
  }
 
  //For the purpose of this demo we don't need the position returned that quickly so let's wait a half second between reads
  //delay() is in milliseconds
  delay(500);
}

רשימה 9: קריאת משתנה encoderPosition‏ ממספר אנקודרים.

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

סיכום

כעת למשתמשים אמורה להיות הבנה בסיסית של הגדרת התצורה וקריאת הנתונים מהאנקודרים האבסולוטיים AMT22 של Same Sky. מאמר זה התמקד באנקודרים האבסולוטיים AMT22. ל-Same Sky יש גם קו של אנקודרים מודולריים AMT‏ המציעים מגוון של גרסות אינקרמנטליות, אבסולוטיות וקומוטציה.

מיאון אחריות: דעות, אמונות ונקודות מבט המובעות על ידי מחברים שונים ו/או משתתפי פורום באתר אינטרנט זה לא בהכרח משקפות את הדעות, האמונות ונקודות המבט של חברת DigiKey או את המדיניות הרשמית של חברת DigiKey.

אודות כותב זה

Damon Tarry, Design Applications Engineer, Same Sky