板子为STM32F407,通过VM8978和I2S进行录音,保存在SRAM中。

1.VM8978

VM8978是欧胜推出的一款全功能音频处理器,集成了对麦克风的支持以及扬声器功效。VM8978的控制通过I2S(飞利浦标准)接口同MCU进行音频数据传输,通过两线(MODE=0,即 IIC 接口)或三线(MODE=1)接口进行配置 。将VM8978作为从机,接受LRC(数据左/右对齐时钟)和BCLK(位时钟,用于同步)。

I2S标准模式,数据在跟随 LRC 传输的 BCLK 的第二个上升沿时传输 MSB,其他位一直到 LSB 按顺序传输。传输依赖于字长、 BCLK 频率和采样率,在每个采样的 LSB 和下一个采样的 MSB 之间都应该有未用的 BCLK 周期。飞利浦标准模式的 I2S 数据传输协议如图

图中, fs 即音频信号的采样率,比如 44.1Khz,因此可以知道, LRC 的频率就是音频信号的采样率。

对VM8978的配置,一般按照通用配置

 WM8978_Write_Reg(1,0X1B);   //R1,MICEN设置为1(MIC使能),BIASEN设置为1(模拟器工作),VMIDSEL[1:0]设置为:11(5K)WM8978_Write_Reg(2,0X1B0);    //R2,ROUT1,LOUT1输出使能(耳机可以工作),BOOSTENR,BOOSTENL使能WM8978_Write_Reg(3,0X6C);   //R3,LOUT2,ROUT2输出使能(喇叭工作),RMIX,LMIX使能  WM8978_Write_Reg(6,0);      //R6,MCLK由外部提供WM8978_Write_Reg(43,1<<4);  //R43,INVROUT2反向,驱动喇叭WM8978_Write_Reg(47,1<<8);   //R47设置,PGABOOSTL,左通道MIC获得20倍增益WM8978_Write_Reg(48,1<<8); //R48设置,PGABOOSTR,右通道MIC获得20倍增益WM8978_Write_Reg(49,1<<1); //R49,TSDEN,开启过热保护 WM8978_Write_Reg(10,1<<3); //R10,SOFTMUTE关闭,128x采样,最佳SNR WM8978_Write_Reg(14,1<<3);  //R14,ADC 128x采样率

2.IS2

I2S(Inter IC Sound)总线,是飞利浦为数字音频设备之间的音频数据传输而制定的一种总线标准。

STM32F4 的 I2S 是与 SPI部分共用的,通过设置SPI_I2SCFGR寄存器的I2SMOD 位即可开启 I2S功能,I2S接口使用了几乎与SPI 相同的引脚、标志和中断。
        STM32F4 的 I2S 支持 4种数据和帧格式组合,分别是:1, 将16位数据封装在16 位帧中;2, 将16位数据封装在32 位帧中; 3, 将24位数据封装在32 位帧中;4, 将32位数据封装在32位帧中。
        将 16 位数据封装在32位帧中时,前16 位(MSB)为有效位,16位LSB被强制清零,无需任何软件操作或DMA请求(只需一个读/写操作)。如果应用程序首选DMA,则24位和32位数据帧需要对SPI_DR执行两次CPU 读取或写入操作,或者需要两次DMA操作。 24位的数据帧,硬件会将8位非有效位扩展到带有 0 位的32位。对于所有数据格式和通信标准而言,始终会先发送最高有效位(MSB优先)。
        STM32F4 的 I2S 支持: MSB 对齐(左对齐)标准、 LSB 对齐(右对齐)标准、飞利浦标准和PCM 标准等 4 种音频标准,我们用飞利浦标准。

I2S 飞利浦标准, 使用 WS 信号来指示当前正在发送的数据所属的通道。该信号从当前通道数据的第一个位(MSB)之前的一个时钟开始有效。发送方在时钟信号(CK)的下降沿改变数据,接收方在上升沿读取数据。 WS 信号也在 CK 的下降沿变化。

一般我们需要根据音频采样率(fs,即CK的频率)来计算各个分频器的值,常用的音频采样率有:22.05Khz、44.1Khz、48Khz、96Khz、196Khz等。当 MCK输出使能时,fs频率计算公式如下:
fs= (1000*PLLI2SN/PLLI2SR )/[256*(2*I2SDIV+ODD)]
常用fs 对应系数表如下:

