乱写一通的笔记.
先配置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的半空中断决定.