ESP32でstdなRust開発入門 その3 デジタル入出力

概要

前回はLチカをしたので、デジタル入出力についてもう少し調べてみました。

ピンモード

PinDriver::pinmode名()

let mut led = PinDriver::output(peripherals.pins.gpio2)?;

前回は上記のようにPinDriverを利用して、outputモードでGPIOを取得していました。

モード入力出力備考
disabled入力も出力も無効
inputenable入力のみ可能
input_outputenablepush pull入力と出力が可能
input_output_odenableopen drain入力とオープンドレインでの出力が可能
outputpush pull出力が可能
output_odopen drainオープンドレインでの出力が可能

モードを調べたところ、6種類ありました。

esp-idf-hal/src/gpio.rs at master · esp-rs/esp-idf-hal
embedded-hal implementation for Rust on ESP32 and ESP-IDF - esp-rs/esp-idf-hal

このへんはドキュメントを見てもわかりにくかったのでソースコードから調べています。Arduinoと比べるとアナログ入力は別機能としてADCに切り出されています。disabledは特殊ですがデフォルトの何もできない状態だと思います。

オープンドレインと普通の出力があるのがわかると思いますが、input_outputモードが注意が必要です。ESP32はデジタル入力は常に取得可能なのですが、outputモードに設定していると入力値がライブラリ的には取得できなくなっています。

とはいえ、通常は入力か出力かのどちらかの状態を明示的に指定したほうがよいと思います。

プルダウン・プルアップ

set_pull()

let mut input = PinDriver::input(peripherals.pins.gpio32)?;
input.set_pull(Pull::Up)?;

上記のようにピンを取得してから設定が可能です。

設定値状態
Pull::Floatingデフォルトのフローティング状態
Pull::Downプルダウン(0V)
Pull::Upプルアップ(3.3V)
Pull::UpDownプルアップかつプルダウン(1.65V)

ちょっと危険がUpDownがあります。Arduinoでは指定できませんが、ハードウエア的には直接レジスタに設定すれば指定することができます。Rustでは指定可能ですが、使わないほうがよいと思われるモードです。

情報取得

pin()

let mut input = PinDriver::input(peripherals.pins.gpio32)?;
print!("Input pin = {}, ", input.pin());

pin()関数を使うことで、どのGPIOなのかを返却してくれます。配列などにpinのリストを入れて、ループで処理するときなどに使うのかな?

入力系

get_level()

if led.get_level() == Level::High {
    "High"
} else {
    "Low"
}

ピンの状態を返却します。たぶんあまり利用しない関数だと思います。

pub enum Level {
    Low,
    High,
}

状態はHighとLowの2種類になります。

is_high()

if input.is_high() {
    "High"
} else {
    "Low"
}

Highの場合にtrueが返却される関数です。bool型で戻ってきたほうが判定が楽になると思います。

is_low()

if input.is_low() {
    "Low"
} else {
    "High"
}

Lowの場合にtrueが返却される関数です。

出力系

set_level()

led.set_level(Level::High)?;

get_level()と対応しておりLowかHighを指定します。おそらくこちらもあまり利用しません。

set_high()

led.set_high()?;

Highに設定する関数です。シンプルな設定はこちらを利用したほうがよいと思います。

set_low()

led.set_low()?;

Lowに設定する関数です。

toggle()

led.toggle()?;

LowであればHighに、HighであればLowに出力を反転する関数です。

is_set_high()

if led.is_set_high() {
    "High"
} else {
    "Low"
}

現在の出力設定がHighかをbool型で返却する関数です。Levelが返却される関数はありませんでした。input_outputモードであればget_level()でも取得可能ですが、通常はこのis_set_high()で十分なはずです。

is_set_low()

if led.is_set_low() {
    "Low"
} else {
    "High"
}

現在の出力設定がLowかをbool型で返却する関数です。

サンプルコード

use esp_idf_hal::delay::FreeRtos;
use esp_idf_hal::gpio::Level;
use esp_idf_hal::gpio::PinDriver;
use esp_idf_hal::gpio::Pull;
use esp_idf_hal::peripherals::Peripherals;

fn main() -> anyhow::Result<()> {
    esp_idf_sys::link_patches();

    let peripherals = Peripherals::take().unwrap();
    let mut input = PinDriver::input(peripherals.pins.gpio32)?;
    let mut led = PinDriver::input_output(peripherals.pins.gpio2)?;

    input.set_pull(Pull::Up)?;
    print!("Input pin = {}, ", input.pin());
    println!("Output pin = {}", led.pin());

    loop {
        if input.is_high() {
            led.set_high()?;
            print!("Input High, ");
            println!(
                "Output {}",
                if led.get_level() == Level::High {
                    "High"
                } else {
                    "Low"
                }
            );
        } else {
            led.set_low()?;
            print!("Input Low, ");
            println!("Output {}", if led.is_set_high() { "High" } else { "Low" });
        }
        FreeRtos::delay_ms(1000);
    }
}

デジタル入出力系に関しては組み込み系Rustであればほぼ共通している処理になると思われます。

ドキュメント

PinDriver in esp_idf_hal::gpio - Rust
A driver for a GPIO pin.

上記がPinDriverのドキュメントですが、実際のところよくわからないと思います。ピンポイントで調べるときにはいいのですが、全体を勉強するときにはソースコードから拾ったほうが良さそうです。

該当ソースコード

esp-idf-hal/src/gpio.rs at master · esp-rs/esp-idf-hal
embedded-hal implementation for Rust on ESP32 and ESP-IDF - esp-rs/esp-idf-hal

私はこちらから情報を拾いました。

まとめ

Rustの特徴として入力モードのときに出力系の関数を使うとエラーになるなど、バグが入りにくい言語仕様になっているのが便利です。

ただし、まとまった資料があまりなかったりするので、全体像を知るのはちょっと大変かなと思っています。

コメント