STM32F767 录音机 SAI DMA双缓冲

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* 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_recorder.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;
SAI_HandleTypeDef hsai_BlockB1;
DMA_HandleTypeDef hdma_sai1_a;
DMA_HandleTypeDef hdma_sai1_b;

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();
		wav_recorder();
		
    /* 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();
  }
  hsai_BlockB1.Instance = SAI1_Block_B;
  hsai_BlockB1.Init.AudioMode = SAI_MODESLAVE_RX;
  hsai_BlockB1.Init.Synchro = SAI_SYNCHRONOUS;
  hsai_BlockB1.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE;
  hsai_BlockB1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_1QF;
  hsai_BlockB1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;
  hsai_BlockB1.Init.MonoStereoMode = SAI_STEREOMODE;
  hsai_BlockB1.Init.CompandingMode = SAI_NOCOMPANDING;
  hsai_BlockB1.Init.TriState = SAI_OUTPUT_NOTRELEASED;
  if (HAL_SAI_InitProtocol(&hsai_BlockB1, 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_Stream0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
  /* DMA2_Stream1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream1_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn);
  /* DMA2_Stream3_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
  /* DMA2_Stream6_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn);

}

/* 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
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* 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 SAI_HandleTypeDef hsai_BlockB1;
extern DMA_HandleTypeDef hdma_sai1_a;
extern DMA_HandleTypeDef hdma_sai1_b;

/* 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_hal_msp.c
  * @brief        This file provides code for the MSP Initialization
  *               and de-Initialization codes.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */
extern DMA_HandleTypeDef hdma_sdmmc1_rx;

extern DMA_HandleTypeDef hdma_sdmmc1_tx;

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */

/* USER CODE END TD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN Define */

/* USER CODE END Define */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN Macro */

/* USER CODE END Macro */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* External functions --------------------------------------------------------*/
/* USER CODE BEGIN ExternalFunctions */

/* USER CODE END ExternalFunctions */

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */
/**
  * Initializes the Global MSP.
  */
void HAL_MspInit(void)
{
  /* USER CODE BEGIN MspInit 0 */

  /* USER CODE END MspInit 0 */

  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_RCC_SYSCFG_CLK_ENABLE();

  /* System interrupt init*/

  /* USER CODE BEGIN MspInit 1 */

  /* USER CODE END MspInit 1 */
}

/**
* @brief SD MSP Initialization
* This function configures the hardware resources used in this example
* @param hsd: SD handle pointer
* @retval None
*/
void HAL_SD_MspInit(SD_HandleTypeDef* hsd)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hsd->Instance==SDMMC1)
  {
  /* USER CODE BEGIN SDMMC1_MspInit 0 */

  /* USER CODE END SDMMC1_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_SDMMC1_CLK_ENABLE();

    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();
    /**SDMMC1 GPIO 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;

//extern DMA_HandleTypeDef hdma_sai1_b;

//static uint32_t SAI1_client =0;

//void HAL_SAI_MspInit(SAI_HandleTypeDef* hsai)
//{

//  GPIO_InitTypeDef GPIO_InitStruct;
///* SAI1 */
//    if(hsai->Instance==SAI1_Block_A)
//    {
//    /* Peripheral clock enable */
//    if (SAI1_client == 0)
//    {
//       __HAL_RCC_SAI1_CLK_ENABLE();
//    }
//    SAI1_client ++;

//    /**SAI1_A_Block_A GPIO Configuration
//    PE2     ------> SAI1_MCLK_A
//    PE4     ------> SAI1_FS_A
//    PE5     ------> SAI1_SCK_A
//    PE6     ------> SAI1_SD_A
//    */
//    GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6;
//    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
//    GPIO_InitStruct.Pull = GPIO_NOPULL;
//    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
//    GPIO_InitStruct.Alternate = GPIO_AF6_SAI1;
//    HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

//      /* Peripheral DMA init*/

//    hdma_sai1_a.Instance = DMA2_Stream1;
//    hdma_sai1_a.Init.Channel = DMA_CHANNEL_0;
//    hdma_sai1_a.Init.Direction = DMA_MEMORY_TO_PERIPH;
//    hdma_sai1_a.Init.PeriphInc = DMA_PINC_DISABLE;
//    hdma_sai1_a.Init.MemInc = DMA_MINC_ENABLE;
//    hdma_sai1_a.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
//    hdma_sai1_a.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
//    hdma_sai1_a.Init.Mode = DMA_CIRCULAR;
//    hdma_sai1_a.Init.Priority = DMA_PRIORITY_LOW;
//    hdma_sai1_a.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
//    hdma_sai1_a.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;
//    hdma_sai1_a.Init.MemBurst = DMA_MBURST_SINGLE;
//    hdma_sai1_a.Init.PeriphBurst = DMA_PBURST_SINGLE;
//    if (HAL_DMA_Init(&hdma_sai1_a) != HAL_OK)
//    {
//      Error_Handler();
//    }

//    /* Several peripheral DMA handle pointers point to the same DMA handle.
//     Be aware that there is only one stream to perform all the requested DMAs. */
//    __HAL_LINKDMA(hsai,hdmarx,hdma_sai1_a);

//    __HAL_LINKDMA(hsai,hdmatx,hdma_sai1_a);

//    }
//    if(hsai->Instance==SAI1_Block_B)
//    {
//      /* Peripheral clock enable */
//      if (SAI1_client == 0)
//      {
//       __HAL_RCC_SAI1_CLK_ENABLE();
//      }
//    SAI1_client ++;

//    /**SAI1_B_Block_B GPIO Configuration
//    PE3     ------> SAI1_SD_B
//    */
//    GPIO_InitStruct.Pin = GPIO_PIN_3;
//    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_b.Instance = DMA2_Stream0;
//    hdma_sai1_b.Init.Channel = DMA_CHANNEL_10;
//    hdma_sai1_b.Init.Direction = DMA_PERIPH_TO_MEMORY;
//    hdma_sai1_b.Init.PeriphInc = DMA_PINC_DISABLE;
//    hdma_sai1_b.Init.MemInc = DMA_MINC_ENABLE;
//    hdma_sai1_b.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
//    hdma_sai1_b.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
//    hdma_sai1_b.Init.Mode = DMA_CIRCULAR;
//    hdma_sai1_b.Init.Priority = DMA_PRIORITY_LOW;
//    hdma_sai1_b.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
//    hdma_sai1_b.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;
//    hdma_sai1_b.Init.MemBurst = DMA_MBURST_SINGLE;
//    hdma_sai1_b.Init.PeriphBurst = DMA_PBURST_SINGLE;
//    if (HAL_DMA_Init(&hdma_sai1_b) != 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_b);
//    __HAL_LINKDMA(hsai,hdmatx,hdma_sai1_b);
//    }
//}

//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);
//    }
//    if(hsai->Instance==SAI1_Block_B)
//    {
//    SAI1_client --;
//      if (SAI1_client == 0)
//      {
//      /* Peripheral clock disable */
//      __HAL_RCC_SAI1_CLK_DISABLE();
//      }

//    /**SAI1_B_Block_B GPIO Configuration
//    PE3     ------> SAI1_SD_B
//    */
//    HAL_GPIO_DeInit(GPIOE, GPIO_PIN_3);

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

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    stm32f7xx_it.c
  * @brief   Interrupt Service Routines.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f7xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

#include "fifo.h"
#include "bsp_wavplay.h" 
#include "bsp_mp3play.h"
#include "bsp_flacplay.h"
#include "bsp_sai.h"

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */

/* USER CODE END TD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/* External variables --------------------------------------------------------*/
extern DMA_HandleTypeDef hdma_sai1_a;
extern DMA_HandleTypeDef hdma_sai1_b;
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 stream0 global interrupt.
  */
void DMA2_Stream0_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Stream0_IRQn 0 */

  /* USER CODE END DMA2_Stream0_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_sai1_b);
  /* USER CODE BEGIN DMA2_Stream0_IRQn 1 */

  /* USER CODE END DMA2_Stream0_IRQn 1 */
}

