👏

Raspberry Pi Pico WH + pico-sdkでLチカ

2024/08/18に公開

背景とかきっかけとか

2年くらい前にArduinoのキットを買って以来、プラレールを改造したりして遊んできたけど、最近n月刊ラムダノートとか技術書展のRubyではじめる電子工作~ラジコンを作ろう~を見てRaspberry Pi Pico + Picoruby + R2P2環境でも遊んでみた。


で、Picorubyをもう少しいじってみようかと思ったけど、Picoruby+R2P2はRaspberry Pi PicoのC/C++用開発環境であるpico-sdkを使っているようで、これについてもある程度知っておかないとダメそうな感じがしたので、まずはpico-sdkでLチカを目指すことにした。

環境とゴール

  • Raspberry Pi Pico WH
    • Raspberry Pi Picoには無印、H, W, WHの4種類がある。Wが付くのは無線モジュール付きで、pico-sdkを使う場合にも設定が無印と異なるところがある。Hが付くのはGPIOピンがあらかじめはんだ付けされているかどうかの違いなので、ソフトウェアを書く側としては違いはない。今回使うのはWHで、秋月電子で1340円。
  • Ubuntu-24.04 on WSL2 on Windows 11
    • シリアルを繋いだりするならネイティブのUbuntu環境が良さそうだけど、今回のLチカ程度ならUbuntu上でビルドしたバイナリをコピーするだけなのでとりあえずOK。

この環境でpico-sdkを使ったLチカができるようにする。LEDはPico WH内蔵のものと、これまた秋月電子で購入した抵抗内蔵LEDを使う。普通のLEDでもいいけど抵抗入れなくていいのは地味に楽なので。

おおまかな手順

手順はだいたいこんな感じ。

  1. pico-sdkのサンプルコードがまとめられたpico-examplesというリポジトリがあり、ここに内蔵LEDを光らせるサンプル (blink) もあるので、まずはこれをそのままビルドして実行する。
  2. blinkサンプルをベースに独自の内蔵LEDのLチカコードを書いてビルド・実行する。
  3. GPIOを使った外部LEDを光らせるコードを試す。

開発環境セットアップ

ディレクトリ構成

最初に使うのはpico-sdkとpico-examples。またpico-sdkを使うにはpicotoolというのも必要っぽいのでこれもインストールする。置き場やインストール先はどこでも良いが、ここではコードとビルドディレクトリは$HOME/pico以下、picotoolのインストール先は$HOME/dev-root/picotoolにする。

$HOME/
  +- pico/
  |    +- pico-sdk/
  |    +- pico-examples/
  |    +- picotool/
  |    +- build-picotool/      # cmakeが生成するので自分で作る必要はない
  |    +- build-pico-examples/ # 同上
  +- dev-root/     # picotoolインストール時に自動で作られるので自分で作る必要はない
       +- picotool/

ダウンロード

執筆時点ではgit cloneした状態でどれもv2.0.0になっていたが、常にそうとは限らなそうなのでバージョンを指定してcheckoutしておく。

$ cd $HOME
$ mkdir pico && cd pico
$ git clone https://github.com/raspberrypi/pico-sdk.git
$ git -C pico-sdk checkout 2.0.0
$ git clone https://github.com/raspberrypi/picotool.git
$ git -C picotool checkout 2.0.0
$ git clone https://github.com/raspberrypi/pico-examples.git
$ git -C pico-examples checkout sdk-2.0.0

pico-sdkはsubmoduleがあるのでこれもダウンロードする。

$ git -C pico-sdk submodule update --init --recursive

必須パッケージインストール

各リポジトリのREADMEに必要なaptパッケージが書かれているが、まとめると必要なのは以下になるのでこれらをすべてインストールする。

  • cmake
  • gcc-arm-none-eabi
  • libnewlib-arm-none-eabi
  • libstdc++-arm-none-eabi-newlib
  • pkg-config
  • libusb-dev

cmakeについて

これからcmakeを使ってpicotoolやpico-examplesのビルドはcmakeを使うが、cmakeをまったく知らないと何をやっているのかさっぱり分からなくなってしまうので、ここで簡単に説明しておく。

cmakeそれ自身はビルドツール(実際にコンパイラを呼び出したりするツール)ではなく、ビルドツール用の設定ファイルを出力するもので、メタビルドシステムなどと呼ばれたりする。cmakeを実行することでGNU Make用のMakefileやNinja用のbuild.ninja、Windows上ならVisual Studio用のソリューション・プロジェクトファイルを生成することができ、実際のビルドはMake, Ninja, MSBuildなどが行う。cmake自体の設定ファイルはCMakeLists.txt。

世の中の説明記事を見るとNinjaを指定するものもあるがここではMakeを使う。Ninjaの方が動作は高速なので大規模なプロジェクトの開発時(つまりコード変更・ビルドを頻繁に行う場合)はNinjaがお勧めだけど、小規模だったり一度のビルドならほぼ変わりない。

picotoolのビルドとインストール

公式のREADMEを参考に、以下のようなスクリプト(run-picotool-cmake.sh)を$HOME/pico以下に置いて実行する。build-picotool以下にMakefileなどが作られるのでそれを使ってビルド・インストールする。インストール先は$HOME/dev-rootを指定するとその中にpicotoolディレクトリが作られる。

run-picotool-cmake.sh
#!/bin/bash

export PICO_SDK_PATH="$HOME/pico/pico-sdk"
opts="$opts -DCMAKE_INSTALL_PREFIX=$HOME/dev-root"
opts="$opts -DPICOTOOL_FLAT_INSTALL=1"
build_dir="./build-picotool"

