个性的啄木鸟 · recorder-core - npm· 1 月前 · |
谦和的数据线 · 嵌入式Linux下的户外报警对讲系统设计 ...· 2 月前 · |
急躁的毛巾 · Linux音频采集和在国产化平台中遇到的坑( ...· 2 月前 · |
豪气的电脑桌 · Spresense SDK 开发指南· 12 月前 · |
强悍的煎饼果子 · html5录音支持pc和Android、io ...· 1 年前 · |
风流倜傥的熊猫 · 现在英国早上几点钟:英国与中国时差及常用时间 ...· 5 月前 · |
傲视众生的电脑桌 · 温州肯恩大学,Wenzhou-Kean,We ...· 1 年前 · |
爱吹牛的小马驹 · 《水浒传》中,高俅的太尉,蔡京的太师,哪个官 ...· 1 年前 · |
完美的长颈鹿 · 印度河流域文明- 知乎· 1 年前 · |
温柔的汽水 · mBlock 3 将在最新macOS ...· 1 年前 · |
Spresense SDK 是用于控制上述硬件的软件。 Spresense SDK 使用RTOS之一的 NuttX 作为OS,并提供了详细的功能以基于该OS发挥 CXD5602 的性能。 Spresense 的每个驱动程序都是根据类似于Linux的 NuttX 的驱动程序框架以及 Spresense 的Audio,GNSS,ASMP等中间件层在驱动程序层之上实现的。
从复位释放 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二进制代码。 请参考 引导加载程序安装 ,以获取二进制文件。
使用 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.
5.1.1.1. 驱动 (Lower Half)NuttX有一个称为Upper Half的驱动程序,用于标准设备和总线。Upper Half驱动程序对应用程序执行接口和协议处理,但不能单独操作。因此,可以通过在BSP中正确实现Lower Half驱动程序来使用该设备。
Spresense SDK提供的Lower Half驱动程序包括以下内容。
某些特定于板的处理过程可能会根据板的实现方式(引脚设置等)而改变。这些过程分为可以在一定程度上共享的过程和完全取决于董事会的过程。
它们分别存储在
bsp/board/common
和
bsp/board/<board name>
中,其中
<board name>
是对应的董事会名称。
在
nsh
中,板初始化(
boardctl()
)被调用。实际的板卡初始化是根据NuttX规则在
board_app_initialize
函数中实现的。该功能执行处理以初始化要使用的板。
初始化过程包括初始化要使用的最低功能 Spresense 和初始化要使用的设备驱动程序。
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
使用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)
顶层API把音频及其子系统作为命令对象进行控制。(High Level API Command System)
命令对象由
AS_SendAudioCommand
向音频及其子系统发送。
被发送的命令对象为
AudioCommand
。
命令的详细记载于
命令对象
。
音频及其子系统,根据发送过来的命令进行处理并返回结果。
结果通过
AudioResult
对象返回,可由
AS_ReceiveAudioResult
获取。
结果的详细记载于
结果格式
。
命令对象
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;
使用 AUDCMD_SETPOWEROFFSTATUS 命令,可迁移到PowerOff状态。 使用 AUDCMD_SETPLAYERSTATUS 命令,可迁移到Player状态。 使用 AUDCMD_SETRECORDERSTATUS 命令,可迁移到Recorder状态。 使用 AUDCMD_SETBASEBANDSTATUS 命令,可迁移到Baseband状态。
从SD卡等存储设备或WiFi/LTE等网络设备解码声音压缩文件,实现AnalogOut或I2S的发音功能的状态。状态中有PlayerReady状态和PlayerActive状态,2个子状态。 PlayerReady状态为音乐播放停止的状态。使用 AUDCMD_PLAYPLAYER 迁移到PlayerActive进行音乐播放动作。 PlayerActive状态为音乐播放状态。使用 AUDCMD_STOPPLAYER 迁移到PlayerReady停止音乐播放。
使用 AUDCMD_SETREADYSTATUS 命令,仅迁移到Ready状态。
这是一种状态,可实现压缩从麦克风输入的语音数据并将其写入SD卡等存储设备,或在WiFi / LTE等网络上启动并记录该功能的状态。 状态由RecorderReady状态和RecorderActive状态这两种子状态构成。 RecorderReady是声音记录停止的状态。通过 AUDCMD_STARTREC 迁移到RecorderActive状态,开始声音记录。 RecorderActive状态为声音记录中的状态。通过 AUDCMD_STOPREC 迁移到RecorderReady状态,停止声音记录。
通过 AUDCMD_SETREADYSTATUS 命令,只能迁移到Ready状态。
这种状态实现了内部处理从Mic输入的音频数据并将其输出到AnalogOut或I2S的功能。 状态有BasebandReady状态和BasebandActive状态两个子状态。 BasebandReady状态为声音输出入停止的状态。通过 AUDCMD_STARTBB 迁移到BasebandActive开始声音的输出入动作。 RecorderActive状态为声音输出入时的状态。通过 AUDCMD_STOPBB 迁移到BasebandReady停止声音的输出入动作。
通过 AUDCMD_SETREADYSTATUS 命令,只能迁移到Ready状态。
NOTE:现在的固件未对应Baseband状态。
AudioSubSystem通过特殊的方法管理使用的数据区域。 MemoryUtility的MemoryManager库,根据Memory Layout定义文件的Layout信息,以固定长度的存储池的方式确保必须的存储区域。 此Layout信息可重复定义,通过切换Layout号的指定,可以确保各功能的必须存储。 Layout信息,可根据Application需求自由指定,但需注意各功能所需的最低限资源。
详细信息,请参考 内存管理 库说明。
另外,各功能合计所需的Layout相关,请参考各example的说明。
AudioSubSystem内的各对象,通过生成指向所需存储空间的MemHandle实例,确保并使用链接到实例的存储片段。
5.3.3.7.2. 消息库使用此存储管理机制时,各任务间需要任务对象的收发信。因此,AudioSubSystem使用在任务同步机制中准备的可以在任务实例间进行收发信的message库。
各任务对象通过追加收信端的ID,可以往指定的任务送信。
举例来说,对象送信时发送收信端的ID,收信端的任务仅在自己的ID发生送信要求时才收信。 发生收信前,此任务sleep并等待。
如此,AudioSubSystem以事件驱动进行对象设计。
Message相关,需要事先准备Message使用的头文件群。 这些头文件,通过作成MessageQueueLayout定义文件(msgq_layout.conf),使用工具生成。
音频及其子系统运行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
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
通过"AudioManager" 、"MediaPlayerObject"、"OuputpuMixerObject"、"RendererComponent" 等
为了控制音频子系统而设计的软件组件,实现Audio Player。
因此,为了实现Player,需要事先调用以下的对象生成函数。
为了使AudioManager有效,需要调用
AS_CreateAudioManager(AudioSubSystemIDs, AudioAttentionCb)
。
AudioSubSystemIDs
中,需要指定由 Message Library Configuration 定义的 MsgQueID 。
指定为
0xFF
的项目为不使用。
AudioAttentionCb
为异步通知,需要设定回调函数。指定为NULL时不发生通知。
static void attention_callback(const ErrorAttentionParam *attparam)
AudioSubSystemIDs ids;
ids.app = MSGQ_AUD_APP;
ids.mng = MSGQ_AUD_MNG;
ids.player_main = MSGQ_AUD_PLY0;
ids.player_sub = MSGQ_AUD_PLY1;
ids.mixer = MSGQ_AUD_OUTPUT_MIX;
ids.recorder = 0xFF;
ids.effector = 0xFF;
ids.recognizer = 0xFF;
AS_CreateAudioManager(ids, attention_callback);
为了使 MediaPlayerObject 有效,需要调用
AS_CreatePlayerMulti(AsPlayerId, AsCreatePlayerParams_t, AudioAttentionCb)
。
AsPlayerId
指定为2个既存Player (
AsPlayerId
的
AS_PLAYER_ID_0
,
AS_PLAYER_ID_1
) 中的一个。使用这2个Player时,请分别Create。
AsCreatePlayerParams_t
中,需要指定由 Message Library Configuration 定义的 MsgQueID 和由 Memory Manager Configuration 定义的 PoolId 。
AudioAttentionCb
为异步通知,需要设定回调函数。不使用AudioManager时设定。使用AS_CreateAudioManager登录回调函数的场合,指定为NULL时不发生通知。
static void attention_callback_from_player0(const ErrorAttentionParam *attparam)
static void attention_callback_from_player1(const ErrorAttentionParam *attparam)
AsCreatePlayerParams_t cparam;
cparam.msgq_id.player = MSGQ_AUD_PLY0;
cparam.msgq_id.mng = MSGQ_AUD_MNG;
cparam.msgq_id.mixer = MSGQ_AUD_OUTPUT_MIX
cparam.msgq_id.dsp = MSGQ_AUD_DSP;
cparam.pool_id.es = S0_DEC_ES_MAIN_BUF_POOL;
cparam.pool_id.pcm = S0_REND_PCM_BUF_POOL;
cparam.pool_id.dsp = S0_DEC_APU_CMD_POOL;
cparam.pool_id.src_work = S0_SRC_WORK_BUF_POOL;
bool act_rst = AS_CreatePlayerMulti(AS_PLAYER_ID_0, &cparam, attention_callback_from_player0);
cparam.msgq_id.player = MSGQ_AUD_PLY1;
cparam.msgq_id.mng = MSGQ_AUD_MNG;
cparam.msgq_id.mixer = MSGQ_AUD_OUTPUT_MIX
cparam.msgq_id.dsp = MSGQ_AUD_DSP;
cparam.pool_id.es = S0_DEC_ES_SUB_BUF_POOL;
cparam.pool_id.pcm = S0_REND_PCM_SUB_BUF_POOL;
cparam.pool_id.dsp = S0_DEC_APU_CMD_POOL;
cparam.pool_id.src_work = S0_SRC_WORK_SUB_BUF_POOL;
act_rst = AS_CreatePlayerMulti(AS_PLAYER_ID_1, &cparam, attention_callback_from_player1);
为了使 OutputMixerObject 有效,需要调用
AS_CreateOutputMixer(AsCreateOutputMixParams_t)
。
AsCreateOutputMixParams_t
中,需要指定由 AMessage Library Configuration 定义的 MsgQueID 和由 Memory Manager Configuration 定义的 PoolId 。
AudioAttentionCb
为异步通知,需要设定回调函数。不使用AudioManager时设定。使用AS_CreateAudioManager登录回调函数的场合,指定为NULL时不发生通知。
static void attention_callback_from_outputmixer(const ErrorAttentionParam *attparam)
AsCreateOutputMixParams_t cparam;
cparam.msgq_id.mixer = MSGQ_AUD_OUTPUT_MIX;
cparam.msgq_id.render_path0_filter_dsp = MSGQ_AUD_PFDSP0;
cparam.msgq_id.render_path1_filter_dsp = MSGQ_AUD_PFDSP1;
cparam.pool_id.render_path0_filter_pcm = S0_PF0_PCM_BUF_POOL;
cparam.pool_id.render_path1_filter_pcm = S0_PF1_PCM_BUF_POOL;
cparam.pool_id.render_path0_filter_dsp = S0_PF0_APU_CMD_POOL;
cparam.pool_id.render_path1_filter_dsp = S0_PF1_APU_CMD_POOL;
result = AS_CreateOutputMixer(&output_mix_act_param, attention_callback_from_outputmixer);
为了使 RendererComponent有效,需要调用
AS_CreateRenderer(AsCreateRendererParam_t)
。
AsCreateRendererParam_t
中,需要指定由 Message Library Configuration 定义的 MsgQueID 和由 Memory Manager Configuration 指定的 PoolId。
如果应用程序仅使用1个Player时,第3第4个参数(dev1_req, dev1_sync)需要设定为0xFF。
AsCreateRendererParam_t renderer_act_param;
cparam.msgq_id.dev0_req = MSGQ_AUD_RND_PLY0;
cparam.msgq_id.dev0_sync = MSGQ_AUD_RND_PLY0_SYNC;
cparam.msgq_id.dev1_req = MSGQ_AUD_RND_PLY1;
cparam.msgq_id.dev1_sync = MSGQ_AUD_RND_PLY1_SYNC;
result = AS_CreateRenderer(&renderer_act_param);
为了给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);
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);
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);
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);
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);
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);
设定
AsStopPlayerStopMode
的停止模式。停止模式有一般停止,ES终端停止,强制停止3种模式。
一般停止在有停止请求时尽早停止。即FIFO中还有残留数据的状态。
ES终端停止在有停止要求时,FIFO里面的数据全部播放完才停止。
强制停止为Audio SubSystem内部发生错误时使用的模式,应用程序无法发出。
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);
[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
AS_ATTENTION_SUB_CODE_SIMPLE_FIFO_UNDERFLOW
WARNING
因为AudioSubSystem无法读取播放数据导致。请提高把播放数据往SimpleFIFO里Write的任务的CPU占用率。
AS_ATTENTION_SUB_CODE_MEMHANDLE_ALLOC_ERROR
ERROR
因为数据区域的片段数不足导致。降低AudioSubSystem以外任务的优先度,或者增加数据区域的片段数。
AS_ATTENTION_SUB_CODE_TASK_CREATE_ERROR
ERROR
因为堆区域不足导致。请扩展堆区域。
AS_ATTENTION_SUB_CODE_DSP_VERSION_ERROR
ERROR
因为DSP二进制的版本不匹配导致。请把DSP 二进制映像文件更新为"sdk/modules/audio/dsp"中的文件。の
AS_ATTENTION_SUB_CODE_STREAM_PARSER_ERROR
ERROR
因为未找到播放文件的Sync word导致。请确认播放文件指定的编码器是否正确。
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 命令,迁移至播放停止状态。请如下设定任务间通信库(Message Library)和内存管理库(Memory Manager)。
需要定义使用AudioPlayer功能时必须的MemoryLayout(pool)。 定义通过MemoaryLayout文件进行,可以使用工具生成包含代码的头文件。
Audio Player 的example中如下进行。
cd examples/audio_player/config python3 mem_layout.confFixedAreas # 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
各参数说明如下。
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 ],
各参数的说明如下。
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],
各参数说明如下。
Using SPI-Flash instead of SD Card文件系统要用SPI-Flash的话,请将example的播放文件,Playlist,DSP二进制映像的指定路径改为"/mnt/spif/*"。 然后,拷贝播放文件,Playlist,DSP二进制映像到此路径下。
Error: /mnt/sd0/AUDIO directory path error. check the path! Error: app_open_contents_dir() failure. Exit AudioPlayer examplePlayList无法识别时,显示如下错误日志。请确认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****)当音频子系统在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将被删除。
PCM (输入) 数据缓存 : 数据缓存 ES Data Buffer (for DSP) : ES_BUF_POOL PreProcess DSP Command : PRE_APU_CMD_POOL Audio Encoder DSP Command : ENC_APU_CMD_POOL
要启用AudioManager,您需要调用 AS_CreateAudioManager(AudioSubSystemIDs) 。 必须在 AudioSubSystemIDs 中指定在“消息库配置”中定义的MsgQueID。 这意味着不使用带有
0xFF
的项目。 AudioAttentionCb 指定用于异步通知的回调函数。如果指定NULL,则不执行通知。static void attention_callback(const ErrorAttentionParam *attparam) AudioSubSystemIDs ids; ids.app = MSGQ_AUD_APP; ids.mng = MSGQ_AUD_MNG; ids.player_main = 0xFF; ids.player_sub = 0xFF; ids.micfrontend = MSGQ_AUD_FRONTEND ids.mixer = 0xFF; ids.recorder = MSGQ_AUD_RECORDER; ids.effector = 0xFF; ids.recognizer = 0xFF; AS_CreateAudioManager(ids, attention_callback);
创建 MicFrontendObject要启用MicFrontendObject,您需要调用 AS_CreateMicFrontend(AsCreateMicFrontendParams_t 。 必须指定在消息库配置中定义的MsgQueID和在内存管理器配置中定义的PoolId。MsgQueID的Dsp,PoolId的输出和dspcmd是 Preprocess仅 有效。否则,您可以指定NULL_POOL。
AudioAttentionCb 指定用于异步通知的回调函数。请在不使用AudioManager时指定。如果回调函数已在AS_CreateAudioManager中注册,则如果指定NULL,则不会发出通知。static void attention_callback_from_frontend(const ErrorAttentionParam *attparam) AsCreateMicFrontendParams_t cparam; cparam.msgq_id.micfrontend = MSGQ_AUD_FRONTEND; cparam.msgq_id.mng = MSGQ_AUD_MNG; cparam.msgq_id.dsp = MSGQ_AUD_PREDSP; cparam.pool_id.capin = S0_INPUT_BUF_POOL; cparam.pool_id.output = S0_PREPROC_BUF_POOL; /* When preprocess will not work, can be set to NULL_POOL */ cparam.pool_id.dspcmd = S0_PRE_APU_CMD_POOL; /* When preprocess will not work, can be set to NULL_POOL */ result = AS_CreateMicFrontend(&cparam, attention_callback_from_frontend);
创建 MediaRecorderObject要启用MediaRecorderObject,您需要调用 AS_CreateMediaRecorder(AsCreateRecorderParams_t) 。 必须指定在消息库配置中定义的MsgQueID和在内存管理器配置中定义的PoolId。
AudioAttentionCb 指定用于执行异步通知的回调函数。 请在不使用AudioManager时指定。如果回调函数已在AS_CreateAudioManager中注册,则如果指定NULL,则不会发出通知。static void attention_callback_from_recorder(const ErrorAttentionParam *attparam) AsCreateRecorderParams_t cparam; cparam.msgq_id.recorder = MSGQ_AUD_RECORDER; cparam.msgq_id.mng = MSGQ_AUD_MNG; cparam.msgq_id.dsp = MSGQ_AUD_DSP; cparam.pool_id.input = S0_INPUT_BUF_POOL; cparam.pool_id.output = S0_ES_BUF_POOL; cparam.pool_id.dsp = S0_ENC_APU_CMD_POOL; result = AS_CreateMediaRecorder(&cparam, attention_callback_from_recorder);
启用CaptureComponent,您需要调用 AS_CreateCapture(AsActCaptureParam_t) 。 必须指定在消息库配置中定义的MsgQueID和在内存管理器配置中定义的PoolId。 如果您的应用程序不使用第二个通道,则必须将第三个和第四个参数设置为0xFF。
AsActCaptureParam_t capture_act_param; cparam.msgq_id.dev0_req = MSGQ_AUD_CAP; cparam.msgq_id.dev0_sync = MSGQ_AUD_CAP_SYNC; cparam.msgq_id.dev1_req = 0xFF; cparam.msgq_id.dev1_sync = 0xFF; result = AS_CreateCapture(&cparam);
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);
init_mic_gain_param[]对于模拟麦克风,可以将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。
配置信息如下所述。
MIC频道选择图设置 [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”的位字段之间的关系如下。
输出设备句柄指定用于存储输出(编码的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 来设置前端操作(例如音频捕获)。
ch_numAS_CHANNEL_MONO : Monoral AS_CHANNEL_STEREO : Stereo AS_CHANNEL_4CH : 4ch AS_CHANNEL_6CH : 6ch AS_CHANNEL_8CH : 8ch
bit_lengthAS_BITLENGTH_16 : 16bit AS_BITLENGTH_24 : 24bit
samples/* 指定每帧的样本数。 */
out_fs指定从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
preproc_type设置预处理类型。
AsMicFrontendPreProcThrough : Through AsMicFrontendPreProcSrc : Sampling Rate Converter AsMicFrontendPreProcUserCustom : User Custom Process
命令设置示例以下是以每帧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时不使用。
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);
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";
AudioCommand command;
command.header.packet_length = LENGTH_START_RECORDER;
command.header.command_code = AUDCMD_STARTREC;
command.header.sub_code = 0x00;
AS_SendAudioCommand(&command)
AudioCommand command;
command.header.packet_length = LENGTH_STOP_RECORDER;
command.header.command_code = AUDCMD_STOPREC;
command.header.sub_code = 0x00;
AS_SendAudioCommand(&command)
:(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
AS_ATTENTION_SUB_CODE_SIMPLE_FIFO_OVERFLOW
WARNING
这是因为AudioSubSystem无法将记录数据输出到SimpleFIFO,请提高CPU占用率。
AS_ATTENTION_SUB_CODE_MEMHANDLE_ALLOC_ERROR
ERROR
这是因为数据区域中的段数不足。 降低AudioSubSystem以外的任务的优先级,或增加数据区域中的段数
AS_ATTENTION_SUB_CODE_TASK_CREATE_ERROR
ERROR
这是因为堆区域不足。扩展堆区域。
AS_ATTENTION_SUB_CODE_DSP_VERSION_ERROR
ERROR
这是因为DSP二进制版本不同。使用 "sdk/modules/audio/dsp” 文件更新DSP二进制映像。
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],
每个参数的说明如下。
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 ],
每个参数的说明如下。
必须定义使用AudioRecorder函数时所需的MessageQueue。 该定义在MessageQueueLayout定义文件中完成,并且可以使用该工具生成要包含在代码中的头文件。
在 音频录制示例 中,执行以下操作。
cd examples/audio_recorder/config python3 msgq_layout.confAudioCommand 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 " ); 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);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],
每个参数的说明如下。
识别动作开始后,开始抓取音频数据,经过Pre处理后往识别库输入音频数据。
Audio SubSystem在RecognizerMode工作时,可以在不关心音频数据(PCM数据)的情况下实现应用层。
Pre处理对音频数据根据语音识别库要求的输入格式作必要的处理(采样率变换及噪音抑制等)。
如果可以将抓取的音频原样输入语音识别库的话,Pre处理设定为“通过”也没问题。
抓取并采取Pre处理后的音频数据会输入到语音识别库。
是否有结果输出,由库决定。它有一个可以每次输入都通知识别结果的框架。请配合应用程序使用。数据流内部,使用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
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
使 AudioManager 有效,需要调用 AS_CreateAudioManager(AudioSubSystemIDs)。
在 AudioSubSystemIDs中,需要指定由 Message Library Configuration 定义的 MsgQueID 。指定为0xFF
的项目意为不使用。
AudioAttentionCb为异步通知,所以需要指定回调函数。指定为NULL
时为不通知。static void attention_callback(const ErrorAttentionParam *attparam) AudioSubSystemIDs ids; ids.app = MSGQ_AUD_APP; ids.mng = MSGQ_AUD_MNG; ids.player_main = 0xFF; ids.player_sub = 0xFF; ids.micfrontend = MSGQ_AUD_FRONTEND ids.mixer = 0xFF; ids.recorder = 0xFF; ids.effector = 0xFF; ids.recognizer = MSGQ_AUD_RECOGNIZER; AS_CreateAudioManager(ids, attention_callback);
创建 MicFrontendObject使MicFrontendObject有效,需要调用 AS_CreateMicFrontend(AsCreateMicFrontendParam_t) 。
需要指定由Message Library Configuration定义的MsgQueID和由Memory Manager Configuration定义的 PoolId。
MsgQueID 里的 dsp,PoolId里的 output 和 dspcmd 请仅在 执行Preprocess 时有效。其他场合可以指定为NULL_POOL 。
AudioAttentionCb为异步通知,所以需要指定回调函数。请在不使用AudioManager的场合指定。
由AS_CreateAudioManager登录回调函数的情况或者指定为NULL
时为不通知。static void attention_callback_from_frontend(const ErrorAttentionParam *attparam) AsCreateMicFrontendParams_t cparam; cparam.msgq_id.micfrontend = MSGQ_AUD_FRONTEND; cparam.msgq_id.mng = MSGQ_AUD_MNG; cparam.msgq_id.dsp = MSGQ_AUD_PREDSP; cparam.pool_id.capin = INPUT_BUF_POOL; cparam.pool_id.output = PREPROC_BUF_POOL; /* When you don't use preprocess, set to NULL_POOL */ cparam.pool_id.dspcmd = PRE_APU_CMD_POOL; /* When you don't use preprocess, set to NULL_POOL */ result = AS_CreateMicFrontend(&cparam, attention_callback_from_frontend);
创建 RecognizerObject使 RecognizerObject 有效,需要调用 AS_CreateRecognizer(AsCreateRecognizerParam_t) 。 需要指定由 Message Library Configuration 定义的 MsgQueID 和由 Memory Manager Configuration 定义的 PoolId 。
AudioAttentionCb 为异步通知,所以需要指定回调函数。请在不使用AudioManager的场合指定。
由AS_CreateAudioManager登录回调函数的情况或者指定为NULL
时为不通知。static void attention_callback_from_recognizer(const ErrorAttentionParam *attparam) AsCreateRecognizerParam_t cparam; cparam.msgq_id.recognizer = MSGQ_AUD_RECOGNIZER; cparam.msgq_id.parent = MSGQ_AUD_MNG; cparam.msgq_id.dsp = MSGQ_AUD_RCGDSP; cparam.pool_id.out = S0_OUTPUT_BUF_POOL; cparam.pool_id.dsp = S0_RCG_APU_CMD_POOL; result = AS_CreateRecognizer(&cparam, attention_callback_from_recognizer);
使 CaptureComponent 有效, 需要调用 AS_CreateCapture(AsActCaptureParam_t)。 需要指定由 Message Library Configuration 定义的 MsgQueID 和由 Memory Manager Configuration 定义的 PoolId 。
如果应用程序不使用第二通道的话,第3和第4参数需要设定为 0xFF 。AsActCaptureParam_t capture_act_param; cparam.msgq_id.dev0_req = MSGQ_AUD_CAP; cparam.msgq_id.dev0_sync = MSGQ_AUD_CAP_SYNC; cparam.msgq_id.dev1_req = 0xFF; cparam.msgq_id.dev1_sync = 0xFF; result = AS_CreateCapture(&cparam);
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);
对于模拟麦克风,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区域的关系如下。
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);
Init mic frontend 通过AUDCMD_INIT_MICFRONTEND, MicFrontendCommand, AsInitMicFrontEnd设置前端动作(音频抓取等)。
ch_numAS_CHANNEL_MONO : Monoral AS_CHANNEL_STEREO : Stereo AS_CHANNEL_4CH : 4ch AS_CHANNEL_6CH : 6ch AS_CHANNEL_8CH : 8ch
bit_lengthAS_BITLENGTH_16 : 16bit AS_BITLENGTH_24 : 24bit
samples/* 指定1帧的采样数。请根据识别库的规格设定。 */
out_fs指定由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
preproc_type设定PreProcess的类别。
AsMicFrontendPreProcThrough : Through AsMicFrontendPreProcSrc : Sampling Rate Converter AsMicFrontendPreProcUserCustom : User Custom Process
下图展示了使用PreprocessDSP的信号处理模块处于Audio Recognizer Function中的位置。
信号处理由高亮部分的CustomprocComp(Pre), UserCustomDSP(Pre)进行。
用户创建信号处理并嵌入到UserCustomDSP。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_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);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_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)
AudioCommand command;
command.header.packet_length = LENGTH_STOP_RECOGNIZER;
command.header.command_code = AUDCMD_STOP_RECOGNIZER;
command.header.sub_code = 0x00;
AS_SendAudioCommand(&command)
:(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
AS_ATTENTION_SUB_CODE_MEMHANDLE_ALLOC_ERROR
ERROR
数据区域的段数不够。降低AudioSubSystem以外任务的优先度,或者增加数据区域的段数。
AS_ATTENTION_SUB_CODE_TASK_CREATE_ERROR
ERROR
堆区域不足。请扩大堆区域。
AS_ATTENTION_SUB_CODE_DSP_VERSION_ERROR
ERROR
DSP二进制的版本不一致。请用"sdk/modules/audio/dsp"的文件更新DSP二进制映像。
需要调用
AS_CreateAudioManager(AudioSubSystemIDs)
使 AudioManager 有效。
AudioSubSystemIDs
中,需要指定由 Message Library Configuration 定义的 MsgQueID 。
指定为
0xFF
的项目意为不使用。
AudioAttentionCb
为异步通知,需要设定回调函数。在不使用AudioManager时设定。AS_CreateAudioManager注册回调函数时,指定为NULL时不通知。
static void attention_callback(const ErrorAttentionParam *attparam)
AudioSubSystemIDs ids;
ids.app = MSGQ_AUD_APP;
ids.mng = MSGQ_AUD_MNG;
ids.player_main = 0xFF;
ids.player_sub = 0xFF;
ids.mixer = 0xFF;
ids.recorder = 0xFF;
ids.effector = 0xFF;
ids.recognizer = 0xFF;
AS_CreateAudioManager(ids, attention_callback);
为了让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);
AudioCommand command;
command.header.packet_length = LENGTH_SET_THROUGH_STATUS;
command.header.command_code = AUDCMD_SETTHROUGHSTATUS;
command.header.sub_code = 0x00;
AS_SendAudioCommand(&command);
AudioCommand command;
command.header.packet_length = LENGTH_SETVOLUME;
command.header.command_code = AUDCMD_SETVOLUME;
command.header.sub_code = 0;
command.set_volume_param.input1_db = 0; /* 0.0dB */
command.set_volume_param.input2_db = 0; /* 0.0dB */
command.set_volume_param.master_db = -200; /* -20.0dB */
AS_SendAudioCommand(&command);
通过 AUDCMD_SETTHROUGHPATH 、 AsSetThroughPathParam 、 AsThroughPath 可以同时设定两路数据路径。 各路数据路径的参数设定如下。
设定数据路径的有效,无效。
true : 有效 false: 无效
设定数据的输入端。
AS_THROUGH_PATH_IN_MIC : 输入为MIC AS_THROUGH_PATH_IN_I2S1 : 输入为I2S AS_THROUGH_PATH_IN_MIXER : 输入为Mixer Out
AudioCommand command;
command.header.packet_length = LENGTH_SET_THROUGH_PATH;
command.header.command_code = AUDCMD_SETTHROUGHPATH;
command.header.sub_code = 0x00;
command.set_through_path.path1.en = true; (1)
command.set_through_path.path1.in = AS_THROUGH_PATH_IN_MIC; (2)
command.set_through_path.path1.out = AS_THROUGH_PATH_OUT_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);
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);
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);
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);
AS_ATTENTION_SUB_CODE_SIMPLE_FIFO_UNDERFLOW
发生时,音频播放将停止,状态变为播放错误。
发生此状态时,请立即发行
AsStopPlayerParam
命令,迁移至播放停止状态。
在上述框架上创建的UserCustomDSP按照下图所示的顺序在每个Audio功能中运行。
有关UserCutstomDSP在Audio Recorer功能中的位置,请参考
初始化麦克风前端(录音)
,对于音频识别器,请参考
初始化麦克风前端(识别器)
。
从下图中可以看到,
Init
和
Set
命令是由用户代码中的API调用触发的。
Exec
和
Flush
命令在AudioSubSystem内部发送(每次捕获音频时)。
(※ 不需要从用户代码发送
Exec
和
Flush
命令。)
总体流程如下。
发向Audio SubSystem的命令出现问题时,作为结果返回 AUDRLT_ERRORRESPONSE , 错误内容保存在 ErrorResponse 的参数中。 这个错误称为 应答错误 。
另外,Audio SubSystem的内部处理发现错误时,内部事件启动,通过由 AS_CreateAudioManager 登录的 回调函数 通知错误,错误内容保存在 ErrorAttention 的参数中。 这个被称为 状态错误 。
对于应答错误,状态错误,对应于各个错误请执行故障对应和错误处理等。
Audio 子系统发出的命令,如果按照式样执行了控制,对各条命令会应答命令结束的结果。
但是,由于违反状态或者参数错误等导致执行了和式样不一致的控制,将返回
AUDRLT_ERRORRESPONSE
的结果,
错误内容包含在
ErrorResponse
的参数中。
状态错误包含,播放动作时ES(Elementary Stream)断流(下溢出),记录动作时ES写入缓存的溢出(上溢出)等流程控制错误,存储资源枯竭,实时处理延迟等系统错误,
HW发生的错误等,修复需要重启系统的致命错误。
这些错误,可以通过"ErrorAttention"附加的"Attention Code"来判断。 请根据发生的"Attention Code"修改。 另外,变更实装方法也可改进错误。
以下为"ErrorAttention" 附加的 "Attention Code" 一览。
表格 21. Attention Code List Table 另外,配置路径中可能未放入需要的DSP。
sdk/modules/audio/dsp/
提供。
AudioPlayer和AudioRecorder都对其他参数(例如编解码器类型和采样频率)的组合有限制。
AudioSubSystem内Object Create时MemoryPool发生校验错误。
可能存储池ID未正确交付给Create的API,或者MemoryPool生成出现错误。
存储池ID的指定请参考如下文件。
Create Media Player
Create Media Recorder
MemoryPool的生成请参考如下文件。
AudioPlayer的PoolAreas设定
AudioPlayer动作中SimpleFIFO发生下溢出。
SimpleFIFO用于应用程序向AudioSubSystem传输ES数据的缓存。
应用程序跟不上AudioSubSystem读取数据的速度。
请调整任务优先度使应用程序来得及提供数据给缓存,
增加缓存尺寸使数据来得及放入等。
AudioRecorder动作中SimpleFIFO发生上溢出。
SimpleFIFO用于AudioSubSystem向应用程序传输EncodedES数据的缓存。
应用程序跟不上AudioSubSystem放入数据的速度。
请调整任务优先度使应用程序来得及读出数据,
增加缓存尺寸使数据来得及读出等。
AudioSubSystem内使用的实例的生成/删除失败。
Object/Component的CreateAPI, DeleteAPI可能发生了2重调用。
或者有可能Head区域不足。
一般不会发生的错误。
SDK内部可能有故障。
CXD5602 具有一个8位并行Camera I/F,并且可以连接具有该I/F的相机模块。目前,Spresense 通过Sony ISX012支持相机模块。 除了数据接口之外,相机模块通常还具有用于控制模块的接口。在ISX012中,I2C用于控制I/F。 以下是硬件配置的概述。
在 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,即可抽象为照相机的功能。
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调整是否完了。
通过执行以下设置的ioctl(VIDIOC_G_EXT_CTRLS),ioctl(VIDIOC_S_EXT_CTRLS), 可以获取3A参数・更改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格式
虽然V4L2_BUF_TYPE_VIDEO_CAPTURE和V4L2_BUF_TYPE_STILL_CAPTURE使用多线程可以并行控制、 但是下图写了NG时机的VIDIOC_S_FMT(V4L2_BUF_TYPE_STILL_CAPTURE)有可能不能正确反映。 (任意一个单独使用、或者下图写了OK的时机没有问题。)
由
image_recognition.MNIST.LeNet
创建的学习模型将28x28大小的图像作为输入并输出10个数组。这10个数组表示由索引识别的数字,并且输出该数组中每个数字的概率。例如,在阵列的顶部(索引0)输出输入图像为数字“0”的概率。
这些功能是POSIX兼容的设备文件,可以从应用程序进行操作。 设备文件为 “/dev/gps”,可以使用open(),close(),read(),seek(),ioctl()进行操作。
NuttX RTOS ioctl具有三个参数。 对于GNSS设备文件,第二个参数“req”是GNSS命令,第三个参数“arg”是in/out data传递。
[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'[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] = 45.6.3.1. 启动
您可以使用 CXD56_GNSS_IOCTL_SELECT_SATELLITE_SYSTEM 设置在启动时使用哪个卫星系统。定位间隔用 CXD56_GNSS_IOCTL_SET_OPE_MODE 设置。
对于“热启动”(“Hot Start”),您需要设置最新的位置和时间。有关详细信息,请参阅 GNSS 备份数据 中的 “Warm Start和Hot Start”设置。
如果使用信号,则将 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.1.2. 保存备份数据使用ioctl命令 CXD56_GNSS_IOCTL_SAVE_BACKUP_DATA 将备份数据保存到闪存等。备份数据以CONFIG_CXD56_GNSS_BACKUP_FILENAME中指定的文件名保存。 但是,如果您频繁写入闪存,则可能会导致内存故障。例如,在系统关闭时保存它。
当定位停止时,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。
字段numsv,numsv_tracking,numsv_calcpos和numsv_calcvel分别是可见卫星数,跟踪卫星数,用于位置计算的卫星数和用于速度计算的卫星数。 可见卫星包括根据卫星轨道信息预计会存在于当前天空中的卫星,但实际上它们被遮挡了并且没有接收到信号。 在跟踪卫星中,接收到的信号较弱且精度不高的信号未用于定位或速度计算。 位置精度取决于信号强度和后述的卫星配置,因此不能无条件地进行说明,但是,通常,如果将numsv_calcpos的数量保持在6以上,则通常会发生几米的误差而继续进行定位。
posDop包括DOP(精度稀释),DOP是根据卫星的几何排列计算得出的位置精度的下降速率。 'p’是整体精度下降率,'h’是水平精度下降率,'v’是垂直精度下降率,'ew’是东西方精度下降率,'ns’是南北精度指示下降率。
DOP越小,位置越好。例如,在城市之类的狭窄天空中,几乎无法接收到卫星,并且DOP变得更糟。 如果pDOP超过5,则表示该条件不适合定位。另一方面,如果pDOP为2或更小,则该条件足以用于定位。
该值表示C/N0比率(或有时称为CN0,CN),它是来自卫星的信号强度与噪声的比率。 由于来自GNSS卫星的信号是扩频信号,因此称为C/N0比,而不是S/N比,单位为dBHz。 该值越大,来自GNSS的卫星信号的可靠性越高,结果,使用它的定位将是稳定的。 另一方面,噪声源在GNSS天线附近,卫星方向与天线方向性差的方向一致,或者卫星与天线之间存在障碍物,卫星信号的跟踪性差。 该值变小并且定位变得不稳定。
俄罗斯定位卫星系统,有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年),其航迹涵盖了东亚和大洋洲,主要在日本,因此在其他地区没有补充作用。
通过将信号通知设置为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消息。
s_atcmd应用程序接收从主机(例如PC)发送的命令,并将处理结果返回给主机。从命令发送到响应的期间,不输出NMEA。nss_atcmd应用程序响应消息,指示命令完成返回消息(“Done”或“Err”)之前,请勿发出其他命令。 从命令发送到返回命令响应的时间取决于命令类型和当时的状态,但是在最坏的情况下可能需要5秒钟。当主机控制器检测到超时时,请确定超时为5秒。
5.6.10.4.1. 命令格式命令格式如下。在“@”(符号后)后发送命令字符串或参数,并在末尾发送换行代码。
@Command [Argument0] [Argument1]...<CR><LF>
nsh>
gnss_atcmd
(开始申请)
@GNS 0x0b↵
(定位卫星选择)
@GCD↵
(Cold Start开始定位)
----- <NMEA输出> ----
@GSTP↵
(停止定位)
@AEXT↵
(申请终止)
首先,选择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>
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↵
(终止申请)
$GNZDA,000008.00,06,01,1980,,*77
$QZQSM,56,9AADF260540002C3F2587F8B101962082C41A588ACB1181623500011439023C*7B
$GPGGA,000009.00,,,,,0,00,,,,,,,*41
每个程序的起始地址必须为
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 。
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)
定序器以采样率指定的间隔获取传感器数据。请注意,采样率不是由传感器设备规格决定的。应用程序将`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
结构中。
事件检测对超过阈值定义的传感器数据宽度的数量进行计数。可用于事件检测的传感器数据为16位。例如,如果获取的数据是每个XYZ轴的数据,则在进行归一化计算后,确定数据是否超过设定值。 当计数达到设定值时,事件检测器将生成一个带时间戳的中断。
归一化计算取决于要监视的数据数(1-3)。
使用
scuev_notify_s
结构配置事件检测。
有两个设置_rise_和_fall_,分别具有_threshold_、_ count0_和_count1_。
当计数的数据达到_count0_ + _count1_时,事件检测器会通知应用程序。
事件检测器使用IIR滤波器的输出,因此必须首先设置IIR滤波器。
通过定序器发送和接收数据时,存储并设置一系列指令,这些指令在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可以进入睡眠状态。
seq_open()
和 seq_close()
API支持SCU睡眠功能。
在实现 open()
或 close()
时,驱动程序开发人员应在调用这些API的同时控制传感器设备的电源模式。
结果,该系统可以实现更多的节能。
SCU_INST_SEND(SENSOR_DATA_REGISTER),
SCU_INST_RECV(SENSOR_BYTESPERSAMPLE) | SCU_INST_LAST,
由于SCU没有设备文件,因此传感器驱动程序必须进行中介,才能在SCU上执行 ioctl()
。
为此,请在每个驱动程序的 ioctl()
中调用 seq_ioctl()
。 使用 _SCUIOCVALID()
宏来确定它是否为SCU IO命令。
if (_SCUIOCVALID(cmd))
/* Redirect SCU commands */
ret = seq_ioctl(priv->seq, priv->id, cmd, arg);
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。
使用示例1
在内存布局定义文件中,示例定义并使用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)
/* 出错处理 */;
使用示例2
使用静态变量定义的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)
/* 出错处理 */;
void* lib_data_va = MemMgrLite::translatePoolAddrToVa(MEMMGR_DATA_AREA_ADDR);
if (MemMgrLite::Manager::initPerCpu(lib_data_va)) != ERR_OK)
/* 出错处理 */;
使用示例2
示例使用由静态变量定义的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本地内存,所以多核支持时无法指定。
使用示例1
通过内存布局定义文件,定义并使用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)
/* 出错处理 */;
使用示例2
通过静态变量定义并使用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)
/* 出错处理 */;
废弃由Manager::createStaticPools()创建的静态内存池。
静态内存未被创建时,不发生任何变化。
Manager::initPerCpu()未被执行时,发出"debug_assert"。
废弃内存池时,进行段释放泄露检测。
发现释放泄露时,发出"assert"。
启用围栏功能时,检测静态内存池的围栏。
发现围栏被破坏时,发出"assert"。
/* 废弃静态内存池 */
MemMgrLite::Manager::destroyStaticPools();
/* 取得当下的内存布局编号 */
MemMgrLite::NumLayout layout_no = MemMgrLite::Manager::getCurrentLayoutNo();
if (layout_no != MemMgrLite::BadLayoutNo)
printf("Current memory layout number: %d\n", layout_no);
printf("Memory layout number not set\n");
参数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
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())
/* 出错处理 */;
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"。
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”的配置
指定是否使用 ”Memory Manager” 各种功能。可以指定的功能和指定方法如下。
MemoryDevices.init(
# name ram addr size
["AUD_SRAM", True, 0x000c0000, 0x00040000],
None # end of definition
每个参数的说明如下。
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
每个参数的说明如下。
定义内存池的布局。
每个池区域的起始地址由每个固定区域的累积大小,对齐和围墙确定。
在头文件中,输出池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宏。
PoolLayout定义的描述示例
# 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个内存池。
为音频定义PoolLayout和为Sensor定义PoolLayout的示例
# 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
每个参数的说明如下。
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的目的地。
接收器还等待传输事件并执行事件驱动的操作。
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是根据应用程序创建的,并且在显式指定用于发送和接收的数据路径时使用。
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之间。
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.
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。
这是为了防止自旋锁在非任务上下文中等待。
等待指定时间的消息包接收。
与MsgLib::send()不同,不支持通过多个任务将recv()同时发送到同一队列。
在os_wrap.h中,TIME_POLLING被定义为0,而TIME_FOREVER被定义为-1。
如果先前收到的消息包尚未被MsgQueBlock::pop()丢弃,则执行ASSERT。
如果消息队列由另一个CPU拥有,请执行ASSERT。
从消息队列中删除消息包。
如果消息包中存在参数,请使用MsgPacket::moveParam()或MsgPacket::popParam()预先丢弃它们。
不支持由多个任务同时pop()同一队列。
如果没有消息包要丢弃,则执行ASSERT。
如果要丢弃的消息包的参数长度不为0,则执行ASSERT。
如果消息队列由另一个CPU拥有,请执行ASSERT。
T param = MsgPacket::peekParam<T>(); /* 复制左侧未引用的参数 */
MsgPacket::popParam<T>(); /* 舍弃参数 */
return param; /* 返回参数的副本 */
通过调用此API,消息参数长度更改为0。
请注意,消息参数的引用和指针无效。
popParam()调用参数析构函数。
popParamNoDestruct()不会调用析构函数,因此它只能用于没有析构函数作为参数的消息包。
除非有特殊原因,否则请使用popParam()。
popParam()
sizeof(T)!= MsgPacket :: getParamSize()
如果是ASSERT。
通过调用这些API,消息参数长度将更改为零。
请注意,消息参数的引用和指针无效。
布局信息用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
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
每个参数的说明如下。
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复制。
boot cause
和 boot mask
具有通用的位标志结构,并且每个引导原因都由一位表示。当 boot mask
的相应位设置为1时,将其作为引导因子使能;而当设置为0时,将其禁用。
当 boot mask
允许的激活因子事件发生在Sleep状态时,从Sleep唤醒。那时,哪个引导因素反映在 boot cause
中。
激活因子和激活掩码的定义如下所示。
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
通过发出up_pm_sleep(),仅CXD5602芯片就转换为睡眠模式。
当转换到Sleep状态时,可能会添加不仅取决于芯片而且取决于板子的处理。
通过在BSP的板相关部分中实现board_power_off()函数,可以转换到包括板控制在内的休眠模式。
在BSP相关部分中实现的睡眠过渡API如下所示。
也可以从NuttShell上的poweroff命令控制它。
Function Prototype
RCOSC模式,LV模式和HV模式可增加工作时钟并提高性能,但是功耗会相应增加。Frequency Lock机制在切换到LV模式时使用LV锁定,在切换到HV模式时使用HV锁定。释放获得的锁定后,将恢复原始时钟状态。
例如,如果同时获取了LV锁定和HV锁定,则会设置HV模式。 释放HV锁定后,它将切换到下一个更高的时钟模式,即LV模式。
释放HV锁定后,它将切换到LV模式,这是下一个更高的时钟模式。 因此,释放LV锁定后,它将返回RCOSC模式。
设置CONFIG_CXD56_HOT_SLEEP=y时,当OS空闲时它将自动转换为Hot Sleep状态。
设置CONFIG_CXD56_GNSS_HOT_SLEEP=y时,可以启用GNSS CPU的Hot Sleep功能。
它会在空闲状态下自动转换为Hot Sleep状态,但是可以通过wakelock机制防止其转换为Sleep状态。
“Sensor Manager”基于Publish-Subscribe Architecture控制多个“Sensor Client”,并分发从此处发布的Sensor Data。
“Sensor Client”是一个“Logical Sensor”,它控制着控制各种“Sensor Device”的驱动程序,“Logical Sensro”,通过融合每个“Sensor Device”中的数据来实现高性能传感器的应用程序 包含用于接收数据的“Sensor Application”。
5.11.2.1. 常见
在“Sensor Manager”中注册的“Sensor Client”将获取的数据启动到“Sensor Manager”,以便“Sensor Manager”将数据正确分发给“Sensor Client”并请求订阅。
它还提供了一个框架,可以关闭未订阅的“传感器客户端”的电源模式。
5.11.3.1. 常见
“Logical Sensor”是用于基于从各种物理传感器获得的传感器数据的某种信号处理算法来创建功能强大的传感器数据的“Sensor Client”,它由软件模块组成。
实际的算法实现分配可以通过以下方式完成。
5.11.3.1.2. DSP在asmp上的逻辑传感器
当用户创建自己的“Logical Sensor”算法并需要将处理任务转移到DSP端(例如繁重的处理)时,请使用ASMP框架在DSP端有可能实现它。
在这种情况下,可以通过将“Logical Sensor”实现为管理任务并将处理请求从管理任务发送到DSP上的工作任务来实现多核处理中的“Logical Sensor”。
表格 30. DSP中逻辑传感器示例
每个解决方案供应商都提供不同的logical sensor算法。在这种情况下,每个供应商都可以根据其合同等提供功能,而无需透露代码。
在这种情况下,可以通过在ASMP框架上加载构建文件和加密的二进制文件来实现。每个供应商都提供加密的DSP。
表格 31. DSP逻辑传感器加密示例
激活传感器管理
要启用“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(®);
要创建“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);
要更改此初始值,您需要调用 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);
“Step Counter”是 StepCounterWrite ,由“ Accelerometer” Subscribe调用,将数据发送到DSP上的Worker并执行传感处理。
此时的顺序如下所示。
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;
Flush
SensorDspCmd dsp_cmd;
dsp_cmd.header.sensor_type = StepCounter;
dsp_cmd.header.event_type = FlushEvent;
必须定义使用“Step Counter”时所需的MemoryLayout(pool)。
定义在MemoaryLayout定义文件中进行,并且可以使用该工具生成要包含在代码中的头文件。
在“Step Counter”示例应用程序中,执行以下操作:
cd examples/step_counter/config
python3 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],
每个参数的说明如下。
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],
每个参数的说明如下。
如果设置更改,请使用该工具生成一个新的头文件。
NOTE: 有关每个定义的详细信息,请参考 examples/step_counter/config/mem_layout.conf 。
如果设置更改,请使用该工具生成一个新的头文件。
有必要定义“MessageQueue”,这在使用“Step Counter”时是必需的。
该定义在MessageQueueLayout定义文件中完成,并且可以使用该工具生成要包含在代码中的头文件。
在“Step Counter”示例应用程序中,执行以下操作:
cd examples/step_counter/config
python3 msgq_layout.conf
原始libjpeg支持的所有格式均为24位/像素或更高,但Spresense支持16位/像素Cb/Y/Cr/Y(YUV4:2:2)格式,因此可以用更少的内存进行解码。
以下定义是Spresense中的有效色彩空间定义。
通过将此参数设置为libjpeg实例的成员out_color_space并执行jpeg_start_decompress(),
允许在任何支持的色彩空间中进行解码。
表格 36. 输出格式(色彩空间)
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总数)
这里的文件描述符是一个条件,该条件是可以使用read()函数读取JPEG数据的文件描述符。
例如,除了open()常规文件的文件描述符外,还可以支持使用socket()创建的套接字描述符。
(当然,在使用套接字描述符的情况下,必须按原样从通信伙伴发送JPEG数据。)
默认情况下,当原始libjpeg和Spresense中均发生错误时,libjpeg API执行任务将由exit()终止。
原始的libjpeg提供了一个示例,该示例使用“setjmp/longjmp”作为不结束任务的方法,
但是由于Spresense(NuttX)不支持setjmp/longjmp,因此无法使用此方法。
将来,我们计划使用setjmp/longjmp以外的方法来支持错误处理。