ラズパイPico2でFreeRTOSを使用する

## はじめに
ラズパイ財団が提供するマイコンボード Raspberry Pi Pico2（以下、ラズパイPico2）が提供され、電子工作などで利用されつつあります。
電子工作の初期段階や簡易な製作品であれば Arduinoに代表される初期化関数（**setup()**）とメインループ処理（**loop()**）があれば制御プログラムを動かすのに十分ですが、複雑な処理や複数機能を同時に動作させようと思った場合にプログラムの作りが難しくなります。
このようなときにはリアルタイムOS（以下、**RTOS**）を導入することでタスクという制御処理のかたまりを複数同時に（正確には『あたかも同時に』）動かすこと可能になります。
ラズパイPico2では標準でRTOSが組み込まれているわけではありませんが、ラズパイ財団が提供する以下のドキュメントをみると**FreeRTOS**の組み込み方が記されています。

・**Raspberry Pi Pico-series C/C++ SDK**
　https://datasheets.raspberrypi.com/pico/raspberry-pi-pico-c-sdk.pdf

このドキュメントを参考にラズパイ上の**ubuntu**を開発環境として使ってラズパイPico2で**FreeRTOS**を組み込むための手順を記します。
また、ラズパイ財団から提供されている pico-examplesを使って実際にFreeRTOSを使ったサンプルプログラムの使い方を記します。

なお、今回の手順は**ラズパイPico2**だけでなく、**ラズパイPico**でも利用できます。
操作手順中の環境変数の設定内容を変えるだけでラズパイPicoで利用できます。

※ドキュメントやSDKの更新でこのページの記述や操作と異なる場合があります。
　この記事の執筆が遅れたことで「Raspberry Pi Pico-series C/C++ SDK」の対象箇所の内容が変わってた・・・


## 環境構築
ビルドマシンはラズパイ５を使い、シリアルコンソールや ssh 経由で接続して使用する想定で説明します。
ラズパイ５での操作結果をもとに記していますが、ラズパイ４、ラズパイ３でも同様の操作で構築可能と思います。

### Linuxのインストール
以下のダウンロードページの「**Ubuntu 24.04.2 LTS**」:「**Download Ubuntu Server 24.04.2 LTS**」を選択してダウンロードします。

　https://ubuntu.com/download/raspberry-pi
　「Ubuntu 24.04.2 LTS」
　　　->　「Download Ubuntu Server 24.04.2 LTS」

![ubuntu_download.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370759/d56f00ea-e008-4339-a535-871e8de1287f.jpeg)

xz形式のファイル **ubuntu-24.04.2-preinstalled-server-arm64+raspi.img.xz** がダウンロードできるはずなので、それを**imgファイル**（**ubuntu-24.04.2-preinstalled-server-arm64+raspi.img**）へ展開します。
展開したファイルをイメージ書き込みツールなどを使い SDカードへ書き込みます。
書き込みが終わったら SDカード上の**config.txt**ファイル を編集し、以下の定義を追加してラズパイでシリアルコンソールが使えるようにします。

```
dtparam=uart0_console
enable_uart=1
```

修正が終わったら SDカードをラズパイへ差し込み、ラズパイを起動すれば **ubuntu**が起動します。


### ubuntuの設定
コンソールからubuntuへログインし、Linux操作に必要なログインパスワード、ネットワーク、エディタ、タイムゾーンなどを利用者の環境に併せて設定します。
とくにネットワークの設定はインターネットからパッケージを取得するのに必要です。


### パッケージのインストール
ビルドやLinuxの操作に必要なツールチェーンなどのパッケージをインストールします。
ビルドに必要なパッケージは以下に記したドキュメント「Getting started with Raspberry Pi Pico」の「2.2. Install the Toolchain」に記されているのでインストールします。

・**Getting started with Raspberry Pi Pico**　（英語）
　https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf

・**Raspberry Pi Picoをセットアップしよう**　（日本語）
　https://datasheets.raspberrypi.com/pico/getting-started-with-pico-JP.pdf


```
sudo apt update
sudo apt -y install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential
```

また、今後の操作や工作のために以下のパッケージも併せてインストールしています。

