12 PDM简介
PDM全称pulse density modulation,即脉冲密度调制。
PDM模块处理来自外部音频前端(如数字麦克风)的脉冲密度调制信号的输入。该模块生成PDM时钟,支持双通道数据输入。
12.2 使用方法
12.2.1 方法概述
PDM使用方法分为PDM结合I2s使用和PDM数据直接DMA搬运两种。
PDM结合I2s:
1. PDM引脚GPIO配置(时钟、数据)
2. 外部时钟配置,使其处于工作模式
3. 写相应配置寄存器
4. 配置I2s数据源为PDM,配置I2s时钟、寄存器、中断
5. 使能PDM,使能I2s
6. 等待I2s中断产生
7. 读取状态寄存器,读取RX_MEM中数据
8. 传输完毕,关闭PDM和I2S
PDM数据DMA搬运:
1. PDM引脚GPIO配置(时钟、数据)
2. 外部时钟配置,使其处于工作模式
3. 写相应配置寄存器
4. 配置DMA寄存器、中断
5. 使能DMA,使能PDM
6. 等待DMA中断产生
7. 传输完毕,关闭PDM和DMA
其中I2s和DMA相关配置不在本节介绍内容范围内,可参考本手册对应章节。
12.3 编程指南
12.3.2 代码示例
下面以PDM结合I2s使用和PDM数据直接DMA搬运两种方式来展示PDM的具体使用方法。
已知现有mic使用的时钟频率为3M,I2s采样率16K。
12.3.2.1 PDM结合I2s:
#define PDM_PIN_MCLK 28
#define PDM_PIN_IN 29
static uint32_t I2s_isr(void *user_data)
{
uint32_t state = I2S_GetIntState(APB_I2S);
I2S_ClearIntState(APB_I2S, state);
int i = I2S_GetRxFIFOCount(APB_I2S);
while (i) {
// do something with these data
i--;
}
}
void audio_input_setup(void)
{
// GPIO & Pin Ctrl
PINCTRL_SetPadMux(PDM_PIN_MCLK, IO_SOURCE_PDM_DMIC_MCLK);
PINCTRL_SetPadMux(PDM_PIN_IN, IO_SOURCE_PDM_DMIC_IN);
PINCTRL_SelPdmIn(PDM_PIN_IN);
// PDM clock configuration, 3M
SYSCTRL_SetPdmClkDiv(8, 1);
SYSCTRL_SelectTypeAClk(SYSCTRL_ITEM_APB_PDM, SYSCTRL_CLK_ADC_DIV);
// PDM register configuration
PDM_Config(APB_PDM, 0, 1, 1, 1, 0);
// I2s configuration, bclk=2.4M, samplerate=16K, data from PDM
I2S_DataFromPDM(1);
I2S_ConfigClk(APB_I2S, 5, 75);
I2S_ConfigIRQ(APB_I2S, 0, 1, 0, 10);
I2S_DMAEnable(APB_I2S, 0, 0);
I2S_Config(APB_I2S, I2S_ROLE_MASTER, I2S_MODE_STANDARD, 0, 1, 0, 1, 24);
// I2s interruption
platform_set_irq_callback(PLATFORM_CB_IRQ_I2S, I2s_isr, 0);
// enable I2s and PDM
I2S_ClearRxFIFO(APB_I2S);
PDM_Start(APB_PDM, 1);
I2S_Enable(APB_I2S, 0, 1);
}上面示例涉及到的关于I2s配置参考手册的I2s章节:
12.3.2.2 PDM数据DMA搬运
#define PDM_PIN_MCLK 28
#define PDM_PIN_IN 29
#define CHANNEL_ID 0
uint32_t buff[80];
DMA_Descriptor test __attribute__((aligned (8)));
static uint32_t DMA_cb_isr(void *user_data)
{
uint32_t state = DMA_GetChannelIntState(CHANNEL_ID);
DMA_ClearChannelIntState(CHANNEL_ID, state);
DMA_EnableChannel(CHANNEL_ID, &test);
// do something with data in buff
}
void DMA_SetUp(void)
{
DMA_Reset(1);
platform_set_irq_callback(PLATFORM_CB_IRQ_DMA, DMA_cb_isr, 0);
test.Next = NULL;
DMA_PreparePeripheral2RAM(&test,
buff,
SYSCTRL_DMA_PDM,
80,
DMA_ADDRESS_INC,
0 | 1 << 24);
DMA_EnableChannel(CHANNEL_ID, &test);
}
void audio_input_setup(void)
{
//GPIO & Pin Ctrl
PINCTRL_SetPadMux(PDM_PIN_MCLK, IO_SOURCE_PDM_DMIC_MCLK);
PINCTRL_SetPadMux(PDM_PIN_IN, IO_SOURCE_PDM_DMIC_IN);
PINCTRL_SelPdmIn(PDM_PIN_IN);
// PDM clock configuration, 3M
SYSCTRL_SetAdcClkDiv(8);
SYSCTRL_SelectTypeAClk(SYSCTRL_ITEM_APB_PDM, SYSCTRL_CLK_ADC_DIV);
// PDM register configuration
PDM_Config(APB_PDM, 0, 1, 1, 0, 0);
PDM_DmaEnable(APB_PDM, 1, 0);
// DMA setup
DMA_SetUp();
// enable DMA and PDM
PDM_DmaEnable(APB_PDM, 1, 1);
PDM_Start(APB_PDM, 1);
}建议采用DMA乒乓搬运的方法进行数据搬运,具体讲解参考手册DMA章节。