//表格式:采样率/10,PLLI2SN,PLLI2SR,I2SDIV,ODD
const u16 I2S_PSC_TBL[][5]=
{
{800 ,256,5,12,1}, //8Khz 采样率
{1102,429,4,19,0}, //11.025Khz 采样率
{1600,213,2,13,0}, //16Khz 采样率
{2205,429,4, 9,1}, //22.05Khz 采样率
{3200,213,2, 6,1}, //32Khz 采样率
{4410,271,2, 6,0}, //44.1Khz 采样率
{4800,258,3, 3,1}, //48Khz 采样率
{8820,316,2, 3,1}, //88.2Khz 采样率
{9600,344,2, 3,1}, //96Khz 采样率
{17640,361,2,2,0}, //176.4Khz 采样率
{19200,393,2,2,0}, //192Khz 采样率
};

IIS的初始化可参对数据手册完成。

3.主程序部分

#include "recorder.h"
#include "malloc.h"
#include "text.h"
#include "wm8978.h"
#include "i2s.h"
#include "led.h"
#include "lcd.h"
#include "delay.h"
#include "key.h"
#include "exfuns.h"
#include "text.h"
#include "string.h"
#include "fft.h"
#include "sram.h"
u8 *i2srecbuf1;         //I2S DMA接收BUF1
u8 *i2srecbuf2;         //I2S DMA接收BUF2//REC录音FIFO管理参数.
//由于FATFS文件写入时间的不确定性,如果直接在接收中断里面写文件,可能导致某次写入时间过长
//从而引起数据丢失,故加入FIFO控制,以解决此问题.
vu8 i2srecfifordpos = 0;   //FIFO读位置
vu8 i2srecfifowrpos = 0;   //FIFO写位置
u8 *i2srecfifobuf[I2S_RX_FIFO_SIZE];//定义10个录音接收FIFOFIL* f_rec=0;           //录音文件
u32 wavsize;            //wav数据大小(字节数,不包括文件头!!)
u8 isRecording = 0;            //录音状态//读取录音FIFO
//buf:数据缓存区首地址
//返回值:0,没有数据可读;
//      1,读到了1个数据块
u8 rec_i2s_fifo_read(u8 **buf){if(i2srecfifordpos==i2srecfifowrpos)return 0;i2srecfifordpos++;      //读位置加1if(i2srecfifordpos>=I2S_RX_FIFO_SIZE)i2srecfifordpos=0;//归零 *buf=i2srecfifobuf[i2srecfifordpos];return 1;
}
//写一个录音FIFO
//buf:数据缓存区首地址
//返回值: 0,写入成功;
//        1,写入失败
u8 rec_i2s_fifo_write(u8 *buf){u16 i;u8 temp=i2srecfifowrpos;//记录当前写位置i2srecfifowrpos++;     //写位置加1if(i2srecfifowrpos>=I2S_RX_FIFO_SIZE)i2srecfifowrpos=0;//归零  if(i2srecfifordpos==i2srecfifowrpos){i2srecfifowrpos=temp;//还原原来的写位置,此次写入失败return 1;    }for(i=0;i<I2S_RX_DMA_BUF_SIZE;i++)i2srecfifobuf[i2srecfifowrpos][i] = buf[i];//拷贝数据return 0;
} //录音 I2S_DMA接收中断服务函数.在中断里面写入数据
void rec_i2s_dma_rx_callback(void) {      if(isRecording){// 典型的乒乓FIFO操作if(DMA1_Stream3->CR&(1<<19)){rec_i2s_fifo_write(i2srecbuf1);   //i2srecbuf1写入FIFO}else{rec_i2s_fifo_write(i2srecbuf2); //i2srecbuf2写入FIFO}}
}//进入PCM 录音模式
void recoder_enter_rec_mode(void){//2个16位数据,用于录音时I2S Block A主机发送.循环发送0.static const u16 i2splaybuf[2]={0X0000,0X0000};WM8978_ADDA_Cfg(0,1);        //开启ADCWM8978_Input_Cfg(1,1,0); //开启输入通道(MIC&LINE IN)WM8978_Output_Cfg(0,1);        //开启BYPASS输出 WM8978_MIC_Gain(46);       //MIC增益设置 WM8978_SPKvol_Set(0);     //关闭喇叭.WM8978_I2S_Cfg(2,0);     //飞利浦标准,16位数据长度I2S2_Init(I2S_Standard_Phillips,I2S_Mode_MasterTx,I2S_CPOL_Low,I2S_DataFormat_16b);          //飞利浦标准,主机发送,时钟低电平有效,16位帧长度 I2S2ext_Init(I2S_Standard_Phillips,I2S_Mode_SlaveRx,I2S_CPOL_Low,I2S_DataFormat_16b);       //飞利浦标准,从机接收,时钟低电平有效,16位帧长度 I2S2_SampleRate_Set(16000); //设置采样率 I2S2_TX_DMA_Init((u8*)&i2splaybuf[0],(u8*)&i2splaybuf[1],1);        //配置TX DMA DMA1_Stream4->CR&=~(1<<4); //关闭传输完成中断(这里不用中断送数据) I2S2ext_RX_DMA_Init(i2srecbuf1,i2srecbuf2,I2S_RX_DMA_BUF_SIZE/2);     //配置RX DMAi2s_rx_callback=rec_i2s_dma_rx_callback;//回调函数指wav_i2s_dma_callbackI2S_Play_Start(); //开始I2S数据发送(主机)I2S_Rec_Start();     //开始I2S数据接收(从机)
//  recoder_remindmsg_show(0);
}#define WAV_LEN (1024)void real_fft(short wave[], float res[]){int i;static complex signal[WAV_LEN];for(i=0; i<WAV_LEN; i++){signal[i].real = wave[i] / 32767.0;signal[i].imag = 0.0f;}fft(WAV_LEN, signal);c_abs(signal, res, WAV_LEN);
}//WAV录音
void wav_recorder(){ u8 i;u8 *pdatabuf;float *fft_data;fft_data   = mymalloc(SRAMIN,I2S_RX_DMA_BUF_SIZE*sizeof(float));//I2S录音FIFO内存申请i2srecbuf1 = mymalloc(SRAMIN,I2S_RX_DMA_BUF_SIZE);//I2S录音FIFO内存申请i2srecbuf2 = mymalloc(SRAMIN,I2S_RX_DMA_BUF_SIZE);//I2S录音FIFO内存申请for(i=0;i<I2S_RX_FIFO_SIZE;i++){i2srecfifobuf[i] = mymalloc(SRAMIN,I2S_RX_DMA_BUF_SIZE);//I2S录音FIFO内存申请if(i2srecfifobuf[i]==NULL)break;         //申请失败}if(i==I2S_RX_FIFO_SIZE){recoder_enter_rec_mode();  //进入录音模式,此时耳机可以听到咪头采集到的音频   for(;;){if( KEY_Scan(0) == KEY0_PRES){if(isRecording) {isRecording = 0;          //停止录音i2srecfifordpos = 0;     //FIFO读写位置重新归零i2srecfifowrpos = 0;LED1 = 1;}else{isRecording = 1;            //开始录音LED1 = 0;}}if(rec_i2s_fifo_read(&pdatabuf)){int i, j;static unsigned char buffer[40];real_fft((short*)pdatabuf, fft_data);//傅里叶变换for(i=0; i<8; i++){float sum = 0.0f;for(j=0; j<32; j++){sum += fft_data[i*32+j];}sprintf((char*)buffer, "%5d~%5dHz: %6.2f", i*2756, (i+1)*2756, sum);Show_Str(10,120+30*i,240,16,buffer,16,0);}}}        }for(i=0;i<I2S_RX_FIFO_SIZE;i++)myfree(SRAMIN,i2srecfifobuf[i]);  //I2S录音FIFO内存释放
}