```
sudo apt install -y python3 git tar wget
sudo apt install -y libusb-1.0-0-dev net-tools
```

## 必要なソースファイルの取得
ラズパイPico2でFreeRTOSを組み込み、動作確認するのに必要な以下のソースファイル群の取得方法を記します。

・Pico2用FreeRTOSのソース
・pico-sdkのソース
・pico-examplesのソース

### Pico2用FreeRTOSのソース取得

以下のドキュメント「Raspberry Pi Pico-series C/C++ SDK」の「2.3.7. FreeRTOS Ports」内の「FreeRTOS-Kernel repository」に
設定されているリンク先（https://github.com/raspberrypi/FreeRTOS-Kernel）からラズパイPico2用のFreeRTOSのソースを取得します。

・**Raspberry Pi Pico-series C/C++ SDK**
　https://datasheets.raspberrypi.com/pico/raspberry-pi-pico-c-sdk.pdf

実際の取得操作は以下のようにgitコマンドで取得します。
この例では**freertos-test**というディレクトリを作成し、このディレクトリ配下で以後の作業を行う想定で記しています。

```
mkdir freertos-test
cd freertos-test
git clone https://github.com/raspberrypi/FreeRTOS-Kernel.git
```

### pico-sdkのソース取得
ラズパイPico2のGPIOな周辺機能を使うのに必要な**pico-sdk**のソースを取得します。

```
git clone https://github.com/raspberrypi/pico-sdk
```

### pico-examplesのソース取得
ラズパイPico2のGPIOな周辺機能を使うのに必要な**pico-sdk**のソースを取得します。

```
git clone https://github.com/raspberrypi/pico-examples
```

必要なソース取得は以上です。
３つのソースファイル群を取得すると以下のようなディレクトリ構成になります。

```
ubuntu@ubuntu:~/freertos-test$ ll
total 20
drwxrwxr-x  5 ubuntu ubuntu 4096 Apr  2 00:44 ./
drwxr-x--- 44 ubuntu ubuntu 4096 Apr  2 00:43 ../
drwxrwxr-x  7 ubuntu ubuntu 4096 Apr  2 00:43 FreeRTOS-Kernel/
drwxrwxr-x 38 ubuntu ubuntu 4096 Apr  2 00:44 pico-examples/
drwxrwxr-x 12 ubuntu ubuntu 4096 Apr  2 00:44 pico-sdk/
```

## プログラムのビルド
**pico-examples**に含まれる**freeRTOS**用のサンプルソースを利用してFreeRTOSを組み込んでプログラムをビルドしていきます。
FreeRTOS用のサンプルソースは**pico-examples**の以下のディレクトリ内にあります。

```
|--pico-examples
|  |--CMakeLists.txt
|  |--freertos
|  |  |--CMakeLists.txt
|  |  |--FreeRTOSConfig.h
|  |  |--FreeRTOSConfig_examples_common.h
|  |  |--FreeRTOS_Kernel_import.cmake
|  |  |--hello_freertos
|  |  |  |--CMakeLists.txt
|  |  |  |--hello_freertos.c
```

（１）ビルド用のディレクトリを作成して移動します

```
mkdir build
cd build
```

（２）pico-examplesのビルド対象を減らす
pico-examplesのビルド対象のディレクトリは **pico-examples/CMakeLists.txt** に記述され、このままだとpico-examples全体をビルドするための cmakeが実行されてしまいます。
FreeRTOS用の実行ファイルそのものに影響はありませんが、cmakeの実行時間やbuildディレクトリ配下のファイルが不用意に大きくなってしまうため、必要なディレクトリのみを対象とするよう**pico-examples/CMakeLists.txt**ファイルを修正します。
**pico-examples/CMakeLists.txt** ファイルの最後にある **add_subdirectory()** の設定を **add_subdirectory(freertos)** だけにします。

修正前：

