✅作者简介:热爱科研的嵌入式开发者,修心和技术同步精进
❤欢迎关注我的知乎:对error视而不见
代码获取、问题探讨及文章转载可私信。
☁ 愿你的生命中有够多的云翳,来造就一个美丽的黄昏。
获取更多嵌入式资料可点击链接进群领取,谢谢支持!
点击领取更多详细资料
在许多音频处理应用中,需要从麦克风采集音频数据并实时播放。INMP441是一款高性能的数字麦克风,而STM32系列微控制器具有强大的处理能力和丰富的外设接口,能够很好地实现对INMP441的控制和音频数据的处理与播放。本实验将详细介绍如何使用STM32读取INMP441麦克风的数据并实时播放。
选择一款具备足够处理能力和外设资源的STM32开发板,如STM32F4系列,其具有较高的主频和丰富的定时器、DMA等资源,适合音频数据的采集和处理。
INMP441是一款数字式麦克风,采用I2S(Inter - IC Sound)接口进行数据传输,具有高灵敏度、低噪声等优点。
可以使用一个音频放大器和扬声器组成的音频播放模块,通过STM32的DAC(Digital - to - Analog Converter)或I2S接口将数字音频数据转换为模拟音频信号进行播放。
使用STM32CubeMX进行工程初始化和配置,选择合适的STM32芯片型号,配置I2S接口和相关外设。同时,使用Keil MDK或其他开发工具进行代码的编写、编译和调试。
在STM32CubeMX中,配置I2S接口以实现与INMP441的通信。具体配置步骤如下:
#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);
}
void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s)
{
if (hi2s == &hi2s2)
{
// 在这里可以对采集到的音频数据进行处理
// 例如将数据通过DAC或I2S发送到音频播放模块
HAL_I2S_Transmit_DMA(&hi2s2, (uint16_t *)audio_buffer, BUFFER_SIZE);
}
}
void Error_Handler(void)
{
while(1)
{
// 可以添加错误指示灯闪烁等操作
}
}
main
函数中,首先进行系统时钟、GPIO、DMA和I2S的初始化。然后启动I2S的DMA接收,将音频数据从INMP441读取到audio_buffer
中。HAL_I2S_RxCpltCallback
函数。在该函数中,可以对采集到的音频数据进行处理,例如将数据通过另一个I2S通道或DAC发送到音频播放模块进行播放。Error_Handler
函数,该函数可以添加一些错误指示操作,如指示灯闪烁等。如果使用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通道,并将采集到的音频数据通过该通道发送到音频播放模块。在HAL_I2S_RxCpltCallback
函数中,可以调用HAL_I2S_Transmit_DMA
函数将数据发送出去。
通过本实验,我们实现了使用STM32读取INMP441麦克风的数据并实时播放。首先介绍了硬件准备和连接方法,然后详细说明了软件设计过程,包括I2S接口配置、代码实现和音频播放的实现方式。最后,给出了实验调试的步骤和注意事项。通过这个实验,我们可以深入了解STM32的I2S接口和DMA传输的使用,以及音频数据的采集和处理方法。