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: rp2040variants: - 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