まんま!の備忘録

ソフトウェア・ファームウェア・ハードウェア関連の備忘録

Zephyr RTOS のRaspberry Pi Pico(RP2040)のボードをカスタマイズ

Zephyr RTOSを使用して、Picoボードに追加のLED、可変抵抗を追加して、その状態をUSBシリアルにて通知するファームウェアを作ってみます。
これ最後に、精神的に落ち着くまでこの系統のブログ書くのやめます。
落ち着いたら再開するかもです。

前回こちらでサンプルの動作確認をしました。
taogya.hatenablog.com

ただ、これはオンボードLEDのエイリアス定義しかないので、以下のどれかで対応する必要があります。
1. ボード設定を修正する。
2. 新規ボードを作成する。
3. オーバーレイファイルを作成する。
1は、元ファイルをいじるのでなんか嫌です。
今後カスタムボード作成する予定なので、練習として新規ボードを作成したいと思います。
その上でオーバーレイする方法をまとめます。


こちらを参考に作成していきます。
docs.zephyrproject.org

ボードの作成

作成するファイル

参考URLを見ると必要なものは以下。(docは除外している)

zephyrproject/zephyr/boards//plank
├── board.yml
├── board.cmake
├── CMakeLists.txt
├── Kconfig.plank
├── Kconfig.defconfig
├── plank_defconfig
├── plank__defconfig
├── plank.dts
├── plank_.dts
└── plank.yaml

rpi_picoにあるのは以下。(doc、supportは除外している)

zephyrproject/zephyr/boards/raspberrypi/rpi_pico
├── board.yml
├── board.cmake
├── Kconfig.rpi_pico
├── Kconfig.defconfig
├── rpi_pico_defconfig
├── rpi_pico.dts
├── rpi_pico-common.dtsi
├── rpi_pico-pinctrl.dtsi
├── rpi_pico.yaml
├── rpi_pico_rp2040_w_defconfig
├── rpi_pico_rp2040_w.dts
└── rpi_pico_rp2040_w.yaml

supportにはOn Chip Debugger の設定ファイルが入っているように見えますが、デバッガー持っていないので除外です。

Pico Wではないので、rp2040_wは不要になります。
なので、各ファイルをコピーし、ファイル名を編集します。

zephyrproject/zephyr/boards/raspberrypi/rpi_pico_custom
├── board.yml
├── board.cmake
├── Kconfig.rpi_pico_custom
├── Kconfig.defconfig
├── rpi_pico_custom_defconfig
├── rpi_pico_custom.dts
├── rpi_pico_custom-common.dtsi
├── rpi_pico_custom-pinctrl.dtsi
└── rpi_pico_custom.yaml

次にファイルを修正していきます。

board.yml

ボード名、SoC、バリアントなどのボードの高レベルのメタデータを記述する YAML ファイル。
name を修正します。また、wはいらないのでvariants以下は消します。

board:
  name: rpi_pico_custom
  vendor: raspberrypi
  socs:
  - name: rp2040
    variants:
    - name: w

board.cmake

フラッシュおよびデバッグのサポートに使用されます。
修正不要。

Kconfig.rpi_pico_custom

SoC およびその他のボードと SoC 関連の設定を選択するための基本ソフトウェア構成。
BOARD_ 以降はボード名の大文字が入るようなので、以下箇所をそのように設定します。

:
config BOARD_RPI_PICO_CUSTOM
:

Kconfig.defconfig

構成システム (Kconfig)形式のソフトウェア構成 。
BOARD_ 以降はボード名の大文字が入るようなので、以下箇所をそのように設定します。

:
if BOARD_RPI_PICO_CUSTOM
:
endif # BOARD_RPI_PICO_CUSTOM

rpi_pico_custom_defconfig

Kconfig.conf 形式のソフトウェア構成。
今回はピコボードそのままなので、修正不要。
prj.conf でプロジェクトごとに変更することもできるようです。

rpi_pico_custom.dts

devicetree形式のハードウェアの説明。
以下箇所をボード名に合わせます。

:
#include "rpi_pico_custom-common.dtsi"
:

ここにカスタムボードに追加したLEDやスイッチなど、デフォルトで実装されている制御のエイリアスを追加したりします。
今回はPicoボードそのままで使って、それに追加パーツをつなげるので、追加パーツのエイリアスは、後ほどオーバーレイファイルで定義します。

rpi_pico_custom-common.dtsi

通常SoC(System on Chip)固有の説明。w モデルとの共通設定かと。
以下箇所をボード名に合わせます。

:
#include #include "rpi_pico_custom-pinctrl.dtsi"
:

rpi_pico_custom-pinctrl.dtsi

通常SoC(System on Chip)固有の説明。デフォルトのピン制御設定かな。
GPIO 22 にPWMを割り当てます。

