一:VS1053介绍

1.vs1053支持ogg/mp3/aac/wma/midi音频解码,IMA ADPCM编码

2.SPI时序图

二:WAV格式介绍

VS1053 MP3模块支持2种格式的WAV录音: PCM格式或者IMA ADPCM格式,其中PCM(脉冲编码调制)是最基本的WAVE 文件格式,这种文件直接存储采样的声音数
据没有经过任何的压缩。而IAM ADPCM则是使用了压缩算法,压缩比率为4:1。 我们主要讨论 PCM,因为这个最简单。我们将利用 VS1053 实现 16 位,8Khz
采样率的单声道WAV 录音(PCM格式)。

WAVE 文件是由若干个 Chunk 组成的。按照在文件中的出现位置包括:RIFF WAVE Chunk、  Format Chunk、  Fact Chunk(可选)和  Data Chunk。

每个Chunk 由块标识符、数据大小和数据三部分组成,如下所示:

其中块标识符由 4 个 ASCII 码构成,数据大小则标出紧跟其后的数据的长度(单位为字节),注意这个长度不包含块标识符和数据大小的长度,即不包含最前面的 8 个字节。所
以实际Chunk的大小为数据大小加 8。

1.首先,我们来看看 RIFF 块(RIFF WAVE Chunk),该块以“RIFF”作为标示,紧跟wav 文件大小(该大小是 wav 文件的总大小-8),然后数据段为“WAVE”,表示是 wav文件。RIFF块的Chunk 结构如下:

//RIFF块
typedef __packed struct
{ u32 ChunkID;         //chunk id;这里固定为"RIFF",即 0X46464952 u32 ChunkSize ;    //集合大小;文件总大小-8 u32 Format;          //格式;WAVE,即0X45564157
}ChunkRIFF ; 

2.Format块(Format Chunk),该块以“fmt ”作为标示(注意有个空格!),一般情况下,该段的大小为 16个字节,但是有些软件生成的 wav格式,该部分可能有18 个字节,含有 2个字节的附加信息。Format块的Chunk 结构如下:

//fmt块
typedef __packed struct
{ u32 ChunkID;         //chunk id;这里固定为"fmt ",即 0X20746D66 u32 ChunkSize ;    //子集合大小(不包括 ID和Size);这里为:20. u16 AudioFormat;       //音频格式;0X10,表示线性PCM;0X11 表示IMA ADu16 NumOfChannels;  //通道数量;1,表示单声道;2,表示双声道; u32 SampleRate;    //采样率;0X1F40,表示 8Khz u32 ByteRate;     //字节速率;   u16 BlockAlign;    //块对齐(字节);   u16 BitsPerSample;    //单个采样数据大小;4位ADPCM,设置为4
}ChunkFMT;   

3.接下来,我们再看看 Fact 块(Fact Chunk),该块为可选块,以“fact”作为标示,不是每个 WAV 文件都有,在非 PCM 格式的文件中,一般会在 Format 结构后面加入一个
Fact块,该块Chunk 结构如下:

//fact块
typedef __packed struct
{ u32 ChunkID;           //chunk id;这里固定为"fact",即 0X74636166; u32 ChunkSize ;          //子集合大小(不包括ID和Size);这里为:4. u32 DataFactSize;         //数据转换为PCM格式后的大小
}ChunkFACT; 

4.最后,我们来看看数据块(Data Chunk),该块是真正保存wav数据的地方,以“data”
'作为该Chunk 的标示。然后是数据的大小。紧接着就是 wav数据。根据Format Chunk 中的
声道数以及采样bit数,wav数据的 bit位置可以分成如表所示的几种形式:

我们采用的是 16位,单声道,所以每个取样为 2 个字节,低字节在前,高字节在后。数据块的Chunk结构如下:

//data块
typedef __packed struct
{ u32 ChunkID;         //chunk id;这里固定为"data",即0X61746164 u32 ChunkSize ;    //子集合大小(不包括 ID和Size);文件大小-60.
}ChunkDATA; 

三:激活PCM录音 
VS1053激活PCM 录音需要设置的寄存器和相关位如表所示:

通过设置 SCI_MODE 寄存器的 2、12、14 位,来激活 PCM 录音,SCI_MODE 的各位描述见VS1053 的数据手册。SCI_AICTRL0 寄存器用于设置采样率,我们本文档用的是 8K

的采样率,所以设置这个值为 8000即可。SCI_AICTRL1 寄存器用于设置AGC,1024相当于数字增加1,这里建议大家设置 AGC 在4(4*1024)左右比较合适。SCI_AICTRL2 用于设置自动AGC的时候的最大值,当设置为0的时候表示最大 64(65536),这个大家按自己的需要设置即可。最后,SCI_AICTRL3,我们本文档用到的是咪头线性PCM单声道录音,所以设置该寄存器值为6。 
通过这几个寄存器的设置,我们就激活VS1053 的PCM录音了。 不过, VS1053 的PCM录音有一个小BUG,必须通过加载 patch才能解决,如果不加载 patch,那么 VS1053 是不输出PCM数据的,VLSI 提供了我们这个patch,只需要通过软件加载即可。 
 1.读取PCM数据 
