数字麦克风PDM信号采集与STM32 I2S接口应用(二)
- 2019 年 10 月 3 日
- 笔记
在使用STM32的数字麦克风I2S接口时,计算采样率让人头疼,芯片手册上没有明确的说法,而手册上的计算方法经过测试却和实验不符。借助搜索引擎,大部分资料都是来自于开发板卖家或开发板论坛,主要是咪头采集然后配置WM89系列解码芯片,然后配合FatFS、MP3解码等模式,主要是讲解I2S录音、存储、放音等。外文资料得到的也寥寥无几,也没有找到讲解STM32数字麦克风配置、计算的文档。加上网上资料转载、抄袭、浅尝辄止的笔记教程,这些更是让检索大海捞针,过程艰辛一言难尽,有些网文三言两语抑或作者都没有搞清楚随手一写,有些是作者搞清楚了但藏着掖着最关键的点没说,有些是跟着别人现成的教程做了一边又写了一篇笔记却没有增加任何新东西,种种缘由搞得技术圈文章氛围差到极点让人恼火。
我的需求是音频特征点检测,所以把声音录制然后传输到PC上是首要任务,经过多次摸索,终于了解一二,现在分享出来方便后来者,恐水平有限有未发现的错误而误导他人,在文尾附上STM32 I2S和PDM采集相关的原厂资料以作参考。
尽管内容庞杂,但争取化繁为简讲清楚数字麦克风单声道模式的PDM信号采集,PDM到PCM解码,以及上位机验证分析如python/matlab脚本读取录音文件和播放,频谱分析等。
1、 STM32 I2S接口标准和数据格式
这些内容参考STM32芯片编程手册,基本上就是4个模式,以及16位32位数据格式,若使用SPI/I2S接口通信还要有MSB、LSB配置。
2、 STM32 I2S采样率的配置
STM32的时钟配置和数据格式决定了音频信号的采样率,如下计算。在使用了ST CUBE MX后,下面的计算过程也省略了,直接配置传感器的数据格式、接口标准、采样率就可以了。
上面是STM32芯片手册给出的信息,在做音频采集时候,发现这和实测效果相差较远,原来这组公式并不适合PDM信号的数字麦克风。PDM麦克风只是利用了STM32的I2S信号时钟和数据线,它的采样只是按照bit采样,每一个sample为1bit,要转换为类似于AD/DA的采样值,还需要进行PDM2PCM转换,ST给出了一个驱动包,可以进行类似转换,可以参考AM3998和UM2372手册。
下面的公式才是PDM麦克风与STM32 I2S接口配合时使用的采样率计算方法,其中FS是PDM bit sample的采样率,DIV是PDM到PCM的抽样因子,经过抽样,把bit sample变为类似于模拟采样的sample。这里的Fs都是指采样sample的速率,虽然PDM输出是以u16或u32方式呈现的,但其采样率是按照bit计算的,一个sample只有1个bit。
例如下面的配置,PDM采样率为32khz,单声道应用,那么PDM2PCM使用64抽取比,则真实的音频采样率为16*2*32khz/64 = 16khz。
/* I2S2 init function */ void MX_I2S2_Init(void) { hi2s2.Instance = SPI2; hi2s2.Init.Mode = I2S_MODE_MASTER_RX; hi2s2.Init.Standard = I2S_STANDARD_MSB; hi2s2.Init.DataFormat = I2S_DATAFORMAT_16B; hi2s2.Init.MCLKOutput = I2S_MCLKOUTPUT_DISABLE; hi2s2.Init.AudioFreq = I2S_AUDIOFREQ_32K; hi2s2.Init.CPOL = I2S_CPOL_LOW; hi2s2.Init.ClockSource = I2S_CLOCK_PLL; hi2s2.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_DISABLE; if (HAL_I2S_Init(&hi2s2) != HAL_OK) { Error_Handler(); } }
此时PDM的时钟频率为32khz*16*2=1024khz,测量结果如下验证了该分析结论。
STM32F407的I2S IP核是按照左右声道来处理音频信号的,所以其SW脚信号频率为32khz。
3、PDM和PCM信号
PDM调制器将缓冲模拟信号转换为串行脉冲密度调制信号,时钟输入( CLK)用于控制PDM调制器。PDM信号无法直接驱动DA进行声音播放,PDM信号要变为声音信号还需要进行下采样,经过一次低通滤波和抽样,然后成为PCM信号。
PDM是一种调制形式,用于表示数字域中的模拟信号。它是1位数字采样的高频数据流。在PDM信号中,脉冲的相对密度对应于模拟信号的幅度。大量的1s对应于高(正)幅度值,而大量的0s对应于低(负)幅度值,交替的1s和0s对应于幅度值0。
PDM转为PCM信号,需要进行滤波和抽取。PDM信号采样率就是I2S的clk时钟频率,可见这是一个高频采样,按bit采样的信号。PCM信号是目标音频的采样率,比如高保真44khz,PDM2PCM的抽样因子M,则M=PDM频率/音频采样率。
ST 提过了PDM2PCM的软件包,可以完成上面的工作。
软件包源码没有开源,使用手册也简洁的让人抓狂,我觉得可能是因为ST更高级的MCU直接带了硬解码,所以对中低端MCU I2S接口的软解码关注度也不够。幸好之前做过信号处理工作,一些概念和内在逻辑能猜个八九不离十,使用起来没有任何难度就上手了,这个软件包使用时需要配置下面几个参数。
1)初始化PDMFilter,包括采样率,低通高通滤波器截止频率,通道个数。
typedef struct { uint16_t Fs; float LP_HZ; float HP_HZ; uint16_t Out_MicChannels; char InternalFilter[34]; } PDMFilter_InitStruct;
2)完成参数初始化后调用滤波器初始化函数。
Void PDM_Filter_Init (PDMFilter_InitStruct * Filter)
3)最后调用下面函数完成PDM2PCM的抽取。
PDM_Filter_XX_XX(uint8_t* data, uint16_t* dataOut, uint16_t MicGain, PDMFilter_InitStruct * Filter)
ST提供了多种PDM2PCM的抽取方法。
int32_t PDM_Filter_64_MSB(uint8_t* data, uint16_t* dataOut, uint16_t MicGain, PDMFilter_InitStruct * Filter); int32_t PDM_Filter_80_MSB(uint8_t* data, uint16_t* dataOut, uint16_t MicGain, PDMFilter_InitStruct * Filter); int32_t PDM_Filter_64_LSB(uint8_t* data, uint16_t* dataOut, uint16_t MicGain, PDMFilter_InitStruct * Filter); int32_t PDM_Filter_80_LSB(uint8_t* data, uint16_t* dataOut, uint16_t MicGain, PDMFilter_InitStruct * Filter);
参考文档:
1、如何将PDM数字麦克风连接到STM32单片机
AN5027 使用STM32 32位Arm® Cortex® MCU连接PDM数字麦克风
2、 PDM audio software decoding on STM32 microcontrollers
3、 UM2372_用于STM32F4_F7_H7的PDM2PCM软件包介绍
STM32Cube PDM2PCM software library for the STM32F4/F7/H7 Series
4、 基于 STM32 I2S 的音频应用开发介绍
尊重原创技术文章,转载请注明。