/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @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"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "delay.h"
#include "bsp_printf.h"
#include "bsp_key.h"
#include "string.h"
#include "bsp_sdram.h"
#include "bsp_malloc.h"
#include "bsp_sdmmc.h"
#include "ff.h" /* Obtains integer types */
#include "bsp_exfuns.h"
#include "bsp_audioplay.h"
//#include "bsp_w25qxx.h"
//#include "bsp_ftl.h"
//#include "bsp_nand.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
SAI_HandleTypeDef hsai_BlockA1;
DMA_HandleTypeDef hdma_sai1_a;
SD_HandleTypeDef hsd1;
DMA_HandleTypeDef hdma_sdmmc1_rx;
DMA_HandleTypeDef hdma_sdmmc1_tx;
UART_HandleTypeDef huart1;
SDRAM_HandleTypeDef hsdram1;
/* USER CODE BEGIN PV */
volatile uint8_t rx_done, tx_done;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_FMC_Init(void);
static void MX_DMA_Init(void);
static void MX_SDMMC1_SD_Init(void);
static void MX_SAI1_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
static void Sdram_SendCommand(uint32_t CommandMode, uint32_t CommandTarget, uint32_t AutoRefreshNumber, uint32_t ModeRegisterDefinition)
{
FMC_SDRAM_CommandTypeDef Command;
Command.AutoRefreshNumber = AutoRefreshNumber;
Command.CommandMode = CommandMode;
Command.CommandTarget = CommandTarget;
Command.ModeRegisterDefinition = ModeRegisterDefinition;
HAL_SDRAM_SendCommand(&hsdram1, &Command, 0);
}
static void Sdram_Init_Sequence(void)
{
uint32_t ModeRegisterDefinition;
Sdram_SendCommand(FMC_SDRAM_CMD_CLK_ENABLE, FMC_SDRAM_CMD_TARGET_BANK1, 1, 0);//时钟配置使能
delay_us(500);//至少延时200us
Sdram_SendCommand(FMC_SDRAM_CMD_PALL, FMC_SDRAM_CMD_TARGET_BANK1, 1, 0);//对所有存储区预充电
Sdram_SendCommand(FMC_SDRAM_CMD_AUTOREFRESH_MODE, FMC_SDRAM_CMD_TARGET_BANK1, 8, 0);//设置自刷新次数
//
#define SDRAM_MODEREG_BURST_LENGTH_1 ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_LENGTH_2 ((uint16_t)0x0001)
#define SDRAM_MODEREG_BURST_LENGTH_4 ((uint16_t)0x0002)
#define SDRAM_MODEREG_BURST_LENGTH_8 ((uint16_t)0x0004)
#define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED ((uint16_t)0x0008)
#define SDRAM_MODEREG_CAS_LATENCY_2 ((uint16_t)0x0020)
#define SDRAM_MODEREG_CAS_LATENCY_3 ((uint16_t)0x0030)
#define SDRAM_MODEREG_OPERATING_MODE_STANDARD ((uint16_t)0x0000)
#define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000)
#define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE ((uint16_t)0x0200)
ModeRegisterDefinition=(uint32_t)SDRAM_MODEREG_BURST_LENGTH_1 | //设置突发长度:1(可以是1/2/4/8)
SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL | //设置突发类型:连续(可以是连续/交错)
SDRAM_MODEREG_CAS_LATENCY_3 | //设置CAS值:3(可以是2/3)
SDRAM_MODEREG_OPERATING_MODE_STANDARD | //设置操作模式:0,标准模式
SDRAM_MODEREG_WRITEBURST_MODE_SINGLE; //设置突发写模式:1,单点访问
Sdram_SendCommand(FMC_SDRAM_CMD_LOAD_MODE, FMC_SDRAM_CMD_TARGET_BANK1, 1, ModeRegisterDefinition);
HAL_SDRAM_ProgramRefreshRate(&hsdram1, 823);
}
//通过串口打印SD卡相关信息
void show_sdcard_info(void)
{
HAL_SD_CardCIDTypeDef cid;
switch(hsd1.SdCard.CardVersion)
{
case CARD_V1_X:printf("Card Version:CARD_V1_X\r\n");break;
case CARD_V2_X:printf("Card Version:CARD_V2_X\r\n");break;
}
switch(hsd1.SdCard.CardType)
{
case CARD_SDSC:printf("Card Type:CARD_SDSC\r\n");break;
case CARD_SDHC_SDXC:printf("Card Type:CARD_SDHC_SDXC\r\n");break;
case CARD_SECURED:printf("Card Type:CARD_SECURED\r\n");break;
}
if(HAL_OK != HAL_SD_GetCardCID(&hsd1, &cid))
{
Error_Handler();
}
printf("Card ManufacturerID:%d\r\n",cid.ManufacturerID); //制造商ID
printf("Card RCA:%d\r\n",hsd1.SdCard.RelCardAdd ); //卡相对地址
printf("Card Capacity:%d MB\r\n",(uint32_t)(((uint64_t)hsd1.SdCard.BlockNbr*hsd1.SdCard.BlockSize)>>20)); //显示容量
printf("Card BlockSize:%d\r\n\r\n",hsd1.SdCard.BlockSize); //显示块大小
printf("Card LogBlockNbr:%d\r\n\r\n",hsd1.SdCard.LogBlockNbr);
printf("Card LogBlockSize:%d\r\n\r\n",hsd1.SdCard.LogBlockSize);
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_FMC_Init();
MX_DMA_Init();
MX_SDMMC1_SD_Init();
MX_SAI1_Init();
/* USER CODE BEGIN 2 */
delay_init(216);
delay_ms(5000);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
Sdram_Init_Sequence();
my_mem_init(SRAMIN); //初始化内部内存池
my_mem_init(SRAMEX); //初始化外部SDRAM内存池
exfuns_init(); //为fatfs相关变量申请内存
f_mount(fs[0],"0:",1); //挂载SD卡
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
audio_play();
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
/** Configure LSE Drive Capability
*/
HAL_PWR_EnableBkUpAccess();
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 25;
RCC_OscInitStruct.PLL.PLLN = 432;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 9;
RCC_OscInitStruct.PLL.PLLR = 2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Activate the Over-Drive mode
*/
if (HAL_PWREx_EnableOverDrive() != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7) != HAL_OK)
{
Error_Handler();
}
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_SAI1
|RCC_PERIPHCLK_SDMMC1|RCC_PERIPHCLK_CLK48;
PeriphClkInitStruct.PLLSAI.PLLSAIN = 288;
PeriphClkInitStruct.PLLSAI.PLLSAIR = 4;
PeriphClkInitStruct.PLLSAI.PLLSAIQ = 4;
PeriphClkInitStruct.PLLSAI.PLLSAIP = RCC_PLLSAIP_DIV2;
PeriphClkInitStruct.PLLSAIDivQ = 1;
PeriphClkInitStruct.PLLSAIDivR = RCC_PLLSAIDIVR_8;
PeriphClkInitStruct.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLLSAI;
PeriphClkInitStruct.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
PeriphClkInitStruct.Clk48ClockSelection = RCC_CLK48SOURCE_PLL;
PeriphClkInitStruct.Sdmmc1ClockSelection = RCC_SDMMC1CLKSOURCE_CLK48;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Enables the Clock Security System
*/
HAL_RCC_EnableCSS();
}
/**
* @brief SAI1 Initialization Function
* @param None
* @retval None
*/
static void MX_SAI1_Init(void)
{
/* USER CODE BEGIN SAI1_Init 0 */
/* USER CODE END SAI1_Init 0 */
/* USER CODE BEGIN SAI1_Init 1 */
/* USER CODE END SAI1_Init 1 */
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 = SAI_AUDIO_FREQUENCY_192K;
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, SAI_PROTOCOL_DATASIZE_16BIT, 2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SAI1_Init 2 */
/* USER CODE END SAI1_Init 2 */
}
/**
* @brief SDMMC1 Initialization Function
* @param None
* @retval None
*/
static void MX_SDMMC1_SD_Init(void)
{
/* USER CODE BEGIN SDMMC1_Init 0 */
/* USER CODE END SDMMC1_Init 0 */
/* USER CODE BEGIN SDMMC1_Init 1 */
/* USER CODE END SDMMC1_Init 1 */
hsd1.Instance = SDMMC1;
hsd1.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING;
hsd1.Init.ClockBypass = SDMMC_CLOCK_BYPASS_DISABLE;
hsd1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE;
hsd1.Init.BusWide = SDMMC_BUS_WIDE_1B;
hsd1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE;
hsd1.Init.ClockDiv = 0;
if (HAL_SD_Init(&hsd1) != HAL_OK)
{
Error_Handler();
}
if (HAL_SD_ConfigWideBusOperation(&hsd1, SDMMC_BUS_WIDE_4B) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SDMMC1_Init 2 */
/* USER CODE END SDMMC1_Init 2 */
}
/**
* @brief USART1 Initialization Function
* @param None
* @retval None
*/
static void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
/* USER CODE END USART1_Init 2 */
}
/**
* 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, 0, 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);
}
/* FMC initialization function */
static void MX_FMC_Init(void)
{
/* USER CODE BEGIN FMC_Init 0 */
/* USER CODE END FMC_Init 0 */
FMC_SDRAM_TimingTypeDef SdramTiming = {0};
/* USER CODE BEGIN FMC_Init 1 */
/* USER CODE END FMC_Init 1 */
/** Perform the SDRAM1 memory initialization sequence
*/
hsdram1.Instance = FMC_SDRAM_DEVICE;
/* hsdram1.Init */
hsdram1.Init.SDBank = FMC_SDRAM_BANK1;
hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9;
hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13;
hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;
hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3;
hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;
hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;
hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;
/* SdramTiming */
SdramTiming.LoadToActiveDelay = 2;
SdramTiming.ExitSelfRefreshDelay = 8;
SdramTiming.SelfRefreshTime = 4;
SdramTiming.RowCycleDelay = 7;
SdramTiming.WriteRecoveryTime = 3;
SdramTiming.RPDelay = 2;
SdramTiming.RCDDelay = 2;
if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK)
{
Error_Handler( );
}
/* USER CODE BEGIN FMC_Init 2 */
/* USER CODE END FMC_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_5, GPIO_PIN_RESET);
/*Configure GPIO pin : PC13 */
GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pin : PA0 */
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : PH2 PH3 */
GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);
/*Configure GPIO pins : PB0 PB5 */
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/*Configure GPIO pin : PB1 */
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.h
* @brief : Header for main.c file.
* This file contains the common defines of the application.
******************************************************************************
* @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 */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f7xx_hal.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
//extern SDRAM_HandleTypeDef hsdram1;
extern SD_HandleTypeDef hsd1;
//extern QSPI_HandleTypeDef hqspi;
//extern NAND_HandleTypeDef hnand1;
extern SAI_HandleTypeDef hsai_BlockA1;
extern DMA_HandleTypeDef hdma_sai1_a;
/* USER CODE END Includes */
/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */
extern volatile uint8_t rx_done, tx_done;
/* USER CODE END ET */
/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */
/* USER CODE END EC */
/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */
/* USER CODE END EM */
/* Exported functions prototypes ---------------------------------------------*/
void Error_Handler(void);
/* USER CODE BEGIN EFP */
/* USER CODE END EFP */
/* Private defines -----------------------------------------------------------*/
/* USER CODE BEGIN Private defines */
定义一些常用的数据类型短关键字
//typedef int32_t s32;
//typedef int16_t s16;
//typedef int8_t s8;
//typedef const int32_t sc32;
//typedef const int16_t sc16;
//typedef const int8_t sc8;
//typedef __IO int32_t vs32;
//typedef __IO int16_t vs16;
//typedef __IO int8_t vs8;
//typedef __I int32_t vsc32;
//typedef __I int16_t vsc16;
//typedef __I int8_t vsc8;
//typedef uint32_t u32;
//typedef uint16_t u16;
//typedef uint8_t u8;
//typedef const uint32_t uc32;
//typedef const uint16_t uc16;
//typedef const uint8_t uc8;
//typedef __IO uint32_t vu32;
//typedef __IO uint16_t vu16;
//typedef __IO uint8_t vu8;
//typedef __I uint32_t vuc32;
//typedef __I uint16_t vuc16;
//typedef __I uint8_t vuc8;
/* USER CODE END Private defines */
#ifdef __cplusplus
}
#endif
#endif /* __MAIN_H */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
/* 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"
/* 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);
}
/**
* @brief Tx Transfer completed callback.
* @param hsai pointer to a SAI_HandleTypeDef structure that contains
* the configuration information for SAI module.
* @retval None
*/
void HAL_SAI_TxCpltCallback(SAI_HandleTypeDef *hsai)
{
wavtransferend = 1;
mp3transferend = 1;
flactransferend = 1;
}
/* 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_NORMAL;
// 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****/
#include "bsp_wm8978.h"
#include "myiic.h"
#include "delay.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F7开发板
//WM8978驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/29
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//
//WM8978寄存器值缓存区(总共58个寄存器,0~57),占用116字节内存
//因为WM8978的IIC操作不支持读操作,所以在本地保存所有寄存器值
//写WM8978寄存器时,同步更新到本地寄存器值,读寄存器时,直接返回本地保存的寄存器值.
//注意:WM8978的寄存器值是9位的,所以要用uint16_t来存储.
static uint16_t WM8978_REGVAL_TBL[58]=
{
0X0000,0X0000,0X0000,0X0000,0X0050,0X0000,0X0140,0X0000,
0X0000,0X0000,0X0000,0X00FF,0X00FF,0X0000,0X0100,0X00FF,
0X00FF,0X0000,0X012C,0X002C,0X002C,0X002C,0X002C,0X0000,
0X0032,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,
0X0038,0X000B,0X0032,0X0000,0X0008,0X000C,0X0093,0X00E9,
0X0000,0X0000,0X0000,0X0000,0X0003,0X0010,0X0010,0X0100,
0X0100,0X0002,0X0001,0X0001,0X0039,0X0039,0X0039,0X0039,
0X0001,0X0001
};
//WM8978初始化
//返回值:0,初始化正常
// 其他,错误代码
uint8_t WM8978_Init(void)
{
uint8_t res;
IIC_Init(); //初始化IIC接口
res=WM8978_Write_Reg(0,0); //软复位WM8978
if(res)return 1; //发送指令失败,WM8978异常
//以下为通用设置
WM8978_Write_Reg(1,0X1B); //R1,MICEN设置为1(MIC使能),BIASEN设置为1(模拟器工作),VMIDSEL[1:0]设置为:11(5K)
WM8978_Write_Reg(2,0X1B0); //R2,ROUT1,LOUT1输出使能(耳机可以工作),BOOSTENR,BOOSTENL使能
WM8978_Write_Reg(3,0X6C); //R3,LOUT2,ROUT2输出使能(喇叭工作),RMIX,LMIX使能
WM8978_Write_Reg(6,0); //R6,MCLK由外部提供
WM8978_Write_Reg(43,1<<4); //R43,INVROUT2反向,驱动喇叭
WM8978_Write_Reg(47,1<<8); //R47设置,PGABOOSTL,左通道MIC获得20倍增益
WM8978_Write_Reg(48,1<<8); //R48设置,PGABOOSTR,右通道MIC获得20倍增益
WM8978_Write_Reg(49,1<<1); //R49,TSDEN,开启过热保护
WM8978_Write_Reg(49,1<<2); //R49,SPEAKER BOOST,1.5x
WM8978_Write_Reg(10,1<<3); //R10,SOFTMUTE关闭,128x采样,最佳SNR
WM8978_Write_Reg(14,1<<3); //R14,ADC 128x采样率
return 0;
}
//WM8978写寄存器
//reg:寄存器地址
//val:要写入寄存器的值
//返回值:0,成功;
// 其他,错误代码
uint8_t WM8978_Write_Reg(uint8_t reg,uint16_t val)
{
IIC_Start();
IIC_Send_Byte((WM8978_ADDR<<1)|0);//发送器件地址+写命令
if(IIC_Wait_Ack())return 1; //等待应答(成功?/失败?)
IIC_Send_Byte((reg<<1)|((val>>8)&0X01));//写寄存器地址+数据的最高位
if(IIC_Wait_Ack())return 2; //等待应答(成功?/失败?)
IIC_Send_Byte(val&0XFF); //发送数据
if(IIC_Wait_Ack())return 3; //等待应答(成功?/失败?)
IIC_Stop();
WM8978_REGVAL_TBL[reg]=val; //保存寄存器值到本地
return 0;
}
//WM8978读寄存器
//就是读取本地寄存器值缓冲区内的对应值
//reg:寄存器地址
//返回值:寄存器值
uint16_t WM8978_Read_Reg(uint8_t reg)
{
return WM8978_REGVAL_TBL[reg];
}
//WM8978 DAC/ADC配置
//adcen:adc使能(1)/关闭(0)
//dacen:dac使能(1)/关闭(0)
void WM8978_ADDA_Cfg(uint8_t dacen,uint8_t adcen)
{
uint16_t regval;
regval=WM8978_Read_Reg(3); //读取R3
if(dacen)regval|=3<<0; //R3最低2个位设置为1,开启DACR&DACL
else regval&=~(3<<0); //R3最低2个位清零,关闭DACR&DACL.
WM8978_Write_Reg(3,regval); //设置R3
regval=WM8978_Read_Reg(2); //读取R2
if(adcen)regval|=3<<0; //R2最低2个位设置为1,开启ADCR&ADCL
else regval&=~(3<<0); //R2最低2个位清零,关闭ADCR&ADCL.
WM8978_Write_Reg(2,regval); //设置R2
}
//WM8978 输入通道配置
//micen:MIC开启(1)/关闭(0)
//lineinen:Line In开启(1)/关闭(0)
//auxen:aux开启(1)/关闭(0)
void WM8978_Input_Cfg(uint8_t micen,uint8_t lineinen,uint8_t auxen)
{
uint16_t regval;
regval=WM8978_Read_Reg(2); //读取R2
if(micen)regval|=3<<2; //开启INPPGAENR,INPPGAENL(MIC的PGA放大)
else regval&=~(3<<2); //关闭INPPGAENR,INPPGAENL.
WM8978_Write_Reg(2,regval); //设置R2
regval=WM8978_Read_Reg(44); //读取R44
if(micen)regval|=3<<4|3<<0; //开启LIN2INPPGA,LIP2INPGA,RIN2INPPGA,RIP2INPGA.
else regval&=~(3<<4|3<<0); //关闭LIN2INPPGA,LIP2INPGA,RIN2INPPGA,RIP2INPGA.
WM8978_Write_Reg(44,regval);//设置R44
if(lineinen)WM8978_LINEIN_Gain(5);//LINE IN 0dB增益
else WM8978_LINEIN_Gain(0); //关闭LINE IN
if(auxen)WM8978_AUX_Gain(7);//AUX 6dB增益
else WM8978_AUX_Gain(0); //关闭AUX输入
}
//WM8978 输出配置
//dacen:DAC输出(放音)开启(1)/关闭(0)
//bpsen:Bypass输出(录音,包括MIC,LINE IN,AUX等)开启(1)/关闭(0)
void WM8978_Output_Cfg(uint8_t dacen,uint8_t bpsen)
{
uint16_t regval=0;
if(dacen)regval|=1<<0; //DAC输出使能
if(bpsen)
{
regval|=1<<1; //BYPASS使能
regval|=5<<2; //0dB增益
}
WM8978_Write_Reg(50,regval);//R50设置
WM8978_Write_Reg(51,regval);//R51设置
}
//WM8978 MIC增益设置(不包括BOOST的20dB,MIC-->ADC输入部分的增益)
//gain:0~63,对应-12dB~35.25dB,0.75dB/Step
void WM8978_MIC_Gain(uint8_t gain)
{
gain&=0X3F;
WM8978_Write_Reg(45,gain); //R45,左通道PGA设置
WM8978_Write_Reg(46,gain|1<<8); //R46,右通道PGA设置
}
//WM8978 L2/R2(也就是Line In)增益设置(L2/R2-->ADC输入部分的增益)
//gain:0~7,0表示通道禁止,1~7,对应-12dB~6dB,3dB/Step
void WM8978_LINEIN_Gain(uint8_t gain)
{
uint16_t regval;
gain&=0X07;
regval=WM8978_Read_Reg(47); //读取R47
regval&=~(7<<4); //清除原来的设置
WM8978_Write_Reg(47,regval|gain<<4);//设置R47
regval=WM8978_Read_Reg(48); //读取R48
regval&=~(7<<4); //清除原来的设置
WM8978_Write_Reg(48,regval|gain<<4);//设置R48
}
//WM8978 AUXR,AUXL(PWM音频部分)增益设置(AUXR/L-->ADC输入部分的增益)
//gain:0~7,0表示通道禁止,1~7,对应-12dB~6dB,3dB/Step
void WM8978_AUX_Gain(uint8_t gain)
{
uint16_t regval;
gain&=0X07;
regval=WM8978_Read_Reg(47); //读取R47
regval&=~(7<<0); //清除原来的设置
WM8978_Write_Reg(47,regval|gain<<0);//设置R47
regval=WM8978_Read_Reg(48); //读取R48
regval&=~(7<<0); //清除原来的设置
WM8978_Write_Reg(48,regval|gain<<0);//设置R48
}
//设置I2S工作模式
//fmt:0,LSB(右对齐);1,MSB(左对齐);2,飞利浦标准I2S;3,PCM/DSP;
//len:0,16位;1,20位;2,24位;3,32位;
void WM8978_I2S_Cfg(uint8_t fmt,uint8_t len)
{
fmt&=0X03;
len&=0X03;//限定范围
WM8978_Write_Reg(4,(fmt<<3)|(len<<5)); //R4,WM8978工作模式设置
}
//设置耳机左右声道音量
//voll:左声道音量(0~63)
//volr:右声道音量(0~63)
void WM8978_HPvol_Set(uint8_t voll,uint8_t volr)
{
voll&=0X3F;
volr&=0X3F;//限定范围
if(voll==0)voll|=1<<6;//音量为0时,直接mute
if(volr==0)volr|=1<<6;//音量为0时,直接mute
WM8978_Write_Reg(52,voll); //R52,耳机左声道音量设置
WM8978_Write_Reg(53,volr|(1<<8)); //R53,耳机右声道音量设置,同步更新(HPVU=1)
}
//设置喇叭音量
//voll:左声道音量(0~63)
void WM8978_SPKvol_Set(uint8_t volx)
{
volx&=0X3F;//限定范围
if(volx==0)volx|=1<<6;//音量为0时,直接mute
WM8978_Write_Reg(54,volx); //R54,喇叭左声道音量设置
WM8978_Write_Reg(55,volx|(1<<8)); //R55,喇叭右声道音量设置,同步更新(SPKVU=1)
}
//设置3D环绕声
//depth:0~15(3D强度,0最弱,15最强)
void WM8978_3D_Set(uint8_t depth)
{
depth&=0XF;//限定范围
WM8978_Write_Reg(41,depth); //R41,3D环绕设置
}
//设置EQ/3D作用方向
//dir:0,在ADC起作用
// 1,在DAC起作用(默认)
void WM8978_EQ_3D_Dir(uint8_t dir)
{
uint16_t regval;
regval=WM8978_Read_Reg(0X12);
if(dir)regval|=1<<8;
else regval&=~(1<<8);
WM8978_Write_Reg(18,regval);//R18,EQ1的第9位控制EQ/3D方向
}
//设置EQ1
//cfreq:截止频率,0~3,分别对应:80/105/135/175Hz
//gain:增益,0~24,对应-12~+12dB
void WM8978_EQ1_Set(uint8_t cfreq,uint8_t gain)
{
uint16_t regval;
cfreq&=0X3;//限定范围
if(gain>24)gain=24;
gain=24-gain;
regval=WM8978_Read_Reg(18);
regval&=0X100;
regval|=cfreq<<5; //设置截止频率
regval|=gain; //设置增益
WM8978_Write_Reg(18,regval);//R18,EQ1设置
}
//设置EQ2
//cfreq:中心频率,0~3,分别对应:230/300/385/500Hz
//gain:增益,0~24,对应-12~+12dB
void WM8978_EQ2_Set(uint8_t cfreq,uint8_t gain)
{
uint16_t regval=0;
cfreq&=0X3;//限定范围
if(gain>24)gain=24;
gain=24-gain;
regval|=cfreq<<5; //设置截止频率
regval|=gain; //设置增益
WM8978_Write_Reg(19,regval);//R19,EQ2设置
}
//设置EQ3
//cfreq:中心频率,0~3,分别对应:650/850/1100/1400Hz
//gain:增益,0~24,对应-12~+12dB
void WM8978_EQ3_Set(uint8_t cfreq,uint8_t gain)
{
uint16_t regval=0;
cfreq&=0X3;//限定范围
if(gain>24)gain=24;
gain=24-gain;
regval|=cfreq<<5; //设置截止频率
regval|=gain; //设置增益
WM8978_Write_Reg(20,regval);//R20,EQ3设置
}
//设置EQ4
//cfreq:中心频率,0~3,分别对应:1800/2400/3200/4100Hz
//gain:增益,0~24,对应-12~+12dB
void WM8978_EQ4_Set(uint8_t cfreq,uint8_t gain)
{
uint16_t regval=0;
cfreq&=0X3;//限定范围
if(gain>24)gain=24;
gain=24-gain;
regval|=cfreq<<5; //设置截止频率
regval|=gain; //设置增益
WM8978_Write_Reg(21,regval);//R21,EQ4设置
}
//设置EQ5
//cfreq:中心频率,0~3,分别对应:5300/6900/9000/11700Hz
//gain:增益,0~24,对应-12~+12dB
void WM8978_EQ5_Set(uint8_t cfreq,uint8_t gain)
{
uint16_t regval=0;
cfreq&=0X3;//限定范围
if(gain>24)gain=24;
gain=24-gain;
regval|=cfreq<<5; //设置截止频率
regval|=gain; //设置增益
WM8978_Write_Reg(22,regval);//R22,EQ5设置
}
#ifndef __BSP_WM8978_H
#define __BSP_WM8978_H
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F7开发板
//WM8978驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/29
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//
#define WM8978_ADDR 0X1A //WM8978的器件地址,固定为0X1A
#define EQ1_80Hz 0X00
#define EQ1_105Hz 0X01
#define EQ1_135Hz 0X02
#define EQ1_175Hz 0X03
#define EQ2_230Hz 0X00
#define EQ2_300Hz 0X01
#define EQ2_385Hz 0X02
#define EQ2_500Hz 0X03
#define EQ3_650Hz 0X00
#define EQ3_850Hz 0X01
#define EQ3_1100Hz 0X02
#define EQ3_14000Hz 0X03
#define EQ4_1800Hz 0X00
#define EQ4_2400Hz 0X01
#define EQ4_3200Hz 0X02
#define EQ4_4100Hz 0X03
#define EQ5_5300Hz 0X00
#define EQ5_6900Hz 0X01
#define EQ5_9000Hz 0X02
#define EQ5_11700Hz 0X03
uint8_t WM8978_Init(void);
void WM8978_ADDA_Cfg(uint8_t dacen,uint8_t adcen);
void WM8978_Input_Cfg(uint8_t micen,uint8_t lineinen,uint8_t auxen);
void WM8978_Output_Cfg(uint8_t dacen,uint8_t bpsen);
void WM8978_MIC_Gain(uint8_t gain);
void WM8978_LINEIN_Gain(uint8_t gain);
void WM8978_AUX_Gain(uint8_t gain);
uint8_t WM8978_Write_Reg(uint8_t reg,uint16_t val);
uint16_t WM8978_Read_Reg(uint8_t reg);
void WM8978_HPvol_Set(uint8_t voll,uint8_t volr);
void WM8978_SPKvol_Set(uint8_t volx);
void WM8978_I2S_Cfg(uint8_t fmt,uint8_t len);
void WM8978_3D_Set(uint8_t depth);
void WM8978_EQ_3D_Dir(uint8_t dir);
void WM8978_EQ1_Set(uint8_t cfreq,uint8_t gain);
void WM8978_EQ2_Set(uint8_t cfreq,uint8_t gain);
void WM8978_EQ3_Set(uint8_t cfreq,uint8_t gain);
void WM8978_EQ4_Set(uint8_t cfreq,uint8_t gain);
void WM8978_EQ5_Set(uint8_t cfreq,uint8_t gain);
#ifdef __cplusplus
}
#endif
#endif /* __BSP_WM8978_H */
#include "bsp_audioplay.h"
#include "ff.h"
#include "bsp_malloc.h"
#include "bsp_printf.h"
#include "bsp_wm8978.h"
#include "bsp_sai.h"
#include "delay.h"
#include "bsp_key.h"
#include "bsp_exfuns.h"
//#include "text.h"
#include "string.h"
#include "bsp_wavplay.h"
#include "bsp_mp3play.h"
#include "bsp_flacplay.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//音乐播放器 应用代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/18
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//
//音乐播放控制器
__audiodev audiodev;
//开始音频播放
void audio_start(void)
{
audiodev.status=3<<0;//开始播放+非暂停
// SAI_Play_Start();
}
//关闭音频播放
void audio_stop(void)
{
audiodev.status=0;
// SAI_Play_Stop();
}
//得到path路径下,目标文件的总个数
//path:路径
//返回值:总有效文件数
uint16_t audio_get_tnum(uint8_t *path)
{
uint8_t res;
uint16_t rval=0;
DIR tdir; //临时目录
FILINFO* tfileinfo; //临时文件信息
tfileinfo=(FILINFO*)mymalloc(SRAMIN,sizeof(FILINFO));//申请内存
res=f_opendir(&tdir,(const TCHAR*)path); //打开目录
if(res==FR_OK&&tfileinfo)
{
while(1)//查询总的有效文件数
{
res=f_readdir(&tdir,tfileinfo); //读取目录下的一个文件
if(res!=FR_OK||tfileinfo->fname[0]==0)break; //错误了/到末尾了,退出
res=f_typetell((uint8_t*)tfileinfo->fname);
if((res&0XF0)==0X40)//取高四位,看看是不是音乐文件
{
rval++;//有效文件数增加1
}
}
}
myfree(SRAMIN,tfileinfo);//释放内存
return rval;
}
//显示曲目索引
//index:当前索引
//total:总文件数
void audio_index_show(uint16_t index,uint16_t total)
{
//显示当前曲目的索引,及总曲目数
printf("%d/%d", index, total);
}
//显示播放时间,比特率 信息
//totsec;音频文件总时间长度
//cursec:当前播放时间
//bitrate:比特率(位速)
void audio_msg_show(uint32_t totsec,uint32_t cursec,uint32_t bitrate)
{
static uint16_t playtime=0XFFFF;//播放时间标记
if(playtime!=cursec) //需要更新显示时间
{
playtime=cursec;
//显示播放时间
// LCD_ShowxNum(60,210,playtime/60,2,16,0X80); //分钟
// LCD_ShowChar(60+16,210,':',16,0);
// LCD_ShowxNum(60+24,210,playtime%60,2,16,0X80); //秒钟
// LCD_ShowChar(60+40,210,'/',16,0);
// //显示总时间
// LCD_ShowxNum(60+48,210,totsec/60,2,16,0X80); //分钟
// LCD_ShowChar(60+64,210,':',16,0);
// LCD_ShowxNum(60+72,210,totsec%60,2,16,0X80); //秒钟
// //显示位率
// LCD_ShowxNum(60+110,210,bitrate/1000,4,16,0X80);//显示位率
// LCD_ShowString(60+110+32,210,200,16,16,"Kbps");
}
}
//播放音乐
void audio_play(void)
{
uint8_t res;
DIR wavdir; //目录
FILINFO *wavfileinfo;//文件信息
uint8_t *pname; //带路径的文件名
uint16_t totwavnum; //音乐文件总数
uint16_t curindex; //当前索引
uint8_t key; //键值
uint32_t temp;
uint32_t *wavoffsettbl; //音乐offset索引表
WM8978_Init(); //初始化WM8978
WM8978_HPvol_Set(40,40); //耳机音量设置
WM8978_SPKvol_Set(50); //喇叭音量设置
WM8978_ADDA_Cfg(1,0); //开启DAC
WM8978_Input_Cfg(0,0,0);//关闭输入通道
WM8978_Output_Cfg(1,0); //开启DAC输出
while(f_opendir(&wavdir,"0:/MUSIC"))//打开音乐文件夹
{
printf("MUSIC file folder error");
delay_ms(200);
}
totwavnum=audio_get_tnum("0:/MUSIC"); //得到总有效文件数
while(totwavnum==NULL)//音乐文件总数为0
{
printf("can't find music file");
delay_ms(200);
}
wavfileinfo=(FILINFO*)mymalloc(SRAMIN,sizeof(FILINFO)); //申请内存
pname=mymalloc(SRAMIN,FF_MAX_LFN*2+1); //为带路径的文件名分配内存
wavoffsettbl=mymalloc(SRAMIN,4*totwavnum); //申请4*totwavnum个字节的内存,用于存放音乐文件off block索引
while(!wavfileinfo||!pname||!wavoffsettbl)//内存分配出错
{
printf("memory allocation failed");
delay_ms(200);
}
//记录索引
// res=f_opendir(&wavdir,"0:/MUSIC"); //打开目录
// if(res==FR_OK)
{
curindex=0;//当前索引为0
while(1)//全部查询一遍
{
temp=wavdir.dptr; //记录当前index
res=f_readdir(&wavdir,wavfileinfo); //读取目录下的一个文件
if(res!=FR_OK||wavfileinfo->fname[0]==0)break; //错误了/到末尾了,退出
res=f_typetell((uint8_t*)wavfileinfo->fname);
if((res&0XF0)==0X40)//取高四位,看看是不是音乐文件
{
wavoffsettbl[curindex]=temp;//记录索引
curindex++;
}
}
}
curindex=0; //从0开始显示
res=f_opendir(&wavdir,(const TCHAR*)"0:/MUSIC"); //打开目录
while(res==FR_OK)//打开成功
{
dir_sdi(&wavdir,wavoffsettbl[curindex]); //改变当前目录索引
res=f_readdir(&wavdir,wavfileinfo); //读取目录下的一个文件
if(res!=FR_OK||wavfileinfo->fname[0]==0)break; //错误了/到末尾了,退出
strcpy((char*)pname,"0:/MUSIC/"); //复制路径(目录)
strcat((char*)pname,(const char*)wavfileinfo->fname); //将文件名接在后面
// Show_Str(60,190,lcddev.width-60,16,(uint8_t*)wavfileinfo->fname,16,0);//显示歌曲名字
audio_index_show(curindex+1,totwavnum);
key=audio_play_song(pname); //播放这个音频文件
if(key==KEY2_PRES) //上一曲
{
if(curindex)curindex--;
else curindex=totwavnum-1;
}else if(key==KEY0_PRES)//下一曲
{
curindex++;
if(curindex>=totwavnum)curindex=0;//到末尾的时候,自动从头开始
}else break; //产生了错误
}
myfree(SRAMIN,wavfileinfo); //释放内存
myfree(SRAMIN,pname); //释放内存
myfree(SRAMIN,wavoffsettbl); //释放内存
}
//播放某个音频文件
uint8_t audio_play_song(uint8_t* fname)
{
uint8_t res;
res=f_typetell(fname);
switch(res)
{
case T_WAV:
res=wav_play_song(fname);
break;
case T_MP3:
res=mp3_play_song(fname); //播放MP3文件
break;
case T_FLAC:
res=flac_play_song(fname); //播放flac文件
break;
default://其他文件,自动跳转到下一曲
printf("can't play:%s\r\n",fname);
res=KEY0_PRES;
break;
}
return res;
}
#ifndef __BSP_AUDIOPLAY_H
#define __BSP_AUDIOPLAY_H
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
#include "ff.h"
//#include "wavplay.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//音乐播放器 应用代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/18
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//
//音乐播放控制器
typedef __packed struct
{
//2个SAI解码的BUF
uint8_t *saibuf1;
uint8_t *saibuf2;
uint8_t *tbuf; //零时数组
FIL *file; //音频文件指针
uint32_t(*file_seek)(uint32_t);//文件快进快退函数
volatile uint8_t status; //bit0:0,暂停播放;1,继续播放
//bit1:0,结束播放;1,开启播放
//bit2~3:保留
//bit4:0,无音乐播放;1,音乐播放中 (对外标记)
//bit5:0,无动作;1,执行了一次切歌操作(对外标记)
//bit6:0,无动作;1,请求终止播放(但是不删除音频播放任务),处理完成后,播放任务自动清零该位
//bit7:0,音频播放任务已删除/请求删除;1,音频播放任务正在运行(允许继续执行)
uint8_t mode; //播放模式
//0,全部循环;1,单曲循环;2,随机播放;
uint8_t *path; //当前文件夹路径
uint8_t *name; //当前播放的MP3歌曲名字
uint16_t namelen; //name所占的点数.
uint16_t curnamepos; //当前的偏移
uint32_t totsec ; //整首歌时长,单位:秒
uint32_t cursec ; //当前播放时长
uint32_t bitrate; //比特率(位速)
uint32_t samplerate; //采样率
uint16_t bps; //位数,比如16bit,24bit,32bit
uint16_t curindex; //当前播放的音频文件索引
uint16_t mfilenum; //音乐文件数目
uint32_t *mfindextbl; //音频文件索引表
}__audiodev;
extern __audiodev audiodev; //音乐播放控制器
void wav_sai_dma_callback(void);
void audio_start(void);
void audio_stop(void);
uint16_t audio_get_tnum(uint8_t *path);
void audio_index_show(uint16_t index,uint16_t total);
void audio_msg_show(uint32_t totsec,uint32_t cursec,uint32_t bitrate);
void audio_play(void);
uint8_t audio_play_song(uint8_t* fname);
//取2个值里面的较小值.
#ifndef AUDIO_MIN
#define AUDIO_MIN(x,y) ((x)<(y)? (x):(y))
#endif
#ifdef __cplusplus
}
#endif
#endif /* __BSP_AUDIOPLAY_H */
#include "bsp_sai.h"
#include "bsp_wavplay.h"
/**
* @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_NORMAL;
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_SAI_H
#define __BSP_SAI_H
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
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_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"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//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传输完成标志
volatile uint8_t wavwitchbuf=0; //saibufx指示标志
//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 t=0;
uint8_t res;
uint32_t fillnum;
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)
{
res=wav_decode_init(fname,&wavctrl);//得到文件的信息
if(res==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);
}
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);
}
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;//开始播放+非暂停
wavwitchbuf = 0;
wavtransferend=0;
fillnum=wav_buffill(audiodev.saibuf1,WAV_SAI_TX_DMA_BUFSIZE,wavctrl.bps);
while(res==0)
{
if(wavwitchbuf == 0)
{
if(wavctrl.bps==16)
{
HAL_SAI_Transmit_DMA(&hsai_BlockA1, audiodev.saibuf1, WAV_SAI_TX_DMA_BUFSIZE/2);
}
else//24bit
{
HAL_SAI_Transmit_DMA(&hsai_BlockA1, audiodev.saibuf1, WAV_SAI_TX_DMA_BUFSIZE/4);
}
fillnum=wav_buffill(audiodev.saibuf2,WAV_SAI_TX_DMA_BUFSIZE,wavctrl.bps);
wavwitchbuf = 1;
}
else
{
if(wavctrl.bps==16)
{
HAL_SAI_Transmit_DMA(&hsai_BlockA1, audiodev.saibuf2, WAV_SAI_TX_DMA_BUFSIZE/2);
}
else//24bit
{
HAL_SAI_Transmit_DMA(&hsai_BlockA1, audiodev.saibuf2, WAV_SAI_TX_DMA_BUFSIZE/4);
}
fillnum=wav_buffill(audiodev.saibuf1,WAV_SAI_TX_DMA_BUFSIZE,wavctrl.bps);
wavwitchbuf = 0;
}
while(wavtransferend==0);//等待wav传输完成;
wavtransferend=0;
if(fillnum!=WAV_SAI_TX_DMA_BUFSIZE)//播放结束?
{
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_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);
volatile extern uint8_t wavtransferend; //sai传输完成标志
extern __wavctrl wavctrl; //WAV控制结构体
#ifdef __cplusplus
}
#endif
#endif /* __BSP_WAVPLAY_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指示标志
//MP3 DMA发送回调函数
//void mp3_sai_dma_tx_callback(void)
//{
// uint16_t i;
// if(DMA2_Stream3->CR&(1<<19))
// {
// mp3witchbuf=0;
// if((audiodev.status&0X01)==0)//暂停了,填充0
// {
// for(i=0;i<2304*2;i++)audiodev.saibuf1[i]=0;
// }
// }else
// {
// mp3witchbuf=1;
// if((audiodev.status&0X01)==0)//暂停了,填充0
// {
// for(i=0;i<2304*2;i++)audiodev.saibuf2[i]=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;
if(mp3witchbuf==0)
{
p=(uint16_t*)audiodev.saibuf1;
}else
{
p=(uint16_t*)audiodev.saibuf2;
}
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];
}
}
while(mp3transferend==0);//等待传输完成
mp3transferend=0;
if(mp3witchbuf == 0)
{
HAL_SAI_Transmit_DMA(&hsai_BlockA1, audiodev.saibuf1, mp3ctrl->outsamples);
mp3witchbuf = 1;
}
else
{
HAL_SAI_Transmit_DMA(&hsai_BlockA1, audiodev.saibuf2, mp3ctrl->outsamples);
mp3witchbuf = 0;
}
}
//解析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指示标志
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);
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(audiodev.status&(1<<1))//正常播放中
// {
// delay_ms(1000/OS_TICKS_PER_SEC);
// 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)break;//没有按下暂停
// }
// if((audiodev.status&(1<<1))==0)//请求结束播放/播放完成
// {
// res=AP_NEXT;//跳出上上级循环
// outofdata=1;//跳出上一级循环
// 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)//下一曲/上一曲
{
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_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_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解码控制结构体
//分析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;
}
volatile uint8_t flactransferend=0; //sai传输完成标志
volatile uint8_t flacwitchbuf=0; //saibufx指示标志
FLAC DMA发送回调函数
//void flac_sai_dma_tx_callback(void)
//{
// uint16_t i;
// uint16_t size;
// if(DMA2_Stream3->CR&(1<<19))
// {
// flacwitchbuf=0;
// if((audiodev.status&0X01)==0)//暂停了,填充0
// {
// if(flacctrl->bps==24)size=flacctrl->outsamples*4;
// else size=flacctrl->outsamples*2;
// for(i=0;i
// }
// }else
// {
// flacwitchbuf=1;
// if((audiodev.status&0X01)==0)//暂停了,填充0
// {
// if(flacctrl->bps==24)size=flacctrl->outsamples*4;
// else size=flacctrl->outsamples*2;
// for(i=0;i
// }
// }
// flactransferend=1;
//}
//得到当前播放时间
//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)
{
FLACContext *fc=0;
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)
{
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位音频数据
{
// WM8978_I2S_Cfg(2,2); //飞利浦标准,24位数据长度
// SAIA_Init(0,1,6); //设置SAI,主发送,24位数据
// SAIA_SampleRate_Set(fc->samplerate); //设置采样率
// SAIA_TX_DMA_Init(audiodev.saibuf1,audiodev.saibuf2,flacctrl->outsamples,2);//配置TX DMA
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);
memset(audiodev.saibuf1,0,fc->max_blocksize*8);
memset(audiodev.saibuf2,0,fc->max_blocksize*8);
}else //16位音频数据
{
// WM8978_I2S_Cfg(2,0); //飞利浦标准,16位数据长度
// SAIA_Init(0,1,4); //设置SAI,主发送,16位数据
// SAIA_SampleRate_Set(fc->samplerate); //设置采样率
// SAIA_TX_DMA_Init(audiodev.saibuf1,audiodev.saibuf2,flacctrl->outsamples,1);//配置TX DMA
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);
memset(audiodev.saibuf1,0,fc->max_blocksize*4);
memset(audiodev.saibuf2,0,fc->max_blocksize*4);
}
//sai_tx_callback=flac_sai_dma_tx_callback; //回调函数指向flac_sai_dma_tx_callback
f_read(audiodev.file,buffer,fc->max_framesize,&br);//读取最大帧长数据
bytesleft=br;
audio_start(); //开始播放
flactransferend=0;
flacwitchbuf=0;
fc->decoded0=(int*)decbuf0; //解码数组0
fc->decoded1=(int*)decbuf1; //解码数组1
flac_fptr=audiodev.file->fptr; //记录当前的文件位置.
while(bytesleft)
{
if(flacwitchbuf == 0)
{
HAL_SAI_Transmit_DMA(&hsai_BlockA1, audiodev.saibuf1, flacctrl->outsamples);
flacwitchbuf = 1;
}
else
{
HAL_SAI_Transmit_DMA(&hsai_BlockA1, audiodev.saibuf2, flacctrl->outsamples);
flacwitchbuf = 0;
}
// if(flac_fptr!=audiodev.file->fptr)//说明外部有进行文件快进/快退操作
// {
// if(audiodev.file->fptrdatastart)//在数据开始之前??
// {
// f_lseek(audiodev.file,flacctrl->datastart);//偏移到数据开始的地方
// }
// f_read(audiodev.file,buffer,fc->max_framesize,&br); //读取一个最大帧的数据量
// bytesleft=flac_seek_frame(buffer,br,fc); //查找帧
// if(bytesleft>=0) //找到正确的帧头.
// {
// f_lseek(audiodev.file,audiodev.file->fptr-fc->max_framesize+bytesleft);
// f_read(audiodev.file,buffer,fc->max_framesize,&br);
// }else printf("flac seek error:%d\r\n",bytesleft);
// bytesleft=br;
// }
if(flacwitchbuf==0)p8=audiodev.saibuf1;
else p8=audiodev.saibuf2;
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(flactransferend==0);//等待传输完成
flactransferend = 0;
// 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;
}
#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 */
/*---------------------------------------------------------------------------/
/ FatFs Functional Configurations
/---------------------------------------------------------------------------*/
#define FFCONF_DEF 86631 /* Revision ID */
/*---------------------------------------------------------------------------/
/ Function Configurations
/---------------------------------------------------------------------------*/
#define FF_FS_READONLY 0
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
/ Read-only configuration removes writing API functions, f_write(), f_sync(),
/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
/ and optional writing functions as well. */
#define FF_FS_MINIMIZE 0
/* This option defines minimization level to remove some basic API functions.
/
/ 0: Basic functions are fully enabled.
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
/ are removed.
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
/ 3: f_lseek() function is removed in addition to 2. */
#define FF_USE_FIND 0
/* This option switches filtered directory read functions, f_findfirst() and
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
#define FF_USE_MKFS 1
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
#define FF_USE_FASTSEEK 1
/* This option switches fast seek function. (0:Disable or 1:Enable) */
#define FF_USE_EXPAND 0
/* This option switches f_expand function. (0:Disable or 1:Enable) */
#define FF_USE_CHMOD 0
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
#define FF_USE_LABEL 0
/* This option switches volume label functions, f_getlabel() and f_setlabel().
/ (0:Disable or 1:Enable) */
#define FF_USE_FORWARD 0
/* This option switches f_forward() function. (0:Disable or 1:Enable) */
#define FF_USE_STRFUNC 1
#define FF_PRINT_LLI 0
#define FF_PRINT_FLOAT 0
#define FF_STRF_ENCODE 0
/* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and
/ f_printf().
/
/ 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect.
/ 1: Enable without LF-CRLF conversion.
/ 2: Enable with LF-CRLF conversion.
/
/ FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2
makes f_printf() support floating point argument. These features want C99 or later.
/ When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character
/ encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE
/ to be read/written via those functions.
/
/ 0: ANSI/OEM in current CP
/ 1: Unicode in UTF-16LE
/ 2: Unicode in UTF-16BE
/ 3: Unicode in UTF-8
*/
/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/
#define FF_CODE_PAGE 936
/* This option specifies the OEM code page to be used on the target system.
/ Incorrect code page setting can cause a file open failure.
/
/ 437 - U.S.
/ 720 - Arabic
/ 737 - Greek
/ 771 - KBL
/ 775 - Baltic
/ 850 - Latin 1
/ 852 - Latin 2
/ 855 - Cyrillic
/ 857 - Turkish
/ 860 - Portuguese
/ 861 - Icelandic
/ 862 - Hebrew
/ 863 - Canadian French
/ 864 - Arabic
/ 865 - Nordic
/ 866 - Russian
/ 869 - Greek 2
/ 932 - Japanese (DBCS)
/ 936 - Simplified Chinese (DBCS)
/ 949 - Korean (DBCS)
/ 950 - Traditional Chinese (DBCS)
/ 0 - Include all code pages above and configured by f_setcp()
*/
#define FF_USE_LFN 3
#define FF_MAX_LFN 255
/* The FF_USE_LFN switches the support for LFN (long file name).
/
/ 0: Disable LFN. FF_MAX_LFN has no effect.
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
/ 2: Enable LFN with dynamic working buffer on the STACK.
/ 3: Enable LFN with dynamic working buffer on the HEAP.
/
/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
/ be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN
/ specification.
/ When use stack for the working buffer, take care on stack overflow. When use heap
/ memory for the working buffer, memory management functions, ff_memalloc() and
/ ff_memfree() exemplified in ffsystem.c, need to be added to the project. */
#define FF_LFN_UNICODE 0
/* This option switches the character encoding on the API when LFN is enabled.
/
/ 0: ANSI/OEM in current CP (TCHAR = char)
/ 1: Unicode in UTF-16 (TCHAR = WCHAR)
/ 2: Unicode in UTF-8 (TCHAR = char)
/ 3: Unicode in UTF-32 (TCHAR = DWORD)
/
/ Also behavior of string I/O functions will be affected by this option.
/ When LFN is not enabled, this option has no effect. */
#define FF_LFN_BUF 255
#define FF_SFN_BUF 12
/* This set of options defines size of file name members in the FILINFO structure
/ which is used to read out directory items. These values should be suffcient for
/ the file names to read. The maximum possible length of the read file name depends
/ on character encoding. When LFN is not enabled, these options have no effect. */
#define FF_FS_RPATH 0
/* This option configures support for relative path.
/
/ 0: Disable relative path and remove related functions.
/ 1: Enable relative path. f_chdir() and f_chdrive() are available.
/ 2: f_getcwd() function is available in addition to 1.
*/
/*---------------------------------------------------------------------------/
/ Drive/Volume Configurations
/---------------------------------------------------------------------------*/
#define FF_VOLUMES 1
/* Number of volumes (logical drives) to be used. (1-10) */
#define FF_STR_VOLUME_ID 0
#define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
/ logical drives. Number of items must not be less than FF_VOLUMES. Valid
/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
/ not defined, a user defined volume string table needs to be defined as:
/
/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
*/
#define FF_MULTI_PARTITION 0
/* This option switches support for multiple volumes on the physical drive.
/ By default (0), each logical drive number is bound to the same physical drive
/ number and only an FAT volume found on the physical drive will be mounted.
/ When this function is enabled (1), each logical drive number can be bound to
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
/ funciton will be available. */
#define FF_MIN_SS 512
#define FF_MAX_SS 512
/* This set of options configures the range of sector size to be supported. (512,
/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
/ harddisk, but a larger value may be required for on-board flash memory and some
/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
/ for variable sector size mode and disk_ioctl() function needs to implement
/ GET_SECTOR_SIZE command. */
#define FF_LBA64 0
/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
/ To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */
#define FF_MIN_GPT 0x10000000
/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and
/ f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */
#define FF_USE_TRIM 0
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
/ To enable Trim function, also CTRL_TRIM command should be implemented to the
/ disk_ioctl() function. */
/*---------------------------------------------------------------------------/
/ System Configurations
/---------------------------------------------------------------------------*/
#define FF_FS_TINY 0
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
/ At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
/ Instead of private sector buffer eliminated from the file object, common sector
/ buffer in the filesystem object (FATFS) is used for the file data transfer. */
#define FF_FS_EXFAT 0
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
#define FF_FS_NORTC 0
#define FF_NORTC_MON 1
#define FF_NORTC_MDAY 1
#define FF_NORTC_YEAR 2020
/* The option FF_FS_NORTC switches timestamp functiton. If the system does not have
/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
/ the timestamp function. Every object modified by FatFs will have a fixed timestamp
/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
/ added to the project to read current time form real-time clock. FF_NORTC_MON,
/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
/ These options have no effect in read-only configuration (FF_FS_READONLY = 1). */
#define FF_FS_NOFSINFO 0
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
/ option, and f_getfree() function at first time after volume mount will force
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
/
/ bit0=0: Use free cluster count in the FSINFO if available.
/ bit0=1: Do not trust free cluster count in the FSINFO.
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
*/
#define FF_FS_LOCK 0
/* The option FF_FS_LOCK switches file lock function to control duplicated file open
/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
/ is 1.
/
/ 0: Disable file lock function. To avoid volume corruption, application program
/ should avoid illegal open, remove and rename to the open objects.
/ >0: Enable file lock function. The value defines how many files/sub-directories
/ can be opened simultaneously under file lock control. Note that the file
/ lock control is independent of re-entrancy. */
/* #include // O/S definitions */
#define FF_FS_REENTRANT 0
#define FF_FS_TIMEOUT 1000
#define FF_SYNC_t HANDLE
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
/ module itself. Note that regardless of this option, file access to different
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
/ to the same volume is under control of this function.
/
/ 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect.
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
/ function, must be added to the project. Samples are available in
/ option/syscall.c.
/
/ The FF_FS_TIMEOUT defines timeout period in unit of time tick.
/ The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
/ SemaphoreHandle_t and etc. A header file for O/S definitions needs to be
/ included somewhere in the scope of ff.h. */
/*--- End of configuration options ---*/
/* ***** BEGIN LICENSE BLOCK *****
* Version: RCSL 1.0/RPSL 1.0
*
* Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved.
*
* The contents of this file, and the files included with this file, are
* subject to the current version of the RealNetworks Public Source License
* Version 1.0 (the "RPSL") available at
* http://www.helixcommunity.org/content/rpsl unless you have licensed
* the file under the RealNetworks Community Source License Version 1.0
* (the "RCSL") available at http://www.helixcommunity.org/content/rcsl,
* in which case the RCSL will apply. You may also obtain the license terms
* directly from RealNetworks. You may not use this file except in
* compliance with the RPSL or, if you have a valid RCSL with RealNetworks
* applicable to this file, the RCSL. Please see the applicable RPSL or
* RCSL for the rights, obligations and limitations governing use of the
* contents of the file.
*
* This file is part of the Helix DNA Technology. RealNetworks is the
* developer of the Original Code and owns the copyrights in the portions
* it created.
*
* This file, and the files included with this file, is distributed and made
* available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
*
* Technology Compatibility Kit Test Suite(s) Location:
* http://www.helixcommunity.org/content/tck
*
* Contributor(s):
*
* ***** END LICENSE BLOCK ***** */
/**************************************************************************************
* Fixed-point MP3 decoder
* Jon Recker ([email protected]), Ken Cooke ([email protected])
* June 2003
*
* buffers.c - allocation and freeing of internal MP3 decoder buffers
*
* All memory allocation for the codec is done in this file, so if you don't want
* to use other the default system malloc() and free() for heap management this is
* the only file you'll need to change.
**************************************************************************************/
// J.Sz. 21/04/2006 #include "hlxclib/stdlib.h" /* for malloc, free */
#include "coder.h"
//#define static_buffers
#ifdef static_buffers
MP3DecInfo mp3DecInfo; // 0x7f0 = 2032
SubbandInfo sbi; // 0x2204 = 8708
IMDCTInfo mi; // 0x1b20 = 6944
HuffmanInfo hi; // 0x1210 = 4624
DequantInfo di; // 0x348 = 840
ScaleFactorInfo sfi; // 0x124 = 292
SideInfo si; // 0x148 = 328
FrameHeader fh; // 0x38 = 56
#else
//#include
#include "bsp_malloc.h"
#endif
/**************************************************************************************
* Function: ClearBuffer
*
* Description: fill buffer with 0's
*
* Inputs: pointer to buffer
* number of bytes to fill with 0
*
* Outputs: cleared buffer
*
* Return: none
*
* Notes: slow, platform-independent equivalent to memset(buf, 0, nBytes)
**************************************************************************************/
void ClearBuffer(void *buf, int nBytes)
{
int i;
unsigned char *cbuf = (unsigned char *)buf;
for (i = 0; i < nBytes; i++)
cbuf[i] = 0;
}
/**************************************************************************************
* Function: AllocateBuffers
*
* Description: allocate all the memory needed for the MP3 decoder
*
* Inputs: none
*
* Outputs: none
*
* Return: pointer to MP3DecInfo structure (initialized with pointers to all
* the internal buffers needed for decoding, all other members of
* MP3DecInfo structure set to 0)
*
* Notes: if one or more mallocs fail, function frees any buffers already
* allocated before returning
*
* Changed by Kasper Jepsen to support static buffers as well.
*
**************************************************************************************/
MP3DecInfo *AllocateBuffers(void)
{
MP3DecInfo *mp3DecInfo_pointer;
#ifdef static_buffers
mp3DecInfo_pointer = (MP3DecInfo*)&mp3DecInfo;
ClearBuffer((void*)&mp3DecInfo, sizeof(MP3DecInfo));
mp3DecInfo.FrameHeaderPS = (void*)&fh;
mp3DecInfo.SideInfoPS = (void*)&si;
mp3DecInfo.ScaleFactorInfoPS = (void*)&sfi;
mp3DecInfo.HuffmanInfoPS = (void*)&hi;
mp3DecInfo.DequantInfoPS = (void*)&di;
mp3DecInfo.IMDCTInfoPS = (void*)&mi;
mp3DecInfo.SubbandInfoPS = (void*)&sbi;
/* important to do this - DSP primitives assume a bunch of state variables are 0 on first use */
ClearBuffer((void*)&fh, sizeof(FrameHeader));
ClearBuffer((void*)&si, sizeof(SideInfo));
ClearBuffer((void*)&sfi, sizeof(ScaleFactorInfo));
ClearBuffer((void*)&hi, sizeof(HuffmanInfo));
ClearBuffer((void*)&di, sizeof(DequantInfo));
ClearBuffer((void*)&mi, sizeof(IMDCTInfo));
ClearBuffer((void*)&sbi, sizeof(SubbandInfo));
// return mp3DecInfo_pointer;
#else
FrameHeader *fh;
SideInfo *si;
ScaleFactorInfo *sfi;
HuffmanInfo *hi;
DequantInfo *di;
IMDCTInfo *mi;
SubbandInfo *sbi;
mp3DecInfo_pointer = (MP3DecInfo *)mymalloc(SRAMIN,sizeof(MP3DecInfo));
if (!mp3DecInfo_pointer)
return 0;
ClearBuffer(mp3DecInfo_pointer, sizeof(MP3DecInfo));
fh = (FrameHeader *) mymalloc(SRAMIN,sizeof(FrameHeader));
si = (SideInfo *) mymalloc(SRAMIN,sizeof(SideInfo));
sfi = (ScaleFactorInfo *) mymalloc(SRAMIN,sizeof(ScaleFactorInfo));
hi = (HuffmanInfo *) mymalloc(SRAMIN,sizeof(HuffmanInfo));
di = (DequantInfo *) mymalloc(SRAMIN,sizeof(DequantInfo));
mi = (IMDCTInfo *) mymalloc(SRAMIN,sizeof(IMDCTInfo));
sbi = (SubbandInfo *) mymalloc(SRAMIN,sizeof(SubbandInfo));
mp3DecInfo_pointer->FrameHeaderPS = (void *)fh;
mp3DecInfo_pointer->SideInfoPS = (void *)si;
mp3DecInfo_pointer->ScaleFactorInfoPS = (void *)sfi;
mp3DecInfo_pointer->HuffmanInfoPS = (void *)hi;
mp3DecInfo_pointer->DequantInfoPS = (void *)di;
mp3DecInfo_pointer->IMDCTInfoPS = (void *)mi;
mp3DecInfo_pointer->SubbandInfoPS = (void *)sbi;
if (!fh || !si || !sfi || !hi || !di || !mi || !sbi) {
FreeBuffers(mp3DecInfo_pointer); /* safe to call - only frees memory that was successfully allocated */
return 0;
}
/* important to do this - DSP primitives assume a bunch of state variables are 0 on first use */
//Optimized away.. hmm
ClearBuffer(fh, sizeof(FrameHeader));
ClearBuffer(si, sizeof(SideInfo));
ClearBuffer(sfi, sizeof(ScaleFactorInfo));
ClearBuffer(hi, sizeof(HuffmanInfo));
ClearBuffer(di, sizeof(DequantInfo));
ClearBuffer(mi, sizeof(IMDCTInfo));
ClearBuffer(sbi, sizeof(SubbandInfo));
#endif
return mp3DecInfo_pointer;
}
#ifndef static_buffers
#define SAFE_FREE(x) {if (x) myfree(SRAMIN,x); (x) = 0;} /* helper macro */
#endif
/**************************************************************************************
* Function: FreeBuffers
*
* Description: frees all the memory used by the MP3 decoder
*
* Inputs: pointer to initialized MP3DecInfo structure
*
* Outputs: none
*
* Return: none
*
* Notes: safe to call even if some buffers were not allocated (uses SAFE_FREE)
**************************************************************************************/
void FreeBuffers(MP3DecInfo *mp3DecInfo)
{
#ifndef static_buffers
if (!mp3DecInfo)
return;
SAFE_FREE(mp3DecInfo->FrameHeaderPS);
SAFE_FREE(mp3DecInfo->SideInfoPS);
SAFE_FREE(mp3DecInfo->ScaleFactorInfoPS);
SAFE_FREE(mp3DecInfo->HuffmanInfoPS);
SAFE_FREE(mp3DecInfo->DequantInfoPS);
SAFE_FREE(mp3DecInfo->IMDCTInfoPS);
SAFE_FREE(mp3DecInfo->SubbandInfoPS);
SAFE_FREE(mp3DecInfo);
#endif
}
/*
* Common bit i/o utils
* Copyright (c) 2000, 2001 Fabrice Bellard.
* Copyright (c) 2002-2004 Michael Niedermayer
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* alternative bitstream reader & writer by Michael Niedermayer
*/
/**
* @file bitstream.c
* bitstream api.
*/
#include
#include
#include
#include
#include "bitstreamf.h"
/* bit input functions */
/**
* reads 0-32 bits.
*/
unsigned int get_bits_long(GetBitContext *s, int n){
if(n<=17) return get_bits(s, n);
else{
int ret= get_bits(s, 16) << (n-16);
return ret | get_bits(s, n-16);
}
}
/**
* shows 0-32 bits.
*/
unsigned int show_bits_long(GetBitContext *s, int n){
if(n<=17) return show_bits(s, n);
else{
GetBitContext gb= *s;
int ret= get_bits_long(s, n);
*s= gb;
return ret;
}
}
void align_get_bits(GetBitContext *s)
{
int n= (-get_bits_count(s)) & 7;
if(n) skip_bits(s, n);
}
/*
* FLAC (Free Lossless Audio Codec) decoder
* Copyright (c) 2003 Alex Beregszaszi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/**
* @file flac.c
* FLAC (Free Lossless Audio Codec) decoder
* @author Alex Beregszaszi
*
* For more information on the FLAC format, visit:
* http://flac.sourceforge.net/
*
* This decoder can be used in 1 of 2 ways: Either raw FLAC data can be fed
* through, starting from the initial 'fLaC' signature; or by passing the
* 34-byte streaminfo structure through avctx->extradata[_size] followed
* by data starting with the 0xFFF8 marker.
*/
#include
#include
//#include "arm.h"
#include "golomb.h"
#include "flacdecoder.h"
#define FFMAX(a,b) ((a) > (b) ? (a) : (b))
#define FFMIN(a,b) ((a) > (b) ? (b) : (a))
static const int sample_rate_table[] ICONST_ATTR =
{ 0, 88200, 176400, 192000,
8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000,
0, 0, 0, 0 };
static const int sample_size_table[] ICONST_ATTR =
{ 0, 8, 12, 0, 16, 20, 24, 0 };
static const int blocksize_table[] ICONST_ATTR = {
0, 192, 576<<0, 576<<1, 576<<2, 576<<3, 0, 0,
256<<0, 256<<1, 256<<2, 256<<3, 256<<4, 256<<5, 256<<6, 256<<7
};
static const uint8_t table_crc8[256] ICONST_ATTR = {
0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15,
0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d,
0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65,
0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d,
0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5,
0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd,
0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85,
0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd,
0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2,
0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea,
0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2,
0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a,
0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32,
0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a,
0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42,
0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a,
0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c,
0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4,
0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec,
0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4,
0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c,
0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44,
0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c,
0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34,
0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b,
0x76, 0x71, 0x78, 0x7f, 0x6a, 0x6d, 0x64, 0x63,
0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b,
0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13,
0xae, 0xa9, 0xa0, 0xa7, 0xb2, 0xb5, 0xbc, 0xbb,
0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83,
0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb,
0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3
};
static int64_t get_utf8(GetBitContext *gb) ICODE_ATTR_FLAC;
static int64_t get_utf8(GetBitContext *gb)
{
uint64_t val;
int ones=0, bytes;
while(get_bits1(gb))
ones++;
if (ones==0) bytes=0;
else if(ones==1) return -1;
else bytes= ones - 1;
val= get_bits(gb, 7-ones);
while(bytes--){
const int tmp = get_bits(gb, 8);
if((tmp>>6) != 2)
return -2;
val<<=6;
val|= tmp&0x3F;
}
return val;
}
static int get_crc8(const uint8_t *buf, int count) ICODE_ATTR_FLAC;
static int get_crc8(const uint8_t *buf, int count)
{
int crc=0;
int i;
for(i=0; i<count; i++){
crc = table_crc8[crc ^ buf[i]];
}
return crc;
}
static int decode_residuals(FLACContext *s, int32_t* decoded, int pred_order) ICODE_ATTR_FLAC;
static int decode_residuals(FLACContext *s, int32_t* decoded, int pred_order)
{
int i, tmp, partition, method_type, rice_order;
int sample = 0, samples;
method_type = get_bits(&s->gb, 2);
if (method_type > 1){
//fprintf(stderr,"illegal residual coding method %d\n", method_type);
return -3;
}
rice_order = get_bits(&s->gb, 4);
samples= s->blocksize >> rice_order;
sample=
i= pred_order;
for (partition = 0; partition < (1 << rice_order); partition++)
{
tmp = get_bits(&s->gb, method_type == 0 ? 4 : 5);
if (tmp == (method_type == 0 ? 15 : 31))
{
//fprintf(stderr,"fixed len partition\n");
tmp = get_bits(&s->gb, 5);
for (; i < samples; i++, sample++)
decoded[sample] = get_sbits(&s->gb, tmp);
}
else
{
for (; i < samples; i++, sample++){
decoded[sample] = get_sr_golomb_flac(&s->gb, tmp, INT_MAX, 0);
}
}
i= 0;
}
return 0;
}
static int decode_subframe_fixed(FLACContext *s, int32_t* decoded, int pred_order) ICODE_ATTR_FLAC;
static int decode_subframe_fixed(FLACContext *s, int32_t* decoded, int pred_order)
{
const int blocksize = s->blocksize;
int a, b, c, d, i;
/* warm up samples */
for (i = 0; i < pred_order; i++)
{
decoded[i] = get_sbits(&s->gb, s->curr_bps);
}
if (decode_residuals(s, decoded, pred_order) < 0)
return -4;
a = decoded[pred_order-1];
b = a - decoded[pred_order-2];
c = b - decoded[pred_order-2] + decoded[pred_order-3];
d = c - decoded[pred_order-2] + 2*decoded[pred_order-3] - decoded[pred_order-4];
switch(pred_order)
{
case 0:
break;
case 1:
for (i = pred_order; i < blocksize; i++)
decoded[i] = a += decoded[i];
break;
case 2:
for (i = pred_order; i < blocksize; i++)
decoded[i] = a += b += decoded[i];
break;
case 3:
for (i = pred_order; i < blocksize; i++)
decoded[i] = a += b += c += decoded[i];
break;
case 4:
for (i = pred_order; i < blocksize; i++)
decoded[i] = a += b += c += d += decoded[i];
break;
default:
return -5;
}
return 0;
}
/* level8 用到这个函数最多
* 之前版本经过验证是不行的
* 这个函数有arm汇编版,但移植不成功
* 找了个C语言版本的,效率不高
*/
int decode_subframe_lpc(FLACContext *s, int32_t* decoded, int pred_order)
{
int i, j;
int coeff_prec, qlevel;
int coeffs[32];
/* warm up samples */
for (i = 0; i < pred_order; i++) {
decoded[i] = get_sbits(&s->gb, s->curr_bps);
}
coeff_prec = get_bits(&s->gb, 4) + 1;
if (coeff_prec == 16) {
// av_log(s->avctx, AV_LOG_ERROR, "invalid coeff precision\n");
return -1;
}
qlevel = get_sbits(&s->gb, 5);
if (qlevel < 0) {
// av_log(s->avctx, AV_LOG_ERROR, "qlevel %d not supported, maybe buggy stream\n",
// qlevel);
return -1;
}
for (i = 0; i < pred_order; i++) {
coeffs[i] = get_sbits(&s->gb, coeff_prec);
}
if (decode_residuals(s, decoded, pred_order) < 0)
return -1;
if (s->bps > 16) {
int64_t sum;
for (i = pred_order; i < s->blocksize; i++) {
sum = 0;
for (j = 0; j < pred_order; j++)
sum += (int64_t)coeffs[j] * decoded[i-j-1];
decoded[i] += sum >> qlevel;
}
} else {
for (i = pred_order; i < s->blocksize-1; i += 2) {
int c;
int d = decoded[i-pred_order];
int s0 = 0, s1 = 0;
for (j = pred_order-1; j > 0; j--) {
c = coeffs[j];
s0 += c*d;
d = decoded[i-j];
s1 += c*d;
}
c = coeffs[0];
s0 += c*d;
d = decoded[i] += s0 >> qlevel;
s1 += c*d;
decoded[i+1] += s1 >> qlevel;
}
if (i < s->blocksize) {
int sum = 0;
for (j = 0; j < pred_order; j++)
sum += coeffs[j] * decoded[i-j-1];
decoded[i] += sum >> qlevel;
}
}
return 0;
}
static __inline int decode_subframe(FLACContext *s, int channel, int32_t* decoded)
{
int type, wasted = 0;
int i, tmp;
s->curr_bps = s->bps;
if(channel == 0){
if(s->decorrelation == RIGHT_SIDE)
s->curr_bps++;
}else{
if(s->decorrelation == LEFT_SIDE || s->decorrelation == MID_SIDE)
s->curr_bps++;
}
if (get_bits1(&s->gb))
{
//fprintf(stderr,"invalid subframe padding\n");
return -9;
}
type = get_bits(&s->gb, 6);
// wasted = get_bits1(&s->gb);
// if (wasted)
// {
// while (!get_bits1(&s->gb))
// wasted++;
// if (wasted)
// wasted++;
// s->curr_bps -= wasted;
// }
#if 0
wasted= 16 - av_log2(show_bits(&s->gb, 17));
skip_bits(&s->gb, wasted+1);
s->curr_bps -= wasted;
#else
if (get_bits1(&s->gb))
{
wasted = 1;
while (!get_bits1(&s->gb))
wasted++;
s->curr_bps -= wasted;
//fprintf(stderr,"%d wasted bits\n", wasted);
}
#endif
//FIXME use av_log2 for types
if (type == 0)
{
//fprintf(stderr,"coding type: constant\n");
tmp = get_sbits(&s->gb, s->curr_bps);
for (i = 0; i < s->blocksize; i++)
decoded[i] = tmp;
}
else if (type == 1)
{
//fprintf(stderr,"coding type: verbatim\n");
for (i = 0; i < s->blocksize; i++)
decoded[i] = get_sbits(&s->gb, s->curr_bps);
}
else if ((type >= 8) && (type <= 12))
{
//fprintf(stderr,"coding type: fixed\n");
if (decode_subframe_fixed(s, decoded, type & ~0x8) < 0)
return -10;
}
else if (type >= 32)
{
//fprintf(stderr,"coding type: lpc\n");
if (decode_subframe_lpc(s, decoded, (type & ~0x20)+1) < 0)
return -11;
}
else
{
//fprintf(stderr,"Unknown coding type: %d\n",type);
return -12;
}
if (wasted)
{
int i;
for (i = 0; i < s->blocksize; i++)
decoded[i] <<= wasted;
}
return 0;
}
static int decode_frame(FLACContext *s) ICODE_ATTR_FLAC;
static int decode_frame(FLACContext *s){
int blocksize_code, sample_rate_code, sample_size_code, assignment, crc8;
int decorrelation, bps, blocksize, samplerate;
int res;
blocksize_code = get_bits(&s->gb, 4);
sample_rate_code = get_bits(&s->gb, 4);
assignment = get_bits(&s->gb, 4); /* channel assignment */
if (assignment < 8 && s->channels == assignment+1)
decorrelation = INDEPENDENT;
else if (assignment >=8 && assignment < 11 && s->channels == 2)
decorrelation = LEFT_SIDE + assignment - 8;
else
{
return -13;
}
sample_size_code = get_bits(&s->gb, 3);
if(sample_size_code == 0)
bps= s->bps;
else if((sample_size_code != 3) && (sample_size_code != 7))
bps = sample_size_table[sample_size_code];
else
{
return -14;
}
if (get_bits1(&s->gb))
{
return -15;
}
/* Get the samplenumber of the first sample in this block */
s->samplenumber=get_utf8(&s->gb);
/* samplenumber actually contains the frame number for streams
with a constant block size - so we multiply by blocksize to
get the actual sample number */
if (s->min_blocksize == s->max_blocksize) {
s->samplenumber*=s->min_blocksize;
}
#if 0
if (/*((blocksize_code == 6) || (blocksize_code == 7)) &&*/
(s->min_blocksize != s->max_blocksize)){
}else{
}
#endif
if (blocksize_code == 0)
blocksize = s->min_blocksize;
else if (blocksize_code == 6)
blocksize = get_bits(&s->gb, 8)+1;
else if (blocksize_code == 7)
blocksize = get_bits(&s->gb, 16)+1;
else
blocksize = blocksize_table[blocksize_code];
if(blocksize > s->max_blocksize){
return -16;
}
if (sample_rate_code == 0){
samplerate= s->samplerate;
}else if ((sample_rate_code > 0) && (sample_rate_code < 12))
samplerate = sample_rate_table[sample_rate_code];
else if (sample_rate_code == 12)
samplerate = get_bits(&s->gb, 8) * 1000;
else if (sample_rate_code == 13)
samplerate = get_bits(&s->gb, 16);
else if (sample_rate_code == 14)
samplerate = get_bits(&s->gb, 16) * 10;
else{
return -17;
}
skip_bits(&s->gb, 8);
crc8= get_crc8(s->gb.buffer, get_bits_count(&s->gb)/8);
if(crc8){
return -18;
}
s->blocksize = blocksize;
s->samplerate = samplerate;
s->bps = bps;
s->decorrelation= (enum decorrelation_type)decorrelation;
/* subframes */
if ((res=decode_subframe(s, 0, s->decoded0)) < 0){
return res-100;
}
if (s->channels==2) {
if ((res=decode_subframe(s, 1, s->decoded1)) < 0){
return res-200;
}
}
align_get_bits(&s->gb);
/* frame footer */
skip_bits(&s->gb, 16); /* data crc */
return 0;
}
//查找下一帧起始地址
//buf:输入数组
//size:数组大小
//fc:flac解码容器
//返回值:-1,没有找到帧标志
// 其他,帧起始偏移量
int flac_seek_frame(uint8_t *buf,uint32_t size,FLACContext * fc)
{
uint8_t *p;
uint32_t i;
uint32_t samplerate;
uint32_t bps;
uint8_t seekok=0;
p=buf;
for(i=0;i<size-3;i++,p++)
{
if(p[0]==0XFF&&((p[1]&0XFC)==0XF8))//找到帧同步字(0XFFF8)
{
samplerate=p[2]&0X0F; //采样率
if(samplerate==0)samplerate=fc->samplerate;
else if(samplerate>11)continue;
else samplerate=sample_rate_table[samplerate];
bps=sample_size_table[(p[3]&0X0F)>>1];//采样深度 16/24位
if(samplerate==fc->samplerate&&bps==fc->bps)
{
seekok=1;
break;
}
}
}
if(seekok)return i; //帧在数组里面的起始地址偏移量
else return -1; //没有找到
}
//解码一帧24位FLAC文件
//fc:flac解码容器
//buf:输入数组(读取到的数据)
//buf_size:输入数组大小
//wavbuf:输出音频PCM数组(32bit)
//返回值:0,解码成功
// 其他,错误
int flac_decode_frame24(FLACContext *fc, uint8_t *buf, int buf_size, s32 *wavbuf)
{
int sampleCnt, *ch0, *ch1;
init_get_bits(&fc->gb, buf, buf_size*8);
skip_bits(&fc->gb, 16);
if((sampleCnt=decode_frame(fc))<0)
{
fc->bitstream_size=0;
fc->bitstream_index=0;
return sampleCnt;
}
fc->framesize = (get_bits_count(&fc->gb)+7)>>3;
sampleCnt = fc->blocksize;
ch0=fc->decoded0;
ch1=fc->decoded1;
switch(fc->decorrelation)
{
case INDEPENDENT :
if (fc->channels==1)
{
do
{
*(wavbuf ++) = *ch0;
*(wavbuf ++) = *(ch0 ++);
}while(-- sampleCnt);
}else
{
do
{
*(wavbuf ++) = *(ch0 ++);
*(wavbuf ++) = *(ch1 ++);
}while(-- sampleCnt);
}
break;
case LEFT_SIDE:
do
{
*(wavbuf ++) = *ch0;
*(wavbuf ++) = *(ch0 ++) - *(ch1 ++);
}while(-- sampleCnt);
break;
case RIGHT_SIDE:
do
{
*(wavbuf ++) = *(ch0 ++) + *ch1;
*(wavbuf ++) = *(ch1 ++);
}while(-- sampleCnt);
break;
case MID_SIDE:
do
{
int mid, side;
mid = *(ch0 ++);
side = *(ch1 ++);
mid -= side>>1;
*(wavbuf ++) = (mid + side);
*(wavbuf ++) = mid;
}while(-- sampleCnt);
break;
default :
do
{
*(wavbuf ++) = 0;
*(wavbuf ++) = 0;
}while(-- sampleCnt);
}
return 0;
}
//解码一帧16位FLAC文件
//fc:flac解码容器
//buf:输入数组(读取到的数据)
//buf_size:输入数组大小
//wavbuf:输出音频PCM数组(16bit)
//返回值:0,解码成功
// 其他,错误
int flac_decode_frame16(FLACContext *fc, uint8_t *buf, int buf_size, s16 *wavbuf)
{
int sampleCnt, *ch0, *ch1;
init_get_bits(&fc->gb, buf, buf_size*8);
skip_bits(&fc->gb, 16);
if((sampleCnt=decode_frame(fc))<0)
{
fc->bitstream_size=0;
fc->bitstream_index=0;
return sampleCnt;
}
fc->framesize = (get_bits_count(&fc->gb)+7)>>3;
sampleCnt = fc->blocksize;
ch0=fc->decoded0;
ch1=fc->decoded1;
switch(fc->decorrelation)
{
case INDEPENDENT :
if (fc->channels==1)
{
do
{
*(wavbuf ++) = *ch0;
*(wavbuf ++) = *(ch0 ++);
}while(-- sampleCnt);
}else
{
do
{
*(wavbuf ++) = *(ch0 ++);
*(wavbuf ++) = *(ch1 ++);
}while(-- sampleCnt);
}
break;
case LEFT_SIDE:
do
{
*(wavbuf ++) = *ch0;
*(wavbuf ++) = *(ch0 ++) - *(ch1 ++);
}while(-- sampleCnt);
break;
case RIGHT_SIDE:
do
{
*(wavbuf ++) = *(ch0 ++) + *ch1;
*(wavbuf ++) = *(ch1 ++);
}while(-- sampleCnt);
break;
case MID_SIDE:
do
{
int mid, side;
mid = *(ch0 ++);
side = *(ch1 ++);
mid -= side>>1;
*(wavbuf ++) = (mid + side);
*(wavbuf ++) = mid;
}while(-- sampleCnt);
break;
default :
do
{
*(wavbuf ++) = 0;
*(wavbuf ++) = 0;
}while(-- sampleCnt);
}
return 0;
}
#include
/* From ffmpeg - libavutil/common.h */
const uint8_t ff_log2_tab[256] = {
0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
};
5、屏蔽stm32f7xx_hal_msp.c中的void HAL_SAI_MspInit(SAI_HandleTypeDef* hsai)、void HAL_SAI_MspDeInit(SAI_HandleTypeDef* hsai),在bsp_sai.c中添加void SAIA_TX_DMA_Init(uint8_t width)、void HAL_SAI_MspInit(SAI_HandleTypeDef* hsai)、void HAL_SAI_MspDeInit(SAI_HandleTypeDef* hsai)
6、在bsp_sai.c中添加void BSP_SAI1_Init(uint32_t datasize, uint32_t AudioFrequency)、uint8_t SAIA_SampleRate_Set(uint32_t samplerate)
7、将ff.c中的函数FRESULT dir_sdi (DIR* dp, DWORD ofs);去掉static,并在ff.h中声明,bsp_audioplay.c中需要用到
8、SAI初始化时需要用到uint8_t SAIA_SampleRate_Set(uint32_t samplerate),HAL库中对采样率的设置不准确,实测播放歌曲会变快一点
9、DMA的设置,如果数据为16位,就设置为HALFWORD,如果数据为32位,就设置为WORD,PeriphDataAlignment必须要这样设置,MemDataAlignment可以不用,但是最好保持一致
10、SAI不用使能中断,运行正常
11、DMA虽然是单缓冲,但是也采用了两个BUFFER,一个BUFFER在发送DMA时,另外一个BUFFER填充数据,这样播放流畅,如果DMA一次发送的数据量太小,速度跟不上会听到卡顿的声音
12、在void SAIA_TX_DMA_Init(uint8_t width)函数中HAL_DMA_Init(&hdma_sai1_a)前添加HAL_DMA_DeInit(&hdma_sai1_a);会导致无法播放音乐
13、SAI初始化时SAIA_TX_DMA_Init(); 应放在BSP_SAI1_Init();SAIA_SampleRate_Set();之后,如果放在前面会导致无法播放音乐
14、cubemx生成代码后只需要屏蔽stm32f7xx_hal_msp.c中的void HAL_SAI_MspInit(SAI_HandleTypeDef* hsai)、void HAL_SAI_MspDeInit(SAI_HandleTypeDef* hsai)即可