单片机学习小组
直播中

陈霞

7年用户 939经验值
私信 关注

如何对STM32cubeMX I2S DMA双缓冲进行配置呢

如何对STM32cubeMX I2S DMA双缓冲进行配置呢?有哪些配置步骤?

回帖(1)

王红梅

2022-1-27 13:45:30
开发测试环境


STM32型号:stm32F401RC
I2S芯片:ES7243
stm32cubeMX 版本:
cubeMX配置方法

1,配置I2S模块;我的是录音模块,所以master RX模式;

1.1 I2S的参数需要根据I2S芯片特性和用户需要来配置。比如通信标准,数据格式和音频采样率…

1.2 DMA 配置

2,配置时钟树。
2.1 mcu使用外部晶振。


I2S 的有些引脚可以灵活选择;我用的硬件

点击生成keil5工程;
修改固件

1,修改stm32f4xx_hal_i2s.c

1,修改函数名为HAL_I2S_Receive_DMA_modiy,增加一个buffer,与已有的buffer形成双缓冲。
2,将函数中的HAL_DMA_Start_IT用DMA双缓冲中断启动函数HAL_DMAEx_MultiBufferStart_IT替换


HAL_StatusTypeDef HAL_I2S_Receive_DMA(I2S_HandleTypeDef *hi2s, uint16_t *pData, uint16_t Size){}
HAL_StatusTypeDef HAL_I2S_Receive_DMA_modiy(I2S_HandleTypeDef *hi2s, uint16_t *pData,uint16_t *pData2, uint16_t Size)
{
  uint32_t tmpreg_cfgr;


  if ((pData == NULL) || (Size == 0U))
  {
    return  HAL_ERROR;
  }


  /* Process Locked */
  __HAL_LOCK(hi2s);


  if (hi2s->State != HAL_I2S_STATE_READY)
  {
    __HAL_UNLOCK(hi2s);
    return HAL_BUSY;
  }


  /* Set state and reset error code */
  hi2s->State = HAL_I2S_STATE_BUSY_RX;
  hi2s->ErrorCode = HAL_I2S_ERROR_NONE;
  hi2s->pRxBuffPtr = pData;


  tmpreg_cfgr = hi2s->Instance->I2SCFGR & (SPI_I2SCFGR_DATLEN | SPI_I2SCFGR_CHLEN);


  if ((tmpreg_cfgr == I2S_DATAFORMAT_24B) || (tmpreg_cfgr == I2S_DATAFORMAT_32B))
  {
    hi2s->RxXferSize = (Size << 1U);
    hi2s->RxXferCount = (Size << 1U);
  }
  else
  {
    hi2s->RxXferSize = Size;
    hi2s->RxXferCount = Size;
  }


  /* Set the I2S Rx DMA Half transfer complete callback */
  hi2s->hdmarx->XferHalfCpltCallback = I2S_DMARxHalfCplt;


  /* Set the I2S Rx DMA transfer complete callback */
  hi2s->hdmarx->XferCpltCallback = I2S_DMARxCplt;


  /* Set the DMA error callback */
  hi2s->hdmarx->XferErrorCallback = I2S_DMAError;


  /* Check if Master Receiver mode is selected */
  if ((hi2s->Instance->I2SCFGR & SPI_I2SCFGR_I2SCFG) == I2S_MODE_MASTER_RX)
  {
    /* Clear the Overrun Flag by a read operation to the SPI_DR register followed by a read
    access to the SPI_SR register. */
    __HAL_I2S_CLEAR_OVRFLAG(hi2s);
  }


  /* Enable the Rx DMA Stream/Channel */
//  if (HAL_OK != HAL_DMA_Start_IT(hi2s->hdmarx, (uint32_t)&hi2s->Instance->DR, (uint32_t)hi2s->pRxBuffPtr,
//                                 hi2s->RxXferSize))
  if (HAL_OK != HAL_DMAEx_MultiBufferStart_IT(hi2s->hdmarx, (uint32_t)&hi2s->Instance->DR, (uint32_t)hi2s->pRxBuffPtr,(uint32_t)pData2,
                                 hi2s->RxXferSize))       


  {
    /* Update SPI error code */
    SET_BIT(hi2s->ErrorCode, HAL_I2S_ERROR_DMA);
    hi2s->State = HAL_I2S_STATE_READY;


    __HAL_UNLOCK(hi2s);
    return HAL_ERROR;
  }


  /* Check if the I2S is already enabled */
  if (HAL_IS_BIT_CLR(hi2s->Instance->I2SCFGR, SPI_I2SCFGR_I2SE))
  {
    /* Enable I2S peripheral */
    __HAL_I2S_ENABLE(hi2s);
  }


  /* Check if the I2S Rx request is already enabled */
  if (HAL_IS_BIT_CLR(hi2s->Instance->CR2, SPI_CR2_RXDMAEN))
  {
    /* Enable Rx DMA Request */
    SET_BIT(hi2s->Instance->CR2, SPI_CR2_RXDMAEN);
  }


  __HAL_UNLOCK(hi2s);
  return HAL_OK;
}


2,修改stm32f4xx_hal_dma_ex.c

2.1 在函数HAL_DMAEx_MultiBufferStart_IT 增加如下函数指针的实例:
/* Current memory buffer used is Memory 1 callback /
hdma->XferCpltCallback = dma_m0_rxcplt_callback; //第一个缓冲区填满后会调用这个函数
/
Current memory buffer used is Memory 0 callback */
hdma->XferM1CpltCallback = dma_m1_rxcplt_callback; //第二个缓冲区填满后会调用这个函数
hdma->XferErrorCallback = XferErrorCallback_app;
2.2. 在函数外增加这3个函数的具体实现;
中断发生后可以在dma_m0_rxcplt_callback,dma_m1_rxcplt_callback中复制I2S DMA数据到用户区用户后续处理。


