STM32读取麦克风INMP441数据并实时播放

✅作者简介:热爱科研的嵌入式开发者,修心和技术同步精进

❤欢迎关注我的知乎:对error视而不见

代码获取、问题探讨及文章转载可私信。

☁ 愿你的生命中有够多的云翳,来造就一个美丽的黄昏。

获取更多嵌入式资料可点击链接进群领取,谢谢支持!

点击领取更多详细资料

一、引言

在许多音频处理应用中,需要从麦克风采集音频数据并实时播放。INMP441是一款高性能的数字麦克风,而STM32系列微控制器具有强大的处理能力和丰富的外设接口,能够很好地实现对INMP441的控制和音频数据的处理与播放。本实验将详细介绍如何使用STM32读取INMP441麦克风的数据并实时播放。

二、硬件准备

(一)STM32开发板

选择一款具备足够处理能力和外设资源的STM32开发板,如STM32F4系列,其具有较高的主频和丰富的定时器、DMA等资源,适合音频数据的采集和处理。

(二)INMP441麦克风模块

INMP441是一款数字式麦克风,采用I2S(Inter - IC Sound)接口进行数据传输,具有高灵敏度、低噪声等优点。

(三)音频播放模块

可以使用一个音频放大器和扬声器组成的音频播放模块,通过STM32的DAC(Digital - to - Analog Converter)或I2S接口将数字音频数据转换为模拟音频信号进行播放。

(四)硬件连接

  • I2S接口连接:将INMP441的BCLK(位时钟)、WS(字选择)、SD(数据)引脚分别连接到STM32的相应I2S接口引脚。
  • 电源连接:为INMP441和音频播放模块提供合适的电源。
  • 音频输出连接:将STM32的音频输出引脚连接到音频播放模块的输入引脚。

三、软件设计

(一)开发环境搭建

使用STM32CubeMX进行工程初始化和配置,选择合适的STM32芯片型号,配置I2S接口和相关外设。同时,使用Keil MDK或其他开发工具进行代码的编写、编译和调试。

(二)I2S接口配置

在STM32CubeMX中,配置I2S接口以实现与INMP441的通信。具体配置步骤如下:

  1. 选择相应的I2S外设(如I2S2)。
  2. 设置工作模式为“Master Transmit”或“Master Receive”,根据实际情况选择。
  3. 配置时钟源、采样频率、数据长度等参数,使其与INMP441的工作参数匹配。例如,采样频率可以设置为44.1kHz或48kHz,数据长度为16位。
  4. 使能DMA(Direct Memory Access)传输,以提高数据传输效率。

(三)代码实现

1. 初始化代码
#include "stm32f4xx_hal.h"
#include "i2s.h"
#include "dma.h"

// 全局变量
#define BUFFER_SIZE 512
uint16_t audio_buffer[BUFFER_SIZE];

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_I2S2_Init(void);

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_I2S2_Init();

  // 启动I2S接收
  HAL_I2S_Receive_DMA(&hi2s2, (uint16_t *)audio_buffer, BUFFER_SIZE);

  while (1)
  {
    // 主循环可以处理其他任务
  }
}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** 初始化RCC振荡器
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 8;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** 初始化CPU、AHB和APB总线时钟
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}

static void MX_I2S2_Init(void)
{
  hi2s2.Instance = SPI2;
  hi2s2.Init.Mode = I2S_MODE_MASTER_RX;
  hi2s2.Init.Standard = I2S_STANDARD_PHILIPS;
  hi2s2.Init.DataFormat = I2S_DATAFORMAT_16B;
  hi2s2.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;
  hi2s2.Init.AudioFreq = I2S_AUDIOFREQ_44K;
  hi2s2.Init.CPOL = I2S_CPOL_LOW;
  hi2s2.Init.FirstBit = I2S_FIRSTBIT_MSB;
  hi2s2.Init.WSInversion = I2S_WS_INVERSION_DISABLE;
  hi2s2.Init.Data24BitAlignment = I2S_DATA_24BIT_ALIGNMENT_RIGHT;
  hi2s2.Init.MasterKeepIOState = I2S_MASTER_KEEP_IO_STATE_DISABLE;
  if (HAL_I2S_Init(&hi2s2) != HAL_OK)
  {
    Error_Handler();
  }
}

