diff --git a/BSP/audio.h b/BSP/audio.h new file mode 100644 index 0000000..8b93673 --- /dev/null +++ b/BSP/audio.h @@ -0,0 +1,122 @@ +/** + ****************************************************************************** + * @file audio.h + * @author MCD Application Team + * @version V4.0.1 + * @date 21-July-2015 + * @brief This header file contains the common defines and functions prototypes + * for the Audio driver. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2015 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __AUDIO_H +#define __AUDIO_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include + +/** @addtogroup BSP + * @{ + */ + +/** @addtogroup Components + * @{ + */ + +/** @addtogroup AUDIO + * @{ + */ + +/** @defgroup AUDIO_Exported_Constants + * @{ + */ + +/* Codec audio Standards */ +#define CODEC_STANDARD 0x04 +#define I2S_STANDARD I2S_STANDARD_PHILIPS + +/** + * @} + */ + +/** @defgroup AUDIO_Exported_Types + * @{ + */ + +/** @defgroup AUDIO_Driver_structure Audio Driver structure + * @{ + */ +typedef struct +{ + uint32_t (*Init)(uint16_t, uint16_t, uint8_t, uint32_t); + void (*DeInit)(void); + uint32_t (*ReadID)(uint16_t); + uint32_t (*Play)(uint16_t, uint16_t*, uint16_t); + uint32_t (*Pause)(uint16_t); + uint32_t (*Resume)(uint16_t); + uint32_t (*Stop)(uint16_t, uint32_t); + uint32_t (*SetFrequency)(uint16_t, uint32_t); + uint32_t (*SetVolume)(uint16_t, uint8_t); + uint32_t (*SetMute)(uint16_t, uint32_t); + uint32_t (*SetOutputMode)(uint16_t, uint8_t); + uint32_t (*Reset)(uint16_t); +}AUDIO_DrvTypeDef; +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __AUDIO_H */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/BSP/stm32f769i_discovery.c b/BSP/stm32f769i_discovery.c new file mode 100644 index 0000000..44ee430 --- /dev/null +++ b/BSP/stm32f769i_discovery.c @@ -0,0 +1,800 @@ +/** + ****************************************************************************** + * @file stm32f769i_discovery.c + * @author MCD Application Team + * @brief This file provides a set of firmware functions to manage LEDs, + * push-buttons, external SDRAM, external QSPI Flash, RF EEPROM, + * available on STM32F769I-Discovery board (MB1225) from + * STMicroelectronics. + ****************************************************************************** + * @attention + * + * Copyright (c) 2016 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Dependencies +- stm32f7xx_hal_cortex.c +- stm32f7xx_hal_gpio.c +- stm32f7xx_hal_uart.c +- stm32f7xx_hal_i2c.c +EndDependencies */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f769i_discovery.h" + +/** @addtogroup BSP + * @{ + */ + +/** @addtogroup STM32F769I_DISCOVERY + * @{ + */ + +/** @defgroup STM32F769I_DISCOVERY_LOW_LEVEL STM32F769I_DISCOVERY LOW LEVEL + * @{ + */ + +/** @defgroup STM32F769I_DISCOVERY_LOW_LEVEL_Private_TypesDefinitions STM32F769I Discovery Low Level Private Typedef + * @{ + */ +/** + * @} + */ + +/** @defgroup STM32F769I_DISCOVERY_LOW_LEVEL_Private_Defines LOW_LEVEL Private Defines + * @{ + */ +/** + * @brief STM32F769I Discovery BSP Driver version number V2.0.2 + */ +#define __STM32F769I_DISCOVERY_BSP_VERSION_MAIN (0x02) /*!< [31:24] main version */ +#define __STM32F769I_DISCOVERY_BSP_VERSION_SUB1 (0x00) /*!< [23:16] sub1 version */ +#define __STM32F769I_DISCOVERY_BSP_VERSION_SUB2 (0x02) /*!< [15:8] sub2 version */ +#define __STM32F769I_DISCOVERY_BSP_VERSION_RC (0x00) /*!< [7:0] release candidate */ +#define __STM32F769I_DISCOVERY_BSP_VERSION ((__STM32F769I_DISCOVERY_BSP_VERSION_MAIN << 24)\ + |(__STM32F769I_DISCOVERY_BSP_VERSION_SUB1 << 16)\ + |(__STM32F769I_DISCOVERY_BSP_VERSION_SUB2 << 8 )\ + |(__STM32F769I_DISCOVERY_BSP_VERSION_RC)) +/** + * @} + */ + +/** @defgroup STM32F769I_DISCOVERY_LOW_LEVEL_Private_Macros LOW_LEVEL Private Macros + * @{ + */ +/** + * @} + */ + +/** @defgroup STM32F769I_DISCOVERY_LOW_LEVEL_Private_Variables LOW_LEVEL Private Variables + * @{ + */ +uint32_t GPIO_PIN[BSP_LEDn] = {BSP_LED1_PIN, + BSP_LED2_PIN}; + +GPIO_TypeDef* GPIO_PORT[BSP_LEDn] = {BSP_LED1_GPIO_PORT, + BSP_LED2_GPIO_PORT}; + +GPIO_TypeDef* BUTTON_PORT[BUTTONn] = {WAKEUP_BUTTON_GPIO_PORT }; + +const uint16_t BUTTON_PIN[BUTTONn] = {WAKEUP_BUTTON_PIN }; + +const uint16_t BUTTON_IRQn[BUTTONn] = {WAKEUP_BUTTON_EXTI_IRQn }; + + +static I2C_HandleTypeDef hI2cAudioHandler = {0}; +static I2C_HandleTypeDef hI2cExtHandler = {0}; + +/** + * @} + */ + +/** @defgroup STM32F769I_DISCOVERY_LOW_LEVEL_Private_FunctionPrototypes LOW_LEVEL Private FunctionPrototypes + * @{ + */ +static void I2Cx_MspInit(I2C_HandleTypeDef *i2c_handler); +static void I2Cx_Init(I2C_HandleTypeDef *i2c_handler); + +static HAL_StatusTypeDef I2Cx_ReadMultiple(I2C_HandleTypeDef *i2c_handler, uint8_t Addr, uint16_t Reg, uint16_t MemAddSize, uint8_t *Buffer, uint16_t Length); +static HAL_StatusTypeDef I2Cx_WriteMultiple(I2C_HandleTypeDef *i2c_handler, uint8_t Addr, uint16_t Reg, uint16_t MemAddSize, uint8_t *Buffer, uint16_t Length); +static HAL_StatusTypeDef I2Cx_IsDeviceReady(I2C_HandleTypeDef *i2c_handler, uint16_t DevAddress, uint32_t Trials); +static void I2Cx_Error(I2C_HandleTypeDef *i2c_handler, uint8_t Addr); + +/* AUDIO IO functions */ +void AUDIO_IO_Init(void); +void AUDIO_IO_DeInit(void); +void AUDIO_IO_Write(uint8_t Addr, uint16_t Reg, uint16_t Value); +uint16_t AUDIO_IO_Read(uint8_t Addr, uint16_t Reg); +void AUDIO_IO_Delay(uint32_t Delay); + +/* HDMI IO functions */ +void HDMI_IO_Init(void); +void HDMI_IO_Delay(uint32_t Delay); +void HDMI_IO_Write(uint8_t Addr, uint8_t Reg, uint8_t Value); +uint8_t HDMI_IO_Read(uint8_t Addr, uint8_t Reg); + +/* I2C EEPROM IO function */ +void EEPROM_IO_Init(void); +HAL_StatusTypeDef EEPROM_IO_WriteData(uint16_t DevAddress, uint16_t MemAddress, uint8_t* pBuffer, uint32_t BufferSize); +HAL_StatusTypeDef EEPROM_IO_ReadData(uint16_t DevAddress, uint16_t MemAddress, uint8_t* pBuffer, uint32_t BufferSize); +HAL_StatusTypeDef EEPROM_IO_IsDeviceReady(uint16_t DevAddress, uint32_t Trials); + +/* TouchScreen (TS) IO functions */ +void TS_IO_Init(void); +void TS_IO_Write(uint8_t Addr, uint8_t Reg, uint8_t Value); +uint8_t TS_IO_Read(uint8_t Addr, uint8_t Reg); +uint16_t TS_IO_ReadMultiple(uint8_t Addr, uint8_t Reg, uint8_t *Buffer, uint16_t Length); +void TS_IO_WriteMultiple(uint8_t Addr, uint8_t Reg, uint8_t *Buffer, uint16_t Length); +void TS_IO_Delay(uint32_t Delay); + +/* LCD Display IO functions */ +void OTM8009A_IO_Delay(uint32_t Delay); +/** + * @} + */ + +/** @defgroup STM32F769I_DISCOVERY_BSP_Public_Functions BSP Public Functions + * @{ + */ + + /** + * @brief This method returns the STM32F769I Discovery BSP Driver revision + * @retval version: 0xXYZR (8bits for each decimal, R for RC) + */ +uint32_t BSP_GetVersion(void) +{ + return __STM32F769I_DISCOVERY_BSP_VERSION; +} + +/** + * @brief Configures LED GPIO. + * @param Led: LED to be configured. + * This parameter can be one of the following values: + * @arg LED1 + * @arg LED2 + * @retval None + */ +void BSP_LED_Init(Led_TypeDef Led) +{ + GPIO_InitTypeDef gpio_init_structure; + + BSP_LEDx_GPIO_CLK_ENABLE(); + /* Configure the GPIO_LED pin */ + gpio_init_structure.Pin = GPIO_PIN[Led]; + gpio_init_structure.Mode = GPIO_MODE_OUTPUT_PP; + gpio_init_structure.Pull = GPIO_PULLUP; + gpio_init_structure.Speed = GPIO_SPEED_HIGH; + + HAL_GPIO_Init(GPIO_PORT[Led], &gpio_init_structure); + +} + + +/** + * @brief DeInit LEDs. + * @param Led: LED to be configured. + * This parameter can be one of the following values: + * @arg LED1 + * @arg LED2 + * @note Led DeInit does not disable the GPIO clock + * @retval None + */ +void BSP_LED_DeInit(Led_TypeDef Led) +{ + GPIO_InitTypeDef gpio_init_structure; + + /* DeInit the GPIO_LED pin */ + gpio_init_structure.Pin = GPIO_PIN[Led]; + + /* Turn off LED */ + HAL_GPIO_WritePin(GPIO_PORT[Led], GPIO_PIN[Led], GPIO_PIN_RESET); + HAL_GPIO_DeInit(GPIO_PORT[Led], gpio_init_structure.Pin); +} + +/** + * @brief Turns selected LED On. + * @param Led: LED to be set on + * This parameter can be one of the following values: + * @arg LED1 + * @arg LED2 + * @retval None + */ +void BSP_LED_On(Led_TypeDef Led) +{ + HAL_GPIO_WritePin(GPIO_PORT[Led], GPIO_PIN[Led], GPIO_PIN_SET); +} + +/** + * @brief Turns selected LED Off. + * @param Led: LED to be set off + * This parameter can be one of the following values: + * @arg LED1 + * @arg LED2 + * @retval None + */ +void BSP_LED_Off(Led_TypeDef Led) +{ + HAL_GPIO_WritePin(GPIO_PORT[Led], GPIO_PIN[Led], GPIO_PIN_RESET); +} + +/** + * @brief Toggles the selected LED. + * @param Led: LED to be toggled + * This parameter can be one of the following values: + * @arg LED1 + * @arg LED2 + * @retval None + */ +void BSP_LED_Toggle(Led_TypeDef Led) +{ + HAL_GPIO_TogglePin(GPIO_PORT[Led], GPIO_PIN[Led]); +} + +/** + * @brief Configures button GPIO and EXTI Line. + * @param Button: Button to be configured + * This parameter can be one of the following values: + * @arg BUTTON_WAKEUP: Wakeup Push Button + * @arg BUTTON_USER: User Push Button + * @param Button_Mode: Button mode + * This parameter can be one of the following values: + * @arg BUTTON_MODE_GPIO: Button will be used as simple IO + * @arg BUTTON_MODE_EXTI: Button will be connected to EXTI line + * with interrupt generation capability + * @retval None + */ +void BSP_PB_Init(Button_TypeDef Button, ButtonMode_TypeDef Button_Mode) +{ + GPIO_InitTypeDef gpio_init_structure; + + /* Enable the BUTTON clock */ + BUTTON_GPIO_CLK_ENABLE(); + + if(Button_Mode == BUTTON_MODE_GPIO) + { + /* Configure Button pin as input */ + gpio_init_structure.Pin = BUTTON_PIN[Button]; + gpio_init_structure.Mode = GPIO_MODE_INPUT; + gpio_init_structure.Pull = GPIO_NOPULL; + gpio_init_structure.Speed = GPIO_SPEED_FAST; + HAL_GPIO_Init(BUTTON_PORT[Button], &gpio_init_structure); + } + + if(Button_Mode == BUTTON_MODE_EXTI) + { + /* Configure Button pin as input with External interrupt */ + gpio_init_structure.Pin = BUTTON_PIN[Button]; + gpio_init_structure.Pull = GPIO_NOPULL; + gpio_init_structure.Speed = GPIO_SPEED_FAST; + + gpio_init_structure.Mode = GPIO_MODE_IT_RISING; + + HAL_GPIO_Init(BUTTON_PORT[Button], &gpio_init_structure); + + /* Enable and set Button EXTI Interrupt to the lowest priority */ + HAL_NVIC_SetPriority((IRQn_Type)(BUTTON_IRQn[Button]), 0x0F, 0x00); + HAL_NVIC_EnableIRQ((IRQn_Type)(BUTTON_IRQn[Button])); + } +} + +/** + * @brief Push Button DeInit. + * @param Button: Button to be configured + * This parameter can be one of the following values: + * @arg BUTTON_WAKEUP: Wakeup Push Button + * @arg BUTTON_USER: User Push Button + * @note PB DeInit does not disable the GPIO clock + * @retval None + */ +void BSP_PB_DeInit(Button_TypeDef Button) +{ + GPIO_InitTypeDef gpio_init_structure; + + gpio_init_structure.Pin = BUTTON_PIN[Button]; + HAL_NVIC_DisableIRQ((IRQn_Type)(BUTTON_IRQn[Button])); + HAL_GPIO_DeInit(BUTTON_PORT[Button], gpio_init_structure.Pin); +} + + +/** + * @brief Returns the selected button state. + * @param Button: Button to be checked + * This parameter can be one of the following values: + * @arg BUTTON_WAKEUP: Wakeup Push Button + * @arg BUTTON_USER: User Push Button + * @retval The Button GPIO pin value + */ +uint32_t BSP_PB_GetState(Button_TypeDef Button) +{ + return HAL_GPIO_ReadPin(BUTTON_PORT[Button], BUTTON_PIN[Button]); +} + +/** + * @} + */ + +/** @defgroup STM32F769I_DISCOVERY_LOW_LEVEL_Private_Functions STM32F769I_DISCOVERY_LOW_LEVEL Private Functions + * @{ + */ + + +/******************************************************************************* + BUS OPERATIONS +*******************************************************************************/ + +/******************************* I2C Routines *********************************/ +/** + * @brief Initializes I2C MSP. + * @param i2c_handler : I2C handler + * @retval None + */ +static void I2Cx_MspInit(I2C_HandleTypeDef *i2c_handler) +{ + GPIO_InitTypeDef gpio_init_structure; + + if (i2c_handler == (I2C_HandleTypeDef*)(&hI2cAudioHandler)) + { + /*** Configure the GPIOs ***/ + /* Enable GPIO clock */ + DISCOVERY_AUDIO_I2Cx_SCL_GPIO_CLK_ENABLE(); + DISCOVERY_AUDIO_I2Cx_SDA_GPIO_CLK_ENABLE(); + /* Configure I2C Tx as alternate function */ + gpio_init_structure.Pin = DISCOVERY_AUDIO_I2Cx_SCL_PIN; + gpio_init_structure.Mode = GPIO_MODE_AF_OD; + gpio_init_structure.Pull = GPIO_NOPULL; + gpio_init_structure.Speed = GPIO_SPEED_FAST; + gpio_init_structure.Alternate = DISCOVERY_AUDIO_I2Cx_SCL_AF; + HAL_GPIO_Init(DISCOVERY_AUDIO_I2Cx_SCL_GPIO_PORT, &gpio_init_structure); + + /* Configure I2C Rx as alternate function */ + gpio_init_structure.Pin = DISCOVERY_AUDIO_I2Cx_SDA_PIN; + gpio_init_structure.Alternate = DISCOVERY_AUDIO_I2Cx_SDA_AF; + HAL_GPIO_Init(DISCOVERY_AUDIO_I2Cx_SDA_GPIO_PORT, &gpio_init_structure); + + /*** Configure the I2C peripheral ***/ + /* Enable I2C clock */ + DISCOVERY_AUDIO_I2Cx_CLK_ENABLE(); + + /* Force the I2C peripheral clock reset */ + DISCOVERY_AUDIO_I2Cx_FORCE_RESET(); + + /* Release the I2C peripheral clock reset */ + DISCOVERY_AUDIO_I2Cx_RELEASE_RESET(); + + /* Enable and set I2C1 Interrupt to a lower priority */ + HAL_NVIC_SetPriority(DISCOVERY_AUDIO_I2Cx_EV_IRQn, 0x0F, 0); + HAL_NVIC_EnableIRQ(DISCOVERY_AUDIO_I2Cx_EV_IRQn); + + /* Enable and set I2C1 Interrupt to a lower priority */ + HAL_NVIC_SetPriority(DISCOVERY_AUDIO_I2Cx_ER_IRQn, 0x0F, 0); + HAL_NVIC_EnableIRQ(DISCOVERY_AUDIO_I2Cx_ER_IRQn); + + } + else + { + /*** Configure the GPIOs ***/ + /* Enable GPIO clock */ + DISCOVERY_EXT_I2Cx_SCL_SDA_GPIO_CLK_ENABLE(); + + /* Configure I2C Tx as alternate function */ + gpio_init_structure.Pin = DISCOVERY_EXT_I2Cx_SCL_PIN; + gpio_init_structure.Mode = GPIO_MODE_AF_OD; + gpio_init_structure.Pull = GPIO_NOPULL; + gpio_init_structure.Speed = GPIO_SPEED_FAST; + gpio_init_structure.Alternate = DISCOVERY_EXT_I2Cx_SCL_SDA_AF; + HAL_GPIO_Init(DISCOVERY_EXT_I2Cx_SCL_SDA_GPIO_PORT, &gpio_init_structure); + + /* Configure I2C Rx as alternate function */ + gpio_init_structure.Pin = DISCOVERY_EXT_I2Cx_SDA_PIN; + HAL_GPIO_Init(DISCOVERY_EXT_I2Cx_SCL_SDA_GPIO_PORT, &gpio_init_structure); + + /*** Configure the I2C peripheral ***/ + /* Enable I2C clock */ + DISCOVERY_EXT_I2Cx_CLK_ENABLE(); + + /* Force the I2C peripheral clock reset */ + DISCOVERY_EXT_I2Cx_FORCE_RESET(); + + /* Release the I2C peripheral clock reset */ + DISCOVERY_EXT_I2Cx_RELEASE_RESET(); + + /* Enable and set I2C1 Interrupt to a lower priority */ + HAL_NVIC_SetPriority(DISCOVERY_EXT_I2Cx_EV_IRQn, 0x0F, 0); + HAL_NVIC_EnableIRQ(DISCOVERY_EXT_I2Cx_EV_IRQn); + + /* Enable and set I2C1 Interrupt to a lower priority */ + HAL_NVIC_SetPriority(DISCOVERY_EXT_I2Cx_ER_IRQn, 0x0F, 0); + HAL_NVIC_EnableIRQ(DISCOVERY_EXT_I2Cx_ER_IRQn); + } +} + +/** + * @brief Initializes I2C HAL. + * @param i2c_handler : I2C handler + * @retval None + */ +static void I2Cx_Init(I2C_HandleTypeDef *i2c_handler) +{ + if(HAL_I2C_GetState(i2c_handler) == HAL_I2C_STATE_RESET) + { + if (i2c_handler == (I2C_HandleTypeDef*)(&hI2cAudioHandler)) + { + /* Audio and LCD I2C configuration */ + i2c_handler->Instance = DISCOVERY_AUDIO_I2Cx; + } + else + { + /* External, camera and Arduino connector I2C configuration */ + i2c_handler->Instance = DISCOVERY_EXT_I2Cx; + } + i2c_handler->Init.Timing = DISCOVERY_I2Cx_TIMING; + i2c_handler->Init.OwnAddress1 = 0; + i2c_handler->Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; + i2c_handler->Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; + i2c_handler->Init.OwnAddress2 = 0; + i2c_handler->Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; + i2c_handler->Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; + + /* Init the I2C */ + I2Cx_MspInit(i2c_handler); + HAL_I2C_Init(i2c_handler); + } +} + +/** + * @brief Reads multiple data. + * @param i2c_handler : I2C handler + * @param Addr: I2C address + * @param Reg: Reg address + * @param MemAddress: memory address + * @param Buffer: Pointer to data buffer + * @param Length: Length of the data + * @retval HAL status + */ +static HAL_StatusTypeDef I2Cx_ReadMultiple(I2C_HandleTypeDef *i2c_handler, uint8_t Addr, uint16_t Reg, uint16_t MemAddress, uint8_t *Buffer, uint16_t Length) +{ + HAL_StatusTypeDef status = HAL_OK; + + status = HAL_I2C_Mem_Read(i2c_handler, Addr, (uint16_t)Reg, MemAddress, Buffer, Length, 1000); + + /* Check the communication status */ + if(status != HAL_OK) + { + /* I2C error occured */ + I2Cx_Error(i2c_handler, Addr); + } + return status; +} + + +/** + * @brief Writes a value in a register of the device through BUS in using DMA mode. + * @param i2c_handler : I2C handler + * @param Addr: Device address on BUS Bus. + * @param Reg: The target register address to write + * @param MemAddress: memory address + * @param Buffer: The target register value to be written + * @param Length: buffer size to be written + * @retval HAL status + */ +static HAL_StatusTypeDef I2Cx_WriteMultiple(I2C_HandleTypeDef *i2c_handler, uint8_t Addr, uint16_t Reg, uint16_t MemAddress, uint8_t *Buffer, uint16_t Length) +{ + HAL_StatusTypeDef status = HAL_OK; + + status = HAL_I2C_Mem_Write(i2c_handler, Addr, (uint16_t)Reg, MemAddress, Buffer, Length, 1000); + + /* Check the communication status */ + if(status != HAL_OK) + { + /* Re-Initiaize the I2C Bus */ + I2Cx_Error(i2c_handler, Addr); + } + return status; +} + +/** + * @brief Checks if target device is ready for communication. + * @note This function is used with Memory devices + * @param i2c_handler : I2C handler + * @param DevAddress: Target device address + * @param Trials: Number of trials + * @retval HAL status + */ +static HAL_StatusTypeDef I2Cx_IsDeviceReady(I2C_HandleTypeDef *i2c_handler, uint16_t DevAddress, uint32_t Trials) +{ + return (HAL_I2C_IsDeviceReady(i2c_handler, DevAddress, Trials, 1000)); +} + +/** + * @brief Manages error callback by re-initializing I2C. + * @param i2c_handler : I2C handler + * @param Addr: I2C Address + * @retval None + */ +static void I2Cx_Error(I2C_HandleTypeDef *i2c_handler, uint8_t Addr) +{ + /* De-initialize the I2C communication bus */ + HAL_I2C_DeInit(i2c_handler); + + /* Re-Initialize the I2C communication bus */ + I2Cx_Init(i2c_handler); +} + +/** + * @} + */ + +/******************************************************************************* + LINK OPERATIONS +*******************************************************************************/ + +/********************************* LINK AUDIO *********************************/ + +/** + * @brief Initializes Audio low level. + */ +void AUDIO_IO_Init(void) +{ + I2Cx_Init(&hI2cAudioHandler); +} + +/** + * @brief DeInitializes Audio low level. + */ +void AUDIO_IO_DeInit(void) +{ + +} + +/** + * @brief Writes a single data. + * @param Addr: I2C address + * @param Reg: Reg address + * @param Value: Data to be written + * @retval None + */ +void AUDIO_IO_Write(uint8_t Addr, uint16_t Reg, uint16_t Value) +{ + uint16_t tmp = Value; + + Value = ((uint16_t)(tmp >> 8) & 0x00FF); + + Value |= ((uint16_t)(tmp << 8)& 0xFF00); + + I2Cx_WriteMultiple(&hI2cAudioHandler, Addr, Reg, I2C_MEMADD_SIZE_16BIT,(uint8_t*)&Value, 2); +} + +/** + * @brief Reads a single data. + * @param Addr: I2C address + * @param Reg: Reg address + * @retval Data to be read + */ +uint16_t AUDIO_IO_Read(uint8_t Addr, uint16_t Reg) +{ + uint16_t read_value = 0, tmp = 0; + + I2Cx_ReadMultiple(&hI2cAudioHandler, Addr, Reg, I2C_MEMADD_SIZE_16BIT, (uint8_t*)&read_value, 2); + + tmp = ((uint16_t)(read_value >> 8) & 0x00FF); + + tmp |= ((uint16_t)(read_value << 8)& 0xFF00); + + read_value = tmp; + + return read_value; +} + +/** + * @brief AUDIO Codec delay + * @param Delay: Delay in ms + */ +void AUDIO_IO_Delay(uint32_t Delay) +{ + HAL_Delay(Delay); +} + +/******************************** LINK I2C EEPROM *****************************/ + +/** + * @brief Initializes peripherals used by the I2C EEPROM driver. + */ +void EEPROM_IO_Init(void) +{ + I2Cx_Init(&hI2cExtHandler); +} + +/** + * @brief Write data to I2C EEPROM driver in using DMA channel. + * @param DevAddress: Target device address + * @param MemAddress: Internal memory address + * @param pBuffer: Pointer to data buffer + * @param BufferSize: Amount of data to be sent + * @retval HAL status + */ +HAL_StatusTypeDef EEPROM_IO_WriteData(uint16_t DevAddress, uint16_t MemAddress, uint8_t* pBuffer, uint32_t BufferSize) +{ + return (I2Cx_WriteMultiple(&hI2cExtHandler, DevAddress, MemAddress, I2C_MEMADD_SIZE_16BIT, pBuffer, BufferSize)); +} + +/** + * @brief Read data from I2C EEPROM driver in using DMA channel. + * @param DevAddress: Target device address + * @param MemAddress: Internal memory address + * @param pBuffer: Pointer to data buffer + * @param BufferSize: Amount of data to be read + * @retval HAL status + */ +HAL_StatusTypeDef EEPROM_IO_ReadData(uint16_t DevAddress, uint16_t MemAddress, uint8_t* pBuffer, uint32_t BufferSize) +{ + return (I2Cx_ReadMultiple(&hI2cExtHandler, DevAddress, MemAddress, I2C_MEMADD_SIZE_16BIT, pBuffer, BufferSize)); +} + +/** + * @brief Checks if target device is ready for communication. + * @note This function is used with Memory devices + * @param DevAddress: Target device address + * @param Trials: Number of trials + * @retval HAL status + */ +HAL_StatusTypeDef EEPROM_IO_IsDeviceReady(uint16_t DevAddress, uint32_t Trials) +{ + return (I2Cx_IsDeviceReady(&hI2cExtHandler, DevAddress, Trials)); +} + +/******************************** LINK TS (TouchScreen) ***********************/ + +/** + * @brief Initializes Touchscreen low level. + * @retval None + */ +void TS_IO_Init(void) +{ + I2Cx_Init(&hI2cAudioHandler); +} + +/** + * @brief Writes a single data. + * @param Addr: I2C address + * @param Reg: Reg address + * @param Value: Data to be written + * @retval None + */ +void TS_IO_Write(uint8_t Addr, uint8_t Reg, uint8_t Value) +{ + I2Cx_WriteMultiple(&hI2cAudioHandler, Addr, (uint16_t)Reg, I2C_MEMADD_SIZE_8BIT,(uint8_t*)&Value, 1); +} + +/** + * @brief Reads a single data. + * @param Addr: I2C address + * @param Reg: Reg address + * @retval Data to be read + */ +uint8_t TS_IO_Read(uint8_t Addr, uint8_t Reg) +{ + uint8_t read_value = 0; + + I2Cx_ReadMultiple(&hI2cAudioHandler, Addr, Reg, I2C_MEMADD_SIZE_8BIT, (uint8_t*)&read_value, 1); + + return read_value; +} + +/** + * @brief Reads multiple data with I2C communication + * channel from TouchScreen. + * @param Addr: I2C address + * @param Reg: Register address + * @param Buffer: Pointer to data buffer + * @param Length: Length of the data + * @retval Number of read data + */ +uint16_t TS_IO_ReadMultiple(uint8_t Addr, uint8_t Reg, uint8_t *Buffer, uint16_t Length) +{ + return I2Cx_ReadMultiple(&hI2cAudioHandler, Addr, (uint16_t)Reg, I2C_MEMADD_SIZE_8BIT, Buffer, Length); +} + +/** + * @brief Writes multiple data with I2C communication + * channel from MCU to TouchScreen. + * @param Addr: I2C address + * @param Reg: Register address + * @param Buffer: Pointer to data buffer + * @param Length: Length of the data + * @retval None + */ +void TS_IO_WriteMultiple(uint8_t Addr, uint8_t Reg, uint8_t *Buffer, uint16_t Length) +{ + I2Cx_WriteMultiple(&hI2cAudioHandler, Addr, (uint16_t)Reg, I2C_MEMADD_SIZE_8BIT, Buffer, Length); +} + +/** + * @brief Delay function used in TouchScreen low level driver. + * @param Delay: Delay in ms + * @retval None + */ +void TS_IO_Delay(uint32_t Delay) +{ + HAL_Delay(Delay); +} + +/**************************** LINK OTM8009A (Display driver) ******************/ +/** + * @brief OTM8009A delay + * @param Delay: Delay in ms + */ +void OTM8009A_IO_Delay(uint32_t Delay) +{ + HAL_Delay(Delay); +} + +/**************************** LINK ADV7533 DSI-HDMI (Display driver) **********/ +/** + * @brief Initializes HDMI IO low level. + * @retval None + */ +void HDMI_IO_Init(void) +{ + I2Cx_Init(&hI2cAudioHandler); +} + +/** + * @brief HDMI writes single data. + * @param Addr: I2C address + * @param Reg: Register address + * @param Value: Data to be written + * @retval None + */ +void HDMI_IO_Write(uint8_t Addr, uint8_t Reg, uint8_t Value) +{ + I2Cx_WriteMultiple(&hI2cAudioHandler, Addr, (uint16_t)Reg, I2C_MEMADD_SIZE_8BIT, &Value, 1); +} + +/** + * @brief Reads single data with I2C communication + * channel from HDMI bridge. + * @param Addr: I2C address + * @param Reg: Register address + * @retval Read data + */ +uint8_t HDMI_IO_Read(uint8_t Addr, uint8_t Reg) +{ + uint8_t value = 0x00; + + I2Cx_ReadMultiple(&hI2cAudioHandler, Addr, (uint16_t)Reg, I2C_MEMADD_SIZE_8BIT, &value, 1); + + return value; +} + +/** + * @brief HDMI delay + * @param Delay: Delay in ms + * @retval None + */ +void HDMI_IO_Delay(uint32_t Delay) +{ + HAL_Delay(Delay); +} +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + diff --git a/BSP/stm32f769i_discovery.h b/BSP/stm32f769i_discovery.h new file mode 100644 index 0000000..eca7826 --- /dev/null +++ b/BSP/stm32f769i_discovery.h @@ -0,0 +1,334 @@ +/** + ****************************************************************************** + * @file stm32f769i_discovery.h + * @author MCD Application Team + * @brief This file contains definitions for STM32F769I-Discovery LEDs, + * push-buttons hardware resources. + ****************************************************************************** + * @attention + * + * Copyright (c) 2016 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F769I_DISCOVERY_H +#define __STM32F769I_DISCOVERY_H + +#ifdef __cplusplus + extern "C" { +#endif + + + /* Includes ------------------------------------------------------------------*/ +#include "stm32f7xx_hal.h" + +/** @addtogroup BSP + * @{ + */ + +/** @addtogroup STM32F769I_DISCOVERY + * @{ + */ + +/** @defgroup STM32F769I_DISCOVERY_LOW_LEVEL STM32F769I-Discovery LOW LEVEL + * @{ + */ + +/** @defgroup STM32F769I_DISCOVERY_LOW_LEVEL_Exported_Types STM32F769I Discovery Low Level Exported Types + * @{ + */ + +/** + * @brief Define for STM32F769I_DISCOVERY board + */ +#if !defined (USE_STM32F769I_DISCO) + #define USE_STM32F769I_DISCO +#endif + +/** @brief Led_TypeDef + * STM32F769I_DISCOVERY board leds definitions. + */ +typedef enum +{ + BSP_LED1 = 0, + BSP_LED_RED = BSP_LED1, + BSP_LED2 = 1, + BSP_LED_GREEN = BSP_LED2 +} Led_TypeDef; + +/** @brief Button_TypeDef + * STM32F769I_DISCOVERY board Buttons definitions. + */ +typedef enum +{ + BUTTON_WAKEUP = 0, +} Button_TypeDef; + +#define BUTTON_USER BUTTON_WAKEUP + +/** @brief ButtonMode_TypeDef + * STM32F769I_DISCOVERY board Buttons Modes definitions. + */ +typedef enum +{ + BUTTON_MODE_GPIO = 0, + BUTTON_MODE_EXTI = 1 + +} ButtonMode_TypeDef; + +/** @addtogroup Exported_types + * @{ + */ +typedef enum +{ + PB_SET = 0, + PB_RESET = !PB_SET +} ButtonValue_TypeDef; + + +/** @brief DISCO_Status_TypeDef + * STM32F769I_DISCO board Status return possible values. + */ +typedef enum +{ + DISCO_OK = 0, + DISCO_ERROR = 1 + +} DISCO_Status_TypeDef; + +/** + * @} + */ + +/** @defgroup STM32F769I_DISCOVERY_LOW_LEVEL_Exported_Constants STM32F769I Discovery Low Level Exported Constants + * @{ + */ + + +/** @addtogroup STM32F769I_DISCOVERY_LOW_LEVEL_LED STM32F769I Discovery Low Level Led + * @{ + */ +/* Always four leds for all revisions of Discovery boards */ +#define BSP_LEDn ((uint8_t)2) + + +/* 2 Leds are connected to MCU directly on PJ13 and PJ5 */ +#define BSP_LED1_GPIO_PORT ((GPIO_TypeDef*)GPIOJ) +#define BSP_LED2_GPIO_PORT ((GPIO_TypeDef*)GPIOJ) + +#define BSP_LEDx_GPIO_CLK_ENABLE() __HAL_RCC_GPIOJ_CLK_ENABLE() +#define BSP_LEDx_GPIO_CLK_DISABLE() __HAL_RCC_GPIOJ_CLK_DISABLE() + +#define BSP_LED1_PIN ((uint32_t)GPIO_PIN_13) +#define BSP_LED2_PIN ((uint32_t)GPIO_PIN_5) + +/** + * @} + */ + +/** @addtogroup STM32F769I_DISCOVERY_LOW_LEVEL_BUTTON STM32F769I Discovery Low Level Button + * @{ + */ +/* Only one User/Wakeup button */ +#define BUTTONn ((uint8_t)1) + +/** + * @brief Wakeup push-button + */ +#define WAKEUP_BUTTON_PIN GPIO_PIN_0 +#define WAKEUP_BUTTON_GPIO_PORT GPIOA +#define WAKEUP_BUTTON_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() +#define WAKEUP_BUTTON_GPIO_CLK_DISABLE() __HAL_RCC_GPIOA_CLK_DISABLE() +#define WAKEUP_BUTTON_EXTI_IRQn EXTI0_IRQn + +/* Define the USER button as an alias of the Wakeup button */ +#define USER_BUTTON_PIN WAKEUP_BUTTON_PIN +#define USER_BUTTON_GPIO_PORT WAKEUP_BUTTON_GPIO_PORT +#define USER_BUTTON_GPIO_CLK_ENABLE() WAKEUP_BUTTON_GPIO_CLK_ENABLE() +#define USER_BUTTON_GPIO_CLK_DISABLE() WAKEUP_BUTTON_GPIO_CLK_DISABLE() +#define USER_BUTTON_EXTI_IRQn WAKEUP_BUTTON_EXTI_IRQn + +#define BUTTON_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() + +/** + * @} + */ + +/** + * @brief USB OTG HS Over Current signal + */ +#define OTG_HS_OVER_CURRENT_PIN GPIO_PIN_4 +#define OTG_HS_OVER_CURRENT_PORT GPIOD +#define OTG_HS_OVER_CURRENT_PORT_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE() + +/** + * @brief SD-detect signal + */ +#define SD_DETECT_PIN ((uint32_t)GPIO_PIN_15) +#define SD_DETECT_GPIO_PORT ((GPIO_TypeDef*)GPIOI) +#define SD_DETECT_GPIO_CLK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE() +#define SD_DETECT_GPIO_CLK_DISABLE() __HAL_RCC_GPIOI_CLK_DISABLE() +#define SD_DETECT_EXTI_IRQn EXTI15_10_IRQn + +/** + * @brief Touch screen interrupt signal + */ +#define TS_INT_PIN ((uint32_t)GPIO_PIN_13) +#define TS_INT_GPIO_PORT ((GPIO_TypeDef*)GPIOI) +#define TS_INT_GPIO_CLK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE() +#define TS_INT_GPIO_CLK_DISABLE() __HAL_RCC_GPIOI_CLK_DISABLE() +#define TS_INT_EXTI_IRQn EXTI15_10_IRQn + +/** + * @brief TouchScreen FT6206 Slave I2C address 1 + */ +#define TS_I2C_ADDRESS ((uint16_t)0x54) + +/** + * @brief TouchScreen FT6336G Slave I2C address 2 + */ +#define TS_I2C_ADDRESS_A02 ((uint16_t)0x70) + +/** + * @brief LCD DSI Slave I2C address 1 + */ +#define LCD_DSI_ADDRESS TS_I2C_ADDRESS + +/** + * @brief LCD DSI Slave I2C address 2 + */ +#define LCD_DSI_ADDRESS_A02 TS_I2C_ADDRESS_A02 + +/** + * @brief Audio I2C Slave address + */ +#define AUDIO_I2C_ADDRESS ((uint16_t)0x34) + +/** + * @brief EEPROM I2C Slave address 1 + */ +#define EEPROM_I2C_ADDRESS_A01 ((uint16_t)0xA0) + +/** + * @brief EEPROM I2C Slave address 2 + */ +#define EEPROM_I2C_ADDRESS_A02 ((uint16_t)0xA6) + +/** + * @brief User can use this section to tailor I2C4/I2C4 instance used and associated + * resources (audio codec). + * Definition for I2C4 clock resources + */ +#define DISCOVERY_AUDIO_I2Cx I2C4 +#define DISCOVERY_AUDIO_I2Cx_CLK_ENABLE() __HAL_RCC_I2C4_CLK_ENABLE() +#define DISCOVERY_AUDIO_I2Cx_SCL_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE() +#define DISCOVERY_AUDIO_I2Cx_SDA_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() + +#define DISCOVERY_AUDIO_I2Cx_FORCE_RESET() __HAL_RCC_I2C4_FORCE_RESET() +#define DISCOVERY_AUDIO_I2Cx_RELEASE_RESET() __HAL_RCC_I2C4_RELEASE_RESET() + +/** @brief Definition for I2C4 Pins + */ +#define DISCOVERY_AUDIO_I2Cx_SCL_PIN GPIO_PIN_12 /*!< PD12 */ +#define DISCOVERY_AUDIO_I2Cx_SCL_AF GPIO_AF4_I2C4 +#define DISCOVERY_AUDIO_I2Cx_SCL_GPIO_PORT GPIOD +#define DISCOVERY_AUDIO_I2Cx_SDA_PIN GPIO_PIN_7 /*!< PB7 */ +#define DISCOVERY_AUDIO_I2Cx_SDA_AF GPIO_AF11_I2C4 +#define DISCOVERY_AUDIO_I2Cx_SDA_GPIO_PORT GPIOB +/** @brief Definition of I2C4 interrupt requests + */ +#define DISCOVERY_AUDIO_I2Cx_EV_IRQn I2C4_EV_IRQn +#define DISCOVERY_AUDIO_I2Cx_ER_IRQn I2C4_ER_IRQn + +/** + * @brief User can use this section to tailor I2C1/I2C1 instance used and associated + * resources. + * Definition for I2C1 clock resources + */ +#define DISCOVERY_EXT_I2Cx I2C1 +#define DISCOVERY_EXT_I2Cx_CLK_ENABLE() __HAL_RCC_I2C1_CLK_ENABLE() +#define DISCOVERY_DMAx_CLK_ENABLE() __HAL_RCC_DMA1_CLK_ENABLE() +#define DISCOVERY_EXT_I2Cx_SCL_SDA_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() + +#define DISCOVERY_EXT_I2Cx_FORCE_RESET() __HAL_RCC_I2C1_FORCE_RESET() +#define DISCOVERY_EXT_I2Cx_RELEASE_RESET() __HAL_RCC_I2C1_RELEASE_RESET() + +/** @brief Definition for I2C1 Pins + */ +#define DISCOVERY_EXT_I2Cx_SCL_PIN GPIO_PIN_8 /*!< PB8 */ +#define DISCOVERY_EXT_I2Cx_SCL_SDA_GPIO_PORT GPIOB +#define DISCOVERY_EXT_I2Cx_SCL_SDA_AF GPIO_AF4_I2C1 +#define DISCOVERY_EXT_I2Cx_SDA_PIN GPIO_PIN_9 /*!< PB9 */ + +/** @brief Definition of I2C interrupt requests + */ +#define DISCOVERY_EXT_I2Cx_EV_IRQn I2C1_EV_IRQn +#define DISCOVERY_EXT_I2Cx_ER_IRQn I2C1_ER_IRQn + +/* I2C TIMING Register define when I2C clock source is SYSCLK */ +/* I2C TIMING is calculated from APB1 source clock = 50 MHz */ +/* Due to the big MOFSET capacity for adapting the camera level the rising time is very large (>1us) */ +/* 0x40912732 takes in account the big rising and aims a clock of 100khz */ +#ifndef DISCOVERY_I2Cx_TIMING +#define DISCOVERY_I2Cx_TIMING ((uint32_t)0x40912732) +#endif /* DISCOVERY_I2Cx_TIMING */ + + +/** + * @} + */ + +/** @defgroup STM32F769I_DISCOVERY_LOW_LEVEL_Exported_Macros STM32F769I Discovery Low Level Exported Macros + * @{ + */ +/** + * @} + */ + +/** @defgroup STM32F769I_DISCOVERY_LOW_LEVEL_Exported_Functions STM32F769I Discovery Low Level Exported Functions + * @{ + */ +uint32_t BSP_GetVersion(void); +void BSP_LED_Init(Led_TypeDef Led); +void BSP_LED_DeInit(Led_TypeDef Led); +void BSP_LED_On(Led_TypeDef Led); +void BSP_LED_Off(Led_TypeDef Led); +void BSP_LED_Toggle(Led_TypeDef Led); +void BSP_PB_Init(Button_TypeDef Button, ButtonMode_TypeDef Button_Mode); +void BSP_PB_DeInit(Button_TypeDef Button); +uint32_t BSP_PB_GetState(Button_TypeDef Button); + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F769I_DISCOVERY_H */ + diff --git a/BSP/stm32f769i_discovery_audio.c b/BSP/stm32f769i_discovery_audio.c new file mode 100644 index 0000000..d917573 --- /dev/null +++ b/BSP/stm32f769i_discovery_audio.c @@ -0,0 +1,2196 @@ +/** + ****************************************************************************** + * @file stm32f769i_discovery_audio.c + * @author MCD Application Team + * @brief This file provides the Audio driver for the STM32F769I-DISCOVERY + * board. + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2016 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + @verbatim + How To use this driver: + ----------------------- + + This driver supports STM32F7xx devices on STM32F769I-DISCOVERY (MB1225) Evaluation boards. + + Call the function BSP_AUDIO_OUT_Init( + OutputDevice: physical output mode (OUTPUT_DEVICE_SPEAKER, + OUTPUT_DEVICE_HEADPHONE or OUTPUT_DEVICE_BOTH) + Volume : Initial volume to be set (0 is min (mute), 100 is max (100%) + AudioFreq : Audio frequency in Hz (8000, 16000, 22500, 32000...) + this parameter is relative to the audio file/stream type. + ) + This function configures all the hardware required for the audio application (codec, I2C, SAI, + GPIOs, DMA and interrupt if needed). This function returns AUDIO_OK if configuration is OK. + If the returned value is different from AUDIO_OK or the function is stuck then the communication with + the codec has failed (try to un-plug the power or reset device in this case). + - OUTPUT_DEVICE_SPEAKER : only speaker will be set as output for the audio stream. + - OUTPUT_DEVICE_HEADPHONE: only headphones will be set as output for the audio stream. + - OUTPUT_DEVICE_BOTH : both Speaker and Headphone are used as outputs for the audio stream + at the same time. + Note. On STM32F769I-DISCOVERY SAI_DMA is configured in CIRCULAR mode. Due to this the application + does NOT need to call BSP_AUDIO_OUT_ChangeBuffer() to assure streaming. + + Call the function BSP_AUDIO_OUT_Play( + pBuffer: pointer to the audio data file address + Size : size of the buffer to be sent in Bytes + ) + to start playing (for the first time) from the audio file/stream. + + Call the function BSP_AUDIO_OUT_Pause() to pause playing + + Call the function BSP_AUDIO_OUT_Resume() to resume playing. + Note. After calling BSP_AUDIO_OUT_Pause() function for pause, only BSP_AUDIO_OUT_Resume() should be called + for resume (it is not allowed to call BSP_AUDIO_OUT_Play() in this case). + Note. This function should be called only when the audio file is played or paused (not stopped). + + For each mode, you may need to implement the relative callback functions into your code. + The Callback functions are named BSP_AUDIO_OUT_XXX_CallBack() and only their prototypes are declared in + the stm32f769i_discovery_audio.h file. (refer to the example for more details on the callbacks implementations) + + To Stop playing, to modify the volume level, the frequency, the audio frame slot, + the device output mode the mute or the stop, use the functions: BSP_AUDIO_OUT_SetVolume(), + AUDIO_OUT_SetFrequency(), BSP_AUDIO_OUT_SetAudioFrameSlot(), BSP_AUDIO_OUT_SetOutputMode(), + BSP_AUDIO_OUT_SetMute() and BSP_AUDIO_OUT_Stop(). + + + Call the function BSP_AUDIO_IN_Init( + AudioFreq: Audio frequency in Hz (8000, 16000, 22500, 32000...) + this parameter is relative to the audio file/stream type. + BitRes: Bit resolution fixed to 16bit + ChnlNbr: Number of channel to be configured for the DFSDM peripheral + ) + This function configures all the hardware required for the audio in application (DFSDM filters and channels, + Clock source for DFSDM periphiral, GPIOs, DMA and interrupt if needed). + This function returns AUDIO_OK if configuration is OK.If the returned value is different from AUDIO_OK then + the configuration should be wrong. + Note: On STM32F769I-DISCOVERY, four DFSDM Channel/Filters are configured and their DMA streams are configured + in CIRCULAR mode. + + Call the function BSP_AUDIO_IN_AllocScratch( + pScratch: pointer to scratch tables + size: size of scratch buffer) + This function must be called before BSP_AUDIO_IN_RECORD() to allocate buffer scratch for each DFSDM channel + and its size. + Note: These buffers scratch are used as intermidiate buffers to collect data within final record buffer. + size is the total size of the four buffers scratch; If size is 512 then the size of each is 128. + This function must be called after BSP_AUDIO_IN_Init() + + Call the function BSP_AUDIO_IN_RECORD( + pBuf: pointer to the recorded audio data file address + Size: size of the buffer to be written in Bytes + ) + to start recording from microphones. + + + Call the function BSP_AUDIO_IN_Pause() to pause recording + + Call the function BSP_AUDIO_IN_Resume() to recording playing. + Note. After calling BSP_AUDIO_IN_Pause() function for pause, only BSP_AUDIO_IN_Resume() should be called + for resume (it is not allowed to call BSP_AUDIO_IN_RECORD() in this case). + + Call the function BSP_AUDIO_IN_Stop() to stop recording + + For each mode, you may need to implement the relative callback functions into your code. + The Callback functions are named BSP_AUDIO_IN_XXX_CallBack() and only their prototypes are declared in + the stm32f769i_discovery_audio.h file. (refer to the example for more details on the callbacks implementations) + + Driver architecture: + -------------------- + + This driver provides the High Audio Layer: consists of the function API exported in the stm32f769i_discovery_audio.h file + (BSP_AUDIO_OUT_Init(), BSP_AUDIO_OUT_Play() ...) + + This driver provide also the Media Access Layer (MAL): which consists of functions allowing to access the media containing/ + providing the audio file/stream. These functions are also included as local functions into + the stm32f769i_discovery_audio.c file (DFSDMx_Init(), DFSDMx_DeInit(), SAIx_Init() and SAIx_DeInit()) + + Known Limitations: + ------------------ + 1- If the TDM Format used to play in parallel 2 audio Stream (the first Stream is configured in codec SLOT0 and second + Stream in SLOT1) the Pause/Resume, volume and mute feature will control the both streams. + 2- Parsing of audio file is not implemented (in order to determine audio file properties: Mono/Stereo, Data size, + File size, Audio Frequency, Audio Data header size ...). The configuration is fixed for the given audio file. + 3- Supports only Stereo audio streaming. + 4- Supports only 16-bits audio data size. + @endverbatim + ****************************************************************************** + */ + +/* Dependencies +- stm32f769i_discovery.c +- stm32f7xx_hal_sai.c +- stm32f7xx_hal_dfsdm.c +- stm32f7xx_hal_dma.c +- stm32f7xx_hal_gpio.c +- stm32f7xx_hal_cortex.c +- stm32f7xx_hal_rcc_ex.h +- wm8994.c +EndDependencies */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f769i_discovery_audio.h" + +/** @addtogroup BSP + * @{ + */ + +/** @addtogroup STM32F769I_DISCOVERY + * @{ + */ + +/** @defgroup STM32F769I_DISCOVERY_AUDIO STM32F769I_DISCOVERY AUDIO + * @brief This file includes the low layer driver for wm8994 Audio Codec + * available on STM32F769I-DISCOVERY discoveryuation board(MB1225). + * @{ + */ + +/** @defgroup STM32F769I_DISCOVERY_AUDIO_Private_Types STM32F769I_DISCOVERY_AUDIO Private Types + * @{ + */ +typedef struct +{ + uint16_t *pRecBuf; /* Pointer to record user buffer */ + uint32_t RecSize; /* Size to record in mono, double size to record in stereo */ +}AUDIOIN_TypeDef; + +/** + * @} + */ + +/** @defgroup STM32F769I_DISCOVERY_AUDIO_Private_Defines STM32F769I_DISCOVERY_AUDIO Private Defines + * @{ + */ +/** + * @} + */ + +/** @defgroup STM32F769I_DISCOVERY_AUDIO_Private_Macros STM32F769I_DISCOVERY_AUDIO Private Macros + * @{ + */ +/*### RECORD ###*/ +#define DFSDM_OVER_SAMPLING(__FREQUENCY__) \ + (__FREQUENCY__ == AUDIO_FREQUENCY_8K) ? 256 \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_11K) ? 256 \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_16K) ? 128 \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_22K) ? 128 \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_32K) ? 64 \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_44K) ? 64 \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_48K) ? 40 : 20 \ + +#define DFSDM_CLOCK_DIVIDER(__FREQUENCY__) \ + (__FREQUENCY__ == AUDIO_FREQUENCY_8K) ? 24 \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_11K) ? 4 \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_16K) ? 24 \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_22K) ? 4 \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_32K) ? 24 \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_44K) ? 4 \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_48K) ? 25 : 25 \ + +#define DFSDM_FILTER_ORDER(__FREQUENCY__) \ + (__FREQUENCY__ == AUDIO_FREQUENCY_8K) ? DFSDM_FILTER_SINC3_ORDER \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_11K) ? DFSDM_FILTER_SINC3_ORDER \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_16K) ? DFSDM_FILTER_SINC3_ORDER \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_22K) ? DFSDM_FILTER_SINC3_ORDER \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_32K) ? DFSDM_FILTER_SINC4_ORDER \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_44K) ? DFSDM_FILTER_SINC3_ORDER \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_48K) ? DFSDM_FILTER_SINC3_ORDER : DFSDM_FILTER_SINC5_ORDER \ + +#define DFSDM_RIGHT_BIT_SHIFT(__FREQUENCY__) \ + (__FREQUENCY__ == AUDIO_FREQUENCY_8K) ? 8 \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_11K) ? 8 \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_16K) ? 3 \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_22K) ? 4 \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_32K) ? 7 \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_44K) ? 0 \ + : (__FREQUENCY__ == AUDIO_FREQUENCY_48K) ? 0 : 4 \ + +/* Saturate the record PCM sample */ +#define SaturaLH(N, L, H) (((N)<(L))?(L):(((N)>(H))?(H):(N))) +/** + * @} + */ + +/** @defgroup STM32F769I_DISCOVERY_AUDIO_Private_Variables STM32F769I_DISCOVERY_AUDIO Private Variables + * @{ + */ +/* PLAY */ +AUDIO_DrvTypeDef *audio_drv; +SAI_HandleTypeDef haudio_out_sai; +SAI_HandleTypeDef haudio_in_sai; + +/* RECORD */ +AUDIOIN_TypeDef hAudioIn; + +DFSDM_Channel_HandleTypeDef hAudioInTopLeftChannel; +DFSDM_Channel_HandleTypeDef hAudioInTopRightChannel; +DFSDM_Filter_HandleTypeDef hAudioInTopLeftFilter; +DFSDM_Filter_HandleTypeDef hAudioInTopRightFilter; +DMA_HandleTypeDef hDmaTopLeft; +DMA_HandleTypeDef hDmaTopRight; + +DFSDM_Channel_HandleTypeDef hAudioInButtomLeftChannel; +DFSDM_Channel_HandleTypeDef hAudioInButtomRightChannel; +DFSDM_Filter_HandleTypeDef hAudioInButtomLeftFilter; +DFSDM_Filter_HandleTypeDef hAudioInButtomRightFilter; +DMA_HandleTypeDef hDmaButtomLeft; +DMA_HandleTypeDef hDmaButtomRight; + +/* Buffers for right and left samples */ +static int32_t *pScratchBuff[2*DEFAULT_AUDIO_IN_CHANNEL_NBR]; +static __IO int32_t ScratchSize; +/* Cannel number to be used: 2 channels by default */ +static uint8_t AudioIn_ChannelNumber = DEFAULT_AUDIO_IN_CHANNEL_NBR; +/* Input device to be used: digital microphones by default */ +static uint16_t AudioIn_Device = INPUT_DEVICE_DIGITAL_MIC; + +/* Buffers status flags */ +static uint32_t DmaTopLeftRecHalfCplt = 0; +static uint32_t DmaTopLeftRecCplt = 0; +static uint32_t DmaTopRightRecHalfCplt = 0; +static uint32_t DmaTopRightRecCplt = 0; +static uint32_t DmaButtomLeftRecHalfCplt = 0; +static uint32_t DmaButtomLeftRecCplt = 0; +static uint32_t DmaButtomRightRecHalfCplt = 0; +static uint32_t DmaButtomRightRecCplt = 0; + +/* Application Buffer Trigger */ +static __IO uint32_t AppBuffTrigger = 0; +static __IO uint32_t AppBuffHalf = 0; + +/** + * @} + */ + +/** @defgroup STM32F769I_DISCOVERY_AUDIO_Private_Function_Prototypes STM32F769I_DISCOVERY_AUDIO Private Function Prototypes + * @{ + */ +static void SAIx_Out_Init(uint32_t AudioFreq); +static void SAIx_Out_DeInit(void); +static void SAI_AUDIO_IN_MspInit(SAI_HandleTypeDef *hsai, void *Params); +static void SAI_AUDIO_IN_MspDeInit(SAI_HandleTypeDef *hsai, void *Params); +static void SAIx_In_Init(uint32_t AudioFreq); +static void SAIx_In_DeInit(void); +static void DFSDMx_ChannelMspInit(void); +static void DFSDMx_FilterMspInit(void); +static void DFSDMx_ChannelMspDeInit(void); +static void DFSDMx_FilterMspDeInit(void); +static uint8_t DFSDMx_Init(uint32_t AudioFreq); +static uint8_t DFSDMx_DeInit(void); + +/** + * @} + */ + +/** @defgroup STM32F769I_DISCOVERY_AUDIO_out_Private_Functions STM32F769I_DISCOVERY_AUDIO_Out Private Functions + * @{ + */ + +/** + * @brief Configures the audio peripherals. + * @param OutputDevice: OUTPUT_DEVICE_SPEAKER, OUTPUT_DEVICE_HEADPHONE, + * or OUTPUT_DEVICE_BOTH. + * @param Volume: Initial volume level (from 0 (Mute) to 100 (Max)) + * @param AudioFreq: Audio frequency used to play the audio stream. + * @retval AUDIO_OK if correct communication, else wrong communication + */ +uint8_t BSP_AUDIO_OUT_Init(uint16_t OutputDevice, uint8_t Volume, uint32_t AudioFreq) +{ + uint8_t ret = AUDIO_ERROR; + uint32_t deviceid = 0x00; + + /* Disable SAI */ + SAIx_Out_DeInit(); + + /* PLL clock is set depending by the AudioFreq (44.1khz vs 48khz groups) */ + BSP_AUDIO_OUT_ClockConfig(&haudio_out_sai, AudioFreq, NULL); + + /* SAI data transfer preparation: + Prepare the Media to be used for the audio transfer from memory to SAI peripheral */ + haudio_out_sai.Instance = AUDIO_OUT_SAIx; + if(HAL_SAI_GetState(&haudio_out_sai) == HAL_SAI_STATE_RESET) + { + /* Init the SAI MSP: this __weak function can be redefined by the application*/ + BSP_AUDIO_OUT_MspInit(&haudio_out_sai, NULL); + } + SAIx_Out_Init(AudioFreq); + + /* wm8994 codec initialization */ + deviceid = wm8994_drv.ReadID(AUDIO_I2C_ADDRESS); + + if((deviceid) == WM8994_ID) + { + /* Reset the Codec Registers */ + wm8994_drv.Reset(AUDIO_I2C_ADDRESS); + /* Initialize the audio driver structure */ + audio_drv = &wm8994_drv; + ret = AUDIO_OK; + } + else + { + ret = AUDIO_ERROR; + } + + if(ret == AUDIO_OK) + { + /* Initialize the codec internal registers */ + audio_drv->Init(AUDIO_I2C_ADDRESS, OutputDevice, Volume, AudioFreq); + } + + return ret; +} + +/** + * @brief Starts playing audio stream from a data buffer for a determined size. + * @param pBuffer: Pointer to the buffer + * @param Size: Number of audio data BYTES. + * @retval AUDIO_OK if correct communication, else wrong communication + */ +uint8_t BSP_AUDIO_OUT_Play(uint16_t* pBuffer, uint32_t Size) +{ + /* Call the audio Codec Play function */ + if(audio_drv->Play(AUDIO_I2C_ADDRESS, (uint16_t *)pBuffer, Size) != 0) + { + return AUDIO_ERROR; + } + else + { + /* Update the Media layer and enable it for play */ + HAL_SAI_Transmit_DMA(&haudio_out_sai, (uint8_t*) pBuffer, DMA_MAX(Size / AUDIODATA_SIZE)); + + return AUDIO_OK; + } +} + +/** + * @brief Sends n-Bytes on the SAI interface. + * @param pData: pointer on data address + * @param Size: number of data to be written + * @retval None + */ +void BSP_AUDIO_OUT_ChangeBuffer(uint16_t *pData, uint16_t Size) +{ + HAL_SAI_Transmit_DMA(&haudio_out_sai, (uint8_t*) pData, Size); +} + +/** + * @brief This function Pauses the audio file stream. In case + * of using DMA, the DMA Pause feature is used. + * @note When calling BSP_AUDIO_OUT_Pause() function for pause, only + * BSP_AUDIO_OUT_Resume() function should be called for resume (use of BSP_AUDIO_OUT_Play() + * function for resume could lead to unexpected behaviour). + * @retval AUDIO_OK if correct communication, else wrong communication + */ +uint8_t BSP_AUDIO_OUT_Pause(void) +{ + /* Call the Audio Codec Pause/Resume function */ + if(audio_drv->Pause(AUDIO_I2C_ADDRESS) != 0) + { + return AUDIO_ERROR; + } + else + { + /* Call the Media layer pause function */ + HAL_SAI_DMAPause(&haudio_out_sai); + + /* Return AUDIO_OK when all operations are correctly done */ + return AUDIO_OK; + } +} + +/** + * @brief Resumes the audio file stream. + * @note When calling BSP_AUDIO_OUT_Pause() function for pause, only + * BSP_AUDIO_OUT_Resume() function should be called for resume (use of BSP_AUDIO_OUT_Play() + * function for resume could lead to unexpected behaviour). + * @retval AUDIO_OK if correct communication, else wrong communication + */ +uint8_t BSP_AUDIO_OUT_Resume(void) +{ + /* Call the Audio Codec Pause/Resume function */ + if(audio_drv->Resume(AUDIO_I2C_ADDRESS) != 0) + { + return AUDIO_ERROR; + } + else + { + /* Call the Media layer pause/resume function */ + HAL_SAI_DMAResume(&haudio_out_sai); + + /* Return AUDIO_OK when all operations are correctly done */ + return AUDIO_OK; + } +} + +/** + * @brief Stops audio playing and Power down the Audio Codec. + * @param Option: could be one of the following parameters + * - CODEC_PDWN_SW: for software power off (by writing registers). + * Then no need to reconfigure the Codec after power on. + * - CODEC_PDWN_HW: completely shut down the codec (physically). + * Then need to reconfigure the Codec after power on. + * @retval AUDIO_OK if correct communication, else wrong communication + */ +uint8_t BSP_AUDIO_OUT_Stop(uint32_t Option) +{ + /* Call the Media layer stop function */ + HAL_SAI_DMAStop(&haudio_out_sai); + + /* Call Audio Codec Stop function */ + if(audio_drv->Stop(AUDIO_I2C_ADDRESS, Option) != 0) + { + return AUDIO_ERROR; + } + else + { + if(Option == CODEC_PDWN_HW) + { + /* Wait at least 100us */ + HAL_Delay(1); + } + /* Return AUDIO_OK when all operations are correctly done */ + return AUDIO_OK; + } +} + +/** + * @brief Controls the current audio volume level. + * @param Volume: Volume level to be set in percentage from 0% to 100% (0 for + * Mute and 100 for Max volume level). + * @retval AUDIO_OK if correct communication, else wrong communication + */ +uint8_t BSP_AUDIO_OUT_SetVolume(uint8_t Volume) +{ + /* Call the codec volume control function with converted volume value */ + if(audio_drv->SetVolume(AUDIO_I2C_ADDRESS, Volume) != 0) + { + return AUDIO_ERROR; + } + else + { + /* Return AUDIO_OK when all operations are correctly done */ + return AUDIO_OK; + } +} + +/** + * @brief Enables or disables the MUTE mode by software + * @param Cmd: Could be AUDIO_MUTE_ON to mute sound or AUDIO_MUTE_OFF to + * unmute the codec and restore previous volume level. + * @retval AUDIO_OK if correct communication, else wrong communication + */ +uint8_t BSP_AUDIO_OUT_SetMute(uint32_t Cmd) +{ + /* Call the Codec Mute function */ + if(audio_drv->SetMute(AUDIO_I2C_ADDRESS, Cmd) != 0) + { + return AUDIO_ERROR; + } + else + { + /* Return AUDIO_OK when all operations are correctly done */ + return AUDIO_OK; + } +} + +/** + * @brief Switch dynamically (while audio file is played) the output target + * (speaker or headphone). + * @param Output: The audio output target: OUTPUT_DEVICE_SPEAKER, + * OUTPUT_DEVICE_HEADPHONE or OUTPUT_DEVICE_BOTH + * @retval AUDIO_OK if correct communication, else wrong communication + */ +uint8_t BSP_AUDIO_OUT_SetOutputMode(uint8_t Output) +{ + /* Call the Codec output device function */ + if(audio_drv->SetOutputMode(AUDIO_I2C_ADDRESS, Output) != 0) + { + return AUDIO_ERROR; + } + else + { + /* Return AUDIO_OK when all operations are correctly done */ + return AUDIO_OK; + } +} + +/** + * @brief Updates the audio frequency. + * @param AudioFreq: Audio frequency used to play the audio stream. + * @note This API should be called after the BSP_AUDIO_OUT_Init() to adjust the + * audio frequency. + * @retval None + */ +void BSP_AUDIO_OUT_SetFrequency(uint32_t AudioFreq) +{ + /* PLL clock is set depending by the AudioFreq (44.1khz vs 48khz groups) */ + BSP_AUDIO_OUT_ClockConfig(&haudio_out_sai, AudioFreq, NULL); + + /* Disable SAI peripheral to allow access to SAI internal registers */ + __HAL_SAI_DISABLE(&haudio_out_sai); + + /* Update the SAI audio frequency configuration */ + haudio_out_sai.Init.AudioFrequency = AudioFreq; + HAL_SAI_Init(&haudio_out_sai); + + /* Enable SAI peripheral to generate MCLK */ + __HAL_SAI_ENABLE(&haudio_out_sai); +} + +/** + * @brief Updates the Audio frame slot configuration. + * @param AudioFrameSlot: specifies the audio Frame slot + * @note This API should be called after the BSP_AUDIO_OUT_Init() to adjust the + * audio frame slot. + * @retval None + */ +void BSP_AUDIO_OUT_SetAudioFrameSlot(uint32_t AudioFrameSlot) +{ + /* Disable SAI peripheral to allow access to SAI internal registers */ + __HAL_SAI_DISABLE(&haudio_out_sai); + + /* Update the SAI audio frame slot configuration */ + haudio_out_sai.SlotInit.SlotActive = AudioFrameSlot; + HAL_SAI_Init(&haudio_out_sai); + + /* Enable SAI peripheral to generate MCLK */ + __HAL_SAI_ENABLE(&haudio_out_sai); +} + +/** + * @brief De-initializes the audio out peripheral. + * @retval None + */ +void BSP_AUDIO_OUT_DeInit(void) +{ + SAIx_Out_DeInit(); + /* DeInit the SAI MSP : this __weak function can be rewritten by the application */ + BSP_AUDIO_OUT_MspDeInit(&haudio_out_sai, NULL); +} + +/** + * @brief Tx Transfer completed callbacks. + * @param hsai: SAI handle + * @retval None + */ +void HAL_SAI_TxCpltCallback(SAI_HandleTypeDef *hsai) +{ + /* Manage the remaining file size and new address offset: This function + should be coded by user (its prototype is already declared in stm32f769i_discovery_audio.h) */ + BSP_AUDIO_OUT_TransferComplete_CallBack(); +} + +/** + * @brief Tx Half Transfer completed callbacks. + * @param hsai: SAI handle + * @retval None + */ +void HAL_SAI_TxHalfCpltCallback(SAI_HandleTypeDef *hsai) +{ + /* Manage the remaining file size and new address offset: This function + should be coded by user (its prototype is already declared in stm32f769i_discovery_audio.h) */ + BSP_AUDIO_OUT_HalfTransfer_CallBack(); +} + +/** + * @brief SAI error callbacks. + * @param hsai: SAI handle + * @retval None + */ +void HAL_SAI_ErrorCallback(SAI_HandleTypeDef *hsai) +{ + if(hsai->Instance == AUDIO_OUT_SAIx) + { + BSP_AUDIO_OUT_Error_CallBack(); + } + else + { + BSP_AUDIO_IN_Error_CallBack(); + } +} + +/** + * @brief Manages the DMA full Transfer complete event. + * @retval None + */ +__weak void BSP_AUDIO_OUT_TransferComplete_CallBack(void) +{ +} + +/** + * @brief Manages the DMA Half Transfer complete event. + * @retval None + */ +__weak void BSP_AUDIO_OUT_HalfTransfer_CallBack(void) +{ +} + +/** + * @brief Manages the DMA FIFO error event. + * @retval None + */ +__weak void BSP_AUDIO_OUT_Error_CallBack(void) +{ +} + +/** + * @brief Initializes BSP_AUDIO_OUT MSP. + * @param hsai: SAI handle + * @param Params + * @retval None + */ +__weak void BSP_AUDIO_OUT_MspInit(SAI_HandleTypeDef *hsai, void *Params) +{ + static DMA_HandleTypeDef hdma_sai_tx; + GPIO_InitTypeDef gpio_init_structure; + + /* Enable SAI clock */ + AUDIO_OUT_SAIx_CLK_ENABLE(); + + + /* Enable GPIO clock */ + AUDIO_OUT_SAIx_MCLK_ENABLE(); + AUDIO_OUT_SAIx_SD_FS_CLK_ENABLE(); + + /* CODEC_SAI pins configuration: FS, SCK, MCK and SD pins ------------------*/ + gpio_init_structure.Pin = AUDIO_OUT_SAIx_FS_PIN | AUDIO_OUT_SAIx_SCK_PIN | AUDIO_OUT_SAIx_SD_PIN; + gpio_init_structure.Mode = GPIO_MODE_AF_PP; + gpio_init_structure.Pull = GPIO_NOPULL; + gpio_init_structure.Speed = GPIO_SPEED_HIGH; + gpio_init_structure.Alternate = AUDIO_OUT_SAIx_AF; + HAL_GPIO_Init(AUDIO_OUT_SAIx_SD_FS_SCK_GPIO_PORT, &gpio_init_structure); + + gpio_init_structure.Pin = AUDIO_OUT_SAIx_MCLK_PIN; + HAL_GPIO_Init(AUDIO_OUT_SAIx_MCLK_GPIO_PORT, &gpio_init_structure); + + /* Enable the DMA clock */ + AUDIO_OUT_SAIx_DMAx_CLK_ENABLE(); + + if(hsai->Instance == AUDIO_OUT_SAIx) + { + /* Configure the hdma_saiTx handle parameters */ + hdma_sai_tx.Init.Channel = AUDIO_OUT_SAIx_DMAx_CHANNEL; + hdma_sai_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; + hdma_sai_tx.Init.PeriphInc = DMA_PINC_DISABLE; + hdma_sai_tx.Init.MemInc = DMA_MINC_ENABLE; + hdma_sai_tx.Init.PeriphDataAlignment = AUDIO_OUT_SAIx_DMAx_PERIPH_DATA_SIZE; + hdma_sai_tx.Init.MemDataAlignment = AUDIO_OUT_SAIx_DMAx_MEM_DATA_SIZE; + hdma_sai_tx.Init.Mode = DMA_CIRCULAR; + hdma_sai_tx.Init.Priority = DMA_PRIORITY_HIGH; + hdma_sai_tx.Init.FIFOMode = DMA_FIFOMODE_ENABLE; + hdma_sai_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; + hdma_sai_tx.Init.MemBurst = DMA_MBURST_SINGLE; + hdma_sai_tx.Init.PeriphBurst = DMA_PBURST_SINGLE; + + hdma_sai_tx.Instance = AUDIO_OUT_SAIx_DMAx_STREAM; + + /* Associate the DMA handle */ + __HAL_LINKDMA(hsai, hdmatx, hdma_sai_tx); + + /* Deinitialize the Stream for new transfer */ + HAL_DMA_DeInit(&hdma_sai_tx); + + /* Configure the DMA Stream */ + HAL_DMA_Init(&hdma_sai_tx); + } + + /* SAI DMA IRQ Channel configuration */ + HAL_NVIC_SetPriority(AUDIO_OUT_SAIx_DMAx_IRQ, AUDIO_OUT_IRQ_PREPRIO, 0); + HAL_NVIC_EnableIRQ(AUDIO_OUT_SAIx_DMAx_IRQ); +} + +/** + * @brief Initializes SAI Audio IN MSP. + * @param hsai: SAI handle + * @param Params + * @retval None + */ +static void SAI_AUDIO_IN_MspInit(SAI_HandleTypeDef *hsai, void *Params) +{ + static DMA_HandleTypeDef hdma_sai_rx; + GPIO_InitTypeDef gpio_init_structure; + + /* Enable SAI clock */ + AUDIO_IN_SAIx_CLK_ENABLE(); + + /* Enable SD GPIO clock */ + AUDIO_IN_SAIx_SD_ENABLE(); + /* CODEC_SAI pin configuration: SD pin */ + gpio_init_structure.Pin = AUDIO_IN_SAIx_SD_PIN; + gpio_init_structure.Mode = GPIO_MODE_AF_PP; + gpio_init_structure.Pull = GPIO_NOPULL; + gpio_init_structure.Speed = GPIO_SPEED_FAST; + gpio_init_structure.Alternate = AUDIO_IN_SAIx_AF; + HAL_GPIO_Init(AUDIO_IN_SAIx_SD_GPIO_PORT, &gpio_init_structure); + + /* Enable Audio INT GPIO clock */ + AUDIO_IN_INT_GPIO_ENABLE(); + /* Audio INT pin configuration: input */ + gpio_init_structure.Pin = AUDIO_IN_INT_GPIO_PIN; + gpio_init_structure.Mode = GPIO_MODE_INPUT; + gpio_init_structure.Pull = GPIO_NOPULL; + gpio_init_structure.Speed = GPIO_SPEED_FAST; + HAL_GPIO_Init(AUDIO_IN_INT_GPIO_PORT, &gpio_init_structure); + + /* Enable the DMA clock */ + AUDIO_IN_SAIx_DMAx_CLK_ENABLE(); + + if(hsai->Instance == AUDIO_IN_SAIx) + { + /* Configure the hdma_sai_rx handle parameters */ + hdma_sai_rx.Init.Channel = AUDIO_IN_SAIx_DMAx_CHANNEL; + hdma_sai_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; + hdma_sai_rx.Init.PeriphInc = DMA_PINC_DISABLE; + hdma_sai_rx.Init.MemInc = DMA_MINC_ENABLE; + hdma_sai_rx.Init.PeriphDataAlignment = AUDIO_IN_SAIx_DMAx_PERIPH_DATA_SIZE; + hdma_sai_rx.Init.MemDataAlignment = AUDIO_IN_SAIx_DMAx_MEM_DATA_SIZE; + hdma_sai_rx.Init.Mode = DMA_CIRCULAR; + hdma_sai_rx.Init.Priority = DMA_PRIORITY_HIGH; + hdma_sai_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; + hdma_sai_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; + hdma_sai_rx.Init.MemBurst = DMA_MBURST_SINGLE; + hdma_sai_rx.Init.PeriphBurst = DMA_MBURST_SINGLE; + + hdma_sai_rx.Instance = AUDIO_IN_SAIx_DMAx_STREAM; + + /* Associate the DMA handle */ + __HAL_LINKDMA(hsai, hdmarx, hdma_sai_rx); + + /* Deinitialize the Stream for new transfer */ + HAL_DMA_DeInit(&hdma_sai_rx); + + /* Configure the DMA Stream */ + HAL_DMA_Init(&hdma_sai_rx); + } + + /* SAI DMA IRQ Channel configuration */ + HAL_NVIC_SetPriority(AUDIO_IN_SAIx_DMAx_IRQ, AUDIO_IN_IRQ_PREPRIO, 0); + HAL_NVIC_EnableIRQ(AUDIO_IN_SAIx_DMAx_IRQ); + + /* Audio INT IRQ Channel configuration */ + HAL_NVIC_SetPriority(AUDIO_IN_INT_IRQ, AUDIO_IN_IRQ_PREPRIO, 0); + HAL_NVIC_EnableIRQ(AUDIO_IN_INT_IRQ); +} + +/** + * @brief De-Initializes SAI Audio IN MSP. + * @param hsai: SAI handle + * @param Params + * @retval None + */ +static void SAI_AUDIO_IN_MspDeInit(SAI_HandleTypeDef *hsai, void *Params) +{ + GPIO_InitTypeDef gpio_init_structure; + + /* SAI DMA IRQ Channel deactivation */ + HAL_NVIC_DisableIRQ(AUDIO_IN_SAIx_DMAx_IRQ); + + if(hsai->Instance == AUDIO_IN_SAIx) + { + /* Deinitialize the DMA stream */ + HAL_DMA_DeInit(hsai->hdmatx); + } + + /* Disable SAI peripheral */ + __HAL_SAI_DISABLE(hsai); + + /* Deactivates CODEC_SAI pin SD by putting them in input mode */ + gpio_init_structure.Pin = AUDIO_IN_SAIx_SD_PIN; + HAL_GPIO_DeInit(AUDIO_IN_SAIx_SD_GPIO_PORT, gpio_init_structure.Pin); + + gpio_init_structure.Pin = AUDIO_IN_INT_GPIO_PIN; + HAL_GPIO_DeInit(AUDIO_IN_INT_GPIO_PORT, gpio_init_structure.Pin); + + /* Disable SAI clock */ + AUDIO_IN_SAIx_CLK_DISABLE(); +} + +/** + * @brief Deinitializes SAI MSP. + * @param hsai: SAI handle + * @param Params + * @retval None + */ +__weak void BSP_AUDIO_OUT_MspDeInit(SAI_HandleTypeDef *hsai, void *Params) +{ + GPIO_InitTypeDef gpio_init_structure; + + /* SAI DMA IRQ Channel deactivation */ + HAL_NVIC_DisableIRQ(AUDIO_OUT_SAIx_DMAx_IRQ); + + if(hsai->Instance == AUDIO_OUT_SAIx) + { + /* Deinitialize the DMA stream */ + HAL_DMA_DeInit(hsai->hdmatx); + } + + /* Disable SAI peripheral */ + __HAL_SAI_DISABLE(hsai); + + /* Deactivates CODEC_SAI pins FS, SCK, MCK and SD by putting them in input mode */ + gpio_init_structure.Pin = AUDIO_OUT_SAIx_FS_PIN | AUDIO_OUT_SAIx_SCK_PIN | AUDIO_OUT_SAIx_SD_PIN; + HAL_GPIO_DeInit(AUDIO_OUT_SAIx_SD_FS_SCK_GPIO_PORT, gpio_init_structure.Pin); + + gpio_init_structure.Pin = AUDIO_OUT_SAIx_MCLK_PIN; + HAL_GPIO_DeInit(AUDIO_OUT_SAIx_MCLK_GPIO_PORT, gpio_init_structure.Pin); + + /* Disable SAI clock */ + AUDIO_OUT_SAIx_CLK_DISABLE(); + + /* GPIO pins clock and DMA clock can be shut down in the applic + by surcharging this __weak function */ +} + +/** + * @brief Clock Config. + * @param hsai: might be required to set audio peripheral predivider if any. + * @param AudioFreq: Audio frequency used to play the audio stream. + * @param Params + * @note This API is called by BSP_AUDIO_OUT_Init() and BSP_AUDIO_OUT_SetFrequency() + * Being __weak it can be overwritten by the application + * @retval None + */ +__weak void BSP_AUDIO_OUT_ClockConfig(SAI_HandleTypeDef *hsai, uint32_t AudioFreq, void *Params) +{ + RCC_PeriphCLKInitTypeDef rcc_ex_clk_init_struct; + + HAL_RCCEx_GetPeriphCLKConfig(&rcc_ex_clk_init_struct); + + /* Set the PLL configuration according to the audio frequency */ + if((AudioFreq == AUDIO_FREQUENCY_11K) || (AudioFreq == AUDIO_FREQUENCY_22K) || (AudioFreq == AUDIO_FREQUENCY_44K)) + { + /* Configure PLLSAI prescalers */ + /* PLLSAI_VCO: VCO_429M + SAI_CLK(first level) = PLLSAI_VCO/PLLSAIQ = 429/2 = 214.5 Mhz + SAI_CLK_x = SAI_CLK(first level)/PLLSAIDIVQ = 214.5/19 = 11.289 Mhz */ + rcc_ex_clk_init_struct.PeriphClockSelection = RCC_PERIPHCLK_SAI1; + rcc_ex_clk_init_struct.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLLI2S; + rcc_ex_clk_init_struct.PLLI2S.PLLI2SN = 429; + rcc_ex_clk_init_struct.PLLI2S.PLLI2SQ = 2; + rcc_ex_clk_init_struct.PLLI2SDivQ = 19; + + HAL_RCCEx_PeriphCLKConfig(&rcc_ex_clk_init_struct); + + } + else /* AUDIO_FREQUENCY_8K, AUDIO_FREQUENCY_16K, AUDIO_FREQUENCY_48K, AUDIO_FREQUENCY_96K */ + { + /* SAI clock config + PLLSAI_VCO: VCO_344M + SAI_CLK(first level) = PLLSAI_VCO/PLLSAIQ = 344/7 = 49.142 Mhz + SAI_CLK_x = SAI_CLK(first level)/PLLSAIDIVQ = 49.142/1 = 49.142 Mhz */ + rcc_ex_clk_init_struct.PeriphClockSelection = RCC_PERIPHCLK_SAI1; + rcc_ex_clk_init_struct.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLLI2S; + rcc_ex_clk_init_struct.PLLI2S.PLLI2SN = 344; + rcc_ex_clk_init_struct.PLLI2S.PLLI2SQ = 7; + rcc_ex_clk_init_struct.PLLI2SDivQ = 1; + + HAL_RCCEx_PeriphCLKConfig(&rcc_ex_clk_init_struct); + } +} + +/******************************************************************************* + Static Functions +*******************************************************************************/ + +/** + * @brief Initializes the Audio Codec audio interface (SAI). + * @param AudioFreq: Audio frequency to be configured for the SAI peripheral. + * @note The default SlotActive configuration is set to CODEC_AUDIOFRAME_SLOT_0123 + * and user can update this configuration using + * @retval None + */ +static void SAIx_Out_Init(uint32_t AudioFreq) +{ + /* Initialize the haudio_out_sai Instance parameter */ + haudio_out_sai.Instance = AUDIO_OUT_SAIx; + + /* Disable SAI peripheral to allow access to SAI internal registers */ + __HAL_SAI_DISABLE(&haudio_out_sai); + + /* Configure SAI_Block_x + LSBFirst: Disabled + DataSize: 16 */ + haudio_out_sai.Init.MonoStereoMode = SAI_STEREOMODE; + haudio_out_sai.Init.AudioFrequency = AudioFreq; + haudio_out_sai.Init.AudioMode = SAI_MODEMASTER_TX; + haudio_out_sai.Init.NoDivider = SAI_MASTERDIVIDER_ENABLED; + haudio_out_sai.Init.Protocol = SAI_FREE_PROTOCOL; + haudio_out_sai.Init.DataSize = SAI_DATASIZE_16; + haudio_out_sai.Init.FirstBit = SAI_FIRSTBIT_MSB; + haudio_out_sai.Init.ClockStrobing = SAI_CLOCKSTROBING_RISINGEDGE; + haudio_out_sai.Init.Synchro = SAI_ASYNCHRONOUS; + haudio_out_sai.Init.OutputDrive = SAI_OUTPUTDRIVE_ENABLED; + haudio_out_sai.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_1QF; + haudio_out_sai.Init.SynchroExt = SAI_SYNCEXT_DISABLE; + haudio_out_sai.Init.CompandingMode = SAI_NOCOMPANDING; + haudio_out_sai.Init.TriState = SAI_OUTPUT_NOTRELEASED; + haudio_out_sai.Init.Mckdiv = 0; + + /* Configure SAI_Block_x Frame + Frame Length: 64 + Frame active Length: 32 + FS Definition: Start frame + Channel Side identification + FS Polarity: FS active Low + FS Offset: FS asserted one bit before the first bit of slot 0 */ + haudio_out_sai.FrameInit.FrameLength = 128; + haudio_out_sai.FrameInit.ActiveFrameLength = 64; + haudio_out_sai.FrameInit.FSDefinition = SAI_FS_CHANNEL_IDENTIFICATION; + haudio_out_sai.FrameInit.FSPolarity = SAI_FS_ACTIVE_LOW; + haudio_out_sai.FrameInit.FSOffset = SAI_FS_BEFOREFIRSTBIT; + + /* Configure SAI Block_x Slot + Slot First Bit Offset: 0 + Slot Size : 16 + Slot Number: 4 + Slot Active: All slot actives */ + haudio_out_sai.SlotInit.FirstBitOffset = 0; + haudio_out_sai.SlotInit.SlotSize = SAI_SLOTSIZE_DATASIZE; + haudio_out_sai.SlotInit.SlotNumber = 4; + haudio_out_sai.SlotInit.SlotActive = CODEC_AUDIOFRAME_SLOT_0123; + + HAL_SAI_Init(&haudio_out_sai); + + /* Enable SAI peripheral to generate MCLK */ + __HAL_SAI_ENABLE(&haudio_out_sai); +} + +/** + * @brief Deinitializes the Audio Codec audio interface (SAI). + * @retval None + */ +static void SAIx_Out_DeInit(void) +{ + /* Initialize the haudio_out_sai Instance parameter */ + haudio_out_sai.Instance = AUDIO_OUT_SAIx; + + /* Disable SAI peripheral */ + __HAL_SAI_DISABLE(&haudio_out_sai); + + HAL_SAI_DeInit(&haudio_out_sai); +} + +/** + * @brief Initializes the Audio Codec audio interface (SAI). + * @param AudioFreq: Audio frequency to be configured for the SAI peripheral. + * @note The default SlotActive configuration is set to CODEC_AUDIOFRAME_SLOT_0123 + * and user can update this configuration using + * @retval None + */ +static void SAIx_In_Init(uint32_t AudioFreq) +{ + /* Initialize SAI1 block A in MASTER TX */ + /* Initialize the haudio_out_sai Instance parameter */ + haudio_out_sai.Instance = AUDIO_OUT_SAIx; + + /* Disable SAI peripheral to allow access to SAI internal registers */ + __HAL_SAI_DISABLE(&haudio_out_sai); + + /* Configure SAI_Block_x + LSBFirst: Disabled + DataSize: 16 */ + haudio_out_sai.Init.MonoStereoMode = SAI_STEREOMODE; + haudio_out_sai.Init.AudioFrequency = AudioFreq; + haudio_out_sai.Init.AudioMode = SAI_MODEMASTER_RX; + haudio_out_sai.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE; + haudio_out_sai.Init.Protocol = SAI_FREE_PROTOCOL; + haudio_out_sai.Init.DataSize = SAI_DATASIZE_16; + haudio_out_sai.Init.FirstBit = SAI_FIRSTBIT_MSB; + haudio_out_sai.Init.ClockStrobing = SAI_CLOCKSTROBING_FALLINGEDGE; + haudio_out_sai.Init.Synchro = SAI_ASYNCHRONOUS; + haudio_out_sai.Init.OutputDrive = SAI_OUTPUTDRIVE_ENABLE; + haudio_out_sai.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_1QF; + haudio_out_sai.Init.SynchroExt = SAI_SYNCEXT_DISABLE; + haudio_out_sai.Init.CompandingMode = SAI_NOCOMPANDING; + haudio_out_sai.Init.TriState = SAI_OUTPUT_NOTRELEASED; + haudio_out_sai.Init.Mckdiv = 0; + + /* Configure SAI_Block_x Frame + Frame Length: 64 + Frame active Length: 32 + FS Definition: Start frame + Channel Side identification + FS Polarity: FS active Low + FS Offset: FS asserted one bit before the first bit of slot 0 */ + haudio_out_sai.FrameInit.FrameLength = 64; + haudio_out_sai.FrameInit.ActiveFrameLength = 32; + haudio_out_sai.FrameInit.FSDefinition = SAI_FS_CHANNEL_IDENTIFICATION; + haudio_out_sai.FrameInit.FSPolarity = SAI_FS_ACTIVE_LOW; + haudio_out_sai.FrameInit.FSOffset = SAI_FS_BEFOREFIRSTBIT; + + /* Configure SAI Block_x Slot + Slot First Bit Offset: 0 + Slot Size : 16 + Slot Number: 4 + Slot Active: All slot actives */ + haudio_out_sai.SlotInit.FirstBitOffset = 0; + haudio_out_sai.SlotInit.SlotSize = SAI_SLOTSIZE_DATASIZE; + haudio_out_sai.SlotInit.SlotNumber = 4; + haudio_out_sai.SlotInit.SlotActive = CODEC_AUDIOFRAME_SLOT_02; + + HAL_SAI_Init(&haudio_out_sai); + + /* Initialize SAI1 block B in SLAVE RX synchronous from SAI1 block A */ + /* Initialize the haudio_in_sai Instance parameter */ + haudio_in_sai.Instance = AUDIO_IN_SAIx; + + /* Disable SAI peripheral to allow access to SAI internal registers */ + __HAL_SAI_DISABLE(&haudio_in_sai); + + /* Configure SAI_Block_x + LSBFirst: Disabled + DataSize: 16 */ + haudio_in_sai.Init.MonoStereoMode = SAI_STEREOMODE; + haudio_in_sai.Init.AudioFrequency = AudioFreq; + haudio_in_sai.Init.AudioMode = SAI_MODESLAVE_RX; + haudio_in_sai.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE; + haudio_in_sai.Init.Protocol = SAI_FREE_PROTOCOL; + haudio_in_sai.Init.DataSize = SAI_DATASIZE_16; + haudio_in_sai.Init.FirstBit = SAI_FIRSTBIT_MSB; + haudio_in_sai.Init.ClockStrobing = SAI_CLOCKSTROBING_RISINGEDGE; + haudio_in_sai.Init.Synchro = SAI_SYNCHRONOUS; + haudio_in_sai.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE; + haudio_in_sai.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_1QF; + haudio_in_sai.Init.SynchroExt = SAI_SYNCEXT_DISABLE; + haudio_in_sai.Init.CompandingMode = SAI_NOCOMPANDING; + haudio_in_sai.Init.TriState = SAI_OUTPUT_RELEASED; + haudio_in_sai.Init.Mckdiv = 0; + + /* Configure SAI_Block_x Frame + Frame Length: 64 + Frame active Length: 32 + FS Definition: Start frame + Channel Side identification + FS Polarity: FS active Low + FS Offset: FS asserted one bit before the first bit of slot 0 */ + haudio_in_sai.FrameInit.FrameLength = 64; + haudio_in_sai.FrameInit.ActiveFrameLength = 32; + haudio_in_sai.FrameInit.FSDefinition = SAI_FS_CHANNEL_IDENTIFICATION; + haudio_in_sai.FrameInit.FSPolarity = SAI_FS_ACTIVE_LOW; + haudio_in_sai.FrameInit.FSOffset = SAI_FS_BEFOREFIRSTBIT; + + /* Configure SAI Block_x Slot + Slot First Bit Offset: 0 + Slot Size : 16 + Slot Number: 4 + Slot Active: All slot active */ + haudio_in_sai.SlotInit.FirstBitOffset = 0; + haudio_in_sai.SlotInit.SlotSize = SAI_SLOTSIZE_DATASIZE; + haudio_in_sai.SlotInit.SlotNumber = 4; + haudio_in_sai.SlotInit.SlotActive = CODEC_AUDIOFRAME_SLOT_02; + + HAL_SAI_Init(&haudio_in_sai); + + /* Enable SAI peripheral */ + __HAL_SAI_ENABLE(&haudio_in_sai); + + /* Enable SAI peripheral to generate MCLK */ + __HAL_SAI_ENABLE(&haudio_out_sai); +} + +/** + * @brief Deinitializes the output Audio Codec audio interface (SAI). + * @retval None + */ +static void SAIx_In_DeInit(void) +{ + /* Initialize the haudio_in_sai Instance parameter */ + haudio_in_sai.Instance = AUDIO_IN_SAIx; + haudio_out_sai.Instance = AUDIO_OUT_SAIx; + /* Disable SAI peripheral */ + __HAL_SAI_DISABLE(&haudio_in_sai); + + HAL_SAI_DeInit(&haudio_in_sai); + HAL_SAI_DeInit(&haudio_out_sai); +} + +/** + * @} + */ + +/** @defgroup STM32F769I_DISCOVERY_AUDIO_In_Private_Functions STM32F769I_DISCOVERY_AUDIO_In Private Functions + * @{ + */ + +/** + * @brief Initialize wave recording. + * @param AudioFreq: Audio frequency to be configured for the DFSDM peripheral. + * @param BitRes: Audio frequency to be configured for the DFSDM peripheral. + * @param ChnlNbr: Audio frequency to be configured for the DFSDM peripheral. + * @retval AUDIO_OK if correct communication, else wrong communication + */ +uint8_t BSP_AUDIO_IN_Init(uint32_t AudioFreq, uint32_t BitRes, uint32_t ChnlNbr) +{ + return BSP_AUDIO_IN_InitEx(INPUT_DEVICE_DIGITAL_MIC, AudioFreq, BitRes, ChnlNbr); +} + +/** + * @brief Initialize wave recording. + * @param InputDevice: INPUT_DEVICE_DIGITAL_MIC or INPUT_DEVICE_ANALOG_MIC. + * @param AudioFreq: Audio frequency to be configured. + * @param BitRes: Audio bit resolution to be configured.. + * @param ChnlNbr: Number of channel to be configured. + * @retval AUDIO_OK if correct communication, else wrong communication + */ +uint8_t BSP_AUDIO_IN_InitEx(uint16_t InputDevice, uint32_t AudioFreq, uint32_t BitRes, uint32_t ChnlNbr) +{ + uint8_t ret = AUDIO_ERROR; + AudioIn_Device = InputDevice; + + if(InputDevice == INPUT_DEVICE_DIGITAL_MIC) + { + AudioIn_ChannelNumber = ChnlNbr; + /* PLL clock is set depending by the AudioFreq (44.1khz vs 48khz groups) */ + BSP_AUDIO_IN_ClockConfig(&hAudioInTopLeftFilter, AudioFreq, NULL); + + /* Init the SAI MSP: this __weak function can be redefined by the application*/ + BSP_AUDIO_IN_MspInit(); + + /* Initializes DFSDM peripheral */ + DFSDMx_Init(AudioFreq); + ret = AUDIO_OK; + } + else + { + /* Disable SAI */ + SAIx_In_DeInit(); + + /* PLL clock is set depending by the AudioFreq (44.1khz vs 48khz groups) */ + BSP_AUDIO_OUT_ClockConfig(&haudio_in_sai, AudioFreq, NULL); + + haudio_in_sai.Instance = AUDIO_IN_SAIx; + if(HAL_SAI_GetState(&haudio_in_sai) == HAL_SAI_STATE_RESET) + { + BSP_AUDIO_OUT_MspInit(&haudio_in_sai, NULL); + BSP_AUDIO_IN_MspInit(); + } + + SAIx_In_Init(AudioFreq); + + if((wm8994_drv.ReadID(AUDIO_I2C_ADDRESS)) == WM8994_ID) + { + /* Reset the Codec Registers */ + wm8994_drv.Reset(AUDIO_I2C_ADDRESS); + /* Initialize the audio driver structure */ + audio_drv = &wm8994_drv; + ret = AUDIO_OK; + } + else + { + ret = AUDIO_ERROR; + } + + if(ret == AUDIO_OK) + { + /* Initialize the codec internal registers */ + audio_drv->Init(AUDIO_I2C_ADDRESS, InputDevice, 100, AudioFreq); + } + } + + /* Return AUDIO_OK when all operations are correctly done */ + return ret; +} + +/** + * @brief Allocate channel buffer scratch + * @param pScratch : pointer to scratch tables. + * @param size of scratch buffer + */ +uint8_t BSP_AUDIO_IN_AllocScratch (int32_t *pScratch, uint32_t size) +{ + uint32_t idx; + + ScratchSize = (size / AudioIn_ChannelNumber); + + /* copy scratch pointers */ + for (idx = 0; idx < AudioIn_ChannelNumber; idx++) + { + pScratchBuff[idx] = (int32_t *)(pScratch + (idx * ScratchSize)); + } + /* Return AUDIO_OK */ + return AUDIO_OK; +} + +/** + * @brief Return audio in channel number + * @retval Number of channel + */ +uint8_t BSP_AUDIO_IN_GetChannelNumber(void) +{ + return AudioIn_ChannelNumber; +} + +/** + * @brief Start audio recording. + * @param pbuf: Main buffer pointer for the recorded data storing + * @param size: Current size of the recorded buffer + * @retval AUDIO_OK if correct communication, else wrong communication + */ +uint8_t BSP_AUDIO_IN_Record(uint16_t* pbuf, uint32_t size) +{ + if (AudioIn_Device == INPUT_DEVICE_DIGITAL_MIC) + { + hAudioIn.pRecBuf = pbuf; + hAudioIn.RecSize = size; + /* Reset Application Buffer Trigger */ + AppBuffTrigger = 0; + AppBuffHalf = 0; + + if(AudioIn_ChannelNumber > 2) + { + /* Call the Media layer start function for buttom right channel */ + if(HAL_OK != HAL_DFSDM_FilterRegularStart_DMA(&hAudioInButtomRightFilter, pScratchBuff[2], ScratchSize)) + { + return AUDIO_ERROR; + } + + /* Call the Media layer start function for buttom left channel */ + if(HAL_OK != HAL_DFSDM_FilterRegularStart_DMA(&hAudioInButtomLeftFilter, pScratchBuff[3], ScratchSize)) + { + return AUDIO_ERROR; + } + } + + /* Call the Media layer start function for top right channel */ + if(HAL_OK != HAL_DFSDM_FilterRegularStart_DMA(&hAudioInTopRightFilter, pScratchBuff[0], ScratchSize)) + { + return AUDIO_ERROR; + } + + /* Call the Media layer start function for top left channel */ + if(HAL_OK != HAL_DFSDM_FilterRegularStart_DMA(&hAudioInTopLeftFilter, pScratchBuff[1], ScratchSize)) + { + return AUDIO_ERROR; + } + } + else + { + /* Start the process receive DMA */ + if(HAL_OK !=HAL_SAI_Receive_DMA(&haudio_in_sai, (uint8_t*)pbuf, size)) + { + return AUDIO_ERROR; + } + } + /* Return AUDIO_OK when all operations are correctly done */ + return AUDIO_OK; +} + +/** + * @brief Stop audio recording. + * @retval AUDIO_OK if correct communication, else wrong communication + */ +uint8_t BSP_AUDIO_IN_Stop(void) +{ + if (AudioIn_Device == INPUT_DEVICE_DIGITAL_MIC) + { + AppBuffTrigger = 0; + AppBuffHalf = 0; + + if(AudioIn_ChannelNumber > 2) + { + /* Call the Media layer stop function for buttom right channel */ + if(HAL_OK != HAL_DFSDM_FilterRegularStop_DMA(&hAudioInButtomRightFilter)) + { + return AUDIO_ERROR; + } + + /* Call the Media layer stop function for buttom left channel */ + if(HAL_OK != HAL_DFSDM_FilterRegularStop_DMA(&hAudioInButtomLeftFilter)) + { + return AUDIO_ERROR; + } + } + + /* Call the Media layer stop function for top right channel */ + if(HAL_OK != HAL_DFSDM_FilterRegularStop_DMA(&hAudioInTopRightFilter)) + { + return AUDIO_ERROR; + } + + /* Call the Media layer stop function for top left channel */ + if(HAL_OK != HAL_DFSDM_FilterRegularStop_DMA(&hAudioInTopLeftFilter)) + { + return AUDIO_ERROR; + } + } + else + { + /* Call the Media layer stop function */ + HAL_SAI_DMAStop(&haudio_in_sai); + + /* Call Audio Codec Stop function */ + if(audio_drv->Stop(AUDIO_I2C_ADDRESS, CODEC_PDWN_HW) != 0) + { + return AUDIO_ERROR; + } + else + { + /* Wait at least 100us */ + HAL_Delay(1); + + /* Return AUDIO_OK when all operations are correctly done */ + return AUDIO_OK; + } + } + /* Return AUDIO_OK when all operations are correctly done */ + return AUDIO_OK; +} + +/** + * @brief Pause the audio file stream. + * @retval AUDIO_OK if correct communication, else wrong communication + */ +uint8_t BSP_AUDIO_IN_Pause(void) +{ + if(AudioIn_ChannelNumber > 2) + { + /* Call the Media layer stop function */ + if(HAL_OK != HAL_DFSDM_FilterRegularStop_DMA(&hAudioInButtomRightFilter)) + { + return AUDIO_ERROR; + } + + /* Call the Media layer stop function */ + if(HAL_OK != HAL_DFSDM_FilterRegularStop_DMA(&hAudioInButtomLeftFilter)) + { + return AUDIO_ERROR; + } + } + /* Call the Media layer stop function */ + if(HAL_OK != HAL_DFSDM_FilterRegularStop_DMA(&hAudioInTopRightFilter)) + { + return AUDIO_ERROR; + } + + /* Call the Media layer stop function */ + if(HAL_OK != HAL_DFSDM_FilterRegularStop_DMA(&hAudioInTopLeftFilter)) + { + return AUDIO_ERROR; + } + + /* Return AUDIO_OK when all operations are correctly done */ + return AUDIO_OK; +} + +/** + * @brief Resume the audio file stream. + * @retval AUDIO_OK if correct communication, else wrong communication + */ +uint8_t BSP_AUDIO_IN_Resume(void) +{ + if(AudioIn_ChannelNumber > 2) + { + /* Call the Media layer start function for buttom right channel */ + if(HAL_OK != HAL_DFSDM_FilterRegularStart_DMA(&hAudioInButtomRightFilter, pScratchBuff[2], ScratchSize)) + { + return AUDIO_ERROR; + } + + /* Call the Media layer start function for buttom left channel */ + if(HAL_OK != HAL_DFSDM_FilterRegularStart_DMA(&hAudioInButtomLeftFilter, pScratchBuff[3], ScratchSize)) + { + return AUDIO_ERROR; + } + } + /* Call the Media layer start function for top right channel */ + if(HAL_OK != HAL_DFSDM_FilterRegularStart_DMA(&hAudioInTopRightFilter, pScratchBuff[0], ScratchSize)) + { + return AUDIO_ERROR; + } + + /* Call the Media layer start function for top left channel */ + if(HAL_OK != HAL_DFSDM_FilterRegularStart_DMA(&hAudioInTopLeftFilter, pScratchBuff[1], ScratchSize)) + { + return AUDIO_ERROR; + } + + /* Return AUDIO_OK when all operations are correctly done */ + return AUDIO_OK; +} + +/** + * @brief Deinit the audio IN peripherals. + * @retval None + */ +void BSP_AUDIO_IN_DeInit(void) +{ + BSP_AUDIO_IN_MspDeInit(); + + if(AudioIn_Device == INPUT_DEVICE_DIGITAL_MIC) + { + DFSDMx_DeInit(); + } + else + { + SAIx_In_DeInit(); + } +} + +/** + * @brief Regular conversion complete callback. + * @note In interrupt mode, user has to read conversion value in this function + using HAL_DFSDM_FilterGetRegularValue. + * @param hdfsdm_filter : DFSDM filter handle. + * @retval None + */ +void HAL_DFSDM_FilterRegConvCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter) +{ + uint32_t index = 0; + + if(hdfsdm_filter == &hAudioInTopLeftFilter) + { + DmaTopLeftRecCplt = 1; + } + else if(hdfsdm_filter == &hAudioInTopRightFilter) + { + DmaTopRightRecCplt = 1; + } + else if(hdfsdm_filter == &hAudioInButtomLeftFilter) + { + DmaButtomLeftRecCplt = 1; + } + else + { + DmaButtomRightRecCplt = 1; + } + + if(AudioIn_ChannelNumber > 2) + { + if((DmaTopLeftRecCplt == 1) && (DmaTopRightRecCplt == 1) && (DmaButtomLeftRecCplt == 1) && (DmaButtomRightRecCplt == 1)) + { + for(index = (ScratchSize/2) ; index < ScratchSize; index++) + { + hAudioIn.pRecBuf[AppBuffTrigger] = (uint16_t)(SaturaLH((pScratchBuff[1][index] >> 8), -32760, 32760)); + hAudioIn.pRecBuf[AppBuffTrigger + 1] = (uint16_t)(SaturaLH((pScratchBuff[0][index] >> 8), -32760, 32760)); + hAudioIn.pRecBuf[AppBuffTrigger + 2] = (uint16_t)(SaturaLH((pScratchBuff[3][index] >> 8), -32760, 32760)); + hAudioIn.pRecBuf[AppBuffTrigger + 3] = (uint16_t)(SaturaLH((pScratchBuff[2][index] >> 8), -32760, 32760)); + AppBuffTrigger +=4; + } + DmaTopLeftRecCplt = 0; + DmaTopRightRecCplt = 0; + DmaButtomLeftRecCplt = 0; + DmaButtomRightRecCplt = 0; + } + } + else + { + if((DmaTopLeftRecCplt == 1) && (DmaTopRightRecCplt == 1)) + { + for(index = (ScratchSize/2) ; index < ScratchSize; index++) + { + hAudioIn.pRecBuf[AppBuffTrigger] = (uint16_t)(SaturaLH((pScratchBuff[1][index] >> 8), -32760, 32760)); + hAudioIn.pRecBuf[AppBuffTrigger + 1] = (uint16_t)(SaturaLH((pScratchBuff[0][index] >> 8), -32760, 32760)); + AppBuffTrigger +=2; + } + DmaTopLeftRecCplt = 0; + DmaTopRightRecCplt = 0; + } + } + + /* Call Half Transfer Complete callback */ + if((AppBuffTrigger == hAudioIn.RecSize/2) && (AppBuffHalf == 0)) + { + AppBuffHalf = 1; + BSP_AUDIO_IN_HalfTransfer_CallBack(); + } + /* Call Transfer Complete callback */ + if(AppBuffTrigger == hAudioIn.RecSize) + { + /* Reset Application Buffer Trigger */ + AppBuffTrigger = 0; + AppBuffHalf = 0; + /* Call the record update function to get the next buffer to fill and its size (size is ignored) */ + BSP_AUDIO_IN_TransferComplete_CallBack(); + } +} + +/** + * @brief Half regular conversion complete callback. + * @param hdfsdm_filter : DFSDM filter handle. + * @retval None + */ +void HAL_DFSDM_FilterRegConvHalfCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter) +{ + uint32_t index = 0; + + if(hdfsdm_filter == &hAudioInTopLeftFilter) + { + DmaTopLeftRecHalfCplt = 1; + } + else if(hdfsdm_filter == &hAudioInTopRightFilter) + { + DmaTopRightRecHalfCplt = 1; + } + else if(hdfsdm_filter == &hAudioInButtomLeftFilter) + { + DmaButtomLeftRecHalfCplt = 1; + } + else + { + DmaButtomRightRecHalfCplt = 1; + } + + if(AudioIn_ChannelNumber > 2) + { + if((DmaTopLeftRecHalfCplt == 1) && (DmaTopRightRecHalfCplt == 1) && (DmaButtomLeftRecHalfCplt == 1) && (DmaButtomRightRecHalfCplt == 1)) + { + for(index = 0 ; index < ScratchSize/2; index++) + { + hAudioIn.pRecBuf[AppBuffTrigger] = (uint16_t)(SaturaLH((pScratchBuff[1][index] >> 8), -32760, 32760)); + hAudioIn.pRecBuf[AppBuffTrigger + 1] = (uint16_t)(SaturaLH((pScratchBuff[0][index] >> 8), -32760, 32760)); + hAudioIn.pRecBuf[AppBuffTrigger + 2] = (uint16_t)(SaturaLH((pScratchBuff[3][index] >> 8), -32760, 32760)); + hAudioIn.pRecBuf[AppBuffTrigger + 3] = (uint16_t)(SaturaLH((pScratchBuff[2][index] >> 8), -32760, 32760)); + AppBuffTrigger +=4; + } + DmaTopLeftRecHalfCplt = 0; + DmaTopRightRecHalfCplt = 0; + DmaButtomLeftRecHalfCplt = 0; + DmaButtomRightRecHalfCplt = 0; + } + } + else + { + if((DmaTopLeftRecHalfCplt == 1) && (DmaTopRightRecHalfCplt == 1)) + { + for(index = 0 ; index < ScratchSize/2; index++) + { + hAudioIn.pRecBuf[AppBuffTrigger] = (uint16_t)(SaturaLH((pScratchBuff[1][index] >> 8), -32760, 32760)); + hAudioIn.pRecBuf[AppBuffTrigger + 1] = (uint16_t)(SaturaLH((pScratchBuff[0][index] >> 8), -32760, 32760)); + AppBuffTrigger +=2; + } + DmaTopLeftRecHalfCplt = 0; + DmaTopRightRecHalfCplt = 0; + } + } + + /* Call Half Transfer Complete callback */ + if((AppBuffTrigger == hAudioIn.RecSize/2) && (AppBuffHalf == 0)) + { + AppBuffHalf = 1; + BSP_AUDIO_IN_HalfTransfer_CallBack(); + } + /* Call Transfer Complete callback */ + if(AppBuffTrigger == hAudioIn.RecSize) + { + /* Reset Application Buffer Trigger */ + AppBuffTrigger = 0; + AppBuffHalf = 0; + /* Call the record update function to get the next buffer to fill and its size (size is ignored) */ + BSP_AUDIO_IN_TransferComplete_CallBack(); + } +} + +/** + * @brief Half reception complete callback. + * @param hsai : SAI handle. + * @retval None + */ +void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hsai) +{ + /* Manage the remaining file size and new address offset: This function + should be coded by user (its prototype is already declared in stm32769i_discovery_audio.h) */ + BSP_AUDIO_IN_HalfTransfer_CallBack(); +} + +/** + * @brief Reception complete callback. + * @param hsai : SAI handle. + * @retval None + */ +void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai) +{ + /* Call the record update function to get the next buffer to fill and its size (size is ignored) */ + BSP_AUDIO_IN_TransferComplete_CallBack(); +} + +/** + * @brief User callback when record buffer is filled. + * @retval None + */ +__weak void BSP_AUDIO_IN_TransferComplete_CallBack(void) +{ + /* This function should be implemented by the user application. + It is called into this driver when the current buffer is filled + to prepare the next buffer pointer and its size. */ +} + +/** + * @brief Manages the DMA Half Transfer complete event. + * @retval None + */ +__weak void BSP_AUDIO_IN_HalfTransfer_CallBack(void) +{ + /* This function should be implemented by the user application. + It is called into this driver when the current buffer is filled + to prepare the next buffer pointer and its size. */ +} + +/** + * @brief Audio IN Error callback function. + * @retval None + */ +__weak void BSP_AUDIO_IN_Error_CallBack(void) +{ + /* This function is called when an Interrupt due to transfer error on or peripheral + error occurs. */ +} + +/** + * @brief Initialize BSP_AUDIO_IN MSP. + * @retval None + */ +__weak void BSP_AUDIO_IN_MspInit(void) +{ + if (AudioIn_Device == INPUT_DEVICE_DIGITAL_MIC) + { + /* MSP channels initialization */ + DFSDMx_ChannelMspInit(); + /* MSP filters initialization */ + DFSDMx_FilterMspInit(); + } + else + { + SAI_AUDIO_IN_MspInit(&haudio_in_sai, NULL); + } +} + +/** + * @brief DeInitialize BSP_AUDIO_IN MSP. + * @retval None + */ +__weak void BSP_AUDIO_IN_MspDeInit(void) +{ + if (AudioIn_Device == INPUT_DEVICE_DIGITAL_MIC) + { + /* MSP channels initialization */ + DFSDMx_ChannelMspDeInit(); + /* MSP filters initialization */ + DFSDMx_FilterMspDeInit(); + } + else + { + SAI_AUDIO_IN_MspDeInit(&haudio_in_sai, NULL); + } +} + +/** + * @brief Clock Config. + * @param hdfsdm_filter: might be required to set audio peripheral predivider if any. + * @param AudioFreq: Audio frequency used to play the audio stream. + * @param Params + * @note This API is called by BSP_AUDIO_IN_Init() + * Being __weak it can be overwritten by the application + * @retval None + */ +__weak void BSP_AUDIO_IN_ClockConfig(DFSDM_Filter_HandleTypeDef *hdfsdm_filter, uint32_t AudioFreq, void *Params) +{ + RCC_PeriphCLKInitTypeDef rcc_ex_clk_init_struct; + + HAL_RCCEx_GetPeriphCLKConfig(&rcc_ex_clk_init_struct); + + /* Set the PLL configuration according to the audio frequency */ + if((AudioFreq == AUDIO_FREQUENCY_11K) || (AudioFreq == AUDIO_FREQUENCY_22K) || (AudioFreq == AUDIO_FREQUENCY_44K)) + { + /* Configure PLLSAI prescalers */ + /* PLLI2S_VCO: VCO_429M + SAI_CLK(first level) = PLLI2S_VCO/PLLI2SQ = 429/2 = 214.5 Mhz + SAI_CLK_x = SAI_CLK(first level)/PLLI2SDIVQ = 214.5/19 = 11.289 Mhz */ + rcc_ex_clk_init_struct.PeriphClockSelection = RCC_PERIPHCLK_SAI2; + rcc_ex_clk_init_struct.Sai2ClockSelection = RCC_SAI2CLKSOURCE_PLLI2S; + rcc_ex_clk_init_struct.PLLI2S.PLLI2SN = 429; + rcc_ex_clk_init_struct.PLLI2S.PLLI2SQ = 2; + rcc_ex_clk_init_struct.PLLI2SDivQ = 19; + + HAL_RCCEx_PeriphCLKConfig(&rcc_ex_clk_init_struct); + + } + else /* AUDIO_FREQUENCY_8K, AUDIO_FREQUENCY_16K, AUDIO_FREQUENCY_32K, AUDIO_FREQUENCY_48K, AUDIO_FREQUENCY_96K */ + { + /* SAI clock config + PLLI2S_VCO: VCO_344M + SAI_CLK(first level) = PLLI2S_VCO/PLLI2SQ = 344/7 = 49.142 Mhz + SAI_CLK_x = SAI_CLK(first level)/PLLI2SDIVQ = 49.142/1 = 49.142 Mhz */ + rcc_ex_clk_init_struct.PeriphClockSelection = RCC_PERIPHCLK_SAI2; + rcc_ex_clk_init_struct.Sai2ClockSelection = RCC_SAI2CLKSOURCE_PLLI2S; + rcc_ex_clk_init_struct.PLLI2S.PLLI2SN = 344; + rcc_ex_clk_init_struct.PLLI2S.PLLI2SQ = 7; + rcc_ex_clk_init_struct.PLLI2SDivQ = 1; + HAL_RCCEx_PeriphCLKConfig(&rcc_ex_clk_init_struct); + } + + rcc_ex_clk_init_struct.PeriphClockSelection = RCC_PERIPHCLK_DFSDM1_AUDIO; + rcc_ex_clk_init_struct.Dfsdm1AudioClockSelection = RCC_DFSDM1AUDIOCLKSOURCE_SAI2; + HAL_RCCEx_PeriphCLKConfig(&rcc_ex_clk_init_struct); +} + +/******************************************************************************* + Static Functions +*******************************************************************************/ +/** + * @brief Initialize the Digital Filter for Sigma-Delta Modulators interface (DFSDM). + * @param AudioFreq: Audio frequency to be used to set correctly the DFSDM peripheral. + * @note Channel output Clock Divider and Filter Oversampling are calculated as follow: + * - Clock_Divider = CLK(input DFSDM)/CLK(micro) with + * 1MHZ < CLK(micro) < 3.2MHZ (TYP 2.4MHZ for MP34DT01TR) + * - Oversampling = CLK(input DFSDM)/(Clock_Divider * AudioFreq) + * @retval AUDIO_OK if correct communication, else wrong communication + */ +static uint8_t DFSDMx_Init(uint32_t AudioFreq) +{ + /****************************************************************************/ + /********************** Channels configuration *****************************/ + /****************************************************************************/ + /* CHANNEL 1 configuration */ + __HAL_DFSDM_CHANNEL_RESET_HANDLE_STATE(&hAudioInTopLeftChannel); + hAudioInTopLeftChannel.Instance = DFSDM1_Channel1; + hAudioInTopLeftChannel.Init.OutputClock.Activation = ENABLE; + hAudioInTopLeftChannel.Init.OutputClock.Selection = DFSDM_CHANNEL_OUTPUT_CLOCK_AUDIO; + /* Set the DFSDM clock OUT audio frequency configuration */ + hAudioInTopLeftChannel.Init.OutputClock.Divider = DFSDM_CLOCK_DIVIDER(AudioFreq); + hAudioInTopLeftChannel.Init.Input.Multiplexer = DFSDM_CHANNEL_EXTERNAL_INPUTS; + hAudioInTopLeftChannel.Init.Input.DataPacking = DFSDM_CHANNEL_STANDARD_MODE; + hAudioInTopLeftChannel.Init.Input.Pins = DFSDM_CHANNEL_SAME_CHANNEL_PINS; + /* Request to sample stable data for LEFT micro on Rising edge */ + hAudioInTopLeftChannel.Init.SerialInterface.Type = DFSDM_CHANNEL_SPI_RISING; + hAudioInTopLeftChannel.Init.SerialInterface.SpiClock = DFSDM_CHANNEL_SPI_CLOCK_INTERNAL; + hAudioInTopLeftChannel.Init.Awd.FilterOrder = DFSDM_CHANNEL_FASTSINC_ORDER; + hAudioInTopLeftChannel.Init.Awd.Oversampling = 10; + hAudioInTopLeftChannel.Init.Offset = 0; + hAudioInTopLeftChannel.Init.RightBitShift = DFSDM_RIGHT_BIT_SHIFT(AudioFreq); + if(HAL_OK != HAL_DFSDM_ChannelInit(&hAudioInTopLeftChannel)) + { + return AUDIO_ERROR; + } + + /* CHANNEL 0 configuration */ + __HAL_DFSDM_CHANNEL_RESET_HANDLE_STATE(&hAudioInTopRightChannel); + hAudioInTopRightChannel.Instance = DFSDM1_Channel0; + hAudioInTopRightChannel.Init.OutputClock.Activation = ENABLE; + hAudioInTopRightChannel.Init.OutputClock.Selection = DFSDM_CHANNEL_OUTPUT_CLOCK_AUDIO; + /* Set the DFSDM clock OUT audio frequency configuration */ + hAudioInTopRightChannel.Init.OutputClock.Divider = DFSDM_CLOCK_DIVIDER(AudioFreq); + hAudioInTopRightChannel.Init.Input.Multiplexer = DFSDM_CHANNEL_EXTERNAL_INPUTS; + hAudioInTopRightChannel.Init.Input.DataPacking = DFSDM_CHANNEL_STANDARD_MODE; + hAudioInTopRightChannel.Init.Input.Pins = DFSDM_CHANNEL_FOLLOWING_CHANNEL_PINS; + /* Request to sample stable data for RIGHT micro on Falling edge */ + hAudioInTopRightChannel.Init.SerialInterface.Type = DFSDM_CHANNEL_SPI_FALLING; + hAudioInTopRightChannel.Init.SerialInterface.SpiClock = DFSDM_CHANNEL_SPI_CLOCK_INTERNAL; + hAudioInTopRightChannel.Init.Awd.FilterOrder = DFSDM_CHANNEL_FASTSINC_ORDER; + hAudioInTopRightChannel.Init.Awd.Oversampling = 10; + hAudioInTopRightChannel.Init.Offset = 0; + hAudioInTopRightChannel.Init.RightBitShift = DFSDM_RIGHT_BIT_SHIFT(AudioFreq); + if(HAL_OK != HAL_DFSDM_ChannelInit(&hAudioInTopRightChannel)) + { + return AUDIO_ERROR; + } + + if(AudioIn_ChannelNumber > 2) + { + /* CHANNEL 5 configuration */ + __HAL_DFSDM_CHANNEL_RESET_HANDLE_STATE(&hAudioInButtomLeftChannel); + hAudioInButtomLeftChannel.Instance = DFSDM1_Channel5; + hAudioInButtomLeftChannel.Init.OutputClock.Activation = ENABLE; + hAudioInButtomLeftChannel.Init.OutputClock.Selection = DFSDM_CHANNEL_OUTPUT_CLOCK_AUDIO; + /* Set the DFSDM clock OUT audio frequency configuration */ + hAudioInButtomLeftChannel.Init.OutputClock.Divider = DFSDM_CLOCK_DIVIDER(AudioFreq); + hAudioInButtomLeftChannel.Init.Input.Multiplexer = DFSDM_CHANNEL_EXTERNAL_INPUTS; + hAudioInButtomLeftChannel.Init.Input.DataPacking = DFSDM_CHANNEL_STANDARD_MODE; + hAudioInButtomLeftChannel.Init.Input.Pins = DFSDM_CHANNEL_SAME_CHANNEL_PINS; + /* Request to sample stable data for LEFT micro on Rising edge */ + hAudioInButtomLeftChannel.Init.SerialInterface.Type = DFSDM_CHANNEL_SPI_RISING; + hAudioInButtomLeftChannel.Init.SerialInterface.SpiClock = DFSDM_CHANNEL_SPI_CLOCK_INTERNAL; + hAudioInButtomLeftChannel.Init.Awd.FilterOrder = DFSDM_CHANNEL_FASTSINC_ORDER; + hAudioInButtomLeftChannel.Init.Awd.Oversampling = 10; + hAudioInButtomLeftChannel.Init.Offset = 0; + hAudioInButtomLeftChannel.Init.RightBitShift = DFSDM_RIGHT_BIT_SHIFT(AudioFreq); + if(HAL_OK != HAL_DFSDM_ChannelInit(&hAudioInButtomLeftChannel)) + { + return AUDIO_ERROR; + } + + /* CHANNEL 4 configuration */ + __HAL_DFSDM_CHANNEL_RESET_HANDLE_STATE(&hAudioInButtomRightChannel); + hAudioInButtomRightChannel.Instance = DFSDM1_Channel4; + hAudioInButtomRightChannel.Init.OutputClock.Activation = ENABLE; + hAudioInButtomRightChannel.Init.OutputClock.Selection = DFSDM_CHANNEL_OUTPUT_CLOCK_AUDIO; + /* Set the DFSDM clock OUT audio frequency configuration */ + hAudioInButtomRightChannel.Init.OutputClock.Divider = DFSDM_CLOCK_DIVIDER(AudioFreq); + hAudioInButtomRightChannel.Init.Input.Multiplexer = DFSDM_CHANNEL_EXTERNAL_INPUTS; + hAudioInButtomRightChannel.Init.Input.DataPacking = DFSDM_CHANNEL_STANDARD_MODE; + hAudioInButtomRightChannel.Init.Input.Pins = DFSDM_CHANNEL_FOLLOWING_CHANNEL_PINS; + /* Request to sample stable data for RIGHT micro on Falling edge */ + hAudioInButtomRightChannel.Init.SerialInterface.Type = DFSDM_CHANNEL_SPI_FALLING; + hAudioInButtomRightChannel.Init.SerialInterface.SpiClock = DFSDM_CHANNEL_SPI_CLOCK_INTERNAL; + hAudioInButtomRightChannel.Init.Awd.FilterOrder = DFSDM_CHANNEL_FASTSINC_ORDER; + hAudioInButtomRightChannel.Init.Awd.Oversampling = 10; + hAudioInButtomRightChannel.Init.Offset = 0; + hAudioInButtomRightChannel.Init.RightBitShift = DFSDM_RIGHT_BIT_SHIFT(AudioFreq); + if(HAL_OK != HAL_DFSDM_ChannelInit(&hAudioInButtomRightChannel)) + { + return AUDIO_ERROR; + } + } + /****************************************************************************/ + /********************** Filters configuration ******************************/ + /****************************************************************************/ + + /* FILTER 0 configuration */ + __HAL_DFSDM_FILTER_RESET_HANDLE_STATE(&hAudioInTopLeftFilter); + hAudioInTopLeftFilter.Instance = AUDIO_DFSDMx_TOP_LEFT_FILTER; + hAudioInTopLeftFilter.Init.RegularParam.Trigger = DFSDM_FILTER_SW_TRIGGER; + hAudioInTopLeftFilter.Init.RegularParam.FastMode = ENABLE; + hAudioInTopLeftFilter.Init.RegularParam.DmaMode = ENABLE; + hAudioInTopLeftFilter.Init.InjectedParam.Trigger = DFSDM_FILTER_SW_TRIGGER; + hAudioInTopLeftFilter.Init.InjectedParam.ScanMode = ENABLE; + hAudioInTopLeftFilter.Init.InjectedParam.DmaMode = DISABLE; + hAudioInTopLeftFilter.Init.InjectedParam.ExtTrigger = DFSDM_FILTER_EXT_TRIG_TIM1_TRGO; + hAudioInTopLeftFilter.Init.InjectedParam.ExtTriggerEdge = DFSDM_FILTER_EXT_TRIG_RISING_EDGE; + hAudioInTopLeftFilter.Init.FilterParam.SincOrder = DFSDM_FILTER_ORDER(AudioFreq); + /* Set the DFSDM Filters Oversampling to have correct sample rate */ + hAudioInTopLeftFilter.Init.FilterParam.Oversampling = DFSDM_OVER_SAMPLING(AudioFreq); + hAudioInTopLeftFilter.Init.FilterParam.IntOversampling = 1; + if(HAL_OK != HAL_DFSDM_FilterInit(&hAudioInTopLeftFilter)) + { + return AUDIO_ERROR; + } + + /* Configure injected channel */ + if(HAL_OK != HAL_DFSDM_FilterConfigRegChannel(&hAudioInTopLeftFilter, AUDIO_DFSDMx_TOP_LEFT_CHANNEL, DFSDM_CONTINUOUS_CONV_ON)) + { + return AUDIO_ERROR; + } + + /* FILTER 1 configuration */ + __HAL_DFSDM_FILTER_RESET_HANDLE_STATE(&hAudioInTopRightFilter); + hAudioInTopRightFilter.Instance = AUDIO_DFSDMx_TOP_RIGHT_FILTER; + hAudioInTopRightFilter.Init.RegularParam.Trigger = DFSDM_FILTER_SYNC_TRIGGER; + hAudioInTopRightFilter.Init.RegularParam.FastMode = ENABLE; + hAudioInTopRightFilter.Init.RegularParam.DmaMode = ENABLE; + hAudioInTopRightFilter.Init.InjectedParam.Trigger = DFSDM_FILTER_SW_TRIGGER; + hAudioInTopRightFilter.Init.InjectedParam.ScanMode = DISABLE; + hAudioInTopRightFilter.Init.InjectedParam.DmaMode = DISABLE; + hAudioInTopRightFilter.Init.InjectedParam.ExtTrigger = DFSDM_FILTER_EXT_TRIG_TIM1_TRGO; + hAudioInTopRightFilter.Init.InjectedParam.ExtTriggerEdge = DFSDM_FILTER_EXT_TRIG_RISING_EDGE; + hAudioInTopRightFilter.Init.FilterParam.SincOrder = DFSDM_FILTER_ORDER(AudioFreq); + /* Set the DFSDM Filters Oversampling to have correct sample rate */ + hAudioInTopRightFilter.Init.FilterParam.Oversampling = DFSDM_OVER_SAMPLING(AudioFreq); + hAudioInTopRightFilter.Init.FilterParam.IntOversampling = 1; + if(HAL_OK != HAL_DFSDM_FilterInit(&hAudioInTopRightFilter)) + { + return AUDIO_ERROR; + } + /* Configure injected channel */ + if(HAL_OK != HAL_DFSDM_FilterConfigRegChannel(&hAudioInTopRightFilter, AUDIO_DFSDMx_TOP_RIGHT_CHANNEL, DFSDM_CONTINUOUS_CONV_ON)) + { + return AUDIO_ERROR; + } + + if(AudioIn_ChannelNumber > 2) + { + /* FILTER 2 configuration */ + __HAL_DFSDM_FILTER_RESET_HANDLE_STATE(&hAudioInButtomLeftFilter); + hAudioInButtomLeftFilter.Instance = AUDIO_DFSDMx_BUTTOM_LEFT_FILTER; + hAudioInButtomLeftFilter.Init.RegularParam.Trigger = DFSDM_FILTER_SYNC_TRIGGER; + hAudioInButtomLeftFilter.Init.RegularParam.FastMode = ENABLE; + hAudioInButtomLeftFilter.Init.RegularParam.DmaMode = ENABLE; + hAudioInButtomLeftFilter.Init.InjectedParam.Trigger = DFSDM_FILTER_SW_TRIGGER; + hAudioInButtomLeftFilter.Init.InjectedParam.ScanMode = ENABLE; + hAudioInButtomLeftFilter.Init.InjectedParam.DmaMode = DISABLE; + hAudioInButtomLeftFilter.Init.InjectedParam.ExtTrigger = DFSDM_FILTER_EXT_TRIG_TIM1_TRGO; + hAudioInButtomLeftFilter.Init.InjectedParam.ExtTriggerEdge = DFSDM_FILTER_EXT_TRIG_RISING_EDGE; + hAudioInButtomLeftFilter.Init.FilterParam.SincOrder = DFSDM_FILTER_ORDER(AudioFreq); + /* Set the DFSDM Filters Oversampling to have correct sample rate */ + hAudioInButtomLeftFilter.Init.FilterParam.Oversampling = DFSDM_OVER_SAMPLING(AudioFreq); + hAudioInButtomLeftFilter.Init.FilterParam.IntOversampling = 1; + if(HAL_OK != HAL_DFSDM_FilterInit(&hAudioInButtomLeftFilter)) + { + return AUDIO_ERROR; + } + + /* Configure injected channel */ + if(HAL_OK != HAL_DFSDM_FilterConfigRegChannel(&hAudioInButtomLeftFilter, AUDIO_DFSDMx_BUTTOM_LEFT_CHANNEL, DFSDM_CONTINUOUS_CONV_ON)) + { + return AUDIO_ERROR; + } + + /* FILTER 3 configuration */ + __HAL_DFSDM_FILTER_RESET_HANDLE_STATE(&hAudioInButtomRightFilter); + hAudioInButtomRightFilter.Instance = AUDIO_DFSDMx_BUTTOM_RIGHT_FILTER; + hAudioInButtomRightFilter.Init.RegularParam.Trigger = DFSDM_FILTER_SYNC_TRIGGER; + hAudioInButtomRightFilter.Init.RegularParam.FastMode = ENABLE; + hAudioInButtomRightFilter.Init.RegularParam.DmaMode = ENABLE; + hAudioInButtomRightFilter.Init.InjectedParam.Trigger = DFSDM_FILTER_SW_TRIGGER; + hAudioInButtomRightFilter.Init.InjectedParam.ScanMode = DISABLE; + hAudioInButtomRightFilter.Init.InjectedParam.DmaMode = DISABLE; + hAudioInButtomRightFilter.Init.InjectedParam.ExtTrigger = DFSDM_FILTER_EXT_TRIG_TIM1_TRGO; + hAudioInButtomRightFilter.Init.InjectedParam.ExtTriggerEdge = DFSDM_FILTER_EXT_TRIG_RISING_EDGE; + hAudioInButtomRightFilter.Init.FilterParam.SincOrder = DFSDM_FILTER_ORDER(AudioFreq); + /* Set the DFSDM Filters Oversampling to have correct sample rate */ + hAudioInButtomRightFilter.Init.FilterParam.Oversampling = DFSDM_OVER_SAMPLING(AudioFreq); + hAudioInButtomRightFilter.Init.FilterParam.IntOversampling = 1; + if(HAL_OK != HAL_DFSDM_FilterInit(&hAudioInButtomRightFilter)) + { + return AUDIO_ERROR; + } + /* Configure injected channel */ + if(HAL_OK != HAL_DFSDM_FilterConfigRegChannel(&hAudioInButtomRightFilter, AUDIO_DFSDMx_BUTTOM_RIGHT_CHANNEL, DFSDM_CONTINUOUS_CONV_ON)) + { + return AUDIO_ERROR; + } + } + return AUDIO_OK; +} + +/** + * @brief De-initialize the Digital Filter for Sigma-Delta Modulators interface (DFSDM). + * @retval AUDIO_OK if correct communication, else wrong communication + */ +static uint8_t DFSDMx_DeInit(void) +{ + /* De-initializes the DFSDM filters to allow access to DFSDM internal registers */ + if(HAL_OK != HAL_DFSDM_FilterDeInit(&hAudioInTopLeftFilter)) + { + return AUDIO_ERROR; + } + + if(HAL_OK != HAL_DFSDM_FilterDeInit(&hAudioInTopRightFilter)) + { + return AUDIO_ERROR; + } + + /* De-initializes the DFSDM channels to allow access to DFSDM internal registers */ + if(HAL_OK != HAL_DFSDM_ChannelDeInit(&hAudioInTopLeftChannel)) + { + return AUDIO_ERROR; + } + + if(HAL_OK != HAL_DFSDM_ChannelDeInit(&hAudioInTopRightChannel)) + { + return AUDIO_ERROR; + } + + if(AudioIn_ChannelNumber > 2) + { + /* De-initializes the DFSDM filters to allow access to DFSDM internal registers */ + if(HAL_OK != HAL_DFSDM_FilterDeInit(&hAudioInButtomLeftFilter)) + { + return AUDIO_ERROR; + } + + if(HAL_OK != HAL_DFSDM_FilterDeInit(&hAudioInButtomRightFilter)) + { + return AUDIO_ERROR; + } + + /* De-initializes the DFSDM channels to allow access to DFSDM internal registers */ + if(HAL_OK != HAL_DFSDM_ChannelDeInit(&hAudioInButtomLeftChannel)) + { + return AUDIO_ERROR; + } + + if(HAL_OK != HAL_DFSDM_ChannelDeInit(&hAudioInButtomRightChannel)) + { + return AUDIO_ERROR; + } + } + + return AUDIO_OK; +} + +/** + * @brief Initialize the DFSDM channel MSP. + * @retval None + */ +static void DFSDMx_ChannelMspInit(void) +{ + GPIO_InitTypeDef GPIO_InitStruct; + + /* Enable DFSDM clock */ + AUDIO_DFSDMx_CLK_ENABLE(); + + /* Enable GPIO clock */ + AUDIO_DFSDMx_DMIC_DATIN_GPIO_CLK_ENABLE(); + AUDIO_DFSDMx_CKOUT_DMIC_GPIO_CLK_ENABLE(); + + /* DFSDM pins configuration: DFSDM_CKOUT, DMIC_DATIN1 pins ------------------*/ + GPIO_InitStruct.Pin = AUDIO_DFSDMx_CKOUT_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = AUDIO_DFSDMx_CKOUT_AF; + HAL_GPIO_Init(AUDIO_DFSDMx_CKOUT_DMIC_GPIO_PORT, &GPIO_InitStruct); + + /* DFSDM pin configuration: DMIC_DATIN1 pin --------------------------------*/ + GPIO_InitStruct.Pin = AUDIO_DFSDMx_DMIC_DATIN1_PIN; + GPIO_InitStruct.Alternate = AUDIO_DFSDMx_DMIC_DATIN_AF; + HAL_GPIO_Init(AUDIO_DFSDMx_DMIC_DATIN_GPIO_PORT, &GPIO_InitStruct); + + if(AudioIn_ChannelNumber > 2) + { + /* DFSDM pin configuration: DMIC_DATIN5 pin --------------------------------*/ + GPIO_InitStruct.Pin = AUDIO_DFSDMx_DMIC_DATIN5_PIN; + GPIO_InitStruct.Alternate = AUDIO_DFSDMx_DMIC_DATIN_AF; + HAL_GPIO_Init(AUDIO_DFSDMx_DMIC_DATIN_GPIO_PORT, &GPIO_InitStruct); + } +} + +/** + * @brief DeInitialize the DFSDM channel MSP. + * @retval None + */ +static void DFSDMx_ChannelMspDeInit(void) +{ + GPIO_InitTypeDef GPIO_InitStruct; + + /* DFSDM pin configuration: DMIC_DATIN1 pin --------------------------------*/ + GPIO_InitStruct.Pin = AUDIO_DFSDMx_CKOUT_PIN; + HAL_GPIO_DeInit(AUDIO_DFSDMx_CKOUT_DMIC_GPIO_PORT, GPIO_InitStruct.Pin); + GPIO_InitStruct.Pin = AUDIO_DFSDMx_DMIC_DATIN1_PIN; + HAL_GPIO_DeInit(AUDIO_DFSDMx_DMIC_DATIN_GPIO_PORT, GPIO_InitStruct.Pin); + + if(AudioIn_ChannelNumber > 2) + { + /* DFSDM pin configuration: DMIC_DATIN5 pin ------------------------------*/ + GPIO_InitStruct.Pin = AUDIO_DFSDMx_CKOUT_PIN; + HAL_GPIO_DeInit(AUDIO_DFSDMx_CKOUT_DMIC_GPIO_PORT, GPIO_InitStruct.Pin); + GPIO_InitStruct.Pin = AUDIO_DFSDMx_DMIC_DATIN5_PIN; + HAL_GPIO_DeInit(AUDIO_DFSDMx_DMIC_DATIN_GPIO_PORT, GPIO_InitStruct.Pin); + } +} + +/** + * @brief Initialize the DFSDM filter MSP. + * @retval None + */ +static void DFSDMx_FilterMspInit(void) +{ + /* Enable DFSDM clock */ + AUDIO_DFSDMx_CLK_ENABLE(); + + /* Enable the DMA clock */ + AUDIO_DFSDMx_DMAx_CLK_ENABLE(); + + /*********** Configure DMA stream for TOP LEFT microphone *******************/ + hDmaTopLeft.Init.Direction = DMA_PERIPH_TO_MEMORY; + hDmaTopLeft.Init.PeriphInc = DMA_PINC_DISABLE; + hDmaTopLeft.Init.MemInc = DMA_MINC_ENABLE; + hDmaTopLeft.Init.PeriphDataAlignment = AUDIO_DFSDMx_DMAx_PERIPH_DATA_SIZE; + hDmaTopLeft.Init.MemDataAlignment = AUDIO_DFSDMx_DMAx_MEM_DATA_SIZE; + hDmaTopLeft.Init.Mode = DMA_CIRCULAR; + hDmaTopLeft.Init.Priority = DMA_PRIORITY_HIGH; + hDmaTopLeft.Instance = AUDIO_DFSDMx_DMAx_TOP_LEFT_STREAM; + hDmaTopLeft.Init.Channel = AUDIO_DFSDMx_DMAx_CHANNEL; + + /* Associate the DMA handle */ + __HAL_LINKDMA(&hAudioInTopLeftFilter, hdmaReg, hDmaTopLeft); + + /* Reset DMA handle state */ + __HAL_DMA_RESET_HANDLE_STATE(&hDmaTopLeft); + + /* Configure the DMA Channel */ + HAL_DMA_Init(&hDmaTopLeft); + + /* DMA IRQ Channel configuration */ + HAL_NVIC_SetPriority(AUDIO_DFSDMx_DMAx_TOP_LEFT_IRQ, AUDIO_OUT_IRQ_PREPRIO, 0); + HAL_NVIC_EnableIRQ(AUDIO_DFSDMx_DMAx_TOP_LEFT_IRQ); + + + /*********** Configure DMA stream for TOP RIGHT microphone ******************/ + hDmaTopRight.Init.Direction = DMA_PERIPH_TO_MEMORY; + hDmaTopRight.Init.PeriphInc = DMA_PINC_DISABLE; + hDmaTopRight.Init.MemInc = DMA_MINC_ENABLE; + hDmaTopRight.Init.PeriphDataAlignment = AUDIO_DFSDMx_DMAx_PERIPH_DATA_SIZE; + hDmaTopRight.Init.MemDataAlignment = AUDIO_DFSDMx_DMAx_MEM_DATA_SIZE; + hDmaTopRight.Init.Mode = DMA_CIRCULAR; + hDmaTopRight.Init.Priority = DMA_PRIORITY_HIGH; + hDmaTopRight.Instance = AUDIO_DFSDMx_DMAx_TOP_RIGHT_STREAM; + hDmaTopRight.Init.Channel = AUDIO_DFSDMx_DMAx_CHANNEL; + + /* Associate the DMA handle */ + __HAL_LINKDMA(&hAudioInTopRightFilter, hdmaReg, hDmaTopRight); + + /* Reset DMA handle state */ + __HAL_DMA_RESET_HANDLE_STATE(&hDmaTopRight); + + /* Configure the DMA Channel */ + HAL_DMA_Init(&hDmaTopRight); + + /* DMA IRQ Channel configuration */ + HAL_NVIC_SetPriority(AUDIO_DFSDMx_DMAx_TOP_RIGHT_IRQ, AUDIO_OUT_IRQ_PREPRIO, 0); + HAL_NVIC_EnableIRQ(AUDIO_DFSDMx_DMAx_TOP_RIGHT_IRQ); + + if(AudioIn_ChannelNumber > 2) + { + /*********** Configure DMA stream for BUTTOM LEFT microphone ****************/ + hDmaButtomLeft.Init.Direction = DMA_PERIPH_TO_MEMORY; + hDmaButtomLeft.Init.PeriphInc = DMA_PINC_DISABLE; + hDmaButtomLeft.Init.MemInc = DMA_MINC_ENABLE; + hDmaButtomLeft.Init.PeriphDataAlignment = AUDIO_DFSDMx_DMAx_PERIPH_DATA_SIZE; + hDmaButtomLeft.Init.MemDataAlignment = AUDIO_DFSDMx_DMAx_MEM_DATA_SIZE; + hDmaButtomLeft.Init.Mode = DMA_CIRCULAR; + hDmaButtomLeft.Init.Priority = DMA_PRIORITY_HIGH; + hDmaButtomLeft.Instance = AUDIO_DFSDMx_DMAx_BUTTOM_LEFT_STREAM; + hDmaButtomLeft.Init.Channel = AUDIO_DFSDMx_DMAx_CHANNEL; + + /* Associate the DMA handle */ + __HAL_LINKDMA(&hAudioInButtomLeftFilter, hdmaReg, hDmaButtomLeft); + + /* Reset DMA handle state */ + __HAL_DMA_RESET_HANDLE_STATE(&hDmaButtomLeft); + + /* Configure the DMA Channel */ + HAL_DMA_Init(&hDmaButtomLeft); + + /* DMA IRQ Channel configuration */ + HAL_NVIC_SetPriority(AUDIO_DFSDMx_DMAx_BUTTOM_LEFT_IRQ, AUDIO_OUT_IRQ_PREPRIO, 0); + HAL_NVIC_EnableIRQ(AUDIO_DFSDMx_DMAx_BUTTOM_LEFT_IRQ); + + + /*********** Configure DMA stream for BUTTOM RIGHT microphone ***************/ + hDmaButtomRight.Init.Direction = DMA_PERIPH_TO_MEMORY; + hDmaButtomRight.Init.PeriphInc = DMA_PINC_DISABLE; + hDmaButtomRight.Init.MemInc = DMA_MINC_ENABLE; + hDmaButtomRight.Init.PeriphDataAlignment = AUDIO_DFSDMx_DMAx_PERIPH_DATA_SIZE; + hDmaButtomRight.Init.MemDataAlignment = AUDIO_DFSDMx_DMAx_MEM_DATA_SIZE; + hDmaButtomRight.Init.Mode = DMA_CIRCULAR; + hDmaButtomRight.Init.Priority = DMA_PRIORITY_HIGH; + hDmaButtomRight.Instance = AUDIO_DFSDMx_DMAx_BUTTOM_RIGHT_STREAM; + hDmaButtomRight.Init.Channel = AUDIO_DFSDMx_DMAx_CHANNEL; + + /* Associate the DMA handle */ + __HAL_LINKDMA(&hAudioInButtomRightFilter, hdmaReg, hDmaButtomRight); + + /* Reset DMA handle state */ + __HAL_DMA_RESET_HANDLE_STATE(&hDmaButtomRight); + + /* Configure the DMA Channel */ + HAL_DMA_Init(&hDmaButtomRight); + + /* DMA IRQ Channel configuration */ + HAL_NVIC_SetPriority(AUDIO_DFSDMx_DMAx_BUTTOM_RIGHT_IRQ, AUDIO_OUT_IRQ_PREPRIO, 0); + HAL_NVIC_EnableIRQ(AUDIO_DFSDMx_DMAx_BUTTOM_RIGHT_IRQ); + } +} + +/** + * @brief DeInitialize the DFSDM filter MSP. + * @retval None + */ +static void DFSDMx_FilterMspDeInit(void) +{ + /* Configure the DMA Channel */ + HAL_DMA_DeInit(&hDmaTopLeft); + HAL_DMA_DeInit(&hDmaTopRight); + if(AudioIn_ChannelNumber > 2) + { + HAL_DMA_DeInit(&hDmaButtomLeft); + HAL_DMA_DeInit(&hDmaButtomRight); + } +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + diff --git a/BSP/stm32f769i_discovery_audio.h b/BSP/stm32f769i_discovery_audio.h new file mode 100644 index 0000000..c1e7a1c --- /dev/null +++ b/BSP/stm32f769i_discovery_audio.h @@ -0,0 +1,324 @@ +/** + ****************************************************************************** + * @file stm32f769i_discovery_audio.h + * @author MCD Application Team + * @brief This file contains the common defines and functions prototypes for + * the stm32f769i_discovery_audio.c driver. + ****************************************************************************** + * @attention + * + * Copyright (c) 2016 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F769I_DISCOVERY_AUDIO_H +#define __STM32F769I_DISCOVERY_AUDIO_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +/* Include audio component Driver */ +#include "wm8994.h" +#include "stm32f769i_discovery.h" +#include + +/** @addtogroup BSP + * @{ + */ + +/** @addtogroup STM32F769I_DISCOVERY + * @{ + */ + +/** @defgroup STM32F769I_DISCOVERY_AUDIO STM32F769I_DISCOVERY_AUDIO + * @{ + */ + +/** @defgroup STM32F769I_DISCOVERY_AUDIO_Exported_Types STM32F769I_DISCOVERY_AUDIO Exported Types + * @{ + */ +/** + * @} + */ + +/** @defgroup STM32F769I_DISCOVERY_AUDIO_Exported_Constants STM32F769I_DISCOVERY_AUDIO Exported Constants + * @{ + */ + +/** @defgroup BSP_Audio_Sample_Rate BSP Audio Sample Rate + * @{ + */ +#define BSP_AUDIO_FREQUENCY_96K SAI_AUDIO_FREQUENCY_96K +#define BSP_AUDIO_FREQUENCY_48K SAI_AUDIO_FREQUENCY_48K +#define BSP_AUDIO_FREQUENCY_44K SAI_AUDIO_FREQUENCY_44K +#define BSP_AUDIO_FREQUENCY_32K SAI_AUDIO_FREQUENCY_32K +#define BSP_AUDIO_FREQUENCY_22K SAI_AUDIO_FREQUENCY_22K +#define BSP_AUDIO_FREQUENCY_16K SAI_AUDIO_FREQUENCY_16K +#define BSP_AUDIO_FREQUENCY_11K SAI_AUDIO_FREQUENCY_11K +#define BSP_AUDIO_FREQUENCY_8K SAI_AUDIO_FREQUENCY_8K +/** + * @} + */ + +/*------------------------------------------------------------------------------ + USER SAI defines parameters + -----------------------------------------------------------------------------*/ +/** CODEC_AudioFrame_SLOT_TDMMode In W8994 codec the Audio frame contains 4 slots : TDM Mode + * TDM format : + * +------------------|------------------|--------------------|-------------------+ + * | CODEC_SLOT0 Left | CODEC_SLOT1 Left | CODEC_SLOT0 Right | CODEC_SLOT1 Right | + * +------------------------------------------------------------------------------+ + */ +/* To have 2 separate audio stream in Both headphone and speaker the 4 slot must be activated */ +#define CODEC_AUDIOFRAME_SLOT_0123 SAI_SLOTACTIVE_0 | SAI_SLOTACTIVE_1 | SAI_SLOTACTIVE_2 | SAI_SLOTACTIVE_3 + +/* To have an audio stream in headphone only SAI Slot 0 and Slot 2 must be activated */ +#define CODEC_AUDIOFRAME_SLOT_02 SAI_SLOTACTIVE_0 | SAI_SLOTACTIVE_2 +/* To have an audio stream in speaker only SAI Slot 1 and Slot 3 must be activated */ +#define CODEC_AUDIOFRAME_SLOT_13 SAI_SLOTACTIVE_1 | SAI_SLOTACTIVE_3 + + +/* SAI peripheral configuration defines */ +#define AUDIO_OUT_SAIx SAI1_Block_A +#define AUDIO_OUT_SAIx_CLK_ENABLE() __HAL_RCC_SAI1_CLK_ENABLE() +#define AUDIO_OUT_SAIx_CLK_DISABLE() __HAL_RCC_SAI1_CLK_DISABLE() +#define AUDIO_OUT_SAIx_AF GPIO_AF6_SAI1 + +#define AUDIO_OUT_SAIx_MCLK_ENABLE() __HAL_RCC_GPIOG_CLK_ENABLE() +#define AUDIO_OUT_SAIx_MCLK_GPIO_PORT GPIOG +#define AUDIO_OUT_SAIx_MCLK_PIN GPIO_PIN_7 +#define AUDIO_OUT_SAIx_SD_FS_CLK_ENABLE() __HAL_RCC_GPIOE_CLK_ENABLE() +#define AUDIO_OUT_SAIx_SD_FS_SCK_GPIO_PORT GPIOE +#define AUDIO_OUT_SAIx_FS_PIN GPIO_PIN_4 +#define AUDIO_OUT_SAIx_SCK_PIN GPIO_PIN_5 +#define AUDIO_OUT_SAIx_SD_PIN GPIO_PIN_6 + +/* SAI DMA Stream definitions */ +#define AUDIO_OUT_SAIx_DMAx_CLK_ENABLE() __HAL_RCC_DMA2_CLK_ENABLE() +#define AUDIO_OUT_SAIx_DMAx_STREAM DMA2_Stream1 +#define AUDIO_OUT_SAIx_DMAx_CHANNEL DMA_CHANNEL_0 +#define AUDIO_OUT_SAIx_DMAx_IRQ DMA2_Stream1_IRQn +#define AUDIO_OUT_SAIx_DMAx_PERIPH_DATA_SIZE DMA_PDATAALIGN_HALFWORD +#define AUDIO_OUT_SAIx_DMAx_MEM_DATA_SIZE DMA_MDATAALIGN_HALFWORD +#define DMA_MAX_SZE 0xFFFF + +#define AUDIO_OUT_SAIx_DMAx_IRQHandler DMA2_Stream1_IRQHandler + +/* Select the interrupt preemption priority and subpriority for the DMA interrupt */ +#define AUDIO_OUT_IRQ_PREPRIO ((uint32_t)0x0E) + +/*------------------------------------------------------------------------------ + AUDIO IN CONFIGURATION +------------------------------------------------------------------------------*/ +/* SAI peripheral configuration defines */ +#define AUDIO_IN_SAIx SAI1_Block_B +#define AUDIO_IN_SAIx_CLK_ENABLE() __HAL_RCC_SAI1_CLK_ENABLE() +#define AUDIO_IN_SAIx_CLK_DISABLE() __HAL_RCC_SAI1_CLK_DISABLE() +#define AUDIO_IN_SAIx_AF GPIO_AF6_SAI1 +#define AUDIO_IN_SAIx_SD_ENABLE() __HAL_RCC_GPIOE_CLK_ENABLE() +#define AUDIO_IN_SAIx_SD_GPIO_PORT GPIOE +#define AUDIO_IN_SAIx_SD_PIN GPIO_PIN_3 + +/* SAI DMA Stream definitions */ +#define AUDIO_IN_SAIx_DMAx_CLK_ENABLE() __HAL_RCC_DMA2_CLK_ENABLE() +#define AUDIO_IN_SAIx_DMAx_STREAM DMA2_Stream4 +#define AUDIO_IN_SAIx_DMAx_CHANNEL DMA_CHANNEL_1 +#define AUDIO_IN_SAIx_DMAx_IRQ DMA2_Stream4_IRQn +#define AUDIO_IN_SAIx_DMAx_PERIPH_DATA_SIZE DMA_PDATAALIGN_HALFWORD +#define AUDIO_IN_SAIx_DMAx_MEM_DATA_SIZE DMA_MDATAALIGN_HALFWORD + +#define AUDIO_IN_INT_GPIO_ENABLE() __HAL_RCC_GPIOJ_CLK_ENABLE() +#define AUDIO_IN_INT_GPIO_PORT GPIOJ +#define AUDIO_IN_INT_GPIO_PIN GPIO_PIN_12 +#define AUDIO_IN_INT_IRQ EXTI15_10_IRQn + +/* DFSDM Configuration defines */ +#define AUDIO_DFSDMx_TOP_RIGHT_CHANNEL DFSDM_CHANNEL_0 +#define AUDIO_DFSDMx_TOP_LEFT_CHANNEL DFSDM_CHANNEL_1 +#define AUDIO_DFSDMx_BUTTOM_RIGHT_CHANNEL DFSDM_CHANNEL_4 +#define AUDIO_DFSDMx_BUTTOM_LEFT_CHANNEL DFSDM_CHANNEL_5 + +#define AUDIO_DFSDMx_TOP_LEFT_FILTER DFSDM1_Filter0 +#define AUDIO_DFSDMx_TOP_RIGHT_FILTER DFSDM1_Filter1 +#define AUDIO_DFSDMx_BUTTOM_LEFT_FILTER DFSDM1_Filter2 +#define AUDIO_DFSDMx_BUTTOM_RIGHT_FILTER DFSDM1_Filter3 + +#define AUDIO_DFSDMx_CLK_ENABLE() __HAL_RCC_DFSDM1_CLK_ENABLE() +#define AUDIO_DFSDMx_CKOUT_PIN GPIO_PIN_3 +#define AUDIO_DFSDMx_CKOUT_DMIC_GPIO_PORT GPIOD +#define AUDIO_DFSDMx_CKOUT_DMIC_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE() +#define AUDIO_DFSDMx_DMIC_DATIN1_PIN GPIO_PIN_3 +#define AUDIO_DFSDMx_DMIC_DATIN5_PIN GPIO_PIN_11 +#define AUDIO_DFSDMx_DMIC_DATIN_GPIO_PORT GPIOC +#define AUDIO_DFSDMx_DMIC_DATIN_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE() +#define AUDIO_DFSDMx_DMIC_DATIN_AF GPIO_AF3_DFSDM1 +#define AUDIO_DFSDMx_CKOUT_AF GPIO_AF3_DFSDM1 + +/* DFSDM DMA Right and Left channels definitions */ +#define AUDIO_DFSDMx_DMAx_CLK_ENABLE() __HAL_RCC_DMA2_CLK_ENABLE() +#define AUDIO_DFSDMx_DMAx_CHANNEL DMA_CHANNEL_8 +#define AUDIO_DFSDMx_DMAx_PERIPH_DATA_SIZE DMA_PDATAALIGN_WORD +#define AUDIO_DFSDMx_DMAx_MEM_DATA_SIZE DMA_MDATAALIGN_WORD + +#define AUDIO_DFSDMx_DMAx_TOP_LEFT_STREAM DMA2_Stream0 +#define AUDIO_DFSDMx_DMAx_TOP_LEFT_IRQ DMA2_Stream0_IRQn +#define AUDIO_DFSDMx_DMAx_TOP_LEFT_IRQHandler DMA2_Stream0_IRQHandler + +#define AUDIO_DFSDMx_DMAx_TOP_RIGHT_STREAM DMA2_Stream5 +#define AUDIO_DFSDMx_DMAx_TOP_RIGHT_IRQ DMA2_Stream5_IRQn +#define AUDIO_DFSDMx_DMAx_TOP_RIGHT_IRQHandler DMA2_Stream5_IRQHandler + +#define AUDIO_DFSDMx_DMAx_BUTTOM_LEFT_STREAM DMA2_Stream6 +#define AUDIO_DFSDMx_DMAx_BUTTOM_LEFT_IRQ DMA2_Stream6_IRQn +#define AUDIO_DFSDMx_DMAx_BUTTOM_LEFT_IRQHandler DMA2_Stream6_IRQHandler + +#define AUDIO_DFSDMx_DMAx_BUTTOM_RIGHT_STREAM DMA2_Stream7 +#define AUDIO_DFSDMx_DMAx_BUTTOM_RIGHT_IRQ DMA2_Stream7_IRQn +#define AUDIO_DFSDMx_DMAx_BUTTOM_RIGHT_IRQHandler DMA2_Stream7_IRQHandler + +/* Select the interrupt preemption priority and subpriority for the DMA interrupt */ +#define AUDIO_IN_IRQ_PREPRIO ((uint32_t)0x0F) + + +/*------------------------------------------------------------------------------ + CONFIGURATION: Audio Driver Configuration parameters +------------------------------------------------------------------------------*/ + +#define AUDIODATA_SIZE 2 /* 16-bits audio data size */ + +/* Audio status definition */ +#define AUDIO_OK ((uint8_t)0) +#define AUDIO_ERROR ((uint8_t)1) +#define AUDIO_TIMEOUT ((uint8_t)2) + +/* Audio In default settings */ +#define DEFAULT_AUDIO_IN_FREQ BSP_AUDIO_FREQUENCY_16K +#define DEFAULT_AUDIO_IN_BIT_RESOLUTION ((uint8_t)16) +#define DEFAULT_AUDIO_IN_CHANNEL_NBR ((uint8_t)2) +#define DEFAULT_AUDIO_IN_VOLUME ((uint16_t)64) + +/*------------------------------------------------------------------------------ + OUTPUT DEVICES definition +------------------------------------------------------------------------------*/ +/* Alias on existing output devices to adapt for 2 headphones output */ +#define OUTPUT_DEVICE_HEADPHONE1 OUTPUT_DEVICE_HEADPHONE +#define OUTPUT_DEVICE_HEADPHONE2 OUTPUT_DEVICE_SPEAKER /* Headphone2 is connected to Speaker output of the wm8994 */ + +/*------------------------------------------------------------------------------ + INPUT DEVICES definition +------------------------------------------------------------------------------*/ +/* MP34DT01TR digital microphone on PCB top side */ +#define INPUT_DEVICE_DIGITAL_MIC ((uint16_t)0) +/* Analog microphone input from 3.5 audio jack connector */ +#define INPUT_DEVICE_ANALOG_MIC INPUT_DEVICE_INPUT_LINE_1 + +/** + * @} + */ + +/** @defgroup STM32F769I_DISCOVERY_AUDIO_Exported_Macros STM32F769I_DISCOVERY_AUDIO Exported Macros + * @{ + */ +#define DMA_MAX(x) (((x) <= DMA_MAX_SZE)? (x):DMA_MAX_SZE) +/** + * @} + */ + +/** @defgroup STM32F769I_DISCOVERY_AUDIO_OUT_Exported_Functions STM32F769I_DISCOVERY_AUDIO_OUT Exported Functions + * @{ + */ +uint8_t BSP_AUDIO_OUT_Init(uint16_t OutputDevice, uint8_t Volume, uint32_t AudioFreq); +void BSP_AUDIO_OUT_DeInit(void); +uint8_t BSP_AUDIO_OUT_Play(uint16_t* pBuffer, uint32_t Size); +void BSP_AUDIO_OUT_ChangeBuffer(uint16_t *pData, uint16_t Size); +uint8_t BSP_AUDIO_OUT_Pause(void); +uint8_t BSP_AUDIO_OUT_Resume(void); +uint8_t BSP_AUDIO_OUT_Stop(uint32_t Option); +uint8_t BSP_AUDIO_OUT_SetVolume(uint8_t Volume); +void BSP_AUDIO_OUT_SetFrequency(uint32_t AudioFreq); +void BSP_AUDIO_OUT_SetAudioFrameSlot(uint32_t AudioFrameSlot); +uint8_t BSP_AUDIO_OUT_SetMute(uint32_t Cmd); +uint8_t BSP_AUDIO_OUT_SetOutputMode(uint8_t Output); + +/* User Callbacks: user has to implement these functions in his code if they are needed. */ +/* This function is called when the requested data has been completely transferred.*/ +void BSP_AUDIO_OUT_TransferComplete_CallBack(void); + +/* This function is called when half of the requested buffer has been transferred. */ +void BSP_AUDIO_OUT_HalfTransfer_CallBack(void); + +/* This function is called when an Interrupt due to transfer error on or peripheral + error occurs. */ +void BSP_AUDIO_OUT_Error_CallBack(void); + +/* These function can be modified in case the current settings (e.g. DMA stream) + need to be changed for specific application needs */ +void BSP_AUDIO_OUT_ClockConfig(SAI_HandleTypeDef *hsai, uint32_t AudioFreq, void *Params); +void BSP_AUDIO_OUT_MspInit(SAI_HandleTypeDef *hsai, void *Params); +void BSP_AUDIO_OUT_MspDeInit(SAI_HandleTypeDef *hsai, void *Params); + +/** + * @} + */ + +/** @defgroup STM32F769I_DISCOVERY_AUDIO_IN_Exported_Functions STM32F769I_DISCOVERY_AUDIO_IN Exported Functions + * @{ + */ +uint8_t BSP_AUDIO_IN_Init(uint32_t AudioFreq, uint32_t BitRes, uint32_t ChnlNbr); +uint8_t BSP_AUDIO_IN_InitEx(uint16_t InputDevice, uint32_t AudioFreq, uint32_t BitRes, uint32_t ChnlNbr); +uint8_t BSP_AUDIO_IN_AllocScratch (int32_t *pScratch, uint32_t size); +uint8_t BSP_AUDIO_IN_GetChannelNumber(void); +void BSP_AUDIO_IN_DeInit(void); +uint8_t BSP_AUDIO_IN_Record(uint16_t *pData, uint32_t Size); +uint8_t BSP_AUDIO_IN_Stop(void); +uint8_t BSP_AUDIO_IN_Pause(void); +uint8_t BSP_AUDIO_IN_Resume(void); + +/* User Callbacks: user has to implement these functions in his code if they are needed. */ +/* This function should be implemented by the user application. + It is called into this driver when the current buffer is filled to prepare the next + buffer pointer and its size. */ +void BSP_AUDIO_IN_TransferComplete_CallBack(void); +void BSP_AUDIO_IN_HalfTransfer_CallBack(void); + +/* This function is called when an Interrupt due to transfer error on or peripheral + error occurs. */ +void BSP_AUDIO_IN_Error_CallBack(void); + +/* These function can be modified in case the current settings (e.g. DMA stream) + need to be changed for specific application needs */ +void BSP_AUDIO_IN_ClockConfig(DFSDM_Filter_HandleTypeDef *hdfsdm_filter, uint32_t AudioFreq, void *Params); +void BSP_AUDIO_IN_MspInit(void); +void BSP_AUDIO_IN_MspDeInit(void); + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F769I_DISCOVERY_AUDIO_H */ + diff --git a/BSP/wm8994.c b/BSP/wm8994.c new file mode 100644 index 0000000..44226fa --- /dev/null +++ b/BSP/wm8994.c @@ -0,0 +1,1075 @@ +/** + ****************************************************************************** + * @file wm8994.c + * @author MCD Application Team + * @brief This file provides the WM8994 Audio Codec driver. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2016 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "wm8994.h" + +/** @addtogroup BSP + * @{ + */ + +/** @addtogroup Components + * @{ + */ + +/** @addtogroup wm8994 + * @brief This file provides a set of functions needed to drive the + * WM8994 audio codec. + * @{ + */ + +/** @defgroup WM8994_Private_Types + * @{ + */ + +/** + * @} + */ + +/** @defgroup WM8994_Private_Defines + * @{ + */ +/* Uncomment this line to enable verifying data sent to codec after each write + operation (for debug purpose) */ +#if !defined (VERIFY_WRITTENDATA) +/*#define VERIFY_WRITTENDATA*/ +#endif /* VERIFY_WRITTENDATA */ +/** + * @} + */ + +/** @defgroup WM8994_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup WM8994_Private_Variables + * @{ + */ + +/* Audio codec driver structure initialization */ +AUDIO_DrvTypeDef wm8994_drv = +{ + wm8994_Init, + wm8994_DeInit, + wm8994_ReadID, + + wm8994_Play, + wm8994_Pause, + wm8994_Resume, + wm8994_Stop, + + wm8994_SetFrequency, + wm8994_SetVolume, + wm8994_SetMute, + wm8994_SetOutputMode, + + wm8994_Reset +}; + +static uint32_t outputEnabled = 0; +static uint32_t inputEnabled = 0; +static uint8_t ColdStartup = 1; + +/** + * @} + */ + +/** @defgroup WM8994_Function_Prototypes + * @{ + */ +static uint8_t CODEC_IO_Write(uint8_t Addr, uint16_t Reg, uint16_t Value); +/** + * @} + */ + + +/** @defgroup WM8994_Private_Functions + * @{ + */ + +/** + * @brief Initializes the audio codec and the control interface. + * @param DeviceAddr: Device address on communication Bus. + * @param OutputInputDevice: can be OUTPUT_DEVICE_SPEAKER, OUTPUT_DEVICE_HEADPHONE, + * OUTPUT_DEVICE_BOTH, OUTPUT_DEVICE_AUTO, INPUT_DEVICE_DIGITAL_MICROPHONE_1, + * INPUT_DEVICE_DIGITAL_MICROPHONE_2, INPUT_DEVICE_DIGITAL_MIC1_MIC2, + * INPUT_DEVICE_INPUT_LINE_1 or INPUT_DEVICE_INPUT_LINE_2. + * @param Volume: Initial volume level (from 0 (Mute) to 100 (Max)) + * @param AudioFreq: Audio Frequency + * @retval 0 if correct communication, else wrong communication + */ +uint32_t wm8994_Init(uint16_t DeviceAddr, uint16_t OutputInputDevice, uint8_t Volume, uint32_t AudioFreq) +{ + uint32_t counter = 0; + uint16_t output_device = OutputInputDevice & 0xFF; + uint16_t input_device = OutputInputDevice & 0xFF00; + uint16_t power_mgnt_reg_1 = 0; + + /* Initialize the Control interface of the Audio Codec */ + AUDIO_IO_Init(); + /* wm8994 Errata Work-Arounds */ + counter += CODEC_IO_Write(DeviceAddr, 0x102, 0x0003); + counter += CODEC_IO_Write(DeviceAddr, 0x817, 0x0000); + counter += CODEC_IO_Write(DeviceAddr, 0x102, 0x0000); + + /* Enable VMID soft start (fast), Start-up Bias Current Enabled */ + counter += CODEC_IO_Write(DeviceAddr, 0x39, 0x006C); + + /* Enable bias generator, Enable VMID */ + if (input_device > 0) + { + counter += CODEC_IO_Write(DeviceAddr, 0x01, 0x0013); + } + else + { + counter += CODEC_IO_Write(DeviceAddr, 0x01, 0x0003); + } + + /* Add Delay */ + AUDIO_IO_Delay(50); + + /* Path Configurations for output */ + if (output_device > 0) + { + outputEnabled = 1; + + switch (output_device) + { + case OUTPUT_DEVICE_SPEAKER: + /* Enable DAC1 (Left), Enable DAC1 (Right), + Disable DAC2 (Left), Disable DAC2 (Right)*/ + counter += CODEC_IO_Write(DeviceAddr, 0x05, 0x0C0C); + + /* Enable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x601, 0x0000); + + /* Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x602, 0x0000); + + /* Disable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x604, 0x0002); + + /* Disable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x605, 0x0002); + break; + + case OUTPUT_DEVICE_HEADPHONE: + /* Disable DAC1 (Left), Disable DAC1 (Right), + Enable DAC2 (Left), Enable DAC2 (Right)*/ + counter += CODEC_IO_Write(DeviceAddr, 0x05, 0x0303); + + /* Enable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x601, 0x0001); + + /* Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x602, 0x0001); + + /* Disable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x604, 0x0000); + + /* Disable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x605, 0x0000); + break; + + case OUTPUT_DEVICE_BOTH: + if (input_device == INPUT_DEVICE_DIGITAL_MIC1_MIC2) + { + /* Enable DAC1 (Left), Enable DAC1 (Right), + also Enable DAC2 (Left), Enable DAC2 (Right)*/ + counter += CODEC_IO_Write(DeviceAddr, 0x05, 0x0303 | 0x0C0C); + + /* Enable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path + Enable the AIF1 Timeslot 1 (Left) to DAC 1 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x601, 0x0003); + + /* Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path + Enable the AIF1 Timeslot 1 (Right) to DAC 1 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x602, 0x0003); + + /* Enable the AIF1 Timeslot 0 (Left) to DAC 2 (Left) mixer path + Enable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x604, 0x0003); + + /* Enable the AIF1 Timeslot 0 (Right) to DAC 2 (Right) mixer path + Enable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x605, 0x0003); + } + else + { + /* Enable DAC1 (Left), Enable DAC1 (Right), + also Enable DAC2 (Left), Enable DAC2 (Right)*/ + counter += CODEC_IO_Write(DeviceAddr, 0x05, 0x0303 | 0x0C0C); + + /* Enable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x601, 0x0001); + + /* Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x602, 0x0001); + + /* Enable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x604, 0x0002); + + /* Enable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x605, 0x0002); + } + break; + + case OUTPUT_DEVICE_AUTO : + default: + /* Disable DAC1 (Left), Disable DAC1 (Right), + Enable DAC2 (Left), Enable DAC2 (Right)*/ + counter += CODEC_IO_Write(DeviceAddr, 0x05, 0x0303); + + /* Enable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x601, 0x0001); + + /* Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x602, 0x0001); + + /* Disable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x604, 0x0000); + + /* Disable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x605, 0x0000); + break; + } + } + else + { + outputEnabled = 0; + } + + /* Path Configurations for input */ + if (input_device > 0) + { + inputEnabled = 1; + switch (input_device) + { + case INPUT_DEVICE_DIGITAL_MICROPHONE_2 : + /* Enable AIF1ADC2 (Left), Enable AIF1ADC2 (Right) + * Enable DMICDAT2 (Left), Enable DMICDAT2 (Right) + * Enable Left ADC, Enable Right ADC */ + counter += CODEC_IO_Write(DeviceAddr, 0x04, 0x0C30); + + /* Enable AIF1 DRC2 Signal Detect & DRC in AIF1ADC2 Left/Right Timeslot 1 */ + counter += CODEC_IO_Write(DeviceAddr, 0x450, 0x00DB); + + /* Disable IN1L, IN1R, IN2L, IN2R, Enable Thermal sensor & shutdown */ + counter += CODEC_IO_Write(DeviceAddr, 0x02, 0x6000); + + /* Enable the DMIC2(Left) to AIF1 Timeslot 1 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x608, 0x0002); + + /* Enable the DMIC2(Right) to AIF1 Timeslot 1 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x609, 0x0002); + + /* GPIO1 pin configuration GP1_DIR = output, GP1_FN = AIF1 DRC2 signal detect */ + counter += CODEC_IO_Write(DeviceAddr, 0x700, 0x000E); + break; + + case INPUT_DEVICE_INPUT_LINE_1 : + /* IN1LN_TO_IN1L, IN1LP_TO_VMID, IN1RN_TO_IN1R, IN1RP_TO_VMID */ + counter += CODEC_IO_Write(DeviceAddr, 0x28, 0x0011); + + /* Disable mute on IN1L_TO_MIXINL and +30dB on IN1L PGA output */ + counter += CODEC_IO_Write(DeviceAddr, 0x29, 0x0035); + + /* Disable mute on IN1R_TO_MIXINL, Gain = +30dB */ + counter += CODEC_IO_Write(DeviceAddr, 0x2A, 0x0035); + + /* Enable AIF1ADC1 (Left), Enable AIF1ADC1 (Right) + * Enable Left ADC, Enable Right ADC */ + counter += CODEC_IO_Write(DeviceAddr, 0x04, 0x0303); + + /* Enable AIF1 DRC1 Signal Detect & DRC in AIF1ADC1 Left/Right Timeslot 0 */ + counter += CODEC_IO_Write(DeviceAddr, 0x440, 0x00DB); + + /* Enable IN1L and IN1R, Disable IN2L and IN2R, Enable Thermal sensor & shutdown */ + counter += CODEC_IO_Write(DeviceAddr, 0x02, 0x6350); + + /* Enable the ADCL(Left) to AIF1 Timeslot 0 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x606, 0x0002); + + /* Enable the ADCR(Right) to AIF1 Timeslot 0 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x607, 0x0002); + + /* GPIO1 pin configuration GP1_DIR = output, GP1_FN = AIF1 DRC1 signal detect */ + counter += CODEC_IO_Write(DeviceAddr, 0x700, 0x000D); + break; + + case INPUT_DEVICE_DIGITAL_MICROPHONE_1 : + /* Enable AIF1ADC1 (Left), Enable AIF1ADC1 (Right) + * Enable DMICDAT1 (Left), Enable DMICDAT1 (Right) + * Enable Left ADC, Enable Right ADC */ + counter += CODEC_IO_Write(DeviceAddr, 0x04, 0x030C); + + /* Enable AIF1 DRC2 Signal Detect & DRC in AIF1ADC1 Left/Right Timeslot 0 */ + counter += CODEC_IO_Write(DeviceAddr, 0x440, 0x00DB); + + /* Disable IN1L, IN1R, IN2L, IN2R, Enable Thermal sensor & shutdown */ + counter += CODEC_IO_Write(DeviceAddr, 0x02, 0x6350); + + /* Enable the DMIC2(Left) to AIF1 Timeslot 0 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x606, 0x0002); + + /* Enable the DMIC2(Right) to AIF1 Timeslot 0 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x607, 0x0002); + + /* GPIO1 pin configuration GP1_DIR = output, GP1_FN = AIF1 DRC1 signal detect */ + counter += CODEC_IO_Write(DeviceAddr, 0x700, 0x000D); + break; + case INPUT_DEVICE_DIGITAL_MIC1_MIC2 : + /* Enable AIF1ADC1 (Left), Enable AIF1ADC1 (Right) + * Enable DMICDAT1 (Left), Enable DMICDAT1 (Right) + * Enable Left ADC, Enable Right ADC */ + counter += CODEC_IO_Write(DeviceAddr, 0x04, 0x0F3C); + + /* Enable AIF1 DRC2 Signal Detect & DRC in AIF1ADC2 Left/Right Timeslot 1 */ + counter += CODEC_IO_Write(DeviceAddr, 0x450, 0x00DB); + + /* Enable AIF1 DRC2 Signal Detect & DRC in AIF1ADC1 Left/Right Timeslot 0 */ + counter += CODEC_IO_Write(DeviceAddr, 0x440, 0x00DB); + + /* Disable IN1L, IN1R, Enable IN2L, IN2R, Thermal sensor & shutdown */ + counter += CODEC_IO_Write(DeviceAddr, 0x02, 0x63A0); + + /* Enable the DMIC2(Left) to AIF1 Timeslot 0 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x606, 0x0002); + + /* Enable the DMIC2(Right) to AIF1 Timeslot 0 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x607, 0x0002); + + /* Enable the DMIC2(Left) to AIF1 Timeslot 1 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x608, 0x0002); + + /* Enable the DMIC2(Right) to AIF1 Timeslot 1 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x609, 0x0002); + + /* GPIO1 pin configuration GP1_DIR = output, GP1_FN = AIF1 DRC1 signal detect */ + counter += CODEC_IO_Write(DeviceAddr, 0x700, 0x000D); + break; + case INPUT_DEVICE_INPUT_LINE_2 : + default: + /* Actually, no other input devices supported */ + counter++; + break; + } + } + else + { + inputEnabled = 0; + } + + /* Clock Configurations */ + switch (AudioFreq) + { + case AUDIO_FREQUENCY_8K: + /* AIF1 Sample Rate = 8 (KHz), ratio=256 */ + counter += CODEC_IO_Write(DeviceAddr, 0x210, 0x0003); + break; + + case AUDIO_FREQUENCY_16K: + /* AIF1 Sample Rate = 16 (KHz), ratio=256 */ + counter += CODEC_IO_Write(DeviceAddr, 0x210, 0x0033); + break; + + case AUDIO_FREQUENCY_32K: + /* AIF1 Sample Rate = 32 (KHz), ratio=256 */ + counter += CODEC_IO_Write(DeviceAddr, 0x210, 0x0063); + break; + + case AUDIO_FREQUENCY_48K: + /* AIF1 Sample Rate = 48 (KHz), ratio=256 */ + counter += CODEC_IO_Write(DeviceAddr, 0x210, 0x0083); + break; + + case AUDIO_FREQUENCY_96K: + /* AIF1 Sample Rate = 96 (KHz), ratio=256 */ + counter += CODEC_IO_Write(DeviceAddr, 0x210, 0x00A3); + break; + + case AUDIO_FREQUENCY_11K: + /* AIF1 Sample Rate = 11.025 (KHz), ratio=256 */ + counter += CODEC_IO_Write(DeviceAddr, 0x210, 0x0013); + break; + + case AUDIO_FREQUENCY_22K: + /* AIF1 Sample Rate = 22.050 (KHz), ratio=256 */ + counter += CODEC_IO_Write(DeviceAddr, 0x210, 0x0043); + break; + + case AUDIO_FREQUENCY_44K: + /* AIF1 Sample Rate = 44.1 (KHz), ratio=256 */ + counter += CODEC_IO_Write(DeviceAddr, 0x210, 0x0073); + break; + + default: + /* AIF1 Sample Rate = 48 (KHz), ratio=256 */ + counter += CODEC_IO_Write(DeviceAddr, 0x210, 0x0083); + break; + } + + if(input_device == INPUT_DEVICE_DIGITAL_MIC1_MIC2) + { + /* AIF1 Word Length = 16-bits, AIF1 Format = DSP mode */ + counter += CODEC_IO_Write(DeviceAddr, 0x300, 0x4018); + } + else + { + /* AIF1 Word Length = 16-bits, AIF1 Format = I2S (Default Register Value) */ + counter += CODEC_IO_Write(DeviceAddr, 0x300, 0x4010); + } + + /* slave mode */ + counter += CODEC_IO_Write(DeviceAddr, 0x302, 0x0000); + + /* Enable the DSP processing clock for AIF1, Enable the core clock */ + counter += CODEC_IO_Write(DeviceAddr, 0x208, 0x000A); + + /* Enable AIF1 Clock, AIF1 Clock Source = MCLK1 pin */ + counter += CODEC_IO_Write(DeviceAddr, 0x200, 0x0001); + + if (output_device > 0) /* Audio output selected */ + { + if (output_device == OUTPUT_DEVICE_HEADPHONE) + { + /* Select DAC1 (Left) to Left Headphone Output PGA (HPOUT1LVOL) path */ + counter += CODEC_IO_Write(DeviceAddr, 0x2D, 0x0100); + + /* Select DAC1 (Right) to Right Headphone Output PGA (HPOUT1RVOL) path */ + counter += CODEC_IO_Write(DeviceAddr, 0x2E, 0x0100); + + /* Startup sequence for Headphone */ + if(ColdStartup) + { + counter += CODEC_IO_Write(DeviceAddr,0x110,0x8100); + + ColdStartup=0; + /* Add Delay */ + AUDIO_IO_Delay(300); + } + else /* Headphone Warm Start-Up */ + { + counter += CODEC_IO_Write(DeviceAddr,0x110,0x8108); + /* Add Delay */ + AUDIO_IO_Delay(50); + } + + /* Soft un-Mute the AIF1 Timeslot 0 DAC1 path L&R */ + counter += CODEC_IO_Write(DeviceAddr, 0x420, 0x0000); + } + /* Analog Output Configuration */ + + /* Enable SPKRVOL PGA, Enable SPKMIXR, Enable SPKLVOL PGA, Enable SPKMIXL */ + counter += CODEC_IO_Write(DeviceAddr, 0x03, 0x0300); + + /* Left Speaker Mixer Volume = 0dB */ + counter += CODEC_IO_Write(DeviceAddr, 0x22, 0x0000); + + /* Speaker output mode = Class D, Right Speaker Mixer Volume = 0dB ((0x23, 0x0100) = class AB)*/ + counter += CODEC_IO_Write(DeviceAddr, 0x23, 0x0000); + + /* Unmute DAC2 (Left) to Left Speaker Mixer (SPKMIXL) path, + Unmute DAC2 (Right) to Right Speaker Mixer (SPKMIXR) path */ + counter += CODEC_IO_Write(DeviceAddr, 0x36, 0x0300); + + /* Enable bias generator, Enable VMID, Enable SPKOUTL, Enable SPKOUTR */ + counter += CODEC_IO_Write(DeviceAddr, 0x01, 0x3003); + + /* Headphone/Speaker Enable */ + + if (input_device == INPUT_DEVICE_DIGITAL_MIC1_MIC2) + { + /* Enable Class W, Class W Envelope Tracking = AIF1 Timeslots 0 and 1 */ + counter += CODEC_IO_Write(DeviceAddr, 0x51, 0x0205); + } + else + { + /* Enable Class W, Class W Envelope Tracking = AIF1 Timeslot 0 */ + counter += CODEC_IO_Write(DeviceAddr, 0x51, 0x0005); + } + + /* Enable bias generator, Enable VMID, Enable HPOUT1 (Left) and Enable HPOUT1 (Right) input stages */ + /* idem for Speaker */ + power_mgnt_reg_1 |= 0x0303 | 0x3003; + counter += CODEC_IO_Write(DeviceAddr, 0x01, power_mgnt_reg_1); + + /* Enable HPOUT1 (Left) and HPOUT1 (Right) intermediate stages */ + counter += CODEC_IO_Write(DeviceAddr, 0x60, 0x0022); + + /* Enable Charge Pump */ + counter += CODEC_IO_Write(DeviceAddr, 0x4C, 0x9F25); + + /* Add Delay */ + AUDIO_IO_Delay(15); + + /* Select DAC1 (Left) to Left Headphone Output PGA (HPOUT1LVOL) path */ + counter += CODEC_IO_Write(DeviceAddr, 0x2D, 0x0001); + + /* Select DAC1 (Right) to Right Headphone Output PGA (HPOUT1RVOL) path */ + counter += CODEC_IO_Write(DeviceAddr, 0x2E, 0x0001); + + /* Enable Left Output Mixer (MIXOUTL), Enable Right Output Mixer (MIXOUTR) */ + /* idem for SPKOUTL and SPKOUTR */ + counter += CODEC_IO_Write(DeviceAddr, 0x03, 0x0030 | 0x0300); + + /* Enable DC Servo and trigger start-up mode on left and right channels */ + counter += CODEC_IO_Write(DeviceAddr, 0x54, 0x0033); + + /* Add Delay */ + AUDIO_IO_Delay(257); + + /* Enable HPOUT1 (Left) and HPOUT1 (Right) intermediate and output stages. Remove clamps */ + counter += CODEC_IO_Write(DeviceAddr, 0x60, 0x00EE); + + /* Unmutes */ + + /* Unmute DAC 1 (Left) */ + counter += CODEC_IO_Write(DeviceAddr, 0x610, 0x00C0); + + /* Unmute DAC 1 (Right) */ + counter += CODEC_IO_Write(DeviceAddr, 0x611, 0x00C0); + + /* Unmute the AIF1 Timeslot 0 DAC path */ + counter += CODEC_IO_Write(DeviceAddr, 0x420, 0x0010); + + /* Unmute DAC 2 (Left) */ + counter += CODEC_IO_Write(DeviceAddr, 0x612, 0x00C0); + + /* Unmute DAC 2 (Right) */ + counter += CODEC_IO_Write(DeviceAddr, 0x613, 0x00C0); + + /* Unmute the AIF1 Timeslot 1 DAC2 path */ + counter += CODEC_IO_Write(DeviceAddr, 0x422, 0x0010); + + /* Volume Control */ + wm8994_SetVolume(DeviceAddr, Volume); + } + + if (input_device > 0) /* Audio input selected */ + { + if ((input_device == INPUT_DEVICE_DIGITAL_MICROPHONE_1) || (input_device == INPUT_DEVICE_DIGITAL_MICROPHONE_2)) + { + /* Enable Microphone bias 1 generator, Enable VMID */ + power_mgnt_reg_1 |= 0x0013; + counter += CODEC_IO_Write(DeviceAddr, 0x01, power_mgnt_reg_1); + + /* ADC oversample enable */ + counter += CODEC_IO_Write(DeviceAddr, 0x620, 0x0002); + + /* AIF ADC2 HPF enable, HPF cut = voice mode 1 fc=127Hz at fs=8kHz */ + counter += CODEC_IO_Write(DeviceAddr, 0x411, 0x3800); + } + else if(input_device == INPUT_DEVICE_DIGITAL_MIC1_MIC2) + { + /* Enable Microphone bias 1 generator, Enable VMID */ + power_mgnt_reg_1 |= 0x0013; + counter += CODEC_IO_Write(DeviceAddr, 0x01, power_mgnt_reg_1); + + /* ADC oversample enable */ + counter += CODEC_IO_Write(DeviceAddr, 0x620, 0x0002); + + /* AIF ADC1 HPF enable, HPF cut = voice mode 1 fc=127Hz at fs=8kHz */ + counter += CODEC_IO_Write(DeviceAddr, 0x410, 0x1800); + + /* AIF ADC2 HPF enable, HPF cut = voice mode 1 fc=127Hz at fs=8kHz */ + counter += CODEC_IO_Write(DeviceAddr, 0x411, 0x1800); + } + else if ((input_device == INPUT_DEVICE_INPUT_LINE_1) || (input_device == INPUT_DEVICE_INPUT_LINE_2)) + { + + /* Disable mute on IN1L, IN1L Volume = +0dB */ + counter += CODEC_IO_Write(DeviceAddr, 0x18, 0x000B); + + /* Disable mute on IN1R, IN1R Volume = +0dB */ + counter += CODEC_IO_Write(DeviceAddr, 0x1A, 0x000B); + + /* AIF ADC1 HPF enable, HPF cut = hifi mode fc=4Hz at fs=48kHz */ + counter += CODEC_IO_Write(DeviceAddr, 0x410, 0x1800); + } + /* Volume Control */ + wm8994_SetVolume(DeviceAddr, Volume); + } + /* Return communication control value */ + return counter; +} + +/** + * @brief Deinitializes the audio codec. + * @param None + * @retval None + */ +void wm8994_DeInit(void) +{ + /* Deinitialize Audio Codec interface */ + AUDIO_IO_DeInit(); +} + +/** + * @brief Get the WM8994 ID. + * @param DeviceAddr: Device address on communication Bus. + * @retval The WM8994 ID + */ +uint32_t wm8994_ReadID(uint16_t DeviceAddr) +{ + /* Initialize the Control interface of the Audio Codec */ + AUDIO_IO_Init(); + + return ((uint32_t)AUDIO_IO_Read(DeviceAddr, WM8994_CHIPID_ADDR)); +} + +/** + * @brief Start the audio Codec play feature. + * @note For this codec no Play options are required. + * @param DeviceAddr: Device address on communication Bus. + * @retval 0 if correct communication, else wrong communication + */ +uint32_t wm8994_Play(uint16_t DeviceAddr, uint16_t* pBuffer, uint16_t Size) +{ + uint32_t counter = 0; + + /* Resumes the audio file playing */ + /* Unmute the output first */ + counter += wm8994_SetMute(DeviceAddr, AUDIO_MUTE_OFF); + + return counter; +} + +/** + * @brief Pauses playing on the audio codec. + * @param DeviceAddr: Device address on communication Bus. + * @retval 0 if correct communication, else wrong communication + */ +uint32_t wm8994_Pause(uint16_t DeviceAddr) +{ + uint32_t counter = 0; + + /* Pause the audio file playing */ + /* Mute the output first */ + counter += wm8994_SetMute(DeviceAddr, AUDIO_MUTE_ON); + + /* Put the Codec in Power save mode */ + counter += CODEC_IO_Write(DeviceAddr, 0x02, 0x01); + + return counter; +} + +/** + * @brief Resumes playing on the audio codec. + * @param DeviceAddr: Device address on communication Bus. + * @retval 0 if correct communication, else wrong communication + */ +uint32_t wm8994_Resume(uint16_t DeviceAddr) +{ + uint32_t counter = 0; + + /* Resumes the audio file playing */ + /* Unmute the output first */ + counter += wm8994_SetMute(DeviceAddr, AUDIO_MUTE_OFF); + + return counter; +} + +/** + * @brief Stops audio Codec playing. It powers down the codec. + * @param DeviceAddr: Device address on communication Bus. + * @param CodecPdwnMode: selects the power down mode. + * - CODEC_PDWN_SW: only mutes the audio codec. When resuming from this + * mode the codec keeps the previous initialization + * (no need to re-Initialize the codec registers). + * - CODEC_PDWN_HW: Physically power down the codec. When resuming from this + * mode, the codec is set to default configuration + * (user should re-Initialize the codec in order to + * play again the audio stream). + * @retval 0 if correct communication, else wrong communication + */ +uint32_t wm8994_Stop(uint16_t DeviceAddr, uint32_t CodecPdwnMode) +{ + uint32_t counter = 0; + + if (outputEnabled != 0) + { + /* Mute the output first */ + counter += wm8994_SetMute(DeviceAddr, AUDIO_MUTE_ON); + + if (CodecPdwnMode == CODEC_PDWN_SW) + { + /* Only output mute required*/ + } + else /* CODEC_PDWN_HW */ + { + /* Mute the AIF1 Timeslot 0 DAC1 path */ + counter += CODEC_IO_Write(DeviceAddr, 0x420, 0x0200); + + /* Mute the AIF1 Timeslot 1 DAC2 path */ + counter += CODEC_IO_Write(DeviceAddr, 0x422, 0x0200); + + /* Disable DAC1L_TO_HPOUT1L */ + counter += CODEC_IO_Write(DeviceAddr, 0x2D, 0x0000); + + /* Disable DAC1R_TO_HPOUT1R */ + counter += CODEC_IO_Write(DeviceAddr, 0x2E, 0x0000); + + /* Disable DAC1 and DAC2 */ + counter += CODEC_IO_Write(DeviceAddr, 0x05, 0x0000); + + /* Reset Codec by writing in 0x0000 address register */ + counter += CODEC_IO_Write(DeviceAddr, 0x0000, 0x0000); + + outputEnabled = 0; + } + } + return counter; +} + +/** + * @brief Sets higher or lower the codec volume level. + * @param DeviceAddr: Device address on communication Bus. + * @param Volume: a byte value from 0 to 255 (refer to codec registers + * description for more details). + * @retval 0 if correct communication, else wrong communication + */ +uint32_t wm8994_SetVolume(uint16_t DeviceAddr, uint8_t Volume) +{ + uint32_t counter = 0; + uint8_t convertedvol = VOLUME_CONVERT(Volume); + + /* Output volume */ + if (outputEnabled != 0) + { + if(convertedvol > 0x3E) + { + /* Unmute audio codec */ + counter += wm8994_SetMute(DeviceAddr, AUDIO_MUTE_OFF); + + /* Left Headphone Volume */ + counter += CODEC_IO_Write(DeviceAddr, 0x1C, 0x3F | 0x140); + + /* Right Headphone Volume */ + counter += CODEC_IO_Write(DeviceAddr, 0x1D, 0x3F | 0x140); + + /* Left Speaker Volume */ + counter += CODEC_IO_Write(DeviceAddr, 0x26, 0x3F | 0x140); + + /* Right Speaker Volume */ + counter += CODEC_IO_Write(DeviceAddr, 0x27, 0x3F | 0x140); + } + else if (Volume == 0) + { + /* Mute audio codec */ + counter += wm8994_SetMute(DeviceAddr, AUDIO_MUTE_ON); + } + else + { + /* Unmute audio codec */ + counter += wm8994_SetMute(DeviceAddr, AUDIO_MUTE_OFF); + + /* Left Headphone Volume */ + counter += CODEC_IO_Write(DeviceAddr, 0x1C, convertedvol | 0x140); + + /* Right Headphone Volume */ + counter += CODEC_IO_Write(DeviceAddr, 0x1D, convertedvol | 0x140); + + /* Left Speaker Volume */ + counter += CODEC_IO_Write(DeviceAddr, 0x26, convertedvol | 0x140); + + /* Right Speaker Volume */ + counter += CODEC_IO_Write(DeviceAddr, 0x27, convertedvol | 0x140); + } + } + + /* Input volume */ + if (inputEnabled != 0) + { + convertedvol = VOLUME_IN_CONVERT(Volume); + + /* Left AIF1 ADC1 volume */ + counter += CODEC_IO_Write(DeviceAddr, 0x400, convertedvol | 0x100); + + /* Right AIF1 ADC1 volume */ + counter += CODEC_IO_Write(DeviceAddr, 0x401, convertedvol | 0x100); + + /* Left AIF1 ADC2 volume */ + counter += CODEC_IO_Write(DeviceAddr, 0x404, convertedvol | 0x100); + + /* Right AIF1 ADC2 volume */ + counter += CODEC_IO_Write(DeviceAddr, 0x405, convertedvol | 0x100); + } + return counter; +} + +/** + * @brief Enables or disables the mute feature on the audio codec. + * @param DeviceAddr: Device address on communication Bus. + * @param Cmd: AUDIO_MUTE_ON to enable the mute or AUDIO_MUTE_OFF to disable the + * mute mode. + * @retval 0 if correct communication, else wrong communication + */ +uint32_t wm8994_SetMute(uint16_t DeviceAddr, uint32_t Cmd) +{ + uint32_t counter = 0; + + if (outputEnabled != 0) + { + /* Set the Mute mode */ + if(Cmd == AUDIO_MUTE_ON) + { + /* Soft Mute the AIF1 Timeslot 0 DAC1 path L&R */ + counter += CODEC_IO_Write(DeviceAddr, 0x420, 0x0200); + + /* Soft Mute the AIF1 Timeslot 1 DAC2 path L&R */ + counter += CODEC_IO_Write(DeviceAddr, 0x422, 0x0200); + } + else /* AUDIO_MUTE_OFF Disable the Mute */ + { + /* Unmute the AIF1 Timeslot 0 DAC1 path L&R */ + counter += CODEC_IO_Write(DeviceAddr, 0x420, 0x0010); + + /* Unmute the AIF1 Timeslot 1 DAC2 path L&R */ + counter += CODEC_IO_Write(DeviceAddr, 0x422, 0x0010); + } + } + return counter; +} + +/** + * @brief Switch dynamically (while audio file is played) the output target + * (speaker or headphone). + * @param DeviceAddr: Device address on communication Bus. + * @param Output: specifies the audio output target: OUTPUT_DEVICE_SPEAKER, + * OUTPUT_DEVICE_HEADPHONE, OUTPUT_DEVICE_BOTH or OUTPUT_DEVICE_AUTO + * @retval 0 if correct communication, else wrong communication + */ +uint32_t wm8994_SetOutputMode(uint16_t DeviceAddr, uint8_t Output) +{ + uint32_t counter = 0; + + switch (Output) + { + case OUTPUT_DEVICE_SPEAKER: + /* Enable DAC1 (Left), Enable DAC1 (Right), + Disable DAC2 (Left), Disable DAC2 (Right)*/ + counter += CODEC_IO_Write(DeviceAddr, 0x05, 0x0C0C); + + /* Enable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x601, 0x0000); + + /* Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x602, 0x0000); + + /* Disable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x604, 0x0002); + + /* Disable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x605, 0x0002); + break; + + case OUTPUT_DEVICE_HEADPHONE: + /* Disable DAC1 (Left), Disable DAC1 (Right), + Enable DAC2 (Left), Enable DAC2 (Right)*/ + counter += CODEC_IO_Write(DeviceAddr, 0x05, 0x0303); + + /* Enable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x601, 0x0001); + + /* Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x602, 0x0001); + + /* Disable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x604, 0x0000); + + /* Disable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x605, 0x0000); + break; + + case OUTPUT_DEVICE_BOTH: + /* Enable DAC1 (Left), Enable DAC1 (Right), + also Enable DAC2 (Left), Enable DAC2 (Right)*/ + counter += CODEC_IO_Write(DeviceAddr, 0x05, 0x0303 | 0x0C0C); + + /* Enable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x601, 0x0001); + + /* Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x602, 0x0001); + + /* Enable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x604, 0x0002); + + /* Enable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x605, 0x0002); + break; + + default: + /* Disable DAC1 (Left), Disable DAC1 (Right), + Enable DAC2 (Left), Enable DAC2 (Right)*/ + counter += CODEC_IO_Write(DeviceAddr, 0x05, 0x0303); + + /* Enable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x601, 0x0001); + + /* Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x602, 0x0001); + + /* Disable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x604, 0x0000); + + /* Disable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path */ + counter += CODEC_IO_Write(DeviceAddr, 0x605, 0x0000); + break; + } + return counter; +} + +/** + * @brief Sets new frequency. + * @param DeviceAddr: Device address on communication Bus. + * @param AudioFreq: Audio frequency used to play the audio stream. + * @retval 0 if correct communication, else wrong communication + */ +uint32_t wm8994_SetFrequency(uint16_t DeviceAddr, uint32_t AudioFreq) +{ + uint32_t counter = 0; + + /* Clock Configurations */ + switch (AudioFreq) + { + case AUDIO_FREQUENCY_8K: + /* AIF1 Sample Rate = 8 (KHz), ratio=256 */ + counter += CODEC_IO_Write(DeviceAddr, 0x210, 0x0003); + break; + + case AUDIO_FREQUENCY_16K: + /* AIF1 Sample Rate = 16 (KHz), ratio=256 */ + counter += CODEC_IO_Write(DeviceAddr, 0x210, 0x0033); + break; + + case AUDIO_FREQUENCY_32K: + /* AIF1 Sample Rate = 32 (KHz), ratio=256 */ + counter += CODEC_IO_Write(DeviceAddr, 0x210, 0x0063); + break; + + case AUDIO_FREQUENCY_48K: + /* AIF1 Sample Rate = 48 (KHz), ratio=256 */ + counter += CODEC_IO_Write(DeviceAddr, 0x210, 0x0083); + break; + + case AUDIO_FREQUENCY_96K: + /* AIF1 Sample Rate = 96 (KHz), ratio=256 */ + counter += CODEC_IO_Write(DeviceAddr, 0x210, 0x00A3); + break; + + case AUDIO_FREQUENCY_11K: + /* AIF1 Sample Rate = 11.025 (KHz), ratio=256 */ + counter += CODEC_IO_Write(DeviceAddr, 0x210, 0x0013); + break; + + case AUDIO_FREQUENCY_22K: + /* AIF1 Sample Rate = 22.050 (KHz), ratio=256 */ + counter += CODEC_IO_Write(DeviceAddr, 0x210, 0x0043); + break; + + case AUDIO_FREQUENCY_44K: + /* AIF1 Sample Rate = 44.1 (KHz), ratio=256 */ + counter += CODEC_IO_Write(DeviceAddr, 0x210, 0x0073); + break; + + default: + /* AIF1 Sample Rate = 48 (KHz), ratio=256 */ + counter += CODEC_IO_Write(DeviceAddr, 0x210, 0x0083); + break; + } + return counter; +} + +/** + * @brief Resets wm8994 registers. + * @param DeviceAddr: Device address on communication Bus. + * @retval 0 if correct communication, else wrong communication + */ +uint32_t wm8994_Reset(uint16_t DeviceAddr) +{ + uint32_t counter = 0; + + /* Reset Codec by writing in 0x0000 address register */ + counter = CODEC_IO_Write(DeviceAddr, 0x0000, 0x0000); + outputEnabled = 0; + inputEnabled=0; + + return counter; +} + +/** + * @brief Writes/Read a single data. + * @param Addr: I2C address + * @param Reg: Reg address + * @param Value: Data to be written + * @retval None + */ +static uint8_t CODEC_IO_Write(uint8_t Addr, uint16_t Reg, uint16_t Value) +{ + uint32_t result = 0; + + AUDIO_IO_Write(Addr, Reg, Value); + +#ifdef VERIFY_WRITTENDATA + /* Verify that the data has been correctly written */ + result = (AUDIO_IO_Read(Addr, Reg) == Value)? 0:1; +#endif /* VERIFY_WRITTENDATA */ + + return result; +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/BSP/wm8994.h b/BSP/wm8994.h new file mode 100644 index 0000000..9108d52 --- /dev/null +++ b/BSP/wm8994.h @@ -0,0 +1,186 @@ +/** + ****************************************************************************** + * @file wm8994.h + * @author MCD Application Team + * @brief This file contains all the functions prototypes for the + * wm8994.c driver. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2016 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __WM8994_H +#define __WM8994_H + +/* Includes ------------------------------------------------------------------*/ +#include "audio.h" + +/** @addtogroup BSP + * @{ + */ + +/** @addtogroup Component + * @{ + */ + +/** @addtogroup WM8994 + * @{ + */ + +/** @defgroup WM8994_Exported_Types + * @{ + */ + +/** + * @} + */ + +/** @defgroup WM8994_Exported_Constants + * @{ + */ + +/******************************************************************************/ +/*************************** Codec User defines ******************************/ +/******************************************************************************/ +/* Codec output DEVICE */ +#define OUTPUT_DEVICE_SPEAKER ((uint16_t)0x0001) +#define OUTPUT_DEVICE_HEADPHONE ((uint16_t)0x0002) +#define OUTPUT_DEVICE_BOTH ((uint16_t)0x0003) +#define OUTPUT_DEVICE_AUTO ((uint16_t)0x0004) +#define INPUT_DEVICE_DIGITAL_MICROPHONE_1 ((uint16_t)0x0100) +#define INPUT_DEVICE_DIGITAL_MICROPHONE_2 ((uint16_t)0x0200) +#define INPUT_DEVICE_INPUT_LINE_1 ((uint16_t)0x0300) +#define INPUT_DEVICE_INPUT_LINE_2 ((uint16_t)0x0400) +#define INPUT_DEVICE_DIGITAL_MIC1_MIC2 ((uint16_t)0x0800) + +/* Volume Levels values */ +#define DEFAULT_VOLMIN 0x00 +#define DEFAULT_VOLMAX 0xFF +#define DEFAULT_VOLSTEP 0x04 + +#define AUDIO_PAUSE 0 +#define AUDIO_RESUME 1 + +/* Codec POWER DOWN modes */ +#define CODEC_PDWN_HW 1 +#define CODEC_PDWN_SW 2 + +/* MUTE commands */ +#define AUDIO_MUTE_ON 1 +#define AUDIO_MUTE_OFF 0 + +/* AUDIO FREQUENCY */ +#define AUDIO_FREQUENCY_192K ((uint32_t)192000) +#define AUDIO_FREQUENCY_96K ((uint32_t)96000) +#define AUDIO_FREQUENCY_48K ((uint32_t)48000) +#define AUDIO_FREQUENCY_44K ((uint32_t)44100) +#define AUDIO_FREQUENCY_32K ((uint32_t)32000) +#define AUDIO_FREQUENCY_22K ((uint32_t)22050) +#define AUDIO_FREQUENCY_16K ((uint32_t)16000) +#define AUDIO_FREQUENCY_11K ((uint32_t)11025) +#define AUDIO_FREQUENCY_8K ((uint32_t)8000) + +#define VOLUME_CONVERT(Volume) (((Volume) > 100)? 100:((uint8_t)(((Volume) * 63) / 100))) +#define VOLUME_IN_CONVERT(Volume) (((Volume) >= 100)? 239:((uint8_t)(((Volume) * 240) / 100))) + +/******************************************************************************/ +/****************************** REGISTER MAPPING ******************************/ +/******************************************************************************/ +/** + * @brief WM8994 ID + */ +#define WM8994_ID 0x8994 + +/** + * @brief Device ID Register: Reading from this register will indicate device + * family ID 8994h + */ +#define WM8994_CHIPID_ADDR 0x00 + +/** + * @} + */ + +/** @defgroup WM8994_Exported_Macros + * @{ + */ +/** + * @} + */ + +/** @defgroup WM8994_Exported_Functions + * @{ + */ + +/*------------------------------------------------------------------------------ + Audio Codec functions +------------------------------------------------------------------------------*/ +/* High Layer codec functions */ +uint32_t wm8994_Init(uint16_t DeviceAddr, uint16_t OutputInputDevice, uint8_t Volume, uint32_t AudioFreq); +void wm8994_DeInit(void); +uint32_t wm8994_ReadID(uint16_t DeviceAddr); +uint32_t wm8994_Play(uint16_t DeviceAddr, uint16_t* pBuffer, uint16_t Size); +uint32_t wm8994_Pause(uint16_t DeviceAddr); +uint32_t wm8994_Resume(uint16_t DeviceAddr); +uint32_t wm8994_Stop(uint16_t DeviceAddr, uint32_t Cmd); +uint32_t wm8994_SetVolume(uint16_t DeviceAddr, uint8_t Volume); +uint32_t wm8994_SetMute(uint16_t DeviceAddr, uint32_t Cmd); +uint32_t wm8994_SetOutputMode(uint16_t DeviceAddr, uint8_t Output); +uint32_t wm8994_SetFrequency(uint16_t DeviceAddr, uint32_t AudioFreq); +uint32_t wm8994_Reset(uint16_t DeviceAddr); + +/* AUDIO IO functions */ +void AUDIO_IO_Init(void); +void AUDIO_IO_DeInit(void); +void AUDIO_IO_Write(uint8_t Addr, uint16_t Reg, uint16_t Value); +uint8_t AUDIO_IO_Read(uint8_t Addr, uint16_t Reg); +void AUDIO_IO_Delay(uint32_t Delay); + +/* Audio driver structure */ +extern AUDIO_DrvTypeDef wm8994_drv; + +#endif /* __WM8994_H */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d770c4..e34f896 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,12 +12,19 @@ # Globally enable some compiler sanity add_compile_options(-fwrapv -fno-strict-aliasing -fsigned-char) +# Create BSP project +project(STBSP) +file(GLOB BSP_FILES "${PROJECT_SOURCE_DIR}/BSP/*.c") +add_library(STBSP STATIC ${BSP_FILES}) +target_link_libraries(STBSP PRIVATE mbed-os) +target_include_directories(STBSP PUBLIC "${PROJECT_SOURCE_DIR}/BSP") + # Create Tardis project project(Tardis) file(GLOB SRC_FILES "${PROJECT_SOURCE_DIR}/src/*.cpp") add_executable(Tardis ${SRC_FILES}) target_include_directories(Tardis PRIVATE "${PROJECT_SOURCE_DIR}/include") -target_link_libraries(Tardis PRIVATE mbed-os mbed-storage-qspif mbed-usb-msd mbed-storage-littlefs-v2) +target_link_libraries(Tardis PRIVATE mbed-os mbed-storage-qspif mbed-usb-msd mbed-storage-littlefs-v2 STBSP) target_compile_options(Tardis PRIVATE -Wall -Wextra -Wpedantic -Werror) mbed_set_post_build(Tardis) diff --git a/src/main.cpp b/src/main.cpp index b3224c5..5a3e748 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,24 +4,138 @@ Copyright (c) 2023 John Watts and the LuminaSensum contributors */ -#include "ButtonThread.h" -#include "Filesystem.h" -#include "FlashErase.h" -#include "MainBD.h" -#include "MainFilesystem.h" -#include "MyUSBMSD.h" #include "mbed.h" - -int main() { - printf("Project Tardis\n"); - - mountFilesystem(mainBD, mainFS); - - Thread buttonThread; - buttonThread.start(buttonTask); - - unmountFilesystem(mainBD, mainFS); - buttonThread.terminate(); - - return 0; +#include "stm32f769i_discovery.h" +#include "stm32f769i_discovery_audio.h" + +static void CopyBuffer(int16_t *pbuffer1, int16_t *pbuffer2, uint16_t BufferSize); + +#define SCRATCH_BUFF_SIZE 1024 +#define RECORD_BUFFER_SIZE 4096 + +typedef enum { + BUFFER_OFFSET_NONE = 0, + BUFFER_OFFSET_HALF = 1, + BUFFER_OFFSET_FULL = 2, +} BUFFER_StateTypeDef; + +volatile uint32_t audio_rec_buffer_state = BUFFER_OFFSET_NONE; +int32_t Scratch[SCRATCH_BUFF_SIZE]; + +/* Buffer containing the PCM samples coming from the microphone */ +int16_t RecordBuffer[RECORD_BUFFER_SIZE]; + +/* Buffer used to stream the recorded PCM samples towards the audio codec. */ +int16_t PlaybackBuffer[RECORD_BUFFER_SIZE]; + +int main() +{ + uint32_t audio_loop_back_init = RESET ; + + printf("\n\nAUDIO LOOPBACK EXAMPLE FOR DISCO-F769NI START:\n"); + + /* Initialize Audio Recorder with 4 channels to be used */ + if (BSP_AUDIO_IN_Init(BSP_AUDIO_FREQUENCY_44K, DEFAULT_AUDIO_IN_BIT_RESOLUTION, 2*DEFAULT_AUDIO_IN_CHANNEL_NBR) == AUDIO_ERROR) { + printf("BSP_AUDIO_IN_Init error\n"); + } + + /* Allocate scratch buffers */ + if (BSP_AUDIO_IN_AllocScratch (Scratch, SCRATCH_BUFF_SIZE) == AUDIO_ERROR) { + printf("BSP_AUDIO_IN_AllocScratch error\n"); + } + + /* Start Recording */ + if (BSP_AUDIO_IN_Record((uint16_t*)&RecordBuffer[0], RECORD_BUFFER_SIZE) == AUDIO_ERROR) { + printf("BSP_AUDIO_IN_Record error\n"); + } + uint8_t ChannelNumber = BSP_AUDIO_IN_GetChannelNumber(); + + audio_rec_buffer_state = BUFFER_OFFSET_NONE; + + while (1) { + /* 1st or 2nd half of the record buffer ready for being copied to the playbakc buffer */ + if(audio_rec_buffer_state != BUFFER_OFFSET_NONE) { + /* Copy half of the record buffer to the playback buffer */ + if(audio_rec_buffer_state == BUFFER_OFFSET_HALF) { + CopyBuffer(&PlaybackBuffer[0], &RecordBuffer[0], RECORD_BUFFER_SIZE/2); + if (audio_loop_back_init == RESET) { + /* Initialize the audio device*/ + if (BSP_AUDIO_OUT_Init(OUTPUT_DEVICE_HEADPHONE, 70, BSP_AUDIO_FREQUENCY_44K) == AUDIO_ERROR) { + printf("BSP_AUDIO_OUT_Init error\n"); + } + if(ChannelNumber > 2) { + BSP_AUDIO_OUT_SetAudioFrameSlot(CODEC_AUDIOFRAME_SLOT_0123); + } else { + BSP_AUDIO_OUT_SetAudioFrameSlot(CODEC_AUDIOFRAME_SLOT_02); + } + + /* Play the recorded buffer */ + if (BSP_AUDIO_OUT_Play((uint16_t *) &PlaybackBuffer[0], 2*RECORD_BUFFER_SIZE) == AUDIO_ERROR) { + printf("BSP_AUDIO_OUT_Play error\n"); + } + /* Audio device is initialized only once */ + audio_loop_back_init = SET; + } + + + } else { /* if(audio_rec_buffer_state == BUFFER_OFFSET_FULL)*/ + CopyBuffer(&PlaybackBuffer[RECORD_BUFFER_SIZE/2], &RecordBuffer[RECORD_BUFFER_SIZE/2], RECORD_BUFFER_SIZE/2); + } + + /* Wait for next data */ + audio_rec_buffer_state = BUFFER_OFFSET_NONE; + } + } +} + +/*------------------------------------------------------------------------------------- + Callbacks implementation: + the callbacks API are defined __weak in the stm32f769i_discovery_audio.c file + and their implementation should be done in the user code if they are needed. + Below some examples of callback implementations. + -------------------------------------------------------------------------------------*/ +/** + * @brief Manages the DMA Transfer complete interrupt. + * @param None + * @retval None + */ +void BSP_AUDIO_IN_TransferComplete_CallBack(void) +{ + audio_rec_buffer_state = BUFFER_OFFSET_FULL; +} + +/** + * @brief Manages the DMA Half Transfer complete interrupt. + * @param None + * @retval None + */ +void BSP_AUDIO_IN_HalfTransfer_CallBack(void) +{ + audio_rec_buffer_state = BUFFER_OFFSET_HALF; +} + +/** + * @brief Audio IN Error callback function. + * @param None + * @retval None + */ +void BSP_AUDIO_IN_Error_CallBack(void) +{ + printf("BSP_AUDIO_IN_Error_CallBack\n"); +} + + +/** + * @brief Copy content of pbuffer2 to pbuffer1 + * @param1 BufferOut + * @param2 BufferIn + * @param3 Size + * @retval None + */ +static void CopyBuffer(int16_t *pbuffer1, int16_t *pbuffer2, uint16_t BufferSize) +{ + uint32_t i = 0; + for(i = 0; i < BufferSize; i++) { + pbuffer1[i] = pbuffer2[i]; + } }