cmake -S picotool -B $build_dir $opts
$ cd $HOME/pico
$ ./run-picotool-cmake.sh
$ cd build-picotool
$ make -j6      # -jは並列実行の数。CPUコア数に合わせて調整する
$ make install

失敗してやり直すときはbuild-picotoolディレクトリを削除して再実行。

pico-examplesのビルド

pico-examplesもcmakeでビルドするので、またrun-pico-examples-cmake.sh$HOME/pico以下に置いてビルドする。

今回はPico WHを使うので、PICO_BOARDpico_wを指定している。またpicotoolのインストール先も指定する必要がある。WifiのSSID、PASSWORDも指定しているが、今回使うのはLチカのサンプル(blink) だけなので必要ないかもしれない。

run-pico-examples-cmake.sh
#!/bin/bash

export PICO_SDK_PATH="$HOME/pico/pico-sdk"
opts="$opts -DPICO_BOARD=pico_w"
opts="$opts -DWIFI_SSID=MyNetwork1"
opts="$opts -DWIFI_PASSWORD=MyPassword1"
opts="$opts -Dpicotool_DIR=$HOME/dev-root/picotool"
build_dir="./build-pico-examples"

cmake -S pico-examples -B $build_dir $opts
$ cd $HOME/pico
$ ./run-pico-examples-cmake.sh
$ cd build-pico-examples
$ make -j6  # 25%くらいのところでエラー
$ make      # 並列度1で再実行 => 85%くらいでエラー

ビルドしたら25%くらいのところでエラーが起きて止まったので-jを付けずにmake再実行。それでも85%くらいでまたエラーになったけど、build-pico-examples/blink/blink.uf2はできていたのでとりあえずヨシ。

blinkの実行

Pico WHのBOOTSELボタンを押しながらUSBケーブルを繋ぐとINDEX.HTMなどを含むフォルダが見えるようになるので、ここにbuild-pico-examples/blink/blink.uf2をドラッグ&ドロップでコピーすれば勝手に再起動して実行される。以下のように本体のLEDが点滅すればOK。

独自Lチカの作成 (内蔵LED)

今度はpico-examplesのblinkを参考に、独自のコードとビルド環境を作ろう。$HOME/pico以下にpico-testディレクトリを作り、そこに.cやCMakeLists.txtなどを置く。

$HOME/
  +- pico/
      +- pico-sdk/
      +- pico-examples/
      +- picotool/
      +- build-picotool/
      +- build-pico-examples/
      +- pico-test/  # 今回作るディレクトリ
          +- CMakeLists.txt
          +- my_blink.c
          +- pico_sdk_import.cmake
          +- run-cmake.sh
          +- build/  # cmakeが作るので自分で作る必要はない

my_blink.cはpico-examplesのblink/blink.cとほぼ同じだが、オリジナルはPico無印とW両方に対応するため#ifdefが使われている。今回はPico WHなのでそれ専用に簡易化してみた。

my_blink.c
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"

int main(){
    int rc = cyw43_arch_init();
    hard_assert(rc == PICO_OK);
    while (true) {
        cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, true);
        sleep_ms(1000);
        cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, false);
        sleep_ms(1000);
    }
}

PCMakeLists.txtはオリジナルでは各種設定がpico-examples直下のCMakeLists.txtblink/CMakeLists.txtに分散している。これを1つにまとめて書く。

pico-test/CMakeLists.txt
cmake_minimum_required(VERSION 3.12)

include(pico_sdk_import.cmake)

project(my_blink C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

pico_sdk_init()
add_executable(my_blink my_blink.c)
target_link_libraries(my_blink pico_stdlib)
target_link_libraries(my_blink pico_cyw43_arch_none)
pico_add_extra_outputs(my_blink)

run-cmake.shはpico-examples用とだいたい同じ。picotool, pico-examplesのときはビルドディレクトリをソースツリーとは別の場所にしたが (Out-of-sourceビルドと呼ばれる)、今回はソースツリー内にbuildディレクトリを作るようにした(In-sourceビルド)。

run-cmake.sh
#!/bin/bash

export PICO_SDK_PATH="$HOME/pico/pico-sdk"
opts="$opts -DPICO_BOARD=pico_w"
opts="$opts -Dpicotool_DIR=$HOME/dev-root/picotool"

cmake -S . -B build $opts

ビルドは以下のとおり。

$ cd $HOME/pico/pico-test
$ ./run-cmake.sh
$ cd build
$ make -j6

build/my_blink.utf2ができていれば成功。先ほどと同様にして実行する。内蔵LEDが1秒おきに点滅すればOK。見た目はさっきとほとんど変わりないので動画は省略。

外部LEDのLチカ

先ほどのmy_blink.cに手を加えて外部LEDを点滅させてみる。

Raspberry Pi Picoのピン配置は以下のようになっているので、ここではGP5にLEDを繋いでみる。

具体的には、USBポートを左に向けたとき、下側ピンの左から7番目(GP5)にLEDの足の長い方(アノード)、8番目(GND)に短い方(カソード)を繋げる。

my_blink.cは以下のようにする。他のファイルは変更なし。

my_blink.c
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"

int main(){
    gpio_init(5);
    gpio_set_dir(5, GPIO_OUT);
    int rc = cyw43_arch_init();
    hard_assert(rc == PICO_OK);
    while (true) {
        cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, true);
        gpio_put(5, true);
        sleep_ms(1000);
        cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, false);
        gpio_put(5, false);
        sleep_ms(1000);
    }
}

ビルドして実行すれば以下のように内蔵LEDと外部LEDが点滅する。

ということでpico-sdkの使い方がちょっとだけ分かった。

Discussion