单片机/MCU论坛
直播中

申根换

7年用户 1594经验值
私信 关注
[文章]

单片机内部AD实现录音WAV文件

  1.硬件设计
  声音采集采用最简单的驻极体麦克风,将信号进行放大滤波后输入到单片机内部AD端口。电路图如下,第一级运放用于放大,第二级为低通滤波。(电阻电容参数仅供参考)。
2.jpg
  单片机采集声音数据后,将数据以WAV格式保存到SD卡中。
  2.WAV文件格式说明
  WAV(Waveform audio format)是微软与IBM公司所开发的一种声音编码格式,它符合RIFF(Resource Interchange File Format)文件规范,用于保存Windows平台的音频信息资源,被Windows平台及其应用程序所广泛支持。
  每个WAV文件的头四个字节便是“RIFF”。WAV文件由文件头和数据体两大部分组成。其中文件头又分为RIFF区块和FORMAT区块。WAV文件各部分内容及格式见下表。
  RIFF区块
2.jpg
  其中Size是整个文件的长度减去ID和Size的长度
  FORMAT区块
2.jpg
  其中音频格式可以是线性PCM或者ADPCM等,本例中使用线性PCM(非压缩)。
  DATA区块
2.jpg
  其中Size表示音频数据的长度,单位字节。Data音频数据。
  3.软件设计
  人耳朵能识别的声音范围为20-20kHz,若要采集20kHz以内频率的声音信号,则采样率至少要40kHz,数据量比较大,单片机资源受限时,可能无法完成数据的处理和存储。
  一般人说话(不唱歌)频率:男声50-500Hz,女声100-1kHz。考虑到谐波以及奈奎斯特采样定理的要求,8k采样率可以满足要求。
  这也是很多数字电话中的采样率。实际测试8k采样率能够比较清楚的还原语音。采样位数可以是8bits或16bits,单片机内部AD为12位,占2个字节,可以当做16位数据。
  为了减小数据量,舍去AD低4位,当做8位数据处理,实际测试8位数据效果可以。
  系统初始化
  使能单片机内部AD,通过定时器事件来触发AD采样,以保证采样率准确,同时使能DMA通道,降低CPU使用率。使能SPI接口用于驱动SD卡,并移植FatFs文件系统。具有步骤不在这里详细介绍,可参考公众化之前的文章。
  程序编写
  AD采样采用双缓冲的方式,当DMA完成缓冲区1后,将DMA的目标地址设置到缓冲区2继续采样,同时将缓冲区1的数据保存的文件,这样交替保存两个缓冲区数据,保证采样数据的连续性。
  将音频数据之前的数据定义为一个结构体,初始化并保存在文件开头,其中数据长度部分还未确定,需要最后重新计算后写入。
  //初始化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=0x10; //大小为16个字节
  wavhead-》fmt.AudioFormat=0X01; //0X01,表示PCM;0X11,表示IMA ADPCM
  wavhead-》fmt.NumOfChannels=1; //单声道
  wavhead-》fmt.SampleRate=8000; //8Khz采样率 采样速率
  wavhead-》fmt.ByteRate=8000; //字节速率=采样率*通道数*(ADC位数/8)
  wavhead-》fmt.BlockAlign=1; //块大小,1个字节为一个块
  wavhead-》fmt.BitsPerSample=8; //8位PCM
  wavhead-》data.ChunkID=0X61746164; //“data”
  wavhead-》data.ChunkSize=0; //数据大小,还需要计算
  res=f_open(&file_rec,(const TCHAR*)FileName, FA_CREATE_ALWAYS | FA_WRITE);
  if(res==FR_OK)
  {
  res=f_write(&file_rec,(const void*)(&wavhead),sizeof(WaveHeader),&bw);//写入头数据
  }
  }
  之后每完成一次DMA,将数据保存一次。一般建议以SD卡扇区的整数倍进行写入操作,以提高写入速度。一次性写入的数据越多,平均写入速度越快。
  res=f_write(&file_rec,Databuf,512,&bw);//写入数据
  录音结束后,重新计算文件长度并写入:
  wavhead.riff.ChunkSize=sectorsize*512+36; //整个文件的大小-8;
  wavhead.data.ChunkSize=sectorsize*512; //数据大小
  res = f_lseek(&file_rec,0); //偏移到文件头。
  res = f_write(&file_rec,(const void*)(&wavhead),sizeof(WaveHeader),&bw);//写入头数据
  res = f_close(&file_rec);






原作者:Mr张工 嵌入式技术开发

更多回帖

发帖
×
20
完善资料,
赚取积分