Arduinoの上でさびを走らせる方法


もともと投稿しました.creativcoder.dev
これを書くとき、およそ1ヵ月前、さびAVRフォークは合併されましたupstream . これは、今すぐ実行することによって、AVR MicroControllerボードのための錆プログラムをコンパイルすることができることを意味しますcargo +nightly build , 提供する.cargo/config.toml あなたの目標avr-unknown-unknown ). それは巨大です!
私は常に物理オブジェクトを操作して、影響を与えるためにコードを使うという考えに魅了されました.これはおそらくArduinoと多分ESP 8266と発見F 3の上でさびによる私の冒険のブログ記事のシリーズであるでしょう.(私はこれらも同様に嘘をついています).この最初のポストでシリーズから離れてキックオフ.
ターゲットオーディエンス:このポストは、Arduinoで錆に埋め込まれたシステムとのヘッドスタートを望む心の中で初心者に初心者で書かれています.一旦このポストを終えたならば、あなたは基本を通してembedded rust book . ポストのコードはLinuxマシン( arch linux )でコンパイルされています.rustc 1.47.0-nightly (22ee68dc5 2020-08-05) .
我々はArduino Unoの上でさびを走らせる方法に関する旋風旅行をします.そして、それはおそらく最も有名で、ホビイスト組込みのドメインで開発ボードを使用しました.The Arduino Uno ATVGA 328 Pに基づきます.そして、それは家族AVRの下で落ちている8ビットのマイクロコントローラです.avrはマイクロチップ技術によって1996年以来開発されたマイクロコントローラのファミリーである.あなたがそれについてもっと読みたいならば、ここで頭を進めてくださいAVR-Rust book
その簡単な歴史を脇に、それを取得しましょう!
我々は、LEDを点滅させているArduinoのHello Worldをします.それは非常に簡単な運動だが、初心者として学ぶことがたくさんあります.

プロジェクトの設定


まず、実行して新しい貨物プロジェクトを作成します.
cargo new rust-arduino-blink
AVRターゲット(ターゲットトリプル)のプロジェクトをクロスコンパイルする必要があります.avr-unknown-unknown ).
このために、依存している木枠のいくつかが不安定な特徴を使用するので、毎晩のツールチェーンに切り替える必要があります.それで実行します.
rustup override set nightly
上記のコマンドは、現在のディレクトリだけを選択するためのツールチェーンを毎晩上書きします.
次に必要なパッケージをインストールします.
pacman -S avr-gcc
The avr-gcc パッケージはリンカを使用する必要があります.
pacman -S arduino-avr-core
The arduino-avr-core パッケージにはavrdude マイクロプロセッサのROMとEEPROMの内容をアップロードし,操作するツールである.
私はアーチのLinuxのディストリビューションpacman パッケージマネージャーです.
で、Cargo.toml 次の依存関係を追加します.
[dependencies]
# A panic handler is needed.  This is a crate with the most basic one.
panic-halt = "0.2.0"

[dependencies.arduino-uno]
git = "https://github.com/Rahix/avr-hal"
rev = "536c5d"
avr-hal ボードによって分離された木箱の束を含むカーゴワークスペースですarduino-uno クレートは一つ
それらのありがとうRahix これを一緒に置くために.
また、AVRターゲット用のカーゴのメタデータをビルドする必要があります.ファイルを作成しますavr-atmega328p プロジェクトのルートで次のコンテンツを入力します.
{
    "llvm-target": "avr-unknown-unknown",
    "cpu": "atmega328p",
    "target-endian": "little",
    "target-pointer-width": "16",
    "target-c-int-width": "16",
    "os": "unknown",
    "target-env": "",
    "target-vendor": "unknown",
    "arch": "avr",
    "data-layout": "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8",

    "executables": true,

    "linker": "avr-gcc",
    "linker-flavor": "gcc",
    "pre-link-args": {
      "gcc": ["-Os", "-mmcu=atmega328p"]
    },
    "exe-suffix": ".elf",
    "post-link-args": {
      "gcc": ["-Wl,--gc-sections"]
    },

    "singlethread": false,
    "no-builtins": false,

    "no-default-libraries": false,

    "eh-frame-header": false
  }

で参照.cargo/config.toml :
[build]
target = "avr-atmega328p.json"

[unstable]
build-std = ["core"]

これにより、ビルドの設定が完了します.

