PDM协议基础
什么是PDM?
-
PDM(Pulse Density Modulation脉冲密度调制) 是一种通过脉冲密度表示模拟信号幅度的数字调制方式。
-
核心原理:
-
麦克风将模拟信号(声音)转换为 单比特数据流,每个时钟周期输出1位(0或1)。
-
数据流中 1的密度 反映输入信号的幅度(例如:高密度1 = 高电压,低密度1 = 低电压)。
-
PDM vs PCM
特性 | PDM | PCM(脉冲编码调制) |
---|---|---|
数据形式 | 单比特流(1位/时钟周期) | 多比特样本(如16位/样本) |
转换方式 | 直接输出密度调制信号 | 需采样、量化、编码 |
应用场景 | 数字麦克风、低功耗设备 | 音频存储、高保真音频处理 |
PDM数据格式
时钟与数据关系:
时钟与数据关系
-
PDM数据流 由 时钟(CLK) 和 数据(DATA) 信号组成:
-
CLK:主控生成的方波时钟(如2 MHz)。
-
DATA:在每个时钟周期输出1位(0或1),密度反映信号幅度。
-
-
(2) ASCII时序图
CLK : _|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_ (2 MHz方波) DATA : 0 1 0 1 1 1 0 1 1 0 1 1 1 1 0 1 (示例数据流) 采样点: ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ (在CLK上升沿采样DATA)
-
上升沿采样:通常在时钟上升沿读取数据位。
-
数据密度:在固定时间段内,统计1的数量(例如:8个时钟周期内有5个1 → 表示高电平)
PDM麦克风硬件连接
典型电路
PDM麦克风引脚 | 物奇7036连接 | 说明 |
---|---|---|
VDD | 3.3V/1.8V | 电源(需电平匹配) |
GND | GND | 共地 |
CLK | GPIO_PDM_CLK | 主控输出的时钟信号 |
DATA | GPIO_PDM_DATA | 麦克风输出的数据流 |
注意事项
-
时钟频率:常见1–3.2 MHz(需参考麦克风规格书)。
-
电平匹配:确保麦克风与主控的电压域一致(如1.8V或3.3V)。
-
去耦电容:在VDD和GND之间添加0.1μF电容,减少电源噪声。
上手Demo(基于物奇7036)
目标:配置PDM接口,采集数据并转换为PCM格式。
(1) 硬件初始化
#include "wq7036_pdm.h" // 物奇7036的PDM驱动头文件
// 定义PDM缓冲区
#define PDM_BUFFER_SIZE 256
uint16_t pdm_buffer[PDM_BUFFER_SIZE];
int16_t pcm_buffer[PDM_BUFFER_SIZE]; // 转换后的PCM数据
void pdm_init() {
// 1. 使能PDM外设时钟
CLK_EnablePDM(true);
// 2. 配置GPIO复用为PDM功能
GPIO_SetFunc(GPIO_PDM_CLK, GPIO_FUNC_PDM_CLK);
GPIO_SetFunc(GPIO_PDM_DATA, GPIO_FUNC_PDM_DATA);
// 3. 配置PDM控制器
PDM_Config_t pdm_cfg = {
.mode = PDM_MASTER, // 主模式(输出时钟)
.clk_freq = 2000000, // 2 MHz时钟
.sample_rate = 16000, // 16 kHz采样率
.data_width = 16, // 16位数据
.osr = 64, // 过采样率(OSR=64)
.data_edge = PDM_RISING_EDGE // 上升沿采样
};
PDM_Init(&pdm_cfg);
// 4. 配置DMA传输
PDM_SetupDMA(pdm_buffer, PDM_BUFFER_SIZE);
}
(2) PDM转PCM函数
// 简易PDM转PCM(需优化为高效实现)
void pdm_to_pcm(uint16_t *pdm, int16_t *pcm, uint32_t len) {
for (int i = 0; i < len; i++) {
int sum = 0;
// 统计每个OSR周期内的1的数量
for (int j = 0; j < 64; j++) { // OSR=64
sum += (pdm[i] >> j) & 0x1;
}
// 转换为有符号16位PCM(范围:-32768 ~ +32767)
pcm[i] = (int16_t)((sum - 32) * 1024); // 调整增益
}
}
(3) 主程序
int main() {
// 初始化PDM
pdm_init();
// 启动PDM采集
PDM_Start();
while (1) {
// 等待DMA完成传输
if (PDM_DMA_Ready()) {
// 转换PDM到PCM
pdm_to_pcm(pdm_buffer, pcm_buffer, PDM_BUFFER_SIZE);
// 处理PCM数据(例如存储或传输)
process_audio(pcm_buffer, PDM_BUFFER_SIZE);
// 重启DMA传输
PDM_StartDMA(pdm_buffer, PDM_BUFFER_SIZE);
}
}
return 0;
}
4. 调试与验证
(1) 示波器检测
-
CLK信号:确认是否为2MHz方波。
-
DATA信号:发声时应有明显的脉冲密度变化。
(2) 数据打印
在process_audio
函数中打印PCM数据,观察是否随声音变化:
void process_audio(int16_t *pcm, uint32_t len) {
for (int i = 0; i < len; i++) {
printf("%d\n", pcm[i]);
}
}