/* * Copyright 2020-2021 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ /* Board includes */ #include "pin_mux.h" #include "board.h" #include "main.h" #include "cmd.h" #include "fsl_sd.h" #include "ff.h" #include "diskio.h" #include "fsl_sd_disk.h" #include "sdmmc_config.h" #include "fsl_debug_console.h" #include #include "fsl_sysctl.h" #include "fsl_codec_common.h" #include "fsl_wm8904.h" #include "fsl_codec_adapter.h" #include "fsl_power.h" /******************************************************************************* * Definitions ******************************************************************************/ #include "app_definitions.h" #define SHELL_TASK_STACK_SIZE (1024) #define SDCARD_TASK_STACK_SIZE (512) /******************************************************************************* * Prototypes ******************************************************************************/ int BOARD_CODEC_Init(void); static void APP_SDCARD_DetectCallBack(bool isInserted, void *userData); /******************************************************************************* * Variables ******************************************************************************/ codec_handle_t codecHandle = {0}; wm8904_config_t wm8904Config = { .i2cConfig = {.codecI2CInstance = BOARD_CODEC_I2C_INSTANCE, .codecI2CSourceClock = BOARD_CODEC_I2C_CLOCK_FREQ}, .recordSource = kWM8904_RecordSourceLineInput, .recordChannelLeft = kWM8904_RecordChannelLeft2, .recordChannelRight = kWM8904_RecordChannelRight2, .playSource = kWM8904_PlaySourceDAC, .slaveAddress = WM8904_I2C_ADDRESS, .protocol = kWM8904_ProtocolI2S, .format = {.sampleRate = kWM8904_SampleRate48kHz, .bitWidth = kWM8904_BitWidth16}, .mclk_HZ = DEMO_I2S_MASTER_CLOCK_FREQUENCY, .master = false, }; codec_config_t boardCodecConfig = {.codecDevType = kCODEC_WM8904, .codecDevConfig = &wm8904Config}; static app_handle_t app; /******************************************************************************* * Code ******************************************************************************/ int BOARD_CODEC_Init(void) { CODEC_Init(&codecHandle, &boardCodecConfig); /* Invert the DAC data in order to output signal with correct polarity - set DACL_DATINV and DACR_DATINV = 1 */ WM8904_WriteRegister((wm8904_handle_t *)codecHandle.codecDevHandle, WM8904_AUDIO_IF_0, 0x1850); /* Initial volume kept low for hearing safety. */ CODEC_SetVolume(&codecHandle, kCODEC_PlayChannelHeadphoneLeft | kCODEC_PlayChannelHeadphoneRight, DEMO_VOLUME); return 0; } void BOARD_InitSysctrl(void) { SYSCTL_Init(SYSCTL); /* select signal source for share set */ SYSCTL_SetShareSignalSrc(SYSCTL, kSYSCTL_ShareSet0, kSYSCTL_SharedCtrlSignalSCK, kSYSCTL_Flexcomm7); SYSCTL_SetShareSignalSrc(SYSCTL, kSYSCTL_ShareSet0, kSYSCTL_SharedCtrlSignalWS, kSYSCTL_Flexcomm7); /* select share set for special flexcomm signal */ SYSCTL_SetShareSet(SYSCTL, kSYSCTL_Flexcomm7, kSYSCTL_FlexcommSignalSCK, kSYSCTL_ShareSet0); SYSCTL_SetShareSet(SYSCTL, kSYSCTL_Flexcomm7, kSYSCTL_FlexcommSignalWS, kSYSCTL_ShareSet0); SYSCTL_SetShareSet(SYSCTL, kSYSCTL_Flexcomm6, kSYSCTL_FlexcommSignalSCK, kSYSCTL_ShareSet0); SYSCTL_SetShareSet(SYSCTL, kSYSCTL_Flexcomm6, kSYSCTL_FlexcommSignalWS, kSYSCTL_ShareSet0); } static void APP_SDCARD_DetectCallBack(bool isInserted, void *userData) { app_handle_t *app = (app_handle_t *)userData; app->sdcardInserted = isInserted; xSemaphoreGiveFromISR(app->sdcardSem, NULL); } void APP_SDCARD_Task(void *param) { const TCHAR driverNumberBuffer[3U] = {SDDISK + '0', ':', '/'}; FRESULT error; app_handle_t *app = (app_handle_t *)param; app->sdcardSem = xSemaphoreCreateBinary(); BOARD_SD_Config(&g_sd, APP_SDCARD_DetectCallBack, BOARD_SDMMC_SD_HOST_IRQ_PRIORITY, app); PRINTF("[APP_SDCARD_Task] start\r\n"); /* SD host init function */ if (SD_HostInit(&g_sd) != kStatus_Success) { PRINTF("[APP_SDCARD_Task] SD host init failed.\r\n"); vTaskSuspend(NULL); } /* Small delay for SD card detection logic to process */ vTaskDelay(100 / portTICK_PERIOD_MS); while (1) { /* Block waiting for SDcard detect interrupt */ xSemaphoreTake(app->sdcardSem, portMAX_DELAY); if (app->sdcardInserted != app->sdcardInsertedPrev) { app->sdcardInsertedPrev = app->sdcardInserted; SD_SetCardPower(&g_sd, false); if (app->sdcardInserted) { /* power on the card */ SD_SetCardPower(&g_sd, true); if (f_mount(&app->fileSystem, driverNumberBuffer, 0U)) { PRINTF("[APP_SDCARD_Task] Mount volume failed.\r\n"); continue; } #if (FF_FS_RPATH >= 2U) error = f_chdrive((char const *)&driverNumberBuffer[0U]); if (error) { PRINTF("[APP_SDCARD_Task] Change drive failed.\r\n"); continue; } #endif PRINTF("[APP_SDCARD_Task] SD card drive mounted\r\n"); xSemaphoreGive(app->sdcardSem); } } } } void APP_Shell_Task(void *param) { PRINTF("[APP_Shell_Task] start\r\n"); /* Handle shell commands. */ shellCmd(); while (1) ; ; } int main(void) { int ret; /* set BOD VBAT level to 1.65V */ POWER_SetBodVbatLevel(kPOWER_BodVbatLevel1650mv, kPOWER_BodHystLevel50mv, false); CLOCK_EnableClock(kCLOCK_InputMux); CLOCK_EnableClock(kCLOCK_Iocon); CLOCK_EnableClock(kCLOCK_Gpio0); CLOCK_EnableClock(kCLOCK_Gpio1); /* USART0 clock */ CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH); /* I2C clock */ CLOCK_AttachClk(kFRO12M_to_FLEXCOMM4); PMC->PDRUNCFGCLR0 |= PMC_PDRUNCFG0_PDEN_XTAL32M_MASK; /*!< Ensure XTAL16M is on */ PMC->PDRUNCFGCLR0 |= PMC_PDRUNCFG0_PDEN_LDOXO32M_MASK; /*!< Ensure XTAL16M is on */ SYSCON->CLOCK_CTRL |= SYSCON_CLOCK_CTRL_CLKIN_ENA_MASK; /*!< Ensure CLK_IN is on */ ANACTRL->XO32M_CTRL |= ANACTRL_XO32M_CTRL_ENABLE_SYSTEM_CLK_OUT_MASK; /*!< Switch PLL0 clock source selector to XTAL16M */ CLOCK_AttachClk(kEXT_CLK_to_PLL0); const pll_setup_t pll0Setup = { .pllctrl = SYSCON_PLL0CTRL_CLKEN_MASK | SYSCON_PLL0CTRL_SELI(8U) | SYSCON_PLL0CTRL_SELP(31U), .pllndec = SYSCON_PLL0NDEC_NDIV(125U), .pllpdec = SYSCON_PLL0PDEC_PDIV(8U), .pllsscg = {0x0U, (SYSCON_PLL0SSCG1_MDIV_EXT(3072U) | SYSCON_PLL0SSCG1_SEL_EXT_MASK)}, .pllRate = 24576000U, .flags = PLL_SETUPFLAG_WAITLOCK}; /*!< Configure PLL to the desired values */ CLOCK_SetPLL0Freq(&pll0Setup); CLOCK_SetClkDiv(kCLOCK_DivPll0Clk, 0U, true); CLOCK_SetClkDiv(kCLOCK_DivPll0Clk, 1U, false); /* I2S clocks */ CLOCK_AttachClk(kPLL0_DIV_to_FLEXCOMM6); CLOCK_AttachClk(kPLL0_DIV_to_FLEXCOMM7); /* Attach PLL clock to MCLK for I2S, no divider */ CLOCK_AttachClk(kPLL0_to_MCLK); SYSCON->MCLKDIV = SYSCON_MCLKDIV_DIV(0U); SYSCON->MCLKIO = 1U; /* reset FLEXCOMM for I2C */ RESET_PeripheralReset(kFC4_RST_SHIFT_RSTn); /* reset FLEXCOMM for DMA0 */ RESET_PeripheralReset(kDMA0_RST_SHIFT_RSTn); /* reset FLEXCOMM for I2S */ RESET_PeripheralReset(kFC6_RST_SHIFT_RSTn); RESET_PeripheralReset(kFC7_RST_SHIFT_RSTn); /* reset NVIC for FLEXCOMM6 and FLEXCOMM7 */ NVIC_ClearPendingIRQ(FLEXCOMM6_IRQn); NVIC_ClearPendingIRQ(FLEXCOMM7_IRQn); /* Enable interrupts for I2S */ EnableIRQ(FLEXCOMM6_IRQn); EnableIRQ(FLEXCOMM7_IRQn); /* Initialize the rest */ BOARD_InitPins(); BOARD_BootClockPLL1_150M(); BOARD_InitDebugConsole(); BOARD_InitSysctrl(); PRINTF("\r\n"); PRINTF("*******************************\r\n"); PRINTF("Maestro audio record demo start\r\n"); PRINTF("*******************************\r\n"); PRINTF("\r\n"); ret = BOARD_CODEC_Init(); if (ret) { PRINTF("CODEC_Init failed\r\n"); return -1; } if (xTaskCreate(APP_SDCARD_Task, "SDCard Task", SDCARD_TASK_STACK_SIZE, &app, configMAX_PRIORITIES - 4, NULL) != pdPASS) { PRINTF("\r\nFailed to create application task\r\n"); while (1) ; } /* Set shell command task priority = 1 */ if (xTaskCreate(APP_Shell_Task, "Shell Task", SHELL_TASK_STACK_SIZE, &app, configMAX_PRIORITIES - 5, &app.shell_task_handle) != pdPASS) { PRINTF("\r\nFailed to create application task\r\n"); while (1) ; } /* Run RTOS */ vTaskStartScheduler(); /* Should not reach this statement */ return 0; } /** * @brief Loop forever if stack overflow is detected. * * If configCHECK_FOR_STACK_OVERFLOW is set to 1, * this hook provides a location for applications to * define a response to a stack overflow. * * Use this hook to help identify that a stack overflow * has occurred. * */ void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) { portDISABLE_INTERRUPTS(); /* Loop forever */ for (;;) ; } /** * @brief Warn user if pvPortMalloc fails. * * Called if a call to pvPortMalloc() fails because there is insufficient * free memory available in the FreeRTOS heap. pvPortMalloc() is called * internally by FreeRTOS API functions that create tasks, queues, software * timers, and semaphores. The size of the FreeRTOS heap is set by the * configTOTAL_HEAP_SIZE configuration constant in FreeRTOSConfig.h. * */ void vApplicationMallocFailedHook() { PRINTF(("\r\nERROR: Malloc failed to allocate memory\r\n")); /* Loop forever */ for (;;) ; }