前回に引き続き、Seeeduino XIAOにZephyr OSを搭載して遊んでみましょう。 前回はUSBシリアルに"Hello world!"を出力させてみました。
今回は、Raspberry Piにもつないでみた温度・湿度・気圧の3つが測れるBME280という便利なセンサーをつないでみましょう☺️
Zephyr OSにはBME280のドライバが含まれているので、簡単にセンサーの値が読めます🥰
サンプルプロジェクトもzephyr/samples/sensor/bme280
に含まれています。これが参考になります。
しかし、このサンプルはZephyr OS v2.5.0では、Seeeduino XIAOでは動きません。
(後述の、I2C周りのボード設定をする必要があります)
なので、前回のHello world!を改造して作ってみます。 早速やってみましょう✊
プロジェクトフォルダの作成
前回は~/myprojects/hello-world-usb
にプロジェクトを作りましたが、今回は~/myprojects/bme280
にしましょう。
センサーの値はUSBシリアルに出力したいと思います。なので、前回のプロジェクトをベースにしましょう。 前回のプロジェクトをコピーします。
$ cp -r ~/myprojects/hello-world-usb ~/myprojects/bme280 $ cd ~/myprojects/bme280
もしbuild
ディレクトリが残っていたら、削除しておいてください🙅♂️
$ rm -rf ./build
BME280ドライバの組み込み
前回はUSBシリアルのドライバを組み込むのに、サンプルのprj.conf
からコピペしました。
今回は違う方法として、menuconfigを使ってみましょう。既存のドライバを簡単に組み込めるので便利です。
Linux Kernelのビルドでもおなじみの機能ですね😊
menuconfigの起動
次のようにして、menuconfigを起動します。
$ west build -b seeeduino_xiao -t menuconfig
次のような画面が出ます。
このメニューで機能の有効無効や各種設定ができます。指定した基板(seeeduino_xiao)や、既存のprj.conf
の設定は自動で読み込まれます。
Board Selectionという項目はSeeeduino XIAOに、SoC/CPU/Configuration Selectionという項目は、Atmel SAMD21 MCU(Seeeduino XIAOに載っているマイコン)が選ばれています。
I2Cドライバの追加
それでは、ドライバを追加しましょう。上下キーでDevice Driversという項目を選び、Enterキーを押します。
ドライバの選択画面に移りました。今回はBME280をI2Cでつなぎます。なので、I2CドライバとBME280ドライバの2つを有効にします。 デフォルトではどちらも無効になっています。
上下キーで、I2C Driversを選び、スペースキーを押すとチェックが入り、有効になります。メニューの右側が--->
になっている項目は、詳細設定画面があります。Enterで移動できます。
このように、I2C関連の設定ができます。今はSAM0 Series I2C SERCOM driverというものが選択されていますね。 これはSeeeduino XIAO搭載のマイコンSAMD21のI2Cドライバのことです。
今回は特に変更する箇所は無いので前の画面に戻ります。ESCキーで1つ前の画面に戻ります。
BME280ドライバの追加
続いて、BME280を有効にします。実はBME280はセンサードライバの1種なので、まずはセンサードライバを有効にします。Sensor Driversを選びスペースキーでチェックをした後、Enterキーで詳細画面に入ります。
BME280/BMP280 sensorを選び、スペースキーでチェックマークを付けます。これで、BME280ドライバが有効になりました。 Enterキーを押すと、詳細設定ができます。
このような項目があります。 例えば、BME280には、各種センサーの値を1回でなく、2回や16回など繰り返しサンプリングしその平均を取得することで精度を高める機能があります。 これらの設定を変更できます。
特にいじる必要はないので、そのままESCキーで戻ります。
設定の保存
menuconfigで変更した設定は、Sキーで保存ができます。保存先が選べますが、デフォルトの場所 build/zephyr/.config
にしておくとビルド時に読み込まれますので、そのまま保存しましょう。
これで保存した設定は、buildディレクトリの中に保存されます。buildディレクトリを消して再ビルドした場合などは、設定も消えてしまいます。
.config
ファイルの一部だけ抜粋しますが、次のようにBME280ドライバが有効になっていることがわかります。
# CONFIG_AMG88XX is not set # CONFIG_AMS_IAQ_CORE is not set # CONFIG_BMA280 is not set # CONFIG_BMC150_MAGN is not set CONFIG_BME280=y CONFIG_BME280_MODE_NORMAL=y # CONFIG_BME280_MODE_FORCED is not set # CONFIG_BME280_TEMP_OVER_1X is not set CONFIG_BME280_TEMP_OVER_2X=y # CONFIG_BME280_TEMP_OVER_4X is not set # CONFIG_BME280_TEMP_OVER_8X is not set # CONFIG_BME280_TEMP_OVER_16X is not set
設定を永続化するには、build/zephyr/.config
をprj.cong
に上書きコピーします。しかし、デフォルト設定も含め全設定が書き込まれるので、1000行近くあって冗長ですしわかりにくいです😥
デフォルトからの変更点のみ保存するには、menuconfig画面でDキーを押します。build/zephyr/kconfig/defconfig
に保存されますので、このファイルをprj.conf
に上書きするとよいでしょう☺️
$ cp build/zephyr/kconfig/defconfig prj.conf
prj.confは次のようになりました。
CONFIG_I2C=y CONFIG_SERIAL=y CONFIG_SOC_SERIES_SAMD21=y CONFIG_SOC_ATMEL_SAMD_XOSC32K=y CONFIG_SOC_PART_NUMBER_SAMD21G18A=y CONFIG_UART_CONSOLE_ON_DEV_NAME="CDC_ACM_0" CONFIG_CONSOLE=y CONFIG_USB_UART_CONSOLE=y CONFIG_UART_LINE_CTRL=y CONFIG_PINMUX=y CONFIG_SENSOR=y CONFIG_BME280=y CONFIG_USB=y CONFIG_USB_DEVICE_STACK=y CONFIG_USB_DEVICE_PRODUCT="Zephyr USB console sample" CONFIG_BOOTLOADER_BOSSA=y CONFIG_BOOTLOADER_BOSSA_ADAFRUIT_UF2=y
もとのprj.confの設定と新しい設定がミックスされて、いい感じになっていますね。
ボード設定ファイルの作成
DTSファイルの作成
BME280をどのポートにつなぐかは、Zephyr OSのDevice Treeの仕組みを使ってDTSファイルで指定します。
プロジェクトフォルダの中に、boards
フォルダの中に、<ボード名>.dts
という名前で作成すると、ビルド時に自動で読み込んでくれます。
今回の場合、myprojects/bme280/boards/seeeduino_xiao.dts
という名前になります。
Zephyr OS v2.5.0では、Seeeduino XIAOのI2Cはデフォルトで無効になっています。 ドライバがあるので簡単に有効にできるのですが、ボード設定ファイルでマイコンの設定をしなければなりません。
Seeeduino XIAOのI2Cピン(上図SDA、SCL)は、Seeeduino XIAOの回路図を見ると、 ATSAMD21のSERCOM2の端子であることがわかります。
SERCOM2というのは、Seeeduino XIAO搭載のマイコンATSAMD21の機能で、I2C、UART、SPIなどが使えるシリアル通信インターフェースです。
Seeeduino XIAOピン | ATSAMD21ピン | 機能 |
---|---|---|
SDA | PA8 | SERCOM2 (PAD0) |
SCL | PA9 | SERCOM2 (PAD1) |
このSERCOM2にI2Cドライバを紐づけて、さらにBME280を接続したことを、DTS形式で記述します。 最初はわかりにくいと思いますが、次のようにします。
&sercom2 { status = "okay"; compatible = "atmel,sam0-i2c"; clock-frequency = <I2C_BITRATE_FAST>; bme280@76 { compatible = "bosch,bme280"; reg = <0x76>; label = "BME280"; }; };
compatible=
で、どのドライバに紐づけるかを指定できます。また、BME280のI2Cアドレスをreg=
で指定します。
このファイルをmyprojects/bme280/boards/seeeduino_xiao.dts
に保存しましょう。
このDevice Treeの記述方法はなかなか慣れませんが、サンプルが豊富なのと、ドキュメントもしっかりしているので、見よう見まねで頑張りましょう😅
ピンコンフィグファイルの作成
Seeeduino XIAOのI2Cはデフォルトで無効になっており、OS側ではピン設定もされていません。 ATSAMD21のSERCOM2を有効にするには、ピンの設定も手動でする必要があります。
ピン設定は、他のボードのファイルを参考にするとよいでしょう。
zephyr/boards/arm/seeeduino_xiao/pinmux.c
のほか、zephyr/boards/arm/adafruit_feather_m0_basic_proto/pinmux.c
が参考になります。
これらを参考に、myprojects/bme280/src/pinmux.c
を以下の内容で作成します。
#if CONFIG_I2C_SAM0 #include <init.h> #include <drivers/pinmux.h> #include <soc.h> static int bme280_pinmux_init(const struct device *dev) { ARG_UNUSED(dev); #if ATMEL_SAM0_DT_SERCOM_CHECK(2, atmel_sam0_i2c) const struct device *muxa = device_get_binding(DT_LABEL(DT_NODELABEL(pinmux_a))); /* SERCOM2 on SDA=PA08, SCL=PA09 */ pinmux_pin_set(muxa, 8, PINMUX_FUNC_D); pinmux_pin_set(muxa, 9, PINMUX_FUNC_D); #endif return 0; } SYS_INIT(bme280_pinmux_init, POST_KERNEL, CONFIG_PINMUX_INIT_PRIORITY); #endif /* CONFIG_I2C_SAM0 */
pinmux_pin_set()
でピンの機能を指定して、PA08、PA09をデフォルトのGPIOからSERCOM2に変更します。
最後のSYS_INIT()
マクロを書いておくと、不思議なことに、Zephyr OSのビルドシステムが認識して、
main()関数が呼ばれる前に指定した関数を実行しておいてくれます。
このような便利な機能が備わっているのがZephyr OSの特徴です😊
プログラムの作成
いよいよBME280を読み取るプログラムを作成しましょう。 とはいえ、ここはサンプルほぼそのままです。
#include <zephyr.h> #include <device.h> #include <devicetree.h> #include <drivers/sensor.h> #include <usb/usb_device.h> #include <drivers/uart.h> #define BME280 DT_INST(0, bosch_bme280) #if DT_NODE_HAS_STATUS(BME280, okay) #define BME280_LABEL DT_LABEL(BME280) #else #error Your devicetree has no enabled nodes with compatible "bosch,bme280" #define BME280_LABEL "<none>" #endif void main(void) { const struct device *bme280 = device_get_binding(BME280_LABEL); const struct device *dev = device_get_binding(CONFIG_UART_CONSOLE_ON_DEV_NAME); uint32_t dtr = 0; if (usb_enable(NULL)) { return; } /* Poll if the DTR flag was set, optional */ while (!dtr) { uart_line_ctrl_get(dev, UART_LINE_CTRL_DTR, &dtr); } if (bme280 == NULL) { printk("No device \"%s\" found; did initialization fail?\n", BME280_LABEL); return; } else { printk("Found device \"%s\"\n", BME280_LABEL); } while (1) { struct sensor_value temp, press, humidity; sensor_sample_fetch(bme280); sensor_channel_get(bme280, SENSOR_CHAN_AMBIENT_TEMP, &temp); sensor_channel_get(bme280, SENSOR_CHAN_PRESS, &press); sensor_channel_get(bme280, SENSOR_CHAN_HUMIDITY, &humidity); printk("temp: %d.%06d; press: %d.%06d; humidity: %d.%06d\n", temp.val1, temp.val2, press.val1, press.val2, humidity.val1, humidity.val2); k_sleep(K_SECONDS(5)); } }
BME280ドライバなどのセンサードライバは、どんなセンサーでも統一的なセンサーAPIでセンサー値の読み取りができます。
sensor_sample_fetch()
でセンサーを実際に読み取り、sensor_channel_get()
で読み取った値を取得できます。
BME280の場合、SENSOR_CHAN_AMBIENT_TEMP
、SENSOR_CHAN_PRESS
、SENSOR_CHAN_HUMIDITY
の3つが取得できます。
それぞれ、温度、気圧、湿度です。
読み取った結果は、struct sensor_value
の構造体に格納されています。
BME280の場合、構造体のval1
に整数部、val2
に小数点部分が格納されているようです。
最後に、読み取ったセンサー値をprintk()で表示しています。
これを5秒ウェイトで繰り返しています。
センサーのAPIの使い方さえわかれば、それほど難しくないですね。 ドライバーを書く必要がないのが便利です☺️
CMakeLists.txt
最後に、CMakeLists.txtを用意しましょう。
これはほぼ前回のそのままですが、pinmux.c
を追加でビルドする必要があるので追加します。
面倒なのでワイルドカードにしました。
cmake_minimum_required(VERSION 3.13.1) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(bme280) FILE(GLOB app_sources src/*.c) target_sources(app PRIVATE ${app_sources})
動かしてみよう
最終的に、プロジェクトフォルダmyprojects/bme280
は次のようになりました。
$ tree . ├── CMakeLists.txt ├── boards │ └── seeeduino_xiao.overlay ├── prj.conf └── src ├── main.c └── pinmux.c
プロジェクトの準備ができたら、早速ビルドしてみましょう。
$ west build -b seeeduino_xiao (略) -- west build: building application [1/138] Preparing syscall dependency handling [138/138] Linking C executable zephyr/zephyr.elf Memory region Used Size Region Size %age Used FLASH: 25152 B 232 KB 10.59% SRAM: 9480 B 32 KB 28.93% IDT_LIST: 0 GB 2 KB 0.00%
ビルドが成功しました。USBシリアルとBME280ドライバを有効にしたくらいだと、FLASHは25KB弱と10%くらいしか使いません。
ファームウェアのバイナリは、build/zephyr/zephyr.bin
にできます。
これをSeeeduino XIAOに書き込んであげて、Seeeduino XIAOのUSB-Cポートをパソコンにつないでみましょう。
Windowsパソコンでも大丈夫です。自動的にドライバがインストールされ、COMポートとして認識されます。
Tera Termなどのソフトウェアを使って、認識されたCOMポートに接続してみましょう。
このように、USBシリアルでBME280のセンサー値が出力されています。気温が25.1度くらい、湿度が59.0%くらいでしょうか。 気圧は100.12などとなっていますが…これはキロパスカル(kPa)表記なのかな?🤔だいたい1001.2hPaくらいですね。 手元に気圧計がないですが、部屋に置いてある100均の温湿度計とは同じくらいになっています。
独特の作法が慣れないところですが、多様なドライバ、ファイルシステム、ネットワーク、マルチスレッドなどとても高機能な組み込みOSなので、慣れれば電子工作の幅が広がることは間違いないでしょう。ぜひ試してみてください😊