以前FreeROSをArduinoにインストールしてLチカさせる方法を紹介しました。
また別のアプローチとして、Rustによる組込プログラミングというのも可能のようです。
今回はRustからどのようにAVRマイコン用のプログラムを使っていくのか考えていきます。

合同会社タコスキングダム|蛸壺の技術ブログ
[Arduino工作 〜 発展編] Arduino Uno Rev3にFreeRTOSのインストールしてLチカも試す

ArduinoにFreeRTOSを入れた時の手順とその時のポイントをメモしておきます。


AVR-Rustでより簡単に組込プログラミング

AVR-Rust はかつては有志によるプロジェクトでしたが、その後Rustが公式でサポートする形で2020年7月からオプションのライブラリとして利用できるようになったようです。
このことでcargoコマンド一発でAVRのビルド環境が構築できるようになり、かなり簡単に扱えるようになっています。

toolchainの環境構築



RustでAVR用のプログラムをビルドする前に、Rustの環境を整えます。
パソコンのOS自体はRustが動作すればなんでもOKですが、今回は
Debian/Ubuntu 系のLinux相当( apt-get コマンドの利用できるディストリビューション)で試しております。 なお、お手元でRustの使える環境であることを前提として、Rustのインストール手順自体はウェブ検索すると山ほど情報が出てきますので省略します。ご了承ください。
さて、まずrustupがビルド時に必須ですので、現在のバージョンを確認します。

            $ rustup --version
rustup 1.23.1 (3df2264a9 2020-11-30)
info: This is the version for the rustup toolchain manager, not the rustc compiler.
info: The currently active `rustc` version is `rustc 1.51.0-nightly (c2de47a9a 2021-01-06)`

        

AVRのプログラムに必要なプログラムを以下のように追加でパッケージインストールします。

            $ sudo apt-get install binutils gcc-avr avr-libc avrdude

        

他のOSへのインストールは
ここのページ に言及されています。
AVR向けのツールチェーンはnightlyにしか組み込まれていませんので、以下のようにrustupでnightlyビルドができるように仕向けます。

            $ rustup update
$ rustup toolchain install nightly
$ rustup component add rust-src --toolchain nightly
$ rustup override set nightly
$ rustc -Vv
rustc 1.51.0-nightly (c2de47a9a 2021-01-06)
binary: rustc
commit-hash: c2de47a9aa4c9812884f341f1852e9c9610f5f7a
commit-date: 2021-01-06
host: x86_64-unknown-linux-gnu
release: 1.51.0-nightly

        

次にビルドターゲットをAVR用に設定しますが、Rustのnightlyに記述されているAVR用ターゲットは
avr-unknown-gnu-atmega328 一択です。
もしATmega328以外のマイコンをターゲットにするには、カスタムターゲット用のJSONを別途作成する必要があります。
カスタムターゲットの例としてATmega328p用のjsonファイルを作ってみましょう。

            $ rustc --print target-spec-json -Z unstable-options \
    --target avr-unknown-gnu-atmega328 > avr-atmega328p.json

        

すると、
avr-atmega328p.json という名前で以下のような内容のjsonファイルができると思います。

            {
    "arch": "avr",
    "atomic-cas": false,
    "cpu": "atmega328",
    "data-layout": "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8",
    "eh-frame-header": false,
    "exe-suffix": ".elf",
    "executables": true,
    "is-builtin": true,
    "late-link-args": {
        "gcc": [
        "-lgcc"
        ]
    },
    "linker": "avr-gcc",
    "linker-is-gnu": true,
    "llvm-target": "avr-unknown-unknown",
    "max-atomic-width": 0,
    "pre-link-args": {
        "gcc": [
        "-mmcu=atmega328",
        "-Wl,--as-needed"
        ]
    },
    "target-c-int-width": "16",
    "target-pointer-width": "16"
}

        

基本的には
cpu 名とgccの -mmcu の2つをターゲットデバイスに合わせるように変えます。

            {
    //...
    "cpu": "atmega328p",
    //...
    "pre-link-args": {
        "gcc": [
            "-mmcu=atmega328p",
            //...
        ]
    },
    //...
}

        

これでひとまずツールチェーンの構築は完了です。


Rustプログラミング手順



では
RustでのLチカのプログラム例 を見ていきます。
このプロジェクトの最低限のフォルダ構造は以下です。

            $ tree
.
├── Cargo.toml
├── src
│   └── main.rs
└── avr-atmega328p.json

        

ビルドターゲット用のjsonは先ほど説明していたファイルですので説明は省略しまして、まずCargo.tomlですが、

            [package]
