rust
RaspberryPi

【Rust】Raspberry PiのGPIOやSPIを操作するCrateまとめ

はじめに

Raspberry PiのGPIOやSPIを操作するCrateがいろいろとありますので紹介します。
同様のものをGitHubに上げてますので、サンプルコードのプロジェクト一式が欲しいから以下のURLからどうぞ。

https://github.com/hukatama024e/raspi_crate_sample

動作環境

  • Raspberry Pi 3 Model B+
  • raspbian 8.0
  • Rust 1.27.0

準備

SPIを使うため、以下の手順でSPIを有効にする必要があります(一連の作業にはroot権限が必要)。
1. raspi-configでSPIを有効設定にするか、/boot/config.txtdtparam=spi=onを追加。
2. rebootなどでRaspberry Piを再起動。

サンプルコード仕様

サンプルコードの回路構成・動作仕様は以下の通りです。

回路構成

以下の図のようにGPIO24にLEDを接続しています。また、MISOとMOSIを直接接続しています。

Circuit.png

動作仕様

  1. LEDの点滅を10回行う。
  2. SPIをサポートしている場合はデータを送受信する。

SPIの設定ですが、直接接続しているので制約があるわけではないのですが以下の表のように設定しています。

項目 設定内容
Slave Select pin SPI0_CE0
クロック速度 62.5MHz
動作モード Mode0(CPOL=0、CPHA=0)
Slave Select極性 Active Low

Crate

Crate毎にサンプルコード、crates.ioのURL、Lincenseを記載します。

各Crateが持つ機能は以下の表の通りです。

rppal rustpi_io sysfs_gpio wiring_pi
GPIO
SPI - -

〇: サポートしていてかつroot権限は不要
△: サポートしていますがroot権限が必要
-: サポートされていません

rppal

rppalはGPIOとSPIをサポートしています。
また、最近のUpdate(v0.7)でI2Cがサポートされるなど、活発に開発されています。
迷ったらrppalを使ってみるのがいいかと思います。

Lincese
MIT

crates.io
https://crates.io/crates/rppal

サンプルコード

extern crate rppal;

use std::thread;
use std::time::Duration;
use rppal::{gpio, spi};
use rppal::gpio::{Gpio, Level};
use rppal::spi::{Spi, Bus, SlaveSelect};

// GPIOピン番号
const GPIO_PIN_NUM : u8 = 24;

// LED点滅回数
const BLINK_CNT : u8 = 10;

fn main() {
    let mut gpio = Gpio::new().expect( "Failed Gpio::new" );
    let mut blinking_cnt = 0;

    // GPIOモードを出力に設定
    gpio.set_mode( GPIO_PIN_NUM, gpio::Mode::Output );

    // LED点滅
    while blinking_cnt < BLINK_CNT {
        gpio.write( GPIO_PIN_NUM, Level::High );
        thread::sleep( Duration::from_secs( 1 ) );

        gpio.write( GPIO_PIN_NUM, Level::Low );
        thread::sleep( Duration::from_secs( 1 ) );
        blinking_cnt = blinking_cnt + 1;
    }

    // GPIOモードを入力に戻す
    gpio.set_mode( GPIO_PIN_NUM, gpio::Mode::Input );

    // SPI設定
    let spi = Spi::new( Bus::Spi0, SlaveSelect::Ss0, 6_2500_000, spi::Mode::Mode0 )
                    .expect( "Failed Spi::new" );

    // SPIデータ送受信
    let write_data = vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0];
    let mut read_data = [0; 8];
    let read_size = spi.transfer( &mut read_data, &write_data ).expect( "Failed Spi::transfer" );

    for i in 0..read_size {
        println!( "read_data[{}] = 0x{:X}", i, read_data[i] );
    }
}

rustpi_io

rustpi_ioはGPIOとSPIをサポートしています。
ただし、GPIOはsysfs(/sys/class/gpio/)経由でアクセスしているためroot権限が必要です。

Lincese
GPL-3.0

crates.io
https://crates.io/crates/rustpi_io

サンプルコード

extern crate rustpi_io;

use std::io::{Read, Write};
use std::thread;
use std::time::Duration;
use rustpi_io::gpio::{GPIO, GPIOData, GPIOMode};
use rustpi_io::serial::{SerialPi, Device, Speed, SpiMode, ComMode};

