从0开始编写VS1053音频解码芯片的底层驱动代码(适用于任何单片机)
VS1053是一个高性能的音频解码器芯片,它是干什么的? 他有两个功能:()用来解码音频文件播放音乐的。(2)将麦克风听到的声音编码成音频文件数据,配合单片机可以保存到SD卡。
单片机加上一个VS1053就可以轻松做一个音乐播放器
、一个录音机
等项目。
VS1053支持两种协议:SPI协议和IIS协议。 这篇文章是介绍采用软件模拟的SPI协议,完成对VS1053芯片的控制。完成音频播放、录音的功能。
写出的这份代码不依赖于任何单片机型号,不依赖单片机的硬件本身功能,属于纯粹的软件层逻辑代码。不管你是51单片机、STM32单片机、还是树莓派等等单片机。都可以使用,都可以驱动VS1053。
文章目录
一、从0开始学习VS1053音频(编码/解码)芯片寄存器与时序
1.1 VS1053B音频编解码器简介
【1】VS1053B功能介绍
VS1053b 是单片Ogg Vorbis/MP3/AAC/WMA/MIDI音频解码器,及IMA ADPCM 编码器和用户加载的Ogg Vorbis编码器。
支持:MP3/WMA/OGG/WAV/FLAC/MIDI/AAC 等音频格式的解码,并支持:OGG/WAV音频格式的录音,支持高低音调节设置,功能十分强大。
它包含了一个高性能、有专利的低功耗DSP 处理器内核VS_DSP4、工作数据存储器、供用户应用程序和任何固化解码器一起运行的16 KiB 指令RAM 及0.5KiB 多的数据RAM、串行的控制和输入数据接口、最多8个可用的通用I/O引脚、一个UART、并有一个优质的可变采样率立体声ADC(“咪”、“线路”、“线路+咪”或“线路*2”)和立体声DAC、和跟随的一个耳机功放及一个公共电压缓冲器。
【2】VS1053B原理图介绍
下面是:原理图
下面是:与开发板的硬件连接关系图
XCS 命令片选(低电平有效)----用于传输命令
XDCS 数据片选 (低电平有效)----用于传输数据
SCK SPI时钟线
MOSI SPI数据线(主出从入)
MISO SPI数据线(主入从出)
DREQ 数据请求线,用于通知控制器,VS1053是否可以接收数据(高电平表示可以接收数据)
RST 复位引脚(硬件复位,低电平有效)
1.2 VS1053B音频解码芯片控制
【1】VS1053B SPI模式时序图
示例代码:
u8 SPI_ReadWriteByte(u8 tx_data)
{
u8 rx_data=0;
u8 i;
for(i=0;i<8;i++)
{
SCLK=0;
if(tx_data&0x80)MOSI=1;
else MOSI=0;
tx_data<<=1;
SCLK=1;
rx_data<<=1;
if(MISO)rx_data|=0x01;
}
return rx_data;
}
【2】SPI传输模式介绍
VS1053的SPI数据传送,分为SDI和SCI,SDI用来传输数据,SCI用于传输命令。
SDI数据传输非常简单,就是标准的SPI通信,不过VS1053的数据传输都是通过DREQ控制到主机必须在判断DREQ 有效(高电平有效)后,才可以发送数据。
SCI命令传输接口包含了一个指令字节、一个地址字节和一个16位的数据字。读写操作可以读写单个寄存器,在 SCK的上升沿读出数据位,所以主机必须在下降沿刷新数据。SCI的字节数据总是高位在前低位在后的。第一个字节指令字节,只有2个指令,也就是读和写,读指令为:0X03,写指令为:0X02。
XCS 命令片选(低电平有效)—-用于传输命令
XDCS 数据片选 (低电平有效)—-用于传输数据
VS1053读取寄存器数据,通过先拉低 XCS,然后发送读指令(0X03),再发送一个地址,最后,我们在 SO 线(MISO)上就可以读到输出的数据了。而同时 SI(MOSI)上的数据将被忽略。
VS1053写寄存器数据也是先发指令,再发地址。写时序中,指令(0X02),数据是通过 SI 写入 VS1053 的, SO 则一直维持低电平。
在写入和读出数据之后,VS1053需要一些时间来处理内部的事情,这段时间,是不允许外部打断的,所以,在 SCI 操作之前,最好判断一 下DREQ 是否为高电平,如果不是,则等待 DREQ 变为高。
这是: SCI读时序
这是:SCI写时序
【3】VS1053 SCI寄存器列表
VS1053总共有16个 SCI寄存器,通过这些寄存器实现对VS1053的各种控制,VS1053的所有SCI 寄存器。
SCI 寄存器
SCI 寄存器 | ||||
---|---|---|---|---|
寄存器 | 类型 | 复位值 | 缩写 | 描述 |
0X00 | RW | 0X0800 | MODE | 模式控制 |
0X01 | RW | 0X003C | STATUS | VS1053 状态 |
0X02 | RW | 0X0000 | BASS | 内置高低音增强器 |
0X03 | RW | 0X0000 | CLOCKF | 时钟频率+倍频数 |
0X04 | RW | 0X0000 | DECODE_TIME | 解码时间 |
0X05 | RW | 0X0000 | AUDATA | 各种音频数据 |
0X06 | RW | 0X0000 | WRAM | RAM 写/读 |
0X07 | RW | 0X0000 | WRAMADDR | RAM 写/读的基址 |
0X08 | R | 0X0000 | HDAT0 | 流头数据 0 |
0X09 | R | 0X0000 | HDAT1 | 流头数据 1 |
0X0A | RW | 0X0000 | AIADDR | 用户代码起始地址 |
0X0B | RW | 0X0000 | VOL | 音量控制 |
0X0C | RW | 0X0000 | AICTRL0 | 应用控制寄存器 0 |
0X0D | RW | 0X0000 | AICTRL1 | 应用控制寄存器 1 |
0X0E | RW | 0X0000 | AICTRL2 | 应用控制寄存器 2 |
0X0F | RW | 0X0000 | AICTRL3 | 应用控制寄存器 3 |
VS1053寄存器定义:
//VS10XX寄存器定义
#define SPI_MODE 0x00
#define SPI_STATUS 0x01
#define SPI_BASS 0x02
#define SPI_CLOCKF 0x03
#define SPI_DECODE_TIME 0x04
#define SPI_AUDATA 0x05
#define SPI_WRAM 0x06
#define SPI_WRAMADDR 0x07
#define SPI_HDAT0 0x08
#define SPI_HDAT1 0x09
#define SPI_AIADDR 0x0a
#define SPI_VOL 0x0b
#define SPI_AICTRL0 0x0c
#define SPI_AICTRL1 0x0d
#define SPI_AICTRL2 0x0e
#define SPI_AICTRL3 0x0f
【4】MODE 寄存器介绍
mode寄存器用于控制 VS1053 的模式,是最关键的寄存器之一,该寄存器的复位值为0x0800,其实就是默认设置为新模式。
VS1053的SPI支持两种模式:
1,VS1002有效模式(即新模式,此模式是 VS1053 的默认模式)
2,VS1001 兼容模式。
下面是 MODE 寄存器的各位描述:
位 | 名称 | 功能 | 值 | 说明 |
---|---|---|---|---|
0 | SM_DIFF | 差分 | 0 | 正常的同相音频 |
1 | 左通道反相 | |||
1 | SM_LAYER12 | 允许 MPEG layers I&II 解码 | 0 | 不允许 |
1 | 允许 | |||
2 | SM_RESET | 软件复位 | 0 | 不复位 |
1 | 复位 | |||
3 | SM_CANCEL | 取消当前文件的解码 | 0 | 不取消 |
1 | 取消 | |||
4 | SM_EARSPEAKER_LO | EarSpeaker 低设定 | 0 | 关闭 |
1 | 激活 | |||
5 | SM_TESTS | 允许 SDI 测试 | 0 | 不允许 |
1 | 允许 | |||
6 | SM_STREAM | 流模式 | 0 | 不是 |
1 | 是 | |||
7 | SM_EARSPEAKER_HI | EarSpeaker 高设定 | 0 | 关闭 |
1 | 激活 | |||
8 | SM_DACT | DCLK 的有效边沿 | 0 | 上升沿 |
1 | 下降沿 | |||
9 | SM_SDIORD | SDI 位顺序 | 0 1 | MSB 在前 MSB 在后 |
10 | SM_SDISHARE | 共享 SPI 片选 | 0 | 不共享 |
1 | 共享 | |||
11 | SM_SDINEW | VS1002 本地 SPI 模式 | 0 | 非本地模式 |
1 | 本地模式(新模式) | |||
12 | SM_ADPCM | ADPCM 录音激活 | 0 | 不激活 |
1 | 激活 | |||
13 | -SM_ADPCM_HP | ADPCM 高通滤波允许 | 0 | 不允许 |
1 | 允许 | |||
14 | SM_LINE_IN | ADPCM 音源选择 | 0 | 麦克风 |
1 | 线路输入 | |||
15 | SM_CLK_RANGE | 输入时钟范围 | 0 | 12…13Mhz |
1 | 24…26Mhz |
这里 SM_RESET,可以提供一次软复位,建议在每播放一首歌曲之后,软复位一次。
SM_SDINEW 为模式设置位,应该选择的是新模式,所以设置该位为 1(默认的设置)。
【5】BASS 寄存器介绍
bass寄存器可以用于设置 VS1053的高低音效。
下面是寄存器的各位描述:
序号 | 位 | 描述 |
---|---|---|
ST_AMPLITUDE | 15:12 | 高音控制,1.5dB 步进(-8…7,0 表示关闭) |
ST_FREQLIMIT | 11:8 | 最低频限 1000Hz 步进(0…15) |
SB_AMPLITUDE | 7:4 | 低音加重,1dB 步进(0…15,0 表示关闭) |
SB_FREQLIMIT | 3:0 | 最低频限 10Hz 步进(2…15) |
通过这个寄存器以上位的一些设置,我们可以随意配置自己喜欢的音效(其实就是高低音的调节)。
【6】CLOCKF寄存器介绍
clockf寄存器用来设置时钟频率、倍频等相关信息。
下面是寄存器的各位描述:
位 | 15:13 | 12:11 | 10:0 |
---|---|---|---|
名称 | SC_MULT | SC_ADD | SC_FREQ |
描述 | 时钟倍频数 | 允许倍频 | 时钟频率 |
说明 | CLKI=XTALI×(SC_MULT×0.5+1) | 倍频增量 =SC_ADD*0.5 | 当外部时钟频率不为 12.288Mhz 时, 外部时钟的频率。外部时钟频率为 12.288Mhz 时,此部分设置为 0 |
SC_FREQ是以4Khz为步进的一个时钟寄存器,当外部时钟不是12.288M 的时候,其计算公式为:
SC_FREQ=(XTALI-8000000)/4000
XTALI 的单位是Hz。
CLKI 是内部时钟频率,XTALI 是外部晶振的时钟频率。开发板上的VS1053B使用的是 12.288M 的晶振,在这里设置此寄存器的值为0x9800,也就是设置内部时钟频率为输入时钟频率的3倍,倍频增量为1.0倍。
【7】DECODE_TIME寄存器介绍
DECODE_TIME寄存器是一个十六位的寄存器,用于存放解码时间,以秒钟为单位,我们通过读取该寄存器的值,就可以得到解码时间。不过它是一个累计时间,所以我们需要在每首歌播放之前把它清空一下,以得到这首歌的准确解码时间。
【8】HDAT0 和 HDTA1寄存器
HDAT0和HDTA1是两个数据流头寄存器,不同的音频文件,读出来的值意义不一样, 我们可以通过这两个寄存器来获取音频文件的码率,从而可以计算音频文件的总长度。这两个寄存器的详细介绍,请参考 VS1053 的数据手册。
【9】VOL输出音量寄存器介绍 (VS1053_SetVol)
VOL寄存器用于控制VS1053的输出音量,该寄存器可以分别控制左右声道的音量,每个声道的控制范围为 0~254,每个增量代表 0.5db 的衰减,所以该值越小,代表音量越大。
比如:设置为 0x0000 则音量最大,而设置为0xFEFE 则音量 最小。
注意:如果设置 VOL 的值为 0xFFFF,将使芯片进入掉电模式。
//右声道是高8位 左声道是低8位
设置示例: u16 tmp=vol_R<<8|vol_L;
VS1053_WriteCmd(SPI_VOL,tmp);//设音量
1.3 VS1053B解码播放音乐步骤说明(VS1053_PlayOneMusic)
【1】播放音乐流程
(1)初始化与VS1053连接的单片机硬件GPIO口,默认上拉片选脚。
(2)配置VS1053的模式为新模式
(3)进行VS1053硬件复位和软件复位,为了让VS1053的状态回到原始状态,准备解码下一首歌曲。
建议在每首歌曲播放之前都执行一次硬件复位和软件复位,以便更好的播放音乐,不出现杂音。
(4)配置VS1053时钟寄存器(设置VS10XX的时钟,3倍频 ,1.5xADD)
(5)设置VS1053播放音量
(6)拉低VS1053_XDCS片选引脚,通过SPI发送函数给VS1053发送音乐数据,只要是 VS1053 支持的音频格式,直接往里面丢就可以了,VS1053 会自动识别,并进行播放。数据发送完毕再拉高VS1053_XDCS片选。
注意: VS1053_DREQ为高电平时,才可继续给VS1053发送数据。
【2】硬件复位步骤
(1)拉低RESET脚,进行硬件复位 RESET=0
(2)至少延时20ms,等待复位
(3)取消数据传输 XDCS=1
(4)取消命令传输 XCS=1
(5)拉高RESET脚,完成复位 RESET=1
【3】进行软件复位并设置SPI为新模式
(1)先等待DREQ数据请求线为高电平
(2)设置模式并启动软件复位(因为模式设置和软件复位在同一个寄存器里,所以可以同时操作)
设置的寄存器是MODE寄存器,设置的值: 0x804
(3)至少等待1.35ms完成复位
(4)等待DREQ数据请求线为高电平,完成复位
【4】向VS1053写寄存器数据
写寄存器之前先等待DREQ为高电平,并将XDCS数据片选线拉高,然后拉低XCS写数据,写完拉高。
【5】读取VS1053寄存器的数据
读寄存器数据之前先等待DREQ为高,并将XDCS数据片选线拉高,然后拉低XCS写/读数据,写完拉高。
1.4 VS1053录音功能
录音功能详细说明,在VS103B中文手册的第9章大概53页,有详细说明。
【1】VS1053录音功能说明与插件加载
VLSI的VS10XX系列芯片都会有一些 patch(插件),用于修复芯片存在的 bug,或者用于增加新的功能。
VS10XX的patch其实就是一段可以在 VS10XX 芯片上面执行的代码,由 VLSI 官方提供,可以在:http://www.vlsi.fi/en/support/software/vs10xxpatches.html这个地址下载到。
patch一般有2种格式:一种是采用 16 位无符号数组存储的,是采用了游程编码(RLE) 压缩算法,进行过压缩的格式。另外一种,是直接采用 2 个 8 位数组存储的,没有进行压缩的格式。
推荐采用16位无符号存储格式的 patch,更省空间。
(1)VS1053 采用的 16 位 RLE 压缩编码规则如下:
首先读寄存器地址 addr(第 1 个数据)和重复数 n(第 2 个数据)。
(2)如果 n&0x8000 为真,那么将下一个数据(第 3 个数据)重复写 n 次到寄存器 addr。
(3)如果 n&0x8000 为假,则写接下来的(第 3 个数据开始)n 个数据到寄存器 addr。
(4)重复以上 3 步,直到数组结束。
如VS1053,WAV录音无数据输出的bug,修正patch如下:
const u16 VS1053_WavPlugin[40]=/* Compressed plugin */
{
0x0007, 0x0001, 0x8010, 0x0006, 0x001c, 0x3e12, 0xb817, 0x3e14, /* 0 */
0xf812, 0x3e01, 0xb811, 0x0007, 0x9717, 0x0020, 0xffd2, 0x0030, /* 8 */
0x11d1, 0x3111, 0x8024, 0x3704, 0xc024, 0x3b81, 0x8024, 0x3101, /* 10 */
0x8024, 0x3b81, 0x8024, 0x3f04, 0xc024, 0x2808, 0x4800, 0x36f1, /* 18 */
0x9811, 0x0007, 0x0001, 0x8028, 0x0006, 0x0002, 0x2a00, 0x040e,
};
根据前面提到的原则,得到:addr=0x0007,n=0x0001,即将接下来的 0x8010 写入寄存器 0X0007 即可,之后:addr=0x0006,n=0x001C,即将接下来的 28 个数据,写入寄存器 0x0006。
后续的以此类推。因此,我们可以得出 patch 加载函数(即 RLE 解压):
void VS1053_LoadPatch(u16 *patch,u16 len) //加载补丁
{
u16 i;
u16 addr, n, val;
for(i=0;i<len;)
{
addr = patch[i++];
n = patch[i++];
if(n & 0x8000U) //RLE run, replicate n samples
{
n &= 0x7FFF;
val = patch[i++];
while(n--)VS1053_WriteCmd(addr, val);
}
else //copy run, copy n sample
{
while(n--)
{
val = patch[i++];
VS1053_WriteCmd(addr, val);
}
}
}
}
【2】激活PCM录音模式 (VS1053_RecoderInit)
如果是IMA ADPCM,采样率计算公式如下: 采样率=CLKI/256*d;
假设d=0,并2倍频,外部晶振为12.288M.那么Fc=(212288000)/2566=16Khz
如果是线性PCM,采样率直接就写采样值。
(1)设置SPI_BASS寄存器的值为0
(2)设置采样率为8Khz: 设置SPI_AICTRL0寄存器的值为8000
(3)设置增益: 0表示自动增益,1024表示1倍,512表示0.5倍,最大值65535=64倍,设置SPI_AICTRL1寄存器的值推荐为1024
(4)设置增益最大值: 设置SPI_AICTRL2寄存器的值为0
(5)左通道(MIC单声道输入): 设置SPI_AICTRL3寄存器为6
(6)设置VS1053的时钟: 设置SPI_CLOCKF值为0X2000
(7)录音激活: 设置SPI_MODE寄存器的值为: 0x1804
(8)延时5ms等待录音功能激活
(9)加载VS1053WAV录音需要的patch(补丁)
【3】WAV音频文件结构
WAV头:
typedef __packed struct
{
//RIFF块
u32 RIFF_ChunkID; //chunk id;这里固定为"RIFF",即 0X46464952
u32 RIFF_ChunkSize; //集合大小;文件总大小(-8)
u32 RIFF_Format; //格式;WAVE,即 0X45564157
//fmt块
u32 FMT_ChunkID; //chunk id;这里固定为"fmt ",即 0X20746D66
u32 FMT_ChunkSize; //子集合大小(不包括 ID 和 Size);这里为:20.
u16 FMT_AudioFormat; //音频格式;0X10,表示线性 PCM;0X11 表示 IMA ADPCM
u16 FMT_NumOfChannels; //通道数量;1,表示单声道;2,表示双声道;
u32 FMT_SampleRate; //采样率;0X1F40,表示 8Khz
u32 FMT_ByteRate; //字节速率;
u16 FMT_BlockAlign; //块对齐(字节);
u16 FMT_BitsPerSample; //单个采样数据大小;4 位 ADPCM,设置为 4
//data块
u32 DATA_ChunkID; //chunk id;这里固定为"data",即 0X61746164
u32 DATA_ChunkSize;//子集合大小(不包括 ID 和 Size);文件大小-60.
}WaveHeader;
WAV音频文件头初始化:
wav->RIFF_ChunkID=0x46464952; //"RIFF"
wav->RIFF_ChunkSize=0; //还未确定,最后需要计算
wav->RIFF_Format=0x45564157; //"WAVE"
wav->FMT_ChunkID=0x20746D66; //"fmt "
wav->FMT_ChunkSize=16; //大小为 16 个字节
wav->FMT_AudioFormat=0X01; //0X01,表示 PCM;0X01,表示 IMA ADPCM
wav->FMT_NumOfChannels=1; //单声道
wav->FMT_SampleRate=13000; //13Khz 采样率 采样速率
wav->FMT_ByteRate=wav->FMT_SampleRate*2;//16 位,即 2 个字节
wav->FMT_BlockAlign=2; //块大小,2 个字节为一个块
wav->FMT_BitsPerSample=16; //16 位 PCM
wav->DATA_ChunkID=0x61746164; //"data"
wav->DATA_ChunkSize=0; //数据大小,还需要计算
向文件写音频数据之前,需要先向音频文件写wav文件头结构进去。
【4】读取咪头采集的音频数据
在激活了 IMA ADPCM 录音之后,寄存器 SCI_HDAT0 和 SCI_HDAT1 有了新的功能。
IMA ADPCM 的采样缓冲区是1024个16位字。缓冲区的填充状态可以通过 SCI_HDAT1 来读取。
如果SCI_HDAT1大于0,则你可以从 SCI_HDAT0 读取许多个16位的字。
如果数据没能足够快的读取,则缓冲区会溢出并返回空的状态。
注意:如果 SCI_HDAT1≥896,它最好是等待缓冲区溢出和在读取采样数据之前清除。
这样是为了让你避开缓冲区混叠。
每个 IMA ADPCM 块是 128 字组,即 256 字节。
如果你想暂停一下读取数据并在稍后恢复,请按照128 字的界限来中止。这样可以跳过完整的块并让编码流保持正确。
WaveHeader wav;
/*1. 创建音频文件*/
<....>
/*2. 初始化wav音频文件*/
<....>
/*3. 写入wav头*/
<....>
/*4. 读取音频数据写入到文件*/
while(1)
{
//判断是否有音频数据可以读取
cnt=VS1053B_ReadReg(VS1053_HDAT1);
if(cnt>=256&&cnt<=896)
{
for(i=0;i<256;i+=2)
{
//读取音频数据
tmp=VS1053B_ReadReg(VS1053_HDAT0);
read_buffer[i]=tmp&0xFF;
read_buffer[i+1]=(tmp>>8)&0xFF;
}
block_cnt++; //记录数据块的次数
//写到文件
<....>
}
key=KEY_Scanf();
if(key)
{
wav.RIFF_ChunkSize=block_cnt*256+sizeof(WaveHeader)-8; //填充结构成员--整个文件的大小
wav.DATA_ChunkSize=block_cnt*256; //填充结构体成员数据大小
//偏移文件指针到文件头
<....>
//重新写wav文件头
<....>
//关闭文件
<....>
break; //跳出循环
}
}
【5】录音的流程(RecoderPlay)
(1)VS1053连接的GPIO口初始化
(2)激活PCM 录音模式
(3)定义wav结构体,并初始化wav数据
(4)创建存放录音的文件
(5)将初始化后的wav数据写入文件
(6)循环读取咪头采集的数据,并写入文件
(7)如果录音结束,重新填充wav结构体的文件大小和数据大小,将文件指针移到文件头,重新将wav结构再写入文件。
(8)关闭文件
(9)结束录音
【6】WAV文件频率设置说明
wav 录音默认采样率为 8Khz,设置为 8Khz,就是正常语调,通过将采样率设置为不同的值可以得到不同的语调,语速设置范围为 4000hz~16000hz,根据设置的语速范围不同,可以实现变调的原理。
注意: 这里的频率范围设置说的是wav文件结构里的频率设置,不是VS1053的采样频率(VS1053的采频率一般推荐填8KHZ和16KHZ)。
比如: 将频率设置为 13000Hz,这个语调比较接近TOM 猫效果。
将频率设置为 4000Hz,这个语调比较接近老人的声音。
二、完整的代码
2.1 vs1053.c
#include "vs1053b.h"
/*
函数功能:移植接口--SPI时序读写一个字节
函数参数:data:要写入的数据
返 回 值:读到的数据
*/
u8 VS1053_SPI_ReadWriteByte(u8 tx_data)
{
u8 rx_data=0;
u8 i;
for(i=0;i<8;i++)
{
VS1053_SCLK=0;
if(tx_data&0x80){VS1053_OUTPUT=1;}
else {VS1053_OUTPUT=0;}
tx_data<<=1;
VS1053_SCLK=1;
rx_data<<=1;
if(VS1053_INPUT)rx_data|=0x01;
}
return rx_data;
}
/*
函数功能:初始化VS1053的IO口
*/
void VS1053_Init(void)
{
RCC->APB2ENR|=1<<0;
AFIO->MAPR&=~(0x7<<24); //释放PA13/14/15
AFIO->MAPR|=0x4<<24;
RCC->APB2ENR|=1<<2;
RCC->APB2ENR|=1<<3;
GPIOA->CRH&=0x00000FFF;
GPIOA->CRH|=0x33338000;
GPIOB->CRL&=0xFFF00FFF;
GPIOB->CRL|=0x00083000;
VS1053_SCLK=1;
VS1053_XCS=1;
VS1053_RESET=1;
}
/*
函数功能:软复位VS10XX
*/
void VS1053_SoftReset(void)
{
u8 retry=0;
while(VS1053_DREQ==0); //等待软件复位结束
VS1053_SPI_ReadWriteByte(0Xff); //启动传输
retry=0;
while(VS1053_ReadReg(SPI_MODE)!=0x0800) // 软件复位,新模式
{
VS1053_WriteCmd(SPI_MODE,0x0804); // 软件复位,新模式
DelayMs(2);//等待至少1.35ms
if(retry++>100)break;
}
while(VS1053_DREQ==0);//等待软件复位结束
retry=0;
while(VS1053_ReadReg(SPI_CLOCKF)!=0X9800)//设置VS10XX的时钟,3倍频 ,1.5xADD
{
VS1053_WriteCmd(SPI_CLOCKF,0X9800); //设置VS10XX的时钟,3倍频 ,1.5xADD
if(retry++>100)break;
}
DelayMs(20);
}
/*
函数 功 能:硬复位MP3
函数返回值:1:复位失败;0:复位成功
*/
u8 VS1053_Reset(void)
{
u8 retry=0;
VS1053_RESET=0;
DelayMs(20);
VS1053_XDCS=1;//取消数据传输
VS1053_XCS=1; //取消数据传输
VS1053_RESET=1;
while(VS1053_DREQ==0&&retry<200)//等待DREQ为高
{
retry++;
DelayUs(50);
}
DelayMs(20);
if(retry>=200)return 1;
else return 0;
}
/*
函数功能:向VS10XX写命令
函数参数:
address:命令地址
data :命令数据
*/
void VS1053_WriteCmd(u8 address,u16 data)
{
while(VS1053_DREQ==0); //等待空闲
VS1053_XDCS=1;
VS1053_XCS=0;
VS1053_SPI_ReadWriteByte(VS_WRITE_COMMAND);//发送VS10XX的写命令
VS1053_SPI_ReadWriteByte(address); //地址
VS1053_SPI_ReadWriteByte(data>>8); //发送高八位
VS1053_SPI_ReadWriteByte(data); //第八位
VS1053_XCS=1;
}
/*
函数参数:向VS1053写数据
函数参数:data:要写入的数据
*/
void VS1053_WriteData(u8 data)
{
VS1053_XDCS=0;
VS1053_SPI_ReadWriteByte(data);
VS1053_XDCS=1;
}
/*
函数功能:读VS1053的寄存器
函数参数:address:寄存器地址
返回值:读到的值
*/
u16 VS1053_ReadReg(u8 address)
{
u16 temp=0;
while(VS1053_DREQ==0);//非等待空闲状态
VS1053_XDCS=1;
VS1053_XCS=0;
VS1053_SPI_ReadWriteByte(VS_READ_COMMAND);//发送VS10XX的读命令
VS1053_SPI_ReadWriteByte(address); //地址
temp=VS1053_SPI_ReadWriteByte(0xff); //读取高字节
temp=temp<<8;
temp+=VS1053_SPI_ReadWriteByte(0xff); //读取低字节
VS1053_XCS=1;
return temp;
}
/*
函数功能:读取VS1053的RAM
函数参数:addr:RAM地址
返 回 值:读到的值
*/
u16 VS1053_ReadRAM(u16 addr)
{
u16 res;
VS1053_WriteCmd(SPI_WRAMADDR, addr);
res=VS1053_ReadReg(SPI_WRAM);
return res;
}
/*
函数功能:写VS1053的RAM
函数参数:
addr:RAM地址
val:要写入的值
*/
void VS1053_WriteRAM(u16 addr,u16 val)
{
VS1053_WriteCmd(SPI_WRAMADDR,addr); //写RAM地址
while(VS1053_DREQ==0); //等待空闲
VS1053_WriteCmd(SPI_WRAM,val); //写RAM值
}
/*
函数参数:发送一次音频数据,固定为32字节
返 回 值:0,发送成功
1,本次数据未成功发送
*/
u8 VS1053_SendMusicData(u8* buf)
{
u8 n;
if(VS1053_DREQ!=0) //送数据给VS10XX
{
VS1053_XDCS=0;
for(n=0;n<32;n++)
{
VS1053_SPI_ReadWriteByte(buf[n]);
}
VS1053_XDCS=1;
}else return 1;
return 0;//成功发送了
}
/*
函数参数:发送一次音频数据,固定为32字节
返 回 值:0,发送成功
1,本次数据未成功发送
*/
void VS1053_SendMusicByte(u8 data)
{
u8 n;
while(VS1053_DREQ==0){}
VS1053_XDCS=0;
VS1053_SPI_ReadWriteByte(data);
VS1053_XDCS=1;
}
/*
函数功能:设定VS1053播放的音量
函数参数:volx:音量大小(0~254)
*/
void VS1053_SetVol(u8 volx)
{
u16 volt=0; //暂存音量值
volt=254-volx; //取反一下,得到最大值,表示最大的表示
volt<<=8;
volt+=254-volx; //得到音量设置后大小
VS1053_WriteCmd(SPI_VOL,volt);//设音量
}
/*--------------------------------------录音功能-----------------------------------------------------*/
/*
函数功能:vs10xx装载patch
函数参数:
patch:patch首地址
len :patch长度
*/
void VS1053_LoadPatch(u16 *patch,u16 len)
{
u16 i;
u16 addr, n, val;
for(i=0;i<len;)
{
addr = patch[i++];
n = patch[i++];
if(n & 0x8000U) //RLE run, replicate n samples
{
n &= 0x7FFF;
val = patch[i++];
while(n--)VS1053_WriteCmd(addr, val);
}
else //copy run, copy n sample
{
while(n--)
{
val = patch[i++];
VS1053_WriteCmd(addr, val);
}
}
}
}
/*
函数参数:初始化WAV头
*/
void VS1053_RecoderWavInit(__WaveHeader* wavhead) //初始化WAV头
{
wavhead->riff.ChunkID=0X46464952; //"RIFF"
wavhead->riff.ChunkSize=0; //还未确定,最后需要计算
wavhead->riff.Format=0X45564157; //"WAVE"
wavhead->fmt.ChunkID=0X20746D66; //"fmt "
wavhead->fmt.ChunkSize=16; //大小为16个字节
wavhead->fmt.AudioFormat=0X01; //0X01,表示PCM;0X01,表示IMA ADPCM
wavhead->fmt.NumOfChannels=1; //单声道
wavhead->fmt.SampleRate=8000; //8Khz采样率 采样速率
wavhead->fmt.ByteRate=wavhead->fmt.SampleRate*2;//16位,即2个字节
wavhead->fmt.BlockAlign=2; //块大小,2个字节为一个块
wavhead->fmt.BitsPerSample=16; //16位PCM
wavhead->data.ChunkID=0X61746164; //"data"
wavhead->data.ChunkSize=0; //数据大小,还需要计算
}
//VS1053的WAV录音有bug,这个plugin可以修正这个问题
const u16 VS1053_WavPlugin[40]=/* Compressed plugin */
{
0x0007, 0x0001, 0x8010, 0x0006, 0x001c, 0x3e12, 0xb817, 0x3e14, /* 0 */
0xf812, 0x3e01, 0xb811, 0x0007, 0x9717, 0x0020, 0xffd2, 0x0030, /* 8 */
0x11d1, 0x3111, 0x8024, 0x3704, 0xc024, 0x3b81, 0x8024, 0x3101, /* 10 */
0x8024, 0x3b81, 0x8024, 0x3f04, 0xc024, 0x2808, 0x4800, 0x36f1, /* 18 */
0x9811, 0x0007, 0x0001, 0x8028, 0x0006, 0x0002, 0x2a00, 0x040e,
};
/*
函数功能:激活PCM 录音模式
函数参数:
agc:0,自动增益
1024相当于1倍
512相当于0.5倍
最大值65535=64倍
*/
void VS1053_RecoderInit(u16 agc)
{
//如果是IMA ADPCM,采样率计算公式如下:
//采样率=CLKI/256*d;
//假设d=0,并2倍频,外部晶振为12.288M.那么Fc=(2*12288000)/256*6=16Khz
//如果是线性PCM,采样率直接就写采样值
VS1053_WriteCmd(SPI_BASS,0x0000);
VS1053_WriteCmd(SPI_AICTRL0,8000); //设置采样率,设置为8Khz
VS1053_WriteCmd(SPI_AICTRL1,agc); //设置增益,0,自动增益.1024相当于1倍,512相当于0.5倍,最大值65535=64倍
VS1053_WriteCmd(SPI_AICTRL2,0); //设置增益最大值,0,代表最大值65536=64X
VS1053_WriteCmd(SPI_AICTRL3,6); //左通道(MIC单声道输入)
VS1053_WriteCmd(SPI_CLOCKF,0X2000); //设置VS10XX的时钟,MULT:2倍频;ADD:不允许;CLK:12.288Mhz
VS1053_WriteCmd(SPI_MODE,0x1804); //MIC,录音激活
DelayMs(5); //等待至少1.35ms
VS1053_LoadPatch((u16*)VS1053_WavPlugin,40);//VS1053的WAV录音需要patch
}
2.2 vs1053.h
#ifndef __VS10XX_H__
#define __VS10XX_H__
#include "sys.h"
#include "delay.h"
#include "usart.h"
/*
VS1053音频解码芯片外部的接口:
*/
#define VS1053_DREQ PAin(11) //DREQ 数据请求
#define VS1053_RESET PAout(12) //RST 硬件复位--低电平有效
#define VS1053_XCS PAout(13) //XCS 片选--低电平有效
#define VS1053_XDCS PAout(14) //XDCS 用于数据片选、字节同步
#define VS1053_SCLK PAout(15)
#define VS1053_OUTPUT PBout(3)
#define VS1053_INPUT PBin(4)
#define VS_WRITE_COMMAND 0x02 //写命令
#define VS_READ_COMMAND 0x03 //读命令
//VS10XX寄存器定义
#define SPI_MODE 0x00
#define SPI_STATUS 0x01
#define SPI_BASS 0x02
#define SPI_CLOCKF 0x03
#define SPI_DECODE_TIME 0x04
#define SPI_AUDATA 0x05
#define SPI_WRAM 0x06
#define SPI_WRAMADDR 0x07
#define SPI_HDAT0 0x08
#define SPI_HDAT1 0x09
#define SPI_AIADDR 0x0a
#define SPI_VOL 0x0b
#define SPI_AICTRL0 0x0c
#define SPI_AICTRL1 0x0d
#define SPI_AICTRL2 0x0e
#define SPI_AICTRL3 0x0f
#define SM_DIFF 0x01
#define SM_JUMP 0x02
#define SM_RESET 0x04
#define SM_OUTOFWAV 0x08
#define SM_PDOWN 0x10
#define SM_TESTS 0x20
#define SM_STREAM 0x40
#define SM_PLUSV 0x80
#define SM_DACT 0x100
#define SM_SDIORD 0x200
#define SM_SDISHARE 0x400
#define SM_SDINEW 0x800
#define SM_ADPCM 0x1000
#define SM_ADPCM_HP 0x2000
#define I2S_CONFIG 0XC040
#define GPIO_DDR 0XC017
#define GPIO_IDATA 0XC018
#define GPIO_ODATA 0XC019
void VS1053_Init(void);
u16 VS1053_ReadReg(u8 address); //读寄存器
u16 VS1053_ReadRAM(u16 addr); //读RAM
void VS1053_WriteRAM(u16 addr,u16 val); //写RAM
void VS1053_WriteData(u8 data); //写数据
void VS1053_WriteCmd(u8 address,u16 data); //写命令
u8 VS1053_Reset(void); //硬复位
void VS1053_SoftReset(void); //软复位
u8 VS1053_SPI_ReadWriteByte(u8 data); //SPI接口,读写一个字节
void VS1053_SoftReset(void); //初始化VS1053
u8 VS1053_SendMusicData(u8* buf); //向VS10XX发送32字节
void VS1053_SetVol(u8 volx); //设置主音量
void VS1053_SendMusicByte(u8 data);
/*-----------------------------------以下是录音功能相关的结构体---------------------------------------*/
//RIFF块
typedef __packed struct
{
u32 ChunkID; //chunk id;这里固定为"RIFF",即0X46464952
u32 ChunkSize ; //集合大小;文件总大小-8
u32 Format; //格式;WAVE,即0X45564157
}ChunkRIFF ;
//fmt块
typedef __packed struct
{
u32 ChunkID; //chunk id;这里固定为"fmt ",即0X20746D66
u32 ChunkSize ; //子集合大小(不包括ID和Size);这里为:20.
u16 AudioFormat; //音频格式;0X10,表示线性PCM;0X11表示IMA ADPCM
u16 NumOfChannels; //通道数量;1,表示单声道;2,表示双声道;
u32 SampleRate; //采样率;0X1F40,表示8Khz
u32 ByteRate; //字节速率;
u16 BlockAlign; //块对齐(字节);
u16 BitsPerSample; //单个采样数据大小;4位ADPCM,设置为4
// u16 ByteExtraData; //附加的数据字节;2个; 线性PCM,没有这个参数
// u16 ExtraData; //附加的数据,单个采样数据块大小;0X1F9:505字节 线性PCM,没有这个参数
}ChunkFMT;
//fact块
typedef __packed struct
{
u32 ChunkID; //chunk id;这里固定为"fact",即0X74636166;
u32 ChunkSize ; //子集合大小(不包括ID和Size);这里为:4.
u32 NumOfSamples; //采样的数量;
}ChunkFACT;
//data块
typedef __packed struct
{
u32 ChunkID; //chunk id;这里固定为"data",即0X61746164
u32 ChunkSize ; //子集合大小(不包括ID和Size);文件大小-60.
}ChunkDATA;
//wav头
typedef __packed struct
{
ChunkRIFF riff; //riff块
ChunkFMT fmt; //fmt块
//ChunkFACT fact; //fact块 线性PCM,没有这个结构体
ChunkDATA data; //data块
}__WaveHeader;
void VS1053_RecoderInit(u16 agc);
void VS1053_RecoderWavInit(__WaveHeader* wavhead);
void VS1053_LoadPatch(u16 *patch,u16 len);
#endif
作者:DS小龙哥