返回文章列表

Rust 實作樹莓派 GPIO 控制與跨平台編譯

本文介紹如何使用 Rust 語言控制 Raspberry Pi 的 GPIO 介面,涵蓋 LED 控制、按鈕輸入檢測、防抖動處理以及跨平台編譯等實用技巧。透過 rust_gpiozero 函式庫,簡化 GPIO 操作,並示範如何設定電路、撰寫 Rust 程式碼來控制 LED 和讀取按鈕狀態。此外,文章也說明瞭如何在

嵌入式系統 Rust

在嵌入式系統開發中,Rust 語言的安全性與效能使其成為控制硬體的理想選擇。本文將引導您使用 Rust 和 rust_gpiozero 函式庫,在 Raspberry Pi 上進行 GPIO 控制,包含 LED 燈的開關和按鈕輸入的偵測,同時也涵蓋防抖動的處理技巧,避免因按鈕彈跳產生的誤判。此外,為了提高編譯速度,本文也將說明如何利用跨平台編譯的方式,在效能更佳的 x86 Linux PC 上編譯 Raspberry Pi 適用的執行檔,最後再將編譯好的檔案佈署至 Raspberry Pi 上執行,有效縮短開發週期。

樹莓派(Raspberry Pi)硬體與作業系統準備

在開始撰寫程式碼之前,瞭解你的樹莓派硬體和作業系統至關重要。現在花點時間做準備,日後將能避免許多麻煩。

認識你的樹莓派

本章節將使用樹莓派4型號B(Raspberry Pi 4 Model B),不過樹莓派3也同樣適用。樹莓派是一款微型電腦,具備一般電腦所需的所有元件:中央處理器(CPU)、記憶體、Wi-Fi、藍牙、HDMI輸出、USB等。

樹莓派4使用ARM架構的CPU,而大多數主流個人電腦則採用x86架構,搭載Intel或AMD的處理器。由於Rust是一種編譯成機器碼的語言,因此必須針對正確的CPU架構進行編譯,否則二進位制檔案將無法執行。

樹莓派具備多種周邊裝置,包括用於載入程式和作業系統的SD卡讀卡器、USB-C供電介面、micro-HDMI輸出介面等。此外,樹莓派還有兩排金屬針腳(GPIO),可用於與外部電子元件(如LED和按鈕)互動。

使用Raspberry Pi Imager安裝樹莓派作業系統

首先,你將學習如何在樹莓派上直接編譯和執行Rust程式。你需要在SD卡上安裝作業系統映像檔,讓樹莓派從該映像檔啟動。官方的樹莓派作業系統稱為Raspberry Pi OS,是一款根據Debian的作業系統,具備友善的桌面環境和許多預先安裝的軟體套件,如Firefox瀏覽器、文字編輯器、電腦等。

