1. Spresense SDK 概述
Spresense SDK 是用于控制 Sony Semiconductor Solutions Corp. 为 CXD5602 开发的 CXD5602 的软件。 CXD5602 的内部块如下。
![CXD5602 blockdiagram](images/CXD5602_blockdiagram.png)
CXD5602由四个称为域的块组成。
-
Application Domain
-
该块主要由用户应用程序控制,并安装了六个CPU(ARM制造的ARM Cortex®-M4F)。
-
-
System IOP Domain
-
这是用于启动系统和管理 CXD5602 中的电源域的模块,并且配备了ARM Cortex-M0。
-
-
GNSS Domain
-
该块使用卫星进行定位,并配备有ARM Cortex-M4F。
-
-
Sensor Domain
-
一个模块,可从连接到I2C和SPI总线的传感器获取数据,而无需CPU的干预。
-
有关 CXD5602 的更多信息,请参考 索尼半导体解决方案有限公司Spresense网站 。 |
Spresense SDK 是用于控制上述硬件的软件。 Spresense SDK 使用RTOS之一的 NuttX 作为OS,并提供了详细的功能以基于该OS发挥 CXD5602 的性能。 Spresense 的每个驱动程序都是根据类似于Linux的 NuttX 的驱动程序框架以及 Spresense 的Audio,GNSS,ASMP等中间件层在驱动程序层之上实现的。
![sdk overview](images/sdk_overview.png)
每个中间件和特性驱动程序的概述如下。
模块名 | 摘要 |
---|---|
Audio Middleware |
提供 Spresense SDK 的音频功能。对应于各种数据路径,除了以各种格式记录和回放之外,还可以实现语音处理。 |
Sensor Framework |
当输出和处理 Spresense SDK 的各种传感器数据时,它成为一个框架,可以轻松地使用发布/订阅体系结构对传感器的交换或处理数据进行编程。 |
Memory Utility |
Spresense SDK 提供了具有参考计数器的固定大小的内存池功能以及用于异步传输和接收实例的任务同步机制。 |
ASMP Framework |
除了管理 CXD5602 的多核用户程序的加载之外,还管理着12个切片( CXD5602 的存储结构的功能之一)。 |
Power Management |
提供节能功能。 |
GNSS Driver |
CXD5602 具有执行GNSS定位的硬件子系统。该驱动程序与子系统交互,并为用户提供与GNSS相关的功能,就像POSIX I/F字符设备一样的I/F。 |
有关每个模块的详细信息,请参见“ Spresense SDK 提供的功能” 一章。
2. 许可证
与 NuttX 一样, Spresense SDK 在BSD 3条款许可下以开源形式发布。详细的许可条款如下。
3. 系统启动顺序
从复位释放 CXD5602 时,BootCPU启动。 与此BootCPU配合使用的是loader.espk。 当loader.espk启动时,控制权转移到loader.espk,后者加载nuttx.spk并启动应用程序CPU。
由于nuttx.spk是使用此SDK构建的二进制文件,因此nuttx.spk的执行内容取决于构建设置和应用程序行为。
如果您的应用程序使用GNSS功能,则在调用初始化API时,loader.espk将加载gnss.espk。
因此,使用 Spresense 板需要loader.espk和gnss.espk二进制代码。 请参考 引导加载程序安装 ,以获取二进制文件。
4. 软件配置管理
本章概述了 Spresense SDK 源代码。
4.1. 知识库
Spresense SDK 由以下两个存储库组成。
仓库名称 | 子库 | 説明 |
---|---|---|
使用 Spresense SDK 主存储库准备开发环境时,请克隆此存储库。它包括 Spresense BSP,受 Spresense 支持的驱动程序,各种示例代码,并将spresense-nuttx,spresense-nuttx-apps作为子模块进行引用。 |
||
Spresense NuttX克隆存储库。 Spresense 中的内核由此存储库管理。 |
||
Clone repository from NuttX Apps (v2.0 or later). And this is the NuttX original application of Spresense. |
4.2. 源树
Spresense SDK 的源树结构如下:
![spresense directory structure2](images/spresense_directory_structure2.png)
每个目录的内容如下:
目录名 | 説明 |
---|---|
spresense |
spresense是Clone创建的目录 |
externals |
Spresense SDK external libraries |
examples |
包含使用 Spresense SDK 支持的驱动程序和模块的示例代码 |
nuttx |
Spresense 包含NuttX内核源代码 |
sdk |
包括 Spresense SDK 支持的驱动程序和模块 |
apps |
Spresense 包含NuttX original application内核源代码 (v2.0 or later) |
configs |
包括Spresense SDK提供的配置 |
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
和 Architectural specific code
。
![Diagram](images/diag-7b3a8011eb336e2a51fcad7d4f0a6496.png)
5.1.1.1. 驱动 (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设备驱动程序 。
5.1.1.3. 板载特定代码
某些特定于板的处理过程可能会根据板的实现方式(引脚设置等)而改变。这些过程分为可以在一定程度上共享的过程和完全取决于董事会的过程。
它们分别存储在 bsp/board/common
和 bsp/board/<board name>
中,其中 <board name>
是对应的董事会名称。
这还包括 电路板初始化过程 。
5.1.2. 目录结构
sdk/bsp
|-- board
| |-- common
| `-- spresense
|-- include
| |-- arch
| | `-- chip
| |-- nuttx
| | |-- lcd
| | |-- sensors
| | `-- video
| `-- sdk
|-- scripts
`-- src
目录名 | 内容 |
---|---|
|
尽管它是特定于电路板的处理,但处理顺序相同,并且包括可通过更改宏共享的例程。 |
|
Spresense 包括用于操作电路板的处理(引脚设置等)。 |
|
包含用于调用由SoC中安装的设备驱动程序提供的API的头文件。 |
|
包含与体系结构无关的设备的头文件。(此目录名称是为了与NuttX内核兼容。) |
|
Spresense 包含SDK中的常见头文件。 SDK的配置标头(config.h)是在构建时生成的。 |
|
包含 |
|
包括Low Half驱动程序和特定于芯片的设备驱动程序。 |
5.2. GPIO/Pin 规格
5.2.2. GPIO 驱动接口
gpioif 为应用程序提供以下功能:
-
GPIO引脚设置
-
功能模式
-
输入/输出设置
-
驱动电流/摆率设置
-
上拉/下拉设置
-
-
GPIO中断设定
-
电平/边沿触发设置
-
噪音过滤器设定
-
-
GPIO I/O控制
-
GPIO 状态
有关API详细信息,请参考 这里 。
5.2.2.1. GPIO 应用工具
GPIO命令是在系统工具中准备的, 可以通过设置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 规格
引脚分配有多种功能,可以通过模式(0-3)更改功能。 将各种引脚分配给引脚组,并通过模式规范按引脚组单位更改功能。
例如,PIN_PWM2,3
![boardspec pinmode](images/boardspec_pinmode.png)
-
在
Mode0
中,PIN_PWM2和PIN_PWM3引脚均设置为GPIO功能 -
在
Mode1
中,PIN_PWM2和PIN_PWM3引脚均设置为PWM功能 -
在
Mode2
中,PIN_PWM2和PIN_PWM3引脚均设置为I2C功能
换句话说,由于功能是以组为单位分配的,因此无法将PIN_PWM2设置为GPIO,并且无法将PIN_PWM3单独设置为PWM。 |
默认情况下,所有引脚都设置为 Mode0(GPIO)
。
5.2.3.1. Pin 表
-
Pin Name: nuttx/arch/arm/include/cxd56xx/pin.h 以
PIN_XXX
形式定义 -
WLCSP: 100引脚封装(CXD5602GF)的定义。。有一些无法使用的PIN。
-
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 配置
使用pinconfig驱动程序配置引脚设置。例如,当使用Mode0(GPIO)以外的I2C和SPI功能时,分别在I2C和SPI驱动器中设置了引脚。 因此,应用程序无需知道模式等的变化。
仅当将其用作Mode0(GPIO)时,请使用上述 gpioif 进行设置。
5.2.3.2.1. 电路板特定的引脚拉力和驱动电流设置
引脚拉动设置和驱动器电流默认设置,定义于 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引脚上的下拉
-
将SPI4驱动电流从2mA更改为4mA
-
PWM驱动电流从2mA变为4mA
上述。
5.3. Audio 子系统
5.3.1. 常用
CXD5602具有可处理高分辨率的音频功能(音频子系统)。 以下是音频子系统的功能概述。
-
Audio Codec硬件(AD/DA、DNC、DEQ等)控制
-
Aduio Player功能
-
Audio Recorder功能
-
Bluetooth功能(如:BT-A2DP)
-
Sound Effector功能(例如,语音通话的带通滤波器)
本文档介绍了可在CXD5602硬件上实现的用于控制音频功能的软件。有关音频硬件,请参考单独的外部参考: Spresense硬件相关文档 。
当前固件不支持蓝牙相关功能(用于BT-A2DP)和音效器功能(例如,用于语音通话的带通滤波器)。 |
5.3.2. 关于层结构
音频子系统堆栈图如下所示。
![Audio 子系统堆栈图](images/AudioStack.png)
音频子系统具有三个主要层。
- Audio Manager(High Level API)
-
这是控制最高抽象级别的顶层。 控制整个系统。
- Object Layer(ObjectLevel API)
-
这是一个控制每个功能对象层中每个功能块抽象级别的层。在每个功能中匹配信号处理和DSP处理。
- Component Layer(Low Level API)
-
该层控制每个信号处理组件层中每个信号处理块的抽象级别。通过将处理与信号处理模块组合来配置,可以实现具有高自由度的音频处理。
5.3.3. 顶层 API
顶层API是由 Audio Utility
中 Audio Manager
提供的。
5.3.3.1. 命令控制发送/接收
顶层API把音频及其子系统作为命令对象进行控制。(High Level API Command System)
命令对象由 AS_SendAudioCommand 向音频及其子系统发送。
被发送的命令对象为 AudioCommand 。
命令的详细记载于 命令对象 。
音频及其子系统,根据发送过来的命令进行处理并返回结果。
结果通过 AudioResult 对象返回,可由 AS_ReceiveAudioResult 获取。
结果的详细记载于 结果格式 。
这个是同步命令。发出命令后,直到返回结果后才能发出下一条命令。 通过 Audio Manager 使用顶层API时,将发送和接收过程编程为以一个命令为单位进行配对。 |
![顶层API命令系统](images/Audio_HighLevel_Command_System.png)
5.3.3.1.1. 控制数据格式(命令格式)
命令对象 AudioCommand 是以1 字(4 字节)的 AudioCommandHeader 开始,并随后根据需要添加尽可能多的参数区域的数据结构。
命令对象是以字单位为基础的,长度为1 字(32 比特、4字节)的整数倍。
命令对象的reserved 区域请设为0 。
命令对象 AudioCommand 是一种数据结构,以 AudioCommandHeader 的一个字(4个字节)开头,然后根据需要添加尽可能多的参数区域。
typedef struct
{
AudioCommandHeader header;
union
{
Command Parameters (Payload)
...
...
};
} AudioCommand;
由于命令对象基于字单元,因此它由一个字的整数倍(32位,4字节)组成。这个单词称为命令头( AudioCommandHeader )。
typedef struct
{
uint8_t reserved;
uint8_t sub_code;
uint8_t command_code;
uint8_t packet_length;
} AudioCommandHeader;
- packet_length
-
表示包含命令头的命令对象长度。
所有的命令对象由整数个字(4字节)构成,
packet_length指定的值为命令包的字长,即命令对象的字节长的1/4。 - command_code
-
命令专用代码。不使用0x00。
命令的类别,请参考 命令一览 。 - sub_code
-
各命令中用于识别设定及控制对象的代码。
5.3.3.1.2. 通知的数据格式(结果格式)
结果对象由1个字(4字节)的 AudioResultHeader 开始,后跟所需的参数区域。
结果对象是以字单位为基础的,长度为1字(32 比特、4字节)的整数倍。
忽略结果对象中的保留字段。
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指定的值为结果数据包的字长,即结果数据包字节大小的1/4。 - result_code
-
结果数据包种类识别用代码。
结果数据包种类,请参考 结果一览 。 不同的代码,参数区域中的数据内容会变化。 - sub_code
-
里面是和执行命令的sub_code相同的值。
5.3.3.2. 状态迁移
顶层API有多种状态。 以下为状态迁移图。
![Diagram](images/diag-1cd1ed11837767c5451b2aedcd69ede8.png)
各模式说明如下。
-
PowerOff状态
生成并启动音频及其子系统的对象后的状态。不使用音频时,迁移到此状态,音频功能块的耗电量基本为0。
使用 AUDCMD_POWERON 命令,仅迁移到Ready状态。
-
Ready状态
音频功能块上电,动作模式为音频功能准备状态。此状态下,耗电量虽不下降,但由于IO/模拟电路已启动,可以快速状态迁移。
状态的迁移如下所示。
使用 AUDCMD_SETPOWEROFFSTATUS 命令,可迁移到PowerOff状态。 使用 AUDCMD_SETPLAYERSTATUS 命令,可迁移到Player状态。 使用 AUDCMD_SETRECORDERSTATUS 命令,可迁移到Recorder状态。 使用 AUDCMD_SETBASEBANDSTATUS 命令,可迁移到Baseband状态。
-
Player状态
从SD卡等存储设备或WiFi/LTE等网络设备解码声音压缩文件,实现AnalogOut或I2S的发音功能的状态。状态中有PlayerReady状态和PlayerActive状态,2个子状态。 PlayerReady状态为音乐播放停止的状态。使用 AUDCMD_PLAYPLAYER 迁移到PlayerActive进行音乐播放动作。 PlayerActive状态为音乐播放状态。使用 AUDCMD_STOPPLAYER 迁移到PlayerReady停止音乐播放。
使用 AUDCMD_SETREADYSTATUS 命令,仅迁移到Ready状态。
-
Recorder状态
这是一种状态,可实现压缩从麦克风输入的语音数据并将其写入SD卡等存储设备,或在WiFi / LTE等网络上启动并记录该功能的状态。 状态由RecorderReady状态和RecorderActive状态这两种子状态构成。 RecorderReady是声音记录停止的状态。通过 AUDCMD_STARTREC 迁移到RecorderActive状态,开始声音记录。 RecorderActive状态为声音记录中的状态。通过 AUDCMD_STOPREC 迁移到RecorderReady状态,停止声音记录。
通过 AUDCMD_SETREADYSTATUS 命令,只能迁移到Ready状态。
-
Baseband状态
这种状态实现了内部处理从Mic输入的音频数据并将其输出到AnalogOut或I2S的功能。 状态有BasebandReady状态和BasebandActive状态两个子状态。 BasebandReady状态为声音输出入停止的状态。通过 AUDCMD_STARTBB 迁移到BasebandActive开始声音的输出入动作。 RecorderActive状态为声音输出入时的状态。通过 AUDCMD_STOPBB 迁移到BasebandReady停止声音的输出入动作。
通过 AUDCMD_SETREADYSTATUS 命令,只能迁移到Ready状态。
NOTE:现在的固件未对应Baseband状态。
5.3.3.3. 命令概览
各命令的概览如下。
命令头部 中指定命令ID发送可以使用相关功能。
5.3.3.3.1. 普通或常用命令
任何状态通用的命令。 任何状态都可调用。
命令名 | 命令编号 | 响应结果 | 描述 |
---|---|---|---|
0x01 |
通知状态 |
取得现在的状态。 |
详细信息,请参考如下Doxygen文件。
5.3.3.3.2. Baseband 初始化命令
进行Baseband HW初始化的命令。 仅可由Ready状态调用。
命令名 | 命令编号 | 响应结果 | 描述 |
---|---|---|---|
0x53 |
InitMicGainCmplt |
麦克风增益的设定 |
|
0x54 |
InitI2SCmplt |
I2S的设定 |
|
0x56 |
InitOutputSelectCmplt |
发声器件的设定 |
|
0x58 |
InitClearStereoCmplt |
清除立体声功能的设定 |
|
0x5c |
SetRenderingClkCmplt |
HiReso设定的切换 |
详细请参考如下Doxygen文件。
5.3.3.3.3. Baseband 设置命令
进来Baseband HW设定的命令。 PowerOff以外的状态可以调用。
命令名 | 命令编号 | 响应结果 | 描述 |
---|---|---|---|
0x59 |
SetVolumeCmplt |
发声时的音量设定 |
|
0x5a |
SetVolumeMuteCmplt |
发声音量的Mute设定 |
|
0x5b |
SetBeepCmplt |
BEEP音的设定 |
详细结果,请参考如下Doxygen文件。
5.3.3.3.4. 播放命令
Player的控制命令。 Player状态可以调用。
命令名 | 命令编号 | 响应结果 | 描述 |
---|---|---|---|
0x21 |
InitPlayCmplt |
Player播放信息的设定 |
|
0x22 |
PlayCmplt |
从缓存开头解码 |
|
0x23 |
StopPlayCmplt |
不依赖缓存状态,停止Player |
|
0x24 |
ClkRecoveryComplete |
发声时间的微调 |
|
0x25 |
SetDecoderGainComplete |
L/R分别对发声进行Gain |
详细信息,请参考如下Doxygen文件。
5.3.3.3.5. 录制命令
Recorder的控制命令。 Recorder状态可以调用。
命令名 | 命令编号 | 响应结果 | 描述 |
---|---|---|---|
0x31 |
InitRecCmplt |
初始化录音功能 |
|
0x32 |
RecCmplt |
开始录音 |
|
0x33 |
StopRecCmplt |
停止录音 |
详细信息,请参考如下Doxygen文件。
5.3.3.3.6. 状态切换命令
命令名 | 命令编号 | 响应结果 | 描述 |
---|---|---|---|
0x71 |
StatusChanged |
迁移至Ready状态 |
|
0x72 |
StatusChanged |
迁移至Power Off 状态 |
|
0x73 |
StatusChanged |
迁移至Baseband状态 |
|
0x74 |
StatusChanged |
迁移至Player状态 |
|
0x75 |
StatusChanged |
迁移至Recorder状态 |
|
0x76 |
StatusChanged |
迁移至Ready状态 |
|
0x77 |
StatusChanged |
迁移至Audio path through状态 |
详细信息,请参考如下Doxygen文件。
5.3.3.4. 结果概览
音频子系统的结果返回通知。
结果头部 中保存结果ID的方式返回。
5.3.3.4.2. Baseband 初始化结果
结果名 | 命令编号 | 触发命令 | 描述 |
---|---|---|---|
0x53 |
InitMicGain |
通知麦克风增益的设定完成。 |
|
0x54 |
InitI2SParam |
通知I2S的设定完成。 |
|
0x56 |
InitOutputSelect |
通知发声设定的设定完成。 |
|
0x58 |
InitClearStereo |
通知清除立体声功能的设定完成。 |
|
0x5c |
InitRenderClk |
通知HiReso设定的切换完成。 |
5.3.3.4.3. Baseband 结果设置
结果名 | 命令编号 | 触发命令 | 描述 |
---|---|---|---|
0x59 |
SetVolume |
通知发声时的音量设定完成。発 |
|
0x5a |
SetVolumeMute |
通知发声音量的Mute设定完成。 |
|
0x5b |
SetBeep |
通知BEEP音的设定完成。 |
5.3.3.4.4. 播放结果
结果名 | 命令编号 | 触发命令 | 描述 |
---|---|---|---|
0x21 |
InitPlayer |
通知Player的播放信息设定完成。 |
|
0x22 |
StartPlayer |
通知Player已经开始工作。 |
|
0x23 |
StopPlayer |
通知Player已经停止工作。 |
|
0x24 |
ClkRecovery |
通知发声时间的微调设定完成。 |
|
0x25 |
SetDecoderGain |
通知发声L/R的Gain设定完成。 |
5.3.3.4.5. 录制结果
结果名 | 命令编号 | 触发命令 | 描述 |
---|---|---|---|
0x31 |
nitRecorder |
通知录音功能的初始化完成。 |
|
0x32 |
StartRecorder |
通知录音已经开始。 |
|
0x33 |
StopRecorder |
通知录音已经结束。 |
5.3.3.4.6. 状态转换结果
结果名 | 命令编号 | 触发命令 | 描述 |
---|---|---|---|
0x71 |
PowerOn SetPowerOffStatus SetBaseBandStatus SetPlayerStatus SetRecorderStatus SetReadyStartus |
通知状态迁移已经完成。 |
5.3.3.7. 存储管理和任务间同步相关
5.3.3.7.1. 管理内存库
AudioSubSystem通过特殊的方法管理使用的数据区域。 MemoryUtility的MemoryManager库,根据Memory Layout定义文件的Layout信息,以固定长度的存储池的方式确保必须的存储区域。 此Layout信息可重复定义,通过切换Layout号的指定,可以确保各功能的必须存储。 Layout信息,可根据Application需求自由指定,但需注意各功能所需的最低限资源。
详细信息,请参考 内存管理 库说明。
另外,各功能合计所需的Layout相关,请参考各example的说明。
AudioSubSystem内的各对象,通过生成指向所需存储空间的MemHandle实例,确保并使用链接到实例的存储片段。
![Get memHandle](images/memhandle_example1.png)
传递此MemHandle的实例给数据管道的下一个对象,下一个对象也可使用确保的存储区域。不需要时,废弃此实例就看释放存储空间。
实例可拷贝,所需对象可各自确保自己的间实例。不要时根据自己的需要废弃,也可以安全地确保/释放存储空间。
![Copy memHandle](images/memhandle_example2.png)
片段的使用不需要时,非同步的对象不需要在意存储管理,通过废弃实例,默认引用终止。
![Use memHandle](images/memhandle_example3.png)
如此,非同步对象间的存储管理变得简单。 所有的引用终止后,存储空间会被释放。
![Release memHandle](images/memhandle_example4.png)
Layout信息作为Application使用的头文件群,需要预先准备。 这些头文件,通过作成Memory Layout定义文件(mem_layout.conf),使用工具生成。
- 工具的使用方法
python3 mem_layout.conf [layout_header] [fence_header] [pool_header]
mem_layout.conf |
Memory Layout定义文件 |
layout_header |
作为C语言的宏输出的各常数的头文件 |
fence_header |
输出FixedArea的存储栏地址的头文件 |
pool_header |
输出PoolArea的各定义的头文件 |
5.3.3.7.2. 消息库
使用此存储管理机制时,各任务间需要任务对象的收发信。因此,AudioSubSystem使用在任务同步机制中准备的可以在任务实例间进行收发信的message库。
各任务对象通过追加收信端的ID,可以往指定的任务送信。
举例来说,对象送信时发送收信端的ID,收信端的任务仅在自己的ID发生送信要求时才收信。 发生收信前,此任务sleep并等待。
如此,AudioSubSystem以事件驱动进行对象设计。
Message相关,需要事先准备Message使用的头文件群。 这些头文件,通过作成MessageQueueLayout定义文件(msgq_layout.conf),使用工具生成。
- 工具的使用方法
python3 msgq_layout.conf [start_address] [size] [id_header] [pool_header]
msgq_layout.conf |
Message Layout定义文件 |
start_address |
Message区域的地址 |
size |
Message区域的尺寸 |
id_header |
输出MessageQueueID宏的文件 |
pool_header |
输出MessageQueuePool定义的文件 |
详细请参考消息库 库说明。
5.3.3.7.3. 简化 FIFO 库
AudioSubSystem和用户应用程序间的音频数据的传递,使用Simple FIFO。此FIFO是简化的FIFO,无需特别介绍。
详细请参考 简化 FIFO 库 的库说明。
5.3.3.9. Audio Player 功能
Audio Player 的简单的数据流如下所示。
![Audio Player Dataflow](images/Audio_player_dataflow.png)
音频及其子系统运行PlayerMode时,User Application往FIFO里输入ES数据。 累积了一定量的数据后,Player启动,根据发声时间,消耗这个ES数据。这个FIFO不发生下溢出时,音频数据不会断续。
Player可以生成2个实例。 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。 |
![Audio Player Message ID](images/player_message_id.png)
另外,各数据的数据区域如下。
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
![Audio Player Pool ID](images/player_pool_id.png)
这些ID需要在生成时指定。
5.3.3.9.1. 使用方法
通过"AudioManager" 、"MediaPlayerObject"、"OuputpuMixerObject"、"RendererComponent" 等
为了控制音频子系统而设计的软件组件,实现Audio Player。
因此,为了实现Player,需要事先调用以下的对象生成函数。
将来,High Level API的生成函数有可能统合到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和player1Mix后播放
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);
![Diagram](images/diag-e2eb67f141f989814817e686cbf83d2e.png)
表示音乐播放的初始化及开始顺序。
通过AUDCMD_INITPLAYER 、 PlayerCommand 、 AsInitPlayerParam 进行播放的初始化设定。
AsPlayerId的实例的ID设定。有2个实例,请设定为其中1个。
实例编号 | 设定值 |
---|---|
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 的场合,由于DPS使用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_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);
![Diagram](images/diag-05054694fe75344e389f0bf22e1d6b52.png)
描述音频播放停止的顺序。
设定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);
![Diagram](images/diag-5ee87f4cb8c449f2e533e3e88579c0b0.png)
5.3.3.9.2. 编译配置
为了使用 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.9.3. 注意事项及方法
音乐播放时的警告一览和对应方法如下。详细请参考 关于音频子系统错误 。
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.9.4. DSP 安装
- DSP binary image install
-
请将DSP二进制映像放入Kconfig设定的路径下。二进制映像在
sdk/modules/audio/dsp
里。表格 16. Binary image required for audio player according to configuration: Image size MP3DEC
61kbyte
WAVDEC
32kbyte
进行高分辨率的采样率播放时,DSP请使用 2 Core(1Core使用192kB)。
※2Core的话,384kB。
请注意资源使用。
5.3.3.9.5. Audio Player 示例
Audio Player example是音乐播放的简单的示例程序。在此说明用法。
使用Audio Player 的示例程序前,请将build configuration如下设定。
[Examples] [Audio player example] <= Y
或者,
cd sdk tools/config.py examples/audio_player
无法同时选择Audio & Logical sensor example 和其他几个示例。选几个的话会出现编译错误。 |
请如下设定任务间通信库(Message Library)和内存管理库(Memory Manager)。
需要定义使用AudioPlayer功能时必须的MemoryLayout(pool)。 定义通过MemoaryLayout文件进行,可以使用工具生成包含代码的头文件。
Audio Player 的example中如下进行。
cd examples/audio_player/config python3 mem_layout.conf
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中如下进行。
cd examples/audio_player/config python3 msgq_layout.conf
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
-
请在SD卡的根目录下作成 "AUDIO/" 目录, 拷贝音乐文件到此。
- Playlist
-
管理需要播放的音乐文件。由 csv 文件生成数据可。文件名为 "TRACK_DB.CSV" 。
在SD卡根目录下作成 "PLAYLIST/" 目录, 拷贝 "TRACK_DB.CSV" 到此。
[filename],[artist],[album],[channel number],[bit length],[sampling rate],[file format]
ABC.mp3,artist1,album1,2,16,44100,mp3
example以使用Playlist为前提。仅播放列表的第一行。 |
由 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.10. Audio Recorder 功能
Audio Recorder的简单数据流如下所示。
![Audio Recorder 数据流](images/Audio_recorder_dataflow.png)
当音频子系统在RecorderMode下运行时,用户应用程序将 必须准备一个FIFO来存储ES数据。 当开始记录音频数据时,在操作一定时间后,音频数据将累积在此FIFO中。该音频数据以指定的压缩格式编码, 可以通过从FIFO中适当地读取音频数据并且不使FIFO溢出来获取连续音频数据。
记录器可以捕获两行作为硬件,但目前不支持生成两个实例并记录两行的功能。
用户应用程序通过根据每个系统的要求处理此音频(例如,将其写入Strage并进行记录,将其发送到Connectivity模块进行云处理等)来实现Recorder应用程序。
数据流内部与Message通信。 消息通信具有每个客户端的ID。 对于Audio Recorder,根据示例示例中的布局,下面显示了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将被删除。
![Audio Recorder 消息编号](images/recorder_message_id.png)
每个数据的数据区域如下。
PCM (输入) 数据缓存 : 数据缓存
ES Data Buffer (for DSP) : ES_BUF_POOL
PreProcess DSP Command : PRE_APU_CMD_POOL
Audio Encoder DSP Command : ENC_APU_CMD_POOL
![Audio Recorder 池塘编号](images/recorder_pool_id.png)
这些ID必须在生成时指定。
5.3.3.10.1. 使用方法
“AudioManager”,“MicFrontendObject”,“MediaRecorderObject”,被设计来控制音频子系统称为“CaptureComponent”,实现了录音机的软件组件。
生成必要的对象后,将执行诸如音频硬件设置,开机和操作模式更改之类的初始化处理,以执行记录器操作。
这可以通过按顺序发出以下命令来实现。
要打开音频模块,请发出 AUDCMD_POWERON 、 PowerOnParam 命令以打开电源,并将AudioSubSystem状态更改为Ready状态。
enable_sound_effect固定为AS_DISABLE_SOUNDEFFECT。
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);
用 AUDCMD_INITMICGAIN 设置麦克风增益。
对于模拟麦克风,可以将dB值乘以10得到的值可以在0(0.0dB)到210(21.0dB)的范围内设置,以5的倍数表示。 默认值为0.0dB。
对于数字麦克风,可以在-7850(-78.50dB)到0(0.00dB)的范围内设置将dB值乘以100得到的值。默认值为-78.50dB。
如果不想更改增益值,请指定 AS_MICGAIN_HOLD
。
以下是将21 dB的增益应用于1ch至4ch输入的设置示例。 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通道选择图”的值设置。 默认设置为模拟麦克风1/2/3/4。
配置信息如下所述。
[Device Drivers] [Board specific drivers] [CXD56 Audio Driver] [Audio baseband config settings] [CXD5247 settings] (0xFFFF4321) MIC channel select map
“MIC channel select map”的值每4位指示MIC ID。
mic_gain元素与“MIC channel select map”的位字段之间的关系如下。
mic_gain 要素 | [7] | [6] | [5] | [4] | [3] | [2] | [1] | [0] |
---|---|---|---|---|---|---|---|---|
位域 |
31-28 |
27-24 |
23-20 |
19-16 |
15-12 |
11-8 |
7-4 |
3-0 |
“MIC channel slect 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开始按顺序设置要使用的麦克风。不能通过跳过来设置元素编号。 不支持模拟和数字麦克风的混合。 设置模拟麦克风时设置元素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设备
指定用于存储输出(编码的ES数据)的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
/* 指定每帧的样本数。 */
指定从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
设置预处理类型。
AsMicFrontendPreProcThrough : Through
AsMicFrontendPreProcSrc : Sampling Rate Converter
AsMicFrontendPreProcUserCustom : User Custom Process
如果您指定 AsMicFrontendPreProcSrc ,则需要在 sdk /modules/audio/DSP/ 中使用 SRC 。
|
使用完整路径(包括文件名)为预处理指定DSP二进制文件。
当preproc_type为AsMicFrontendPreProcThrough时不使用。
"/mnt/sd0/BIN/PREPROC" : 将名为PREPROC的二进制文件放入SD卡的BIN文件夹中时 "/mnt/spif/SRC" : 当直接在SPI-FLASH下放置一个称为SRC的二进制文件时
从MicFrontendObject指定音频数据的输出目的地。
AsMicFrontendDataToRecorder : 发到录音设备
AsMicFrontendDataToRecognizer : 发到识别器
下图显示了在音频识别器功能中使用PreprocessDSP处理信号的位置。
在突出显示的区域中使用Customproc和UserCustomDSP执行信号处理。
用户创建信号处理并将其合并到此UserCustomDSP中。
![Audio recorder preprocess](images/Audio_recorder_preprocess.png)
以下是以每帧Mono/16bit/768sample per frame捕获音频时的设置示例。
预处理由UserCustom的DSP执行,DSP的二进制文件位于SD卡的BIN文件夹中。
捕获的音频被设置为用于录音机。
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_PREPROCESS_FILE_PATH_LEN,
"%s", "/mnt/sd0/BIN/PREPROC");
command.init_micfrontend_param.data_dest = AsMicFrontendDataToRecorder;
使用AUDCMD_INIT_PREPROCESS_DSP 、 AsInitRecorderParam 初始化DSP以进行预处理。 为ereProcess指定DSP二进制文件,其完整路径包括文件名。 AsMicFrontendPreProcThrough时不使用type。 当preproc_type为AsMicFrontendPreProcThrough时不使用。 当preproc_type为AsMicFrontendPreProcThrough时不使用。
如果在 AUDCMD_INIT_MICFRONTEND 中将 preproc_type 设置为 AsMicFrontendPreProcThrough ,则无需执行此步骤。
|
初始化命令包的地址。命令格式取决于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
、 doxygen: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
指定存储编码器或滤波器DSP映像的绝对路径。最多24个字符。
输入设备和通道数量的组合受到限制。 |
输入 | 通道数 |
---|---|
Mic |
1ch(Monoral), 2ch(Stereo), 4ch(*1), 6ch(*2), 8ch(*2) |
-
(*1. 仅LPCM)
-
(*2. 仅在使用LPCM和DigitalMic时)
编解码器,位长,采样频率和位速率的组合受到限制。 |
编码名 | 位长 | 采样频率 | 比特率 |
---|---|---|---|
MP3 |
16bit |
16kHz |
8000(*1), 16000 ~ 32000 |
48kHz |
32000 ~ 2560000 |
||
LPCM |
16bit |
16kHz, 48kHz |
- |
24bit(*2) |
16kHz, 48kHz, 192kHz(*2) |
- |
-
(*1. 指定1ch时)
-
(*2. 需要高分辨率模式)
以下是以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";
![Diagram](images/diag-cf49f33fa5ca6db7e508e7fc57cce311.png)
开始用 AUDCMD_STARTREC 录音。
开始录制后不久,音频系统会将ES数据写入FIFO。
为了正确记录音频数据,必须在FIFO溢出之前读取写入的数据。
您将收到有关写入数据的通知,因此请根据此事件适当阅读。
如果FIFO已满,则音频系统无法写入并丢弃无法写入的音频数据。因此,直接记录会导致音频数据不连续。 |
AudioCommand command;
command.header.packet_length = LENGTH_START_RECORDER;
command.header.command_code = AUDCMD_STARTREC;
command.header.sub_code = 0x00;
AS_SendAudioCommand(&command)
![Diagram](images/diag-32a7123dc4b2f56ae58f932ee770d149.png)
- Stop Recorder.
-
停止使用 AUDCMD_STOPREC 录音。 当接收到停止指令时,记录器会在编码点停止,直到捕获的音频数据为止。
AudioCommand command;
command.header.packet_length = LENGTH_STOP_RECORDER;
command.header.command_code = AUDCMD_STOPREC;
command.header.sub_code = 0x00;
AS_SendAudioCommand(&command)
![Diagram](images/diag-b074920aba8aa65cd9098c358350c661.png)
5.3.3.10.2. 编译配置
使用AudioRecorder功能
cd sdk/ tools/config.py -m
打开配置菜单并设置以下配置。
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.10.3. 注意事项及方法
以下是音频录制过程中的警告列表以及如何处理它们。有关详细信息,请参见 关于音频子系统错误 。
编号 | 事件代码 | 事件级别 | Approach |
---|---|---|---|
0x06 |
AS_ATTENTION_SUB_CODE_SIMPLE_FIFO_OVERFLOW |
WARNING |
这是因为AudioSubSystem无法将记录数据输出到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二进制版本不同。使用 "sdk/modules/audio/dsp” 文件更新DSP二进制映像。 |
5.3.3.10.4. 安装 DSP
- DSP binary image install
-
将DSP二进制映像存储在Kconfig设置的路径中。 二进制映像位于
sdk /modules/audio/dsp
中。
镜像 | 大小 |
---|---|
MP3ENC |
111kbyte |
SRC(采样率转换器) |
21kbyte |
LPCM不需要压缩处理,但是需要频率转换处理,因此需要SRC(采样率转换器)DSP负载。 |
5.3.3.10.5. 音频录制示例
有一个简单的Recorder Example,您可以检查Recorder的操作。
要使用Audio Recorder示例程序,请进行以下设置。
加载audio_recorder配置。
cd sdk/ tools/config.py examples/audio_recorder
确保启用了录音机。
tools/config.py -m
(audio recorder:)
[Examples]
[Audio recorder example] <= Y
无法同时选择音频和逻辑传感器示例以及其他示例。如果选择多个,将发生编译错误。 |
有关详细信息,请参考 开始录制 。
必须定义使用AudioRecorder函数时所需的MemoryLayout(池)。
定义在MemoaryLayout定义文件中进行,并且可以使用该工具生成要包含在代码中的头文件。
在 音频录制示例 中,执行以下操作。
cd examples/audio_recorder/config python3 mem_layout.conf
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 |
该区域的起始对齐方式。 指定MinAlign的倍数(= 4),不包括0 |
size |
区域的大小。 指定的值是0以外的4的倍数 |
fence |
启用/禁用围栏(如果UseFence为False,则忽略此项目) |
每个名称的用法如下。
AUDIO_WORK_AREA |
由AudioSubSystem使用 |
MSG_QUE_AREA |
由MessageQueue使用(固定名称)。不要超过msgq_id.h中的(MSGQ_END_DRM-MSGQ_TOP_DRAM)的大小。 |
MEMMGR_WORK_AREA |
内存管理器使用的工作区(固定名称,固定大小) |
MEMMGR_DATA_AREA |
Memery Manager使用的数据区域(固定名称,固定大小) |
确保每个名称的总大小不超过由 mpshm_init() 、 doxygen:mpshm_remap [mpshm_remap()] 保护的共享内存的大小。
固定区域无法定制 |
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 |
池的起始对齐方式。 指定MinAlign的倍数(= 4),不包括0。 |
pool-size |
游泳池的大小。 该值是4的整数倍,但0除外。 对于基本池,段大小*段数 |
seg |
段数。指定一个介于1到255之间的值 |
fence |
指定围栏有效还是无效。 如果UseFence为False,则忽略此项目 |
每个名称的用法如下。
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定义文件中完成,并且可以使用该工具生成要包含在代码中的头文件。
在 音频录制示例 中,执行以下操作。
cd examples/audio_recorder/config python3 msgq_layout.conf
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 |
用以“MSGQ”开头的字符串指定消息队列ID的名称。 |
n_size |
正常优先级队列的每个元素中的字节数(8到512)。指定固定的标头长度(8个字节)+参数长度(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 |
应用程序用于接收命令响应 |
MSGQ_AUD_DSP |
用于接收来自DSP的响应(解码器) |
MSGQ_AUD_RECORDER |
用于接收MediaRecorderObject命令 |
MSGQ_AUD_CAP |
用于接收CaptureComponent命令 |
MSGQ_AUD_CAP_SYNC |
用于CaptureComponent的内部同步处理 |
有关每个定义的详细信息,
参见链接 examples/audio_recorder/config/msgq_layout.conf 。 如果设置更改,请使用该工具生成一个新的头文件。 |
5.3.3.11. Audio Recognizer 功能
Audio Recognizer 的简单数据流如下所示。
![Audio Recognizer Dataflow](images/Audio_recognizer_dataflow.png)
Audio Recognizer Function 提供用于实现语音识别功能的框架。
识别动作开始后,开始抓取音频数据,经过Pre处理后往识别库输入音频数据。
Pre处理对音频数据根据语音识别库要求的输入格式作必要的处理(采样率变换及噪音抑制等)。
如果可以将抓取的音频原样输入语音识别库的话,Pre处理设定为“通过”也没问题。
抓取并采取Pre处理后的音频数据会输入到语音识别库。
是否有结果输出,由库决定。它有一个可以每次输入都通知识别结果的框架。请配合应用程序使用。
+ Audio SubSystem在RecognizerMode工作时,可以在不关心音频数据(PCM数据)的情况下实现应用层。
数据流内部,使用Message通信。
Message通信,每个客户端都有ID。
Audio Recognizer的场合,根据example中的示例显示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
![Audio Recognizer Message ID](images/recognizer_message_id.png)
另外,各数据的数据区域如下。
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
![Audio Recognizer Pool ID](images/recognizer_pool_id.png)
这些ID需要在生成时指定。
5.3.3.11.1. 使用方法
通过被称为"AudioManager"、"MicFrontendObject"、"RecognizerObject"、"CaptureComponent" 的用于控制音频子系统而设计的软件组件,来实现Audio识别。
必要的对象生成后,为了让Recognizer动作,进行Audio的HW的设定,电源On,动作模式变更等初始化处理。
可以按顺序发出以下命令来实现。
为了给Audio功能块上电,通过发出AUDCMD_POWERON 、 PowerOnParam 命令,上电使Audio子系统的状态迁移至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获得的值可以在0(0.0dB)到210(21.0dB)的范围内以5的倍数设定。缺省值为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。
![Audio recognizer preprocess](images/Audio_recognizer_preprocess.png)
Preprocess可以根据识别库的输入格式,使用DPS进行用户自己的信号处理。
比如,识别库的输入与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)相关位置进行识别处理。
![Audio recognizer recognizerprocess](images/Audio_recognizer_recognizerprocess.png)
如下,由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);
语音识别功能初始化为止的顺序如下图所示。
![Diagram](images/diag-bfe5a19ce55db623a0aeda2a9767d413.png)
通过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)
![Diagram](images/diag-8a7244f567478db4cba6e237df0c0fb4.png)
通过 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)
![Diagram](images/diag-8fb62e6e2c23d8dc57df71c010b6818a.png)
5.3.3.11.2. 编译配置
为了使用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.11.3. 注意事项
语音识别的警告一览及对应方法如下。详细请参考 关于音频子系统错误 。
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二进制的版本不一致。请用"sdk/modules/audio/dsp"的文件更新DSP二进制映像。 |
5.3.3.12. Audio Through 功能
Audio Through 的简单数据流如下所示。
![Audio Through 数据流](images/audio_through_dataflow.png)
Audio SubSystem工作在ThroughMode时,用户程序可以设定不经过CPU的数据流。
数据的输入端可以指定为I2S或MIC。 数据的输出端可以指定为扬声器或I2S。
此外,User Application可以设定2个数据流。 使用MIXER可以MIX 2个数据流为1个。
User Application要求的设定会通过Message发出命令。 Message通信在每个客户端用于ID。 Audio Through的场合,ID如下所示。
User Application : MSGQ_AUD_APP Audio Manager : MSGQ_AUD_MNG
![Audio Through Message ID](images/audio_through_message_id.png)
此ID需要在生成时指定。
5.3.3.12.1. Audio HW 内部数据流
Audio Through时,Audio HW的内部为 Audio HW 内部数据流 中显示的数据流。
数据流的输入端有I2S In、MIC In、Mixer Out。
数据流的输出端有Mixer In1、 Mixer In2、I2SOut。输出端设定为Mixer In1或者Mixer In2时,输出由Mixer Out到扬声器。
![Diagram](images/diag-65e983773cddcc639db0941b79e4fdb8.png)
可以设定的输入端和输出端的关系如下。
输入端 | 输出端 |
---|---|
I2S In |
Mixer In1, Mixer In2 |
MIC In |
Mixer In1, Mixer In2, I2S Out |
Mixer Out |
I2S Out, |
5.3.3.12.2. 使用方法
生成了必要的对象后,进行Audio的HW的设定,电源On,工作模式变更等初始化处理,以执行Audio Through动作。
按顺序发出以下命令可以实现。
为了让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。
Mix MIXER1和MIXER2後的音量。の音量。设定范围同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来实现。 |
![Diagram](images/diag-58655d46ad555e08e00e0de46af35622.png)
设定数据流的路径,开始数据的输入和输出。
通过 AUDCMD_SETTHROUGHPATH 、 AsSetThroughPathParam 、 AsThroughPath 可以同时设定两路数据路径。 各路数据路径的参数设定如下。
设定数据路径的有效,无效。
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 |
![Diagram](images/diag-cd6d142421022eb676e8d1fbc5aad9bf.png)
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 |
![Diagram](images/diag-9e31aab8ad24a88571c9d1f443a4db31.png)
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的设置 |
![Diagram](images/diag-7d633c524d51c4368fc35ca75018a85d.png)
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 |
![Diagram](images/diag-37574f27f4d427b95cf63048a3e8a9d8.png)
![Diagram](images/diag-8ff3456753187077134a0b21e7a8bde2.png)
5.3.3.12.3. 编译配置
使用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 [SDK audio] <= Y
5.3.3.12.4. 注意事项及解决方案
播放音乐时的警告一览及对应方法如下。详细请参考 关于音频子系统错误 。
编号 | 代码 | 级别 | 解决方案 |
---|---|---|---|
0x0F |
AS_ATTENTION_SUB_CODE_TASK_CREATE_ERROR |
ERROR |
堆区域不足。请扩大堆区域。 |
AS_ATTENTION_SUB_CODE_SIMPLE_FIFO_UNDERFLOW
发生时,音频播放将停止,状态变为播放错误。
发生此状态时,请立即发行 AsStopPlayerParam 命令,迁移至播放停止状态。 迁移到播放停止后,请一定清空FIFO。否则会产生噪音。 |
5.3.4. 对象级 API
音频功能以比顶层API更好的单位提供API。
通过在调用高级API时使用内部使用的对象的组合,可以创建更多的免费应用程序。
您可以使用创建更多免费的应用程序。
这称为 Object Level API 。
5.3.4.1. 有关用例
这是使用Object LevelAPI的用例示例。
(这只是一个示例,因为组合和用法是免费的。)
5.3.4.1.1. 用例1
在这种情况下,音频数据将使用MediaPlayerObject和OutputMixerObject进行解码并输出。
从MediaPlayerObject返回的PCM数据可以在由Application处理之后发送到OutputMixerObject。
![Diagram](images/diag-ab93aa2802beb8c61e03e7c1143ecd69.png)
5.3.4.2. MediaPlayerObject
MediaPlayerObject执行音频数据解码管理和解码结果PCM输出。
可以同时使用两个Player,并且每个API中的PlayerID参数分别对其进行控制。
应用程序通过称为SimpleFIFO的缓冲区将ES数据传递给MediaplayerObject。
缓冲区下溢时播放停止。 在设计应用程序时应考虑到这一点。
解码完成后,MediaPlayerObject将把PCM数据通知MemoryHandle。
![Diagram](images/diag-f4b4ec84d21ebaf098aadeca6c6e0294.png)
也可以将PCM数据发送到OutputMixerObject。在这种情况下,对应用程序无响应。
但是,当然,必须创建并启动OutputMixerObject。
(默认是对应用程序的回调响应,如上所示。)
![Diagram](images/diag-bcaf7c36bef0baccb48dd3e7ffb724d4.png)
5.3.6. UserCustomDSP 架构
UserCustomDSP是一种机制,允许将特定于用户的信号处理应用于音频信号。
信号处理由DSP执行。 这是它如何工作的详细说明。
5.3.6.1. 框架代码
要添加信号处理,您必须首先创建一个UserCustomDSP。
SDK至少提供了所需的源代码作为框架。
通过将这些内容与用户创建的DSP代码一起构建来创建UserCustomDSP。
有关创建UserCustomDSP的过程,请参考 Audio Recorder 教程 。 |
![Diagram](images/diag-25a03e75844acdce8cd8a80f2463a143.png)
5.3.6.2. 框架代码和用户代码之间的关系
SDK提供的框架与用户代码之间的关系必须如下图所示。
用户在图中编辑与“User Edit DSP Codes”相对应的部分。
(无需编辑框架代码。)
![Diagram](images/diag-0b46c0dab991ebadf39516a6f62223f2.png)
创建用户代码时要记住的要点如下:
-
可以自由创建与UserCustomDSP的通信命令定义,但是必须使用继承框架的
CustomprocCommand::CmdBase
的结构,并遵循 数据格式如下所示 。图表 40. 通讯指令格式例如:
struct InitPram : public CustomprocCommand::CmdBase { uint8_t ch_num; ... }
-
编写用户处理的类(在 以上)继承了框架
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
命令执行 状态转换 ,如下所示。
![Diagram](images/diag-08e6f503b014bb37cd4f99f0886ae829.png)
5.3.6.3.2. DSP操作顺序
在上述框架上创建的UserCustomDSP按照下图所示的顺序在每个Audio功能中运行。
有关UserCutstomDSP在Audio Recorer功能中的位置,请参考 初始化麦克风前端(录音) ,对于音频识别器,请参考 初始化麦克风前端(识别器) 。
从下图中可以看到, Init
和 Set
命令是由用户代码中的API调用触发的。
Exec
和 Flush
命令在AudioSubSystem内部发送(每次捕获音频时)。
(※ 不需要从用户代码发送 Exec
和 Flush
命令。)
总体流程如下。
-
从 AUDCMD_INIT_MICFRONTEND 指定的二进制文件中启动。
-
AUDCMD_INIT_PREPROCESS_DSP 将向UserCustomDSP发送一个
Init
命令。 用于发送处理所需的参数,例如通道数和位长。 -
AUDCMD_STARTREC 开始音频捕获,并使用
Exec
命令顺序发送它们。这由UserCustomDSP处理。 -
AUDCMD_SET_PREPROCESS_DSP 及
Set
命令将发送。 由于即使在记录操作期间也可以发送,因此可用于设置滤波器系数。 -
音频捕获使用强力停止 AUDCMD_STOPREC,并且在最后一帧中的
Exec
之后发送Flush
。 用于清除延迟等。
![Diagram](images/diag-1a34003c6e2a67a420c158fd8b1d560b.png)
总体流程如下:
-
DSP由 AUDCMD_INIT_RECOGNIZER 指定的二进制文件启动。
-
带有 AUDCMD_INIT_RECOGNIZER_DSP 的
Init
命令发送到UserCustomDSP。 用于发送识别处理所需的参数,例如通道数和位长。 -
AUDCMD_START_RECOGNIZER 开始音频捕获,并使用
Exec
命令顺序发送它们。 这是UserCustomDSP识别的。 -
AUDCMD_SET_RECOGNIZER_DSP 的
Set
命令将发送。 由于即使在识别操作期间也可以发送,因此可用于设置识别参数。 -
使用 AUDCMD_STOP_RECOGNIZER 停止音频捕获,并且在最后一帧的
Exec
之后发送Flush
。 用于清除延迟。 -
AudioRecognizer函数HighLevelAPI命令和UserCustomDSP操作顺序
![Diagram](images/diag-251ef3e2521fa76aab040bc063ac7173.png)
5.3.7. 关于音频子系统错误
5.3.7.1. 概要
Audio SubSystem 的High Level API 拥有命令~结果的收发信界面。
发向Audio SubSystem的命令出现问题时,作为结果返回 AUDRLT_ERRORRESPONSE , 错误内容保存在 ErrorResponse 的参数中。 这个错误称为 应答错误 。
另外,Audio SubSystem的内部处理发现错误时,内部事件启动,通过由 AS_CreateAudioManager 登录的 回调函数 通知错误,错误内容保存在 ErrorAttention 的参数中。 这个被称为 状态错误 。
对于应答错误,状态错误,对应于各个错误请执行故障对应和错误处理等。
5.3.7.2. 应答错误
Audio 子系统发出的命令,如果按照式样执行了控制,对各条命令会应答命令结束的结果。
但是,由于违反状态或者参数错误等导致执行了和式样不一致的控制,将返回 AUDRLT_ERRORRESPONSE 的结果,
错误内容包含在 ErrorResponse 的参数中。
![Diagram](images/diag-674ed13aec6d69686c4146073f628e6a.png)
结果的应答错误的数据形式,请参考 结果格式 和 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 |
DSP解码器初始化失败 |
|
0x0E |
DSP编码器初始化失败 |
|
0x0F |
DSP过滤器初始化失败 |
|
0x11 |
解编码类别指定错误 |
|
0x13 |
通道数指定错误 |
|
0x14 |
采用频率指定错误 |
|
0x15 |
码率指定错误 |
|
0x16 |
码长度指定错误 |
|
0x17 |
压缩率指定错误 |
|
0x18 |
Player界面指定错误 |
|
0x19 |
输入设备指定错误 |
|
0x1A |
输出设备指定错误 |
|
0x1B |
输入设备句柄指定错误 |
|
0x28 |
静音参数指定错误 |
|
0x2B |
输入出功能初始化失败 |
|
0x2C |
输入数据获取失败 |
|
0x2E |
存储池设定失败 |
|
0x2F |
SimpleFIFO的数据枯竭 |
|
0x30 |
麦克风增量指定失败 |
|
0x32 |
输出端指定错误 |
|
0x34 |
音量指定错误 |
|
0x35 |
静音对象指定错误 |
|
0x36 |
堆参数指定错误 |
|
0x37 |
数据序列管理失败 |
|
0x39 |
动作模式指定错误 |
|
0x3A |
动作模式设定失败 |
有关"Error Code"的详细信息,请参考 此处 。
5.3.7.3. 状态错误
Audio 子系统内部处理中(非命令处理)发现错误的时候,发生通知用的事件。 获取这个事件,需要事先通过 AS_CreateAudioManager 登录回调函数。
![Diagram](images/diag-61e7ac2732cc09448530338bdfa8a75d.png)
状态错误数据形式 请参考 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通知有级别指定,指明错误严重性的同时恢复的处理方式也会不同。
级别 | 值 | 描述 |
---|---|---|
致命 |
0x03 |
系统调用错误等,无法修复,恢复需要重启系统。 |
错误 |
0x02 |
内部错误(队列Full/Empty,DSP加载/卸载等)导致Audio系统动作无法继续的错误。回归系统初期状态(Ready状态)可以恢复。 |
警告 |
0x01 |
可能发生解码/编码错误,数据的上溢出/下溢出,动作异常,声音数据异常等,但动作可以继续。 |
5.3.7.6. Module ID List
AudioSubSystem内部使用模块的ID一览。
和状态码一起由Attention回调通知,可以据此判断哪个模块发生了错误。
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支持相机模块。 除了数据接口之外,相机模块通常还具有用于控制模块的接口。在ISX012中,I2C用于控制I/F。 以下是硬件配置的概述。
![camera hw overview](images/camera_hw_overview.png)
在 CXD5602 中,有一个名为CISIF的Camera I/F模块,该模块桥接8位并行信号和 CXD5602 内部总线。除了用于ISX012摄像机模块之外,还使用I2C总线进行控制。
本章概述了如何使用 Spresense SDK 控制连接到此相机I/F的相机模块。
Spresense SDK Camera控件提供的驱动程序I/F与Linux熟悉的V4L2非常相似,因此可以轻松地从V4L2代码转移。此I/F称为V4S(视频显示)。V4S提供了一个API,该API无需使用设备(例如ISX012),通过设备文件使用打开,关闭和ioctl之类的标准I/F,即可抽象为照相机的功能。
![camera v4s overview](images/camera_v4s_overview.png)
V4S提供了两个用于摄像机控制的虚拟视频流。 在这两个虚拟流中,一个充当用于处理电影(例如“摄像机预览”图像)的流,另一个充当用于获取静止图像的流。
![camera v4s dataflow](images/camera_v4s_dataflow.png)
要从应用程序获取这两个流的数据,可以通过使用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到捕获图像数据的一般流程如下:
![Diagram](images/diag-a00acb00cc4c9d6fe1c833146e10468b.png)
5.4.2. 状态转移
状态是为每个流管理的,可以从应用程序并行控制。 但是,V4L2_BUF_TYPE_STILL_CAPTURE控件被赋予用于图像获取的优先级,并且V4L2_BUF_TYPE_VIDEO_CAPTURE侧的图像获取在VIDIOC_TAKEPICT_START和VIDIOC_TAKEPICT_STOP之间停止(状态转换图中不是“dma”状态)。
![Diagram](images/diag-a37aae5cc66f685365183c425462c9e5.png)
5.4.3. V4S支持ioctl命令
类别 | 命令 | 功能 | Spresense定制 |
---|---|---|---|
缓冲区控制 |
执行驱动程序缓冲区管理区域的初始设置。 |
添加参数“ v4l2_buf_mode模式 ” 缓冲区组设计为具有RING结构。 |
|
排队应用程序提供的缓冲区。 |
符合V4L2 |
||
使包含图像数据的缓冲区出队。 |
符合V4L2 |
||
取消VIDIOC_DQBUF。 |
Spresense原始命令已添加到左侧。 |
||
流控制 |
开始直播。 |
符合V4L2 |
|
停止播放。 |
符合V4L2 |
||
开始拍摄静态影像 |
Spresense原始命令已添加到左侧。 |
||
静止画撮影停止 |
Spresense原始命令已添加到左侧。 |
||
检查框架设置范围 |
检查摄像头设备支持的像素格式 |
符合V4L2 |
|
检查相机设备支持的图像尺寸 |
将V4l2_buf_type添加到该参数,以便可以为每个流确认该参数。 |
||
检查摄像头设备支持的帧间隔 |
将V4l2_buf_type添加到该参数,以便可以为每个流确认该参数。 |
||
检查是否可以设置IN参数中指定的像素格式和图像尺寸的组合。 |
符合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. ISX012原始规格
5.4.4.1. JPEG + YUV4:2:2格式
在ioctl(VIDIOC_S_FMT)中
-
将参数pixelformat设置为V4L2_PIX_FMT_JPEG_WITH_SUBIMG
-
参数subimg_pixelformat中的V4L2_PIX_FMT_UYVY
通过设置,可以同时以JPEG和YUV4:2:2两种数据格式获取一帧图像。
例如,您可以实现一个应用程序,在不使用JPEG解码器的情况下拍摄照片并将其保存为JPEG时显示与保存的图像相同的图像。
但是,与单独获取每种格式相比,对图像大小和帧速率的限制更加严格。
![Diagram](images/diag-c25f72fe2a2fa4e91246c472be29732e.png)
5.4.4.2. 电源开启后缩短3A调整时间的设定API
通过将V4L2_CID_3A_PARAMETER与ioctl(VIDIOC_S_EXT_CTRLS)和ioctl(VIDIOC_G_EXT_CTRLS)配合使用,可以缩短开机后3A的调整时间。
另外,可以通过V4L2_CID_3A_STATUS确认3A调整是否完了。
![Diagram](images/diag-fc4f9795067c480877c6764d7ff272b2.png)
5.4.4.2.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.5. 图像大小和帧速率的限制
VIDIOC_S_FMT设置的图像大小和VIDIOC_S_PARM设置的frame interval(frame interval的倒数)有关联。
(如果增加图像大小,则不能设置较大的frame interval。)
下面显示了每种像素格式对于Spresense + ISX012的支持范围。
在ISX012中能设置的frame rate将取得离散值,可以设置120 / 60 / 30 / 15 / 7.5 / 6 / 5 7种类型。
在下图中,例如,对于YUV 4:2:2格式
-
QVGA及以下:因为max FPS=120,因此可以设置120FPS~5FPS 7种类型
-
大于QVGA:因为max FPS=60,因此可以设置60FPS~5FPS 6种类型
という制約になります。
![Diagram](images/diag-e20be42c17fe936f4b3ca31aee2cb114.png)
![Diagram](images/diag-2d164fbd359fa17a6c4988de576bdaef.png)
![Diagram](images/diag-1415c4a641b352130a344b6b54cad2cc.png)
在JPEG + YUV4; 2; 2格式中,YUV422 image大小支持从96x64到WQVGA(400x240) 在此范围内,frame rate约束将不受影响。
5.5. DNN 运行态
5.5.1. DNN 运行态简介
DNN运行时库(dnnrt)可以使用从索尼提供的神经网络库或神经网络控制台(NNC)学习的模型,使用深度神经网络(DNN)执行识别处理。
为了使用DNN运行时执行识别处理,必须预先使用NNC创建训练后的模型(nnb文件格式)。 有关如何创建训练模型的信息,请参见外部 网络模型的准备 。
![dnnrt overview cn](images/dnnrt_overview_cn.png)
有关神经网络库和神经网络控制台的信息,请参见下面的官方网站。
DNN运行时库使用NNabla C运行态。
5.5.2. 示例代码
该示例代码是一个示例代码,用于通过执行使用 网络模型的准备 中的 image_recognition.MNIST.LeNet
创建的学习模型来执行手写字符识别。
由 image_recognition.MNIST.LeNet
创建的学习模型将28x28大小的图像作为输入并输出10个数组。这10个数组表示由索引识别的数字,并且输出该数组中每个数字的概率。例如,在阵列的顶部(索引0)输出输入图像为数字“0”的概率。
有关详细细腻,请参考 DNNRT 示例 README 。
![Diagram](images/diag-5d6a115741cc83949996cdb8e91e35aa.png)
5.6. GNSS
GNSS库提供了接收GPS/GLONASS/BeiDou/Galileo卫星信号并计算当前位置的功能。 要使用此功能,您需要为GNSS安装天线。 Spresense 板上有一个芯片天线,因此不需要其他天线。
5.6.1. 主要特点
-
除GPS/GLONASS/BeiDou/Galileo外,还支持可以从QZSS(Michibiki)卫星系统接收信号的Multi GNSS
-
可用SBAS(WAAS)和QZSS L1S进行定位加固
-
由于应用程序核心和GNSS核心是分开的,因此可以独立执行定位和通知。
-
支持Geofence
这些功能是POSIX兼容的设备文件,可以从应用程序进行操作。 设备文件为 “/dev/gps”,可以使用open(),close(),read(),seek(),ioctl()进行操作。
NuttX RTOS ioctl具有三个参数。 对于GNSS设备文件,第二个参数“req”是GNSS命令,第三个参数“arg”是in/out data传递。
5.6.2. 配置
要使用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”。 根据测试环境设置EXAMPLE_GNSS_FACTORY_SVID。 如果要直接运行工厂测试,请将[Application entry point]更改为“gnss_factory_test”。 测试结果(cn和多普勒)是数值的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'
有关详细信息,请参见以下自述文件。
|
在任何情况下都可以更改以下设置。
-
'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轮询或信号将定位结果通知应用程序。 可以通过以下配置设置可以轮询的号码。默认情况下,轮询数为4,信号数为4。 有关更多详细信息,请参考 定位计算通知 。
[System Type] [CXD56xx Package Configuration] [GNSS setting] [GNSS max poll waiters] = 4 [GNSS max signal receivers] = 4
5.6.3. 设备控制
GNSS库通过ioctl()控制。
ioctl命令使用IOCTL_前缀定义。 GNSS设备文件可以同时从多个应用程序open()。 但是,GNSS设备一次仅接受一个IOCTL命令。换句话说,这并不意味着哪个应用程序具有优先权,它会按照被接受的顺序进行处理。
5.6.3.1. 启动
您可以使用 CXD56_GNSS_IOCTL_SELECT_SATELLITE_SYSTEM 设置在启动时使用哪个卫星系统。定位间隔用 CXD56_GNSS_IOCTL_SET_OPE_MODE 设置。
对于“热启动”(“Hot Start”),您需要设置最新的位置和时间。有关详细信息,请参阅 GNSS 备份数据 中的 “Warm Start和Hot Start”设置。
5.6.3.2. 开始定位
您可以使用 CXD56_GNSS_IOCTL_START 指定“启动模式”。
接下来的初始位置计算时间TTFF是参考值。这取决于您的系统和环境。 |
启动模式 | IOCTL 命令参数 | TTFF (*1) | 备注 |
---|---|---|---|
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. 停止定位
使用 CXD56_GNSS_IOCTL_STOP 停止定位。停止需要200到300毫秒。
5.6.3.4. 定位计算通知
通知有两种类型:poll和信号。
使用轮询时,可以使用 CONFIG_CXD56_GNSS_NPOLLWAITERS 设置poll次数。
如果使用信号,则将 cxd56_gnss_signal_setting_s 数据(字段描述符设置为GNSS设备文件描述符),enable设置为1,signo设置为任何信号编号,并将gnsssig设置为 CXD56_GNSS_SIG_GNSS , 调用ioctl函数作为IOCTL命令 CXD56_GNSS_IOCTL_SIGNAL_SET 的参数,以将信号与定位通知相关联。
应用程序可以使用sigwaitinfo接收通知作为信号。 如果不再需要该信号,则可以通过在CXD56_GNSS_IOCTL_SIGNAL_SET启用字段中将 cxd56_gnss_signal_setting_s 数据设置为0来清除关联。 可以通过配置中的CONFIG_CXD56_GNSS_NSIGNALRECEIVERS更改最大信号关联数。
可以在 gnss应用程序示例程序 中找到设置了CONFIG_EXAMPLES_GNSS_USE_SIGNAL的示例。
5.6.4. 关于快速定位
为了通过“Hot Start”缩短TTFF,最快的方法是使用卫星轨道信息和备份数据中包含的其他信息。 使用ioctl命令显示使用备份数据的标准“Hot Start”过程。
![Diagram](images/diag-0859fec2f1f6d08bd01409199cd4b183.png)
5.6.4.1. GNSS 备份数据
当通过“Hot Start”开始定位时,将使用备份数据中包含的最终定位位置,星历表,历书和TCXO时钟偏移值。 可以使用ioctl命令将备份数据存储在闪存中。在这种情况下,即使电源关闭,在下次启动时也会从闪存扩展到SRAM,并可用于“Hot Start”。
5.6.4.1.1. 电源状况和备份数据
随着系统的运行,GNSS定位正在工作,并且所有相关信息都已存储。当系统处于“Hot Sleep”状态时,SRAM数据将保留并且RTC保持运行,因此可以通过“Hot Start”开始定位。在“Deep Sleep”状态下,RTC继续运行,但是SRAM已关闭,因此有必要恢复和使用闪存中保存的备份数据进行“Hot Start”。
5.6.4.1.2. 保存备份数据
使用ioctl命令 CXD56_GNSS_IOCTL_SAVE_BACKUP_DATA 将备份数据保存到闪存等。备份数据以CONFIG_CXD56_GNSS_BACKUP_FILENAME中指定的文件名保存。 但是,如果您频繁写入闪存,则可能会导致内存故障。例如,在系统关闭时保存它。
5.6.4.2. GPS 时间
要执行“Hot Start”,GNSS设备的当前时间必须在60秒内。如果之前进行过定位并且未关闭系统,则将RTC_GPS设置为上次定位时设置的时间。接通电源后,RTC_GPS的值立即未定义,因此有必要使用应用程序中的ioctl命令设置时间。
5.6.4.2.1. RTC_GPS 精度
当定位停止时,GNSS设备会将LSI中的RTC时钟精度计时器RTC_GPS更新为GPS时间。 停止定位后,GNSS设备的时钟停止,并且该RTC_GPS保持时间。 RTC_GPS时间与实时时间之间的误差取决于外部RTC Xtal的时钟精度。 GNSS设备在恢复定位时会根据RTC_GPS时间执行定位计算,因此停止时间越长,RTC_GPS时间相对于实时的误差就越大,这会影响TTFF和初始定位位置的准确性。 从定位停止位置超过15分钟后,对于后续的“Hot Start”定位结果,RTC_GPS时间错误将无法忽略。 如果即使在较长的定位停止时间后仍需要较短的TTFF或较高的初始位置精度,则可以考虑使用ioctl命令设置时间。
打开系统后,RTC_GPS会立即显示“ 0h 6 - Jan - 1980”。RTC_GPS设置为GPS时间,因为在完成定位后可以获取GPS时间。除非指定时间,否则定位计算基于RTC_GPS。
5.6.4.2.2. 通过命令设置时间
使用ioctl命令 CXD56_GNSS_IOCTL_SET_TIME 指定应用程序从网络获取的时间。
5.6.4.3. 当前位置
对于“Hot Start”,需要最新的位置数据。如果应用程序没有位置数据,则GNSS设备将基于先前测量的位置信息执行“Hot Start”。
5.6.4.3.1. 通过命令设置位置
当指定应用程序从网络获取的位置信息时,请使用ioctl命令 CXD56_GNSS_IOCTL_SET_RECEIVER_POSITION_ELLIPSOIDAL 或 CXD56_GNSS_IOCTL_SET_RECEIVER_POSITION_ORTHOGONAL 。
5.6.5. 位置准确性
为了进行准确的定位,需要在适当的接收条件下进行定位。从定位数据可以在某种程度上估计定位环境是否不影响高精度定位,例如建筑物中卫星的不可见性,大型建筑物的反射波的影响以及WIFI的噪声。
5.6.5.1. 接收器定位数据中的含义
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(精度稀释),DOP是根据卫星的几何排列计算得出的位置精度的下降速率。 '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. 卫星定位数据的含义
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卫星的信号是扩频信号,因此称为C/N0比,而不是S/N比,单位为dBHz。 该值越大,来自GNSS的卫星信号的可靠性越高,结果,使用它的定位将是稳定的。 另一方面,噪声源在GNSS天线附近,卫星方向与天线方向性差的方向一致,或者卫星与天线之间存在障碍物,卫星信号的跟踪性差。 该值变小并且定位变得不稳定。
5.6.5.3. 多 GNSS
如上所述,存在随着捕获和跟踪的卫星数量的增加而提高定位精度的趋势。 GNSS设备可以同时使用多个卫星系统,以增加用于定位计算的卫星信号数量。
-
GLONASS
俄罗斯定位卫星系统,有24架飞机像GPS一样覆盖全世界。 当GPS足够时,如果使用70m GLONASS系统信号执行定位计算,则定位精度可能会低于单独的GPS,这低于GPS系统标准精度20m。 请注意,某些天线不支持GLONASS频率。
-
Galileo
欧洲定位卫星系统,像GPS一样覆盖整个世界。
-
BeiDou
中国定位卫星系统,像GPS一样覆盖整个世界。
-
QZSS-L1C/A
日本发送的卫星信号。 由于此信号与GPS L1C/A兼容,因此使用它似乎使GPS卫星的数量增加了。这被Michibiki称为GPS补充功能。 Michibiki有4架飞机(截至2018年),其航迹涵盖了东亚和大洋洲,主要在日本,因此在其他地区没有补充作用。
5.6.5.4. 增强
使用WAAS或Michibiki的QZSS-L1S发送的增强信号可以提高定位精度。 加固信号是基于SBAS格式的计算参数,可提高每隔几分钟更新一次的定位计算的准确性。 使用增强信号时,无法使用没有增强信息的GLONASS进行定位。
-
WAAS
WAAS是美国大陆、加拿大、夏威夷和各省的有效加固信号。 提高了GPS卫星信号所需的伪距离(卫星与接收器之间的距离)的精度。
-
QZSS-L1S
QZSS-L1S仅在日本有效。 提高GPS卫星信号和QZSS-L1C/A所需的伪距精度(卫星与接收器之间的距离)。 低仰角卫星(截至2018年9月,仰角屏蔽为20度)将不会接收到增强信息,也无法用于定位。
5.6.5.5. 选择定位和增强卫星系统
在该GNSS设备中,假定使用以下定位卫星系统和增强信号的组合来执行定位。 您可以设置与 CXD56_GNSS_IOCTL_SELECT_SATELLITE_SYSTEM 一起使用的卫星系统。
GLONASS, Galileo, BeiDou に関して同時に使用することはできません。いずれか一つを選択してください。 GPS, QZSS-L1C/A や WAAS, QZSS-L1S といった補強信号に関しては組み合わせの制約はありません。 |
以下定位误差(精度)是一般条件下的参考值。它取决于您的系统和环境。 |
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. 发送短信
GNSS设备可以接收灾难(灾难/危机)报告,例如从QZSS Michibiki发送的灾难信息和危机管理信息。
通过将信号通知设置为GNSS设备,应用程序可以异步接收来自GNSS设备的灾难通知。 灾难通知信号通知设置为 cxd56_gnss_signal_setting_s ,在字段fd中具有GNSS设备文件描述符,enable为1,gnsssig中为任意信号号, 对于灾难通知信号通知设置,cxd56_gnss_signal_setting_s 会在字段fd中设置GNSS设备的文件描述符,将1设置为启用,将gnsssig设置为任意信号号,将 CXD56_GNSS_SIG_SBAS 设置为gnsssig时,请将ioctl函数作为一个函数调用IOCTL命令 CXD56_GNSS_IOCTL_SIGNAL_SET 的自变量,将信号与灾难报告相关联。
GNSS设备收到SBAS消息类型43(气象局防灾信息)或44(可选信息)时,它会发出关联的信号。 在sigwaitinfo信号处理中,可以通过读取 cxd56_gnss_sbasdata_s 和 CXD56_GNSS_READ_OFFSET_SBAS 作为数据缓冲区中的偏移量来读取引起通知的SBAS消息。
5.6.7. 功能使用
5.6.7.1. NMEA 转换器
NMEA是广泛用于GPS应用程序中的位置信息文本格式,并且gnss库还支持NMEA格式输出。
要使用它,必须将CONFIG_MLIB设置为 'y' 。 将存储在 cxd56_gnss_sv_s 中的GNSS设备的内容转换为NMEA。
5.6.8. 地理围栏
5.6.8.1. 重要特性
Geofence是一个图书馆,可在您靠近指定区域时通知您。
-
可以通过纬度、经度、半径指定区域
-
您最多可以指定20个区域
![geofence notification](images/geofence_notification.png)
通知共有三种类型:“ENTER”、“DWELL”和“EXIT”。在“DWELL”的情况下,您还可以设置停留时间的通知条件。
5.6.8.2. 配置
使用Geofence时,将GNSS设备和Geofence支持指定为 'y' 。
[System Type] [CXD56xx Package Configuration] [GNSS device] (CXD56_GNSS) = Y [Geofence Support] = Y
5.6.8.3. 添加区域和设置选项
要使用Geofence,请使用“/dev/geofence”设备文件。
5.6.8.5. 盲区
盲区是指由定义的区域半径(半径)定义的同心区域。
-
如果定义了“ENTER”,则进入同心区域时将通知您
-
如果定义了“EXIT”,当您退出同心区域时,它将通知您
![Geofence 盲区](images/geofence_deadzone.png)
5.6.8.6. 读取地理围栏传送的数据
状态更改通过投票通知。 状态转换信息存储在 cxd56_geofence_status_s 中。
有关更多信息,请参见应用程序样本 Geofencing 迁移 。
5.6.9. PVTLog 打印输出
5.6.9.1. 主要特性
PVTLog是有关位置,速度和时间的信息的日志。
最多可以累积170个日志。如果日志溢出,可以通过信号通知应用程序。 例如,如果每15分钟获取一次日志,则可以连续记录42个小时。
有关日志数据的详细信息,请参考 cxd56_pvtlog_s 。
5.6.9.2. 配置
要使用PVTLog,请将GNSS设备支持设置为 'y' 。
[System Type] [CXD56xx Package Configuration] [GNSS device] (CXD56_GNSS) = Y
5.6.9.3. 开始和停止存储日志
要开始记录,可以使用ioctl命令 CXD56_GNSS_IOCTL_PVTLOG_START 也可以使用此命令记录间隔。
日志记录间隔必须大于定位间隔。
定位开始时,日志自动开始。要停止记录,请使用ioctl命令 CXD56_GNSS_IOCTL_PVTLOG_STOP 。
5.6.9.4. 通知
如果您记录的日志超过170条,则会有一个信号通知应用程序该日志已溢出。 信号配置可以用 CXD56_GNSS_SIG_PVTLOG 完成。
收到此通知时,必须先阅读日志,然后再进行下一次记录。否则,第一个日志数据将被下一个定位数据覆盖。
可以从由 CXD56_GNSS_READ_OFFSET_PVTLOG 指定偏移量的位置读取日志数据。
日志数据记录在备份RAM中。 如果数据已经存在,将被覆盖而不会被擦除。 使用 CXD56_GNSS_IOCTL_PVTLOG_DELETE_LOG 清理备份。 |
有关详细信息,请参见示例程序 PVTLog 打印输出 。
5.6.10. 应用程序示例
5.6.10.1. 定位通知
GNSS定位的通知通过轮询和信号完成。您可以在Kconfig中选择CONFIG_EXAMPLES_GNSS_USE_SIGNAL或CONFIG_EXAMPLES_GNSS_USE_POLL。
5.6.10.3. 引言
gnss_atcmd是在Spresense SDK上运行的GNSS功能评估的示例应用程序。 当从主机(例如PC)通过串行端口发送命令时,NMEA语句结果将输出到该串行端口。后面将描述特定的命令规范和应用程序使用示例。
5.6.10.4. @Command规范
描述从主机发送的命令的规范。
s_atcmd应用程序接收从主机(例如PC)发送的命令,并将处理结果返回给主机。从命令发送到响应的期间,不输出NMEA。nss_atcmd应用程序响应消息,指示命令完成返回消息(“Done”或“Err”)之前,请勿发出其他命令。 从命令发送到返回命令响应的时间取决于命令类型和当时的状态,但是在最坏的情况下可能需要5秒钟。当主机控制器检测到超时时,请确定超时为5秒。
5.6.10.4.1. 命令格式
命令格式如下。在“@”(符号后)后发送命令字符串或参数,并在末尾发送换行代码。
@Command [Argument0] [Argument1]...<CR><LF>
- Command
-
字符串,最多4个字符。 有关详细信息,请参见下表 命令清单 。
- Argument
-
以十进制表示的数字值。 如果字符串以“0x”开头,则表示一个十六进制数。 有些命令带有多个参数。
- <CR><LF>
-
表示换行代码。以CR(回车)+ LF(换行)结束命令行。
5.6.10.4.2. 普通回复格式
正常响应结果的格式如下。在[发送命令字符串]之后,返回“Done”字符串。
[Command] Done<CR><LF>
- Command
-
代表收到的命令字符串。
- <CR><LF>
-
代表换行。在响应行的末尾添加了CR(回车)+ LF(换行)。
5.6.10.4.3. 错误响应格式
错误响应结果的格式如下。在[发送命令字符串]之后,返回“Err”字符串和错误代码。
[Command] Err ErrorCode<CR><LF>
- Command
-
代表收到的命令字符串。
- ErrorCode
-
代表负错误代码。
- <CR><LF>
-
代表换行。在响应行的末尾添加了CR(回车)+ LF(换行)。
5.6.10.4.5. 命令清单
gnss_atcmd可以处理的命令列表如下所示。
命令 | 参数 | 描述 | ||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@AEXT |
- |
退出申请。 定位停止时执行此命令。 |
||||||||||||||||||||||||||||||
@BSSL |
NMEA输出掩码 |
在NMEA 0183(版本4.00)标准中定义的NMEA语句中, 要输出的NMEA语句被指定为具有位掩码值的自变量。 最初,NMEA掩码设置为0xef。
xx代表以下内容:
命令示例:
|
||||||||||||||||||||||||||||||
@BUP |
- |
将收到的星历表和各种参数保存到Flash。 保存的数据在下次启动时自动恢复。 定位停止时执行此命令。 |
||||||||||||||||||||||||||||||
@GCD |
- |
通过Cold Start开始定位并定期输出NMEA语句。 |
||||||||||||||||||||||||||||||
@GNS |
卫星掩码 |
通过参数的位掩码值选择用于定位的卫星。
NOTE
GLONASS, Galileo, BeiDou 不能同时使用。 命令示例:
|
||||||||||||||||||||||||||||||
@GPOE |
<纬度[度]> |
以椭球坐标设置接收器当前位置。 命令示例:
|
||||||||||||||||||||||||||||||
@GSR |
- |
通过Hot start开始定位并定期输出NMEA语句。 |
||||||||||||||||||||||||||||||
@GSTP |
- |
停止定位。 |
||||||||||||||||||||||||||||||
@GSW |
- |
通过Warm Start开始定位,并定期输出NMEA语句。 |
||||||||||||||||||||||||||||||
@GTIM |
<年> |
设置UTC时间。 命令示例:
|
||||||||||||||||||||||||||||||
@VER |
- |
返回所有零的版本号。 |
5.6.10.5. 应用程序操作示例
gnss_atcmd应用程序的操作示例如下所示。
5.6.10.5.1. 配置
要运行此应用程序,请启用以下SDK配置。
CONFIG_CXD56_GNSS=y
CONFIG_GPSUTILS_CXD56NMEA_LIB=y
CONFIG_EXAMPLES_GNSS_ATCMD=y
您可以通过执行以下命令来自动启用这些配置。
./tools/config.py examples/gnss_atcmd
同样,可以通过配置GNSS Command IO切换用于命令输入/输出的端口。
│ Prompt: GNSS Command IO │ Location: │ -> Examples │ -> GNSS CXD5603 @command emulator example (EXAMPLES_GNSS_ATCMD [=y])
-
Example uses USB CDC tty : 使用扩展板上的USB端口(默认)
-
Example uses STDINOUT for nsh debug UART : 主板上的USB端口(UART1)
-
Example uses UART ttyS0 : 主板上的USB端口(UART1)
-
Example uses UART ttyS1 : 不支持
-
Example uses UART ttyS2 : 主板和扩展板上的UART端口(UART2)
在nsh提示符下运行gnss_atcmd应用程序后,从终端输入以上命令。
5.6.10.5.2. 示例:通过Cold Start开始和停止定位
选择GPS、Glonass和QZSS L1C/A作为定位卫星,然后从Cold Start开始定位。 之后,输出NMEA语句,经过一段适当的时间后,停止定位并终止gnss_atcmd。
nsh> gnss_atcmd (开始申请)
@GNS 0x0b↵ (定位卫星选择)
@GCD↵ (Cold Start开始定位)
----- <NMEA输出> ----
@GSTP↵ (停止定位)
@AEXT↵ (申请终止)
nsh>
5.6.10.5.3. 示例:GNSS结束后的热启动定位
首先,选择GPS,Glonass和QZSS L1C/A作为定位卫星,然后使用Cold Start开始定位。 定位后,在保持Spresense功率的同时终止GNSS,并通过Hot Start再次启动定位。 在Hot Start中,定位将在开始定位后几秒钟固定。
nsh> gnss_atcmd (开始申请)
@GNS 0x0b↵ (选择定位卫星)
@GCD↵ (Cold Start开始定位)
----- <NMEA输出> ----
@GSTP↵ (停止定位)
@AEXT↵ (申请终止)
nsh> gnss_atcmd (开始申请)
@GSR↵ (Hot Start开始定位)
----- <NMEA出力> ----
@GSTP↵ (停止定位)
@AEXT↵ (终止申请)
nsh>
5.6.10.5.4. 示例:Spresense电源关闭后的Hot Start定位
首先,选择GPS、Glonass和QZSS L1C/A作为定位卫星,然后使用冷启动开始定位。 之后,退出GNSS后关闭Spresense电源。
再次打开Spresense后,输入UTC时间和当前位置,并通过Hot Start开始定位。 在Hot Start中,定位将在开始定位后几秒钟固定。
nsh> gnss_atcmd (开始申请)
@GNS 0x0b↵ (选择定位卫星)
@GCD↵ (Cold Start定位开始)
----- <NMEA输出> ----
@GSTP↵ (停止定位)
@BUP↵ (备份到闪存)
@AEXT↵ (终止申请)
----- <Power OFF> -----
----- <Power ON> -----
nsh> gnss_atcmd (开始申请)
@GTIM 2018 11 09 02 45 05↵ (设置UTC)
@GPOE 35 39 31 139 44 05↵ (设置当前位置)
@GSR↵ (Hot Start开始定位)
----- <NMEA出力> ----
@GSTP↵ (停止定位)
@AEXT↵ (终止申请)
nsh>
5.6.10.5.5. 准天顶卫星QZQSM句子输出
选择QZSS L1/CA作为卫星掩码,启用带有NMEA掩码的QZQSM语句,然后开始定位。 如果接收条件良好,则在不到10秒的时间内输出第一个QZQSM语句,然后每4秒输出一次。
nsh> gnss_atcmd
@GNS 0x29↵
@BSSL 0x40ef↵
@GCD↵
$GPGGA,000001.00,,,,,0,00,,,,,,,*49
$GNGLL,,,,,000001.00,V,N*55
…
$GNZDA,000008.00,06,01,1980,,*77
$QZQSM,56,9AADF260540002C3F2587F8B101962082C41A588ACB1181623500011439023C*7B
$GPGGA,000009.00,,,,,0,00,,,,,,,*41
…
5.7. ASMP 框架
5.7.1. ASMP 框架简介
ASMP是准备好轻松处理多核处理器的框架。
此框架中定义了两个任务。
-
Supervisor任务 (主CPU任务)
-
Worker任务 (副CPU任务)
![Diagram](images/diag-c4534bd31de8be84c88a5b2bb7d3bfc7.png)
主管任务使用ASMP框架API来控制辅助任务的启动和停止。 详细信息,请参考 多核任务 。
Worker任务是完全独立于主CPU并在副CPU上并行处理的任务。因此,需要一种通信方法来在任务之间交换数据和状态。
ASMP框架为Supervisor任务和Worker任务的通信任务提供以下功能。
多核消息队列 是类似于POSIX消息队列的消息交换接口。
![Diagram](images/diag-d2717059fdc2bcbb3223b39c19a30144.png)
多核互斥锁 提供了类似于pthread互斥锁的独占控制机制。
![Diagram](images/diag-e14bf772ca9c7d753d2e5553217419ec.png)
多核共享内存 提供了每个任务共享的存储区域。
![Diagram](images/diag-bbeb95a67ff807e65635e69b7ab7b9b1.png)
5.7.2. 配置
[ASMP] = Y [ASMP shared memory size] = 0x100000
ASMP框架要求内核管理之外的内存区域用于任务之间的共享内存。可以使用 ASMP shared memory size
选项进行更改。如果您不需要ASMP框架,则内核可以利用1.5MB的内存。
以128KB为单位设置 ASMP Shared memory size 设置(0x20000)。
|
5.7.3. Worker 启动顺序
![Diagram](images/diag-23ec4d8e910d986725caaa06adbbc73f.png)
-
调用 mptask_init 创建一个Worker程序
-
根据需要准备消息队列,共享内存等,并将其链接到Worker程序
-
调用 mptask_exec 运行Worker程序
-
完成后,调用 mptask_destroy 停止Worker程序
-
如果已创建消息队列或共享内存,则将其丢弃
有关详细信息,请参考 Supervisor任务示例 和 Worker Task示例 。
5.7.4. 创建 Worker
Worker任务加载并执行ELF格式文件。但是,由于Worker在副CPU上运行,因此无法直接调用内核和SDK API。
ASMP框架支持的ELF文件布局如下。
![Diagram](images/diag-d1ca866e2680ce3dc99cf576a184d309.png)
每个程序的起始地址必须为 0x00000000
,并且每个部分都必须是连续的。
SDK提供了为Worker生成ELF文件所需的编译器选项,链接器选项和链接器脚本。
这些在 sdk/Make.defs
中分别定义为 CELFFLAGS
和 LDRAWELFFLAGS
。有关特定的构建规则,请参考 ASMP Worker任务 'Hello World' 示例 。
此外,需要一个Worker程序库才能将ASMP框架与Worker一起使用。该库包含配置工作程序所需的代码(上图中的蓝色部分)。但是,由于未在SDK生成序列中准备此库,因此用户必须在生成工作程序之前先生成它。
有关如何生成Worker程序库的示例,请参考 Worker Task Makefile 和 Worker Task Library Makefile 。
5.8. 传感器控制单元
5.8.1. 概览
传感器控制单元(SCU)可以从连接到SCU设备中SPI或I2C总线的传感器设备获取传感数据。
-
通过定序器自动获取传感器数据
-
传感器数据稀疏功能(抽取器)
-
IIR滤波器功能
-
事件通知功能
可以从应用程序中设置这些功能。
SCU具有SPI,两个I2C总线,八个定序器和两个抽取器。
![Diagram](images/diag-a66a2aee7a119a9117ae56bdfd03ac90.png)
5.8.2. 配置
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 | 音序器采样预分频器会影响音序器采样率 |
2 | SCU时钟模式指定SCU工作时钟 |
3 | SCU32K时钟源可以选择采样频率(32768Hz)时钟源 |
4 | 可以将一个SCU抽取器分配给两个以上的传感器,但是最多可以将两个传感器分配给抽取器。 |
5.8.3. 音序器
SCU有两种类型的音序器。普通定序器定期获取传感器数据并将其存储在FIFO中。设备驱动程序从此FIFO获取数据。
![Diagram](images/diag-c1785ba91bc9f1ad5a61bda8b03d2997.png)
每个定序器的FIFO被划分为 SCUIOC_SETFIFO
指定的大小。FIFO内存总计40KB。在使用设备驱动程序之前,必须先设置应用程序。
ret = ioctl(fd, SCUIOC_SETFIFO, sizeof(struct three_axis_s) * 128);
定序器以采样率指定的间隔获取传感器数据。请注意,采样率不是由传感器设备规格决定的。应用程序将`doxygen:SCUIOC_SETSA发送给定序器必须在使用MPLE []使用采样率之前进行设置。
在使用采样率之前,必须使用 SCUIOC_SETSAMPLE
为音序器配置应用程序。
ret = ioctl(fd, SCUIOC_SETSAMPLE, 2);
SCUIOC_SETSAMPLE
指定基本时钟(32KHz)除以2的幂。
Sequencer sampling rate = 32768 / CONFIG_CXD56_SCU_PREDIV / (2 ^ n)
如果 CONFIG_CXD56_SCU_PREDIV
是默认值64,并且在 SCUIOC_SETSAMPLE
中指定了2,则采样率为128Hz。
应用程序应注意,定序器采样率与传感器规格中指定的采样率不同。该应用程序设置传感器设备的采样率和音序器,您需要确保比赛顺利进行。
该应用程序可以接收由 SCUIOC_SETWATERMARK
定义的信号。当传感器数据存储到定序器FIFO中,直到达到 watermark
中指定的数量时,SCU驱动程序就会向应用程序发出信号。
例如,如果应用程序将采样率设置为128 Hz,而 watermark
设置为128,则设备驱动程序将每秒发出一个信号。
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中获取的数据来扩展定序器功能。抽取器具有三个FIFO。
![Diagram](images/diag-0b3cd224d7ba24a3341d235eafb78bcc.png)
5.8.4.1. 抽取处理
抽取器应用CIC滤波器来稀疏音序器获取的数据。 应用程序可以使用 doxygen: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的幂以提取采样率指定的数据。 例如,如果采样率为64 Hz,并且_ratio_为1,(64 /(2 ^ 1)),则实际采样率为32 Hz。 注意,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滤波器和事件检测。 IIR滤波器可以在数据路径上的三个位置设置。 必须为要使用的每个FIFO进行设置。
5.8.5.1. IIR 滤波器
![Diagram](images/diag-ad9db3a7d27d9a0f8bdd7ae0a7ff047d.png)
A |
适用于FIFO和事件检测器 |
F |
仅适用于FIFO |
E |
仅提供给事件检测器(Event Detector) |
该应用程序可以设置两个IIR滤波器。FIFO是定序器FIFO或抽取器FIFO。事件检测是一项特殊功能,如下所述:
-
两者都适用(A)
-
(A)并适用于(F)
-
(A)并适用于(E)
-
两者都适用(F)
-
适用于(F)和(E)
-
两者都适用(E)
IIR过滤器设置在 filter_pos_e
中定义。
IIR滤波器可以调整一个因子。
![IIR 过滤器块图](images/scu_IIR_filter.png)
每个系数均为34位固定点,s2.31格式。因此,IIR滤波器的系数由32位(h)和8位(l)组成。
![struct_iir_coeff_s](images/scu_IIR_coeff.png)
31位_h_为S(带符号位)。,0 =加号,1 =减号。
30至29位_h_是整数部分。
十进制部分是从28到0的_H_和7到6位的_l_。
5.8.5.2. 事件检测(Event detector)
事件检测器(Event detector)监视传感器数据的状态并检测其中的变化。该应用程序将能够确定何时应采集传感器。
事件检测对超过阈值定义的传感器数据宽度的数量进行计数。可用于事件检测的传感器数据为16位。例如,如果获取的数据是每个XYZ轴的数据,则在进行归一化计算后,确定数据是否超过设定值。 当计数达到设定值时,事件检测器将生成一个带时间戳的中断。
归一化计算取决于要监视的数据数(1-3)。
-
如果是一个(例如,仅X轴):
norm = abs(x)
-
如果有两个(例如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滤波器。
![scu event](images/scu_event.png)
-
计数值超过阈值
-
当计数达到_count0_时,启动_count1_。
-
如果数据数量在达到_count0_之前降至阈值以下,则停止计数并重置计数。
-
达到总数_count0_ + _count1_时通知事件
-
如果_count1_为零,则一旦_count0_的值达到阈值,便会通知一个事件。
-
如果_count0_为零,则忽略所有设置
5.8.7. 驱动程序开发人员指南
要开发支持SCU功能的驱动程序,您需要了解以下内容:
-
了解音序器指令
-
与传感器装置的通信方法
-
如何连接定序器初始化(Open)和传感器驱动程序
-
从定序器中读取样本数据
-
如何通过ioctl命令音序器
5.8.7.1. 了解音序器指令
定序器指令为每条指令16位,并使用SPI或I2C总线与传感器设备进行通信。
开发驱动程序时,可以使用 doxygen:SCU_INST_SEND[]
或 doxygen: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和传感器驱动程序,则直接连接到SCU的SPI和I2C总线可能会导致冲突。设备驱动程序开发人员应仅通过提供的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;
在此示例中,打开定序器并将其存储在设备数据中。
可以从三个FIFO中读取抽取器,因此会生成三个设备文件。
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()
API支持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中。
使用 seq_read()
函数从FIFO读取累积的传感器数据。
len = seq_read(priv->seq, priv->id, buffer, len);
5.9. 内存使用库
5.9.1. 概要
媒体处理和传感器处理功能中,需要对数据用的大量存储空间进行安全的,没有碎片泄露之类的管理。因此,在Spresense中提供了内存实用程序库。
程序库包含”Memory Manager” 、"Message Library"、"Simple FIFO"三个库。
-
”Memory Manager” 是为了在 Multi task 间安全地管理存储空间, 隐含地进行段区间管理的固定长度内存池库。
-
"Message Library" 是为了使用 ”Memory Manager” ,使类对象在任务间通信用的库。
-
"Simple FIFO" 是为了应用程序和框架间进行数据传输用的最基本的FIFO。
这些库的使用是媒体出来和传感器处理的前提。 详细请参考以下各章。
5.9.2. 内存管理
”Memory Manager” 的说明。
5.9.2.1. 概要
”Memory Manager" 为隐含地对获取和释放进行管理的固定长度内存库。 根据用途定义内存空间为内存池,并在其中确保需要的段,来完成需要使用内存的获取和释放。
确定池的种类,段数等进行内存空间的分配,可以防止内存空间的碎片化。 这个分配信息称作 "memory_layout" ,根据应用程序的需求切换这个布局,来确保功能所需的内存。
这个内存布局可以根据应用程序自由确定及生成。生成方法记录在 配置及生成 里。
可以通过生成 "MemHandle" 并调用段分配API取得所需的 "memory segment" 。此方法分配的段,在 "MemHandle" 的实例被废弃前一直有效。
多用户异步分别的 "MemHandle",即使有用户释放,此空间还将保持,直到所有用户不用时才被释放。
5.9.2.2. 段的获取和释放机制
各用户对象通过生成指向所需内存空间段的MemHandle,调用段区间获取API,确保相关的内存段。
此时,段空间参考计数器+1。 由此,该段空间为使用状态,确保不会被其他要求使用。
![Get memHandle](images/memhandle_example1.png)
该引用计数器,在 "operator=" 及 "Copy Constructor" 中也做+1操作。
由此,拷贝并传递此 "MemHandle" 的实例给数据管道中的下一个对象, 对象可以引用 "MemHandle" 绑定的段,确保这个空间。
![Copy memHandle](images/memhandle_example2.png)
相反,不需要空间时,废弃 "MemHandle" 的实例,在析构函数中参考计数器-1。
由此,所有的引用实例废弃后,链接的段会被自动释放。
![Use memHandle](images/memhandle_example3.png)
这种机制使得所需的对象对各自的内存进行异步确保所需的对象间实例,不用时即使根据各自的时间废弃,也可以安全地分配和释放内存。无需特别关注,在所有的用户废弃实例后,将默认断开所有引用。
![Release memHandle](images/memhandle_example4.png)
关于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,用来以byte单位指定lib_area的大小。 尺寸小于sizeof(MemMgrLite::Manager)时,返回ERR_DATA_SIZE。 当前的实装所需的尺寸为 (4 + 4 * NUM_MEM_POOLS) ,所有最小12bytes, 最大1028bytes。 //可选项中使用动态创建池的场合,作为追加要求,(8 + 5 * NUM_DYN_POOLS)需要四舍五入为4的倍数。 //此库支持多核(共享池)时,该空间 必须是所有CPU都可访问。 //ARM的TCM,MPS的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时,返回ERR_STS。 //此库在使用多核(共享池)支持模式时,需要在等待所有的CPU执行(平台特定方式)完成Manager::initFirst()后,调用本函数。
示例使用由内存格式定义文件定义的MemMgrLite的数据空间。
void* lib_data_va = MemMgrLite::translatePoolAddrToVa(MEMMGR_DATA_AREA_ADDR);
if (MemMgrLite::Manager::initPerCpu(lib_data_va)) != ERR_OK)
{
/* 出错处理 */;
}
示例使用由静态变量定义的MemMgrLite的数据空间。
if (MemMgrLite::Manager::initPerCpu(s_lib_data)) != ERR_OK)
{
/* 出错处理 */;
}
static err_t Manager::createStaticPools(NumLayout layout_no, void* work_area, uint32_t area_size, const PoolAttr *pool_attr)
NumLayout layout_no : 内存布局编号 (0 origin) void* work_area : 操作静态内存池的工作区域 uint32_t area_size : 工作区域的大小(byte单位) const PoolAttr *pool_attr : 内存布局的地址
ERR_OK : 初始化成功 ERR_STS : 未执行initPerCpu(),或者本函数已经被执行 ERR_ADR_ALIGN : work_area不是4的倍数 ERR_DATA_SIZE : area_size小于最大工作区域
创建已经指定了布局编号的内存池群。 内存布局需要变更时,先使用Manager::destroyStaticPools() 废弃掉现有内存池群后,再次调用本函数。 Manager::initPerCpu()未被调用, 或者已经调用过本函数的话,返回ERR_STS。 参数layout_no,用来指定被使用的内存布局的编号。 参数work_area用来指定静态内存库操作用的工作区域。 地址不是4的倍数时,返回ERR_ADR_ALIGN。 指定的区域,在调用Manager::destroyStaticPools()前将一直存在。 另外,性能上期望是SRAM等高速内存。 参数area_size用来指定静态内存库操作用的工作区域的大小。 指定的值大于 每个布局编号定义的宏MEMMGR_Lx_WORK_SIZE(x未布局编号) 全部布局中最大的工作区域大小,由宏MEMMGR_MAX_WORK_SIZE定义。 工作区域不足时,返回ERR_DATA_SIZE。 //此库用于多核(共享池)支持时,此空间必须是所有的CPU都可访问。 //ARM的TCM,MIPS的ScratchPad等,因为使用CPU本地内存,所以多核支持时无法指定。
通过内存布局定义文件,定义并使用MemMgrLite用的工作区域的示例。 多核支持时,或者配置SRAM时,此方法易用。
S_ASSERT(MEMMGR_WORK_AREA_SIZE >= MEMMGR_MAX_WORK_SIZE);
const NumLayout layout_no = 0;
void* work_va = MemMgrLite::translatePoolAddrToVa(MEMMGR_WORK_AREA_ADDR);
if (MemMgrLite::Manager::createStaticPools(layout_no, work_va, MEMMGR_WORK_AREA_SIZE) != ERR_OK)
{
/* 出错处理 */;
}
通过静态变量定义并使用MemMgrLite用的工作区域的示例。 区域需要保证4byte对齐,所以使用uint32_t的队列方式定义。
static uint32_t s_work_area[MEMMGR_MAX_WORK_SIZE / sizeof(uint32_t)];
const NumLayout layout_no = 0;
if(MemMgrLite::Manager::createStaticPools(layout_no, s_work_area, sizeof(s_work_area) != ERR_OK)
{
/* 出错处理 */;
}
Spresense 只能选择SRAM。 |
static void Manager::destroyStaticPools()
无
无
废弃由Manager::createStaticPools()创建的静态内存池。 静态内存未被创建时,不发生任何变化。 Manager::initPerCpu()未被执行时,发出"debug_assert"。 废弃内存池时,进行段释放泄露检测。 发现释放泄露时,发出"assert"。 启用围栏功能时,检测静态内存池的围栏。 发现围栏被破坏时,发出"assert"。
/* 废弃静态内存池 */
MemMgrLite::Manager::destroyStaticPools();
static NumLayout Manager::getCurrentLayoutNo()
无
当下设定的内存布局编号 未被设定时,返回BadLayoutNo
取得当下的内存布局编号。 内存布局编号的初始值为BadLayoutNo。 调用Manager::createStaticPools()设定为参数指定的值。 调用Manager::destroyStaticPools()重置为初始值。
/* 取得当下的内存布局编号 */
MemMgrLite::NumLayout layout_no = MemMgrLite::Manager::getCurrentLayoutNo();
if (layout_no != MemMgrLite::BadLayoutNo)
{
printf("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个来源)*/
参考说明
isPoolAvailable()返回当下的内存布局指定的池ID是否有效。 getPoolType(), getPoolAddr(), getPoolSize(), getPoolNumSegs()分别返回 在创建时指定的池类别,地址,大小,段数。 getPoolNumAvailSegs()返回目前的空段数。 isPoolFenceEnable()返回创建池的时候指定的围栏设定。 本函数仅在围栏功能有效时(UseFence = True)被定义。 //getPoolSpinLockId()返回创建池的时候指定的旋转锁ID。 //本函数仅在多核支持有效时(UseMultiCore = True)被定义。 指定为无效的池会发生访问空指针的问题, 请确认池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. 配置及生成
介绍如何编写和创建 ”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. 如何编写内存布局文件
"mem_layout.conf"用来进行”Memory Manager”配置,用户常量的定义,设备的定义,PoolLayout的定义。各功能如下说明。
指定是否使用 ”Memory Manager” 各种功能。可以指定的功能和指定方法如下。 .
特性 | 描述 |
---|---|
UseFence |
使用True/False指定是否使用围栏。 启用后,将定义USE_MEMMGR_FENCE宏,并在指定区域之前和之后4个字节 配置栅栏以进行覆写检测。 另外,定义了围栏检查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
定义各种存储设备。这些定义用于定义固定区域。 它作为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 |
该区域的地址。 指定的值是4的整数倍,但0除外。 |
size |
区域的大小。 指定的值是4的整数倍,但0除外。 |
为每个存储设备定义一个区域。
每个区域的起始地址由每个设备的累积大小,对齐和围墙确定。
它作为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 |
该区域的起始对齐方式。指定MinAlign的倍数(= 4),不包括0。 |
size |
区域的大小。 指定的值是4的整数倍,但0除外。 |
fence |
指定是启用还是禁用围栏。(如果UseFence为False,则忽略此项目) |
定义内存池的布局。
每个池区域的起始地址由每个固定区域的累积大小,对齐和围墙确定。
在头文件中,输出池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 )
在单独的区域中最多可以定义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 )
每个参数的说明如下。
参数名 | 说明 |
---|---|
name |
池名称(以大写字母开头,以“POOL”结尾。可以使用大写字母,数字和) |
area |
FixedArea区域名称用作池区域。将区域放在RAM中。 |
align |
池的起始对齐方式。指定MinAlign的倍数(= 4),不包括0。 |
pool-size |
游泳池的大小。该值是4的整数倍,但0除外。段大小*段数。 |
seg |
段数。指定一个介于1和255之间的值。 |
fence |
指定是启用还是禁用围栏。如果UseFence为False,则忽略此项目。 |
5.9.2.4.2. 生成方式
使用“mem_layout.py”创建布局文件的方法如下。
- 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”的每个参数的说明如下。
参数 | 描述 |
---|---|
mem_layout.conf |
内存布局定义文件 |
layout_header |
头文件,其中各种常量值作为宏输出。为了调整内存大小,将以注释格式输出每个布局的剩余可用大小(/*剩余XXXX_AREA = 0xXXXXXXXX */)。 |
fence_header |
输出FixedArea的内存防护地址的头文件。该文件由“内存管理器”使用,用户不应使用。 |
pool_header |
输出PoolArea的各种定义的头文件。该文件由“内存管理器”使用,用户不应使用。 |
5.9.3. 消息库
主要是,当需要在任务之间(例如“Memory Manager”)发送和接收类实例时,通常无法处理OS提供的系统调用,因此可以使用“Message Library”来实现。 本节介绍此“Message Library”。
5.9.3.1. 常用
“Message Library”是一个任务同步库,可以在任务之间发送和接收类实例。
该库发送显式指定为ID的目的地。 接收器还等待传输事件并执行事件驱动的操作。
![Message Sequence](images/message_sequence.png)
另外,该消息具有类型,并且由类型来判断以确定接收到的实例是否存在。
但是,您要发送和接收的类必须正确实现复制构造函数。 |
5.9.3.2. 消息编号及类型
5.9.3.2.1. 消息编号
Message ID是根据下面的 如何编写消息内存布局文件 的配置静态创建的。 有关配置的详细信息,请参阅该内容。
确认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和要回复的ret_id ID,创建要发送的类(对象)的实例,然后使用MsgLib::send发送它。
该ID是根据应用程序创建的,并且在显式指定用于发送和接收的数据路径时使用。
5.9.3.2.2. 消息类型
消息库使用Message Type来确定发送了什么对象,以便从接收到的Message Packet中检索适当的实例。 因此,消息类型被添加到所有消息包。
此Message Type在“sdk/modules/include/memutils/message/message_type.h”中定义。
Message Type采用以下数据结构。
![Diagram](images/diag-788d4c0f09923d010e16c0d729677b6e.png)
每个字段的说明如下。
参数名 | 值域 | 说明 |
---|---|---|
REQ |
[15] |
指示消息是请求还是响应。0是响应,1是请求。 |
MSG_USER |
[14-12] |
消息的用户信息。您可以指定一个使用消息的系统,该消息的值介于0到7之间。但是,由于AudioSubSystem保留了6个,SensorSubSystem保留了7个,因此实际上可以使用0到5之间的值。 |
MSG_CATEGORY |
[11-8] |
消息类别信息。您可以指定一个介于0到15之间的值的类别。 |
MSG_SUB_TYPE |
[7-0] |
消息子类型信息。您可以在类别中指定一个消息类型,其值在0到255之间。 |
定义的ID如下。
指示消息方向。
D15 | Description |
---|---|
0 |
response |
1 |
request |
指定使用该消息的系统。 当前,音频和传感器ID被保留。
D14-D12 | 描述 |
---|---|
0-5 |
保留 |
6 |
Audio 子系统 |
7 |
Sensor 子系统 |
请在每个系统中自由定义。
例如,对于音频,
"sdk/modules/include/audio/audio_message_types.h"
对于传感器
"sdk/modules/include/sensing/sensor_message_types.h"
定义于。
请在每个系统中自由定义。
例如,对于音频
sdk/modules/audio/include/commmon/audio_interanl_message_types.h
对于传感器
sdk/modules/include/sensing/sensor_message_types.h
定义于。
如果消息类型是不同的ID,则即使定义了相同的值,也可以对其进行操作,但是它易于调试,可更改性(例如更改数据路径和发送到另一个ID)等。确保所有内容都是唯一的。 |
使用以下实现从Message Packet中提取实例。
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. 接口
“Message Library”的界面如下。 有关详细信息,请参考 memutils_message 。
5.9.3.3.1. MsgLib类函数
static err_t MsgLib::initFirst()
无
ERR_OK : 初始化成功 ERR_STS : 该功能已执行
初始化整个消息库。 在使用此库中的任何其他API之前,应仅通过预先确定的单个CPU对该函数执行一次。
static err_t MsgLib::initPerCpu(uint32_t num_pools, uint32_t top_drm)
uint32_t num_pools : 信息段的数量(信息类型的数量) uint32_t top_drm : 信息库的管理数据区的地址
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。 endIsr()指定了共享队列,则执行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或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类函数
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()用于与传输时具有相同类型的引用,而peepkParamOther() 用于与传输时具有不同类型的引用(例如,参数标头类型)。 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,消息参数长度将更改为零。 请注意,消息参数的引用和指针无效。
5.9.3.4. 配置及生成
本节介绍如何创建和创建“Message Library”布局文件。
布局信息用Python编写在“ msgq_layout.conf”中(可以更改名称) 带有两个C++标头的名为“ msgq_layout.py”的工具。 生成“msgq_id.h” 、 “msgq_pool.h”。 用户可以通过包含此标头来使用“Message Library”。
5.9.3.4.1. 如何编写消息内存布局文件
“msgq_layout.conf”定义“Message Library”的用户常数,消息队列池,调试值和消息参数检查。每个说明如下所示。
可以将以“ U_”开头的用户定义名称分配给消息布局定义文件中使用的常量。该定义可以是任何Python表达式。
# 用户定义的常数必须具有以“ U_”开头的大写字母和数字名称 # 如果使用以“U_MSGQ_”开头的名称进行定义,则该名称也将作为定义宏在msgq_id.h中输出。 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 |
使用以“ MSGQ_”开头的字符串指定消息队列池ID的名称。“MSGQ_NULL”、“MSGQ_TOP”、“MSGQ_END”已保留,无法使用。 |
n_size |
正常优先级队列的每个元素中的字节数(8到512)。 指定固定的标头长度(8个字节)+参数长度(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. 生成方式
使用“msgq_layout.py”创建布局文件的方法如下。
- Usage
$python3 msgq_layout.conf [start_addr] [size] [id_header] [pool_header]
- Example
python3 msgq_layout.conf 0x000fe000 0x1000 msgq_id.h msgq_pool.h
“msgq_layout.py”的每个参数的说明如下。
参数 | 描述 |
---|---|
msgq_layout.conf |
消息队列布局定义文件 |
start_addr |
留言区地址 |
size |
区域大小(以字节为单位) |
id_header |
消息队列ID宏输出到的文件 |
pool_header |
输出消息队列池定义的文件 |
5.9.4. 简单 FIFO
5.9.4.2. 接口
有关详细信息,请参见Doxygen。 API列表如下。
API 名称 | 描述 |
---|---|
CMN_SimpleFifoInitialize |
SimpleFIFO初始化。设置用于FIFO的存储区。 |
CMN_SimpleFifoOffer |
将数据推入FIFO。使用memcpy()进行复制处理。 |
CMN_SimpleFifoOfferWithSpecificCopier |
将数据推入FIFO。复制过程由用户准备。假定使用DMA复制。 |
CMN_SimpleFifoOfferContinuous |
将数据推入FIFO。将指定的尺寸复制到连续区域。如果没有连续的区域用于指定的大小,则不执行推送。使用memcpy()进行复制处理。 |
CMN_SimpleFifoOfferContinuousWithSpecificCopier |
将数据推入FIFO。将指定的尺寸复制到连续区域。如果没有连续的区域用于指定的大小,则不执行推送。复制过程由用户准备。假定使用DMA复制。 |
CMN_SimpleFifoPoll |
从FIFO弹出数据。弹出的数据从FIFO中删除。使用memcpy()进行复制处理。 |
CMN_SimpleFifoPollWithSpecificCopier |
从FIFO弹出数据。弹出的数据从FIFO中删除。复制过程由用户准备。假定使用DMA复制。 |
CMN_SimpleFifoPeekWithOffset |
从FIFO的开头从指定的偏移量获取指定大小的数据的地址信息。当您想引用FIFO数据而不删除它时,请使用它。 |
CMN_SimpleFifoPeek |
从FIFO的开头获取指定大小数据的地址信息。当您想引用FIFO数据而不删除它时,请使用它。 |
CMN_SimpleFifoClear |
清除FIFO读/写指针,使其为空。 |
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. 电源管理
5.10.2. 电源状态
Spresense SDK支持三种睡眠模式: Deep Sleep
、 Cold Sleep
和 Hot Sleep
以节省电量。
Deep Sleep
-
在CXD5602中,所有电源域都被关闭,只有CXD5247 PMIC(电源管理IC)通电。 此时,CXD5247也处于休眠状态,并且可以将功耗降至最低。
Cold Sleep
-
仅CXD5602中的最小必需电源域为上电。与
Deep Sleep
相比,例如警报计时器、USB插入/提取、GPIO信号更改等。 Hot Sleep
-
当OS处于空闲状态时,过渡到此睡眠状态。由于在休眠期间保持SRAM状态,因此唤醒时无需从SPI-Flash重新加载程序,因此您可以快速从休眠状态返回。
当前的SDK版本不支持热睡眠。 |
5.10.2.1. 电源状态转换
主要电源状态的状态转换图如下所示。
![电源状态图](images/pm_state.png)
状态 | 描述 |
---|---|
PowerOff |
包括CXD5247 PMIC(电源管理IC)在内的所有电源都已关闭 |
Battery Check |
正在监视电源电压电平。 CXD5602 在电源为3.4V或更高时启动,并转换为“运行”状态。 |
Run |
正常执行状态。 |
Idle |
操作系统空闲。OS空闲任务等待WFI的中断(等待中断)。 |
Hot Sleep |
当OS空闲状态预计将持续一定时间时,将进入此状态。 在Hot Sleep期间,SRAM数据将保留。 |
Cold Sleep |
仅打开CXD5602的最低必需电源,并且关闭其他电源(包括SRAM)。 |
Deep Sleep |
CXD5602断电,只有CXD5247 PMIC(电源管理IC)上电。 |
5.10.3. 电源状态控制 API
Spresense SDK具有来自各种电源状态的启动因子(以下称为 boot cause
)和用于控制许可/禁止启动因子的启动掩码( boot mask
)。
- boot cause
-
代表起床和从各种睡眠模式开始的因素
- boot mask
-
控制启用/禁用激活因子
boot cause
和 boot mask
具有通用的位标志结构,并且每个引导原因都由一位表示。当 boot mask
的相应位设置为1时,将其作为引导因子使能;而当设置为0时,将其禁用。
当 boot mask
允许的激活因子事件发生在Sleep状态时,从Sleep唤醒。那时,哪个引导因素反映在 boot cause
中。
激活因子和激活掩码的定义如下所示。
-
引导类型指示系统是通过POR(上电复位)启动,通过Reboot重新启动还是从Deep Sleep或Cold Sleep状态启动。
-
仅当是时才允许将Maskable用作激活因子。 没有人不能被禁止作为激活因素。
boot cause / boot mask | Boot Type | Maskable | Description |
---|---|---|---|
PM_BOOT_POR_NORMAL |
POR Boot |
No |
电池或电源已打开 |
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 |
(*2) 当检测到WKUPL信号时 |
PM_BOOT_DEEP_WKUPS |
Deep Boot |
Yes (*1) |
检测到WKUPS信号 (*2) |
PM_BOOT_DEEP_RTC |
Deep Boot |
Yes (*1) |
发出RTC警报 |
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警报 |
PM_BOOT_COLD_RTC_ALM0 |
Cold Boot |
Yes |
RTC警报0(SDK使用警报0) |
PM_BOOT_COLD_RTC_ALM1 |
Cold Boot |
Yes |
已触及RTC警告1(未使用) |
PM_BOOT_COLD_RTC_ALM2 |
Cold Boot |
Yes |
触发了RTC Alarm2(未使用) |
PM_BOOT_COLD_RTC_ALMERR |
Cold Boot |
Yes |
发生RTC警报错误(未使用) |
PM_BOOT_COLD_GPIO |
Cold Boot |
Yes |
检测到GPIO中断 |
PM_BOOT_COLD_SEN_INT |
Cold Boot |
Yes |
检测到传感器中断(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板上不支持。
提供以下与电源状态控制有关的电源管理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模式
-
调用此函数没有任何回报,并且状态转换为休眠状态。
-
- Arguments
-
模式枚举 描述 PM_SLEEP_DEEP
进入Deep Sleep
PM_SLEEP_COLD
进入Cold Sleep
通过发出up_pm_sleep(),仅CXD5602芯片就转换为睡眠模式。 当转换到Sleep状态时,可能会添加不仅取决于芯片而且取决于板子的处理。 通过在BSP的板相关部分中实现board_power_off()函数,可以转换到包括板控制在内的休眠模式。
在BSP相关部分中实现的睡眠过渡API如下所示。 也可以从NuttShell上的poweroff命令控制它。
- Function Prototype
int board_power_off(int status)
- Arguments
-
状态枚举 描述 NuttShell 命令 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断电
-
CXD5247进入睡眠模式,并且关闭了提供给CXD5602的内核电源和IO电源
-
CXD5247保留了RTC时间(假设系统具有RTC XTAL)
-
CXD5247 GPO开关在Deep Sleep期间保持值
-
如果在深度睡眠期间不必保持GPO处于打开状态,则从节电的角度出发,建议在进入深度睡眠之前将其关闭
-
-
CXD5247负载开关已关闭
-
由于CXD5602 IO已关闭,请注意不要使电流从外围设备泄漏到CXD5602
-
5.10.4.1.2. 电源消耗
-
电池末端的电池电流消耗约为uA至几uA。由于CXD5602的电源已关闭,请仔细设计电路板,以确保从外围设备到CXD5602的电流不会泄漏。
关于Sleep 的功耗,如果将SD卡插入扩展板,则电流消耗将增加约5 mA,具体取决于SD卡的功耗。 |
5.10.4.1.3. 睡眠状况
-
通过调用up_pm_sleep(PM_SLEEP_DEEP)或board_poweroff(BOARD_POWEROFF_DEEP)转换为深度睡眠状态
-
当使用WKUPL信号时,在声明WKUPL 3秒以上时转换到Deep Sleep状态
由于WKUPL引脚没有出现在Spresense板上,因此无法使用WKUPL信号将功能转换为深度睡眠 |
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警报触发时 -
PM_BOOT_DEEP_USB_ATTACH
: 连接USB电缆时-
通常,连接USB电缆后,您将无法进入深度睡眠状态。 但是,如果CONFIG_BOARD_USB_DISABLE_IN_DEEP_SLEEPING=y,则可以禁用USB功能并转换为Deep Sleep状态。 目前,您无法使用USB连接作为触发器来唤醒。
-
由于WKUPL和WKUPS端子没有出现在Spresense板上,因此无法通过WKUPL和WKUPS信号使用深度睡眠中的唤醒功能 |
5.10.4.2. Cold Sleep
5.10.4.2.1. 特性
-
对于CXD5602,仅打开芯片内部的PMU电源域
-
CXD5602 I/O引脚使能
-
备份SRAM内容将保留
-
-
CXD5247正常运行
-
CXD5247保留了RTC时间(假设系统具有RTC XTAL)
-
CXD5247 GPO开关在Cold Sleep期间保持其值
-
CXD5247负载开关即使在Cold Sleep期间也能保持数值
-
5.10.4.2.4. 唤醒条件
发生唤醒条件时,程序将以SPI-Flash加载的方式启动,因此大约需要与Power-On-Reset启动相同的时间。
-
PM_BOOT_COLD_SCU_INT
: 触发SCU中断时 -
PM_BOOT_COLD_SEN_INT
: 当传感器中断触发时 -
PM_BOOT_COLD_PMIC_INT
: 当CXD5247的中断触发时-
当WKUPS信号有效时
-
通知电池电量不足时
-
-
PM_BOOT_COLD_GPIO
: 当GPIO中断有效时 -
PM_BOOT_COLD_RTC_ALM0
: 当RTC警报触发时 -
PM_BOOT_COLD_USB_ATTACH
: 连接USB电缆时 -
PM_BOOT_COLD_USB_DETACH
: 断开USB连接线时
IMPROTANT: Spresense板不支持通过USB插入/拔出从Cold Sleep中唤醒的功能
5.10.4.3. Hot Sleep
当前的SDK版本不支持Hot Sleep。 |
5.10.4.3.3. 睡眠状况
-
通过设置CONFIG_CXD56_HOT_SLEEP=y启用热睡眠。
-
以下CONFIG参数可用于更改转换为热睡眠状态的条件。
-
CONFIG_CXD56_HOT_SLEEP_WAIT_COUNT(毫秒)
-
CONFIG_CXD56_HOT_SLEEP_THRESHOLD(毫秒)
-
-
获取和释放唤醒锁
-
Hot Sleep控制由稍后描述的wakelock机制执行。只要获得至少一个wakelock,就禁止过渡到Hot Sleep。
-
-
电源管理器使用以下算法转换为Hot Sleep状态。
-
如果NuttX OS处于空闲状态,则
Power Manager
将计算空闲状态下花费的时间-
如果发生任何中断,请退出空闲状态并重置计数器
-
-
如果计数器超过CONFIG_CXD56_HOT_SLEEP_WAIT_COUNT定义的值,则
Power Manager
将在此之后计算预期的空闲时间 -
如果超过了估计的空闲时间> CONFIG_CXD56_HOT_SLEEP_THRESHOLD,则系统将转换为Hot Sleep状态
-
如果获取了wakelock,则不允许系统转换为热睡眠状态
-
5.10.5. 节电控制
Spresense SDK 提供以下节电功能。
-
CPU系统时钟控制
-
CPU睡眠控制
5.10.5.1. CPU 系统时钟控制
系统时钟提供三种主要模式。
模式 | CPU 时钟 | CXD5602 核电压 |
---|---|---|
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
启用动态时钟控制后,以HV模式启动后,系统将时钟降至RCOSC模式。在稳定状态下,以RCOSC模式运行。
为了临时执行高性能处理,可以使用Frequency Lock动态更改时钟。
RCOSC模式,LV模式和HV模式可增加工作时钟并提高性能,但是功耗会相应增加。Frequency Lock机制在切换到LV模式时使用LV锁定,在切换到HV模式时使用HV锁定。释放获得的锁定后,将恢复原始时钟状态。 例如,如果同时获取了LV锁定和HV锁定,则会设置HV模式。 释放HV锁定后,它将切换到下一个更高的时钟模式,即LV模式。 释放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);
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. 传感器融合框架
5.11.1. 常见
CXD5602具有低功率恒定感测功能和具有许多内核和存储器的传感器融合功能。
SDK提供了一个框架来简化这一过程。
传感器融合框架由每个传感器的“Sensor Manager”和“Sensor Client”组成。
-
“Sensor Manager”基于Publish-Subscribe Architecture控制多个“Sensor Client”,并分发从此处发布的Sensor Data。
-
“Sensor Client”是一个“Logical Sensor”,它控制着控制各种“Sensor Device”的驱动程序,“Logical Sensro”,通过融合每个“Sensor Device”中的数据来实现高性能传感器的应用程序 包含用于接收数据的“Sensor Application”。
Sensor Framework的体系结构图如下。
![Sensor Framework(Publish/subscribe) Architecture](images/sensor_FW_pub_sub_diagram.png)
5.11.2. 传感器管理
“传感器管理器”注册并管理多个“传感器客户端”正确分发传感器数据。 正确分发传感器数据。
5.11.2.1. 常见
在“Sensor Manager”中注册的“Sensor Client”将获取的数据启动到“Sensor Manager”,以便“Sensor Manager”将数据正确分发给“Sensor Client”并请求订阅。
它还提供了一个框架,可以关闭未订阅的“传感器客户端”的电源模式。
5.11.2.2. API 接口
“Sensor Manager”提供以下API。 “Sensor Manager”界面可控制,以称为数据包的数据格式发布命令。 注册并删除“Sensor Client”并传输数据。
APIs | 描述 | 对应 API | 对应数据包 |
---|---|---|---|
Register |
注册传感器客户到管理者,作为订阅者。 |
sensor_command_register_t |
|
Release |
在管理者注销传感器客户。 |
sensor_command_release_t |
|
SendData |
不通过MemHandle发送函数到管理者。 |
sensor_command_data_t |
|
SendData(MH) |
通过MemHandler发送函数到管理者。 |
sensor_command_data_mh_t |
|
SendSetPower |
设置传感器供电状态。 |
sensor_command_power_t |
5.11.3. 逻辑传感器
5.11.3.1. 常见
“Logical Sensor”是用于基于从各种物理传感器获得的传感器数据的某种信号处理算法来创建功能强大的传感器数据的“Sensor Client”,它由软件模块组成。
实际的算法实现分配可以通过以下方式完成。
-
在NuttX上实现为任务。
-
使用asmp框架在DSP上实现。
-
使用供应商提供的加密DSP实施。
5.11.3.1.1. Nuttx中逻辑传感器任务
在NuttX上实现为模块或任务。
特别地,在不繁重的处理或不频繁执行的处理的情况下,通过以这种方式实现,可以在不消耗存储器和CPU资源的情况下实现。
内容 | 示例来源 |
---|---|
Barometer |
传感器供应商 |
TAP Gesture (Tapping Detector) |
SDK |
5.11.3.1.2. DSP在asmp上的逻辑传感器
当用户创建自己的“Logical Sensor”算法并需要将处理任务转移到DSP端(例如繁重的处理)时,请使用ASMP框架在DSP端有可能实现它。
在这种情况下,可以通过将“Logical Sensor”实现为管理任务并将处理请求从管理任务发送到DSP上的工作任务来实现多核处理中的“Logical Sensor”。
内容 | 示例来源 |
---|---|
"" |
SDK |
![SuperVisor and Worker](images/sensor_supervisor_worker.png)
有关ASMP框架,请参考 ASMP 框架 。 |
5.11.4. 逻辑传感器API
每个逻辑传感器都提供以下API:
APIs | 描述 |
---|---|
Create |
创建对象来跟worker通信。 |
Open |
加载和启动worker。 |
Write |
发送数据到worker。 |
Close |
销毁worker任务。 |
这些请求被定义为以下事件,并用于发送和接收DSP上的工作任务。
有关详细信息,请参见每个主管。
事件 | 描述 |
---|---|
Init |
初始化事件。 |
Exec |
执行事件。 |
Flush |
终止事件。 |
5.11.6. 计步器
Step Counter 的框图如下所示。
![Configuration diagram of Step Counter](images/sensor_step_counter_configuration_diagram.png)
5.11.6.1. 监视
主管是“Logical Sensor”的框架。 提供多个API来控制主管的DSP上的Worker。
5.11.6.1.1. 接口 API
Step Counter 提供以下五个API。
接口 | 描述 | 对应接口 |
---|---|---|
Create |
创建StepCounterClass对象。 |
|
Open |
加载StepCounter库并启动worker。 |
|
Write |
发送数据到StepCounter worker任务。 |
|
Close |
销毁StepCounter的worker任务。 |
|
Set |
设置StepCounter库。 |
5.11.6.1.2. 计步器数据格式
“Step Counter”要求使用以下指定格式的“Accelerometer”数据。
使用Write API将这些数据发送给工作人员。
![加速器数据格式](images/sensor_step_counter_require_data_format_accel.png)
5.11.6.1.3. 感应数据
“Step Counter”每秒输出以下Step Counter信息。 此信息存储在 StepCounterStepInfo 。
数据 | 单位 | 备注 |
---|---|---|
频率 |
Hz |
取值范围为1到3Hz。 即使停止,该值仍为1 Hz或更高。 |
歩幅 |
cm |
步行和跑步的固定值。 |
速度 |
m/s |
|
累积移动距离 |
m |
|
歩数 |
- |
|
行走模式 |
- |
指示停止/步行/跑步状态。 |
5.11.6.2. 使用方法
5.11.6.2.1. 准备
用于控制多个“Sensor Client”的框架称为"Sensor Manager” 控制“Step Counter”。
因此,要控制“Step Counter”,必须事先启用“Sensor Manager”。
要启用“Sensor Manager”,您需要调用 SS_ActivateSensorSubSystem(MsgQueId,api_response_callback_t) 。
MsgQueId必须是在消息库配置中定义的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”后,将“Accelerometer”注册为“Step Counter”请求订阅“Sensor Manager”的“Sensor Client”。
这时,指定调用回调函数 StepCounterWrite 作为Subscribe的过程。
另外,为了使“Application”了解“Step Counter”的检测结果
将“Application”请求订阅的“Step Counter”注册为“Step Client”。
此时,请指定回调函数来处理订阅。
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(®);
![Diagram](images/diag-ed4255411bdcaca75955e24be60f6fae.png)
5.11.6.2.2. 创建及打开
完成准备后,生成并启用“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*中,您需要指定一个指向“Step Counter”实例的指针,该实例是 StepCounterCreate 。
StepCounterOpen(step_counter_instance);
![Diagram](images/diag-c4781ad5b42780790d1d378c6b75f43d.png)
5.11.6.2.3. 设置步长
感测时的步幅的初始值在步行状态下为60cm,在跑步状态下为80cm。
要更改此初始值,您需要调用 StepCounterSet(FAR StepCounterClass *,StepCounterSetting *) 。
在StepCounterClass *中,您需要指定一个指向“Step Counter”实例的指针,该实例是 StepCounterCreate 。
对于StepCounterSetting *,将在步行状态和跑步状态下的步幅设置为以cm为单位的step_length。最大步幅为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);
![Diagram](images/diag-6d4244f82815bb2f67d4a11a52fd9da7.png)
“Step Counter”是 StepCounterWrite ,由“ Accelerometer” Subscribe调用,将数据发送到DSP上的Worker并执行传感处理。
此时的顺序如下所示。
![Diagram](images/diag-d2e295d53f5bc56412ddb18bef05f264.png)
5.11.6.2.4. 关闭
要禁用“Step Counter”,您需要调用 StepCounterClose(FAR StepCounterClass *) 。
在StepCounterClass *中,您需要指定一个指向“Step Counter”实例的指针,该实例是 StepCounterCreate 。
StepCounterClose(step_counter_instance);
![Diagram](images/diag-457068923d2998772d6ed9f72e49a249.png)
5.11.6.3. 编译配置
要使用“Step Counter”功能,
cd sdk tools/config.py -m
打开配置菜单并设置以下选项。
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在其他内核上运行,并分析从Supervisor发送的传感器数据。
它返回所请求事件的处理结果(步骤数,状态等)。
“Step Counter”的Worker需要下面显示的数据进行分析。
・ Accelerometer数据(32Hz、32样本/1秒)
5.11.6.4.1. 接口 API
“Step Counter”的worker提供以下三个API:
接口 | 描述 |
---|---|
Init |
为AESM(活动引擎和步数表)库初始化。 |
Exec |
对AESM库中的传感器数据执行计算。 |
Flush |
结束AESM库的执行过程。 |
根据从Supervisor发送的数据包中包含的事件类型和命令类型来调用这些API。
关系如下。
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”示例作为“Step Counter”的示例应用程序。 本节说明如何使用它。
5.11.6.5.1. 准备
要使用“ Step Counter”示例应用程序,请按如下所示设置配置选项。
按以下方式选择:
[Examples] [Step counter sensor example] <= Y
或使用“Step Counter”的默认配置。
./tools/config.py examples/step_counter
如下设置任务间通信库(Message Library)和内存管理库(Memory Manager)。
必须定义使用“Step Counter”时所需的MemoryLayout(pool)。
定义在MemoaryLayout定义文件中进行,并且可以使用该工具生成要包含在代码中的头文件。
在“Step Counter”示例应用程序中,执行以下操作:
cd examples/step_counter/config python3 mem_layout.conf
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 |
该区域的起始对齐方式。 指定MinAlign的倍数(= 4),不包括0 |
size |
区域的大小。 指定的值是0以外的4的倍数 |
fence |
指定启用/禁用围栏(如果UseFence为false,则忽略此项目) |
每个名称的用法如下。
SENSOR_WORK_AREA |
用于传感器融合 |
MSG_QUE_AREA |
由MessageQueue使用(固定名称)。不要超过msgq_id.h中的(MSGQ_END_DRM-MSGQ_TOP_DRAM)的大小。 |
MEMMGR_WORK_AREA |
内存管理器使用的工作区(固定名称,固定大小) |
MEMMGR_DATA_AREA |
Memery Manager使用的数据区域(固定名称,固定大小) |
确保每个名称的总大小不超过由mpshm_init() 、 doxygen:mpshm_remap [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 |
池的起始对齐方式。 指定MinAlign的倍数(= 4),不包括0 |
pool-size |
游泳池的大小。 该值是4的整数倍,但0除外。 对于基本池,段大小*段数 |
seg |
段数。 指定一个介于1到255之间的值 |
fence |
指定围栏有效还是无效。 如果UseFence为false,则忽略此项目 |
每个名称的用法如下。
SENSOR_DSP_CMD_BUF_POOL |
与Worker发送和接收的缓冲区 |
ACCEL_DATA_BUF_POOL |
Accelerometer数据缓冲区 |
如果设置更改,请使用该工具生成一个新的头文件。 NOTE: 有关每个定义的详细信息,请参考 examples/step_counter/config/mem_layout.conf 。 如果设置更改,请使用该工具生成一个新的头文件。
有必要定义“MessageQueue”,这在使用“Step Counter”时是必需的。 该定义在MessageQueueLayout定义文件中完成,并且可以使用该工具生成要包含在代码中的头文件。
在“Step Counter”示例应用程序中,执行以下操作:
cd examples/step_counter/config python3 msgq_layout.conf
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 |
使用以“ MSGQ_”开头的字符串指定消息队列池标识的名称。 |
n_size |
正常优先级队列的每个元素中的字节数(8到512)。 指定固定的标头长度(8个字节)+参数长度(4的倍数)。 |
n_num |
正常优先级队列中的元素数(1到16384)。 |
h_size |
高优先级队列的每个元素中的字节数(0或8到512)。不使用时指定0。 |
h_num |
高优先级队列中的元素数(0或1到16384)。不使用时指定0。 |
n_size是最佳值,不应更改。
n_num不需要更改,但是在其他应用程序中使用Step Counter时,可能需要考虑负载而增加该值。
要优先处理步骤计数器时,请使用h_size和h_nums。
有关每个定义的详细信息,请参考 examples/step_counter/config/msgq_layout.conf 。 如果设置更改,请使用该工具生成一个新的头文件。 |
5.12. JPEG 解码
5.12.1. 简介
提供基于IJG开发的libjpeg库的JPEG解码功能。
尽管它总体上遵循原始libjpeg库的API规范,但仍有针对Spresense定制的要点,因此本文档详细介绍了定制。
在以下说明中,“libjpeg实例”指的是struct jpeg_decompress_struct类型变量,该变量必须由使用libjpeg的应用程序准备。
5.12.2. Spresense 定制
输出格式(色彩空间)
原始libjpeg支持的所有格式均为24位/像素或更高,但Spresense支持16位/像素Cb/Y/Cr/Y(YUV4:2:2)格式,因此可以用更少的内存进行解码。
以下定义是Spresense中的有效色彩空间定义。
通过将此参数设置为libjpeg实例的成员out_color_space并执行jpeg_start_decompress(),
允许在任何支持的色彩空间中进行解码。
宏名 | 含义 | 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压缩单元块,基本上是8x8像素。 大小根据JPEG编码参数(在JPEG标头中设置)和解码参数(由应用程序设置)而有所不同。 作为应用程序,在执行jpeg_start_decompress()之后,您可以从libjpeg实例信息中了解1MCU的大小,如下所示: * 1MCU宽度 = output_width / MCUs_per_row (总宽度/宽度方向上的MCU总数) * 1MCU高度 = output_height / MCU_rows_in_scan(总高度/高度方向上的MCU总数) |
对于MCU单元,每个单元的像素数(数据大小)不取决于图像大小,因此,随着图像大小的增加,这两种类型的单元之间的差异变得更加明显,如下例所示。
単位 | 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中均发生错误时,libjpeg API执行任务将由exit()终止。 原始的libjpeg提供了一个示例,该示例使用“setjmp/longjmp”作为不结束任务的方法, 但是由于Spresense(NuttX)不支持setjmp/longjmp,因此无法使用此方法。 将来,我们计划使用setjmp/longjmp以外的方法来支持错误处理。
libjpeg的错误处理是在“错误处理程序不返回”的前提下实现的,如果错误处理程序返回,则不能保证操作。 |
错误处理程序 终止方式 |
目的 | Spresense | 原生 |
---|---|---|---|
exit |
libjpeg API执行任务结束 |
○ |
○ |
longjmp |
到使用setjmp保存的堆栈上下文 |
× |
○ |
return |
返回错误检测功能 |
× |
× |
除程序错误外,“错误”还可能由外部原因引起。 有关典型错误,请参考 输出到标准错误输出 。 |
5.12.5. 输出到标准错误输出
当检测到错误或警告时,libjpeg库会将消息输出到英语标准错误输出。 消息的含义和输出原因与原始libjpeg相同。
以下是一些常见的消息。
输出信息 | 错误/警告 | 输出时机 |
---|---|---|
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 |
警告 |
霍夫曼代码解码错误 |
Premature end of JPEG file |
警告 |
当输入的JPEG数据到达EOF而没有EOI标记出现时输出 |
5.13. Alpha blend
5.13.1. 概要
Alpha blend API提供了以指定比例(alpha)混合两个图像(背景(dst)和前景(src))的功能。
仅支持YUV 4:2:2像素格式。 |
alpha可以指定每个像素,并且src的比率由256分率表示。
|
![Diagram](images/diag-82916c4cd85bd6da5694354b9bcbb5f9.png)
5.13.2. Alpha blend API详情
背景图像(dst),前景图像(src)和α图像(alpha), 基于dst混合对象像素原点来指定混合位置坐标(pos_x,pos_y)。
每个图像还可以指定剪辑位置(左上角坐标(x1,y1)和右下角坐标(x2,y2))。
负pos_x表示混合对象区域左边缘的左侧。 |
下面显示的是将pox_x,pos_y指定为负值时的混合图像。
图中只有黄色框圈的部分(重叠部分)会被混合结果更新。
![alpha blend pos](images/alpha_blend_pos.png)
5.13.2.1. 图像信息的指定方法
三个图像全部都使用imageproc_imginfo_t结构指定。
imageproc_imginfo_t的每个成员代表什么,如下所示。
![alpha blend imginfo t](images/alpha_blend_imginfo_t.png)
如果未指定rect(设置rect=NULL的话)、则将使用整个图像。
6. NuttX提供的功能
f请参考 NuttX.org ,了解NuttX提供的功能。