[数字麦克风] 读STSW-STLKT01的AudioLoop例程 看看AudioLoop是如何把麦克风数据通过DAC播放的

littleshrimp   2016-12-25 14:23 楼主

AudioLoop的主要代码都集成在main.c

先来看main.c这个文件

/* Private define ------------------------------------------------------------*/

#define AUDIO_CHANNELS                      1

#define AUDIO_SAMPLING_FREQUENCY                48000

#define AUDIO_IN_BUF_LEN   (AUDIO_CHANNELS*AUDIO_SAMPLING_FREQUENCY/1000)

#define AUDIO_OUT_BUF_LEN  (AUDIO_IN_BUF_LEN*8)

AUDIO_CHANNELS定义了麦克风输入使用了1个通道

AUDIO_SAMPLING_FREQUENCY定义了输入和输出的采样率都是48KHz

AUDIO_IN_BUF_LEN定义了输入缓冲区的数据长度是481*4800/1000

AUDIO_OUT_BUF_LEN定义了输出缓冲区的数据长度是38448*8

/* Private variables ---------------------------------------------------------*/

 

uint16_t PCM_Buffer[AUDIO_IN_BUF_LEN];

volatile int16_t audio_out_buffer[AUDIO_OUT_BUF_LEN];

 

PCM_Buffer用来存放DFSDM外设从麦克风读回的数据

audio_out_buffer用来存放让PCM1774播放的数据

 

  /* Configure Audio Output peripheral (SAI) and external DAC */

 

  BSP_AUDIO_OUT_Init(PCM1774_0, &PCM1774_X_0_handle, NULL, 20, AUDIO_SAMPLING_FREQUENCY);

  BSP_AUDIO_OUT_SetVolume(PCM1774_X_0_handle, 20);

 

 

BSP_AUDIO_OUT_InitBSP_AUDIO_OUT_SetVolume函数用来配置输出和通过I2C接口将配置信息发送至PCM1774 DAC

20是播放音量,范围是0~63

 

/* Configure Audio Input peripheral - DFSDM */ 

  BSP_AUDIO_IN_Init(AUDIO_SAMPLING_FREQUENCY, 16, AUDIO_CHANNELS); 

 

  /* Start Microphone acquisition */

  BSP_AUDIO_IN_Record(PCM_Buffer,0);

 

 

BSP_AUDIO_IN_Init配置麦克风的采样率和通道数

BSP_AUDIO_IN_Record用来启动录音

16在这个例程里无意义

 

/**

* @brief  Transfer Complete user callback, called by BSP functions.

* @param  None

* @retval None

*/

void BSP_AUDIO_IN_TransferComplete_CallBack(void)

{

  AudioProcess();

}

 

/**

* @brief  Half Transfer Complete user callback, called by BSP functions.

* @param  None

* @retval None

*/

void BSP_AUDIO_IN_HalfTransfer_CallBack(void)

{

  AudioProcess();

}

 

录音通过2个回调函数通知AudioProcess()处理程序

BSP_AUDIO_IN_HalfTransfer_CallBack表示读取了一半数据

BSP_AUDIO_IN_TransferComplete_CallBack表示读取了全部数据

这里的一半和全部是指DFSDMDMA数据长度的一半和全部

SensorTile_audio.cBSP_AUDIO_IN_Record函数里

