• 正文
  • 相关推荐
申请入驻 产业图谱

STM32的MDF外设你了解吗?让单片机可以听到声音

10/10 08:44
346
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

在数字麦克风和一些数字测量系统里,传感器送给单片机的数据,往往还不能直接拿来使用。以 PDM 麦克风为例,数据线每秒可能输出几百万个 0 和 1。想把它们变成可以录音、观察波形的采样值,需要先滤波,再降低采样率。如果这些工作全部由 CPU 完成,就要在数据搬运和滤波计算上持续投入资源。

STM32 的 MDF,就是用来处理这类数字采样数据的硬件外设。它把串行输入、数字滤波、抽取和结果输出组织在一起,配合 DMA,可以持续把处理后的数据送进内存。

这篇文章,博主就从 MDF 的作用开始,沿着一条完整的数据链,介绍内部模块、滤波原理、时钟计算,以及 CubeMX 和 HAL 的使用方法。

   具体配置以 STM32N6、STM32N6570-DK 和对应的 HAL 驱动 为例。其他带 MDF 的 STM32 可以参考这套思路,具体引脚、实例数量和参数范围要对应自己的型号。文中的曲线是理论示意,采样率和缓冲时间是配置计算值。

1、MDF 在整个采集系统里负责什么

MDF 的全称是 Multifunction Digital Filter,多功能数字滤波器。从功能上看,它负责接收已经数字化的数据,并对这些数据进行滤波、抽取、偏移补偿和幅度缩放,最后输出多位采样结果。

MDF 本身没有把模拟电压转换成数字量的模拟输入前端。使用模拟传感器时,这一步由 ADC 或外部调制器完成;使用 PDM 数字麦克风时,声学信号的转换主要在麦克风内部完成。

把它放回系统中,就比较容易理解:

从声音到传感器模拟前端,通过传感器内置的ADC通过MDF接收和数字滤波,之后单片机把采样结果存入内存。

对于数字麦克风,传感器、模拟前端和调制器通常集成在器件内部,STM32 接到的是时钟与数据接口。MDF 的用途也不局限于音频。外部 Σ-Δ 调制器的数据处理、多通道数字测量,以及片内 ADC 数据的进一步滤波,都属于它可以参与的场景。STM32N6 的 MDF 提供六路串行数字输入、六条数字滤波路径,以及连接片内 ADC 的接口

STM32N6570-DK开发板中搭载的麦克风芯片是

MP23DB01HPTR我们查阅其芯片手册看其数据输出格式:

这是一颗PDM单比特输出的全向数字麦克风,我们注意其输出格式,如果是模拟电压,我们一般选择ADC 采样;PDM 比特流则选择 MDF, I²S则选I²S通讯。

2、PDM 为什么需要滤波和抽取

PDM 是 Pulse Density Modulation,脉冲密度调制。每次传输只有一个比特,信号幅度由一段时间内的比特分布表达。

对于常见的双极性表示,可以把 0、1 映射为 -1、+1。在短时间内,1 和 0 的数量接近,平均值接近零;1 的占比增加,平均值向正方向变化;0 的占比增加,平均值向负方向变化。

比如在 128 个比特里,假设有 N1 个 1,简单平均可以写成:平均值 = [N1 × (+1) + (128 - N1) × (-1)] / 128 = (2 × N1 - 128) / 128

有 64 个 1 时,平均值为 0;有 96 个 1 时,平均值为 0.5。这个例子只帮助理解幅度编码。实际 MDF 使用的多级滤波器有明确的频率响应,不能把一次简单的比特计数等同于整条硬件滤波链。

假设输入是 2 MHz,希望输出为 15.625 kHz,二者相差 128 倍。

如果只是每隔 128 个比特取一个,拿到的仍然只有 0 和 1,而且高频成分会在降采样时混叠进低频。混叠发生以后,再加一个低通滤波器,也无法把折叠进来的噪声与正常信号分开。

因此,抽取的基本顺序是:先限制频带,再减少样本数量。 MDF 把这个过程分成多级,让不同模块在不同速率下处理数据。

3、沿着数据流认识 MDF 的内部模块

理解配置项时,先知道它作用在哪个位置,会比单独记住参数名容易得多。

下面是功能示意,省略了监测、触发和部分数值处理细节:

 

RCC 为 MDF 选择内核时钟,CKGEN 再经过内部配置,产生处理时钟和公共输出时钟。

CCK0、CCK1 可以给外部器件提供时钟。对于 PDM 麦克风,STM32 输出时钟,麦克风按这个节拍产生数据,MDF 再按约定边沿采样。

因此,CPU 主频、MDF 内核频率和麦克风时钟是不同概念。CPU 跑得很快,并不能直接说明麦克风用了什么采样节拍。

SITF 是 Serial Interface,负责按照配置接收串行数据。

N6 HAL 支持普通 SPI、低频主 SPI,以及 Manchester 编码相关模式。PDM 示例可以采用普通 SPI 接收模式,接收与时钟同步的单比特流。

这里的 SPI 模式描述串行采样机制。PDM 麦克风通常没有传统 SPI 的命令事务和片选接口,不能因为界面出现 SPI,就给它套上普通寄存器读写流程。

SITF 可以选择内部 CCK 时钟,也可以使用外部 CKI。外部器件提供时钟时,输入速率由外部时钟决定。

BSMX 是 Bitstream Matrix,用来把可用比特流连接到滤波路径。

例如:

