STM32--vs1053 WAV录音实现(wav保存在flash)

上一篇文件保存在SD卡中http://blog.csdn.net/zhangjikuan/article/details/48978627

项目中要节约成本,只把录音数据放到flash w25x16中WiFi发送

下面是上层程序

#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"	    
#include "flash.h"    
#define debugmsg
#define RECORDERADDR	(4*1024)
#define DATASIZE  2048
#define READSIZE  1024
//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);	//设置采样率,设置为8Khz
 	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);		//左通道(MIC单声道输入)
	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
}

//初始化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 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;			//数据大小,还需要计算  
}
							    
//显示录音时长
//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)
{	 
 
	u8 rval=0;	  
	u8 *databuf;	
	u16 i=0,j=0;
	u32 sectorsize=0,n=0; 			
	databuf=(u8*)mymalloc(512);		//开辟512字节的内存区域
	if(databuf==NULL)rval=0XFF ;//内存申请失败.
	if(rval==0)
	{	  
		VS_HD_Reset();		   	//硬复位
		VS_Soft_Reset();  		//软复位 
		VS_Set_Vol(220);		//设置音量  			 
		VS_Reset_DecodeTime();	//复位解码时间 	  	 
		VS_SPI_SpeedHigh();	//高速	
		//读取音频数据总长度,文件总长度-8
		SPI_Flash_Read(databuf,RECORDERADDR-44+4,4);
		sectorsize+=databuf[3]<<24;
		sectorsize+=databuf[2]<<16;
		sectorsize+=databuf[1]<<8;
		sectorsize+=databuf[0];
		sectorsize+=8;//文件数据总长度
		while(rval==0)
		{
				SPI_Flash_Read(databuf,RECORDERADDR-44+n*512,512);	
				//printf("sectorsize=%d\r\n",n);
				i=0;
				do//主播放循环
					{  	
					if(VS_Send_MusicData(databuf+i)==0)i+=32;//给VS10XX发送音频数据
					else //显示播放时间	   
					{
						if(j!=VS_Get_DecodeTime())
						{
							#ifdef debugmsg
							recoder_show_time(VS_Get_DecodeTime());
							#endif
							j=VS_Get_DecodeTime();
							LED0=!LED0;//
						}	
					}							
				}while(i<512);//循环发送1024个字节 
			
			n++;
			if(n==((sectorsize-44)/512)+1)
			{
				rval=0;
				#ifdef debugmsg
				printf("the file size=%dkByte\r\n",sectorsize/1024);
				#endif
				break;//读完了.		  
			} 							 
		}
			   	  
	}	
	myfree(databuf);
	return rval;	  	 		  	    
}	 
//录音机
//所有录音文件,均保存在SD卡RECORDER文件夹内.
u8 recoder_play(void)
{
	u8 key;
	u8 rval=0;
	__WaveHeader *wavhead=0;
	u16 sectorsize=0;
	u8 *recbuf;						//数据内存	 
 	u16 w;
	u16 idx=0;	    
	u8 rec_sta=0;					//录音状态
									//[7]:0,没有录音;1,有录音;
									//[6:1]:保留
									//[0]:0,正在录音;1,暂停录音;
 	u8 *pname=0;  
	u32 recsec=0;					//录音时间
 	u8 recagc=4;					//默认增益为4 
	u8 playFlag=0;					//播放标志
 
 	wavhead=(__WaveHeader*)mymalloc(sizeof(__WaveHeader));//开辟__WaveHeader字节的内存区域
	if(wavhead==NULL)rval=1; 
	recbuf=mymalloc(DATASIZE); 	
	if(recbuf==NULL)rval=1;	  		   
	pname=mymalloc(30);					//申请30个字节内存,类似"0:RECORDER/REC00001.wav"
	if(pname==NULL)rval=1;
 	if(rval==0)									//内存申请OK
	{      
 		#ifdef debugmsg
  	recoder_show_time(recsec);				//显示时间
		recoder_show_agc(recagc);				//显示agc
		#endif
		pname[0]=0;								//pname没有任何文件名		 
 	  while(rval==0)
		{		
			key=KEY_Scan(0);
			switch(key)
			{		
				case KEY0_PRES:	//STOP&SAVE
					#ifdef debugmsg
					printf("key0:stop/save is down\r\n");
				  #endif
					if(rec_sta&0X80)//有录音
					{
						wavhead->riff.ChunkSize=sectorsize*DATASIZE+36;	//整个文件的大小-8;
				   	wavhead->data.ChunkSize=sectorsize*DATASIZE;		//数据大小
						SPI_Flash_Write((u8*)wavhead,RECORDERADDR-44,sizeof(__WaveHeader));//写入头数据 44Byte
						#ifdef debugmsg
						printf("save file in flash ok!all file size=%dkByte\r\n",(sectorsize*DATASIZE+44)/1024);
						#endif
						sectorsize=0;
					}
					rec_sta=0;
				 	LED1=1;	 						//关闭DS1	 
					#ifdef debugmsg
					recoder_show_time(recsec);		//显示时间
					#endif
					recsec=0;
					break;	 
				case KEY1_PRES:	//REC/PAUSE
					#ifdef debugmsg
					//printf("key1:rec is down\r\n");
				  #endif
					if(rec_sta&0X01)//原来是暂停,继续录音
					{
						//rec_sta&=0XFE;//取消暂停
					}else if(rec_sta&0X80)//已经在录音了,暂停
					{
						//rec_sta|=0X01;	//暂停
					}else				//还没开始录音 
					{
						recoder_enter_rec_mode(1024*recagc);				
						while(VS_RD_Reg(SPI_HDAT1)>>8);			//等到buf 较为空闲再开始  
	 					rec_sta|=0X80;	//开始录音	 	 
				 		recoder_wav_init(wavhead);				//初始化wav数据	
	 				} 
					break;
			  case WKUP_PRES://播放录音(仅在非录音状态下有效)
					 #ifdef debugmsg
			  	  printf("wk_up:play is down\r\n");
				   #endif
				   if(rec_sta==0)playFlag=1;
					 LED1=1;
				   break;
			} 
///
//读取数据			  
			if(rec_sta==0X80)//已经在录音了
			{
		  	w=VS_RD_Reg(SPI_HDAT1);//看缓冲区中有多少个16位数据	
				if((w>=768)&&(w<896))//SPI_HDAT0是一个1024个16位数据的缓冲区,也就是2048字节,由于下面读取字节要耗时,边读取的同时
				{                    //vs1053还是会把数据放入缓冲区,所以当缓冲区中有768*2字节的数据时开始读取1024*2个字节,不准确读写
	 				idx=0;				   	 //音频做到差不多就行,不需要那么的精确
		  			while(idx<DATASIZE) 	//为什么是768?因为读完后还要存储Flash后再循环过来读取一次,存储Flash耗时较长,所以每次存储后容易丢失数据,因此尽量减少存储次数
					{	                      //所以说存储完要赶紧翻过头来读取数据,不然就会发生溢出数据清零,所以程序扫描过程中的printf对音频影响也很大
			 			w=VS_RD_Reg(SPI_HDAT0);				   	    
		 				recbuf[idx++]=w&0XFF;
						recbuf[idx++]=w>>8;
					}	  		 
					if(sectorsize<=2048*2/DATASIZE*512)//flash未满
					{
						SPI_Flash_Write(recbuf,RECORDERADDR+sectorsize*DATASIZE,DATASIZE);
						sectorsize++;//扇区数增加1,约为16ms	
						//printf("sectorsize=%d\r\n",sectorsize);
					}
					else 
					{
						sectorsize=2048*2/DATASIZE*512;
						printf("err:flash is all,sectorsize=%d\r\n",sectorsize);		
					}	 
				}			
			}else//没有开始录音,按KEY0播放音频
			{								  
				if(playFlag)
				{			
					#ifdef debugmsg
					printf("开始播放:\r\n");		   
					#endif
					rec_play_wav(pname);						//播放pname	
					#ifdef debugmsg
			  	recoder_show_time(recsec);					//显示时间
					recoder_show_agc(recagc);					//显示agc 
					#endif
					playFlag = 0;
 				}
				LED1=0;//DS1长亮
				LED0=1;
			}
/
 			if(recsec!=(sectorsize*16/125))//录音时间显示
			{	   
				LED1=!LED1;//DS1闪烁 
				recsec=sectorsize*16/125;
				#ifdef debugmsg
				recoder_show_time(recsec);//显示时间
				#endif
			}
		}					   
	}	   		   				    
	myfree(wavhead);
	myfree(recbuf);	   
	myfree(pname);
	return rval;
}



























  • 5
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
stm32--tsl256.zip_stm32是一个压缩文件,其中包含了与stm32和tsl256芯片相关的文件和代码。stm32是一种32位的单片机系列,由STMicroelectronics公司推出。它具有高性能、低功耗和丰富的外设接口,适用于各种嵌入式系统应用。tsl256是一种环境光传感器芯片,用于检测周围环境的光照强度。 这个压缩文件可以包含stm32芯片和tsl256传感器之间的通信协议、引脚连接方式、初始化代码以及一些示例应用程序。通过使用这些文件和代码,开发人员可以很方便地在stm32上使用tsl256传感器来测量光照强度。例如,可以通过读取tsl256传感器输出的模拟电压值,转换为相应的光照强度数值。开发人员可以根据需要进行修改和定制,以适应具体的应用场景。 使用stm32--tsl256.zip_stm32文件,开发人员可以节省大量的开发时间和精力。他们无需从零开始编写通信协议和初始化代码,也无需从头研究如何与tsl256传感器进行交互。相反,他们可以直接使用这些提供的文件和代码,快速实现与tsl256传感器的集成,并进行光照强度的测量和应用开发。 总之,stm32--tsl256.zip_stm32是一个方便开发人员在stm32上使用tsl256传感器的压缩文件。它包含了相关的文件和代码,可以用于通信协议、引脚连接、初始化以及示例应用程序。通过使用这些文件和代码,开发人员可以快速、方便地实现stm32上使用tsl256传感器进行光照强度测量和应用开发。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值