Developer World Spresense
English 日本語
目录

1. 用Spresense 写个Arduino程序吧!

安装了Spresense的Spresense Arduino Library后,在Arduino IDE 的菜单 File → Examples → Spresense 下就可以看到很多的样例程序。关于Spresense Arduino Library的安装请参考 Spresense Arduino Library 的使用方法 的用法 。

多数Arduino程序可以在Spresense中运行,但Arduino Uno与Spresense在硬件上有几处差异,不是所有Sketch程序不做改动就可以直接运行的。详细内容请参考:Spresense 与 Arduino Uno 的区别

1.1. 使用Spresense Arduino Library编写Sketch程序时的注意事项

Spresense 支持兼容Arduino的API,但使用时带有限制或存在差异,有的还有追加功能。本章说明这些相关的注意事项。对于编程语言规范、请参考Arduino Language Reference

1.1.1. LED

Spresense 主板上装载有4个内置LED。APP可以自由使用。LED定义为 LED0 到 LED3。这些LED可以通过 pinMode() (仅限OUTPUT)、digitalWrite() 进行操作,通过 ledOn() 或 ledOff() 这样的专用API也同样可以操作。LED_BUILTIN 在Spresense Arduino Library中对应分配为LED0。

1.1.2. EEPROM

Spresense 虽然没有内置EEPROM,但可以通过Flash内存模拟EEPROM。处理永久性数据时可以使用EEPROM程序库或是Spresense扩展板的SD卡。

1.1.3. Serial

Spresense 上配备有2个串行端口(UART)、支持Serial 。主板上的USB端口可以通过将其定义为 Serial 用于调试串口来使用。另一方面,D00和D01引脚可以用作 Serial2

1.1.4. pinMode()

关于pinMode(pin, mode) 的模式, INPUT , OUTPUT, INPUT_PULLUP, INPUT_PULLDOWN 都是支持的。关于I/O电压,主板侧的数字端子为1.8V,但扩展板的数字端子设计为3.3V或5V输出(通过跳线JP1选择)。由于这种电平转换是通过电平转换器和上拉电阻完成的,因此扩展板的数字端子有一些限制。

  • 启动时,扩展板的所有数字端子都被拉至 HIGH 。即使使用pinMode()指定 INPUT_PULLDOWN ,因为上拉的电平更高,所以输入电平也为 HIGH

  • Pin接口为INPUT时,初始值为 HIGH 。注意:这时会对需要下拉电阻的回路产生影响。

  • Pin输出是通过 digitalWrite() 设置的,初始为 HIGH 或是 LOW

有关电平转换器和上拉电阻的详细信息、请参考Spresense扩展板电路图

1.1.5. analogRead()

Spresense 配有从A0到A5的 6个模拟输入专用Pin接口。这些Pin接口不可以用做数字Pin接口。 analogRead() 中指定的Pin接口定义为如 A0A1A2A3A4A5 ,而不仅仅是使用Pin接口编号的数字。

1.1.6. analogReference()

Spresense 扩展板的模拟输入参考电压固定为5V,主板的模拟输入参考电压固定为0.7V。 analogReference() 是不支持的。

1.1.7. analogWrite()

analogWrite(pin, value) 输出一个周期约为490Hz的PWM信号。当指定的 pin 号为3,5,6,9时,PWM信号由硬件输出。如果指定了其他 pin ,则通过软件控制,使用定时器交替输出High/Low信号来生成PWM信号。

  • pin 3, 5, 6, 9 : 硬件控制PWM

  • 其他pin : 软件控制PWM

当使用硬件控制PWM时,引脚3和引脚9将同时切换到PWM模式。当使用其中一个引脚为PWM模式时,那另一个引脚将不能够用作GPIO。引脚5和引脚6的组合也有类似的限制。

1.1.8. tone()

tone(pin, frequency [, duration]) 是使用定时器通过软件控制来生成音调信号。此时,由于定时器的分辨率为1微秒,当频率的半周期不能整除1微秒时会发生频率误差。 可设置的最大频率约为166kHz。 tone() 函数与 attachTimerInterrupt() 函数使用的是相同的计时器资源,所以不能与 attachTimerInterrupt() 同时使用。

1.1.9. F()

不支持宏 F() 。虽然可以使用 F() 宏进行编译,但是它只是像普通字符串数据一样放在RAM上。(F()示例: Serial.println(F("This string will be stored in flash memory")); )

1.1.10. PROGMEM

PROGMEM 是不支持的。 PROGMEM被定义为空宏,即使被使用,也不会出现编译错误。

1.1.11. attachInterrupt()

Pin接口的中断编号由attachInterrupt()函数动态分配。 digitalPinToInterrupt() 定义为空的宏函数,使用方式如下,可任选其一。

API

attachInterrupt(digitalPinToInterrupt(pin), void (*isr)(void), mode);
attachInterrupt(pin, void (*isr)(void), mode);

Parameter

pin: D05等Pin接口编号

isr: 发生中断时调用的函数。本函数既无参数也没有返回值。

本函数由中断处理程序调用,所以可调用的API有限制。 例如,本函数内不可以使用Analog I/O函数。

mode:中断触发模式

  • LOW: Pin接口状态为LOW时

  • CHANGE: Pin接口状态发生变化时

  • RISING: Pin接口状态从LOW变为HIGH时

  • FALLING: Pin接口状态从HIGH变为LOW时

  • HIGH: Pin接口状态为HIGH时

针对Pin接口编号变化时所产生的抖振(chattering),应用了RTC(32.768kHz)3个循环期的噪音过滤装置。 因此,当发生突然的脉冲信号变化时可能会有无法获取中断的情况。

1.1.12. attachTimerInterrupt()

支持定时器(timer)中断功能。 本功能与tone()使用同一计时资源,所以不能和tone()同时使用。

attachTimerInterrupt()在系统中只能有一个在使用。如果在已使用时再次调用attachTimerInterrupt(),则会发生错误。再次设置不同的处理程序前,请先调用detachTimerInterrupt()以释放资源,然后再次调用attachTimerInterrupt()。

API

void attachTimerInterrupt(unsigned int (*isr)(void), unsigned int us);

Parameter

isr: 发生定时器(timer)中断时调用的函数。本函数返回下一计时周期作为返回值。

  • 当本函数返回0值时,计时停止。可作为单次触发定时器(one-shot timer)。

  • 作为循环定时器时,需要返回 attachTimerInterrupt() 中所指定的与us时間相等的值。

  • 其他,通过return返回任意值,则可动态更改到下次计时中断为止的时间。

本函数由中断处理程序调用,所以可调用的API有限制。 例如,本函数内不可以使用Analog I/O函数。

us: 微秒

1.1.13. micros()

micros()在RTC (32.768kHz)周期内计时,所以可精确到约30微秒。

1.2. 内存使用状况报告

Spresense 的特点是,已编译的Sketch程序全部保存在闪存内。 执行这些程序时,包括代码和只读数据在内的所有内容会由闪存展开到RAM,并在RAM上执行。

默认情况下,可用的最大RAM为768千字节(=786432字节),这是1.5MB应用程序SRAM大小的一半。 当Sketch程序大小超出此RAM容量时,编译将出错。

Arduino IDE环境下编译完成Sketch程序后会输出如下的内存使用状况报告。根 据此报告可以确认Sketch程序使用的内存大小。

最大786432字节的闪存中,程序使用xxxxxx字节(xx%)。
最大786432字节的RAM中,全局变量使用xxxxxx字节(xx%),局部变量可用xxxxxx字节。

本报告中使用的用语与原本的意思不同。阅读时请按如下理解。

  • 闪存: 供Sketch使用的RAM容量大小

  • 全局变量 :表示Sketch程序静态使用的RAM大小

  • 局部变量:从RAM容量中减掉Sketch程序静态使用的RAM容量后剩余的RAM容量

Sketch程序所使用的内存,除了静态使用的内存外,还有动态运行时的堆(HEAP)区内存。 arduino IDE编译日志显示的 '余留XXXX字节局部变量' 是Flash可用大小与实际可运行sketch文件大小的差值。 通常sketch运行时还需要利用动态的内存如HEAP,因此编写Sketch程序时要适当确保空余RAM容量。 一般情况下当Sketch程序使用容量超出可用FLASH容量75%时,会出现Sketch程序可用内存减少,运行可能不稳定的警告提示,此时请检查Sketch程序,适当减少代码量。

2. Spresense Arduino 程序库

2.1. Audio 程序库

Spresense Audio 程序库中可使用SD卡进行包括高解析音源在内的音频数据的播放与录制。通过简单的Sketch程序就可以体验高解析音频的世界。

