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);
(APB_I2S, state);
I2S_ClearIntState
int i = I2S_GetRxFIFOCount(APB_I2S);
while (i) {
// do something with these data
--;
i}
}
void audio_input_setup(void)
{
// GPIO & Pin Ctrl
(PDM_PIN_MCLK, IO_SOURCE_PDM_DMIC_MCLK);
PINCTRL_SetPadMux(PDM_PIN_IN, IO_SOURCE_PDM_DMIC_IN);
PINCTRL_SetPadMux(PDM_PIN_IN);
PINCTRL_SelPdmIn
// PDM clock configuration, 3M
(8, 1);
SYSCTRL_SetPdmClkDiv(SYSCTRL_ITEM_APB_PDM, SYSCTRL_CLK_ADC_DIV);
SYSCTRL_SelectTypeAClk
// PDM register configuration
(APB_PDM, 0, 1, 1, 1, 0);
PDM_Config
// I2s configuration, bclk=2.4M, samplerate=16K, data from PDM
(1);
I2S_DataFromPDM(APB_I2S, 5, 75);
I2S_ConfigClk(APB_I2S, 0, 1, 0, 10);
I2S_ConfigIRQ(APB_I2S, 0, 0);
I2S_DMAEnable(APB_I2S, I2S_ROLE_MASTER, I2S_MODE_STANDARD, 0, 1, 0, 1, 24);
I2S_Config
// I2s interruption
(PLATFORM_CB_IRQ_I2S, I2s_isr, 0);
platform_set_irq_callback
// enable I2s and PDM
(APB_I2S);
I2S_ClearRxFIFO(APB_PDM, 1);
PDM_Start(APB_I2S, 0, 1);
I2S_Enable}
上面示例涉及到的关于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];
((aligned (8)));
DMA_Descriptor test __attribute__static uint32_t DMA_cb_isr(void *user_data)
{
uint32_t state = DMA_GetChannelIntState(CHANNEL_ID);
(CHANNEL_ID, state);
DMA_ClearChannelIntState
(CHANNEL_ID, &test);
DMA_EnableChannel
// do something with data in buff
}
void DMA_SetUp(void)
{
(1);
DMA_Reset(PLATFORM_CB_IRQ_DMA, DMA_cb_isr, 0);
platform_set_irq_callback.Next = NULL;
test(&test,
DMA_PreparePeripheral2RAM,
buff,
SYSCTRL_DMA_PDM80,
,
DMA_ADDRESS_INC0 | 1 << 24);
(CHANNEL_ID, &test);
DMA_EnableChannel}
void audio_input_setup(void)
{
//GPIO & Pin Ctrl
(PDM_PIN_MCLK, IO_SOURCE_PDM_DMIC_MCLK);
PINCTRL_SetPadMux(PDM_PIN_IN, IO_SOURCE_PDM_DMIC_IN);
PINCTRL_SetPadMux(PDM_PIN_IN);
PINCTRL_SelPdmIn
// PDM clock configuration, 3M
(8);
SYSCTRL_SetAdcClkDiv(SYSCTRL_ITEM_APB_PDM, SYSCTRL_CLK_ADC_DIV);
SYSCTRL_SelectTypeAClk
// PDM register configuration
(APB_PDM, 0, 1, 1, 0, 0);
PDM_Config(APB_PDM, 1, 0);
PDM_DmaEnable
// DMA setup
();
DMA_SetUp
// enable DMA and PDM
(APB_PDM, 1, 1);
PDM_DmaEnable(APB_PDM, 1);
PDM_Start}
建议采用DMA乒乓搬运的方法进行数据搬运,具体讲解参考手册DMA章节。