:
	pwm_ch4b_default: pwm_ch4b_default {
		group1 {
 			pinmux = <PWM_4B_P25>,<PWM_3A_P22>;
		};
	};
:

rpi_pico_custom.yaml

テスト ランナー (Twister)によって使用されるさまざまなメタデータを含む YAML ファイル 。
以下箇所をボード名に合わせます。

identifier: rpi_pico_custom
:


ボード上のFlashとか変更入る場合は、そのほか変更するところはあると思いますが、一旦ボード作成は以上。
このボードでLチカできるか試してみます。Picoのボタンを押しながらPCに接続して、

$ cd ~/develop/zephyrproject
$ . ./.venv/bin/activate
$ cd zephyr
$ rm -rf build
$ west build -b rpi_pico_custom samples/basic/blinky
$ cp build/zephyr/zephyr.uf2 /Volumes/RPI-RP2

ソースコード

作成するファイル

適当なディレクトリに作成します。

src
├── prj.conf
├── rpi_pico_custom.overlay
├── main.cpp
└── CMakeLists.txt

今回作成するプログラム仕様は以下とする。

  • 可変抵抗の抵抗値(電圧値)でPWM のduty比を変更する。(0V -> 0%, 3.3V -> 100%)
  • PWMの更新は10ms周期に行う。この時に、今のLEDの状態(PWM duty比)をUSBシリアルにて通知する。

prj.conf

プロジェクト固有の設定。

CONFIG_GPIO=y
CONFIG_ADC=y
CONFIG_PWM=y

CONFIG_USB_DEVICE_STACK=y # Enables the USB Device Stack (required for USB Serial)
CONFIG_USB_DEVICE_PRODUCT="RPi Pico USB Serial Console" # Gives the USB Device a human-readable name (optional)

CONFIG_SERIAL=y # Enable options for serial drivers
CONFIG_CONSOLE=y # Enables a console for use with USB CDC applications
CONFIG_UART_CONSOLE=y # Sets the default for printk and printf to output to the UART serial console
CONFIG_UART_LINE_CTRL=y # This enables the API for apps to control the serial line, such as baud rate, CTS and RTS.

CONFIG_USB_DEVICE_VID=0xF055
CONFIG_USB_DEVICE_PID=0x1234

CONFIG_SYS_CLOCK_TICKS_PER_SEC=1000

rpi_pico_custom.overlay

オーバーレイファイル。
ここに追加パーツのエイリアスを定義します。エイリアスは以下の定義とする。

/ {
    chosen {
        zephyr,console = &cdc_acm_uart0;
    };
    zephyr,user {
        io-channels = <&adc 0>;
        io-channel-names = "VR0";
    };
	pwm_leds {
		compatible = "pwm-leds";
		status = "disabled";
		pwm_led1: pwm_led_1 {
			pwms = <&pwm 6 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
			label = "PWM_LED1";
		};
	};

	aliases {
		pwm-led1 = &pwm_led1;
	};
	
};

&zephyr_udc0 {
    cdc_acm_uart0: cdc_acm_uart0 {
        compatible = "zephyr,cdc-acm-uart";
        label = "CDC_ACM_0";
    };
};

&adc {
	#address-cells = <1>;
	#size-cells = <0>;

	channel@0 {
		reg = <0>;
		zephyr,gain = "ADC_GAIN_1";
		zephyr,reference = "ADC_REF_INTERNAL";
		zephyr,acquisition-time = ;
		zephyr,resolution = <12>;
	};
};

&pwm_led0 {
	status = "okay";
};

&pwm_led1 {
	status = "okay";
};

&pwm {
	status = "okay";
	divider-frac-4 = <15>;
	divider-int-4 = <255>;
	divider-frac-3 = <15>;
	divider-int-3 = <255>;
};

main.cpp

#include 
#include 
#include 
#include 
#include 
#include 
#include 

static const struct adc_dt_spec vr_dt = ADC_DT_SPEC_GET_BY_NAME(DT_PATH(zephyr_user), vr0);
static const struct pwm_dt_spec on_board_led = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led0));
static const struct pwm_dt_spec add_led = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led1));

void init_pwm()
{
	pwm_is_ready_dt(&on_board_led);
	pwm_is_ready_dt(&add_led);
}

void init_usb()
{
	const struct device *usb_device = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
	uint32_t dtr = 0;
	usb_enable(NULL);
	while (!dtr)
	{
		uart_line_ctrl_get(usb_device, UART_LINE_CTRL_DTR, &dtr);
		k_sleep(K_MSEC(100));
	}
	printk("USB ready\n");
}

void init_adc(void)
{
	adc_is_ready_dt(&vr_dt);
	adc_channel_setup_dt(&vr_dt);
	printk("ADC ready\n");
}