// GPIOピン番号
const GPIO_PIN_NUM : u8 = 24;

// LED点滅回数
const BLINK_CNT : u8 = 10;

fn main() {
    let gpio = GPIO::new( GPIO_PIN_NUM, GPIOMode::Write ).expect( "Failed GPIO::new" );
    let mut blinking_cnt = 0;

    // LED点滅
    while blinking_cnt < BLINK_CNT {
        gpio.set( GPIOData::High ).expect( "Failed GPIO.set" );
        thread::sleep( Duration::from_secs( 1 ) );

        gpio.set( GPIOData::Low ).expect( "Failed GPIO.set" );
        thread::sleep( Duration::from_secs( 1 ) );
        blinking_cnt = blinking_cnt + 1;
    }

    // SPI設定
    let mut spi = SerialPi::new( Device::CE0, Speed::Mhz62_5, SpiMode::Mode0, ComMode::FullDuplex )
                    .expect( "Failed SerialPi::new" );

    // SPIデータ送信
    let mut write_data = vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0];
    spi.write( &mut write_data ).expect( "Failed SerialPi::write" );

    // SPIデータ受信
    let mut read_data = [0; 8];
    let read_size = spi.read( &mut read_data ).expect( "Failed SerialPi::read" );

    for i in 0..read_size {
        println!( "read_data[{}] = 0x{:X}", i, read_data[i] );
    }
}

sysfs_gpio

sysfs_gpioはsysfsでGPIOにアクセスするCrateです。
そのため、rustpi_ioと同様にroot権限が必要です。

Lincese
MIT/Apache 2.0

crates.io
https://crates.io/crates/sysfs-gpio

サンプルコード

extern crate sysfs_gpio;

use std::thread;
use std::time::Duration;
use sysfs_gpio::Pin;
use sysfs_gpio::Direction;

// GPIOピン番号
const GPIO_PIN_NUM : u64 = 24;

// LED点滅回数
const BLINK_CNT : u8 = 10;

// GPIO設定値
const LOW : u8  = 0;
const HIGH : u8 = 1;


fn main() {
    let pin = Pin::new( GPIO_PIN_NUM );
    let mut blinking_cnt = 0;

    pin.with_exported( || {

        // GPIOモードを出力に設定
        pin.set_direction( Direction::Out ).expect( "Failed Pin::set_direction" );

        // LED点滅
        while blinking_cnt < BLINK_CNT {
            pin.set_value( LOW ).expect( "Failed Pin::set_value" );
            thread::sleep( Duration::from_secs( 1 ) );

            pin.set_value( HIGH ).expect( "Failed Pin::set_value" );
            thread::sleep( Duration::from_secs( 1 ) );
            blinking_cnt = blinking_cnt + 1;
        }

        // GPIOモードを入力に戻す
        pin.set_direction( Direction::In ).expect( "Failed Pin::set_direction" );

        Ok( () )
    } ).expect( "Failed Pin::with_exported" );
}

wiringpi

wiringpiはC言語のライブラリ「Wiring Pi」のラッパーでGPIOをサポートしています。
現在の「Wiring Pi」の方はBCMのピン番号の方に対応しているので他のCrateと同じようなピン番号の指定を行うことが出来ますが、このCrateを使う場合はWiring Pi固有のピン番号を指定する必要があります。

Lincese
MIT

crates.io
https://crates.io/crates/wiringpi

サンプルコード

extern crate wiringpi;

use std::thread;
use std::time::Duration;
use wiringpi::pin::Value::{High, Low};

// GPIO24のWiring Pi固有のピン番号
const WPI_GPIO24_PIN_NUM : u16 = 5;

// LED点滅回数
const BLINK_CNT : u8 = 10;

fn main() {
    let wiringpi = wiringpi::setup();
    let pin = wiringpi.output_pin( WPI_GPIO24_PIN_NUM );
    let mut blinking_cnt = 0;

    // LED点滅
    while blinking_cnt < BLINK_CNT {
        pin.digital_write( High );
        thread::sleep( Duration::from_secs( 1 ) );

        pin.digital_write( Low );
        thread::sleep( Duration::from_secs( 1 ) );
        blinking_cnt = blinking_cnt + 1;
    }
}