【前言】
在前面,我驱动了SD卡并实现在fatfs功能,这一篇分享如何驱动音频。
【硬件】
在开发板的原理图上,可以看到我们这块开发板使用的音频为CS43L22如下图所示:
【驱动移植】
驱动有很多例子都是在用的,比如\STM32Cube_FW_F4_V1.26.2\Projects\STM32469I-Discovery\Applications\Audio\Audio_playback_and_record以及STM32Cube_FW_F4_V1.26.2\Projects\STM32469I-Discovery\Examples\BSP都有他的驱动的。
经分析这两个例子,都是使用了官方的驱动,他的驱动在:\STM32Cube_FW_F4_V1.26.2\Drivers\BSP\STM32469I-Discovery,当然还需要调用STM32Cube_FW_F4_V1.26.2\Drivers\BSP\Components\cs43l22下的驱动。
1、引入cs43l22.c到工程Drivers/BSP/Components下面,同时把板载audio驱动stm32f469_discovery_audio.c引入到工程的/Drivers/BSP/STM32f469-Discovery目录下面:
2、复制示例STM32Cube_FW_F4_V1.26.2\Projects\STM32469I-Discovery\Examples\BSP\Src下面的audio_play.c到工程User目录下面:
3、在stm32f4xx_it.c中添加audio的dma中断函数:
void AUDIO_I2Sx_DMAx_IRQHandler(void) { HAL_DMA_IRQHandler(haudio_in_i2s.hdmarx); } void AUDIO_SAIx_DMAx_IRQHandler(void) { HAL_DMA_IRQHandler(haudio_out_sai.hdmatx); }
4、原先读取的是rom里面的数据,在这个工程中,需要修改为读取sd卡中的wav数据。
因此修改代码如下:
/** ****************************************************************************** * @file BSP/Src/audio.c_play * @author MCD Application Team * @brief This example code shows how to use the audio feature in the * stm32469i_discovery driver ****************************************************************************** * @attention * * <h2><center>© COPYRIGHT(c) 2017 STMicroelectronics</center></h2> * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of STMicroelectronics nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include <stdio.h> /** @addtogroup STM32F4xx_HAL_Examples * @{ */ /** @addtogroup BSP * @{ */ /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ /*Since SysTick is set to 1ms (unless to set it quicker) */ /* to run up to 48khz, a buffer around 1000 (or more) is requested*/ /* to run up to 96khz, a buffer around 2000 (or more) is requested*/ #define AUDIO_DEFAULT_VOLUME 70 /* Audio file size and start address are defined here since the audio file is stored in Flash memory as a constant table of 16-bit data */ #define AUDIO_START_OFFSET_ADDRESS 0 /* Offset relative to audio file header size */ #define AUDIO_BUFFER_SIZE extern float AudioCurrentTime; /* Private typedef -----------------------------------------------------------*/ typedef enum { AUDIO_STATE_IDLE = 0, AUDIO_STATE_INIT, AUDIO_STATE_PLAYING, }AUDIO_PLAYBACK_StateTypeDef; typedef enum { BUFFER_OFFSET_NONE = 0, BUFFER_OFFSET_HALF, BUFFER_OFFSET_FULL, }BUFFER_StateTypeDef; typedef struct { uint8_t buff[AUDIO_BUFFER_SIZE]; uint32_t fptr; BUFFER_StateTypeDef state; uint32_t AudioFileSize; uint32_t *SrcAddress; }AUDIO_BufferTypeDef; typedef enum { TS_ACT_NONE = 0, TS_ACT_FREQ_DOWN, TS_ACT_FREQ_UP, TS_ACT_VOLUME_DOWN, TS_ACT_VOLUME_UP, TS_ACT_PAUSE = 0xFE }TS_ActionTypeDef; /* Private macro -------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ static AUDIO_BufferTypeDef buffer_ctl; static AUDIO_PLAYBACK_StateTypeDef audio_state; __IO uint32_t uwVolume = 50; __IO uint32_t uwPauseEnabledStatus = 0; /* Private function prototypes -----------------------------------------------*/ uint32_t GetData(void *pdata, uint32_t offset, uint8_t *pbuf, uint32_t NbrOfData); extern uint32_t AudioFre; extern char* pDirectoryFiles[MAX_BMP_FILES]; extern uint8_t corruntPlayingFileNo; /* Private functions ---------------------------------------------------------*/ /** * @brief Starts Audio streaming. * @param None * @retval Audio error */ AUDIO_ErrorTypeDef AUDIO_Play_Start(uint32_t *psrc_address, uint32_t file_size) { uint32_t bytesread; buffer_ctl.state = BUFFER_OFFSET_NONE; buffer_ctl.AudioFileSize = file_size; buffer_ctl.SrcAddress = psrc_address; bytesread = GetData( (void *)psrc_address, 0, &buffer_ctl.buff[0], AUDIO_BUFFER_SIZE); if(bytesread > 0) { BSP_AUDIO_OUT_Play((uint16_t *)&buffer_ctl.buff[0], AUDIO_BUFFER_SIZE); audio_state = AUDIO_STATE_PLAYING; buffer_ctl.fptr = bytesread; return AUDIO_ERROR_NONE; } return AUDIO_ERROR_IO; } /** * @brief Manages Audio process. * @param None * @retval Audio error */ uint8_t AUDIO_Play_Process(void) { uint32_t bytesread; AUDIO_ErrorTypeDef error_state = AUDIO_ERROR_NONE; switch(audio_state) { case AUDIO_STATE_PLAYING: if(buffer_ctl.fptr >= buffer_ctl.AudioFileSize) { /* Play audio sample again ... */ buffer_ctl.fptr = 0; error_state = AUDIO_ERROR_EOF; } /* 1st half buffer played; so fill it and continue playing from bottom*/ if(buffer_ctl.state == BUFFER_OFFSET_HALF) { AudioCurrentTime+=(float)AUDIO_BUFFER_SIZE/(8*(float)AudioFre); bytesread =8*1024; Storage_OpenRead_8KB_File(&buffer_ctl.buff[0], pDirectoryFiles[corruntPlayingFileNo]); if( bytesread >0) { buffer_ctl.state = BUFFER_OFFSET_NONE; buffer_ctl.fptr += bytesread; } } /* 2nd half buffer played; so fill it and continue playing from top */ if(buffer_ctl.state == BUFFER_OFFSET_FULL) { AudioCurrentTime+=(float)AUDIO_BUFFER_SIZE/(8*(float)AudioFre); bytesread =8*1024; Storage_OpenRead_8KB_File(&buffer_ctl.buff[AUDIO_BUFFER_SIZE /2], pDirectoryFiles[corruntPlayingFileNo]); if( bytesread > 0) { buffer_ctl.state = BUFFER_OFFSET_NONE; buffer_ctl.fptr += bytesread; } } break; default: error_state = AUDIO_ERROR_NOTREADY; break; } return (uint8_t) error_state; } /*------------------------------------------------------------------------------ Callbacks implementation: the callbacks API are defined __weak in the stm32469i_discovery_audio.c file and their implementation should be done the user code if they are needed. Below some examples of callback implementations. ----------------------------------------------------------------------------*/ /** * @brief Manages the full Transfer complete event. * @param None * @retval None */ void BSP_AUDIO_OUT_TransferComplete_CallBack(void) { if(audio_state == AUDIO_STATE_PLAYING) { /* allows AUDIO_Play_Process() to refill 2nd part of the buffer */ buffer_ctl.state = BUFFER_OFFSET_FULL; } } /** * @brief Manages the DMA Half Transfer complete event. * @param None * @retval None */ void BSP_AUDIO_OUT_HalfTransfer_CallBack(void) { if(audio_state == AUDIO_STATE_PLAYING) { /* allows AUDIO_Play_Process() to refill 1st part of the buffer */ buffer_ctl.state = BUFFER_OFFSET_HALF; } } /** * @brief Manages the DMA FIFO error event. * @param None * @retval None */ /** * @brief Gets Data from storage unit. * @param None * @retval None */ uint32_t GetData(void *pdata, uint32_t offset, uint8_t *pbuf, uint32_t NbrOfData) { uint8_t *lptr = pdata; uint32_t ReadDataNbr; ReadDataNbr = 0; while(((offset + ReadDataNbr) < buffer_ctl.AudioFileSize) && (ReadDataNbr < NbrOfData)) { pbuf[ReadDataNbr]= lptr [offset + ReadDataNbr]; ReadDataNbr++; } return ReadDataNbr; } /** * @} */ /** * @} */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
6、在这个函数中,我们采用i2s dma传输,采用半传输中断与传输完中断,在不同的中断产生后,我们向缓冲区读入数据来实现不间隔断的播放功能。