DMA的长度实际上是PCM_Buffer[]长度的2倍(SENSORTILE_AudioIn_Handler.Sampling_Freq / 1000 * 2

 

uint8_t BSP_AUDIO_IN_Record(uint16_t* pbuf, uint32_t size)

{ 

  int32_t counter = 0;

  SENSORTILE_AudioIn_Handler.PCM_Data = pbuf;

 

  for (counter = SENSORTILE_AudioIn_Handler.MicChannels; counter > 0; counter --)

  {

    if (HAL_OK != HAL_DFSDM_FilterRegularStart_DMA(&haudio_in_dfsdmfilter[counter-1],

                                                   (int32_t*) RecBuff[counter-1],

                                                   SENSORTILE_AudioIn_Handler.Sampling_Freq / 1000 * 2))

    {

      return AUDIO_ERROR;

    }   

  }

  return AUDIO_OK;

}

 

所以BSP_AUDIO_IN_HalfTransfer_CallBackBSP_AUDIO_IN_TransferComplete_CallBack函数都返回一个完整的PCM_Buffer数组

所以2个函数都执行了AudioProcess()用来处理收到的音频数据

 

void AudioProcess(void)

{

  /*for L4 PDM to PCM conversion is performed in hardware by DFSDM peripheral*/

  static uint32_t IndexOut = 0;

  static uint32_t AudioOutActive = 0;

  uint32_t indexIn;

 

AudioProcess函数内有2个静态变量IndexOutAudioOutActive和一个局部变量indexIn

IndexOut用来索引输出audio_out_buffer缓冲区

AudioOutActive用来标记是否启动了音频输出

2个静态变量在下次进入函数时只留上一次设置的数据)

indexIn用来读取PCM_Buffer数据时使用

 

  for(indexIn=0;indexIn<AUDIO_IN_BUF_LEN;indexIn++)

  {

    audio_out_buffer[IndexOut++] = PCM_Buffer[indexIn];

    audio_out_buffer[IndexOut++] = PCM_Buffer[indexIn];

  }

 

上边的for循环用来将整个PCM_Buffer的数据复制到audio_out_buffer

audio_out_buffer是一个双通道数组,奇数代表一个通道,偶数代表一个通道

可以看出两个通道使用了相同的数据

PCM_Buffer的长度是audio_out_buffer1/8

所以要执行4AudioProcess()函数才能填充到audio_out_buffer的一半

 

 

  if(!AudioOutActive && IndexOut==AUDIO_OUT_BUF_LEN/2)

  {

    BSP_AUDIO_OUT_Play(PCM1774_X_0_handle,(uint16_t*)audio_out_buffer, AUDIO_OUT_BUF_LEN);

    AudioOutActive=1;

  }

 

 

AudioOutActive不为1时也就是没有执行过BSP_AUDIO_OUT_Play函数时

和输出缓冲区的数据为整个缓冲区数据长度的一半时进入if语句

执行BSP_AUDIO_OUT_Play函数播放audio_out_buffer数组内的音频数据

这时SAI模块会从audio_out_buffer的第0个数据开始依次将数据发送到PCM1774 DAC

输出索引达到AUDIO_OUT_BUF_LEN时再从0开始反复循环

同时将AudioOutActive1

在重启模块前不会再次进入上边的if判断也不会再执行BSP_AUDIO_OUT_Play函数

可以注意到if语句里判断的是IndexOut==AUDIO_OUT_BUF_LEN/2

而在BSP_AUDIO_OUT_Play函数中传递的长度却是AUDIO_OUT_BUF_LEN

audio_out_buffer的后半部分的数据不是还没有赋值吗?

现在是这样的

别忘了还有BSP_AUDIO_IN_TransferComplete_CallBackBSP_AUDIO_IN_HalfTransfer_CallBack函数

它们还在不断的把数据通过AudioProcessfor循环把采集到的麦克风数据填充到audio_out_buffer数组

因为音频输入和输出的采样率是一样的

所以在SAI模块还没有播放到audio_out_buffer1/2位置时后边的数据已经准备好了

SAI模块播放audio_out_buffer的后半部分数据时前边的数据又更新了

 

 

  if(IndexOut==AUDIO_OUT_BUF_LEN)

  {

    IndexOut=0;

  }

}

 

这个if用来判断IndexOut索引是否达到了audio_out_buffer缓冲区的结尾

如果到了再从audio_out_buffer0处填充

 

就这样反反复复读取播放,我们就能听到麦克风传来的数据了

 

 

 

另外还有一点疑问

DFSDM模块读取到PCM_Buffer的数据是int16_t,有符号数据

void HAL_DFSDM_FilterRegConvHalfCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter)

{ 

  uint32_t i, j = 0; 

  if (hdfsdm_filter == &haudio_in_dfsdmfilter[0])

  {

    for(j=0; j < SENSORTILE_AudioIn_Handler.MicChannels; j ++)

    {

      for (= 0; i < SENSORTILE_AudioIn_Handler.Sampling_Freq / 1000; i++)

      {

        SENSORTILE_AudioIn_Handler.HP_Filters[j].= ((RecBuff[j][i] >> 8) * AudioInVolume) >> 7;

        SENSORTILE_AudioIn_Handler.HP_Filters[j].oldOut = (0xFC * (SENSORTILE_AudioIn_Handler.HP_Filters[j].oldOut + SENSORTILE_AudioIn_Handler.HP_Filters[j].- SENSORTILE_AudioIn_Handler.HP_Filters[j].oldIn)) / 256;

        SENSORTILE_AudioIn_Handler.HP_Filters[j].oldIn = SENSORTILE_AudioIn_Handler.HP_Filters[j].Z;

        SENSORTILE_AudioIn_Handler.PCM_Data[* SENSORTILE_AudioIn_Handler.MicChannels + j] =SaturaLH(SENSORTILE_AudioIn_Handler.HP_Filters[j].oldOut, -32760, 32760);       

      }    

    }

    BSP_AUDIO_IN_HalfTransfer_CallBack();

  }

}

 

 

从上边代码(灰底)可以看出,奇怪的是BSP_AUDIO_IN_Record函数里的参数和PCM_Buffer变量却定义成有符号的

另外audio_out_buffer定义为int16_t可是在BSP_AUDIO_OUT_Play函数的参数里却将它转换成无符号的(uint16_t*)audio_out_buffer

试着修改两个变量的符号类型对演示没产生明显影响

还希望有经验的大神给分析分析

 

虾扯蛋,蛋扯虾,虾扯蛋扯虾

回复评论 (3)

虾哥,这个代码外面的框框是怎么弄出来的?
点赞  2016-12-25 22:25
引用: johnrey 发表于 2016-12-25 22:25
虾哥,这个代码外面的框框是怎么弄出来的?

使用WORD插入1*1表格颜色为黑色,然后在表格属性里设置一下边框颜色为蓝色
代码使用Notepad++复制为html格式
虾扯蛋,蛋扯虾,虾扯蛋扯虾
点赞  2016-12-25 23:09
学习 马克 虾哥威武
点赞  2017-11-15 09:29
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复