在激活了PCM录音之后,SCI_HDAT0和 SCI_HDAT1 有了新的功能。VS1053的PCM采样缓冲区由1024个 16位数据组成,如果SCI_HDAT1 大于0,则说明可以从 SCI_HDAT0
读取至少 SCI_HDAT1 个 16 位数据,如果数据没有被及时读取,那么将溢出,并返回空的状态。

注意,如果 SCI_HDAT1≥896,最好等待缓冲区溢出,以免数据混叠。所以,对我们只需要判断SCI_HDAT1 的值非零,然后从 SCI_HDAT0读取对应长度的数据,即完

成一次数据读取,以此循环,即可实现PCM数据的持续采集。 
四:实现WAV 录音需要经过哪些步骤:   
1) 设置 VS1053 PCM采样参数 
这一步,我们要设置 PCM 的格式(线性 PCM)、采样率(8K)、位数(16 位)、通道数(单声道)等重要参数,同时还要选择采样通道(咪头),还包括AGC设置等。可以
说这里的设置直接决定了我们 wav文件的性质。 
2) 激活 VS1053的 PCM模式,加载patch 
通过激活VS1053的PCM格式,让其开始PCM数据采集,同时,由于VS1053的 BUG,我们需要加载patch,以实现正常的PCM数据接收。 
3) 创建WAV文件,并保存 wav头 
在前两部设置成功之后,我们即可正常的从SCI_HDAT0 读取我们需要的PCM数据了,不过在这之前,我们需要先在创建一个新的文件,并写入 wav 头,然后才能开始写入我们
的PCM数据。 
4) 读取PCM数据 
经过前面几步的处理,这一步就比较简单了,只需要不停的从 SCI_HDAT0读取数据,然后存入 wav 文件即可,不过这里我们还需要做文件大小统计,在最后的时候写入 wav 头
里面。

5) 计算整个文件大小,重新保存 wav头并关闭文件 
在结束录音的时候,我们必须知道本次录音的大小(数据大小和整个文件大小),然后更新 wav 头,重新写入文件,最后因为 FATFS,在文件创建之后,必须调用 f_close,文件
才会真正体现在文件系统里面,否则是不会写入的!所以最后还需要调用 f_close,以保存文件。

五:vs1053代码驱动程序

