You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

STM32F401RET6使用DMA接收I2S麦克风数据时内存无更新问题

问题描述

我正尝试将STM32F401RET6 Nucleo-64微控制器与Adafruit I2S麦克风以单声道配置对接,为实现该功能,我希望启用DMA功能。

我使用STM32 Cube IDE中的设备配置工具激活I2S3,配置参数如下:

I2S3配置

  • 全双工主模式
  • 传输模式:主接收模式
  • 通信标准:MSB优先(左对齐)
  • 数据与帧格式:32位帧承载24位数据
  • 选定音频频率:48 kHz
  • 时钟源:I2S PLL时钟
  • 时钟极性:低
  • 主时钟输出禁用

DMA配置

  • SPI_RX3,DMA 1 流2,外设到内存,高优先级
  • FIFO阈值设为满,数据宽度为半字,突发大小为单次
  • NVIC设置中已启用DMA 1 流2和SPI3的中断

随后我使用代码生成工具自动生成初始代码,并对初始代码做了如下修改:

  • 设置GPIO_PULL_DOWN,确保三态状态下始终读入0

我已使用示波器采集麦克风输出的数字数据波形,波形显示正常:声音触发麦克风时,最高有效位会出现对应变化,据此我判断问题出在数据读取格式环节。单声道采集所需的100kΩ电阻已按数据手册要求接入电路。

我在main.c程序中调用HAL库函数HAL_I2S_Receive_DMA,尝试填充长度为500的采样数组。

main.c代码如下:

/* Includes ------------------------------------------------------------------*/
#include "main.h"
I2S_HandleTypeDef hi2s3;
DMA_HandleTypeDef hdma_spi3_rx;


/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2S3_Init(void);
static void MX_DMA_Init(void);


int main(void)
{
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_I2S3_Init();
  MX_DMA_Init();

  /* Infinite loop */
  HAL_StatusTypeDef retval; // return value

  volatile int16_t data[500] = {0};
  int16_t data_shifted[500];

  while (1)
  {
      retval = HAL_I2S_Receive_DMA(&hi2s3, data, 500);
//    for(short i=0; i<500; i++){
//        data_shifted[i] = data[i] >> 14;
//    }

  HAL_Delay(1000);
  }
}


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

  /* Configure the main internal regulator output voltage */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);
  /* Initializes the RCC Oscillators according to the specified parameters
     in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = 8;
  RCC_OscInitStruct.PLL.PLLN = 84;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /* Initializes the CPU, AHB and APB buses clocks */
  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_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

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


static void MX_I2S3_Init(void)
{
  hi2s3.Instance = SPI3;
  hi2s3.Init.Mode = I2S_MODE_MASTER_RX;
  hi2s3.Init.Standard = I2S_STANDARD_MSB;
  hi2s3.Init.DataFormat = I2S_DATAFORMAT_24B;
  hi2s3.Init.MCLKOutput = I2S_MCLKOUTPUT_DISABLE;
  hi2s3.Init.AudioFreq = I2S_AUDIOFREQ_48K;
  hi2s3.Init.CPOL = I2S_CPOL_LOW;
  hi2s3.Init.ClockSource = I2S_CLOCK_PLL;
  hi2s3.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_ENABLE;
  if (HAL_I2S_Init(&hi2s3) != HAL_OK)
  {
    Error_Handler();
  }
}

// Enable DMA controller clock
static void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Stream2_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream2_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream2_IRQn);

}

/*GPIO Initialization Function */
static void MX_GPIO_Init(void)
{

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();

}

/* This function is executed in case of error occurrence. */
void Error_Handler(void)
{
  /* Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
}

#ifdef  USE_FULL_ASSERT
/**
  Reports the name of the source file and the source line number
  where the assert_param error has occurred.
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
}
#endif /* USE_FULL_ASSERT */

我对代码进行调试时,在main.c第34行设置断点,发现微控制器内存中采样数组的数值未发生对应更新,所有值始终为0,两个DMA状态标志均被置1。我猜测问题与时序相关,但目前尚未找到解决方法。


问题排查与解决方案

你遇到的问题由几处配置和代码逻辑错误共同导致,按优先级修改即可:

  1. 外设初始化顺序错误
    HAL库要求DMA初始化必须早于使用DMA的外设初始化,你当前的初始化顺序是MX_GPIO_Init() → MX_I2S3_Init() → MX_DMA_Init(),I2S初始化时DMA还未配置,无法完成句柄绑定。调整顺序为MX_DMA_Init() → MX_GPIO_Init() → MX_I2S3_Init()即可。

  2. 缺少I2S引脚复用配置
    你的MX_GPIO_Init()函数仅开启了GPIO端口时钟,完全没有配置I2S3对应的SCK、WS、SD引脚的复用功能。STM32F4的I2S3默认引脚为PC10(SCK)、PC11(WS)、PC12(SD),需要在GPIO初始化中把这三个引脚配置为复用推挽模式,复用功能选择AF6_SPI3。

  3. 缺少中断处理函数实现
    你已经开启了DMA1流2和SPI3的中断,但没有实现对应的中断服务函数,HAL库的DMA传输逻辑无法正常触发。需要在stm32f4xx_it.c中添加对应的中断函数,内部调用HAL库的处理接口:

void DMA1_Stream2_IRQHandler(void)
{
  HAL_DMA_IRQHandler(&hdma_spi3_rx);
}

void SPI3_IRQHandler(void)
{
  HAL_I2S_IRQHandler(&hi2s3);
}
  1. 循环调用DMA接收函数逻辑错误
    你在while循环中每秒重复调用一次HAL_I2S_Receive_DMA(),第一次调用启动DMA传输后,未等传输完成就再次调用会打断正在进行的传输,也无法正确处理传输完成事件。建议添加传输完成标志位,在DMA接收完成回调函数中置位标志,主循环判断标志置位后再处理数据、重新启动DMA传输。

  2. 冗余全双工配置修正(可选)
    你仅使用I2S接收功能,不需要开启全双工模式,关闭全双工配置可避免不必要的逻辑冲突。


内容的提问来源于stack exchange,提问作者IsaacNuketon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 21:27:04