开发板主芯片采用的是 NUC980DK61Y ARM926EJ-S,300MHz 集成 64MB DDR-II。具体的参数参考 datesheet,这里就不列出了。
I2S( Integrated Inter-IC Sound Bus)主要用于 IC 之间传输数字音频资料的一种串行总线接口标准。常用于发送 PCM 格式的音频到 DAC 中。
支持 I2S 和 PCM,精度 8,16,18,20,24 bit,在 18/20/24 bit 下,每个声道采样存储到 32-bit 的WORD 中,采用 16 bit 精度时,MSB 存储右声道,LSB 存储左声道。支持 DMA。
驱动: bsp/nuvoton/libraries/nuc980/driver/Source/nu_i2s.c
RTT 移植:bsp/nuvoton/libraries/nuc980/rtt_port/drv_i2s.c
SDH(Secure Digital Host):包含 DMAC 和 SD 两个单元。
DMAC 能够提供为 SD 提供系统内存和 128 字节的 shared buffer 之间的数据交换。SD 单元是 SD/SDHC 的接口控制。主要特点主持单 DMA 通道,使用一个 128 字节的共享 buffer 来进行数据交换。
驱动:bsp/nuvoton/libraries/nuc980/driver/Source/nu_sdh.c
RTT 移植:bsp/nuvoton/libraries/nuc980/rtt_port/drv_sdh.c
编解码器(CODEC):NAU88C22YG,具体的寄存器参考 datesheet,这里就不列出了。
驱动在 bsp/nuvoton/libraries/nu_packages/AudioCodec/acodec_nau8822.c
这里主要以基于 RT-Thread 的 Demo 播放存储在 SD 卡中的 WAV 和 MP3 音频文件来做一个初步的直观评测,因为奔着使用这款芯片到产品中去,所以对应用 RT-Thread 这些驱动和软件包做开发更为关注,毕竟用更短的时间开发出满足需求和稳定的产品才是第一目的。
当然具体的性能和参数评测可以进一步利用工具或者编写程序来详细地测试,比如测试一下 SD Card 的读写速率。如果能够用较短的时间在 SD 卡中很流畅的播放 MP3,那么至少说明 I2S 和 SDH 接口本身及其在 RT-Thread 中的 BSP 移植是很不错的。
开发板拿到手的时候自带的 Linux 系统裁剪得比较好,启动速度很快,尝试了一下里边的音频 Demo
/usr/aplay -c 2 -f S16_LE /usr/alsa/8k2ch.pcm
Playing raw data '/usr/alsa/8k2ch.pcm' : Signed 16 bit Little Endian, Rate 8000 Hz, Stereo
设置音量
./amixer set PCM 85%
./amixer set Headphone 90%
一首《寂寞边界》的副歌部分就从耳机里传出来了,整体的音质还是可以的。
这次我们体验的是 RT-Thread。
安装 RT-Studio,安装包有 1G,网站速度还是可以的。
安装 bsp
添加 wavplayer helix decoder MP3player ulog 等软件包和组件:
wav 格式由于不需要解码,wavplayer 和 wavrecord 我体验过都能正常工作,没问题,这个让我想到几个应用,比如记录鼾声,还有 Tom 猫学人说话,再往前可以尝试做一个智能音响都是可以的,不过新塘好像有专门的语音识别 SoC.
直接编译的程序并不能播放MP3,会报内存对齐相关错误,
经过一段比较艰难的排查,应该问题出现在 mp3player 这个软件包中,内存对齐的问题并不好排查,因为肉眼看不管怎么看都看不出代码的问题,只有编译后运行在目标机器上才会报错重启。
在 mp3_player.h 中定义了有关 decoder 的结构体,使用了 #pragma pack(1) 来做内存对齐,但在 gcc 编译之后的程序只要运行 mp3play 指令就会报 data abort,网上有说需要在编译选项中将 -O2 改为 -O0,实际不管改都不行。
既然能确定是内存对齐问题,那么为了尽快听到 mp3,我就将这个头文件里的结构体稍稍调整了一下,取消 pack 的方式,手动对齐结构体,虽然可能费点内存(我们有 64MB ),但终于运行不报内存的错误了。(关于内存,实际测试发现,开启 MMU 比不开启明显效率要高很多)。里边增加的 reserved/reserved1 是我添加的,32-bit 对齐。
// #pragma pack(1)
typedef struct
{
uint8_t read_ptr;
uint8_t reserved1;
uint8_t reserved2;
uint8_t reserved3;
int read_offset;
int bytes_left;
} decode_oper_t;
/
music basic info structure definition
*/
typedef struct
{
uint8_t title[30];
uint8_t artist[30];
uint8_t year[4];
uint8_t comment[30];
uint8_t genre;
uint8_t reserved;
} mp3_basic_info_t;a
struct mp3_player
{
int state;
char *uri;
uint8_t *in_buffer;
uint16_t *out_buffer;
uint16_t *reserved;
rt_device_t audio_device;
rt_mq_t mq;
rt_mutex_t lock;
struct rt_completion ack;
FILE fp;
int volume;
/ helix decoder */
HMP3Decoder mp3_decoder;
MP3FrameInfo mp3_frameinfo;
mp3_info_t mp3_info;
decode_oper_t decode_oper;
};
// #pragma pack()
虽然做了上述修改,但依然还是听不到 mp3,启动播放后,会出现如下的错误:
其中 undefined instruction 给了我提示,但我想,都是 C 有什么不支持的指令呢?在这个过程中将 helix 相关的代码的相关结构进行了一番分析,终于发现里边有汇编的代码,主要是两个文件(asmmisc_gcc.s 和 asmploy_thumb2_gcc.s):
这两个文件中有 cortex-m3 和 thumb ,那么这就解释了为啥会出现 undefined instruction 了。NUC980 是基于 ARM926EJ-S,后来才出现的 cortex-m3 系列和 thumb 指令集,所以不支持后边的指令集。好在10 年前读过《Cortex-M3 权威指南》,多少了解一点。
修改也比较简单,两个文件可以各复制一份,将其中的 .cpu 由 cortext-m3 改为 arm926ejs,去掉 .thumb .thumb_func
原来的:
.cpu cortex-m3
.fpu softvfp
.syntax unified
.thumb
.cpu arm926ejs
.fpu softvfp
.syntax unified
.text
另外需要在 Sconscript 中修改对应的文件名,然后还需要在 pub/mp3dec.h 中注释 //#defined ARM_ADS, 因为这里使用的是 gcc 编译而不是 ads。
以上有关完成后,终于可以听到 mp3 了。
进一步测试发现对播放过程中并不能调整音量,也就是在对 codec 的 11 和 12 号寄存器的写入并不成功,但在初始化的时候可以成功,所以这个 NU8822 的驱动可能还需要进一步优化一下。
在这里不得不说 RT-Thread 的软件包确实非常的便利,在 Linux 上开发一个应用,找软件包非常的头痛,每次都要去各种地方下载,版本还分散,官网有时候还打不开,打开了下载速度特别慢,RT-Thread 的软件包解决了下载的问题并且都经过一些测试和验证,基本上下载下来就能直接编译,这个是非常方便的。
RT-Studio 的实际体验还是不错的,要去下载最新的版本体验,旧版本可能会随着软件的迭代有些页面打不开了,好在官网的下载速度是非常快的。
SDH 部分,bsp 中已经移植好了相关的驱动,插入 SD 卡后会挂载到 /mnt/sd1。
插入 SD 卡需要先对 SD 卡格式化一下,直接调用 mkfs -t elm sd1 即可。
我插入了一个 64G 的 SD 卡,目前测试很稳定并没有出现什么问题。
将 MP3 复制到 SD 卡后,执行 mp3paly -s /mnt/sd1/[歌曲名].mp3 就可以了。
关于 NUC980 程序的调试和下载,我觉非常方便的一点是,我觉得是可以支持下载到 Ram 运行,省去了拨 boot ,擦除 Nand,烧写程序,拨回 boot,重新上电一系列的‘体力活’(够懒),这样就可以把更多的时间放在写代码上来了,配合脚本,可以编译下载启动一个指令,不用触碰开发板。
另外,我编译通过了 NuWriter CMD 的 Windows 版,目前还测试,后面上传上来给需要的人。
关于上传文件到 Rtt 文件系统的问题。如果没有 SD 卡用来测试,可以将 mp3 上传到内部的 ramdisk。上传方法可以用支持 ymodem 的终端,也可以用 tftp 命令行(前提添加 tftps,添加 webnet 可以考虑用 web 上传),我这里具体操作是这样的,我不想来回拔插 SD 卡,所以我通过脚本(实际调用 curl -T )来上传文件到 ramdisk,然后复制到 SD 卡/Nand Flash.
后续我会继续和大家分享,如果评测时间足够,咱们一起在这个基础上来实现一个网络收音机。
原作者:SimonOnRT
更多回帖