安裝Raspberry Pi OS最簡單的方法是使用Raspberry Pi Imager工具。以下是安裝步驟:

  • 前往Raspberry Pi Imager下載頁面(https://www.raspberrypi.com/software/),依照指示安裝。
  • 將至少8GB且格式化為FAT格式的SD卡插入電腦。
  • 以root許可權啟動Raspberry Pi Imager,並依照螢幕指示將Raspberry Pi OS安裝到SD卡上。
  • 將SD卡插入樹莓派。
  • 將樹莓派連線鍵盤、滑鼠和HDMI顯示器。
  • 使用5V/3A USB-C電源供應器(通常是手機充電器)為樹莓派供電,啟動裝置。
  • 啟動後,你應該會看到類別似圖8-4的桌面環境。

內容解密:

安裝Raspberry Pi OS的過程涉及下載並使用Raspberry Pi Imager工具。這個工具提供了一個逐步的安裝精靈,引導你完成整個安裝過程。首先,你需要下載並安裝這個工具。接著,將SD卡插入電腦,並啟動Raspberry Pi Imager。根據螢幕上的指示,將Raspberry Pi OS安裝到SD卡上。完成後,將SD卡插入樹莓派,並連線必要的周邊裝置,如鍵盤、滑鼠和顯示器。最後,使用適當的電源供應器為樹莓派供電,裝置就會啟動並顯示桌面環境。

安裝Rust工具鏈

如同在Linux桌面上所做的那樣,你可以在Raspberry Pi OS上使用rustup安裝Rust編譯器和Cargo。開啟終端機並執行以下命令(來自Rust官方安裝頁面:https://www.rust-lang.org/tools/install):

curl https://sh.rustup.rs -sSf | sh

這會在樹莓派上安裝Rust工具鏈。rustup會偵測到ARM CPU,並建議安裝不同的目標架構(armv7-unknown-linux-gnueabihf)。你應該接受rustup的建議,安裝適用於ARM的工具鏈。安裝完成後,別忘了將Cargo資料夾加入PATH環境變數,以便使用Cargo命令。

內容解密:

這段程式碼使用curl命令下載並執行Rust安裝指令碼。rustup會根據你的CPU架構(在本例中為ARM)建議適當的目標架構。你需要接受這個建議,以確保Rust程式碼能夠正確編譯並在樹莓派上執行。

瞭解GPIO針腳

設定好樹莓派後,你將會看到佔據電路板一側的兩排金屬針腳。這些針腳被稱為GPIO(通用輸入/輸出)針腳,用於與外部世界通訊。當針腳作為輸出時,你可以透過軟體控制它輸出3.3伏特(標記為「3V3」)或0伏特。當針腳作為輸入時,它可以偵測針腳是否具有高電壓(3V3)或低電壓(0V)。

內容解密:

GPIO針腳是用於與外部電子元件互動的重要介面。你可以透過軟體控制GPIO針腳的輸出電壓,或者偵測其輸入電壓。這使得樹莓派能夠與各種外部裝置進行互動,例如LED燈和按鈕。

GPIO針腳型別

GPIO針腳有多種型別,包括:

  • 5V:5伏特電源供應
  • 3V3:3.3伏特電源供應
  • GND:接地(恆定0V)
  • 編號:對於GPIO針腳,編號是Broadcom SOC通道(BCM)編號。這是用於在範例程式碼中識別針腳的編號。

內容解密:

瞭解不同型別的GPIO針腳對於正確使用它們至關重要。你需要知道哪些針腳提供電源、哪些針腳接地,以及哪些針腳可用於輸入/輸出操作。此外,BCM編號是用於識別GPIO針腳的重要參考依據。

使用 Rust 控制 Raspberry Pi 的 GPIO

在物聯網和嵌入式系統開發中,Raspberry Pi 是一款非常流行的開發板。它提供了豐富的 GPIO(通用輸入/輸出)介面,可以用於連線各種外部裝置,如 LED、按鈕和感測器等。本文將介紹如何使用 Rust 語言控制 Raspberry Pi 的 GPIO 介面,以實作對 LED 的控制。

GPIO 簡介

Raspberry Pi 的 GPIO 介面是一組可以用於輸入或輸出訊號的引腳。這些引腳可以用於實作各種功能,如數字輸入/輸出、脈寬調製(PWM)、序列外設介面(SPI)、I2C 匯流排和序列通訊(UART)等。在本例中,我們將重點介紹如何使用 GPIO 引腳控制 LED。

使用 rust_gpiozero 函式庫

為了簡化 GPIO 操作,我們可以使用 rust_gpiozero 函式庫。該函式庫提供了對 GPIO 引腳的抽象,使得控制外部裝置變得更加容易。首先,您需要在 Raspberry Pi 上建立一個新的 Rust 專案,並將 rust_gpiozero 新增到專案的依賴中。

$ cargo new physical-computing
$ cd physical-computing
$ cargo add rust_gpiozero

控制 LED

在開始之前,您需要搭建一個簡單的電路,將 LED 連線到 Raspberry Pi 的 GPIO 引腳上。請確保在電路中新增一個電阻,以限制流經 LED 的電流,防止 LED 損壞。

電路連線

  1. 將 LED 的正極(通常是較長的引腳)連線到 GPIO 引腳 2。
  2. 將 LED 的負極連線到電阻的一端。
  3. 將電阻的另一端連線到 GND(地)。

Rust 程式碼

// src/main.rs
use rust_gpiozero::*;
use std::thread::sleep;
use std::time::Duration;

fn main() {
    let led = LED::new(2);
    loop {
        println!("LED on");
        led.on();
        sleep(Duration::from_secs(1));
        println!("LED off");
        led.off();
        sleep(Duration::from_secs(1));
    }
}

內容解密:

  1. 初始化 LEDlet led = LED::new(2); 這行程式碼建立了一個新的 LED 結構例項,並將其關聯到 GPIO 引腳 2。在初始化過程中,rust_gpiozero 函式庫會自動將該引腳設定為輸出模式。
  2. 控制 LED 狀態led.on()led.off() 分別用於開啟和關閉 LED。當呼叫 led.on() 時,GPIO 引腳 2 被設定為高電平(3.3V),使得 LED 點亮。相反,當呼叫 led.off() 時,引腳被設定為低電平(0V),LED 熄滅。
  3. 迴圈控制:程式碼中使用了一個無限迴圈,使得 LED 每隔一秒鐘閃爍一次。sleep(Duration::from_secs(1)) 用於在開啟和關閉 LED 之間引入延遲。

使用 Rust 控制 GPIO:從 LED 控制到按鈕輸入

本章將探討如何使用 Rust 語言控制 Raspberry Pi 的 GPIO(通用輸入輸出)介面,從基本的 LED 控制到按鈕輸入檢測,並介紹如何處理按鈕抖動問題。

LED 控制:基礎與進階

首先,我們來看看如何使用 Rust 控制 LED 燈的開關。透過 rust_gpiozero 函式庫,我們可以輕鬆地控制 GPIO 腳位。

簡單的 LED 開關

// src/main.rs
use rust_gpiozero::*;
use std::time::Duration;
use std::thread::sleep;

fn main() {
    let mut led = LED::new(2);
    loop {
        println!("on");
        led.on();
        sleep(Duration::from_secs(1));
        println!("off");
        led.off();
        sleep(Duration::from_secs(1));
    }
}

rust_gpiozero 提供了 LED::blink() 函式,讓我們可以更簡單地實作 LED 的閃爍效果。

// src/main.rs
use rust_gpiozero::*;

fn main() {
    let mut led = LED::new(2);
    led.blink(1.0, 1.0);
    led.wait(); // 防止程式立即離開
}

內容解密:

  1. LED::new(2) 初始化一個新的 LED 結構,引數 2 表示使用的 GPIO 腳位。
  2. led.blink(1.0, 1.0) 使 LED 每隔一秒閃爍一次,第一個 1.0 表示 LED 亮起的時間,第二個 1.0 表示 LED 熄滅的時間。
  3. led.wait() 用於保持程式執行,防止其在 led.blink() 之後立即離開。

按鈕輸入檢測

接下來,我們將學習如何使用 GPIO 檢測物理按鈕的輸入。

按鈕電路設計

為了檢測按鈕的按下,我們需要組態 GPIO 腳位為輸入模式,並使用內部上拉或下拉電阻來穩定電壓。

// src/main.rs
use rust_gpiozero::*;

fn main() {
    let mut led = LED::new(2);
    let mut button = Button::new(4);
    loop {
        println!("wait for button");
        button.wait_for_press(None);
        println!("button pressed!");
        led.toggle();
    }
}

內容解密:

  1. Button::new(4) 初始化一個按鈕結構,使用 GPIO 4 號腳位,並預設啟用內部上拉電阻。
  2. button.wait_for_press(None) 使程式阻塞,直到按鈕被按下。None 表示無逾時限制,可以替換為 Some(f32) 來設定逾時時間(單位:秒)。
  3. led.toggle() 在按鈕按下後切換 LED 的狀態。

處理按鈕抖動

在實際應用中,按鈕的機械結構可能會導致抖動,從而觸發多次按鈕事件。為瞭解決這個問題,我們可以實作按鈕去抖動。

// src/main.rs
use rust_gpiozero::*;
use std::time::{Duration, Instant};

fn main() {
    let mut led = LED::new(2);
    let mut button = Button::new(4);
    let mut last_clicked = Instant::now();
    loop {
        button.wait_for_press(None);
        if last_clicked.elapsed() < Duration::new(1, 0) {
            continue;
        }
        led.toggle();
        last_clicked = Instant::now();
    }
}

內容解密:

  1. 使用 Instant::now() 紀錄上一次按鈕被按下的時間。
  2. 在檢測到按鈕按下後,檢查距離上一次按下是否已經超過一秒。如果未超過,則忽略此次事件。
  3. 超過一秒才會切換 LED 狀態,並更新 last_clicked 時間。

實作 Raspberry Pi 的實體運算與跨平台編譯

在前面的章節中,我們探討瞭如何在 Raspberry Pi 上使用 Rust 進行實體運算。本章節將探討如何使用 Rust 在 Raspberry Pi 上進行 GPIO 控制,並介紹跨平台編譯的概念。

8.2 防彈跳處理

當我們使用按鈕控制 LED 時,可能會遇到按鈕彈跳的問題。按鈕彈跳是指當按鈕被按下或釋放時,由於機械原因導致的多次觸發。為瞭解決這個問題,我們需要進行防彈跳處理。

let mut last_clicked = Instant::now();
loop {
    button.wait_for_press().unwrap();
    if last_clicked.elapsed() < Duration::new(1, 0) {
        continue;
    }
    led.toggle();
    last_clicked = Instant::now();
}

內容解密:

  1. last_clicked 變數用於記錄上一次按鈕被按下的時間。
  2. button.wait_for_press() 傳回時,我們檢查 last_clicked.elapsed(),即自上次按鈕被按下以來所經過的時間。
  3. 如果經過的時間少於一秒 (Duration::new(1, 0)),則此次按下事件被視為彈跳並被忽略。
  4. 如果已經過了一秒,則切換 LED 的狀態,並更新 last_clicked 的時間戳。

8.3 跨平台編譯到 Raspberry Pi

由於 Raspberry Pi 的 CPU 效能相對較弱,直接在 Raspberry Pi 上編譯 Rust 程式可能會比較慢。因此,我們需要在更強大的主機(如 x86 Linux PC)上進行跨平台編譯。

首先,我們需要在 x86 Linux PC 上安裝針對 Raspberry Pi 4 Model B 的編譯工具鏈。Raspberry Pi 4 Model B 使用 ARM Cortex-A72 CPU,因此我們需要安裝 armv7-unknown-linux-gnueabihf 目標。

rustup target add armv7-unknown-linux-gnueabihf

內容解密:

  1. 使用 rustup target add 命令新增 armv7-unknown-linux-gnueabihf 目標。
  2. 由於 Raspberry Pi OS 預設為 32 位元,因此我們需要使用 armv7 目標,即使 CPU 是 ARMv8 架構。

接下來,我們需要安裝連結器(linker)。在 Ubuntu 系統上,可以透過安裝 gcc-10-multilib-arm-linux-gnueabihf 套件來獲得 ARM 連結器。

sudo apt-get install gcc-10-multilib-arm-linux-gnueabihf

然後,我們需要在 ~/.cargo/config 檔案中設定連結器。

[target.armv7-unknown-linux-gnueabihf]
linker = "arm-linux-gnueabihf-gcc-10"

內容解密:

  1. 設定 Cargo 使用 arm-linux-gnueabihf-gcc-10 作為 armv7-unknown-linux-gnueabihf 目標的連結器。

現在,我們可以建立一個新的 Cargo 專案,並使用 --target 引數進行跨平台編譯。

cargo new blink-cross-compile
cargo build --target=armv7-unknown-linux-gnueabihf

內容解密:

  1. 使用 cargo new 建立新的專案。
  2. 使用 --target 引數指定目標平台為 armv7-unknown-linux-gnueabihf

編譯完成後,我們可以將產生的二進位檔案複製到 Raspberry Pi 上執行。

8.4 GPIO 程式碼的運作原理

rust_gpiozero 套件抽象了大部分 GPIO 控制的複雜性。但我們仍然可以探討其底層的運作原理。

Sysfs 提供了一種虛擬檔案系統,用於存取 GPIO 暫存器。我們可以透過寫入 Sysfs 虛擬檔案來控制 LED。

echo "2" > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio2/direction
echo "1" > /sys/class/gpio/gpio2/value

內容解密:

  1. 將 GPIO pin 2 輸出到 Sysfs。
  2. 設定 GPIO pin 2 的方向為輸出。
  3. 將 GPIO pin 2 的值設為高電位,開啟 LED。

本章節介紹瞭如何在 Raspberry Pi 上使用 Rust 進行實體運算,並探討了跨平台編譯的概念。透過使用跨平台編譯,我們可以在更強大的主機上編譯程式,然後在 Raspberry Pi 上執行。未來,我們可以進一步探討更多關於實體運算和跨平台編譯的技術。