name = "blink"
version = "0.1.0"
authors = ["Dylan McKay <me@dylanmckay.io>"]
edition = '2018'

[dependencies]
ruduino = "0.3"

        

となっています。
ここで唯一依存性の指定が入っている
ruduino はArduino Uno向けのクレートです。 Arduino UnoはATmega328pマイコンの搭載された製品ですので、この ruduino クレートがそのまま利用できます。
他のAVRマイコンへの実装例もこの
ruduinoのプロジェクトソースコード を眺めて、自作するヒントが掴めるかも知れませんので、色々と勉強してみると良いでしょう。
次にいよいよ
src/main.rs ですが、

            #![feature(llvm_asm)]

#![no_std]
#![no_main]

use ruduino::Pin;
use ruduino::cores::current::{port};

#[no_mangle]
pub extern fn main() {
    port::B5::set_output();
    loop {
        port::B5::set_high();
        ruduino::delay::delay_ms(1000);
        port::B5::set_low();
        ruduino::delay::delay_ms(1000);
    }
}

        

となって、いかにもArduino風にプログラミングされいます。
こうスッキリと完結したコードになっているのも、ruduinoからインポートしたメソッドを利用できている恩恵で、一般に自分で一からコードを実装すると、もっと長くなると思います。
如何せんこれで材料が揃いましたので以下のコマンドでビルドします。

            $ export AVR_CPU_FREQUENCY_HZ=16000000
$ cargo build -Z build-std=core --target avr-atmega328p.json --release

        

これで
target/[デバイスのタイトル]/release フォルダ以下に、実行ファイルの blink.elf が吐き出されていたら完了です。
ちなみにArduinoにはビルドインLEDとマイコン側のPB5ピンは接続されているので、Arduino上で試すとこのLEDがチカチカすることが分かります。
あとはavrdudeでマイコンに書き込む手順は別記事で説明した内容と一緒です。
以下のコマンドはAtmel-ICEからマイコンへ書き込むときのコマンド例です。

            $ sudo avrdude -p m328p -c atmelice_isp -P usb -U flash:w:target/avr-atmega328p/release/blink.elf:e

        
合同会社タコスキングダム|蛸壺の技術ブログ
[Atmel-ICE on Linux] Debianからavrdudeを使ってブレッドボード上でAtmega328pのプログラムを書き込む

新品のAtmega328pにDebian Linux & Atmel-ICEでavrdudeを使ったプログラムをブレッドボード上で書き込みする手順を特集します。




書き込み装置の種類でavrdudeのオプションが違いますので、avrdudeのマニュアルなどを参照にプログラムを書き込んでください。


LLVM ERRORで止まる(2021年1月頃の報告)



2021年3月現在で、nightlyのバージョンによっては他のライブラリとの整合性が内部で取れていないのか、llvmでビルド中のバグが報告されています。

参考:LLVM ERROR: Not supported instr

以下のように
compiler_builtins のエラーという内容です。

            $ cargo build -Z build-std=core --target avr-unknown-gnu-atmega328 --release
   Compiling compiler_builtins v0.1.39
   Compiling nb v1.0.0
   #...
   Compiling embedded-hal v0.2.4
LLVM ERROR: Not supported instr: <MCInst 258 <MCOperand Reg:1> <MCOperand Imm:15> <MCOperand Reg:40>>
error: could not compile `compiler_builtins`

To learn more, run the command again with --verbose.
warning: build failed, waiting for other jobs to finish...
error: build failed

        

現行で最新のnightlyではまだこのバグが残ったままでしたので、バグの出ない
nightly-2021-01-07 というバージョンを指定して利用するようにします。

            $ rustup toolchain install nightly-2021-01-07
$ rustup component add rust-src --toolchain nightly-2021-01-07
$ rustup override set nightly-2021-01-07

        

として再度cargoビルドしてあげると上手くコンパイルできるはずです。


まとめ



今回はRustでAVRマイコンを実装する場合の手順を紹介していきました。
感触としてはすでにC言語でプログラミングするのと殆ど同じように感じました。
個人的に、Rustで組込プログラミングというのは水面下でかなり流行ってきてる手法ですので、これを気に何かサーボモータを制御して可動する装置を作ってみたくなりました。

参考サイト

The AVR-Rust Guidebook

Arduinoへ直接書き込みする

2020版: RustのArduino向けプログラムでLチカする方法RustでLチカする2種類の方法(Arduino編)

Raspberry Pi Pico関連



最近話題のRaspberry Pi Picoも同様の手法でRustによるプログラミングが可能ですが、書き込み方法は別の環境を構築する必要があります。

Raspberry Pi Pico Gets supports for Rust, RT-Thread OS and FreeRTOS