MdfHandle0.Init.FilterBistream = MDF_BITSTREAM0_RISING;

这表示选择比特流 0 的上升沿路径。HAL 字段名的实际拼写就是 `FilterBistream`。

上升沿或下降沿要结合麦克风的 L/R 设置、数据有效时序和接口定义选择。有数据变化只能说明线路在活动,还不足以证明采样边沿正确。

DFLT 是数字滤波路径。它负责把输入处理成采样结果,并交给 FIFO。

FIFO 给 CPU 或 DMA 留出一定服务时间。处理后的数据仍会持续产生,FIFO 的容量也有限,因此取数据的速度必须跟得上。

CPU 可以读取数据寄存器,DMA 可以把结果直接送入 RAM。二者是不同的搬运方式,后面的缓冲组织也要随之调整。

CIC 的全称是 Cascaded Integrator–Comb,级联积分梳状滤波器。

典型结构由积分器、抽取环节和梳状差分器组成,主要使用加法、减法和延迟,适合处理高采样率输入。单级操作可以写成:

积分器:y[n] = y[n-1] + x[n]

梳状器:z[m] = y[m] - y[m-M]

积分器工作在输入速率,梳状器工作在抽取后的速率。N 级串联形成相应阶数的滤波响应,M 表示差分延迟。

Sinc4 是四阶,Sinc5 是五阶,表示滤波器阶数,与采样结果有多少位没有关系。

抽取比决定该级输出速率,输入 2 MHz、CIC 抽取比R = 32 时,该级输出为 62.5 kHz。最终是不是 62.5 kHz,还要看后面是否继续抽取。改变 R,会同时改变输出速率和滤波特性。只把它理解成“把采样率调低”,容易忽略通带变化与混叠抑制能力。

除此之外还有增益Gain,偏置offset等,总而言之MDF的配置还是非常有讲究的。

例如我们设置抽取比为128的话,已知输入是2 MHZ,那么最后就会得到 2MHZ / 128 = 15.625 KHZ。15.625 kHz 的奈奎斯特频率为 7.8125 kHz,但实际有效通带仍由 CIC、Reshape 和高通共同决定。接近边缘时,幅度可能已经明显下降。

4、连接麦克风并配置 CubeMX

典型 PDM 麦克风需要电源、地、CLK、DATA,以及相应 L/R 设置。以 STM32N6570-DK 的麦克风路径为例:

麦克风时钟 | PE2 / MDF1_CCK0 | STM32 输出

PDM 数据   | PE8 / MDF1_SDI0  | STM32 输入

设置MDF1的时钟为64MHZ,方面后面调整,修改CIC Mode为Sinc4 order。其他默认即可。

SITF设置为Normal SPI格式即可。

5、代码使用

HAL_StatusTypeDef MDF_ReadPcm16(int16_t *sample){    HAL_StatusTypeDef status;    int32_t raw;    if (sample == NULL)        return HAL_ERROR;    status = HAL_MDF_PollForAcq(&MdfHandle0, 20);    if (status != HAL_OK)        return status;    status = HAL_MDF_GetAcqValue(&MdfHandle0, &raw);    if (status == HAL_OK)        *sample = (int16_t)(raw >> 16);    return status;}

初始化后调用 HAL_MDF_AcqStart(&MdfHandle0, &MdfFilterConfig0),再反复读取。结束时调用 HAL_MDF_AcqStop()。

这个例子使用 Not empty 阈值,且传入有效目标指针。PollForAcq()`等待数据条件,GetAcqValue()读取一个结果,后者不会替你等待下一份新数据。

N6 普通采集模式下,`DFLTDR` 的 24 位结果位于 bit31:8,占据 32 位寄存器的高 24 位。

使用的 HAL 直接返回原始寄存器值,没有自动把这 24 位结果右对齐。读取方式可以写成:

/* raw 为 int32_t,保存 GetAcqValue 返回的原始值。 */
int32_t pcm24 = raw >> 8;
int16_t pcm16 = (int16_t)(raw >> 16);

例如 raw = 0x12345600,24 位右对齐结果为 0x00123456,高 16 位为 0x1234。如果把右对齐的 24 位数直接转换成 `int16_t`,截取的是低 16 位,不能当作正常缩小满量程。

DMA 设置 MsbOnly = ENABLE 后,HAL 从数据寄存器的高半字取值。配合 16 位源、目标宽度,内存里的 int16_t 已经是所需高 16 位,后面无需重复右移。

DMA 适合按块搬运结果,减少 CPU 逐点处理的开销。N6 MDF 使用 MDF_DmaConfigTypeDef 描述搬运目标:

#define PCM_DMA_SAMPLES 1024Ustaticint16_t pcm_dma[PCM_DMA_SAMPLES]    __attribute__((aligned(32)));HAL_StatusTypeDef MDF_StartPcmDma(void){    MDF_DmaConfigTypeDef dma = {0};    dma.Address = (uint32_t)(uintptr_t)pcm_dma;    dma.DataLength = sizeof(pcm_dma);    dma.MsbOnly = ENABLE;    return HAL_MDF_AcqStart_DMA(&MdfHandle0, &MdfFilterConfig0, &dma);}

DataLength 按字节填写。1024 个 int16_t 占 2048 字节,所以这里使用 sizeof(pcm_dma)。

最后将我们采集到的数据,通过串口上传到PC端即可。

相关推荐

嵌入式开发兴趣爱好者,热衷于电子信息技术,嵌入式开发,物联网技术等领域开发。