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

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

/**
  * 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
  *
  * 

© Copyright (c) 2021 STMicroelectronics. * All rights reserved.

* * 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
  *
  * 

© Copyright (c) 2021 STMicroelectronics. * All rights reserved.

* * 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 Configuration PC8 ------> SDMMC1_D0 PC9 ------> SDMMC1_D1 PC10 ------> SDMMC1_D2 PC11 ------> SDMMC1_D3 PC12 ------> SDMMC1_CK PD2 ------> 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 Configuration PC8 ------> SDMMC1_D0 PC9 ------> SDMMC1_D1 PC10 ------> SDMMC1_D2 PC11 ------> SDMMC1_D3 PC12 ------> SDMMC1_CK PD2 ------> 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 Configuration PA9 ------> USART1_TX PA10 ------> 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 Configuration PA9 ------> USART1_TX PA10 ------> 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 Configuration PF0 ------> FMC_A0 PF1 ------> FMC_A1 PF2 ------> FMC_A2 PF3 ------> FMC_A3 PF4 ------> FMC_A4 PF5 ------> FMC_A5 PC0 ------> FMC_SDNWE PC2 ------> FMC_SDNE0 PC3 ------> FMC_SDCKE0 PF11 ------> FMC_SDNRAS PF12 ------> FMC_A6 PF13 ------> FMC_A7 PF14 ------> FMC_A8 PF15 ------> FMC_A9 PG0 ------> FMC_A10 PG1 ------> FMC_A11 PE7 ------> FMC_D4 PE8 ------> FMC_D5 PE9 ------> FMC_D6 PE10 ------> FMC_D7 PE11 ------> FMC_D8 PE12 ------> FMC_D9 PE13 ------> FMC_D10 PE14 ------> FMC_D11 PE15 ------> FMC_D12 PD8 ------> FMC_D13 PD9 ------> FMC_D14 PD10 ------> FMC_D15 PD14 ------> FMC_D0 PD15 ------> FMC_D1 PG2 ------> FMC_A12 PG4 ------> FMC_BA0 PG5 ------> FMC_BA1 PG8 ------> FMC_SDCLK PD0 ------> FMC_D2 PD1 ------> FMC_D3 PG15 ------> FMC_SDNCAS PE0 ------> FMC_NBL0 PE1 ------> 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 Configuration PF0 ------> FMC_A0 PF1 ------> FMC_A1 PF2 ------> FMC_A2 PF3 ------> FMC_A3 PF4 ------> FMC_A4 PF5 ------> FMC_A5 PC0 ------> FMC_SDNWE PC2 ------> FMC_SDNE0 PC3 ------> FMC_SDCKE0 PF11 ------> FMC_SDNRAS PF12 ------> FMC_A6 PF13 ------> FMC_A7 PF14 ------> FMC_A8 PF15 ------> FMC_A9 PG0 ------> FMC_A10 PG1 ------> FMC_A11 PE7 ------> FMC_D4 PE8 ------> FMC_D5 PE9 ------> FMC_D6 PE10 ------> FMC_D7 PE11 ------> FMC_D8 PE12 ------> FMC_D9 PE13 ------> FMC_D10 PE14 ------> FMC_D11 PE15 ------> FMC_D12 PD8 ------> FMC_D13 PD9 ------> FMC_D14 PD10 ------> FMC_D15 PD14 ------> FMC_D0 PD15 ------> FMC_D1 PG2 ------> FMC_A12 PG4 ------> FMC_BA0 PG5 ------> FMC_BA1 PG8 ------> FMC_SDCLK PD0 ------> FMC_D2 PD1 ------> FMC_D3 PG15 ------> FMC_SDNCAS PE0 ------> FMC_NBL0 PE1 ------> 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;		//设置PLLI2SN
	tempreg|=(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时钟来源为PLLI2SQ
	RCC->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;	//设置MCKDIV
	tempreg|=1<<16;							//使能SAI1 Block A
	tempreg|=1<<17;							//使能DMA
	SAI1_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 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);

     

    }
}

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);
    }
}


#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",即0X46464952
	uint32_t ChunkSize ;		   	//集合大小;文件总大小-8
	uint32_t Format;	   			//格式;WAVE,即0X45564157
}ChunkRIFF ;
//fmt块
typedef __packed struct
{
	uint32_t ChunkID;		   	//chunk id;这里固定为"fmt ",即0X20746D66
	uint32_t ChunkSize ;		   	//子集合大小(不包括ID和Size);这里为:20.
	uint16_t AudioFormat;	  	//音频格式;0X01,表示线性PCM;0X11表示IMA ADPCM
	uint16_t NumOfChannels;		//通道数量;1,表示单声道;2,表示双声道;
	uint32_t SampleRate;			//采样率;0X1F40,表示8Khz
	uint32_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",即0X5453494C
	uint32_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 ADPCM
	uint16_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,32bit

	uint32_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 

//	 
//本程序移植自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];		   	//ID
    uint8_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];		   	//帧ID
    uint8_t size[4];			//帧大小
    uint16_t flags;			//帧标志
}ID3V23_FrameHead;

//MP3 Xing帧信息(没有全部列出来,仅列出有用的部分)
typedef __packed struct 
{
    uint8_t id[4];		   	//帧ID,为Xing/Info
    uint8_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/Info
	uint8_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每帧采样数等于1152
					else 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读指针指向buffer
			offset=0;		//偏移量为0
			outofdata=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里面,数据量大小为:bytesleft
						f_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 
#include 
#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,32bit
	
	uint32_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;		//解码数组0
		fc->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、
STM32F767 音乐播放器 SAI DMA双缓冲 可播放WAV、MP3、FLAC文件_第1张图片
2、
STM32F767 音乐播放器 SAI DMA双缓冲 可播放WAV、MP3、FLAC文件_第2张图片
3、双缓冲模式DMA需要设置为DMA_CIRCULAR
4、SAI_DMA中断优先级比SD_DMA中断优先级低,是因为在wavplay中断程序中会执行读SD卡

你可能感兴趣的:(STM32,stm32)