[cpp] view plaincopy
  1. #include "vs10XX.h"
  2. #include "delay.h"
  3. #include "mmc_sd.h"
  4. #include "spi.h"
  5. #include "usart.h"
  6. //VS10XX默认设置参数
  7. _vs10xx_obj vsset=
  8. {
  9. 210,    //音量:210
  10. 6,      //低音上线 60Hz
  11. 15,     //低音提升 15dB
  12. 10,     //高音下限 10Khz
  13. 15,     //高音提升 10.5dB
  14. 0,      //空间效果
  15. };
  16. //移植时候的接口
  17. //data:要写入的数据
  18. //返回值:读到的数据
  19. u8 VS_SPI_ReadWriteByte(u8 data)
  20. {
  21. return SPI1_ReadWriteByte(data);
  22. }
  23. //SD卡初始化的时候,需要低速
  24. void VS_SPI_SpeedLow(void)
  25. {
  26. SPI1_SetSpeed(SPI_SPEED_64);//设置到低速模式
  27. }
  28. //SD卡正常工作的时候,可以高速了
  29. void VS_SPI_SpeedHigh(void)
  30. {
  31. SPI1_SetSpeed(SPI_SPEED_8);//设置到高速模式
  32. }
  33. //初始化VS10XX的IO口
  34. void VS_Init(void)
  35. {
  36. RCC->APB2ENR|=1<<2;    //PORTA时钟使能
  37. RCC->APB2ENR|=1<<3;    //PORTB时钟使能
  38. GPIOB->CRL&=0XFFFFFFF0;  //PB0 XCS
  39. GPIOB->CRL|=0X00000003;
  40. GPIOA->CRL&=0XFFF00F0F;  //PA3 XRESET;PA1 DQ;PA4 XDCS
  41. GPIOA->CRL|=0X00033080;
  42. GPIOA->ODR|=(1<<3)|(1<<1)|(1<<4);  //PA2,1,4,上拉
  43. GPIOB->ODR|=(1<<0);    //PB0上拉
  44. SPI1_Init();                                //初始化SPI
  45. }
  46. //软复位VS10XX
  47. void VS_Soft_Reset(void)
  48. {
  49. u8 retry=0;
  50. while(VS_DQ==0); //等待软件复位结束
  51. VS_SPI_ReadWriteByte(0Xff);//启动传输
  52. retry=0;
  53. while(VS_RD_Reg(SPI_MODE)!=0x0800)// 软件复位,新模式
  54. {
  55. VS_WR_Cmd(SPI_MODE,0x0804);// 软件复位,新模式
  56. delay_ms(2);//等待至少1.35ms
  57. if(retry++>100)break;
  58. }
  59. while(VS_DQ==0);//等待软件复位结束
  60. retry=0;
  61. while(VS_RD_Reg(SPI_CLOCKF)!=0X9800)//设置VS10XX的时钟,3倍频 ,1.5xADD
  62. {
  63. VS_WR_Cmd(SPI_CLOCKF,0X9800);//设置VS10XX的时钟,3倍频 ,1.5xADD
  64. if(retry++>100)break;
  65. }
  66. delay_ms(20);
  67. }
  68. //硬复位MP3
  69. //返回1:复位失败;0:复位成功
  70. u8 VS_HD_Reset(void)
  71. {
  72. u8 retry=0;
  73. VS_RST=0;
  74. delay_ms(20);
  75. VS_XDCS=1;//取消数据传输
  76. VS_XCS=1;//取消数据传输
  77. VS_RST=1;
  78. while(VS_DQ==0&&retry<200)//等待DREQ为高
  79. {
  80. retry++;
  81. delay_us(50);
  82. };
  83. delay_ms(20);
  84. if(retry>=200)return 1;
  85. else return 0;
  86. }
  87. //正弦测试
  88. void VS_Sine_Test(void)
  89. {
  90. VS_HD_Reset();
  91. VS_WR_Cmd(0x0b,0X2020);   //设置音量
  92. VS_WR_Cmd(SPI_MODE,0x0820);//进入VS10XX的测试模式
  93. while(VS_DQ==0);     //等待DREQ为高
  94. //printf("mode sin:%x\n",VS_RD_Reg(SPI_MODE));
  95. //向VS10XX发送正弦测试命令:0x53 0xef 0x6e n 0x00 0x00 0x00 0x00
  96. //其中n = 0x24, 设定VS10XX所产生的正弦波的频率值,具体计算方法见VS10XX的datasheet
  97. VS_SPI_SpeedLow();//低速
  98. VS_XDCS=0;//选中数据传输
  99. VS_SPI_ReadWriteByte(0x53);
  100. VS_SPI_ReadWriteByte(0xef);
  101. VS_SPI_ReadWriteByte(0x6e);
  102. VS_SPI_ReadWriteByte(0x24);
  103. VS_SPI_ReadWriteByte(0x00);
  104. VS_SPI_ReadWriteByte(0x00);
  105. VS_SPI_ReadWriteByte(0x00);
  106. VS_SPI_ReadWriteByte(0x00);
  107. delay_ms(100);
  108. VS_XDCS=1;
  109. //退出正弦测试
  110. VS_XDCS=0;//选中数据传输
  111. VS_SPI_ReadWriteByte(0x45);
  112. VS_SPI_ReadWriteByte(0x78);
  113. VS_SPI_ReadWriteByte(0x69);
  114. VS_SPI_ReadWriteByte(0x74);
  115. VS_SPI_ReadWriteByte(0x00);
  116. VS_SPI_ReadWriteByte(0x00);
  117. VS_SPI_ReadWriteByte(0x00);
  118. VS_SPI_ReadWriteByte(0x00);
  119. delay_ms(100);
  120. VS_XDCS=1;
  121. //再次进入正弦测试并设置n值为0x44,即将正弦波的频率设置为另外的值
  122. VS_XDCS=0;//选中数据传输
  123. VS_SPI_ReadWriteByte(0x53);
  124. VS_SPI_ReadWriteByte(0xef);
  125. VS_SPI_ReadWriteByte(0x6e);
  126. VS_SPI_ReadWriteByte(0x44);
  127. VS_SPI_ReadWriteByte(0x00);
  128. VS_SPI_ReadWriteByte(0x00);
  129. VS_SPI_ReadWriteByte(0x00);
  130. VS_SPI_ReadWriteByte(0x00);
  131. delay_ms(100);
  132. VS_XDCS=1;
  133. //退出正弦测试
  134. VS_XDCS=0;//选中数据传输
  135. VS_SPI_ReadWriteByte(0x45);
  136. VS_SPI_ReadWriteByte(0x78);
  137. VS_SPI_ReadWriteByte(0x69);
  138. VS_SPI_ReadWriteByte(0x74);
  139. VS_SPI_ReadWriteByte(0x00);
  140. VS_SPI_ReadWriteByte(0x00);
  141. VS_SPI_ReadWriteByte(0x00);
  142. VS_SPI_ReadWriteByte(0x00);
  143. delay_ms(100);
  144. VS_XDCS=1;
  145. }
  146. //ram 测试
  147. //返回值:RAM测试结果
  148. // VS1003如果得到的值为0x807F,则表明完好;VS1053为0X83FF.
  149. u16 VS_Ram_Test(void)
  150. {
  151. VS_HD_Reset();
  152. VS_WR_Cmd(SPI_MODE,0x0820);// 进入VS10XX的测试模式
  153. while (VS_DQ==0); // 等待DREQ为高
  154. VS_SPI_SpeedLow();//低速
  155. VS_XDCS=0;                  // xDCS = 1,选择VS10XX的数据接口
  156. VS_SPI_ReadWriteByte(0x4d);
  157. VS_SPI_ReadWriteByte(0xea);
  158. VS_SPI_ReadWriteByte(0x6d);
  159. VS_SPI_ReadWriteByte(0x54);
  160. VS_SPI_ReadWriteByte(0x00);
  161. VS_SPI_ReadWriteByte(0x00);
  162. VS_SPI_ReadWriteByte(0x00);
  163. VS_SPI_ReadWriteByte(0x00);
  164. VS_XDCS=1;
  165. delay_ms(150);
  166. return VS_RD_Reg(SPI_HDAT0);// VS1003如果得到的值为0x807F,则表明完好;VS1053为0X83FF.;
  167. }
  168. //向VS10XX写命令
  169. //address:命令地址
  170. //data:命令数据
  171. void VS_WR_Cmd(u8 address,u16 data)
  172. {
  173. while(VS_DQ==0);//等待空闲
  174. VS_SPI_SpeedLow();//低速
  175. VS_XDCS=1;
  176. VS_XCS=0;
  177. VS_SPI_ReadWriteByte(VS_WRITE_COMMAND);//发送VS10XX的写命令
  178. VS_SPI_ReadWriteByte(address); //地址
  179. VS_SPI_ReadWriteByte(data>>8); //发送高八位
  180. VS_SPI_ReadWriteByte(data);  //第八位
  181. VS_XCS=1;
  182. VS_SPI_SpeedHigh();//高速
  183. }
  184. //向VS10XX写数据
  185. //data:要写入的数据
  186. void VS_WR_Data(u8 data)
  187. {
  188. VS_SPI_SpeedHigh();//高速,对VS1003B,最大值不能超过36.864/4Mhz,这里设置为9M
  189. VS_XDCS=0;
  190. VS_SPI_ReadWriteByte(data);
  191. VS_XDCS=1;
  192. }
  193. //读VS10XX的寄存器
  194. //address:寄存器地址
  195. //返回值:读到的值
  196. //注意不要用倍速读取,会出错
  197. u16 VS_RD_Reg(u8 address)
  198. {
  199. u16 temp=0;
  200. while(VS_DQ==0);    //等待空闲
  201. VS_SPI_SpeedLow();//低速
  202. VS_XDCS=1;
  203. VS_XCS=0;
  204. VS_SPI_ReadWriteByte(VS_READ_COMMAND);  //发送VS10XX的读命令
  205. VS_SPI_ReadWriteByte(address);          //地址
  206. temp=VS_SPI_ReadWriteByte(0xff);        //读取高字节
  207. temp=temp<<8;
  208. temp+=VS_SPI_ReadWriteByte(0xff);       //读取低字节
  209. VS_XCS=1;
  210. VS_SPI_SpeedHigh();//高速
  211. return temp;
  212. }
  213. //读取VS10xx的RAM
  214. //addr:RAM地址
  215. //返回值:读到的值
  216. u16 VS_WRAM_Read(u16 addr)
  217. {
  218. u16 res;
  219. VS_WR_Cmd(SPI_WRAMADDR, addr);
  220. res=VS_RD_Reg(SPI_WRAM);
  221. return res;
  222. }
  223. //设置播放速度(仅VS1053有效)
  224. //t:0,1,正常速度;2,2倍速度;3,3倍速度;4,4倍速;以此类推
  225. void VS_Set_Speed(u8 t)
  226. {
  227. VS_WR_Cmd(SPI_WRAMADDR,0X1E04); //速度控制地址
  228. while(VS_DQ==0);                //等待空闲
  229. VS_WR_Cmd(SPI_WRAM,t);          //写入播放速度
  230. }
  231. //FOR WAV HEAD0 :0X7761 HEAD1:0X7665
  232. //FOR MIDI HEAD0 :other info HEAD1:0X4D54
  233. //FOR WMA HEAD0 :data speed HEAD1:0X574D
  234. //FOR MP3 HEAD0 :data speed HEAD1:ID
  235. //比特率预定值,阶层III
  236. const u16 bitrate[2][16]=
  237. {
  238. {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0},
  239. {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0}
  240. };
  241. //返回Kbps的大小
  242. //返回值:得到的码率
  243. u16 VS_Get_HeadInfo(void)
  244. {
  245. unsigned int HEAD0;
  246. unsigned int HEAD1;
  247. HEAD0=VS_RD_Reg(SPI_HDAT0);
  248. HEAD1=VS_RD_Reg(SPI_HDAT1);
  249. //printf("(H0,H1):%x,%x\n",HEAD0,HEAD1);
  250. switch(HEAD1)
  251. {
  252. case 0x7665://WAV格式
  253. case 0X4D54://MIDI格式
  254. case 0X4154://AAC_ADTS
  255. case 0X4144://AAC_ADIF
  256. case 0X4D34://AAC_MP4/M4A
  257. case 0X4F67://OGG
  258. case 0X574D://WMA格式
  259. case 0X664C://FLAC格式
  260. {
  261. printf("HEAD0:%d\n",HEAD0);
  262. HEAD1=HEAD0*2/25;//相当于*8/100
  263. if((HEAD1%10)>5)return HEAD1/10+1;//对小数点第一位四舍五入
  264. else return HEAD1/10;
  265. }
  266. default://MP3格式,仅做了阶层III的表
  267. {
  268. HEAD1>>=3;
  269. HEAD1=HEAD1&0x03;
  270. if(HEAD1==3)HEAD1=1;
  271. else HEAD1=0;
  272. return bitrate[HEAD1][HEAD0>>12];
  273. }
  274. }
  275. }
  276. //得到平均字节数
  277. //返回值:平均字节数速度
  278. u32 VS_Get_ByteRate(void)
  279. {
  280. return VS_WRAM_Read(0X1E05);//平均位速
  281. }
  282. //得到需要填充的数字
  283. //返回值:需要填充的数字
  284. u16 VS_Get_EndFillByte(void)
  285. {
  286. return VS_WRAM_Read(0X1E06);//填充字节
  287. }
  288. //发送一次音频数据
  289. //固定为32字节
  290. //返回值:0,发送成功
  291. //       1,VS10xx不缺数据,本次数据未成功发送
  292. u8 VS_Send_MusicData(u8* buf)
  293. {
  294. u8 n;
  295. if(VS_DQ!=0)  //送数据给VS10XX
  296. {
  297. VS_XDCS=0;
  298. for(n=0;n<32;n++)
  299. {
  300. VS_SPI_ReadWriteByte(buf[n]);
  301. }
  302. VS_XDCS=1;
  303. }else return 1;
  304. return 0;//成功发送了
  305. }
  306. //切歌
  307. //通过此函数切歌,不会出现切换“噪声”
  308. void VS_Restart_Play(void)
  309. {
  310. u16 temp;
  311. u16 i;
  312. u8 n;
  313. u8 vsbuf[32];
  314. for(n=0;n<32;n++)vsbuf[n]=0;//清零
  315. temp=VS_RD_Reg(SPI_MODE);   //读取SPI_MODE的内容
  316. temp|=1<<3;                   //设置SM_CANCEL位
  317. temp|=1<<2;                   //设置SM_LAYER12位,允许播放MP1,MP2
  318. VS_WR_Cmd(SPI_MODE,temp);   //设置取消当前解码指令
  319. for(i=0;i<2048;)         //发送2048个0,期间读取SM_CANCEL位.如果为0,则表示已经取消了当前解码
  320. {
  321. if(VS_Send_MusicData(vsbuf)==0)//每发送32个字节后检测一次
  322. {
  323. i+=32;                      //发送了32个字节
  324. temp=VS_RD_Reg(SPI_MODE);   //读取SPI_MODE的内容
  325. if((temp&(1<<3))==0)break;    //成功取消了
  326. }
  327. }
  328. if(i<2048)//SM_CANCEL正常
  329. {
  330. temp=VS_Get_EndFillByte()&0xff;//读取填充字节
  331. for(n=0;n<32;n++)vsbuf[n]=temp;//填充字节放入数组
  332. for(i=0;i<2052;)
  333. {
  334. if(VS_Send_MusicData(vsbuf)==0)i+=32;//填充
  335. }
  336. }else VS_Soft_Reset();      //SM_CANCEL不成功,坏情况,需要软复位
  337. temp=VS_RD_Reg(SPI_HDAT0);
  338. temp+=VS_RD_Reg(SPI_HDAT1);
  339. if(temp)                    //软复位,还是没有成功取消,放杀手锏,硬复位
  340. {
  341. VS_HD_Reset();          //硬复位
  342. VS_Soft_Reset();        //软复位
  343. }
  344. }
  345. //重设解码时间
  346. void VS_Reset_DecodeTime(void)
  347. {
  348. VS_WR_Cmd(SPI_DECODE_TIME,0x0000);
  349. VS_WR_Cmd(SPI_DECODE_TIME,0x0000);//操作两次
  350. }
  351. //得到mp3的播放时间n sec
  352. //返回值:解码时长
  353. u16 VS_Get_DecodeTime(void)
  354. {
  355. u16 dt=0;
  356. dt=VS_RD_Reg(SPI_DECODE_TIME);
  357. return dt;
  358. }
  359. //vs10xx装载patch.
  360. //patch:patch首地址
  361. //len:patch长度
  362. void VS_Load_Patch(u16 *patch,u16 len)
  363. {
  364. u16 i;
  365. u16 addr, n, val;
  366. for(i=0;i<len;)
  367. {
  368. addr = patch[i++];
  369. n    = patch[i++];
  370. if(n & 0x8000U) //RLE run, replicate n samples
  371. {
  372. n  &= 0x7FFF;
  373. val = patch[i++];
  374. while(n--)VS_WR_Cmd(addr, val);
  375. }else //copy run, copy n sample
  376. {
  377. while(n--)
  378. {
  379. val = patch[i++];
  380. VS_WR_Cmd(addr, val);
  381. }
  382. }
  383. }
  384. }
  385. //设定VS10XX播放的音量和高低音
  386. //volx:音量大小(0~254)
  387. void VS_Set_Vol(u8 volx)
  388. {
  389. u16 volt=0;             //暂存音量值
  390. volt=254-volx;          //取反一下,得到最大值,表示最大的表示
  391. volt<<=8;
  392. volt+=254-volx;         //得到音量设置后大小
  393. VS_WR_Cmd(SPI_VOL,volt);//设音量
  394. }
  395. //设定高低音控制
  396. //bfreq:低频上限频率  2~15(单位:10Hz)
  397. //bass:低频增益         0~15(单位:1dB)
  398. //tfreq:高频下限频率  1~15(单位:Khz)
  399. //treble:高频增益       0~15(单位:1.5dB,小于9的时候为负数)
  400. void VS_Set_Bass(u8 bfreq,u8 bass,u8 tfreq,u8 treble)
  401. {
  402. u16 bass_set=0; //暂存音调寄存器值
  403. signed char temp=0;
  404. if(treble==0)temp=0;            //变换
  405. else if(treble>8)temp=treble-8;
  406. else temp=treble-9;
  407. bass_set=temp&0X0F;             //高音设定
  408. bass_set<<=4;
  409. bass_set+=tfreq&0xf;            //高音下限频率
  410. bass_set<<=4;
  411. bass_set+=bass&0xf;             //低音设定
  412. bass_set<<=4;
  413. bass_set+=bfreq&0xf;            //低音上限
  414. VS_WR_Cmd(SPI_BASS,bass_set);   //BASS
  415. }
  416. //设定音效
  417. //eft:0,关闭;1,最小;2,中等;3,最大.
  418. void VS_Set_Effect(u8 eft)
  419. {
  420. u16 temp;
  421. temp=VS_RD_Reg(SPI_MODE);   //读取SPI_MODE的内容
  422. if(eft&0X01)temp|=1<<4;       //设定LO
  423. else temp&=~(1<<5);           //取消LO
  424. if(eft&0X02)temp|=1<<7;       //设定HO
  425. else temp&=~(1<<7);           //取消HO
  426. VS_WR_Cmd(SPI_MODE,temp);   //设定模式
  427. }
  428. ///
  429. //设置音量,音效等.
  430. void VS_Set_All(void)
  431. {
  432. VS_Set_Vol(vsset.mvol);
  433. VS_Set_Bass(vsset.bflimit,vsset.bass,vsset.tflimit,vsset.treble);
  434. VS_Set_Effect(vsset.effect);
  435. }