/**
  * @brief This function handles DMA2 stream1 global interrupt.
  */
void DMA2_Stream1_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Stream1_IRQn 0 */

  /* USER CODE END DMA2_Stream1_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_sai1_a);
  /* USER CODE BEGIN DMA2_Stream1_IRQn 1 */

  /* USER CODE END DMA2_Stream1_IRQn 1 */
}

/**
  * @brief This function handles DMA2 stream3 global interrupt.
  */
void DMA2_Stream3_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Stream3_IRQn 0 */

  /* USER CODE END DMA2_Stream3_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_sdmmc1_rx);
  /* USER CODE BEGIN DMA2_Stream3_IRQn 1 */

  /* USER CODE END DMA2_Stream3_IRQn 1 */
}

/**
  * @brief This function handles DMA2 stream6 global interrupt.
  */
void DMA2_Stream6_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Stream6_IRQn 0 */

  /* USER CODE END DMA2_Stream6_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_sdmmc1_tx);
  /* USER CODE BEGIN DMA2_Stream6_IRQn 1 */

  /* USER CODE END DMA2_Stream6_IRQn 1 */
}

/* USER CODE BEGIN 1 */

/**
  * @brief Rx Transfer completed callbacks
  * @param hsd: Pointer SD handle
  * @retval None
  */
void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
{
	HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_1);
	rx_done = 1;
}

/**
  * @brief Tx Transfer completed callbacks
  * @param hsd: Pointer to SD handle
  * @retval None
  */
void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
{
	HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_1);
	tx_done = 1;
}

/**
  * @brief SD error callbacks
  * @param hsd: Pointer SD handle
  * @retval None
  */
void HAL_SD_ErrorCallback(SD_HandleTypeDef *hsd)
{
	HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
}


/* USER CODE END 1 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

#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);
void BSP_SAIA1_Init(uint32_t datasize, uint32_t AudioFrequency);
void BSP_SAIB1_Init(uint32_t datasize, uint32_t AudioFrequency);
uint8_t SAIA_SampleRate_Set(uint32_t samplerate);
void SAIA_TX_DMA_Init(uint8_t width);	
void SAIA1_TX_DMA_Init(uint8_t width);
void SAIB1_RX_DMA_Init(uint8_t width);

#ifdef __cplusplus
}
#endif

#endif /* __BSP_SAI_H */


#include "bsp_sai.h"
#include "bsp_wavplay.h"
#include "delay.h"

void (*sai_rx_callback)(void);	//RX回调函数

/**
  * @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();
  }
}

/**
  * @brief SAI1 Initialization Function
  * @param None
  * @retval None
  */
void BSP_SAIA1_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();
  }
}

/**
  * @brief SAI1 Initialization Function
  * @param None
  * @retval None
  */
void BSP_SAIB1_Init(uint32_t datasize, uint32_t AudioFrequency)
{
	HAL_SAI_DeInit(&hsai_BlockB1);
	
	hsai_BlockB1.Instance = SAI1_Block_B;
  hsai_BlockB1.Init.AudioMode = SAI_MODESLAVE_RX;
  hsai_BlockB1.Init.Synchro = SAI_SYNCHRONOUS;
  hsai_BlockB1.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE;
  hsai_BlockB1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_1QF;
  hsai_BlockB1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;
  hsai_BlockB1.Init.MonoStereoMode = SAI_STEREOMODE;
  hsai_BlockB1.Init.CompandingMode = SAI_NOCOMPANDING;
  hsai_BlockB1.Init.TriState = SAI_OUTPUT_NOTRELEASED;
  if (HAL_SAI_InitProtocol(&hsai_BlockB1, SAI_I2S_STANDARD, datasize, 2) != HAL_OK)
  {
    Error_Handler();
  }
	hsai_BlockB1.Instance->CR1 |= SAI_xCR1_DMAEN;
	__HAL_SAI_ENABLE(&hsai_BlockB1);
}