static void MX_DMA_Init(void)
{
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA1 channel3 configuration */
  hdma_spi2_rx.Instance = DMA1_Stream3;
  hdma_spi2_rx.Init.Channel = DMA_CHANNEL_0;
  hdma_spi2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
  hdma_spi2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma_spi2_rx.Init.MemInc = DMA_MINC_ENABLE;
  hdma_spi2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
  hdma_spi2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
  hdma_spi2_rx.Init.Mode = DMA_CIRCULAR;
  hdma_spi2_rx.Init.Priority = DMA_PRIORITY_LOW;
  hdma_spi2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  if (HAL_DMA_Init(&hdma_spi2_rx) != HAL_OK)
  {
    Error_Handler();
  }

  __HAL_LINKDMA(&hi2s2, hdmarx, hdma_spi2_rx);
}

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  __HAL_RCC_GPIOB_CLK_ENABLE();

  /* I2S2 GPIO Configuration
  PB12     ------> I2S2_WS
  PB13     ------> I2S2_CK
  PB15     ------> I2S2_SD
  */
  GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_15;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
2. DMA传输完成回调函数
void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s)
{
  if (hi2s == &hi2s2)
  {
    // 在这里可以对采集到的音频数据进行处理
    // 例如将数据通过DAC或I2S发送到音频播放模块
    HAL_I2S_Transmit_DMA(&hi2s2, (uint16_t *)audio_buffer, BUFFER_SIZE);
  }
}
3. 错误处理函数
void Error_Handler(void)
{
  while(1)
  {
    // 可以添加错误指示灯闪烁等操作
  }
}

(四)代码解释

  1. 初始化部分:在main函数中,首先进行系统时钟、GPIO、DMA和I2S的初始化。然后启动I2S的DMA接收,将音频数据从INMP441读取到audio_buffer中。
  2. DMA传输完成回调函数:当I2S的DMA接收完成时,会触发HAL_I2S_RxCpltCallback函数。在该函数中,可以对采集到的音频数据进行处理,例如将数据通过另一个I2S通道或DAC发送到音频播放模块进行播放。
  3. 错误处理函数:当初始化或其他操作出现错误时,会调用Error_Handler函数,该函数可以添加一些错误指示操作,如指示灯闪烁等。

四、音频播放实现

(一)使用DAC进行音频播放

如果使用DAC进行音频播放,需要在STM32CubeMX中配置DAC外设,并将采集到的音频数据通过DAC转换为模拟音频信号。以下是一个简单的DAC初始化和数据输出示例:

#include "dac.h"

DAC_HandleTypeDef hdac;

static void MX_DAC_Init(void)
{
  DAC_ChannelConfTypeDef sConfig = {0};

  /** DAC Initialization
  */
  hdac.Instance = DAC;
  if (HAL_DAC_Init(&hdac) != HAL_OK)
  {
    Error_Handler();
  }
  /** DAC channel OUT1 configuration
  */
  sConfig.DAC_Trigger = DAC_TRIGGER_NONE;
  sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
  if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
}

void play_audio_through_dac(uint16_t *audio_data, uint32_t length)
{
  HAL_DAC_Start(&hdac, DAC_CHANNEL_1);
  for (uint32_t i = 0; i < length; i++)
  {
    HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, audio_data[i]);
  }
  HAL_DAC_Stop(&hdac, DAC_CHANNEL_1);
}

(二)使用I2S进行音频播放

如果使用I2S进行音频播放,需要配置另一个I2S通道,并将采集到的音频数据通过该通道发送到音频播放模块。在HAL_I2S_RxCpltCallback函数中,可以调用HAL_I2S_Transmit_DMA函数将数据发送出去。

五、实验调试与注意事项

(一)调试步骤

  1. 检查硬件连接是否正确,确保INMP441和音频播放模块的电源、信号引脚连接无误。
  2. 编译并下载代码到STM32开发板,观察是否有错误信息。
  3. 使用示波器或逻辑分析仪观察I2S接口的信号波形,确保数据传输正常。
  4. 检查音频播放模块是否有声音输出,如有问题,检查音频信号的幅度和频率是否符合要求。

(二)注意事项

  1. 确保STM32的I2S接口配置与INMP441的工作参数匹配,包括采样频率、数据长度等。
  2. 注意音频数据的处理和传输,避免数据丢失或失真。可以使用DMA传输提高数据传输效率。
  3. 在音频播放过程中,要注意音频信号的幅度和频率,避免音频失真或产生噪声。

六、总结

通过本实验,我们实现了使用STM32读取INMP441麦克风的数据并实时播放。首先介绍了硬件准备和连接方法,然后详细说明了软件设计过程,包括I2S接口配置、代码实现和音频播放的实现方式。最后,给出了实验调试的步骤和注意事项。通过这个实验,我们可以深入了解STM32的I2S接口和DMA传输的使用,以及音频数据的采集和处理方法。

你可能感兴趣的:(stm32,嵌入式硬件,单片机)