以下为在单缓冲程序基础上修改
/**
* 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、
2、
3、双缓冲模式DMA需要设置为DMA_CIRCULAR
4、SAI_DMA中断优先级比SD_DMA中断优先级低,是因为在wavplay中断程序中会执行读SD卡