Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

可攜性

在嵌入式環境中,可攜性是非常重要的主題:每個廠商,甚至同一廠商的不同家族,都提供不同的周邊與能力,與周邊互動的方式也會有所差異。

平衡這些差異的一個常見方式是透過稱為硬體抽象層(Hardware Abstraction Layer,HAL)的層次。

硬體抽象是在軟體中模擬部分平台特有細節的一組程式例程,讓程式能直接存取硬體資源。

它們通常透過向硬體提供標準作業系統(OS)呼叫,讓程式設計者能撰寫與裝置無關、高效能的應用程式。

Wikipedia:Hardware Abstraction Layer

嵌入式系統在這方面有些特殊,因為我們通常沒有作業系統與可由使用者安裝的軟體,而是整體編譯的韌體映像,以及許多其他限制。因此,雖然 Wikipedia 所定義的傳統作法可能可行,但可能不是最有效率的可攜性確保方式。

在 Rust 中如何做到?答案是 embedded-hal

什麼是 embedded-hal

簡而言之,它是一組 traits,用於定義 HAL 實作驅動程式應用程式(或韌體) 之間的實作契約。這些契約同時包含能力(例如某個型別實作了某 trait,表示 HAL 實作提供了某種能力)與方法(例如你能建立一個實作 trait 的型別,就保證擁有 trait 所指定的方法)。

典型的分層可能如下:

embedded-hal 中定義的一些 trait 包含:

  • GPIO(輸入與輸出腳位)
  • 序列通訊
  • I2C
  • SPI
  • 計時器/倒數計時
  • 類比訊號轉換

擁有 embedded-hal traits 以及實作並使用它們的套件,主要是為了控制複雜度。若一個應用程式同時要實作硬體周邊的使用、應用程式本身,以及可能的額外硬體驅動,很容易看出可重用性會非常有限。用數學表示,若 M 是周邊 HAL 實作數量,N 是驅動數量,若每個應用都重造輪子,最終會有 M*N 種實作;而使用 embedded-hal traits 提供的 API,實作複雜度會趨近 M+N。當然還有其他好處,例如因為 API 定義清楚且可直接使用,能減少試錯。

embedded-hal 的使用者

如上所述,HAL 有三種主要使用者:

HAL 實作

HAL 實作提供硬體與 HAL traits 使用者之間的介面。典型實作包含三部分:

  • 一或多個硬體特定型別
  • 用來建立並初始化該型別的函式,通常提供各種設定選項(速度、操作模式、使用腳位等)
  • 該型別的一或多個 embedded-hal traits 的 trait impl

這樣的 HAL 實作 可有不同形式:

  • 透過低階硬體存取,例如暫存器
  • 透過作業系統,例如在 Linux 使用 sysfs
  • 透過轉接層,例如用於單元測試的型別 mock
  • 透過硬體轉接器的驅動程式,例如 I2C 多工器或 GPIO 擴充器

驅動程式

驅動程式為內部或外部元件提供一組自訂功能,該元件連接到實作 embedded-hal traits 的周邊。典型例子包括各種感測器(溫度、磁力計、加速度計、光)、顯示裝置(LED 陣列、LCD 顯示器)與致動器(馬達、發射器)。

驅動程式必須以實作 embedded-hal 某個 trait 的型別實例初始化,這由 trait bound 保證,並提供其自有型別實例與自訂方法集合,用來與所驅動的裝置互動。

應用程式

應用程式把各部分組合起來,確保達成所需功能。在不同系統間移植時,這部分需要最多的調整努力,因為應用程式必須透過 HAL 實作正確初始化實體硬體,而不同硬體的初始化方式可能差異很大。此外,使用者選擇也常扮演重要角色,因為元件可能實體連接到不同端子,硬體匯流排有時需要外部硬體配合設定,或是內部周邊的使用需要不同取捨(例如存在多個能力不同的計時器或周邊彼此衝突)。