```
 :
 :
# Add blink example
add_subdirectory_exclude_platforms(blink)
add_subdirectory_exclude_platforms(blink_simple)

# Add hello world example
add_subdirectory_exclude_platforms(hello_world)

 :
 :
# Hardware-specific examples in subdirectories:
add_subdirectory(adc)
add_subdirectory(binary_info)
add_subdirectory(bootloaders)
add_subdirectory(clocks)
add_subdirectory(cmake)
add_subdirectory(dcp)
add_subdirectory(divider)
add_subdirectory(dma)
add_subdirectory(flash)
add_subdirectory(gpio)
add_subdirectory(hstx)
add_subdirectory(i2c)
add_subdirectory(interp)
add_subdirectory(multicore)
add_subdirectory(otp)
add_subdirectory(picoboard)
add_subdirectory(pico_w)
add_subdirectory(pio)
add_subdirectory(pwm)
add_subdirectory(reset)
add_subdirectory(rtc)
add_subdirectory(spi)
add_subdirectory(system)
add_subdirectory(timer)
add_subdirectory(uart)
add_subdirectory(universal)
add_subdirectory(usb)
add_subdirectory(watchdog)
add_subdirectory(sha)
add_subdirectory(freertos)
```

↓

修正後：

```
 :
 :
# Add blink example

# Add hello world example

 :
 :
# Hardware-specific examples in subdirectories:
add_subdirectory(freertos)
```

（３）pico-sdkディレクトリのパスを環境変数へ設定し、cmakeでMakeファイルを作成します

```
export PICO_SDK_PATH=../pico-sdk
cmake -DPICO_PLATFORM=rp2350 -DPICO_BOARD=pico2 -DFREERTOS_KERNEL_PATH=../FreeRTOS-Kernel ../pico-examples
```

環境変数やマクロの意味は以下の通りです。

| 環境変数名 | 設定値 | 意味 |
|:------|:------|:------|
| PICO_SDK_PATH | ../pico-sdk | pico-sdkディレクトリのパス |
| PICO_PLATFORM | rp2350 | CPU種別 |
| PICO_BOARD | pico2 | ボード種別 |
| FREERTOS_KERNEL_PATH | ../FreeRTOS-Kernel | FreeRTOSディレクトリへのパス |

cmakeが成功すると以下のようなディレクトリ構成になります。

```
ubuntu@ubuntu:~/freertos-test/build$ ll
total 72
drwxrwxr-x 7 ubuntu ubuntu  4096 Apr  2 01:34 ./
drwxrwxr-x 6 ubuntu ubuntu  4096 Apr  2 01:14 ../
-rw-rw-r-- 1 ubuntu ubuntu 28238 Apr  2 01:34 CMakeCache.txt
drwxrwxr-x 5 ubuntu ubuntu  4096 Apr  2 01:34 CMakeFiles/
-rw-rw-r-- 1 ubuntu ubuntu  8049 Apr  2 01:34 Makefile
drwxrwxr-x 5 ubuntu ubuntu  4096 Apr  2 01:34 _deps/
-rw-rw-r-- 1 ubuntu ubuntu  1770 Apr  2 01:34 cmake_install.cmake
drwxrwxr-x 5 ubuntu ubuntu  4096 Apr  2 01:34 freertos/
drwxrwxr-x 3 ubuntu ubuntu  4096 Apr  2 01:34 generated/
drwxrwxr-x 6 ubuntu ubuntu  4096 Apr  2 01:34 pico-sdk/
-rw-rw-r-- 1 ubuntu ubuntu    60 Apr  2 01:34 pico_flash_region.ld
```

**ラズパイpico**の場合には以下の設定になります。

| 環境変数名 | 設定値 | 意味 |
|:------|:------|:------|
| PICO_SDK_PATH | ../pico-sdk | pico-sdkディレクトリのパス |
| PICO_PLATFORM | rp2040 | CPU種別 |
| PICO_BOARD | pico | ボード種別 |
| FREERTOS_KERNEL_PATH | ../FreeRTOS-Kernel | FreeRTOSディレクトリへのパス |

```
export PICO_SDK_PATH=../pico-sdk
cmake -DPICO_PLATFORM=rp2040 -DPICO_BOARD=pico -DFREERTOS_KERNEL_PATH=../FreeRTOS-Kernel ../pico-examples
```

ただし**ラズパイPico**の場合には **PICO_PLATFORM** と **PICO_BOARD** の設定は省略し、以下の操作でも可能です。

