本帖最后由 sujingliang 于 2025-2-9 14:57 编辑
STM32H745I-DISCO没有播放WAV文件的例程,但是STM32H743I-EVAL下有一个例程,可以照葫芦画瓢实现一下。
1、定义一些常量
- #define AUDIO_FREQUENCY SAI_AUDIO_FREQUENCY_16K
- #define AUDIO_CHANNEL_NUMBER 2U
- #define AUDIO_BUFFER_SIZE 256U
- #define AUDIO_PCM_CHUNK_SIZE 32U
- #define AUDIO_FILE_ADDRESS 0x08080000
- #define AUDIO_FILE_SIZE (180*1024)
- #define PLAY_HEADER 0x2C
- #define PLAY_BUFF_SIZE 4096
音频采样频率:16 kHz。
音频通道数:2(立体声)。
缓冲区大小:256字节(用于DMA传输)。
PCM数据块大小:32字节。
音频文件存储地址:0x08080000。
音频文件大小:180 KB。
音频文件头大小:44字节。
播放缓冲区大小:4 KB。
- __IO int16_t UpdatePointer = -1;
UpdatePointer变量用于管理双缓冲机制。当DMA传输完一半缓冲区时,会更新UpdatePointer,指示需要重新填充哪一半缓冲区。
- ALIGN_32BYTES (uint16_t PlayBuff[PLAY_BUFF_SIZE]);
从预定义的内存地址(AUDIO_FILE_ADDRESS)加载音频数据到播放缓冲区(PlayBuff)。音频数据包括文件头(PLAY_HEADER)和实际的播放数据(PLAY_BUFF_SIZE)
2、main函数
- int main(void)
- {
- /* System Init, System clock, voltage scaling and L1-Cache configuration are done by CPU1 (Cortex-M7)
- in the meantime Domain D2 is put in STOP mode(Cortex-M4 in deep-sleep)
- */
- __IO uint32_t PlaybackPosition = PLAY_BUFF_SIZE + PLAY_HEADER;
- /* Configure the MPU attributes */
- MPU_Config();
- /* Enable the CPU Cache */
- CPU_CACHE_Enable();
- /* STM32H7xx HAL library initialization:
- - Systick timer is configured by default as source of time base, but user
- can eventually implement his proper time base source (a general purpose
- timer for example or other time source), keeping in mind that Time base
- duration should be kept 1ms since PPP_TIMEOUT_VALUEs are defined and
- handled in milliseconds basis.
- - Set NVIC Group Priority to 4
- - Low Level Initialization
- */
- HAL_Init();
- /* Configure the system clock to 400 MHz */
- SystemClock_Config();
- /* When system initialization is finished, Cortex-M7 could wakeup (when needed) the Cortex-M4 by means of
- HSEM notification or by any D2 wakeup source (SEV,EXTI..) */
- /* Configure LED1 */
- BSP_LED_Init(LED1);
- /* Configure LED2 */
- BSP_LED_Init(LED2);
- /* Initialize playback */
- Playback_Init();
- WM8994_Init_t codec_init;
-
- codec_init.Resolution = 0;
-
- /* Fill codec_init structure */
- codec_init.Frequency = 16000;
- codec_init.InputDevice = WM8994_IN_NONE;
- codec_init.OutputDevice = AUDIO_OUT_DEVICE_HEADPHONE;
-
- /* Convert volume before sending to the codec */
- codec_init.Volume = VOLUME_OUT_CONVERT(30);
-
-
-
- /* Initialize the data buffer */
- for(int i=0; i < PLAY_BUFF_SIZE; i+=2)
- {
- PlayBuff[i]=*((__IO uint16_t *)(AUDIO_FILE_ADDRESS + PLAY_HEADER + i));
- }
-
-
- /* Start the playback */
- if(Audio_Drv->Init(Audio_CompObj, &codec_init) != 0)
- {
- Error_Handler();
- }
-
- /* Start the playback */
- if(Audio_Drv->Play(Audio_CompObj) < 0)
- {
- Error_Handler();
- }
-
-
- if(HAL_OK != HAL_SAI_Transmit_DMA(&SaiOutputHandle, (uint8_t *)PlayBuff, PLAY_BUFF_SIZE))
- {
- Error_Handler();
- }
- /* Start loopback */
- while(1)
- {
-
- /* Wait a callback event */
- while(UpdatePointer==-1);
-
- int position = UpdatePointer;
- UpdatePointer = -1;
-
- /* Update the first or the second part of the buffer */
- for(int i = 0; i < PLAY_BUFF_SIZE/2; i++)
- {
- PlayBuff[i+position] = *(uint16_t *)(AUDIO_FILE_ADDRESS + PlaybackPosition);
- PlaybackPosition+=2;
- }
-
- /* Clean Data Cache to update the content of the SRAM */
- SCB_CleanDCache_by_Addr((uint32_t*)&PlayBuff[position], PLAY_BUFF_SIZE);
-
- /* check the end of the file */
- if((PlaybackPosition+PLAY_BUFF_SIZE/2) > AUDIO_FILE_SIZE)
- {
- PlaybackPosition = PLAY_HEADER;
- }
-
- if(UpdatePointer != -1)
- {
- /* Buffer update time is too long compare to the data transfer time */
- Error_Handler();
- }
-
-
- }
- }
Playback_Init():初始化音频播放系统。
WM8994_Init_t codec_init:配置音频编解码器(WM8994),设置采样率、输入/输出设备、音量等参数。
Audio_Drv->Init():初始化音频驱动,传入编解码器的配置参数。
Audio_Drv->Play():开始音频播放。
HAL_SAI_Transmit_DMA():配置SAI(串行音频接口)外设,使用DMA(直接内存访问)将音频数据从PlayBuff传输到音频编解码器。DMA可以在后台完成数据传输,减少CPU的负担。
主循环(音频播放处理)
UpdatePointer 机制:UpdatePointer变量用于管理双缓冲机制。当DMA传输完一半缓冲区时,会更新UpdatePointer,指示需要重新填充哪一半缓冲区。
缓冲区填充:主循环检查UpdatePointer,并从AUDIO_FILE_ADDRESS中加载新的音频数据到相应的缓冲区部分。
缓存维护:SCB_CleanDCache_by_Addr()函数确保更新后的音频数据写入SRAM,而不是仅仅停留在缓存中。
文件结束处理:如果音频文件播放完毕,播放位置会重置到文件开头(PLAY_HEADER)
双缓冲机制:使用两个缓冲区部分(PlayBuff的两半)实现连续音频播放,避免播放中断。当一半在播放时,另一半在填充新数据。
DMA 高效传输:DMA用于数据传输,减少CPU的负担,使其可以处理其他任务。
缓存管理:在带有缓存的系统中,正确维护缓存是确保CPU和外设之间数据一致性的关键。
3、SAI PCM Output init
|