Spresense SDK 開発ガイド
1. Spresense SDKの概要
Spresense SDKはSony Semiconductor Solutions Corp.が提供するCXD5602用に開発されたCXD5602を制御するためのソフトウェアです。 CXD5602の内部ブロックは以下のようになります。
CXD5602は大きく4つのドメインと呼ばれるブロックから構成されています。
-
Application Domain
-
主にユーザアプリケーションプログラムが制御するブロックで、この中にユーザプログラムが動作するCPU (ARM社製 Cortex®-M4F) が6個搭載されています。
-
-
System IOP Domain
-
システムの起動やCXD5602内部の電源ドメイン管理などを行うブロックで、ARM社製 Cortex-M0が搭載されています。
-
-
GNSS Domain
-
衛星を使った位置測位を行うブロックで、ARM社製 Cortex-M4Fが搭載されています。
-
-
Sensor Domain
-
I2CやSPIバスに接続されたセンサーのデータをCPUの介在無しに取得するブロックで低消費電力での常時センシングを行うためのソニー独自のハードウェアになります。
-
CXD5602に関する詳しい情報は、 ソニーセミコンダクタソリューションズ(株) Spresenseホームページを参照してください。 |
Spresense SDKは上記のハードウェアを制御するためのソフトウェアになります。 Spresense SDKはOSとしてRTOSの1つであるNuttXを利用しており、このOSをベースにして、CXD5602の性能を引き出すための細かな機能を提供します。 Spresenseの各ドライバは、NuttXがもつLinuxライクなDriver Frameworkに準拠して実装されており、そのドライバレイヤの上に、Spresenseが持つ、Audio、GNSS、ASMPなどのミドルウェアのレイヤがあります。
各ミドルウェアと特徴的なドライバの概要は以下の通りになります。
Module Name | Summary |
---|---|
Audio Middleware | Spresense SDKのAudio機能を提供します。様々なデータパスに対応し、各種フォーマットでの録音、再生に加えて音声加工処理を実現できます。 |
Sensor Framework | Spresense SDKの各種センサーデータの出力・加工などの処理を行う際に、センサーもしくは加工データのやり取りをPublish/Subscribeアーキテクチャにて簡便にプログラム出来るようなフレームワークになります。 |
Memory Utility | Spresense SDKにて、リファレンスカウンター付、固定長メモリープール機能の提供及び、インスタンスの非同期送受信するタスク同期機構を提供しています。 |
ASMP Framework | CXD5602が持つマルチコアに対してユーザプログラムのロードなどの管理に加えて、CXD5602のメモリ構造の特徴の一つである12枚のタイルの管理を行っています。 |
Power Management | 省電力を実現するための機能を提供します。 |
GNSS Driver | CXD5602にはGNSSの測位を行うHWサブシステムが備わっています。このドライバはそのサブシステムとのやり取りを行い、ユーザにPOSIX I/FのキャラクタデバイスライクなI/FとしてGNSSに関する機能を提供しています。 |
各モジュールの詳細は、「Spresense SDKが提供する機能」の章を参照してください。
2. License
Spresense SDKはNuttXと同様、BSD 3項型ライセンスの元でオープンソースとして公開しています。詳しいライセンス条項は、下記の通りです。
3. システム起動シーケンス
CXD5602はリセットが解除されると、BootCPUが起動します。このBootCPUで動作するのが、loader.espkになります。loader.espkが起動すると、loader.espkに制御が移り、loader.espkはnuttx.spkをロードし、アプリケーションCPUをスタートさせます。
nuttx.spkは本SDKでビルドされたバイナリですので、nuttx.spkの実行内容自体は、ビルド時の設定やアプリケーションの動作によって変わります。
アプリケーションがGNSSの機能を使う場合、初期化のAPIをコールした段階でgnss.espkがloader.espkによってロードされるようになっています。
したがって、loader.espkとgnss.espkはSpresense基板を利用する上で、必須のバイナリコードになります。 バイナリの入手方法については、こちらを参照してください。
4. ソフトウェア構成管理
この章ではSpresense SDKのソースコードについてその概要を説明します。
4.1. リポジトリ
Spresense SDKでは以下の2つのRepositoryで構成されます。
リポジトリ名 | サブリポジトリ | 説明 |
---|---|---|
Spresense SDK のメインリポジトリで開発環境を整える場合はこちらのレポジトリをCloneします。 | ||
Spresense NuttX のクローンリポジトリです |
4.2. ソースツリー
Spresense SDKのソースツリー構成は以下のようになっています
各ディレクトリの内容は以下のようになっています
ディレクトリ名 | 説明 |
---|---|
spresense | spresenseをCloneすると生成されるディレクトリです |
nuttx | Spresense NuttX のKernelソースコードが含まれます |
examples | Spresense SDKでサポートされるドライバ、モジュールを使ったExampleコードが含まれます |
sdk | Spresense SDKでサポートされるドライバ、モジュール類が含まれます |
bsp | Spresense SDKでのBSPが含まれます |
configs | Spresense SDKで提供しているConfigurationが含まれます |
drivers | Spresense SDKで提供しているドライバが含まれます |
modules | Spresense SDKで提供しているオーディオやセンサなどのモジュールが含まれます |
system | Spresense SDKで提供しているシステムツールが含まれます |
tools | Spresense SDKでの開発に必要なツールおよびスクリプトが含まれます |
5. 開発環境構築
この章ではSpresense SDKを使ってユーザが自身のアプリケーションを開発するのに必要なツールや環境などの説明を行います。
-
なお、現時点でサポートしているPC環境は、Ubuntu 16.04 LTS 64bitになります。
5.1. ツールの準備
-
GCC ARM toolchain の他、必要なパッケージをインストールします。
$ sudo apt-get install git gperf libncurses5-dev flex bison gcc-arm-none-eabi genromfs pkg-config autoconf automake cmake --install-recommends
-
nuttx-tools から kconfig-frontends を取得し、インストールします。
$ git clone https://bitbucket.org/nuttx/tools.git $ cd tools/kconfig-frontends/ $ ./configure --disable-shared $ make $ sudo make install
5.2. リポジトリのクローン
$ git clone --recursive https://github.com/sonydevworld/spresense.git
5.3. ブートローダーのインストール
Spresense メインボードを正しく動作させるためにはバージョンに合わせたブートローダーのインストールが必要になります。
Spresense ボードは出荷状態ではローダーはインストールされていません。 |
Spresenseのローダーを利用するためにはEnd User License Agreementの同意が必要です。 |
tools/config.py
及び tools/flash.sh
実行時にバージョンに合わせたブートローダーが見つからない場合は次のような WARNING
が出るのでそれに従ってインストールを行います。
WARNING: New loader v1.0.003 is required, please download and install.
Download URL : https://developer.sony.com/file/download/eula-binaries-vx.x.x.zip
Install command:
1. Extract loader archive into host PC.
./tools/flash.sh -e <download zip file>
2. Flash loader into Board.
./tools/flash.sh -l /home/user/mySpresense/spresense/firmware/spresense -c <port>
-
バイナリのダウンロード
WARNING
上のDownload URL
に記載されているリンクを開きバイナリのZIPファイルをダウンロードします。 -
バイナリのPCへのインストール
ダウンロードしたZIPファイルで次のコマンドを使いPCにインストールします。
$ ./tools/flash.sh -e eula-binaries-vx.x.x.zip
-
バイナリのSpresense メインボードへのロード
WARNING
上で表示されたコマンドをそのまま実行します。$ ./tools/flash.sh -l /home/user/mySpresense/spresense/firmware/spresense -c <port>
-
ロードが完了し自動で再起動されます。
6. ビルド方法
この章では、ビルド方法について説明します。
6.1. コンフィギュレーション
NuttXカーネルおよび Spresense SDK は、様々な状況に対応できるよう、ユーザーの目的にあったコンフィギュレーションを行うことが可能です。
コンフィギュレーションは、Linuxでも使用されているKconfigビルドシステムを使用します。ツールの準備 を参照して、コンフィギュレーションツールをインストールしてください。
コンフィギュレーションは、NuttXカーネルとSDKのコンフィギュレーションに分かれています。Spresense SDKでは、それらのコンフィギュレーションを行うためのツール tools/config.py
tools/mkdefconfig.py
を提供しています。
~/spresense/sdk$ tools/config.py -h
usage: config.py [-h] [-k] [-l] [-m] [-q] [-g] [-d DIR] [-v]
[<config name> [<config name> ...]]
Configuration tool
positional arguments:
<config name> configuration name
optional arguments:
-h, --help show this help message and exit
-k, --kernel kernel config
-l, --list list default configurations. show kernel defconfigs with
--kernel.
-m, --menuconfig run config in "menuconfig"
-q, --qconfig run config in "qconfig"
-g, --gconfig run config in "gconfig"
-d DIR, --dir DIR change configs directory
-v, --verbose verbose messages
~/spresense/sdk$ tools/mkdefconfig.py -h
usage: mkdefconfig.py [-h] [-k] [-d DIR] [--all] [--verbose] <config name>
Make default config from current config
positional arguments:
<config name> configuration name
optional arguments:
-h, --help show this help message and exit
-k save kernel configuration
-d DIR, --dir DIR change configs directory
--all Save SDK and kernel configuration with same name
--verbose, -v verbose messages
tools/config.py
ツールは、 menuconfig
などのフロントエンドおよび定義済みコンフィギュレーション(後述)のロードなどを行います。
このツールは、NuttXカーネルおよび Spresense SDK の設定を行うことができますが、どちらの設定を行うかどうかは -k
または --kernel
オプションによって切り替えます。 -k
オプションが指定されていた場合はNuttXカーネルの設定、指定されていない場合はSDKの設定を変更します。例えば、カーネルのコンフィギュレーションを変更したい場合は、以下のようにします。
$ cd spresense/sdk
$ tools/config.py -k -m
tools/config.py tools/mkdefconfig.py はディレクトリ構造に依存しているため、必ず sdk ディレクトリ以下で実行してください。 |
6.1.1. menuconfig, qconfig, gconfig
設定を変更したい場合は、 tools/config.py
の以下のオプションを指定し、設定画面を呼び出します。
オプション | |
| ncursesライブラリを用いた、CUIメニュー選択 |
| Qtライブラリを使用したGUIメニュー選択 |
| gtkライブラリを使用したGUIメニュー選択 |
上記のメニュー選択方式は kconfig-frontend
の機能に依存しています。kconfig-frontend
をビルドする際のオプションによっては使用できない場合がありますが、いずれの方式でも機能は同等です。
6.1.2. 定義済みコンフィギュレーション(defconfig)
定義済みコンフィギュレーション(defconfig)は、ある機能を使用したい場合に必要な設定の組み合わせを事前に定義したものです。defconfigを使用したい場合は、 tools/config.py
ツールの引数にdefconfig名を指定します。defconfig名には、 -l
オプションで表示されdefconfigを指定できます。
定義済みdefconfigのリストは次のようにして確認できます。
$ tools/config.py --kernel --list # list kernel predefined configs
$ tools/config.py --list # list SDK predefined configs
定義済みコンフィギュレーションを指定する場合は次のようにして行うことができます。
$ tools/config.py --kernel release
$ tools/config.py board/spresense
defconfigは同時に複数指定することができます。defconfigを複数指定した場合は、指定されたdefconfigの組み合わせとなります。
$ tools/config.py board/spresense device/sdcard
tools/config.py にコンフィグ名を指定した場合は、指定されたdefconfigにリセットされます。 |
6.1.3. 設定の保存
ユーザーが変更した設定は保存しておくことが可能です。以下のコマンドで現在のSDKの設定を <config name>
として保存します。
$ tools/mkdefconfig.py <config name>
上記の例では、SDKの設定のみ保存され、NuttXカーネルの設定は保存されません。カーネルの設定を保存したい場合は、 -k
オプションまたは --all
オプションを使用します。 --all
オプションを指定した場合は、SDKとカーネルの両方の現在の設定が指定された名前で保存されます。
$ tools/mkdefconfig.py -k <config name>
保存した設定は tools/config.py
に指定することができるようになります。
<config name> に指定した名前が既に存在する場合は、上書きするかどうかの確認が表示されます。 |
tools/config.py
と tools/mkdefconfig.py
は、 sdk/configs
以下にあるdefconfigを対象に適用/保存を行います。もしユーザーが別のディレクトリでコンフィギュレーションを管理したい場合は、 -d
または --dir
オプションで保存先を変更することができます。
$ tools/mkdefconfig.py -d ../path/to/configs <config name>
別のディレクトリに保存したdefconfigを使用する場合は以下のようにします。
$ tools/config.py -d ../path/to/configs <config name>
ディレクトリの変更は、 -k
オプションと組み合わせても使用可能です。
6.2. ビルド
ビルドは、コンフィギュレーションと同様にNuttXカーネルとSDKに分かれています。それぞれビルドする必要があります。どちらの場合も、 sdk
ディレクトリ以下で行います。
6.2.1. NuttXカーネルのビルド
NuttXカーネルのビルドを行う場合は、以下のように入力します。ビルドを行う前に、カーネルのコンフィギュレーションを行っておく必要があります。
$ cd spresense/sdk
$ tools/config.py -k release
$ tools/config.py -k -m
(設定の変更)
$ make buildkernel
カーネルのコンフィギュレーションを省略したい場合は、以下のようにすることもできます。
$ cd spresense/sdk
$ make buildkernel KERNCONF=release
NuttXカーネルの設定変更およびソースの修正を行っていない場合は、再度ビルドする必要はありません。 |
6.2.2. SDKのビルド
SDKのビルドを行う場合は、以下のように入力します。カーネルの場合と同様、設定は make
コマンドの前に行っておく必要があります。
$ tools/config.py default
$ tools/config.py -m
(設定の変更)
$ make
ビルドが成功すると sdk
ディレクトリ以下に nuttx
ファイルと nuttx.spk
ファイルができます。
nuttx
-
ELF形式の実行ファイルです。デバッガでロードする場合などで使用します。
nuttx.spk
-
実機に書き込むためにパッケージングされた独自形式のファイルです。
7. 実機へのロード方法
この章ではブートローダ及びビルドしたビルドしたソフトウェアイメージをSpresense基板に書き込む方法について、説明します。
7.1. Spresense メインボードへのブートローダーのインストール
Spresense メインボードを正しく動作させるためにはバージョンに合わせたブートローダーのインストールが必要になります。
Spresense ボードは出荷状態ではローダーはインストールされていません。 |
Spresenseのローダーを利用するためにはEnd User License Agreementの同意が必要です。 |
SDKのコンフィギュレーション 及び Spresense メインボードへのイメージのロード 時にバージョンに合わせたブートローダーが見つからない場合は次のような WARNING
が出るのでそれに従ってインストールを行います。
WARNING: New loader v1.0.003 is required, please download and install.
Download URL : https://developer.sony.com/file/download/spresense-binaries-vx.x.x.zip
Install command:
1. Extract loader archive into host PC.
./tools/flash.sh -e <download zip file>
2. Flash loader into Board.
./tools/flash.sh -l /home/user/mySpresense/spresense/firmware/spresense -c <port>
-
バイナリのダウンロード
WARNING
上のDownload URL
に記載されているリンクを開きバイナリのZIPファイルをダウンロードします。 -
バイナリのPCへのインストール
ダウンロードしたZIPファイルで次のコマンドを使いPCにインストールします。
$ ./tools/flash.sh -e spresense-binaries-vx.x.x.zip
-
バイナリのSpresense メインボードへのロード
WARNING
上で表示されたコマンドをそのまま実行します。$ ./tools/flash.sh -l ../firmware/spresense -c <port>
-
ロードが完了し自動で再起動されます。
7.2. Spresense メインボードへのイメージのロード
ビルドしたイメージ nuttx.spk
をSpresenseにロードするためには tools/flash.sh
を用います。
-
Spresense メインボードとPCをUSBケーブルで接続します。
-
Spresense メインボードが接続されているシリアルポートを探すには、以下のコマンドを試してみてください。
$ dmesg | grep "cp21.*attached" [12220.625979] usb 1-1: cp210x converter now attached to ttyUSB0
この場合は、
ttyUSB0
に接続されていることになります。フルパスは、/dev/ttyUSB0
になります。 -
次のコマンドを使い
nuttx.spk
をSpresenseへロードします。$ tools/flash.sh -c /dev/ttyUSB0 nuttx.spk
図 5.nuttx.spk
ロード画面 -
ロードが完了し自動で再起動されます。
8. examples
この章では、このSpresense SDKで提供している各exampleの説明と各exampleのビルド手順、例としてexample内のhelloのビルドと実行手順を説明します。
8.1. 各Example
Spresense SDKでは、NuttXがもつ、NSHシェルの機能を用いて、Built-inコマンドの形で各Exampleを実装・提供しています。 各Exampleは以下のようになります。
Example名 | 説明 |
---|---|
accel | 加速度センサのサンプルです |
adc | A/Dコンバータのサンプルです |
alarm | アラームのサンプルです |
asmp | ASMPのサンプルです |
audio_player | オーディオプレーヤのサンプルです |
audio_recorder | オーディオレコーダのサンプルです |
audio_through | オーディオパス |
camera | カメラのサンプルです |
colorsensor | 色センサのサンプルです |
epaper | 電子ペーパのサンプルです |
fwupdate | Firmwareアップデートのサンプルです |
geofence | Geofenceのサンプルです |
gnss | GPSのサンプルです |
gyro | ジャイロセンサのサンプルです |
hello | Cベースのhelloworldサンプルです |
helloxx | C++のhelloworldサンプルです |
light | 照度センサのサンプルです |
mag | 地磁気センサのサンプルです |
press | 気圧センサのサンプルです |
proximity | 近接センサのサンプルです |
tilt | チルトセンサのサンプルです |
8.2. "hello" Exampleの実行手順
8.2.1. "hello" Exampleのビルド方法
Configuration方法
まず、以下のコマンドでKernelおよびSDKのコンフィギュレーションを行います
$ tools/config.py --kernel release
$ tools/config.py board/spresense
次に、以下のコマンドでSDKのコンフィギュレーションを更新します
$ tools/config.py -m
するとMenuが開くので "Examples" → "Hello, World! example" にチェックを入れて保存してください
Build方法
ビルドは次のコマンドを使います
$ make buildkernel
$ make
Load方法
Build方法で生成された nuttx.spk
を tools/flash.sh
を用いて Spresense 基板にロードします。
$ tools/flash.sh -c /dev/ttyUSB0 nuttx.spk
8.2.2. "hello" Exampleの実行方法
ターミナルにて"hello"と入力し改行すると実行されます
9. ユーザーアプリの追加
ユーザーが新しいアプリを作成する場合は、以下の方法があります。
ASMP のWorkerプログラムをビルドする方法は、ASMP Framework の章を参照してください。 |
9.1. examplesに追加する
もっとも簡単な方法は、hello
をテンプレートとして使用することです。 hello
をディレクトリごと examples
以下にコピーし、Makefile
、Make.defs
、Kconfig
、hello_main.c
の中にある hello
の文字列をすべて任意の文字列に置き換えてください。hello_main.c
のファイル名も任意のファイル名に変更してください。(アプリ追加例参照)
すべての文字列を置き換えたら、sdk
ディレクトリで一旦 make clean
をして、tools/config.py
でコンフィグメニューを表示します。 追加した項目が表示されるので、それを有効にします。
$ cd sdk $ make clean $ tools/config.py -m
Examples ---> [ ] My first app example (NEW)
Kconfig ファイルを追加または削除した場合は、sdk ディレクトリで make clean を実行することで、コンフィグメニューが更新されます。 |
有効にしたらSDKをビルドします。
$ make
作成された nuttx.spk
をインストールし、再起動すれば追加したアプリがコマンドとして実行可能になります。
9.1.1. アプリ追加例
config EXAMPLES_MYFIRSTAPP bool "My first app example" default n ---help--- Enable the my first app example if EXAMPLES_MYFIRSTAPP config EXAMPLES_MYFIRSTAPP_PROGNAME string "Program name" default "myfirstapp" depends on BUILD_KERNEL ---help--- This is the name of the program that will be use when the NSH ELF program is installed. config EXAMPLES_MYFIRSTAPP_PRIORITY int "My firstapp task priority" default 100 config EXAMPLES_MYFIRSTAPP_STACKSIZE int "My firstapp stack size" default 2048 endif
Kconfigファイル内の |
Kconfig ファイルに定義するオプションシンボルはSDKで一意である必要があります。シンボルが重複した場合、コンフィギュレーションツール実行時に警告が表示されます。 |
-include $(TOPDIR)/Make.defs -include $(SDKDIR)/Make.defs # My first app built-in application info CONFIG_EXAMPLES_MYFIRSTAPP_PRIORITY ?= SCHED_PRIORITY_DEFAULT CONFIG_EXAMPLES_MYFIRSTAPP_STACKSIZE ?= 2048 APPNAME = myfirstapp PRIORITY = $(CONFIG_EXAMPLES_MYFIRSTAPP_PRIORITY) STACKSIZE = $(CONFIG_EXAMPLES_MYFIRSTAPP_STACKSIZE) # My first app Example ASRCS = CSRCS = MAINSRC = myfirstapp_main.c CONFIG_EXAMPLES_MYFIRSTAPP_PROGNAME ?= myfirstapp$(EXEEXT) PROGNAME = $(CONFIG_EXAMPLES_MYFIRSTAPP_PROGNAME) include $(APPDIR)/Application.mk
上記のAPPNAME、PRIORITYおよびSTACKSIZEのいずれかが設定されていない場合は、コマンドになりません。 |
ifeq ($(CONFIG_EXAMPLES_MYFIRSTAPP),y) CONFIGURED_APPS += myfirstapp endif
|
9.2. 別ディレクトリに追加する
ユーザーがリポジトリの外でアプリを管理したい場合は、別のディレクトリを作成し、それをビルド対象に追加することができます。 別ディレクトリをビルド対象に加えるには以下のようにします。
-
任意のディレクトリをsdkと同じディレクトリに作成
-
examples
ディレクトリから、LibTarget.mk
、Makefile
、Make.defs
およびApplication.mk
をコピー -
LibTarget.mk
内のアーカイブのパスを変更 -
Application.mk
内のアーカイブのパスを変更 -
Makefile
内のBIN
変数のファイル名をLibTarget.mk
で変更したファイル名と同じファイル名に変更 -
上述のexamplesに追加すると同じ手順で、新規に作成したディレクトリ内に任意のアプリケーションを追加
-
sdk
ディレクトリでmake
9.2.1. 別ディレクトリ追加例
ディレクトリを myapps
、アプリケーションを myfirstapp
とした場合の例
|-- sdk |-- examples |-- myapps |-- Application.mk |-- LibTarget.mk |-- Make.defs |-- Makefile `-- myfirstapp |-- Kconfig |-- Make.defs |-- Makefile `-- myfirstapp_main.c
# ディレクトリをexamplesからmyappsに変更 # アーカイブ名をlibexamplesからlibmyappsに変更 $(SDKDIR)$(DELIM)..$(DELIM)myapps$(DELIM)libmyapps$(LIBEXT): context $(Q) $(MAKE) -C $(dir $@) TOPDIR="$(TOPDIR)" SDKDIR="$(SDKDIR)" $(notdir $@) lib$(DELIM)libmyapps$(LIBEXT): $(SDKDIR)$(DELIM)..$(DELIM)myapps$(DELIM)libmyapps$(LIBEXT) $(Q) install $< $@ EXTLIBS += lib$(DELIM)libmyapps$(LIBEXT)
|
MENUDESC = "My Apps" BIN = libmyapps$(LIBEXT) # libexamplesをlibmyappsに変更 include $(SDKDIR)/Makefile.ext
|
ifeq ($(WINTOOL),y) BIN = "${shell cygpath -w $(APPDIR)$(DELIM)libmyapps$(LIBEXT)}" # libexamplesをlibmyappsに変更 INSTALL_DIR = "${shell cygpath -w $(BIN_DIR)}" else BIN = $(APPDIR)$(DELIM)libmyapps$(LIBEXT) # libexamplesをlibmyappsに変更 INSTALL_DIR = $(BIN_DIR) endif ROOTDEPPATH = --dep-path . VPATH = all: .built .PHONY: clean preconfig depend distclean .PRECIOUS: $(APPDIR)/libmyapps$(LIBEXT) # libexamplesをlibmyappsに変更
9.3. ツールを使用する
前述のexamplesに追加する手順及び別ディレクトリに追加する手順は、下記ヘルパーツールを使用して簡単に行うことができます。
usage: mkcmd.py [-h] [-d BASEDIR] [-v] <app name> [desc] Create a new application positional arguments: <app name> New application name desc Menu description optional arguments: -h, --help show this help message and exit -d BASEDIR, --basedir BASEDIR Base directory to create new application -v, --verbose Verbose messages This tool create a new application at examples directory. You can use '-d' option to change application directory, it is a same level of sdk and examples.
usage: mkappsdir.py [-h] [-v] <dir name> [desc] Create a new application directory at the outside of sdk repository This tool must be run on sdk directory. positional arguments: <dir name> New application directory name desc Menu description optional arguments: -h, --help show this help message and exit -v, --verbose verbose messages
上記ツールは、 sdk ディレクトリで実行してください。 |
9.3.1. 使用例
myfirstapp
を追加する$ cd spresense/sdk $ tools/mkcmd.py myfirstapp "My first app example"
myapps
を作成し、そこにアプリ myfirstapp
を追加する$ cd spresense/sdk $ tools/mkappsdir.py myapps "My Apps" $ tools/mkcmd.py -d myapps myfirstapp "My first app example"
10. Debug方法
ここでは、ユーザアプリをデバッグする際に有効となる情報やツールなどについて説明します。
10.1. Eclipse IDEでのデバッグ方法
10.1.1. ハードウェア
-
CMSIS-DAP 準拠デバッグアダプタ (ICE)
-
SWD connector
SWDコネクタのマウント
SWD コネクタのマウント方法は、ハードウェアドキュメントを参照してください。
10.1.2. OpenOCDのインストール
ビルドの準備
OpenOCDのビルドには、以下の外部ライブラリが必要です。Ubuntuでは以下のようにしてインストールします。
$ sudo apt install build-essential autoconf automake libtool pkg-config libusb-1.0.0-dev libhidapi-dev
ビルドとインストール
-
このリポジトリからクローンします https://github.com/sonydevworld/spresense-openocd
-
ビルド&インストール
$ git clone https://github.com/sonydevworld/spresense-openocd.git $ cd spresense-openocd $ ./bootstrap $ ./configure --disable-werror $ make $ sudo make install
Ubuntuの場合は、さらに以下のコマンドでICEに接続する権限を付与する必要があります。 ICEを接続する前に行ってください。
$ sudo cp /usr/local/share/openocd/contrib/60-openocd.rules /etc/udev/rules.d/ $ sudo udevadm control --reload
10.1.3. Eclipseのインストール
Eclipseを使用するために、JREをインストールします。
$ sudo apt install default-jre
Eclipse 公式サイト ( https://www.eclipse.org/downloads/ ) から最新版をダウンロードします。インストーラー(eclipse-inst
)を実行すると、以下のようなメニューが表示されるので、Eclipse IDE for C/C++ Developers
をクリックします。
Eclipseのインストールが完了したら、追加のプラグインをインストールします。
-
メニュー
Help
→Eclipse Marketplace…
-
検索ボックスに
gnu mcu
と入力し、Go
ボタンを押す -
GNU MCU Eclipse
をインストール
10.1.4. Eclipse (CDT)のインストール (obsolete)
- WARNING
-
(2019/02/22現在) Eclipse 環境の更新に伴い、ここに記載されているインストール手順は情報が古く、正しくインストールできないことがあります。前章のEclipseのインストールを参照してください。
Ubuntuでは、APT経由でインストールできます。
$ apt install eclipse-cdt
Windowsの場合は、Eclipseのサイトからインストーラをダウンロードしてインストールしてください。
Eclipseのインストールが完了したら、追加のプラグインをインストールします。
-
メニュー
Help
→Install New Software…
-
ダウンロードするサイトを選択し、以下のプラグインをインストール
-
CDT Optional Features
-
C/C++ GDB Hardware Debugging
-
-
10.1.5. Spresense ボードの設定
Spresenseボードは、nuttx
という名前のファイルがインストールされている場合、自動的にこれを起動します。 ファイルがインストールされていない場合は、デバッグモードに入ります。 Eclipseでデバッグを行う前に、Spresenseボードをデバッグモードにする必要があります。
-
'r' キーを押したままリセット
-
nuttx
という名前のファイルを別の名前に変更
updater# mv nuttx nuttx.old
デバッグモードに入ると、ターミナルに以下のメッセージが表示されます。
Waiting for debugger connection..
デバッグモードを解除したい場合は、変更したファイルを元の nuttx
に戻してください。
GDBの設定
OpenOCDはgdbserverとして動作し、SpresenseでEclipseを使用する場合はこの機能を使用するため、ユーザーはgdbの設定をする必要があります。 Eclipseワークスペースの .cproject
, .project
のあるディレクトリに .gdbinit
という名前のファイルを作成し、以下のスクリプトを記述します。
define hookpost-load if &g_readytorun != 0 eval "monitor nuttx.pid_offset %d", &((struct tcb_s *)(0))->pid eval "monitor nuttx.xcpreg_offset %d", &((struct tcb_s *)(0))->xcp.regs eval "monitor nuttx.state_offset %d", &((struct tcb_s *)(0))->task_state eval "monitor nuttx.name_offset %d", &((struct tcb_s *)(0))->name eval "monitor nuttx.name_size %d", sizeof(((struct tcb_s *)(0))->name) end end
10.1.6. デバッグ (GNU MCU Eclipse plugin)
設定
-
メニュー
Run
→Debug Configurations…
-
GDB OpenOCD Debugging
を選択 -
New
ボタンを押して新しいOpenOCDデバッグ設定を作成 -
下記を設定する
-
Main
-
C/C++ Application →
nuttx
-
-
Debugger
-
OpenOCD Setup
グループのExecutable path
,Actual executable
→openocd
へのパス -
Start GDB Session
→ 有効 -
GDB Client Setup
グループのActual executable
→arm-none-eabi-gdb
へのパス -
OpenOCD Setup
グループのConfig options
→ 下記を設定
-
-f interface/cmsis-dap.cfg -f target/cxd5602_adsp0.cfg
-
Startup
-
Load symbols
→ 有効 -
Load executable
→ 有効 -
Debug in RAM
→ 有効
-
デバッグの実行
以下の手順でデバッグを開始します。
-
Spresenseボードのリセットスイッチを押す
-
OpenOCDデバッグ設定の
Debug
ボタンを押す
10.1.7. デバッグ (obsolete)
- WARNING
-
(2019/02/22現在)
GDB Hardware Debugging
プラグインを使用したデバッグができないことが確認されています。前章のデバッグ (GNU MCU Eclipse plugin)を参照してください。-
メニュー
Run
→Debug Configurations…
-
GDB Hardware Debugging
を選択 -
New
ボタンを押して新しいデバッグ設定を作成 -
下記を設定する
-
Spresenseボードのリセットスイッチを押す
-
Debug
ボタンを押してデバッグ開始
-
-
Main
-
C/C++ Application →
nuttx
-
-
Debugger
-
GDB Command →
arm-none-eabi-gdb
へのパス -
Use remote target → 有効
-
JTAG Device → OpenOCD を選択
-
GDB Connection String → 下記を設定
-
| openocd -f interface/cmsis-dap.cfg -f target/cxd5602_adsp0.cfg -c "gdb_port pipe; log_output openocd.log"
-
Startup
-
Reset and Delay → 有効
-
Halt → 有効
-
Load image → 有効
-
Load symbols → 有効
-
11. Spresense SDKが提供する機能
ここでは、Spresense SDKで提供している各機能について、説明します。
11.1. BSP
11.1.1. 概要
BSP (Board Support Package) は、ボード固有の設定や処理を行うためのソースコードが含まれています。
NuttXでは、以下のようなドライバソフトウェアアーキテクチャになっており、BSPディレクトリには Driver (Lower Half)
、 Board specific code
および Architecture specific code
などのドライバソフトウェアが含まれます。
Driver (Lower Half)
NuttX には、標準的なデバイスやバスに対してUpper Halfと呼ばれるドライバがあります。Upper Halfドライバは、アプリケーションへのインターフェースやプロトコル処理などを行いますが、それ単体で動作させることはできません。このため、BSPでLower Half ドライバを適切に実装することで、そのデバイスを使用することができるようになります。
Spresense SDKが提供しているLower Halfドライバには以下のようなものがあります。
-
I2C (cxd56_i2c.c)
-
PWM (cxd56_pwm.c)
-
RTC (cxd56_rtc_lowerhalf.c)
-
SDIO Device (cxd56_sdhci.c)
-
Serial Device (cxd56_serial.c)
-
SPI (cxd56_spi.c)
-
Timer (cxd56_timer.c)
-
USB Device (cxd56_usbdev.c)
-
Watchdog timer (cxd56_wdt.c)
NuttXのドライバに関する詳細は、NuttX Device Drivers を参照してください。
Architecture specific code
アーキテクチャ固有ドライバは、チップに搭載されている機能を使用するためのドライバ(電源管理など)が含まれます。
Board specific code
ボード固有の処理には、ボードの実装によって可変になるもの(ピン設定など)があります。これらの処理には、ある程度共通化できるものと完全にボード依存になっているものに分かれます。これらはそれぞれ、bsp/board/common
および bsp/board/<board name>
( <board name>
は対応しているボード名)に格納されています。
また、ボードの初期化処理もこれに含まれます。
11.1.2. ディレクトリ構成
sdk/bsp
|-- board
| |-- common
| `-- spresense
|-- include
| |-- arch
| | `-- chip
| |-- nuttx
| | |-- lcd
| | |-- sensors
| | `-- video
| `-- sdk
|-- scripts
`-- src
ディレクトリ名 | 内容 |
---|---|
| ボード固有の処理だが、処理シーケンスが同じで、マクロの変更等で共通化できるルーチンが含まれます。 |
| Spresense基板を動作させるための処理(ピン設定など)が含まれます。 |
| SoCに搭載されているデバイスドライバが提供しているAPIを呼び出す場合のヘッダファイルが含まれます。 |
| アーキテクチャ非依存のデバイスのヘッダファイルが含まれます。(NuttXカーネルとの互換性のため、このディレクトリ名となっています) |
| Spresense SDKで共通のヘッダファイルが含まれます。SDKのコンフィグヘッダ( |
|
|
| Lower Halfドライバやチップ固有のデバイスドライバなどが含まれます。 |
11.1.3. ボードの初期化処理
メインCPUが起動すると、NuttXカーネルの初期化が行われ、SDKのエントリポイント関数が呼び出されます。デフォルトの設定では、nsh
(NuttShell) を起動します。
nsh
では、ボードの初期化( boardctl()
)が呼び出されます。実際のボードの初期化は、NuttXのルールに従い board_app_initialize
関数に実装されています。この関数では、使用するボードの初期化を行うための処理を行います。
初期化の処理には、Spresenseを使用するための最低限の機能の初期化や、使用するデバイスドライバの初期化などがあります。
SDK_USER_ENTRYPOINT で起動するタスクを nsh から変更した場合は、ユーザーが自ら boardctl() の呼び出しを行う必要があります。 |
11.2. GPIO/Pin Specification
11.2.1. Software Structure
11.2.2. GPIO Driver Interface
gpioifは、アプリケーションに対して以下の機能を提供します。
-
GPIO ピン設定
-
機能モード
-
入出力設定
-
ドライブ電流/スルーレート設定
-
プルアップ/プルダウン設定
-
-
GPIO 割り込み設定
-
レベル・エッジトリガ設定
-
ノイズフィルタ設定
-
-
GPIO 入出力制御
-
GPIO 状態取得
APIの詳細は、こちらを参照してください。
GPIO Utility tool
System tools に GPIO Command が用意されており、 CONFIG_SYSTEM_GPIO=y にすることで、NuttShell から gpio
コマンドが利用可能です。
gpio コマンドの使い方
nsh> gpio
USAGE: gpio command
stat [<from_pin>] [<end_pin>]
conf <pin> [-m <0|1|2|3>] [-i] [-H] [-p <0|1|2|3>]
-m: function mode
-i: input enable
-H: Higher drive current/slew rate
-p: 0=float, 1=pullup, 2=pulldown, 3=buskeeper
read <pin>
write <pin> <0|1|-1>
CONFIG_SYSTEM_GPIO_STATUS=y にすることで、NuttShell から gpio stat
状態表示コマンドが有効になります。
nsh> gpio stat
-------------------------------------------------------------
( No)PIN NAME : Mode I/O mA Pull Read IRQ Type NF EN
-------------------------------------------------------------
( 1)PIN_I2C4_BCK : 1 I/ 2 -- 1 -1
( 2)PIN_I2C4_BDT : 1 I/ 2 -- 1 -1
( 3)PIN_PMIC_INT : 1 I/ 2 -- 0 -1
( 4)PIN_RTC_IRQ_OUT : 0 / 2 -- 0 -1
( 5)PIN_AP_CLK : 0 / 2 -- 0 -1
( 6)PIN_GNSS_1PPS_OUT : 0 / 2 -- 0 -1
( 17)PIN_SPI0_CS_X : 1 / 2 -- 0 -1
( 18)PIN_SPI0_SCK : 1 I/ 2 -- 1 -1
( 19)PIN_SPI0_MOSI : 0 / 2 -- 0 -1
( 20)PIN_SPI0_MISO : 0 / 2 -- 0 -1
:
(101)PIN_MCLK : 0 / 2 -- 0 -1
(102)PIN_PDM_CLK : 0 / 2 -- 0 -1
(103)PIN_PDM_IN : 0 / 2 -- 0 -1
(104)PIN_PDM_OUT : 0 / 2 -- 0 -1
(105)PIN_USB_VBUSINT : 1 I/ 2 -- 1 -1
11.2.3. Pin specification
ピンは複数の機能が割り当てられており、Mode(0-3) によって機能を変更することができます。 各種ピンは Pin Group に割り当てられており、Mode指定によって Pin Group 単位で機能が変更されます。
例えば、 PWM0,1 について、
-
Mode0
の場合は、PWM0 と PWM1 の Pin は 両方とも GPIO 機能に設定されます -
Mode1
の場合は、PWM0 と PWM1 の Pin は 両方とも PWM 機能に設定されます
つまり、Group 単位で機能が割り当てられるため、PWM0 を GPIO として、PWM1 を PWM として個別に設定することはできません |
全てのピンは、初期状態で Mode0 (GPIO)
になります。
Pin List
-
Pin Name: sdk/bsp/include/arch/chip/pin.h に、
PIN_XXX
という形式で定義されています -
WLCSP: 100-pin package (CXD5602GF) の定義です。いくつか使用できないピンがあります。
-
FCBGA: 185-pin full package (CXD5602GG) の定義です。Spresense 基板で使用されています。
-
Mode0-3: それぞれのモードにおけるピンの機能が記述されています。
Pin Name | Arduino compatible Pin Name | WLCSP | FCBGA | Mode0 | Mode1 | Mode2 | Mode3 | |
---|---|---|---|---|---|---|---|---|
I2C4_BCK | * | * | GPIO | I2C#4 | ||||
I2C4_BDT | * | * | ||||||
PMIC_INT | * | * | GPIO | PMIC | PMIC Interrupt | |||
RTC_IRQ_OUT | * | GPIO | RTC_IRQ_OUT | RTC_IRQ_OUT | ||||
AP_CLK | * | * | GPIO | AP_CLK | PMU_WDT | PMU_WDT | ||
GNSS_1PPS_OUT | * | GPIO | GNSS_1PPS_OUT | CPU_WDT | CPU_WDT | |||
SPI0_CS_X | * | * | GPIO | UART#1 | SPI#0 | |||
SPI0_SCK | * | * | ||||||
SPI0_MOSI | * | GPIO | I2C#2 | |||||
SPI0_MISO | * | |||||||
SPI1_CS_X | * | * | GPIO | SPI#1 | SPI#0 | |||
SPI1_SCK | * | * | ||||||
SPI1_IO0 | * | * | ||||||
SPI1_IO1 | * | * | ||||||
SPI1_IO2 | * | * | GPIO | |||||
SPI1_IO3 | * | * | ||||||
SPI2_CS_X | * | * | GPIO | SPI#2 | UART#0 | I2C#3 | ||
SPI2_SCK | * | * | ||||||
SPI2_MOSI | D04 | * | * | GPIO | ||||
SPI2_MISO | D08 | * | * | |||||
HIF_IRQ_OUT | D02 | * | * | GPIO | HIF_IRQ_OUT | HIF_IRQ_OUT | GNSS_1PPS_OUT | |
HIF_GPIO0 | * | GPIO | GPS_EXTLD | |||||
SEN_IRQ_IN | D22 | * | * | GPIO | SEN_IRQ_IN | |||
SPI3_CS0_X | * | * | GPIO | SPI3_CS0_X | ||||
SPI3_CS1_X | D07 | * | * | GPIO | SPI3_CS1_X | |||
SPI3_CS2_X | * | * | GPIO | SPI3_CS2_X | ||||
SPI3_SCK | * | * | GPIO | SPI#3 | ||||
SPI3_MOSI | * | * | ||||||
SPI3_MISO | * | * | ||||||
I2C0_BCK | D15 | * | * | GPIO | I2C#0 | |||
I2C0_BDT | D14 | * | * | |||||
PWM0 | D06 | * | * | GPIO | PWM#0,1 | |||
PWM1 | D05 | * | * | |||||
PWM2 | D09 | * | * | GPIO | PWM#2,3 | I2C#1 | ||
PWM3 | D03 | * | * | |||||
IS_CLK | * | GPIO | CMOS Image Sensor | |||||
IS_VSYNC | * | |||||||
IS_HSYNC | * | |||||||
IS_DATA0 | * | |||||||
IS_DATA1 | * | |||||||
IS_DATA2 | * | |||||||
IS_DATA3 | * | |||||||
IS_DATA4 | * | |||||||
IS_DATA5 | * | |||||||
IS_DATA6 | * | |||||||
IS_DATA7 | * | |||||||
UART2_TXD | D01 | * | * | GPIO | UART#2 | |||
UART2_RXD | D00 | * | * | |||||
UART2_CTS | D27 | * | * | |||||
UART2_RTS | D28 | * | * | |||||
SPI4_CS_X | D10 | * | * | GPIO | SPI#4 | |||
SPI4_SCK | D13 | * | * | |||||
SPI4_MOSI | D11 | * | * | |||||
SPI4_MISO | D12 | * | * | |||||
EMMC_CLK | D23 | * | * | GPIO | eMMC | SPI#5 | ||
EMMC_CMD | D24 | * | * | |||||
EMMC_DATA0 | D16 | * | * | |||||
EMMC_DATA1 | D17 | * | * | |||||
EMMC_DATA2 | D20 | * | * | GPIO | ||||
EMMC_DATA3 | D21 | * | * | |||||
SDIO_CLK | * | GPIO | SDIO | SPI#5 | ||||
SDIO_CMD | * | |||||||
SDIO_DATA0 | * | |||||||
SDIO_DATA1 | * | |||||||
SDIO_DATA2 | * | GPIO | ||||||
SDIO_DATA3 | * | |||||||
SDIO_CD | * | GPIO | SDIO | |||||
SDIO_WP | * | |||||||
SDIO_CMDDIR | * | GPIO | SDIO | |||||
SDIO_DIR0 | * | |||||||
SDIO_DIR1_3 | * | |||||||
SDIO_CLKI | * | GPIO | SDIO | |||||
I2S0_BCK | D26 | * | * | GPIO | I2S#0 | |||
I2S0_LRCK | D25 | * | * | |||||
I2S0_DATA_IN | D19 | * | * | |||||
I2S0_DATA_OUT | D18 | * | * | |||||
I2S1_BCK | LED0 | * | GPIO | I2S#1 | ||||
I2S1_LRCK | LED1 | * | ||||||
I2S1_DATA_IN | LED2 | * | ||||||
I2S1_DATA_OUT | LED3 | * | ||||||
MCLK | * | * | GPIO | MCLK | ||||
PDM_CLK | * | * | GPIO | PDM | ||||
PDM_IN | * | * | ||||||
PDM_OUT | * | * | ||||||
USB_VBUSINT | * | * | GPIO | USB VBUS Interrupt |
Pin Configuration
Pin 設定は、pinconfigドライバで設定されます。例えば、Mode0(GPIO)以外の I2C や SPI 機能として使用する場合は、 それぞれ I2C, SPI ドライバの中で、Pin が設定されます。そのため、アプリケーションが Mode の変更などを 意識する必要はありません。
Mode0(GPIO) として使用する場合に限り、前述した gpioif を使用して設定してください。
Board Specific Pin Pull and Drive Current Setting
Pin のプル設定やドライブ電流のデフォルト設定は、 sdk/bsp/src/chip/cxd5602_pinconfig.h に定義されています。
基本的に、プル設定は Hi-Z フローティング状態、ドライブ電流は多くが 2mA に設定されています。
基板に依存して、これらの設定を変更する場合は、CONFIG_BOARD_CUSTOM_PINCONFIG=y 有効にして、 sdk/bsp/board/spresense/include/board_pinconfig.h を参考に定義してください。
Spresense基板の例では、
/* Customize from default to the board specific pin configuration
* The default pin configurations are defined in sdk/bsp/src/chip/cxd5602_pinconfig.h.
* Mode: shared pin function mode
* ENZI: 1=Input Enable, 0=Input Disable
* 4mA : Drive Current 1=4mA, 0=2mA
* Pull: 0=HiZ floating, PINCONF_PULLUP, PINCONF_PULLDOWN
* M E P
* P o N 4 u
* i d Z m l
* n e I A l
*/
#undef PINCONF_UART2_CTS
#define PINCONF_UART2_CTS PINCONF(PIN_UART2_CTS, 1, 1, 0, PINCONF_PULLDOWN)
#undef PINCONF_SPI4_CS_X
#undef PINCONF_SPI4_SCK
#undef PINCONF_SPI4_MOSI
#define PINCONF_SPI4_CS_X PINCONF(PIN_SPI4_CS_X, 1, 0, 1, 0)
#define PINCONF_SPI4_SCK PINCONF(PIN_SPI4_SCK, 1, 0, 1, 0)
#define PINCONF_SPI4_MOSI PINCONF(PIN_SPI4_MOSI, 1, 0, 1, 0)
#undef PINCONF_PWM0
#undef PINCONF_PWM1
#undef PINCONF_PWM2
#undef PINCONF_PWM3
#define PINCONF_PWM0 PINCONF(PIN_PWM0, 1, 0, 1, 0)
#define PINCONF_PWM1 PINCONF(PIN_PWM1, 1, 0, 1, 0)
#define PINCONF_PWM2 PINCONF(PIN_PWM2, 1, 0, 1, 0)
#define PINCONF_PWM3 PINCONF(PIN_PWM3, 1, 0, 1, 0)
-
UART2_CTS ピンを Pull Down を有効
-
SPI4 のドライブ電流を 2mA から 4mA に変更
-
PWM のドライブ電流を 2mA から 4mA に変更
しています。
11.3. Audio Subsystem
11.3.1. General
CXD5602 はハイレゾリューションを扱えるオーディオ機能(オーディオ・サブシステム)が搭載されています。 オーディオ・サブシステムの機能概要を以下に示します。
-
Audio Codec ハードウェア (AD/DA, DNC, DEQ, etc.) の制御
-
Audio Player 機能
-
Audio Recorder 機能
-
Bluetooth 関連機能(for BT-A2DP)
-
Sound Effector 機能(例えば、音声通話用のバンドパスフィルタなど)
このドキュメントは、CXD5602 のハードウェア上で実現できるオーディオ機能を制御するためのソフトウェアが記載されています。オーディオハードウェアに関しては、別紙Spresenseハードウェアドキュメント を参照ください。
現在のファームウェアでは、Bluetooth 関連機能(for BT-A2DP) 及び、Sound Effector 機能(例えば、音声通話用のバンドパスフィルタなど) は、未対応です。 |
11.3.2. レイヤ構造について
オーディオ・サブシステムのスタックダイアグラムを以下に示します。
オーディオ・サブシステムは、大きく3つのレイヤを持ちます。
- Audio Manager(High Level API)
-
最上位のレイヤで、最上位の抽象度での制御を行うレイヤです。システム全体の整合を取りながら制御します。
- Object Layer(ObjectLevel API)
-
各機能Objectのレイヤで、機能ブロック単位での抽象度で制御を行うレイヤです。各機能内の信号処理・DSP処理などに関して整合を取ります。
- Component Layer(Low Level API)
-
各信号処理componetのレイヤで、信号処理ブロック単位での抽象度で制御を行うレイヤです。信号処理ブロックの組み合わせで処理を構成することで、自由度の高いオーディオ処理を実現できます。
11.3.3. High Level API
High Level API は Audio Utility
の Audio Manager
が提供する API です。
コマンド送受信による制御ついて
High Level API は、オーディオ・サブシステムをコマンドオブジェクトで制御します。(High Level API Command System)
コマンドオブジェクトは、AS_SendAudioCommandでオーディオ・サブシステムに送信されます。
送信されるコマンドオブジェクトは、AudioCommandになります。
コマンドの詳細は コマンドフォーマット に記載します。
オーディオ・サブシステムは、送信されたコマンドに応じて処理を行い、結果を返します。
結果は、AudioResult オブジェクトで返され、AS_ReceiveAudioResult で取得することができます。
リザルトの詳細は リザルトフォーマット に記載します。
コマンドは同期コマンドになります。コマンドを発行したらリザルトが返るまで次のコマンドを発行できません。Audio Manager を介して High Level API を使う場合は、送信・受信の手順が1コマンド単位で対になるようにプログラムしてください。 |
制御データ形式(コマンドフォーマット)について
コマンドオブジェクト AudioCommand は1 ワード(4 バイト)の AudioCommandHeader で始まり、その後に必要なだけのパラメータ領域を付加したデータ構造です。
コマンドオブジェクトは、ワード単位をベースとしているため、1 ワード(32 ビット、4バイト)の整数倍の長さで構成されています。
コマンドオブジェクトのreserved フィールドには、0 を設定してください。
typedef struct
{
AudioCommandHeader header;
union
{
Command Parameters (Payload)
...
...
};
} AudioCommand;
全てのコマンドオブジェクトの先頭1 ワード(4 バイト)は、以下の形式です。この1 ワードをコマンドヘッダ('AudioCommandHeader')と呼びます。
typedef struct
{
uint8_t reserved;
uint8_t sub_code;
uint8_t command_code;
uint8_t packet_length;
} AudioCommandHeader;
- packet_length
-
コマンドヘッダを含めたコマンドオブジェクトの長さを示します。
全てのコマンドオブジェクトは整数個のワード(4バイト)で構成されており、
packet_lengthで指定する値はコマンド・パケットのワード長、すなわちコマンドオブジェクトのバイト長の4分の1となります。 - command_code
-
コマンドに固有のコードです。値0x00は使用しません。
コマンドの種類については コマンド一覧 を参照してください。 - sub_code
-
各コマンドにおいて設定および制御を行う対象を識別するためのコードです。
通知データ形式(リザルトフォーマット)
リザルトオブジェクトは1 ワード(4バイト) 'AudioResultHeader' で始まり、その後に必要なだけのパラメータ領域が付加されたデータ構造です。
リザルトオブジェクトは、ワード単位をベースとしているため、1 ワード(32 ビット、4 バイト)の整数倍の長さで構成されています。
リザルトオブジェクトのreservedフィールドは無視してください。
typedef struct
{
AudioResultHeader header;
union
{
Result Parameters (Payload)
...
...
};
} AudioResult;
全てのリザルトオブジェクトの先頭1ワード(4バイト)は、以下の形式です。
この1ワードをリザルト・ヘッダ('AudioResultHeader')と呼びます。
typedef struct
{
uint8_t reserved;
uint8_t sub_code;
uint8_t result_code;
uint8_t packet_length;
} AudioResultHeader;
- packet_length
-
リザルトヘッダを含めたリザルトオブジェクトの長さを示します。
全てのリザルトオブジェクトは整数個のワード(4バイト)で構成されており、
packet_lengthで指定する値はリザルトオブジェクトのワード長、すなわちリザルトオブジェクトのバイト長の4分の1となります。 - result_code
-
リザルトの種類を識別するためのコードです。
リザルトの種類については リザルト一覧 を参照してください。 このコードにより、パラメータ領域のデータ内容が変わります。 - sub_code
-
実行したコマンドのsub_codeと同じ値が入ります。
コマンド一覧
各コマンドの一覧は以下になります。
コマンド・ヘッダ にこのコマンドIDを指定し送信することで機能を使用することが出来ます。
どの状態でも共通のコマンドです。 どの状態からも呼べます。
Command Name | Command ID | Response Result | Detail |
---|---|---|---|
0x01 | NotifyStatus | 現在の状態を取得します |
詳細は、以下の、Doxygenファイルを参照してください。
Baseband HWの初期化を行うコマンドです。 Ready状態からのみ呼べます。
Command Name | Command ID | Response Result | Detail |
---|---|---|---|
0x53 | InitMicGainCmplt | マイクゲインの設定を行います | |
0x54 | InitI2SCmplt | I2Sの設定を行います | |
0x56 | InitOutputSelectCmplt | 発音するデバイスの設定を行います | |
0x58 | InitClearStereoCmplt | クリアステレオ機能の設定を行います | |
0x5c | SetRenderingClkCmplt | HiReso設定の切り替えを行います |
詳細は、以下の、Doxygenファイルを参照してください。
Baseband HWの設定を行うコマンドです。 PowerOff状態以外の状態から呼べます。
Command Name | Command ID | Response Result | Detail |
---|---|---|---|
0x59 | SetVolumeCmplt | 発音時のボリュームの設定を行います | |
0x5a | SetVolumeMuteCmplt | 発音ボリュームのMute設定を行います | |
0x5b | SetBeepCmplt | BEEP音の設定を行います |
詳細は、以下の、Doxygenファイルを参照してください。
Playerの制御を行うコマンドです。 Player状態から呼べます。
Command Name | Command ID | Response Result | Detail |
---|---|---|---|
0x21 | InitPlayCmplt | Playerの再生情報を設定します | |
0x22 | PlayCmplt | バッファの先頭からデコードを行います | |
0x23 | StopPlayCmplt | バッファの状態に依らずPlayerを停止させます | |
0x24 | ClkRecoveryComplete | 出音時間の微調整をします | |
0x25 | SetDecoderGainComplete | 出音レベルにL/RそれぞれGainをかけます |
詳細は、以下の、Doxygenファイルを参照してください。
Recorderの制御を行うコマンドです。 Recorder状態から呼べます。
Command Name | Command ID | Response Result | Detail |
---|---|---|---|
0x31 | InitRecCmplt | 音声記録機能を初期化します | |
0x32 | RecCmplt | 音声記録を開始します | |
0x33 | StopRecCmplt | 音声記録を停止します |
詳細は、以下の、Doxygenファイルを参照してください。
Command Name | Command ID | Response Result | Detail |
---|---|---|---|
0x71 | StatusChanged | Ready状態へ遷移します | |
0x72 | StatusChanged | Power Off 状態へ遷移します | |
0x73 | StatusChanged | Baseband状態へ遷移します | |
0x74 | StatusChanged | Player状態へ遷移します | |
0x75 | StatusChanged | Recorder状態へ遷移します | |
0x76 | StatusChanged | Ready状態へ遷移します | |
0x77 | StatusChanged | Audio path through状態へ遷移します |
詳細は、以下の、Doxygenファイルを参照してください。
リザルト一覧
オーディオサブシステムからのリザルト通知です。
リザルト・ヘッダ にこのリザルトIDを格納して応答されます。
Result Name | Command ID | Trigger Command | Detail |
---|---|---|---|
0x01 | GetStatus | 現在の状態を通知します。 |
Result Name | Command ID | Trigger Command | Detail |
---|---|---|---|
0x53 | InitMicGain | マイクゲインの設定が完了したことを通知します。 | |
0x54 | InitI2SParam | I2Sの設定が完了したことを通知します。 | |
0x56 | InitOutputSelect | 発音するデバイスの設定が完了したことを通知します。 | |
0x58 | InitClearStereo | クリアステレオ機能の設定が完了したことを通知します。 | |
0x5c | InitRenderClk | HiReso設定の切り替えが完了したことを通知します。 |
Result Name | Command ID | Trigger Command | Detail |
---|---|---|---|
0x59 | SetVolume | 発音時のボリュームの設定が完了したことを通知します。 | |
0x5a | SetVolumeMute | 発音ボリュームのMute設定が完了したことを通知します。 | |
0x5b | SetBeep | BEEP音の設定が完了したことを通知します。 |
Result Name | Command ID | Trigger Command | Detail |
---|---|---|---|
0x21 | InitPlayer | Playerの再生情報設定が完了したことを通知します。 | |
0x22 | StartPlayer | Playerが動作を開始したことを通知します。 | |
0x23 | StopPlayer | Playerが動作を停止したことを通知します。 | |
0x24 | ClkRecovery | 出音時間の微調整設定が完了したことを通知します。 | |
0x25 | SetDecoderGain | 出音レベルL/R Gain設定が完了したことを通知します。 |
Result Name | Command ID | Trigger Command | Detail |
---|---|---|---|
0x31 | nitRecorder | 音声記録機能の初期化が完了したことを通知します。 | |
0x32 | StartRecorder | 音声記録が開始したことを通知します。 | |
0x33 | StopRecorder | 音声記録が停止したことを通知します。 |
Result Name | Command ID | Trigger Command | Detail |
---|---|---|---|
0x71 | PowerOn SetPowerOffStatus SetBaseBandStatus SetPlayerStatus SetRecorderStatus SetReadyStartus | 状態遷移が完了したことを通知します。 |
Result Name | Command ID | Trigger Command | Detail |
---|---|---|---|
0xf1 | エラーが発生したことを通知します。 | ||
0xf2 | 内部処理エラーが発生したことを通知します。 |
コマンドパケット詳細
リザルトパケット詳細
ErrorAttention
状態遷移
High Level API は、複数の状態を持ちます。 以下に、状態遷移図を示します。
各モードの説明は以下になります。
PowerOff 状態:
オーディオ・サブシステムのオブジェクトを生成し、起動した直後の状態です。オーディオを使用しない場合には、この状態に遷移しておくことで、オーディオブロックでの消費電力をほぼ0にします。
AUDCMD_POWERON コマンドによって、Ready状態にのみ遷移します。
Ready状態:
オーディオブロックに電源を入れて、動作モードにオーディオ機能を動作できるように準備している状態です。この状態では、消費電力は下がっていないのですが、IO/アナログが起動している状態ですので、モード遷移が速やかに行えます。
状態の遷移は、以下になります。
AUDCMD_SETPOWEROFFSTATUS コマンドによって、PowerOff状態に遷移できます。 AUDCMD_SETPLAYERSTATUS コマンドによって、Player状態に遷移できます。 AUDCMD_SETRECORDERSTATUS コマンドによって、Recorder状態に遷移できます。 AUDCMD_SETBASEBANDSTATUS コマンドによって、Baseband状態に遷移できます。
Player状態:
SDカードなどのストレージや WiFi/LTE などのネットワークからの圧縮音声ファイルをデコードし、AnalogOutやI2Sに発音する機能を実現する状態です。状態の中にPlayerReady状態とPlayerActive状態の2つのサブ状態を持ちます。 PlayerReady状態は、音楽再生停止の状態です。AUDCMD_PLAYPLAYER によってPlayerActiveに遷移し音楽再生動作を行います。 PlayerActive状態は、音楽再生中の状態です。AUDCMD_STOPPLAYER によってPlayerReadyに遷移し音楽再生を停止します。
AUDCMD_SETREADYSTATUS コマンドによって、Ready状態にのみ遷移します。
Recorder状態:
Mic、I2Sから入力された音声データを圧縮して、SDカードなどのストレージに書き出したり、WiFi/LTE などのネットワークに打ち上げて記録する機能を実現する状態です。 状態の中にRecorderReady状態とRecorderActive状態の2つのサブ状態を持ちます。 RecorderReady状態は、音声記録停止の状態です。AUDCMD_STARTREC によってRecorderActiveに遷移し音声記録動作を行います。 RecorderActive状態は、音声記録中の状態です。AUDCMD_STOPREC によってRecorderReadyに遷移し音声記録を停止します。
AUDCMD_SETREADYSTATUS コマンドによって、Ready状態にのみ遷移します。
Baseband状態:
Mic、または、I2Sから入力された音声データを、内部でエフェクト処理を行い、AnalogOut、または、I2Sに出力する機能を実現する状態です。 状態の中にBasebandReady状態とBasebandActive状態の2つのサブ状態を持ちます。 BasebandReady状態は、音声入出力停止の状態です。AUDCMD_STARTBB によってBasebandActiveに遷移し音声入出力動作を開始します。 RecorderActive状態は、音声入出力動作中状態です。AUDCMD_STOPBB によってBasebandReadyに遷移し音声入出力動作を停止します。
AUDCMD_SETREADYSTATUS コマンドによって、Ready状態にのみ遷移します。
現在のファームウェアでは、Baseband状態は未対応です。 |
メモリ管理とタスク間同期について
Memory Manager Library
AudioSubSystemは、使用するデータ領域を特殊な管理方法で管理します。 MemoryUtilityのMemoryManagerと呼ばれるライブラリは、Memory Layout定義ファイルのLayout情報に従い、必要なメモリエリアを固定長のメモリプールとして確保します。 このLayout情報は複数定義することができ、Layout番号を指定して切り替えることで、機能ごとの必要メモリを確保することができます。 Layout情報は、Applicationのニーズに従って自由に決めてもらうものになりますが、各機能で最低限必要なリソースはありますので、ご注意ください。
詳細は、MemoryManagerのライブラリ説明をご参照ください。
また、各機能に合わせた必要なLayout情報に関しては、各exampleの説明をご参照下さい。
AudioSubSystem内の、各オブジェクトは、必要なメモリエリアのセグメントを指し示すMemHandleのインスタンスを生成することで、それに紐付くメモリセグメントを確保し、そこを利用します。
データパイプラインの次に位置するオブジェクトに、このMemHandleのインスタンスを渡していくことで、確保されたエリアを次のオブジェクトが使用ることができ、不要になった場合、このインスタンスを破棄することで、メモリ領域が解放されます。
インスタンスはコピーでき、必要なオブジェクトが各自メモリが必要な間インスタンスを確保し、不要になったら各自のタイミングで破棄したとしても、安全にメモリを確保・解放することができます。
セグメントの使用が不要になった際、非同期に実行されるオブジェクトは、メモリ管理を意識することなく、インスタンスを破棄することで、暗黙の裡に参照が外れます。
これにより、非同期オブジェクト間でのメモリ管理を簡便に行っています。 すべての参照がなくなったら、メモリを開放します。
Layout情報は、Applicationが使うためのヘッダファイル群として、あらかじめ用意する必要があります。 これらのヘッダファイルは、Memory Layout定義ファイル(mem_layout.conf)を作成し、ツールを使うことで、生成されます。
ツールの使い方: ruby -Itool_path mem_layout.conf layout_header fence_header pool_header
-Itool_path | mem_layout.rbのパス |
mem_layout.conf | Memory Layout定義ファイル |
layout_header | 各種定数値が、C言語のマクロとして出力されるヘッダファイル |
fence_header | FixedAreaのメモリフェンスアドレスが出力されるヘッダファイル |
pool_header | PoolAreaの各種定義が出力されるヘッダファイル |
Message Library
このメモリ管理機構を使用するうえで、各タスク間でのクラスオブジェクトの送受信が必要になります。これを実現するため、タスク同期機構にクラスインスタンスの送受信を可能にしたmessageライブラリを用意しており、AudioSubSystemは、これを利用しています。
各タスクにあるオブジェクトに送受信先のIDを付加していくことで、送受信したいタスクへの送信が可能になります。
例えば、送信する場合は、送信先のIDにオブジェクトを送信し、受信側のタスクは、自分のIDの送信要求が発生したときのみ、受信されることになります。 受信するまでは、そのタスクはsleepして待ちます。
これにより、AudioSubSystemは、イベント駆動でのオブジェクト設計を行っています。
Messageに関しては、Messageを使うためのヘッダファイル群をあらかじめ用意する必要があります。 これらのヘッダファイルは、MessageQueueLayout定義ファイル(msgq_layout.conf)を作成し、ツールを使うことで、生成されます。
ツールの使い方: ruby -Itool_path msgq_layout.conf area_address area_size id_header pool_header
-Itool_path | msgq_layout.rbのパス |
msgq_layout.conf | Message Layout定義ファイル |
area_address | Message領域のアドレス |
area_size | Message領域のサイズ |
id_header | MessageQueueIDマクロが出力されるファイル |
pool_header | MessageQueuePoolの定義が出力されるファイル |
詳細は、Message Libraryのライブラリ説明をご参照ください。
Simple FIFO Library
AudioSubSystem とユーザアプリケーションとの間でのオーディオデータの受け渡しを行う場合、Simple FIFOを用います。このFIFOは、単純なFIFOであり、特に特筆すべきものはありません。
詳細は、Simple FIFOのライブラリ説明をご参照ください。
各機能の詳細
Audio Player Functions
Audio Player の簡単なデータの流れを以下に示します。
オーディオ・サブシステムがPlayerModeで動作する場合、User Applicationは、FIFOにESデータを入力します。 一定以上たまった状態で、Playerを起動すると、発音時間に合わせて、このESデータを消費していきます。このFIFOがUnderflowしない限り、音声データは途切れることなく発音します。
Playerは、2つのインスタンスを生成することが可能です。それぞれでデコードした音声は、OutputMixerで、Mixingして発音します。
データフロー内部は、Messageで通信します。 Message通信は、各クライアントごとにIDを持ちます。 Audio Playerの場合、exampleにあるサンプルLayouをもとに、IDを示すと以下のようになります。
User Application : MSGQ_AUD_APP
Audio Manager : MSGQ_AUD_MNG
Audio Player (Main Sound) : MSGQ_AUD_PLY
Audio Player (Effect Sound) : MSGQ_AUD_SFX
Output Mixer : MSGQ_AUD_OUTPUT_MIX
Audio DSP : MSGQ_AUD_DSP
Rendering Component(Main Sound) : MSGQ_AUD_RND_PLY
Rendering Component(Effect Sound) : MSGQ_AUD_RND_SFX
Rendering Component Sync(Main Sound) : MSGQ_AUD_RND_PLY_SYNC
Rendering Component Sync(Effect Sound) : MSGQ_AUD_RND_SFX_SYNC
Post Filter (Channel0) : MSGQ_AUD_PFDSP0
Post Filter (Channel1) : MSGQ_AUD_PFDSP1
MSGQ_AUD_RND_PLY/SFX_SYNCは削除されます。 |
また、各データのデータ領域は、以下になります。
ES Data (Main Sound) : DEC_ES_MAIN_BUF_POOL
ES Data (Effect Sound) : DEC_ES_SFX_BUF_POOL
PCM Data (Main Sound) : REND_PCM_BUF_POOL
PCM Data (Effect Sound) : REND_PCM_SFX_BUF_POOL
Audio Decoder DSP Command : DEC_APU_CMD_POOL
SamplingRateConverter Work Buffer (Main Sound) : SRC_WORK_BUF_POOL
SamplingRateConverter Work Buffer (Effect Sound) : SRC_WORK_SFX_BUF_POOL
Post Filter PCM Data (Channel0) : PF0_PCM_BUF_POOL
Post Filter PCM Data (Channel1) : PF1_PCM_BUF_POOL
Post Filter DSP Command (Channel0) : PF0_APU_CMD_POOL
Post Filter DSP Command (Channel1) : PF1_APU_CMD_POOL
これらのIDを生成時に指定する必要があります。
How to use
"AudioManager", "MediaPlayerObject", "OuputpuMixerObject", "RendererComponent" と呼ばれる
オーディオ・サブシステムを制御するために設計されたソフトウェアコンポーネントで、Audio Player を実現します。
そのため、Playerを実現するには、以下のオブジェクトの生成関数を事前に呼ぶ必要があります。
将来的には、HighLevelAPIでの生成関数は、AudioManagerのみになります。 |
必要なオブジェクトが生成されたら、Player動作を行わせるためにAudioのHWの設定や電源On、動作モードの変更などの初期化処理を行います。
以下のコマンドを順に発行することで、実現が可能です。
Audioブロックに電源を入れるために、AUDCMD_POWERON, PowerOnParamコマンドを発行することで、電源を入れてAudioSubSystemの状態をReady状態に遷移します。
enable_sound_effectは、AS_DISABLE_SOUNDEFFECT固定としてください。
AudioCommand command;
command.header.packet_length = LENGTH_POWERON;
command.header.command_code = AUDCMD_POWERON;
command.header.sub_code = 0x00;
command.power_on_param.enable_sound_effect = AS_DISABLE_SOUNDEFFECT;
AS_SendAudioCommand(&command);
PowerOnを行い、Ready状態に遷移したら、AUDCMD_INITOUTPUTSELECT, InitOutputSelectParam コマンドでMixerからの出力先の選択を行います。
output_device_selの設定は以下の通りです。
AS_OUT_OFF : 出力OFF
AS_OUT_SP : スピーカーからの出力
AS_OUT_I2S : I2Sからの出力
AudioCommand command;
command.header.packet_length = LENGTH_INITOUTPUTSELECT;
command.header.command_code = AUDCMD_INITOUTPUTSELECT;
command.header.sub_code = 0x00;
command.init_output_select_param.output_device_sel = AS_OUT_SP;
AS_SendAudioCommand(&command);
AUDCMD_INITI2SPARAMは未対応です。I2Sの設定はKconfigから変更して下さい。 |
スピーカーを駆動するデジタルアンプの駆動能力を、AUDCMD_SETSPDRVMODE, SetSpDrvModeParam コマンドで設定することが出来ます。
駆動能力を示すmodeの設定は以下の通りです。
スピーカーの使用方法に関しては、ハードウェアドキュメントのスピーカーの使用方法を参照ください。
AS_SP_DRV_MODE_LINEOUT : 駆動能力 弱。ライン出力用。
AS_SP_DRV_MODE_1DRIVER : 駆動能力 中。ヘッドホン出力用。
AS_SP_DRV_MODE_4DRIVER : 駆動能力 強。スピーカー出力用。
AudioCommand command;
command.header.packet_length = LENGTH_SETSPDRVMODE;
command.header.command_code = AUDCMD_SETSPDRVMODE;
command.header.sub_code = 0x00;
command.set_sp_drv_mode.mode = AS_SP_DRV_MODE_LINEOUT;
AS_SendAudioCommand(&command);
AUDCMD_SETPLAYERSTATUS, SetPlayerStsParam コマンドでAudioSubSystemの状態をPlayer状態に遷移します。
各パラメータの設定は以下の通りです。
AS_ACTPLAYER_MAIN : player0のみ再生
AS_ACTPLAYER_SUB : player1のみ再生
AS_ACTPLAYER_BOTH : player0とplayer1をMixして再生
AS_SETPLAYER_INPUTDEVICE_RAM:: RAMからの入力(固定)
SimpleFifoのハンドル情報のポインタを指定します。
- simple_fifo_handler
-
CMN_SimpleFifoInitialize()で取得されたハンドラを指定します。
- callback_function
-
PlayerObjectがSimpleFifoから読み出したイベントを通知するCallbackです。読みだしたデータのサイズが通知されます。
- notification_threshold_size
-
PlayerObjectが何バイト読み出した時点で、callbackの通知を行うかを指定します。ここで指定したサイズ以上読みだした際に通知されます。 0を指定すると、PlayerObjectが読みだす度に通知します。
AS_SETPLAYER_OUTPUTDEVICE_SPHP : スピーカーからの出力
AS_SETPLAYER_OUTPUTDEVICE_I2S : I2Sからの出力
output_deviceは未対応です。出力先の選択は、AUDCMD_INITOUTPUTSELECT (InitOutputSelectParam) コマンドで行ってください。 |
AsPlayerInputDeviceHdlrForRAM input0_ram_handler;
input0_ram_handler.simple_fifo_handler = &input0_handle;
input0_ram_handler.callback_function = input0_device_callback;
input0_ram_handler.notification_threshold_size = 0;
AsPlayerInputDeviceHdlrForRAM input1_ram_handler;
input1_ram_handler.simple_fifo_handler = &input1_handle;
input1_ram_handler.callback_function = input1_device_callback;
input1_ram_handler.notification_threshold_size = 0;
AudioCommand command;
command.header.packet_length = LENGTH_SET_PLAYER_STATUS;
command.header.command_code = AUDCMD_SETPLAYERSTATUS;
command.header.sub_code = 0x00;
command.set_player_sts_param.active_player = AS_ACTPLAYER_BOTH;
command.set_player_sts_param.player0.input_device = AS_SETPLAYER_INPUTDEVICE_RAM;
command.set_player_sts_param.player0.ram_handler = &input0_ram_handler;
command.set_player_sts_param.player0.output_device = 0x00;
command.set_player_sts_param.player1.input_device = AS_SETPLAYER_INPUTDEVICE_RAM;
command.set_player_sts_param.player1.ram_handler = &input1_ram_handler;
command.set_player_sts_param.player1.output_device = 0x00;
AS_SendAudioCommand(&command);
player1を利用する場合は、AS_CreatePlayerMulti(AsPlayerId, AsCreatePlayerParam_t, AudioAttentionCb)で、 AS_PLAYER_ID_1 を有効にしてください。 |
出力にスピーカーを設定した場合、AUDCMD_SETVOLUME, SetVolumeParam で音量を設定できます。 各パラメータの設定は以下の通りです。
I2Sでは音量は変更できません。
player0の音量。dBを10倍の整数値で設定します。設定範囲は-1020(-102.0dB)から120(+12.0dB)で、ステップ幅5(0.5dB)で設定できます。
player1の音量。設定範囲はinput1_dbと同じです。
player0とplayer1のMix後の音量。設定範囲はinput1_dbと同じです。
AudioCommand command;
command.header.packet_length = LENGTH_SETVOLUME;
command.header.command_code = AUDCMD_SETVOLUME;
command.header.sub_code = 0;
command.set_volume_param.input1_db = 0; /* 0.0dB */
command.set_volume_param.input2_db = 0; /* 0.0dB */
command.set_volume_param.master_db = -200; /* -20.0dB */
AS_SendAudioCommand(&command);
音楽再生初期化及び開始シーケンスを示します。
AUDCMD_INITPLAYER, PlayerCommand, AsInitPlayerParam で再生の初期設定を行います。
AsPlayerIdのインスタンスのIDを設定します。インスタンスは2つあり、どちらかを設定して下さい。
インスタンス番号 | 設定値 |
---|---|
0 | AS_PLAYER_ID_0 |
1 | AS_PLAYER_ID_1 |
再生コンテンツのコーデックの種別を設定して下さい。MP3, WAVに対応しています。
コーデック種別 | 設定値 |
---|---|
MP3 | AS_CODECTYPE_MP3 |
WAV | AS_CODECTYPE_WAV |
MP3ファイルに関して、すべてのファイルに現時点で対応できていません。現在、ID3v2 TAG(特に画像データのような大きなメタデータ)がある場合、デコーダがparseエラーになります。 MP3Tag などのツールで、タグ情報を削除してください。 |
再生コンテンツの1サンプルあたりのbit長を設定します。16bitと24bitに対応しています。
bit長 | 設定値 |
---|---|
16 | AS_BITLENGTH_16 |
24 | AS_BITLENGTH_24 |
24bitのデコードができるメモリのLayoutが必要です。 |
再生コンテンツのチャンネル数を設定します。モノラル(1ch), ステレオ(2ch)に対応しています。
チャンネル数 | 設定値 |
---|---|
1 | AS_CHANNEL_MONO |
2 | AS_CHANNEL_STEREO |
再生コンテンツのサンプリング周波数を設定します。コーデック種別ごとに設定可能な設定値が異なります。
サンプリング周波数 | 設定値 | 対応コーデック種別 |
---|---|---|
16kHz | AS_SAMPLINGRATE_16000 | MP3,WAV |
32kHz | AS_SAMPLINGRATE_32000 | MP3,WAV |
44.1kHz | AS_SAMPLINGRATE_44100 | MP3,WAV |
48kHz | AS_SAMPLINGRATE_48000 | MP3,WAV |
88.2kHz | AS_SAMPLINGRATE_88200 | WAV |
96kHz | AS_SAMPLINGRATE_96000 | WAV |
176.4kHz | AS_SAMPLINGRATE_176400 | WAV |
192kHz | AS_SAMPLINGRATE_192000 | WAV |
自動判別 | AS_SAMPLINGRATE_AUTO | MP3 |
AS_SAMPLINGRATE_AUTO は、ストリーム上のSyntaxからサンプリング周波数を自動判定して欲しいときに 使用します。現時点では、MP3のみ対応です。 |
ハイレゾリューションサンプリングレート、すなわち、AS_SAMPLINGRATE_88200 、AS_SAMPLINGRATE_96000 、AS_SAMPLINGRATE_176400 の場合、DSPをDualCore使用しWorking領域も大きく使用するため、Dual Decodeを行おうとする場合、DSP領域だけで384kB必要になります。必要に応じて、SDKのConfigurationを変更しDSP領域を変更して下さい。 |
DecoderのDSPバイナリイメージを格納している絶対パスを指定します。最大24文字です。
AudioCommand command;
command.header.packet_length = LENGTH_INIT_PLAYER;
command.header.command_code = AUDCMD_INITPLAYER;
command.header.sub_code = 0x00;
command.player.player_id = AS_PLAYER_ID_0;
command.player.init_param.codec_type = AS_CODECTYPE_MP3;
command.player.init_param.bit_length = AS_BITLENGTH_16;
command.player.init_param.channel_number = AS_CHANNEL_STEREO;
command.player.init_param.sampling_rate = AS_SAMPLINGRATE_48000;
command.player.init_param.dsp_path = "/mnt/sd0/bin";
AS_SendAudioCommand(&command);
AUDCMD_PLAYPLAYER, PlayerCommandで再生を開始します。 音楽再生を開始するとFIFOから圧縮音声データを読み出し始めます。
このため、音楽生成開始時までに、十分な量の圧縮音声データをFIFOに入力しておくようにしてください。
開始時に、十分な量のデータをFIFOに入力していないと、開始直後にUnderflowしてしまい音声再生が停止してしまいます。 |
AsPlayerIdのインスタンスのIDを設定します。AUDCMD_INITPLAYERで初期設定済みのインスタンスIDを 設定して下さい。
インスタンス番号 | 設定値 |
---|---|
0 | AS_PLAYER_ID_0 |
1 | AS_PLAYER_ID_1 |
AudioCommand command;
command.header.packet_length = LENGTH_PLAY_PLAYER;
command.header.command_code = AUDCMD_PLAYPLAYER;
command.header.sub_code = 0x00;
command.player.player_id = AS_PLAYER_ID_0;
AS_SendAudioCommand(&command);
音声再生停止のシーケンスを示します。
AUDCMD_PLAYPLAYER, PlayerCommand, AsStopPlayerParamで再生を停止します。
AsPlayerIdのインスタンスのIDを設定します。AUDCMD_PLAYPLAYERで再生を停止したいインスタンスIDを指定してください。開始済みのインスタンスIDでなければいけません。
インスタンス番号 | 設定値 |
---|---|
0 | AS_PLAYER_ID_0 |
1 | AS_PLAYER_ID_1 |
AsStopPlayerStopModeの停止モードを設定します。停止モードは通常停止とES終端停止、強制停止の3種類があります。
通常停止は、停止要求のタイミングでできるだけ早く停止します。すなわちFIFOの中身は残っている状態になります。
ES終端停止は、停止要求時点で、FIFOに入っているデータをすべて発音してから停止します。
強制停止は、Audio SubSystem内部でのエラー時に使用されるモードで、アプリケーションからは発行しません。
停止モード | 設定値 |
---|---|
通常停止 | AS_STOPPLAYER_NORMAL |
ES終端停止 | AS_STOPPLAYER_ESEND |
強制停止 | AS_STOPPLAYER_FORCIBLY |
AudioCommand command;
command.header.packet_length = LENGTH_STOP_PLAYER;
command.header.command_code = AUDCMD_STOPPLAYER;
command.header.sub_code = 0x00;
command.player.player_id = AS_PLAYER_ID_0;
command.player.stop_param.stop_mode = AS_STOPPLAYER_NORMAL;
AS_SendAudioCommand(&command);
Build Configurations
AudioPlayer の機能を使用するためには
$>cd sdk
$>tools/config.py -m
でConfig menu を開き、以下のConfigを設定する必要があります。
Select options in below:
[CXD65xx Configuration] [SDIO SD Card] <= Y (If use SD card) [Audio] <= Y [SDK audio] <= Y [Audio Utilities] [Audio Player] <= Y [Playlist manager] <= Y (If use PlayList) [Memory Manager] <= Y [Memory Utilities] <= Y [ASMP] <= Y
Error Attentions and Approach
音楽再生時の警告の一覧と、対処方法は以下の通りです。詳細は オーディオサブシステムのエラーについてを参照してください。
ID | Attention Code | Attention Level | Approach |
---|---|---|---|
0x05 | AS_ATTENTION_SUB_CODE_SIMPLE_FIFO_UNDERFLOW | WARNING | AudioSubSystemが再生データを読み込めなかったことが原因です。再生データをSimpleFIFOにWriteするタスクのCPU占有度を上げてください。 |
0x0D | AS_ATTENTION_SUB_CODE_MEMHANDLE_ALLOC_ERROR | ERROR | データ領域のセグメント数が不足したことが原因です。AudioSubSystem以外のタスクの優先度を下げるか、データ領域のセグメント数を増やしてください。 |
0x0F | AS_ATTENTION_SUB_CODE_TASK_CREATE_ERROR | ERROR | ヒープ領域が不足していることが原因です。ヒープ領域を拡張して下さい。 |
0x18 | AS_ATTENTION_SUB_CODE_DSP_VERSION_ERROR | ERROR | DSPバイナリのバージョンが異なることが原因です。DSP バイナリイメージを"sdk/modules/audio/dsp"のファイルで更新して下さい。 |
0x1A | AS_ATTENTION_SUB_CODE_STREAM_PARSER_ERROR | ERROR | 再生ファイルにSync wordが見つからなかったことが原因です。再生ファイルと指定したコーデックが合っているか確認してください。 |
0x21 | AS_ATTENTION_SUB_CODE_ALLOC_HEAP_MEMORY | WARNING | プール領域ではなく、ヒープ領域が使われたことが原因です。Sampling Rate Converterのwork bufferのプール領域(SRC_WORK_BUF_POOL)が設定されているか確認して下さい。 |
AS_ATTENTION_SUB_CODE_SIMPLE_FIFO_UNDERFLOW が発生した場合、音声再生が停止し、再生エラーの状態になります。 この状態が発生した場合は、直ちにAsStopPlayerParamコマンドを発行し、再生停止状態に遷移させてください。 再生停止に遷移後、FIFOのクリアを必ず行ってください。行わないとノイズが発生してしまいます。 |
DSP install
- DSP binary image install
-
DSPバイナリイメージをKconfigで設定したパスに格納して下さい。バイナリイメージは、
sdk/modules/audio/dsp
にあります。表 17. Binary image required for audio player according to configuration: Image size MP3DEC
61kbyte
WAVDEC
32kbyte
ハイレゾリューションサンプリングレートの再生を行う場合、DSPを2 Core(1Coreあたり192kB)使用します。
※2Coreの場合、384kB。
使用するリソースにご注意ください。
Audio Player Example
音楽再生の簡単なサンプリアプリケーションとして、Audio Player exampleがあります。ここでは、その使い方などを説明します。
Audio Player のサンプルプログラムを使うには、build configurationを以下の設定をしてください。
[Examples] [Audio player example] <= Y
または、
$>cd sdk $>tools/config.py examples/audio_player
Audio & Logical sensor example と他の複数のサンプルは同時に選択できません。複数選択するとコンパイルエラーが出ます。 |
タスク間通信ライブラリ(Message Library)とメモリ管理ライブラリ(Memory Manager)の設定は、以下のように行ってください。
AudioPlayer機能を使用する際に必要となるMessageQueueの定義を行う必要があります。 定義はMessageQueueLayout定義ファイルで行い、ツールでコードに組み込むヘッダファイルを生成することが出来ます。
Audio Player のexampleでは下記のように行います。
$>cd examples/audio_player/config $>ruby -I../../../sdk/modules/memutils/message/tool msgq_layout.conf 0x000fe000 0x1000 msgq_id.h msgq_pool.h
MessageQueueLayout定義ファイル(msgq_layout.conf)の記述内容は下記の通りです。
MsgQuePool
# ID, n_size n_num h_size h_nums
["MSGQ_AUD_MNG", 88, 4, 0, 0],
["MSGQ_AUD_APP", 40, 2, 0, 0],
["MSGQ_AUD_DSP", 20, 5, 0, 0],
["MSGQ_AUD_PFDSP0", 20, 5, 0, 0],
["MSGQ_AUD_PFDSP1", 20, 5, 0, 0],
["MSGQ_AUD_PLY", 48, 5, 0, 0],
["MSGQ_AUD_SFX", 48, 5, 0, 0],
["MSGQ_AUD_OUTPUT_MIX", 48, 8, 0, 0],
["MSGQ_AUD_RND_PLY", 32, 16, 0, 0],
["MSGQ_AUD_RND_PLY_SYNC", 16, 8, 0, 0],
["MSGQ_AUD_RND_SFX", 32, 16, 0, 0],
["MSGQ_AUD_RND_SFX_SYNC", 16, 8, 0, 0],
各パラメータの説明は以下の通りです。
パラメータ | 説明 |
---|---|
ID | メッセージキュープールIDの名称を、"MSGQ_"で始まる文字列で指定。 |
n_size | 通常優先度キューの各要素のバイト数(8以上512以下)。固定ヘッダ長(8byte) + パラメタ長を4の倍数で指定する。 |
n_num | 通常優先度キューの要素数(1以上16384以下)。 |
h_size | 高優先度キューの各要素のバイト数(0または、8以上512以下)。未使用時は0を指定すること。 |
h_num | 高優先度キューの要素数(0または、1以上16384以下)。未使用時は0を指定すること。 |
各IDはAudio Player FunctionsのAudio Player Message IDを参照してください。
n_sizeは最適値となっているため、変更は行わないでください。
n_numも変更の必要はありませんが、他のApplicationでAudioPlayer機能を使う場合は、負荷を考慮して値を増やす必要が出てくる可能性があります。
h_size, h_numsはAudioPlayer機能を優先的に処理したい場合に利用して下さい。
それぞれの定義の詳細については、 examples/audio_player/config/msgq_layout.confを参照してください。 設定が変わった場合は、ツールを使って新しいヘッダーファイルを生成してください。 |
AudioPlayer機能を使用する際に必要となるMemoryLayout(pool)の定義を行う必要があります。
定義はMemoaryLayout定義ファイルで行い、ツールでコードに組み込むヘッダファイルを生成することが出来ます。
Audio Player のexampleでは下記のように行います。
$>cd examples/audio_player/config $>ruby -I../../../sdk/modules/memutils/memory_manager/tool -I. mem_layout.conf mem_layout.h fixed_fence.h pool_layout.h
MemoaryLayout定義ファイル(mem_layout.conf)の記述内容は下記の通りです。
FixedAreas
# name, device, align, size, fence
["AUDIO_WORK_AREA", "AUD_SRAM", U_STD_ALIGN, 0x0003e000, false], # Audio work area
["MSG_QUE_AREA", "AUD_SRAM", U_STD_ALIGN, 0x00001000, false], # message queue area
["MEMMGR_WORK_AREA", "AUD_SRAM", U_STD_ALIGN, 0x00000200, false], # MemMgrLite WORK Area
["MEMMGR_DATA_AREA", "AUD_SRAM", U_STD_ALIGN, 0x00000100, false], # MemMgrLite DATA Area
各パラメータの説明は以下の通りです。
パラメータ | 説明 |
---|---|
name | 領域名(英大文字で始まり、"_AREA"で終わる名称。英大文字, 数字, _が使用可能) |
device | 領域を確保するMemoryDevicesのデバイス名 |
align | 領域の開始アライメント。0を除くMinAlign(=4)の倍数を指定する |
size | 領域のサイズ。0を除く4の倍数の値を指定する |
fence | フェンスの有効・無効を指定する(この項目は、UseFenceがfalseの場合は無視される) |
各nameの用途は以下の通りです。
AUDIO_WORK_AREA | AudioSubSystemが利用する |
MSG_QUE_AREA | MessageQueueが利用する(固定名)。msgq_id.hの(MSGQ_END_DRM - MSGQ_TOP_DRAM)のサイズを超えないこと。 |
MEMMGR_WORK_AREA | Memory Managerが利用する作業領域(固定名, 固定サイズ) |
MEMMGR_DATA_AREA | Memery Managerが利用するデータ領域(固定名, 固定サイズ) |
各nameの合計のサイズがmpshm_init(), mpshm_remap()で確保するシェアメモリのサイズを超えないようにしてください。
Fixed Areas can not be customized |
PoolAreas
# name, area, align, pool-size, seg, fence
["DEC_ES_MAIN_BUF_POOL", "AUDIO_WORK_AREA", U_STD_ALIGN, U_DEC_ES_MAIN_BUF_POOL_SIZE, U_DEC_ES_MAIN_BUF_SEG_NUM, true ],
["REND_PCM_BUF_POOL", "AUDIO_WORK_AREA", U_STD_ALIGN, U_REND_PCM_BUF_POOL_SIZE, U_REND_PCM_BUF_SEG_NUM, true ],
["DEC_APU_CMD_POOL", "AUDIO_WORK_AREA", U_STD_ALIGN, U_DEC_APU_CMD_POOL_SIZE, U_DEC_APU_CMD_SEG_NUM, true ],
["SRC_WORK_BUF_POOL", "AUDIO_WORK_AREA", U_STD_ALIGN, U_SRC_WORK_BUF_POOL_SIZE, U_SRC_WORK_BUF_SEG_NUM, true ],
["PF0_PCM_BUF_POOL", "AUDIO_WORK_AREA", U_STD_ALIGN, U_POF_PCM_BUF_SIZE, U_POF_PCM_BUF_SEG_NUM, true ],
["PF1_PCM_BUF_POOL", "AUDIO_WORK_AREA", U_STD_ALIGN, U_POF_PCM_BUF_SIZE, U_POF_PCM_BUF_SEG_NUM, true ],
["PF0_APU_CMD_POOL", "AUDIO_WORK_AREA", U_STD_ALIGN, U_DEC_APU_CMD_POOL_SIZE, U_DEC_APU_CMD_SEG_NUM, true ],
["PF1_APU_CMD_POOL", "AUDIO_WORK_AREA", U_STD_ALIGN, U_DEC_APU_CMD_POOL_SIZE, U_DEC_APU_CMD_SEG_NUM, true ],
各パラメータの説明は以下の通りです。
パラメータ | 説明 |
---|---|
name | プール名(英大文字で始まり、"_POOL"で終わる名称。英大文字, 数字, _が使用可能) |
area | プール領域として使用するFixedAreaの領域名。領域はRAMに配置されていること |
align | プールの開始アライメント。0を除くMinAlign(=4)の倍数を指定する |
pool-size | プールのサイズ。0を除く4の倍数の値。Basicプールでは、セグメントサイズ * セグメント数 |
seg | セグメント数。1以上、255以下の値を指定する |
fence | フェンスの有効・無効を指定する。この項目は、UseFenceがfalseの場合は無視される |
各nameの用途は以下の通りです。
DEC_ES_MAIN_BUF_POOL | player0用入力データの格納用バッファ領域 |
REND_PCM_BUF_POOL | player0用Decode済みデータの出力用バッファ領域 |
DEC_APU_CMD_POOL | DSP(Decoder)用のコマンド領域 |
SRC_WORK_BUF_POOL | DSP(SamplingRateConverter)のワークバッファ領域 |
PF0_PCM_BUF_POOL | PostFilter0用のバッファ領域 |
PF1_PCM_BUF_POOL | PostFilter1用のバッファ領域 |
PF0_APU_CMD_POOL | PostFilter0用のコマンド領域 |
PF1_APU_CMD_POOL | PostFilter1用のコマンド領域 |
それぞれの定義の詳細については、 examples/audio_player/config/mem_layout.confを参照してください。 設定が変わった場合は、ツールを使って新しいヘッダーファイルを生成してください。 |
sampling rate | PCM bit length | channel number | CPU frequency lock | |
---|---|---|---|---|
mp3 | 16kHz / 32kHz 44.1kHz / 48kHz | 16bit | 1ch / 2ch | High voltage |
wav (Low Power) | 16kHz / 32kHz 44.1kHz / 48kHz | 16bit | 1ch/2ch | Low voltage |
wav | 48kHz / 88.4kHz / 96kHz / 176.4kHz / 196kHz | 16bit/24bit | 1ch/2ch | High voltage |
- Music file
-
"AUDIO/" ディレクトリをSDカードのルートディレクトリに生成し、 音楽ファイルをコピーしてください。
- Playlist
-
再生したい音楽ファイルのリストを管理します。csv ファイルからデータベースを生成します。ファイル名は "TRACK_DB.CSV" とします。
"PLAYLIST/" ディレクトリを SDカードのルートディレクトリに生成し、"TRACK_DB.CSV" をコピーします。
[filename],[artist],[album],[channel number],[bit length],[sampling rate],[file format]
ABC.mp3,artist1,album1,2,16,44100,mp3
exampleはPlaylistを使う前提となっています。再生されるのはリストの1行目のみです。 |
NuttShell から player アプリケーションを起動します。
nsh> player
playerアプリケーションが起動し、次のログが表示されます。
Start AudioPlayer example
PlayListの先頭のファイルの再生が開始されます。
sdカードを認識できない場合は、次のエラーログが表示されます。sdカードの状態を確認して下さい。 Error: /mnt/sd0/AUDIO directory path error. check the path! Error: app_open_contents_dir() failure. Exit AudioPlayer example PlayListを認識できない場合は、次のエラーログが表示されます。PlayListのpathが正しいか確認してください。 Track db(playlist) /mnt/sd0/PLAYLIST/TRACK_DB.CSV open error. check paths and files! /mnt/sd0/PLAYLIST/alias_list_alltrack.bin cannot opened. PlayFileを認識できない場合は、次のエラーログが表示されます。pathにFileがあるかどうか、またはPlayListとFile名が一致しているかを確認して下さい。 Error: /mnt/sd0/AUDIO/***.mp3 open error. check paths and files! Error: app_start_player() failure. SamplingRateConverterのwork bufferのプール領域(SRC_WORK_BUF_POOL)を設定しない場合は、次の警告ログが表示されます。プール領域の代わりにヒープ領域が使用され、フラグメンテーションが発生する可能性があります。AS_CreatePlayerMultiでSRC_WORK_BUF_POOLを設定してください。 Attention: module[5] attention id[1]/code[33] (objects/media_player/media_player_obj.cpp L****) |
10秒再生後、Playerアプリケーションは終了します
Exit AudioPlayer example
Audio Recorder Functions
Audio Recorder の簡単なデータの流れを以下に示します。
Audio SubSystemがRecorderModeで動作する場合、User Applicationは、 ESデータを格納するためのFIFOを用意する必要があります。 音声データの記録を開始すると、一定時間動作後、このFIFOに音声データがたまります。この音声データは、指定された圧縮フォーマットにエンコードされており、をFIFOから適宜読みだし、FIFOから溢れないようにすることで、連続音声データを取得することができます。
Recorderは、HWとしては、2系統キャプチャが可能ですが、現時点では、2つのインスタンスを生成し、2系統記録する機能には未対応です。
User Applicationは、この音声を各システムの要求に合わせて、(例えば、Strageに書き出し記録したり、Connectivityモジュールに送ってクラウド処理するなど。)処理を行うことで、Recorderアプリケーションを実現します。
データフロー内部は、Messageで通信します。 Message通信は、各クライアントごとにIDを持ちます。 Audio Recorderの場合、exampleにあるサンプルLayouをもとに、IDを示すと以下のようになります。
User Application : MSGQ_AUD_APP
Audio Manager : MSGQ_AUD_MGR
Audio Recorder : MSGQ_AUD_RECORDER
Audio Capture Component : MSGQ_AUD_CAP
Audio DSP : MSGQ_AUD_DSP
※MSGQ_AUD_CAP_SYNCは削除されます。
また、各データのデータ領域は、以下になります。
PCM (Input) Data Buffer : INPUT_BUF_POOL
ES Data Buffer (for DSP) : ES_BUF_POOL
Audio Encoder DSP Command : ENC_APU_CMD_POOL
これらのIDを生成時に指定する必要があります。
How to use
"AudioManager", "MediaRecorderObject", "CaptureComponent" と呼ばれる オーディオ・サブシステムを制御するために設計されたソフトウェアコンポーネントで、Audio Recorder を実現します。
必要なオブジェクトが生成されたら、Recorder動作を行わせるためにAudioのHWの設定や電源On、動作モードの変更などの初期化処理を行います。
以下のコマンドを順に発行することで、実現が可能です。
Audioブロックに電源を入れるために、AUDCMD_POWERON, PowerOnParamコマンドを発行することで、電源を入れてAudioSubSystemの状態をReady状態に遷移します。
enable_sound_effectは、AS_DISABLE_SOUNDEFFECT固定となります。
AS_DISABLE_SOUNDEFFECT: SoundEffect無効
AudioCommand command;
command.header.packet_length = LENGTH_POWERON;
command.header.command_code = AUDCMD_POWERON;
command.header.sub_code = 0x00;
command.power_on_param.enable_sound_effect = AS_DISABLE_SOUNDEFFECT;
AS_SendAudioCommand(&command);
AUDCMD_INITMICGAINでMicのGainを設定します。
アナログマイクの場合、dB値を10倍にした値を、5の倍数で0(0.0dB)~210(21.0dB)の範囲で設定できます。デフォルト値は0.0dBです。
デジタルマイクの場合、dB値を100倍にした値を、-7850(-78.50dB)~0(0.00dB)の範囲で設定できます。デフォルト値は-78.50dBです。
Gainの値を変更したくない場合は、'AS_MICGAIN_HOLD'を指定してください。
AudioCommand command;
command->header.packet_length = LENGTH_INITMICGAIN;
command->header.command_code = AUDCMD_INITMICGAIN;
command->header.sub_code = 0;
command->init_mic_gain_param.mic_gain[0] = 210;
command->init_mic_gain_param.mic_gain[1] = 210;
command->init_mic_gain_param.mic_gain[2] = 210;
command->init_mic_gain_param.mic_gain[3] = 210;
command->init_mic_gain_param.mic_gain[4] = AS_MICGAIN_HOLD;
command->init_mic_gain_param.mic_gain[5] = AS_MICGAIN_HOLD;
command->init_mic_gain_param.mic_gain[6] = AS_MICGAIN_HOLD;
command->init_mic_gain_param.mic_gain[7] = AS_MICGAIN_HOLD;
AS_SendAudioCommand(&command);
mic_gain[]の各要素はマイクのIDに対応しています。マイクのIDはConfigの"MIC channel select map"の値で設定されます。デフォルトの設定は、アナログマイク1/2/3/4が設定されています。
以下に、configrationの情報を記載します。
[CXD56xx Configuration] [Audio] [Audio baseband config settings] [CXD5247 settings] (0xFFFF4321) MIC channel select map
"MIC channel select map"の値は4bitごとにMICのIDを示します。
mic_gainの要素と"MIC channel select map"のbitフィールドの関係は下記の通りです。
mic_gainの要素 | [7] | [6] | [5] | [4] | [3] | [2] | [1] | [0] |
---|---|---|---|---|---|---|---|---|
bitフィールド | 31-28 | 27-24 | 23-20 | 19-16 | 15-12 | 11-8 | 7-4 | 3-0 |
"MIC channel select map"の値(ID)とマイクの種別の関係は下記の通りです。
HEX値(ID) | マイク種別 |
---|---|
0x1 | CXD5247アナログマイク1 |
0x2 | CXD5247アナログマイク2 |
0x3 | CXD5247アナログマイク3 |
0x4 | CXD5247アナログマイク4 |
0x5 | CXD5247デジタルマイク1 |
0x6 | CXD5247デジタルマイク2 |
0x7 | CXD5247デジタルマイク3 |
0x8 | CXD5247デジタルマイク4 |
0x9 | CXD5247デジタルマイク5 |
0xA | CXD5247デジタルマイク6 |
0xB | CXD5247デジタルマイク7 |
0xC | CXD5247デジタルマイク8 |
使用するマイクを要素0から順に設定して下さい。要素番号をSkipして設定することはできません。 アナログマイクとデジタルマイクの混合は対応していません。 アナログマイクを設定する場合は要素0-3を設定して下さい。 要素が偶数の場合、Lチャンネル、要素が奇数の場合、Rチャンネルとなります。
AUDCMD_SETRECORDERSTATUSでAudioSubSystemの状態をRecorder状態に遷移します。
記録対象の入力デバイスを指定します。AUDCMD_INITMICGAINで設定したマイク種別と合わせる必要があります。
AS_SETRECDR_STS_INPUTDEVICE_MIC_A : CXD5247アナログマイク AS_SETRECDR_STS_INPUTDEVICE_MIC_D : CXD5247デジタルマイク AS_SETRECDR_STS_INPUTDEVICE_I2S_IN : I2S入力
現時点では0固定です。
エンコードしたESデータの出力先デバイスを指定します。
現時点ではRAMデバイスの出力のみサポートされます。
AS_SETRECDR_STS_OUTPUTDEVICE_RAM : RAMデバイスへ出力
出力(Encoded ES data)の格納先SimpleFIFOのハンドラを指定します。
simple_fifo_handlerは、CMN_SimpleFifoInitialize()で取得されます。
AudioCommand command;
command.header.packet_length = LENGTH_SET_RECORDER_STATUS;
command.header.command_code = AUDCMD_SETRECORDERSTATUS;
command.header.sub_code = 0x00;
command.set_recorder_status_param.input_device = AS_SETRECDR_STS_INPUTDEVICE_MIC_A;
command.set_recorder_status_param.input_device_handler = 0x00;
command.set_recorder_status_param.output_device = AS_SETRECDR_STS_OUTPUTDEVICE_RAM;
command.set_recorder_status_param.output_device_handler = &s_recorder_info.fifo.output_device;
AS_SendAudioCommand(&command);
音声記録開始シーケンスを示します。
AUDCMD_INITREC, RecorderCommand, AsInitRecorderParamで記録動作の設定をします。
AS_SAMPLINGRATE_8000 : 8kHz AS_SAMPLINGRATE_16000 : 16kHz AS_SAMPLINGRATE_48000 : 48kHz
AS_CHANNEL_MONO : Monoral AS_CHANNEL_STEREO : Stereo AS_CHANNEL_4CH : 4ch AS_CHANNEL_6CH : 6ch AS_CHANNEL_8CH : 8ch
AS_BITLENGTH_16 : 16bit AS_BITLENGTH_24 : 24bit
AS_CODECTYPE_MP3 : MP3 AS_CODECTYPE_LPCM : LinerPCM
MP3エンコード時のみ有効
AS_BITRATE_8000 : 8000 AS_BITRATE_16000 : 16000 AS_BITRATE_24000 : 24000 AS_BITRATE_32000 : 32000 AS_BITRATE_40000 : 40000 AS_BITRATE_48000 : 48000 AS_BITRATE_56000 : 56000 AS_BITRATE_64000 : 64000 AS_BITRATE_80000 : 80000 AS_BITRATE_96000 : 96000 AS_BITRATE_112000 : 112000 AS_BITRATE_128000 : 128000 AS_BITRATE_144000 : 144000 AS_BITRATE_160000 : 160000 AS_BITRATE_192000 : 192000 AS_BITRATE_224000 : 224000 AS_BITRATE_256000 : 256000 AS_BITRATE_320000 : 320000
DecoderのDSPイメージを格納している絶対パスを指定します。最大24文字です。
入力デバイスとch数の組み合わせは制限が有ります。 |
入力 | ch数 |
---|---|
Mic | 1ch(Monoral), 2ch(Stereo), 4ch(*1), 6ch(*2), 8ch(*2) |
I2S | 2ch(Stereo) |
-
(*1. LPCMのみ)
-
(*2. LPCMかつDigitalMic使用時のみ)
Codec, bit長, サンプリング周波数, ビットレートの組み合わせには制限が有ります。 |
Codec | bit長 | サンプリング周波数 | ビットレート |
---|---|---|---|
MP3 | 16bit | 16kHz | 8000(*1), 16000 〜 160000 |
48kHz | 32000 〜 320000 | ||
LPCM | 16bit | 16kHz, 48kHz | - |
24bit(*2) | 16kHz, 48kHz, 192kHz(*2) | - |
-
(*1. 1ch指定時)
-
(*2. 要 HiResoモード指定)
AudioCommand command;
command.header.packet_length = LENGTH_INIT_RECORDER;
command.header.command_code = AUDCMD_INITREC;
command.header.sub_code = 0x00;
command.recorder.init_param.sampling_rate = s_recorder_info.file.sampling_rate;
command.recorder.init_param.channel_number = s_recorder_info.file.channel_number;
command.recorder.init_param.bit_length = AS_BITLENGTH_16;
command.recorder.init_param.codec_type = AS_CODECTYPE_OPUS;
command.recorder.init_param.bitrate = AS_BITRATE_8000;
command.recorder.init_param.computational_complexity = AS_INITREC_COMPLEXITY_0
command.recorder.init_param.dsp_path = "/mnt/sd0/bin";
AUDCMD_STARTRECで記録を開始します。
記録を開始して少しすると、Audio Systemは、FIFOにESデータを書き込みます。 正しく音声データを記録するためには、書き込んだデータをFIFOがあふれる前に読み出す必要があります。
データの書き込みに関しては、通知されますので、このイベントに合わせて適宜読み出してください。
FIFOがFullの場合、Audio Systemは、書き込むことができないため、書き込めない音声データを破棄してしまいます。そのため、そのまま記録すると音声データとしては不連続の音声になってしまうことになります。 |
AudioCommand command;
command.header.packet_length = LENGTH_START_RECORDER;
command.header.command_code = AUDCMD_STARTREC;
command.header.sub_code = 0x00;
AS_SendAudioCommand(&command)
- Stop Recorder.
-
AUDCMD_STOPRECで記録を停止します。 停止指示を受けた時点でキャプチャしている音声データまで、Encodeした時点で、Recorderは停止します。
AudioCommand command;
command.header.packet_length = LENGTH_STOP_RECORDER;
command.header.command_code = AUDCMD_STOPREC;
command.header.sub_code = 0x00;
AS_SendAudioCommand(&command)
Build Configurations
AudioRecorderの機能を使用するために
$>cd sdk/
$>tools/config.py -m
でConfig menuを開き、以下のConfigを設定する必要が有ります。
Select options in below:
:(Select audio recorder) [CXD56xx Configuration] [SDIO SD Card] <= Y (If using the SD card) [Audio] <= Y [Audio baseband config settings] [CXD5247 settings] [X'tal frequency of the CXD5247] <= 49.152MHz (If DVT board) [SDK audio] <= Y [Audio Utilities] [Audio Recorder] <= Y [Memory Manager] <= Y [Memory Utilities] <= Y [ASMP] <= Y
Error Attentions and Approach
音声録音時の警告の一覧と、対処方法は以下の通りです。詳細は オーディオサブシステムのエラーについてを参照してください。
ID | Attention Code | Attention Level | Approach |
---|---|---|---|
0x06 | AS_ATTENTION_SUB_CODE_SIMPLE_FIFO_OVERFLOW | WARNING | AudioSubSystemが録音データををSimpleFIFOに出力できなかったことが原因です。録音データをSimpleFIFOから取得するタスクのCPU占有度を上げてください。 |
0x0D | AS_ATTENTION_SUB_CODE_MEMHANDLE_ALLOC_ERROR | ERROR | データ領域のセグメント数が不足したことが原因です。AudioSubSystem以外のタスクの優先度を下げるか、データ領域のセグメント数を増やしてください。 |
0x0F | AS_ATTENTION_SUB_CODE_TASK_CREATE_ERROR | ERROR | ヒープ領域が不足していることが原因です。ヒープ領域を拡張して下さい。 |
0x18 | AS_ATTENTION_SUB_CODE_DSP_VERSION_ERROR | ERROR | DSPバイナリのバージョンが異なることが原因です。DSP バイナリイメージを"sdk/modules/audio/dsp"のファイルで更新して下さい。 |
DSP install
- DSP binary image install
-
DSPバイナリイメージをKconfigで設定したパスに格納して下さい。バイナリイメージは、
sdk/modules/audio/dsp
にあります。
Image | size |
---|---|
MP3ENC | 111kbyte |
SRC (Sampling Rate Converter) | 21kbyte |
LPCM は、圧縮処理を必要としていませんが、周波数変換処理が必要なため、SRC (Sampling Rate Converter) のDSPのloadが必要になります。 |
Audio Recorder Example
単純なRecorder Exampleがあり、Recorder動作を確認することが出来ます。
Audio Recorder のサンプルプログラムを使うには、以下の設定を行って下さい。
audio_recorderのconfigを読み込みます。
$>cd sdk/
$>tools/config.py examples/audio_recorder
Audio recorderが有効になっていることを確認します。
$>tools/config.py -m
(audio recorder:)
[Examples]
[Audio recorder example] <= Y
Audio & Logical sensor example と他の複数のサンプルは同時に選択できません。複数選択するとコンパイルエラーが出ます。 |
詳細はStart Recの項目を参照して下さい。
AudioRecorder機能を使用する際に必要となるMessageQueueの定義を行う必要があります。 定義はMessageQueueLayout定義ファイルで行い、ツールでコードに組み込むヘッダファイルを生成することが出来ます。
Audio Recorder のexampleでは下記のように行います。
$>cd examples/audio_recorder/config
$>ruby -I../../../sdk/modules/memutils/message/tool msgq_layout.conf 0x000fe000 0x1000 msgq_id.h msgq_pool.h
MessageQueueLayout定義ファイル(msgq_layout.conf)の記述内容は下記の通りです。
MsgQuePool
# ID, n_size n_num h_size h_num
["MSGQ_AUD_MNG", 88, 3, 0, 0],
["MSGQ_AUD_APP", 40, 2, 0, 0],
["MSGQ_AUD_DSP", 20, 5, 0, 0],
["MSGQ_AUD_RECORDER", 48, 5, 0, 0],
["MSGQ_AUD_CAP", 24, 16, 0, 0],
["MSGQ_AUD_CAP_SYNC", 16, 8, 0, 0],
各パラメータの説明は以下の通りです。
パラメータ | 説明 |
---|---|
ID | メッセージキューIDの名称を、"MSGQ_"で始まる文字列で指定。 |
n_size | 通常優先度キューの各要素のバイト数(8以上512以下)。固定ヘッダ長(8byte) + パラメタ長を4の倍数で指定する。 |
n_num | 通常優先度キューの要素数(1以上16384以下)。 |
h_size | 高優先度キューの各要素のバイト数(0または、8以上512以下)。未使用時は0を指定すること。 |
h_num | 高優先度キューの要素数(0または、1以上16384以下)。未使用時は0を指定すること。 |
各IDの用途は以下の通りです。
MSGQ_AUD_MNG | AudioManagerのコマンド受信に利用する |
MSGQ_AUD_APP | Applicationがコマンドの応答受信に利用する |
MSGQ_AUD_DSP | DSP(Decorder)からの応答受信に利用する |
MSGQ_AUD_RECORDER | MediaRecorderObjectのコマンド受信に利用する |
MSGQ_AUD_CAP | CaptureComponentのコマンド受信に利用する |
MSGQ_AUD_CAP_SYNC | CaptureComponentの内部同期処理に利用する |
それぞれの定義の詳細については、 examples/audio_recorder/config/msgq_layout.confを参照してください。 設定が変わった場合は、ツールを使って新しいヘッダーファイルを生成してください。 |
AudioRecorder機能を使用する際に必要となるMemoryLayout(pool)の定義を行う必要があります。
定義はMemoaryLayout定義ファイルで行い、ツールでコードに組み込むヘッダファイルを生成することが出来ます。
Audio Recorder のexampleでは下記のように行います。
$>cd examples/audio_recorder/config $>ruby -I../../../sdk/modules/memutils/memory_manager/tool -I. mem_layout.conf mem_layout.h fixed_fence.h pool_layout.h
MemoaryLayout定義ファイル(mem_layout.conf)の記述内容は下記の通りです。
FixedAreas
# name, device, align, size, fence
["AUDIO_WORK_AREA", "AUD_SRAM", U_TILE_ALIGN, 0x0003c000, false],
["MSG_QUE_AREA", "AUD_SRAM", U_MSGQ_ALIGN, 0x00003140, false],
["MEMMGR_WORK_AREA", "AUD_SRAM", U_STD_ALIGN, 0x00000200, false],
["MEMMGR_DATA_AREA", "AUD_SRAM", U_STD_ALIGN, 0x00000100, false],
各パラメータの説明は以下の通りです。
パラメータ | 説明 |
---|---|
name | 領域名(英大文字で始まり、"_AREA"で終わる名称。英大文字, 数字, _が使用可能) |
device | 領域を確保するMemoryDevicesのデバイス名 |
align | 領域の開始アライメント。0を除くMinAlign(=4)の倍数を指定する |
size | 領域のサイズ。0を除く4の倍数の値を指定する |
fence | フェンスの有効・無効を指定する(この項目は、UseFenceがfalseの場合は無視される) |
各nameの用途は以下の通りです。
AUDIO_WORK_AREA | AudioSubSystemが利用する |
MSG_QUE_AREA | MessageQueueが利用する(固定名)。msgq_id.hの(MSGQ_END_DRM - MSGQ_TOP_DRAM)のサイズを超えないこと。 |
MEMMGR_WORK_AREA | Memory Managerが利用する作業領域(固定名, 固定サイズ) |
MEMMGR_DATA_AREA | Memery Managerが利用するデータ領域(固定名, 固定サイズ) |
各nameの合計のサイズがmpshm_init(), mpshm_remap()で確保するシェアメモリのサイズを超えないようにしてください。
Fixed Areas can not be customized |
PoolAreas
# name, area, align, pool-size, seg, fence
["ES_BUF_POOL", "AUDIO_WORK_AREA", U_STD_ALIGN, 0x00008700, 5, true],
["INPUT_BUF_POOL", "AUDIO_WORK_AREA", U_STD_ALIGN, 0x00003000, 2, true],
["ENC_APU_CMD_POOL", "AUDIO_WORK_AREA", U_STD_ALIGN, 0x000000FC, 3, true],
["SRC_APU_CMD_POOL", "AUDIO_WORK_AREA", U_STD_ALIGN, 0x000000FC, 3, true],
各パラメータの説明は以下の通りです。
パラメータ | 説明 |
---|---|
name | プール名(英大文字で始まり、"_POOL"で終わる名称。英大文字, 数字, _が使用可能) |
area | プール領域として使用するFixedAreaの領域名。領域はRAMに配置されていること |
align | プールの開始アライメント。0を除くMinAlign(=4)の倍数を指定する |
pool-size | プールのサイズ。0を除く4の倍数の値。Basicプールでは、セグメントサイズ * セグメント数 In the final area of each area, RemainderSize indicating the remaining size can be specified. |
seg | セグメント数。1以上、255以下の値を指定する |
fence | フェンスの有効・無効を指定する。この項目は、UseFenceがfalseの場合は無視される |
各nameの用途は以下の通りです。
ES_BUF_POOL | 入力音声をエンコードした結果の格納バッファ領域 |
INPUT_BUF_POOL | 記録する音声データの入力格納バッファ領域 |
ENC_APU_CMD_POOL | Encoder DSPとの通信コマンドバッファ領域 |
SRC_APU_CMD_POOL | SRC DSPとの通信コマンドバッファ領域の通信 |
それぞれの定義の詳細については、 examples/audio_recorder/config/mem_layout.confを参照してください。 設定が変わった場合は、ツールを使って新しいヘッダーファイルを生成してください。 |
NuttShell から Recorder アプリケーションを起動します。
nsh>recorder
Audio Recorderアプリケーションが起動し記録が開始されます。
記録ファイルは "/mnt/sd0/REC/YYMMDD_HHMMSS.mp3"となります。
Start AudioRecorder example
10秒記録後、AudioRecorderアプリケーションは終了します。
Exit AudioRecorder example
Audio Through Functions
Audio Through の簡単なデータの流れを以下に示します。
Audio SubSystemがThroughModeで動作する場合、User Applicationは、 CPUを介さないデータフローを設定することが出来ます。
データの入力元は、I2SもしくはMICが指定できます。 データの出力先は、スピーカーもしくはI2Sが指定できます。
また、User Applicationは2つのデータフローを設定できます。 MIXERを利用することで、2つの入力データを1つにMIXすることが出来ます。
User Applicationからの設定はコマンドを、Messageで通信します。 Message通信は、各クライアントごとにIDを持ちます。 Audio Throughの場合、IDを示すと以下のようになります。
User Application : MSGQ_AUD_APP Audio Manager : MSGQ_AUD_MNG
これらのIDを生成時に指定する必要があります。
Audio HW internal dataflow
Audio Throughにおいて、Audio HWの内部ではAudio HW internal dataflowに示すデータフローとなります。
データフローの入力元として、I2S In、MIC In、Mixer Outがあります。
データフローの出力先として、Mixer In1、 Mixer In2、I2SOutがあります。出力先にMixer In1もしくは、Mixer In2を設定した場合は、Mixer Outからスピーカーに出力されます。
設定可能な入力元と出力先の関係は下記の通りです。
入力元 | 出力先 |
---|---|
I2S In | Mixer In1, Mixer In2 |
MIC In | Mixer In1, Mixer In2, I2S Out |
Mixer Out | I2S Out |
How to use
"AudioManager" と呼ばれる
オーディオ・サブシステムを制御するために設計されたソフトウェアコンポーネントで、Audio Throughを実現します。
そのため、Audio Throughを実現するには、以下のオブジェクトの生成関数を事前に呼ぶ必要があります。
必要なオブジェクトが生成されたら、Audio Through動作を行わせるためにAudioのHWの設定や電源On、動作モードの変更などの初期化処理を行います。
以下のコマンドを順に発行することで、実現が可能です。
Audioブロックに電源を入れるために、AUDCMD_POWERON, PowerOnParamコマンドを発行することで、電源を入れてAudioSubSystemの状態をReady状態に遷移します。
enable_sound_effectは、AS_DISABLE_SOUNDEFFECT固定としてください。
AudioCommand command;
command.header.packet_length = LENGTH_POWERON;
command.header.command_code = AUDCMD_POWERON;
command.header.sub_code = 0x00;
command.power_on_param.enable_sound_effect = AS_DISABLE_SOUNDEFFECT;
AS_SendAudioCommand(&command);
PowerOnを行い、Ready状態に遷移したら、AUDCMD_INITOUTPUTSELECT, InitOutputSelectParam コマンドでMixerからの出力先の選択を行います。
output_device_selの設定は以下の通りです。
AS_OUT_OFF : 出力OFF
AS_OUT_SP : スピーカーからの出力
AS_OUT_I2S : I2Sからの出力
HWの電源を制御するため、AS_OUT_I2Sを選択すると、スピーカーから出力されなくなります。Audio ThroughでI2Sとスピーカーを両方使いたい場合は、AS_OUT_SPを選択してください。 |
AudioCommand command;
command.header.packet_length = LENGTH_INITOUTPUTSELECT;
command.header.command_code = AUDCMD_INITOUTPUTSELECT;
command.header.sub_code = 0x00;
command.init_output_select_param.output_device_sel = AS_OUT_SP;
AS_SendAudioCommand(&command);
AUDCMD_SETTHROUGHSTATUSコマンドでAudioSubSystemの状態をThrough状態に遷移します。
AudioCommand command;
command.header.packet_length = LENGTH_SET_THROUGH_STATUS;
command.header.command_code = AUDCMD_SETTHROUGHSTATUS;
command.header.sub_code = 0x00;
AS_SendAudioCommand(&command);
出力にスピーカーを設定する場合、AUDCMD_SETVOLUME, SetVolumeParam で音量を設定できます。 各パラメータの設定は以下の通りです。
I2Sでは音量は変更できません。
MIXER1の音量。dBを10倍の整数値で設定します。設定範囲は-1020(-102.0dB)から120(+12.0dB)で、ステップ幅5(0.5dB)で設定できます。
MIXER2の音量。設定範囲はinput1_dbと同じです。
MIXER1とMIXER2のMix後の音量。設定範囲はinput1_dbと同じです。
AudioCommand command;
command.header.packet_length = LENGTH_SETVOLUME;
command.header.command_code = AUDCMD_SETVOLUME;
command.header.sub_code = 0;
command.set_volume_param.input1_db = 0; /* 0.0dB */
command.set_volume_param.input2_db = 0; /* 0.0dB */
command.set_volume_param.master_db = -200; /* -20.0dB */
AS_SendAudioCommand(&command);
AudioRecorder を参照してください。
使用できるマイクは、CXD5247アナログマイク1, CXD5247アナログマイク2の組み合わせか、 CXD5247デジタルマイク1,CXD5247デジタルマイク1の組み合わせのどちらかです。 |
データフローのパスを設定することで、データの入出力を開始します。
AUDCMD_SETTHROUGHPATH, AsSetThroughPathParam, AsThroughPathでデータパスを同時に2つ設定できます。 それぞれのデータパスの各パラメータの設定は以下の通りです。
データパスの有効、無効を設定します。
true : 有効 false: 無効
データの入力元を設定します。
AS_THROUGH_PATH_IN_MIC : MICを入力元にします AS_THROUGH_PATH_IN_I2S1 : I2Sを入力元にします AS_THROUGH_PATH_IN_MIXER : Mixer Outを入力元にします
MICはアナログマイクの場合、CXD5247アナログマイク1とCXD5247アナログマイク2を指します。デジタルマイクの場合は、CXD5247デジタルマイク1とCXD5247デジタルマイク2を指します。I2Sは、I2S0を指します。 |
データの出力先を設定します。
AS_THROUGH_PATH_OUT_MIXER1 : Mixer In1を出力先にします AS_THROUGH_PATH_OUT_MIXER2 : Mixer In2を出力先にします AS_THROUGH_PATH_OUT_I2S1 : I2Sを出力先にします
I2Sは、I2S0を指します。 |
AudioCommand command;
command.header.packet_length = LENGTH_SET_THROUGH_PATH;
command.header.command_code = AUDCMD_SETTHROUGHPATH;
command.header.sub_code = 0x00;
command.set_through_path.path1.en = true; (1)
command.set_through_path.path1.in = AS_THROUGH_PATH_IN_MIC; (2)
command.set_through_path.path1.out = AS_THROUGH_PATH_OUT_I2S1; (3)
command.set_through_path.path2.en = true; (4)
command.set_through_path.path2.in = AS_THROUGH_PATH_IN_I2S1; (5)
command.set_through_path.path2.out = AS_THROUGH_PATH_OUT_MIXER1; (6)
AS_SendAudioCommand(&command);
1 | データパス1の設定を有効にします |
2 | データパス1はMIC Inを入力元にします |
3 | データパス1はI2S Outを出力先にします |
4 | データパス2の設定を有効にします |
5 | データパス2はI2S Inを入力元にします |
6 | データパス2はMixer In1を出力先にします |
AudioCommand command;
command.header.packet_length = LENGTH_SET_THROUGH_PATH;
command.header.command_code = AUDCMD_SETTHROUGHPATH;
command.header.sub_code = 0x00;
command.set_through_path.path1.en = true; (1)
command.set_through_path.path1.in = AS_THROUGH_PATH_IN_MIC; (2)
command.set_through_path.path1.out = AS_THROUGH_PATH_OUT_MIXER1; (3)
command.set_through_path.path2.en = true; (4)
command.set_through_path.path2.in = AS_THROUGH_PATH_IN_MIXER; (5)
command.set_through_path.path2.out = AS_THROUGH_PATH_OUT_I2S1; (6)
AS_SendAudioCommand(&command);
1 | データパス1の設定を有効にします |
2 | データパス1はMIC Inを入力元にします |
3 | データパス1はMixer In2を出力先にします |
4 | データパス2の設定を有効にします |
5 | データパス2はMixer Outを入力元にします |
6 | データパス2はI2S Outを出力先にします |
AudioCommand command;
command.header.packet_length = LENGTH_SET_THROUGH_PATH;
command.header.command_code = AUDCMD_SETTHROUGHPATH;
command.header.sub_code = 0x00;
command.set_through_path.path1.en = true; (1)
command.set_through_path.path1.in = AS_THROUGH_PATH_IN_MIC; (2)
command.set_through_path.path1.out = AS_THROUGH_PATH_OUT_MIXER2; (3)
command.set_through_path.path2.en = false; (4)
AS_SendAudioCommand(&command);
1 | データパス1の設定を有効にします |
2 | データパス1はMIC Inを入力元にします |
3 | データパス1はMixer In2を出力先にします |
4 | データパス2の設定を無効にします |
AudioCommand command;
AudioResult result;
command.header.packet_length = LENGTH_SET_THROUGH_PATH;
command.header.command_code = AUDCMD_SETTHROUGHPATH;
command.header.sub_code = 0x00;
command.set_through_path.path1.en = true; (1)
command.set_through_path.path1.in = AS_THROUGH_PATH_IN_I2S1; (2)
command.set_through_path.path1.out = AS_THROUGH_PATH_OUT_MIXER1; (3)
command.set_through_path.path2.en = true; (4)
command.set_through_path.path2.in = AS_THROUGH_PATH_IN_I2S1; (5)
command.set_through_path.path2.out = AS_THROUGH_PATH_OUT_MIXER2; (6)
AS_SendAudioCommand(&command);
AS_ReceiveAudioResult(&result); (7)
command.header.packet_length = LENGTH_SET_THROUGH_PATH;
command.header.command_code = AUDCMD_SETTHROUGHPATH;
command.header.sub_code = 0x00;
command.set_through_path.path1.en = true;
command.set_through_path.path1.in = AS_THROUGH_PATH_IN_MIXER; (8)
command.set_through_path.path1.out = AS_THROUGH_PATH_OUT_I2S1; (9)
command.set_through_path.path2.en = false;
AS_SendAudioCommand(&command);
1 | データパス1の設定を有効にします |
2 | データパス1はI2S Inを入力元にします |
3 | データパス1はMixer In1を出力先にします |
4 | データパス2の設定を有効にします |
5 | データパス2はMic Inを入力元にします |
6 | データパス2はMixer In1を出力先にします |
7 | 結果を受け取ります |
8 | データパス1はMixer Outを入力元にします |
9 | データパス1はI2S Outを出力先にします |
Build Configurations
AudioThrough の機能を使用するためには
$>cd sdk $>tools/config.py -m
でConfig menu を開き、以下のConfigを設定する必要があります。
Select options in below:
[CXD65xx Configuration] [Audio] <= Y [SDK audio] <= Y
Error Attentions and Approach
音楽再生時の警告の一覧と、対処方法は以下の通りです。詳細はオーディオサブシステムのエラーについてを参照してください。
ID | Attention Code | Attention Level | Approach |
---|---|---|---|
0x0F | AS_ATTENTION_SUB_CODE_TASK_CREATE_ERROR | ERROR | ヒープ領域が不足していることが原因です。ヒープ領域を拡張して下さい。 |
AS_ATTENTION_SUB_CODE_SIMPLE_FIFO_UNDERFLOW が発生した場合、音声再生が停止し、再生エラーの状態になります。 この状態が発生した場合は、直ちにAsStopPlayerParamコマンドを発行し、再生停止状態に遷移させてください。 再生停止に遷移後、FIFOのクリアを必ず行ってください。行わないとノイズが発生してしまいます。 |
11.3.4. Object Level API
Audio機能では、High Level API よりも細かい単位でもAPIを提供しています。
High Level API をコールした際に内部で使用されるObject群を個別に組み合わせて
使用することでより自由なアプリケーションを作成することが出来ます。
これを Object Level API と呼ぶこととします。
About Usecase
Object Level APIを使用したUsecaseの例を示します。
(組み合わせ方・使い方は自由ですので、これはあくまで例です。)
Usecase 1
MediaPlayerObjectとOutputMixerObjectを使用して、Audioデータのデコード〜出音を行うケースです。
MediaPlayerObjectから応答されたPCMデータをApplicationで処理した後OutputMixerObjectへ送ることが出来ます。
Usecase 2
MediaPlayerObjectを使用せずに、RAM上に置かれたPCMデータをOutputMixerObjectへ送るケースです。
デコードをしないので、例えば効果音など即応性が求められるような音源の再生に適します。
Usecase 3
MediaRecorderObjectを使い記録したMIC・I2S入力をApplicationで加工して
OutputMixer経由でスピーカーやI2S出力に送出するケースです。
MediaPlayerObject
MediaPlayerObjectは、Audioデータのデコード管理とデコード結果PCMの出力を行います。
2つのPlayerを同時に使用することが出来、各APIにあるPlayerIDのパラメータで個別に制御します。
ApplicationはSimpleFIFOと呼ばれるバッファ経由でMediaplayerObjectにESデータを渡します。
バッファがアンダーフローすると再生が停止します。Applicationはそれを考慮した設計をする必要が有ります。
デコードが完了するとMediaPlayerObjectからはPCMデータのMemoryHandleが通知されます。
PCMデータをOutputMixerObjectへ送ることも可能です。この場合はApplicationへの応答はありません。
ただし、当然ですがOutputMixerObjectの生成・起動をしておくことが必要です。
(Defaultは上図の通りApplicationへのCallback応答です。)
Functions
Sequence
MediaPlayerObjectの簡易シーケンスです。
OutputMixerObject
OutputMixerObjectはPCMデータの送出(Rendering)を管理します。
ApplicationはPCMデータをMemoryHandle経由でOutputMixerObjectへ送ります。
送出(Rendering)が完了するとOutputMixerObjectからはcallbackが応答されます。
Functions
Sequence
OutputMixerObjectの簡易シーケンスです。
MediaRecorderObject
MediaRecorderObjectは、Audioデータのエンコード管理とエンコード結果ESの出力を行います。
CaptureしたMICまたはIS2からの入力(PCMデータ)をエンコードしSimpleFIFOバッファに順次格納します。
Applicationはそれを引き抜くことで、ESデータを取得することが出来ます。引き抜きが間に合わずバッファが
オーバーフローすると記録が停止しますので、Applicationはそれを考慮して設計する必要が有ります。
Functions
Sequence
11.3.5. Low Level API
Now Under constructions. mOm |
11.3.6. オーディオサブシステムのエラーについて
概要
Audio SubSystem のHigh Level API は、コマンド・リザルトのデータ送受信によるインターフェースを持っています。
Audio SubSystem に向けて発行したコマンドに問題があった場合に、リザルトでAUDRLT_ERRORRESPONSEが返り、 ErrorResponseパラメータにエラー内容が格納されます。 このエラーをレスポンスエラーと呼びます。
また、Audio SubSystemの内部処理でエラーを検出した場合、内部イベントが発生し、AS_CreateAudioManagerで登録した callback関数でエラーが通知され、ErrorAttentionパラメータにエラー内容が格納されます。 これをアテンションエラーと呼びます。
レスポンスエラー、アテンションエラーに対しては、それぞれのエラーに応じて、不具合対応、エラー処理追加などを行って下さい。
レスポンスエラー
Audio SubSystem に発行したコマンドで、仕様通りの制御を行った場合、リザルトはそれぞれのコマンドに対する完了レスポンスとなります。
しかし、状態違反やパラメータの誤りなど、仕様と異なった制御を行った場合、リザルトでAUDRLT_ERRORRESPONSEが返り、 ErrorResponseパラメータにエラー内容が格納されます。
リザルトのレスポンスエラーのデータ形式については、 リザルトフォーマット と ErrorResponse を参照してください。
この "ErrorResponse" には、"Error Code" が付加されており、この "Error Code" によって、どのような要因で、エラーが発生しているかがわかるようになっています。
以下に"Error Code" の一覧を示します。
Error Code | Value | Description |
---|---|---|
0x01 | 状態違反 | |
0x02 | パケット長パラメータの誤り | |
0x03 | 不明なコマンド | |
0x04 | 無効なコマンド | |
0x05 | 電源ONの失敗 | |
0x06 | 電源OFFの失敗 | |
0x07 | DSPの起動失敗 | |
0x08 | DSPの終了失敗 | |
0x09 | DSPのバージョン不一致 | |
0x0A | 入出力パラメータの誤り | |
0x0B | データパスのクリア失敗 | |
0x0C | 入出力が無効 | |
0x0D | Decoder DSPの初期化失敗 | |
0x0E | Encoder DSPの初期化失敗 | |
0x0F | Filter DSPの初期化失敗 | |
0x11 | コーデック種別の指定の誤り | |
0x13 | チャンネル数の指定の誤り | |
0x14 | サンプリング周波数の指定の誤り | |
0x15 | ビットレートの指定の誤り | |
0x16 | ビット長の指定の誤り | |
0x17 | 圧縮率の指定の誤り | |
0x18 | Playerインスタンスの指定の誤り | |
0x19 | 入力デバイスの指定の誤り | |
0x1A | 出力デバイスの指定の誤り | |
0x1B | 入力デバイスハンドルの指定の誤り | |
0x28 | ミュートパラメータの指定の誤り | |
0x2B | 入出力機能の初期化失敗 | |
0x2C | 入力データの取得失敗 | |
0x2E | メモリプールの設定誤り | |
0x2F | SimpleFIFOのデータが枯渇 | |
0x30 | マイクゲインの指定誤り | |
0x32 | 出力先設定の指定誤り | |
0x33 | ボリュームの指定誤り | |
0x34 | ボリュームの指定誤り | |
0x35 | ミュート対象の指定誤り | |
0x36 | ビープパラメータの指定誤り | |
0x37 | データキュー管理の失敗 | |
0x39 | 動作モードの指定誤り | |
0x3A | 動作モードの設定失敗 |
"Error Code" の詳細は こちら を参照してください。
アテンションエラー
Audio SubSystem内部での処理中(コマンド処理ではなく)に何らかのエラーを検出した場合、通知用のイベントが発生します。 このイベントを受け取るためには、AS_CreateAudioManager でcallback関数を登録しておく必要があります。
アテンションエラーのデータ形式については ErrorAttention を参照してください。
アテンションエラーには 再生動作時のES(Elementary Stream)の供給の途切れ(アンダーフロー)や、記録動作時のES書き込みバッファのあふれ(オーバーフロー)などのフロー制御のエラー、メモリリソースの枯渇や、リアルタイム処理の遅延といったシステムエラー、 HWから発生したエラーなど、復帰にシステムのリセットが必要となる致命的なエラーなどがあります。
これらのエラーは、"ErrorAttention"に付加されている"Attention Code"で判断することが出来ます。 発生した"Attention Code"に基づいて修正を行ってください。 また、実装方法を変えることで、エラーが改善されることもあります。
以下に、"ErrorAttention" に付加される、 "Attention Code" の一覧を示します。
Attention Code | Value | Description |
---|---|---|
0x01 | DMA転送のアンダーフロー | |
0x02 | DMA転送のオーバーフロー | |
0x03 | DMA転送の失敗 | |
0x05 | SimpleFIFOのアンダーフロー | |
0x06 | SimpleFIFOのオーバーフロー | |
0x07 | 不正なイベントの受信 | |
0x08 | 内部状態の異常 | |
0x09 | 内部パラメータの異常 | |
0x0A | 内部キューのPOPエラ | |
0x0B | 内部キューのPUSHエラ | |
0x0C | 内部キューの枯渇 | |
0x0D | メモリハンドルの取得失敗 | |
0x0E | メモリハンドルの解放失敗 | |
0x0F | タスクの生成失敗 | |
0x10 | インスタンスの生成や削除の失敗 | |
0x12 | DSPの起動失敗 | |
0x13 | DSPの終了失敗 | |
0x14 | DSPの処理でエラー | |
0x16 | DSPから不正なデータ受信 | |
0x18 | DSPのバージョン不一致 | |
0x19 | AudioDriverでエラー | |
0x1A | ESデータの解析エラー | |
0x1E | DSPのログ用バッファの取得失敗 | |
0x1F | DSPの処理で致命的エラー | |
0x20 | DSPへのコマンド送信エラー |
"Attention Code" の詳細は こちら を参照してください。
Attention通知にはレベルが指定されており、そのエラーに対する深刻度と同時に復帰の処理方法が変わります。
Level | value | Description |
---|---|---|
FATAL | 0x03 | システムコールエラー等、回復不能なもので、復帰にリセットを要求します。 |
ERROR | 0x02 | 内部エラー(キューFull/Empty,DSPロード/アンロードなど)でAudioシステムの動作が継続できないようなエラーです。システムを初期状態(Ready状態)に戻すことで復帰が可能になります。 |
WARN | 0x01 | エンコード・デコードエラー、データのアンダーフロー・オーバーフローなど、動作に異常があり、音声データなどには異常が発生している可能性があるが、動作は継続できるものです。 |
Error Code List
"Error Code"の詳細は下記の通りです。
AS_ECODE_STATE_VIOLATION
AS_ECODE_PACKET_LENGTH_ERROR
AS_ECODE_COMMAND_CODE_ERROR
AS_ECODE_COMMAND_NOT_SUPPOT
AS_ECODE_AUDIO_POWER_ON_ERROR
AS_ECODE_AUDIO_POWER_OFF_ERROR
AS_ECODE_DSP_LOAD_ERROR
AS_ECODE_DSP_UNLOAD_ERROR
AS_ECODE_DSP_VERSION_ERROR
AS_ECODE_SET_AUDIO_DATA_PATH_ERROR
AS_ECODE_CLEAR_AUDIO_DATA_PATH_ERROR
AS_ECODE_NOT_AUDIO_DATA_PATH
AS_ECODE_DECODER_LIB_INITIALIZE_ERROR
AS_ECODE_ENCODER_LIB_INITIALIZE_ERROR
AS_ECODE_FILTER_LIB_INITIALIZE_ERROR
AS_ECODE_COMMAND_PARAM_CODEC_TYPE
AS_ECODE_COMMAND_PARAM_CHANNEL_NUMBER
AS_ECODE_COMMAND_PARAM_SAMPLING_RATE
AS_ECODE_COMMAND_PARAM_BIT_RATE
AS_ECODE_COMMAND_PARAM_BIT_LENGTH
AS_ECODE_COMMAND_PARAM_COMPLEXITY
AS_ECODE_COMMAND_PARAM_ACTIVE_PLAYER
AS_ECODE_COMMAND_PARAM_INPUT_DEVICE
AS_ECODE_COMMAND_PARAM_OUTPUT_DEVICE
AS_ECODE_COMMAND_PARAM_INPUT_HANDLER
AS_ECODE_COMMAND_PARAM_CONFIG_TABLE
AS_ECODE_COMMAND_PARAM_WITH_MFE
AS_ECODE_COMMAND_PARAM_WITH_MPP
AS_ECODE_COMMAND_PARAM_INPUT_DB
AS_ECODE_DMAC_INITIALIZE_ERROR
AS_ECODE_DMAC_READ_ERROR
AS_ECODE_CHECK_MEMORY_POOL_ERROR
AS_ECODE_SIMPLE_FIFO_UNDERFLOW
AS_ECODE_SET_MIC_GAIN_ERROR
AS_ECODE_SET_OUTPUT_SELECT_ERROR
AS_ECODE_INIT_CLEAR_STEREO_ERROR
AS_ECODE_SET_VOLUME_ERROR
AS_ECODE_SET_VOLUME_MUTE_ERROR
AS_ECODE_SET_BEEP_ERROR
AS_ECODE_QUEUE_OPERATION_ERROR
AS_ECODE_COMMAND_PARAM_RENDERINGCLK
AS_ECODE_SET_RENDERINGCLK_ERROR
Attention Code List
"Attention Code" の詳細は下記の通りです。
AS_ATTENTION_SUB_CODE_DMA_UNDERFLOW
AS_ATTENTION_SUB_CODE_DMA_OVERFLOW
AS_ATTENTION_SUB_CODE_DMA_ERROR
AS_ATTENTION_SUB_CODE_SIMPLE_FIFO_UNDERFLOW
AS_ATTENTION_SUB_CODE_SIMPLE_FIFO_OVERFLOW
AS_ATTENTION_SUB_CODE_ILLEGAL_REQUEST
AS_ATTENTION_SUB_CODE_INTERNAL_STATE_ERROR
AS_ATTENTION_SUB_CODE_UNEXPECTED_PARAM
AS_ATTENTION_SUB_CODE_QUEUE_POP_ERROR
AS_ATTENTION_SUB_CODE_QUEUE_PUSH_ERROR
AS_ATTENTION_SUB_CODE_QUEUE_MISSING_ERROR
AS_ATTENTION_SUB_CODE_MEMHANDLE_ALLOC_ERROR
AS_ATTENTION_SUB_CODE_MEMHANDLE_FREE_ERROR
AS_ATTENTION_SUB_CODE_TASK_CREATE_ERROR
AS_ATTENTION_SUB_CODE_RESOURCE_ERROR
AS_ATTENTION_SUB_CODE_DSP_LOAD_ERROR
AS_ATTENTION_SUB_CODE_DSP_UNLOAD_ERROR
AS_ATTENTION_SUB_CODE_DSP_EXEC_ERROR
AS_ATTENTION_SUB_CODE_DSP_ILLEGAL_REPLY
AS_ATTENTION_SUB_CODE_DSP_VERSION_ERROR
AS_ATTENTION_SUB_CODE_BASEBAND_ERROR
AS_ATTENTION_SUB_CODE_STREAM_PARSER_ERROR
AS_ATTENTION_SUB_CODE_DSP_LOG_ALLOC_ERROR
AS_ATTENTION_SUB_CODE_DSP_ASSETION_FAIL
AS_ATTENTION_SUB_CODE_DSP_SEND_ERROR
Module ID List
AudioSubSystem内部で使用するモジュールのID一覧です。
Attention callbackでアテンションコードとともに通知され、どのモジュールでエラーが発生したかを判断します。
Audio Manager ID.
Audio Baseband Driver Module ID.
Input Data Manager Object ID.
Media Recorder Object ID.
Output Mix Object ID.
Player Object ID.
Recognition Object ID.
Sound Effect Object ID.
Capture Component ID.
Decoder Component ID.
Encoder Component ID.
Filter Component ID.
Recognition Component ID.
Renderer Component ID.
Postfilter Component ID.
11.3.7. 使用しているライブラリ
オーディオ・サブシステムは以下のライブラリを必要とします。
11.4. Camera
11.4.1. 概要
CXD5602には8ビットパラレルのCamera I/Fが備わっており、このI/Fを持つカメラモジュールを接続することができます。現在、Spresenseでは、Sony ISX012を搭載するカメラモジュールをサポートしています。 通常、カメラモジュールはデータ用のインターフェースに加えて、モジュールの制御を行うインターフェースを持っています。ISX012では、その制御用I/FにI2Cが用いられています。 以下にHWの構成の概要を示します。
CXD5602内部には、CISIFと呼ばれるCamera I/Fブロックがあり、このブロックで8ビットパラレル信号とCXD5602内部バスのブリッジを行なっています。ISX012カメラモジュールの場合、これに加えて、I2Cバスを使って制御を行なっています。
この章では、このCamera I/Fに接続されたカメラモジュールをSpresense SDKで制御するための概要を説明します。
Spresense SDKのCamera制御では、Linuxでお馴染みのV4L2に非常によく似たドライバI/Fを提供しており、V4L2を用いたコードからの流用をしやすくしています。このI/FをV4S (Video for Spresense) と呼びます。V4Sでは、デバイスファイルを介して、open, close, ioctlなどの標準のI/Fを用い、ISX012などのデバイスを意識することなくCameraとしての機能を抽象化したAPIを提供します。
V4Sでは、カメラの制御として、2つの仮想Videoストリームを提供します。 2つの仮想ストリームのうち、1つはCameraのPreview画像のような動画を扱うためのストリームとして、もう一つは静止画を取得するためのストリームとしての役割を持っています。
これら2つのストリームに対して、アプリケーションからデータを取得するには、アプリケーションで用意したバッファをVIDIOC_QBUFを用いてドライバにセットし、VIDIOC_DQBUFでVIDIOC_QBUFでセットしたバッファを取ることでイメージデータを取り出すことができます。
アプリケーションが用意するバッファは、memalign()等を用いて獲得した、32ビットアラインメントである必要があります。 |
各ストリームの指定は、v4l2_buf_typeで指定し、V4L2_BUF_TYPE_VIDEO_CAPTUREが動画を扱うストリーム、V4L2_BUF_TYPE_STILL_CAPTUREが静止画用のストリームに対応しています。
この、V4L2_BUF_TYPE_STILL_CAPTUREは、V4S固有のパラメータになります。 |
V4Sの初期化からイメージデータをキャプチャするまでの概略の流れは以下のようになります。
11.4.2. 状態遷移
ストリーム毎に状態を管理しており、アプリケーションからは並行して制御可能です。 ただし、imageの取得についてはV4L2_BUF_TYPE_STILL_CAPTURE制御優先であり、VIDIOC_TAKEPICT_START - VIDIOC_TAKEPICT_STOP間はV4L2_BUF_TYPE_VIDEO_CAPTURE側のimage取得は停止します(状態遷移図において"dma"状態ではなくなる)。
11.4.3. V4Sサポート ioctlコマンド
分類 | command | 目的 | Spresenseカスタマイズ |
---|---|---|---|
バッファ制御 | ドライバのバッファ管理領域の初期設定を行う。 | パラメータ"v4l2_buf_mode mode"を追加し、 バッファ群にRING構造を持たせられるようにしています。 | |
アプリが用意したバッファをエンキューする。 | V4L2準拠 | ||
imageデータが入ったバッファをデキューする。 | V4L2準拠 | ||
VIDIOC_DQBUFを取り消す。 | 左記目的のために追加したSpresense独自コマンド。 | ||
ストリーム制御 | ストリームを開始する。 | V4L2準拠 | |
ストリームを停止する。 | V4L2準拠 | ||
静止画撮影開始 | 左記目的のために追加したSpresense独自コマンド。 | ||
静止画撮影停止 | 左記目的のために追加したSpresense独自コマンド。 | ||
フレーム設定の値域確認 | カメラデバイスが対応しているpixelフォーマットの確認 | V4L2準拠 | |
カメラデバイスが対応しているimageサイズの確認 | パラメータにv4l2_buf_typeを追加し、ストリーム毎に確認できるようにしています。 | ||
カメラデバイスが対応しているframe intervalの確認 | パラメータにv4l2_buf_typeを追加し、ストリーム毎に確認できるようにしています。 | ||
INパラメータで指定したpixelフォーマットとimageサイズの組み合わせが 設定可能かどうかの確認。 | V4L2準拠 | ||
フレーム設定の変更 | pixelフォーマットとimageサイズを設定する。 | V4L2準拠 | |
frame intervalを設定する(分数の分子と分母を指定できる)。 | V4L2準拠 | ||
カメラ設定の値域確認 | カメラ設定の値域を確認する。 | V4L2準拠 | |
カメラ設定の値域を確認する。 VIDIOC_QUERYCTRLの拡張APIで、VIDIOC_QUERYCTRLを包含する。 | V4L2準拠 | ||
カメラ設定で離散値をとる項目について、とりうる値を取得する。 | V4L2準拠 | ||
カメラ設定の現在値確認 | カメラ設定の現在値を確認する。 | V4L2準拠 | |
カメラ設定の現在値を確認する。 VIDIOC_G_CTRLの拡張APIで、VIDIOC_G_CTRLを包含する。 | V4L2準拠 | ||
カメラ設定の変更 | カメラ設定を変更する。 | V4L2準拠 | |
カメラ設定を変更する。 VIDIOC_S_CTRLの拡張APIで、VIDIOC_S_CTRLを包含する。 | V4L2準拠 | ||
シャッターボタンを半押しした場合に相当する設定変更を行う。 | アプリケーションにおける半押し制御を簡略化するために追加したSpresense独自コマンド。 |
11.4.4. ISX012独自仕様
JPEG + YUV4:2:2フォーマット
ioctl(VIDIOC_S_FMT)において、
-
パラメータ pixelformat に V4L2_PIX_FMT_JPEG_WITH_SUBIMG
-
パラメータ subimg_pixelformat に V4L2_PIX_FMT_UYVY
を設定することによって、 ある1フレームのimageを JPEG、YUV4:2:2の2つのデータフォーマットで同時に取得することができます。
例えば、写真を撮ってJPEGで保存しつつ、保存したものと同じ画像をディスプレイに表示するようなアプリをJPEGデコーダを用いずに実現できます。
ただし、それぞれのフォーマットを単独で取得する場合と比較して、imageサイズとframe rateの制約が厳しくなっております。
11.4.5. imageサイズとframe rateの制約
VIDIOC_S_FMTで設定できるimageサイズと、VIDIOC_S_PARMで設定できるframe interval(frame rateの逆数)には関連があります。
(imageサイズを大きくすると、大きいframe rateは設定できなくなります。)
以下、Spresense + ISX012におけるサポート範囲をpixelフォーマット毎に表したものになります。 ISX012に設定できるframe rateは離散値をとり、120 / 60 / 30 / 15 / 7.5 / 6 / 5 の7種類が設定可能です。
下図において、例えば、YUV4:2:2フォーマットの場合は
-
QVGA以下: max FPS=120なので、120FPS~5FPSの7種類を設定可能
-
QVGAより大きい: max FPS=60なので、60FPS~5FPSの6種類を設定可能
という制約になります。
JPEG + YUV4;2;2フォーマットにおいて、 YUV422 imageサイズは 96x64からWQVGA(400x240)までをサポートしており、 この範囲内であればframe rateの制約には影響しません。
11.4.6. サンプルコード
11.5. DNN Runtime
11.5.1. DNN Runtime 概要
DNN Runtimeライブラリ(dnnrt)は、Sonyが提供するNeural Network LibrariesまたはNeural Network Console(NNC)で学習したモデルを使用して、Deep Neural Network (DNN) を用いた認識処理を行うことができます。
DNN Runtime を使用して認識処理を行うためには、事前にNNCで学習済みモデル(nnbファイル形式)を作成する必要があります。 学習済みモデルの作成方法は、こちら学習済みモデルの準備を参照してください。
Neural Network Libraries および Neural Network Console は、以下の公式サイトをご覧ください。
また、DNN Runtime ライブラリは、NNabla C Runtimeを使用しています。
11.5.2. サンプルコード
このサンプルコードは、こちら学習済みモデルの準備の章で使用している image_recognition.MNIST.LeNet
で作成された学習モデルを実行し、手書き文字認識を行うためのサンプルコードです。
image_recognition.MNIST.LeNet
で作成された学習モデルは、28x28サイズの画像を1つ入力にとり、10個の配列を出力します。この10個の配列はインデックスが認識した数字を意味しており、配列の中にそれぞれの数字である確率が出力されます。例えば、配列の先頭(インデックス0)には、入力された画像が数字の「0」である確率が出力されます。
詳細は、DNNRT example READMEを参照してください。
11.6. GNSS
GNSS library はGPS/GLONASS 衛星の信号を受信し現在位置を算出する機能を提供します。 この機能を使うには、GNSS用のアンテナを装着する必要があります。 Spresenseボードでは、チップアンテナが搭載されているため、追加のアンテナは不要です。
11.6.1. 主な特徴
-
GPS/GLONASS に加え QZSS (みちびき)衛星システムからの信号を受信可能な Multi GNSS をサポート
-
SBAS(WAAS)及びQZSS L1Sによる測位補強が利用可能です
-
アプリケーションコアとGNSSコアは分離しているため、独立して測位と通知を行うことができます
-
Geofence をサポートします
これらの機能は POSIX 準拠のデバイスファイルでアプリケーションから操作することができます。 デバイスファイルは、"/dev/gps" で、open(), close(), read(), seek(), ioctl() で操作します。
NuttX RTOS の ioctl は3つの引数を持ちます。 GNSS デバイスファイルに関しては、2番目の引数"req" が GNSS コマンド、3番目の引数"arg" が in/out data の引き渡しになります。
11.6.2. Configuration
GNSS 機能を使うには、CONFIG_CXD56_GNSS を "y" にセットする必要があります。
[System Type] [CXD56xx Peripheral Support] [GNSS device] (CXD56_GNSS) = Y
NMEA 形式を使いたい場合は、CONFIG_CXD56_GNSS, CONFIG_LIBM, CONFIG_GPSUTILS_CXD56NMEA_LIB を "y" にセットする必要があります。
[System Type] [CXD56xx Peripheral Support] [GNSS device] (CXD56_GNSS) = Y [Library Routines] [Standard Math library] (LIBM) = Y [Application Configuration] [GPS Utilities] [Support CXD56xx gnss NMEA convert library] (GPSUTILS_CXD56NMEA_LIB) = Y
また、サンプルアプリケーションを有効にするには、CONFIG_CXD56_GNSS に加え CONFIG_EXAMPLES_GNSS を "y" にセットしてください。
[System Type] [CXD56xx Peripheral Support] [GNSS device] (CXD56_GNSS) = Y [Application Configuration] [Examples] [GNSS positioning example] (EXAMPLES_GNSS) = Y
NMEAのサンプルをアプリケーションを有効にするには、NMEA設定の他に、CONFIG_EXAMPLES_GNSS_ATCMD を "y" にセットする必要があります。
[System Type] [CXD56xx Peripheral Support] [GNSS device] (CXD56_GNSS) = Y [Library Routines] [Standard Math library] (LIBM) = Y [Device Drivers] [USB Device Driver Support] [USB Modem (CDC/ACM) support] = Y [Application Configuration] [GPS Utilities] [Support CXD56xx gnss NMEA convert library] (GPSUTILS_CXD56NMEA_LIB) = Y [Application Configuration] [Examples] [GNSS CXD5603 @command emulator example] (EXAMPLES_GNSS_ATCMD) = Y
(参考情報) ファクトリテストを有効にしたい場合は、CONFIG_CXD56_GNSS に加え CONFIG_EXAMPLES_GNSS_FACTORY を "y" にセットします。 また、EXAMPLES_GNSS_FACTORY_SVID をテスト環境に合わせて設定してください。 ファクトリテストを直接動かす場合は、[Application entry point] を "gnss_factory_test" に変更します。 テスト結果 (cn と doppler) は 1000000 倍の値が出てきます。
[System Type] [CXD56xx Peripheral Support] [GNSS device] (CXD56_GNSS) = Y [Application Configuration] [Examples] [GNSS FACTORY test] (EXAMPLES_GNSS_FACTORY) = Y [FACTORY TEST svid] (EXAMPLES_GNSS_FACTORY_SVID) = 1 [RTOS Features] [Tasks and Scheduling] [Application entry point] set 'gnss_factory_test'
詳細については、以下の README を参照してください。 |
以下の設定はどんな場合でも変更可能です。
-
'Disable power control of the LNA device from GNSS device driver' は、PMIC による基盤上の LNA (Low Noise Amplifier) デバイス電源制御設定の項目です。 PMICにて電源制御しない場合は 'Y' としてください。詳細は基板の回路図を参照して、設定するべきか否か判断してください。
-
'Enable GNSS HOT Sleep' は GNSS 測位停止中に GNSS CPU を "Hot Sleep" 状態にするかどうかを設定します。
-
'GNSS backup file name' と 'GNSS CEP file name' は GNSS によって使用されるファイルです。パス名をシステムの構成にあわせて変更してください。
[System Type] [CXD56xx Peripheral Support] [GNSS setting] [Disable power control of the LNA device from GNSS device driver] = N [Enable GNSS HOT Sleep] = N [GNSS backup file name] = '/mnt/spif/gnss_backup.bin' [GNSS CEP file name] = '/mnt/vfat/gnss_cep.bin'
GNSS デバイスドライバは アプリケーションに POSIX形式の poll もしくは シグナルを通じて測位結果を通知します。 poll できる数は以下のコンフィギュレーションで設定できます。デフォルトでは、poll 数は 4、signal 数は 3 です。 詳細については、 Position calculation notification を参照してください。
[System Type] [CXD56xx Peripheral Support] [GNSS setting] [GNSS max poll waiters] = 4 [GNSS max signal receivers] = 3
11.6.3. Device control
GNSS ライブラリは ioctl() を通じて制御します。
ioctl のコマンドは、IOCTL_ のサフィックスが付与されて定義されています。 GNSS デバイスファイルは、複数のアプリケーションから同時に open() することができます。 ただし、GNSS デバイスは、一度に一つの IOCTL コマンドしか受け付けません。言い換えると、どのアプリケーションが優先されるということでなく、受け付けられた順番に処理されます。
Startup
どの衛星システムを起動時に使うか CXD56_GNSS_IOCTL_SELECT_SATELLITE_SYSTEM で設定できます。また測位間隔については、CXD56_GNSS_IOCTL_SET_OPE_MODE で設定します。
"Hot Start" の場合("Hot Start")、直近の位置と時間を設定する必要があります。詳細については、Using GNSS backup data の "Warm Start と Hot Start" の設定を参照してください。
Start positioning
"start mode" を CXD56_GNSS_IOCTL_START で指定することができます。
以下の初期位置算出時間 TTFF は参考値です。お使いのシステム及び環境によって変動します。 |
Start mode | IOCTL command parameter | TTFF [1] | Information to use |
---|---|---|---|
Cold Start | > 45 sec | 全ての測位データ、時間、衛星軌道情報は全て消去され、次回起動時は衛星の捕捉から開始します。 | |
Warm Start | > 20 sec | 直近の測位データ、時間、アルマナック(粒度の粗い衛星軌道情報)を使って起動します。エフェメリス(詳細な衛星軌道情報)は用いません。 | |
Hot Start | >= 1 sec | 直近の測位データ、時間、アルマナック、エフェメリスを使って起動します。 |
"Warm Start" もしくは、"Hot Start" であったとしても、前回の動作時から長い時間が経過し、衛星軌道情報が古く期限切れの条件では、GNSS デバイスは "Cold Start" と同様に衛星の捕捉から開始します。
Stop positioning
測位を停止するには、 CXD56_GNSS_IOCTL_STOP を使用します。停止するには200から300ミリ秒かかります。
Position calculation notification
通知は、poll と シグナルの2種類あります。
poll を使う場合は、CONFIG_CXD56_GNSS_NPOLLWAITERS でポーリング数を設定することができます。
シグナルを使う場合は、フィールド fd に GNSS デバイスのファイルディスクリプタを, enable には 1 を, signo に任意のシグナル番号を、さらに gnsssig に CXD56_GNSS_SIG_GNSS を設定した cxd56_gnss_signal_setting_s データを、IOCTRLコマンド CXD56_GNSS_IOCTL_SIGNAL_SET の引数として ioctl関数を呼び出してシグナルと測位通知を関連付けして下さい。
アプリケーションは、sigwaitinfo で通知をシグナルとして受け取ることができます。 シグナルが不要になった場合は、フィールドenableに 0 を指定した cxd56_gnss_signal_setting_s データを、 CXD56_GNSS_IOCTL_SIGNAL_SET で設定することで関連付けをクリアすることができます。 シグナルの関連付けの最大数は、コンフィギュレーションの CONFIG_CXD56_GNSS_NSIGNALRECEIVERS で変更できます。
CONFIG_EXAMPLES_GNSS_USE_SIGNAL を設定した場合のサンプルは、gnss application example programで参照できます。
11.6.4. For faster positioning
"Hot Start" でTTFFを短くするためには、バックアップデータ中に含まれる衛星軌道上やその他の情報を活用することが早道です。 ioctl コマンドを使って、バックアップデータを活用した標準的な "Hot Start" の手順を示します。
Using GNSS backup data
バックアップデータに含まれる最終測位位置、エフェメリス、アルマナック、TCXO クロックのオフセット値が、 "Hot Start" で測位を開始する際に利用されます。 バックアップデータは ioctl コマンドによって、フラッシュメモリに蓄積することができます。その場合、電源がオフになった場合でも次の起動時にフラッシュメモリからSRAMに展開され、"Hot Start" に利用することができます。
Power condition and backup data
システムが起動している状態では、GNSS測位は動いており、全ての関連情報はストアされます。システムが "Hot Sleep" している状態では、SRAM のデータは保持され、 RTC は動き続けているため、"Hot Start" で測位を開始することができます。"Deep Sleep" 状態では RTC は動作を続けますが、SRAM はオフとなるため、"Hot Start" のためにはフラッシュメモリに保存したバックアップデータをリストアして利用する必要があります。
Saving backup data
バックアップデータをフラッシュメモリなどに保存するには、ioctl コマンド CXD56_GNSS_IOCTL_SAVE_BACKUP_DATA を使用します。バックアップデータは、CONFIG_CXD56_GNSS_BACKUP_FILENAME に指定されたファイル名で保存されます。 ただし、フラッシュメモリは頻繁に書き込むとメモリの故障の原因になりますので、頻繁な書き込みは控えるようにしてください。例えば、システムがシャットダウンするタイミングで保存するのが良いでしょう。
Invalid backup data
まれに、保存したバックアップデータが壊れていることがあります。その場合、データのリストアはチェックサムエラーで失敗し、測位も "Cold Start" から開始します。
Expired data
エフェメリスやアルマナックの期限が切れていた場合、それらのデータは無視されます。その場合は、"Cold Start" 相当の処理となりますのでTTFFも長くなります。測位を開始後、最新のエフェメリスやアルマナックを衛星から受信した際に、バックアップデータの衛星軌道情報も上書きされます。
GPS time
"Hot Start" を行うためには、GNSS デバイスに誤差 60 秒以内の現在時刻が設定されている必要があります。以前に測位を行いシステムの電源が切られていなければ RTC_GPS に最後の測位時に設定された時刻が設定されています。電源投入直後には RTC_GPS の値は不定ですのでアプリケーションから ioctl コマンドにて時刻を設定する必要があります。
Accuracy of RTC_GPS
測位の停止時に、GNSS デバイスはLSI内に持つRTCクロック精度のタイマー RTC_GPS を GPS 時間に更新します。 測位停止中は GNSS デバイスの時計は停止し、この RTC_GPS が計時します。 RTC_GPS が保持する時刻と実時間の誤差は、外付けRTC Xtalのクロック精度に依存します。 測位を再開するさい、GNSS デバイスはこの RTC_GPS の時刻を基に測位演算を行うため、停止期間が長いほど RTC_GPS の時刻は実時間に対して誤差を持ち、TTFFと初回測位位置の精度に影響を与えます。 測位停止から15分以上経つとRTC_GPS の時刻誤差は、その後の "Hot Start" の測位結果に対して無視し得なくなります。 長い測位停止期間の後にも、短いTTFFや高い初回位置精度を求める場合は、ioctlコマンドで時刻を設定することを考慮しても良いでしょう。
システムが電源オンされた直後は、RTC_GPS は "0h 6 - Jan - 1980" を示します。測位が完了すると GPS 時間が取得できるため、RTC_GPSがGPS時間にセットされます。時刻が指定されていない限り、測位計算は RTC_GPS をベースに行われます。
Set time by the command
アプリケーションがネットワーク等から取得した時刻を指定する場合は、ioctl コマンド CXD56_GNSS_IOCTL_SET_TIME を用います。
Current location
"Hot Start" のためには、直近の位置データが必要です。アプリケーションが位置データを保持していない場合は、GNSS デバイスは前回測定した位置情報をベースに "Hot Start" を行います。
Set location by the command
アプリケーションがネットワーク等から取得した位置情報を指定する場合は、ioctl コマンド CXD56_GNSS_IOCTL_SET_RECEIVER_POSITION_ELLIPSOIDAL もしくは、CXD56_GNSS_IOCTL_SET_RECEIVER_POSITION_ORTHOGONAL を用います。
11.6.5. Accurate Positioning
正確な測位を行うために、適切な受信条件で測位を行う必要があります。ビル街における衛星の見えにくさや大きな建物の反射波の影響、WIFIからのノイズなど、測位環境が精度の高い測位に影響を与えていないかが、測位データからある程度類推することができます。
Indication in receiver positioning data
Field numsv, numsv_tracking, numsv_calcpos, numsv_calcvel
Type | uint8_t |
Unit | none |
フィールド numsv, numsv_tracking, numsv_calcpos, numsv_calcvel は、それぞれ可視衛星数、追尾衛星数、位置演算に利用している衛星数、速度演算に利用している衛星数です。 可視衛星には、衛星軌道情報から現在の天空に存在が予想されるが、実際には障害物があり信号を受信していない衛星も含みます。 追尾している衛星の中でも受信が弱く正確性に欠く信号は測位や測速の演算に利用しません。 位置精度は後述の信号の強度や衛星の配置にも依存するため一概には言えませんが、一般的に numsv_calcpos の個数を 6 個以上に保つと数メートルの誤差で測位を続けられることが多いようです。
Field posDop, velIdx of struct cxd56_gnss_receiver_s
Type | struct cxd56_gnss_dop_s |
Sub fields | pDop, hDop, vDop, ewDop, nsDop as float |
Unit | none |
posDop は 衛星の幾何学的な配置から求められる位置精度低下率である DOP(Dilution Of Precision) を含みます。 'p' は全体的な精度低下率、 'h' は水平方向の精度低下率、 'v' は縦方向の精度低下率、 'ew' は東西の精度低下率、 'ns' は南北の精度低下率を表しています。
DOP が小さいほど、測位するのに適した場所であることを示しています。例えば、街中のような狭い空では受信できる衛星は少なく、DOP は悪化します。pDOP が 5 を超えるような場合、測位には不適な条件であることを意味しています。一方、pDOP が 2以下だった場合は、測位するのに十分な条件を満たしています。
Field posAcc of struct cxd56_gnss_receiver_s
Type | struct cxd56_gnss_var_s |
Sub fields | hVar, vVar as float |
Unit | meter |
これらの値は、測位データの正確さを表し、位置と速度の時系列値に対する共分散の平方根です。これらは、受信ノイズやその他の要因による測位データへの影響を見ることができます。'h' は水平方向の正確さ、 'v' は縦方向の正確さを表しています。
PosAcc は、測位誤差の標準偏差を表しています。衛星信号に対するノイズやマルチパスの影響、DOP の悪化などが測位結果に誤差を与えます。この値が大きいほど、測位データが不正確であることを示します。
Indication in satellite positioning data
Field sigLevel of struct cxd56_gnss_sv_s
Type | float |
Sub fields | none |
Unit | dBHz |
この値は、ノイズに対する衛星からの信号の強さの比率である C/N0 比(または CN0, CN と表されることもあり)を表しています。 GNSS衛星からの信号はスペクトラム拡散のため、これをS/N比ではなく C/N0 比と呼び、また単位も dBHz となります。 この値が大きいほど、GNSSからの衛星信号の信頼性は高い事を示し、結果としてそれを用いた測位も安定します。 一方、ノイズ源が GNSS アンテナの近くにある、衛星の方向がアンテナの指向性が悪い方向と一致している、あるいは衛星とアンテナの間に障害物がある、さらに衛星信号の追尾が不良であると、この値は小さくなり、測位が不安定になります。 なお衛星信号個々にこれら C/N0比 悪化の要因は異なるため、各衛星によってC/N0比も異なります。 一般的に、5つ以上の衛星から信号の C/N0 が 30 dBHz以上で受信できていると測位結果は安定します。
Multi-GNSS
前出の様に捕捉追尾する衛星数が多いほど測位精度が向上する傾向があります。 GNSS デバイスは複数の衛星システムを同時に利用し、測位演算に供する衛星信号の数を増やすことが出来ます。
-
GLONASS
GPSと同様に全世界をカバーする24機体制のロシアの測位衛星システムです。 GPSだけでも十分衛星数が確保出来ている場合に、GPSシステムの標準精度 20m に比べると劣る精度 70m のGLONASSシステムの信号を混在して測位演算すると、GPS 単独の場合よりも位置精度が落ちる可能性があります。 なおアンテナにはGLONASSの周波数に対応していない物もあるため注意が必要です。
-
QZSS-L1C/A
日本のみちびきから送信される衛星信号です。 この信号はGPS L1C/Aと互換のため、これを利用するとあたかもGPS衛星が増えたかのように見えます。これをみちびきによるGPSの補完機能と呼んでいます。 みちびきは4機体制(2018年時点)で、その軌道は日本を中心に東アジアとオセアニア上空をカバーするため、それ以外の地域では補完効果がありません。
Augmentation
WAAS もしくは みちびきの QZSS-L1S から送信される補強信号を利用することで測位精度を上げることが出来ます。 補強信号はSBASフォーマットをベースとした、数分ごとに更新される測位演算の精度を向上するための計算パラメータです。 補強信号を使う場合、補強情報が無いGLONASSを混在させての測位は出来ません。
-
WAAS
WAASはアメリカ合衆国本土、カナダ、ハワイ及び属州で有効な補強信号です。 GPS衛星の信号から求められる擬似距離 [2]の精度を向上します。
-
QZSS-L1S
QZSS-L1Sは日本の範囲のみで有効です。 GPS衛星信号及びQZSS-L1C/Aから求められる擬似距離 [2]の精度を向上します。 仰角の低い衛星(2018年9月時点で仰角マスクは20度)は補強情報が送信されず、測位に使うことができません。
Select positioning and augmentation satellite systems
本 GNSS デバイスでは、以下の測位衛星システム及び補強信号の組み合わせで測位することを想定しています。 どの衛星システムを使うかは CXD56_GNSS_IOCTL_SELECT_SATELLITE_SYSTEM で設定できます。
以下の測位誤差(Accuracy)は一般的な好条件下での参考値です。お使いのシステム及び環境によって変動します。 |
GPS | GLONASS | QZSS-L1C/A | WAAS | QZSS-L1S | 95% Accuracy | Effective place |
---|---|---|---|---|---|---|
x | <5m | under the open sky | ||||
x | x | <5m | in East Asia and Oceania | |||
x | x | <7.8m | in the city | |||
x | x | x | <7.8m | in the city of East Asia and Oceania | ||
x | x | x | <2m | in Japan |
11.6.6. Short message delivery
GNSS デバイスはQZSSみちびきから送信される災害情報、危機管理情報といった災危(災害・危機)通報を受信できます。
アプリケーションはシグナル通知を GNSS デバイスに設定することで、GNSS デバイスからの災危通報を非同期にシグナルで受けることができるようになります。 災危通報のシグナル通知の設定は、フィールド fd に GNSS デバイスのファイルディスクリプタを, enable には 1 を, gnsssig に任意のシグナル番号を、さらに gnsssig に CXD56_GNSS_SIG_SBAS を設定した cxd56_gnss_signal_setting_s データを、IOCTRLコマンド CXD56_GNSS_IOCTL_SIGNAL_SET の引数として ioctl関数を呼び出してシグナルと災危通報を関連付けして下さい。
GNSS デバイスは SBAS メッセージタイプ の 43 (気象庁防災情報)または 44 (任意情報)を受信すると、関連付けしたシグナルを発行します。 sigwaitinfoでのシグナル処理にて データバッファに doxgen:cxd56_gnss_sbasdata_s[] 、 CXD56_GNSS_READ_OFFSET_SBAS をオフセットとしてGNSSデバイスを read することで通知の要因となった SBAS メッセージを読み出すことが出来ます。
11.6.7. Utilities
NMEA converter
NMEA は GPSアプリケーションで広く使われている位置情報のテキストフォーマットで、gnss ライブラリは NMEA フォーマットの出力もサポートしています。
利用するには、CONFIG_MLIB を 'y' に設定する必要があります。cxd56_gnss_sv_s に格納された GNSS デバイスの内容を NMEA に変換します。
詳細については、gnss_nmea ならびに、NMEA Output を参照してください。
11.6.8. Geofence
Key features
Geofence はあらかじめ指定した領域に近づいたときに通知するためのライブラリです。
-
領域は Latitude, Longitude, Radius で指定できます
-
最大 20 領域まで指定できます
通知は "ENTER", "DWELL", "EXIT" の3種類あります。"DWELL" の場合は、その領域に滞在した時間も通知条件に設定することができます。
Configuration
Geofence を使う場合、GNSS device と Geofence Support を 'y' に指定してください。
[System Type] CXD56xx Peripheral Support --> [*] GNSS device [*] Geofence Support
Add Region and setup options
Geofence を使うには "/dev/geofence" デバイスファイルを使います。
CXD56_GEOFENCE_IOCTL_ADD
領域を追加するためのコマンドです。
領域は最大20箇所、設定することができます。それぞれの領域は、Latitude, Longitude, Radius で設定できます。
North latitude と East longitude の方向は '+' を与えます。South latitude と West longitude の方向は '-' になります。
CXD56_GEOFENCE_IOCTL_SET_MODE
Geofence が定義された領域の同心円状の領域(Dead Zone)と、その領域に侵入を許す期間 "dwelling period" を指定します。
State transition
Dead zone
"Dead Zone" とは定義された領域の半径 (Radius) で定義された同心円状の領域を示します。
-
"ENTER" が定義された場合、その同心円領域に入ると通知します
-
"EXIT" が定義された場合、その同心円領域から出ると通知します
Read Geofence transition data
状態変化は、poll によって通知されます。 cxd56_geofence_status_s に状態遷移の情報が格納されています。
詳細については、アプリケーションサンプル Geofencing Transitions を参照してください。
11.6.9. PVTLog
Key features
PVTLog は、Position, Velocity, Time の情報のログです。
最大 170 のログを蓄積することができます。ログがあふれた場合は、アプリケーションはシグナルにより通知を受けることができます。 例えば、15分ごとにログを取得した場合、42時間は継続して記録することができます。
ログデータの詳細については、cxd56_pvtlog_s を参照してください。
Configuration
PVTLog を使う場合は、GNSS device Support を 'y' に設定してください。
[System Type] CXD56xx Peripheral Support --> [*] GNSS device
Start and stop log store
ログを開始するには、ioctl コマンド CXD56_GNSS_IOCTL_PVTLOG_START で行うことができます
記録する間隔についても、このコマンドを使ってください。ログを記録する間隔は測位する間隔よりも大きくする必要があります。
測位を開始するとログは自動的に開始します。ログを停止するには、ioctl コマンド CXD56_GNSS_IOCTL_PVTLOG_STOP を使ってください。
Notification
記録しているログが 170 を超えると、シグナルによりアプリケーションにログが溢れたことが通知されます。 シグナルの設定は、CXD56_GNSS_SIG_PVTLOG で行うことができます。
この通知を受け取ったら、次の記録が行われるまでにログを読み出す必要があります。でなければ、最初のログデータが次の測位データに上書きされることになります。
ログデータは、CXD56_GNSS_READ_OFFSET_PVTLOG でオフセットが指定された位置から読み込むことができます。
ログデータは、バックアップ RAM に記録されます。すでにデータが存在する場合は、消去されることなく上書きされます。 バックアップをクリーンナップする場合は、CXD56_GNSS_IOCTL_PVTLOG_DELETE_LOG を使います。 |
詳細に関しては、サンプルプログラム PVTLog を参照してください。
11.6.10. Application Examples
Notify Positioning
GNSS 測位の通知は、poll とシグナルによって行われます。どちらを使うかは、CONFIG_EXAMPLES_GNSS_USE_SIGNAL もしくは CONFIG_EXAMPLES_GNSS_USE_POLL を Kconfig で選択できます。
NMEA Output
はじめに
gnss_atcmd は Spresense SDK 上で動作する GNSS 機能評価用のサンプルアプリケーションです。 PC 等のホストからシリアルポートを介してコマンドを送信すると、そのシリアルポートに NMEA センテンスの結果が出力されます。具体的なコマンドの仕様とアプリケーションの使用例について後述します。
@コマンド仕様
ホストから送信するコマンドの仕様について説明します。
PC 等のホストから送信されたコマンドを gnss_atcmd アプリケーションが受信し、処理結果をホストに返します。コマンド送信から結果が応答されるまでの期間は、NMEA は出力されません。gnss_atcmd アプリケーションからコマンド完了を示す応答メッセージ (Done または Err ) が返ってくる前に別のコマンドを発行しないようにして下さい。なお、コマンド送信からコマンド応答が返るまでの時間はコマンドの種別およびその時の状態により異なりますが、ワーストケースで約 5 秒かかることがあります。ホストコントローラにてタイムアウトを検出する際は 5 秒にてタイムアウトと判断して下さい。
コマンドフォーマット
コマンドの書式は以下の通りです。"@" (アットマーク)に続いてコマンド文字列や引数を送信し、最後に改行コードを送ります。
@Command [Argument0] [Argument1]...<CR><LF>
- Command
-
4 文字以内の文字列です。詳細は下表 コマンド一覧 を参照してください。
- Argument
-
10 進数で表される数値です。文字列の先頭が "0x" の場合は 16 進数を表します。 コマンドによっては複数の引数をとります。
- <CR><LF>
-
改行コードを表します。コマンド行の終わりには CR (Carriage Return) + LF (Line Feed) を付けてください。
正常応答フォーマット
正常応答結果の書式は以下の通りです。[送信したコマンド文字列]に続いて、"Done"の文字列が返ります。
[Command] Done<CR><LF>
- Command
-
受信したコマンド文字列を表します。
- <CR><LF>
-
改行を表します。応答行の終わりには CR (Carriage Return) + LF (Line Feed) が付加されています。
エラー応答フォーマット
エラー応答結果の書式は以下の通りです。[送信したコマンド文字列]に続いて、"Err"の文字列とエラーコードが返ります。
[Command] Err ErrorCode<CR><LF>
- Command
-
受信したコマンド文字列を表します。
- ErrorCode
-
負値のエラーコードを表します。
- <CR><LF>
-
改行を表します。応答行の終わりには CR (Carriage Return) + LF (Line Feed) が付加されています。
コマンドシーケンス
@GCD のような測位開始のコマンドを発行すると、"Done" の応答を返した後に、定期的に NMEA センテンスが Host へ送られます。
@VER コマンドを発行すると、バージョン情報が送られた後に、"Done" の応答が返ります。
コマンド一覧
gnss_atcmd が処理可能なコマンド一覧を下記に示します。
Command | Argument | Description | ||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@AEXT | - | アプリケーションを終了します。 本コマンドは測位停止中に実行してください。 | ||||||||||||||||||||||||||||||
@BSSL | NMEAマスク | NMEA 0183 (ver 4.00) 規格で定義されているNMEAセンテンスのうち、 出力するNMEAセンテンスをビットマスク値で引数に指定します。 初期状態ではNMEAマスクには0xefが設定されています。
xx は、以下を表します。
コマンド例:
| ||||||||||||||||||||||||||||||
@BUP | - | 受信済みのエフェメリス及び各種パラメータを Flash へセーブします。 セーブされたデータは次回起動時に自動的にリストアされます。 本コマンドは測位停止中に実行して下さい。 | ||||||||||||||||||||||||||||||
@GCD | - | Cold Start による測位を開始し、NMEAセンテンスを周期的に出力します。 | ||||||||||||||||||||||||||||||
@GNS | 衛星マスク | 測位に使用する衛星を引数のビットマスク値で選択します。
NOTE 補正信号は排他的使用が前提です。使用方法についてはSDKドキュメントをご参照ください。 コマンド例:
| ||||||||||||||||||||||||||||||
@GPOE | <緯度[度]> | 楕円体座標の受信機現在位置を設定します。 コマンド例:
| ||||||||||||||||||||||||||||||
@GSR | - | Hot Start による測位を開始し、NMEAセンテンスを周期的に出力します。 | ||||||||||||||||||||||||||||||
@GSTP | - | 測位を停止します。 | ||||||||||||||||||||||||||||||
@GSW | - | Warm Start による測位を開始し、NMEAセンテンスを周期的に出力します。 | ||||||||||||||||||||||||||||||
@GTIM | <年> | UTC時刻を設定します。 コマンド例:
| ||||||||||||||||||||||||||||||
@VER | - | ALLゼロのバージョン番号を返します。 |
アプリケーション動作例
gnss_atcmd アプリケーションの動作実施例を示します。
Configuration
本アプリケーションを実行するためには、以下の SDK Configuration を有効にしてください。
CONFIG_CXD56_GNSS=y
CONFIG_GPSUTILS_CXD56NMEA_LIB=y
CONFIG_EXAMPLES_GNSS_ATCMD=y
下記のコマンド実行することでこれらの Configuration を自動的に有効にすることができます。
$ ./tools/config.py examples/gnss_atcmd
また、コマンド入出力に使用するポートを、コンフィグレーション GNSS Command IO によって切り替えることができます。
│ Prompt: GNSS Command IO │ Location: │ -> Examples │ -> GNSS CXD5603 @command emulator example (EXAMPLES_GNSS_ATCMD [=y])
-
Example uses USB CDC tty : 拡張ボード上の USB ポートを使用 (デフォルト)
-
Example uses STDINOUT for nsh debug UART : メイン基板上の USB ポート (UART1)
-
Example uses UART ttyS0 : メイン基板上の USB ポート (UART1)
-
Example uses UART ttyS1 : 非サポート
-
Example uses UART ttyS2 : メインボード及び拡張ボード上の UART ポート (UART2)
nsh プロンプトから gnss_atcmd アプリケーションを実行した後に、ターミナルから前述したコマンドを入力します。
例: Cold Start による測位の開始と停止
GPS、Glonass、QZSS L1C/A を測位衛星として選び、Cold Start で測位を開始します。 その後 NMEA センテンスが出力され、適当な期間の後、測位を停止して gnss_atcmd を終了します。
nsh> gnss_atcmd (アプリケーション開始)
@GNS 0x0b↵ (測位衛星の選択)
@GCD↵ (Cold Start測位開始)
----- <NMEA出力> ----
@GSTP↵ (測位停止)
@AEXT↵ (アプリケーション終了)
nsh>
例: GNSS 終了後の Hot Start 測位
初めに GPS、Glonass、QZSS L1C/A を測位衛星として選び、Cold Start で測位を開始します。 測位された後に、Spresense の電源は保ちつつ GNSS を終了し、再度 Hot Start で測位を開始します。 Hot Start では測位開始から数秒後に測位が FIX します。
nsh> gnss_atcmd (アプリケーション開始)
@GNS 0x0b↵ (測位衛星の選択)
@GCD↵ (Cold Start測位開始)
----- <NMEA出力> ----
@GSTP↵ (測位停止)
@AEXT↵ (アプリケーション終了)
nsh> gnss_atcmd (アプリケーション開始)
@GSR↵ (Hot Start測位開始)
----- <NMEA出力> ----
@GSTP↵ (測位停止)
@AEXT↵ (アプリケーション終了)
nsh>
例: Spresense 電源 OFF 後の Hot Start 測位
初めに GPS、Glonass、QZSS L1C/A を測位衛星として選び、Cold Start で測位を開始します。 その後、GNSS を終了した後に Spresense の電源を OFF します。
再度 Spresense の電源を入れた後、UTC 時刻と現在位置を入力して Hot Start で測位を開始します。 Hot Start では測位開始から数秒後に測位が FIX します。
nsh> gnss_atcmd (アプリケーション開始)
@GNS 0x0b↵ (測位衛星の選択)
@GCD↵ (Cold Start測位開始)
----- <NMEA出力> ----
@GSTP↵ (測位停止)
@BUP↵ (Flashへバックアップ)
@AEXT↵ (アプリケーション終了)
----- <Power OFF> -----
----- <Power ON> -----
nsh> gnss_atcmd (アプリケーション開始)
@GTIM 2018 11 09 02 45 05↵ (UTC 時刻設定)
@GPOE 35 39 31 139 44 05↵ (現在位置の設定)
@GSR↵ (Hot Start測位開始)
----- <NMEA出力> ----
@GSTP↵ (測位停止)
@AEXT↵ (アプリケーション終了)
nsh>
みちびきQZQSMセンテンスの出力
衛星マスクにQZSS L1/CAを選び、NMEAマスクでQZQSMセンテンスを有効にした後、測位を開始します。 受信条件が良ければ10秒弱で最初のQZQSMセンテンスが出力され、その後4秒毎に出力されます。
nsh> gnss_atcmd
@GNS 0x29↵
@BSSL 0x40ef↵
@GCD↵
$GPGGA,000001.00,,,,,0,00,,,,,,,*49 $GNGLL,,,,,000001.00,V,N*55
…
$GNZDA,000008.00,06,01,1980,,*77 $QZQSM,56,9AADF260540002C3F2587F8B101962082C41A588ACB1181623500011439023C*7B $GPGGA,000009.00,,,,,0,00,,,,,,,*41
…
Geofencing Transitions
Factory Test
GNSS Status on e-ink Display
11.7. ASMP Framework
11.7.1. ASMP Framework の概要
ASMP Frameworkはマルチコアプロセッサを簡単に扱えるように用意されたフレームワークです。
このフレームワークには二つのタスクが定義されています。
-
Supervisor タスク (Main CPU task)
-
Worker タスク (Sub CPU task)
Supervisorタスクは、ASMP Framework API を使用してWorkerタスクの起動、停止などの制御を行います。 詳細は、 MP Task を参照してください。
Workerタスクは、 Main CPUとは完全に独立したタスクで、Sub CPU上で並列処理されるタスクです。したがって、タスク間でデータや状態をやりとりするための通信手段が必要になります。
ASMP Frameworkでは、SupervisorおよびWorkerそれぞれのタスクが通信するための以下の機能を提供しています。
MP message queue は POSIX の message queue に似たメッセージ交換用のインターフェースです。
MP mutex は、pthread mutex に似た排他制御用のメカニズムを提供します。
MP shared memory は、それぞれのタスクで共有されるメモリ領域を提供します。
11.7.2. Configuration
[ASMP] = Y [ASMP shared memory size] = 0x100000
ASMP framework は、 タスク間の共有メモリのために、カーネル管理外のメモリ領域を必要とします。このメモリ領域は、 ASMP shared memory size
オプションで変更できます。もし、 ASMP frameworkを必要としないのであれば、カーネルは 1.5MB のメモリを活用することができます。
ASMP Shared memory size 設定は、128KB単位(0x20000)で設定してください。 |
11.7.3. Workerの起動シーケンス
-
mptask_init を呼び出してWorkerを生成します
-
必要に応じてメッセージキューや共有メモリなどを準備し、Workerに紐付けます
-
mptask_exec を呼び出してWorkerを実行します
-
終了時には mptask_destroy を呼び出してWorkerを停止させます
-
メッセージキューや共有メモリなどを作成していた場合は破棄させます
詳細は、Supervisor Task example および Worker Task example を参照してください。
11.7.4. Workerのビルド
Workerタスクは、ELFフォーマットのファイルをロードして実行します。ただし、WorkerはサブCPUで動作するため、カーネルやSDKのAPIを直接呼び出すことはできません。
ASMP frameworkがサポートする ELFファイルのレイアウトは以下になります。
それぞれのプログラムの開始アドレスは 0x00000000
でなくてはならず、それぞれのセクションは連続である必要があります。 SDKでは、Worker用ELFファイルを生成するために必要なコンパイラオプション、リンカオプションおよびリンカスクリプトを提供しています。 これらはそれぞれ CELFFLAGS
と LDRAWELFFLAGS
として sdk/Make.defs
内で定義されています。具体的なビルドルールは ASMP Worker Task 'Hello World' example を参照してください。
さらに、WorkerでASMP Frameworkを使用するためには、Worker用ライブラリが必要です。このライブラリには、Workerを構成するための必要なコード(上図の青色部)が含まれています。ただし、このライブラリはSDKのビルドシーケンス中に用意されないため、ユーザーがWorkerをビルドする際に事前に生成しておく必要があります。
Worker用ライブラリを生成するためのサンプルは、Worker Task Makefile および Worker Task Library Makefile を参照してください。
11.7.5. サンプルコード
11.8. Sensor Control Unit
11.8.1. Overview
Sensor Control Unit (SCU) は、SCU デバイス内の SPI もしくは I2C バスに接続されたセンサーデバイスからのセンシングデータを取得することができます。CPUとは独立して動作するため、センサー取得に関わるCPUの負荷を軽減することができます。
-
シーケンサーによるセンサーデータの自動取得
-
センサーデータ間引き機能(デシメーター)
-
IIR フィルター機能
-
イベント通知機能
これらの機能は、アプリケーションから設定することができます。
SCU は SPI と 2つの I2C バスと 8つのシーケンサーと2つのデシメーターを搭載しています。
11.8.2. Configuration
CXD56xx Configuration ---> Sensor Control Unit (SCU) ---> Sequencer Sampling Predivider = 64 (default) (1) SCU clock mode = RCOSC (default) (2) SCU32K clock source = RTC (default) (3) SCU Decimator assignments (4) ... (Decimator supported drivers) ... SCU Debug = N (default) DMAC support = Y (default)
1 | Sequencer Sampling Predivider は、シーケンサーのサンプリングレートに影響します |
2 | SCU clock mode はSCUの動作クロックを指定します |
3 | SCU32K clock source は サンプリング周波数(32768Hz)のクロックソースを選択できます |
4 | SCU デシメーターは 3つ以上のセンサーに割り当てられますが、デシメーターに割り当てたセンサーの同時使用は2つまでです。 |
11.8.3. シーケンサー
SCU は2つの種類のシーケンサーがあります。通常のシーケンサーは、センサーデータを定期的に取得し、FIFO に蓄積していきます。デバイスドライバは、このFIFOからデータを取得していきます。
それぞれのシーケンサーの FIFO は、SCUIOC_SETFIFO
で指定されるサイズに分割されます。FIFOメモリーは全部で 40KB です。アプリケーションは、デバイスドライバを使う前にセットする必要があります。
ret = ioctl(fd, SCUIOC_SETFIFO, sizeof(struct three_axis_s) * 128);
シーケンサーは、サンプリングレートで指定される間隔でセンサーデータを取得していきます。センサーデバイスの仕様で決められたサンプリングレートではないことに注意してください。アプリケーションは、シーケンサーに対し、SCUIOC_SETSAMPLE
を使用して、サンプリングレートを使用する前に設定する必要があります。
ret = ioctl(fd, SCUIOC_SETSAMPLE, 2);
SCUIOC_SETSAMPLE
は、2 のべき乗でベースクロック(32KHz)を割った数を指定します。
Sequencer sampling rate = 32768 / CONFIG_CXD56_SCU_PREDIV / (2 ^ n)
CONFIG_CXD56_SCU_PREDIV
がデフォルト値64の場合に、 SCUIOC_SETSAMPLE
で2を指定した場合のサンプリングレートは 128 Hz になります。
アプリケーションは、シーケンサーのサンプリングレートが、センサーの仕様で定められたサンプリングレートと異なることに注意する必要があります。アプリケーションは、センサーデバイスのサンプリングレートとシーケンサーの両方に設定し、その組み合わせがうまく行くことを確認する必要があります。
アプリケーションは SCUIOC_SETWATERMARK
で定められたシグナルを受信することができます。シーケンサーのFIFOに watermark
に定められた数までセンサーデータが格納されると、SCUドライバはアプリケーションに対してシグナルを発行します。
例えば、アプリケーションがサンプリングレートを 128 Hz に設定し、watermark
を 128 に設定したとすると デバイスドライバは1秒ごとにシグナルを発行することになります。
wm.signo = CONFIG_EXAMPLES_MAG_SIGNO;
wm.ts = &ts;
wm.watermark = 128;
ret = ioctl(fd, SCUIOC_SETWATERMARK, (unsigned long)(uintptr_t)&wm);
アプリケーションが、指定した watermark シグナルを受信すると、FIFOの最初のサンプリングデータの取得タイムスタンプがシグナルの siginfo
構造体に格納されます。
11.8.4. デシメーター
デシメーターは、FIFOに取得したデータを間引きする機能でシーケンサーの機能を拡張するために設けられたものです。デシメーターは 3 つの FIFO を持っています。
デシメーション処理
デシメーターは CICフィルターを適用してシーケンサーが取得したデータを間引きします。アプリケーションは、デシメーターのパラメーターを SCUIOC_SETDECIMATION
コマンドを使って、 decimation_s
構造体で指定して変更することができます。
dec.ratio = 1;
dec.leveladj = SCU_LEVELADJ_X1;
dec.forcethrough = 0;
ret = ioctl(d->fd, SCUIOC_SETDECIMATION, (unsigned long)(uintptr_t)&dec);
ratio は、サンプリングレートで指定されたデータを取り出す間隔を2のべき乗数で指定します。例えば、サンプリングレートが 64Hz で、ratio が 1 だった場合、 ( 64 / (2 ^ 1) ), となり実際のサンプリングレートは 32Hz になります。 CIC フィルターはサンプリングされたデータに対して適用されることに注意してください。もし、CIC フィルターを使いたくない場合は、 forcethrough を 1に設定してください。
取得データの増幅
デシメーターは、 decimation_s
構造体の leveladj を使うことで取得データを増幅させることができます。増幅されたデータはそのもとのデータ幅を超過してしまいますので、使用の際は注意が必要です。
leveladj は以下の設定ができます。
-
SCU_LEVELADJ_X1
(x1) -
SCU_LEVELADJ_X2
(x2) -
SCU_LEVELADJ_X4
(x4) -
SCU_LEVELADJ_X8
(x8)
デシメーターサポート済みセンサー
デシメーターを使う場合は、センサードライバが対応している必要があります。現在、サポートされているセンサードライバ は以下になります。
-
KX022
-
BMI1422GMV
11.8.5. センサーデータへの信号処理
SCU は、取得したデータに対して信号処理を加えることができるようになっています。 SCUが行える信号処理は、IIRフィルターとイベント検出の2つがあります。 IIRフィルターはデータパス上の3箇所に設定をすることができます。使用するFIFO毎に設定する必要があります。
IIR フィルター
A | FIFO と イベント検出(Event detector)の両方に適用します |
F | FIFOにのみ適用します |
E | イベント検出(Event detector)にのみ提供します |
アプリケーションは二つのIIRフィルターを設定することができます。FIFOは、シーケンサーFIFOかデシメーターFIFO です。イベント検出は特殊な機能であり、後述します。
-
(A)に2つとも適用
-
(A) と (F)に適用
-
(A) と (E)に適用
-
(F) に2つとも適用
-
(F) と (E)に適用
-
(E) に2つとも適用
IIR フィルターの設定箇所は、 filter_pos_e
に定義されています IIR フィルターは、係数によって調整することができます。
それぞれの係数は 34 bit の固定小数点で、 s2.31 format です。したがって、IIR フィルターの係数は、32 bit (h) と 8bit (l) で構成されます。
31 bit の h は S(signed bit) です。, 0 = plus, 1 = minus.
30 から 29 bit の h は整数部です。
28 から 0 の h と、 7 から 6 bit の l は、小数部になります。
イベント検出 (Event detector)
イベント検出 (Event detector) は、センサーデータの状態を監視し、その変化を検出します。アプリケーションはどのタイミングでセンサーを取得すべきか判断できるようになります。
イベント検出は、閾値で定められたセンサーデータの幅を超過している数をカウントします。イベント検出に使用できるセンサーデータは 16bitになります。取得したデータが、例えば、XYZ軸それぞれのデータだった場合、normalize 計算を行った後、データが設定値から超過しているか判定します。カウントした数が設定値に達したら、イベント検出器はタイムスタンプ付きの割り込みを発生させます。
normalize 計算は、監視するデータの数(1-3)よって異なります。
-
1 つだった場合 (例えば、X軸のみ):
norm = abs(x)
-
2 つだった場合 (例えば、X軸とY軸 ):
norm = max(abs(x), abs(y)) * 123 / 128 + min(abs(x), abs(y)) * 51 / 128
-
3 つだった場合 (例えば、X軸, Y軸, Z軸):
L1 = max(abs(x), abs(y)) * 120 / 128 + min(abs(x), abs(y)) * 49 / 128 norm = max(L1, abs(z)) + min(L1, abs(z)) * 44 / 128
イベント検出の設定には scuev_notify_s
構造体を使用します。 rise と fall の二つの設定値があり、それぞれ threshold, count0 , count1 を持っています。 イベント検出器は カウントしたデータが、 count0 + count1. に達したらアプリケーションに通知を行います。
イベント検出器は IIR フィルターの出力を使うため、IIR フィルターは最初に設定されている必要があります。
-
閾値を超えた値をカウントします
-
カウント数が count0 に達したら、 count1 をスタートします。
-
もし、データ数が count0 に達する前に閾値より小さくなったらカウントをストップしカウント数をリセットします。
-
トータルカウント count0 + count1 に達したらイベントを通知します
-
count1 がゼロだった場合、 count0 の値が閾値に達したら、ただちにイベントを通知します。
-
count0 がゼロだった場合、設定はすべて無視されます
11.8.6. 制限
デシメーター、IIR フィルター、イベント検出器が扱えるデータには、以下の制限があります。
-
1つのサンプリングデータが16 bit
-
最大3つのデータ要素
11.8.7. ドライバ開発者のためのガイドライン
SCU機能をサポートしたドライバを開発するには以下の内容を理解する必要があります
-
シーケンサーインストラクションの理解
-
センサーデバイスとの通信方法
-
シーケンサーの初期化(Open) とセンサードライバとの接続方法
-
シーケンサーからサンプルデータを読み出す方法
-
シーケンサーに対して ioctl を介してコマンドを指示する方法
シーケンサーインストラクションの理解
シーケンサーインストラクションは1命令あたり16bitで、センサーデバイスと通信するために、SPI もしくは I2C バスを使います。
ドライバを開発する際は、 SCU_INST_SEND
もしくは SCU_INST_RECV
マクロを使うことができます。
シーケンサーを通してデータの送受信をする際は、送受信の方法を設定した一連の命令をuinit16_tの配列に格納して設定します。また、送受信の最後には、送信が終了したことを示す SCU_INST_LAST
を指定する必要があります。
inst[0] = SCU_INST_SEND(0x00); // Send register address 0x00
inst[1] = SCU_INST_RECV(2) | SCU_INST_LAST; // Read 0x00 and 0x01 address data, and indicate the last of instructions
センサーデバイスとの通信方法
最初にドライバはターゲットのセンサーデバイスを初期化しなければなりません。SCU機能をサポートするドライバは、scu_spitransfer
もしくは scu_i2ctransfer
を使ってセンサーデバイスのレジスタにアクセスする必要があります。
static void sensor_putreg8(FAR struct sensor_dev_s *priv, uint8_t regaddr, uint8_t regval)
{
uint16_t inst[2];
/* Send register address and set the value */
inst[0] = SCU_INST_SEND(regaddr);
inst[1] = SCU_INST_SEND(regval) | SCU_INST_LAST;
scu_i2ctransfer(priv->port, priv->addr, inst, 2, NULL, 0);
}
SCU に直接つながっている SPI と I2C バスは、SCU とセンサードライバが同時にアクセスが発生すると衝突が発生し、誤作動の原因になります。デバイドライバ開発者は、提供される API を通じてのみ、デバイスにアクセスするようにしてください。 |
シーケンサーの初期化(Open) とセンサードライバとの接続方法
SCU機能をサポートしたドライバはシーケンサーオブジェクトをすべての操作で扱うことになります。それぞれのドライバは、このオブジェクトを保持しておく必要があります。
g_seq = seq_open(MAG_SEQ_TYPE, SCU_BUS_I2C0);
if (!g_seq)
{
return -ENOENT;
}
priv->seq = g_seq;
このサンプルの場合、シーケンサーをOpenし、デバイスデータの中に格納しています。
デシメーターについては、3つのFIFOから読み出すことが可能なので、3つのデバイスファイルを生成しています。
seq_open()
は必ず一度だけ呼び出す必要があります。ドライバ開発者は重複して初期化しないように注意する必要があります。
if (g_refcnt == 0)
{
int ret;
ret = sensor_seqinit(priv);
if (ret < 0)
{
return ret;
}
}
else
{
/* Set existing sequencer */
priv->seq = g_seq;
}
g_refcnt++;
SCU はシーケンサーが処理をしていないときにスリープすることができます。 seq_open()
と seq_close()
APIs は SCU のスリープ機能をサポートしています。 ドライバ開発者は、open()
もしくは close()
を実装するときに、これらの API を呼び出すと同時にセンサーデバイスのパワーモードもコントロールするようにしてください。 それによりシステムは、より省電力を実現することができるようになります。
シーケンサーからサンプルデータを読み出す方法
シーケンサーがセンサーデータを読み出すときは、データを読み出すための一連のシーケンサ命令群を seq_setinstruction
で設定します。
以下の例では、 SENSOR_DATA_REGISTER
から SENSOR_BYTESPERSAMPLE
数分だけ読みだしています。
static const uint16_t g_sensorinst[] =
{
SCU_INST_SEND(SENSOR_DATA_REGISTER),
SCU_INST_RECV(SENSOR_BYTESPERSAMPLE) | SCU_INST_LAST,
};
seq_setinstruction(priv->seq, g_sensorinst, itemsof(g_sensorinst));
シーケンサーは、センサーデータをこの一連のシーケンサ命令群によって定期的に読み出し、FIFO にデータを蓄積します。
蓄積されたセンサーデータをFIFOから読み出す場合は seq_read()
関数を使用してください。
len = seq_read(priv->seq, priv->id, buffer, len);
シーケンサーに対して ioctl を介してコマンドを指示する方法
SCUはデバイスファイルを持たないため、SCUに対して ioctl()
を行うために、センサードライバが代わりに仲介する必要があります。 これを行うためには、seq_ioctl()
を各ドライバの ioctl()
内で呼ぶようにします。SCUのIOコマンドかどうかを判定するためには、 _SCUIOCVALID()
マクロを使用してください。
if (_SCUIOCVALID(cmd))
{
/* Redirect SCU commands */
ret = seq_ioctl(priv->seq, priv->id, cmd, arg);
}
11.8.8. Examples
SCU機能サポートデバイスドライバの初期化サンプル
SCU機能サポートデバイスドライバのサンプル
11.9. Memory Utility Libraries
11.9.1. 概要
メディア処理&センサー処理機能では、データ用に多くのメモリを安全に、フラグメンテーションなどのリークを発生させずに管理する必要があります。このため、Spresenseでは、メモリユーティリティライブラリを提供しています。
ライブラリは、以下の3つのライブラリ、”Memory Manager” 、"Message Library"、"Simple FIFO"で構成されています。
-
”Memory Manager” は、Multi task 間で、メモリ領域を安全に管理するため、 暗黙的にセグメントエリアを管理する固定長メモリプールライブラリです。
-
"Message Library" は、”Memory Manager” を使うために、クラスオブジェクトをタスク間で送受信するためのライブラリです。
-
"Simple FIFO" は、アプリケーションとフレームワークの間でデータをやりとりするために使用される基本的なFIFOです。
これらのライブラリの利用をメディア処理&センサー処理では、前提としています。 詳細は、以下の各章に記載します。
11.9.2. Memory Manager
”Memory Manager” の説明をします。
概要
”Memory Manager" は、暗黙的な取得と解放を管理する固定長メモリプールです。 用途に応じたメモリ領域をメモリプールとして定義し、その中に必要なセグメントを確保することで、必要に応じたメモリの取得、解放が可能です。
プールの種別、セグメントの数などを決めて、メモリ領域を割り当てることで、メモリ領域のフラグメントなどを避けることができます。 この割り当て情報を "memory_layout" と呼び、このレイアウトをアプリケーションのニーズに合わせて切り替えることで、機能ごとに必要なメモリを確保します。
このメモリLayoutは、Applicationのニーズに従って自由に決めて、作成することが可能です。作成方法は、Configurations and Generateに記載します。
必要な "memory segment"の取得は、"MemHandle" を作成し、セグメントの確保のAPIを呼ぶことで可能です。これによって確保されたセグメントは、"MemHandle" のインスタンスが破棄されるまで、保証されます。
複数のユーザーが非同期に "MemHandle"を確保、解放を行っても、その領域は保証され、すべてのユーザから参照されなくなった時点で解放されます。
セグメント獲得、解放の仕組み
各ユーザオブジェクトは、必要なメモリエリアのセグメントを指し示すMemHandleのインスタンスを生成し、セグメントエリアを取得するAPIを呼ぶことで、それに紐付くメモリセグメントを確保します。
その際、セグメントエリアの参照カウンタを+1します。 これによって、セグメントエリアは使用状態になり、他の要求からは使えないように管理されます。
この参照カウンタは、"operator=" や、"Copy Constructor" 内でも+1を行います。
このことで、データパイプラインの次に位置するオブジェクトに、この "MemHandle" のインスタンスをコピーし、渡していくことで、 "MemHandle" に紐付いたセグメントを参照することができるとともに、そのエリアを保証できます。
逆に、不要になった場合は、"MemHandle" のインスタンスを破棄することで、"Destoractor" 内で、参照カウンタが-1されます。
このことで、参照しているインスタンスがすべてなくなった時点で、自動的にリンクされているセグメントは解放されます。
この仕組みにより、必要なオブジェクトが各自メモリが、非同期に必要な間インスタンスを確保し、不要になったら各自のタイミングで破棄したとしても、安全にメモリを確保・解放することができ、意識することなく、すべてのユーザがインスタンスを破棄することで、暗黙の裡に参照が外れます。
Memory Layoutに関しては、Memory Layoutを使うためのヘッダファイル群をあらかじめ用意する必要があります。 これらのヘッダファイルは、Memory Layout定義ファイル(mem_layout.conf)を作成し、専用のツール(mem_layout.rb)を使うことで、生成されます。
APIs
”Memory Manager"のインタフェースは次の通りです。 詳細は、 memutils_memory_manager を参照してください。
Managerクラスの関数
static err_t Manager::initFirst(void* lib_area, uint32_t area_size)
void* lib_area : ライブラリが使用するデータ領域 uint32_t area_size : 領域のサイズ(byte単位)
ERR_OK : 初期化成功 ERR_STS : 2回以上、本関数を実行している ERR_DATA_SIZE : area_sizeが、sizeof(MemMgrLite::Manager)未満 ERR_ADR_ALIGN : lib_areaが、4の倍数ではない
ライブラリ全体の初期化を行う。 フェンス機能が有効な場合は、FixedAreaのフェンスの初期化を行う。 本ライブラリの他のAPIを使用する前に、 //事前に取り決めた単一のCPUで一回のみ 本関数を実行すること。 引数lib_areaは、ライブラリ用のデータ領域のアドレスを指定する。 アドレスが、4の倍数でなければ、ERR_ADR_ALIGNを返す。 指定する領域は、永続寿命を持つ領域であること。また性能上、SRAMなどの 高速なメモリが望ましい。 引数area_sizeは、lib_areaのサイズをbyte単位で指定する。 サイズが、sizeof(MemMgrLite::Manager)未満の場合は、ERR_DATA_SIZEを返す。 現在の実装で必要なサイズは、(4 + 4 * NUM_MEM_POOLS) なので、最小で12bytes 最大で1028bytesとなる。 //オプションの動的生成プールを使用する場合は追加で、(8 + 5 * NUM_DYN_POOLS)を4の倍数に切り上げたサイズが必要となる。 //本ライブラリをマルチコア(共有プール)サポートで使用する場合は、この領域は 全てのCPUからアクセスできる必要がある。 //ARMのTCMやMIPSのScratchPadなどは、CPUローカルなメモリなので、マルチコア サポート時のlib_areaには指定できない。
メモリレイアウト定義ファイルで、MemMgrLite用データ領域を定義して使用する例を示します。
//S_ASSERT(MEMMGR_DATA_AREA_SIZE >= sizeof(MemMgrLite::Manager));
void* lib_data_va = MemMgrLite::translatePoolAddrToVa(MEMMGR_DATA_AREA_ADDR);
if (MemMgrLite::Manager::initFirst(lib_data_va, MEMMGR_DATA_AREA_SIZE)) != ERR_OK)
{
/* エラー処理 */;
}
静的変数で定義したMemMgrLiteのデータ領域を使用する例を示します。
sizeofで領域を確保できるため、サイズ不足の心配がありません。 なお領域は、4byteアラインを保障するため、uint32_tの配列として定義しています。
static uint32_t s_lib_data[sizeof(MemMgrLite::Manager) / sizeof(uint32_t)];
if (MemMgrLite::Manager::initFirst(s_lib_data, sizeof(s_lib_data)) != ERR_OK)
{
/* エラー処理 */;
}
static err_t Manager::initPerCpu(void* lib_area)
void* lib_area /* ライブラリが使用するデータ領域 */
ERR_OK : 初期化成功 ERR_STS : 2回以上、本関数を実行している、あるいはinitFirst()の未実行
ライブラリのCPU毎の初期化を行う。 引数lib_areaは、Manager::initFirst()に指定したものと同じアドレスを指定する。 Manager::initFirst()が未実行か、 既に本APIを実行済みのCPUで、再度本APIを実行した場合は、ERR_STSを返す。 //本ライブラリをマルチコア(共有プール)サポートで使用する場合は、全てのCPUはManager::initFirst()の実行完了を(プラットフォーム固有の方法で)待ってから、本関数を実行する必要がある。
メモリレイアウト定義ファイルで定義したMemMgrLiteのデータ領域を使用する例を示します。
void* lib_data_va = MemMgrLite::translatePoolAddrToVa(MEMMGR_DATA_AREA_ADDR);
if (MemMgrLite::Manager::initPerCpu(lib_data_va)) != ERR_OK)
{
/* エラー処理 */;
}
静的変数で定義したMemMgrLiteのデータ領域を使用する例を示します。
if (MemMgrLite::Manager::initPerCpu(s_lib_data)) != ERR_OK)
{
/* エラー処理 */;
}
static err_t Manager::createStaticPools(NumLayout layout_no, void* work_area, uint32_t area_size, const PoolAttr *pool_attr)
NumLayout layout_no : メモリレイアウト番号 (0 origin) void* work_area : 静的メモリプール操作用の作業領域 uint32_t area_size : 作業領域のサイズ(byte単位) const PoolAttr *pool_attr : メモリレイアウトのアドレス
ERR_OK : 初期化成功 ERR_STS : initPerCpu()が未実行、あるいは本関数が実行済み ERR_ADR_ALIGN : work_areaが、4の倍数ではない ERR_DATA_SIZE : area_sizeが、最大作業領域サイズ未満
指定されたレイアウト番号のメモリプール郡を生成する。 メモリレイアウトを変更する場合には、いったんManager::destroyStaticPools()で メモリプール郡を破棄してから、再度本関数を実行すること。 Manager::initPerCpu()が未実行の場合、 既に本関数が実行済みの場合は、ERR_STSを返す。 引数layout_noは、使用するメモリレイアウトの番号を指定する。 引数work_areaは、静的メモリプール操作用の作業領域のアドレスを指定する。 アドレスが、4の倍数でなければ、ERR_ADR_ALIGNを返す。 指定する領域は、Manager::destroyStaticPools()が呼び出されるまで存在する 領域であること。また性能上、SRAMなどの高速なメモリが望ましい。 引数area_sizeは、静的メモリプール操作用の作業領域のサイズを指定する。 レイアウト番号毎に定義されたマクロMEMMGR_Lx_WORK_SIZE(xはレイアウト番号) 以上の値を指定する。 全レイアウト中の最大作業領域サイズは、マクロMEMMGR_MAX_WORK_SIZEで定義される。 作業領域のサイズが不足した場合は、ERR_DATA_SIZEを返す。 //本ライブラリをマルチコア(共有プール)サポートで使用する場合は、この領域は全てのCPUからアクセスできる必要がある。 //ARMのTCMやMIPSのScratchPadなどは、CPUローカルなメモリなので、マルチコアサポート時には指定できない。
メモリレイアウト定義ファイルで、MemMgrLite用作業領域を定義して使用する例を示します。 マルチコアサポート時やSRAM配置時は、この方法が使いやすいです。
S_ASSERT(MEMMGR_WORK_AREA_SIZE >= MEMMGR_MAX_WORK_SIZE);
const NumLayout layout_no = 0;
void* work_va = MemMgrLite::translatePoolAddrToVa(MEMMGR_WORK_AREA_ADDR);
if (MemMgrLite::Manager::createStaticPools(layout_no, work_va, MEMMGR_WORK_AREA_SIZE) != ERR_OK)
{
/* エラー処理 */;
}
静的変数で定義したMemMgrLiteの作業領域を使用する例を示します。 領域は、4byteアラインを保障するため、uint32_tの配列として定義しています。
static uint32_t s_work_area[MEMMGR_MAX_WORK_SIZE / sizeof(uint32_t)];
const NumLayout layout_no = 0;
if(MemMgrLite::Manager::createStaticPools(layout_no, s_work_area, sizeof(s_work_area) != ERR_OK)
{
/* エラー処理 */;
}
Spresense では、SRAMのみ選択できます。 |
static void Manager::destroyStaticPools()
なし
なし
Manager::createStaticPools()で生成した静的メモリプールを破棄する。 静的メモリプール未生成時は、何もしない。 Manager::initPerCpu()が未実行の場合は、"debug_assert"する。 メモリプールの破棄時には、セグメント解放漏れのチェックを行う。 解放漏れが検出された場合は、"assert"する。 フェンス機能が有効な場合は、静的メモリプールのフェンスチェックを行い フェンス破壊が検出された場合は、"assert"する。
/* 静的メモリプールを破棄する */
MemMgrLite::Manager::destroyStaticPools();
static NumLayout Manager::getCurrentLayoutNo()
なし
現在設定されているメモリレイアウト番号 未設定の場合は、BadLayoutNo
現在のメモリレイアウト番号を取得する。 メモリレイアウト番号の初期値は、BadLayoutNo。 Manager::createStaticPools()を呼ぶと引数で指定した値が設定される。 Manager::destroyStaticPools()を呼ぶと初期値にリセットされる。
/* 現在のメモリレイアウト番号を取得する */
MemMgrLite::NumLayout layout_no = MemMgrLite::Manager::getCurrentLayoutNo();
if (layout_no != MemMgrLite::BadLayoutNo)
{
printf("現在のメモリレイアウト番号: %d\n", layout_no);
}
else
{
printf("メモリレイアウト番号は未設定\n");
}
static uint32_t Manager::getStaticPoolsUsedSegs(MemHandle* mhs, uint32_t num_mhs) //static uint32_t Manager::getUsedSegs(PoolId id, MemHandle* mhs, uint32_t num_mhs)
PoolId id : プールID MemHandle* mhs : 使用中のメモリセグメントを格納するメモリハンドル配列 uint32_t num_mhs : 配列の要素数
メモリハンドル配列に格納したセグメント数
メモリプール内で使用中のセグメントをメモリハンドル配列に格納する。 格納されたセグメントの参照カウントは、1増加する。 使用中のセグメント数が、引数num_mhsで指定した値よりも多い時は、num_mhs個の セグメントを格納した時点で、処理を打ち切る。 getStaticPoolsUsedSegs()は、現在のメモリレイアウトの静的プール全体を対象とする。 //getUsedSegs()は、指定されたIDの静的または動的メモリプールが対象となる。 引数mhsは、メモリセグメントを保持していない空のメモリハンドル配列を指定すること。 引数idが有効なプールIDでなければ、"debug_assert"する。 引数mhsまたはnum_mhsが0の場合は、"debug_assert"する。 これらのAPIは、メモリプールを破棄する前に、解放されていないセグメントの詳細な 情報を取得する用途を想定している。 なお、使用中のセグメント数を知るだけであれば、下記の方が効率が良い。 Manager::getPoolNumSegs(id) - Manager::getPoolNumAvailSegs(id)
const uint32_t MaxSegInfo = 8;
MemHandle mhs[MaxSegInfo];
uint32_t num_used = Manager::getStaticPoolsUsedSegs(mhs, MaxSegInfo);
for (uint32_t i = 0; i < num_used; ++i)
{
printf("ID=%u SegNo=%u VA=%08x Size=%u RefCnt=%u\n",
mhs[i].getPoolId(), mhs[i].getSegNo(), mhs[i].getVa(),
mhs[i].getSize(), mhs[i].getRefCnt());
}
static bool Manager::isPoolAvailable(PoolId id) static PoolType Manager::getPoolType(PoolId id) static PoolAddr Manager::getPoolAddr(PoolId id) static PoolSize Manager::getPoolSize(PoolId id) static NumSeg Manager::getPoolNumSegs(PoolId id) static NumSeg Manager::getPoolNumAvailSegs(PoolId id) static bool Manager::isPoolFenceEnable(PoolId id) //static LockId Manager::getPoolLockId(PoolId id)
PoolId id /* メモリプールID (1 origin) */
説明を参照
isPoolAvailable()は、現在のメモリレイアウトで、指定されたプールIDが有効か否かを返す。 getPoolType(), getPoolAddr(), getPoolSize(), getPoolNumSegs()は、プール 生成時に指定された、プール種別、アドレス、サイズ、セグメント数を返す。 getPoolNumAvailSegs()は、現在の空きセグメント数を返す。 isPoolFenceEnable()は、プール生成時に指定されたフェンス指定を返す。 本関数は、フェンス機能有効時(UseFence = true)のみ定義される。 //getPoolSpinLockId()は、プール生成時に指定されたスピンロックIDを返す。 //本関数は、マルチコアサポート有効時(UseMultiCore = true)のみ定義される。 無効なプールIDを指定するとNULLポインタアクセスを引き起こすので、プールIDの 有効性を確認して使用すること。
if (MemMgrLite::Manager::isPoolAvailable(my_pool_id))
{
printf("type=%d addr=%08x size=%08x num_seg=%u avail_seg=%u",
MemMgrLite::Manager::getPoolType(my_pool_id),
MemMgrLite::Manager::getPoolAddr(my_pool_id),
MemMgrLite::Manager::getPoolSize(my_pool_id),
MemMgrLite::Manager::getPoolNumSegs(my_pool_id),
MemMgrLite::Manager::getPoolNumAvailSegs(my_pool_id));
#ifdef USE_MEMMGR_FENCE
printf(" fence=%u", MemMgrLite::Manager::isPoolFenceEnable(my_pool_id));
#endif
//#ifdef USE_MEMMGR_MULTI_CORE
// printf(" spl_id=%u", MemMgrLite::Manager::getPoolSpinLockId(my_pool_id));
//#endif
}
MemHandleクラスの関数
MemHandle::MemHandle() // default constructor MemHandle::MemHandle(PoolId id, size_t size) // segment allocate constructor MemHandle::MemHandle(const MemHandle& mh) // copy constructor MemHandle::~MemHandle() // destructor
PoolId id : プールID size_t size : 要求サイズ(byte単位) const MemHandle& mh : コピー元メモリハンドル
なし
MemHandleクラスのインスタンスを生成または破棄する。 引数idは、メモリセグメントを取得するプールIDを指定する。 取得結果は、isAvail() or isNull()で判定する。 引数sizeは、要求サイズを指定する。 現状この引数は、セグメントサイズとの比較にのみ使用され、セグメントサイズを 超える値が指定された場合は、"debug_assert"する。 引数mhは、コピー元のメモリハンドルを指定する。 コピー元のメモリハンドルが、メモリセグメントを保持している場合は メモリセグメントの参照カウントが、1増加する。 メモリセグメントを保持しているインスタンスのデストラクタは、メモリセグメントの 参照カウントを、1減少させる。
MemMgrLite::MemHandle mh(MY_POOL_ID, sizeof(MyData));
if (mh.isNull())
{
/* エラー処理 */;
}
MemHandle::MemHandle& operator=(const MemHandle& mh)
const MemHandle& mh : コピー元メモリハンドル
自身への参照
自身とコピー元の値が異なる場合にインスタンスの代入を行う。 同じ場合は何もしない。 メモリセグメントを保持している場合は、代入前にメモリセグメントの 参照カウントを、1減少させる。 コピー元のメモリハンドルが、メモリセグメントを保持している場合は 代入後にメモリセグメントの参照カウントを、1増加させる。
MemMgrLite::MemHandle mh; /* default constructor */
mh = src_mh; /* call operator=() */
err_t MemHandle::allocSeg(PoolId id, size_t size, MemHandleProxy &proxy)
PoolId id : プールID size_t size : 要求サイズ(byte単位) MemHandleProxy &proxy : メモリセグメントの参照
ERR_OK : 取得成功 ERR_DATA_SIZE : sizeがセグメントサイズを超えている ERR_MEM_EMPTY : 取得できるセグメントが無い
指定されたメモリプールからメモリセグメントを取得する。 引数idは、メモリセグメントを取得するプールIDを指定する。 引数sizeは、要求サイズを指定する。 現状この引数は、セグメントサイズとの比較にのみ使用され、セグメントサイズを 超える値が指定された場合は、ERR_DATA_SIZEを返す。 取得できるセグメントが無い場合は、ERR_MEM_EMPTYを返す。
MemMgrLite::MemHandle mh;
if (mh.allocSeg(MY_POOL_ID, sizeof(MyData)) != ERR_OK)
{
/* エラー処理 */;
}
void MemHandle::freeSeg()
なし
なし
デストラクタに頼らずに明示的にメモリセグメントを解放する。 メモリセグメントを保持していない場合は、何もしない。 メモリセグメントを保持している場合は、メモリセグメントの参照カウントを 1減少させてから、インスタンスを再初期化する。
bool MemHandle::isAvail() bool MemHandle::isNull() bool MemHandle::isSame(const MemHandle& mh) PoolId MemHandle::getPoolId() NumSeg MemHandle::getSegNo() PoolAddr MemHandle::getAddr() PoolSize MemHandle::getSize() SegRefCnt MemHandle::getRefCnt()
const MemHandle& mh : 比較先メモリハンドル
説明参照
isAvail()は、インスタンスがメモリセグメントを保持しているか否かを返す。 isNull()は、インスタンスがメモリセグメントを未保持か否かを返す。 isSame()は、インスタンスと引数mhが同じ値か否かを返す。 getPoolId()は、インスタンスがメモリセグメントを保持していれば セグメントが所属しているプールのIDを返す。 セグメントを保持していなければ、NullPoolIdを返す。 getSegNo()は、インスタンスがメモリセグメントを保持していれば セグメントが所属しているプール内でのセグメント番号(1 origin)を返す。 セグメントを保持していなければ、NullSegNoを返す。 getSegAddr()は、インスタンスがメモリセグメントを保持していれば セグメントのアドレスを返す。 セグメントを保持していなければ、"debug_assert"する。 getSegSize()は、インスタンスがメモリセグメントを保持していれば セグメントのサイズを返す。 セグメントを保持していなければ、"debug_assert"する。 getRefCnt()は、インスタンスがメモリセグメントを保持していれば セグメントの参照カウントを返す。 セグメントを保持していなければ、"debug_assert"する。
Configurations and Generate
”Memory Manager” のLayoutファイルの記述方法と作成方法を説明します。
Layout情報は、MemoryLayout定義ファイル"mem_layout.conf" (名前は変えられます。) の中に、Rubyで記載され、 "mem_layout.rb" というツールで、C++言語の3つヘッダ、 "mem_layout.h" "fixed_fence.h" "pool_layout.h" を生成します。 ユーザは、このヘッダをインクルードすることで、”Memory Manager” を使えることになります。
How to write a Mamory Layout File
"mem_layout.conf"は、”Memory Manager”のコンフィグレーション、ユーザー定数の定義、デバイスの定義、固定領域の定義、PoolLayoutの定義を行います。それぞれの説明を以下に示します。
”Memory Manager” の各種機能の使用有無を指定できます。指定できる機能と指定方法は下記の通りです。 .
Feature | Description |
---|---|
UseFence | フェンスの使用有無をtrue/falseで指定します。 有効にするとUSE_MEMMGR_FENCEマクロが定義され、指定された領域の前後に4byteの 上書き検出用フェンスを配置します。またフェンスチェック用APIが定義されます。 |
UseFence = true # Use of a pool fence
MemoryLayout定義ファイル内で使用する定数に、"U_"で開始されるユーザー独自の名称をつける ことができます。また、定義には、Rubyの任意の式を記述することが出来ます。
# スクリプト内定義と重ならないように、"U_"で開始して英大文字、数字と"_"のみとする # "U_MEM_"で始まる名称で定義すると、ヘッダファイルに同名のマクロが出力される U_STD_ALIGN = 8 # standard alignment U_MSGQ_ALIGN = 64 # message queue area alignment
各種Memoryデバイスを定義します。これらの定義は、固定領域の定義に使用されます。 ヘッダファイルには、name_ADDRマクロとname_SIZEマクロとして出力されます。
MemoryDevices.init( # name ram addr size ["AUD_SRAM", true, 0x000c0000, 0x00040000], nil # end of definition )
各パラメータの説明は以下の通りです。
パラメータ | 説明 |
---|---|
name | デバイス名(3文字以上。英大文字で始まり、英大文字, 数字, "_"が使用可能) |
ram | デバイスがRAMならば、true。それ以外ならばfalse。 |
addr | 領域のアドレス。0を除く4の倍数の値を指定します。 |
size | 領域のサイズ。0を除く4の倍数の値を指定します。 |
各メモリデバイスに領域を定義します。 各領域の開始アドレスは、デバイス毎の累積サイズ、align、fenceにより決定されます。
ヘッダファイルには、name_ALIGN, name_ADDR, name_SIZEマクロとして出力されます。
FixedAreas.init( # name, device, align, size, fence ["AUDIO_WORK_AREA", "AUD_SRAM", U_STD_ALIGN, 0x0003e000, false], # Audio work area ["MSG_QUE_AREA", "AUD_SRAM", U_STD_ALIGN, 0x00001000, false], # message queue area ["MEMMGR_WORK_AREA", "AUD_SRAM", U_STD_ALIGN, 0x00000200, false], # MemMgrLite WORK Area ["MEMMGR_DATA_AREA", "AUD_SRAM", U_STD_ALIGN, 0x00000100, false], # MemMgrLite DATA Area nil # end of definition )
各パラメータの説明は以下の通りです。
パラメータ | 説明 |
---|---|
name | 領域名(英大文字で始まり、"_AREA"で終わる名称。英大文字, 数字, _が使用可能) |
device | 領域を確保するMemoryDevicesのデバイス名 |
align | 領域の開始アライメント。0を除くMinAlign(=4)の倍数を指定します。 |
size | 領域のサイズ。0を除く4の倍数の値を指定します。 |
fence | フェンスの有効・無効を指定します。(この項目は、UseFenceがfalseの場合は無視されます) |
メモリプールのレイアウトを定義します。 各プール領域の開始アドレスは、固定領域毎の累積サイズ、align、fenceにより決定されます。
ヘッダファイルには、プールIDとNUM_MEM_POOLS, NUM_MEM_LAYOUTSおよび Lx_name_ALIGN, Lx_name_ADDR, Lx_name_SIZE, Lx_name_NUM_SEG, Lx_name_SEG_SIZE マクロとして出力されます。(xはレイアウト番号)
フェンスが有効な場合は、Lx_name_L_FENCE, Lx_name_U_FENCEマクロも出力されます。
# Definition for player U_MAIN_BUF_SIZE = 1024 U_MAIN_BUF_SEG_NUM = 4 U_MAIN_BUF_POOL_SIZE = U_MAIN_BUF_SIZE * U_MAIN_BUF_SEG_NUM U_PCM_BUF_SIZE = 1024 U_PCM_BUF_SEG_NUM = 8 U_PCM_BUF_POOL_SIZE = U_PCM_BUF_SIZE * U_PCM_BUF_SEG_NUM # Definition for recorder U_REC_BUF_SIZE = 2048 U_REC_BUF_SEG_NUM = 6 U_REC_BUF_POOL_SIZE = U_REC_BUF_SIZE * U_REC_BUF_SEG_NUM PoolAreas.init( [ # layout 0 for Player #[ name, area, align, pool-size, seg, fence] ["MAIN_BUF_POOL", "AUDIO_WORK_AREA", U_STD_ALIGN, U_MAIN_BUF_POOL_SIZE, U_MAIN_BUF_SEG_NUM, true ], ["PCM_BUF_POOL", "AUDIO_WORK_AREA", U_STD_ALIGN, U_PCM_BUF_POOL_SIZE, U_PCM_BUF_SEG_NUM, true ], nil # end of each layout ], # end of layout 0 [ # layout 1 for Recorder #[ name, area, align, pool-size, seg, fence] ["REC_BUF_POOL", "AUDIO_WORK_AREA", U_STD_ALIGN, U_REC_BUF_POOL_SIZE, U_REC_BUF_SEG_NUM, true ], nil # end of each layout ], # end of layout 1 nil # end of definition )
各パラメータの説明は以下の通りです。
パラメータ | 説明 |
---|---|
name | プール名(英大文字で始まり、"_POOL"で終わる名称。英大文字, 数字, _が使用可能) |
area | プール領域として使用するFixedAreaの領域名。領域はRAMに配置して下さい。 |
align | プールの開始アライメント。0を除くMinAlign(=4)の倍数を指定します。 |
pool-size | プールのサイズ。0を除く4の倍数の値。セグメントサイズ * セグメント数。各areaの最終領域には、残りサイズを示すRemainderSizeを指定することができます。 |
seg | セグメント数。1以上、255以下の値を指定します。 |
fence | フェンスの有効・無効を指定します。この項目は、UseFenceがfalseの場合は無視されます。 |
How To Generate
"mem_layout.rb" を使ったLayoutファイルの作成方法は以下の通りです。
ruby -Itool_path mem_layout.conf layout_header fence_header pool_header Ex) ruby -I../../../sdk/modules/memutils/memory_manager/tool mem_layout.conf mem_layout.h fixed_fence.h pool_layout.h
"mem_layout.rb"の各引数の説明は下記の通りです。
Parameter | Description |
---|---|
-Itool_path | mem_layout.rbのパス |
mem_layout.conf | Memory Layout定義ファイル |
layout_header | 各種定数値がマクロとして出力されるヘッダファイル |
fence_header | FixedAreaのメモリフェンスアドレスが出力されるヘッダファイル。”Memory Manager”が使用するファイルのため、ユーザーは使用しないで下さい。 |
pool_header | PoolAreaの各種定義が出力されるヘッダファイル。”Memory Manager”が使用するファイルなので、ユーザーは使用しないで下さい。 |
11.9.3. Message Library
主に、"Memory Manager" のような Task間でClass Instanceの送受信を行う必要がある場合、通常、OSが提供しているシステムコールでは対応できなため、"Message Library" を用いて、これを実現しています。 ここでは、この "Message Library" の説明を行います。
General
"Message Library" は、タスク間でClass Instanceの送受信を行えるタスク間同期ライブラリです。
このライブラリは、送り先をIDとして明示的に指定して送信します。 また、受信側は、送信イベントを待ち受けて、イベントドリブン動作を実現します。
また、このメッセージにはTypeを持っており、受け取ったインスタンスが、あったかなかったか、あった場合何であったかは、Typeによって判断し取り出します。
ただし、送受信したいクラスは、正しくコピーコンストラクタを実装している必要があります。 |
Message ID と Type
Message ID
Message ID は、以下 How to write a Message Layout File の Configurationに従って、静的に作成します。 Configuration の詳細はそちらを参照してください。
IDを確定したら、タスクのループの中で、
MsgQueId id = XX; // Assign the created ID to a variable "id".
MsgQueBlock *que;
MsgPacket *msg;
err_t err = MsgLib::referMsgQueBlock(id, &que);
err = que->recv(TIME_FOREVER, &msg);
とすることで、Massageイベント待ちになり、Message ID = XXのイベントドリブン処理が実現できます。
逆に、イベントを送信する場合は、
MsgQueId send_id = XX; // Assign ID that be sent to a variable "send_id".
MsgQueId ret_id = XX; // Assign ID that will return to a variable "self_id".
Object instance; // Class and Instance you want to send.
instance(); // Construction.
err_t err = MsgLib::send<Object>(send_id, MsgPriNormal, MSG_TYPE, ret_id, instance);
このように、送信したいIDを send_id、返信してほしいIDを ret_idに代入し、送信したいクラス(Object)のインスタンスを作成し、MsgLib::send で送信します。
ID は、アプリケーションに応じて作成し、明示的に送受信のデータパスを指定しながら使用します。
Message Type
Message ライブラリは、受け取った Message Packet から適切なInstance を取り出すために、 Message Type を用いてどういったオブジェクトが送られたかを判断します。 このため、 Message Packet には、すべて Message Type を付加します。
この Message Type は、"sdk/modules/include/memutils/message/message_type.h"で定義されています。
Message Type は、以下のようなデータ構造を取ります。
各フィールドの説明は以下の通りです。
フィールド名 | フィールド | 説明 |
---|---|---|
REQ | [15] | メッセージが要求か応答かを示します。0は応答、1は要求です。 |
MSG_USER | [14-12] | メッセージの使用者情報です。0~7の値でメッセージを使用するシステムを指定することが出来ます。ただし、6は、AudioSubSystem, 7はSensorSubSystemが予約しているため、実際には0~5の値を使用することが出来ます。 |
MSG_CATEGORY | [11-8] | メッセージのカテゴリ情報です。0~15の値でカテゴリを指定することが出来ます。 |
MSG_SUB_TYPE | [7-0] | メッセージのサブタイプ情報です。0~255の値でカテゴリ内のメッセージタイプを指定することが出来ます。 |
定義されているIDは、以下になります。
メッセージの方向を示します。
D15 | Description |
---|---|
0 | response |
1 | request |
メッセージを使用するシステムを指定します。 現在、AudioとSensorのIDは予約されています。
D14-D12 | Description |
---|---|
0-5 | reserved |
6 | Audio Sub System |
7 | Sensor Sub System |
各システム内で自由に定義してください。
例えば、Audioの場合は、
"sdk/modules/include/audio/audio_message_types.h"
Sensorの場合は、
"sdk/modules/include/sensing/sensor_message_types.h"
で定義されています。
各システム内で自由に定義してください。
例えば、Audioの場合は、sdk/modules/audio/include/commmon/audio_interanl_message_types.h
Sensorの場合は、sdk/modules/include/sensing/sensor_message_types.h
で定義されています。
メッセージタイプは、IDが違う場合、同じ値を定義したとしても運用可能ですが、デバッグしやすさと、データパスが変わって、別なIDに送信するような変更などに対しての変更容易性などを鑑みて、すべてをユニークに作るようにしてください。 |
以下のような実装で、Message Packet からInstanceを取り出します。
MsgQueBlock *que;
MsgPacket *msg;
err_t err = MsgLib::referMsgQueBlock(id, &que);
err = que->recv(TIME_FOREVER, &msg);
if (msg->getType() == MSG_TYPE) { // Check that the message type is as expected or not.
Object instance = msg->moveParam<Object>(); // get an instance of type Object from Message packet.
}
APIs
"Message Library"のインタフェースは次の通りです。 詳細は、 memutils_message を参照してください。
MsgLibクラスの関数
static err_t MsgLib::initFirst()
なし
ERR_OK : 初期化成功 ERR_STS : 本関数が実行済み
メッセージライブラリ全体の初期化を行う。 本ライブラリの他のAPIを使用する前に、事前に取り決めた単一のCPUで一回だけ本関数を実行すること。
static err_t MsgLib::initPerCpu()
なし
ERR_OK : 初期化成功 ERR_STS : MsgLib::initFirst()が未実行
メッセージライブラリのCPU毎の初期化を行う。 CPU毎の各種領域や計数セマフォ(OS環境のみ)の初期化など。 本ライブラリを使用する全てのCPUは、MsgLib::initFirst()の実行完了を 待ってから、本関数を実行する必要がある。 MsgLib::initFirst()が未実行の場合は、ASSERTする。 既に本APIを実行済みのCPUで、再度本APIを実行した場合は、ASSERTする。
static err_t MsgLib::finalize()
なし
ERR_OK : 初期化成功 ERR_STS : MsgLib::initFirst()が未実行
メッセージライブラリの終了処理を行う。 内部管理情報をクリアする。
static bool MsgLib::isInitComplete(MsgQueId id)
MsgQueId id : メッセージキューID
true : 初期化済み(キューを所有しているCPUで、初期化が完了している) false : 未初期化
メッセージキューの初期化状態を取得する。 引数idの示すメッセージキューブロックが存在しない場合は、ASSERTする。 MsgLib::initFirst()が未実行の場合は、ASSERTする。
static err_t MsgLib::send(MsgQueId dest, MsgPri pri, MsgType type, MsgQueId reply) template<typename T> static err_t MsgLib::send(MsgQueId dest, MsgPri pri, MsgType type, MsgQueId reply, const T& param) static err_t send(MsgQueId dest, MsgPri pri, MsgType type, MsgQueId reply, const void* param, size_t param_size) static err_t MsgLib::sendIsr(MsgQueId dest, MsgPri pri, MsgType type, MsgQueId reply) template<typename T> static err_t MsgLib::sendIsr(MsgQueId dest, MsgPri pri, MsgType type, MsgQueId reply, const T& param) static err_t sendIsr(MsgQueId dest, MsgPri pri, MsgType type, MsgQueId reply, const void* param, size_t param_size)
MsgQueId dest : 送信先メッセージキューID MsgPri pri : メッセージの優先度 MsgType type : メッセージ種別を識別するための値 MsgQueId reply : 返信先メッセージキューID const T& param : メッセージのパラメタ const void* param : メッセージパラメタへのアドレス size_t param_size : パラメータサイズ
ERR_OK : 送信成功 ERR_QUE_FULL : メッセージキューに空きがない
引数destとpriで示されるキューに、メッセージパケットを格納する。 send()はタスクコンテキスト、sendIsr()は非タスクコンテキストで使用する。 sendIsr()でパラメタを使用する場合、割込み処理の最短化のため、できるだけ クラスインスタンスは使用せず、パラメタサイズも最小化する事。 引数paramのサイズが大きすぎてキューに格納できない場合は、ASSERTする。 引数destとpriで示されるキューが存在しないか、あるいは初期化が完了して いない場合は、ASSERTする。 他CPU所有で、自CPUとのスピンロック共有がないキューを指定した場合は、ASSERTする。 sendIsr()で、共有キューを指定した場合は、ASSERTする。 これは、非タスクコンテキストでのスピンロック待ちを防止するためである。
static void MsgLib::notifyRecv(MsgQueId dest)
MsgQueId dest : 通知先メッセージキューID
なし
他CPUからのメッセージの受信を通知する。 本関数は、CPU間通信割込みハンドラから呼び出されることを想定している。 引数destの示すメッセージキューブロックが存在しないか、あるいは初期化が完了していない場合は、ASSERTする。
MsgQueBlockクラスの関数
static MsgQueBlock& MsgLib::referMsgQueBlock(MsgQueId id)
MsgQueId id : メッセージキューID
メッセージキューブロック参照
メッセージキューブロックへの参照を取得する。 引数idの示すメッセージキューブロックが存在しないか、あるいは初期化が完了していない場合は、ASSERTする。
MsgPacket* MsgQueBlock::recv(uint32_t ms)
uint32_t ms : 受信待ち時間(ミリ秒)または、TIME_POLLING or TIME_FOREVER
NULL以外: メッセージキュー内のメッセージパケットへのポインタ NULL : 受信タイムアウト
指定された時間、メッセージパケットの受信待ちを行う。 MsgLib::send()とは異なり、複数タスクによる同一キューへの同時recv()はサポートしない。 os_wrap.hで、TIME_POLLINGは0、TIME_FOREVERは-1に定義されている。 前回受信したメッセージパケットが、MsgQueBlock::pop()により破棄されていない場合は、ASSERTする。 メッセージキューが他CPU所有の場合は、ASSERTする。
void MsgQueBlock::pop()
なし
なし
メッセージキューからメッセージパケットを取り除く。 メッセージパケットにパラメタが存在する場合は、MsgPacket::moveParam()か MsgPacket::popParam()により、事前にパラメタを破棄すること。 複数タスクによる同一キューの同時pop()はサポートしない。 破棄対象メッセージパケットが存在しない場合は、ASSERTする。 破棄対象メッセージパケットのパラメタ長が、0以外の場合は、ASSERTする。 メッセージキューが他CPU所有の場合は、ASSERTする。
uint16_t MsgQueBlock::getNumMsg(MsgPri pri) const
MsgPri pri : メッセージキュー優先度
メッセージパケット数
メッセージキュー内のメッセージパケット数を取得する。 引数priの値が不正な場合は、ASSERTする
uint16_t MsgQueBlock::getRest(MsgPri pri) const
MsgPri pri : メッセージキュー優先度
格納可能なメッセージパケット数
メッセージキューに格納可能なメッセージパケット数を取得する。 未使用のキューは、常に0を返す。 引数priの値が不正な場合は、ASSERTする。
MsgPacketクラスの関数
MsgType MsgPacket::getType() const
なし
メッセージタイプID
メッセージパケットのタイプIDを取得する。
MsgQueId MsgPacket::getReply() const
なし
リプライキューID
メッセージパケットのリプライキューIDを取得する。
uint16_t MsgPacket::getParamSize() const
なし
メッセージパラメタ長。パラメタなし時は0が返される。
メッセージパケットのパラメタ長を取得する。
template<typename T> T MsgPacket::moveParam()
なし
メッセージパラメタ
メッセージパケットのパラメタを取り出す(移動)。 sizeof(T) != MsgPacket::getParamSize() の場合、ASSERTする。 本APIは、以下の処理と等価となる。 T param = MsgPacket::peekParam<T>(); /* 左辺を非参照にして、パラメタのコピーを作成 */ MsgPacket::popParam<T>(); /* パラメタを破棄 */ return param; /* パラメタのコピーを返す */ 本APIの呼出しにより、メッセージパラメタ長が0に変更される。 メッセージパラメタへの参照やポインタは無効となるので、注意すること。
template<typename T> const T& MsgPacket::peekParam() const template<typename T> const T& MsgPacket::peekParamOther() const
なし
メッセージパラメタのconst参照
メッセージパケットのパラメタへの参照を取得する。 peekParam()は、送信時と同じ型、peekParamOther()は、送信時と異なる型 (例えばパラメタのヘッダの型)での参照に使用する。 peekParam()は sizeof(T) != MsgPacket::getParamSize() の場合、ASSERTする。 peekParamOther()は、 sizeof(T) > MsgPacket::getParamSize() の場合、ASSERTする。 これらのAPIの戻り値を非参照で受け取るとパラメタのコピーが取得できる。
template<typename T> void MsgPacket::popParam() void MsgPacket::popParamNoDestruct()
なし
なし
メッセージパケットのパラメタを破棄する。 popParam()は、パラメタのデストラクタを呼出す。 popParamNoDestruct()はデストラクタを呼出さないので、パラメタにデストラクタが 存在しないメッセージパケットにのみ使用することができる。 特別な理由がない限り、popParam()を使用すること。 popParam()は、 sizeof(T) != MsgPacket::getParamSize() の場合、ASSERTする。 これらのAPIの呼出しにより、メッセージパラメタ長が0に変更される。 メッセージパラメタへの参照やポインタは無効となるので、注意すること。
Configurations and Generate
”Message Library” のLayoutファイルの記述方法と作成方法を説明します。
Layout情報は、"msgq_layout.conf" (名前は変えられます。) の中に、Rubyで記載され、 "msgq_layout.rb" というツールで、C++言語の2つヘッダ、 "msgq_id.h" "msgq_pool.h" を生成します。 ユーザは、このヘッダをインクルードすることで、”Message Library” を使えることになります。
How to write a Message Layout File
"msgq_layout.conf"は、”Message Library”のユーザー定数の定義、メッセージキュープールの定義、デバッグ値の指定、メッセージパラメータのチェック指定を行います。それぞれの説明を以下に示します。
Message Layout定義ファイル内で使用する定数に、"U_"で開始されるユーザー独自の名称をつける ことができます。また、定義には、Rubyの任意の式を記述することが出来ます。
# ユーザー定義定数は、"U_"で始まる英大文字・数字の名称とすること # "U_MSGQ_"で始まる名称で定義すると、msgq_id.hにdefineマクロとしても出力される U_HEADER_SIZE = 8 # Message packet header size
ユーザー定義定数を使って、メッセージキュープールを定義することが出来ます。
U_MSG_SIZE = 16 U_MSG_NUM = 8 MsgQuePool = [ # ID, n_size n_num h_size h_nums ["MSGQ_USER_APP", U_MSG_SIZE, U_MSG_NUM, 0, 0], ["MSGQ_DSP_CMD", 256, 10, U_MSG_SIZE, U_MSG_NUM], nil # end of user definition ] # end of MsgQuePool
各パラメータの説明は以下の通りです。
パラメータ | 説明 |
---|---|
ID | メッセージキュープールIDの名称を、"MSGQ_"で始まる文字列で指定します。"MSGQ_NULL", "MSGQ_TOP", "MSGQ_END"は予約済みのため使用禁止です。 |
n_size | 通常優先度キューの各要素のバイト数(8以上512以下)。固定ヘッダ長(8byte) + パラメタ長を4の倍数で指定します。 |
n_num | 通常優先度キューの要素数(1以上16384以下)。 |
h_size | 高優先度キューの各要素のバイト数(0または、8以上512以下)。未使用時は0を指定して下さい。 |
h_num | 高優先度キューの要素数(0または、1以上16384以下)。未使用時は0を指定して下さい。 |
デバッグ値の指定やメッセージパラメータのチェック指定もありますが、使用しない設定としてください。 MsgFillValueAfterPop = 0x00 MsgParamTypeMatchCheck = false |
How To Generate
"msgq_layout.rb" を使ったLayoutファイルの作成方法は以下の通りです。
ruby -Itool_path msgq_layout.conf start_addr size id_header pool_header Ex) ruby -I../../../sdk/modules/memutils/message/tool msgq_layout.conf 0x000fe000 0x1000 msgq_id.h msgq_pool.h
"msgq_layout.rb"の各引数の説明は下記の通りです。
Parameter | Description |
---|---|
-Itool_path | msgq_layout.rbのパス |
msgq_layout.conf | メッセージキューレイアウト定義ファイル |
start_addr | メッセージ領域のアドレス |
size | バイト単位の領域サイズ |
id_header | メッセージキューIDマクロが出力されるファイル |
pool_header | メッセージキュープールの定義が出力されるファイル |
Sequence
11.9.4. Simple FIFO
General
Simple FIFO This library supports one-writer and one-reader access without any exclusive control. Some exclusive access control is required to support multi-writer/reader outside of this library.
This library supports access from multi-processor inserting proper data-sync-barriers and data-memory-barriers. It makes sure the order of update data contents and WP/RP for the purpose.
APIs
詳細は、Doxygenを参照してください。 APIの一覧は下記の通りです。
API name | Description |
---|---|
CMN_SimpleFifoInitialize | SimpleFIFOの初期化。FIFOに使用するメモリ領域を設定します。 |
CMN_SimpleFifoOffer | FIFOにデータをpushします。コピー処理はmemcpy()を使用します。 |
CMN_SimpleFifoOfferWithSpecificCopier | FIFOにデータをpushします。コピー処理はユーザー側で用意します。DMA用いたコピーなどを想定しています。 |
CMN_SimpleFifoOfferContinuous | FIFOにデータをpushします。指定したサイズ分連続領域にコピーします。指定したサイズ分の連続領域が無い場合は、pushを行いません。コピー処理はmemcpy()を使用します。 |
CMN_SimpleFifoOfferContinuousWithSpecificCopier | FIFOにデータをpushします。指定したサイズ分連続領域にコピーします。指定したサイズ分の連続領域が無い場合は、pushを行いません。コピー処理はユーザー側で用意します。DMA用いたコピーなどを想定しています。 |
CMN_SimpleFifoPoll | FIFOからデータをpopします。popしたデータはFIFOから削除されます。コピー処理はmemcpy()を使用します。 |
CMN_SimpleFifoPollWithSpecificCopier | FIFOからデータをpopします。popしたデータはFIFOから削除されます。コピー処理はユーザー側で用意します。DMA用いたコピーなどを想定しています。 |
CMN_SimpleFifoPeekWithOffset | FIFOの先頭から指定したOffsetから、指定したサイズのデータのアドレス情報を取得します。FIFOのデータを削除せずに参照したい場合に使用します。 |
CMN_SimpleFifoPeek | FIFOの先頭から指定したサイズのデータのアドレス情報を取得します。FIFOのデータを削除せずに参照したい場合に使用します。 |
CMN_SimpleFifoClear | FIFOのRead/Writeポインタをクリアし、空の状態にします。 |
CMN_SimpleFifoGetVacantSize | FIFOの空きサイズを取得します。 |
CMN_SimpleFifoGetOccupiedSize | FIFOの使用サイズを取得します。 |
CMN_SimpleFifoGetExtInfo | CMN_SimpleFifoInitialize()で設定したFIFOの拡張情報を取得します。 |
CMN_SimpleFifoGetDataSizeOfPeekHandle | CMN_SimpleFifoPeek()で取得したアドレス情報の要素数を取得します。 |
CMN_SimpleFifoCopyFromPeekHandle | CMN_SimpleFifoPeek()で取得したアドレス情報からデータを取得します。コピー処理はmemcpy()を使用します。 |
CMN_SimpleFifoCopyFromPeekHandleWithSpecificCopier | CMN_SimpleFifoPeek()で取得したアドレス情報からデータを取得します。コピー処理はユーザー側で用意します。DMA用いたコピーなどを想定しています。 |
Sequence
SimpleFIFOの簡易シーケンスです。
11.9.5. Code Examples
These libraries are used with audio features. So, their samples are audio samples.
-
examples/audio_player
-
examples/audio_recorder
11.10. Power Management
11.10.1. Overview
Power Management
は、Spresense SDK の特徴的な機能である省電力を実現するために電源管理を行うモジュールです。CXD5602 内部の電源ドメインやクロックの管理、及び、各種スリープモードのサポートを行います。
11.10.2. 電源状態
Spresense SDK は、省電力を実現するために、Deep Sleep
、Cold Sleep
、Hot Sleep
の3つの Sleep モードをサポートしています。
Deep Sleep
-
CXD5602 は全ての電源ドメインが OFF されていて、CXD5247 PMIC(Power Management IC) のみ通電している状態です。このとき CXD5247 も Sleep 状態に入っており、最も消費電力を低く抑えることができます。
Cold Sleep
-
CXD5602 内で必要最小限の電源ドメインのみ ON されている状態です。アラームタイマーや、USB 挿抜、GPIO 信号の変化など、
Deep Sleep
に比べて、多くの起床要因をトリガとして、この Sleep 状態から起床することができます。 Hot Sleep
-
OS がアイドル状態のときに、この Sleep 状態へと遷移します。Sleep 中も SRAM の状態が保持されているため、起床の際に、SPI-Flash からプログラムを再ロードする必要がなく、高速に Sleep 状態から復帰することができます。
現在のSDKバージョンでは、Hot Sleep はサポートしておりません。 |
電源状態遷移
主な電源状態の状態遷移図を以下に示します。
State | Description |
---|---|
PowerOff | CXD5247 PMIC(Power Management IC) も含めて、全ての電源が OFF されている状態です |
Battery Check | 電源供給の電圧レベルを監視している状態です。電源供給が 3.4 V 以上のときに CXD5602 がブートし、Run 状態に遷移します。 |
Run | 通常の実行状態です。 |
Idle | OS アイドル状態です。OS アイドルタスクでは WFI (Wait for Interrupt) で割込み待ちをしています。 |
Hot Sleep | OS アイドル状態が一定の時間以上続くことが予想される場合に、この状態に遷移します。Hot Sleep 状態中は、SRAM のデータは保持されています。 |
Cold Sleep | CXD5602 の必要最小限な電源のみ ON され、SRAM 含めてそれ以外の電源は OFF 状態です。 |
Deep Sleep | CXD5602 が電源 OFF されており、CXD5247 PMIC(Power Management IC) だけが ON している状態です。 |
電源ドメイン階層構造
CXD5602 チップ内部は、階層構造をもったいくつかの電源ドメインに分かれて構成されています。 各種 Sleep モードと電源ドメインとの関係を以下に示します。
11.10.3. 電源状態制御 API
Spresense SDK は、各種電源状態からの起動要因 (以下、boot cause
) 、及び、起動要因の許可・禁止を制御するための起動マスク (以下、boot mask
) をもっています。
- boot cause
-
各種スリープモードから起床・起動された要因を表します
- boot mask
-
起動要因の許可・禁止を制御します
boot cause
、及び、boot mask
は共通のビットフラグ構造をしており、それぞれの起動要因が1つのビットで表されます。boot mask
の該当ビットに1がセットされると起動要因として許可、0をセットすると禁止になります。Sleep 状態にいるときに、boot mask
で許可された起動要因のイベントが発生したときに Sleep 状態から起床します。そのとき、どの起動要因が boot cause
に反映されます。
起動要因、起動マスクの定義を以下に示します。
-
Boot Type は、POR (Power-On-Reset) により起動したのか、Reboot による再起動なのか、Deep Sleep, Cold Sleep 状態からの起動なのかを表します。
-
Maskable は、Yes のものだけ起動要因として禁止することが許可されています。No のものは起動要因として禁止することはできません。
boot cause / boot mask | Boot Type | Maskable | Description |
---|---|---|---|
PM_BOOT_POR_NORMAL | POR Boot | No | バッテリーもしくは電源が ON された |
PM_BOOT_POR_DEADBATT | POR Boot | No | バッテリーもしくは電源が 3.4V 以上になり起動した |
PM_BOOT_WDT_REBOOT | Reboot | No | システムウォッチドッグによりリブートした |
PM_BOOT_WDT_RESET | Reboot | No | CXD5602 単体のウォッチドッグによりリセットされた |
PM_BOOT_DEEP_WKUPL | Deep Boot | Yes | WKUPL 信号を検知した(*2) |
PM_BOOT_DEEP_WKUPS | Deep Boot | Yes (*1) | WKUPS 信号を検知した(*2) |
PM_BOOT_DEEP_RTC | Deep Boot | Yes (*1) | RTC Alarm が発火した |
PM_BOOT_DEEP_USB_ATTACH | Deep Boot | No | USB が接続された(*2) |
PM_BOOT_DEEP_OTHERS | Deep Boot | No | 未使用 |
PM_BOOT_COLD_SCU_INT | Cold Boot | Yes | SCU 割り込みを検知した |
PM_BOOT_COLD_RTC | Cold Boot | Yes | RTC Alarm が発火した |
PM_BOOT_COLD_RTC_ALM0 | Cold Boot | Yes | RTC Alarm0 が発火した(SDK は Alarm0 を使用します) |
PM_BOOT_COLD_RTC_ALM1 | Cold Boot | Yes | RTC Alarm1 が発火した(未使用) |
PM_BOOT_COLD_RTC_ALM2 | Cold Boot | Yes | RTC Alarm2 が発火した(未使用) |
PM_BOOT_COLD_RTC_ALMERR | Cold Boot | Yes | RTC Alarm Error が発生した(未使用) |
PM_BOOT_COLD_GPIO | Cold Boot | Yes | GPIO 割り込みを検出した |
PM_BOOT_COLD_SEN_INT | Cold Boot | Yes | Sensor 割り込み (SEN_INT) を検出した |
PM_BOOT_COLD_PMIC_INT | Cold Boot | Yes | PMIC (CXD5247) 割り込みを検出した |
PM_BOOT_COLD_USB_DETACH | Cold Boot | Yes | USB ケーブルが外された(*2) |
PM_BOOT_COLD_USB_ATTACH | Cold Boot | Yes | USB ケーブルが接続された(*2) |
電源状態制御に関連して、次の Power Management API が提供されます。
-
起動要因の取得
-
起動マスクの取得
-
起動マスクの許可・禁止
-
Sleep モードへの遷移
-
リブート
起動要因の取得 API
- Function Prototype
uint32_t up_pm_get_bootcause(void);
- Description
-
-
起動要因を取得します
-
起動マスクの取得 API
- Function Prototype
uint32_t up_pm_get_bootmask(void);
- Description
-
-
起動要因として何が許可・禁止されているかを表す起動マスクを取得します
-
デフォルトでは、全ての起動要因が許可されています
-
Deep Sleep、もしくは、Power-On-Reset により、この起動マスク値はリセットされます。 Hot Sleep もしくは Cold Sleep 中はこの起動マスク値は保持されています。
-
-
起動要因の許可 API
- Function Prototype
uint32_t up_pm_set_bootmask(uint32_t mask);
- Description
-
-
引数で指定した起動要因を有効にします
-
戻り値は、更新後の起動マスクを返します
-
起動要因の禁止 API
- Function Prototype
uint32_t up_pm_clr_bootmask(uint32_t mask);
- Description
-
-
引数で指定した起動要因を無効にします
-
戻り値は、更新後の起動マスクを返します
-
- Example
-
#include <arch/chip/pm.h> uint32_t bootmask; bootmask = up_pm_get_bootmask(); // Get the current bootmask printf("bootmask=0x%08x/n", bootmask); bootmask = up_pm_clr_bootmask(PM_BOOT_COLD_USB_DETACH); // Disable wakeup by USB detached printf("bootmask=0x%08x/n", bootmask); // Display the updated bootmask
Sleep 遷移 API
- Function Prototype
int up_pm_sleep(enum pm_sleepmode_e mode);
- Description
-
-
Sleep モードに遷移します
-
この関数の呼び出しから戻ってくることはなく、そのまま Sleep 状態に遷移します。
-
- Arguments
-
引数 mode Description PM_SLEEP_DEEP
Deep Sleep に移行します
PM_SLEEP_COLD
Cold Sleep に移行します
up_pm_sleep() の発行により、CXD5602 チップ単体として Sleep モードへ遷移します。 Sleep 状態へ遷移するにあたり、チップだけでなく基板に依存した処理を追加で行う場合があります。 BSP の基板依存部に、board_power_off() 関数を実装することで、基板の制御を含めた Sleep モードへの遷移を行います。
BSP 依存部に実装される Sleep 遷移 API を以下に示します。 これは、NuttShell 上の poweroff コマンドからも制御できます。
- Function Prototype
int board_power_off(int status)
- Arguments
-
引数 status Description NuttShell command BOARD_POWEROFF_DEEP
Deep Sleep に移行します
poweroff
BOARD_POWEROFF_COLD
Cold Sleep に移行します
poweroff --cold
リブート
- Function Prototype
int up_pm_reboot(void);
- Description
-
-
システムを再起動します
-
この関数の呼び出しから戻ってくることはなく、そのまま再起動します
-
BSP 基板依存部に、board_reset() 関数を実装することで、基板の制御を含めた再起動を実現することができます。
- Function Prototype
int board_reset(int status);
board_reset() は、NuttShell 上の reboot コマンドからも制御できます。
11.10.4. スリープモード
Deep Sleep
特徴
Deep Sleep は次のような特徴をもちます。
-
CXD5602 は、電源 OFF 状態です
-
CXD5247 は、Sleep モードに入り、CXD5602 へ供給されるコア電源、IO電源は OFF されます
-
CXD5247 RTC 時刻は保持されます(システムが RTC XTAL を持っていることが前提です)
-
CXD5247 GPO スイッチは Deep Sleep 中も値が保持されています
-
Deep Sleep 中に GPO を ON にしておく必要がないなら、省電力の観点から Deep Sleep に入る前に OFF することを推奨します
-
-
CXD5247 Load Switch は OFF されます
-
CXD5602 の IO が OFF になりますので、周辺デバイスから CXD5602 へ電流がリークしないように注意して下さい
-
消費電力
-
バッテリーの消費電流は、バッテリー端でおよそ 数uA ~ 数+uA程度になります。ただし、この値は基板の設計に依存します。CXD5602 が電源 OFF されるため、周辺デバイスから CXD5602 へのリーク電流が発生しないように注意して基板を設計してください。
Sleep 中の消費電力に関して、拡張ボードに SD カードが挿入されていると SD カードの電源消費分により 約 5 mA ほど消費電流が増加します。 |
スリープ条件
-
up_pm_sleep(PM_SLEEP_DEEP) や board_poweroff(BOARD_POWEROFF_DEEP) を呼び出すことによって、Deep Sleep 状態に遷移します
-
WKUPL 信号を使用している場合は、WKUPL が 3 秒以上アサートされた場合に、Deep Sleep 状態に遷移します
Spresense 基板では WKUPL 端子が出ていないため、WKUPL 信号による Deep Sleep への遷移機能を使用することはできません |
起床条件
起床条件が発生すると、プログラムは SPI-Flash のロードから開始されるため、Power-On-Resetによる起動とほぼ同じだけの時間がかかります。
-
PM_BOOT_DEEP_WKUPL
: WKUPL 信号が3秒以上アサートされた場合 -
PM_BOOT_DEEP_WKUPS
: WKUPS 信号がアサートされた場合 -
PM_BOOT_DEEP_RTC
: RTC Alarm が発火した場合 -
PM_BOOT_DEEP_USB_ATTACH
: USB ケーブルが接続された場合-
通常、USB ケーブルが接続されていた場合は、Deep Sleep 状態に入ることはできません。 ただし、CONFIG_BOARD_USB_DISABLE_IN_DEEP_SLEEPING=y にすれば、USB 機能を Disable して Deep Sleep 状態に遷移することができます。このとき、USB 接続をトリガにして起床することはできません。
-
Spresense 基板では WKUPL, WKUPS 端子が出ていないため、WKUPL, WKUPS 信号による Deep Sleep からの起床機能を使用することはできません |
Cold Sleep
特徴
-
CXD5602 は、チップ内部の PMU 電源ドメインのみ ON された状態です
-
CXD5602 I/O ピンは有効になっています
-
Backup SRAM の内容は保持されます
-
-
CXD5247 は通常動作状態です
-
CXD5247 RTC 時刻は保持されます(システムが RTC XTAL を持っていることが前提です)
-
CXD5247 GPO スイッチは Cold Sleep 中も値が保持されます
-
CXD5247 Load Switch は Cold Sleep 中も値が保持されます
-
消費電力
-
バッテリーの消費電流は、バッテリー端でおおよそ数百 uA です。この電流値は基板の設計に依存します。
Sleep 中の消費電力に関して、拡張ボードに SD カードが挿入されていると SD カードの電源消費分により 約 5 mA ほど消費電流が増加します。 |
スリープ条件
-
up_pm_sleep(PM_SLEEP_COLD) や board_poweroff(BOARD_POWEROFF_COLD) を呼び出すことによって、Cold Sleep 状態に遷移します
起床条件
起床条件が発生すると、プログラムは SPI-Flash のロードから開始されるため、Power-On-Resetによる起動とほぼ同じだけの時間がかかります。
-
PM_BOOT_COLD_SCU_INT
: SCU 割り込みが発火した場合 -
PM_BOOT_COLD_SEN_INT
: Sensor 割り込みが発火した場合 -
PM_BOOT_COLD_PMIC_INT
: CXD5247 からの割り込みが発火した場合-
WKUPS 信号がアサートされた場合
-
Low Battery が通知された場合
-
-
PM_BOOT_COLD_GPIO
: GPIO 割り込みがアサートされた場合 -
PM_BOOT_COLD_RTC_ALM0
: RTC Alarm が発火した場合 -
PM_BOOT_COLD_USB_ATTACH
: USB ケーブルが接続された場合 -
PM_BOOT_COLD_USB_DETACH
: USB ケーブルが外された場合
Spresense 基板では USB 挿抜による Cold Sleep からの起床機能はサポートされていません |
Hot Sleep
現在のSDKバージョンでは、Hot Sleep はサポートしておりません。 |
特徴
-
CXD5602 は、通常動作状態にほぼ等しいですが、NuttX が 動作しているアプリケーション CPU は電源 OFF されます
-
但し、Hot Sleep 期間中も SRAM の値は保持されています
-
-
CXD5247 は通常動作状態です
消費電力
-
バッテリーの消費電流は、バッテリー端で最小で数百 uA 以下にまで下げることができますが、アプリケーションとして何を動作させている状態かに強く依存します。
スリープ条件
-
CONFIG_CXD56_HOT_SLEEP=y にすることで、Hot Sleep が有効になります。
-
以下の CONFIG パラメーターで、Hot Sleep 状態に遷移する条件を変更できます
-
CONFIG_CXD56_HOT_SLEEP_WAIT_COUNT (milliseconds)
-
CONFIG_CXD56_HOT_SLEEP_THRESHOLD (milliseconds)
-
-
wakelock の取得と解除
-
後述する wakelock 機構で Hot Sleep 制御を行います。一つでも wakelock が獲得されている限り、Hot Sleep への遷移が禁止されます
-
-
Power Manager は、以下に示すアルゴリズムで Hot Sleep 状態へ遷移します。
-
NuttX OS がアイドル状態の場合、
Power Manager
はアイドル状態に滞在する時間をカウントします-
何かしらの割り込みが発生した場合は、アイドル状態から抜けて、カウンタはリセットされます
-
-
カウンタが、CONFIG_CXD56_HOT_SLEEP_WAIT_COUNT で定義された値を超えた場合、
Power Manager
は、この後に続くアイドル時間を予測して計算します -
もし、この予測アイドル時間 > CONFIG_CXD56_HOT_SLEEP_THRESHOLD を超えていた場合、システムは Hot Sleep 状態へと遷移します
-
wakelock が獲得されている場合は、システムは Hot Sleep 状態への遷移は禁止されます
-
起床条件
-
アプリケーション CPU に対する割り込みが発生した場合
-
UART1 受信割り込み
-
UART2 受信割り込み
-
SCU (Sensor Control Unit) Watermark 割り込み
-
USB 挿抜割り込み
-
CXD5247 からの割り込み
-
GPIO 割り込み
-
他の CPU からの CPU 間通信による割り込み
-
OS Timer 割り込み
-
11.10.5. 省電力制御
Spresense SDK は、以下のような省電力機能を提供します。
-
CPU システムクロック制御
-
CPU スリープ制御
CPU システムクロック制御
システムクロックは、主に 3 つのモードを提供しています。
Mode | CPU Clock | CXD5602 Core Voltage |
---|---|---|
HV (High Voltage) モード | PLL 156MHz | 1.0 V |
LV (Low Voltage) モード | PLL 32MHz | 0.7 V |
RCOSC モード | RCOSC 約 8MHz | 0.7 V |
クロック制御を行わない場合、常に HV モードで動作します。 動的なクロック制御による省電力機能を有効にする場合は、以下のコンフィグレーションを設定してください。
[CXD56xx Configuration] [Power Management] [Dynamic clock control] <= Y
Dynamic clock control を有効にすると、システムは HV モード で起動した後に RCOSC モードまでクロックを落とします。定常状態では、RCOSC モードで動作します。
一時的に、高いパフォーマンスが要求される処理を行う場合は、 Frequency Lock 機構を使用して動的にクロックを変更することが可能です。
RCOSC モード、LV モード、HV モードの順番で動作クロックが上がりパフォーマンスも向上しますが、その分だけ消費電力も増加します。Frequency Lock 機構は、LV モードに切り替えたい場合は LV ロックを、HV モードに切り替えたい場合は HV ロックを獲得します。獲得したロックを解放すると、元のクロック状態に戻ります。システム全体のうち、どれか一つでも、より高いクロックモードのロックを獲得している限りは、システムは高いクロックモードで動作します。例として、LV ロック、HV ロックの両方が獲得されている場合は、HV モードになります。HV ロックが解放された場合に、次に高いクロックモードである LV モードへと切り替わります。そこで、LV ロックも解放されると RCOSC モードに戻ります。
-
HV ロック獲得
struct pm_cpu_freqlock_s lock; lock.flag = PM_CPUFREQLOCK_FLAG_HV; up_pm_acquire_freqlock(&lock);
-
HV ロック解放
up_pm_release_freqlock(&lock);
-
LV ロック獲得
struct pm_cpu_freqlock_s lock; lock.flag = PM_CPUFREQLOCK_FLAG_LV; up_pm_acquire_freqlock(&lock);
-
LV ロック解放
up_pm_release_freqlock(&lock);
CPU スリープ制御
SDK は アプリケーション CPU が アイドル状態のときに自動的に Hot Sleep に遷移する機能を提供します。
現在のSDKバージョンでは、スリープ制御機能はサポートしておりません。 |
デフォルト設定では、Hot Sleep 機能は無効になっています。この機能を有効にするには以下の設定が必要です。
[CXD56xx Configuration] [Power Management] [Hot Sleep] <= Y [Hot Sleep wait counter] <= 20 [Hot Sleep threshold] <= 1000 [Enable GNSS Hot Sleep] <= Y
CONFIG_CXD56_HOT_SLEEP=y にすると、OS アイドル状態のときに自動的に Hot Sleep 状態に遷移します。 CONFIG_CXD56_GNSS_HOT_SLEEP=y にすると、GNSS CPU の Hot Sleep 機能を有効にすることができます。
アイドル状態で自動的に Hot Sleep 状態に遷移しますが、wakelock 機構により Sleep 状態に遷移するのを抑止することができます。
-
wakelock ロック獲得
struct pm_cpu_wakelock_s lock; lock.count = 0; up_pm_acquire_wakelock(&lock);
-
wakelock ロック解放
up_pm_release_wakelock(&lock);
11.11. Sensor Fusion Framework
11.11.1. General
CXD5602 はLow Power 常時センシング機能を備えているとともに、 多くのCoreとメモリによるセンサフュージョン機能が実現可能です。
SDKでは、これらを容易に実現するためのフレームワークを提供しています。
センサフュージョンフレームワークは、"Sensor Manager" と各センサの"Sensor Client"から構成されます。
-
"Sensor Manager" は、Publish-Subscribe Architectureに基づき、複数の"Sensor Client"を制御し、ここからPublishされるSensor Dataを配信します。
-
"Sensor Client"は、各種 "Sensor Device"を制御するドライバを制御する、"Physical Sensor"、 各"Sensor Device"からのデータをFusionして、高機能なセンサを実現する"Logical Sensor"、 Applicationがデータを受け取るための、"Sensor Application"から構成されています。
Sensor Framework のArchitecture図は以下の通りです。
11.11.2. Sensor Manager
"Sensor Manager" は、複数の"Sensor Client"の登録、管理を行い、 適切にセンサデータの配信を行います。
General
"Sensor Manager" に登録された、"Sensor Client"は、取得したデータを"Sensor Manager" に打ち上げることで、"Sensor Manager" が、Subscribeを要求している "Sensor Client" に適切にデータを配信します。
また、Subscribeされていない"Sensor Client"の電源モードを落とせるような枠組みを提供します。
APIs
"Sensor Manager" は、以下のAPIを提供します。 "Sensor Manager" のインターフェースは、パケットと呼ばれるデータ形式のコマンドを を発行すること制御可能です。 "Sensor Client" の登録、削除、データの受け渡しなどを行います。
APIs | Description | Corresponding API | Corresponding packet |
---|---|---|---|
Register | Resister a Sensor Client to Sensor Manager as subscriber. | sensor_command_register_t | |
Release | Unresister the Sensor Client from Sensor Manager. | sensor_command_release_t | |
SendData | Sender function to Sensor Manager without MemHandle. | sensor_command_data_t | |
SendData(MH) | Sender function to Sensor Manager with MemHandle. | sensor_command_data_mh_t | |
SendSetPower | Set power status of sensors. | sensor_command_power_t |
11.11.3. Logical Sensors
General
"Logical Sensor"とは、各種の物理センサから得られたセンサデータを何らかの信号処理アルゴリズムなどに基づき、高機能なセンサデータを作成するための"Sensor Client"で、ソフトウェアモジュールで構成されています。
実際のアルゴリズムの実装アロケーションは、下記のいくつかの方法があります。
-
NuttX上のタスクとして実装する。
-
asmpフレームワークを使って、DSP上に実装する。
-
各社ベンダが提供した暗号化されたDSPを用いて実装する。
A logical sensor task on NuttX.
NuttX上のモジュール、もしくはタスクとして実装をします。
特に、重くない処理であったり、頻度の多くない処理の場合、このように実装することで、メモリ、CPUリソースなどを消費せず実装が可能です。
Contents | Sample provider |
---|---|
Barometer | From sensor vender |
TAP Gesture (Tapping Detector) | From SDK |
A logical sensor on DSP by asmp.
ユーザが独自に"Logical Sensor"のアルゴリズムを作成し、その処理をDSP側にオフロードする必要があるような場合(例えば、処理が重いなど)、ASMPフレームワークを用いて、DSP側に実装することが可能です。
このような場合、"Logical Sensor"をスーパーバイザタスクで実装し、そのスーパーバイザタスクからDSP上のWorkerタスクへ処理要求を送って処理をさせることで、マルチコア処理での"Logical Sensor"を実現できます。
Contents | Sample provider |
---|---|
"" | From SDK |
ASMPフレームワークについては、ASMP Frameworkを参照してください。 |
A logical sensor on DSP with encryption.
各ソリューションベンダは、さまざまな logical sensor のアルゴリズムを提供します。その場合、各ベンダは、コードを開示せず、それぞれの契約等に基づき、機能を提供することが可能です。
このケースの場合は、ASMPフレームワーク上に、build 及び encryption 済のバイナリをロードすることで、実現が可能です。Encrypted DSPは、各ベンダから提供されます。
Contents | Sample provider |
---|---|
AESM (Activity Engine and Step Meter) | From SDK |
11.11.4. Logical Sensor API
各 Logical sensorは次のAPIを提供します。
APIs | Description |
---|---|
Create | Create a class instance to communicate with workers. |
Open | Load library and boot up as worker task. |
Write | Send data to worker task. |
Close | Destroy worker task. |
これらの要求は、以下のイベントとして定義され、DSP上のWorkerタスクとの送受信に使用されます。
詳細については、各スーパーバイザを参照してください。
Event | Description |
---|---|
Init | Initialization event. |
Exec | Execution event. |
Flush | Terminal event. |
11.11.5. Details of each Logical Sensors
11.11.6. Step Counter
"Step Counter" の構成図を以下に示します。
Supervisor
スーパーバイザは"Logical Sensor"のためのフレームワークです。 スーパーバイザのDSP上のWorkerを制御するためのいくつかのAPIを提供します。
APIs
"Step Counter"は以下の5つのAPIを提供します。
APIs | Description | Corresponding API |
---|---|---|
Create | Create StepCounterClass instance. | |
Open | Load StepCounter library and boot up as worker task. | |
Write | Send data to StepCounter worker task. | |
Close | Destory StepCounter worker task. | |
Set | Set setting to StepCounter library . |
Data format for Step Counter
"Step Counter"には、以下のように指定されたフォーマットで"Accelerometer"のデータが必要です。
これらのデータをWriteのAPIでWorkerに送信します。
Result of sensing
"Step Counter”は1秒ごとに以下の歩数計情報を出力します。 これらの情報は、StepCounterStepInfo に格納されます。
情報 | 単位 | 備考 |
---|---|---|
テンポ | Hz | 停止状態時も1Hz以上の値となります。 |
歩幅 | cm | 歩行と走行でそれぞれ固定値となります。 |
速度 | m/s | |
累積移動距離 | m | |
歩数 | - | |
行動認識 | - | 停止/歩行/走行状態を示します。 |
How to use
Preparation
"Sensor Manager"と呼ばれる複数の"Sensor Client"を制御するために設計されたフレームワークで、 "Step Counter"を制御します。
そのため、"Step Counter"を制御するには、"Sensor Manager"を事前に有効にする必要があります。
"Sensor Manager" を有効にするには、SS_ActivateSensorSubSystem(MsgQueId, api_response_callback_t) を呼ぶ必要があります。
MsgQueId は、Message Library Configuration で定義された MsgQueID を指定する必要があります。
api_response_callback_t は、非同期の通知を行うためのコールバック関数を指定します。
NULLを指定した場合は通知は行われません。
static void sensor_manager_api_response(unsigned int code,
unsigned int ercd,
unsigned int self)
{
...
}
SS_ActivateSensorSubSystem(MSGQ_SEN_MGR, sensor_manager_api_response);
"Sensor Manager"の有効後、"Sensor Manager"に"Step Counter"がSubscribeを要求する"Sensor Client"として、"Accelerometer"を登録します。
この際、Subscribeの処理として、StepCounterWriteを呼び出すコールバック関数を指定します。
また、"Application"が"Step Counter"のセンシング結果を知るために、 "Application"がSubscribeを要求する"Sensor Client"として、"Step Counter"を登録します。
この際、Subscribeの処理を行うコールバック関数を指定します。
bool step_counter_receive_data(sensor_command_data_mh_t& data)
{
StepCounterWrite(sp_step_counter_ins, &data);
return true;
}
bool step_counter_recieve_result(sensor_command_data_mh_t& data)
{
bool ret = true;
FAR SensorCmdStepCounter *result_data =
reinterpret_cast<SensorCmdStepCounter *>(data.mh.getVa());
if (SensorOK == result_data->result.exec_result)
{
if (result_data->exec_cmd.cmd_type ==
STEP_COUNTER_CMD_UPDATE_ACCELERATION)
{
...
}
}
return ret;
}
sensor_command_register_t reg;
reg.header.code = ResisterClient;
reg.self = stepcounterID;
reg.subscriptions = (0x01 << accelID);
reg.callback = NULL;
reg.callback_mh = &step_counter_receive_data;
SS_SendSensorResister(®);
reg.header.code = ResisterClient;
reg.self = app0ID;
reg.subscriptions = (0x01 << stepcounterID);
reg.callback = NULL;
reg.callback_mh = &step_counter_recieve_result;
SS_SendSensorResister(®);
Create and Open
事前準備の完了後、"Step Counter"の生成、有効化を行います。
"Step Counter"の生成には、StepCounterCreate(PoolId) を呼ぶ必要があります。
PoolIdには、"Memory Manager Configuration"で定義された IDを指定する必要があります。
戻り値として、"Step Counter"のインスタンスへのポインタが返ります。
FAR StepCounterClass *step_counter_instance;
step_counter_instance = StepCounterCreate(SENSOR_DSP_CMD_BUF_POOL);
"Step Counter"の有効化には、StepCounterOpen(FAR StepCounterClass*) を呼ぶ必要があります。
StepCounterClass*には、StepCounterCreateの戻り値である、"Step Counter"のインスタンスへのポインタを指定する必要があります。
StepCounterOpen(step_counter_instance);
Set stride
センシングする際の歩幅の初期値は、walking状態で60cm、running状態で80cmです。
この初期値を変更する場合は、StepCounterSet(FAR StepCounterClass*, StepCounterSetting*) を呼ぶ必要があります。
StepCounterClass*には、StepCounterCreateの戻り値である、"Step Counter"のインスタンスへのポインタを指定する必要があります。
StepCounterSetting*には、walking状態とrunning状態のそれぞれの歩幅を step_lengthに単位cmで設定して下さい。歩幅最大値は250cmです。
step_modeは、"STEP_COUNTER_MODE_FIXED_LENGTH"固定です。
StepCounterSetting set;
set.walking.step_length = 70; /* Set stride to 70 cm */
set.walking.step_mode = STEP_COUNTER_MODE_FIXED_LENGTH;
set.running.step_length = 90; /* Set stride to 90 cm */
set.running.step_mode = STEP_COUNTER_MODE_FIXED_LENGTH;
StepCounterSet(step_counter_instance, &set);
"Step Counter"は"Accelerometer"のSubscribeを契機に呼び出される StepCounterWriteで、DSP上のWorkerにデータを送信し、センシング処理を行います。
この際のシーケンスを以下に示します。
Close
"Step Counter"の無効化には、StepCounterClose(FAR StepCounterClass*) を呼ぶ必要があります。
StepCounterClass*には、StepCounterCreateの戻り値である、"Step Counter"のインスタンスへのポインタを指定する必要があります。
StepCounterClose(step_counter_instance);
Release from Sensor Manager
"Step Counter"の無効後に、"Sensor Manager"への登録を解除します。 この際、"Step Counter"にSubscribeする"Sensor Client"の動作を事前に停止させてください。
sensor_command_register_t reg;
rel.header.code = ReleaseClient;
rel.self = stepcounterID;
SS_SendSensorRelease(&rel);
Build Configurations
"Step Counter"の機能を使用するためには、
$>cd sdk $>tools/config.py -m
でconfigurationのmenuを開き、以下のoptionを設定する必要があります。
Select options in below:
[CXD56xx Configuration] [I2C0] <= Y [Sensor Control Unit] <= Y [Memory manager] <= Y [Memory Utilities] [Memory manager] <= Y [Message] <= Y [Drivers] [Sensor Drivers] <= Y [Bosch BMI160 Sensor support] <= Y [SCU Sequencer] <= Y [Sensing] [Sensing manager] <= Y [Step counter] <= Y [ASMP] <= Y
Worker
Workerは他のCore上で動作し、Supervisorから送信されたセンサデータを分析します。
そして、要求されたイベントの処理結果(ステップ数、状態など)を返します。
"Step Counter"のWorkerは、分析のために以下に示すデータを必要とします。
・ Accelerometerデータ(32Hz、32サンプル/1秒)
APIs
"Step Counter"のWorkerは以下の3つのAPIを提供します。
APIs | Description |
---|---|
Init | Initialize for AESM(Activity Engine and Step Meter) library. |
Exec | Execute calculate on sensor data on AESM library. |
Flush | End execute process of AESM library. |
これらのAPIは、Supervisorから送信されたパケットに含まれるイベントタイプやコマンドタイプによって、 呼び出されます。
関係は下記の通りです。
SensorDspCmd dsp_cmd;
dsp_cmd.header.sensor_type = StepCounter;
dsp_cmd.header.event_type = InitEvent;
SensorDspCmd dsp_cmd;
dsp_cmd.header.sensor_type = StepCounter;
dsp_cmd.header.event_type = ExecEvent;
dsp_cmd.exec_aesm_cmd.cmd_type = AESM_CMD_UPDATE_ACCELERATION;
SensorDspCmd dsp_cmd;
dsp_cmd.header.sensor_type = StepCounter;
dsp_cmd.header.event_type = FlushEvent;
Step Counter Example
"Step Counter"のsample applicationとして、"Step Counter" exampleがあります。ここでは、その使い方などを説明します。
Preparation
"Step Counter"のsample applicationを使うには、configurationのoptionを以下の設定にしてください。
Select options in below:
[Examples] [Step counter sensor example] <= Y
または、"Step Counter"のdefault configurationを使用して下さい。
$ ./tools/config.py examples/step_counter
タスク間通信ライブラリ(Message Library)とメモリ管理ライブラリ(Memory Manager)の設定は、以下のように行ってください。
"Step Counter"を使用する際に必要となる"MessageQueue"の定義を行う必要があります。 定義はMessageQueueLayout定義ファイルで行い、ツールでコードに組み込むヘッダファイルを生成することが出来ます。
"Step Counter"のsample applicationでは下記のように行います。
$>cd examples/step_counter/config $>ruby -I../../../sdk/modules/memutils/message/tool msgq_layout.conf 0x000fe000 0x1000 msgq_id.h msgq_pool.h
MessageQueueLayout定義ファイル(msgq_layout.conf)の記述内容は下記の通りです。
MsgQuePool
# ID, n_size n_num h_size h_nums
["MSGQ_SEN_MGR", 40, 8, 0, 0],
各パラメータの説明は以下の通りです。
パラメータ | 説明 |
---|---|
ID | メッセージキュープールIDの名称を、"MSGQ_"で始まる文字列で指定。 |
n_size | 通常優先度キューの各要素のバイト数(8以上512以下)。固定ヘッダ長(8byte) + パラメタ長を4の倍数で指定する。 |
n_num | 通常優先度キューの要素数(1以上16384以下)。 |
h_size | 高優先度キューの各要素のバイト数(0または、8以上512以下)。未使用時は0を指定すること。 |
h_num | 高優先度キューの要素数(0または、1以上16384以下)。未使用時は0を指定すること。 |
n_sizeは最適値となっているため、変更は行わないでください。
n_numも変更の必要はありませんが、他のapplicationでStep Counterを使う場合は、負荷を考慮して値を増やす必要が出てくる可能性があります。
h_size, h_numsはStep Counterを優先的に処理したい場合に利用して下さい。
それぞれの定義の詳細については、 examples/step_counter/config/msgq_layout.confを参照してください。 設定が変わった場合は、ツールを使って新しいヘッダーファイルを生成してください。 |
"Step Counter"を使用する際に必要となるMemoryLayout(pool)の定義を行う必要があります。
定義はMemoaryLayout定義ファイルで行い、ツールでコードに組み込むヘッダファイルを生成することが出来ます。
"Step Counter"のsample applicationでは下記のように行います。
$>cd examples/step_counter/config $>ruby -I../../../sdk/modules/memutils/memory_manager/tool -I. mem_layout.conf mem_layout.h fixed_fence.h pool_layout.h
MemoaryLayout定義ファイル(mem_layout.conf)の記述内容は下記の通りです。
FixedAreas
# name, device, align, size, fence
["SENSOR_WORK_AREA", "SHM_SRAM", U_STD_ALIGN, 0x0001e000, false],
["MSG_QUE_AREA", "SHM_SRAM", U_STD_ALIGN, 0x00001000, false],
["MEMMGR_WORK_AREA", "SHM_SRAM", U_STD_ALIGN, 0x00000200, false],
["MEMMGR_DATA_AREA", "SHM_SRAM", U_STD_ALIGN, 0x00000100, false],
各パラメータの説明は以下の通りです。
パラメータ | 説明 |
---|---|
name | 領域名(英大文字で始まり、"_AREA"で終わる名称。英大文字, 数字, _が使用可能) |
device | 領域を確保するMemoryDevicesのデバイス名 |
align | 領域の開始アライメント。0を除くMinAlign(=4)の倍数を指定する |
size | 領域のサイズ。0を除く4の倍数の値を指定する |
fence | フェンスの有効・無効を指定する(この項目は、UseFenceがfalseの場合は無視される) |
各nameの用途は以下の通りです。
SENSOR_WORK_AREA | センサフュージョンが利用する |
MSG_QUE_AREA | MessageQueueが利用する(固定名)。msgq_id.hの(MSGQ_END_DRM - MSGQ_TOP_DRAM)のサイズを超えないこと。 |
MEMMGR_WORK_AREA | Memory Managerが利用する作業領域(固定名, 固定サイズ) |
MEMMGR_DATA_AREA | Memery Managerが利用するデータ領域(固定名, 固定サイズ) |
各nameの合計のサイズがmpshm_init(), mpshm_remap()で確保するシェアメモリのサイズを超えないようにしてください。
PoolAreas
# name, area, align, pool-size, seg, fence
["SENSOR_DSP_CMD_BUF_POOL", "SENSOR_WORK_AREA", U_STD_ALIGN, U_SENSOR_DSP_CMD_POOL_SIZE, U_SENSOR_DSP_CMD_SEG_NUM, false],
["ACCEL_DATA_BUF_POOL", "SENSOR_WORK_AREA", U_STD_ALIGN, U_ACCEL_DATA_BUF_POOL_SIZE, U_ACCEL_DATA_BUF_SEG_NUM, false],
各パラメータの説明は以下の通りです。
パラメータ | 説明 |
---|---|
name | プール名(英大文字で始まり、"_POOL"で終わる名称。英大文字, 数字, _が使用可能) |
area | プール領域として使用するFixedAreaの領域名。領域はRAMに配置されていること |
align | プールの開始アライメント。0を除くMinAlign(=4)の倍数を指定する |
pool-size | プールのサイズ。0を除く4の倍数の値。Basicプールでは、セグメントサイズ * セグメント数 |
seg | セグメント数。1以上、255以下の値を指定する |
fence | フェンスの有効・無効を指定する。この項目は、UseFenceがfalseの場合は無視される |
各nameの用途は以下の通りです。
SENSOR_DSP_CMD_BUF_POOL | Workerとの送受信用バッファ領域 |
ACCEL_DATA_BUF_POOL | Accelerometerデータのバッファ領域 |
それぞれの定義の詳細については、 examples/step_counter/config/mem_layout.confを参照してください。 設定が変わった場合は、ツールを使って新しいヘッダーファイルを生成してください。 |
11.12. JPEG Decoder
11.12.1. 概要
IJGが開発したlibjpegライブラリをベースにしたJPEGデコード機能を提供します。
概ねオリジナルlibjpegライブラリのAPI仕様に準じておりますが、Spresense向けにカスタマイズしている点がありますので、 このドキュメントで、カスタマイズ内容について詳述します。
以下の説明において、「libjpegインスタンス」とは、libjpeg利用アプリケーションが用意しなければいけないstruct jpeg_decompress_struct型変数を指します。
11.12.2. Spresense向けカスタマイズ内容
出力フォーマット(色空間)
オリジナルlibjpegでサポートしているフォーマットはいずれも24bit/pixel以上ですが、 Spresenseでは16bit/pixelのCb/Y/Cr/Y(YUV4:2:2)フォーマットをサポートすることで、より少ないメモリでデコードできるようにしています。
下記defineがSpresenseで有効な色空間の定義です。 このパラメータをlibjpegインスタンスのメンバ out_color_space に設定してjpeg_start_decompress()を実行することで、 任意のサポート色空間でデコードすることが可能になります。
define名 | 意味 | bits/pixel | Spresense | オリジナル |
---|---|---|---|---|
JCS_CbYCrY | Cb/Y/Cr/Y(YUV4:2:2) | 16 | ○ | × |
JCS_YCbCr | Y/Cb/Cr(YUV) | 24 | × | ○ |
JCS_RGB | sRGB | 24 | × | ○ |
JCS_CMYK | C/M/Y/K | 32 | × | ○ |
JCS_YCCK | Y/Cb/Cr/K | 32 | × | ○ |
デコード結果読み出し単位
オリジナルlibjpegでは行単位の読み出しをサポートしていますが、 それに加えてSpresenseでは、より小さなMCU単位での読み出しもサポートしています。
API名 | 用途 | Spresense | オリジナル |
---|---|---|---|
jpeg_read_scanlines | 行単位での読み出し | ○ | ○ |
jpeg_read_mcus | MCU単位での読み出し | ○ | × |
MCUは、JPEGの圧縮単位ブロックで、基本は8×8pixelとなります。 JPEGエンコード時のパラメータ(JPEGヘッダに設定されている)およびデコード時のパラメータ(アプリケーションが設定する)によってサイズが変化します。 アプリケーションとしては、jpeg_start_decompress()実行後にlibjpegインスタンスの情報から以下のように1MCUのサイズを知ることができます。
|
MCU単位の場合、1単位あたりのpixel数(データサイズ)が画像サイズに依存しないので、この2種類の単位の差は下記の例のように画像サイズが大きいほど顕著になります。
単位 | QVGAの場合 | HDの場合 | 5Mの場合 |
---|---|---|---|
行単位 | 320 | 1280 | 2560 |
MCU単位 | 128 | 128 | 128 |
JPEGデータ入力方法
オリジナルlibjpegでは、ファイルポインタもしくはバッファでJPEGデータを入力できましたが、 Spresenseでは、それらに加えてファイルディスクリプタにも対応します。
API名 | 意味 | Spresense | オリジナル |
---|---|---|---|
jpeg_stdio_src | ファイルポインタ | ○ | ○ |
jpeg_fd_src | ファイルディスクリプタ | ○ | × |
jpeg_mem_src | バッファ | ○ | ○ |
ここでいうファイルディスクリプタとは、read()関数でJPEGデータの読み出しができるファイルディスクリプタであることが条件です。
例えば、通常ファイルをopen()したファイルディスクリプタ以外にも、socket()で作成したソケットディスクリプタも対応可能になります。 (もちろん、ソケットディスクリプタの場合、通信相手からJPEGデータがそのまま送られてくる必要があります。)
エラーハンドリング
オリジナルlibjpegもSpresenseもデフォルトではエラー発生時はexit()によってlibjpeg API実行タスクが終了します。 タスクを終了させない方法として、オリジナルlibjpegでは「setjmp/longjmp」を用いるexampleを提示していますが、 Spresense(NuttX)はsetjmp/longjmpをサポートしていないため、この手法は使えません。 今後、setjmp/longjmp以外の方法でエラーハンドリングできるようサポートする予定です。
libjpegのエラーハンドリングは、「エラーハンドラがreturnしない」前提で実装されており、 仮にエラーハンドラがreturnした場合の動作保証はしておりません。 |
エラーハンドラの 終了手段 | 意味 | Spresense | オリジナル |
---|---|---|---|
exit | libjpeg API実行タスクの終了 | ○ | ○ |
longjmp | setjmpで保存したスタックコンテキストへの | × | ○ |
return | エラー検出関数へのreturn | × | × |
「エラー」にはプログラム異常によるものの他に、外部要因によるものあります。代表的なエラーについては標準エラー出力への出力参照。 |
11.12.3. 状態遷移
11.12.4. 動作シーケンス
11.12.5. 標準エラー出力への出力
libjpegライブラリは、エラー もしくはWarningを検出した場合に標準エラー出力にメッセージを英文で出力します。 メッセージの意味や出力理由については、オリジナルlibjpegと変わりありません。
ここでは、よく発生するメッセージについて説明します。
出力メッセージ | エラー/Warning | 出力契機 |
---|---|---|
Improper call to JPEG library in state %d | エラー | API実行順が状態遷移図に従っていない |
Unsupported color conversion request | エラー | サポートしていない出力フォーマットを指定した |
Not a JPEG file: starts with 0x%02x 0x%02x | エラー | 0xFF D8から始まっていない |
Corrupt JPEG data: bad Huffman code | Warning | ハフマン符号のデコードエラー |
Premature end of JPEG file | Warning | 入力されたJPEGデータがEOIマーカーが出てこないままEOFに到達した場合に出力されます。 |
11.12.6. サンプルコード
このサンプルコードは、オリジナルlibjpegライブラリに添付されているexample.cのデコードプログラムをベースにして、 Spresense版libjpegの以下の特徴を盛り込んだものになります。
-
YUV4:2:2フォーマットで出力する
-
JPEGデータの入力にファイルディスクリプタを利用できる
-
MCU単位のデコードができる
12. NuttXが提供する機能
NuttXが提供する機能については、NuttX.orgを参照してください。