void update_adc_thread(void *p1, void *p2, void *p3)
{
	uint16_t buf;
	int32_t vref_mv = (int32_t)adc_ref_internal(vr_dt.dev);
	struct adc_sequence sequence = {
		.buffer = &buf,
		.buffer_size = sizeof(buf),
	};
	uint32_t max_period = PWM_MSEC(5U);
	int32_t pwm = 0;
	int32_t duty = 0;

	(void)adc_sequence_init_dt(&vr_dt, &sequence);

	while (1)
	{
		int32_t val_mv;
		adc_read_dt(&vr_dt, &sequence);
		val_mv = (int32_t)buf;
		adc_raw_to_millivolts_dt(&vr_dt, &val_mv);
		pwm = (val_mv * 100) / vref_mv;
		duty = max_period * pwm / 100;
		pwm_set_dt(&on_board_led, max_period, duty);
		pwm_set_dt(&add_led, max_period, duty);
		printk("%" PRId32 " %\n", pwm);

		k_sleep(K_MSEC(10));
	}
}

int main(void)
{
	init_usb();
	init_adc();
	init_pwm();
	update_adc_thread(NULL, NULL, NULL);

	return 0;
}

// K_THREAD_DEFINE(adc_tid, 50,
// 				update_adc_thread, NULL, NULL, NULL,
// 				5, 0, 0);

CMakeLists.txt

# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(sample_app)

target_sources(app PRIVATE main.cpp)

可変抵抗を回すとLEDの光の強さが変わります。
リポジトリ作成しました。
github.com

Raspberry Pi Pico(RP2040)でRTOSの簡単な比較

1秒周期にLED Aを点滅、10ミリ秒周期にLED Bを点滅した場合の実行率を比較。
実行回数はUSBシリアルにてログを出力してそれをカウント。

OSをつまない、割り込みだけのコード:LED Bは、98%程度実行(1秒間に98回程度実行)
FreeRTOSを使用したコード:LED Bは、100%程度実行(1秒間に100回実行)
ZephyrRTOSを使用したコード:LED Bは、100%程度実行(1秒間に100回実行)

LED Aはどれも100%でした。

サンプルコード
github.com

Raspberry Pi Pico(RP2040)でZephyr RTOSを動かす

RTOS(Real-time OS)である、Zephyr RTOSをPicoで動かしてみます。
www.zephyrproject.org

こちらを参考に進めます。
記載の手順と変えていて、環境は ~/develop/zephyrproject 配下に格納していきます。
docs.zephyrproject.org

Mac M1での作業なので、Mac OSの手順をメモしていきます。
Homebrewと依存パッケージは入っている前提で進めます。

環境の構築

Zephyr開発のワークスペースを作成します。

$ python -V
Python 3.12.3

$ python3 -m venv ~/develop/zephyrproject/.venv
$ cd ~/develop/zephyrproject
$ . ./.venv/bin/activate

Zephyrのインストール

エラー出る場合があるので、west コマンドに 「-v」 オプションを入れておきます。
2つ入れているのはverboseレベルを上げるためです。

$ pip install west
$ west -v -v init
$ west -v -v update

5GBくらいのデータをダウンロードするので、少し待ちます。

Zephyr CMake パッケージのエクスポート

これをすることで作成するアプリの CMakeLists.txt で 「find_packagefind_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})」とすると、Zephyr RTOSが使用できるようです。

$ west zephyr-export

依存関係のインストール

$ pip install -r zephyr/scripts/requirements.txt

SDKのインストール

$ curl -L -O https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.5-1/zephyr-sdk-0.16.5-1_macos-aarch64.tar.xz
$ curl -L https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.5-1/sha256.sum | shasum --check --ignore-missing
$ tar xvf zephyr-sdk-0.16.5-1_macos-aarch64.tar.xz
$ cd zephyr-sdk-0.16.5-1 
$ ./setup.sh
:
Install host tools [y/n]? y
Register Zephyr SDK CMake package [y/n]? y
:
$ cd ..

サンプルプロジェクトのビルド

ボード一覧を見てみます。

$ cd zephyr
$ west boards | sort
:
rpi_pico
:

LED点灯サンプルをビルドしてみます。
コマンドはこちら。
docs.zephyrproject.org

$ west build -b rpi_pico samples/basic/blinky

書き込み

「west flash」コマンドでもできるようですが、Picoprobeなどのデバッガーがないとできないので、ビルドしたファイルをコピーします。
Picoのボタンを押しながらPCに接続します。

$ cp build/zephyr/zephyr.uf2 /Volumes/RPI-RP2 

【Tkinter】Raspberry Pi Zero WH でGUIアプリ作成