定义接收FIFO队列长度为10,每个DMA大小为4096.

STM32:利用VM8978和I2S实现录音的频率分析相关推荐

  1. STM32 利用空闲中断接收数据

    STM32 利用串口空闲中断接收不定长数据 利用cubeMX打开DMA串口接收中断 利用CubeMX打开串口中断 HAL_NVIC_SetPriority(USART2_IRQn, 0, 0); HA ...

  2. STM32利用SPI读写SD卡的程序详解

    STM32利用SPI读写SD卡的一些程序详解 关于SD卡的基础知识这里不做过多陈述,如果有对这方面感兴趣的朋友可以直接百度一下,有很多讲SD卡的文章,这里主要是针对SD卡的读写程序实现做一些详细说明. ...

  3. pyaudio usb playback_利用python工具包pyaudio实现录音

    想训练一套自己的语音识别系统(命令词识别系统),首先得准备一套自己的语料库,发动身边的帅哥美女们帮忙进行录音.Python无所不能,很多工具包能给让我们的工作变的简单.优雅. 今天就给大家分享一下基于 ...

  4. stm32利用TOFSense模块测距教程

    stm32利用TOFSense模块测距教程 TOFSense是什么 简单来讲,TOFSense就是一个激光测距模块,它前面那个小小的黑框框就是它测距的地方即发射激光的地方.它有两种通讯方式:串口和ca ...

  5. STM32利用STM32CubeMX驱动SDRAM

    STM32利用STM32CubeMX驱动SDRAM 本次发布FMC总线驱动SDRAM的博客,为下次发布LTDC外设驱动RGB屏的博客做准备.利用STM32CubeMX和HAL库,生成的工程代码具有很强 ...

  6. STM32利用FATFS文件系统给SD卡读写数据

    STM32利用FATFS文件系统给SD卡读写数据 注:FATFS文件系统官方网址(内含各种函数具体功能及参数配置): FATFS文件系统官方网址 1. 所需.c和.h文件 FATFS中的文件的作用如下 ...

  7. stm32利用mqtt与小程序通信

    stm32利用mqtt与小程序通信 stm32连接服务器端 小程序连接服务器 本项目实现的功能如下:通过stm32连接esp8266,然后利用mqtt协议连接到服务器,同时小程序端也连接到服务器,通过 ...

  8. python短期预测图_Python中利用长短期记忆模型LSTM进行时间序列预测分析

    原文链接:http://tecdat.cn/?p=6663 此示例中,神经网络用于使用2011年4月至2013年2月期间的数据预测都柏林市议会公民办公室的能源消耗. 每日数据是通过总计每天提供的15分 ...

  9. 用MATLAB编程正弦稳态相量图,matlab课程设计--利用MATLAB对线性电路正弦稳态特性分析...

    matlab课程设计--利用MATLAB对线性电路正弦稳态特性分析 课程设计任务书 学生姓名: 专业班级: 指导教师: 刘 新 华 工作单位:信息工程学院 题 目: 利用MATLAB对线性电路正弦稳态 ...

  10. 高职计算机期末质量分析,利用SPASS进行计算机基础考试试卷质量分析

    利用SPASS进行计算机基础考试试卷质量分析 廖丽 (南昌大学,江西南昌,330000) 摘 要:试卷质量分析是评价试卷质量的基本出发点和基本依据.本文运用SPASS软件,采取定性与定量相结合的方法, ...

