Raspberry Pi Pico と馴れ初め戯れた記録

- 限界開発鯖 Advent Calender 2023, 1 日目です
- 最近 embedded に触れ始めたのでその体験録というか, 早い話**駄文**です
- 知識をお求めの方は go back

## 怪文書

アキバに行って念願の Raspberry Pi Pico を手に入れた Nanai10a はまるで手頃なオモチャを手に入れたが如く (元から for hobby use です) Raspberry Pi Pico (俗称 "ラズピコ" / 本記事では "Pico") を Linux と Rust と CLI の力で弄ぶ… **べない！！！** (知識不足) **あああ何もわからない何もわからない！！！** という訳で "Rust の Embedded は良いゾ" なんて触れ回っていた私が実は**実機では初** (と言いつつ Arduino Uno にちょっと書き込もうとしたことはあるんですけどね！) となる Embedded Rust をちょっと体験したりラジバンダリ, 能動的な電子工作 (受動的な電子工作: 弊学の演習など) に身を浸し己の無力さを記し学びを忘れぬように *memorial...* な文章を遺す, そんなポエミー且つ完全無経験から Pico と戯れた, そんなアレコレを書き起こした記事がこちら, はーじまーるよー.

## アキバ (東京, 秋葉原) でしたこと

某日, 諸用事により東京を訪れていたので~~送料をケチる為~~実店舗で買い物がしたいなあと思っていた秋葉原の秋月電気通商 (俗称 "秋月") に行ってきました. 目的は電子工作を諸々する為の諸々を買うこと！ (計画性は皆無です) …まあ, 何事もなく ¥5k くらい飛ばしてきました. 多趣味はつらい (金銭的に).

ここでちょっと注意なのですが, '23/10 月末当時 Pico は店頭に**配置されてない**商品なので注意しましょう. 確か店頭の何処かに案内が書いてあったはずですが. 会計時に "Raspberry Pi Pico ください" って言わないと姿すら拝めません. ご注意を.

## 通過儀礼 blink をする

とりあえず手元には Pico と適当な PC (powered by Arch Linux), そして micro-USB Type-B のケーブルがあるので, まずはこのラズピコに灯りを灯す (あかりをともす) 所から始めるとしましょう. やっぱマイコンと言えばコレだよね *[単純な課題だから？]*.

`[PC: USB Type-A]` → `[Pico: micro-USB Type-B]` と繋いで, まずは… **これ給電されてるのか？** (※繋いでも何の応答も無い) 心配になってきた. まあきっと疎通してるやろ. 駄目だったらまた考えよう.

そして親切ご丁寧な記事を発見. 先っちょだけ読みます (熟読してなくてごめんなさい).

https://qiita.com/ochaochaocha3/items/1969d76debd6d3b42269

