MCUXpresso_MIMXRT1052xxxxB/boards/evkbimxrt1050/demo_apps/sai/sai.c
Yilin Sun 6baf4427ce
Updated to v2.15.000
Signed-off-by: Yilin Sun <imi415@imi.moe>
2024-03-18 23:15:10 +08:00

491 lines
15 KiB
C

/*
* Copyright (c) 2015, Freescale Semiconductor, Inc.
* Copyright 2016-2020 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "sai.h"
#include "fsl_wm8960.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "board.h"
#include "fsl_codec_common.h"
#include "fsl_codec_adapter.h"
#include "fsl_dmamux.h"
/*******************************************************************************
* Definitions
******************************************************************************/
/* SAI instance and clock */
#define DEMO_CODEC_WM8960
#define DEMO_CODEC_VOLUME 100U
#define DEMO_SAI SAI1
#define DEMO_SAI_CHANNEL (0)
#define DEMO_SAI_IRQ SAI1_IRQn
#define DEMO_SAITxIRQHandler SAI1_IRQHandler
#define DEMO_SAI_TX_SYNC_MODE kSAI_ModeAsync
#define DEMO_SAI_RX_SYNC_MODE kSAI_ModeSync
#define DEMO_SAI_MCLK_OUTPUT true
#define DEMO_SAI_MASTER_SLAVE kSAI_Master
#define SAI_UserIRQHandler SAI1_IRQHandler
/* demo audio master clock */
#define DEMO_AUDIO_MASTER_CLOCK DEMO_SAI_CLK_FREQ
/* IRQ */
#define DEMO_SAI_TX_IRQ SAI1_IRQn
#define DEMO_SAI_RX_IRQ SAI1_IRQn
/* DMA */
#define DEMO_DMA DMA0
#define DEMO_DMAMUX DMAMUX
#define DEMO_TX_EDMA_CHANNEL (0U)
#define DEMO_RX_EDMA_CHANNEL (1U)
#define DEMO_SAI_TX_SOURCE kDmaRequestMuxSai1Tx
#define DEMO_SAI_RX_SOURCE kDmaRequestMuxSai1Rx
/* Select Audio/Video PLL (786.48 MHz) as sai1 clock source */
#define DEMO_SAI1_CLOCK_SOURCE_SELECT (2U)
/* Clock pre divider for sai1 clock source */
#define DEMO_SAI1_CLOCK_SOURCE_PRE_DIVIDER (3U)
/* Clock divider for sai1 clock source */
#define DEMO_SAI1_CLOCK_SOURCE_DIVIDER (15U)
/* Get frequency of sai1 clock */
#define DEMO_SAI_CLK_FREQ \
(CLOCK_GetFreq(kCLOCK_AudioPllClk) / (DEMO_SAI1_CLOCK_SOURCE_DIVIDER + 1U) / \
(DEMO_SAI1_CLOCK_SOURCE_PRE_DIVIDER + 1U))
/* I2C instance and clock */
#define DEMO_I2C LPI2C1
/* Select USB1 PLL (480 MHz) as master lpi2c clock source */
#define DEMO_LPI2C_CLOCK_SOURCE_SELECT (0U)
/* Clock divider for master lpi2c clock source */
#define DEMO_LPI2C_CLOCK_SOURCE_DIVIDER (5U)
/* Get frequency of lpi2c clock */
#define DEMO_I2C_CLK_FREQ ((CLOCK_GetFreq(kCLOCK_Usb1PllClk) / 8) / (DEMO_LPI2C_CLOCK_SOURCE_DIVIDER + 1U))
/* SD power control function */
#define DEMO_SDCARD_POWER_CTRL_FUNCTION_EXIST
#define BOARD_MASTER_CLOCK_CONFIG()
#define BOARD_CONFIGCODEC_FOR_RECORD_PLAYBACK()
#define BOARD_CONFIGCODEC_FOR_PLAYBACK()
/*******************************************************************************
* Prototypes
******************************************************************************/
void BOARD_PowerOffSDCARD(void);
void BOARD_PowerOnSDCARD(void);
static void txCallback(I2S_Type *base, sai_edma_handle_t *handle, status_t status, void *userData);
static void rxCallback(I2S_Type *base, sai_edma_handle_t *handle, status_t status, void *userData);
#if defined DEMO_SDCARD
#include "ff.h"
#include "diskio.h"
#include "fsl_sd.h"
#include "sdmmc_config.h"
/*!
* @brief wait card insert function.
*/
static status_t sdcardWaitCardInsert(void);
static int SD_FatFsInit(void);
#endif
/*******************************************************************************
* Variables
******************************************************************************/
wm8960_config_t wm8960Config = {
.i2cConfig = {.codecI2CInstance = BOARD_CODEC_I2C_INSTANCE, .codecI2CSourceClock = BOARD_CODEC_I2C_CLOCK_FREQ},
.route = kWM8960_RoutePlaybackandRecord,
.leftInputSource = kWM8960_InputDifferentialMicInput3,
.rightInputSource = kWM8960_InputDifferentialMicInput2,
.playSource = kWM8960_PlaySourceDAC,
.slaveAddress = WM8960_I2C_ADDR,
.bus = kWM8960_BusI2S,
.format = {.mclk_HZ = 6144000U, .sampleRate = kWM8960_AudioSampleRate16KHz, .bitWidth = kWM8960_AudioBitWidth16bit},
.master_slave = false,
};
codec_config_t boardCodecConfig = {.codecDevType = kCODEC_WM8960, .codecDevConfig = &wm8960Config};
/*
* AUDIO PLL setting: Frequency = Fref * (DIV_SELECT + NUM / DENOM)
* = 24 * (32 + 768/1000)
* = 786.432 MHz
*/
const clock_audio_pll_config_t audioPllConfig = {
.loopDivider = 32, /* PLL loop divider. Valid range for DIV_SELECT divider value: 27~54. */
.postDivider = 1, /* Divider after the PLL, should only be 1, 2, 4, 8, 16. */
.numerator = 768, /* 30 bit numerator of fractional loop divider. */
.denominator = 1000, /* 30 bit denominator of fractional loop divider */
};
AT_QUICKACCESS_SECTION_DATA(sai_edma_handle_t txHandle);
edma_handle_t dmaTxHandle = {0};
AT_QUICKACCESS_SECTION_DATA(sai_edma_handle_t rxHandle);
edma_handle_t dmaRxHandle = {0};
AT_NONCACHEABLE_SECTION_ALIGN(uint8_t audioBuff[BUFFER_SIZE * BUFFER_NUM], 4);
extern codec_config_t boardCodecConfig;
volatile bool istxFinished = false;
volatile bool isrxFinished = false;
volatile uint32_t beginCount = 0;
volatile uint32_t sendCount = 0;
volatile uint32_t receiveCount = 0;
volatile bool sdcard = false;
volatile uint32_t fullBlock = 0;
volatile uint32_t emptyBlock = BUFFER_NUM;
#if defined DEMO_SDCARD
/* static values for fatfs */
AT_NONCACHEABLE_SECTION(FATFS g_fileSystem); /* File system object */
AT_NONCACHEABLE_SECTION(FIL g_fileObject); /* File object */
AT_NONCACHEABLE_SECTION(BYTE work[FF_MAX_SS]);
extern sd_card_t g_sd; /* sd card descriptor */
#endif
sai_transceiver_t saiConfig;
codec_handle_t codecHandle;
/*******************************************************************************
* Code
******************************************************************************/
void BOARD_EnableSaiMclkOutput(bool enable)
{
if (enable)
{
IOMUXC_GPR->GPR1 |= IOMUXC_GPR_GPR1_SAI1_MCLK_DIR_MASK;
}
else
{
IOMUXC_GPR->GPR1 &= (~IOMUXC_GPR_GPR1_SAI1_MCLK_DIR_MASK);
}
}
static void txCallback(I2S_Type *base, sai_edma_handle_t *handle, status_t status, void *userData)
{
sendCount++;
emptyBlock++;
if (sendCount == beginCount)
{
istxFinished = true;
SAI_TransferTerminateSendEDMA(base, handle);
sendCount = 0;
}
}
static void rxCallback(I2S_Type *base, sai_edma_handle_t *handle, status_t status, void *userData)
{
receiveCount++;
fullBlock++;
if (receiveCount == beginCount)
{
isrxFinished = true;
SAI_TransferTerminateReceiveEDMA(base, handle);
receiveCount = 0;
}
}
#if defined DEMO_SDCARD
static status_t sdcardWaitCardInsert(void)
{
BOARD_SD_Config(&g_sd, NULL, BOARD_SDMMC_SD_HOST_IRQ_PRIORITY, NULL);
/* SD host init function */
if (SD_HostInit(&g_sd) != kStatus_Success)
{
PRINTF("\r\nSD host init fail\r\n");
return kStatus_Fail;
}
/* wait card insert */
if (SD_PollingCardInsert(&g_sd, kSD_Inserted) == kStatus_Success)
{
PRINTF("\r\nCard inserted.\r\n");
/* power off card */
SD_SetCardPower(&g_sd, false);
/* power on the card */
SD_SetCardPower(&g_sd, true);
}
else
{
PRINTF("\r\nCard detect fail.\r\n");
return kStatus_Fail;
}
return kStatus_Success;
}
int SD_FatFsInit()
{
/* If there is SDCard, Initialize SDcard and Fatfs */
FRESULT error;
static const TCHAR driverNumberBuffer[3U] = {SDDISK + '0', ':', '/'};
static const TCHAR recordpathBuffer[] = DEMO_RECORD_PATH;
PRINTF("\r\nPlease insert a card into board.\r\n");
if (sdcardWaitCardInsert() != kStatus_Success)
{
return -1;
}
error = f_mount(&g_fileSystem, driverNumberBuffer, 1U);
if (error == FR_OK)
{
PRINTF("Mount volume Successfully.\r\n");
}
else if (error == FR_NO_FILESYSTEM)
{
#if FF_USE_MKFS
PRINTF("\r\nMake file system......The time may be long if the card capacity is big.\r\n");
if (f_mkfs(driverNumberBuffer, 0, work, sizeof work) != FR_OK)
{
PRINTF("Make file system failed.\r\n");
return -1;
}
#else
PRINTF("No file system detected, Please check.\r\n");
return -1;
#endif /* FF_USE_MKFS */
}
else
{
PRINTF("Mount volume failed.\r\n");
return -1;
}
#if (FF_FS_RPATH >= 2U)
error = f_chdrive((char const *)&driverNumberBuffer[0U]);
if (error)
{
PRINTF("Change drive failed.\r\n");
return -1;
}
#endif
PRINTF("\r\nCreate directory......\r\n");
error = f_mkdir((char const *)&recordpathBuffer[0U]);
if (error)
{
if (error == FR_EXIST)
{
PRINTF("Directory exists.\r\n");
}
else
{
PRINTF("Make directory failed.\r\n");
return -1;
}
}
return 0;
}
#endif /* DEMO_SDCARD */
/*!
* @brief Main function
*/
int main(void)
{
edma_config_t dmaConfig = {0};
char input = '1';
uint8_t userItem = 1U;
BOARD_ConfigMPU();
BOARD_InitBootPins();
BOARD_InitBootClocks();
CLOCK_InitAudioPll(&audioPllConfig);
BOARD_InitDebugConsole();
/*Clock setting for LPI2C*/
CLOCK_SetMux(kCLOCK_Lpi2cMux, DEMO_LPI2C_CLOCK_SOURCE_SELECT);
CLOCK_SetDiv(kCLOCK_Lpi2cDiv, DEMO_LPI2C_CLOCK_SOURCE_DIVIDER);
/*Clock setting for SAI1*/
CLOCK_SetMux(kCLOCK_Sai1Mux, DEMO_SAI1_CLOCK_SOURCE_SELECT);
CLOCK_SetDiv(kCLOCK_Sai1PreDiv, DEMO_SAI1_CLOCK_SOURCE_PRE_DIVIDER);
CLOCK_SetDiv(kCLOCK_Sai1Div, DEMO_SAI1_CLOCK_SOURCE_DIVIDER);
/*Enable MCLK clock*/
BOARD_EnableSaiMclkOutput(true);
/* Init DMAMUX */
DMAMUX_Init(DEMO_DMAMUX);
DMAMUX_SetSource(DEMO_DMAMUX, DEMO_TX_EDMA_CHANNEL, (uint8_t)DEMO_SAI_TX_SOURCE);
DMAMUX_EnableChannel(DEMO_DMAMUX, DEMO_TX_EDMA_CHANNEL);
DMAMUX_SetSource(DEMO_DMAMUX, DEMO_RX_EDMA_CHANNEL, (uint8_t)DEMO_SAI_RX_SOURCE);
DMAMUX_EnableChannel(DEMO_DMAMUX, DEMO_RX_EDMA_CHANNEL);
PRINTF("SAI Demo started!\n\r");
/* Create EDMA handle */
/*
* dmaConfig.enableRoundRobinArbitration = false;
* dmaConfig.enableHaltOnError = true;
* dmaConfig.enableContinuousLinkMode = false;
* dmaConfig.enableDebugMode = false;
*/
EDMA_GetDefaultConfig(&dmaConfig);
EDMA_Init(DEMO_DMA, &dmaConfig);
EDMA_CreateHandle(&dmaTxHandle, DEMO_DMA, DEMO_TX_EDMA_CHANNEL);
EDMA_CreateHandle(&dmaRxHandle, DEMO_DMA, DEMO_RX_EDMA_CHANNEL);
#if defined(FSL_FEATURE_EDMA_HAS_CHANNEL_MUX) && FSL_FEATURE_EDMA_HAS_CHANNEL_MUX
EDMA_SetChannelMux(DEMO_DMA, DEMO_TX_EDMA_CHANNEL, DEMO_SAI_TX_EDMA_CHANNEL);
EDMA_SetChannelMux(DEMO_DMA, DEMO_RX_EDMA_CHANNEL, DEMO_SAI_RX_EDMA_CHANNEL);
#endif
/* SAI init */
SAI_Init(DEMO_SAI);
SAI_TransferTxCreateHandleEDMA(DEMO_SAI, &txHandle, txCallback, NULL, &dmaTxHandle);
SAI_TransferRxCreateHandleEDMA(DEMO_SAI, &rxHandle, rxCallback, NULL, &dmaRxHandle);
/* I2S mode configurations */
SAI_GetClassicI2SConfig(&saiConfig, DEMO_AUDIO_BIT_WIDTH, kSAI_Stereo, 1U << DEMO_SAI_CHANNEL);
saiConfig.syncMode = DEMO_SAI_TX_SYNC_MODE;
saiConfig.masterSlave = DEMO_SAI_MASTER_SLAVE;
SAI_TransferTxSetConfigEDMA(DEMO_SAI, &txHandle, &saiConfig);
saiConfig.syncMode = DEMO_SAI_RX_SYNC_MODE;
SAI_TransferRxSetConfigEDMA(DEMO_SAI, &rxHandle, &saiConfig);
/* set bit clock divider */
SAI_TxSetBitClockRate(DEMO_SAI, DEMO_AUDIO_MASTER_CLOCK, DEMO_AUDIO_SAMPLE_RATE, DEMO_AUDIO_BIT_WIDTH,
DEMO_AUDIO_DATA_CHANNEL);
SAI_RxSetBitClockRate(DEMO_SAI, DEMO_AUDIO_MASTER_CLOCK, DEMO_AUDIO_SAMPLE_RATE, DEMO_AUDIO_BIT_WIDTH,
DEMO_AUDIO_DATA_CHANNEL);
/* master clock configurations */
BOARD_MASTER_CLOCK_CONFIG();
/* Use default setting to init codec */
if (CODEC_Init(&codecHandle, &boardCodecConfig) != kStatus_Success)
{
assert(false);
}
if (CODEC_SetVolume(&codecHandle, kCODEC_PlayChannelHeadphoneLeft | kCODEC_PlayChannelHeadphoneRight,
DEMO_CODEC_VOLUME) != kStatus_Success)
{
assert(false);
}
/* Enable interrupt to handle FIFO error */
SAI_TxEnableInterrupts(DEMO_SAI, kSAI_FIFOErrorInterruptEnable);
SAI_RxEnableInterrupts(DEMO_SAI, kSAI_FIFOErrorInterruptEnable);
EnableIRQ(DEMO_SAI_TX_IRQ);
EnableIRQ(DEMO_SAI_RX_IRQ);
#if defined DEMO_SDCARD
/* Init SDcard and FatFs */
if (SD_FatFsInit() != 0)
{
return -1;
}
#endif /* DEMO_SDCARD */
PRINTF("\n\rPlease choose the option :\r\n");
while (1)
{
PRINTF("\r%d. Record and playback at same time\r\n", userItem++);
PRINTF("\r%d. Playback sine wave\r\n", userItem++);
#if defined DEMO_SDCARD
PRINTF("\r%d. Record to SDcard, after record playback it\r\n", userItem++);
#endif /* DEMO_SDCARD */
#if defined DIG_MIC
PRINTF("\r%d. Record using digital mic and playback at the same time\r\n", userItem++);
#endif
PRINTF("\r%d. Quit\r\n", userItem);
input = GETCHAR();
PUTCHAR(input);
PRINTF("\r\n");
if (input == (userItem + 48U))
{
break;
}
switch (input)
{
case '1':
#if defined DIG_MIC
/* Set the audio input source to AUX */
DA7212_ChangeInput((da7212_handle_t *)((uint32_t)(codecHandle.codecDevHandle)), kDA7212_Input_AUX);
#endif
#if defined(BOARD_CONFIGCODEC_FOR_RECORD_PLAYBACK)
BOARD_CONFIGCODEC_FOR_RECORD_PLAYBACK();
if (CODEC_Init(&codecHandle, &boardCodecConfig) != kStatus_Success)
{
assert(false);
}
#endif
RecordPlayback(DEMO_SAI, 30);
break;
case '2':
#if defined(BOARD_CONFIGCODEC_FOR_PLAYBACK)
BOARD_CONFIGCODEC_FOR_PLAYBACK();
if (CODEC_Init(&codecHandle, &boardCodecConfig) != kStatus_Success)
{
assert(false);
}
#endif
PlaybackSine(DEMO_SAI, 250, 5);
break;
#if defined DEMO_SDCARD
case '3':
RecordSDCard(DEMO_SAI, 5);
break;
#endif
#if defined DIG_MIC
case userItem - 1U + 48U:
/* Set the audio input source to DMIC */
DA7212_ChangeInput((da7212_handle_t *)((uint32_t)(codecHandle.codecDevHandle)), kDA7212_Input_MIC1_Dig);
RecordPlayback(DEMO_SAI, 30);
break;
#endif
default:
PRINTF("\rInvallid Input Parameter, please re-enter\r\n");
break;
}
userItem = 1U;
}
if (CODEC_Deinit(&codecHandle) != kStatus_Success)
{
assert(false);
}
PRINTF("\n\r SAI demo finished!\n\r ");
while (1)
{
}
}
void SAI_UserTxIRQHandler(void)
{
/* Clear the FEF flag */
SAI_TxClearStatusFlags(DEMO_SAI, kSAI_FIFOErrorFlag);
SAI_TxSoftwareReset(DEMO_SAI, kSAI_ResetTypeFIFO);
SDK_ISR_EXIT_BARRIER;
}
void SAI_UserRxIRQHandler(void)
{
SAI_RxClearStatusFlags(DEMO_SAI, kSAI_FIFOErrorFlag);
SAI_RxSoftwareReset(DEMO_SAI, kSAI_ResetTypeFIFO);
SDK_ISR_EXIT_BARRIER;
}
void SAI_UserIRQHandler(void)
{
if (SAI_TxGetStatusFlag(DEMO_SAI) & kSAI_FIFOErrorFlag)
{
SAI_UserTxIRQHandler();
}
if (SAI_RxGetStatusFlag(DEMO_SAI) & kSAI_FIFOErrorFlag)
{
SAI_UserRxIRQHandler();
}
SDK_ISR_EXIT_BARRIER;
}