什麼是結構(struct)?

結構(也稱為 struct )是一種便捷的程式設計工具,用於將相關的變數分組到一個單獨的位置。這個概念適用於大多數類 C 程式語言,包括 C 和 C++,它們常用於嵌入式微控制器應用。

本工程簡報以 Arduino Braccio 機械手臂(圖 1)為例,介紹 struct 構造。此 struct 封裝了控制遙控舵機所需的所有資料(成員)。其中包括:

  • 物件指標:Arduino myServo 物件
  • char[10]:包含伺服馬達通用名稱的字串。,例如 “SHOULDER”
  • uint16_t:伺服限位最小值
  • uint16_t:伺服限位最大值
  • uint16_t:伺服原點位置
  • uint16_t:伺服指令值
  • float:伺服過渡累加器

圖 1:Arduino Braccio 機械手臂的圖片,該機械手臂配備六個遙控伺服。struct 封裝了每個遙控伺服的資料。

技術提示:struct 的一個顯著特徵是能夠容納不同的資料類型。在本例中,struct 包含了 16-bit 整數、浮點數、Arduino 伺服物件的位址,甚至還有一個描述伺服的 ASCII 字串。

C 和 C++ 中物件之間的關係

此 struct 封裝了與單一遙控伺服相關的資訊。我們可以將其描述為單一伺服的「類別物件」容器。

如果我們切換到像 C++ 這樣的完全物件導向程式設計 (OOP)語言,則可以使用「物件」一詞。但這需要我們將 struct 與用於操作資料的方法關聯起來。

為了清晰、簡潔和最大程度的兼容性,我們將繼續使用 C 語言實作。因此,我們將使用「類別物件」一詞,並使用一系列 setter 和 getter 方法來操作 struct 中包含的資料。

為什麼要使用 struct?

機器手臂是 struct 應用的絕佳案例,因為遙控伺服提供了一個物件封裝的具體範例。六個獨立的遙控伺服代表了中等複雜程度。為了實現平滑可控的手臂運動,確實需要協調所有伺服的動作。

要回答「為什麼要使用 struct」這個問題,最好的方法是考慮將所有伺服從停點 A 移動到停點 B 所需的程式碼。我們可以編寫獨立的程式碼來控制每個伺服。然而,建立專門操作伺服的函數具有明顯的優勢:

  • 使用單一功能來處理多個伺服,使程式更容易除錯。

  • 易於實現諸如伺服最小和最大位置之類的限制。

  • 可擴展性強,因為增加額外的伺服相對容易。

  • 易於實現涉及所有伺服的受控機械手臂運動。

struct 是如何使用的?

struct由兩部分組成:定義和各種敘述的使用。
以下是機械手臂的定義:

// Define the ServoData struct (private to this file)
typedef struct {
  Servo servo;                 // The servo object (from the Servo library)
  char name[10];               // ASCII name for the servo
  uint16_t minPulse;           // Minimum allowed pulse width
  uint16_t maxPulse;           // Maximum allowed pulse width
  uint16_t home;               // Home (default) position
  uint16_t cmd;                // Active servo command pulse width
  float transitionAccumulator;  // Intermediate value used when transitioning from one step to another.
} ServoData;

請注意,該 struct 保存的是單一遙控伺服的資料。然後,我們可以將這些伺服分組到一個「struct 陣列」中,就像這裡看到的那樣。

static ServoData servos[NUM_SERVOS];

我們可以透過諸如以下的命令來存取成員:

servos[servoID].cmd = 1000;

當需要將機械手臂送回初始位置時,這項技術的真正威力就顯現出來了。由於 struct 成員是高度有序的,我們可以使用一個簡單的 FOR 迴圈將 struct 的初始位置複製到 struct 的活動舵機指令位置。之後呼叫 updateAllServos() 函數,該函數會平滑地控制所有伺服,直到機械手臂到達其預設的初始位置。

void HomeAllServos() {
  for (int i = 0; i < NUM_SERVOS; i++) {
    servos[i].cmd = servos[i].home;
  }
  updateAllServos();
}

技術提示:請注意 HomeAllServos()updateAllServos() 函數的簡潔性。它們之所以簡潔,是因為都操作共享的 struct 陣列。例如,只需對程式碼進行少量修改即可新增新的伺服馬達。由於各個函數對 struct 的成員執行簡單的操作,因此故障排除也大大簡化。

完結前感想

這個機器手臂提供了一個簡單易懂的例子,展示了 struct 的強大功能和便利性。在 C 語言中,我們以類似物件的方式封裝每個遙控伺服,並使用一系列 getter 和 setter 方法來處理 struct 成員。根據您的應用場景,您可能需要切換到 C++,並將遙控伺服資料封裝到一個完整的物件中,該物件包含用於操作 struct 成員的方法。無論採用哪一種方式,struct 都是簡化程式碼的關鍵,在嵌入式領域有著廣泛的應用。

目前,這個機器人手臂仍在開發中。後續文章將詳細介紹相關的程式碼技術。

歡迎您在下面留言,分享在嵌入式程式碼中使用 struct 的經驗,並參與討論。也歡迎您就嵌入式 C 與 C++ 的長期爭論發表自己的看法。

相關資訊

請點擊以下連結查看相關實用資訊:

DigiKey 產品選擇指南