rustup に target として `thumbv6m-none-eabi` の導入, cargo-install で flip-link ([crates.io](https://crates.io/crates/flip-link]) と elf2uf2-rs ([crates.io](https://crates.io/crates/elf2uf2-rs)) を導入すると便利というか good な様子. そして… 有志でこんな template がある様子？

https://github.com/rp-rs/rp2040-project-template

ふむふむ. 前述のツールもここで紹介されてますね. あ, しかも公式の sdk がここに用意されてるんだ.

https://github.com/raspberrypi/pico-sdk

でこれが examples ね. 環境を構築するの面倒だし, 取り敢えずこれ動かすか…

https://github.com/raspberrypi/pico-examples

```:/bin/zsh
> git clone https://github.com/raspberrypi/pico-sdk
> cd pico-sdk
> git submodule update --init
> cd ..

> git clone https://github.com/raspberrypi/pico-examples
> cd pico-examples
> cmake . -DPICO_SDK_PATH=$PWD/../pico-sdk
```

pico-sdk の submodules を落とすのは結構時間掛かった (**8**min).
さてさて, cmake の結果ですが:

```:/bin/zsh
ICO_SDK_PATH is /home/nanai/Workspace/rbpp/pico-sdk
Defaulting PICO_PLATFORM to rp2040 since not specified.
Defaulting PICO platform compiler to pico_arm_gcc since not specified.
-- Defaulting build type to 'Release' since not specified.
PICO compiler is pico_arm_gcc
CMake Error at /home/nanai/Workspace/rbpp/pico-sdk/cmake/preload/toolchains/find_compiler.cmake:28 (message):
  Compiler 'arm-none-eabi-gcc' not found, you can specify search path with
  "PICO_TOOLCHAIN_PATH".
Call Stack (most recent call first):
  /home/nanai/Workspace/rbpp/pico-sdk/cmake/preload/toolchains/pico_arm_gcc.cmake:20 (pico_find_compiler)
  /usr/share/cmake/Modules/CMakeDetermineSystem.cmake:148 (include)
  CMakeLists.txt:8 (project)


CMake Error: CMake was unable to find a build program corresponding to "Unix Makefiles".  CMAKE_MAKE_PROGRAM is not set.  You probably need to select a different build tool.
CMake Error: CMAKE_C_COMPILER not set, after EnableLanguage
CMake Error: CMAKE_CXX_COMPILER not set, after EnableLanguage
CMake Error: CMAKE_ASM_COMPILER not set, after EnableLanguage
-- Configuring incomplete, errors occurred!
```

ああはいはい, toolchain が入ってませんでしたね. `yay arm-none-eabi` (`-Y`) で 検索したら色々ヒットしたので, それっぽいのを導入 (pacman):

```:/bin/zsh
Package (3)                   New Version       Net Change   Download Size

extra/arm-none-eabi-binutils  2.41-1              19.10 MiB       3.65 MiB
extra/arm-none-eabi-gcc       13.2.0-2          1717.58 MiB     218.25 MiB
extra/arm-none-eabi-newlib    4.3.0.20230120-1   242.21 MiB      17.60 MiB

Total Download Size:    239.51 MiB
Total Installed Size:  1978.90 MiB
```

うわ, **でっっっか**. 下り >5Mbps の我が家にはちとキツいなあ. のんびり待とう.
ちなみに `extra/armnone-eabi-gdb` は何故か入ってました. 何故？

導入が終わったので再挑戦. 特に言うことも無く, 正常終了.
次は make します. 何も考えずに pico-examples 直下で `make -j8` (私の CPU Threads は **4** です！ｗ).

どうでもいいけど, warning が出ない大規模 build って気持ちが良いよね. AUR の `-git` package とか build してると warning は呼吸だから (？) ちょっと落ち着かないんだよね. 閑話休題.

built が終わり, 各 example の directory には *なんか色々* な物が散乱している様子:

```:/bin/zsh
> lsd -l blink
.rwxr-xr-x nanai nanai 8.5 KB Sun Nov  5 13:16:41 2023  blink.bin
.rw-r--r-- nanai nanai 484 B  Sun Nov  5 13:00:41 2023  blink.c
.rw-r--r-- nanai nanai 154 KB Sun Nov  5 13:16:41 2023  blink.dis
.rwxr-xr-x nanai nanai  41 KB Sun Nov  5 13:16:41 2023  blink.elf
.rw-r--r-- nanai nanai 219 KB Sun Nov  5 13:16:41 2023  blink.elf.map
.rw-r--r-- nanai nanai  24 KB Sun Nov  5 13:16:41 2023  blink.hex
.rw-r--r-- nanai nanai  18 KB Sun Nov  5 13:16:41 2023  blink.uf2
.rw-r--r-- nanai nanai 1.1 KB Sun Nov  5 13:16:00 2023  cmake_install.cmake
drwxr-xr-x nanai nanai 4.0 KB Sun Nov  5 13:16:37 2023  CMakeFiles
.rw-r--r-- nanai nanai 245 B  Sun Nov  5 13:00:41 2023  CMakeLists.txt
drwxr-xr-x nanai nanai 4.0 KB Sun Nov  5 13:15:55 2023  elf2uf2
.rw-r--r-- nanai nanai  87 KB Sun Nov  5 13:16:00 2023  Makefile
```

入用なのは `blink.uf2` のはずなので, コイツをどうにかしてラズピコに書き込みましょう.
何も考えずに Thunar を開くとそこには珍しく外部ストレージの項が見えましたが, CLI 狂信者なので無視 (なんで？).

![我が Thunar は基本的に何も認識しないことで有名, Thunar の所為ではないかもだけど…](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/623040/13a04ab8-9eeb-2dfe-6c06-15ea04d05e1a.png)

多分きっと恐らく Thunar が余計なことをしたのでラズピコを再接続 (ケーブルを外して, 繋ぎ直す).
調べるの面倒なので… ええいままよ, [Phind](https://www.phind.com/) に訊いた (ref: [cached](https://www.phind.com/search?cache=rj92tycbvqw8dmtoxlmfuhcy)).

`lsblk` 見たら弊環境では `/dev/sdc1` として認識されている模様:

```:/bin/zsh
> lsblk
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
sda      8:0    0 931.5G  0 disk
├─sda1   8:1    0   511M  0 part
├─sda2   8:2    0  32.5G  0 part [SWAP]
├─sda3   8:3    0   317G  0 part /
├─sda4   8:4    0   300G  0 part /share
├─sda5   8:5    0   512M  0 part /boot
├─sda6   8:6    0   512M  0 part
└─sda7   8:7    0 280.5G  0 part
sdb      8:16   0 238.5G  0 disk
├─sdb1   8:17   0   260M  0 part
├─sdb2   8:18   0    16M  0 part
├─sdb3   8:19   0 237.2G  0 part
└─sdb4   8:20   0   990M  0 part
sdc      8:32   1   128M  0 disk
└─sdc1   8:33   1   128M  0 part
sr0     11:0    1  1024M  0 rom
```

無心 mount して, 突っ込もう:

```:/bin/zsh
# 弊環境では `/mnt` 直下に `mount{0,1,...}` と sub-directory を設けて利用するようにしています
> sudo mount /dev/sdc1 /mnt/mount0
> sudo cp blink/blink.uf2 /mnt/mount0
```

何事もなく実行が終わり, そわそわしているうちに我がラズピコが光りだした. 文明の灯火 (電気) だ…！！！

![VID20231105133455.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/623040/c4b053b6-c978-f256-30bb-5dc2731e066d.gif)


なんとなく作業ルーチンは把握出来た気がする. ヨシ.

## 錆びたる blink で勝利する

さて, 先に実践した blink は公式 sdk と C-lang で書かれたものでした.
**ならぬ**. Rust でやらなければ気が済まぬ. という訳で先に挙げた文献を見つつ, Rust で blink を組んでみよう.

sdk と examples を clone した同階層にて作業開始. テンプレ.

```:/bin/zsh
> cargo new --bin blink
> cd blink

# まだやってなかった
> rustup target add thumbv6m-none-eabi
> cargo add cortex-m cortex-m-rt embedded-hal -F embedded-hal/unproven defmt defmt-rtt panic-probe -F panic-probe/print-defmt rp-pico
```

さて色々書いていこう. といって前述の template をパクってるだけですが.

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

use panic_probe as _;
use defmt_rtt as _;

#[rp_pico::entry]
fn main() -> ! {
    use embedded_hal::digital::v2::OutputPin;
    use rp_pico::hal::clocks::Clock;

    let mut pp = rp_pico::hal::pac::Peripherals::take().unwrap();
    let core = rp_pico::hal::pac::CorePeripherals::take().unwrap();
    let sio = rp_pico::hal::sio::Sio::new(pp.SIO);

    let pins = rp_pico::Pins::new(pp.IO_BANK0, pp.PADS_BANK0, sio.gpio_bank0, &mut pp.RESETS);
    let mut led = pins.led.into_push_pull_output();

    let mut watchdog = rp_pico::hal::watchdog::Watchdog::new(pp.WATCHDOG);
    let clocks = rp_pico::hal::clocks::init_clocks_and_plls(
        12_000_000,
        pp.XOSC,
        pp.CLOCKS,
        pp.PLL_SYS,
        pp.PLL_USB,
        &mut pp.RESETS,
        &mut watchdog,
    )
    .ok()
    .unwrap();

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

    loop {
        led.set_high().unwrap();
        delay.delay_ms(500);

        led.set_low().unwrap();
        delay.delay_ms(500);
    }
}
```

```rust:build.rs
use std::env;
use std::error::Error;
use std::fs::File;
use std::io::Write;
use std::path::Path;

fn main() -> Result<(), Box<dyn Error>> {
    let out_dir = env::var_os("OUT_DIR").unwrap();
    let out = Path::new(&out_dir);

    File::create(out.join("memory.x"))?.write_all(include_bytes!("memory.x"))?;

    println!("cargo:rustc-link-search={}", out.display());
    println!("cargo:rerun-if-changed=memory.x");

    Ok(())
}
```

```:memory.x
MEMORY {
    BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100
    FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100
    RAM   : ORIGIN = 0x20000000, LENGTH = 256K
}

EXTERN(BOOT2_FIRMWARE)

SECTIONS {
    /* ### Boot loader */
    .boot2 ORIGIN(BOOT2) :
    {
        KEEP(*(.boot2));
    } > BOOT2
} INSERT BEFORE .text;
```

```toml:.cargo/config.toml
[build]
target = "thumbv6m-none-eabi"

[target.thumbv6m-none-eabi]
runner = "elf2uf2-rs"

rustflags = [
  "-C", "linker=flip-link",

  "-C", "link-arg=--nmagic",

  "-C", "link-arg=-Tlink.x",
  "-C", "link-arg=-Tdefmt.x",
]
```

(↑ なんで syntax でキレられてるんだ？) 必要な tool を install し, いざ (鎌倉).

```:/bin/zsh
> cargo install elf2uf2-rs flip-link
> cargo run blink.uf2
> sudo cp blink.uf2 /mnt/mount0
```

…あれ！？**いつの間にかラズピコが disk として認識されなくなってる**. `/dev/sdc` が居ない.
助けて [Phind](https://www.phind.com) (ref: [cached](https://www.phind.com/search?cache=dwp3ecopmb8qoc1pmlacgs61)) ！ …ああ, **一度書き込むと一旦認識されなくなるのね**. 逆に書き込める状態のことを "BOOTSEL mode" と呼ぶらしい.
次書き込むには "BOOTSEL" を**押しながら PC と接続**すると BOOTSEL mode で接続できるんだ. 解決.

ラズピコを再 mount して, 再度書き込み…

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/623040/1e97c40c-95e5-87f1-8b0d-4aa27e711035.gif)

✨✨✨ **it works!** ✨✨✨

良いね.

## 言い訳

本当ならもっと Pico を使って色々やりたいことあったんですが, 別記事のデバイス周りで死ぬ程 (言い過ぎ) 苦労したのでそちらに作業時間が吸われ, さらに最近クソ忙しかった (当社比) のでただの日記になってしまいました. そんな事があったんだな良かったねーくらいに思って下さい (お目汚し失礼しました). **あ, あと予約投稿時に下書きのままですみませんでした**. 動画付けるのに手間取りました…

では. Happy embedded hacking ;)