//SAI Block A采样率设置
//采样率计算公式:
//MCKDIV!=0: Fs=SAI_CK_x/[512*MCKDIV]
//MCKDIV==0: Fs=SAI_CK_x/256
//SAI_CK_x=(HSE/pllm)*PLLI2SN/PLLI2SQ/(PLLI2SDIVQ+1)
//一般HSE=25Mhz
//pllm:在Stm32_Clock_Init设置的时候确定,一般是25
//PLLI2SN:一般是192~432 
//PLLI2SQ:2~15 
//PLLI2SDIVQ:0~31
//MCKDIV:0~15 
//SAI A分频系数表@pllm=8,HSE=25Mhz,即vco输入频率为1Mhz 
const uint16_t SAI_PSC_TBL[][5]=
{
	{800 ,344,7,0,12},	//8Khz采样率
	{1102,429,2,18,2},	//11.025Khz采样率 
	{1600,344,7, 0,6},	//16Khz采样率
	{2205,429,2,18,1},	//22.05Khz采样率
	{3200,344,7, 0,3},	//32Khz采样率
	{4410,429,2,18,0},	//44.1Khz采样率
	{4800,344,7, 0,2},	//48Khz采样率
	{8820,271,2, 2,1},	//88.2Khz采样率
	{9600,344,7, 0,1},	//96Khz采样率
	{17640,271,6,0,0},	//176.4Khz采样率 
	{19200,295,6,0,0},	//192Khz采样率
};  
//设置SAIA的采样率(@MCKEN)
//samplerate:采样率,单位:Hz
//返回值:0,设置成功;1,无法设置.
uint8_t SAIA_SampleRate_Set(uint32_t samplerate)
{ 
	uint8_t i=0; 
	uint32_t tempreg=0;
	samplerate/=10;//缩小10倍   
	for(i=0;i<(sizeof(SAI_PSC_TBL)/10);i++)//看看改采样率是否可以支持
	{
		if(samplerate==SAI_PSC_TBL[i][0])break;
	}
	RCC->CR&=~(1<<26);						//先关闭PLLI2S  
	if(i==(sizeof(SAI_PSC_TBL)/10))return 1;//搜遍了也找不到
	tempreg|=(uint32_t)SAI_PSC_TBL[i][1]<<6;		//设置PLLI2SN
	tempreg|=(uint32_t)SAI_PSC_TBL[i][2]<<24;	//设置PLLI2SQ 
	RCC->PLLI2SCFGR=tempreg;				//设置I2SxCLK的频率 
	tempreg=RCC->DCKCFGR1;			
	tempreg&=~(0X1F);						//清空PLLI2SDIVQ设置.
	tempreg&=~(0X03<<20);					//清空SAI1ASRC设置.
	tempreg|=SAI_PSC_TBL[i][3]<<0;			//设置PLLI2SDIVQ 
	tempreg|=1<<20;							//设置SAI1A时钟来源为PLLI2SQ
	RCC->DCKCFGR1=tempreg;					//设置DCKCFGR寄存器 
	RCC->CR|=1<<26;							//开启I2S时钟
	while((RCC->CR&1<<27)==0);				//等待I2S时钟开启成功. 
	tempreg=SAI1_Block_A->CR1;			
	tempreg&=~(0X0F<<20);					//清除MCKDIV设置
	tempreg|=(uint32_t)SAI_PSC_TBL[i][4]<<20;	//设置MCKDIV
	tempreg|=1<<16;							//使能SAI1 Block A
	tempreg|=1<<17;							//使能DMA
	SAI1_Block_A->CR1=tempreg;				//配置MCKDIV,同时使能SAI1 Block A 
	return 0;
}  

void SAIA_TX_DMA_Init(uint8_t width) 
{  
	/* Peripheral DMA init*/
	
	//HAL_DMA_DeInit(&hdma_sai1_a);
		
	if(width==16)
	{
		hdma_sai1_a.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
		hdma_sai1_a.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
	}
	else if(width==24)
	{
		hdma_sai1_a.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
		hdma_sai1_a.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
	}

	hdma_sai1_a.Instance = DMA2_Stream1;
	hdma_sai1_a.Init.Channel = DMA_CHANNEL_0;
	hdma_sai1_a.Init.Direction = DMA_MEMORY_TO_PERIPH;
	hdma_sai1_a.Init.PeriphInc = DMA_PINC_DISABLE;
	hdma_sai1_a.Init.MemInc = DMA_MINC_ENABLE;
	hdma_sai1_a.Init.Mode = DMA_CIRCULAR;
	hdma_sai1_a.Init.Priority = DMA_PRIORITY_LOW;
	hdma_sai1_a.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
	hdma_sai1_a.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;
	hdma_sai1_a.Init.MemBurst = DMA_MBURST_SINGLE;
	hdma_sai1_a.Init.PeriphBurst = DMA_PBURST_SINGLE;
	if (HAL_DMA_Init(&hdma_sai1_a) != HAL_OK)
	{
		Error_Handler();
	}

	/* Several peripheral DMA handle pointers point to the same DMA handle.
	 Be aware that there is only one stream to perform all the requested DMAs. */
	__HAL_LINKDMA(&hsai_BlockA1,hdmarx,hdma_sai1_a);

	__HAL_LINKDMA(&hsai_BlockA1,hdmatx,hdma_sai1_a);
}  

void SAIA1_TX_DMA_Init(uint8_t width) 
{  
	/* Peripheral DMA init*/
	
	//HAL_DMA_DeInit(&hdma_sai1_a);
		
	if(width==16)
	{
		hdma_sai1_a.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
		hdma_sai1_a.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
	}
	else if(width==24)
	{
		hdma_sai1_a.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
		hdma_sai1_a.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
	}

	hdma_sai1_a.Instance = DMA2_Stream1;
	hdma_sai1_a.Init.Channel = DMA_CHANNEL_0;
	hdma_sai1_a.Init.Direction = DMA_MEMORY_TO_PERIPH;
	hdma_sai1_a.Init.PeriphInc = DMA_PINC_DISABLE;
	hdma_sai1_a.Init.MemInc = DMA_MINC_ENABLE;
	hdma_sai1_a.Init.Mode = DMA_CIRCULAR;
	hdma_sai1_a.Init.Priority = DMA_PRIORITY_LOW;
	hdma_sai1_a.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
	hdma_sai1_a.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;
	hdma_sai1_a.Init.MemBurst = DMA_MBURST_SINGLE;
	hdma_sai1_a.Init.PeriphBurst = DMA_PBURST_SINGLE;
	if (HAL_DMA_Init(&hdma_sai1_a) != HAL_OK)
	{
		Error_Handler();
	}

	/* Several peripheral DMA handle pointers point to the same DMA handle.
	 Be aware that there is only one stream to perform all the requested DMAs. */
	__HAL_LINKDMA(&hsai_BlockA1,hdmarx,hdma_sai1_a);

	__HAL_LINKDMA(&hsai_BlockA1,hdmatx,hdma_sai1_a);
}  

void SAIB1_RX_DMA_Init(uint8_t width) 
{  
	/* Peripheral DMA init*/
		
	if(width==16)
	{
		hdma_sai1_b.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
		hdma_sai1_b.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
	}
	else if(width==24)
	{
		hdma_sai1_b.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
		hdma_sai1_b.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
	}

    hdma_sai1_b.Instance = DMA2_Stream0;
    hdma_sai1_b.Init.Channel = DMA_CHANNEL_10;
    hdma_sai1_b.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_sai1_b.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_sai1_b.Init.MemInc = DMA_MINC_ENABLE;
    hdma_sai1_b.Init.Mode = DMA_CIRCULAR;
    hdma_sai1_b.Init.Priority = DMA_PRIORITY_LOW;
    hdma_sai1_b.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    hdma_sai1_b.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;
    hdma_sai1_b.Init.MemBurst = DMA_MBURST_SINGLE;
    hdma_sai1_b.Init.PeriphBurst = DMA_PBURST_SINGLE;

    if (HAL_DMA_Init(&hdma_sai1_b) != 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_BlockB1,hdmarx,hdma_sai1_b);
    __HAL_LINKDMA(&hsai_BlockB1,hdmatx,hdma_sai1_b);
		

}  

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);
    }
    if(hsai->Instance==SAI1_Block_B)
    {
      /* Peripheral clock enable */
      if (SAI1_client == 0)
      {
       __HAL_RCC_SAI1_CLK_ENABLE();
      }
    SAI1_client ++;

    /**SAI1_B_Block_B GPIO Configuration
    PE3     ------> SAI1_SD_B
    */
    GPIO_InitStruct.Pin = GPIO_PIN_3;
    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);
    }
    if(hsai->Instance==SAI1_Block_B)
    {
    SAI1_client --;
      if (SAI1_client == 0)
      {
      /* Peripheral clock disable */
      __HAL_RCC_SAI1_CLK_DISABLE();
      }

    /**SAI1_B_Block_B GPIO Configuration
    PE3     ------> SAI1_SD_B
    */
    HAL_GPIO_DeInit(GPIOE, GPIO_PIN_3);

    /* SAI1 DMA Deinit */
    HAL_DMA_DeInit(hsai->hdmarx);
    HAL_DMA_DeInit(hsai->hdmatx);
    }
}


