- static int32_t WM8994_Probe(void)
- {
- int32_t ret = BSP_ERROR_NONE;
- WM8994_IO_t IOCtx;
- static WM8994_Object_t WM8994Obj;
- uint32_t id;
- /* Configure the audio driver */
- IOCtx.Address = AUDIO_I2C_ADDRESS;
- IOCtx.Init = BSP_I2C4_Init;
- IOCtx.DeInit = BSP_I2C4_DeInit;
- IOCtx.ReadReg = BSP_I2C4_ReadReg16;
- IOCtx.WriteReg = BSP_I2C4_WriteReg16;
- IOCtx.GetTick = BSP_GetTick;
- if(WM8994_RegisterBusIO (&WM8994Obj, &IOCtx) != WM8994_OK)
- {
- ret = BSP_ERROR_BUS_FAILURE;
- }
- else
- {
- /* Reset the codec */
- if(WM8994_Reset(&WM8994Obj) != WM8994_OK)
- {
- ret = BSP_ERROR_COMPONENT_FAILURE;
- }
- else if(WM8994_ReadID(&WM8994Obj, &id) != WM8994_OK)
- {
- ret = BSP_ERROR_COMPONENT_FAILURE;
- }
- else if(id != WM8994_ID)
- {
- ret = BSP_ERROR_UNKNOWN_COMPONENT;
- }
- else
- {
- Audio_Drv = (AUDIO_Drv_t *) &WM8994_Driver;
- Audio_CompObj = &WM8994Obj;
- }
- }
- return ret;
- }
三、SAI初始化
- static void Playback_Init(void)
- {
- RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInitStruct;
- /* Configure PLLSAI1 prescalers */
- /* PLLSAI_VCO: VCO_512M
- SAI_CLK(first level) = PLLSAI_VCO/PLLSAIP = 512/125 = 4.096 Mhz */
- RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SAI2;
- RCC_PeriphCLKInitStruct.Sai23ClockSelection = RCC_SAI2CLKSOURCE_PLL2;
- RCC_PeriphCLKInitStruct.PLL2.PLL2P = 125;
- RCC_PeriphCLKInitStruct.PLL2.PLL2Q = 1;
- RCC_PeriphCLKInitStruct.PLL2.PLL2R = 1;
- RCC_PeriphCLKInitStruct.PLL2.PLL2N = 512;
- RCC_PeriphCLKInitStruct.PLL2.PLL2FRACN = 0;
- RCC_PeriphCLKInitStruct.PLL2.PLL2M = 25;
- if(HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct) != HAL_OK)
- {
- Error_Handler();
- }
- /* Configure PLLSAI4A prescalers */
- RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SAI4A;
- RCC_PeriphCLKInitStruct.Sai4AClockSelection = RCC_SAI4ACLKSOURCE_PLL2;
- if(HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct) != HAL_OK)
- {
- Error_Handler();
- }
- /* SAI PCM Output init */
- __HAL_SAI_RESET_HANDLE_STATE(&haudio_out_sai);
- haudio_out_sai.Instance = AUDIO_OUT_SAIx;
- haudio_out_sai.Init.AudioMode = SAI_MODEMASTER_TX;
- haudio_out_sai.Init.Synchro = SAI_ASYNCHRONOUS;
- haudio_out_sai.Init.OutputDrive = SAI_OUTPUTDRIVE_ENABLE;
- haudio_out_sai.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;
- haudio_out_sai.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_1QF;
- haudio_out_sai.Init.AudioFrequency = AUDIO_FREQUENCY;
- haudio_out_sai.Init.Protocol = SAI_FREE_PROTOCOL;
- haudio_out_sai.Init.DataSize = SAI_DATASIZE_16;
- haudio_out_sai.Init.FirstBit = SAI_FIRSTBIT_MSB;
- haudio_out_sai.Init.ClockStrobing = SAI_CLOCKSTROBING_FALLINGEDGE;
- haudio_out_sai.FrameInit.FrameLength = 128;
- haudio_out_sai.FrameInit.ActiveFrameLength = 64;
- haudio_out_sai.FrameInit.FSDefinition = SAI_FS_CHANNEL_IDENTIFICATION;
- haudio_out_sai.FrameInit.FSPolarity = SAI_FS_ACTIVE_LOW;
- haudio_out_sai.FrameInit.FSOffset = SAI_FS_BEFOREFIRSTBIT;
- haudio_out_sai.SlotInit.FirstBitOffset = 0;
- haudio_out_sai.SlotInit.SlotSize = SAI_SLOTSIZE_DATASIZE;
- haudio_out_sai.SlotInit.SlotNumber = 4;
- haudio_out_sai.SlotInit.SlotActive = (SAI_SLOTACTIVE_0 | SAI_SLOTACTIVE_2);
- /* DeInit SAI PCM input */
- HAL_SAI_DeInit(&haudio_out_sai);
- /* Init SAI PCM input */
- if(HAL_OK != HAL_SAI_Init(&haudio_out_sai))
- {
- Error_Handler();
- }
- /* Enable SAI to generate clock used by audio driver */
- __HAL_SAI_ENABLE(&haudio_out_sai);
- WM8994_Probe();
- /* Init PDM Filters */
- // AUDIO_IN_PDMToPCM_Init(AUDIO_FREQUENCY, AUDIO_CHANNEL_NUMBER);
- }
四、列出所有的mp3文件名
- void list_mp3_files(const char *path)
- {
- DIR dir;
- FILINFO fileInfo;
- // 挂载文件系统
- FRESULT res = f_mount(&MMCFatFs, (TCHAR const*)MMCPath, 0);
- if (res != FR_OK) {
- printf("Failed to mount filesystem: %d\n", res);
- return;
- }
- // 打开目录
- res = f_opendir(&dir, path);
- if (res != FR_OK) {
- printf("Failed to open directory: %d\n", res);
- f_mount(NULL, "", 0); // 卸载文件系统
- return;
- }
- // 遍历目录
- while (1) {
- res = f_readdir(&dir, &fileInfo);
- if (res != FR_OK || fileInfo.fname[0] == 0) {
- break; // 遍历完成或出错
- }
- // 检查是否为 MP3 文件
- if (!(fileInfo.fattrib & AM_DIR)) { // 排除目录
- char *ext = strrchr(fileInfo.fname, '.'); // 获取文件扩展名
- if (ext && strcmp(ext, ".mp3") == 0) { // 检查扩展名是否为 .mp3
- strncpy(mp3Files[mp3Count], fileInfo.fname, 255); // 存储文件名
- mp3Count++;
- if (mp3Count >= 100) {
- break; // 达到最大存储数量
- }
- }
- }
- }
- // 关闭目录
- f_closedir(&dir);
- // 卸载文件系统
- f_mount(NULL, "", 0);
- // 打印所有 MP3 文件名
- for (int i = 0; i < mp3Count; i++) {
- printf("MP3 File: %s\n", mp3Files[i]);
- }
- }
五、初始化MP3播放器
- /**
- * [url=home.php?mod=space&uid=247401]@brief[/url] 初始化MP3播放器
- * @param mp3_file: MP3文件的名字
- * @param vol: 音量
- * @param output_device: 0--耳机 1--喇叭
- * @retval 1:成功 0:失败
- */
- unsigned char st_mp3_player_Init( char *mp3_file, unsigned char vol, unsigned char output_device )
- {
- unsigned int read_num;
- unsigned int ID3_Length, temp1;
- unsigned char buf[4];
- uint32_t *AudioFreq_ptr;
- uwVolume = vol;
-
- memset( SongInfo.FileName, 0, 30 );
-
- /* Register the file system object to the FatFs module */
- if(f_mount(&MMCFatFs, (TCHAR const*)MMCPath, 0) != FR_OK)
- {
- printf("f_mount error\r\n");
- return 0;
- }
-
- if( f_open( &file_object, mp3_file, FA_OPEN_EXISTING | FA_READ ) != FR_OK )
- {
- printf("MP3 File Open Err!!!\r\n" );
- return 0;
- }
-
- /* 使能FATFS的快速读写功能 */
- if( f_lseek(&file_object, 4)!= FR_OK )
- {
- f_close( &file_object );
- printf("faftfs fast seek feature err!!!\r\n" );
- return 0;
- }
-
- file_object.cltbl = clmt; /* Enable fast seek feature (cltbl != NULL) */
- clmt[0] = 2400; /* Set table size */
-
- if( f_lseek(&file_object, CREATE_LINKMAP) != FR_OK ) /* Create CLMT */
- {
- f_close( &file_object );
- printf("faftfs fast seek feature err!!!\r\n" );
- return 0;
- }
-
- if( f_lseek(&file_object, 0)!= FR_OK )
- {
- f_close( &file_object );
- printf("faftfs fast seek feature err!!!\r\n" );
- return 0;
- }
-
- //计算文件大小
- SongInfo.FileSize = f_size( &file_object );
- memcpy( SongInfo.FileName, mp3_file, strlen( mp3_file) );
-
- //判断打开的文件是否是mp3文件
- if( f_read( &file_object, buf, 3, &read_num ) != FR_OK )
- {
- f_close( &file_object );
- return 0;
- }
-
- if( memcmp( buf, (unsigned char *)"ID3", 3 ) != 0 )
- {
- f_close( &file_object );
- printf("It is not mp3 file!!!\r\n" );
- return 0;
- }
-
- //开始计算ID3标签头大小
- f_lseek( &file_object, 6 );
-
- if( f_read( &file_object, buf, 4, &read_num ) != FR_OK )
- {
- f_close( &file_object );
- // printf("读取文件失败。\r\n" );
- return 0;
- }
-
- ID3_Length = (buf[0] & 0x7f) * 0x200000; //计算ID3标签头的大小
- ID3_Length = ID3_Length + ( (buf[1] & 0x7f) * 0x400 );
- ID3_Length = ID3_Length + ( (buf[2] & 0x7f) * 0x80 );
- ID3_Length = ID3_Length + (buf[3] & 0x7f) + 10;
-
- //跳转到ID3标签结束位置,准备获取mp3文件信息
- f_lseek( &file_object, ID3_Length );
- SongInfo.file_ptr = ID3_Length;
-
- //初始化MP3解码器
- memset( &g_MP3Decoder, 0, sizeof( TSpiritMP3Decoder ) );
- SpiritMP3DecoderInit( &g_MP3Decoder, // MP3 decoder object
- RetrieveMP3Data, // Input callback function
- NULL, // No post-process callback
- NULL // Callback parameter
- );
-
- //获取mp3文件信息
- while( 1 )
- {
- Audio_Delay_1ms( 3 );
-
- temp1 = SpiritMP3Decode( &g_MP3Decoder, /* Decoder structure */
- (short *)&audio_output_buffer[0], /* [OUT] Output PCM buffer */
- 1152, /* Number of samples to decode (1 sample = 32 bit = 2ch*16 bit) */
- &MP3Info /* [OUT, opt] Optional informational structure */
- );
- //到达文件末尾,没有获取mp3文件信息,退出
- if( temp1 < 1152 )
- {
- f_close( &file_object );
- printf("Get MP3 Info Err!!!\r\n");
- return 0;
- }
-
- //获取到正确的MP3信息
- if( MP3Info.IsGoodStream == 1 && MP3Info.nSampleRateHz != 0 && MP3Info.nBitrateKbps != 0 )
- {
- MPEGAudioFrameInfo.mSamplesPerFrame = MP3Info.nSamplesPerFrame;
- MPEGAudioFrameInfo.mBitrate = MP3Info.nBitrateKbps * 1000;
- SongInfo.Bitrate = MPEGAudioFrameInfo.mBitrate;
- MPEGAudioFrameInfo.mChannelMode = MP3Info.nChannels;
- MPEGAudioFrameInfo.mLayer = MP3Info.nLayer;
- MPEGAudioFrameInfo.mSamplerate = MP3Info.nSampleRateHz;
- //计算歌曲的播放时长
- SongInfo.time = (SongInfo.FileSize - ID3_Length) * 8 / SongInfo.Bitrate;
-
- printf("\r\n");
- printf( "mp3 SamplesPerFrame:%d\r\n", MPEGAudioFrameInfo.mSamplesPerFrame );
- printf( "mp3 Bitrate:%d\r\n", MPEGAudioFrameInfo.mBitrate );
- printf( "mp3 Samplerate:%d\r\n", MPEGAudioFrameInfo.mSamplerate );
- printf( "mp3 ChannelMode:%d\r\n", MPEGAudioFrameInfo.mChannelMode );
- printf( "mp3 Layer:%d\r\n", MPEGAudioFrameInfo.mLayer );
- printf( "mp3 FileSize:%d\r\n", SongInfo.FileSize );
- printf( "mp3 PlayTime:%d\r\n", SongInfo.time );
-
- Display_SongDescription();
- break;
- }
- }
-
-
- //重新初始化MP3解码器,准备开始解码
- memset( &g_MP3Decoder, 0, sizeof( TSpiritMP3Decoder ) );
- SpiritMP3DecoderInit( &g_MP3Decoder, // MP3 decoder object
- RetrieveMP3Data, // Input callback function
- NULL, // No post-process callback
- NULL // Callback parameter
- );
-
- //跳转到ID3标签头结束位置,从此位置开始解码
- f_lseek( &file_object, ID3_Length );
- SongInfo.file_ptr = ID3_Length;
- /*
- for(int i=0;i<sizeof(AudioFreq);i++)
- {
- if(abs((int)(AudioFreq[i]-MPEGAudioFrameInfo.mSamplerate))<100)
- {
- AudioFreq_ptr=&AudioFreq[i];
- break;
- }
- }
- */
- AudioFreq[0]=MPEGAudioFrameInfo.mSamplerate;
- AudioFreq_ptr=&AudioFreq[0];
- //AudioFreq_ptr = &AudioFreq[6]; /*96K*/
-
- AudioPlayInit->Device = AUDIO_OUT_DEVICE_HEADPHONE;
- AudioPlayInit->ChannelsNbr = 2;
- AudioPlayInit->SampleRate = *AudioFreq_ptr;
- AudioPlayInit->BitsPerSample = AUDIO_RESOLUTION_16B;
- AudioPlayInit->Volume = uwVolume;
-
- //配置WM8994的工作模式
- if( BSP_AUDIO_OUT_Init( 0, AudioPlayInit ) != 0 )
- {
- f_close( &file_object );
- printf("WM8994 Init Err!!!\r\n");
- return 0;
- }
-
- //if( output_device == 0 ) BSP_AUDIO_OUT_SetAudioFrameSlot(CODEC_AUDIOFRAME_SLOT_02);
- //else BSP_AUDIO_OUT_SetAudioFrameSlot(CODEC_AUDIOFRAME_SLOT_13);
- return 1;
- }
六、播放MP3函数
- /**
- * [url=home.php?mod=space&uid=247401]@brief[/url] 播放MP3
- * @param time: MP3文件开始播放的时间点(不超过MP3播放时间的最大值),单位:s
- * @retval 1:成功 0:失败
- */
- unsigned char st_mp3play( unsigned short int time )
- {
- signed int temp = 0;
- unsigned int nSamples = 0;
- unsigned int addr = 0;
- uint8_t index=mp3Current;
-
- SongInfo.state = OK;
- SongInfo.time_count = time;
-
- if( time >= (SongInfo.time-2) )
- {
- BSP_AUDIO_OUT_Stop(CODEC_PDWN_SW);
- f_close( &file_object );
- return 1;
- }
-
- //根据播放起始时间,计算文件指针起始位置
- addr = (time * SongInfo.Bitrate / 8 );
- if( addr == 0 )
- {
- if( f_lseek(&file_object, SongInfo.file_ptr ) != FR_OK )
- {
- f_close( &file_object );
- BSP_AUDIO_OUT_Stop(CODEC_PDWN_SW);
- return 0;
- }
- }
- else
- {
- addr += SongInfo.file_ptr;
- SongInfo.file_ptr = addr;
-
- if( f_lseek(&file_object, SongInfo.file_ptr) != FR_OK )
- {
- f_close( &file_object );
- BSP_AUDIO_OUT_Stop(CODEC_PDWN_SW);
- return 0;
- }
- }
-
- /* 先解码两帧mp3音频数据,用于填充audio_output_buffer */
- temp = 0;
- while( temp < 2 )
- {
- nSamples = SpiritMP3Decode( &g_MP3Decoder, /* Decoder structure */
- (short *)&audio_output_buffer[temp*MPEGAudioFrameInfo.mSamplesPerFrame*2], /* [OUT] Output PCM buffer */
- MPEGAudioFrameInfo.mSamplesPerFrame, /* Number of samples to decode (1 sample = 32 bit = 2ch*16 bit) */
- &MP3Info /* [OUT, opt] Optional informational structure */
- );
-
- printf("Samples:%d\r\n", nSamples);
- if( MP3Info.IsGoodStream == 1 )
- {
- temp++;
- }
-
- /* 到达文件末尾,则退出 */
- if( nSamples < MPEGAudioFrameInfo.mSamplesPerFrame||index!= mp3Current)
- {
- f_close( &file_object );
- BSP_AUDIO_OUT_Stop(CODEC_PDWN_SW);
- return 0;
- }
-
- Audio_Delay_1ms( 3 );
- }
-
- TxHalfCpltFlag = 0;
- TxCpltFlag = 0;
- TxBufNum = 0;
- AudioOutFlag = 0;
- Audio_ErrorFlag = 0;
-
- //开始播放
- BSP_AUDIO_OUT_Play( 0,(uint8_t*)audio_output_buffer, (MPEGAudioFrameInfo.mSamplesPerFrame*2*2*2) );
- AudioOutFlag = 1;
- while( 1 )
- {
- while( AudioOutFlag == 1 )
- {
- //播放状态--暂停
- if( SongInfo.state == Pause )
- {
- BSP_AUDIO_OUT_Pause(0);
-
- while( SongInfo.state == Pause )
- {
- Audio_Delay_1ms( 10 );
- }
- }
- //播放状态--恢复
- else if( SongInfo.state == Resume )
- {
- BSP_AUDIO_OUT_Resume( 0 );
- SongInfo.state = OK;
- }
- //播放状态--停止
- else if( SongInfo.state == Stop )
- {
- BSP_AUDIO_OUT_Stop(CODEC_PDWN_SW);
- f_close( &file_object );
-
- SongInfo.state = OK;
- return 1;
- }
- //播放状态--错误
- else if( Audio_ErrorFlag == 1 )
- {
- BSP_AUDIO_OUT_Stop(CODEC_PDWN_SW);
- f_close( &file_object );
- return 0;
- }
-
- Audio_Delay_1ms( 3 );
- }
-
- //计算当前歌曲时间点
- SongInfo.time_count = SongInfo.file_ptr * 8 / SongInfo.Bitrate ;
- //printf("SongInfo.time_count:%d\r\n",SongInfo.time_count);
-
- //audio_output_buffer已通过DMA传输一半
- if( TxHalfCpltFlag == 1 )
- {
- //解码MP3音频数据,用来填充audio_output_buffer
- while( 1 )
- {
- nSamples = SpiritMP3Decode( &g_MP3Decoder, /* Decoder structure */
- (short *)&audio_output_buffer[0], /* [OUT] Output PCM buffer */
- MPEGAudioFrameInfo.mSamplesPerFrame, /* Number of samples to decode (1 sample = 32 bit = 2ch*16 bit) */
- &MP3Info /* [OUT, opt] Optional informational structure */
- );
- /*到达文件末尾, 则退出*/
- if( nSamples < MPEGAudioFrameInfo.mSamplesPerFrame ||index!= mp3Current)
- {
- BSP_AUDIO_OUT_Stop(CODEC_PDWN_SW);
- f_close( &file_object );
- return 1;
- }
-
- if( !( !MP3Info.IsGoodStream || (MP3Info.nBitrateKbps == 0) || (MP3Info.nSampleRateHz == 0) ) )
- {
- break;
- }
- }
- }
-
- //audio_output_buffer已通过DMA全部传输完成
- if( TxCpltFlag == 1 )
- {
- while( 1 )
- {
- nSamples = SpiritMP3Decode( &g_MP3Decoder, /* Decoder structure */
- (short *)&audio_output_buffer[MPEGAudioFrameInfo.mSamplesPerFrame*2], /* [OUT] Output PCM buffer */
- MPEGAudioFrameInfo.mSamplesPerFrame, /* Number of samples to decode (1 sample = 32 bit = 2ch*16 bit) */
- &MP3Info /* [OUT, opt] Optional informational structure */
- );
- /*到达文件末尾,则退出*/
- if( nSamples < MPEGAudioFrameInfo.mSamplesPerFrame )
- {
- BSP_AUDIO_OUT_Stop(CODEC_PDWN_SW);
- f_close( &file_object );
- return 1;
- }
-
- if( !( !MP3Info.IsGoodStream || (MP3Info.nBitrateKbps == 0) || (MP3Info.nSampleRateHz == 0) ) )
- {
- break;
- }
- }
- }
-
- if( TxHalfCpltFlag == 1 && TxCpltFlag == 1 )
- {
- TxHalfCpltFlag = 0;
- TxCpltFlag = 0;
-
- BSP_AUDIO_OUT_Resume(0);
- }
- else if( TxHalfCpltFlag == 1 ) TxHalfCpltFlag = 0;
- else if( TxCpltFlag == 1 ) TxCpltFlag = 0;
-
- AudioOutFlag = 1;
- }
-
- return 1;
- }
七、中断处理
- void DMA2_Stream1_IRQHandler(void)
- {
- /* USER CODE BEGIN DMA2_Stream1_IRQn 0 */
- HAL_DMA_IRQHandler(haudio_out_sai.hdmatx);
- /* USER CODE END DMA2_Stream1_IRQn 0 */
-
- /* USER CODE BEGIN DMA2_Stream1_IRQn 1 */
- /* USER CODE END DMA2_Stream1_IRQn 1 */
- }
回调
- void BSP_AUDIO_OUT_TransferComplete_CallBack(uint32_t Instance)
- {
- TxCpltFlag = 1;
- AudioOutFlag = 0;
- TxBufNum = 0;
- //printf("BSP_AUDIO_OUT_TransferComplete_CallBack\r\n");
- if( TxHalfCpltFlag == 1 )
- {
- BSP_AUDIO_OUT_Pause(0);
- TxBufNum = 1;
- printf("fault1\r\n");
- }
- }
- void BSP_AUDIO_OUT_HalfTransfer_CallBack(uint32_t Instance)
- {
- TxHalfCpltFlag = 1;
- AudioOutFlag = 0;
- TxBufNum = 1;
- //printf("BSP_AUDIO_OUT_HalfTransfer_CallBack\r\n");
- if( TxCpltFlag == 1 )
- {
- BSP_AUDIO_OUT_Pause(0);
- TxBufNum = 0;
- printf("fault2\r\n");
- }
- }
八、播放DEMO
- void mp3_player_demo(void)
- {
- if( st_mp3_player_Init( mp3Files[mp3Current%mp3Count], 80, 0) == 1 )
- {
- printf("mp3 init ok\r\n");
- st_mp3play( 0 );
- }
- }
- void BSP_PB_Callback(Button_TypeDef Button)
- {
- printf("BSP_PB_Callback\r\n");
- if(Button==BUTTON_USER)
- {
- mp3Current++;
- if(mp3Current>=mp3Count) mp3Current=0;
-
- }
- }
按键处理