Spresense 上搭载了ADC和DAC (包括全数字放大器功能)、可通过Audio 程序库控制这些功能。Spresense音频信号处理使用与应用程序处理器不同的其他内核(以下这些内核均简称为DSP,可以非常高效且简单地制作音频应用程序。

  • 高解析音源目前仅支持192kHz的WAV(LPCM)格式。

  • 根据应用程序所需的音频功能,需要事先安装相应的 DSP Codec二进制文件,并指定 DSP 安装位置。详细内容请参考后续说明。

Audio 程序库的主要特征

  • 录音器

  • 音频播放器

  • 音量控制

  • 平衡(L/R 增益)

  • 蜂鸣音产生

    • 注意:蜂鸣(beep)音与Arduino的tone()函数不同,会由耳机输出。

这个Audio 程序库可在input channel、output channel两者中选择。

音频输入通道
  • 模拟Mic

  • 数字Mic

  • I2S

音频输出通道
  • 模拟耳机

  • I2S

DSP codec支持的格式如下:

  • MP3

MP3 文件中存在ID3v2 TAG(尤其是图片数据这样大的元数据)时, 解码时会发生解析错误。 MP3Tag 请使用MP3Tag等工具删除标签信息。
  • WAV (PCM)

录制・播放时需要指定采样频率(Sampling rate)、比特率(Bit rate)。例如,MP3 codec 支持32000、44100、48000 Hz。

详细内容请参考:Audio 子系统

2.1.1. Audio 运行环境

运行Audio程序库前需要事先准备的物品如下所示:

  • Spresense 扩展板

  • 录制用Mic

  • 播放用耳机

  • MicroSD卡 

Spresense 扩展板带有Mic输入专用Pin接口、SD卡槽、耳机插孔。耳机插孔为3极3.5mm立体声插孔。

Pin接口配置与音频相关接口等详细内容请参考: Mic的使用方法扬声器的使用方法

2.1.2. Audio相关Sketch程序样例

使用了Audio程序库的Sketch样例程序可在Arduino IDE中的 文件 → 程序样例 → Spresense用Sketch程序样例 Audio → application下找到。运行Audio相关的样例Sketch程序时,需要事先准备好DSP codec二进制文件。

2.1.2.1. 录制样例 (recorder)

本样例中使用SD卡和Mic。

参考 Mic的使用方法 连接扩展板与Mic。

如果想要修改Mic相关配置的时候,请参照 MIC频道选择图设置 部分的内容。 SDK的编译(build)以及向ArduinoIDE的导入请参考:How to prepare Arduino environment

请参考DSP Codec二进制文件的安装,分别安装DSP二进制文件文件。MP3编码对应为MP3ENC,WAV、PCM编码对应为SRC。

在Arduino IDE中选择文件 →Sketch程序样例 → Spresense用Sketch程序样例 Audio → application → recorder,启动执行Sketch样例程序。

样例程序启动后开始录制,一段时间后停止录制。录制后数据将以"Sound.mp3"文件保存到SD卡中。

录音格式可使用initRecorder参数进行切换。

录制WAV文件时,需要在执行前调用 writeWavHeader 来创建WAV头文件。
同时也请参考 recorder_wav

需要以高音质(192kHz)方式录制时,可在执行setRecorderMode前, 在_AS_CLKMODE_HIRES_中调用setRenderingClockMode来实现。

在设定Mic的Gain的时候,请设定setRecorderMode的"input_gain"。
数字Mic的设定范围为0db~-78.5db,
模拟Mic的设定范围为21db~-78.5db。

串口监控上出现错误提示时,请参考 关于音频子系统错误

确认录制完毕的数据时请将SD卡取下并在PC上进行。 另,使用后面章节中的播放样例代码也可播放该数据。

即使取到了语音数据,但由于SD卡的写入性能和所用应用程序等因素,也可能出现无法写入所有数据的情况。
当前的扩展板最大设置为8ch/48kHz或2ch/192kHz。

同时,这也取决于SD卡的速度等级。请尽量使用等级高的SD卡。

向SD卡中写入Audio数据时需要的buffer大小默认是160K字节。这个大小能够支持高解析度的WAV文件写入,在48KHz的MP3文件写入的时候,这段大小的buffer是不必要的。
因此,可以通过setRecorderMode 来更改所使用的buffer大小。作为基准,写入高解析度WAV文件的时候推荐160K字节大小的buffer,写入48KHz的MP3文件的时候,推荐8K字节大小的buffer。

关于buffer的大小,上面仅仅是个推荐基准。需要根据SD卡的性能,应用程序的处理量等来进行调整。 Buffer太小的时候会出现SD卡的写入错误。当发现写入时overflow的时候,请将buffer大小调大。

当使用的是数字Mic的时候,请将setRecorderMode的"is_digital"设置为"true"。

2.1.2.2. 音乐播放样例 (player)

本样例中使用SD卡与耳机。

请连接扩展板与耳机。

请将要播放的MP3/WAV文件或是通过recorder样例Sketch程序录制完毕的 MP3/WAV文件以"Sound.mp3"或是"Sound.wav"的名称保存到SD卡的根目录下。

  • MP3 文件的采样速率支持 32000、44100 或是48000 bps 。

  • MP3 文件中存在ID3v2 TAG(尤其是图片数据这样大的元数据)时,解码时会发生解析错误。 请使用MP3Tag 等工具删除标签信息。

请参考DSP Codec二进制文件的安装,分别安装DSP二进制文件文件。MP3解码对应为MP3DEC;WAV解码对应为WAVDEC。

在Arduino IDE上选择 文件 →Sketch程序样例 → Spresense用Sketch程序样例 Audio → application →player ,启动并执行Sketch样例程序。

Sketch程序上传并执行后,SD卡中的MP3/WAV文件将从耳机播放出来。

播放高品质(192kHz)音源时,可在执行setPlayerMode前在_AS_CLKMODE_HIRES_中调用setRenderingClockMode来实现。

在高品质、普通品质间切换时,需要调用setReadyMode切换为Ready状态。

需要将输出设备切换为I2S时,请将setPlayerMode中的输出设备更改为 AS_SETPLAYER_OUTPUTDEVICE_I2SOUTPUT
I2S的操作模式只支持Slave模式、I2S格式模式、48000Hz、Stereo。

从SD卡上读取文件时所需要的buffer大小默认是160K字节。这个大小的buffer能够支持高解析度的WAV文件读取。在48KHz的MP3文件读取时,这个大小的buffer是不必要的。
由此,可以通过setPlayerMode来变更buffer的大小。作为推荐基准,在播放192KHz的WAV文件的时候,设置buffer为160K字节大小;在播放128kbps左右的MP3文件的时候,设置buffer为24K字节大小。

关于这个buffer大小,上述仅仅只是一个推荐基准。还请根据SD卡的性能,应用程序的处理量等来合适地调整。
如果buffer设定得太小,可能会发生从SD卡的读取错误。如果发生数据读取underflow的时候,请增大buffer的大小。

当Audio的出力是从SPRESENSE扩展板audio connector的line-out的时候,请将setPlayerMode的"sp_drv"设置为"AS_SP_DRV_MODE_LINEOUT"。
当缺省使用Speaker的pin,经CXD5247的amplifier输出audio的时候,请将"sp_drv"设定为"AS_SP_DRV_MODE_4DRIVER"。 缺省值是"AS_SP_DRV_MODE_LINEOUT"。

串口监控上出现错误提示时,请参考 关于音频子系统错误
2.1.2.3. 蜂鸣音样例 (beep)

本样例以蜂鸣音为例。 使用耳机。

在Arduino IDE 上选择 文件 →Sketch程序样例 → Spresense用Sketch程序样例 Audio → application →beep,启动并执行Sketch样例程序。 发出Beep音。

播放Beep音需要调用setPlayerMode更改为音乐播放模式。

2.1.2.4. Playlist音乐播放的示例(play_playlist)

这个示例是使用playlist进行音乐播放的示例程序。

为了使用playlist功能,需要在PC等环境下事先做成playlist,并拷贝到SD卡中。 在本示例程序中所使用的SD卡中的playlist文件夹结构如下。

SD卡根目录
|-- BIN/
|   |-- MP3DEC
|   `-- WAVDEC
|-- AUDIO/
|   |-- Sound1.mp3
|   |-- Sound2.mp3
|   |--   :
|   |-- Sound1.wav
|   |-- Sound2.wav
|   |--   :
`-- PLAYLIST/
    `-- TRACK_DB.CSV
  • 按照后续的安装方法,将MP3DEC和WAVDEC等DSP二进制文件拷贝到 BIN/ 中。

  • 将playlist中的音乐文件拷贝到 AUDIO/ 中。

  • 将playlist的列表文件( TRACK_DB.CSV )拷贝到 PLAYLIST/ 中。

playlist中的列表文件( TRACK_DB.CSV )格式信息如下。 各个文件的信息是用CSV (comma-separated values) 形式以行为单位记录的文本文件。

[filename],[artist],[album],[channel number],[bit length],[sampling rate],[file format]
[filename],[artist],[album],[channel number],[bit length],[sampling rate],[file format]
[filename],[artist],[album],[channel number],[bit length],[sampling rate],[file format]
 :

请记录各音乐的文件名、艺术家名、专辑名、编码信息(声道数、比特长度、采样率Hz)信息。 TRACK_DB.CSV 中的示例信息如下。

Sound1.mp3,Artist1,Album1,2,16,44100,mp3
Sound2.mp3,Artist1,Album1,2,16,44100,mp3
Sound1.wav,Artist2,Album2,2,16,48000,wav
Sound2.wav,Artist2,Album2,2,24,192000,wav

另外,为了更加方便地做成playlist文件,可以使用脚本文件mkplaylist.py。 请右键点击这里进行脚本文件下载。 在这个脚本中因为使用到了ffmpeg-python,请事先安装好ffmpeg和ffmpeg-python。

mkplaylist.py的使用方法如下。

python mkplaylist.py
usage: python mkplaylist.py dirname [dirname2] [dirname3] ...
usage: python mkplaylist.py -f filename

Generate an audio playlist file named as TRACK_DB.CSV

请在参数中指定包含音乐文件的文件夹路径。 正常运行结束后,TRACK_DB.CSV文件会自动生成到当前的文件夹下。 如果已经存在TRACK_DB.CSV文件的话,会在这个文件的最后进行追加。

python mkplaylist.py MyMusic
  • 音乐文件的后缀名请使用 .mp3 或者是 .wav。

  • 文件名请使用ASCII编码。

  • 因为playlist是使用CSV形式,请不要在filename、artist、album的名字中包含 ","。

请在SD卡插入的状态下从Arduino IDE菜单 File → Examples → Examples for Spresense Audio → application → play_playlist 中选择并执行示例程序。

执行后会在串口输出中显示如下菜单。根据输入的key可以进行各种不同的操作。

=== MENU (input key ?) ==============
p: play  s: stop  +/-: volume up/down
l: list  n: next  b: back
r: repeat on/off  R: random on/off
a: auto play      m,h,?: menu
=====================================

另外,所设定的volume、random / repeat /auto play等信息会通过EEPROM library永久保存起来。 下次执行时,示例程序会根据前次的设定情况开始动作。

当在设计自有的音乐播放器时请予以参考。

2.1.2.5. Audio Through示例 (through)

在这个示例程序中,把从I2S输入的数字音声通过模拟输出口进行输出。因为从Mic的输入到I2S的输出使用的是Audio的HW path,所以非常省电且低延迟。
在这个示例中,没有使用SW的Audio信号处理,所以不需要DSP,即SD卡。
在使用Mic的时候,请阅读 Mic的使用方法 把Mic连接到扩展板。

如果想要修改Mic相关配置的时候,请参照 MIC频道选择图设置 部分的内容。 SDK的编译(build)以及向ArduinoIDE的导入请参考:How to prepare Arduino environment

例如,想将I2S的输入进行模拟输出的时候, 请将setThroughMode
"input"设定为"I2sIn",
"i2s_out"设定为"None",
"sp_out"设定为"true"。

想将Mic的输入通过I2S输出的时候, 请将setThroughMode
"input"设定为"MicIn",
"i2s_out"设定为"Mic",
"sp_out"设定为"false"。

想设定Mic的Gain的时候, 请设定setThroughMode的"input_gain"。
数字MiC范围: 0db~-78.5db
模拟Mic范围: 21db~-78.5db

当Audio的出力是从SPRESENSE扩展板audio connector的line-out的时候,请将setThroughMode的"sp_drv"设置为"AS_SP_DRV_MODE_LINEOUT"。
当缺省使用Speaker的pin,经CXD5247的amplifier输出audio的时候,请将"sp_drv"设定为"AS_SP_DRV_MODE_4DRIVER"。缺省值是"AS_SP_DRV_MODE_LINEOUT"。

2.1.2.6. 其他的复合示例程序

在一般的示例程序外,也提供了几个复合动作的示例程序。

  • dual_players
    同时进行2个音乐播放的示例程序。

  • pcm_capture
    Capture音声数据的示例程序。在信号分析的时候使用。

  • rec_play
    录音后对录音文件进行播放。切换Audio动作的示例程序。

  • Recorder_with_rendering
    边听边录音的示例程序。

  • Voice_effector
    对输入的音声一边进行信号处理一边进行输出的示例程序。

2.1.3. DSP Codec二进制文件的安装

执行音频的播放&录制功能时,需要按下面的步骤安装DSP二进制文件。

表格 1. DSP的种类
DSP文件名 DSP安装包 说明

MP3DEC

mp3_dec_installer

播放MP3格式的音频文件

WAVDEC

wav_dec_installer

播放WAV(PCM)格式的音频文件

MP3ENC

mp3_enc_installer

录制成MP3格式的音频文件

SRC

src_installer

录制成WAV(PCM)格式的音频文件

DSP二进制文件可选择安装到SD卡或是闪存中。安装方法可以通过PC简单地将文件复制到SD卡中,也可以使用DSP安装程序完成安装。

DSP二进制文件只要DSP文件没有发生更新,只安装执行一次即可,无需每次安装。
  • 将DSP文件复制到SD卡进行安装时

    1. 上表中的DSP文件从按以下步骤安装完毕的Spresense Arduino包中取出。根据您使用的PC平台,安装步骤如下。

      • Windows平台下

        • 复制以下文本,创建pickup_dsp.bat 文件。

          set SPRESENSE_DIR=%userprofile%\AppData\Local\Arduino15\packages\SPRESENSE\hardware\spresense
          set AUDIO_DSP_DIR=libraries\Audio\examples\dsp_installer
          
          for /f "usebackq tokens=*" %%i IN (`dir /B %SPRESENSE_DIR%`) DO @set BOARD_VERSION=%%i
          
          set DSP_OUT=dsp_ver%BOARD_VERSION%
          
          echo "DSP binary copy to %DSP_OUT%"
          
          mkdir %DSP_OUT%"
          
          copy %SPRESENSE_DIR%\%BOARD_VERSION%\%AUDIO_DSP_DIR%\mp3_dec_installer\MP3DEC %DSP_OUT%\
          copy %SPRESENSE_DIR%\%BOARD_VERSION%\%AUDIO_DSP_DIR%\mp3_enc_installer\MP3ENC %DSP_OUT%\
          copy %SPRESENSE_DIR%\%BOARD_VERSION%\%AUDIO_DSP_DIR%\src_installer\SRC %DSP_OUT%\
          copy %SPRESENSE_DIR%\%BOARD_VERSION%\%AUDIO_DSP_DIR%\wav_dec_installer\WAVDEC %DSP_OUT%\
        • 双击执行 pickup_dsp.bat 文件,DSP文件将被复制到 dsp_ver1.x.x 中。

      • Ubuntu/Macintosh平台下

        • 复制以下文本,创建 pickup_dsp.sh 文件。

          #! /bin/bash
          
          HOST=`uname`
          if [ "${HOST}" == "Linux" ]; then
                  ARDUINO_ROOT=~/.arduino15
          elif [ "${HOST}" == "Darwin" ]; then
                  ARDUINO_ROOT=~/Library/Arduino15
          else
                  echo "ERROR: Cannot detect your machine."
                  exit
          fi
          
          SPRESENSE_ROOT=${ARDUINO_ROOT}/packages/SPRESENSE/hardware/spresense
          AUDIO_DSP_PATH=libraries/Audio/examples/dsp_installer
          
          BOARD_VERSION=`ls ${SPRESENSE_ROOT}`
          
          DSP_OUT=dsp_ver${BOARD_VERSION}
          
          echo "DSP binary copy to `pwd`/${DSP_OUT}"
          
          mkdir -p ${DSP_OUT}
          cp -a ${SPRESENSE_ROOT}/${BOARD_VERSION}/${AUDIO_DSP_PATH}/mp3_dec_installer/MP3DEC ${DSP_OUT}/
          cp -a ${SPRESENSE_ROOT}/${BOARD_VERSION}/${AUDIO_DSP_PATH}/mp3_enc_installer/MP3ENC ${DSP_OUT}/
          cp -a ${SPRESENSE_ROOT}/${BOARD_VERSION}/${AUDIO_DSP_PATH}/src_installer/SRC ${DSP_OUT}/
          cp -a ${SPRESENSE_ROOT}/${BOARD_VERSION}/${AUDIO_DSP_PATH}/wav_dec_installer/WAVDEC ${DSP_OUT}/
        • 通过以下命令执行 pickup_dsp.sh ,DSP文件将被复制到 dsp_ver1.x.x 中。

          bash pickup_dsp.sh
          
    2. 在SD卡中创建BIN目录,将取到的文件放到该目录下。

      arduino dsp copy cn
      图表 1. DSP文件的复制

      音频播放&录制应用程序中,需要将DSP文件的安装位置以 initPlayer() 函数或是 initRecorder() 函数参数的方式指定。安装到SD卡中时,请指定为 /mnt/sd0/BIN 。Sketch样例程序中已指定为 /mnt/sd0/BIN

  • 使用DSP安装程序进行安装时

    DSP安装程序在Arduino IDE的文件 →Sketch程序样例 → Spresense用Sketch程序样例 Audio → dsp_installer路径下。不同的DSP二进制文件使用不同的安装程序,请根据上表选择并启动相应的DSP安装程序。

    arduino dsp installer cn
    图表 2. DSP安装程序的启动

    编译&写入(Upload)处理执行结束后,启动串口监控。

    1. 选择波特率115200 bps时,显示下图所示信息。

    2. 请输入要安装的位置编号。 安装到SD卡时,Spresense SD卡槽中插入已格式化的SD卡。

    3. 按下发送按钮。

      arduino dsp installer monitor1 cn
      图表 3. DSP安装位置的选择

      安装成功后显示下图所示信息。

      arduino dsp installer monitor2 cn
      图表 4. 执行DSP进行安装

    音频播放&录制应用程序中,需要将DSP文件的安装位置以 initPlayer() 函数或是 initRecorder() 函数参数的方式指定。安装到SD卡中时,请指定为 /mnt/sd0/BIN 。安装到闪存中时,请指定为 /mnt/spif/BIN 。Sketch样例程序中SD卡已指定为 /mnt/sd0/BIN

2.1.4. 详细内容

更多信息请参见以下链接。


2.2. Camera 库

本节介绍 Spresense 名称中的Arduino IDE中可用的Camera库。

2.2.1. 简介

以下是Camera库的示意图。

arduino camera overview
图表 5. Camera Overview

Spresense Camera提供两个库类。 一个是CameraClass类的实例“theCamera",另一个是CamImage类,负责处理从Camera获取的图像。

"theCamera"有三个主要功能。

  • 预览Camera的VideoStream

  • 控制Camera参数

  • 使用Camera拍摄高分辨率的照片,格式为JPEG

CamImage 类是用户操作从theCamera获取的图像图像的函数。 下图显示了CamImage类的概述。

arduino camera camimage
图表 6. CamImage Overview

CamImage 类有两个功能。

  • 获取有关使用Camera拍摄的图像的信息。

  • 转换检索到的图像。

以下是theCamera和CamImage的这五个功能。

2.2.2. 通过VideoStream获取Camera预览

通常,Camera取景器会显示摄像机上实时图像。 在这里,此实时图像(实时视频)称为预览图像。 "theCamera"具有获取此预览图像每帧的功能。

若要获取预览图像,首先使用begin()方法函数确定预览图像的图像格式。 begin()方法函数的定义如下所示:

begin(
    int buff_num=1,
    CAM_VIDEO_FPS fps = CAM_VIDEO_FPS_30,
    int video_width   = CAM_IMGSIZE_QVGA_H,
    int video_height  = CAM_IMGSIZE_QVGA_V,
    CAM_IMAGE_PIX_FMT fmt=CAM_IMAGE_PIX_FMT_YUV422)

提供给begin()的所有参数都确定预览图像的参数。 每个theCamera内具有预览图像缓冲区数和视频帧速率(每秒获取多少帧图像), 图像的大小和图像数据的像素格式。 默认情况下,每个内部图像缓冲区数为30FPS(每秒30个)、图像大小为QVGA(320x240)和YUV422像素格式。 如果内部图像缓冲区的数量是默认的,请使用默认值。

增加内部图像缓冲区数的一个示例是当要处理大量图像(如处理各种图像)时,可以将图像处理与Camera图像检索并行化。 在这种情况下,内部图像缓冲区数为2,允许并行从Camera获取图像,同时执行繁重的处理,从而改善帧速率。 但是,QVGA会消耗大约150KB的缓冲区内存。因此,如果需要设置更多内存,请小心。

目前,图像大小仅支持QVGA。 如果设置QVGA以外的值,则begin()将导致错误。 此外,预览图像的像素格式仅支持YUV422。

begin()方法函数是使用theCamera时必须首先调用的函数。 当begin()方法函数成功完成后,使用startStreaming()方法函数注册回调函数以获取预览图像。 回调函数的类型如下所示:

void camera_callback(CamImage img)

用户实现自己的类型函数,并使用startStreaming()方法函数进行注册。 如果启动Streaming()的第一个参数为"true",则每次检索图像时,将开始检索预览的视频图像,并调用已注册的回调函数。 获取图像的频率由begin()方法函数中指定的帧速率决定。 但是,除非用户实现的回调函数结束,否则不会调用下一帧的回调函数。 如果要停止获取预览图像,请将startStreaming()方法函数的第一个参数设置为false并调用startStreaming()方法函数。

2.2.3. 控制Camera参数的能力

在普通的Camera中,可以设置各种参数,如调整颜色、亮度和 Spresense Camera, 也可以进行一些设置。

表格 2. Camera Parameter Control method functions
Method name Description

setAutoWhiteBalance()

停止Camera自动白平衡

setAutoISOSensitive()

停止相机自动ISO感光度启动

setISOSensitivity()

停止Camera自动ISO感光度启动

setAutoWhiteBalanceMode()

设置白平衡自动模式

setColorEffect()

设置图像效果

在调用begin()方法函数后,可以在任意时间调用这些设置方法函数。

2.2.4. 在Camera中获取高分辨率JPEG图像的功能

预览图像的分辨率较低,但以视频帧速率的频率获取图像。 另一方面,如果将数据作为照片检索, Spresense Camera可以获取高分辨率JPEG压缩图像。

首先,对theCamera执行"设置胶片"的过程。 执行此操作的方法函数是setStillPictureImageFormat()。 使用此方法函数设置静止图像(照片)的图片大小和像素格式。

setStillPictureImageFormat(int width, int height, CAM_IMAGE_PIX_FMT fmt = CAM_IMAGE_PIX_FMT_JPEG)

第一个和第二个参数指定图像的垂直和水平大小。第三个参数指定图像的像素格式。

目前,图像的像素格式仅支持JPEG。

setStillPictureImageFormat()方法函数一次设置,除非更改参数,否则设置将永久生效。

设置完图像后,可以随时执行"关闭快门"过程以获取照片数据。 为此,方法函数是takePicture()。 takePicture()返回CamImage实例作为返回值。 如果照片拍摄中出现任何错误,则返回空的CamImage。 要确定CamImage实例是否为空,可以使用CamImage类的isAvailable()检查它。

2.2.5. 获取有关使用Camera拍摄的图像的信息

在startStreaming()中回调和takePicture()返回值的CamImage实例包含检索到的图像信息。 可以使用CamImage类的方法函数来获取有关图像的信息。

表格 3. Methods to get Informations from CamImage
Method name Description

isAvailable()

检查获取的CamImage实例是否可用

getWidth()

获取图像的宽度(以像素为单位)

getHeight()

获取图像的高度(以像素为单位)

getPixFormat()

获取图像的像素格式

getImgSize()

获取图像的像素格式

getImgBuff()

获取图像数据的缓冲区地址

2.2.6. 转换检索到的图像

CamImage实例提供单个图像转换方法函数。

表格 4. Methods to convert CamImage
Method name Description

convertPixFormat()

将图像从当前像素格式转换为其他像素格式

如果要将图像显示到显示器,则显示器的像素格式通常采用 RGB 格式。 特别是,如果有一个预览图像,在很多情况下,想显示获得的图像。 在这种情况下,可以使用此方法函数转换像素格式。

convertPixFormat()在数据内存中转换像素格式,因此当前CamImage实例将被覆盖。

目前存在以下限制:

  • convertPixformat()仅支持两个:YUV422到RGB、YUV422到GLAY

2.2.7. 示例代码说明

实际上,使用 Spresense Arduino 包中的Camera示例代码,将说明如何使用 Spresense Camera。

从Arduino IDE菜单栏打开示例代码。

"File" ⇒ "Sketch example" ⇒ "Camera in Spresense sketch example" ⇒ "camera"

/*
 *  camera.ino - One minute interval time-lapse Camera
 *  Copyright 2018 Sony Semiconductor Solutions Corporation
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *  This is a test app for the camera library.
 *  This library can only be used on the Spresense with the FCBGA chip package.
 */

#include <SDHCI.h>
#include <stdio.h>  /* for sprintf */

#include <Camera.h>

#define BAUDRATE                (115200)

SDClass  theSD;
int take_picture_count = 0;


/**
 * Callback from Camera library when video frame is captured.
 */

void CamCB(CamImage img)
{

  /* Check the img instance is available or not. */

  if (img.isAvailable())
    {

      /* If you want RGB565 data, convert image data format to RGB565 */

      img.convertPixFormat(CAM_IMAGE_PIX_FMT_RGB565);

      /* You can use image data directly by using getImgSize() and getImgBuff().
       * for displaying image to a display, etc. */

      Serial.print("Image data size = ");
      Serial.print(img.getImgSize(), DEC);
      Serial.print(" , ");

      Serial.print("buff addr = ");
      Serial.print((unsigned long)img.getImgBuff(), HEX);
      Serial.println("");
    }
  else
    {
      Serial.print("Failed to get video stream image\n");
    }
}

/**
 * @brief Initialize camera
 */
void setup()
{

  /* Open serial communications and wait for port to open */

  Serial.begin(BAUDRATE);
  while (!Serial)
    {
      ; /* wait for serial port to connect. Needed for native USB port only */
    }


  /* begin() without parameters means that
   * number of buffers = 1, 30FPS, QVGA, YUV 4:2:2 format */

  Serial.println("Prepare camera");
  theCamera.begin();


  /* Start video stream.
   * If received video stream data from camera device,
   *  camera library call CamCB.
   */

  Serial.println("Start streaming");
  theCamera.startStreaming(true, CamCB);

  /* Auto white balance configuration */

  Serial.println("Set Auto white balance parameter");
  theCamera.setAutoWhiteBalanceMode(CAM_WHITE_BALANCE_DAYLIGHT);

  /* Set parameters about still picture.
   * In the following case, QUADVGA and JPEG.
   */

  Serial.println("Start streaming");
  theCamera.setStillPictureImageFormat(
     CAM_IMGSIZE_QUADVGA_H,
     CAM_IMGSIZE_QUADVGA_V,
     CAM_IMAGE_PIX_FMT_JPG);
}

/**
 * @brief Take picture with format JPEG per second
 */

void loop()
{
  sleep(1); /* wait for one second to take still picture. */

  /* You can change the format of still picture at here also, if you want. */

  /* theCamera.setStillPictureImageFormat(
   *   CAM_IMGSIZE_HD_H,
   *   CAM_IMGSIZE_HD_V,
   *   CAM_IMAGE_PIX_FMT_JPG);
   */

  /* This sample code can take 100 pictures in every one second from starting. */

  if (take_picture_count < 100)
    {

      /* Take still picture.
      * Unlike video stream(startStreaming) , this API wait to receive image data
      *  from camera device.
      */

      Serial.println("call takePicture()");
      CamImage img = theCamera.takePicture();

      /* Check availability of the img instance. */
      /* If any error was occured, the img is not available. */

      if (img.isAvailable())
        {
          /* Create file name */

          char filename[16] = {0};
          sprintf(filename, "PICT%03d.JPG", take_picture_count);

          Serial.print("Save taken picture as ");
          Serial.print(filename);
          Serial.println("");

          /* Save to SD card as the finename */

          File myFile = theSD.open(filename, FILE_WRITE);
          myFile.write(img.getImgBuff(), img.getImgSize());
          myFile.close();
        }

      take_picture_count++;
    }
}

此示例将预览图像设置QVGA,帧速率为60FPS,获取60帧数据时拍摄单个JPEG 照片,并将其存储在SDCard上。

当建立完成之后,请继续完成这些步骤:“initial setting",”setup()“,“preview callback”和“loop()”。

因此,请仔细查看这四个。

2.2.7.1. initial setting
/*
 *  camera.ino - One minute interval time-lapse Camera
 *  Copyright 2018 Sony Semiconductor Solutions Corporation
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *  This is a test app for the camera library.
 *  This library can only be used on the Spresense with the FCBGA chip package.
 */

#include <SDHCI.h>
#include <stdio.h>  /* for sprintf */

#include <Camera.h>

#define BAUDRATE                (115200)

SDClass  theSD;
int take_picture_count = 0;

首先,如果使用Camera库,则必须包括头文件<Camera.h>。

#include <Camera.h>

通过包括此头文件,可以使用thCamera实例。

然后,定义回调函数和loop()函数共有的变量。

int take_picture_count = 0;

take_picture_count是一个变量,它每隔一秒计算一次takePicture()。该变量用来 控制写入SDCard的文件名和要创建的文件数限制。 (在本示例中,拍摄了100张照片,并结束了摄影。)

2.2.7.2. preview callback
/**
 * Callback from Camera library when video frame is captured.
 */

void CamCB(CamImage img)
{

  /* Check the img instance is available or not. */

  if (img.isAvailable())
    {

      /* If you want RGB565 data, convert image data format to RGB565 */

      img.convertPixFormat(CAM_IMAGE_PIX_FMT_RGB565);

      /* You can use image data directly by using getImgSize() and getImgBuff().
       * for displaying image to a display, etc. */

      Serial.print("Image data size = ");
      Serial.print(img.getImgSize(), DEC);
      Serial.print(" , ");

      Serial.print("buff addr = ");
      Serial.print((unsigned long)img.getImgBuff(), HEX);
      Serial.println("");
    }
  else
    {
      Serial.print("Failed to get video stream image\n");
    }
}

在startStreaming()中注册的摄像机预览是输出时调用的函数。 在此函数中,检查作为函数参数获取的 CamImage 实例是否可用。 然后,将像素格式转换为 RGB565。 转换后,显示getImgSize()和getImgBuff()检索的数据大小和内存地址。 通常,在此阶段将图像数据输出到连接的显示器等,构建相机的取景器视图。

2.2.7.3. setup()
/**
 * @brief Initialize camera
 */
void setup()
{

  /* Open serial communications and wait for port to open */

  Serial.begin(BAUDRATE);
  while (!Serial)
    {
      ; /* wait for serial port to connect. Needed for native USB port only */
    }


  /* begin() without parameters means that
   * number of buffers = 1, 30FPS, QVGA, YUV 4:2:2 format */

  Serial.println("Prepare camera");
  theCamera.begin();


  /* Start video stream.
   * If received video stream data from camera device,
   *  camera library call CamCB.
   */

  Serial.println("Start streaming");
  theCamera.startStreaming(true, CamCB);

  /* Auto white balance configuration */

  Serial.println("Set Auto white balance parameter");
  theCamera.setAutoWhiteBalanceMode(CAM_WHITE_BALANCE_DAYLIGHT);

  /* Set parameters about still picture.
   * In the following case, QUADVGA and JPEG.
   */

  Serial.println("Start streaming");
  theCamera.setStillPictureImageFormat(
     CAM_IMGSIZE_QUADVGA_H,
     CAM_IMGSIZE_QUADVGA_V,
     CAM_IMAGE_PIX_FMT_JPG);
}

setup()首先设置Serial以显示消息,然后调用Camera的begin()方法函数。 默认情况下,Camera的begin()具有一个帧缓冲区内存、30FPS的帧速率和QVGA图像大小。 然后,在startStreaming()中启用预览回调函数的设置和回调,并在setAutoWhiteBalanceMode()中在白天设置。 最后,setStillPictureImageFormat()为QUAD VGA大小的照片设置。

2.2.7.4. loop()
/**
 * @brief Take picture with format JPEG per second
 */

void loop()
{
  sleep(1); /* wait for one second to take still picture. */

  /* You can change the format of still picture at here also, if you want. */

  /* theCamera.setStillPictureImageFormat(
   *   CAM_IMGSIZE_HD_H,
   *   CAM_IMGSIZE_HD_V,
   *   CAM_IMAGE_PIX_FMT_JPG);
   */

  /* This sample code can take 100 pictures in every one second from starting. */

  if (take_picture_count < 100)
    {

      /* Take still picture.
      * Unlike video stream(startStreaming) , this API wait to receive image data
      *  from camera device.
      */

      Serial.println("call takePicture()");
      CamImage img = theCamera.takePicture();

      /* Check availability of the img instance. */
      /* If any error was occured, the img is not available. */

      if (img.isAvailable())
        {
          /* Create file name */

          char filename[16] = {0};
          sprintf(filename, "PICT%03d.JPG", take_picture_count);

          Serial.print("Save taken picture as ");
          Serial.print(filename);
          Serial.println("");

          /* Save to SD card as the finename */

          File myFile = theSD.open(filename, FILE_WRITE);
          myFile.write(img.getImgBuff(), img.getImgSize());
          myFile.close();
        }

      take_picture_count++;
    }
}

在loop()函数中,首先在sleep()函数中等待大约一秒钟。 一秒钟后,检查take_picture_count变量的值是否超过100,如果超过,则进入摄影过程。 在拍摄过程中,调用takePicture()方法函数,拍摄照片,并将捕获的照片分配给img变量。 使用isAvailable()函数验证img变量是否可用,并将检索到的图像保存到SD卡。 最后,通过take_picture_count递增(增加1)来结束loop()函数。

现在,使用示例的说明就到这里。

现在,可以使用 Spresense Camera创建原始相机设备。


2.3. DNNRT 程序库

DNN Runtime Library可以对使用Sony提供的Neural Network Libraries和Neural Network Console所学习得到的网络模型进行Deep Neural Network (DNN)推演处理。

dnnrt overview cn

DNNRT library 中包含核心的 DNNRT 部分以及数据输入输出所使用的 DNNVariable 部分 。

在这里,使用识别手写0~9数字的示例程序(number_recognition.ino)作为例子来进行说明。

2.3.1. 网络模型的准备

2.3.1.1. Neural Network Console

可以使用Neural Network Console (NNC)来设计和训练网络模型。既可以通过链接官方网站 直接使用Cloud版,也可以下载Windows版来使用。

2.3.1.2. 学习
  1. 在Cloud版本上,基于示例项目 image_recognition.MNIST.LeNet 来创建新项目。如果选择好了示例项目,NNC就会基于所选择的示例项目创建一个新的项目。此时会弹出一个输入项目名的对话框,可以输入自己所喜爱的项目名。(在此,以 myproject 为例 )

    通过选择新创建的 myproject 来进行编辑。

    edit
    图表 7. 编辑画面(Cloud版)

    在Windows版本上,通过选择 LeNet.sdcproj 来使用。

  2. 在此,直接使用示例中的网络模型来进行训练。

    在Cloud版本上,点击编辑画面右上角的 Run 按键。

    start
    图表 8. 学习开始(Cloud版)

    在Windows版本上,点击 Training 下的那个三角按键。

    start
    图表 9. 学习开始(Windows版)
  3. 如果在窗口下方的控制台中有 Training Completed 信息显示的话,学习就完成了。

    TRAINING
    TIP

    机器学习的过程会需要非常长的时间

  4. 评价学习完成后的学习结果。

    在Cloud版本上,点击学习画面右上角的 Run 按键。

    EVALUATE
    图表 10. 评价开始(Cloud版)

    在Windows版本上,点击 Evaluation 下的那个三角按键。

    EVALUATE
    图表 11. 评价开始(Windows版)
  5. 评价结束后会显示评价结果 x:image 列中显示的图像与 y:label 列中显示的数字应该是一致的。为了在DNNRT中使用,请下载这个学习完成的网络模型。

    在Cloud版本上,从评价画面右上角的列表中选择 NNB (NNabla C Runtime file format) ,点击下方的 Download Project

    DOWNLOAD

    在Windows版本上,从 ACTION 菜单的 Export 中选择 NNB (NNabla C Runtime file format)

    DOWNLOAD
  6. 将所下载的学习完成的网络模型 result.nnb (Cloud版本)或者是 model.nnb (Windows版本)以及想进行识别的图片一起拷贝到SD卡中。并请将这个网络模型的文件名更改为 network.nnb 。 在这个示例中所支持的图片格式仅仅为PGM (Portable Greyscale Map)。如果在不能制作PGM文件的情况下,请使用 示例PGM图片文件

到此为止,为运行 number_recognition.ino 所需要的准备均已完成。

2.3.2. 手写文字识别示例程序

通过 image_recognition.MNIST.LeNet 或者 LeNet.sdcproj 所学习后的网络模型,可以对一个28x28大小的输入图片输出10个数字的排列结果。这其中的10个排列数据是对图片识别后的数字结果的表示。在这个10个排列中,也会输出对每个数字的识别确信度。例如,排列中第一个(index 0)就是表示所输入图片中的数字是「0」的确信度。

各个数字确信度的表示
DNNVariable output = dnnrt.outputVariable(0);

Serial.println(output[0]);   // 输入图像是数字「0」的确信度
 ...
Serial.println(output[9]);   // 输入图像是数字「9」的确信度

number_recognition.ino 会将确信度最高的index作为识别数字,将其与确信度一起输出。

可以通过 number_recognition.ino 中下面的部分来修改所要输入的图片。

  File pgmfile("number4.pgm");

2.4. EEPROM 程序库

EEPROM可在断电情况下保存内容不消失的非挥发性数据。 Spresense EEPROM,但可通过SPI-Flash内存模拟EEPROM。 本程序库对SPI-Flash模拟的EEPROM可执行写入和读取操作。

EEPROM程序库的API规格请参考:Arduino EEPROM程序库

EEPROM的样例代码在Arduino IDE菜单文件 →Sketch程序样例 → Spresense用Sketch程序样例 EEPROM下。 EEPROM 程序库与Arduino EEPROM程序库兼容,可直接运行Arduino样例代码。

EEPROM容量大小由EEPROM.h中定义的 E2END 值决定,为4000字节。

Spresense 电路板中,可从应用程序访问的SPI-Flash最大容量为4MByte。 超过此限制时,可通过更改 E2END 增加EEPROM的容量。

2.5. eMMC 程序库

2.5.1. 简介

eMMC程序库是用于访问eMMC外接板(以后发布)设备的库。

eMMC程序库 Arduino SD程序库 与API配置类似,将对象名称从"SD"替换为"eMMC",您可以使用与SD库相同的接口访问eMMC设备。

这个库还支持USB MSC(Mass Storage Class)功能作为附加功能。 连接 Spresense 扩展板上的USB和PC,通过调用"beginUsbmsc()"函数,您可以直接从电脑在eMMC设备上访问文件。

2.5.2. 功能

有关各种API的详细信息,请参阅 eMMC Library API API参考手册。

Function Description

eMMC.begin()

eMMC 初始化设备。这个API可以调用eMMC设备。

eMMC.beginUsbMsc()

USB MSC (Mass Storage Class) 启动功能。

eMMC.endUsbMsc()

USB MSC (Mass Storage Class) 终止功能。

eMMC.format()

eMMC 格式化设备。默认FAT32文件系统的格式。

eMMC.open()

通过指定文件名在eMMC设备中打开文件。
FILE_READ: 打开文件只读(默认)
FILE_WRITE: 打开读写文件。文件位置指向末尾。
这个函数返回一个文件对象。有关文件操作的信息,请参阅 File程序库 文件库说明。

eMMC.exists()

eMMC 检查设备中是否有文件。

eMMC.mkdir()

eMMC 在设备中创建目录。

eMMC.rmdir()

eMMC 删除设备中的目录。

eMMC.remove()

eMMC 删除设备中的文件。

2.5.3. 示例

eMMC程序库提供4个简单示例。

Example Description

format.ino

eMMC 格式化设备。首次使用eMMC设备时,仅运行该设备一次。

read_write.ino

eMMC 读取和写入设备上的文件的示例。

UsbMsc.ino

这是USB MSC功能的示例。当eMMC设备作为驱动器安装在PC上,您可以直接从PC访问eMMC设备。

UsbMscAndFileOperation.ino

将USB MSC和文件操作结合在一起的复合应用程序。对于eMMC设备中的文件,必须专门操作示例中的文件操作和从PC中进行的文件操作。在这个示例中,您可以从应用程序获取和查看文件列表,然后启用USB MSC功能,以便从电脑处理eMMC设备中的文件。退出USB MSC功能后,将检索文件列表,并从程序中再次显示。


2.6. File 程序库

2.6.1. 简介

File程序库是一个常用库。 File对象由 SDHCIFlasheMMC 库中的open()函数生成的。 这个库提供了对打开的文件对象执行文件操作的功能。

2.6.2. 功能

有关各种API的详细信息,请参阅 File Library API API用户手册。

当文件实例名称为myFile时,程序会按下面代码打开:

#include <SDHCI.h>
SDClass SD;
File myFile;

  myFile = SD.open("test.txt");

为 File 对象提供了以下函数:

Function Description

myFile.write()

将数据从文件位置写入文件。

myFile.read()

从文件位置读取文件数据。

myFile.peek()

读取一个字节,而无需从文件中移动读取位置。

myFile.available()

检查是否有可以从文件中读取的字节。

myFile.flush()

保证写入文件的数据将被放入存储。

myFile.seek()

将文件中的文件位置从开始移动到指定位置。

myFile.position()

获取文件中的当前位置。

myFile.size()

获取文件的大小。

myFile.close()

关闭文件。

myFile.bool()

返回文件或目录是否存在。

myFile.name()

获取文件名。

myFile.isDirectory()

返回文件是否为目录。

myFile.openNextFile()

打开目录中的下一个文件。

myFile.rewindDirectory()

恢复到目录中的第一个文件。

2.6.3. 示例

File库提供以下示例:

Example Description

read_write.ino

读取和写入文件的示例。


2.7. Flash 程序库

2.7.1. 简介

Flash程序库是用于访问 Spresense 主板上的闪存设备的库。

Flash程序库 Arduino SD程序库 与API配置类似,通过将对象名称从"SD"替换为"Flash",您可以使用与SD库相同的接口访问Flash设备。

2.7.2. 功能

有关各种API的详细信息,请参阅 Flash Library API API参考手册。

Function Description

Flash.begin()

虚拟功能。始终返回True。

Flash.format()

Flash 格式化设备。

Flash.open()

打开Flash设备上的文件。
FILE_READ: 打开文件只读(默认)
FILE_WRITE: 打开和写入文件。文件位置指向末尾。
这个函数返回一个文件对象。有关文件操作的信息,请参阅 File程序库 操作手册。

Flash.exists()

Flash 检查设备中是否有文件。

Flash.mkdir()

Flash 在设备中创建目录。

Flash.rmdir()

Flash 删除设备中的目录。

Flash.remove()

Flash 删除设备中的文件。

2.7.3. 示例

Flash程序库提供了两个简单示例。

Example Description

format.ino

格式化Flash设备。Flash设备出厂时已格式化,但应重新格式化以删除Flash中的数据。格式化时,除引导加载程序或示例加载的程序外,所有用户区域都将被删除。闪存中存在的 EEPROM 数据也将被删除。
这个例子使用串行监视器。请按通信速率115200波特打开串行监视器。
通过从串行监视器输入"Y"来启动格式。
在格式化过程中,四个LEDS亮起,并且格式完成后指示灯闪烁。
格式大约需要40秒。格式化时,请注意不要关闭电源。

read_write.ino

在Flash设备上读取和写入文件的示例。


2.8. GNSS 程序库

GNSS程序库用于管理控制Spresense的GNSS定位功能并获取位置信息。在Arduino程序中可以很容易地使用。

使用时无需额外的硬件设置。天线也已装载到Spresense主板上。只要拿到视野开阔的户外就可以轻松获取位置信息。启动时搜索卫星需要花费1~2分钟的时间,设计应用程序时需要注意。

使用本程序库时无需Spresense扩展板。主板可独立运行。

GNSS程序库中提供以下2个Sketch样例程序。

  • gnss.ino是将GNSS中定位到的信息输出到USB串口时的简单样例。本样例中位置信息以GNSS程序库特有的格式输出。详细内容请参考:GNSS.h

  • gnss_tracker.ino 是一个稍许复杂些的样例。本样例位置信息将保存在SD卡中,所以需要用到Spresense扩展板。SD卡中的位置信息以常见的NMEA格式保存,可在各种应用程序中使用。

2.8.1. 详细内容

更多信息请参见: Spresense SDK GNSS documentation


2.9. LowPower 程序库

LowPower程序库为 Spresense 的省电功能提供支持。

LowPower程序库主要提供以下功能。

  • 进入Deep SleepCold Sleep 等各种睡眠状态

    Deep Sleep

    耗电最低的睡眠模式。只为CXD5247 PMIC (Power Management IC) 供电,CXD5602 断电。

    Cold Sleep

    只为CXD5602 内最小必须的电源域供电的睡眠模式。与”Deep Sleep“相比耗电增多,但通过 GPIO 电平变化触发,可以从”Cold Sleep“状态被唤醒。

关于Sleep状态的耗电情况,当扩展板上插有SD卡时,由于SD 卡的消耗,耗电约增加5mA。
  • 获取从睡眠状态被唤醒的启动原因

  • 用于设置许可/禁止启动原因的启动掩码

  • 动态切换到三种不同的时钟模式

    CLOCK_MODE_156MHz

    具有最高性能的时钟模式。此时钟模式在启动时选择。CPU 使用 PLL 在156MHz 下运行。

    CLOCK_MODE_32MHz

    将全系统时钟降至32MHz,从而降低功耗。如果选择此时钟模式,CXD5602核心电压工作在0.7V。CPU使用PLL以32MHz运行。

    CLOCK_MODE_8MHz

    全系统时钟下降到8MHz。这是功耗最小的时钟模式。核心电压在0.7V下工作,CPU在不使用锁相环的情况下使用内部振荡器工作。如果不使用PLL,则可以将时钟模式转换为此模式,并通过关闭TCXO电源来进一步降低功耗。

通常,降低时钟会降低功耗,但也会降低操作性能。 此外,并非所有硬件功能都在低时钟模式下工作。下面将对此进行更多介绍。

有关各种API的详细信息,请参阅 LowPower Library API API参考手册。

2.9.1. 时钟模式

clockMode() 的参数 CLOCK_MODE_156MHzCLOCK_MODE_32MHzCLOCK_MODE_8MHz 指定一个动态切换应用程序 CPU 的时钟和整个系统的时钟。降低工作时钟可以降低功耗。

作为参考,三种模式表示电池边缘的电流消耗。 测量条件仅使用Spresense主板,电源由电池连接器提供3.7V。 软件处于空闲状态是在什么都不工作的情况下。

lowpower current
图表 12. Battery current measurement with LowPower
Clock Mode Battery current

CLOCK_MODE_156MHz

6.68 mA

CLOCK_MODE_32MHz

3.66 mA

CLOCK_MODE_8MHz

3.20 mA

CLOCK_MODE_8MHz + TCXO=PowerOff

1.16 mA

此测量还包括主板上的电源LED的电流消耗。

除了用户应用程序的时钟模式控制外,还可以在系统内自动切换时钟模式。

  • 时钟模式自动成为 CLOCK_MODE_156MHz ,同时使用USB通信功能,如 USB MSC(大容量存储)。

  • 使用GNSS定位功能时,时钟模式会自动变为 CLOCK_MODE_156MHzCLOCK_MODE_32MHz

  • 在 SD 卡的安装过程中,它将自动计时到 CLOCK_MODE_156MHz ,并在安装后恢复到以前的状态。

请注意, 这不一定会将用户更改为指定的时钟模式。 当前时钟模式可以通过 getClockMode() 功能获得,以确定时钟模式是否已切换。

此外,并非所有功能都在低时钟模式下工作。以下是限制:

  • Audio功能在 CLOCK_MODE_156MHz 设置下工作

  • Camera功能在 CLOCK_MODE_156MHz 或者 CLOCK_MODE_32MHz 设置下工作

2.9.2. 示例

有关LowPower程序库的基本用法, 请参阅示例中的说明。 示例sketch 可以从Arduino IDE 菜单 File → Examples → Examples for Spresense LowPower 打开。

Example Description

ExternalWakeup

通过GPIO外部管脚变化从Cold Sleep唤醒

TimedWakeup

通过RTC闹钟从Cold Sleep唤醒

TimedWakeupDeep

通过RTC闹钟从Deep Sleep唤醒

Reboot

系统重启

WatchdogReboot

通过看门狗定时器重启系统

ClockChange

切换到156MHz/32MHz/8MHz,用于时钟模式


2.10. MultiCore MP 程序库

2.10.1. 简介

MultiCore MP程序库(以下简称MP程序库)是一个库,用于支持Arduino环境中的多核编程。默认情况下调用的CPU内核称为 MainCoreSubCore ,后者从 MainCore 调用。从 SubCore 1SubCore 5 ,总共有5个 SubCore

Tools→ core 的Arduino IDE 菜单中选择 MainCoreSubCore 1-5 。程序在每个内核加载和移动每个核心编译的二进制文件到Spresense主板。MP程序库可用于与每个核心项目合作。

2.10.2. 功能

MP程序库主要提供以下功能。

  • SubCore启动/终止控制

  • 核心间通信功能

  • 核心独占控制功能

  • 获取/释放共享内存

  • 独占日志输出功能

有关API详细信息,请参阅 MP Library API 文档。

2.10.2.1. MP类

MP对象由mp.h生成。

#include <MP.h>

MP类提供的函数列表如下所示。 有些功能在 MainCoreSubCore 中不同,有些功能不受 SubCore 的支持。( * :支持, - :不支持)

Function Description MainCore SubCore

MP.begin()

控制多核的启动。
(MainCore) 使用指定数量的参数启动SubCore。
(SubCore) 在没有参数的情况下调用,并通知MainCore启动完成。
有关详细信息,请参阅 SubCore 启动过程 文档。

*

*

MP.end()

控制多核停机。
(MainCore) 停止参数中指定数字的Subcore。
(SubCore) 无操作。
有关详细信息,请参阅 SubCore 停止过程 文档。

*

*

MP.Send()

将数据发送到指定的核心。
(MainCore) 使用指定数量的参数将数据发送到SubCore。
(SubCore) 使用指定数量的参数将数据发送到SubCore。如果省略该数字,请将其发送到MainCore。
有关详细信息,请参阅 核心间通信方法 文档。

*

*

MP.Recv()

从指定的内核接收数据。
(MainCore) 从SubCore接收具有指定参数数的数据。
(SubCore) 从SubCore接收具有指定参数数的数据。如果省略该数字,则将从MainCore接收该数字。
有关详细信息,请参阅 核心间通信方法 文档。

*

*

MP.RecvTimeout()

Recv() 设置接收器的模式和超时值。
有三种类型的等待模式,并由参数选择。
有关详细信息,请参阅 核心间通信方法 文档。

*

*

MP.GetRecvTimeout()

RecvTimeout() 获取中设置的值。

*

*

MP.Virt2Phys()

将参数指定的虚拟地址转换为物理地址。物理地址映射在所有内核中都很常见。另一方面,虚拟地址对于每个核心来说是不同的。SubCore在虚拟地址上工作,如果要将地址发送到其他核心,则需要通过这功能转换为物理地址使用。
有关详细信息,请参阅 应用程序内存 文档。

*

*

MP.GetMemoryInfo()

获取应用程序内存可用性。
usedMem: 已分配内存空间大小 (Byte)
freeMem: 未分配内存空间大小 (Byte)
largestFreeMem: 可用连续最大内存空间大小 (Byte)
有关详细信息,请参阅 应用程序内存 文档。

*

*

MP.EnableConsole()

允许从窜行监视器输入(Serial)。
从串行输入可以接受任何一个核心。只有MainCore在启动后立即被允许。

*

*

MP.DisableConsole()

禁止从串行监视器输入(Serial)。
如果要更改要使用特定SubCore接受的串行输入,请从MainCore调用DisableConsole(),并在子核心端调用DisableConsole()。
请参阅 示例 shell 示例。

*

*

MP.AllocSharedMemory()

分配共享内存并返回物理首地址。安全和可用的共享内存大小为128 KByte。从参数中指定的大小分配128 KByte对齐的内存。如果没有足够的内存来保护它,则返回 NULL。
请参阅 示例 SharedMemory 示例。

*

-

MP.FreeSharedMemory()

AllocSharedMemory()释放共享内存。

*

-

2.10.2.2. MPMutex类

多核之间的独占控制功能。 通过将MP_MUTEX_ID号(0 ~ 10)指定为参数来生成 MUTEX 对象。

#include <MPMutex.h>

MPMutex mutex(MP_MUTEX_ID0);

MPMutex提供以下功能。

Function Description MainCore SubCore

mutex.Trylock()

上锁。如果可用,则返回0,否则返回非0。
请参阅 示例 Mutex 示例。

*

*

mutex.Unlock()

解Trylock()获得的互斥锁。

*

*

2.10.2.3. MPLog() 函数

多核环境中的日志记录功能。

Function Description MainCore SubCore

MPLog()

用于在内核之间执行独占控制的日志输出的函数宏。
(MainCore) [Main] 出现在日志开头。
(SubCore) [Sub编号] 出现在日志开头。
可以在多核环境中标识每个内核的日志输出。
但是,从中断处理程序中调用时,可以混合并输出日志。

*

*

2.10.2.4. 应用程序内存

应用程序的所有SRAM内存为1.5MByte(= 1536 KByte)。 内部硬件由128KBytex12 内存磁贴组成。 每个CPU和SRAM都通过内存磁贴连接到总线矩阵。 如果有一个CPU访问内存磁贴,则可以读取和写入SRAM,无需wait周期。 (这意味着与CPU体系结构中常见的缓存相同的性能)。 另一方面,如果从多个CPU访问同一内存磁贴,内存访问速度慢。

为了防止这种访问冲突,基本上移动SubCore时,SubCore程序将在其内存磁贴上工作,以确保专用内存磁贴。

以下是Arduino环境中SRAM 1.5 MByte分布。

diag 64935e6555171d8a58adc590aadf0c3c
图表 13. 使用SRAM

MainCore 固定使用前6个磁贴(768 KByte)。 共享和使用SubCore和其它库中剩余的6个磁贴(768 KByte)等。

上图显示,示例中SubCore1分配256 KByte内存和SubCore2分别 128 Kbyte内存。 SubCore工作原理是将分配的物理内存映射到从零街开始的虚拟地址。 如果需要引用CPU之间通信的公共地址,如 MP.Virt2Phys()

此外,除MainCore的使用区域外,剩下768 KByte的共享内存也可以从SubCore以外的各种库中使用。 每个库的内存大小及其用例如下所示。

Library UseCase Used Memory Size

Audio, Sensing

对于初始化过程中的各种缓冲区

256 KByte

Audio

用于在MP3播放过程中加载 MP3DEC 解码器

128 KByte

Audio

用于在MP3录制过程中加载 MP3ENC 编码器

128 KByte

Audio

用于在WAV播放过程中加载 WAVDEC 解码器

256 KByte

Audio

在 WAV 记录过程中加载 SRC 采样率转换器(采样率48kHz 除外)

256 KByte

DNNRT

初始化时,负载 dnnrt-mp

128 KByte

SubCore所需的内存量取决于用户应用程序。 编译草图时,将在 "编译结果" 窗口中显示以下日志。此值表示使SubCore工作所需的内存大小。

arduino multicore boot6 sub1

根据应用程序的组合,您可能无法耗尽内存。 您可以使用 MP.GetMemoryInfo() 来检查内存使用情况。

下面是示例代码。

{
  int usedMem, freeMem, largestFreeMem;

  MP.GetMemoryInfo(usedMem, freeMem, largestFreeMem);

  MPLog("Used:%4d [KB] / Free:%4d [KB] (Largest:%4d [KB])\n",
        usedMem / 1024, freeMem / 1024, largestFreeMem / 1024);
}
2.10.2.5. SubCore 启动过程

需要在参数中调用的SubCore编号(1-5)并通过MainCore调用 MP.begin(1~5) 。 在启动的SubCore端,可以调用 MP.begin() ,而不使用参数来发出启动完成通知。

diag 98ff0312883d4ab0998aaacfacbdc1c3
2.10.2.6. SubCore 停止过程

需用要在参数中终止的SubCore编号(1-5)并通过MainCore 调用 MP.end(1~5) 。 指定的SubCore将停止并关闭操作。

diag 916fbe6a6910c9b06b25096d29b61846
2.10.2.7. 核心间通信方法

MP.Send()MP.Recv() 函数发送和接收任何数据。
有三种等待接收的模式,设置方法是 MP.RecvTimeout() 。 默认设置是MP_RECV_BLOCKING。

  • MP_RECV_BLOCKING (0): 这是用于接受数据的阻塞等待模式。(默认设置)

  • MP_RECV_POLLING (0xffffffff): 这是用于接收数据的轮询模式。如果调用Recv()时,没有收到任何数据,请立即退出。这个模式不会进入等待。

  • 时间 (其它值): 设置等待超时(以毫秒为单位)。当调用Recv()时,等待指定的时间量,如果传入的数据没有来,则退出时显示超时。

diag 02b0d389f58d91f788efbfe9cf26943b

有两种类型的通信方法, MP.Send()MP.Receive() ,用于传递数据和地址。

  • 数据传输

    • 格式

      int MP.Send(int8_t msgid, uint32_t msgdata, int subid)
      int MP.Recv(int8_t *msgid, uint32_t *msgdata, int subid)

    • 参数

      msgid: 用户端消息ID (8 bit)。
      msgdata: 用户端Message Data (32 bit)。
      subid: 目的SubCore编号。如果省略,则与MainCore通信。

    • 返回值

      如果成功,返回0值,否则返回非0值。

    • 描述

      Message ID和Message Data都可以用ID和Data通过Send()发送,并用Recv()接收数据。

      Send() 异步发送。Send()函数在发送数据到内部8级缓冲区时,如成功则返回0。
      如果在接收内核获取数据之前此发送缓冲区已满,则Send()函数将返回-EAGAIN(-11)错误。
      另一方面,接收器也有8级接收缓冲器。如果接收缓冲区在没有调用Recv()的情况下变满, 则系统会假定它处于异常状态且程序assert。如果发生此assert,则接收核心可能无法正常工作或通信负载过高。

  • 地址发送和接收

    • 格式

      int Send(int8_t msgid, void *msgaddr, int subid)
      int Recv(int8_t *msgid, void *msgaddr, int subid)

    • 参数

      msgid: 用户端消息ID (8 bit)。
      msgaddr: 用户端 Message Address (指针)。
      subid: 目的SubCore编号。如果省略,则与MainCore通信。

    • 返回值

      如果成功,返回0值,否则返回非0值。

    • 描述

      Message ID和Message Address都可以通过ID和Send()发送,并在Address通过Recv()接受。 要通信的数据位于不同的内存中,在仅发送该数据的指针地址的情况下使用。
      如果要从SubCore发送地址,则需要将虚拟地址转换为物理地址,但对于MSGADR, 在Send()内部进行地址转换。如果 msgaddr 指针指向地址信息, 如果要在内核的各个方面引用该地址,请在用户端执行地址转换。
      Send()异步与发送和接收数据时相同。Send()函数实际上不知道是否收到消息, 不管任何返回值,都能够返回。因此,如果重写指针在Send()之后,立即指向的数据, 您将无法保证在接收端收到的数据。是否要使用单独的 Send ()/recv () 通信 (如 "确认") 进行同步? 或者添加一个标志来保证数据包中的数据,您需要设计。

Send()/Recv()功能具体用法,请参阅 示例 Message 示例。

Message/MessageData

用于传递数据的示例代码

Message/MessageHello

传递地址的示例代码

2.10.2.8. SubCore 程序库内容

SubCore提供Arduino标准核心库。

某些特定于拼写的库不能从 SubCore 中使用。 如果合并了以下错误消息,则会在编译时打印。

XXX library is NOT supported by SubCore.

SubCore提供以下库。

Library MainCore SubCore Description

MP

*

*

RTC

*

*

Alarm功能在SubCore中不可用。

SPI

*

*

Servo

*

*

SoftwareSerial

*

*

Watchdog

*

*

Wire

*

*

Audio

*

-

Camera

*

-

DNNRT

*

-

GNSS

*

-

LowPower

*

-

Sensing

*

-

Storage (EEPROM, eMMC, File, Flash, SDHCI)

*

-

2.10.2.9. SubCore 编程 TIPS
SubCore 增加程序的堆大小的方法

SubCore所需的内存大小在编译时自动确定,但描述了详细的逻辑。

.stack 大小是在编译时静态确定的,但 .heap 的大小取决于程序。 堆内存至少有32 KByte大小,在进行最终链接时,总内存128 KByte 将最多分配到对齐的限制作为堆内存。

diag b04beb3f5ef99d3c277e407a3275d202
图表 14. SubCore プログラムサイズ

上述规则确保至少32 KByte堆空间。 但是,某些用户程序可能会耗尽堆空间。 如果要扩展堆空间,可以在示例中使用USER_HEAP_size()宏,如下所示: 您可以扩展该大小。

USER_HEAP_SIZE(64 * 1024);

指定64 KByte通过这种方式可以确保堆内存为64 KByte或更大。

关于用于SubCore 标识数字的编译选项

在构建SubCore时,-DSUBCORE=编号被指定为编译选项。 如果要使用通用源代码为每个内核使用ifdef分隔代码,请参阅以下内容:

#if   (SUBCORE == 1)
// 建立SubCore1

#elif (SUBCORE == 2)
// 建立SubCore2

#elif (SUBCORE == 3)
// 建立SubCore3

#elif (SUBCORE == 4)
// 建立SubCore4

#elif (SUBCORE == 5)
// 建立SSubCore5

#else
// MainCore

#endif

通过应用此方法,您可以将以下说明添加到用户示例中,您可以防止选择、构建或上载错误的内核。

  • 如果在MainCore示例中指定了SubCore,则会出现生成错误

#ifdef SUBCORE
#error "Core selection is wrong!!"
#endif
  • 在SubCore示例中指定MainCore,出错

#ifndef SUBCORE
#error "Core selection is wrong!!"
#endif
  • 如果SubCore1示例指定了非SubCore1内核,则会出现生成错误

#if (SUBCORE != 1)
#error "Core selection is wrong!!"
#endif
关于示例目录配置

MainCore和SubCore通用同一个头文件。
例如,一个想法是通过进行include目录配置来包含一个通用的common.h头文件。

diag 66900e49037f717735e83d1d138ec99a

SubSketch.ino 在

#include "common.h"

MainSketch.ino 在

#include "SubSketch/common.h"

另一个选项是放置一个公共头文件,将库库放在Arduino环境中的位置。
可以从任何示例中看到libraries下面是否有头文件。
libraries将头文件放在 Arduino IDE 菜单的下方 Sketch → Include Library → Add .ZIP Library…​ 可以通过指定包含头文件的文件夹的位置来添加到libraries中。

2.10.3. 示例

有关MP库的基本用法,请参阅示例中的示例。 示例是Arduino IDE菜单 File → Examples → Examples for Spresense → MultiCore MP

Example Description

Boot

启动四个SubCore的示例。每个启动的子核心控制不同的LED, 并使用L-Chica的多核。

Message/MessageData

这是在MainCore和SubCore之间进行数据通信的示例。

Message/MessageHello

MainCore和SubCore之间进行地址通信的示例。

Mutex

然后,利用硬件激发、MPMutex的功能,在MainCore和SubCore之间进行排他控制。

SharedMemory

MainCore确保共享内存/释放示例。

Shell

在从MainCore开始运行的SubCore一侧Shell的示例。

AudioFFT

SubCore的应用应用的示例。使用Audio库在MainCore上捕获PCM 4ch数据,然后把它传送到SubCore上。SubCore会实时进行FFT (Fast Fourier Transform)分析,然后显示峰值频率。可以使用Arduino IDE的串行模型作为时序数据的图表化显示。


2.11. RTC库

RTC库控制着Spresense的实时时钟,是一个单调递增的计数器,初始值为0。 由于RTC由UNIX时间管理,因此初始值0表示1970年1月1日00:00:00。 RTC可以在睡眠期间保持时间,例如 Deep SleepCold Sleep 。 但是,当电源关闭时,RTC计数器复位并重置为初始值0。

使用RTC库时,请务必在开头调用RTC.begin()。 供电后,RTC XTAL稳定需要大约2秒钟。 由于振荡稳定的等待是在RTC.begin()中执行的,因此RTC.begin()返回后,您可以安全地使用RTC。

RTC库提供以下功能。 有关各种API的详细信息,请参阅API参考手册 RTC Library API

  • RTC.setTime() 设置绝对时间

  • RTC.getTime() 获得绝对时间

  • RTC.attachAlarm() 注册RTC警报处理程序

  • RTC.detachAlarm() 释放RTC警报处理程序

  • RTC.setAlarm() 设置RTC警报

  • RTC.setAlarmSeconds() 设置指定秒数的RTC警报

  • RTC.cancelAlarm() 取消RTC警报

有关RTC库的基本用法,请参阅示例中的示例程序。 可以通过Arduino IDE菜单 File → Examples → Examples for Spresense RTC 打开。

除了RTC库,也可以通过添加#include <time.h>, 使用POSIX标准API,例如strftime()和clock_gettime(CLOCK_REALTIME,tp)。

有关更多信息,请参阅NuttX 用户手册。 NuttX配置中已禁用本地时间,因此不支持localtime()。


2.12. SDHCI 程序库

2.12.1. 简介

SDHCI程序库用于访问Spresense扩展板上的microSD卡。

SDHCI程序库与Arduino SD程序库类似,为API结构。可以从应用了SD程序库的既存Sketch程序中简单移植过来。

与使用SPI接口的SD程序库不同,它需要使用SD Host Controller Interface专用硬件来访问SD卡。其特点如下:

  • SD卡由专用端子连接,不需要SPI端子。SPI端子可用作其他用途。

  • 与SPI 通信相比速度极快。支持SDR25传输模式。

这个程序库也提供USB MSC(Mass Storage Class)功能。通过连接Spresense扩展板上的USB和PC,您可以直接从电脑访问SD卡上的文件。

2.12.2. 功能

有关各种API的详细信息,请参阅 SD card Library API API参考手册。

由于尚未在SDHCI库中,生成SDHCI实例,可按照下面代码进行定义:

#include <SDHCI.h>
SDClass SD;
Function Description

SD.begin()

检查SD卡是否已安装。验证此函数返回True并已装入后,请使用其它API。
当SD卡未装入时,最多等待两秒钟,并返回结果。

SD.beginUsbMsc()

USB MSC (Mass Storage Class) 启动功能。

SD.endUsbMsc()

USB MSC (Mass Storage Class) 终止功能。

SD.format()

格式化SD设备。默认FAT32文件系统的格式。

SD.open()

通过指定文件名在 SD 设备中打开该文件。
FILE_READ: 打开文件只读(默认)
FILE_WRITE: 打开读写文件。文件位置指向末尾。
此函数返回一个文件对象。请 File程序库 参阅文件操作。

SD.exists()

检查SD设备中是否有文件。

SD.mkdir()

在SD设备中创建目录。

SD.rmdir()

删除SD设备中的目录。

SD.remove()

删除SD设备中的文件。

2.12.3. 示例

SDHCI 库提供三个示例草图。在FAT32格式下,使用SD卡。

Example Description

read_write.ino

读取和写入 SD 卡上的文件的示例。

UsbMsc.ino

USB MSC功能示例。当Spresense扩展板SD卡作为驱动器安装在PC上,您可以直接从电脑访问SD卡。

UsbMscAndFileOperation.ino

这个例子是一个结合 USB MSC 功能和文件操作的复合应用程序。对于SD卡中的文件,必须专门使用示例中的文件操作和PC上USB MSC文件操作。在这个例子中,在检索和显示应用程序中的文件列表后,可以启用USB MSC功能,然后从PC中使用SD卡中的文件。USB MSC函数完成后,将检索文件列表并从程序中再次显示。


2.13. Sensing程序库

Spresense 具有始终使用低功耗感知的能力以及边缘计算所能做到的性能。此外,通过结合各种传感器数据,进行信号处理和AI处理,可以将其转换为数据量小的抽象数据。这一系列过程可以创建为传感器(称为逻辑传感器)。
Sensing程序库提供创建和使用这样一个逻辑传感器的框架。

逻辑传感器可与多核一起使用,多核是 Spresense 的一个功能,允许在不同的内核(以下简称 DSP)进行信号和识别处理。

Sensing程序库主要特征
  • 基于Publish-Subscribe Architecture,多个Sensor融合易于执行和更改

  • 将使用Arduino提供的生态系统中创建的传感器驱动程序,检测到的数据发布到创建的逻辑传感器,并由Arduino应用程序subscribe,可从 Arduino 获得 Spresense SDK 相同的功能

Arduino程序库本质上是一个Arduino包装库,用于处理 Spresense 的功能和API。
有关详细信息,请参阅 SDK側 文档。

2.13.1. 体系结构和数据流

首先,Sensor Framework显示在 Spresense 中。

请参阅 传感器融合框架 文档。

Sensor Framework(Publish/subscribe) Architecture
图表 15. Sensing Library (Publish/subscribe) Architecture

下面显示Arduino的传感库的体系结构。

Sensor Framework(Publish/subscribe) Architecture
图表 16. Sensing Library (Publish/subscribe) Architecture

由 Spresense SDK 提供的Sensor Framework部分是作为"Sensing"库提供的,以实现与SDK相同的功能。
如果您正在使用Arduino(传感器的驱动程序)、使用传感器的应用程序以及用自己创建更抽象的逻辑传感器进行开发,请通过实现逻辑传感器的信号处理部分,您可以使用复杂的逻辑传感器创建应用程序。

Sensing程序库说明
  • SensorManagerClass:
    管理每个SDK传感器客户端的(Sensor Manager)包装类。

  • SensorClient:
    SensorClient基类是每个传感器的一个元素。用户不会直接调用此类。
    此外,如果要创建自己的逻辑传感器,请继承这个类。

    • AccelSensorClass:
      通过在Arduino中发布从传感器驱动程序 (加速度计的物理传感器) 中获得的传感器数据,每个传感器客户端都可以使用加速度计数据。

    • ApplicationSensorClass:
      这个应用程序类是用于 Subsribe 传感器数据的传感器客户端。

      • StepCountReaderClass:
        用于读取应用程序中的步进计数器传感器的数据的传感器客户端。继承和创建ApplicationSensorClass类。

    • AesmClass:
      由 Spresense SDK 提供的计步器算法(AESM: Activity Engine and StepMeter) 是已实现的逻辑传感器客户端。 使用加速度计。
      SDK提供Arduino的无需处理publish/Subscribe的逻辑传感器。

2.13.2. 类API说明

下面是每个类的API说明。

2.13.2.1. SensorManagerClass

下面是SensorManagerClass的API。

begin
begin();
  • 启动传感器管理器。在使用框架之前调用一次。

end
end();
  • 停止传感器管理器。使用完框架后,请调用它一次。

2.13.2.2. SensorClient

下面是SensorClient的API。 这个类是每个传感器客户端的基类。

begin
begin(int      id,
      uint32_t subscriptions        = 0,
      int      rate                 = 0,
      int      sample_watermark_num = 0,
      int      size_per_sample      = 0,
      sensor_data_mh_callback_t cb  =NULL):

在传感器客户端生成过程中只调用一次。

  • int id
    传感器客户端的ID。

  • uint32_t subscriptions
    指定要Subscribe的传感器。 您可以通过创建ID中1位来读取该ID的数据。

  • int rate
    指定要输入的传感器数据的采样率。

  • int sample_watermark_num
    指定单个流程中的样本数。

  • int size_per_sample
    每个指定样品的大小。

  • sensor_data_mh_callback_t cb
    指定在subscribe传感器数据时要调用的callback函数。

publish
int publish(FAR void* data,
            uint32_t  size_per_sample,
            uint32_t  freq,
            uint32_t  sample_watermark_num,
            uint32_t  timestamp);

这个API发布传感器客户端获得和生成的传感器数据。

  • FAR void* data
    指定要发送的传感器数据的地址。

  • uint32_t size_per_sample
    每个样本单位指定大小。

  • uint32_t freq
    指定传感的频率。

  • uint32_t sample_watermark_num
    指定传感器数据的样本数。

  • uint32_t timestamp
    指定要发送的传感器数据的第一个示例的时间戳。

int publish(PoolId    id,
            FAR void* data,
            uint32_t  size_per_sample,
            uint32_t  freq,
            uint32_t  sample_watermark_num,
            uint32_t  timestamp);

独立定义Memory区的管理区域,在减少内存时进行调用。现在不使用。

int publish(MemMgrLite::MemHandle& mh,
            uint32_t size_per_sample,
            uint32_t freq,
            uint32_t sample_watermark_num,
            uint32_t timestamp);

当你使用MemoryUtil的MHandle来管理传感器客户端中的传感器数据时,可以调用该系统。现在不使用了。

subscribe
int subscribe(sensor_command_data_mh_t& data);

调用SensorClient的Subscribe,可以获取保存SensorClient数据的地址。 请在SubClass中使用。

subscribe的数据类型

sensor_command_data_mh_t

变成。这是由Spresense SDK定义的。

typedef struct
{
  sensor_command_header_t header;  /**< command header    */
  unsigned int self: 8;            /**< sender sensor ID  */
  unsigned int time: 24;           /**< time stamp        */
  unsigned int fs: 16;             /**< frequensy         */
  unsigned int size: 16;           /**< number of samples */
  MemMgrLite::MemHandle  mh;       /**< mem handle for send data */

  unsigned int get_self(void)
  {
    return self;
  }

} sensor_command_data_mh_t;

是。 timestamp信息等,请根据需要取得。

2.13.2.3. AccelSensorClass

事实上,我们还提供一个物理传感器客户端来进行加速度传感。SensorClient是用继承的形式做成的。

begin
bool begin(int id,
           int rate                 = 0,
           int sample_watermark_num = 0);

产生加速度传感器客户端。

  • int id
    指定你的传感器ID。请指定 SEN_accelID

  • int rate
    加速度传感器的速率。 这次提供的StepCounter(AESM)的情况,请设置为50hz。   

  • int sample_watermark_num
    是一次传送的样品数。能够指定的值在50以下。如果没有特别需要更改的话请指定50个。

write_data
int write_data(float x,
               float y,
               float z);

实际上是在写入加速度数据的函数。 请把从传感器获得的数据换算成G并写入。 在此接口上,每个样本一次写入,只写入sample_watermack_num数的话,会被Publish。

2.13.2.4. ApplicationSensorClass

应用传感器是将传感器数据传递到应用的传感器客户端。因此,subscribe只被实现。

begin
bool begin(int id,
           uint32_t subscriptions,
           sensor_data_mh_callback_t cb);
  • int id
    指定你的传感器ID。请指定 SEN_app*ID(*:0~3)

  • uint32_t subscriptions
    指定需要Subscribe传感器。 在此,将第一个ID的bit设为1,就能读取该ID的数据。

  • sensor_data_mh_callback_t cb
    当传感器数据被subscribe发出时,它会指定它是callback。

subscribe
int subscribe(sensor_command_data_mh_t& data);

可以通过subscribe获得期望传感器(物理传感器和逻辑传感器)的数据。 请根据应用程序安装。

2.13.2.5. StepCountReaderClass

为了在应用传感器中,读出StepCounter数据而准备的应用传感器客户端。

begin
bool begin(int      id,
           uint32_t subscriptions,
           sensor_data_mh_callback_t cb);
  • int id
    指定你的传感器ID。请指定 SEN_app0ID

  • uint32_t subscriptions
    指定想要做Subscribe的传感器。 这里,将读出StepCounter的逻辑传感器,所以请指定 SUBSCRIPTION(SEN_stepcounterID)

  • sensor_data_mh_callback_t cb
    当传感器数据被subscribe发出时,它会指定它是callback。

subscribe
int subscribe(sensor_command_data_mh_t& data);

可以利用subscribe从Step Counter逻辑传感器中获得数据。

这是下面的数据结构。

typedef struct
{
  SensorExecResult exec_result;

  union
    {
      StepCounterStepInfo steps;
      SensorAssertionInfo assert_info;
    };
} SensorResultStepCounter;

其中,实际发生的 StepCounterStepInfo steps; 这就是传感器数据的实体。

以下是StepCounter传感器数据。

typedef struct {

  float    tempo;
  float    stride;
  float    speed;
  float    distance;
  uint32_t step;
  StepCounterMovementType  movement_type;
  uint64_t          time_stamp;

} StepCounterStepInfo;
2.13.2.6. AesmClass:

这是一个传感器客户端,用于调用索尼独立准备的步进计数器库。 这个算法已在Bosch的BMI160的基础上进行了调整。建议使用 BMI160 传感数据,但作为加速度数据,如果可以以所需的采样率获得数据,则它可以作为逻辑传感器发挥作用。

begin
bool begin(int      id,
           uint32_t subscriptions,
           int      input_rate,
           int      input_sample_watermark_num,
           int      input_size_per_sample);

   * int id
指定传感器 ID。请指定 SEN_stepcounterID

  • uint32_t subscriptions
    指定要Subscribe的传感器。 在这种情况下,我们将使用加速度计数据,因此请指定 SEN_accelID

  • int rate
    指定要输入的传感器数据的采样率。这个算法已创建基于50Hz,所以请使用50。

  • int sample_watermark_num
    指定单个流程中的样本数。 该算法在一秒钟内完成处理,因此请设置50。

  • int size_per_sample
    由于浮点的值为3轴: x、y和z,因此请指定12。

set
int set(uint8_t walking_stride,
        uint8_t running_stride)

设置步幅的函数。当前不可用。

2.13.3. 示例

2.13.3.1. Step Counter示例

对于传感,我们使用索尼提供的步进计数器提供样品。

这个示例要求在v1.3.0后更新引导加载程序。

Spresense SDK 中的步骤计数器函数由以下部分组成。

有关详细信息,请参阅 传感器融合框架 文档。

Sensor Framework(Publish/subscribe) Architecture
图表 17. Sensing Library (Publish/subscribe) Architecture

对于Arduino,Sensor程序库提供了下图所示各部分的库。

Sensor Framework(Publish/subscribe) Architecture
图表 18. Sensing Library (Publish/subscribe) Architecture

作为示例,阅读加速度传感器和要提供给框架的部件,读取生成的StepCounter数据要实现的部分的实施。

以下が、シーケンスになります。

diag dc59d36dd13e99875d80c0e9421f88a2
图表 19. 初始化过程
diag 72c80e0593bec6d659c767d8ff34595c
图表 20. 执行顺序

通过这种方式,您可以通过实现来获取StepCounter数据。

有关如何使用它的详细信息,请参阅 教程 文档。


2.14. Servo 程序库

Servo 程序库为Spresense提供RC servo控制功能。

Servo 程序库的使用与Arduino中定义的Arduino Servo 程序库相同。

Spresense 支持4个PWM,通过以下这些来控制RC舵机。

  • D06 PWM0

  • D05 PWM1

  • D09 PWM2

  • D03 PWM3

大多数舵机需要5V或更多电源。因此,如果从Spresense扩展板供给电源的话,马达的运转会消耗更多的电力,可能无法为主体提供足够的电源。因此,马达的电源请考虑通过外部电源提供。

同时请注意,Spresense扩展板可通过IOREF跳线方式更改为3.3V或5V。虽然大多数的RC servo都可以使用3.3V或5V电源,但仍需要认真确认其实际规格。

2.14.1. 制限

  • Servo功能需要使用PWM。Spresense的servo库最多只能控制4个RC servo。


2.15. Software Serial 程序库

Spresense Arduino Library有2个串行端口。USB接口的调试串口在Serial中,DO、D1的Pin接口对应为Serial2。

SoftwareSerial 程序库可将 Spresense 的数字端子作为串口端子使用。最大传输速率为250,000bps。

SoftwareSerial 程序库与Arduino的SoftwareSerial程序库可互用。可直接使用。

2.15.1. 制限

  • 最大传输速率为250,000bps

  • 可用的GPIO的Pin接口为 (D0 - D28)。TX可用任意Pin接口,但RX使用时有几处限制。TX可从以下Pin接口选择6针接口: 0, 1, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28、这时 RX 可从以下Pin接口选择6针接口: 2, 3, 4, 5, 6, 7, 8, 9, 14, 15, 22.


2.16. SPI 程序库

Spresense SPI 程序库为Spresense提供标准接口,以供与带有各种SPI接口的智能设备连接。 Spresense SPI 程序库与Arduino SPI 程序库兼容,可互换使用。

Spresense 电路板备有独立于主板和扩展板的其他SPI接口。

SPI接口端子如下所示:

SPI接口端子

SPI实例名称 专用接口 MOSI MISO SCK SS IO电压

SPI (or SPI4)

扩展板

D11

D12

D13

D10

3.3V or 5V

SPI5

主板

D16

D17

D23

D24

1.8V

2.16.1. 功能与相关限制

  • Spresense SPI程序库支持8bit及16bit和8bit data array传输方式

  • SPI SS在开始每个事务处理时自动降为LOW。事务结束后更新为 HIGH 。这对高速传输来说是一个非常方便的功能。如果与应用程序不匹配时,可考虑将其他数字端子分配给SS,通过 digitalWrite() 函数进行 HIGH/LOW 控制

  • 16 bit data通过Little Endian方式传输

  • 默认时钟 4 MHz

  • 最大传输速度 20 MHz

样例程序请参考SPI_loopback.ino。运行此样例时,请在电路板外侧将MOSI与 MISO以回环方式连接。可通过自行接收发送的数据来确认数据的收发处理。


2.17. Storage 程序库

2.17.1. 简介

Storage程序库是各类存储库(如SDHCI、SPI-Flash、eMMC)的基类。 基本上,每个存储设备将通过库 SDHCIFlasheMMC 使用, 但还可以通过指定包含挂载目录的绝对路徑使用。

各种存储和装载目录名称之间的关系参考如下。

Device Mount Path

SD card

/mnt/sd0

Flash

/mnt/spif

eMMC

/mnt/emmc

2.17.2. 功能

有关各种API的详细信息,请参阅 Storage Library API API参考手册。

Function Description

Storage.open()

通过指定包含挂载目录的文件名,在存储设备中打开该文件。
FILE_READ: 打开文件只读(默认)
FILE_WRITE: 打开和写入文件。文件位置指向末尾。
这个函数返回一个文件对象。有关文件操作的信息,请参阅 File程序库 文件库。

Storage.exists()

Storage 检查设备中是否有文件。

Storage.mkdir()

Storage 在设备中创建目录。

Storage.rmdir()

Storage 删除设备中的目录 。

Storage.remove()

Storage 删除设备中的文件。

2.17.3. 示例

Storage程序库提供一个简单示例。

Example Description

read_write.ino

读取和写入文件的示例。


2.18. Watchdog库

Spresense Watchdog库监视Spresense的系统状态,能够在系统异常时进行系统复位的硬件Watchdog功能。

该库包含以下功能:

  • 设置执行硬件复位的时间

  • 获取复位时间

  • 复位复位时间

例如,loop() 必须在10秒内退出, 但如果已经过了这个时间还不退出时想进行复位的场合就可以使用这个功能。

2.18.1. 使用方法

按照以下步骤使用此库,也可以使用 Watchdog 作为 WatchdogClass 的实例。

  1. 使用 Watchdog.begin() 初始化一个Watchdog

  2. 使用 Watchdog.start(time) 开始监控

    您需要为 time 设置一个时间(ms), 当超过这个时间时就会触发硬件复位。time 最大能够设置为40秒。

  3. 使用 Watchdog.kick() 复位复位时间

    如果程序正常运行,会复位复位时间。

  4. 如果您不再需要使用Watchdog进行监视,请运行 Watchdog.stop()

    如果指定的时间过去而没有调用 Watchdog.stop()Watchdog.kick() ,则会发生硬件复位。

如果要检查触发硬件复位的剩余时间,可以使用 Watchdog.timeleft()

diag 5fb225bcda5d982093f2da202307bce8

2.18.2. 示例

该库的示例位于Arduino IDE上的 File> Sketch example> Watchdog> Watchdog_bite 中。


2.19. Wire 程序库

Spresense Wire程序库可在Spresense中与I2C / TWI设备通信。I2C是大多数感应器设备所采用的通信接口。

Spresense Wire程序库可与Arduino Wire 程序库 同样使用。

Spresense 主板/扩展板的Pin接口配置如下所示。IO电压在主板端为1.8V,扩展板端为3.3V或5.0V。扩展板与主板使用相同的Pin接口,使用时请注意。

  • D15 I2C_SCL Clock line

  • D14 I2C_SDA Data line

注意:扩展板上Pin接口的物理配置虽与Arudino Uno相同,但I2C的Pin接口编号配置不同。所以,复用Arduino Uno的Sketch程序时需要做相应修改。

扩展板的IO电压可通过IOREF跳线电阻的方式进行调整。I2C的IO电压也可通过IOREF跳线电阻的方法进行调整。

主板的Pin接口插座也可使用I2C,但与IOREF无关,IO电压固定为1.8V。

2C 通信时通常需要Pull-up寄存器,扩展板和主板则无此需要。

  • Spresense 扩展板上带有1kΩ的上拉电阻,不需要追加组件。

  • Spresense 主板上内置有4.7kΩ的上拉电阻,不需要追加组件。

2.19.1. 功能与相关限制

通信速度定义如下。启动时可从以下3种中选择。

  • #define TWI_FREQ_100KHZ (100000) // standard mode

  • #define TWI_FREQ_400KHZ (400000) // fast mode

  • #define TWI_FREQ_1MHZ (1000000) // fast mode plus

默认通信速度为100kHz 。

程序库支持以下2种I2C地址长度:

  • #define TWI_ADDR_LEN_7_BIT (7)

  • #define TWI_ADDR_LEN_10_BIT (10)

默认的地址长度为7 bit。

与原始的Arduino Wire程序库一样,Spresense Wire 程序库也配备了32 byte大小的buffer。因此,与设备间的通信数据需要控制在此范围内。数据中超出此范围的部分将被丢弃。