乱写一通的笔记.
先配置SAI PLL时钟.
if ( (Frequency == AUDIO_FREQUENCY_11K)
|| (Frequency == AUDIO_FREQUENCY_22K)
|| (Frequency == AUDIO_FREQUENCY_44K) )
{
/* Configure PLLSAI prescalers */
/* SAI clock config
PLLSAI1_VCO= 8 Mhz * PLLSAI1N = 8 * 24 = VCO_192M
SAI_CK_x = PLLSAI1_VCO/PLLSAI1P = 192/17 = 11.294 Mhz */
RCC_ExCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SAI1;
RCC_ExCLKInitStruct.PLLSAI1.PLLSAI1N = 24;
RCC_ExCLKInitStruct.PLLSAI1.PLLSAI1P = 17;
RCC_ExCLKInitStruct.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_SAI1CLK;
RCC_ExCLKInitStruct.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLLSAI1;
}
else /* AUDIO_FREQUENCY_8K, AUDIO_FREQUENCY_16K, AUDIO_FREQUENCY_48K, AUDIO_FREQUENCY_96K */
{
/* SAI clock config
PLLSAI1_VCO= 8 Mhz * PLLSAI1N = 8 * 43 = VCO_344M
SAI_CK_x = PLLSAI1_VCO/PLLSAI1P = 344/7 = 49.142 Mhz */
RCC_ExCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SAI1;
RCC_ExCLKInitStruct.PLLSAI1.PLLSAI1N = 43;
RCC_ExCLKInitStruct.PLLSAI1.PLLSAI1P = 7;
RCC_ExCLKInitStruct.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_SAI1CLK;
RCC_ExCLKInitStruct.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLLSAI1;
}
然后配置Channel和Filter.先是HAL_DFSDM_ChannelInit,再HAL_DFSDM_FilterInit.其中假设我们是96K的音频频率,SAI_CK是49.142MHz(实际上最理想49.152MHz),16Bit的分辨率,再由于配置了10倍的OverSample.按照程序除以32就是1.536MHz,这个数除以16Bit就是96K采样率.倒推就是(49.15200 MHz) / (96 kHz * 16) = 32分频,32就是这么得到的.然后后面配了个TIM8做触发?
static uint8_t AUDIO_DFSDMx_Init(uint32_t AudioFreq)
{
/*####CHANNEL 2####*/
hAudioIn.hDfsdmLeftChannel.Init.OutputClock.Activation = ENABLE;
hAudioIn.hDfsdmLeftChannel.Init.OutputClock.Selection = DFSDM_CHANNEL_OUTPUT_CLOCK_AUDIO;
/* Set the DFSDM clock OUT audio frequency configuration */
hAudioIn.hDfsdmLeftChannel.Init.OutputClock.Divider = DFSDMClockDivider(AudioFreq);
hAudioIn.hDfsdmLeftChannel.Init.Input.Multiplexer = DFSDM_CHANNEL_EXTERNAL_INPUTS;
hAudioIn.hDfsdmLeftChannel.Init.Input.DataPacking = DFSDM_CHANNEL_STANDARD_MODE;
hAudioIn.hDfsdmLeftChannel.Init.Input.Pins = DFSDM_CHANNEL_SAME_CHANNEL_PINS;
/* Request to sample stable data for LEFT micro on Rising edge */
hAudioIn.hDfsdmLeftChannel.Init.SerialInterface.Type = DFSDM_CHANNEL_SPI_RISING;
hAudioIn.hDfsdmLeftChannel.Init.SerialInterface.SpiClock = DFSDM_CHANNEL_SPI_CLOCK_INTERNAL;
hAudioIn.hDfsdmLeftChannel.Init.Awd.FilterOrder = DFSDM_CHANNEL_SINC1_ORDER;
hAudioIn.hDfsdmLeftChannel.Init.Awd.Oversampling = 10;
hAudioIn.hDfsdmLeftChannel.Init.Offset = 0;
hAudioIn.hDfsdmLeftChannel.Init.RightBitShift = DFSDMRightBitShift(AudioFreq);
hAudioIn.hDfsdmLeftChannel.Instance = DFSDM1_Channel2;
/* Init the DFSDM Channel */
if (HAL_DFSDM_ChannelInit(&hAudioIn.hDfsdmLeftChannel) != HAL_OK)
{
return AUDIO_ERROR;
}
/*####FILTER 0####*/
BSP_AUDIO_hDfsdmLeftFilter.Init.RegularParam.Trigger = DFSDM_FILTER_SW_TRIGGER;
BSP_AUDIO_hDfsdmLeftFilter.Init.RegularParam.FastMode = ENABLE;
BSP_AUDIO_hDfsdmLeftFilter.Init.RegularParam.DmaMode = ENABLE;
BSP_AUDIO_hDfsdmLeftFilter.Init.InjectedParam.Trigger = DFSDM_FILTER_SW_TRIGGER;
BSP_AUDIO_hDfsdmLeftFilter.Init.InjectedParam.ScanMode = DISABLE;
BSP_AUDIO_hDfsdmLeftFilter.Init.InjectedParam.DmaMode = DISABLE;
BSP_AUDIO_hDfsdmLeftFilter.Init.InjectedParam.ExtTrigger = DFSDM_FILTER_EXT_TRIG_TIM8_TRGO;
BSP_AUDIO_hDfsdmLeftFilter.Init.InjectedParam.ExtTriggerEdge = DFSDM_FILTER_EXT_TRIG_BOTH_EDGES;
BSP_AUDIO_hDfsdmLeftFilter.Init.FilterParam.SincOrder = DFSDMFilterOrder(AudioFreq);
/* Set the DFSDM Filters Oversampling to have correct sample rate */
BSP_AUDIO_hDfsdmLeftFilter.Init.FilterParam.Oversampling = DFSDMOverSampling(AudioFreq);
BSP_AUDIO_hDfsdmLeftFilter.Init.FilterParam.IntOversampling = 1;
BSP_AUDIO_hDfsdmLeftFilter.Instance = AUDIO_DFSDMx_LEFT_FILTER;
/* Init the DFSDM Filter */
if (HAL_DFSDM_FilterInit(&BSP_AUDIO_hDfsdmLeftFilter) != HAL_OK)
{
return AUDIO_ERROR;
}
/* Configure regular channel */
if (HAL_DFSDM_FilterConfigRegChannel(&BSP_AUDIO_hDfsdmLeftFilter,
DFSDM_CHANNEL_2,
DFSDM_CONTINUOUS_CONV_ON) != HAL_OK)
{
return AUDIO_ERROR;
}
return AUDIO_OK;
}
注册了3个CallBack,分别是传输完成中断,传输一半中断,传输错误中断.AudioRecorder_Error_Callback + AudioRecorder_HalfTransfer_CallBack + AudioRecorder_TransferComplete_CallBack,ERROR就是设置一个ERROR标志.传一半和传完都是给一个信号量给系统.
/**
* @brief Callback invoked when second half of the record buffer has been
* filled in.
* @param None
* @retval None
*/
static void AudioRecorder_TransferComplete_CallBack(void)
{
if(hAudioRecorder.state == AUDIORECORDER_START)
{
if (osMessagePut(hAudioRecorder.osMsgId, RECORD_BUFFER_OFFSET_FULL, 0) != osOK)
{
Error_Handler();
}
}
}
/**
* @brief Callback invoked when first half of the record buffer has been
* filled in.
* @param None
* @retval None
*/
static void AudioRecorder_HalfTransfer_CallBack(void)
{
if(hAudioRecorder.state == AUDIORECORDER_START)
{
if (osMessagePut(hAudioRecorder.osMsgId, RECORD_BUFFER_OFFSET_HALF, 0) != osOK)
{
Error_Handler();
}
}
}
之前就说过他是启动一个叫AudioRecorder_Thread的任务.所以,实际上就是任务里面做接受的.收消息函数.