HAL_StatusTypeDef HAL_DMAEx_MultiBufferStart_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t SecondMemAddress, uint32_t DataLength)
{
  HAL_StatusTypeDef status = HAL_OK;
  
  /* Check the parameters */
  assert_param(IS_DMA_BUFFER_SIZE(DataLength));
  
  /* Memory-to-memory transfer not supported in double buffering mode */
  if (hdma->Init.Direction == DMA_MEMORY_TO_MEMORY)
  {
    hdma->ErrorCode = HAL_DMA_ERROR_NOT_SUPPORTED;
    return HAL_ERROR;
  }
  /* Current memory buffer used is Memory 1 callback */
  hdma->XferCpltCallback   = dma_m0_rxcplt_callback; //第一个缓冲区填满后会调用这个函数
  /* Current memory buffer used is Memory 0 callback */
  hdma->XferM1CpltCallback = dma_m1_rxcplt_callback; //第二个缓冲区填满后会调用这个函数
  hdma->XferErrorCallback = XferErrorCallback_app;  
  /* Check callback functions */
  if ((NULL == hdma->XferCpltCallback) || (NULL == hdma->XferM1CpltCallback) || (NULL == hdma->XferErrorCallback))
  {
    hdma->ErrorCode = HAL_DMA_ERROR_PARAM;
    return HAL_ERROR;
  }
  
  /* Process locked */
  __HAL_LOCK(hdma);
  
  if(HAL_DMA_STATE_READY == hdma->State)
  {
    /* Change DMA peripheral state */
    hdma->State = HAL_DMA_STATE_BUSY;
   
    /* Initialize the error code */
    hdma->ErrorCode = HAL_DMA_ERROR_NONE;
   
    /* Enable the Double buffer mode */
    hdma->Instance->CR |= (uint32_t)DMA_SxCR_DBM;
   
    /* Configure DMA Stream destination address */
    hdma->Instance->M1AR = SecondMemAddress;
   
    /* Configure the source, destination address and the data length */
    DMA_MultiBufferSetConfig(hdma, SrcAddress, DstAddress, DataLength);
   
    /* Clear all flags */
    __HAL_DMA_CLEAR_FLAG (hdma, __HAL_DMA_GET_TC_FLAG_INDEX(hdma));
    __HAL_DMA_CLEAR_FLAG (hdma, __HAL_DMA_GET_HT_FLAG_INDEX(hdma));
    __HAL_DMA_CLEAR_FLAG (hdma, __HAL_DMA_GET_TE_FLAG_INDEX(hdma));
    __HAL_DMA_CLEAR_FLAG (hdma, __HAL_DMA_GET_DME_FLAG_INDEX(hdma));
    __HAL_DMA_CLEAR_FLAG (hdma, __HAL_DMA_GET_FE_FLAG_INDEX(hdma));


    /* Enable Common interrupts*/
    hdma->Instance->CR  |= DMA_IT_TC | DMA_IT_TE | DMA_IT_DME;
    hdma->Instance->FCR |= DMA_IT_FE;
   
    if((hdma->XferHalfCpltCallback != NULL) || (hdma->XferM1HalfCpltCallback != NULL))
    {
      hdma->Instance->CR  |= DMA_IT_HT;
    }
   
    /* Enable the peripheral */
    __HAL_DMA_ENABLE(hdma);
  }
  else
  {     
    /* Process unlocked */
    __HAL_UNLOCK(hdma);          
   
    /* Return error status */
    status = HAL_BUSY;
  }  
  return status;
}


3,在MX_I2S_Init函数里增加

i2S使能;DMA双缓冲配置和使能DMA中断;


static void MX_I2S3_Init(void)
{


  /* USER CODE BEGIN I2S3_Init 0 */


  /* USER CODE END I2S3_Init 0 */


  /* USER CODE BEGIN I2S3_Init 1 */


  /* USER CODE END I2S3_Init 1 */
  hi2s3.Instance = SPI3;
  hi2s3.Init.Mode = I2S_MODE_MASTER_RX;
  hi2s3.Init.Standard = I2S_STANDARD_PHILIPS;
  hi2s3.Init.DataFormat = I2S_DATAFORMAT_16B;
  hi2s3.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;
  hi2s3.Init.AudioFreq = I2S_AUDIOFREQ_16K;
  hi2s3.Init.CPOL = I2S_CPOL_HIGH;
  hi2s3.Init.ClockSource = I2S_CLOCK_PLL;
  hi2s3.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_DISABLE;
  if (HAL_I2S_Init(&hi2s3) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN I2S3_Init 2 */
        //HAL_I2S_Init  include  HAL_I2S_MspInit(hi2s);
                __HAL_I2S_ENABLE(&hi2s3);                                        //使能I2S2       
       
                __HAL_DMA_DISABLE(&hdma_spi3_rx);
       
                HAL_I2S_Receive_DMA_modiy(&hi2s3,(uint16_t*)test_333[0],(uint16_t*)test_333[1],128);
       
                __HAL_DMA_ENABLE(&hdma_spi3_rx);
    __HAL_DMA_ENABLE_IT(&hdma_spi3_rx,DMA_IT_TC|DMA_IT_DME);                             //开启传输完成中断               
       
  /* USER CODE END I2S3_Init 2 */


}
4,测试

编译通过后下载跟踪,可以看到test_333[1],test_333[0]的内存交替刷新。 DMA 中断服务程序dma_m0_rxcplt_callback,dma_m1_rxcplt_callback中的变量在不断增加.
DMA 双缓冲OK!!!

后续将实现:1,将采集到的语音通过u***传到pc,做成u*** microphone。
2,通过5.8G无线模块发送到dongle。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分