#ifndef __BSP_RECORDER_H
#define __BSP_RECORDER_H

#ifdef __cplusplus
extern "C" {
#endif

#include "main.h"
#include "bsp_wavplay.h" 

#define SAI_RX_DMA_BUF_SIZE    	4096		//定义RX DMA 数组大小
#define SAI_RX_FIFO_SIZE		10			//定义接收FIFO大小

#define REC_SAMPLERATE			44100		//采样率,44.1Khz

uint8_t rec_sai_fifo_read(uint8_t **buf);
uint8_t rec_sai_fifo_write(uint8_t *buf);

//void rec_sai_dma_rx_callback(void);
void recoder_enter_rec_mode(void);
void recoder_wav_init(__WaveHeader* wavhead);
void recoder_msg_show(uint32_t tsec,uint32_t kbps);
void recoder_remindmsg_show(uint8_t mode);
void recoder_new_pathname(uint8_t *pname); 
void wav_recorder(void);



 
#ifdef __cplusplus
}
#endif

#endif /* __BSP_RECORDER_H */


#include "bsp_recorder.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 "string.h"  

//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//录音机 应用代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/11
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved									  
// 	
  
uint8_t *sairecbuf1;			//SAI1 DMA接收BUF1
uint8_t *sairecbuf2; 		//SAI1 DMA接收BUF2

//REC录音FIFO管理参数.
//由于FATFS文件写入时间的不确定性,如果直接在接收中断里面写文件,可能导致某次写入时间过长
//从而引起数据丢失,故加入FIFO控制,以解决此问题.
volatile uint8_t sairecfifordpos=0;	//FIFO读位置
volatile uint8_t sairecfifowrpos=0;	//FIFO写位置
uint8_t *sairecfifobuf[SAI_RX_FIFO_SIZE];//定义10个录音接收FIFO

FIL* f_rec=0;			//录音文件	
uint32_t wavsize;			//wav数据大小(字节数,不包括文件头!!)
uint8_t rec_sta=0;			//录音状态
						//[7]:0,没有开启录音;1,已经开启录音;
						//[6:1]:保留
						//[0]:0,正在录音;1,暂停录音;
						
void BSP_SAI_REC_M0_RX_Callback(DMA_HandleTypeDef *_hdma)	//RX回调函数  
{
	if(rec_sta==0X80)//录音模式
	{  
    if(((SCB->CCR)>>16)&0X01)SCB_CleanInvalidateDCache();
		rec_sai_fifo_write(sairecbuf1);	//sairecbuf1写入FIFO
	}
}

void BSP_SAI_REC_M1_RX_Callback(DMA_HandleTypeDef *_hdma)	//RX回调函数  
{
	if(rec_sta==0X80)//录音模式
	{  
    if(((SCB->CCR)>>16)&0X01)SCB_CleanInvalidateDCache();
		rec_sai_fifo_write(sairecbuf2);						//sairecbuf2写入FIFO 
	}
} 		

//读取录音FIFO
//buf:数据缓存区首地址
//返回值:0,没有数据可读;
//      1,读到了1个数据块
uint8_t rec_sai_fifo_read(uint8_t **buf)
{
	if(sairecfifordpos==sairecfifowrpos)return 0;
	sairecfifordpos++;		//读位置加1
	if(sairecfifordpos>=SAI_RX_FIFO_SIZE)sairecfifordpos=0;//归零 
	*buf=sairecfifobuf[sairecfifordpos];
	return 1;
}
//写一个录音FIFO
//buf:数据缓存区首地址
//返回值:0,写入成功;
//      1,写入失败
uint8_t rec_sai_fifo_write(uint8_t *buf)
{
	uint16_t i;
	uint8_t temp=sairecfifowrpos;//记录当前写位置
	sairecfifowrpos++;		//写位置加1
	if(sairecfifowrpos>=SAI_RX_FIFO_SIZE)sairecfifowrpos=0;//归零  
	if(sairecfifordpos==sairecfifowrpos)
	{
		sairecfifowrpos=temp;//还原原来的写位置,此次写入失败
		return 1;	
	}
	for(i=0;i<SAI_RX_DMA_BUF_SIZE;i++)sairecfifobuf[sairecfifowrpos][i]=buf[i];//拷贝数据
	return 0;
} 