コードを書きましょう


依存関係を除いて、下にコードを加えましょうmain.rs そして、それを段階的に進めます.
(クイックヒント:あなたは走ることができますcargo doc --open あなたのディレクトリには、このプロジェクトのドキュメントを用意します.
// main.rs

#![no_std]
#![no_main]
まず、いくつかのグローバル属性を指定する必要があります.コンパイラは、異なる環境にあることを知っています.
我々は、その機能を持っていない埋め込まれた環境にあるstandard library crate さびのヒープ割り当てAPI、スレッド、ネットワークAPIなどに依存するので、我々は#[no_std] 属性は上部にある.また、デフォルトのエントリポイントをオーバーライドする必要がありますfn main() ) 使用#[no_main] プログラムに自分のエントリポイントを提供し、定義することになるからです.エントリポイントを定義するには、arduino_uno crateカスタムエントリポイントを定義します.通常使用するボードの枠は、あなたにエントリマクロを提供します.
次にuse 必要な項目をスコープ内の依存するcratesから取得するステートメント.
extern crate panic_halt;
use arduino_uno::prelude::*;
use arduino_uno::hal::port::portb::PB5;
use arduino_uno::hal::port::mode::Output;
注意panic_halt クレート?パニックハンドラが必要です.
標準ライブラリでは、定義された動作があります.しかしながら、標準ライブラリのないプログラムでは、パニックになっている動作は未定義です.振舞いは、Councho\[ PanicHunler ]関数を宣言することで選択できます.ソース:[ソース]embedded rust book ]
動きましょう:
#[arduino_uno::entry]
fn main() -> ! {

}
それから私たちはmain 関数はentry 属性マクロarduino_uno クレート.The ! is
関数が返すことのないNone型.
LEDを点滅させるには、いくつかのコードラインを必要とし、関連するピンをハイまたはローに設定します.UNOでATMEGA 328 Pチップのピンダイアグラムを見てみましょう.