Raspberry Pi Zero WH にLite OSを入れてCUI環境で使っていたのですが、GUIアプリも作りたくなり。。
PyQt とか Tkinter とかその他色々あるのですが、とりあえず標準ライブラリの Tkinter を使用してみました。
Python を使用して作れます。
PyQtは商用利用時にライセンス料金発生します。)


サンプルを以下に置いています。
github.com

Raspberry Pi Zero WH で NodeRED

ビジュアルプログラミングツールに NodeRED というものがあります。
それをRaspberry Pi Zero WH で 動かして見ました。

シングルコアなのでまともに動くか不安でしたがちゃんと動きますね。
そして、このツール面白いですね。


サンプルを以下に置いています。(NodeRED はDockerを使用しています)
github.com

Raspberry Pi Pico で加速度センサー MMA8451の動作確認

Raspberry Pi Picoで加速度センサー「MMA8451」の動作確認してみました。
コードは以下のArduinoフォルダに置いてます。
github.com


Processing*1使って、動き見るサンプルはProcessingフォルダに置いてます。
参考:qiita.com

*1:Processing公式サイト processing.org

Raspberry Pi Pico用LoRa 通信ライブラリ(E220-900T22S(JP))

LoRa通信をしたくて、E220-900T22S(JP)を友人におすすめされたので買ってみました。
https://dragon-torch.tech/cat-components/rf-modules/e220-900t22s-jp/dragon-torch.tech

Raspberry Pi Picoでまずは動作確認ということで、Arduinoでやりたかったのですが、
本家にはMicroPython用のライブラリしかないのです。

ということでESP32用のライブラリはあったので、Pico用に改造してみました。
github.com

ボードは以下を使用してください。
arduino-pico.readthedocs.io


動作確認はできたものの、今のままでは消費電流高いので、次はマイコン選びだなぁ。

Arduino (Mac)のRaspberry Pi Picoのピン定義 (pins_arduino.h)の場所

Mac BookにてArduino IDEを使ってRaspberry Pi Picoの実験中です。
Arduinoを使ったのはもう10年くらい前なので、ほぼほぼ初心者です。
インストーラは以下から持ってきました。(このブログ時点で、Arduino IDE 2.2.1)
www.arduino.cc

インストール作業は簡単なので手順は書きません。

Boardは「Arduino Mbed OS RP2040 Boards」を選択します。


そして、Raspberry Pi Picoで加速度センサー「Adafruit MMA8451」を繋ごうと思ったのだけど、PicoのI2Cどこにさせばいいのだ。。
調べると「pins_arduino.h」に書いてあるらしい。

Finderで検索かけて見た。
見つからず。。

もう一回調べた結果、自分の環境と合致するのはなかったが、以下にありました!

/Users/your_pc/Library/Arduino15/packages/arduino/hardware/mbed_rp2040/4.0.8/variants/RASPBERRY_PI_PICO/pins_arduino.h
// Wire
#define PIN_WIRE_SDA        (4u)
#define PIN_WIRE_SCL        (5u)

GP4/GP5にあるようですね。めでたしめでたし。
ピン配はこちら。
https://datasheets.raspberrypi.com/pico/pico-datasheet.pdf


最後に、、
この機能めちゃ便利ですね。感動です。
nn-hokuson.hatenablog.com

Raspberry Piのプッシュスイッチによるシャットダウン

Raspberry Piをプッシュスイッチでシャットダウンする方法を記載します。

こちら参考にしました。電源ONインジケータを設定する方法も記載されていますが、今回は割愛します。
External Button Shutdown for Raspberry Pi · GitHub
Raspberry Pi Zero WHの回路図
https://datasheets.raspberrypi.com/rpizero/raspberry-pi-zero-w-reduced-schematics.pdf


GPIO3, Low Active, Internal Pull-up, 1000ms押下でシャットダウンの設定としました。

taogya@taogya-pi0:~ $ sudo vi /boot/config.txt 
:
#dtparam=i2c_arm=on
:
[all]
dtoverlay=gpio-shutdown,gpio_pin=3,active_low=1,gpio_pull=up,debounce=1000
:

※default設定のGPIO3で使用する場合は、I2CをOFFにする必要があります。


パラメータについて

Parameter Summary Default Setting Value
gpio_pin GPIOピン番号 3 3
active_low 入力信号のエッジ
Low: 1
High: 0
1 1
gpio_pull GPIOピン処理
up: Pull-up
down: Pull-down
off: Hi-Z
up up
debounce シャットダウン検出時間[ms] 100 1000


動きとしては以下の感じでした。

  • 電源投入 → ブート
  • プッシュスイッチ長押し(1秒) → シャットダウン
  • シャットダウン状態からプッシュスイッチ短押し → ブート