//录音 SAI_DMA接收中断服务函数.在中断里面写入数据
void rec_sai_dma_rx_callback(void) 
{      
	if(rec_sta==0X80)//录音模式
	{  
    if(((SCB->CCR)>>16)&0X01)SCB_CleanInvalidateDCache();
		if(DMA2_Stream5->CR&(1<<19))rec_sai_fifo_write(sairecbuf1);	//sairecbuf1写入FIFO
		else rec_sai_fifo_write(sairecbuf2);						//sairecbuf2写入FIFO 
	} 
}  
const uint16_t saiplaybuf[2]={0X0000,0X0000};//2个16位数据,用于录音时SAI Block A主机发送.循环发送0.
//进入PCM 录音模式 		  
void recoder_enter_rec_mode(void)
{
	WM8978_ADDA_Cfg(0,1);		//开启ADC
	WM8978_Input_Cfg(1,1,0);	//开启输入通道(MIC&LINE IN)
	WM8978_Output_Cfg(0,1);		//开启BYPASS输出 
	WM8978_MIC_Gain(46);		//MIC增益设置 
	WM8978_SPKvol_Set(0);		//关闭喇叭.
	WM8978_I2S_Cfg(2,0);		//飞利浦标准,16位数据长度

	BSP_SAIA1_Init(SAI_PROTOCOL_DATASIZE_16BIT, REC_SAMPLERATE); 
	BSP_SAIB1_Init(SAI_PROTOCOL_DATASIZE_16BIT, REC_SAMPLERATE); 
	SAIA_SampleRate_Set(REC_SAMPLERATE);//设置采样率 
	SAIA1_TX_DMA_Init(16);
	HAL_DMAEx_MultiBufferStart(&hdma_sai1_a, (uint32_t)&saiplaybuf[0], (uint32_t)&hsai_BlockA1.Instance->DR, (uint32_t)&saiplaybuf[1], 1);
	SAIB1_RX_DMA_Init(16);
	HAL_DMA_RegisterCallback(&hdma_sai1_b, HAL_DMA_XFER_CPLT_CB_ID, BSP_SAI_REC_M0_RX_Callback);
	HAL_DMA_RegisterCallback(&hdma_sai1_b, HAL_DMA_XFER_M1CPLT_CB_ID, BSP_SAI_REC_M1_RX_Callback);
	HAL_DMAEx_MultiBufferStart_IT(&hdma_sai1_b, /*(uint32_t)&SAI1_Block_B->DR*/(uint32_t)&hsai_BlockB1.Instance->DR, (uint32_t)sairecbuf1, (uint32_t)sairecbuf2, SAI_RX_DMA_BUF_SIZE/2);

	recoder_remindmsg_show(0);
}  
//进入PCM 放音模式 		  
void recoder_enter_play_mode(void)
{
	WM8978_ADDA_Cfg(1,0);		//开启DAC 
	WM8978_Input_Cfg(0,0,0);	//关闭输入通道(MIC&LINE IN)
	WM8978_Output_Cfg(1,0);		//开启DAC输出 
	WM8978_MIC_Gain(0);			//MIC增益设置为0 
	WM8978_SPKvol_Set(50);		//喇叭音量设置
	recoder_remindmsg_show(1);
}
//初始化WAV头.
void recoder_wav_init(__WaveHeader* wavhead) //初始化WAV头			   
{
	wavhead->riff.ChunkID=0X46464952;	//"RIFF"
	wavhead->riff.ChunkSize=0;			//还未确定,最后需要计算
	wavhead->riff.Format=0X45564157; 	//"WAVE"
	wavhead->fmt.ChunkID=0X20746D66; 	//"fmt "
	wavhead->fmt.ChunkSize=16; 			//大小为16个字节
	wavhead->fmt.AudioFormat=0X01; 		//0X01,表示PCM;0X01,表示IMA ADPCM
 	wavhead->fmt.NumOfChannels=2;		//双声道
 	wavhead->fmt.SampleRate=REC_SAMPLERATE;//设置采样速率
 	wavhead->fmt.ByteRate=wavhead->fmt.SampleRate*4;//字节速率=采样率*通道数*(ADC位数/8)
 	wavhead->fmt.BlockAlign=4;			//块大小=通道数*(ADC位数/8)
 	wavhead->fmt.BitsPerSample=16;		//16位PCM
  wavhead->data.ChunkID=0X61746164;	//"data"
 	wavhead->data.ChunkSize=0;			//数据大小,还需要计算  
} 						    
//显示录音时间和码率
//tsec:秒钟数.
void recoder_msg_show(uint32_t tsec,uint32_t kbps)
{   
//	//显示录音时间			 
//	LCD_ShowString(30,210,200,16,16,"TIME:");	  	  
//	LCD_ShowxNum(30+40,210,tsec/60,2,16,0X80);	//分钟
//	LCD_ShowChar(30+56,210,':',16,0);
//	LCD_ShowxNum(30+64,210,tsec%60,2,16,0X80);	//秒钟	
//	//显示码率		 
//	LCD_ShowString(140,210,200,16,16,"KPBS:");	  	  
//	LCD_ShowxNum(140+40,210,kbps/1000,4,16,0X80);	//码率显示 	
}  	
//提示信息
//mode:0,录音模式;1,放音模式
void recoder_remindmsg_show(uint8_t mode)
{
	if(mode==0)	//录音模式
	{
		printf("KEY0:REC/PAUSE\n"); 
		printf("KEY2:STOP&SAVE\n"); 
		printf("WK_UP:PLAY\n"); 
	}else		//放音模式
	{
		printf("KEY0:STOP Play\n");  
		printf("WK_UP:PLAY/PAUSE\n"); 
	}
}
//通过时间获取文件名
//仅限在SD卡保存,不支持FLASH DISK保存
//组合成:形如"0:RECORDER/REC20120321210633.wav"的文件名
void recoder_new_pathname(uint8_t *pname)
{	 
	uint8_t res;					 
	uint16_t index=0;
	while(index<0XFFFF)
	{
		sprintf((char*)pname,"0:RECORDER/REC%05d.wav",index);
		res=f_open(ftemp,(const TCHAR*)pname,FA_READ);//尝试打开这个文件
		if(res==FR_NO_FILE)break;		//该文件名不存在=正是我们需要的.
		index++;
	}
} 
//WAV录音 
void wav_recorder(void)
{ 
	uint8_t res,i;
	uint8_t key;
	uint8_t rval=0;
	__WaveHeader *wavhead=0; 
 	DIR recdir;	 					//目录  
 	uint8_t *pname=0;
	uint8_t *pdatabuf;
	uint8_t timecnt=0;					//计时器   
	uint32_t recsec=0;					//录音时间 
	
	WM8978_Init();				    //初始化WM8978
	WM8978_HPvol_Set(40,40);	    //耳机音量设置
	WM8978_SPKvol_Set(40);		    //喇叭音量设置
	
  while(f_opendir(&recdir,"0:/RECORDER"))//打开录音文件夹
 	{	 
		printf("RECORDER文件夹错误!");
		delay_ms(200);				  			  
		f_mkdir("0:/RECORDER");				//创建该目录   
	}   
	sairecbuf1=mymalloc(SRAMIN,SAI_RX_DMA_BUF_SIZE);//SAI录音内存1申请
	sairecbuf2=mymalloc(SRAMIN,SAI_RX_DMA_BUF_SIZE);//SAI录音内存2申请  
	for(i=0;i<SAI_RX_FIFO_SIZE;i++)
	{
		sairecfifobuf[i]=mymalloc(SRAMIN,SAI_RX_DMA_BUF_SIZE);//SAI录音FIFO内存申请
		if(sairecfifobuf[i]==NULL)break;			//申请失败
	}
  f_rec=(FIL *)mymalloc(SRAMIN,sizeof(FIL));		//开辟FIL字节的内存区域  
 	wavhead=(__WaveHeader*)mymalloc(SRAMIN,sizeof(__WaveHeader));//开辟__WaveHeader字节的内存区域 
	pname=mymalloc(SRAMIN,30);						//申请30个字节内存,类似"0:RECORDER/REC00001.wav" 
	if(!sairecbuf1||!sairecbuf2||!f_rec||!wavhead||!pname||i!=SAI_RX_FIFO_SIZE)rval=1; 	
	if(rval==0)		
	{
		recoder_enter_rec_mode();	//进入录音模式,此时耳机可以听到咪头采集到的音频   
		pname[0]=0;					//pname没有任何文件名 
 	  while(rval==0)
		{
			key=KEY_Scan(0);
			switch(key)
			{		
				case KEY2_PRES:	//STOP&SAVE
					if(rec_sta&0X80)//有录音
					{
						rec_sta=0;	//关闭录音
						wavhead->riff.ChunkSize=wavsize+36;		//整个文件的大小-8;
				   	wavhead->data.ChunkSize=wavsize;		//数据大小
						f_lseek(f_rec,0);						//偏移到文件头.
				  	f_write(f_rec,(const void*)wavhead,sizeof(__WaveHeader),&bw);//写入头数据
						f_close(f_rec);
						wavsize=0;
						sairecfifordpos=0;	//FIFO读写位置重新归零
						sairecfifowrpos=0;
					}
					rec_sta=0;
					recsec=0;      
				break;	 
					
				case KEY0_PRES:	//REC/PAUSE
					if(rec_sta&0X01)//原来是暂停,继续录音
					{
						rec_sta&=0XFE;//取消暂停
					}else if(rec_sta&0X80)//已经在录音了,暂停
					{
						rec_sta|=0X01;	//暂停
					}else				//还没开始录音 
					{
						recsec=0;	 
						recoder_new_pathname(pname);			//得到新的名字
						printf("录制:%s\n", pname+11);		   
				 		recoder_wav_init(wavhead);				//初始化wav数据	
	 					res=f_open(f_rec,(const TCHAR*)pname, FA_CREATE_ALWAYS | FA_WRITE); 
						if(res)			//文件创建失败
						{
							rec_sta=0;	//创建文件失败,不能录音
							rval=0XFE;	//提示是否存在SD卡
						}else 
						{
							res=f_write(f_rec,(const void*)wavhead,sizeof(__WaveHeader),&bw);//写入头数据
							recoder_msg_show(0,0);
 							rec_sta|=0X80;	//开始录音	 
						} 
 					}
				break;  
					
				case WKUP_PRES:	//播放最近一段录音
					if(rec_sta!=0X80)//没有在录音
					{   	 		 				  
						if(pname[0])//如果触摸按键被按下,且pname不为空
						{				 
							printf("播放:%s\n",pname+11);		   
							recoder_enter_play_mode();	//进入播放模式
							audio_play_song(pname);		//播放pname
							recoder_enter_rec_mode();	//重新进入录音模式 
						}
					}
					break;
			}
			if(rec_sai_fifo_read(&pdatabuf))//读取一次数据,读到数据了,写入文件
			{
				res=f_write(f_rec,pdatabuf,SAI_RX_DMA_BUF_SIZE,(UINT*)&bw);//写入文件
				if(res)
				{
					printf("write error:%d\r\n",res);
				}
				wavsize+=SAI_RX_DMA_BUF_SIZE;				
			}else delay_ms(5); 
 			if(recsec!=(wavsize/wavhead->fmt.ByteRate))	//录音时间显示
			{	   
				recsec=wavsize/wavhead->fmt.ByteRate;	//录音时间
				recoder_msg_show(recsec,wavhead->fmt.SampleRate*wavhead->fmt.NumOfChannels*wavhead->fmt.BitsPerSample);//显示码率
			}
		}		 
	}    
	myfree(SRAMIN,sairecbuf1);	//释放内存
	myfree(SRAMIN,sairecbuf2);	//释放内存  
	for(i=0;i<SAI_RX_FIFO_SIZE;i++)myfree(SRAMIN,sairecfifobuf[i]);//SAI录音FIFO内存释放
	myfree(SRAMIN,f_rec);		//释放内存
	myfree(SRAMIN,wavhead);		//释放内存  
	myfree(SRAMIN,pname);		//释放内存  
}