其中事件就两个:RECORD_BUFFER_OFFSET_HALF + RECORD_BUFFER_OFFSET_FULL.我简单抽取了一下程序.
case RECORD_BUFFER_OFFSET_HALF:
if (AudioRecorder_QspiProgram ((uint8_t*)hAudioRecorder.buffer,
hAudioRecorder.RecordTotalSize,
RECORD_BUFFER_SIZE,
&NbBytesWritten) != 0)
{
Error_Handler();
}
hAudioRecorder.RecordTotalSize += NbBytesWritten;
break;
case RECORD_BUFFER_OFFSET_FULL:
if (AudioRecorder_QspiProgram ((uint8_t*)&hAudioRecorder.buffer[RECORD_BUFFER_SIZE/2],
hAudioRecorder.RecordTotalSize,
RECORD_BUFFER_SIZE,
&NbBytesWritten) != 0)
{
Error_Handler();
}
hAudioRecorder.RecordTotalSize += NbBytesWritten;
/* Toggle the green led */
BSP_LED_Toggle(LED_GREEN);
break;
总体来说就是传过来就写入,但是写入为什么是hAudioRecorder.buffer和hAudioRecorder.buffer/2呢,如果已经传了一半, 那么就是从0 到 一半的数据都是有效的,如果全部传完,那么就是从一半到最后就是有效,RecordTotalSize是写入数据位置指针.但是哪里开始录呢?那就得看AudioRecorderExec -> AudioRecorder_Run.
static void AudioRecorder_Run(void)
{
JOYState_TypeDef JOYState = JOY_NONE;
/* Start audio recorder */
if (AudioRecorder_Start(RECORD_SAMPLE_RATE) != AUDIORECORDER_ERROR_NONE)
{
Error_Handler();
}
/* Audio recorder event loop */
do
{
JOYState = kMenu_GetEvent(osWaitForever);
} while (JOYState != JOY_LEFT );
/* Stop audio recorder */
if (AudioRecorder_Stop() != AUDIORECORDER_ERROR_NONE)
{
Error_Handler();
}
}
实际上一个AudioRecorder_Start,一个AudioRecorder_Stop.AudioRecorder_Start内实际调用BSP_AUDIO_IN_Record,然后是HAL_DFSDM_FilterRegularStart_DMA,实际上录音到LeftRecBuff,那pRecBuf在哪里呢?原来也有HAL_DFSDM_FilterRegConvCpltCallback + HAL_DFSDM_FilterRegConvHalfCpltCallback + HAL_DFSDM_FilterErrorCallback,看名字知道最后一个是处理错误的,前面两个就是半传输和完全传输,既然DFSDM也有半输出和所有传输,但是这个跟之前的传输一半传输完成好像没交集啊,不是的,他注册了.因为下面代码.
/* Set Callback function pointers */
BSP_AUDIO_IN_RegisterCallbacks(AudioRecorder_Error_Callback,
AudioRecorder_HalfTransfer_CallBack,
AudioRecorder_TransferComplete_CallBack);
在中断内,把东西算完了,然后buf填好,回调之前设定的传输一半/完成函数.
/**
* @brief Half regular conversion complete callback.
* @param hdfsdm_filter : DFSDM filter handle.
* @retval None
*/
void HAL_DFSDM_FilterRegConvHalfCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter)
{
uint32_t index;
uint32_t recbufsize = (hAudioIn.RecSize/DEFAULT_AUDIO_IN_CHANNEL_NBR);
for(index = 0; index < (recbufsize/2); index++)
{
hAudioIn.pRecBuf[index] = (uint16_t)(SaturaLH((hAudioIn.LeftRecBuff[index] >> 8), -32768, 32767));
}
/* Invoke the registered 'HalfTransfer' callback function (if any) */
if (hAudioIn.CbHalfTransfer != (Audio_CallbackTypeDef)NULL)
{
hAudioIn.CbHalfTransfer();
}
}
如何STOP,那就开DMA.HAL_DFSDM_FilterRegularStop_DMA,然后把用到的buf释放.在FreeRTOS里面是pvPortMalloc和vPortFree作为申请释放函数.
实际上录音模块虽然非常复杂,但是处理起来没那么难.参数微调各种,慢慢研究.
全面总结:配置时钟->配置CH->配置Filter->开始DMA->......->关闭DMA(停止)
大神您好,麻烦问一下Board support package 中 函数uint8_t BSP_AUDIO_IN_Record(uint16_t *pbuf, uint32_t size)是怎么工作的?比如我要录20秒音频,用BSP_AUDIO_IN_Init初始化完,调用BSP_AUDIO_IN_Record,记入20秒音频,是不是每次调用BSP_AUDIO_IN_Record 就仅仅只记入一个sample吗,然后20s的音频就需要不断调用这个函数N次(基于系统时钟)?如果是这样的话,每次调用这个函数,指针pbuf要自加吗?是自加4吗?
@Damon 传递进去是个地址,不会改变你穿进去的地址,你可以理解成传递了一个数组,然后他给数组里面填数据,至于填多少数据,DMA的半空中断决定.