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つのリポジトリで構成されます。
リポジトリ名 | サブリポジトリ | 説明 |
---|---|---|
Spresense SDK のメインリポジトリで開発環境を整える場合はこちらのリポジトリをCloneします。SpresenseのBSP及びSpresenseがサポートするドライバ、各種Exampleコードが含まれ、spresense-nuttx, spresense-nuttx-appsをSubmoduleとして参照しています。 |
||
Spresense NuttX のクローンリポジトリです。SpresenseにおけるKernelがこのリポジトリで管理されています |
||
Spresense NuttX Apps のクローンリポジトリです (v2.0以降)。SpresenseにおけるNuttXオリジナルアプリケーションがこのリポジトリで管理されています |
4.2. ソースツリー
Spresense SDKのソースツリー構成は以下のようになっています
各ディレクトリの内容は以下のようになっています
ディレクトリ名 | 説明 |
---|---|
spresense |
spresenseをCloneすると生成されるディレクトリです |
examples |
Spresense SDKでサポートされるドライバ、モジュールを使ったExampleコードが含まれます |
externals |
Spresense SDKでサポートされる外部ライブラリが含まれます |
nuttx |
Spresense NuttX のKernelソースコードが含まれます |
sdk |
Spresense SDKでサポートされるドライバ、モジュール類が含まれます |
apps |
Spresense NuttX オリジナルアプリケーションのソースコードが含まれます (v2.0以降) |
configs |
Spresense SDKで提供しているConfigurationが含まれます |
modules |
Spresense SDKで提供しているオーディオやセンサなどのモジュールが含まれます |
system |
Spresense SDKで提供しているシステムツールが含まれます |
tools |
Spresense SDKでの開発に必要なツールおよびスクリプトが含まれます |
5. Spresense SDKが提供する機能
ここでは、Spresense SDKで提供している各機能について、説明します。
5.1. BSP
5.1.1. 概要
BSP (Board Support Package) は、ボード固有の設定や処理を行うためのソースコードが含まれています。
NuttXでは、以下のようなドライバソフトウェアアーキテクチャになっており、BSPディレクトリには Driver (Lower Half)
、 Board specific code
および Architecture specific code
などのドライバソフトウェアが含まれます。
5.1.1.1. 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 を参照してください。
5.1.1.3. Board specific code
ボード固有の処理には、ボードの実装によって可変になるもの(ピン設定など)があります。これらの処理には、ある程度共通化できるものと完全にボード依存になっているものに分かれます。これらはそれぞれ、nuttx/boards/arm/cxd56xx/common
および nuttx/boards/arm/cxd56xx/<board name>
( <board name>
は対応しているボード名)に格納されています。
また、ボードの初期化処理もこれに含まれます。
5.1.2. ディレクトリ構成
spresense/nuttx
|-- arch
| `-- arm
| |-- include
| | `-- cxd56xx
| `-- src
| `-- cxd56xx
`-- boards
`-- arm
`-- cxd56xx
|-- common
|-- drivers
`-- spresense
`-- scripts
ディレクトリ名 | 内容 |
---|---|
|
ボード固有の処理だが、処理シーケンスが同じで、マクロの変更等で共通化できるルーチンが含まれます。 |
|
Spresense基板を動作させるための処理(ピン設定など)が含まれます。 |
|
リンカスクリプトが含まれます。 |
|
Spresense固有のドライバが含まれます。 |
|
SoCに搭載されているデバイスドライバが提供しているAPIを呼び出す場合のヘッダファイルが含まれます。 |
|
Lower Halfドライバやチップ固有のデバイスドライバなどが含まれます。 |
5.1.3. ボードの初期化処理
メインCPUが起動すると、NuttXカーネルの初期化が行われ、Userのエントリポイント関数が呼び出されます。デフォルトの設定では、spresense_main
関数が呼ばれ、そこから nsh
(NuttShell) を起動します。CONFIG_USER_ENTRYPOINT
を変更することでUserのエントリポイントを変更することができます。
nsh
では、ボードの初期化( boardctl()
)が呼び出されます。実際のボードの初期化は、NuttXのルールに従い board_app_initialize
関数に実装されています。この関数では、使用するボードの初期化を行うための処理を行います。
初期化の処理には、Spresenseを使用するための最低限の機能の初期化や、使用するデバイスドライバの初期化などがあります。
CONFIG_USER_ENTRYPOINT で起動するタスクを spresense_main から変更した場合は、ユーザーが自ら boardctl() の呼び出しを行う必要があります。
|
5.2. GPIO/Pin Specification
5.2.2. GPIO Driver Interface
gpioif は、アプリケーションに対して以下の機能を提供します。
-
GPIO ピン設定
-
機能モード
-
入出力設定
-
ドライブ電流/スルーレート設定
-
プルアップ/プルダウン設定
-
-
GPIO 割り込み設定
-
レベル・エッジトリガ設定
-
ノイズフィルタ設定
-
-
GPIO 入出力制御
-
GPIO 状態取得
APIの詳細は、こちらを参照してください。
GPIO 割り込みは、board_gpio_intconfig 関数により登録できますが、登録可能な上限数が決まっています。 SYS GPIO と APP GPIO を合わせて最大12本まで登録することができます。
ピン名称やピン番号の定義については、nuttx/arch/arm/include/cxd56xx/pin.h を参照してください。 |
5.2.2.1. 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
5.2.3. Pin specification
ピンは複数の用途が割り当てられており、Mode(0-3) によって機能を変更することができます。 それぞれのピンはまとまった単位で Pin Group としてグルーピングされており、 Mode番号の指定によってその Group 単位で機能が切り替わります。
ここで PIN_PWM2, PIN_PWM3 のピンについての具体例を示します。
PIN_PWM2 と PIN_PWM3 は同一 Group に属しており、上の図にあるように、 Mode の選択によって 2 つのピンの機能が連動して同時に切り替わります。
-
Mode0
の場合は、PIN_PWM2 と PIN_PWM3 の Pin は 両方とも GPIO 機能に設定されます -
Mode1
の場合は、PIN_PWM2 と PIN_PWM3 の Pin は 両方とも PWM 機能に設定されます -
Mode2
の場合は、PIN_PWM2 と PIN_PWM3 の Pin は 両方とも I2C 機能に設定されます
Group 単位で機能が割り当てられるため、PIN_PWM2 を GPIO として、PIN_PWM3 を PWM として個別に設定することはできません。
また、全てのピンについて、LSI の初期状態は Mode0 (GPIO) が選択されています。
|
5.2.3.1. Pin List
-
Pin Name: nuttx/arch/arm/include/cxd56xx/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 |
D41 |
* |
GPIO |
RTC_IRQ_OUT |
RTC_IRQ_OUT |
|||
AP_CLK |
D40 |
* |
* |
GPIO |
AP_CLK |
PMU_WDT |
PMU_WDT |
|
GNSS_1PPS_OUT |
D44 |
* |
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 |
D42 |
* |
* |
GPIO |
SPI#2 |
UART#0 |
I2C#3 |
|
SPI2_SCK |
D43 |
* |
* |
|||||
SPI2_MOSI |
D04 |
* |
* |
GPIO |
||||
SPI2_MISO |
D08 |
* |
* |
|||||
HIF_IRQ_OUT |
D02 |
* |
* |
GPIO |
HIF_IRQ_OUT |
HIF_IRQ_OUT |
GNSS_1PPS_OUT |
|
HIF_GPIO0 |
D39 |
* |
GPIO |
GPS_EXTLD |
||||
SEN_IRQ_IN |
D22 |
* |
* |
GPIO |
SEN_IRQ_IN |
|||
SPI3_CS0_X |
D32 |
* |
* |
GPIO |
SPI3_CS0_X |
|||
SPI3_CS1_X |
D07 |
* |
* |
GPIO |
SPI3_CS1_X |
|||
SPI3_CS2_X |
- |
* |
* |
GPIO |
SPI3_CS2_X |
|||
SPI3_SCK |
D29 |
* |
* |
GPIO |
SPI#3 |
|||
SPI3_MOSI |
D31 |
* |
* |
|||||
SPI3_MISO |
D30 |
* |
* |
|||||
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 |
D36 |
* |
GPIO |
SDIO |
||||
SDIO_WP |
D37 |
* |
||||||
SDIO_CMDDIR |
D33 |
* |
GPIO |
SDIO |
||||
SDIO_DIR0 |
D34 |
* |
||||||
SDIO_DIR1_3 |
D35 |
* |
||||||
SDIO_CLKI |
D38 |
* |
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 |
5.2.3.2. Pin Configuration
Pin 設定は、pinconfigドライバで設定されます。例えば、Mode0(GPIO)以外の I2C や SPI 機能として使用する場合は、 それぞれ I2C, SPI ドライバの中で、Pin が設定されます。そのため、アプリケーションが Mode の変更などを 意識する必要はありません。
Mode0(GPIO) として使用する場合に限り、前述した gpioif を使用して設定してください。
5.2.3.2.1. Board Specific Pin Pull and Drive Current Setting
Pin のプル設定やドライブ電流のデフォルト設定は、 nuttx/arch/arm/src/cxd56xx/hardware/cxd5602_pinconfig.h に定義されています。
基本的に、プル設定は Hi-Z フローティング状態、ドライブ電流は多くが 2mA に設定されています。
基板に依存して、これらの設定を変更する場合は、CONFIG_BOARD_CUSTOM_PINCONFIG=y 有効にして、 nuttx/boards/arm/cxd56xx/spresense/include/board_pinconfig.h を参考に定義してください。
Spresense基板の例では、
/* Customize from default to the board specific pin configuration
* The default pin configurations are defined in
* boards/arm/cxd56xx/spresense/include/board_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 に変更
しています。
5.3. Audio Subsystem
5.3.1. General
CXD5602 はハイレゾリューションを扱えるオーディオ機能(オーディオ・サブシステム)が搭載されています。 オーディオ・サブシステムの機能概要を以下に示します。
-
Audio Codec ハードウェア (AD/DA, DNC, DEQ, etc.) の制御
-
Audio Recorder 及び Capture 機能
-
Audio Player 及び Renderer 機能
-
Bluetooth 関連機能(for BT-A2DP)
-
Sound Effector 機能(例えば、音声通話用のバンドパスフィルタなど)
-
Sound Sensing 機能(例えば、ノイズ計測、異常音検知など)
このドキュメントは、CXD5602 のハードウェア上で実現できるオーディオ機能を制御するためのソフトウェアが記載されています。
現在のファームウェアでは、Bluetooth 関連機能(for BT-A2DP) 及び、Sound Effector 機能(例えば、音声通話用のバンドパスフィルタなど) は、未対応です。 |
5.3.2. レイヤ構造について
オーディオ・サブシステムのスタックダイアグラムを以下に示します。
オーディオ・サブシステムは、大きく3つのレイヤを持ちます。
- Audio Manager(High Level API)
-
最上位のレイヤで、最上位の抽象度での制御を行うレイヤです。システム全体の整合を取りながら制御します。
- Object Layer(ObjectLevel API)
-
各機能Objectのレイヤで、機能ブロック単位での抽象度で制御を行うレイヤです。各機能内の信号処理・DSP処理などに関して整合を取ります。
- Component Layer(Low Level API)
-
各信号処理componetのレイヤで、信号処理ブロック単位での抽象度で制御を行うレイヤです。信号処理ブロックの組み合わせで処理を構成することで、自由度の高いオーディオ処理を実現できます。
5.3.3. High Level API
High Level API は Spresense SDK Audio
の Audio Manager
が提供する API です。
5.3.3.1. コマンド送受信による制御ついて
High Level API は、オーディオ・サブシステムをコマンドオブジェクトで制御します。(High Level API Command System)
コマンドオブジェクトは、AS_SendAudioCommandでオーディオ・サブシステムに送信されます。
送信されるコマンドオブジェクトは、AudioCommandになります。
コマンドの詳細は コマンドフォーマット に記載します。
オーディオ・サブシステムは、送信されたコマンドに応じて処理を行い、結果を返します。
結果は、AudioResult オブジェクトで返され、AS_ReceiveAudioResult で取得することができます。
リザルトの詳細は リザルトフォーマット に記載します。
コマンドは同期コマンドになります。コマンドを発行したらリザルトが返るまで次のコマンドを発行できません。Audio Manager を介して High Level API を使う場合は、送信・受信の手順が1コマンド単位で対になるようにプログラムしてください。
|
5.3.3.1.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
-
各コマンドにおいて設定および制御を行う対象を識別するためのコードです。
5.3.3.1.2. 通知データ形式(リザルトフォーマット)
リザルトオブジェクトは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と同じ値が入ります。
5.3.3.2. 状態遷移
High Level API は、複数の状態を持ちます。 以下に、状態遷移図を示します。
各モードの説明は以下になります。
-
PowerOff 状態
オーディオ・サブシステムのオブジェクトを生成し、起動した直後の状態です。オーディオを使用しない場合には、この状態に遷移しておくことで、オーディオブロックでの消費電力をほぼ0にします。
AUDCMD_POWERON コマンドによって、Ready状態にのみ遷移します。
-
Ready状態
オーディオブロックに電源を入れて、動作モードにオーディオ機能を動作できるように準備している状態です。この状態では、消費電力は下がっていないのですが、IO/アナログが起動している状態ですので、モード遷移が速やかに行えます。
状態の遷移は、以下になります。
AUDCMD_SETPOWEROFFSTATUS コマンドによって、PowerOff状態に遷移できます。 AUDCMD_SETPLAYERSTATUS コマンドによって、Player状態に遷移できます。 AUDCMD_SETRECORDERSTATUS コマンドによって、Recorder状態に遷移できます。 AUDCMD_SETBASEBANDSTATUS コマンドによって、Baseband状態に遷移できます。 AUDCMD_SETRECOGNIZERSTATUS コマンドによって、recognizer状態に遷移できます。
-
Player状態
SDカードなどのストレージや WiFi/LTE などのネットワークからの圧縮音声ファイルをデコードし、AnalogOutやI2Sに発音する機能を実現する状態です。状態の中にPlayerReady状態とPlayerActive状態の2つのサブ状態を持ちます。 PlayerReady状態は、音楽再生停止の状態です。AUDCMD_PLAYPLAYER によってPlayerActiveに遷移し音楽再生動作を行います。 PlayerActive状態は、音楽再生中の状態です。AUDCMD_STOPPLAYER によって音楽再生を停止しPlayerReadyに遷移します。
AUDCMD_SETREADYSTATUS コマンドによって、Ready状態にのみ遷移します。
-
Recorder状態
Micから入力された音声データを圧縮して、SDカードなどのストレージに書き出したり、WiFi/LTE などのネットワークに打ち上げて記録する機能を実現する状態です。 状態の中にRecorderReady状態とRecorderActive状態の2つのサブ状態を持ちます。 RecorderReady状態は、音声記録停止の状態です。AUDCMD_STARTREC によってRecorderActiveに遷移し音声記録動作を行います。 RecorderActive状態は、音声記録中の状態です。AUDCMD_STOPREC によって音声記録を停止しRecorderReadyに遷移します。
AUDCMD_SETREADYSTATUS コマンドによって、Ready状態にのみ遷移します。
-
Baseband状態
Micから入力された音声データを、内部でエフェクト処理を行い、AnalogOut、または、I2Sに出力する機能を実現する状態です。 状態の中にBasebandReady状態とBasebandActive状態の2つのサブ状態を持ちます。 BasebandReady状態は、音声入出力停止の状態です。AUDCMD_STARTBB によってBasebandActiveに遷移し音声入出力動作を開始します。 BaseBandActive状態は、音声入出力動作中状態です。AUDCMD_STOPBB によって音声入出力動作を停止しBasebandReadyに遷移します。
AUDCMD_SETREADYSTATUS コマンドによって、Ready状態にのみ遷移します。
-
Recognizer状態
Micから入力された音声データに対して、何からの認識処理を行う機能を実現する状態です。 状態の中にRecognizerReady状態とRecognizerActive状態の2つのサブ状態を持ちます。 RecognizerReady状態は、音声入出Recognizer力停止の状態です。AUDCMD_START_RECOGNIZER によってRecognizerActiveに遷移し音声入出力動作を開始します。 RecognizerActive状態は、音声入出力動作中状態です。AUDCMD_STOP_RECOGNIZER によって音声入出力動作を停止しRecognizerReadyに遷移します。
AUDCMD_SETREADYSTATUS コマンドによって、Ready状態にのみ遷移します。
5.3.3.3. インスタンスの生成
High Level API
を実行するために、各クラスインスタンスの生成、メモリ管理、メッセージ通信用のリソース割り当て等を行う必要があります。
これらは、各レイヤーごとの機能ブロックに対する create
関数を呼ぶことで実行が可能です。以下に各レイヤごとの create
関数のリストを示します。
5.3.3.3.1. Audio Manager の生成
High Level API
を使用する場合、 Audio Manager
のインスタンス生成とリソース割り当てを行う必要があります。
これらを行う場合は、以下の関数を呼ぶ必要があります。
-
関数名
-
引数
-
各リソース(メモリマネージャ、メッセージ)のID
typedef struct { /*! \brief [in] MsgQueID of Application */ uint8_t app; /*! \brief [in] MsgQueID of audio_manager */ uint8_t mng; /*! \brief [in] MsgQueID of playerObject */ uint8_t player_main; /*! \brief [in] MsgQueID of playerObject for Sound Effect */ uint8_t player_sub; /*! \brief [in] MsgQueID of FrontendObject */ uint8_t micfrontend; /*! \brief [in] MsgQueID of recorderObject */ uint8_t recorder; /*! \brief [in] MsgQueID of mixerObject */ uint8_t mixer; /*! \brief [in] MsgQueID of effectorObject */ uint8_t effector; /*! \brief [in] MsgQueID of recognizerObject */ uint8_t recognizer; } AudioSubSystemIDs;
-
リザルト受信用のcallback
AudioAttentionCb
を設定する。typedef void (*AudioAttentionCb)(const ErrorAttentionParam *attparam);
-
AS_CreateAudioManager は、起動直後か、AS_DeleteAudioManager 呼出し後にのみ呼び出し可能です。
|
5.3.3.3.2. Object Layer の生成
High Level API
を使用する場合でも、 Object Layer
で使用する各 Object
の生成は必要です。各 Object
ごとのインスタンス生成とリソース割り当てを行います。
これらを行う場合は、以下の関数を呼ぶ必要があります。
-
関数名
-
引数
-
各リソース(メモリマネージャ、メッセージ)のID
/** Message queue ID parameter of activate function */ typedef struct { /*! \brief [in] Message queue id of micfrontend */ uint8_t micfrontend; /*! \brief [in] Message queue id of audio_manager */ uint8_t mng; /*! \brief [in] Message queue id of DSP */ uint8_t dsp; } AsMicFrontendMsgQueId_t;
/** Pool ID parameter of activate function */ typedef struct { /*! \brief [in] Memory pool id of input data */ MemMgrLite::PoolId input; /*! \brief [in] Memory pool id of PreProc */ MemMgrLite::PoolId output; /*! \brief [in] Memory pool id of dsp command data */ MemMgrLite::PoolId dsp; } AsMicFrontendPoolId_t;
typedef struct { /*! \brief [in] ID for sending messages to each function */ AsMicFrontendMsgQueId_t msgq_id; /*! \brief [in] ID of memory pool for processing data */ AsMicFrontendPoolId_t pool_id; } AsCreateMicFrontendParams_t;
-
リザルト受信用のcallback
AudioAttentionCb
を設定する。typedef void (*AudioAttentionCb)(const ErrorAttentionParam *attparam);
-
AS_CreateMicFrontend は、起動直後か、AS_DeleteMicFrontend 呼出し後にのみ呼び出し可能です。
|
-
関数名
-
引数
-
各リソース(メモリマネージャ、メッセージ)のID
/** Message queue ID parameter of activate function */ typedef struct { /*! \brief [in] Message queue id of recorder */ uint8_t recorder; /*! \brief [in] Message queue id of audio_manager */ uint8_t mng; /*! \brief [in] Message queue id of DSP */ uint8_t dsp; } AsRecorderMsgQueId_t;
/** Pool ID parameter of activate function */ typedef struct { /*! \brief [in] Memory pool id of input data */ MemMgrLite::PoolId input; /*! \brief [in] Memory pool id of output data */ MemMgrLite::PoolId output; /*! \brief [in] Memory pool id of dsp command data */ MemMgrLite::PoolId dsp; } AsRecorderPoolId_t;
typedef struct { /*! \brief [in] ID for sending messages to each function */ AsRecorderMsgQueId_t msgq_id; /*! \brief [in] ID of memory pool for processing data */ AsRecorderPoolId_t pool_id; } AsCreateRecorderParams_t;
-
リザルト受信用のcallback
AudioAttentionCb
を設定する。typedef void (*AudioAttentionCb)(const ErrorAttentionParam *attparam);
-
AS_CreateMediaRecorder は、起動直後か、AS_DeleteMediaRecorder 呼出し後にのみ呼び出し可能です。
|
-
関数名
-
引数
-
各リソース(メモリマネージャ、メッセージ)のID
/** Message queue ID parameter of activate function */ typedef struct { /*! \brief [in] Message queue id of output mixer */ uint8_t mixer; /*! \brief [in] Message queue id of audio_manager */ uint8_t mng; /*! \brief [in] Message queue id of dsp * Effective only when use postfilter */ uint8_t render_path0_filter_dsp; uint8_t render_path1_filter_dsp; } AsOutputMixMsgQueId_t;
/** Pool ID parameter of activate function */ typedef struct { /*! \brief [in] Memory pool id of pcm data * Effective only when use postfilter */ MemMgrLite::PoolId render_path0_filter_pcm; MemMgrLite::PoolId render_path1_filter_pcm; /*! \brief [in] Memory pool id of dsp command data * Effective only when use postfilter */ MemMgrLite::PoolId render_path0_filter_dsp; MemMgrLite::PoolId render_path1_filter_dsp; } AsOutputMixPoolId_t;
typedef struct { /*! \brief [in] ID for sending messages to each function */ AsOutputMixMsgQueId_t msgq_id; /*! \brief [in] ID of memory pool for processing data */ AsOutputMixPoolId_t pool_id; } AsCreateOutputMixParams_t;
-
リザルト受信用のcallback
AudioAttentionCb
を設定する。typedef void (*AudioAttentionCb)(const ErrorAttentionParam *attparam);
-
AS_CreateOutputMixer は、起動直後か、AS_DeleteOutputMix 呼出し後にのみ呼び出し可能です。
|
-
関数名
-
引数
-
Player ID
Playerのインスタンスは2つまで生成可能です。それぞれのインスタンスのIDを指定します。typedef enum { AS_PLAYER_ID_0 = 0, AS_PLAYER_ID_1, AS_PLAYER_ID_NUM, } AsPlayerId;
-
各リソース(メモリマネージャ、メッセージ)のID
/** Message queue ID parameter of activate function */ typedef struct { /*! \brief [in] Message queue id of player */ uint8_t player; /*! \brief [in] Message queue id of audio_manager */ uint8_t mng; /*! \brief [in] Message queue id of output mixer */ uint8_t mixer; /*! \brief [in] Message queue id of DSP */ uint8_t dsp; } AsPlayerMsgQueId_t;
/** Pool ID parameter of activate function */ typedef struct { /*! \brief [in] Memory pool id of es data */ MemMgrLite::PoolId es; /*! \brief [in] Memory pool id of pcm data */ MemMgrLite::PoolId pcm; /*! \brief [in] Memory pool id of dsp command data */ MemMgrLite::PoolId dsp; /*! \brief [in] Memory pool id of src work area */ MemMgrLite::PoolId src_work; } AsPlayerPoolId_t;
typedef struct { /*! \brief [in] ID for sending messages to each function */ AsPlayerMsgQueId_t msgq_id; /*! \brief [in] ID of memory pool for processing data */ AsPlayerPoolId_t pool_id; } AsCreatePlayerParams_t;
-
リザルト受信用のcallback
AudioAttentionCb
を設定する。typedef void (*AudioAttentionCb)(const ErrorAttentionParam *attparam);
-
AS_CreatePlayerMulti は、起動直後か、AS_DeletePlayer 呼出し後にのみ呼び出し可能です。
|
-
関数名
-
引数
-
各リソース(メモリマネージャ、メッセージ)のID
/** Message queue ID parameter of activate function */ typedef struct { /*! \brief [in] Message queue id of sound recognizer */ uint8_t recognizer; /*! \brief [in] Message queue id of audio_manager */ uint8_t mng; /*! \brief [in] Message queue id of DSP */ uint8_t dsp; } AsRecognizerMsgQueId_t;
/** Pool ID parameter of activate function */ typedef struct { /*! \brief [in] Memory pool id of recognition result data */ MemMgrLite::PoolId out; /*! \brief [in] Memory pool id of DSP communication Message */ MemMgrLite::PoolId dsp; } AsRecognizerPoolId_t;
typedef struct { /*! \brief [in] ID for sending messages to each function */ AsRecognizerMsgQueId_t msgq_id; /*! \brief [in] ID of memory pool for processing data */ AsRecognizerPoolId_t pool_id; } AsCreateRecognizerParam_t;
-
リザルト受信用のcallback
AudioAttentionCb を設定する。
typedef void (*AudioAttentionCb)(const ErrorAttentionParam *attparam);
-
AS_CreateRecognizer は、起動直後か、AS_DeleteRecognizer 呼出し後にのみ呼び出し可能です。
|
-
引数
-
各リソース(メモリマネージャ、メッセージ)のID
/** Message queue ID parameter of activate function */ typedef struct { /*! \brief [in] Message queue id of synthesizer */ uint8_t synthesizer; /*! \brief [in] Message queue id of audio_manager */ uint8_t mng; /*! \brief [in] Message queue id of DSP */ uint8_t dsp; } AsSynthesizerMsgQueId_t;
/** Pool ID parameter of activate function */ typedef struct { /*! \brief [in] Memory pool id of output data */ MemMgrLite::PoolId output; /*! \brief [in] Memory pool id of dsp command data */ MemMgrLite::PoolId dsp; } AsSynthesizerPoolId_t;
/** Activate function parameter */ typedef struct { /*! \brief [in] ID for sending messages to each function */ AsSynthesizerMsgQueId_t msgq_id; /*! \brief [in] ID of memory pool for processing data */ AsSynthesizerPoolId_t pool_id; } AsCreateSynthesizerParam_t;
-
リザルト受信用のcallback
AudioAttentionCb
を設定する。typedef void (*AudioAttentionCb)(const ErrorAttentionParam *attparam);
-
AS_CreateMediaSynthesizer は、起動直後か、AS_DeleteMediaSynthesizer 呼出し後にのみ呼び出し可能です。
|
5.3.3.3.3. Component Layer の生成
High Level API
および Object Level API
を使用する場合でも、 Component Layer
で使用する各Componentの生成は必要です。各 Component
ごとのインスタンス生成とリソース割り当てを行います。
これらを行う場合は、以下の関数を呼ぶ必要があります。
-
関数名
-
引数
-
各リソース(メモリマネージャ、メッセージ)のID
音声取得(Capture)は、マイク入力とI2Sの2系統の中から2つで選択し実行が可能です。そのため2系統分のリソースの設定を行うことが可能になっています。
-
1系統のみ使用する場合は、dev0 側のみ設定し、dev0 には null を設定しても問題ありません。
|
/** Message queue ID parameter of activate function */
typedef struct
{
/*! \brief [in] Message queue id of capture device0 request */
uint8_t dev0_req;
/*! \brief [in] Message queue id of capture device0 for syncronizing */
uint8_t dev0_sync;
/*! \brief [in] Message queue id of capture device1 request */
uint8_t dev1_req;
/*! \brief [in] Message queue id of capture device1 for syncronizing */
uint8_t dev1_sync;
} AsCaptureMsgQueId_t;
typedef struct
{
/*! \brief [in] ID for sending messages to each channels */
AsCaptureMsgQueId_t msgq_id;
} AsCreateCaptureParam_t;
AS_CreateCapture は、起動直後か、AS_DeleteCapture 呼出し後にのみ呼び出し可能です。
|
-
関数名
-
引数
-
各リソース(メモリマネージャ、メッセージ)のID
音声出力(Render)は、2系統をMixingして出力することが可能です。そのため2系統分のリソースの設定を行うことが可能になっています。
-
1系統のみ使用する場合は、dev0 側のみ設定し、dev0 には null を設定しても問題ありません。
|
/** Message queue ID parameter of activate function */
typedef struct
{
/*! \brief [in] Message queue id of renderer device0 request */
uint8_t dev0_req;
/*! \brief [in] Message queue id of renderer device0 for syncronizing */
uint8_t dev0_sync;
/*! \brief [in] Message queue id of renderer device1 request */
uint8_t dev1_req;
/*! \brief [in] Message queue id of renderer device1 for syncronizing */
uint8_t dev1_sync;
} AsRendererMsgQueId_t;
typedef struct
{
/*! \brief ID for sending messages to each channels */
AsRendererMsgQueId_t msgq_id;
} AsCreateRendererParam_t;
AS_CreateRenderer は、起動直後か、AS_DeleteRenderer 呼出し後にのみ呼び出し可能です。
|
5.3.3.4. インスタンスの削除
High Level API
でのAudio動作が終了し、Audioで使用した様々なリソースの開放やHWの停止および電源Offなどを行うためには、 delete
関数を呼び出す必要があります。
この delete
関数もすべてのレイヤーでのリソースを確保したすべての機能ブロックのインスタンスに対して呼び出す必要があります。以下に各レイヤごとの delete
関数のリストを示します。
5.3.3.4.1. Audio Manager の削除
High Level API
を使用を終了する場合、Audio Manager
のインスタンス削除とリソース割り当ての解除を行う必要があります。以下の関数を呼ぶことで、これらが実行できます。
-
引数
なし
AS_DeleteAudioManager は、各種リソースや
HWの使用状況によって正しくリース開放やHWの電源Offができなくなってしまうことを防ぐため、Ready状態でのみ呼び出しが可能となります。
|
5.3.3.4.2. Object Layer の削除
High Level API
を使用する場合でも、 Object Layer
のリソースの生成・削除が必要になります。Audio機能の終了時に Object Layer
のリソースを解放したい場合は、以下の関数を呼ぶ必要があります。
-
関数名
-
引数
なし
AS_DeleteMicFrontend は、各種リソースや
HWの使用状況によって正しくリース開放やHWの電源Offができなくなってしまうことを防ぐため、High Level API の場合は、Ready状態でのみ呼び出し可能であり、Object Level API の場合は、すべての系統での音声取得動作を停止している時のみ呼び出しが可能になります。
|
-
関数名
-
引数
なし
AS_DeleteMediaRecorder は、各種リソースや
HWの使用状況によって正しくリース開放やHWの電源Offができなくなってしまうことを防ぐため、High Level API の場合は、Ready状態でのみ呼び出し可能であり、Object Level API の場合は、音声録音動作を停止している時のみ呼び出しが可能になります。
|
-
関数名
-
引数
なし
AS_DeleteOutputMix は、各種リソースや
HWの使用状況によって正しくリース開放やHWの電源Offができなくなってしまうことを防ぐため、High Level API の場合は、Ready状態でのみ呼び出し可能であり、Object Level API の場合は、すべての系統での音声出力動作を停止している時のみ呼び出しが可能になります。
|
-
関数名
-
引数
なし
AS_DeletePlayer は、各種リソースや
HWの使用状況によって正しくリース開放やHWの電源Offができなくなってしまうことを防ぐため、High Level API の場合は、Ready状態でのみ呼び出し可能であり、Object Level API の場合は、すべての系統での音声再生動作を停止している時のみ呼び出しが可能になります。
|
-
関数名
-
引数
なし
AS_DeleteRecognizer は、各種リソースや
HWの使用状況によって正しくリース開放やHWの電源Offができなくなってしまうことを防ぐため、High Level API の場合は、Ready状態でのみ呼び出し可能であり、Object Level API の場合は、音声認識動作を停止している時のみ呼び出しが可能になります。
|
5.3.3.4.3. Component Layer の削除
High Level API
および Object Level API
を使用する場合でも、 Component Layer
で使用する各Componentの生成および削除が必要になります。Audio機能の終了時に Component Layer
のリソースを解放したい場合は、以下の関数を呼ぶ必要があります。
-
関数名
-
引数
なし
AS_DeleteCapture は、各種リソースや
HWの使用状況によって正しくリース開放やHWの電源Offができなくなってしまうことを防ぐため、High Level API の場合は、Ready状態でのみ呼び出し可能であり、Object Level API の場合は、 Mic Frontend Object がすべての系統での音声取得動作を停止している時のみ呼び出しが可能になります。
|
5.3.3.5. コマンド一覧
各コマンドの一覧は以下になります。
コマンド・ヘッダ にこのコマンドIDを指定し送信することで機能を使用することが出来ます。
5.3.3.5.1. General or Common Command
どの状態でも共通のコマンドです。 どの状態からも呼べます。
Command Name | Command ID | Response Result | Description |
---|---|---|---|
0x01 |
NotifyStatus |
現在の状態を取得します |
詳細は、以下の、Doxygenファイルを参照してください。
5.3.3.5.2. Baseband Initialize Command
Baseband HWの初期化を行うコマンドです。 Ready状態からのみ呼べます。
Command Name | Command ID | Response Result | Description |
---|---|---|---|
0x53 |
InitMicGainCmplt |
マイクゲインの設定を行います |
|
0x54 |
InitI2SCmplt |
I2Sの設定を行います |
|
0x56 |
InitOutputSelectCmplt |
発音するデバイスの設定を行います |
|
0x58 |
InitClearStereoCmplt |
クリアステレオ機能の設定を行います |
|
0x5c |
SetRenderingClkCmplt |
HiReso設定の切り替えを行います |
|
0x5d |
SetSpDrvCmplt |
スピーカーのドライブ能力の設定を行います |
詳細は、以下の、Doxygenファイルを参照してください。
5.3.3.5.3. Baseband Set Command
Baseband HWの設定を行うコマンドです。 PowerOff状態以外の状態から呼べます。
Command Name | Command ID | Response Result | Description |
---|---|---|---|
0x59 |
SetVolumeCmplt |
発音時のボリュームの設定を行います |
|
0x5a |
SetVolumeMuteCmplt |
発音ボリュームのMute設定を行います |
|
0x5b |
SetBeepCmplt |
BEEP音の設定を行います |
|
0x5e |
SetMicMapCmplt |
使用するマイクの選択・順番を設定します |
詳細は、以下の、Doxygenファイルを参照してください。
5.3.3.5.4. Player Command
Playerの制御を行うコマンドです。 Player状態から呼べます。
Command Name | Command ID | Response Result | Description |
---|---|---|---|
0x21 |
InitPlayCmplt |
Playerの再生情報を設定します |
|
0x22 |
PlayCmplt |
バッファの先頭からデコードを行います |
|
0x23 |
StopPlayCmplt |
バッファの状態に依らずPlayerを停止させます |
|
0x24 |
ClkRecoveryComplete |
出音時間の微調整をします |
|
0x25 |
SetDecoderGainComplete |
出音レベルにL/RそれぞれGainをかけます |
詳細は、以下の、Doxygenファイルを参照してください。
5.3.3.5.5. Recorder Command
Recorderの制御を行うコマンドです。 Recorder状態から呼べます。
Command Name | Command ID | Response Result | Description |
---|---|---|---|
0x31 |
InitRecCmplt |
音声記録機能を初期化します |
|
0x32 |
RecCmplt |
音声記録を開始します |
|
0x33 |
StopRecCmplt |
音声記録を停止します |
詳細は、以下の、Doxygenファイルを参照してください。
Command Name | Command ID | Response Result | Description |
---|---|---|---|
0x43 |
InitRecognizerCmplt |
認識機能を初期化します |
|
0x41 |
StartRecognizerCmplt |
認識機能を開始します |
|
0x42 |
StopRecognizerCmplt |
認識機能を停止します |
詳細は、以下の、Doxygenファイルを参照してください。
5.3.3.5.6. State Transition Command
Command Name | Command ID | Response Result | Description |
---|---|---|---|
0x71 |
StatusChanged |
Ready状態へ遷移します |
|
0x72 |
StatusChanged |
Power Off 状態へ遷移します |
|
0x73 |
StatusChanged |
Baseband状態へ遷移します |
|
0x74 |
StatusChanged |
Player状態へ遷移します |
|
0x75 |
StatusChanged |
Recorder状態へ遷移します |
|
0x76 |
StatusChanged |
Ready状態へ遷移します |
|
0x77 |
StatusChanged |
Audio path through状態へ遷移します |
|
0x79 |
StatusChanged |
Recognizer状態へ遷移します |
詳細は、以下の、Doxygenファイルを参照してください。
5.3.3.6. リザルト一覧
オーディオサブシステムからのリザルト通知です。
リザルト・ヘッダ にこのリザルトIDを格納して応答されます。
5.3.3.6.1. General or Common Result
Result Name | Command ID | Trigger Command | Description |
---|---|---|---|
0x01 |
GetStatus |
現在の状態を通知します。 |
5.3.3.6.2. Baseband Initialize Result
Result Name | Command ID | Trigger Command | Description |
---|---|---|---|
0x53 |
InitMicGain |
マイクゲインの設定が完了したことを通知します。 |
|
0x54 |
InitI2SParam |
I2Sの設定が完了したことを通知します。 |
|
0x56 |
InitOutputSelect |
発音するデバイスの設定が完了したことを通知します。 |
|
0x58 |
InitClearStereo |
クリアステレオ機能の設定が完了したことを通知します。 |
|
0x5c |
InitRenderClk |
HiReso設定の切り替えが完了したことを通知します。 |
|
0x5d |
SetSpDrv |
スピーカーのドライブ能力の切り替えが完了したことを通知します。 |
5.3.3.6.3. Baseband Set Result
Result Name | Command ID | Trigger Command | Description |
---|---|---|---|
0x59 |
SetVolume |
発音時のボリュームの設定が完了したことを通知します。 |
|
0x5a |
SetVolumeMute |
発音ボリュームのMute設定が完了したことを通知します。 |
|
0x5b |
SetBeep |
BEEP音の設定が完了したことを通知します。 |
|
0x5e |
SetMicMap |
使用するマイクの選択・順番の設定が完了したことを通知します。 |
5.3.3.6.4. Player Result
Result Name | Command ID | Trigger Command | Description |
---|---|---|---|
0x21 |
InitPlayer |
Playerの再生情報設定が完了したことを通知します。 |
|
0x22 |
StartPlayer |
Playerが動作を開始したことを通知します。 |
|
0x23 |
StopPlayer |
Playerが動作を停止したことを通知します。 |
|
0x24 |
ClkRecovery |
出音時間の微調整設定が完了したことを通知します。 |
|
0x25 |
SetDecoderGain |
出音レベルL/R Gain設定が完了したことを通知します。 |
5.3.3.6.5. Recorder Result
Result Name | Command ID | Trigger Command | Description |
---|---|---|---|
0x31 |
nitRecorder |
音声記録機能の初期化が完了したことを通知します。 |
|
0x32 |
StartRecorder |
音声記録が開始したことを通知します。 |
|
0x33 |
StopRecorder |
音声記録が停止したことを通知します。 |
5.3.3.6.6. Recognizer Result
Result Name | Command ID | Trigger Command | Description |
---|---|---|---|
0x43 |
nitRecognizer |
認識処理の初期化が完了したことを通知します。 |
|
0x41 |
StartRecognizer |
認識処理が開始したことを通知します。 |
|
0x42 |
StopRecognizer |
認識処理が停止したことを通知します。 |
5.3.3.6.7. State Transition Result
Result Name | Command ID | Trigger Command | Description |
---|---|---|---|
0x71 |
PowerOn SetPowerOffStatus SetBaseBandStatus SetPlayerStatus SetRecorderStatus SetRecognizerStatus SetReadyStartus |
状態遷移が完了したことを通知します。 |
5.3.3.7. コマンドパケット詳細
5.3.3.7.11. SetMicMap
MicMapの設定は、MIC channel select map の設定 を参照してください。 |
5.3.3.9. メモリ管理とタスク間同期について
5.3.3.9.1. Memory Manager Library
AudioSubSystemは、使用するデータ領域を特殊な管理方法で管理します。 MemoryUtilityのMemoryManagerと呼ばれるライブラリは、Memory Layout定義ファイルのLayout情報に従い、必要なメモリエリアを固定長のメモリプールとして確保します。 このLayout情報は複数定義することができ、Layout番号を指定して切り替えることで、機能ごとの必要メモリを確保することができます。 Layout情報は、Applicationのニーズに従って自由に決めてもらうものになりますが、各機能で最低限必要なリソースはありますので、ご注意ください。
詳細は、Memory Manager のライブラリ説明をご参照ください。
また、各機能に合わせた必要なLayout情報に関しては、各exampleの説明をご参照下さい。
AudioSubSystem内の、各オブジェクトは、必要なメモリエリアのセグメントを指し示すMemHandleのインスタンスを生成することで、それに紐付くメモリセグメントを確保し、そこを利用します。
データパイプラインの次に位置するオブジェクトに、このMemHandleのインスタンスを渡していくことで、確保されたエリアを次のオブジェクトが使用ることができ、不要になった場合、このインスタンスを破棄することで、メモリ領域が解放されます。
インスタンスはコピーでき、必要なオブジェクトが各自メモリが必要な間インスタンスを確保し、不要になったら各自のタイミングで破棄したとしても、安全にメモリを確保・解放することができます。
セグメントの使用が不要になった際、非同期に実行されるオブジェクトは、メモリ管理を意識することなく、インスタンスを破棄することで、暗黙の裡に参照が外れます。
これにより、非同期オブジェクト間でのメモリ管理を簡便に行っています。 すべての参照がなくなったら、メモリを開放します。
Layout情報は、Applicationが使うためのヘッダファイル群として、あらかじめ用意する必要があります。 これらのヘッダファイルは、Memory Layout定義ファイル(mem_layout.conf)を作成し、ツールを使うことで、生成されます。
- Usage
python3 mem_layout.conf [layout_header] [fence_header] [pool_header]
mem_layout.conf |
Memory Layout定義ファイル |
layout_header |
各種定数値が、C言語のマクロとして出力されるヘッダファイル。この引数を省略した場合、"mem_layout.h"という名前のファイルを生成します。 |
fence_header |
FixedAreaのメモリフェンスアドレスが出力されるヘッダファイル。この引数を省略した場合、"fixed_fence.h"という名前のファイルを生成します。 |
pool_header |
PoolAreaの各種定義が出力されるヘッダファイル。この引数を省略した場合、"pool_layout.h"という名前のファイルを生成します。 |
5.3.3.9.2. Message Library
このメモリ管理機構を使用するうえで、各タスク間でのクラスオブジェクトの送受信が必要になります。これを実現するため、タスク同期機構にクラスインスタンスの送受信を可能にしたmessageライブラリを用意しており、AudioSubSystemは、これを利用しています。
各タスクにあるオブジェクトに送受信先のIDを付加していくことで、送受信したいタスクへの送信が可能になります。
例えば、送信する場合は、送信先のIDにオブジェクトを送信し、受信側のタスクは、自分のIDの送信要求が発生したときのみ、受信されることになります。 受信するまでは、そのタスクはsleepして待ちます。
これにより、AudioSubSystemは、イベント駆動でのオブジェクト設計を行っています。
Messageに関しては、Messageを使うためのヘッダファイル群をあらかじめ用意する必要があります。 これらのヘッダファイルは、MessageQueueLayout定義ファイル(msgq_layout.conf)を作成し、ツールを使うことで、生成されます。
- Usage
python3 msgq_layout.conf [start_address] [size] [id_header] [pool_header]
msgq_layout.conf |
Message Layout定義ファイル |
start_address |
Message領域のアドレス。この引数を省略した場合、"mem_layout.h"から必要なアドレスを自動的に読み込みます。 |
size |
Message領域のサイズ。この引数を省略した場合、"mem_layout.h"から必要なアドレスを自動的に読み込みます。 |
id_header |
MessageQueueIDマクロが出力されるファイル。この引数を省略した場合、"msgq_id.h"という名前のファイルを生成します。 |
pool_header |
MessageQueuePoolの定義が出力されるファイル。この引数を省略した場合、"msgq_pool.h"という名前のファイルを生成します。 |
詳細は、Message Library のライブラリ説明をご参照ください。
5.3.3.9.3. Simple FIFO Library
AudioSubSystem とユーザアプリケーションとの間でのオーディオデータの受け渡しを行う場合、Simple FIFOを用います。このFIFOは、単純なFIFOであり、特に特筆すべきものはありません。
詳細は、Simple FIFO のライブラリ説明をご参照ください。
5.3.3.11. Audio Player Functions
Audio Player の簡単なデータの流れを以下に示します。
オーディオ・サブシステムがPlayerModeで動作する場合、User Applicationは、FIFOにESデータを入力します。 一定以上たまった状態で、Playerを起動すると、発音時間に合わせて、このESデータを消費していきます。このFIFOがUnderflowしない限り、音声データは途切れることなく発音します。
Playerは、2つのインスタンスを生成することが可能です。それぞれでデコードした音声は、OutputMixerで、Mixingして発音します。
データフロー内部は、Messageで通信します。 Message通信は、各クライアントごとにIDを持ちます。 Audio Playerの場合、exampleにあるサンプルLayoutをもとに、IDを示すと以下のようになります。
User Application : MSGQ_AUD_APP
Audio Manager : MSGQ_AUD_MNG
Audio Player0 : MSGQ_AUD_PLY0
Audio Player1 : MSGQ_AUD_PLY1 # Set this value when you use player1, in other case, set 0xff.
Output Mixer : MSGQ_AUD_OUTPUT_MIX
Audio DSP : MSGQ_AUD_DSP
Rendering Component(Audio Player0) : MSGQ_AUD_RND_PLY0
Rendering Component(Audio Player1) : MSGQ_AUD_RND_PLY1 # Set this value when you use player1, in other case, set 0xff.
Rendering Component Sync(Audio Player0) : MSGQ_AUD_RND_PLY0_SYNC
Rendering Component Sync(Audio Player1) : MSGQ_AUD_RND_PLY1_SYNC # Set this value when you use player1, in other case, set 0xff.
Post Filter (Channel0) : MSGQ_AUD_PFDSP0
Post Filter (Channel1) : MSGQ_AUD_PFDSP1
将来的には、MSGQ_AUD_RND_PLY0/PLY1_SYNCが削除され、APIが変更される可能性があります。 |
また、各データのデータ領域は、以下になります。
ES Data (Audio Player0) : S0_DEC_ES_MAIN_BUF_POOL
ES Data (Audio Player1) : S0_DEC_ES_SUB_BUF_POOL # Set this value when you use player1, in other case, set S0_NULL_POOL.
PCM Data (Audio Player0) : S0_REND_PCM_BUF_POOL
PCM Data (Audio Player1) : S0_REND_PCM_SUB_BUF_POOL # Set this value when you use player1, in other case, set S0_NULL_POOL.
Audio Decoder DSP Command : S0_DEC_APU_CMD_POOL
SamplingRateConverter Work Buffer (Audio Player0) : S0_SRC_WORK_BUF_POOL
SamplingRateConverter Work Buffer (Audio Player1) : S0_SRC_WORK_SUB_BUF_POOL # Set this value when you use player1, in other case, set S0_NULL_POOL.
Post Filter PCM Data (Channel0) : S0_PF0_PCM_BUF_POOL
Post Filter PCM Data (Channel1) : S0_PF1_PCM_BUF_POOL
Post Filter DSP Command (Channel0) : S0_PF0_APU_CMD_POOL
Post Filter DSP Command (Channel1) : S0_PF1_APU_CMD_POOL
これらのIDを生成時に指定する必要があります。
5.3.3.11.1. 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が読みだす度に通知します。
下記は、Player0, Player1とも再生するようにした場合の設定例です。 Player0, Player1はそれぞれ別のSimpleFIFOを使ってデータを投入する設定です。
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, AsCreatePlayerParams_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と同じです。
下記は、Player0を0dB, Player1を0dBにしMasterボリュームは-20dBとした設定例です。
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文字です。
下記は、Player0にmp3/16bit/Stereo/48kHzのコンテンツを再生するように初期化した設定例です。 また、再生に使用するデコーダの配置パスはSDカードのBINフォルダを指定しています。
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_INIT_OUTPUTMIXER, AsInitMixerParam で音声出力の初期設定を行います。
AsPlayerIdのインスタンスのIDを設定します。インスタンスは2つあり、どちらかを設定して下さい。
PostProcessの種別を設定します。
AsPostprocTypeThrough : Through,
AsPostprocTypeUserCustom : User Custom Process,
PostProcess用DSPバイナリをファイル名を含むフルパスで指定します。
postproc_type が AsPostprocTypeThrough である場合には使われません。
"/mnt/sd0/BIN/POSTPROC" : POSTPROC というバイナリをSDカードのBINフォルダに置く場合
"/mnt/spif/POSTPROC" : POSTPROC というバイナリをSPI-FLASHの直下に置く場合
PostprocessDSPを使用した信号処理がAudio Player Fuction内でどこに位置するかを下図に示します。
ハイライトされた箇所にあるCustomproc, UserCustomDSPで信号処理をおないます。
ユーザーはこのUserCustomDSPに信号処理を作成、組み込みます。
下記は、出力するPlayer0の音声に対して /mnt/sd0/BIN/POSTPROC
のユーザーカスタムDSPで信号処理を行う設定の例です。
AudioCommand command;
command.header.packet_length = LENGTH_INIT_OUTPUTMIXER;
command.header.command_code = AUDCMD_INIT_OUTPUTMIXER;
command.header.sub_code = 0x00;
command.init_mixer_param.player_id = AS_PLAYER_0;
command.init_mixer_param.postproc_type = AsPostprocTypeUserCustom;
snprintf(command.init_mixer_param.dsp_path,
AS_POSTPROC_FILE_PATH_LEN,
"%s", "/mnt/sd0/BIN/POSTPROC");
AS_SendAudioCommand(&command);
AUDCMD_INITMPP, AsInitMediaPlayerPost でPost処理用DSPの初期化をします。
AUDCMD_INIT_OUTPUTMIXER で post_enable を AsPostprocTypeThrough にしている場合はこの手順は不要です。
|
AsPlayerIdのインスタンスのIDを設定します。インスタンスは2つあり、どちらかを設定して下さい。
初期化コマンドパケットのアドレスです。コマンドのフォーマットはPost処理用DSPに依存します。
設定したアドレス領域は、このAPIの応答まで保持しておく必要があります。
初期化コマンドパケットのサイズです。
下記は、 initpostcmd
データをPostprocessDSPの初期化コマンドとして送信する例です。
InitParam initpostcmd;
AudioCommand command;
command.header.packet_length = LENGTH_INITMPP;
command.header.command_code = AUDCMD_INITMPP;
command.init_mpp_param.player_id = AS_PLAYER_ID_0;
command.init_mpp_param.initpp_param.addr = reinterpret_cast<uint8_t *>(&initpostcmd);
command.init_mpp_param.initpp_param.size = sizeof(initpostcmd);
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 |
下記は、Player0を再生開始する設定例です。
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 |
下記は、Player0を通常停止する設定例です。
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);
5.3.3.11.2. Build Configurations
AudioPlayer の機能を使用するためには
cd sdk tools/config.py -m
でConfig menu を開き、以下のConfigを設定する必要があります。
Select options in below:
[Device Drivers] [MMCSD driver support] <= Y (If using the SD card) [Board specific drivers] [CXD56 Audio Driver] <= Y [Application Configuration] [Spresense SDK] [SDK audio] <= Y [Audio Utilities] [Audio Player] <= Y [Playlist manager] <= Y (If use PlayList) [Memory Manager] <= Y [Memory Utilities] <= Y [ASMP] <= Y
5.3.3.11.3. 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のクリアを必ず行ってください。行わないとノイズが発生してしまいます。 |
5.3.3.11.4. DSP install
- DSP binary image install
-
DSPバイナリイメージをKconfigで設定したパスに格納して下さい。バイナリイメージは、
sdk/modules/audio/dsp
にあります。表 18. Binary image required for audio player according to configuration: Image 使用メモリ バイナリサイズ MP3DEC
128kbyte
61kbyte
WAVDEC
256kbyte
32kbyte
実行時に必要なメモリサイズは、使用メモリ のサイズになります。
|
ハイレゾリューションサンプリングレートの再生を行う場合、DSPを2 Core(1Coreあたり192kB)使用します。 ※2Coreの場合、384kB。 使用するリソースにご注意ください。 |
5.3.3.11.5. 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 と他の複数のサンプルは同時に選択できません。複数選択するとコンパイルエラーが出ます。 |
メモリ管理ライブラリ(Memory Manager)とタスク間通信ライブラリ(Message Library)の設定は、以下のように行ってください。
AudioPlayer機能を使用する際に必要となるMemoryLayout(pool)の定義を行う必要があります。
定義はMemoaryLayout定義ファイルで行い、ツールでコードに組み込むヘッダファイルを生成することが出来ます。
Audio Player のexampleでは下記のように行います。
cd examples/audio_player/config python3 mem_layout.conf
"mem_layout.h", "fixed_fence.h", "pool_layout.h" ファイルが生成されます。
"mem_layout.h"ファイルは、後述する msgq_layout ツールの実行時に参照されます。
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()で確保するシェアメモリのサイズを超えないようにしてください。
FixedAreasは変更しないでください。 |
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を参照してください。 設定が変わった場合は、ツールを使って新しいヘッダーファイルを生成してください。 |
AudioPlayer機能を使用する際に必要となるMessageQueueの定義を行う必要があります。 定義はMessageQueueLayout定義ファイルで行い、ツールでコードに組み込むヘッダファイルを生成することが出来ます。
Audio Player のexampleでは下記のように行います。
前述した"mem_layout.h"ファイルが存在している状態で実行してください。
cd examples/audio_player/config python3 msgq_layout.conf
"msgq_id.h", "msgq_pool.h" ファイルが生成されます。
mem_layout.conf で生成されたファイルを含めて全てのヘッダファイルを include 以下にコピーします。
cd examples/audio_player/config mv *.h ../include
MessageQueueLayout定義ファイル(msgq_layout.conf)の記述内容は下記の通りです。
MsgQuePool
# ID, n_size n_num h_size h_nums
["MSGQ_AUD_MNG", 88, 30, 0, 0],
["MSGQ_AUD_APP", 64, 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_PLY0", 48, 5, 0, 0],
["MSGQ_AUD_PLY1", 48, 5, 0, 0],
["MSGQ_AUD_OUTPUT_MIX", 48, 8, 0, 0],
["MSGQ_AUD_RND_PLY0", 32, 16, 0, 0],
["MSGQ_AUD_RND_PLY0_SYNC", 16, 8, 0, 0],
["MSGQ_AUD_RND_PLY1", 32, 16, 0, 0],
["MSGQ_AUD_RND_PLY1_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を参照してください。 設定が変わった場合は、ツールを使って新しいヘッダーファイルを生成してください。 |
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
5.3.3.12. 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にあるサンプルLayoutをもとに、IDを示すと以下のようになります。
User Application : MSGQ_AUD_APP
Audio Manager : MSGQ_AUD_MNG
Audio Frontend : MSGQ_AUD_FRONTEND
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
PreProcess DSP Command : PRE_APU_CMD_POOL
Audio Encoder DSP Command : ENC_APU_CMD_POOL
これらのIDを生成時に指定する必要があります。
5.3.3.12.1. How to use
"AudioManager", "MicFrontendObject", "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
を指定してください。
下記は、1ch〜4chの入力に21dBのゲインをかける場合の設定例です。 5ch〜8chの原因は変更しない設定です。
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の情報を記載します。
[Device Drivers] [Board specific drivers] [CXD56 Audio Driver] [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デジタルマイク
現時点では0固定です。
エンコードしたESデータの出力先デバイスを指定します。
現時点ではRAMデバイスの出力のみサポートされます。
AS_SETRECDR_STS_OUTPUTDEVICE_RAM : RAMデバイスへ出力
出力(Encoded ES data)の格納先SimpleFIFOのハンドラを指定します。
simple_fifo_handlerは、CMN_SimpleFifoInitialize()で取得されます。
下記は、マイク入力をSimpleFIFOに記録する場合の設定例です。
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_INIT_MICFRONTEND, MicFrontendCommand, AsInitMicFrontEndでフロントエンド動作(音声の取り込みなど)の設定をします。
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
/* 1フレームのサンプル数を指定します。 */
MicFrontendから出力する音声のサンプリングレートを指定します。
preproc_type
が AsMicFrontendPreProcSrc
の時のみ有効です。
AS_SAMPLINGRATE_8000 : 8kHz
AS_SAMPLINGRATE_16000 : 16kHz
AS_SAMPLINGRATE_44100 : 44.1kHz
AS_SAMPLINGRATE_48000 : 48kHz
...
...
AS_SAMPLINGRATE_192000 : 192kHz
PreProcessの種別を設定します。
AsMicFrontendPreProcThrough : Through
AsMicFrontendPreProcSrc : Sampling Rate Converter
AsMicFrontendPreProcUserCustom : User Custom Process
AsMicFrontendPreProcSrc を指定する場合は sdk/modules/audio/DSP/ にある SRC が必要です。
|
PreProcess用DSPバイナリをファイル名を含むフルパスで指定します。
preproc_type が AsMicFrontendPreProcThrough である場合には使われません。
"/mnt/sd0/BIN/PREPROC" : PREPROC というバイナリをSDカードのBINフォルダに置く場合 "/mnt/spif/SRC" : SRC というバイナリをSPI-FLASHの直下に置く場合
MicFrontendObjectからのAudioデータの出力先を指定します。
AsMicFrontendDataToRecorder : Send to Recorder
AsMicFrontendDataToRecognizer : Send to Recognizer
PreprocessDSPを使用した信号処理がAudio Recorder Function内でどこに位置するかを下図に示します。
ハイライトされた箇所にあるCustomproc, UserCustomDSPで信号処理を行います。
ユーザーはこのUserCustomDSPに信号処理を作成、組み込みます。
下記は、Mono/16bit/768sample per frameで音声をキャプチャする場合の設定例です。
Pre処理はUserCustomのDSPで行い、そのDSP用バイナリファイルはSDカードのBINフォルダに配置します。
また、キャプチャした音声はRecorderに使う設定としています。
AudioCommand command;
command.header.packet_length = LENGTH_INIT_MICFRONTEND;
command.header.command_code = AUDCMD_INIT_MICFRONTEND;
command.header.sub_code = 0x00;
command.init_micfrontend_param.ch_num = AS_CHANNEL_MONO
command.init_micfrontend_param.bit_length = AS_BITLENGTH_16;
command.init_micfrontend_param.sample = 768; /* この値はCodecにあわせて設定して下さい。 */
command.init_micfrontend_param.outfs = AS_SAMPLINGRATE_16000;
command.init_micfrontend_param.preproc_type = AsMicFrontendPreProcUserCustom;
snprintf(command.init_micfrontend_param.preprocess_dsp_path,
AS_PREPROCESS_FILE_PATH_LEN,
"%s", "/mnt/sd0/BIN/PREPROC");
command.init_micfrontend_param.data_dest = AsMicFrontendDataToRecorder;
AUDCMD_INIT_PREPROCESS_DSP, AsInitRecorderParamでPre処理用DSPの初期化をします。
AUDCMD_INIT_MICFRONTENDで preproc_type を AsMicFrontendPreProcThrough にしている場合はこの手順は不要です。
|
初期化コマンドパケットのアドレスです。コマンドのフォーマットはPre処理用DSPに依存します。
設定したアドレス領域は、このAPIの応答まで保持しておく必要があります。
初期化コマンドパケットのサイズです。
下記は、s_initparamのデータをDSPの初期化コマンドとして送信する例です。
static uint8_t s_initparam = 0;
AudioCommand command;
command.header.packet_length = LENGTH_INIT_PREPROCESS_DSP;
command.header.command_code = AUDCMD_INIT_PREPROCESS_DSP;
command.header.sub_code = 0x00;
command.init_preproc_param.packet_addr = reinterpret_cast<uint8_t *>(&s_initparam);
command.init_preproc_param.packet_size = sizeof(s_initparam);
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 : LinearPCM
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
EncoderまたはFilterのDSPイメージを格納している絶対パスを指定します。最大24文字です。
入力デバイスとch数の組み合わせは制限が有ります。 |
入力 | ch数 |
---|---|
Mic |
1ch(Monoral), 2ch(Stereo), 4ch(*1), 6ch(*2), 8ch(*2) |
-
(*1. LPCMのみ)
-
(*2. LPCMかつDigitalMic使用時のみ)
Codec, bit長, サンプリング周波数, ビットレートの組み合わせには制限が有ります。 |
Codec | bit長 | サンプリング周波数 | ビットレート |
---|---|---|---|
MP3 |
16bit |
16kHz |
8000(*1), 16000 ~ 32000 |
48kHz |
32000 ~ 2560000 |
||
LPCM |
16bit |
16kHz, 48kHz |
- |
24bit(*2) |
16kHz, 48kHz, 192kHz(*2) |
- |
-
(*1. 1ch指定時)
-
(*2. 要 HiResoモード指定)
下記は、16kHz/Mono/LPCMで記録を行う際の設定例です。 エンコードに使用するDSP用のバイナリは、SDカードのBINフォルダに配置します。
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 = AS_SAMPLINGRATE_16000;
command.recorder.init_param.channel_number = AS_CHANNEL_MONO;
command.recorder.init_param.bit_length = AS_BITLENGTH_16;
command.recorder.init_param.codec_type = AS_CODECTYPE_LPCM;
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)
5.3.3.12.2. Build Configurations
AudioRecorderの機能を使用するために
cd sdk/ tools/config.py -m
でConfig menuを開き、以下のConfigを設定する必要が有ります。
Select options in below:
:(Select audio recorder) [Device Drivers] [MMCSD driver support] <= Y (If using the SD card) [Board specific drivers] [CXD56 Audio Driver] <= Y [Application Configuration] [Spresense SDK] [SDK audio] <= Y [Audio Utilities] [Audio Recorder] <= Y [Memory Manager] <= Y [Memory Utilities] <= Y [ASMP] <= Y
5.3.3.12.3. 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"のファイルで更新して下さい。 |
5.3.3.12.4. DSP install
- DSP binary image install
-
DSPバイナリイメージをKconfigで設定したパスに格納して下さい。バイナリイメージは、
sdk/modules/audio/dsp
にあります。
Image | 使用メモリ | バイナリサイズ |
---|---|---|
MP3ENC |
256kbyte |
111kbyte |
SRC (Sampling Rate Converter) |
128kbyte |
21kbyte |
実行時に必要なメモリサイズは、使用メモリ のサイズになります。
|
LPCM は、圧縮処理を必要としていませんが、出力周波数によっては周波数変換処理が必要なため、SRC (Sampling Rate Converter) のDSPのloadが必要になります。 |
5.3.3.12.5. 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機能を使用する際に必要となるMemoryLayout(pool)の定義を行う必要があります。
定義はMemoaryLayout定義ファイルで行い、ツールでコードに組み込むヘッダファイルを生成することが出来ます。
Audio Recorder Example では下記のように行います。
cd examples/audio_recorder/config python3 mem_layout.conf
"mem_layout.h", "fixed_fence.h", "pool_layout.h" ファイルが生成されます。
"mem_layout.h"ファイルは、後述する msgq_layout ツールの実行時に参照されます。
MemoaryLayout定義ファイル(mem_layout.conf)の記述内容は下記の通りです。
FixedAreas
# name, device, align, size, fence
["AUDIO_WORK_AREA", "AUD_SRAM", U_STD_ALIGN, 0x0003d000, False],
["MSG_QUE_AREA", "AUD_SRAM", U_STD_ALIGN, 0x00002000, 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, 0x0000F000, 5, True ],
["PREPROC_BUF_POOL", "AUDIO_WORK_AREA", U_STD_ALIGN, 0x0000F000, 5, True ],
["INPUT_BUF_POOL", "AUDIO_WORK_AREA", U_STD_ALIGN, 0x0000F000, 5, True ],
["ENC_APU_CMD_POOL", "AUDIO_WORK_AREA", U_STD_ALIGN, 0x00000114, 3, True ],
["SRC_APU_CMD_POOL", "AUDIO_WORK_AREA", U_STD_ALIGN, 0x00000114, 3, True ],
["PRE_APU_CMD_POOL", "AUDIO_WORK_AREA", U_STD_ALIGN, 0x00000114, 3, True ],
各パラメータの説明は以下の通りです。
パラメータ | 説明 |
---|---|
name |
プール名(英大文字で始まり、"_POOL"で終わる名称。英大文字, 数字, _が使用可能) |
area |
プール領域として使用するFixedAreaの領域名。領域はRAMに配置されていること |
align |
プールの開始アライメント。0を除くMinAlign(=4)の倍数を指定する |
pool-size |
プールのサイズ。0を除く4の倍数の値。Basicプールでは、セグメントサイズ * セグメント数 |
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を参照してください。 設定が変わった場合は、ツールを使って新しいヘッダーファイルを生成してください。 |
AudioRecorder機能を使用する際に必要となるMessageQueueの定義を行う必要があります。 定義はMessageQueueLayout定義ファイルで行い、ツールでコードに組み込むヘッダファイルを生成することが出来ます。
Audio Recorder Example では下記のように行います。
前述した"mem_layout.h"ファイルが存在している状態で実行してください。
cd examples/audio_recorder/config python3 msgq_layout.conf
"msgq_id.h", "msgq_pool.h" ファイルが生成されます。
mem_layout.conf で生成されたファイルを含めて全てのヘッダファイルを include 以下にコピーします。
cd examples/audio_recorder/config mv *.h ../include
MessageQueueLayout定義ファイル(msgq_layout.conf)の記述内容は下記の通りです。
MsgQuePool
# ID, n_size n_num h_size h_num
["MSGQ_AUD_MGR", 88, 30, 0, 0],
["MSGQ_AUD_APP", 64, 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],
["MSGQ_AUD_FRONTEND", 48, 10, 0, 0],
["MSGQ_AUD_PREDSP", 20, 5, 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を参照してください。 設定が変わった場合は、ツールを使って新しいヘッダーファイルを生成してください。 |
5.3.3.13. Audio Recognizer Functions
Audio Recognizer の簡単なデータの流れを以下に示します。
Audio Recognizer Function は音声認識機能を実現するためのフレームワークを提供します。
認識動作を開始すると、音声データのキャプチャを開始し、Pre処理を行ったのち認識ライブラリへ音声データを投入します。
Pre処理では、音声認識ライブラリの要求する入力フォーマットに合わせて音声データに必要な処理(サンプリングレート変換やノイズ抑制など)を施します。
音声認識ライブラリにキャプチャした音声をそのまま入力出来るのであれば、Pre処理はスルー設定で構いません。
キャプチャしPre処理を施した音声データは音声認識ライブラリに投入されます。
結果が出力されるかどうかは、ライブラリ次第です。投入の度に認識の結果を通知できるようなフレームワークを持ちます。アプリケーションに合わせて使用してください。
+ Audio SubSystemがRecognizerModeで動作する場合、アプリケーションレイヤでは音声データ(PCMデータ)を意識することなく実装することが可能です。
データフロー内部は、Messageで通信します。
Message通信は、各クライアントごとにIDを持ちます。
Audio Recognizerの場合、exampleにあるサンプルLayoutをもとにIDを示すと以下のようになります。
User Application : MSGQ_AUD_APP
Audio Manager : MSGQ_AUD_MNG
Audio Frontend : MSGQ_AUD_FRONTEND
Audio Recognizer : MSGQ_AUD_RECOGNIZER
Audio Capture Component : MSGQ_AUD_CAP
Audio DSP : MSGQ_AUD_DSP
また、各データのデータ領域は以下になります。
PCM (Input) Data Buffer : INPUT_BUF_POOL
PreProcess Data Buffer : PREPROC_BUF_POOL
PreProcess DSP Command : PRE_APU_CMD_POOL
Recognizer DSP Command : RCG_APU_CMD_POOL
これらのIDを生成時に指定する必要があります。
5.3.3.13.1. How to use
"AudioManager", "MicFrontendObject", "RecognizerObject", "CaptureComponent" と呼ばれる オーディオ・サブシステムを制御するために設計されたソフトウェアコンポーネントで、Audio Recognizer を実現します。
必要なオブジェクトが生成されたら、Recognizer動作を行わせるために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'を指定してください。
下記は、1ch〜4chの入力に21dBのゲインをかける場合の設定例です。 5ch〜8chの原因は変更しない設定です。
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の情報を記載します。
[Device Drivers] [Board specific drivers] [CXD56 Audio Driver] [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_SETRECOGNIZERSTATUSでAudioSubSystemの状態をRecognizer状態に遷移します。
記録対象の入力デバイスを指定します。
マイクのみ指定が可能です。 |
AsMicFrontendDeviceMic : マイク
下記は、マイク入力を認識に使用する設定例です。
AudioCommand command;
command.header.packet_length = LENGTH_SET_RECOGNIZER_STATUS;
command.header.command_code = AUDCMD_SETRECOGNIZERSTATUS;
command.header.sub_code = 0x00;
command.set_recognizer_status_param.input_device = AsMicFrontendDeviceMic;
AS_SendAudioCommand(&command);
音声認識開始シーケンスを示します。
AUDCMD_INIT_MICFRONTEND, MicFrontendCommand, AsInitMicFrontEndでフロントエンド動作(音声の取り込みなど)の設定をします。
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
/* 1フレームのサンプル数を指定します。認識ライブラリの仕様に合わせて設定してください。 */
MicFrontendから出力する音声のサンプリングレートを指定します。
preproc_type
が AsMicFrontendPreProcSrc
の時のみ有効です。
AS_SAMPLINGRATE_8000 : 8kHz
AS_SAMPLINGRATE_16000 : 16kHz
AS_SAMPLINGRATE_44100 : 44.1kHz
AS_SAMPLINGRATE_48000 : 48kHz
...
...
AS_SAMPLINGRATE_192000 : 192kHz
PreProcessの種別を設定します。
AsMicFrontendPreProcThrough : Through
AsMicFrontendPreProcSrc : Sampling Rate Converter
AsMicFrontendPreProcUserCustom : User Custom Process
'AsMicFrontendPreProcSrc' を指定する場合は sdk/modules/audio/DSP/ にある SRC が必要です。
|
PreProcess用DSPバイナリをファイル名を含むフルパスで指定します。
"/mnt/sd0/BIN/PREPROC" : PREPROC というバイナリをSDカードのBINフォルダに置く場合 "/mnt/spif/SRC" : SRC というバイナリをSPI-FLASHの直下に置く場合
MicFrontendObjectからのAudioデータの出力先を指定します。
AsMicFrontendDataToRecorder : Send to Recorder
AsMicFrontendDataToRecognizer : Send to Recognizer
PreprocessDSPを使用した信号処理がAudio Recognizer Function内でどこに位置するかを下図に示します。
ハイライトされた箇所にあるCustomprocComp(Pre), UserCustomDSP(Pre)で信号処理を行います。
ユーザーはこのUserCustomDSPに信号処理を作成、組み込みます。
Preprocessでは認識ライブラリの入力フォーマットに合わせて、DSPを使用したユーザー独自の信号処理を行うことが出来ます。
例えば、認識ライブラリの入力がBasebandの入力(48kHz or 192kHz)と異なる場合の周波数変換や、ノイズ抑制用のフィルタを前処理として行います。
下記は、Mono/16bit/768sample per frameで音声をキャプチャする場合の設定例です。
Pre処理はUserCustomのDSPで行い、そのDSP用バイナリファイルはSDカードのBINフォルダに配置します。
また、キャプチャした音声はRecognizerに使う設定としています。
AudioCommand command;
command.header.packet_length = LENGTH_INIT_MICFRONTEND;
command.header.command_code = AUDCMD_INIT_MICFRONTEND;
command.header.sub_code = 0x00;
command.init_micfrontend_param.ch_num = AS_CHANNEL_MONO
command.init_micfrontend_param.bit_length = AS_BITLENGTH_16;
command.init_micfrontend_param.sample = 768; /* この値は認識ライブラリの仕様に合わせて設定してください。 */
command.init_micfrontend_param.outfs = AS_SAMPLINGRATE_16000;
command.init_micfrontend_param.preproc_type = AsMicFrontendPreProcUserCustom;
snprintf(command.init_micfrontend_param.preprocess_dsp_path,
AS_RECOGNIZER_FILE_PATH_LEN,
"%s", "/mnt/sd0/BIN/PREPROC");
command.init_micfrontend_param.data_dest = AsMicFrontendDataToRecognizer;
AUDCMD_INIT_PREPROCESS_DSP, AsInitPreProcParamでPre処理用DSPの初期化をします。
AUDCMD_INIT_MICFRONTENDで preproc_type を AsMicFrontendPreProcThrough にしている場合はこの手順は不要です。
|
初期化コマンドパケットのアドレスです。コマンドのフォーマットはPre処理用DSPに依存します。
設定したアドレス領域は、このAPIの応答まで保持しておく必要があります。
初期化コマンドパケットのサイズです。
static uint8_t s_initparam = 0;
AudioCommand command;
command.header.packet_length = LENGTH_INIT_PREPROCESS_DSP;
command.header.command_code = AUDCMD_INIT_PREPROCESS_DSP;
command.header.sub_code = 0x00;
command.init_preproc_param.packet_addr = reinterpret_cast<uint8_t *>(&s_initparam);
command.init_preproc_param.packet_size = sizeof(s_initparam);
AS_SendAudioCommand(&command);
AUDCMD_INIT_RECOGNIZER, RecognizerCommand, AsInitRecognizerParamで認識動作の設定をします。
認識結果をAudioSusSystemから受け取るためのコールバック関数を登録します。
認識結果はプレーンデータで通知され、その構成などについては認識ライブラリに依存します。
static void recognizer_find_callback(AsRecognitionInfo)
認識エンジンのタイプを設定します。
SDK v1.4.0時点では下記の設定値のみが指定可能です。 |
AsRecognizerTypeUserCustom
認識用DSPバイナリをファイル名を含むフルパスで指定します。
"/mnt/sd0/BIN/RCGPROC" : RCGPROCというバイナリをSDカードのBINフォルダに置く場合
ここで指定された認識用DSPバイナリは下図中でハイライトされた
CustomprocComp(Recogniton), UserCustomDSP(Recognition)に当たる箇所で認識処理を行います。
下記は、recognizer_find_callback()で認識結果を受け取る場合の設定例です。 認識用DSPはSDカードのBINディレクトリに配置します。
static void recognizer_find_callback(AsRecognitionInfo info)
{
...
}
AudioCommand command;
command.header.packet_length = LENGTH_INIT_RECOGNIZER;
command.header.command_code = AUDCMD_INIT_RECOGNIZER;
command.header.sub_code = 0x00;
command.init_recognizer.fcb = recognizer_find_callback;
command.init_recognizer.recognizer_type = AsRecognizerTypeUserCustom;
snprintf(command.init_recognizer.recognizer_dsp_path,
AS_RECOGNIZER_FILE_PATH_LEN,
"%s", "/mnt/sd0/BIN/RCGPROC");
AUDCMD_INIT_RECOGNIZER_DSP, AsInitRecognizerProcParamで認識処理用DSPの初期化をします。
初期化コマンドパケットのアドレスです。コマンドのフォーマットは認識処理用DSPに依存します。
設定したアドレス領域は、このAPIの応答まで保持しておく必要があります。
初期化コマンドパケットのサイズです。
static uint8_t s_initparam = 0;
AudioCommand command;
command.header.packet_length = LENGTH_INIT_RECOGNIZER_DSP;
command.header.command_code = AUDCMD_INIT_RECOGNIZER_DSP;
command.header.sub_code = 0x00;
command.init_rcg_param.packet_addr = reinterpret_cast<uint8_t *>(&s_initparam);
command.init_rcg_param.packet_size = sizeof(s_initparam);
AS_SendAudioCommand(&command);
音声認識機能の初期化までのシーケンスを下図に示します。
AUDCMD_START_RECOGNIZERで認識動作を開始します。
開始するとAudioSubSystemが音声データをキャプチャし前処理を施したのち認識ライブラリに投入します。
認識結果はInit Recognizerで設定したコールバック関数で通知されます。
AudioCommand command;
command.header.packet_length = LENGTH_START_RECOGNIZER;
command.header.command_code = AUDCMD_START_RECOGNIZER;
command.header.sub_code = 0x00;
AS_SendAudioCommand(&command)
AUDCMD_STOP_RECOGNIZERで認識動作を停止します。
停止するとAudioSubSystemは音声データキャプチャを停止し、認識ライブラリへの投入も止まります。
AudioCommand command;
command.header.packet_length = LENGTH_STOP_RECOGNIZER;
command.header.command_code = AUDCMD_STOP_RECOGNIZER;
command.header.sub_code = 0x00;
AS_SendAudioCommand(&command)
5.3.3.13.2. Build Configurations
AudioRecognizerの機能を使用するために
cd sdk/ tools/config.py -m
でConfig menuを開き、以下のConfigを設定する必要が有ります。
Select options in below:
:(Select audio recognizer)
[Device Drivers]
[MMCSD driver support] <= Y (If using the SD card)
[Board specific drivers]
[CXD56 Audio Driver] <= Y
[Application Configuration]
[Spresense SDK]
[SDK audio] <= Y
[Audio Utilities]
[Sound Recognizer] <= Y
[Mic Front End] <= Y
[Memory Manager] <= Y
[Memory Utilities] <= Y
[ASMP] <= Y
5.3.3.13.3. Error Attentions and Approach
音声認識時の警告の一覧と、対処方法は以下の通りです。詳細は オーディオサブシステムのエラーについて を参照してください。
ID | Attention Code | Attention Level | Approach |
---|---|---|---|
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"のファイルで更新して下さい。 |
5.3.3.14. 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を生成時に指定する必要があります。
5.3.3.14.1. 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, |
5.3.3.14.2. 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);
Recorder Init Mic Gain を参照してください。
使用できるマイクは、CXD5247アナログマイク1, CXD5247アナログマイク2の組み合わせか、
CXD5247デジタルマイク1,CXD5247デジタルマイク2の組み合わせのどちらかです。 使用するマイクを変更したい場合は、Recorder Init Mic Gain と同様、Layoutを変更することで、可能です。 |
データフローのパスを設定することで、データの入出力を開始します。
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を出力先にします |
5.3.3.14.3. Build Configurations
AudioThrough の機能を使用するためには
cd sdk tools/config.py -m
でConfig menu を開き、以下のConfigを設定する必要があります。
Select options in below:
[Device Drivers] [Board specific drivers] [CXD56 Audio Driver] <= Y [Application Configuration] [Spresense SDK] [SDK audio] <= Y
5.3.3.14.4. 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のクリアを必ず行ってください。行わないとノイズが発生してしまいます。 |
5.3.4. Object Level API
Audio機能では、High Level API よりも細かい単位でもAPIを提供しています。
High Level API をコールした際に内部で使用されるObject群を個別に組み合わせて
使用することでより自由なアプリケーションを作成することが出来ます。
これを Object Level API と呼ぶこととします。
5.3.4.1. About Usecase
Object Level APIを使用したUsecaseの例を示します。
(組み合わせ方・使い方は自由ですので、これはあくまで例です。)
5.3.4.1.1. Usecase 1
MediaPlayerObjectとOutputMixerObjectを使用して、Audioデータのデコード〜出音を行うケースです。
MediaPlayerObjectから応答されたPCMデータをApplicationで処理した後OutputMixerObjectへ送ることが出来ます。
5.3.4.2. MediaPlayerObject
MediaPlayerObjectは、Audioデータのデコード管理とデコード結果PCMの出力を行います。
2つのPlayerを同時に使用することが出来、各APIにあるPlayerIDのパラメータで個別に制御します。
ApplicationはSimpleFIFOと呼ばれるバッファ経由でMediaplayerObjectにESデータを渡します。
バッファがアンダーフローすると再生が停止します。Applicationはそれを考慮した設計をする必要が有ります。
デコードが完了するとMediaPlayerObjectからはPCMデータのMemoryHandleが通知されます。
PCMデータをOutputMixerObjectへ送ることも可能です。この場合はApplicationへの応答はありません。
ただし、当然ですがOutputMixerObjectの生成・起動をしておくことが必要です。
(Defaultは上図の通りApplicationへのCallback応答です。)
5.3.6. UserCustomDSPのアーキテクチャ
UserCustomDSPは音声信号に対してユーザー独自の信号処理をかけることが出来る仕組みです。
信号処理はDSPで行います。ここではその仕組みについて詳細に説明します。
5.3.6.1. フレームワークのコード
信号処理を追加するにはまずUserCustomDSPを作成しなければなりません。
最低限必要なソースコードは、フレームワークとしてSDKに用意されています。
これらを取り込み、ユーザー作成のDSP用コードとまとめてビルドすることでUserCustomDSPを作成します。
UserCustomDSP作成の手順については Audio RecorderのTutorial を参照してください。 |
5.3.6.2. フレームワークのコードとユーザーコードの関連
SDKの提供するのフレームワークとユーザーのコードの関連は下記の図の様でなければなりません。
ユーザーは図中の"User Edit DSP Codes"にあたる部分を編集します。
(フレームワークのコードを編集する必要は有りません。)
ユーザーのコード作成に当たっての留意点は下記の通りです。
-
UserCustomDSPとの通信コマンド定義は自由に作成できるが、フレームワークの
CustomprocCommand::CmdBase
を継承した構造体を使用し、下図のデータフォーマットに従う必要がある図 41. 通信コマンドフォーマット例えば、下記の様にする。
struct InitPram : public CustomprocCommand::CmdBase { uint8_t ch_num; ... }
-
ユーザー処理を書くクラス(上図では
UserProc
)は、下記の様にフレームワークのCustomprocDspUserProcIf
(抽象クラス)を継承しそのメソッドをオーバーライドし実装するclass UserProc : public CustomprocDspUserProcIf { void init(CustomprocCommand::CmdBase*); ... }
5.3.6.3. UserCustomDSPの動作
5.3.6.3.1. DSP内部の状態遷移
UserCustomDSPは Init
, Exec
, Flush
, Set
の各コマンドに対して下図のような状態遷移を行います。
5.3.6.3.2. DSPの動作シーケンス
上記のフレームワーク上で作成したUserCustomDSPは、Audioの各機能の中で下図のシーケンスで動作します。
Audio Recorer機能の中のUserCutstomDSPの位置づけについてはInit MicFrontend(Recorder)を、Audio Recognizerの場合はInit MicFrontend(Recognizer)を参照して下さい。
下図からも分かる通り、 Init
, Set
コマンドはユーザーコードからのAPIコールがトリガとなり送られます。
Exec
, Flush
コマンドはAudioSubSystem内部で(音声キャプチャする度に)送られるようになっています。
(※ Exec
, Flush
コマンドはユーザーコードから送る必要はありません。)
全体の流れは下記の通りです。
-
AUDCMD_INIT_MICFRONTENDで指定されたバイナリファイルからDSPを起動します。
-
AUDCMD_INIT_PREPROCESS_DSPでUserCustomDSPに
Init
コマンドが送られます。ch数やビット長など、処理に必要なパラメータを送るのに使用します。 -
AUDCMD_STARTRECで音声キャプチャが開始され、それらが順次
Exec
コマンドで送られます。これにUserCustomDSPで信号処理を行います。 -
AUDCMD_SET_PREPROCESS_DSPで
Set
コマンドが送られます。記録動作中でも送ることが出来ますのでフィルタ係数などを設定するのに使用します。 -
AUDCMD_STOPRECで音声キャプチャが停止され、最終フレームの
Exec
に続いてFlush
が送られます。遅延分の掃出しなどに使用します。
全体の流れは下記の通りです。
-
AUDCMD_INIT_RECOGNIZERで指定されたバイナリファイルからDSPを起動します。
-
AUDCMD_INIT_RECOGNIZER_DSPでUserCustomDSPに
Init
コマンドが送られます。ch数やビット長など、認識処理に必要なパラメータを送るのに使用します。 -
AUDCMD_START_RECOGNIZERで音声キャプチャが開始され、それらが順次
Exec
コマンドで送られます。これにUserCustomDSPで認識処理を行います。 -
AUDCMD_SET_RECOGNIZER_DSPで
Set
コマンドが送られます。認識動作中でも送ることが出来ますのでに認識のパラメータなどを設定するのに使用します。 -
AUDCMD_STOP_RECOGNIZERで音声キャプチャが停止され、最終フレームの
Exec
に続いてFlush
が送られます。遅延分の掃出しなどに使用します。
5.3.7. オーディオサブシステムのエラーについて
5.3.7.1. 概要
Audio SubSystem のHigh Level API は、コマンド・リザルトのデータ送受信によるインターフェースを持っています。
Audio SubSystem に向けて発行したコマンドに問題があった場合に、リザルトでAUDRLT_ERRORRESPONSEが返り、 ErrorResponseパラメータにエラー内容が格納されます。 このエラーをレスポンスエラーと呼びます。
また、Audio SubSystemの内部処理でエラーを検出した場合、内部イベントが発生し、AS_CreateAudioManagerで登録した callback関数でエラーが通知され、ErrorAttentionパラメータにエラー内容が格納されます。 これをアテンションエラーと呼びます。
レスポンスエラー、アテンションエラーに対しては、それぞれのエラーに応じて、不具合対応、エラー処理追加などを行って下さい。
5.3.7.2. レスポンスエラー
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 |
動作モードの設定失敗 |
|
0x3B |
スピーカドライブ能力指定誤り |
|
0x3D |
マイク指定の誤り |
|
0x3E |
Objectレイヤモジュールを生成せずに使用した |
"Error Code" の詳細は こちら を参照してください。
5.3.7.3. アテンションエラー
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 |
エンコード・デコードエラー、データのアンダーフロー・オーバーフローなど、動作に異常があり、音声データなどには異常が発生している可能性があるが、動作は継続できるものです。 |
5.3.7.6. Module ID List
AudioSubSystem内部で使用するモジュールのID一覧です。
Attention callbackでアテンションコードとともに通知され、どのモジュールでエラーが発生したかを判断します。
Module ID | Value | Description |
---|---|---|
AS_MODULE_ID_AUDIO_MANAGER |
0 |
Audio Manager |
AS_MODULE_ID_AUDIO_DRIVER |
1 |
Audio Baseband Driver |
AS_MODULE_ID_MIC_FRONTEND_OBJ |
2 |
FrontEnd Object |
AS_MODULE_ID_INPUT_DATA_MNG_OBJ |
3 |
Input Data Manager Object |
AS_MODULE_ID_MEDIA_RECORDER_OBJ |
4 |
Media Recorder Object |
AS_MODULE_ID_OUTPUT_MIX_OBJ |
5 |
Output Mix Object |
AS_MODULE_ID_PLAYER_OBJ |
6 |
Player Object |
AS_MODULE_ID_RECOGNITION_OBJ |
7 |
Recognition Object |
AS_MODULE_ID_SOUND_EFFECT_OBJ |
8 |
Sound Effect Object |
AS_MODULE_ID_SYNTHESIZER_OBJ |
9 |
Synthesizer Object |
AS_MODULE_ID_CAPTURE_CMP |
10 |
Capture Component |
AS_MODULE_ID_DECODER_CMP |
11 |
Decoder Component |
AS_MODULE_ID_ENCODER_CMP |
12 |
Encoder Component |
AS_MODULE_ID_FILTER_CMP |
13 |
Filter Component |
AS_MODULE_ID_RECOGNITION_CMP |
14 |
Recognition Component |
AS_MODULE_ID_RENDERER_CMP |
15 |
Renderer Component |
AS_MODULE_ID_POSTPROC_CMP |
16 |
Postfilter Component |
AS_MODULE_ID_OSCILLATOR_CMP |
17 |
Oscillator Component |
AS_MODULE_ID_CUSTOM_CMP |
18 |
Custom Component |
5.4. Camera
5.4.1. 概要
CXD5602には8ビットパラレルのCamera I/Fが備わっており、このI/Fを持つカメラモジュールを接続することができます。現在、Spresenseでは、Sony ISX012もしくはSony ISX019を搭載するカメラモジュールをサポートしています。 通常、カメラモジュールはデータ用のインターフェースに加えて、モジュールの制御を行うインターフェースを持っています。ISX012とISX019では、その制御用I/FにI2Cが用いられています。 以下にHWの構成の概要を示します。
CXD5602内部には、CISIFと呼ばれるCamera I/Fブロックがあり、このブロックで8ビットパラレル信号とCXD5602内部バスのブリッジを行なっています。ISX012カメラモジュールとISX019カメラモジュールの場合、これに加えて、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の初期化からイメージデータをキャプチャするまでの概略の流れは以下のようになります。
5.4.2. 状態遷移
ストリーム毎に状態を管理しており、アプリケーションからは並行して制御可能です。 ただし、imageの取得についてはV4L2_BUF_TYPE_STILL_CAPTURE制御優先であり、VIDIOC_TAKEPICT_START - VIDIOC_TAKEPICT_STOP間はV4L2_BUF_TYPE_VIDEO_CAPTURE側のimage取得は停止します(状態遷移図において"dma"状態ではなくなる)。
5.4.3. V4Sサポート ioctlコマンド
分類 | command | 目的 | Spresenseカスタマイズ |
---|---|---|---|
Capability取得 |
接続デバイスの情報を取得する。 |
ドライバ名を取得するためのメンバdriverのみサポートしています。 |
|
バッファ制御 |
ドライバのバッファ管理領域の初期設定を行う。 |
パラメータ"v4l2_buf_mode mode"を追加し、 バッファ群にRING構造を持たせられるようにしています。 |
|
アプリが用意したバッファをエンキューする。 |
V4L2準拠 |
||
imageデータが入ったバッファをデキューする。 |
V4L2準拠 |
||
VIDIOC_DQBUFを取り消す。 |
左記目的のために追加したSpresense独自コマンド。 |
||
ストリーム制御 |
ストリームを開始する。 |
V4L2準拠 |
|
ストリームを停止する。 |
V4L2準拠 |
||
静止画撮影開始 |
左記目的のために追加したSpresense独自コマンド。 |
||
静止画撮影停止 |
左記目的のために追加したSpresense独自コマンド。 |
||
フレーム設定の値域確認 |
INパラメータで指定したpixelフォーマットとimageサイズの組み合わせが 設定可能かどうかの確認。 |
V4L2準拠 |
|
フレーム設定の変更 |
pixelフォーマットとimageサイズを設定する。 |
V4L2準拠 |
|
frame intervalを設定する。(秒単位の値を分数の形で設定する。分数は分母と分子をそれぞれ整数で設定する。) |
V4L2準拠 |
||
frame intervalを取得する。(秒単位の値を分数の形で取得する。分数は分母と分子がそれぞれ整数で表現される。) |
V4L2準拠 |
||
クリップ領域の変更 |
クリップ領域の左上端の座標とサイズを設定する。 |
V4L2準拠 |
|
設定されているクリップ領域の左上端の座標とサイズを取得する。 |
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独自コマンド。 |
5.4.4. SPRESENSE独自仕様
5.4.4.1. ioctl(VIDIOC_QUERYCTRL) および ioctl(VIDIOC_QUERY_EXT_CTRL)によって得られるコントロールタイプ
ioctl(VIDIOC_QUERYCTRL) および ioctl(VIDIOC_QUERY_EXT_CTRL)で得られるコントロールタイプが、 一部のパラメータについて、V4L2での仕様と異なるものがあります。
V4L2ではV4L2_CTRL_TYPE_MENU(文字列で構成されるメニュー)、SPRESENSEではV4L2_CTRL_TYPE_INTEGER_MENU(整数値で構成されるメニュー)
-
V4L2_CID_COLORFX
-
V4L2_CID_EXPOSURE_AUTO
-
V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE
-
V4L2_CID_ISO_SENSITIVITY_AUTO
-
V4L2_CID_EXPOSURE_METERING
-
V4L2_CID_SCENE_MODE
V4L2ではV4L2_CTRL_TYPE_BOOLEAN(false/trueの二値)、SPRESENSEではV4L2_CTRL_TYPE_INTEGER(0(OFF),1(ON),2(AUTO切り替え)の3値)
-
V4L2_CID_WIDE_DYNAMIC_RANGE
5.4.4.2. JPEG + YUV422フォーマット
ioctl(VIDIOC_S_FMT)を用いて、ある1フレームのimageを JPEG、YUV422の2つのデータフォーマットで同時に取得する ことができます。
例えば、写真を撮ってJPEGで保存しつつ、保存したものと同じ画像をディスプレイに表示 するようなアプリをJPEGデコーダを用いずに実現できます。
この機能を用いて取得したYUV422データについては、imageproc_convert_yuv2rgb() APIを用いてRGB565フォーマットに変換することができます。 |
5.4.4.2.1. 設定方法
通常、VIDIOC_S_FMTを1回実行して、width, height、pixelformatを設定しますが、 このフォーマットを使用する場合は計2回実行します。
パラメータ構造体 struct v4l2_formatの メンバtypeには、1回目、2回目ともに同一の設定で、このフォーマットを設定したいストリームV4L2_BUF_TYPE_VIDEO_CAPTUREもしくはV4L2_BUF_TYPE_STILL_CAPTUREを設定します。 メンバ fmt.pixには、以下に従って、1回目にJPEGの設定を行い、2回目にYUV422の設定を行います。
回目 | pixelformat | width | height |
---|---|---|---|
1回目 |
V4L2_PIX_FMT_JPEG_WITH_SUBIMG |
JPEGのwidth |
JPEGのheight |
2回目 |
V4L2_PIX_FMT_SUBIMG_UYVY |
YUV422のwidth |
YUV422のheight |
2回目のpixelformat指定は_SUBIMG_がついたデファインを指定する必要があります。 例えばV4L2_PIX_FMT_UYVYを設定した場合は、単純にYUV422フォーマットが設定された状態になります。
また、使用可能な設定については、使用デバイスに応じて、ISX012で使用可能な設定もしくはISX019で使用可能な設定を参照ください。
5.4.4.3. 電源ON後の3A調整時間短縮用の設定API
ioctl(VIDIOC_S_EXT_CTRLS)およびioctl(VIDIOC_G_EXT_CTRLS)でV4L2_CID_3A_PARAMETERを利用することで、電源ON後の3A調整時間を短くすることができます。
また、3A調整完了有無はV4L2_CID_3A_STATUSで確認できます。
5.4.4.3.1. 3Aパラメータ取得・3A調整値の初期値変更
以下の設定で、ioctl(VIDIOC_G_EXT_CTRLS), ioctl(VIDIOC_S_EXT_CTRLS)を実行することで、 3Aパラメータの取得・3A調整値の初期値変更が行えます。
-
ctrl_class
: V4L2_CTRL_CLASS_CAMERAを設定する。 -
control→id
: V4L2_CID_3A_PARAMETERを設定する。 -
control→p_u16
: 要素数3のuint16型配列の実体を定義して、アドレスをp_u16に設定する。
ioctl(VIDIOC_G_EXT_CTRLS)実行時は、 control→p_u16
が指すアドレスに3Aパラメータが設定されてきます。
ioctl(VIDIOC_S_EXT_CTRLS)実行時は、逆に control→p_u16
が指すアドレスに値を設定することで、3A調整値の初期値を変更することができます。
5.4.4.3.2. 3A調整状態取得
以下の設定で、ioctl(VIDIOC_G_EXT_CTRLS)を実行することで、3A調整状態を取得することができます。
-
ctrl_class
: V4L2_CTRL_CLASS_CAMERAを設定する。 -
control→id
: V4L2_CID_3A_STATUSを設定する。
調整完了している場合、 control→value
にV4L2_3A_STATUS_STABLEが設定されます。
調整中の機能がある場合は、該当機能をあらわすビット(V4L2_3A_STATUS_AE_OPERATING, V4L2_3A_STATUS_AWB_OPERATING)がONとなります。
5.4.5. imageサイズとframe rateの制約
5.4.5.1. ISX012で使用可能な設定
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の制約には影響しません。
5.4.5.2. ISX019で使用可能な設定
ISX019では、データフォーマット・フレームサイズにかかわらず、
30FPS, 15FPS, 10FPS, 7.5FPSが利用できます。
FPSの設定は、ioctl(VIDIOC_S_PARM)の parm.capture.timeperframe
の各メンバを用いてフレーム間隔の形で設定します。
FPS | numerator | denominator |
---|---|---|
30 |
1 |
30 |
15 |
1 |
15 |
10 |
1 |
10 |
7.5 |
2 |
15 |
フレームサイズはフォーマットによって、特定のサイズのみが設定可能です。
1st ioctl(VIDIOC_S_FMT) | 2nd ioctl(VIDIOC_S_FMT) | ||||
---|---|---|---|---|---|
pixelformat |
width |
height |
pixelformat |
width |
height |
V4L2_PIX_FMT_JPEG |
1280 |
960 |
- |
- |
- |
1280 |
720 |
||||
640 |
480 |
||||
640 |
360 |
||||
320 |
240 |
||||
160 |
120 |
||||
V4L2_PIX_FMT_RGB565 V4L2_PIX_FMT_UYVY |
320 |
240 |
- |
- |
- |
160 |
120 |
||||
V4L2_PIX_FMT_JPEG_WITH_SUBIMG |
1280 |
960 |
V4L2_PIX_FMT_SUBIMG_UYVY |
320 |
240 |
160 |
120 |
||||
1280 |
720 |
V4L2_PIX_FMT_SUBIMG_UYVY |
320 |
180 |
|
160 |
90 |
||||
640 |
480 |
V4L2_PIX_FMT_SUBIMG_UYVY |
320 |
240 |
|
160 |
120 |
||||
640 |
360 |
V4L2_PIX_FMT_SUBIMG_UYVY |
320 |
180 |
|
160 |
90 |
||||
320 |
240 |
V4L2_PIX_FMT_SUBIMG_UYVY |
320 |
240 |
|
160 |
120 |
||||
160 |
120 |
V4L2_PIX_FMT_SUBIMG_UYVY |
160 |
120 |
5.5. DNN Runtime
5.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を使用しています。
5.5.2. サンプルコード
このサンプルコードは、こちら 学習済みモデルの準備 の章で使用している image_recognition.MNIST.LeNet
で作成された学習モデルを実行し、手書き文字認識を行うためのサンプルコードです。
image_recognition.MNIST.LeNet
で作成された学習モデルは、28x28サイズの画像を1つ入力にとり、10個の配列を出力します。この10個の配列はインデックスが認識した数字を意味しており、配列の中にそれぞれの数字である確率が出力されます。例えば、配列の先頭(インデックス0)には、入力された画像が数字の「0」である確率が出力されます。
詳細は、DNNRT example READMEを参照してください。
5.6. GNSS
GNSS library はGPS/GLONASS/BeiDou/Galileo 衛星の信号を受信し現在位置を算出する機能を提供します。 この機能を使うには、GNSS用のアンテナを装着する必要があります。 Spresenseボードでは、チップアンテナが搭載されているため、追加のアンテナは不要です。
5.6.1. 主な特徴
-
GPS、GLONASS/BeiDou/Galileo に加え 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 の引き渡しになります。
5.6.2. Configuration
GNSS 機能を使うには、CONFIG_CXD56_GNSS を "y" にセットする必要があります。
[System Type] [CXD56xx Package Configuration] [GNSS device] (CXD56_GNSS) = Y
NMEA 形式を使いたい場合は、CONFIG_CXD56_GNSS, CONFIG_LIBM, CONFIG_GPSUTILS_CXD56NMEA_LIB を "y" にセットする必要があります。
[System Type] [CXD56xx Package Configuration] [GNSS device] (CXD56_GNSS) = Y [Application Configuration] [Spresense SDK] [Sensing] [Support CXD56xx gnss NMEA convert library] (GPSUTILS_CXD56NMEA_LIB) = Y
また、サンプルアプリケーションを有効にするには、CONFIG_CXD56_GNSS に加え CONFIG_EXAMPLES_GNSS を "y" にセットしてください。
[System Type] [CXD56xx Package Configuration] [GNSS device] (CXD56_GNSS) = Y [Application Configuration] [Spresense SDK] [Examples] [GNSS positioning example] (EXAMPLES_GNSS) = Y
NMEAのサンプルをアプリケーションを有効にするには、NMEA設定の他に、CONFIG_EXAMPLES_GNSS_ATCMD を "y" にセットする必要があります。
[System Type] [CXD56xx Package Configuration] [GNSS device] (CXD56_GNSS) = Y [Device Drivers] [USB Device Driver Support] [USB Modem (CDC/ACM) support] = Y [Application Configuration] [Spresense SDK] [Sensing] [Support CXD56xx gnss NMEA convert library] (GPSUTILS_CXD56NMEA_LIB) = Y [Application Configuration] [Spresense SDK] [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 Package Configuration] [GNSS device] (CXD56_GNSS) = Y [Application Configuration] [Spresense SDK] [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 を参照してください。
|
以下の設定はどんな場合でも変更可能です。
-
'GNSS backup file name' と 'GNSS CEP file name' は GNSS によって使用されるファイルです。パス名をシステムの構成にあわせて変更してください。
[System Type] [CXD56xx Package Configuration] [GNSS setting] [GNSS backup file name] = '/mnt/spif/gnss_backup.bin' [GNSS CEP file name] = '/mnt/sd0/gnss_cep.bin'
GNSS デバイスドライバは アプリケーションに POSIX形式の poll もしくは シグナルを通じて測位結果を通知します。 poll できる数は以下のコンフィギュレーションで設定できます。デフォルトでは、poll 数は 4、signal 数は 4 です。 詳細については、 Position calculation notification を参照してください。
[System Type] [CXD56xx Package Configuration] [GNSS setting] [GNSS max poll waiters] = 4 [GNSS max signal receivers] = 4
5.6.3. Device control
GNSS ライブラリは ioctl() を通じて制御します。
ioctl のコマンドは、IOCTL_ のプレフィックスが付与されて定義されています。 GNSS デバイスファイルは、複数のアプリケーションから同時に open() することができます。 ただし、GNSS デバイスは、一度に一つの IOCTL コマンドしか受け付けません。言い換えると、どのアプリケーションが優先されるということでなく、受け付けられた順番に処理されます。
5.6.3.1. 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" の設定を参照してください。
5.6.3.2. 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 |
直近の測位データ、時間、アルマナック、エフェメリスを使って起動します。 |
(*1) 初期位置算出時間 (Time To First Fix)
"Warm Start" もしくは、"Hot Start" であったとしても、前回の動作時から長い時間が経過し、衛星軌道情報が古く期限切れの条件では、GNSS デバイスは "Cold Start" と同様に衛星の捕捉から開始します。
5.6.3.3. Stop positioning
測位を停止するには、 CXD56_GNSS_IOCTL_STOP を使用します。停止するには200から300ミリ秒かかります。
5.6.3.4. 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 データを、IOCTLコマンド 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で参照できます。
5.6.4. For faster positioning
"Hot Start" でTTFFを短くするためには、バックアップデータ中に含まれる衛星軌道上やその他の情報を活用することが早道です。 ioctl コマンドを使って、バックアップデータを活用した標準的な "Hot Start" の手順を示します。
5.6.4.1. Using GNSS backup data
バックアップデータに含まれる最終測位位置、エフェメリス、アルマナック、TCXO クロックのオフセット値が、 "Hot Start" で測位を開始する際に利用されます。 バックアップデータは ioctl コマンドによって、フラッシュメモリに蓄積することができます。その場合、電源がオフになった場合でも次の起動時にフラッシュメモリからSRAMに展開され、"Hot Start" に利用することができます。
5.6.4.1.1. Power condition and backup data
システムが起動している状態では、GNSS測位は動いており、全ての関連情報はストアされます。システムが "Hot Sleep" している状態では、SRAM のデータは保持され、 RTC は動き続けているため、"Hot Start" で測位を開始することができます。"Deep Sleep" 状態では RTC は動作を続けますが、SRAM はオフとなるため、"Hot Start" のためにはフラッシュメモリに保存したバックアップデータをリストアして利用する必要があります。
5.6.4.1.2. Saving backup data
バックアップデータをフラッシュメモリなどに保存するには、ioctl コマンド CXD56_GNSS_IOCTL_SAVE_BACKUP_DATA を使用します。バックアップデータは、CONFIG_CXD56_GNSS_BACKUP_FILENAME に指定されたファイル名で保存されます。 ただし、フラッシュメモリは頻繁に書き込むとメモリの故障の原因になりますので、頻繁な書き込みは控えるようにしてください。例えば、システムがシャットダウンするタイミングで保存するのが良いでしょう。
5.6.4.2. GPS time
"Hot Start" を行うためには、GNSS デバイスに誤差 60 秒以内の現在時刻が設定されている必要があります。以前に測位を行いシステムの電源が切られていなければ RTC_GPS に最後の測位時に設定された時刻が設定されています。電源投入直後には RTC_GPS の値は不定ですのでアプリケーションから ioctl コマンドにて時刻を設定する必要があります。
5.6.4.2.1. 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 をベースに行われます。
5.6.4.2.2. Set time by the command
アプリケーションがネットワーク等から取得した時刻を指定する場合は、ioctl コマンド CXD56_GNSS_IOCTL_SET_TIME を用います。
5.6.4.3. Current location
"Hot Start" のためには、直近の位置データが必要です。アプリケーションが位置データを保持していない場合は、GNSS デバイスは前回測定した位置情報をベースに "Hot Start" を行います。
5.6.4.3.1. Set location by the command
アプリケーションがネットワーク等から取得した位置情報を指定する場合は、ioctl コマンド CXD56_GNSS_IOCTL_SET_RECEIVER_POSITION_ELLIPSOIDAL もしくは、CXD56_GNSS_IOCTL_SET_RECEIVER_POSITION_ORTHOGONAL を用います。
5.6.5. Accurate Positioning
正確な測位を行うために、適切な受信条件で測位を行う必要があります。ビル街における衛星の見えにくさや大きな建物の反射波の影響、WIFIからのノイズなど、測位環境が精度の高い測位に影響を与えていないかが、測位データからある程度類推することができます。
5.6.5.1. Indication in receiver positioning data
5.6.5.1.1. Field numsv, numsv_tracking, numsv_calcpos, numsv_calcvel
Type |
uint8_t |
Unit |
none |
フィールド numsv, numsv_tracking, numsv_calcpos, numsv_calcvel は、それぞれ可視衛星数、追尾衛星数、位置演算に利用している衛星数、速度演算に利用している衛星数です。 可視衛星には、衛星軌道情報から現在の天空に存在が予想されるが、実際には障害物があり信号を受信していない衛星も含みます。 追尾している衛星の中でも受信が弱く正確性に欠く信号は測位や測速の演算に利用しません。 位置精度は後述の信号の強度や衛星の配置にも依存するため一概には言えませんが、一般的に numsv_calcpos の個数を 6 個以上に保つと数メートルの誤差で測位を続けられることが多いようです。
5.6.5.1.2. 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以下だった場合は、測位するのに十分な条件を満たしています。
5.6.5.1.3. 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 の悪化などが測位結果に誤差を与えます。この値が大きいほど、測位データが不正確であることを示します。
5.6.5.2. Indication in satellite positioning data
5.6.5.2.1. 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以上で受信できていると測位結果は安定します。
5.6.5.3. Multi-GNSS
前出の様に捕捉追尾する衛星数が多いほど測位精度が向上する傾向があります。 GNSS デバイスは複数の衛星システムを同時に利用し、測位演算に供する衛星信号の数を増やすことが出来ます。
-
GLONASS
GPSと同様に全世界をカバーする24機体制のロシアの測位衛星システムです。 GPSだけでも十分衛星数が確保出来ている場合に、GPSシステムの標準精度 20m に比べると劣る精度 70m のGLONASSシステムの信号を混在して測位演算すると、GPS 単独の場合よりも位置精度が落ちる可能性があります。 なおアンテナにはGLONASSの周波数に対応していない物もあるため注意が必要です。
-
Galileo
GPSと同様に全世界をカバーする欧州の測位衛星システムです。
-
BeiDou
GPSと同様に全世界をカバーする中国の測位衛星システムです。
-
QZSS-L1C/A
日本のみちびきから送信される衛星信号です。 この信号はGPS L1C/Aと互換のため、これを利用するとあたかもGPS衛星が増えたかのように見えます。これをみちびきによるGPSの補完機能と呼んでいます。 みちびきは4機体制(2018年時点)で、その軌道は日本を中心に東アジアとオセアニア上空をカバーするため、それ以外の地域では補完効果がありません。
5.6.5.4. Augmentation
WAAS もしくは みちびきの QZSS-L1S から送信される補強信号を利用することで測位精度を上げることが出来ます。 補強信号はSBASフォーマットをベースとした、数分ごとに更新される測位演算の精度を向上するための計算パラメータです。 補強信号を使う場合、補強情報が無いGLONASSを混在させての測位は出来ません。
-
WAAS
WAASはアメリカ合衆国本土、カナダ、ハワイ及び属州で有効な補強信号です。 GPS衛星の信号から求められる擬似距離(衛星から受信機間の距離)の精度を向上します。
-
QZSS-L1S
QZSS-L1Sは日本の範囲のみで有効です。 GPS衛星信号及びQZSS-L1C/Aから求められる擬似距離(衛星から受信機間の距離)の精度を向上します。 仰角の低い衛星(2018年9月時点で仰角マスクは20度)は補強情報が送信されず、測位に使うことができません。
5.6.5.5. Select positioning and augmentation satellite systems
本 GNSS デバイスでは、以下の測位衛星システム及び補強信号の組み合わせで測位することを想定しています。 どの衛星システムを使うかは CXD56_GNSS_IOCTL_SELECT_SATELLITE_SYSTEM で設定できます。
GLONASS, Galileo, BeiDou に関して同時に使用することはできません。いずれか一つを選択してください。 GPS, QZSS-L1C/A や WAAS, QZSS-L1S といった補強信号に関しては組み合わせの制約はありません。 |
以下の測位誤差(Accuracy)は一般的な好条件下での参考値です。お使いのシステム及び環境によって変動します。 |
GPS | GLONASS | Galileo | BeiDou | 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 |
<7.8m |
in the city |
|||||
x |
x |
<7.8m |
in the city |
|||||
x |
x |
x |
<7.8m |
in the city of East Asia and Oceania |
||||
x |
x |
x |
<7.8m |
in the city of East Asia and Oceania |
||||
x |
x |
x |
<7.8m |
in the city of East Asia and Oceania |
||||
x |
(x) |
(x) |
(x) |
x |
x |
<2m |
in Japan |
5.6.6. Short message delivery
GNSS デバイスはQZSSみちびきから送信される災害情報、危機管理情報といった災危(災害・危機)通報を受信できます。
アプリケーションはシグナル通知を GNSS デバイスに設定することで、GNSS デバイスからの災危通報を非同期にシグナルで受けることができるようになります。 災危通報のシグナル通知の設定は、フィールド fd に GNSS デバイスのファイルディスクリプタを, enable には 1 を, gnsssig に任意のシグナル番号を、さらに gnsssig に CXD56_GNSS_SIG_SBAS を設定した cxd56_gnss_signal_setting_s データを、IOCTLコマンド CXD56_GNSS_IOCTL_SIGNAL_SET の引数として ioctl関数を呼び出してシグナルと災危通報を関連付けして下さい。
GNSS デバイスは SBAS メッセージタイプ の 43 (気象庁防災情報)または 44 (任意情報)を受信すると、関連付けしたシグナルを発行します。 sigwaitinfoでのシグナル処理にて データバッファに cxd56_gnss_sbasdata_s 、 CXD56_GNSS_READ_OFFSET_SBAS をオフセットとしてGNSSデバイスを read することで通知の要因となった SBAS メッセージを読み出すことが出来ます。
5.6.7. Utilities
5.6.7.1. NMEA converter
NMEA は GPSアプリケーションで広く使われている位置情報のテキストフォーマットで、gnss ライブラリは NMEA フォーマットの出力もサポートしています。
cxd56_gnss_sv_s に格納された GNSS デバイスの内容を NMEA に変換します。
詳細については、gnss_nmea ならびに、NMEA Output を参照してください。
5.6.7.2. 1PPS signal output
UTC(協定世界時)に同期した高精度のタイムパルス(1PPS)信号を出力します。
5.6.8. Geofence
5.6.8.1. Key features
Geofence はあらかじめ指定した領域に近づいたときに通知するためのライブラリです。
-
領域は Latitude, Longitude, Radius で指定できます
-
最大 20 領域まで指定できます
通知は "ENTER", "DWELL", "EXIT" の3種類あります。"DWELL" の場合は、その領域に滞在した時間も通知条件に設定することができます。
5.6.8.2. Configuration
Geofence を使う場合、GNSS device と Geofence Support を 'y' に指定してください。
[System Type] [CXD56xx Package Configuration] [GNSS device] (CXD56_GNSS) = Y [Geofence Support] = Y
5.6.8.3. Add Region and setup options
Geofence を使うには "/dev/geofence" デバイスファイルを使います。
5.6.8.5. Dead zone
"Dead Zone" とは定義された領域の半径 (Radius) で定義された同心円状の領域を示します。
-
"ENTER" が定義された場合、その同心円領域に入ると通知します
-
"EXIT" が定義された場合、その同心円領域から出ると通知します
5.6.8.6. Read Geofence transition data
状態変化は、poll によって通知されます。 cxd56_geofence_status_s に状態遷移の情報が格納されています。
詳細については、アプリケーションサンプル Geofencing Transitions を参照してください。
5.6.9. PVTLog
5.6.9.1. Key features
PVTLog は、Position, Velocity, Time の情報のログです。
最大 170 のログを蓄積することができます。ログがあふれた場合は、アプリケーションはシグナルにより通知を受けることができます。 例えば、15分ごとにログを取得した場合、42時間は継続して記録することができます。
ログデータの詳細については、cxd56_pvtlog_s を参照してください。
5.6.9.2. Configuration
PVTLog を使う場合は、GNSS device Support を 'y' に設定してください。
[System Type] [CXD56xx Package Configuration] [GNSS device] (CXD56_GNSS) = Y
5.6.9.3. Start and stop log store
ログを開始するには、ioctl コマンド CXD56_GNSS_IOCTL_PVTLOG_START で行うことができます
記録する間隔についても、このコマンドを使ってください。ログを記録する間隔は測位する間隔よりも大きくする必要があります。
測位を開始するとログは自動的に開始します。ログを停止するには、ioctl コマンド CXD56_GNSS_IOCTL_PVTLOG_STOP を使ってください。
5.6.9.4. Notification
記録しているログが 170 を超えると、シグナルによりアプリケーションにログが溢れたことが通知されます。 シグナルの設定は、CXD56_GNSS_SIG_PVTLOG で行うことができます。
この通知を受け取ったら、次の記録が行われるまでにログを読み出す必要があります。でなければ、最初のログデータが次の測位データに上書きされることになります。
ログデータは、CXD56_GNSS_READ_OFFSET_PVTLOG でオフセットが指定された位置から読み込むことができます。
ログデータは、バックアップ RAM に記録されます。すでにデータが存在する場合は、消去されることなく上書きされます。 バックアップをクリーンナップする場合は、CXD56_GNSS_IOCTL_PVTLOG_DELETE_LOG を使います。 |
詳細に関しては、サンプルプログラム PVTLog を参照してください。
5.6.10. GNSS 性能
5.6.10.1. 軌跡例
Spresenseのメイン基板に搭載の内蔵チップアンテナを使ったケースと、改修して取り付ける外付けGNSSアンテナを使ったケースでのトラック走行の軌跡を掲載します。外付けアンテナを使用した場合は、受信感度が少し上がるので更に正確な軌跡となって表れています。
- 条件
-
-
衛星モード:GPS + GLONASS
-
測定環境:オープンスカイの下、良好な信号環境
-
事前条件:一度測位させてから、衛星を捕捉した状態でトラックを走行
-
アンテナ向き:トラック走行中は、上腕に取り付けて腕振りあり
-
速度:軽いジョギング(約6km/h)
-
- 実際の軌跡
-
400mトラックを3周(走行位置はトラックの内側のコース)
これらの画像は、Creative Commons Attribution-ShareAlike 2.0 のライセンスの下で、 ”© OpenStreetMap contributors”によって提供される地図画像を利用して作成されています。 |
ここでの軌跡は、NMEAデータをGPXデータに変換してOpenStreetMapで描画したものになります。 |
5.6.11. Application Examples
5.6.11.1. Notify Positioning
GNSS 測位の通知は、poll とシグナルによって行われます。どちらを使うかは、CONFIG_EXAMPLES_GNSS_USE_SIGNAL もしくは CONFIG_EXAMPLES_GNSS_USE_POLL を Kconfig で選択できます。
5.6.11.2. NMEA Output
GNSS ATCMD アプリケーション を参照してください。
5.7. ASMP Framework
5.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 は、それぞれのタスクで共有されるメモリ領域を提供します。
5.7.2. Configuration
[ASMP] = Y [ASMP shared memory size] = 0x100000
ASMP framework は、Sub CPU上で動作するプログラム自身やタスク間の共有メモリのために、カーネル管理外のメモリ領域を必要とします。このメモリ領域のサイズは、 ASMP shared memory size
オプションで変更することができます。
例えば、ASMP shared memory size
の値を 0x80000 (512KByte)
に設定した場合、トータル1.5MbyteのSRAMが、
-
Main CPU側のメモリサイズ(カーネル管理領域): 1MByte
-
Sub CPU側のメモリサイズ(カーネル管理外領域): 512KByte
という割り当てになります。
もし、 ASMP frameworkを必要としないのであれば、カーネルは最大 1.5MByte のメモリを活用することができます。
ASMP Shared memory size 設定は、128KBbyte単位(0x20000)で設定してください。
|
5.7.3. Workerの起動シーケンス
-
mptask_init を呼び出してWorkerを生成します
-
必要に応じてメッセージキューや共有メモリなどを準備し、Workerに紐付けます
-
mptask_exec を呼び出してWorkerを実行します
-
終了時には mptask_destroy を呼び出してWorkerを停止させます
-
メッセージキューや共有メモリなどを作成していた場合は破棄させます
詳細は、Supervisor Task example および Worker Task example を参照してください。
5.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 を参照してください。
5.8. Sensor Control Unit
5.8.1. Overview
Sensor Control Unit (SCU) は、SCU デバイス内の SPI もしくは I2C バスに接続されたセンサーデバイスからのセンシングデータを取得することができます。CPUとは独立して動作するため、センサー取得に関わるCPUの負荷を軽減することができます。
-
シーケンサーによるセンサーデータの自動取得
-
センサーデータ間引き機能(デシメーター)
-
IIR フィルター機能
-
イベント通知機能
これらの機能は、アプリケーションから設定することができます。
SCU は SPI と 2つの I2C バスと 8つのシーケンサーと2つのデシメーターを搭載しています。
5.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つまでです。 |
5.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
構造体に格納されます。
5.8.4. デシメーター
デシメーターは、FIFOに取得したデータを間引きする機能でシーケンサーの機能を拡張するために設けられたものです。デシメーターは 3 つの FIFO を持っています。
5.8.4.1. デシメーション処理
デシメーターは 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に設定してください。
5.8.4.2. 取得データの増幅
デシメーターは、 decimation_s
構造体の leveladj を使うことで取得データを増幅させることができます。増幅されたデータはそのもとのデータ幅を超過してしまいますので、使用の際は注意が必要です。
leveladj は以下の設定ができます。
-
SCU_LEVELADJ_X1
(x1) -
SCU_LEVELADJ_X2
(x2) -
SCU_LEVELADJ_X4
(x4) -
SCU_LEVELADJ_X8
(x8)
5.8.5. センサーデータへの信号処理
SCU は、取得したデータに対して信号処理を加えることができるようになっています。 SCUが行える信号処理は、IIRフィルターとイベント検出の2つがあります。 IIRフィルターはデータパス上の3箇所に設定をすることができます。使用するFIFO毎に設定する必要があります。
5.8.5.1. 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 は、小数部になります。
5.8.5.2. イベント検出 (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 がゼロだった場合、設定はすべて無視されます
5.8.7. ドライバ開発者のためのガイドライン
SCU機能をサポートしたドライバを開発するには以下の内容を理解する必要があります
-
シーケンサーインストラクションの理解
-
センサーデバイスとの通信方法
-
シーケンサーの初期化(Open) とセンサードライバとの接続方法
-
シーケンサーからサンプルデータを読み出す方法
-
シーケンサーに対して ioctl を介してコマンドを指示する方法
5.8.7.1. シーケンサーインストラクションの理解
シーケンサーインストラクションは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
5.8.7.2. センサーデバイスとの通信方法
最初にドライバはターゲットのセンサーデバイスを初期化しなければなりません。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 を通じてのみ、デバイスにアクセスするようにしてください。 |
5.8.7.3. シーケンサーの初期化(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 を呼び出すと同時にセンサーデバイスのパワーモードもコントロールするようにしてください。
それによりシステムは、より省電力を実現することができるようになります。
5.8.7.4. シーケンサーからサンプルデータを読み出す方法
シーケンサーがセンサーデータを読み出すときは、データを読み出すための一連のシーケンサ命令群を 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);
5.8.7.5. シーケンサーに対して 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);
}
5.9. Memory Utility Libraries
5.9.1. 概要
メディア処理&センサー処理機能では、データ用に多くのメモリを安全に、フラグメンテーションなどのリークを発生させずに管理する必要があります。このため、Spresenseでは、メモリユーティリティライブラリを提供しています。
ライブラリは、以下の3つのライブラリ、”Memory Manager” 、"Message Library"、"Simple FIFO"で構成されています。
-
”Memory Manager” は、Multi task 間で、メモリ領域を安全に管理するため、 暗黙的にセグメントエリアを管理する固定長メモリプールライブラリです。
-
"Message Library" は、”Memory Manager” を使うために、クラスオブジェクトをタスク間で送受信するためのライブラリです。
-
"Simple FIFO" は、アプリケーションとフレームワークの間でデータをやりとりするために使用される基本的なFIFOです。
これらのライブラリの利用をメディア処理&センサー処理では、前提としています。 詳細は、以下の各章に記載します。
5.9.2. Memory Manager
”Memory Manager” の説明をします。
5.9.2.1. 概要
”Memory Manager" は、暗黙的な取得と解放を管理する固定長メモリプールです。 用途に応じたメモリ領域をメモリプールとして定義し、その中に必要なセグメントを確保することで、必要に応じたメモリの取得、解放が可能です。
プールの種別、セグメントの数などを決めて、メモリ領域を割り当てることで、メモリ領域のフラグメントなどを避けることができます。 この割り当て情報を "memory_layout" と呼び、このレイアウトをアプリケーションのニーズに合わせて切り替えることで、機能ごとに必要なメモリを確保します。
このメモリLayoutは、Applicationのニーズに従って自由に決めて、作成することが可能です。作成方法は、Configurations and Generateに記載します。
必要な "memory segment"の取得は、"MemHandle" を作成し、セグメントの確保のAPIを呼ぶことで可能です。これによって確保されたセグメントは、"MemHandle" のインスタンスが破棄されるまで、保証されます。
複数のユーザーが非同期に "MemHandle"を確保、解放を行っても、その領域は保証され、すべてのユーザから参照されなくなった時点で解放されます。
5.9.2.2. セグメント獲得、解放の仕組み
各ユーザオブジェクトは、必要なメモリエリアのセグメントを指し示すMemHandleのインスタンスを生成し、セグメントエリアを取得するAPIを呼ぶことで、それに紐付くメモリセグメントを確保します。
その際、セグメントエリアの参照カウンタを+1します。 これによって、セグメントエリアは使用状態になり、他の要求からは使えないように管理されます。
この参照カウンタは、"operator=" や、"Copy Constructor" 内でも+1を行います。
このことで、データパイプラインの次に位置するオブジェクトに、この "MemHandle" のインスタンスをコピーし、渡していくことで、 "MemHandle" に紐付いたセグメントを参照することができるとともに、そのエリアを保証できます。
逆に、不要になった場合は、"MemHandle" のインスタンスを破棄することで、デストラクタ内で、参照カウンタが-1されます。
このことで、参照しているインスタンスがすべてなくなった時点で、自動的にリンクされているセグメントは解放されます。
この仕組みにより、必要なオブジェクトが各自メモリが、非同期に必要な間インスタンスを確保し、不要になったら各自のタイミングで破棄したとしても、安全にメモリを確保・解放することができ、意識することなく、すべてのユーザがインスタンスを破棄することで、暗黙の裡に参照が外れます。
Memory Layoutに関しては、Memory Layoutを使うためのヘッダファイル群をあらかじめ用意する必要があります。 これらのヘッダファイルは、Memory Layout定義ファイル(mem_layout.conf)を作成し、専用のツール(mem_layout.py)を使うことで、生成されます。
5.9.2.3. APIs
”Memory Manager"のインタフェースは次の通りです。 詳細は、 memutils_memory_manager を参照してください。
5.9.2.3.1. 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(uint8_t sec_no, NumLayout layout_no, void* work_area, uint32_t area_size, const PoolSectionAttr *pool_attr)
uint8_t sec_no : セクション番号 (0 origin) NumLayout layout_no : メモリレイアウト番号 (0 origin) void* work_area : 静的メモリプール操作用の作業領域 uint32_t area_size : 作業領域のサイズ(byte単位) const PoolSectionAttr *pool_attr : メモリレイアウトのアドレス
ERR_OK : 初期化成功 ERR_STS : initPerCpu()が未実行、あるいは本関数が実行済み ERR_ADR_ALIGN : work_areaが、4の倍数ではない ERR_DATA_SIZE : area_sizeが、最大作業領域サイズ未満
指定されたレイアウト番号のメモリプール郡を生成する。 メモリレイアウトを変更する場合には、いったんManager::destroyStaticPools()で メモリプール郡を破棄してから、再度本関数を実行すること。 Manager::initPerCpu()が未実行の場合、 既に本関数が実行済みの場合は、ERR_STSを返す。 引数sec_noは、使用するセクション番号を指定する。 引数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);
uint8_t sec_no = 0;
const NumLayout layout_no = 0;
void* work_va = MemMgrLite::translatePoolAddrToVa(MEMMGR_WORK_AREA_ADDR);
if (MemMgrLite::Manager::createStaticPools(sec_no, 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)];
uint8_t sec_no = 0;
const NumLayout layout_no = 0;
if(MemMgrLite::Manager::createStaticPools(sec_no, layout_no, s_work_area, sizeof(s_work_area) != ERR_OK)
{
/* エラー処理 */;
}
Spresense では、SRAMのみ選択できます。 |
static void Manager::destroyStaticPools(uint8_t sec_no)
uint8_t sec_no : セクション番号 (0 origin)
なし
Manager::createStaticPools()で生成したセクション番号の静的メモリプールを破棄する。 静的メモリプール未生成時は、何もしない。 Manager::initPerCpu()が未実行の場合は、"debug_assert"する。 メモリプールの破棄時には、セグメント解放漏れのチェックを行う。 解放漏れが検出された場合は、"assert"する。 フェンス機能が有効な場合は、静的メモリプールのフェンスチェックを行い フェンス破壊が検出された場合は、"assert"する。
/* 静的メモリプールを破棄する */
MemMgrLite::Manager::destroyStaticPools(0);
static NumLayout Manager::getCurrentLayoutNo()
なし
現在設定されているメモリレイアウト番号 未設定の場合は、BadLayoutNo
現在のメモリレイアウト番号を取得する。 メモリレイアウト番号の初期値は、BadLayoutNo。 Manager::createStaticPools()を呼ぶと引数で指定した値が設定される。 Manager::destroyStaticPools()を呼ぶと初期値にリセットされる。
/* 現在のメモリレイアウト番号を取得する */
MemMgrLite::NumLayout layout_no = MemMgrLite::Manager::getCurrentLayoutNo();
if (layout_no != MemMgrLite::BadLayoutNo)
{
printf("Current memory layout number: %d\n", layout_no);
}
else
{
printf("Memory layout number not set\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\n",
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\n", MemMgrLite::Manager::isPoolFenceEnable(my_pool_id));
#endif
//#ifdef USE_MEMMGR_MULTI_CORE
// printf(" spl_id=%u\n", MemMgrLite::Manager::getPoolSpinLockId(my_pool_id));
//#endif
}
5.9.2.3.2. 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"する。
5.9.2.4. Configurations and Generate
”Memory Manager” のLayoutファイルの記述方法と作成方法を説明します。
Layout情報は、MemoryLayout定義ファイル"mem_layout.conf" (名前は変えられます。) の中に、Pythonで記載され、 "mem_layout.py" というツールで、C++言語の3つヘッダ、 "mem_layout.h" "fixed_fence.h" "pool_layout.h" を生成します。 ユーザは、このヘッダをインクルードすることで、”Memory Manager” を使えることになります。
5.9.2.4.1. How to write a Memory 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_"で開始されるユーザー独自の名称をつける ことができます。また、定義には、Pythonの任意の式を記述することが出来ます。
# スクリプト内定義と重ならないように、"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], None # 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 None # 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 ], None # 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 ], None # end of each layout ], # end of layout 1 None # end of definition )
プレーヤーとレコーダーを切り替えて使う
/* Player: レイアウト No.0 を使う */
err = Manager::createStaticPools(0, // Section no 0
0, // Layout no 0 for Player
translatePoolAddrToVa(S0_MEMMGR_WORK_AREA_ADDR),
S0_MEMMGR_WORK_AREA_SIZE,
MemoryPoolLayouts[0][0]);
/* Playerの処理 */
...
/* 再生中 */
...
/* Playerの終了 */
...
/* レイアウトの破棄 */
destroyStaticPools(0);
...
/* Recorder 処理開始 */
/* Recorder: レイアウト No.1 を使う */
err = Manager::createStaticPools(0, // Section no 0
1, // Layout no 0 for recorder
translatePoolAddrToVa(S0_MEMMGR_WORK_AREA_ADDR),
S0_MEMMGR_WORK_AREA_SIZE,
MemoryPoolLayouts[0][1]);
/* Recorderの処理 */
...
/* 記録中 */
...
/* Recorderの終了 */
...
/* レイアウトの破棄 */
destroyStaticPools(0);
メモリセクションは別々の領域に3つまで定義できます。
# 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 # Sensor U_SENSOR_DSP_CMD_SIZE = 0x300 U_SENSOR_DSP_CMD_SEG_NUM = 8 U_SENSOR_DSP_CMD_POOL_SIZE = U_SENSOR_DSP_CMD_SIZE * U_SENSOR_DSP_CMD_SEG_NUM U_SENSOR_DATA_BUF_SIZE = 0x300 U_SENSOR_DATA_BUF_SEG_NUM = 8 U_SENSOR_DATA_BUF_POOL_SIZE = U_SENSOR_DATA_BUF_SIZE * U_SENSOR_DATA_BUF_SEG_NUM # section 0 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 ], None # 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 ], None # end of each layout ], # end of layout 1 None # end of definition ) # section 1 PoolAreas.init( [ # layout 0 for Sensor #[ name, area, align, pool-size, seg, fence] ["SENSOR_DSP_CMD_BUF_POOL", "COMMON_WORK_AREA", U_STD_ALIGN, U_SENSOR_DSP_CMD_POOL_SIZE, U_SENSOR_DSP_CMD_SEG_NUM, False], ["SENSOR_DATA_BUF_POOL", "COMMON_WORK_AREA", U_STD_ALIGN, U_SENSOR_DATA_BUF_POOL_SIZE, U_SENSOR_DATA_BUF_SEG_NUM, False], None # end of each layout ], # end of layout 0 None # end of definition )
レコーダーとセンサーを同時に使う
/* Recorder: レイアウト No.1 を使う */
err = Manager::createStaticPools(0, // Section no 0
1, // Layout no 0 for Recorder
translatePoolAddrToVa(S0_MEMMGR_WORK_AREA_ADDR),
S0_MEMMGR_WORK_AREA_SIZE,
MemoryPoolLayouts[0][1]);
/* Sensor: レイアウト No.0 を使う */
err = Manager::createStaticPools(1, // Section no 1
0, // Layout no 0
translatePoolAddrToVa(S0_MEMMGR_WORK_AREA_ADDR),
S0_MEMMGR_WORK_AREA_SIZE,
MemoryPoolLayouts[0][0]);
/* Recorderの処理 */
/* Sensorの処理 */
...
/* 再生中 */
/* Sensing */
...
/* Recorderの終了 */
/* Sensorの終了 */
...
/* レイアウトの破棄(Recorder) */
destroyStaticPools(0);
/* レイアウトの破棄(Sensor) */
destroyStaticPools(1);
...
各パラメータの説明は以下の通りです。
パラメータ | 説明 |
---|---|
name |
プール名(英大文字で始まり、"_POOL"で終わる名称。英大文字, 数字, _が使用可能) |
area |
プール領域として使用するFixedAreaの領域名。領域はRAMに配置して下さい。 |
align |
プールの開始アライメント。0を除くMinAlign(=4)の倍数を指定します。 |
pool-size |
プールのサイズ。0を除く4の倍数の値。セグメントサイズ * セグメント数。 |
seg |
セグメント数。1以上、255以下の値を指定します。 |
fence |
フェンスの有効・無効を指定します。この項目は、UseFenceがFalseの場合は無視されます。 |
5.9.2.4.2. How To Generate
"mem_layout.py" を使ったLayoutファイルの作成方法は以下の通りです。
- Usage
python3 mem_layout.conf [layout_header] [fence_header] [pool_header]
- Example
python3 mem_layout.conf mem_layout.h fixed_fence.h pool_layout.h
"mem_layout.py"の各引数の説明は下記の通りです。
Parameter | Description |
---|---|
mem_layout.conf |
Memory Layout定義ファイル |
layout_header |
各種定数値がマクロとして出力されるヘッダファイル。この引数を省略した場合、"mem_layout.h"という名前のファイルを生成します。メモリサイズ調整用として、レイアウト毎に残り使用可能サイズがコメント形式(/* Remainder XXXX_AREA=0xXXXXXXXX */)で出力されます。 |
fence_header |
FixedAreaのメモリフェンスアドレスが出力されるヘッダファイル。この引数を省略した場合、"fixed_fence.h"という名前のファイルを生成します。”Memory Manager”が使用するファイルのため、ユーザーは使用しないで下さい。 |
pool_header |
PoolAreaの各種定義が出力されるヘッダファイル。この引数を省略した場合、"pool_layout.h"という名前のファイルを生成します。”Memory Manager”が使用するファイルなので、ユーザーは使用しないで下さい。 |
5.9.3. Message Library
主に、"Memory Manager" のような Task間でClass Instanceの送受信を行う必要がある場合、通常、OSが提供しているシステムコールでは対応できなため、"Message Library" を用いて、これを実現しています。 ここでは、この "Message Library" の説明を行います。
5.9.3.1. General
"Message Library" は、タスク間でClass Instanceの送受信を行えるタスク間同期ライブラリです。
このライブラリは、送り先をIDとして明示的に指定して送信します。 また、受信側は、送信イベントを待ち受けて、イベントドリブン動作を実現します。
また、このメッセージにはTypeを持っており、受け取ったインスタンスが、あったかなかったか、あった場合何であったかは、Typeによって判断し取り出します。
ただし、送受信したいクラスは、正しくコピーコンストラクタを実装している必要があります。 |
5.9.3.2. Message ID と Type
5.9.3.2.1. 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 は、アプリケーションに応じて作成し、明示的に送受信のデータパスを指定しながら使用します。
5.9.3.2.2. 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.
}
5.9.3.3. APIs
"Message Library"のインタフェースは次の通りです。 詳細は、 memutils_message を参照してください。
5.9.3.3.1. MsgLibクラスの関数
static err_t MsgLib::initFirst(uint32_t num_pools, uint32_t top_drm)
uint32_t num_pools : メッセージのセグメント数(メッセージの種別数) uint32_t top_drm : メッセージ管理エリアのアドレス
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する。 これは、非タスクコンテキストでのスピンロック待ちを防止するためである。
5.9.3.3.2. 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する。
5.9.3.3.3. MsgPacketクラスの関数
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に変更される。 メッセージパラメタへの参照やポインタは無効となるので、注意すること。
5.9.3.4. Configurations and Generate
”Message Library” のLayoutファイルの記述方法と作成方法を説明します。
Layout情報は、"msgq_layout.conf" (名前は変えられます。) の中に、Pythonで記載され、 "msgq_layout.py" というツールで、C++言語の2つヘッダ、 "msgq_id.h" "msgq_pool.h" を生成します。 ユーザは、このヘッダをインクルードすることで、”Message Library” を使えることになります。
5.9.3.4.1. How to write a Message Layout File
"msgq_layout.conf"は、”Message Library”のユーザー定数の定義、メッセージキュープールの定義、デバッグ値の指定、メッセージパラメータのチェック指定を行います。それぞれの説明を以下に示します。
Message Layout定義ファイル内で使用する定数に、"U_"で開始されるユーザー独自の名称をつける ことができます。また、定義には、Pythonの任意の式を記述することが出来ます。
# ユーザー定義定数は、"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], None # 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 |
5.9.3.4.2. How To Generate
"msgq_layout.py" を使ったLayoutファイルの作成方法は以下の通りです。
- Usage
python3 msgq_layout.py [--help] [-h] [--without_memory_layout] [-n]
[address | fixed_file] [size | fixed_ID]
[id_header] [pool_header]
-n, --without_memory_layout Allocate message memory in a global variable
-h, --help Show this usage and exit
- Example
python3 msgq_layout.conf or python3 msgq_layout.conf 0x00800000 0x20000 or python3 msgq_layout.conf ../include/mem_layout.h or python3 msgq_layout.conf --without_memory_layout or python3 msgq_layout.conf mem_layout.h MSG_QUE_AREA msgq_id.h msgq_pool.h
ツールを実行しているディレクトリ内に、[address | fixed_file] や [size | fixed_ID] を省略する場合、実行しているディレクトリ内に mem_layout.h が存在している必要があります。
|
--without_memory_layout を指定すると、内部で静的にメモリを確保し、そこを利用します。
|
"msgq_layout.py"の各引数の説明は下記の通りです。
Parameter | Description |
---|---|
msgq_layout.conf |
メッセージキューレイアウト定義ファイル |
address or fixed_file |
メッセージ領域のアドレスです。もしくは、Memory Manager で生成された 'memory_layout.h' を指定します。この引数を省略した場合、本ツールを実行したディレクトリ内にある 'memory_layout.h' を読み込みます。ディレクトリ内に 'memory_layout.h' がない場合は必ず指定してください。 |
size or fixed_ID |
バイト単位の領域サイズです。'memory_layout.h' を指定した場合は、メッセージライブラリとして使用する |
id_header |
メッセージキューIDマクロが出力されるファイル。この引数を省略した場合、"msgq_id.h"という名前のファイルを生成します。 |
pool_header |
メッセージキュープールの定義が出力されるファイル。この引数を省略した場合、"msgq_pool.h"という名前のファイルを生成します。 |
5.9.4. Simple FIFO
5.9.4.1. General
Simple FIFO
本ライブラリは、1ユーザからの書き込みと1ユーザからの読み出しを排他制御なしでFIFO機能を実現します。 複数のユーザからの書き込みと複数ユーザからの読み出しを行いたい場合は、排他制御が必要です。
5.9.4.2. 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用いたコピーなどを想定しています。 |
5.10. Power Management
5.10.1. Overview
Power Management
は、Spresense SDK の特徴的な機能である省電力を実現するために電源管理を行うモジュールです。CXD5602 内部の電源ドメインやクロックの管理、及び、各種スリープモードのサポートを行います。
5.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 はサポートしておりません。 |
5.10.2.1. 電源状態遷移
主な電源状態の状態遷移図を以下に示します。
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 している状態です。 |
5.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) |
(*1) PM_BOOT_DEEP_WKUPS と PM_BOOT_DEEP_RTC の両方の起動マスクを禁止にすることはできません。
(*2) Spresenseボードではサポートされていません。
電源状態制御に関連して、次の Power Management API が提供されます。
-
起動要因の取得
-
起動マスクの取得
-
起動マスクの許可・禁止
-
Sleep モードへの遷移
-
リブート
5.10.3.2. 起動マスクの取得 API
- Function Prototype
uint32_t up_pm_get_bootmask(void);
- Description
-
-
起動要因として何が許可・禁止されているかを表す起動マスクを取得します
-
デフォルトでは、全ての起動要因が許可されています
-
Deep Sleep、もしくは、Power-On-Reset により、この起動マスク値はリセットされます。 Hot Sleep もしくは Cold Sleep 中はこの起動マスク値は保持されています。
-
-
5.10.3.3. 起動要因の許可 API
- Function Prototype
uint32_t up_pm_set_bootmask(uint32_t mask);
- Description
-
-
引数で指定した起動要因を有効にします
-
戻り値は、更新後の起動マスクを返します
-
5.10.3.4. 起動要因の禁止 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
5.10.3.5. 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 1
5.10.4. スリープモード
5.10.4.1. Deep Sleep
5.10.4.1.1. 特徴
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 へ電流がリークしないように注意して下さい
-
5.10.4.1.2. 消費電力
-
バッテリーの消費電流は、バッテリー端でおよそ 数uA ~ 数+uA程度になります。ただし、この値は基板の設計に依存します。CXD5602 が電源 OFF されるため、周辺デバイスから CXD5602 へのリーク電流が発生しないように注意して基板を設計してください。
Sleep 中の消費電力に関して、拡張ボードに SD カードが挿入されていると SD カードの電源消費分により 約 5 mA ほど消費電流が増加します。 |
5.10.4.1.3. スリープ条件
-
up_pm_sleep(PM_SLEEP_DEEP) や board_poweroff(BOARD_POWEROFF_DEEP) を呼び出すことによって、Deep Sleep 状態に遷移します
-
WKUPL 信号を使用している場合は、WKUPL が 3 秒以上アサートされた場合に、Deep Sleep 状態に遷移します
Spresense 基板では WKUPL 端子が出ていないため、WKUPL 信号による Deep Sleep への遷移機能を使用することはできません |
5.10.4.1.4. 起床条件
起床条件が発生すると、プログラムは 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 からの起床機能を使用することはできません |
5.10.4.2. Cold Sleep
5.10.4.2.1. 特徴
-
CXD5602 は、チップ内部の PMU 電源ドメインのみ ON された状態です
-
CXD5602 I/O ピンは有効になっています
-
Backup SRAM の内容は保持されます
-
-
CXD5247 は通常動作状態です
-
CXD5247 RTC 時刻は保持されます(システムが RTC XTAL を持っていることが前提です)
-
CXD5247 GPO スイッチは Cold Sleep 中も値が保持されます
-
CXD5247 Load Switch は Cold Sleep 中も値が保持されます
-
5.10.4.2.2. 消費電力
-
バッテリーの消費電流は、バッテリー端でおおよそ数百 uA です。この電流値は基板の設計に依存します。
Sleep 中の消費電力に関して、拡張ボードに SD カードが挿入されていると SD カードの電源消費分により 約 5 mA ほど消費電流が増加します。 |
5.10.4.2.3. スリープ条件
-
up_pm_sleep(PM_SLEEP_COLD) や board_poweroff(BOARD_POWEROFF_COLD) を呼び出すことによって、Cold Sleep 状態に遷移します
5.10.4.2.4. 起床条件
起床条件が発生すると、プログラムは 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 からの起床機能はサポートされていません |
5.10.4.3. Hot Sleep
現在のSDKバージョンでは、Hot Sleep はサポートしておりません。 |
5.10.4.3.1. 特徴
-
CXD5602 は、通常動作状態にほぼ等しいですが、NuttX が 動作しているアプリケーション CPU は電源 OFF されます
-
但し、Hot Sleep 期間中も SRAM の値は保持されています
-
-
CXD5247 は通常動作状態です
5.10.4.3.3. スリープ条件
-
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 状態への遷移は禁止されます
-
5.10.5. 省電力制御
Spresense SDK は、以下のような省電力機能を提供します。
-
CPU システムクロック制御
-
CPU スリープ制御
5.10.5.1. 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);
HV, LV ロックの取得/解放に応じてシステム全体のクロックが動的に変更されますが、 例えば、SPI 転送中などクロック変更を避けたい期間が存在するかもしれません。 PM_CPUFREQLOCK_FLAG_HOLD フラグを使用することで、そのフラグがロックされている期間は、 システムクロックの変更を抑止することができます。
PM_CPUFREQLOCK_FLAG_HOLD フラグを同一スレッドから二重にロックしないようにしてください。 PM_CPUFREQLOCK_FLAG_HOLD フラグの実装にセマフォを使用しているため、 同一スレッドから二重にロックする(up_pm_acquire_freqlock()を連続で呼び出す)とそのスレッドがデッドロックします。 |
-
HOLD ロック獲得
struct pm_cpu_freqlock_s lock; lock.flag = PM_CPUFREQLOCK_FLAG_HOLD; up_pm_acquire_freqlock(&lock);
-
HOLD ロック解放
up_pm_release_freqlock(&lock);
5.10.5.2. 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);
5.11. Sensor Fusion Framework
5.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図は以下の通りです。
5.11.2. Sensor Manager
"Sensor Manager" は、複数の"Sensor Client"の登録、管理を行い、 適切にセンサデータの配信を行います。
5.11.2.1. General
"Sensor Manager" に登録された、"Sensor Client"は、取得したデータを"Sensor Manager" に打ち上げることで、"Sensor Manager" が、Subscribeを要求している "Sensor Client" に適切にデータを配信します。
また、Subscribeされていない"Sensor Client"の電源モードを落とせるような枠組みを提供します。
5.11.2.2. 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 |
5.11.3. Logical Sensors
5.11.3.1. General
"Logical Sensor"とは、各種の物理センサから得られたセンサデータを何らかの信号処理アルゴリズムなどに基づき、高機能なセンサデータを作成するための"Sensor Client"で、ソフトウェアモジュールで構成されています。
実際のアルゴリズムの実装アロケーションは、下記のいくつかの方法があります。
-
NuttX上のタスクとして実装する。
-
asmpフレームワークを使って、DSP上に実装する。
-
各社ベンダが提供した暗号化されたDSPを用いて実装する。
5.11.3.1.1. A logical sensor task on NuttX.
NuttX上のモジュール、もしくはタスクとして実装をします。
特に、重くない処理であったり、頻度の多くない処理の場合、このように実装することで、メモリ、CPUリソースなどを消費せず実装が可能です。
Contents | Sample provider |
---|---|
Barometer |
From sensor vender |
TAP Gesture (Tapping Detector) |
From SDK |
5.11.3.1.2. 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 |
5.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. |
5.11.6. Step Counter
"Step Counter" の構成図を以下に示します。
5.11.6.1. Supervisor
スーパーバイザは"Logical Sensor"のためのフレームワークです。 スーパーバイザのDSP上のWorkerを制御するためのいくつかのAPIを提供します。
5.11.6.1.1. 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 . |
5.11.6.1.2. Data format for Step Counter
"Step Counter"には、以下のように指定されたフォーマットで"Accelerometer"のデータが必要です。
これらのデータをWriteのAPIでWorkerに送信します。
5.11.6.1.3. Result of sensing
"Step Counter”は1秒ごとに以下の歩数計情報を出力します。 これらの情報は、StepCounterStepInfo に格納されます。
情報 | 単位 | 備考 |
---|---|---|
テンポ |
Hz |
1~3Hzの値をとります。停止状態時も1Hz以上の値となります。 |
歩幅 |
cm |
歩行と走行でそれぞれ固定値となります。 |
速度 |
m/s |
|
累積移動距離 |
m |
|
歩数 |
- |
|
行動認識 |
- |
停止/歩行/走行状態を示します。 |
5.11.6.2. How to use
5.11.6.2.1. 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(®);
5.11.6.2.2. 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);
5.11.6.2.3. 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にデータを送信し、センシング処理を行います。
この際のシーケンスを以下に示します。
5.11.6.2.4. Close
"Step Counter"の無効化には、StepCounterClose(FAR StepCounterClass*) を呼ぶ必要があります。
StepCounterClass*には、StepCounterCreateの戻り値である、"Step Counter"のインスタンスへのポインタを指定する必要があります。
StepCounterClose(step_counter_instance);
5.11.6.3. 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
5.11.6.4. Worker
Workerは他のCore上で動作し、Supervisorから送信されたセンサデータを分析します。
そして、要求されたイベントの処理結果(ステップ数、状態など)を返します。
"Step Counter"のWorkerは、分析のために以下に示すデータを必要とします。
・ Accelerometerデータ(32Hz、32サンプル/1秒)
5.11.6.4.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;
5.11.6.5. Step Counter Example
"Step Counter"のsample applicationとして、"Step Counter" exampleがあります。ここでは、その使い方などを説明します。
5.11.6.5.1. 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
メモリ管理ライブラリ(Memory Manager)とタスク間通信ライブラリ(Message Library)の設定は、以下のように行ってください。
"Step Counter"を使用する際に必要となるMemoryLayout(pool)の定義を行う必要があります。
定義はMemoaryLayout定義ファイルで行い、ツールでコードに組み込むヘッダファイルを生成することが出来ます。
"Step Counter"のsample applicationでは下記のように行います。
cd examples/step_counter/config python3 mem_layout.conf
"mem_layout.h", "fixed_fence.h", "pool_layout.h" ファイルが生成されます。
"mem_layout.h"ファイルは、後述する msgq_layout ツールの実行時に参照されます。
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],
["GNSS_DATA_BUF_POOL", "SENSOR_WORK_AREA", U_STD_ALIGN, U_GNSS_DATA_BUF_POOL_SIZE, U_GNSS_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を参照してください。 設定が変わった場合は、ツールを使って新しいヘッダーファイルを生成してください。 |
"Step Counter"を使用する際に必要となる"MessageQueue"の定義を行う必要があります。 定義はMessageQueueLayout定義ファイルで行い、ツールでコードに組み込むヘッダファイルを生成することが出来ます。
"Step Counter"のsample applicationでは下記のように行います。
cd examples/step_counter/config python3 msgq_layout.conf
"msgq_id.h", "msgq_pool.h" ファイルが生成されます。
mem_layout.conf で生成されたファイルを含めて全てのヘッダファイルを include 以下にコピーします。
cd examples/step_counter/config mv *.h ../include
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を参照してください。 設定が変わった場合は、ツールを使って新しいヘッダーファイルを生成してください。 |
5.12. JPEG Decoder
5.12.1. 概要
IJGが開発したlibjpegライブラリをベースにしたJPEGデコード機能を提供します。
概ねオリジナルlibjpegライブラリのAPI仕様に準じておりますが、Spresense向けにカスタマイズしている点がありますので、 このドキュメントで、カスタマイズ内容について詳述します。
以下の説明において、「libjpegインスタンス」とは、libjpeg利用アプリケーションが用意しなければいけないstruct jpeg_decompress_struct型変数を指します。
5.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 |
× |
× |
「エラー」にはプログラム異常によるものの他に、外部要因によるものあります。代表的なエラーについては標準エラー出力への出力参照。 |
5.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に到達した場合に出力されます。 |
5.13. LTE
5.13.2. 用語・略語
用語 | 説明 |
---|---|
PDN |
Packet Data Network |
APN |
Access Point Name |
IMS |
IP Multimedia Subsystem |
IMSI |
International Mobile Subscriber Identity |
IMEI |
International Mobile Equipment Identifier |
eDRX |
extended Discontinuous Reception |
PSM |
Power Saving Mode |
CE |
Coverage Enhancement |
RAT |
Radio Access Technology |