六:录音代码
#include "recorder.h"
#include "delay.h"
#include "usart.h"
#include "key.h"
#include "led.h"
//#include "lcd.h"
#include "vs10xx.h"
#include "malloc.h"
#include "ff.h"
#include "exfuns.h"
#include "text.h"     //VS1053的WAV录音有bug,这个plugin可以修正这个问题
const u16 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); //设置采样率,设置为8KhzVS_WR_Cmd(SPI_AICTRL1,agc);      //设置增益,0,自动增益.1024相当于1倍,512相当于0.5倍,最大值65535=64倍    VS_WR_Cmd(SPI_AICTRL2,0);       //设置增益最大值,0,代表最大值65536=64XVS_WR_Cmd(SPI_AICTRL3,6);        //左通道(MIC单声道输入)VS_WR_Cmd(SPI_CLOCKF,0X2000);    //设置VS10XX的时钟,MULT:2倍频;ADD:不允许;CLK:12.288MhzVS_WR_Cmd(SPI_MODE,0x1804);     //MIC,录音激活    delay_ms(5);                  //等待至少1.35ms VS_Load_Patch((u16*)wav_plugin,40);//VS1053的WAV录音需要patch
}//初始化WAV头.
void recoder_wav_init(__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 ADPCMwavhead->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位PCMwavhead->data.ChunkID=0X61746164;   //"data"wavhead->data.ChunkSize=0;            //数据大小,还需要计算
}//显示录音时长
//x,y:地址
//tsec:秒钟数.
void recoder_show_time(u32 tsec)
{   //显示录音时间             printf("TIME:");       printf("%d",tsec/60);   //分钟printf(":");printf("%d\r\n",tsec%60);   //秒钟
}
//通过时间获取文件名
//仅限在SD卡保存,不支持FLASH DISK保存
//组合成:形如"0:RECORDER/REC20120321210633.wav"的文件名
void recoder_new_pathname(u8 *pname)
{    u8 res;                     u16 index=0;while(index<0XFFFF){sprintf((char*)pname,"0:RECORDER/REC%05d.wav",index);res=f_open(ftemp,(const TCHAR*)pname,FA_READ);//尝试打开这个文件if(res==FR_NO_FILE)break;        //该文件名不存在=正是我们需要的.index++;}
}
//显示AGC大小
//x,y:坐标
//agc:增益值 1~15,表示1~15倍;0,表示自动增益
void recoder_show_agc(u8 agc)
{  printf("AGC:    ");        //显示名称,同时清楚上次的显示      if(agc==0)printf("AUTO\r\n"); //自动agc       else printf("%d\r\n",agc);          //显示AGC值
} //播放pname这个wav文件(也可以是MP3等)
u8 rec_play_wav(u8 *pname)
{    FIL* fmp3;u16 br;u8 res,rval=0;     u8 *databuf;             u16 i=0;              fmp3=(FIL*)mymalloc(sizeof(FIL));//申请内存databuf=(u8*)mymalloc(512);      //开辟512字节的内存区域if(databuf==NULL||fmp3==NULL)rval=0XFF ;//内存申请失败.if(rval==0){    VS_HD_Reset();            //硬复位VS_Soft_Reset();       //软复位 VS_Set_Vol(220);      //设置音量               VS_Reset_DecodeTime(); //复位解码时间         res=f_open(fmp3,(const TCHAR*)pname,FA_READ);//打开文件    if(res==0)//打开成功.{ VS_SPI_SpeedHigh();   //高速                           while(rval==0){res=f_read(fmp3,databuf,512,(UINT*)&br);//读出4096个字节  i=0;do//主播放循环{   if(VS_Send_MusicData(databuf+i)==0)i+=32;//给VS10XX发送音频数据else recoder_show_time(VS_Get_DecodeTime());//显示播放时间           }while(i<512);//循环发送4096个字节 if(br!=512||res!=0){rval=0;break;//读完了.         }                              }f_close(fmp3);}else rval=0XFF;//出现错误       } myfree(fmp3);myfree(databuf);return rval;
}
//录音机
//所有录音文件,均保存在SD卡RECORDER文件夹内.
u8 recoder_play(void)
{u8 res;u8 key;u8 rval=0;__WaveHeader *wavhead=0;u32 sectorsize=0;FIL* f_rec=0;                 //文件            DIR recdir;                     //目录u8 *recbuf;                     //数据内存   u16 w;u16 idx=0;      u8 rec_sta=0;                  //录音状态//[7]:0,没有录音;1,有录音;//[6:1]:保留//[0]:0,正在录音;1,暂停录音;u8 *pname=0;u8 timecnt=0;                  //计时器   u32 recsec=0;                  //录音时间u8 recagc=4;                 //默认增益为4 u8 playFlag=0;                    //播放标志while(f_opendir(&recdir,"0:/RECORDER"))//打开录音文件夹{    printf("RECORDER文件夹错误!\r\n");delay_ms(200);                            f_mkdir("0:/RECORDER");             //创建该目录   } f_rec=(FIL *)mymalloc(sizeof(FIL));    //开辟FIL字节的内存区域 if(f_rec==NULL)rval=1;    //申请失败wavhead=(__WaveHeader*)mymalloc(sizeof(__WaveHeader));//开辟__WaveHeader字节的内存区域if(wavhead==NULL)rval=1; recbuf=mymalloc(512);  if(recbuf==NULL)rval=1;             pname=mymalloc(30);                 //申请30个字节内存,类似"0:RECORDER/REC00001.wav"if(pname==NULL)rval=1;if(rval==0)                                 //内存申请OK{      recoder_enter_rec_mode(1024*recagc);             while(VS_RD_Reg(SPI_HDAT1)>>8);           //等到buf 较为空闲再开始  recoder_show_time(recsec);             //显示时间recoder_show_agc(recagc);             //显示agcpname[0]=0;                             //pname没有任何文件名       while(rval==0){key=KEY_Scan(0);switch(key){     case KEY0_PRES: //STOP&SAVEprintf("key0 is down\r\n");if(rec_sta&0X80)//有录音{wavhead->riff.ChunkSize=sectorsize*512+36;   //整个文件的大小-8;wavhead->data.ChunkSize=sectorsize*512;     //数据大小f_lseek(f_rec,0);                         //偏移到文件头.f_write(f_rec,(const void*)wavhead,sizeof(__WaveHeader),&bw);//写入头数据f_close(f_rec);sectorsize=0;}rec_sta=0;recsec=0;LED1=1;                            //关闭DS1      recoder_show_time(recsec);     //显示时间break;     case KEY1_PRES:    //REC/PAUSEprintf("key1 is down\r\n");if(rec_sta&0X01)//原来是暂停,继续录音{rec_sta&=0XFE;//取消暂停}else if(rec_sta&0X80)//已经在录音了,暂停{rec_sta|=0X01; //暂停}else               //还没开始录音 {rec_sta|=0X80;   //开始录音       recoder_new_pathname(pname);           //得到新的名字printf("%s\r\n",pname+11);   //显示当前录音文件名字recoder_wav_init(wavhead);              //初始化wav数据  res=f_open(f_rec,(const TCHAR*)pname, FA_CREATE_ALWAYS | FA_WRITE); if(res)            //文件创建失败{rec_sta=0;    //创建文件失败,不能录音rval=0XFE;    //提示是否存在SD卡}else res=f_write(f_rec,(const void*)wavhead,sizeof(__WaveHeader),&bw);//写入头数据} break;case WKUP_PRES://播放录音(仅在非录音状态下有效)printf("wk_up is down\r\n");if(rec_sta==0)playFlag=1;break;}
///
//读取数据            if(rec_sta==0X80)//已经在录音了{w=VS_RD_Reg(SPI_HDAT1);  if((w>=256)&&(w<896)){idx=0;                     while(idx<512)  //一次读取512字节{     w=VS_RD_Reg(SPI_HDAT0);                       recbuf[idx++]=w&0XFF;recbuf[idx++]=w>>8;}            res=f_write(f_rec,recbuf,512,&bw);//写入文件if(res){printf("err:%d\r\n",res);printf("bw:%d\r\n",bw);break;//写入出错.   }sectorsize++;//扇区数增加1,约为32ms    }          }else//没有开始录音,按KEY0播放音频{                               if(playFlag&&pname[0])//如果wk_up按键被按下,且pname不为空{                printf("播放:");          printf("%s\r\n",pname+11);    //显示当播放的文件名字   rec_play_wav(pname);                     //播放pnamerecoder_enter_rec_mode(1024*recagc);       //重新进入录音模式      while(VS_RD_Reg(SPI_HDAT1)>>8);               //等到buf 较为空闲再开始  recoder_show_time(recsec);                 //显示时间recoder_show_agc(recagc);                 //显示agc playFlag = 0;}delay_ms(5);timecnt++;if((timecnt%20)==0)LED1=!LED1;//DS1闪烁 }
/if(recsec!=(sectorsize*4/125))//录音时间显示{      LED1=!LED1;//DS0闪烁 recsec=sectorsize*4/125;recoder_show_time(recsec);//显示时间}}                     }                                myfree(wavhead);myfree(recbuf);   myfree(f_rec);     myfree(pname);return rval;
}

STM32--vs1053 WAV录音实现(保存在SD卡)相关推荐

  1. 【STM32调试(三)】采集bmp图像保存在SD卡

    将图像保存在SD卡 一.思路 二.移植文件系统 三.保存图片 四.实验结果 一.思路 这里保存的是BMP图像,需要先连接bmp图像的数据格式.在STM32上采集的数据格式是RGB565方便在LCD上显 ...

  2. Android—将Bitmap图片保存到SD卡目录下或者指定目录

    直接上代码就不废话啦 一:保存到SD卡下 [java] view plain copy File file = new File(Environment.getExternalStorageDirec ...

  3. Android将应用调试log信息保存在SD卡

    转载:http://blog.csdn.net/way_ping_li/article/details/8487866 把自己应用的调试信息写入到SD卡中. package com.sdmc.hote ...

  4. Linux笔记(开机自动将kerne log保存到SD卡中)

    有时候为了测试机器的稳定性,需要煲机测试几天的情况,这个时候机器已经封装好,不能再接串口线出来. 为了追溯问题,就需要将log信息保存下来. 于是就需要这样一个功能:系统启动后,自动将kernel的l ...

  5. android把音乐存到sd卡上,如何将音乐文件从原始文件夹保存到SD卡中android

    嗨,我需要将音乐文件保存到SD卡我试过使用下面的代码只有文件夹在SD卡中创建的文件夹没有音乐文件保存任何一个建议我在哪里我做了错误...如何将音乐文件从原始文件夹保存到SD卡中android Fill ...

  6. 绘制STM32最小系统电路原理图、STM32F103读取SD卡的数据

    绘制STM32最小系统电路原理图.STM32F103读取SD卡的数据 文章目录 绘制STM32最小系统电路原理图.STM32F103读取SD卡的数据 1 AltiumDesigner 软件配置 2 A ...

  7. Android相机、相册获取图片显示并保存到SD卡

    如题,这个需求本不是一个很复杂的过程,但是却存在一些隐患,我也是最近在项目中碰到这个问题,将Android通过相机或相册获取图片并最终显示在界面上做了一个小研究,现将一些结果和附上的一个Demo叙述如 ...

  8. esp32-智能语音-录音(保存于SD卡)

    1 //初始化ES8388,更改为双麦 2 ESP_LOGI(TAG, "[ 2 ] Start codec chip"); 3 audio_hal_codec_config_t ...

  9. Android自定义照相机实现(拍照、保存到SD卡,音视频开发工程师需要具备的知识

    @author wwj @date 2013/4/29 */ public class MainActivity extends Activity { private View layout; pri ...

  10. Android 数据访问之External Storage 数据保存在sd卡 demo+笔记

    使用Activity的openFileOutput()方法保存文件,文件是存放在手机空间上,一般手机的存储空间不是很大,存放些小文件还行,如果要存放像视频这样的大文件,是不可行的.对于像视频这样的大文 ...

最新文章

  1. java ee中javamail注解_JavaEE之注解
  2. ExtJS4.2:自定义主题 入门
  3. SAP Spartacus B2B功能,只渲染BodyContent position里的UI
  4. spring react_使用Spring Cloud Gateway保护React式微服务
  5. java 类 加载 初始化_java类的加载与初始化
  6. 齐博cms任意登陆漏洞
  7. LeetCode(530)——二叉搜索树的最小绝对差(JavaScript)
  8. postgres数据库授权失败
  9. java实现动态规划求解给定矩阵的和最大的子数组(矩阵中数字正负均存在)
  10. VS2010上winform打包发布、打包安装程序(超全超详细)
  11. 2022-2027年中国植物染料行业市场全景评估及发展战略规划报告
  12. 抖音·某无关痛痒的信息泄露接口
  13. 【题解】「THUPC 2017」体育成绩统计 / Score
  14. GC暂停时间过长——未关闭Swap
  15. JavaScript 中阶 打地鼠游戏(基础版)
  16. Google地图查地址、经纬度
  17. docker-镜像加速
  18. python遇到错误跳过_python跳过错误
  19. 归一化相关 matlab,matlab – 归一化互相关的基础知识
  20. 回顾历史上第一台计算机,回顾一下,世界上第一台计算机的出现,不忘初心

热门文章

  1. ccproxy使用指南
  2. 百合网创始人慕岩:煽情类广告效果最好 最看不上的商业模式最赚钱
  3. MongoDB实战-分片概念和原理
  4. INT_MAX和INT_MIN的含义和用法
  5. JavaScript运筹帷幄,掌控全局
  6. Windows下获得当前目录下的所有文件的文件名并输出到文件
  7. 传感器中的NC引脚和DNC引脚的区别
  8. python字符串介绍_python字符串详解
  9. ESP32实践FreeRTOS
  10. Laravel 事件监听