在使用 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.
例如下面的配置, 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
AN3998 在 STM32 微控制器上的 PDM 软件音频解码
3, UM2372_用于 STM32F4_F7_H7 的 PDM2PCM 软件包介绍
STM32Cube PDM2PCM software library for the STM32F4/F7/H7 Series
4, 基于 STM32 I2S 的音频应用开发介绍
基于 STM32 I2S 的音频应用开发介绍
尊重原创技术文章, 转载请注明.
https://www.cnblogs.com/pingwen/p/11301935.html
来源: https://www.cnblogs.com/pingwen/p/11301935.html