以下为在单缓冲程序基础上修改

/*** Enable DMA controller clock*/
static void MX_DMA_Init(void)
{/* DMA controller clock enable */__HAL_RCC_DMA2_CLK_ENABLE();/* DMA interrupt init *//* DMA2_Stream1_IRQn interrupt configuration */HAL_NVIC_SetPriority(DMA2_Stream1_IRQn, 1, 0);HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn);/* DMA2_Stream3_IRQn interrupt configuration */HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 0, 0);HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);/* DMA2_Stream6_IRQn interrupt configuration */HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 0, 0);HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn);}
/* USER CODE BEGIN Header */
/********************************************************************************* @file    stm32f7xx_it.c* @brief   Interrupt Service Routines.******************************************************************************* @attention** <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.* All rights reserved.</center></h2>** This software component is licensed by ST under BSD 3-Clause license,* the "License"; You may not use this file except in compliance with the* License. You may obtain a copy of the License at:*                        opensource.org/licenses/BSD-3-Clause********************************************************************************/
/* USER CODE END Header *//* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f7xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */#include "fifo.h"
#include "bsp_wavplay.h"
#include "bsp_mp3play.h"
#include "bsp_flacplay.h"
#include "bsp_sai.h"/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD *//* USER CODE END TD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END 0 *//* External variables --------------------------------------------------------*/
extern DMA_HandleTypeDef hdma_sai1_a;
extern DMA_HandleTypeDef hdma_sdmmc1_rx;
extern DMA_HandleTypeDef hdma_sdmmc1_tx;
extern SD_HandleTypeDef hsd1;
/* USER CODE BEGIN EV *//* USER CODE END EV *//******************************************************************************/
/*           Cortex-M7 Processor Interruption and Exception Handlers          */
/******************************************************************************/
/*** @brief This function handles Non maskable interrupt.*/
void NMI_Handler(void)
{/* USER CODE BEGIN NonMaskableInt_IRQn 0 *//* USER CODE END NonMaskableInt_IRQn 0 */HAL_RCC_NMI_IRQHandler();/* USER CODE BEGIN NonMaskableInt_IRQn 1 */while (1){}/* USER CODE END NonMaskableInt_IRQn 1 */
}/*** @brief This function handles Hard fault interrupt.*/
void HardFault_Handler(void)
{/* USER CODE BEGIN HardFault_IRQn 0 *//* USER CODE END HardFault_IRQn 0 */while (1){/* USER CODE BEGIN W1_HardFault_IRQn 0 *//* USER CODE END W1_HardFault_IRQn 0 */}
}/*** @brief This function handles Memory management fault.*/
void MemManage_Handler(void)
{/* USER CODE BEGIN MemoryManagement_IRQn 0 *//* USER CODE END MemoryManagement_IRQn 0 */while (1){/* USER CODE BEGIN W1_MemoryManagement_IRQn 0 *//* USER CODE END W1_MemoryManagement_IRQn 0 */}
}/*** @brief This function handles Pre-fetch fault, memory access fault.*/
void BusFault_Handler(void)
{/* USER CODE BEGIN BusFault_IRQn 0 *//* USER CODE END BusFault_IRQn 0 */while (1){/* USER CODE BEGIN W1_BusFault_IRQn 0 *//* USER CODE END W1_BusFault_IRQn 0 */}
}/*** @brief This function handles Undefined instruction or illegal state.*/
void UsageFault_Handler(void)
{/* USER CODE BEGIN UsageFault_IRQn 0 *//* USER CODE END UsageFault_IRQn 0 */while (1){/* USER CODE BEGIN W1_UsageFault_IRQn 0 *//* USER CODE END W1_UsageFault_IRQn 0 */}
}/*** @brief This function handles System service call via SWI instruction.*/
void SVC_Handler(void)
{/* USER CODE BEGIN SVCall_IRQn 0 *//* USER CODE END SVCall_IRQn 0 *//* USER CODE BEGIN SVCall_IRQn 1 *//* USER CODE END SVCall_IRQn 1 */
}/*** @brief This function handles Debug monitor.*/
void DebugMon_Handler(void)
{/* USER CODE BEGIN DebugMonitor_IRQn 0 *//* USER CODE END DebugMonitor_IRQn 0 *//* USER CODE BEGIN DebugMonitor_IRQn 1 *//* USER CODE END DebugMonitor_IRQn 1 */
}/*** @brief This function handles Pendable request for system service.*/
void PendSV_Handler(void)
{/* USER CODE BEGIN PendSV_IRQn 0 *//* USER CODE END PendSV_IRQn 0 *//* USER CODE BEGIN PendSV_IRQn 1 *//* USER CODE END PendSV_IRQn 1 */
}/*** @brief This function handles System tick timer.*/
void SysTick_Handler(void)
{/* USER CODE BEGIN SysTick_IRQn 0 *//* USER CODE END SysTick_IRQn 0 */HAL_IncTick();/* USER CODE BEGIN SysTick_IRQn 1 *//* USER CODE END SysTick_IRQn 1 */
}/******************************************************************************/
/* STM32F7xx Peripheral Interrupt Handlers                                    */
/* Add here the Interrupt Handlers for the used peripherals.                  */
/* For the available peripheral interrupt handler names,                      */
/* please refer to the startup file (startup_stm32f7xx.s).                    */
/******************************************************************************//*** @brief This function handles SDMMC1 global interrupt.*/
void SDMMC1_IRQHandler(void)
{/* USER CODE BEGIN SDMMC1_IRQn 0 *//* USER CODE END SDMMC1_IRQn 0 */HAL_SD_IRQHandler(&hsd1);/* USER CODE BEGIN SDMMC1_IRQn 1 *//* USER CODE END SDMMC1_IRQn 1 */
}/*** @brief This function handles DMA2 stream1 global interrupt.*/
void DMA2_Stream1_IRQHandler(void)
{/* USER CODE BEGIN DMA2_Stream1_IRQn 0 *//* USER CODE END DMA2_Stream1_IRQn 0 */HAL_DMA_IRQHandler(&hdma_sai1_a);/* USER CODE BEGIN DMA2_Stream1_IRQn 1 *//* USER CODE END DMA2_Stream1_IRQn 1 */
}/*** @brief This function handles DMA2 stream3 global interrupt.*/
void DMA2_Stream3_IRQHandler(void)
{/* USER CODE BEGIN DMA2_Stream3_IRQn 0 *//* USER CODE END DMA2_Stream3_IRQn 0 */HAL_DMA_IRQHandler(&hdma_sdmmc1_rx);/* USER CODE BEGIN DMA2_Stream3_IRQn 1 *//* USER CODE END DMA2_Stream3_IRQn 1 */
}/*** @brief This function handles DMA2 stream6 global interrupt.*/
void DMA2_Stream6_IRQHandler(void)
{/* USER CODE BEGIN DMA2_Stream6_IRQn 0 *//* USER CODE END DMA2_Stream6_IRQn 0 */HAL_DMA_IRQHandler(&hdma_sdmmc1_tx);/* USER CODE BEGIN DMA2_Stream6_IRQn 1 *//* USER CODE END DMA2_Stream6_IRQn 1 */
}/* USER CODE BEGIN 1 *//*** @brief Rx Transfer completed callbacks* @param hsd: Pointer SD handle* @retval None*/
void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
{HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_1);rx_done = 1;
}/*** @brief Tx Transfer completed callbacks* @param hsd: Pointer to SD handle* @retval None*/
void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
{HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_1);tx_done = 1;
}/*** @brief SD error callbacks* @param hsd: Pointer SD handle* @retval None*/
void HAL_SD_ErrorCallback(SD_HandleTypeDef *hsd)
{HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
}/* USER CODE END 1 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
/* USER CODE BEGIN Header */
/********************************************************************************* @file         stm32f7xx_hal_msp.c* @brief        This file provides code for the MSP Initialization*               and de-Initialization codes.******************************************************************************* @attention** <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.* All rights reserved.</center></h2>** This software component is licensed by ST under BSD 3-Clause license,* the "License"; You may not use this file except in compliance with the* License. You may obtain a copy of the License at:*                        opensource.org/licenses/BSD-3-Clause********************************************************************************/
/* USER CODE END Header *//* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes *//* USER CODE END Includes */
extern DMA_HandleTypeDef hdma_sdmmc1_rx;extern DMA_HandleTypeDef hdma_sdmmc1_tx;/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD *//* USER CODE END TD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN Define *//* USER CODE END Define *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN Macro *//* USER CODE END Macro *//* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* External functions --------------------------------------------------------*/
/* USER CODE BEGIN ExternalFunctions *//* USER CODE END ExternalFunctions *//* USER CODE BEGIN 0 *//* USER CODE END 0 */
/*** Initializes the Global MSP.*/
void HAL_MspInit(void)
{/* USER CODE BEGIN MspInit 0 *//* USER CODE END MspInit 0 */__HAL_RCC_PWR_CLK_ENABLE();__HAL_RCC_SYSCFG_CLK_ENABLE();/* System interrupt init*//* USER CODE BEGIN MspInit 1 *//* USER CODE END MspInit 1 */
}/**
* @brief SD MSP Initialization
* This function configures the hardware resources used in this example
* @param hsd: SD handle pointer
* @retval None
*/
void HAL_SD_MspInit(SD_HandleTypeDef* hsd)
{GPIO_InitTypeDef GPIO_InitStruct = {0};if(hsd->Instance==SDMMC1){/* USER CODE BEGIN SDMMC1_MspInit 0 *//* USER CODE END SDMMC1_MspInit 0 *//* Peripheral clock enable */__HAL_RCC_SDMMC1_CLK_ENABLE();__HAL_RCC_GPIOC_CLK_ENABLE();__HAL_RCC_GPIOD_CLK_ENABLE();/**SDMMC1 GPIO ConfigurationPC8     ------> SDMMC1_D0PC9     ------> SDMMC1_D1PC10     ------> SDMMC1_D2PC11     ------> SDMMC1_D3PC12     ------> SDMMC1_CKPD2     ------> SDMMC1_CMD*/GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);GPIO_InitStruct.Pin = GPIO_PIN_2;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);/* SDMMC1 DMA Init *//* SDMMC1_RX Init */hdma_sdmmc1_rx.Instance = DMA2_Stream3;hdma_sdmmc1_rx.Init.Channel = DMA_CHANNEL_4;hdma_sdmmc1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;hdma_sdmmc1_rx.Init.PeriphInc = DMA_PINC_DISABLE;hdma_sdmmc1_rx.Init.MemInc = DMA_MINC_ENABLE;hdma_sdmmc1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;hdma_sdmmc1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;hdma_sdmmc1_rx.Init.Mode = DMA_PFCTRL;hdma_sdmmc1_rx.Init.Priority = DMA_PRIORITY_LOW;hdma_sdmmc1_rx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;hdma_sdmmc1_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;hdma_sdmmc1_rx.Init.MemBurst = DMA_MBURST_INC4;hdma_sdmmc1_rx.Init.PeriphBurst = DMA_PBURST_INC4;if (HAL_DMA_Init(&hdma_sdmmc1_rx) != HAL_OK){Error_Handler();}__HAL_LINKDMA(hsd,hdmarx,hdma_sdmmc1_rx);/* SDMMC1_TX Init */hdma_sdmmc1_tx.Instance = DMA2_Stream6;hdma_sdmmc1_tx.Init.Channel = DMA_CHANNEL_4;hdma_sdmmc1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;hdma_sdmmc1_tx.Init.PeriphInc = DMA_PINC_DISABLE;hdma_sdmmc1_tx.Init.MemInc = DMA_MINC_ENABLE;hdma_sdmmc1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;hdma_sdmmc1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;hdma_sdmmc1_tx.Init.Mode = DMA_PFCTRL;hdma_sdmmc1_tx.Init.Priority = DMA_PRIORITY_LOW;hdma_sdmmc1_tx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;hdma_sdmmc1_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;hdma_sdmmc1_tx.Init.MemBurst = DMA_MBURST_INC4;hdma_sdmmc1_tx.Init.PeriphBurst = DMA_PBURST_INC4;if (HAL_DMA_Init(&hdma_sdmmc1_tx) != HAL_OK){Error_Handler();}__HAL_LINKDMA(hsd,hdmatx,hdma_sdmmc1_tx);/* SDMMC1 interrupt Init */HAL_NVIC_SetPriority(SDMMC1_IRQn, 3, 0);HAL_NVIC_EnableIRQ(SDMMC1_IRQn);/* USER CODE BEGIN SDMMC1_MspInit 1 *//* USER CODE END SDMMC1_MspInit 1 */}}/**
* @brief SD MSP De-Initialization
* This function freeze the hardware resources used in this example
* @param hsd: SD handle pointer
* @retval None
*/
void HAL_SD_MspDeInit(SD_HandleTypeDef* hsd)
{if(hsd->Instance==SDMMC1){/* USER CODE BEGIN SDMMC1_MspDeInit 0 *//* USER CODE END SDMMC1_MspDeInit 0 *//* Peripheral clock disable */__HAL_RCC_SDMMC1_CLK_DISABLE();/**SDMMC1 GPIO ConfigurationPC8     ------> SDMMC1_D0PC9     ------> SDMMC1_D1PC10     ------> SDMMC1_D2PC11     ------> SDMMC1_D3PC12     ------> SDMMC1_CKPD2     ------> SDMMC1_CMD*/HAL_GPIO_DeInit(GPIOC, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12);HAL_GPIO_DeInit(GPIOD, GPIO_PIN_2);/* SDMMC1 DMA DeInit */HAL_DMA_DeInit(hsd->hdmarx);HAL_DMA_DeInit(hsd->hdmatx);/* SDMMC1 interrupt DeInit */HAL_NVIC_DisableIRQ(SDMMC1_IRQn);/* USER CODE BEGIN SDMMC1_MspDeInit 1 *//* USER CODE END SDMMC1_MspDeInit 1 */}}/**
* @brief UART MSP Initialization
* This function configures the hardware resources used in this example
* @param huart: UART handle pointer
* @retval None
*/
void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{GPIO_InitTypeDef GPIO_InitStruct = {0};if(huart->Instance==USART1){/* USER CODE BEGIN USART1_MspInit 0 *//* USER CODE END USART1_MspInit 0 *//* Peripheral clock enable */__HAL_RCC_USART1_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();/**USART1 GPIO ConfigurationPA9     ------> USART1_TXPA10     ------> USART1_RX*/GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF7_USART1;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/* USER CODE BEGIN USART1_MspInit 1 *//* USER CODE END USART1_MspInit 1 */}}/**
* @brief UART MSP De-Initialization
* This function freeze the hardware resources used in this example
* @param huart: UART handle pointer
* @retval None
*/
void HAL_UART_MspDeInit(UART_HandleTypeDef* huart)
{if(huart->Instance==USART1){/* USER CODE BEGIN USART1_MspDeInit 0 *//* USER CODE END USART1_MspDeInit 0 *//* Peripheral clock disable */__HAL_RCC_USART1_CLK_DISABLE();/**USART1 GPIO ConfigurationPA9     ------> USART1_TXPA10     ------> USART1_RX*/HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);/* USER CODE BEGIN USART1_MspDeInit 1 *//* USER CODE END USART1_MspDeInit 1 */}}static uint32_t FMC_Initialized = 0;static void HAL_FMC_MspInit(void){/* USER CODE BEGIN FMC_MspInit 0 *//* USER CODE END FMC_MspInit 0 */GPIO_InitTypeDef GPIO_InitStruct ={0};if (FMC_Initialized) {return;}FMC_Initialized = 1;/* Peripheral clock enable */__HAL_RCC_FMC_CLK_ENABLE();/** FMC GPIO ConfigurationPF0   ------> FMC_A0PF1   ------> FMC_A1PF2   ------> FMC_A2PF3   ------> FMC_A3PF4   ------> FMC_A4PF5   ------> FMC_A5PC0   ------> FMC_SDNWEPC2   ------> FMC_SDNE0PC3   ------> FMC_SDCKE0PF11   ------> FMC_SDNRASPF12   ------> FMC_A6PF13   ------> FMC_A7PF14   ------> FMC_A8PF15   ------> FMC_A9PG0   ------> FMC_A10PG1   ------> FMC_A11PE7   ------> FMC_D4PE8   ------> FMC_D5PE9   ------> FMC_D6PE10   ------> FMC_D7PE11   ------> FMC_D8PE12   ------> FMC_D9PE13   ------> FMC_D10PE14   ------> FMC_D11PE15   ------> FMC_D12PD8   ------> FMC_D13PD9   ------> FMC_D14PD10   ------> FMC_D15PD14   ------> FMC_D0PD15   ------> FMC_D1PG2   ------> FMC_A12PG4   ------> FMC_BA0PG5   ------> FMC_BA1PG8   ------> FMC_SDCLKPD0   ------> FMC_D2PD1   ------> FMC_D3PG15   ------> FMC_SDNCASPE0   ------> FMC_NBL0PE1   ------> FMC_NBL1*/GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_FMC;HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_2|GPIO_PIN_3;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_FMC;HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_8|GPIO_PIN_15;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_FMC;HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_FMC;HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_FMC;HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);/* USER CODE BEGIN FMC_MspInit 1 *//* USER CODE END FMC_MspInit 1 */
}void HAL_SDRAM_MspInit(SDRAM_HandleTypeDef* hsdram){/* USER CODE BEGIN SDRAM_MspInit 0 *//* USER CODE END SDRAM_MspInit 0 */HAL_FMC_MspInit();/* USER CODE BEGIN SDRAM_MspInit 1 *//* USER CODE END SDRAM_MspInit 1 */
}static uint32_t FMC_DeInitialized = 0;static void HAL_FMC_MspDeInit(void){/* USER CODE BEGIN FMC_MspDeInit 0 *//* USER CODE END FMC_MspDeInit 0 */if (FMC_DeInitialized) {return;}FMC_DeInitialized = 1;/* Peripheral clock enable */__HAL_RCC_FMC_CLK_DISABLE();/** FMC GPIO ConfigurationPF0   ------> FMC_A0PF1   ------> FMC_A1PF2   ------> FMC_A2PF3   ------> FMC_A3PF4   ------> FMC_A4PF5   ------> FMC_A5PC0   ------> FMC_SDNWEPC2   ------> FMC_SDNE0PC3   ------> FMC_SDCKE0PF11   ------> FMC_SDNRASPF12   ------> FMC_A6PF13   ------> FMC_A7PF14   ------> FMC_A8PF15   ------> FMC_A9PG0   ------> FMC_A10PG1   ------> FMC_A11PE7   ------> FMC_D4PE8   ------> FMC_D5PE9   ------> FMC_D6PE10   ------> FMC_D7PE11   ------> FMC_D8PE12   ------> FMC_D9PE13   ------> FMC_D10PE14   ------> FMC_D11PE15   ------> FMC_D12PD8   ------> FMC_D13PD9   ------> FMC_D14PD10   ------> FMC_D15PD14   ------> FMC_D0PD15   ------> FMC_D1PG2   ------> FMC_A12PG4   ------> FMC_BA0PG5   ------> FMC_BA1PG8   ------> FMC_SDCLKPD0   ------> FMC_D2PD1   ------> FMC_D3PG15   ------> FMC_SDNCASPE0   ------> FMC_NBL0PE1   ------> FMC_NBL1*/HAL_GPIO_DeInit(GPIOF, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15);HAL_GPIO_DeInit(GPIOC, GPIO_PIN_0|GPIO_PIN_2|GPIO_PIN_3);HAL_GPIO_DeInit(GPIOG, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_8|GPIO_PIN_15);HAL_GPIO_DeInit(GPIOE, GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1);HAL_GPIO_DeInit(GPIOD, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1);/* USER CODE BEGIN FMC_MspDeInit 1 *//* USER CODE END FMC_MspDeInit 1 */
}void HAL_SDRAM_MspDeInit(SDRAM_HandleTypeDef* hsdram){/* USER CODE BEGIN SDRAM_MspDeInit 0 *//* USER CODE END SDRAM_MspDeInit 0 */HAL_FMC_MspDeInit();/* USER CODE BEGIN SDRAM_MspDeInit 1 *//* USER CODE END SDRAM_MspDeInit 1 */
}extern DMA_HandleTypeDef hdma_sai1_a;//static uint32_t SAI1_client =0;//void HAL_SAI_MspInit(SAI_HandleTypeDef* hsai)
//{//  GPIO_InitTypeDef GPIO_InitStruct;
///* SAI1 */
//    if(hsai->Instance==SAI1_Block_A)
//    {//    /* Peripheral clock enable */
//    if (SAI1_client == 0)
//    {//       __HAL_RCC_SAI1_CLK_ENABLE();
//    }
//    SAI1_client ++;//    /**SAI1_A_Block_A GPIO Configuration
//    PE2     ------> SAI1_MCLK_A
//    PE4     ------> SAI1_FS_A
//    PE5     ------> SAI1_SCK_A
//    PE6     ------> SAI1_SD_A
//    */
//    GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6;
//    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
//    GPIO_InitStruct.Pull = GPIO_NOPULL;
//    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
//    GPIO_InitStruct.Alternate = GPIO_AF6_SAI1;
//    HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);//      /* Peripheral DMA init*///    hdma_sai1_a.Instance = DMA2_Stream1;
//    hdma_sai1_a.Init.Channel = DMA_CHANNEL_0;
//    hdma_sai1_a.Init.Direction = DMA_MEMORY_TO_PERIPH;
//    hdma_sai1_a.Init.PeriphInc = DMA_PINC_DISABLE;
//    hdma_sai1_a.Init.MemInc = DMA_MINC_ENABLE;
//    hdma_sai1_a.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
//    hdma_sai1_a.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
//    hdma_sai1_a.Init.Mode = DMA_CIRCULAR;
//    hdma_sai1_a.Init.Priority = DMA_PRIORITY_LOW;
//    hdma_sai1_a.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
//    hdma_sai1_a.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;
//    hdma_sai1_a.Init.MemBurst = DMA_MBURST_SINGLE;
//    hdma_sai1_a.Init.PeriphBurst = DMA_PBURST_SINGLE;
//    if (HAL_DMA_Init(&hdma_sai1_a) != HAL_OK)
//    {//      Error_Handler();
//    }//    /* Several peripheral DMA handle pointers point to the same DMA handle.
//     Be aware that there is only one stream to perform all the requested DMAs. */
//    __HAL_LINKDMA(hsai,hdmarx,hdma_sai1_a);//    __HAL_LINKDMA(hsai,hdmatx,hdma_sai1_a);//    }
//}//void HAL_SAI_MspDeInit(SAI_HandleTypeDef* hsai)
//{///* SAI1 */
//    if(hsai->Instance==SAI1_Block_A)
//    {//    SAI1_client --;
//    if (SAI1_client == 0)
//      {//      /* Peripheral clock disable */
//       __HAL_RCC_SAI1_CLK_DISABLE();
//      }//    /**SAI1_A_Block_A GPIO Configuration
//    PE2     ------> SAI1_MCLK_A
//    PE4     ------> SAI1_FS_A
//    PE5     ------> SAI1_SCK_A
//    PE6     ------> SAI1_SD_A
//    */
//    HAL_GPIO_DeInit(GPIOE, GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6);//    /* SAI1 DMA Deinit */
//    HAL_DMA_DeInit(hsai->hdmarx);
//    HAL_DMA_DeInit(hsai->hdmatx);
//    }
//}/* USER CODE BEGIN 1 *//* USER CODE END 1 *//************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
#ifndef __BSP_SAI_H
#define __BSP_SAI_H#ifdef __cplusplus
extern "C" {#endif#include "main.h"extern void (*BSP_SAI_TX_Callback)(DMA_HandleTypeDef *_hdma);  //TX回调函数  void BSP_SAI1_Init(uint32_t datasize, uint32_t AudioFrequency);
uint8_t SAIA_SampleRate_Set(uint32_t samplerate);
void SAIA_TX_DMA_Init(uint8_t width);   #ifdef __cplusplus
}
#endif#endif /* __BSP_SAI_H */
#include "bsp_sai.h"
#include "bsp_wavplay.h"//SAI DMA回调函数指针
void (*BSP_SAI_TX_Callback)(DMA_HandleTypeDef *_hdma);  //TX回调函数  /*** @brief SAI1 Initialization Function* @param None* @retval None*/
void BSP_SAI1_Init(uint32_t datasize, uint32_t AudioFrequency)
{HAL_SAI_DeInit(&hsai_BlockA1);hsai_BlockA1.Instance = SAI1_Block_A;hsai_BlockA1.Init.AudioMode = SAI_MODEMASTER_TX;hsai_BlockA1.Init.Synchro = SAI_ASYNCHRONOUS;hsai_BlockA1.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE;hsai_BlockA1.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;hsai_BlockA1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_1QF;hsai_BlockA1.Init.AudioFrequency = AudioFrequency;hsai_BlockA1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;hsai_BlockA1.Init.MonoStereoMode = SAI_STEREOMODE;hsai_BlockA1.Init.CompandingMode = SAI_NOCOMPANDING;hsai_BlockA1.Init.TriState = SAI_OUTPUT_NOTRELEASED;if (HAL_SAI_InitProtocol(&hsai_BlockA1, SAI_I2S_STANDARD, datasize, 2) != HAL_OK){Error_Handler();}
}//SAI Block A采样率设置
//采样率计算公式:
//MCKDIV!=0: Fs=SAI_CK_x/[512*MCKDIV]
//MCKDIV==0: Fs=SAI_CK_x/256
//SAI_CK_x=(HSE/pllm)*PLLI2SN/PLLI2SQ/(PLLI2SDIVQ+1)
//一般HSE=25Mhz
//pllm:在Stm32_Clock_Init设置的时候确定,一般是25
//PLLI2SN:一般是192~432
//PLLI2SQ:2~15
//PLLI2SDIVQ:0~31
//MCKDIV:0~15
//SAI A分频系数表@pllm=8,HSE=25Mhz,即vco输入频率为1Mhz
const uint16_t SAI_PSC_TBL[][5]=
{{800 ,344,7,0,12}, //8Khz采样率{1102,429,2,18,2}, //11.025Khz采样率 {1600,344,7, 0,6},   //16Khz采样率{2205,429,2,18,1},    //22.05Khz采样率{3200,344,7, 0,3}, //32Khz采样率{4410,429,2,18,0},    //44.1Khz采样率{4800,344,7, 0,2},  //48Khz采样率{8820,271,2, 2,1},    //88.2Khz采样率{9600,344,7, 0,1},  //96Khz采样率{17640,271,6,0,0},    //176.4Khz采样率 {19200,295,6,0,0},    //192Khz采样率
};
//设置SAIA的采样率(@MCKEN)
//samplerate:采样率,单位:Hz
//返回值:0,设置成功;1,无法设置.
uint8_t SAIA_SampleRate_Set(uint32_t samplerate)
{ uint8_t i=0; uint32_t tempreg=0;samplerate/=10;//缩小10倍   for(i=0;i<(sizeof(SAI_PSC_TBL)/10);i++)//看看改采样率是否可以支持{if(samplerate==SAI_PSC_TBL[i][0])break;}RCC->CR&=~(1<<26);                        //先关闭PLLI2S  if(i==(sizeof(SAI_PSC_TBL)/10))return 1;//搜遍了也找不到tempreg|=(uint32_t)SAI_PSC_TBL[i][1]<<6;     //设置PLLI2SNtempreg|=(uint32_t)SAI_PSC_TBL[i][2]<<24; //设置PLLI2SQ RCC->PLLI2SCFGR=tempreg;                //设置I2SxCLK的频率 tempreg=RCC->DCKCFGR1;           tempreg&=~(0X1F);                      //清空PLLI2SDIVQ设置.tempreg&=~(0X03<<20);                   //清空SAI1ASRC设置.tempreg|=SAI_PSC_TBL[i][3]<<0;            //设置PLLI2SDIVQ tempreg|=1<<20;                           //设置SAI1A时钟来源为PLLI2SQRCC->DCKCFGR1=tempreg;                 //设置DCKCFGR寄存器 RCC->CR|=1<<26;                            //开启I2S时钟while((RCC->CR&1<<27)==0);              //等待I2S时钟开启成功. tempreg=SAI1_Block_A->CR1;           tempreg&=~(0X0F<<20);                    //清除MCKDIV设置tempreg|=(uint32_t)SAI_PSC_TBL[i][4]<<20;    //设置MCKDIVtempreg|=1<<16;                            //使能SAI1 Block Atempreg|=1<<17;                          //使能DMASAI1_Block_A->CR1=tempreg;               //配置MCKDIV,同时使能SAI1 Block A return 0;
}  void SAIA_TX_DMA_Init(uint8_t width)
{  /* Peripheral DMA init*///HAL_DMA_DeInit(&hdma_sai1_a);if(width==16){hdma_sai1_a.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;hdma_sai1_a.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;}else if(width==24){hdma_sai1_a.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;hdma_sai1_a.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;}hdma_sai1_a.Instance = DMA2_Stream1;hdma_sai1_a.Init.Channel = DMA_CHANNEL_0;hdma_sai1_a.Init.Direction = DMA_MEMORY_TO_PERIPH;hdma_sai1_a.Init.PeriphInc = DMA_PINC_DISABLE;hdma_sai1_a.Init.MemInc = DMA_MINC_ENABLE;hdma_sai1_a.Init.Mode = DMA_CIRCULAR;hdma_sai1_a.Init.Priority = DMA_PRIORITY_LOW;hdma_sai1_a.Init.FIFOMode = DMA_FIFOMODE_ENABLE;hdma_sai1_a.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;hdma_sai1_a.Init.MemBurst = DMA_MBURST_SINGLE;hdma_sai1_a.Init.PeriphBurst = DMA_PBURST_SINGLE;if (HAL_DMA_Init(&hdma_sai1_a) != HAL_OK){Error_Handler();}/* Several peripheral DMA handle pointers point to the same DMA handle.Be aware that there is only one stream to perform all the requested DMAs. */__HAL_LINKDMA(&hsai_BlockA1,hdmarx,hdma_sai1_a);__HAL_LINKDMA(&hsai_BlockA1,hdmatx,hdma_sai1_a);
}  static uint32_t SAI1_client =0;void HAL_SAI_MspInit(SAI_HandleTypeDef* hsai)
{GPIO_InitTypeDef GPIO_InitStruct;
/* SAI1 */if(hsai->Instance==SAI1_Block_A){/* Peripheral clock enable */if (SAI1_client == 0){__HAL_RCC_SAI1_CLK_ENABLE();}SAI1_client ++;/**SAI1_A_Block_A GPIO ConfigurationPE2     ------> SAI1_MCLK_APE4     ------> SAI1_FS_APE5     ------> SAI1_SCK_APE6     ------> SAI1_SD_A*/GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;GPIO_InitStruct.Alternate = GPIO_AF6_SAI1;HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);}
}void HAL_SAI_MspDeInit(SAI_HandleTypeDef* hsai)
{/* SAI1 */if(hsai->Instance==SAI1_Block_A){SAI1_client --;if (SAI1_client == 0){/* Peripheral clock disable */__HAL_RCC_SAI1_CLK_DISABLE();}/**SAI1_A_Block_A GPIO ConfigurationPE2     ------> SAI1_MCLK_APE4     ------> SAI1_FS_APE5     ------> SAI1_SCK_APE6     ------> SAI1_SD_A*/HAL_GPIO_DeInit(GPIOE, GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6);/* SAI1 DMA Deinit */HAL_DMA_DeInit(hsai->hdmarx);HAL_DMA_DeInit(hsai->hdmatx);}
}
#ifndef __BSP_WAVPLAY_H
#define __BSP_WAVPLAY_H#ifdef __cplusplus
extern "C" {#endif#include "main.h"//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//WAV 解码代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/18
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//********************************************************************************
//V1.0 说明
//1,支持16位/24位WAV文件播放
//2,最高可以支持到192K/24bit的WAV格式.
//  #define WAV_SAI_TX_DMA_BUFSIZE    4096      //定义WAV TX DMA 数组大小(播放192Kbps@24bit的时候,需要设置4096大才不会卡)//RIFF块
typedef __packed struct
{uint32_t ChunkID;          //chunk id;这里固定为"RIFF",即0X46464952uint32_t ChunkSize ;            //集合大小;文件总大小-8uint32_t Format;              //格式;WAVE,即0X45564157
}ChunkRIFF ;
//fmt块
typedef __packed struct
{uint32_t ChunkID;          //chunk id;这里固定为"fmt ",即0X20746D66uint32_t ChunkSize ;            //子集合大小(不包括ID和Size);这里为:20.uint16_t AudioFormat;        //音频格式;0X01,表示线性PCM;0X11表示IMA ADPCMuint16_t NumOfChannels;      //通道数量;1,表示单声道;2,表示双声道;uint32_t SampleRate;         //采样率;0X1F40,表示8Khzuint32_t ByteRate;           //字节速率; uint16_t BlockAlign;            //块对齐(字节); uint16_t BitsPerSample;      //单个采样数据大小;4位ADPCM,设置为4
//  uint16_t ByteExtraData;     //附加的数据字节;2个; 线性PCM,没有这个参数
}ChunkFMT;
//fact块
typedef __packed struct
{uint32_t ChunkID;          //chunk id;这里固定为"fact",即0X74636166;uint32_t ChunkSize ;           //子集合大小(不包括ID和Size);这里为:4.uint32_t NumOfSamples;        //采样的数量;
}ChunkFACT;
//LIST块
typedef __packed struct
{uint32_t ChunkID;          //chunk id;这里固定为"LIST",即0X74636166;uint32_t ChunkSize ;           //子集合大小(不包括ID和Size);这里为:4.
}ChunkLIST;//data块
typedef __packed struct
{uint32_t ChunkID;          //chunk id;这里固定为"data",即0X5453494Cuint32_t ChunkSize ;            //子集合大小(不包括ID和Size)
}ChunkDATA;//wav头
typedef __packed struct
{ ChunkRIFF riff;   //riff块ChunkFMT fmt;    //fmt块
//  ChunkFACT fact; //fact块 线性PCM,没有这个结构体    ChunkDATA data;    //data块
}__WaveHeader; //wav 播放控制结构体
typedef __packed struct
{ uint16_t audioformat;         //音频格式;0X01,表示线性PCM;0X11表示IMA ADPCMuint16_t nchannels;              //通道数量;1,表示单声道;2,表示双声道; uint16_t blockalign;                //块对齐(字节);  uint32_t datasize;              //WAV数据大小 uint32_t totsec ;             //整首歌时长,单位:秒uint32_t cursec ;               //当前播放时长uint32_t bitrate;               //比特率(位速)uint32_t samplerate;               //采样率 uint16_t bps;                 //位数,比如16bit,24bit,32bituint32_t datastart;             //数据帧开始的位置(在文件里面的偏移)
}__wavctrl; uint8_t wav_decode_init(uint8_t* fname,__wavctrl* wavx);
uint32_t wav_buffill(uint8_t *buf,uint16_t size,uint8_t bits);
//void wav_sai_dma_tx_callback(void);
uint8_t wav_play_song(uint8_t* fname);extern __wavctrl wavctrl;     //WAV控制结构体#ifdef __cplusplus
}
#endif#endif /* __BSP_WAVPLAY_H */
#include "bsp_wavplay.h"
#include "bsp_audioplay.h"
#include "bsp_printf.h"
#include "delay.h"
#include "bsp_malloc.h"
#include "ff.h"
#include "bsp_sai.h"
#include "bsp_wm8978.h"
#include "bsp_key.h"
#include "string.h"//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//WAV 解码代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/18
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//********************************************************************************
//V1.0 说明
//1,支持16位/24位WAV文件播放
//2,最高可以支持到192K/24bit的WAV格式.
//  __wavctrl wavctrl;      //WAV控制结构体
volatile uint8_t wavtransferend=0; //sai传输完成标志void BSP_SAI_WAV_M0_TX_Callback(DMA_HandleTypeDef *_hdma)    //TX回调函数
{uint32_t fillnum; if((audiodev.status&0X01)==0){memset(audiodev.saibuf1,0,WAV_SAI_TX_DMA_BUFSIZE);}else{fillnum=wav_buffill(audiodev.saibuf1,WAV_SAI_TX_DMA_BUFSIZE,wavctrl.bps);if(fillnum!=WAV_SAI_TX_DMA_BUFSIZE)//播放结束?{wavtransferend=1;} }
}void BSP_SAI_WAV_M1_TX_Callback(DMA_HandleTypeDef *_hdma)  //TX回调函数
{uint32_t fillnum; if((audiodev.status&0X01)==0){memset(audiodev.saibuf2,0,WAV_SAI_TX_DMA_BUFSIZE);}else{fillnum=wav_buffill(audiodev.saibuf2,WAV_SAI_TX_DMA_BUFSIZE,wavctrl.bps);if(fillnum!=WAV_SAI_TX_DMA_BUFSIZE)//播放结束?{wavtransferend=1;} }
} //WAV解析初始化
//fname:文件路径+文件名
//wavx:wav 信息存放结构体指针
//返回值:0,成功;1,打开文件失败;2,非WAV文件;3,DATA区域未找到.
uint8_t wav_decode_init(uint8_t* fname,__wavctrl* wavx)
{FIL*ftemp;uint8_t *buf; uint32_t br=0;uint8_t res=0;ChunkRIFF *riff;ChunkFMT *fmt;ChunkFACT *fact;ChunkDATA *data;ftemp=(FIL*)mymalloc(SRAMIN,sizeof(FIL));buf=mymalloc(SRAMIN,512);if(ftemp&&buf) //内存申请成功{res=f_open(ftemp,(TCHAR*)fname,FA_READ);//打开文件if(res==FR_OK){f_read(ftemp,buf,512,&br); //读取512字节在数据riff=(ChunkRIFF *)buf;     //获取RIFF块if(riff->Format==0X45564157)//是WAV文件{fmt=(ChunkFMT *)(buf+12);  //获取FMT块 fact=(ChunkFACT *)(buf+12+8+fmt->ChunkSize);//读取FACT块if(fact->ChunkID==0X74636166||fact->ChunkID==0X5453494C)wavx->datastart=12+8+fmt->ChunkSize+8+fact->ChunkSize;//具有fact/LIST块的时候(未测试)else wavx->datastart=12+8+fmt->ChunkSize;  data=(ChunkDATA *)(buf+wavx->datastart);  //读取DATA块if(data->ChunkID==0X61746164)//解析成功!{wavx->audioformat=fmt->AudioFormat;       //音频格式wavx->nchannels=fmt->NumOfChannels;        //通道数wavx->samplerate=fmt->SampleRate;       //采样率wavx->bitrate=fmt->ByteRate*8;          //得到位速wavx->blockalign=fmt->BlockAlign;      //块对齐wavx->bps=fmt->BitsPerSample;           //位数,16/24/32位wavx->datasize=data->ChunkSize;            //数据块大小wavx->datastart=wavx->datastart+8;       //数据流开始的地方. printf("wavx->audioformat:%d\r\n",wavx->audioformat);printf("wavx->nchannels:%d\r\n",wavx->nchannels);printf("wavx->samplerate:%d\r\n",wavx->samplerate);printf("wavx->bitrate:%d\r\n",wavx->bitrate);printf("wavx->blockalign:%d\r\n",wavx->blockalign);printf("wavx->bps:%d\r\n",wavx->bps);printf("wavx->datasize:%d\r\n",wavx->datasize);printf("wavx->datastart:%d\r\n",wavx->datastart);  }else res=3;//data区域未找到.}else res=2;//非wav文件}else res=1;//打开文件错误}f_close(ftemp);myfree(SRAMIN,ftemp);//释放内存myfree(SRAMIN,buf); return 0;
}//填充buf
//buf:数据区
//size:填充数据量
//bits:位数(16/24)
//返回值:读到的数据个数
uint32_t wav_buffill(uint8_t *buf,uint16_t size,uint8_t bits)
{uint16_t readlen=0;uint32_t bread;uint16_t i;uint32_t *p,*pbuf;if(bits==24)//24bit音频,需要处理一下{readlen=(size/4)*3;        //此次要读取的字节数f_read(audiodev.file,audiodev.tbuf,readlen,(UINT*)&bread);//读取数据 pbuf=(uint32_t*)buf;for(i=0;i<size/4;i++){  p=(uint32_t*)(audiodev.tbuf+i*3);pbuf[i]=p[0];  } bread=(bread*4)/3;     //填充后的大小.}else {f_read(audiodev.file,buf,size,(UINT*)&bread);//16bit音频,直接读取数据  if(bread<size)//不够数据了,补充0{for(i=bread;i<size-bread;i++)buf[i]=0; }}return bread;
}  //得到当前播放时间
//fx:文件指针
//wavx:wav播放控制器
void wav_get_curtime(FIL*fx,__wavctrl *wavx)
{long long fpos;    wavx->totsec=wavx->datasize/(wavx->bitrate/8);    //歌曲总长度(单位:秒) fpos=fx->fptr-wavx->datastart;                     //得到当前文件播放到的地方 wavx->cursec=fpos*wavx->totsec/wavx->datasize; //当前播放到第多少秒了?
}//播放某个WAV文件
//fname:wav文件路径.
//返回值:
//KEY0_PRES:下一曲
//KEY1_PRES:上一曲
//其他:错误
uint8_t wav_play_song(uint8_t* fname)
{uint8_t key;uint8_t res;  audiodev.file=(FIL*)mymalloc(SRAMIN,sizeof(FIL));audiodev.saibuf1=mymalloc(SRAMIN,WAV_SAI_TX_DMA_BUFSIZE);audiodev.saibuf2=mymalloc(SRAMIN,WAV_SAI_TX_DMA_BUFSIZE);audiodev.tbuf=mymalloc(SRAMIN,WAV_SAI_TX_DMA_BUFSIZE);if(audiodev.file&&audiodev.saibuf1&&audiodev.saibuf2&&audiodev.tbuf){memset(audiodev.saibuf1,0,WAV_SAI_TX_DMA_BUFSIZE);memset(audiodev.saibuf2,0,WAV_SAI_TX_DMA_BUFSIZE);       res=wav_decode_init(fname,&wavctrl);//得到文件的信息if(res==0)//解析文件成功{wavtransferend=0;if(wavctrl.bps==16){WM8978_I2S_Cfg(2,0); //飞利浦标准,16位数据长度BSP_SAI1_Init(SAI_PROTOCOL_DATASIZE_16BIT, wavctrl.samplerate);SAIA_SampleRate_Set(wavctrl.samplerate);//设置采样率  SAIA_TX_DMA_Init(16);    HAL_DMA_RegisterCallback(&hdma_sai1_a, HAL_DMA_XFER_CPLT_CB_ID, BSP_SAI_WAV_M0_TX_Callback);HAL_DMA_RegisterCallback(&hdma_sai1_a, HAL_DMA_XFER_M1CPLT_CB_ID, BSP_SAI_WAV_M1_TX_Callback);HAL_DMAEx_MultiBufferStart_IT(&hdma_sai1_a, (uint32_t)audiodev.saibuf1, (uint32_t)&hsai_BlockA1.Instance->DR, (uint32_t)audiodev.saibuf2, WAV_SAI_TX_DMA_BUFSIZE/2);}else if(wavctrl.bps==24){WM8978_I2S_Cfg(2,2);   //飞利浦标准,24位数据长度BSP_SAI1_Init(SAI_PROTOCOL_DATASIZE_24BIT, wavctrl.samplerate);  SAIA_SampleRate_Set(wavctrl.samplerate);//设置采样率 SAIA_TX_DMA_Init(24);HAL_DMA_RegisterCallback(&hdma_sai1_a, HAL_DMA_XFER_CPLT_CB_ID, BSP_SAI_WAV_M0_TX_Callback);HAL_DMA_RegisterCallback(&hdma_sai1_a, HAL_DMA_XFER_M1CPLT_CB_ID, BSP_SAI_WAV_M1_TX_Callback);HAL_DMAEx_MultiBufferStart_IT(&hdma_sai1_a, (uint32_t)audiodev.saibuf1, (uint32_t)&hsai_BlockA1.Instance->DR, (uint32_t)audiodev.saibuf2, WAV_SAI_TX_DMA_BUFSIZE/4);}else{res=0XFF;}if(res==0){res=f_open(audiodev.file,(TCHAR*)fname,FA_READ);   //打开文件if(res==0){f_lseek(audiodev.file, wavctrl.datastart);       //跳过文件头audiodev.status=3<<0;//开始播放+非暂停while(res==0){if(wavtransferend)//播放结束?{res=KEY0_PRES;break;} while(1){key=KEY_Scan(0); if(key==WKUP_PRES)//暂停{if(audiodev.status&0X01)audiodev.status&=~(1<<0);else audiodev.status|=0X01;  }if(key==KEY2_PRES||key==KEY0_PRES)//下一曲/上一曲{res=key;break; }wav_get_curtime(audiodev.file,&wavctrl);//得到总时间和当前播放的时间 audio_msg_show(wavctrl.totsec,wavctrl.cursec,wavctrl.bitrate);if((audiodev.status&0X01)==0)delay_ms(10);else break;}}audiodev.status=0;}else {res=0XFF;}}}else{res=0XFF;} }else{res=0XFF; }        myfree(SRAMIN,audiodev.tbuf);   //释放内存myfree(SRAMIN,audiodev.saibuf1);//释放内存myfree(SRAMIN,audiodev.saibuf2);//释放内存 myfree(SRAMIN,audiodev.file);    //释放内存 return res;
} 
#ifndef __BSP_MP3PLAY_H
#define __BSP_MP3PLAY_H#ifdef __cplusplus
extern "C" {#endif#include "main.h"#include <mp3dec.h>//
//本程序移植自helix MP3解码库
//ALIENTEK STM32开发板
//MP3 解码代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2014/6/29
//版本:V1.0
//********************************************************************************
//V1.0 说明
//1,支持16位单声道/立体声MP3的解码
//2,支持CBR/VBR格式MP3解码
//3,支持ID3V1和ID3V2标签解析
//4,支持所有比特率(MP3最高是320Kbps)解码
//  #define MP3_TITSIZE_MAX     40      //歌曲名字最大长度
#define MP3_ARTSIZE_MAX     40      //歌曲名字最大长度
#define MP3_FILE_BUF_SZ    5*1024   //MP3解码时,文件buf大小//ID3V1 标签
typedef __packed struct
{uint8_t id[3];         //ID,TAG三个字母uint8_t title[30];      //歌曲名字uint8_t artist[30];       //艺术家名字uint8_t year[4];         //年代uint8_t comment[30];        //备注uint8_t genre;          //流派
}ID3V1_Tag;//ID3V2 标签头
typedef __packed struct
{uint8_t id[3];         //IDuint8_t mversion;       //主版本号uint8_t sversion;     //子版本号uint8_t flags;            //标签头标志uint8_t size[4];         //标签信息大小(不包含标签头10字节).所以,标签大小=size+10.
}ID3V2_TagHead;//ID3V2.3 版本帧头
typedef __packed struct
{uint8_t id[4];         //帧IDuint8_t size[4];           //帧大小uint16_t flags;            //帧标志
}ID3V23_FrameHead;//MP3 Xing帧信息(没有全部列出来,仅列出有用的部分)
typedef __packed struct
{uint8_t id[4];         //帧ID,为Xing/Infouint8_t flags[4];       //存放标志uint8_t frames[4];        //总帧数uint8_t fsize[4];      //文件总大小(不包含ID3)
}MP3_FrameXing;//MP3 VBRI帧信息(没有全部列出来,仅列出有用的部分)
typedef __packed struct
{uint8_t id[4];         //帧ID,为Xing/Infouint8_t version[2];     //版本号uint8_t delay[2];      //延迟uint8_t quality[2];     //音频质量,0~100,越大质量越好uint8_t fsize[4];        //文件总大小uint8_t frames[4];       //文件总帧数
}MP3_FrameVBRI;//MP3控制结构体
typedef __packed struct
{uint8_t title[MP3_TITSIZE_MAX];    //歌曲名字uint8_t artist[MP3_ARTSIZE_MAX];  //艺术家名字uint32_t totsec ;                //整首歌时长,单位:秒uint32_t cursec ;               //当前播放时长uint32_t bitrate;               //比特率uint32_t samplerate;               //采样率uint16_t outsamples;               //PCM输出数据量大小(以16位为单位),单声道MP3,则等于实际输出*2(方便DAC输出)uint32_t datastart;              //数据帧开始的位置(在文件里面的偏移)
}__mp3ctrl;extern __mp3ctrl * mp3ctrl;void mp3_i2s_dma_tx_callback(void) ;
void mp3_fill_buffer(uint16_t* buf,uint16_t size,uint8_t nch);
uint8_t mp3_id3v1_decode(uint8_t* buf,__mp3ctrl *pctrl);
uint8_t mp3_id3v2_decode(uint8_t* buf,uint32_t size,__mp3ctrl *pctrl);
uint8_t mp3_get_info(uint8_t *pname,__mp3ctrl* pctrl);
uint8_t mp3_play_song(uint8_t* fname);extern volatile uint8_t mp3transferend;#ifdef __cplusplus
}
#endif#endif /* __BSP_MP3PLAY_H */
#include "bsp_mp3play.h"
#include "bsp_audioplay.h"
//#include "sys.h"
#include "delay.h"
#include "bsp_malloc.h"
#include "bsp_printf.h"
//#include "ff.h"
#include "string.h"
#include "bsp_sai.h"
#include "bsp_wm8978.h"
#include "bsp_key.h"
//#include "led.h"
//
//本程序移植自helix MP3解码库
//ALIENTEK STM32开发板
//MP3 解码代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2014/6/29
//版本:V1.0
//********************************************************************************
//V1.0 说明
//1,支持16位单声道/立体声MP3的解码
//2,支持CBR/VBR格式MP3解码
//3,支持ID3V1和ID3V2标签解析
//4,支持所有比特率(MP3最高是320Kbps)解码
//  __mp3ctrl * mp3ctrl;    //mp3控制结构体
volatile uint8_t mp3transferend=0; //sai传输完成标志
volatile uint8_t mp3witchbuf=0;        //saibufx指示标志void BSP_SAI_MP3_M0_TX_Callback(DMA_HandleTypeDef *_hdma)  //TX回调函数
{mp3witchbuf = 1;mp3transferend=1;
}void BSP_SAI_MP3_M1_TX_Callback(DMA_HandleTypeDef *_hdma)  //TX回调函数
{mp3witchbuf = 0;mp3transferend=1;
} //填充PCM数据到DAC
//buf:PCM数据首地址
//size:pcm数据量(16位为单位)
//nch:声道数(1,单声道,2立体声)
void mp3_fill_buffer(uint16_t* buf,uint16_t size,uint8_t nch)
{uint16_t i; uint16_t *p;while(mp3transferend==0);//等待传输完成mp3transferend=0;if(mp3witchbuf==0){p=(uint16_t*)audiodev.saibuf2;}else {p=(uint16_t*)audiodev.saibuf1;}if((audiodev.status&0X01)==0){memset(p,0,2304*2);    //数据清零 }else{if(nch==2)for(i=0;i<size;i++)p[i]=buf[i];else //单声道{for(i=0;i<size;i++){p[2*i]=buf[i];p[2*i+1]=buf[i];}}}
} //解析ID3V1
//buf:输入数据缓存区(大小固定是128字节)
//pctrl:MP3控制器
//返回值:0,获取正常
//    其他,获取失败
uint8_t mp3_id3v1_decode(uint8_t* buf,__mp3ctrl *pctrl)
{ID3V1_Tag *id3v1tag;id3v1tag=(ID3V1_Tag*)buf;if (strncmp("TAG",(char*)id3v1tag->id,3)==0)//是MP3 ID3V1 TAG{if(id3v1tag->title[0])strncpy((char*)pctrl->title,(char*)id3v1tag->title,30);if(id3v1tag->artist[0])strncpy((char*)pctrl->artist,(char*)id3v1tag->artist,30); }else return 1;return 0;
}
//解析ID3V2
//buf:输入数据缓存区
//size:数据大小
//pctrl:MP3控制器
//返回值:0,获取正常
//    其他,获取失败
uint8_t mp3_id3v2_decode(uint8_t* buf,uint32_t size,__mp3ctrl *pctrl)
{ID3V2_TagHead *taghead;ID3V23_FrameHead *framehead; uint32_t t;uint32_t tagsize;   //tag大小uint32_t frame_size; //帧大小 taghead=(ID3V2_TagHead*)buf; if(strncmp("ID3",(const char*)taghead->id,3)==0)//存在ID3?{tagsize=((uint32_t)taghead->size[0]<<21)|((uint32_t)taghead->size[1]<<14)|((uint16_t)taghead->size[2]<<7)|taghead->size[3];//得到tag 大小pctrl->datastart=tagsize;       //得到mp3数据开始的偏移量if(tagsize>size)tagsize=size;    //tagsize大于输入bufsize的时候,只处理输入size大小的数据if(taghead->mversion<3){printf("not supported mversion!\r\n");return 1;}t=10;while(t<tagsize){framehead=(ID3V23_FrameHead*)(buf+t);frame_size=((uint32_t)framehead->size[0]<<24)|((uint32_t)framehead->size[1]<<16)|((uint32_t)framehead->size[2]<<8)|framehead->size[3];//得到帧大小if (strncmp("TT2",(char*)framehead->id,3)==0||strncmp("TIT2",(char*)framehead->id,4)==0)//找到歌曲标题帧,不支持unicode格式!!{strncpy((char*)pctrl->title,(char*)(buf+t+sizeof(ID3V23_FrameHead)+1),AUDIO_MIN(frame_size-1,MP3_TITSIZE_MAX-1));}if (strncmp("TP1",(char*)framehead->id,3)==0||strncmp("TPE1",(char*)framehead->id,4)==0)//找到歌曲艺术家帧{strncpy((char*)pctrl->artist,(char*)(buf+t+sizeof(ID3V23_FrameHead)+1),AUDIO_MIN(frame_size-1,MP3_ARTSIZE_MAX-1));}t+=frame_size+sizeof(ID3V23_FrameHead);} }else pctrl->datastart=0;//不存在ID3,mp3数据是从0开始return 0;
} //获取MP3基本信息
//pname:MP3文件路径
//pctrl:MP3控制信息结构体
//返回值:0,成功
//    其他,失败
uint8_t mp3_get_info(uint8_t *pname,__mp3ctrl* pctrl)
{HMP3Decoder decoder;MP3FrameInfo frame_info;MP3_FrameXing* fxing;MP3_FrameVBRI* fvbri;FIL*fmp3;uint8_t *buf;uint32_t br;uint8_t res;int offset=0;uint32_t p;short samples_per_frame;  //一帧的采样个数uint32_t totframes;                //总帧数fmp3=mymalloc(SRAMIN,sizeof(FIL)); buf=mymalloc(SRAMIN,5*1024);      //申请5K内存 if(fmp3&&buf)//内存申请成功{         f_open(fmp3,(const TCHAR*)pname,FA_READ);//打开文件res=f_read(fmp3,(char*)buf,5*1024,&br);if(res==0)//读取文件成功,开始解析ID3V2/ID3V1以及获取MP3信息{  mp3_id3v2_decode(buf,br,pctrl);  //解析ID3V2数据f_lseek(fmp3,fmp3->obj.objsize-128);  //偏移到倒数128的位置f_read(fmp3,(char*)buf,128,&br);//读取128字节mp3_id3v1_decode(buf,pctrl);  //解析ID3V1数据  decoder=MP3InitDecoder();         //MP3解码申请内存f_lseek(fmp3,pctrl->datastart);   //偏移到数据开始的地方f_read(fmp3,(char*)buf,5*1024,&br); //读取5K字节mp3数据offset=MP3FindSyncWord(buf,br);   //查找帧同步信息if(offset>=0&&MP3GetNextFrameInfo(decoder,&frame_info,&buf[offset])==0)//找到帧同步信息了,且下一阵信息获取正常 { p=offset+4+32;fvbri=(MP3_FrameVBRI*)(buf+p);if(strncmp("VBRI",(char*)fvbri->id,4)==0)//存在VBRI帧(VBR格式){if (frame_info.version==MPEG1)samples_per_frame=1152;//MPEG1,layer3每帧采样数等于1152else samples_per_frame=576;//MPEG2/MPEG2.5,layer3每帧采样数等于576 totframes=((uint32_t)fvbri->frames[0]<<24)|((uint32_t)fvbri->frames[1]<<16)|((uint16_t)fvbri->frames[2]<<8)|fvbri->frames[3];//得到总帧数pctrl->totsec=totframes*samples_per_frame/frame_info.samprate;//得到文件总长度}else   //不是VBRI帧,尝试是不是Xing帧(VBR格式){  if (frame_info.version==MPEG1)  //MPEG1 {p=frame_info.nChans==2?32:17;samples_per_frame = 1152; //MPEG1,layer3每帧采样数等于1152}else{p=frame_info.nChans==2?17:9;samples_per_frame=576;       //MPEG2/MPEG2.5,layer3每帧采样数等于576}p+=offset+4;fxing=(MP3_FrameXing*)(buf+p);if(strncmp("Xing",(char*)fxing->id,4)==0||strncmp("Info",(char*)fxing->id,4)==0)//是Xng帧{if(fxing->flags[3]&0X01)//存在总frame字段{totframes=((uint32_t)fxing->frames[0]<<24)|((uint32_t)fxing->frames[1]<<16)|((uint16_t)fxing->frames[2]<<8)|fxing->frames[3];//得到总帧数pctrl->totsec=totframes*samples_per_frame/frame_info.samprate;//得到文件总长度}else   //不存在总frames字段{pctrl->totsec=fmp3->obj.objsize/(frame_info.bitrate/8);} }else        //CBR格式,直接计算总播放时间{pctrl->totsec=fmp3->obj.objsize/(frame_info.bitrate/8);}} pctrl->bitrate=frame_info.bitrate;           //得到当前帧的码率mp3ctrl->samplerate=frame_info.samprate;  //得到采样率. if(frame_info.nChans==2)mp3ctrl->outsamples=frame_info.outputSamps; //输出PCM数据量大小 else mp3ctrl->outsamples=frame_info.outputSamps*2; //输出PCM数据量大小,对于单声道MP3,直接*2,补齐为双声道输出}else res=0XFE;//未找到同步帧  MP3FreeDecoder(decoder);//释放内存      } f_close(fmp3);}else res=0XFF;myfree(SRAMIN,fmp3);myfree(SRAMIN,buf); return res;
}
//得到当前播放时间
//fx:文件指针
//mp3x:mp3播放控制器
void mp3_get_curtime(FIL*fx,__mp3ctrl *mp3x)
{uint32_t fpos=0;       if(fx->fptr>mp3x->datastart)fpos=fx->fptr-mp3x->datastart; //得到当前文件播放到的地方 mp3x->cursec=fpos*mp3x->totsec/(fx->obj.objsize-mp3x->datastart);   //当前播放到第多少秒了?
}
//mp3文件快进快退函数
//pos:需要定位到的文件位置
//返回值:当前文件位置(即定位后的结果)
uint32_t mp3_file_seek(uint32_t pos)
{if(pos>audiodev.file->obj.objsize){pos=audiodev.file->obj.objsize;}f_lseek(audiodev.file,pos);return audiodev.file->fptr;
}
//播放一曲MP3音乐
//fname:MP3文件路径.
//返回值:0,正常播放完成
//[b7]:0,正常状态;1,错误状态
//[b6:0]:b7=0时,表示操作码
//       b7=1时,表示有错误(这里不判定具体错误,0X80~0XFF,都算是错误)
uint8_t mp3_play_song(uint8_t* fname)
{ HMP3Decoder mp3decoder;MP3FrameInfo mp3frameinfo;uint8_t key;uint8_t res;uint8_t* buffer;     //输入buffer  uint8_t* readptr;   //MP3解码读指针int offset=0;    //偏移量int outofdata=0;//超出数据范围int bytesleft=0;//buffer还剩余的有效数据uint32_t br=0; int err=0;  mp3transferend=0;  //sai传输完成标志mp3witchbuf=0;      //saibufx指示标志
//  BSP_SAI_TX_Callback = BSP_SAI_TX_Callback_MP3;mp3ctrl=mymalloc(SRAMIN,sizeof(__mp3ctrl)); buffer=mymalloc(SRAMIN,MP3_FILE_BUF_SZ);   //申请解码buf大小audiodev.file=(FIL*)mymalloc(SRAMIN,sizeof(FIL));audiodev.saibuf1=mymalloc(SRAMIN,2304*2);audiodev.saibuf2=mymalloc(SRAMIN,2304*2);audiodev.tbuf=mymalloc(SRAMIN,2304*2);audiodev.file_seek=mp3_file_seek;if(!mp3ctrl||!buffer||!audiodev.file||!audiodev.saibuf1||!audiodev.saibuf2||!audiodev.tbuf)//内存申请失败{myfree(SRAMIN,mp3ctrl);myfree(SRAMIN,buffer);myfree(SRAMIN,audiodev.file);myfree(SRAMIN,audiodev.saibuf1);myfree(SRAMIN,audiodev.saibuf2);myfree(SRAMIN,audiodev.tbuf); return 0xff;  //错误} memset(audiodev.saibuf1,0,2304*2);    //数据清零 memset(audiodev.saibuf2,0,2304*2);   //数据清零 memset(mp3ctrl,0,sizeof(__mp3ctrl));//数据清零 res=mp3_get_info(fname,mp3ctrl);  if(res==0){ printf("     title:%s\r\n",mp3ctrl->title); printf("    artist:%s\r\n",mp3ctrl->artist); printf("   bitrate:%dbps\r\n",mp3ctrl->bitrate); printf("samplerate:%d\r\n", mp3ctrl->samplerate);  printf("  totalsec:%d\r\n",mp3ctrl->totsec); WM8978_I2S_Cfg(2,0);  //飞利浦标准,16位数据长度BSP_SAI1_Init(SAI_PROTOCOL_DATASIZE_16BIT, mp3ctrl->samplerate);SAIA_SampleRate_Set(mp3ctrl->samplerate);//设置采样率   SAIA_TX_DMA_Init(16);   HAL_DMA_RegisterCallback(&hdma_sai1_a, HAL_DMA_XFER_CPLT_CB_ID, BSP_SAI_MP3_M0_TX_Callback);HAL_DMA_RegisterCallback(&hdma_sai1_a, HAL_DMA_XFER_M1CPLT_CB_ID, BSP_SAI_MP3_M1_TX_Callback);HAL_DMAEx_MultiBufferStart_IT(&hdma_sai1_a, (uint32_t)audiodev.saibuf1, (uint32_t)&hsai_BlockA1.Instance->DR, (uint32_t)audiodev.saibuf2, mp3ctrl->outsamples);mp3decoder=MP3InitDecoder();                    //MP3解码申请内存res=f_open(audiodev.file,(char*)fname,FA_READ); //打开文件}if(res==0&&mp3decoder!=0)//打开文件成功{ f_lseek(audiodev.file,mp3ctrl->datastart);  //跳过文件头中tag信息audio_start();                             //开始播放 //HAL_SAI_Transmit_DMA(&hsai_BlockA1, audiodev.saibuf1, mp3ctrl->outsamples);while(res==0){readptr=buffer; //MP3读指针指向bufferoffset=0;      //偏移量为0outofdata=0;    //数据正常bytesleft=0; res=f_read(audiodev.file,buffer,MP3_FILE_BUF_SZ,&br);//一次读取MP3_FILE_BUF_SZ字节if(res)//读数据出错了{res=0xff;break;}if(br==0)       //读数为0,说明解码完成了.{res=KEY0_PRES; //播放完成break;}bytesleft+=br;   //buffer里面有多少有效MP3数据?err=0;            while(!outofdata)//没有出现数据异常(即可否找到帧同步字符){offset=MP3FindSyncWord(readptr,bytesleft);//在readptr位置,开始查找同步字符if(offset<0) //没有找到同步字符,跳出帧解码循环{ outofdata=1;//没找到帧同步字符}else    //找到同步字符了{readptr+=offset;        //MP3读指针偏移到同步字符处.bytesleft-=offset;        //buffer里面的有效数据个数,必须减去偏移量err=MP3Decode(mp3decoder,&readptr,&bytesleft,(short*)audiodev.tbuf,0);//解码一帧MP3数据if(err!=0){printf("decode error:%d\r\n",err);break;}else{MP3GetLastFrameInfo(mp3decoder,&mp3frameinfo);   //得到刚刚解码的MP3帧信息if(mp3ctrl->bitrate!=mp3frameinfo.bitrate)       //更新码率{mp3ctrl->bitrate=mp3frameinfo.bitrate; }mp3_fill_buffer((uint16_t*)audiodev.tbuf,mp3frameinfo.outputSamps,mp3frameinfo.nChans);//填充pcm数据}if(bytesleft<MAINBUF_SIZE*2)//当数组内容小于2倍MAINBUF_SIZE的时候,必须补充新的数据进来.{ memmove(buffer,readptr,bytesleft);//移动readptr所指向的数据到buffer里面,数据量大小为:bytesleftf_read(audiodev.file,buffer+bytesleft,MP3_FILE_BUF_SZ-bytesleft,&br);//补充余下的数据if(br<MP3_FILE_BUF_SZ-bytesleft){memset(buffer+bytesleft+br,0,MP3_FILE_BUF_SZ-bytesleft-br); }bytesleft=MP3_FILE_BUF_SZ;  readptr=buffer; }    while(1){key=KEY_Scan(0); if(key==WKUP_PRES)//暂停{if(audiodev.status&0X01)audiodev.status&=~(1<<0);else audiodev.status|=0X01;  }if(key==KEY2_PRES||key==KEY0_PRES)//下一曲/上一曲{outofdata=1;res=key;break; }mp3_get_curtime(audiodev.file,mp3ctrl); audiodev.totsec=mp3ctrl->totsec;  //参数传递audiodev.cursec=mp3ctrl->cursec;audiodev.bitrate=mp3ctrl->bitrate;audiodev.samplerate=mp3ctrl->samplerate;audiodev.bps=16;//MP3仅支持16位if((audiodev.status&0X01)==0){delay_ms(10);}  else//正常播放{break;}                          }}                  }  }audio_stop();//关闭音频输出}else res=0xff;//错误f_close(audiodev.file);MP3FreeDecoder(mp3decoder);     //释放内存  myfree(SRAMIN,mp3ctrl);myfree(SRAMIN,buffer);myfree(SRAMIN,audiodev.file);myfree(SRAMIN,audiodev.saibuf1);myfree(SRAMIN,audiodev.saibuf2);myfree(SRAMIN,audiodev.tbuf);return res;
}
#ifndef __BSP_FLACPLAY_H
#define __BSP_FLACPLAY_H #ifdef __cplusplus
extern "C" {#endif#include "main.h"#include <inttypes.h>
#include <string.h>
#include "flacdecoder.h"
#include "ff.h"  //
//本程序移植自RockBox的flac解码库
//ALIENTEK STM32开发板
//FLAC 解码代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2014/6/29
//版本:V1.0
//********************************************************************************
//V1.0 说明
//1,支持16/24位单声道/立体声flac的解码
//2,最高支持192K/16bit或96K/24bit的flac解码
//   //flaC 标签
typedef __packed struct
{uint8_t id[3];         //ID,在文件起始位置,必须是flaC 4个字母
}FLAC_Tag;//metadata 数据块头信息结构体
typedef __packed struct
{uint8_t head;          //metadata block头uint8_t size[3];           //metadata block数据长度
}MD_Block_Head;//FLAC控制结构体
typedef __packed struct
{ uint32_t totsec ;             //整首歌时长,单位:秒uint32_t cursec ;               //当前播放时长uint32_t bitrate;               //比特率uint32_t samplerate;               //采样率uint16_t outsamples;               //PCM输出数据量大小uint16_t bps;                   //位数,比如16bit,24bit,32bituint32_t datastart;             //数据帧开始的位置(在文件里面的偏移)
}__flacctrl;extern __flacctrl * flacctrl;uint8_t flac_init(FIL* fx,__flacctrl* fctrl,FLACContext* fc);
void flac_i2s_dma_tx_callback(void);
void flac_get_curtime(FIL*fx,__flacctrl *flacx);
uint8_t flac_play_song(uint8_t* fname);extern volatile uint8_t flactransferend; //sai传输完成标志#ifdef __cplusplus
}
#endif#endif /* __BSP_FLACPLAY_H */
#include "bsp_flacplay.h"
#include "ff.h"
#include "bsp_sai.h"
#include "bsp_wm8978.h"
#include "bsp_malloc.h"
#include "bsp_printf.h"
#include "bsp_key.h"
//#include "led.h"
#include "delay.h"
#include "bsp_audioplay.h"//
//本程序移植自RockBox的flac解码库
//ALIENTEK STM32开发板
//FLAC 解码代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2014/6/29
//版本:V1.0
//********************************************************************************
//V1.0 说明
//1,支持16/24位单声道/立体声flac的解码
//2,最高支持192K/16bit或96K/24bit的flac解码
//   __flacctrl * flacctrl; //flac解码控制结构体
volatile uint8_t flactransferend=0;    //sai传输完成标志
volatile uint8_t flacwitchbuf=0;       //saibufx指示标志
FLACContext *fc=0; void BSP_SAI_FLAC_M0_TX_Callback(DMA_HandleTypeDef *_hdma)  //TX回调函数
{flacwitchbuf = 1;flactransferend=1;
}void BSP_SAI_FLAC_M1_TX_Callback(DMA_HandleTypeDef *_hdma) //TX回调函数
{flacwitchbuf = 0;flactransferend=1;
} //分析FLAC文件
//fx:flac文件指针
//fc:flac解码容器
//返回值:0,分析成功
//    其他,错误代码
uint8_t flac_init(FIL* fx,__flacctrl* fctrl,FLACContext* fc)
{FLAC_Tag * flactag;MD_Block_Head *flacblkh;uint8_t *buf; uint8_t endofmetadata=0;         //最后一个metadata标记int blocklength; uint32_t br;uint8_t res;buf=mymalloc(SRAMIN,512); //申请512字节内存if(!buf)return 1;            //内存申请失败 f_lseek(fx,0);             //偏移到文件头f_read(fx,buf,4,&br);       //读取4字节 flactag=(FLAC_Tag*)buf;        //强制转换为flac tag标签if(strncmp("fLaC",(char*)flactag->id,4)!=0) {myfree(SRAMIN,buf);     //释放内存return 2;             //非flac文件} while(!endofmetadata) {f_read(fx,buf,4,&br);if(br<4)break;flacblkh=(MD_Block_Head*)buf;endofmetadata=flacblkh->head&0X80;    //判断是不是最后一个block?blocklength=((uint32_t)flacblkh->size[0]<<16)|((uint16_t)flacblkh->size[1]<<8)|(flacblkh->size[2]);//得到块大小if((flacblkh->head&0x7f)==0)      //head最低7位为0,则表示是STREAMINFO块{ res=f_read(fx,buf,blocklength,&br);if(res!=FR_OK)break;  fc->min_blocksize=((uint16_t)buf[0]<<8) |buf[1];                 //最小块大小fc->max_blocksize=((uint16_t)buf[2]<<8) |buf[3];                   //最大块大小fc->min_framesize=((uint32_t)buf[4]<<16)|((uint16_t)buf[5]<<8)|buf[6];//最小帧大小fc->max_framesize=((uint32_t)buf[7]<<16)|((uint16_t)buf[8]<<8)|buf[9];//最大帧大小fc->samplerate=((uint32_t)buf[10]<<12)|((uint16_t)buf[11]<<4)|((buf[12]&0xf0)>>4);//采样率fc->channels=((buf[12]&0x0e)>>1)+1;                            //音频通道数fc->bps=((((uint16_t)buf[12]&0x01)<<4)|((buf[13]&0xf0)>>4))+1;  //采样位数16?24?32? fc->totalsamples=((uint32_t)buf[14]<<24)|((uint32_t)buf[15]<<16)|((uint16_t)buf[16]<<8)|buf[17];//一个声道的总采样数fctrl->samplerate=fc->samplerate;fctrl->totsec=(fc->totalsamples/fc->samplerate);//得到总时间 }else    //忽略其他帧的处理 { if(f_lseek(fx,fx->fptr+blocklength)!=FR_OK){ myfree(SRAMIN,buf);return 3;}}} myfree(SRAMIN,buf);//释放内存.if(fctrl->totsec){fctrl->outsamples=fc->max_blocksize*2;//PCM输出数据量(*2,表示2个声道的数据量)fctrl->bps=fc->bps;          //采样位数(16/24/32)fctrl->datastart=fx->fptr;   //FLAC数据帧开始的地址fctrl->bitrate=((fx->obj.objsize-fctrl->datastart)*8)/fctrl->totsec;//得到FLAC的位速}else return 4;   //总时间为0?有问题的flac文件return 0;
} //得到当前播放时间
//fx:文件指针
//flacctrl:flac播放控制器
void flac_get_curtime(FIL*fx,__flacctrl *flacctrl)
{long long fpos=0;      if(fx->fptr>flacctrl->datastart)fpos=fx->fptr-flacctrl->datastart; //得到当前文件播放到的地方 flacctrl->cursec=fpos*flacctrl->totsec/(fx->obj.objsize-flacctrl->datastart);   //当前播放到第多少秒了?
}
//flac文件快进快退函数
//pos:需要定位到的文件位置
//返回值:当前文件位置(即定位后的结果)
uint32_t flac_file_seek(uint32_t pos)
{if(pos>audiodev.file->obj.objsize){pos=audiodev.file->obj.objsize;}f_lseek(audiodev.file,pos);return audiodev.file->fptr;
}
//播放一曲FLAC音乐
//fname:FLAC文件路径.
//返回值:0,正常播放完成
//[b7]:0,正常状态;1,错误状态
//[b6:0]:b7=0时,表示操作码
//       b7=1时,表示有错误(这里不判定具体错误,0X80~0XFF,都算是错误)
uint8_t flac_play_song(uint8_t* fname)
{ int bytesleft;int consumed;uint8_t res=0;  uint32_t br=0; uint8_t* buffer=0;    uint8_t* decbuf0=0;   uint8_t* decbuf1=0;  uint8_t* p8=0;    uint32_t flac_fptr=0; uint8_t key;fc=mymalloc(SRAMIN,sizeof(FLACContext)); flacctrl=mymalloc(SRAMIN,sizeof(__flacctrl)); audiodev.file=(FIL*)mymalloc(SRAMIN,sizeof(FIL));audiodev.file_seek=flac_file_seek;if(!fc||!audiodev.file||!flacctrl)res=1;//内存申请错误else{ memset(fc,0,sizeof(FLACContext));//fc所有内容清零 res=f_open(audiodev.file,(char*)fname,FA_READ); //读取文件错误 if(res==FR_OK){res=flac_init(audiodev.file,flacctrl,fc);  //flac解码初始化   if(fc->min_blocksize==fc->max_blocksize&&fc->max_blocksize!=0)//必须min_blocksize等于max_blocksize{if(fc->bps==24)   //24位音频数据{  audiodev.saibuf1=mymalloc(SRAMIN,fc->max_blocksize*8);audiodev.saibuf2=mymalloc(SRAMIN,fc->max_blocksize*8);  }else         //16位音频数据{audiodev.saibuf1=mymalloc(SRAMIN,fc->max_blocksize*4);audiodev.saibuf2=mymalloc(SRAMIN,fc->max_blocksize*4); }buffer=mymalloc(SRAMDTCM,fc->max_framesize);    //申请解码帧缓存 decbuf0=mymalloc(SRAMDTCM,fc->max_blocksize*4);decbuf1=mymalloc(SRAMDTCM,fc->max_blocksize*4);}else res+=1;//不支持的音频格式  }}if(buffer&&audiodev.saibuf1&&audiodev.saibuf2&&decbuf0&&decbuf1&&res==0){ flactransferend=0;    flacwitchbuf=0;        printf("\r\n  Blocksize: %d .. %d\r\n", fc->min_blocksize,fc->max_blocksize);printf("  Framesize: %d .. %d\r\n",fc->min_framesize,fc->max_framesize);printf("  Samplerate: %d\r\n", fc->samplerate);printf("  Channels: %d\r\n", fc->channels);printf("  Bits per sample: %d\r\n", fc->bps);printf("  Metadata length: %d\r\n", flacctrl->datastart);printf("  Total Samples: %lu\r\n",fc->totalsamples);printf("  Duration: %d s\r\n",flacctrl->totsec);printf("  Bitrate: %d kbps\r\n",flacctrl->bitrate); if(flacctrl->bps==24)      //24位音频数据{  memset(audiodev.saibuf1,0,fc->max_blocksize*8);memset(audiodev.saibuf2,0,fc->max_blocksize*8);WM8978_I2S_Cfg(2,2);    //飞利浦标准,24位数据长度BSP_SAI1_Init(SAI_PROTOCOL_DATASIZE_24BIT, fc->samplerate);SAIA_SampleRate_Set(fc->samplerate);    //设置采样率  SAIA_TX_DMA_Init(24);HAL_DMA_RegisterCallback(&hdma_sai1_a, HAL_DMA_XFER_CPLT_CB_ID, BSP_SAI_FLAC_M0_TX_Callback);HAL_DMA_RegisterCallback(&hdma_sai1_a, HAL_DMA_XFER_M1CPLT_CB_ID, BSP_SAI_FLAC_M1_TX_Callback);HAL_DMAEx_MultiBufferStart_IT(&hdma_sai1_a, (uint32_t)audiodev.saibuf1, (uint32_t)&hsai_BlockA1.Instance->DR, (uint32_t)audiodev.saibuf2, flacctrl->outsamples);}else                       //16位音频数据{memset(audiodev.saibuf1,0,fc->max_blocksize*4);memset(audiodev.saibuf2,0,fc->max_blocksize*4);WM8978_I2S_Cfg(2,0);  //飞利浦标准,16位数据长度BSP_SAI1_Init(SAI_PROTOCOL_DATASIZE_16BIT, fc->samplerate);SAIA_SampleRate_Set(fc->samplerate);    //设置采样率    SAIA_TX_DMA_Init(16);    HAL_DMA_RegisterCallback(&hdma_sai1_a, HAL_DMA_XFER_CPLT_CB_ID, BSP_SAI_FLAC_M0_TX_Callback);HAL_DMA_RegisterCallback(&hdma_sai1_a, HAL_DMA_XFER_M1CPLT_CB_ID, BSP_SAI_FLAC_M1_TX_Callback);HAL_DMAEx_MultiBufferStart_IT(&hdma_sai1_a, (uint32_t)audiodev.saibuf1, (uint32_t)&hsai_BlockA1.Instance->DR, (uint32_t)audiodev.saibuf2, flacctrl->outsamples);}  f_read(audiodev.file,buffer,fc->max_framesize,&br);//读取最大帧长数据       bytesleft=br;audio_start();                    //开始播放  fc->decoded0=(int*)decbuf0;     //解码数组0fc->decoded1=(int*)decbuf1;      //解码数组1 flac_fptr=audiodev.file->fptr;  //记录当前的文件位置.while(bytesleft) { while(flactransferend==0);//等待传输完成flactransferend = 0;if(flacwitchbuf==0)p8=audiodev.saibuf2;else p8=audiodev.saibuf1; if(fc->bps==24)res=flac_decode_frame24(fc,buffer,bytesleft,(s32*)p8);else res=flac_decode_frame16(fc,buffer,bytesleft,(s16*)p8); if(res!=0)//解码出错了 {res=0xff;break;} consumed=fc->gb.index/8;memmove(buffer,&buffer[consumed],bytesleft-consumed);bytesleft-=consumed; res=f_read(audiodev.file,&buffer[bytesleft],fc->max_framesize-bytesleft,&br); if(res)//读数据出错了{res=0xff;break;}if(br>0) {bytesleft+=br;}flac_fptr=audiodev.file->fptr;   //记录当前的文件位置.//          while(audiodev.status&(1<<1)) //正常播放中
//          {
//              flac_get_curtime(audiodev.file,flacctrl);//得到总时间和当前播放的时间
//              audiodev.totsec=flacctrl->totsec;       //参数传递
//              audiodev.cursec=flacctrl->cursec;
//              audiodev.bitrate=flacctrl->bitrate;
//              audiodev.samplerate=flacctrl->samplerate;
//              audiodev.bps=flacctrl->bps;
//                  if(audiodev.status&0X01)break;  //没有按下暂停
//              //else delay_ms(1000/OS_TICKS_PER_SEC);
//          }
//          if((audiodev.status&(1<<1))==0)     //请求结束播放/播放完成
//          {
//              break;
//          }    while(1){key=KEY_Scan(0); if(key==WKUP_PRES)//暂停{if(audiodev.status&0X01)audiodev.status&=~(1<<0);else audiodev.status|=0X01;  }if(key==KEY2_PRES||key==KEY0_PRES)//下一曲/上一曲{bytesleft = 0;res=key;break; }flac_get_curtime(audiodev.file,flacctrl);//得到总时间和当前播放的时间 audiodev.totsec=flacctrl->totsec;      //参数传递audiodev.cursec=flacctrl->cursec;audiodev.bitrate=flacctrl->bitrate;audiodev.samplerate=flacctrl->samplerate;audiodev.bps=flacctrl->bps;  if((audiodev.status&0X01)==0){delay_ms(10);}  else//正常播放{break;}                          }} audio_stop();}else res=0xff;f_close(audiodev.file);myfree(SRAMIN,fc);myfree(SRAMIN,flacctrl);myfree(SRAMIN,audiodev.file);myfree(SRAMIN,audiodev.saibuf1);myfree(SRAMIN,audiodev.saibuf2); myfree(SRAMDTCM,buffer);myfree(SRAMDTCM,decbuf0);myfree(SRAMDTCM,decbuf1); return res;
} 

总结:
1、

2、

3、双缓冲模式DMA需要设置为DMA_CIRCULAR
4、SAI_DMA中断优先级比SD_DMA中断优先级低,是因为在wavplay中断程序中会执行读SD卡

STM32F767 音乐播放器 SAI DMA双缓冲 可播放WAV、MP3、FLAC文件相关推荐

  1. dma接收双缓存 stm32_STM32和WM8960 I2S 利用DMA双缓冲音频播放和录音(二)

    前面简单讲解了WM8960语音芯片工作方式,WM8960做master,之前参数配置ADC/DAC采样速率的是44.1K,有点问题,现在改为16K,下面会解释为什么要改成16K. WM8960参数配置 ...

  2. java串口设备中断_利用DMA双缓冲或半完成中断实现串口不定长数据的接收

    在<HAL版本DMA循环模式串口数据收发>中介绍了利用DMA循环模式进行串口数据的收发,STM32F4xx的DMA还提供了双缓冲的功能,采用双缓冲模式,可以在一个DMA完成接收后,对其缓冲 ...

  3. 搬砖之路----MusicPlayer 一个基于Vlc(2.0+)开发的android音乐播放器--浅析在android开发过程中播放器选择之路!

    前言 MusicPlayer 是一款基于vlc播放器开发的一个音乐播放器,你也可以理解为在此核心上的搬砖之路,核心的内容并不是我写的,因此在正式写blog之前,感谢那些vlc核心的开发人员让我用到这么 ...

  4. PotPlayer播放器中英双字幕设置

    PotPlayer播放器中英双字幕设置 动画片.电影发的视频类,部分有多国语言字幕, 默认播放器只输出一种语言字幕. 许多同学想*同时*输出两种字幕,以potplayer为例,设置方法如下: mvk内 ...

  5. STM32F407学习之DMA双缓冲模式HAL库实现

    本学期第一次培训要求用单片机实现1到40KHz的单步进变频正弦波输出,本菜鸡一开始用了通过修改定时器预分频系数和自动重装载值改变输出波表频率.修改定时器 + DDS两种方法,但实现的效果不太理想,不仅 ...

  6. JWPlayer Flash播放器如何实现视频分段载入播放从而节省带宽?

    近期由于一个项目的需要,对Flash版本的播放器JWPlayer做了一些改进以支持一些功能,这里把中间用到的一些思路和做法记录下. 首先一个功能是:客户的很多flv视频都是完整的一个大视频,希望JWP ...

  7. [微信音频播放器] html5 audio 制作的微信播放器

    weixinAudio.js 一个简单的微信样式播放器 播放器DOM及CSS是微信里内置的音频播放器的样式,重新创建了控制层js,方便在在公众号,APP等场景使用. 例子 demo 地址 github ...

  8. 自定义音频播放器_创建自定义HTML5音频播放器

    自定义音频播放器 在本教程中,我将向您介绍HTML5音频,并向您展示如何创建自己的播放器. 如果您想走捷径,请查看Envato市场上可用的现成的HTML5音频播放器 . 它使您可以从各种来源创建播放列 ...

  9. 带倍速音频播放器_带有播放列表HTML5音频播放器

    带倍速音频播放器 HTML5 Audio player with playlist HTML5 audio player. Many of you faced with the task of cre ...

最新文章

  1. 2019诺贝尔生理学\医学奖率先颁出!英美3学者加冕,揭秘血与氧关系,抗击肿瘤和癌症...
  2. caffe读书笔记1 CIFAR-10在caffe上进行训练与学习
  3. [deviceone开发]-数据绑定示例
  4. mysql 增量备份_云计算-开源数据库-备份
  5. 采用推理的方法认知单词、CBOW模型
  6. MFC开发-垂直滚动条一直处于底部
  7. 异地多活,企业上云的必然趋势!
  8. Veritas Backup Exec 21设置备份任务
  9. python下载电影_Python3.x+迅雷x 自动下载高分电影的实现方法
  10. SQLFeatureNotSupportedException: isValid
  11. linux查看gc日志,GC通用日志解读
  12. 北、上、广、深数据分析职位分析
  13. 2020春节假期VR/AR资讯汇总
  14. 旋转编码器的原理和使用方法
  15. K8s二进制部署-flanneld报(Couldn‘t fetch network config)
  16. AS第一次作业:实现APP门户界面框架设计
  17. 听我瞎说--路指人仙
  18. 防火墙安全策略功能入门
  19. 【STM32】烧录器与程序下载
  20. 某客户领导汇报材料编写问题与思考

热门文章

  1. 桌面总是出现计算机内存不足,电脑 总显示内存不足,而且运行还很卡,怎么办...
  2. AD如何使用向导快速画DIP系列
  3. python jsonpath库_Python json解析库jsonpath原理及使用示例
  4. python机器学习 | 多项式回归和拟合
  5. Win 2003 防***提升权限设置IIS服务器安全配置整理
  6. 网络工程属计算机专业吗,网络工程专业属于什么门类
  7. centos7配置mysql主从数据库
  8. 香港中文大学深圳(CUHKSZ)2020fall数据科学硕士申请及笔面经
  9. 联想修复服务器系统还原,如何使用Windows恢复环境(Re)修复Windows 10系统
  10. 早起看CMU15213课程 第五天