```
export PICO_SDK_PATH=../pico-sdk
cmake -DFREERTOS_KERNEL_PATH=../FreeRTOS-Kernel ../pico-examples
```

（４）ビルド（ make ）を実行します

```
make -j4
```

makeが成功すると**freertos/hello_freertos**ディレクトリ配下に実行ファイルが生成されます。

```
ubuntu@ubuntu:~/freertos-test/build$ ll freertos/hello_freertos/
total 4000
drwxrwxr-x 3 ubuntu ubuntu   4096 Apr  2 01:35 ./
drwxrwxr-x 5 ubuntu ubuntu   4096 Apr  2 01:34 ../
drwxrwxr-x 4 ubuntu ubuntu   4096 Apr  2 01:34 CMakeFiles/
-rw-rw-r-- 1 ubuntu ubuntu 202879 Apr  2 01:34 Makefile
-rw-rw-r-- 1 ubuntu ubuntu   1159 Apr  2 01:34 cmake_install.cmake
-rwxrwxr-x 1 ubuntu ubuntu  31168 Apr  2 01:35 hello_freertos1.bin*
-rw-rw-r-- 1 ubuntu ubuntu 504888 Apr  2 01:35 hello_freertos1.dis
-rwxrwxr-x 1 ubuntu ubuntu 661548 Apr  2 01:35 hello_freertos1.elf*
-rw-rw-r-- 1 ubuntu ubuntu 479196 Apr  2 01:35 hello_freertos1.elf.map
-rw-rw-r-- 1 ubuntu ubuntu  87729 Apr  2 01:35 hello_freertos1.hex
-rw-rw-r-- 1 ubuntu ubuntu  62976 Apr  2 01:35 hello_freertos1.uf2
-rwxrwxr-x 1 ubuntu ubuntu  36144 Apr  2 01:35 hello_freertos2.bin*
-rw-rw-r-- 1 ubuntu ubuntu 588930 Apr  2 01:35 hello_freertos2.dis
-rwxrwxr-x 1 ubuntu ubuntu 742384 Apr  2 01:35 hello_freertos2.elf*
-rw-rw-r-- 1 ubuntu ubuntu 483975 Apr  2 01:35 hello_freertos2.elf.map
-rw-rw-r-- 1 ubuntu ubuntu 101737 Apr  2 01:35 hello_freertos2.hex
-rw-rw-r-- 1 ubuntu ubuntu  73216 Apr  2 01:35 hello_freertos2.uf2
```

・hello_freertos1.uf2
　→configNUMBER_OF_CORES=1 の定義が含まれシングルコア用の実行ファイル

・hello_freertos2.uf2
　→configNUMBER_OF_CORES=1 の定義が含まれずマルチコア用の実行ファイル

※ pico-examples/freertos/hello_freertos/CMakeLists.txt を見ると**hello_freertos1**の設定には**configNUMBER_OF_CORES=1**の定義が含まれ、**hello_freertos2**の設定には**configNUMBER_OF_CORES**の定義が含まれていないのでシングルコア用かマルチコア用かが分かります。


## プログラムの実行

hello_freertos1.uf2、hello_freertos2.uf2 いずれかの実行ファイルをラズパイPico2へ書き込むとFreeRTOSを含んだサンプルプログラムが実行されます。

### ラズパイPico2のUART出力の準備
pico-examplesに含まれるFreeRTOSのサンプルプログラムでは、実行結果をUARTへ出力するようになっています。
このため、ラズパイPico2の以下のピンへUSB-シリアル変換ボードを使ってPCなどへ結果を出力できるようにします。

| ピン番号 | USB-シリアル変換側 | 意味 |
|:------|:------|:------|
| GP0 | Rx | Pico2から見て Tx出力 |
| GP1 | Tx | Pico2から見て Rx入力 |
| GND | GND | GND端子 |

なおラズパイPico2のピン設定は以下のファイルで定義されています。
使用するピンを変更したい場合はcmakeのコマンドラインなどへマクロ定義を追加することで変更可能と思います。