最新文章

  1. FPGA之道(61)时空变换之时间换空间
  2. 为什么不能在init和dealloc函数中使用accessor方法
  3. python代码300行程序_Python:游戏:300行代码实现俄罗斯方块
  4. MySQL---第07章_单行函数
  5. python 下载拉钩教育AES加密视频
  6. 如何通过 C# 判断一个 路径 是本机还是远程 ?
  7. STM32----摸石头过河系列(三)
  8. java scanner构造函数_Java使用Scanner作为构造函数的参数
  9. 支付系统源码商业版 完美可运营
  10. 下载篇:程序员修炼之道+从小工到专家(高清、免费)
  11. pythonfor循环100次_Python之for循环
  12. PMSM学习笔记1——永磁同步电机的工作原理与数学模型
  13. 2018.8.7 ACM 信息学奥赛之数学一本通 暑假训练总结(生死看淡,不服就干)
  14. 微信公众号里打开链接下载APP
  15. Python二级(04)——Python语言基本语法元素
  16. Dbeaver_ee使用mysql和mongodb
  17. 深度解读Coatue:向加密行业转舵的「老虎环球基金」
  18. 阿里面试必备:100个高频Spring面试题,助你一臂之力!
  19. Google电面狂出难题,春招比秋招难多了……
  20. 2022 中秋节,晒下新浪的月饼,另外留言送红包

热门文章

  1. ssh pem登陆及pem是什么
  2. bin文件转换成hex的方法
  3. ZZNU 1992: 情人节的尴尬
  4. C语言自学记录-Class6-函数
  5. 一个人、一瓶酒、半斤肉饼、两杯饮料
  6. 寡人的难题 (数据结构作业)
  7. 吃白菜一样用micropython玩esp32(三)—— 触摸按键、ADC
  8. 论文写作 之 Related work
  9. Android 拍照、选择图片并裁剪
  10. 一文快速入门三维人脸重建学习