#ifndef __BSP_WAVPLAY_H
#define __BSP_WAVPLAY_H

#ifdef __cplusplus
extern "C" {
#endif

#include "main.h"

//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//WAV 解码代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/18
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved				
//********************************************************************************
//V1.0 说明
//1,支持16位/24位WAV文件播放
//2,最高可以支持到192K/24bit的WAV格式. 
// 	
 

#define WAV_SAI_TX_DMA_BUFSIZE    4096		//定义WAV TX DMA 数组大小(播放192Kbps@24bit的时候,需要设置4096大才不会卡)
 
//RIFF块
typedef __packed struct
{
	uint32_t ChunkID;		   	//chunk id;这里固定为"RIFF",即0X46464952
	uint32_t ChunkSize ;		   	//集合大小;文件总大小-8
	uint32_t Format;	   			//格式;WAVE,即0X45564157
}ChunkRIFF ;
//fmt块
typedef __packed struct
{
	uint32_t ChunkID;		   	//chunk id;这里固定为"fmt ",即0X20746D66
	uint32_t ChunkSize ;		   	//子集合大小(不包括ID和Size);这里为:20.
	uint16_t AudioFormat;	  	//音频格式;0X01,表示线性PCM;0X11表示IMA ADPCM
	uint16_t NumOfChannels;		//通道数量;1,表示单声道;2,表示双声道;
	uint32_t SampleRate;			//采样率;0X1F40,表示8Khz
	uint32_t ByteRate;			//字节速率; 
	uint16_t BlockAlign;			//块对齐(字节); 
	uint16_t BitsPerSample;		//单个采样数据大小;4位ADPCM,设置为4
//	uint16_t ByteExtraData;		//附加的数据字节;2个; 线性PCM,没有这个参数
}ChunkFMT;	   
//fact块 
typedef __packed struct 
{
	uint32_t ChunkID;		   	//chunk id;这里固定为"fact",即0X74636166;
	uint32_t ChunkSize ;		   	//子集合大小(不包括ID和Size);这里为:4.
	uint32_t NumOfSamples;	  	//采样的数量; 
}ChunkFACT;
//LIST块 
typedef __packed struct 
{
	uint32_t ChunkID;		   	//chunk id;这里固定为"LIST",即0X74636166;
	uint32_t ChunkSize ;		   	//子集合大小(不包括ID和Size);这里为:4. 
}ChunkLIST;

//data块 
typedef __packed struct 
{
	uint32_t ChunkID;		   	//chunk id;这里固定为"data",即0X5453494C
	uint32_t ChunkSize ;		   	//子集合大小(不包括ID和Size) 
}ChunkDATA;

//wav头
typedef __packed struct
{ 
	ChunkRIFF riff;	//riff块
	ChunkFMT fmt;  	//fmt块
//	ChunkFACT fact;	//fact块 线性PCM,没有这个结构体	 
	ChunkDATA data;	//data块		 
}__WaveHeader; 

//wav 播放控制结构体
typedef __packed struct
{ 
	uint16_t audioformat;			//音频格式;0X01,表示线性PCM;0X11表示IMA ADPCM
	uint16_t nchannels;				//通道数量;1,表示单声道;2,表示双声道; 
	uint16_t blockalign;				//块对齐(字节);  
	uint32_t datasize;				//WAV数据大小 

	uint32_t totsec ;				//整首歌时长,单位:秒
	uint32_t cursec ;				//当前播放时长

	uint32_t bitrate;	   			//比特率(位速)
	uint32_t samplerate;				//采样率 
	uint16_t bps;					//位数,比如16bit,24bit,32bit

	uint32_t datastart;				//数据帧开始的位置(在文件里面的偏移)
}__wavctrl; 


uint8_t wav_decode_init(uint8_t* fname,__wavctrl* wavx);
uint32_t wav_buffill(uint8_t *buf,uint16_t size,uint8_t bits);
//void wav_sai_dma_tx_callback(void); 
uint8_t wav_play_song(uint8_t* fname);


extern __wavctrl wavctrl;		//WAV控制结构体

#ifdef __cplusplus
}
#endif

