[備忘録] kubuntu 24.04上にRaspberry pi pico + Rust開発環境を構築する

# はじめに
[slint](https://slint.dev/)に興味津々なRust初学者のhermit4です。本格的な勉強を始めるにあたり、Raspberry Pi PicoとRustで遊ぶための環境を用意したいなと思って作業記録をつけておくことにします。OSは個人的な趣味でkubuntu 24.04となります。

なにかツッコミどころ、こうしたほうが効率的、これがおすすめなどあれば、ご意見をコメントしてくだされば助かります。

# 早速インストール手順

## OS環境の追加

```bash:
sudo apt install build-essential libfontconfig-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libglu1-mesa-dev libudev-dev cmake ninja-build lldb openocd gdb-multiarch curl
```
ツール郡やslintのビルドで必要になるパッケージ類。


## Rustのインストール

Rust環境はUbuntu公式からaptで入れる方法と、rustupから入れる方法の二通りがあります。公式のおすすめ手順ですし、curlからシェルを拾って入れる解説のほうが多いです。ここは素直に従っておきます。

```bash:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```
途中のプロンプトはデフォルトにしておきます。

```shell-session:
1) Proceed with standard installation (default - just press enter)
2) Customize installation
3) Cancel installation
>
```

## ターゲットの追加とコンポーネントの追加
```bash:
. ~/.bashrc
rustup target add thumbv6m-none-eabi
rustup component add rust-analysis rust-src rust-analyzer
```

## 外部ツールの追加
```bash:
cargo install --locked probe-rs-tools elf2uf2-rs flip-link cargo-generate cargo-binutils
```

## probe-rsのセットアップ
```bash:
wget https://probe.rs/files/69-probe-rs.rules
sudo install -o root -g root -m 644 69-probe-rs.rules /etc/udev/rules.d
sudo udevadm control --reload
```

## Visual Studio Codeのインストール
```bash:
wget -q -O - https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
sudo install -o root -g root -m 644 microsoft.gpg /etc/apt/trusted.gpg.d/
sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main" > /etc/apt/sources.list.d/vscode.list'
sudo apt update
sudo apt install code
```

## Visual Studio Codeのセットアップ

ここからはVisual Studio Codewo起動してVisual Studio Codeでの作業となります。
```bash:
code
```

### 日本語化
英語環境のほうがカッコいいと言う人はskipしてどうぞ

1. Ctrl+Shift+P
2. displayと入力して出る候補から"Configure Display Language"を選択
3. 日本語を選択
3. ダイアログでRestartを実行

### 拡張のインストール
1. Ctrl+Shift+X
1. rustと入力しrustをインストール(以下の３つの拡張が入る)
    - rust-analyzer
    - crates
    - Rust Syntax
1. Debugger for probe-rsと入力してインストール
1. tomlと入力してEven Better TOMLをインストール
1. CodeLLDBと入力してインストール
1. Serial Monitorと入力してインストール

### フォーマッタの設定(この辺は好みの問題かな）
1. Ctrl+Shift+P
1. settingsと入力しユーザー設定を開く(JSON)を選択
```json:settings.json
{
    "[rust]": {
	    "editor.defaultFormatter": "rust-lang.rust-analyzer",
	    "editor.formatOnSave": true,
	    "editor.inlayHints.enabled": "off",
        "editor.tabSize": 4,
    },
    "rust-analyzer.rustfmt.rangeFormatting.enable": true,
    "rust-analyzer.checkOnSave": true,
    "rust-analyzer.check.command": "clippy",
    "rust-analyzer.check.extraArgs": ["--", "-A", "clippy::needless_return"],
}
```

# とりあえずホストマシンでHello Workd
## プロジェクト生成
Ctrl+`でターミナルを開いで以下のコマンドを実行

```bash:
mkdir devel && cd $_
cargo new hello && code -r $_
```
Hello Worldを出力するテンプレートが生成されます。

## ビルド
1. Ctrl+Shift+B
1. rust: cargo buildを選択する

## launch.jsonの生成(Host実行用LLDB)
1. Ctrl+Shift+P
1. Debug Add Configと入力しデバッグの構成を選択
1. LLDBを選択

## 実行
1. Ctrl+F5

## デバッグ実行
1. 事前にブレークポイントなどを貼っておく
1. F5

# 本命のRaspberry Pi Pico上でLチカ
## Raspberry Pi PicoとDebug Probeを配線する

ここからはRaspberry Pi PicoでのRust準備です。まずは接続します。利用した機材は以下の通り。
 - [Raspberry Pi Pico WH](https://akizukidenshi.com/catalog/g/g118086/)
 - [Raspberry Pi Debug Probe](https://akizukidenshi.com/catalog/g/g117971/)
 - [Pico Breadboard Kit](https://www.amazon.co.jp/-/en/GeeekPi-Raspberry-Breadboard-breadboard-included/dp/B097MXJYQJ?th=1)

### Raspberry Pi Debug Probe
<img width='120' alt='probe' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58828/571715f5-b750-06e7-9c9c-b6d5a6e29d7a.png' />


Debug Probeはラズベリー財団が出している公式のSWD用のプローブです。SWDとUARTの機能が乗っています。SWDは、ARM Serial Wire Debugの略称で、JTAGの様にバウンダリスキャンなどはできず、単にチップ上のデバッグレジスタに読み書きするだけの簡単な機能を提供されています。

これを使うとデバッガを使ったデバッグが可能になります。

ちなみにUがUARTですが、Uの表記を正面に見て左から

| 色 | 機能     |
|:---|--------:|
| 橙 | UART TX |
| 黒 | GND     |
| 黄 | UART RX |

という並びになっています。PicoのUART0はデフォルトで
| pin | 機能     |
|:---|--------:|
| 1 | UART RX |
| 2 | UART TX |
| 3 | GND     |

となっているので、Picoと接続じはちょっと捻って接続する形になります。
SWDの側は、コネクタがついていればそのまま指すだけなので簡単です。コネクタなしでかった場合は、ハンダつけ頑張るしかないでしょうけど。

以下公式の図です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58828/67da3a16-2d13-6589-9bb6-55b7d2446438.png)
https://www.raspberrypi.com/documentation/microcontrollers/images/wiring.png


### Pico Breadboard Kit

Pico Bread Board Kitはpicoを載せるソケットと、取り出し端子、スイッチ、LED、ブザー、ブレッドボードがセットになったボードです。使わない時はBeepとGNDを短絡しないとブザーが鳴り続けるという欠点はありますが、ちょっとしたお試しには便利です。

![picobb.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58828/51357507-a6f7-bffd-bbc6-bd0f0ea629d2.png)

手持ちのPicoはWHで、オンボードLEDはライブラリを使わないとチカチカできないので、今回はGPIO-15と外部のLED1を結線して利用します。

## Raspberry Pi Pico向けにプロジェクトを作る

1. Ctrl+K F　でフォルダを閉じる
1. Ctrl+` でターミナルを開く
1. ターミナルでプロジェクト作成を実行
```bash:
cd devel
cargo generate --git https://github.com/rp-rs/rp2040-project-template --branch main --name pico-hello && code -r $_
```
probe-rsを使うので、フラッシュはprobe-rsを選択しておく。
cargo generateはネット上のテンプレートをベースに新しいプロジェクトを生成します。生成されたrp-rsのテンプレートはオンボードLED(GPIO25)のLチカコードになっています。こちらをGPIO15に変更します。
```rust:main.rs
-     let mut led_pin = pins.led.into_push_pull_output();
+     let mut led_pin = pins.gpio15.into_push_pull_output();
```

## まずはコマンドラインでの確認
### Debug Probeの認識
```shell_session:
probe-rs list
The following debug probes were found:
[0]: Debug Probe (CMSIS-DAP) -- 2e8a:000c:nnnnnnnnnnnnnnnnn (CMSIS-DAP)
```

### 実行確認
```shell_session:
cargo run
```
cargo runはビルドして、probe-rs経由でフラッシュして、実行を開始してくれます。

## Visual Studio Codeからの実行・デバッグ
Host上での実行と同じくlaunch.jsonの設定が必要です。

1. Ctrl+Shift+P
1. Debug Add Configと入力しデバッグの構成を選択
1. エラーになるものも含まれてるっぽいので、minimam設定になるように編集(programBinaryはプロジェクトに合わせて修正）
```json:launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "preLaunchTask": "rust: cargo build",
            "type": "probe-rs-debug",
            "request": "launch",
            "name": "rp2040-project",
            "chip": "rp2040",
            "connectUnderReset": false,
            "flashingConfig": {
                "flashingEnabled": true,
            },
            "coreConfigs": [
                {
                    "programBinary": "target/thumbv6m-none-eabi/debug/pico-hello",
                }
            ],
            "consoleLogLevel": "Info", //Error, Warn, Info, Debug, Trace
            "wireProtocol": "Swd"
        }
    ]
}
```

これで、適当にブレークポイントをはってF5でデバッグ実行すれば、ソースコードを見ながらデバッグが可能になります。なお、停めたい場所でうまくブレークポイントが設定できないとか、止まらない場合は、cargo.tomlのopt-levelを見直してください。0にすれば止まるようになるかと思います。

# ついでなのでUARTも試しておきましょう

```rust:main.rs
#![no_std]
#![no_main]

use bsp::entry;
use bsp::hal::fugit::RateExtU32;
use defmt::*;
use defmt_rtt as _;
use embedded_hal::digital::OutputPin;
use panic_probe as _;
use rp_pico as bsp;

use bsp::hal::{
    clocks::{init_clocks_and_plls, Clock},
    pac,
    sio::Sio,
    uart::{DataBits, StopBits, UartConfig},
    watchdog::Watchdog,
};

#[entry]
fn main() -> ! {
    info!("Program start");
    let mut pac = pac::Peripherals::take().unwrap();
    let core = pac::CorePeripherals::take().unwrap();
    let mut watchdog = Watchdog::new(pac.WATCHDOG);
    let sio = Sio::new(pac.SIO);

    let external_xtal_freq_hz = 12_000_000u32;
    let clocks = init_clocks_and_plls(
        external_xtal_freq_hz,
        pac.XOSC,
        pac.CLOCKS,
        pac.PLL_SYS,
        pac.PLL_USB,
        &mut pac.RESETS,
        &mut watchdog,
    )
    .ok()
    .unwrap();

    let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());

    let pins = bsp::Pins::new(
        pac.IO_BANK0,
        pac.PADS_BANK0,
        sio.gpio_bank0,
        &mut pac.RESETS,
    );
    let mut led_pin = pins.gpio15.into_push_pull_output();
    let uart_pins = (pins.gpio0.into_function(), pins.gpio1.into_function());
    let uart = bsp::hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS)
        .enable(
            UartConfig::new(9600.Hz(), DataBits::Eight, None, StopBits::One),
            clocks.peripheral_clock.freq(),
        )
        .unwrap();

    uart.write_full_blocking(b"Hello World!\n");

    loop {
        info!("on!");
        led_pin.set_high().unwrap();
        delay.delay_ms(500);
        info!("off!");
        led_pin.set_low().unwrap();
        delay.delay_ms(500);
    }
}
```
HALに便利なAPIが既にあるので、ピンを設定して、コンフィグレーションを指定して、そこに書き込むだけです。

UARTを9600Hzで設定したので、Visual Studio Codeのシリアルモニターをその様に設定して監視してF5でプログラムを実行します。書き込みを指定したHello Worldが出力されれば成功です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58828/bd0a2672-9f59-2e09-cba6-bc71c81ed134.png)

簡単ですね。

# 2024/07/10 追記
probe-rsですが、templateからgenerateしたわけではない場合どうしたらよいのかというと、実際の設定は、project/.cargo/config.tomlに以下のように設定されます。

```toml:config.toml
runner = "probe-rs run --chip RP2040 --protocol swd"
```

設定ファイルは
 - projects/hoge/fuga/piyo/.cargo/config.toml 
 - projects/hoge/fuga/.cargo/config.toml
 - projects/hoge/.cargo/config.toml
 - projects/.cargo/config.toml
 - .cargo/config.toml
 - $HOME/.cargo/config.toml

と探索されていきます。pico向けにslintのデモとかを動かしたりカスタマイズしたりな人とかは

```toml:$HOME/.cargo/config.toml
[target.thumbv6m-none-eabi]
runner = "probe-rs run --chip RP2040 --protocol swd"
```
とかしておくと楽になるのかもしれませんね。


# まとめ
というわけで、備忘録がてらすごくざっくりVisual Studio CodeでDebug Probeを使ったデバックも可能なシリアル監視付き開発環境を構築しました。ま、こんなのでも残しておけば誰かの役には立つかもしれませんということで。

今後はつなげるデバイスを増やしつつ学習を続けていく予定です。