``` ~/freertos-test/pico-sdk/src/boards/include/boards/pico.h
// --- UART ---
#ifndef PICO_DEFAULT_UART
#define PICO_DEFAULT_UART 0
#endif
#ifndef PICO_DEFAULT_UART_TX_PIN
#define PICO_DEFAULT_UART_TX_PIN 0
#endif
#ifndef PICO_DEFAULT_UART_RX_PIN
#define PICO_DEFAULT_UART_RX_PIN 1
#endif
```

### プログラムの実行

各実行ファイルを実行後のUART出力結果の抜粋は以下の通りです。

``` hello_freertos1.uf2
Starting FreeRTOS on core 0:
Hello from worker count=0
Hello from main task count=0
blink_task starts
Hello from main task count=1
Hello from main task count=2
Hello from main task count=3
Hello from worker count=1
Hello from main task count=4
Hello from main task count=5
Hello from main task count=6
Hello from worker count=2
```

``` hello_freertos2.uf2
Starting FreeRTOS SMP on both cores:
Hello from worker count=0
worker is on core 0
main task is on core 0
Hello from main task count=0
blink_task starts
blink task is on core 1
Hello from main task count=1
Hello from main task count=2
Hello from main task count=3
Hello from worker count=1
blink task is on core 0
Hello from main task count=4
blink task is on core 1
Hello from main task count=5
Hello from main task count=6
Hello from worker count=2
```

## FreeRTOSのコンフィギュレーション
FreeRTOSの各機能のリソース数などのコンフィギュレーション定義は以下のファイルに記されています。

```
~/freertos-test/pico-examples/freertos/FreeRTOSConfig_examples_common.h
```

自身のアプリケーションを作成し実行する場合、コンフィギュレーションファイルにも注意した方がよいと思います。
（特に configTOTAL_HEAP_SIZE ）

``` ~/freertos-test/pico-examples/freertos/FreeRTOSConfig_examples_common.h 抜粋
/* Scheduler Related */
#define configUSE_PREEMPTION                    1
#define configUSE_TICKLESS_IDLE                 0
#define configUSE_IDLE_HOOK                     0
#define configUSE_TICK_HOOK                     0
#define configTICK_RATE_HZ                      ( ( TickType_t ) 1000 )
#define configMAX_PRIORITIES                    32
#define configMINIMAL_STACK_SIZE                ( configSTACK_DEPTH_TYPE ) 512
#define configUSE_16_BIT_TICKS                  0

#define configIDLE_SHOULD_YIELD                 1

/* Synchronization Related */
#define configUSE_MUTEXES                       1
#define configUSE_RECURSIVE_MUTEXES             1
#define configUSE_APPLICATION_TASK_TAG          0
#define configUSE_COUNTING_SEMAPHORES           1
#define configQUEUE_REGISTRY_SIZE               8
#define configUSE_QUEUE_SETS                    1
#define configUSE_TIME_SLICING                  1
#define configUSE_NEWLIB_REENTRANT              0
// todo need this for lwip FreeRTOS sys_arch to compile
#define configENABLE_BACKWARD_COMPATIBILITY     1
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5

/* System */
#define configSTACK_DEPTH_TYPE                  uint32_t
#define configMESSAGE_BUFFER_LENGTH_TYPE        size_t

/* Memory allocation related definitions. */
#define configSUPPORT_STATIC_ALLOCATION         0
#define configSUPPORT_DYNAMIC_ALLOCATION        1
#define configTOTAL_HEAP_SIZE                   (128*1024)
#define configAPPLICATION_ALLOCATED_HEAP        0
```

## 最後に
ラズパイ財団が提供するドキュメント「**Raspberry Pi Pico-series C/C++ SDK**」に記されている内容であったり、**pico-examples**に含まれるサンプルプログラムなので既に知られている内容かもしれませんが、初期のドキュメントやpico-examplesには含まれていなかった内容なので気が付いていない人がいるのではと思い執筆しました。

趣味の電子工作にRTOSまで必要なのか？ という意見もあるかもしれませんが、マルチタスクや時間管理などはボタンの検出や複数のLEDを点滅させる時にとても便利だと思っています。
一度試されてはどうでしょうか。

以上