#endif /* __BSP_WAVPLAY_H */




#include "bsp_wavplay.h" 
#include "bsp_audioplay.h"
#include "bsp_printf.h"
#include "delay.h" 
#include "bsp_malloc.h"
#include "ff.h"
#include "bsp_sai.h"
#include "bsp_wm8978.h"
#include "bsp_key.h"
#include "string.h"

//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//WAV 解码代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/18
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved				
//********************************************************************************
//V1.0 说明
//1,支持16位/24位WAV文件播放
//2,最高可以支持到192K/24bit的WAV格式. 
// 	
 
__wavctrl wavctrl;		//WAV控制结构体
volatile uint8_t wavtransferend=0;	//sai传输完成标志

void BSP_SAI_WAV_M0_TX_Callback(DMA_HandleTypeDef *_hdma)	//TX回调函数  
{
	uint32_t fillnum; 

	if((audiodev.status&0X01)==0)
	{
		memset(audiodev.saibuf1,0,WAV_SAI_TX_DMA_BUFSIZE);
	}
	else
	{
		fillnum=wav_buffill(audiodev.saibuf1,WAV_SAI_TX_DMA_BUFSIZE,wavctrl.bps);
		if(fillnum!=WAV_SAI_TX_DMA_BUFSIZE)//播放结束?
		{
			wavtransferend=1;
		} 
	}
}

void BSP_SAI_WAV_M1_TX_Callback(DMA_HandleTypeDef *_hdma)	//TX回调函数  
{
	uint32_t fillnum; 

	if((audiodev.status&0X01)==0)
	{
		memset(audiodev.saibuf2,0,WAV_SAI_TX_DMA_BUFSIZE);
	}
	else
	{
		fillnum=wav_buffill(audiodev.saibuf2,WAV_SAI_TX_DMA_BUFSIZE,wavctrl.bps);
		if(fillnum!=WAV_SAI_TX_DMA_BUFSIZE)//播放结束?
		{
			wavtransferend=1;
		} 
	}
} 

//WAV解析初始化
//fname:文件路径+文件名
//wavx:wav 信息存放结构体指针
//返回值:0,成功;1,打开文件失败;2,非WAV文件;3,DATA区域未找到.
uint8_t wav_decode_init(uint8_t* fname,__wavctrl* wavx)
{
	FIL*ftemp;
	uint8_t *buf; 
	uint32_t br=0;
	uint8_t res=0;
	
	ChunkRIFF *riff;
	ChunkFMT *fmt;
	ChunkFACT *fact;
	ChunkDATA *data;
	ftemp=(FIL*)mymalloc(SRAMIN,sizeof(FIL));
	buf=mymalloc(SRAMIN,512);
	if(ftemp&&buf)	//内存申请成功
	{
		res=f_open(ftemp,(TCHAR*)fname,FA_READ);//打开文件
		if(res==FR_OK)
		{
			f_read(ftemp,buf,512,&br);	//读取512字节在数据
			riff=(ChunkRIFF *)buf;		//获取RIFF块
			if(riff->Format==0X45564157)//是WAV文件
			{
				fmt=(ChunkFMT *)(buf+12);	//获取FMT块 
				fact=(ChunkFACT *)(buf+12+8+fmt->ChunkSize);//读取FACT块
				if(fact->ChunkID==0X74636166||fact->ChunkID==0X5453494C)wavx->datastart=12+8+fmt->ChunkSize+8+fact->ChunkSize;//具有fact/LIST块的时候(未测试)
				else wavx->datastart=12+8+fmt->ChunkSize;  
				data=(ChunkDATA *)(buf+wavx->datastart);	//读取DATA块
				if(data->ChunkID==0X61746164)//解析成功!
				{
					wavx->audioformat=fmt->AudioFormat;		//音频格式
					wavx->nchannels=fmt->NumOfChannels;		//通道数
					wavx->samplerate=fmt->SampleRate;		//采样率
					wavx->bitrate=fmt->ByteRate*8;			//得到位速
					wavx->blockalign=fmt->BlockAlign;		//块对齐
					wavx->bps=fmt->BitsPerSample;			//位数,16/24/32位
					
					wavx->datasize=data->ChunkSize;			//数据块大小
					wavx->datastart=wavx->datastart+8;		//数据流开始的地方. 
					 
					printf("wavx->audioformat:%d\r\n",wavx->audioformat);
					printf("wavx->nchannels:%d\r\n",wavx->nchannels);
					printf("wavx->samplerate:%d\r\n",wavx->samplerate);
					printf("wavx->bitrate:%d\r\n",wavx->bitrate);
					printf("wavx->blockalign:%d\r\n",wavx->blockalign);
					printf("wavx->bps:%d\r\n",wavx->bps);
					printf("wavx->datasize:%d\r\n",wavx->datasize);
					printf("wavx->datastart:%d\r\n",wavx->datastart);  
				}else res=3;//data区域未找到.
			}else res=2;//非wav文件
			
		}else res=1;//打开文件错误
	}
	f_close(ftemp);
	myfree(SRAMIN,ftemp);//释放内存
	myfree(SRAMIN,buf); 
	return 0;
}

//填充buf
//buf:数据区
//size:填充数据量
//bits:位数(16/24)
//返回值:读到的数据个数
uint32_t wav_buffill(uint8_t *buf,uint16_t size,uint8_t bits)
{
	uint16_t readlen=0;
	uint32_t bread;
	uint16_t i;
	uint32_t *p,*pbuf;
	if(bits==24)//24bit音频,需要处理一下
	{
		readlen=(size/4)*3;		//此次要读取的字节数
		f_read(audiodev.file,audiodev.tbuf,readlen,(UINT*)&bread);//读取数据 
		pbuf=(uint32_t*)buf;
		for(i=0;i<size/4;i++)
		{  
			p=(uint32_t*)(audiodev.tbuf+i*3);
			pbuf[i]=p[0];  
		} 
		bread=(bread*4)/3;		//填充后的大小.
	}else 
	{
		f_read(audiodev.file,buf,size,(UINT*)&bread);//16bit音频,直接读取数据  
		if(bread<size)//不够数据了,补充0
		{
			for(i=bread;i<size-bread;i++)buf[i]=0; 
		}
	}
	return bread;
}  

