この文章は、ZephyrRTOS Advent Calendar 2024 1の7日目のエントリとして書かれました。
はじめに
これまで、何本かmicro:bitでZephyrを使う記事を書いてきましたが、全てサンプルを動かすというレベルのものでした。
そろそろ、なにか適当なアプリケーションを書きたいなぁと思ったところに、Zephyr and the BBC Microbit V2 Tutorial Part 3: I2C 2 というI2Cデバイスを使う記事があることに気が付きました。これは、内蔵I2Cに接続されている加速度センサーLSM303を使うという話になっています。
「micro:bitって外側端子にもI2Cが出ているから、ひょっとして外付けでI2Cデバイスが使えるんじゃね」と考えたので、使えないか試してみることにしました。
最終的にI2C接続のジョイスティックを動かすことができたので、報告したいと思います。
ハードウエア
利用したハードウエアは以下の通りです。
ハードウエア | SKU | 役割 |
---|---|---|
micro:bit v2.0 | SEDU-066006 | メインボード |
M5Stack用 I2Cジョイスティックユニット | M5STACK-U024-C | I2Cジョイスティック |
M5:Bit micro:bit用変換ボード | M5STACK-A051 | micro:bitからI2Cを出す |
GROVE - 4ピン-ジャンパメスケーブル | SEEED-110990028 | M5:Bitとジョイスティック接続用 |
接続イメージは、以下の図の通りです。
以下に各デバイスに関して説明します。
micro:bit
今回もmicro:bitを利用しました。
J-Link化したmicro:bit v2.0 3 を使っています。
初期設定などの詳細は、macOSでZephyrのmicro:bitデモを動かす 4 をご覧ください。
最新のmicro:bitはv2.2はJ-Link化できませんので、注意してください。
M5Stack用 I2Cジョイスティックユニット
M5Stack用 I2Cジョイスティックユニット 5 は、Grove接続のI2Cジョイスティックです。
プロトコルは非常に簡単で、I2Cアドレス0x52のレジスタ0x00から3バイト読み込むと、それぞれx方向の値,y方向の値,btn状態が得られます。
初期化などは必要ありません。
接続用ハードウエア
以下のハードウエアをmicro:bitとジョイスティックの接続用に使いました。
M5:Bit micro:bit用変換ボード
M5:Bit micro:bit用変換ボード 6 は、micro:bitのコネクタをピンに出すためボードです。この種のボードとしては、非常に安価になっています。
I2Cが右下の端子で2系統出せるようになっています。I2CがGroveで出ていればもっと良かったのですが…
さらに、シリアルはGrove端子に出ているので、簡単に利用できます。
この製品に限らず、micro:bitからI2Cを出すボードは各種出ていますので、どれを使っても問題ありません。
売り切れが多いですが、"grove micro:bit"で検索する 7 とそのような商品が出てきます。
いくつかの商品は直接Grove接続できるので、次のケーブルを使わなくても、普通のGroveケーブルでジョイスティックと接続できるようになります。
GROVE - 4ピン-ジャンパメスケーブル
GROVE - 4ピン-ジャンパメスケーブル 8 は、グローブケーブルを2.54 mmピッチのジャンパメスコネクタに変換するケーブルです。
M5:Bit側が4ピンオスコネクタで、ジョイスティック側がGroveになるため、必要になります。
Seeed製品のGroveケーブルとM5Stack製品のGroveケーブルでは、黄色と白の信号線の色が反対になっているので注意してください。
私は、これで数時間溶かしました…
ソースコード
今回作成したアプリケーションのソースコードは、以下の通りです。
他のサンプルと同じように、zephyr/samples/boards/bbc/microbit/m5stack_joystick/
に以下のように配置していることを仮定しています。
prj.conf
CMakeLists.txt
app.overlay
-
src/
main.c
プロジェクト設定ファイル:prj.conf
プロジェクトで利用する機能を指定します。
今回は、I2Cを有効にしています。
CONFIG_I2C=y
Cmakefile:CMakeLists.txt
アプリケーションbuild用のcmake
の設定ファイルです。
ソースは、main.c
だけなので、それを指定しているだけです。
cmake_minimum_required(VERSION 3.13.1)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(m5stack_joystick)
target_sources(app PRIVATE src/main.c)
zephyr_include_directories(${ZEPHYR_BASE}/boards/arm/bbc_microbit_v2)
Device Tree Source Overlay:app.overlay
Device Tree Source(DTS)の上書きを行う場合に利用するファイルです。
外部I2C端子であるi2c1
の定義を行っています。
外部I2Cでは、SDLが0x20
、SCLが0x1a
のため、これを指定しています。
&pinctrl {
i2c1_default: i2c1_default {
group1 {
psels = <NRF_PSEL(TWIM_SDA, 0, 0x20)>,
<NRF_PSEL(TWIM_SCL, 0, 0x1a)>;
};
};
};
&i2c1 {
compatible = "nordic,nrf-twim";
status = "okay";
pinctrl-0 = <&i2c1_default>;
pinctrl-names = "default";
};
アプリケーション本体:src/main.c
アプリケーション本体です。
ソースコードをシンプルにするため、返り値を確認してのエラー処理などは全くしてませんので、利用する時はちゃんとするようにしてください。
i2c = DEVICE_DT_GET(DT_NODELABEL(i2c1))
でI2Cバスの初期化を行うだけで、このデバイスの場合は初期化処理の必要はありません。
データは、i2c_burst_read(i2c,0x52,0x00,val,3);
で3バイトのデータval[]
を受け取ります。
変数 | 意味 |
---|---|
val[0] |
x方向の値 |
val[1] |
y方向の値 |
val[2] |
ボタンの状態 |
受け取った値は、printk
を使ってシリアルコンソールに出力します。
#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/drivers/i2c.h>
static const struct device *i2c;
int main(void)
{
uint8_t val[3];
// I2Cの初期化
i2c = DEVICE_DT_GET(DT_NODELABEL(i2c1));
k_msleep(1000);
while (1) {
// I2Cアドレス0x52のレジスタ0x00から3バイト読み込み
i2c_burst_read(i2c,0x52,0x00,val,3);
// 結果の出力
printk("(%d,%d):%d\n",val[0],val[1],val[2]);
k_msleep(100);
}
}
動作手順
動作は、以下のように行います。
build
以下のコマンドでbuildします。
$ west build -p always -b bbc_microbit_v2 samples/boards/bbc/microbit/m5stack_joystick
...
[151/151] Linking C executable zephyr/zephyr.elf
Memory region Used Size Region Size %age Used
FLASH: 27556 B 512 KB 5.26%
RAM: 5808 B 128 KB 4.43%
IDT_LIST: 0 GB 32 KB 0.00%
Generating files from /Users/mutoh/work/zephyrproject/zephyr/build/zephyr/zephyr.elf for board: bbc_microbit_v2
flash
J-Link化したmicro:bitでは、単に以下のコマンドでflashできます。
$ west flash
-- west flash: rebuilding
ninja: no work to do.
-- west flash: using runner pyocd
-- runners.pyocd: Flashing file: /Users/mutoh/work/zephyrproject/zephyr/build/zephyr/zephyr.hex
0005885 I Loading /Users/mutoh/work/zephyrproject/zephyr/build/zephyr/zephyr.hex [load_cmd]
[==================================================] 100%
0010621 I Erased 28672 bytes (7 sectors), programmed 28672 bytes (7 pages), skipped 0 bytes (0 pages) at 5.95 kB/s [loader]
動作確認
printk
の出力は、シリアルコンソールに出されるため、これを確認する必要があります。
今回は、minicom
を使いました。
# シリアルデバイスの確認
$ ls -l /dev/cu.*
crw-rw-rw- 1 root wheel 0x16000005 1 6 11:55 /dev/cu.Bluetooth-Incoming-Port
crw-rw-rw- 1 root wheel 0x16000011 1 16 08:14 /dev/cu.usbmodem0007820596381
# minicomの起動
$ minicom -D /dev/cu.usbmodem0007820596381
(14,68):0
(33,39):0
(46,29):0
(58,19):1
(59,18):0
(59,39):0
(110,146):1
(104,162):0
(99,196):0
(75,239):1
...
動作の様子は、以下の動画 9 のようになります。
おわりに
サンプルを使うのではなく、はじめてZephyrでコードを書きました。
標準のDTSで定義されていないバスの情報などがアプリケーション側で上書きできたりするのには、ちょっと驚きました。
参考になりましたら、幸いです。