前言
VS1053是一款利用SPI通讯的音频解码模块,支持大部分的音频文件的解码播放和编码保存
我现在记录下来我的学习经历,仅做参考
注意
如果是录音的话,不要频繁的打开写入关闭你的音频文件,因为这样的话,有可能你的音频文件会写入的不完整,表现是:保存的音频文件很小,而且语速很快,模糊不清。一般是录音时先进入录音模式,再打开你的音频文件,写入wav头,之后不要关闭!继续将读到的音频数据写下去,最后再回去修改wav头中的数据块大小,关闭文件就可以了。
我是先将数据先保存到sdram中,最后结束录音再慢慢将sdram中的数据写入到音频文件中,比如sd卡或者外部Flash中。
正文
第一步当然是如何播放音乐了
思路:先初始化你的SPI及其引脚,SPI通信流程是先发送读命令或者写命令,写的话再发送你要写的VS1053寄存器的地址,再发送你要发送的数据,先是高8位之后是低8位。读的话就是再发送你要读的寄存器地址,再去读,读到的值先是高8位,之后是低8位,组合为16位。注意的是读写的SPI是低速的,还有DCS和CS通信时的电平顺序。
再初始化你的VS1053,硬复位,软复位一下就可以了,硬复位就是再拉高一次RST,然后等待DREQ为高就好了。软复位就是配置VS1053的MODE寄存器和CLOCKF寄存器。配置一下播放的寄存器,比如音量、音效、音速。。。,重新设置一下你的解码时间,最后来直接循环向VS1053发送32字节的音频文件就可以了,VS1053会自动解码你的音频文件并且播放。在发送音频数据的时候可以使用SPI高速模式。
下面是我带ucosiii的相关代码,配合fatfs文件系统。我是先将音频文件先缓存到SDRAM中再播放的。
FATFS fs; /* FatFs文件系统对象 */
FIL fnew; /* 文件对象 */
FRESULT res_flash; /* 文件操作结果 */
UINT fnum; /* 文件成功读写数量 */
//播放音乐
void vs1053_player_song(uint8_t *filepath,unsigned long* file_size )
{
uint32_t i=0;
OS_ERR err;
VS_HD_Reset();
VS_Soft_Reset();
OSTimeDly(5,OS_OPT_TIME_DLY,&err);
// VS_Restart_Play(); //切歌不会出现失真
VS_Set_All();
VS_Reset_DecodeTime();
if(strstr((const char*)filepath,".flac")||strstr((const char*)filepath,".FLAC"))
VS_Load_Patch((u16*)vs1053b_patch,VS1053B_PATCHLEN);
res_flash=f_open(&fnew,(const TCHAR*)filepath,FA_READ);
if(res_flash==0)
{
VS_SPI_SpeedHigh();
while(1)
{
i=0;
res_flash=f_read(&fnew,VS1053_Mem,f_size(&fnew),&fnum);
OSSchedLock(&err);
do
{
if(VS_Send_MusicData(VS1053_Mem+i)==0)
{
i+=32;
}
}
while(i
这是一些具体的函数,网上的资料都有,我放一些关键的出来。
//设定VS10XX播放的音量和高低音
//volx:音量大小(0~254)
void VS_Set_Vol(u8 volx)
{
u16 volt=0; //暂存音量值
volt=254-volx; //取反一下,得到最大值,表示最大的表示
volt<<=8;
volt+=254-volx; //得到音量设置后大小
VS_WR_Cmd(SPI_VOL,volt);//设音量
}
//设定高低音控制
//bfreq:低频上限频率 2~15(单位:10Hz)
//bass:低频增益 0~15(单位:1dB)
//tfreq:高频下限频率 1~15(单位:Khz)
//treble:高频增益 0~15(单位:1.5dB,小于9的时候为负数)
void VS_Set_Bass(u8 bfreq,u8 bass,u8 tfreq,u8 treble)
{
u16 bass_set=0; //暂存音调寄存器值
signed char temp=0;
if(treble==0)temp=0; //变换
else if(treble>8)temp=treble-8;
else temp=treble-9;
bass_set=temp&0X0F; //高音设定
bass_set<<=4;
bass_set+=tfreq&0xf; //高音下限频率
bass_set<<=4;
bass_set+=bass&0xf; //低音设定
bass_set<<=4;
bass_set+=bfreq&0xf; //低音上限
VS_WR_Cmd(SPI_BASS,bass_set); //BASS
}
//设定音效
//eft:0,关闭;1,最小;2,中等;3,最大.
void VS_Set_Effect(u8 eft)
{
u16 temp;
temp=VS_RD_Reg(SPI_MODE); //读取SPI_MODE的内容
if(eft&0X01)temp|=1<<4; //设定LO
else temp&=~(1<<4); //取消LO
if(eft&0X02)temp|=1<<7; //设定HO
else temp&=~(1<<7); //取消HO
VS_WR_Cmd(SPI_MODE,temp); //设定模式
}
//设置播放速度(仅VS1053有效)
//t:0,1,正常速度;2,2倍速度;3,3倍速度;4,4倍速;以此类推
void VS_Set_Speed(u8 t)
{
VS_WRAM_Write(0X1E04,t); //写入播放速度
}
//重设解码时间
void VS_Reset_DecodeTime(void)
{
VS_WR_Cmd(SPI_DECODE_TIME,0x0000);
VS_WR_Cmd(SPI_DECODE_TIME,0x0000);//操作两次
}
//软复位VS10XX
void VS_Soft_Reset(void)
{
u8 retry=0;
while(VS_DREQ_IN==0); //等待软件复位结束
VS_SPI_ReadWriteByte(0Xff); //启动传输
retry=0;
while(VS_RD_Reg(SPI_MODE)!=0x0800) // 软件复位,新模式
{
VS_WR_Cmd(SPI_MODE,0x0804); // 软件复位,新模式
Delay_ms(2);//等待至少1.35ms
if(retry++>100)break;
}
while(VS_DREQ_IN==0);//等待软件复位结束
retry=0;
while(VS_RD_Reg(SPI_CLOCKF)!=0X9800)//设置VS1053的时钟,3倍频 ,1.5xADD
{
VS_WR_Cmd(SPI_CLOCKF,0X9800); //设置VS1053的时钟,3倍频 ,1.5xADD
if(retry++>100)break;
}
Delay_ms(20);
}
//硬复位MP3
//返回1:复位失败;0:复位成功
u8 VS_HD_Reset(void)
{
u8 retry=0;
VS_RST_CLR;
Delay_ms(20);
VS_XDCS_SET;//取消数据传输
VS_XCS_SET;//取消数据传输
VS_RST_SET;
while(VS_DREQ_IN==0&&retry<200)//等待DREQ为高
{
retry++;
delay_us(50);
};
Delay_ms(20);
if(retry>=200)return 1;
else return 0;
}
接下来是如何录音。
先进入录音模式 8K采样率,再将从SPI_HDAT0寄存器中读值保存下来,最后在文件开头加上具体的wav文件头,保存下来就是你录制的音频文件了。
static uint32_t write_time; //用来记录究竟读到了多少的音频数据的标志位
void vs1053_record_start() //开始录音,会听到它的监听音,而这监听音就是你录下的音
{
OS_ERR err;
VS_HD_Reset();
VS_Soft_Reset();
OSTimeDly(5,OS_OPT_TIME_DLY,&err);
recoder_enter_rec_mode(4*1024);
while(VS_RD_Reg(SPI_HDAT1)>>8); //等到buf 较为空闲再开始
}
void vs1053_record_run() //记录一次音频数据,建议33ms左右执行一次
{
uint16_t regval;
uint16_t idx;
regval=VS_RD_Reg(SPI_HDAT1);
if((regval>=256)&&(regval<896))
{
idx=0;
while(idx<512)
{
regval=VS_RD_Reg(SPI_HDAT0);
VS1053_Mem[write_time]=regval&0XFF;
VS1053_Mem[write_time+1]=regval>>8;
idx+=2;
write_time+=2;
}
}
}
void vs1053_record_stop(char* filepath) //停止录音并且保存录音
{
__WaveHeader pWav_Header;
pWav_Header.riff.ChunkID=0X46464952; //"RIFF"
pWav_Header.riff.ChunkSize=write_time+36;
pWav_Header.riff.Format=0X45564157; //"WAVE"
pWav_Header.fmt.ChunkID=0X20746D66; //"fmt "
pWav_Header.fmt.ChunkSize=16; //大小为16个字节
pWav_Header.fmt.AudioFormat=0X01; //0X01,表示PCM;0X01,表示IMA ADPCM
pWav_Header.fmt.NumOfChannels=1; //单声道
pWav_Header.fmt.SampleRate=8000; //8Khz采样率 采样速率
pWav_Header.fmt.ByteRate=pWav_Header.fmt.SampleRate*2;//16位,即2个字节
pWav_Header.fmt.BlockAlign=2; //块大小,2个字节为一个块
pWav_Header.fmt.BitsPerSample=16; //16位PCM
pWav_Header.data.ChunkID=0X61746164; //"data"
pWav_Header.data.ChunkSize=write_time;
f_open(&fnew, (const TCHAR*)filepath, FA_OPEN_ALWAYS| FA_WRITE);
f_write(&fnew,&pWav_Header,sizeof(__WaveHeader),&fnum); //从SDRAN写入SPI_Flash
f_close(&fnew);
f_open(&fnew, (const TCHAR*)filepath, FA_OPEN_ALWAYS| FA_WRITE);
f_lseek(&fnew,f_size(&fnew));
f_write(&fnew,VS1053_Mem,write_time,&fnum);
f_close(&fnew);
VS_HD_Reset(); //硬复位
VS_Soft_Reset(); //软复位
memset(VS1053_Mem,0,write_time);
write_time=0;
}
下面是进入录音模式相关代码
/*----------------- 录音功能函数 --------------------------*/
//VS1053的WAV录音有bug,这个plugin可以修正这个问题
const uint16_t wav_plugin[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 recoder_enter_rec_mode(u16 agc)
{
//如果是IMA ADPCM,采样率计算公式如下:
//采样率=CLKI/256*d;
//假设d=0,并2倍频,外部晶振为12.288M.那么Fc=(2*12288000)/256*6=16Khz
//如果是线性PCM,采样率直接就写采样值
VS_WR_Cmd(SPI_BASS,0x0000);
VS_WR_Cmd(SPI_AICTRL0,8000); //设置采样率
VS_WR_Cmd(SPI_AICTRL1,agc); //设置增益,0,自动增益.1024相当于1倍,512相当于0.5倍,最大值65535=64倍
VS_WR_Cmd(SPI_AICTRL2,0); //设置增益最大值,0,代表最大值65536=64X
VS_WR_Cmd(SPI_AICTRL3,6); //4:线性PCM模式 + 2: 左通道 3: 右通道
VS_WR_Cmd(SPI_CLOCKF,0X2000); //设置VS10XX的时钟,MULT:2倍频;ADD:不允许;CLK:12.288Mhz
VS_WR_Cmd(SPI_MODE,0x1804); //MIC,录音激活
Delay_ms(5); //等待至少1.35ms
VS_Load_Patch((u16*)wav_plugin,40);//VS1053的WAV录音需要patch
}
其中的VS1053_Mem是一个 4MByte的SDRAM的数组作为缓存。如果没有的话,可以自行修改程序,先写入RAM中,再写入SD卡中或外部Flash中。
如果还是不理解的朋友可以看正点原子的教学视频学习,这里只是做一个比较精简的汇总。
附上传送门:https://chuanke.baidu.com/v7206678-245150-1858328.html