//得到当前播放时间
//fx:文件指针
//wavx:wav播放控制器
void wav_get_curtime(FIL*fx,__wavctrl *wavx)
{
	long long fpos;  	
 	wavx->totsec=wavx->datasize/(wavx->bitrate/8);	//歌曲总长度(单位:秒) 
	fpos=fx->fptr-wavx->datastart; 					//得到当前文件播放到的地方 
	wavx->cursec=fpos*wavx->totsec/wavx->datasize;	//当前播放到第多少秒了?	
}

//播放某个WAV文件
//fname:wav文件路径.
//返回值:
//KEY0_PRES:下一曲
//KEY1_PRES:上一曲
//其他:错误
uint8_t wav_play_song(uint8_t* fname)
{
	uint8_t key;
	uint8_t res;  
	
	audiodev.file=(FIL*)mymalloc(SRAMIN,sizeof(FIL));
	audiodev.saibuf1=mymalloc(SRAMIN,WAV_SAI_TX_DMA_BUFSIZE);
	audiodev.saibuf2=mymalloc(SRAMIN,WAV_SAI_TX_DMA_BUFSIZE);
	audiodev.tbuf=mymalloc(SRAMIN,WAV_SAI_TX_DMA_BUFSIZE);
	
	if(audiodev.file&&audiodev.saibuf1&&audiodev.saibuf2&&audiodev.tbuf)
	{
		memset(audiodev.saibuf1,0,WAV_SAI_TX_DMA_BUFSIZE);
		memset(audiodev.saibuf2,0,WAV_SAI_TX_DMA_BUFSIZE);		
		res=wav_decode_init(fname,&wavctrl);//得到文件的信息
		if(res==0)//解析文件成功
		{
			wavtransferend=0;
			
			if(wavctrl.bps==16)
			{
				WM8978_I2S_Cfg(2,0);	//飞利浦标准,16位数据长度
				BSP_SAI1_Init(SAI_PROTOCOL_DATASIZE_16BIT, wavctrl.samplerate);
				SAIA_SampleRate_Set(wavctrl.samplerate);//设置采样率  
				SAIA_TX_DMA_Init(16);	
				HAL_DMA_RegisterCallback(&hdma_sai1_a, HAL_DMA_XFER_CPLT_CB_ID, BSP_SAI_WAV_M0_TX_Callback);
				HAL_DMA_RegisterCallback(&hdma_sai1_a, HAL_DMA_XFER_M1CPLT_CB_ID, BSP_SAI_WAV_M1_TX_Callback);
				HAL_DMAEx_MultiBufferStart_IT(&hdma_sai1_a, (uint32_t)audiodev.saibuf1, (uint32_t)&hsai_BlockA1.Instance->DR, (uint32_t)audiodev.saibuf2, WAV_SAI_TX_DMA_BUFSIZE/2);
			}
			else if(wavctrl.bps==24)
			{
				WM8978_I2S_Cfg(2,2);	//飞利浦标准,24位数据长度
				BSP_SAI1_Init(SAI_PROTOCOL_DATASIZE_24BIT, wavctrl.samplerate); 	
				SAIA_SampleRate_Set(wavctrl.samplerate);//设置采样率 
				SAIA_TX_DMA_Init(24);
				HAL_DMA_RegisterCallback(&hdma_sai1_a, HAL_DMA_XFER_CPLT_CB_ID, BSP_SAI_WAV_M0_TX_Callback);
				HAL_DMA_RegisterCallback(&hdma_sai1_a, HAL_DMA_XFER_M1CPLT_CB_ID, BSP_SAI_WAV_M1_TX_Callback);
				HAL_DMAEx_MultiBufferStart_IT(&hdma_sai1_a, (uint32_t)audiodev.saibuf1, (uint32_t)&hsai_BlockA1.Instance->DR, (uint32_t)audiodev.saibuf2, WAV_SAI_TX_DMA_BUFSIZE/4);
			}
			else
			{
				res=0XFF;
			}
			
			if(res==0)
			{
				res=f_open(audiodev.file,(TCHAR*)fname,FA_READ);	//打开文件
				if(res==0)
				{
					f_lseek(audiodev.file, wavctrl.datastart);		//跳过文件头
					audiodev.status=3<<0;//开始播放+非暂停

					while(res==0)
					{
						if(wavtransferend)//播放结束?
						{
							res=KEY0_PRES;
							break;
						} 
						
						while(1)
						{
							key=KEY_Scan(0); 
							if(key==WKUP_PRES)//暂停
							{
								if(audiodev.status&0X01)audiodev.status&=~(1<<0);
								else audiodev.status|=0X01;  
							}
							if(key==KEY2_PRES||key==KEY0_PRES)//下一曲/上一曲
							{
								res=key;
								break; 
							}
							wav_get_curtime(audiodev.file,&wavctrl);//得到总时间和当前播放的时间 
							audio_msg_show(wavctrl.totsec,wavctrl.cursec,wavctrl.bitrate);

							if((audiodev.status&0X01)==0)delay_ms(10);
							else break;
						}
					}
					audiodev.status=0;
				}
				else 
				{
					res=0XFF;
				}
			}
		}
		else
		{
			res=0XFF;
		} 
	}
	else
	{
		res=0XFF; 
	}		
	myfree(SRAMIN,audiodev.tbuf);	//释放内存
	myfree(SRAMIN,audiodev.saibuf1);//释放内存
	myfree(SRAMIN,audiodev.saibuf2);//释放内存 
	myfree(SRAMIN,audiodev.file);	//释放内存 
	return res;
} 


总结:
1、
在这里插入图片描述
2、
在这里插入图片描述
3、
在这里插入图片描述
4、
在这里插入图片描述
5、
在这里插入图片描述
6、
在这里插入图片描述
7、void BSP_SAIB1_Init(uint32_t datasize, uint32_t AudioFrequency)函数中需要加入 hsai_BlockB1.Instance->CR1 |= SAI_xCR1_DMAEN;__HAL_SAI_ENABLE(&hsai_BlockB1);void BSP_SAIA1_Init(uint32_t datasize, uint32_t AudioFrequency)中不用加是因为uint8_t SAIA_SampleRate_Set(uint32_t samplerate)中有设置;
HAL库中HAL_StatusTypeDef HAL_SAI_Transmit_DMA(SAI_HandleTypeDef *hsai, uint8_t *pData, uint16_t Size)等函数中有设置,但是双缓冲HAL_StatusTypeDef HAL_DMAEx_MultiBufferStart_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t SecondMemAddress, uint32_t DataLength)中没有设置

8、HAL_StatusTypeDef HAL_DMAEx_MultiBufferStart(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t SecondMemAddress, uint32_t DataLength)
播放音乐发送时&hsai_BlockA1.Instance->DR是目的地址;
录音接收时t)&hsai_BlockB1.Instance->DR是源地址

9、SAI DMA中断优先级低于SD DMA中断优先级,否则程序会卡死在读SD DMA中断

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值