みかんのゆるふわ技術ブログ

Raspberry PiやIoT関係のことを書き残していきます

Zephyr OSでUSBシリアルコンソール出力

前回の記事では、Seeeduino XIAOのUARTポートからHello, world!を出力してみました。

www.mikan-tech.net

まあ、Zephyr OS付属のサンプルそのままですが…😅

今回は、Seeeduino XIAOのUSB-CポートUSBシリアルコンソール(USB CDC ACM)として使い、ここにHello, world!を出力してみます。 前は外部にUSB-UART変換モジュールを用意してパソコンと繋ぎましたが、今回はSeeeduino XIAOだけでパソコンと通信できます。

Seeeduino XIAOは600円くらいと安い開発ボードなのに、USBまで使えてとても便利ですよね!🥰

早速やってみましょう。

プロジェクトフォルダの作成

前回は~/myprojects/hello-worldにプロジェクトを作成しましたが、今回は~/myprojects/hello-world-usbにしましょう。

$ mkdir -p ~/myprojects/hello-world-usb
$ cd ~/myprojects/hello-world-usb

プロジェクトの作成

Zephyr OSのプロジェクトには最低限、CMakeLists.txtprj.conf、ファームウェアのソースファイルの3つが必要です。 まずはこれらを用意してみます。

├── CMakeLists.txt
├── prj.conf
└── src
    └── main.c

こんな感じになるようにファイルを作っていきましょう✊

CMakeLists.txt

最低限の中身のCMakeLists.txtは次のようになります。

cmake_minimum_required(VERSION 3.13.1)

find_package(Zephyr)
project(hello_world_usb)

target_sources(app PRIVATE src/main.c)

find_pachage(Zephyr)でZephyr OSのビルドシステムを取り込み、appというターゲットにアプリのソースファイルを指定します。 project()はプロジェクト名の指定です。

これはプロジェクト名以外、前回のUART版Hello, world!と同じです。

prj.conf

続いてZephyr OSのどの機能を使うかを指定するprj.confですが、USB機能を有効にしたり、USBシリアル機能関連の設定をする必要があります。

Zephyr OSにはいろんなサンプルが付属していて、それを真似するだけでOKになっているのでわかりやすいです☺️

USBシリアルのサンプルは、zephyrproject/zephyr/samples/subsys/usbにあります。このサンプルをそのまま使って、中身を次のようにします。

CONFIG_USB=y
CONFIG_USB_DEVICE_STACK=y
CONFIG_USB_DEVICE_PRODUCT="Zephyr USB console sample"
CONFIG_USB_UART_CONSOLE=y

CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_UART_LINE_CTRL=y
CONFIG_UART_CONSOLE_ON_DEV_NAME="CDC_ACM_0"

CONFIG_USB=yでUSBドライバを有効にします。CONFIG_USB_DEVICE_STACK=yはUSBのデバイスモードを有効にします。もっとも、Zephyr OSは現時点ではUSBホストモードは対応しておらず、デバイスモードのみです。そして、CONFIG_USB_UART_CONSOLE=yで、USBシリアルコンソールを有効にします。 その他、いくつかUSBやUARTのオプションを有効化したり設定したりしています。

ちなみに、このオプションだけではUSBシリアルコンソールへの出力はできますが、入力は受け付けません。USBシリアルコンソールの入出力を両方使う場合のサンプルは、zephyrproject/zephyr/samples/subsys/cdc_acmを参照してください。

また、現時点ではZephyr OSは複数のUSBデバイスクラスを同時に使うことは対応していません。例えば、USBシリアルコンソールとUSBマスストレージを同時に有効にすることはできません🙅‍♀️

CONFIG_UART_CONSOLE_ON_DEV_NAME="CDC_ACM_0"、Zephyr OSのコンソールの出力先を"CDC_ACM_0"にします。 この"CDC_ACM_0"というのがUSBシリアルになります。

src/main.c

いよいよソースコードを用意しましょう。とはいえ、これもサンプルほぼそのままです。

#include <zephyr.h>
#include <sys/printk.h>
#include <sys/util.h>
#include <string.h>
#include <usb/usb_device.h>
#include <drivers/uart.h>

void main(void)
{
    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);
    }

    printk("Hello World!\n");
}

USB機能を有効にするには、usb_enable()を呼び出します。 Zephyr OSのドライバによっては、main()に入る前に自動で初期化・有効化してくれるものも多いのですが、USBは明示的に呼び出す必要があるようです。

使用するUSBデバイスクラスによって、usb_enable()の前にアプリ側の設定などを行う必要があるからかと思われます。

続いて、

while (!dtr) {
    uart_line_ctrl_get(dev, UART_LINE_CTRL_DTR, &dtr);
}

で、DTR(Data Treminal Ready)フラグが立つのを待っています。 これで、パソコン側とUSBシリアルコンソールの接続が確立し、通信できるようになるのを待つことができます。

この3行を外すと、パソコン側との接続の有無にかかわらず、先へ進みます。

そして最後に、printk()でHello world!を出力します。

簡単ですね😉

動かしてみよう

プロジェクトの準備ができたら、早速ビルドしてみましょう。

$ west build -b seeeduino_xiao

ファームウェアのバイナリは、build/zephyr/zephyr.binにできます。 これをSeeeduino XIAOに書き込んであげて、Seeeduino XIAOのUSB-Cポートをパソコンにつないでみましょう。

Windowsパソコンでも大丈夫です。自動的にドライバがインストールされ、COMポートとして認識されます。

Tera Termなどのソフトウェアを使って、認識されたCOMポートに接続してみましょう。

このように、USBシリアルでHello World!が出力されました✨

前回と違いSeeeduino XIAO単体でパソコンにコンソール出力できるので、とても便利ですね☺️

今回はSeeeduino XIAOからパソコンへの出力だけでしたが、別のサンプルを参考にすれば、パソコンでの入力をSeeeduino XIAOで受けることもできます。

高機能なZephyr OSをぜひいろいろ試してみてください!

次回は、以前Rapberry PiにつないだBME280という温湿度気圧センサーをつないでみようと思います!