本帖最后由 dirty123 于 2025-11-23 11:29 编辑
本篇实现MICMIC拾音功能。
一.硬件原理
开发板板载两个PDM(脉冲密度调制)微型数字麦克风,在开发板背面边角侧。引脚有PDM_CLK时钟脚和PDM_DATA数字脚,硬件原理如下
此麦克风使用到PDM (脉冲密度调制)和PCM (脉冲编码调制)。PDM信号是高速的单比特流,无法被传统的音频处理器(DSP)或软件直接识别和处理,因此需要转换为标准的PCM格式。PCM作为数字音频领域的绝对标准,几乎所有软件、硬件和文件格式(如WAV、FLAC)都直接支持PCM数据,便于进行滤波、混音、均衡器等复杂的数字信号处理。这个过程本质上是数字信号处理中的采样率转换和数据重构,核心步骤有两步:
采样率转换(抽取 - Decimation)
目的是将极高的PDM采样率(如3.072 MHz)降低到标准PCM采样率(如48 kHz)。这个降低的比率称为“抽取因子”,例如从3.072 MHz到48 kHz的抽取因子是64:1。直接简单地丢弃样本会造成严重的信号失真,因此必须配合第二步的滤波操作。
低通滤波与噪声整形
这是转换的核心。PDM信号中包含的大量高频量化噪声必须被滤除。转换过程通常使用一种高效的CIC(级联积分梳状)滤波器 来实现
。该滤波器会计算一小段时间窗口(例如64个PDM时钟周期)内“1”的密度,并将这个密度值转换成一个多比特的幅度值输出。这样,既完成了数据的转换,也将大部分高频噪声滤除了。
PDM侧重于高效率的模数转换和传输,PCM侧重于通用的处理和兼容性。
二.工程代码准备
1.创建工程
选择外设里音频PDM PCM Audio,如下
2.VSCode创建的工程如下
3.工程代码梳理
1.宏定义与按键事件
(1)参数阈值引脚等宏定义
- /*******************************************************************************
- * Macros
- ********************************************************************************/
- /* Define how many samples in a frame */
- #define FRAME_SIZE (1024)
- /* Noise threshold hysteresis */
- #define THRESHOLD_HYSTERESIS 3u
- /* Volume ratio for noise and print purposes */
- #define VOLUME_RATIO (4*FRAME_SIZE)
- /* Desired sample rate. Typical values: 8/16/22.05/32/44.1/48kHz */
- #define SAMPLE_RATE_HZ 8000u
- /* Decimation Rate of the PDM/PCM block. Typical value is 64 */
- #define DECIMATION_RATE 64u
- /* Audio Subsystem Clock. Typical values depends on the desire sample rate:
- - 8/16/48kHz : 24.576 MHz
- - 22.05/44.1kHz : 22.579 MHz */
- #define AUDIO_SYS_CLOCK_HZ 24576000u
- /* PDM/PCM Pins */
- #define PDM_DATA P10_5
- #define PDM_CLK P10_4
(2)pdm、pcm配置。主要采样率、编码率与模式(立体声,使用左右mic)等。
- /* HAL Config */
- const cyhal_pdm_pcm_cfg_t pdm_pcm_cfg =
- {
- .sample_rate = SAMPLE_RATE_HZ,
- .decimation_rate = DECIMATION_RATE,
- .mode = CYHAL_PDM_PCM_MODE_STEREO,
- .word_length = 16, /* bits */
- .left_gain = 0, /* dB */
- .right_gain = 0, /* dB */
- };
(3)按键中断回调。主要是通过事件置标志位,在主函数调高噪声阈值(noise_threshold)
- void button_isr_handler(void *arg, cyhal_gpio_event_t event)
- {
- (void) arg;
- (void) event;
- button_flag = true;
- }
- cyhal_gpio_callback_data_t cb_data =
- {
- .callback = button_isr_handler,
- .callback_arg = NULL
- };
2.main函数
(1)系统与时钟、串口、用户LED、按键初始化
(2)pdm、pcm初始化。
这里有注册pdm_pcm_isr_handler回调,检查MIC用数据,在回调里置标志位
- void pdm_pcm_isr_handler(void *arg, cyhal_pdm_pcm_event_t event)
- {
- (void) arg;
- (void) event;
- pdm_pcm_flag = true;
- }
(3)主函数实现
检查microphone有数据,计算音量。根据当前音量与比率,打印多少个“-”。根据音量是否超过(噪声)阈值,使用户led来亮/灭。通过按键,来调高(噪声)阈值.实现代码如下
- for(;;)
- {
- /* Check if any microphone has data to process */
- if (pdm_pcm_flag)
- {
- /* Clear the PDM/PCM flag */
- pdm_pcm_flag = 0;
- /* Reset the volume */
- volume = 0;
- /* Calculate the volume by summing the absolute value of all the
- * audio data from a frame */
- for (uint32_t index = 0; index < FRAME_SIZE; index++)
- {
- volume += abs(audio_frame[index]);
- }
- /* Prepare line to report the volume */
- printf("\n\r");
- /* Report the volume */
- for (uint32_t index = 0; index < (volume/VOLUME_RATIO); index++)
- {
- printf("-");
- }
- /* Turn ON the LED when the volume is higher than the threshold */
- if ((volume/VOLUME_RATIO) > noise_threshold)
- {
- cyhal_gpio_write((cyhal_gpio_t) CYBSP_USER_LED, CYBSP_LED_STATE_ON);
- }
- else
- {
- cyhal_gpio_write((cyhal_gpio_t) CYBSP_USER_LED, CYBSP_LED_STATE_OFF);
- }
- /* Setup to read the next frame */
- cyhal_pdm_pcm_read_async(&pdm_pcm, audio_frame, FRAME_SIZE);
- }
- /* Reset the noise threshold if User Button is pressed */
- if (button_flag)
- {
- /* Reset button flag */
- button_flag = false;
- /* Get the current volume and add a hysteresis as the new threshold */
- noise_threshold = (volume/VOLUME_RATIO) + THRESHOLD_HYSTERESIS;
- /* Report the new noise threshold over UART */
- printf("\n\rNoise threshold: %lu\n\r", (unsigned long) noise_threshold);
- }
- cyhal_syspm_sleep();
- }
- }
三.调试测验
1.编译烧录
2.打开串口,查看日志
随着外接声音的高低,出现打印“-”的长短,用户led灯伴随亮/灭。
至此,实现麦克风拾音功能。
|