上の図では、マイクロコントローラ上の様々なピンに気づくことができます.ほとんどのマイクロコントローラは、すべてでなければ、デバイスが外部回路にデジタル値を読み書きすることを可能にするピンを含む.それらのうちのいくつかはI/Oポートに分類されます.
ポートは標準インターフェイスを表すピンのグループです.これらのポートはポートレジスタによって制御されます.これは我々のコードで変更できる特殊バイト変数として考えられます.
ATMEGA 328 Pの場合、3つのポートレジスタがあります.
  • アナログピン0〜5用
  • デジタルピン0〜7
  • デジタルピン8〜13用
  • 詳細はこちらPort Manipulation
    UNOを見れば、ビルトインLEDに接続されたデジタルピン13があります.
    我々は、LEDを操作するために、すなわち、それをハイまたはローに設定するために、我々のコードのピンへのアクセスを必要とします.
    より多くのコードを加えましょう:
    #[arduino_uno::entry]
    fn main() -> ! {
        let peripherals = arduino_uno::Peripherals::take().unwrap();
    
        let mut pins = arduino_uno::Pins::new(
            peripherals.PORTB,
            peripherals.PORTC,
            peripherals.PORTD,
        );
    
        let mut led = pins.d13.into_output(&mut pins.ddr);
    
        loop {
            stutter_blink(&mut led, 25);
        }
    }
    
    上のコードで多くのことが起こっている!
    まず、インスタンスを作成しますPeripheral UNOのすべての周辺機器のリストです.
    周辺機器は、外部デバイス、センサーなどを使用してチップ/CPUとの通信をブリッジするデバイスです.
    周辺機器の例は、あなたのタイマー、カウンタ、シリアルポートなどです.
    組込プロセッサは、一連の制御およびステータスレジスタを経て周辺機器と相互作用する.
    次に、新しいPin 周辺インスタンスからのポートでのインスタンス渡しperipherals .
    次に変数を定義するled それは、LEDが接続されているピン番号を保持します.これは出力ピンとしてピン13を設定することによって作成されますinto_output メソッドd13 ピンと私たちへの変な参照で渡すddr レジスタ.
    DDRレジスタは、特定のポートのピンが入力か出力かどうか決定します.DDRレジスタは8ビット長であり、各ビットはそのI/Oポート上のピンに対応する.例えば、DDRBの第1ビット(ビット0)は、PB 0が入力または出力であるかを判定し、一方、最後のビット(ビット7)は、PB 7が入力または出力であるかどうかを判定する.私はまだ完全にDDRレジスタを理解するためにもう少し読む必要があります.
    次に、我々はloop {} 呼び出しstutter_blink 関数は、我々のled インスタンス
    と回数25 ) 我々が瞬きたいです.
    こちらがstutter_blink 関数定義
    fn stutter_blink(led: &mut PB5<Output>, times: usize) {
        (0..times).map(|i| i * 10).for_each(|i| {
            led.toggle().void_unwrap();
            arduino_uno::delay_ms(i as u16);
        });
    }
    
    私たちがStutterSoundsブリンクで行うすべては、LEDの上の呼び出しトグルであり、続いてArduinoCount UNOモジュールからdelaylose ms呼び出しをします.これはすべてイテレータで行います.私たちは漸近的にLEDトグルを目立つ量で遅らせることができるように、範囲(0 . times .)をマップしてあとに続きます.私たちは確実にforループを使用してこれを行うことができました、そして、それはより読みやすいです、しかし、私は我々がさびからすべての高いレベルAPIと抽象を使うことができるということを証明したかったです.私たちは、抽象化がゼロコストである組み込みシステムのための機能コードを書いています.それは私が知っている限り錆だけで可能なことです!
    以下は完全なコードです.
    // main.rs
    
    #![no_std]
    #![no_main]
    
    extern crate panic_halt;
    use arduino_uno::prelude::*;
    use arduino_uno::hal::port::portb::PB5;
    use arduino_uno::hal::port::mode::Output;
    
    fn stutter_blink(led: &mut PB5<Output>, times: usize) {
        (0..times).map(|i| i * 10).for_each(|i| {
            led.toggle().void_unwrap();
            arduino_uno::delay_ms(i as u16);
        });
    }
    
    #[arduino_uno::entry]
    fn main() -> ! {
        let peripherals = arduino_uno::Peripherals::take().unwrap();
    
        let mut pins = arduino_uno::Pins::new(
            peripherals.PORTB,
            peripherals.PORTC,
            peripherals.PORTD,
        );
    
        let mut led = pins.d13.into_output(&mut pins.ddr);
    
        loop {
            stutter_blink(&mut led, 25);
        }
    }
    
    
    この木箱を作りましょう.
    cargo build
    
    すべてがうまくいくなら、ELFファイルを見るべきですrust-arduino-blink.elf 生成下target/avr-atmega328p/debug/ ディレクトリ.それは私たちのウノにフラッシュする必要がバイナリです.ELFファイルをフラッシュするにはavrdude ユーティリティ.ルートディレクトリにスクリプトを作りましょうflash.sh それでcargo build 私たちの宇野を点滅させた後で
    #! /usr/bin/zsh
    
    set -e
    
    if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then
        echo "usage: $0 <path-to-binary.elf>" >&2
        exit 1
    fi
    
    if [ "$#" -lt 1 ]; then
        echo "$0: Expecting a .elf file" >&2
        exit 1
    fi
    
    sudo -u creativcoder cargo build
    avrdude -q -C/etc/avrdude.conf -patmega328p -carduino -P/dev/ttyACM0 -D "-Uflash:w:$1:e"
    
    
    これにより、実行できます( UNOがUSBケーブル経由で接続されているか確認してください)../flash.sh target/avr-atmega328p/debug/rust-arduino-blink.elfそこに行く

    さびを実行しているArduinoの我々の最初のまばたきプログラム!
    アクセス許可時にアクセス許可が拒否された場合/dev/ttyACM0 . ユーザーを追加する必要があります
    シリアルインタフェースにアクセスできるLinuxユーザグループに.
    まず、グループを使用します.ls -l /dev/ttyACM0私のマシンで次の出力を得るcrw-rw---- 1 root uucp 166, 0 Aug 19 03:29 /dev/ttyACM0ユーザー名をuucp グループを実行します.sudo usermod -a -G uucp creativcoder提案/コメント/訂正最も歓迎します.
    私はいくつかの埋め込まれた趣味プロジェクトを計画している、私は彼らが進行中として将来的にそれらについて書きます.ここではGithub 上記のコードのために.
    あなたがさびた埋め込まれたスペースの開発において最新に続くことを望むならばembedded rust working group
    次回まで!