From 747995010b540dee05dcbb8d36012943445f5144 Mon Sep 17 00:00:00 2001 From: jonny Date: Mon, 15 Dec 2025 19:02:07 +0100 Subject: [PATCH] Add received files waveplayer, audio --- Versuch5/Core/Inc/audio.h | 49 +++ Versuch5/Core/Inc/waveplayer.h | 11 + Versuch5/Core/Src/audio.c | 624 +++++++++++++++++++++++++++++++++ Versuch5/Core/Src/waveplayer.c | 66 ++++ 4 files changed, 750 insertions(+) create mode 100644 Versuch5/Core/Inc/audio.h create mode 100644 Versuch5/Core/Inc/waveplayer.h create mode 100644 Versuch5/Core/Src/audio.c create mode 100644 Versuch5/Core/Src/waveplayer.c diff --git a/Versuch5/Core/Inc/audio.h b/Versuch5/Core/Inc/audio.h new file mode 100644 index 0000000..cac3618 --- /dev/null +++ b/Versuch5/Core/Inc/audio.h @@ -0,0 +1,49 @@ +/******************************************************************* + File: audio.h + Date: 9-September-2020 + Author: Peter Spindler + ********************************************************************/ + +#ifndef _AUDIO_H_ +#define _AUDIO_H_ + +#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) + + +#define CODEC_PDWN_HW 1 +#define CODEC_PDWN_SW 2 + +#define AUDIO_MUTE_ON 1 +#define AUDIO_MUTE_OFF 0 + +#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 AUDIO_OK ((uint8_t)0) +#define AUDIO_ERROR ((uint8_t)1) +#define AUDIO_TIMEOUT ((uint8_t)2) + +uint8_t Audio_Init(uint16_t OutputDevice, uint8_t Volume, uint32_t AudioFreq ); +uint8_t Audio_SetFrequency( uint32_t AudioFreq ); +uint8_t Audio_SetVolume( uint8_t Volume ); +uint8_t Audio_Play( uint8_t *Buffer, uint32_t Size ); +uint8_t Audio_Pause( void ); +uint8_t Audio_Resume( void ); +uint8_t Audio_Stop( void ); + +#endif diff --git a/Versuch5/Core/Inc/waveplayer.h b/Versuch5/Core/Inc/waveplayer.h new file mode 100644 index 0000000..3c96068 --- /dev/null +++ b/Versuch5/Core/Inc/waveplayer.h @@ -0,0 +1,11 @@ +/******************************************************************* +File: waveplayer.h +Date: 9-Sep-2020 +Author: Peter Spindler +********************************************************************/ +#ifndef _WAVEPLAYER_H_ +#define _WAVEPLAYER_H_ +void Player_Init( void ); +void Player_Start( const char *Filename ); +void Player_Stop( void ); +#endif diff --git a/Versuch5/Core/Src/audio.c b/Versuch5/Core/Src/audio.c new file mode 100644 index 0000000..c9dff13 --- /dev/null +++ b/Versuch5/Core/Src/audio.c @@ -0,0 +1,624 @@ +/******************************************************************* + File: audio.c + Date: 9-September-2020 + Author: Peter Spindler + Description: Audio driver, including codec via I2C and data stream via SAI + + Based on audio driver from STMicroelectronics: stm32f723e_discovery_audio.c + STM32Cube Firmware F7 Version 1.16.0, STM32F723E Discovery board + + License from 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. +********************************************************************/ + +#include "stm32f7xx_hal.h" +#include "audio.h" +#include + +#define AUDIO_I2C_ADDRESS 0x34 +#define AUDIO_I2C_HANDLE hi2c1 +#define AUDIO_SAI_HANDLE hsai_BlockA2 + +extern I2C_HandleTypeDef AUDIO_I2C_HANDLE; +extern SAI_HandleTypeDef AUDIO_SAI_HANDLE; + +static uint32_t AudioCodec_OutputEnabled = 0; +static uint32_t AudioCodec_InputEnabled = 0; +static uint8_t AudioCodec_ColdStartup = 1; + +#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))) + +#define CODEC_AUDIOFRAME_SLOT_0123 SAI_SLOTACTIVE_0 | SAI_SLOTACTIVE_1 | SAI_SLOTACTIVE_2 | SAI_SLOTACTIVE_3 +#define CODEC_AUDIOFRAME_SLOT_02 SAI_SLOTACTIVE_0 | SAI_SLOTACTIVE_2 +#define CODEC_AUDIOFRAME_SLOT_13 SAI_SLOTACTIVE_1 | SAI_SLOTACTIVE_3 + +//extern void Player_CompleteTransfer_CallBack( void ); +//extern void Player_HalfTransfer_CallBack( void ); + +static uint32_t AudioCodec_SetFrequency( uint32_t AudioFreq ); +static uint32_t AudioCodec_SetVolume(uint8_t Volume); +static uint32_t AudioCodec_SetMute( uint32_t Cmd ); + +static uint16_t AudioCodec_Read( uint16_t Reg ) { + uint16_t read_value = 0, tmp = 0; + HAL_I2C_Mem_Read(&AUDIO_I2C_HANDLE, AUDIO_I2C_ADDRESS, Reg, I2C_MEMADD_SIZE_16BIT, (uint8_t*)&read_value, 2, 1000); + tmp = ((uint16_t)(read_value >> 8) & 0x00FF); + tmp |= ((uint16_t)(read_value << 8)& 0xFF00); + read_value = tmp; + return read_value; +} + +static uint8_t AudioCodec_Write( uint16_t Reg, uint16_t Value ) { + uint32_t result = 0; + uint16_t tmp = Value; + Value = ((uint16_t)(tmp >> 8) & 0x00FF); + Value |= ((uint16_t)(tmp << 8)& 0xFF00); + HAL_I2C_Mem_Write(&AUDIO_I2C_HANDLE, AUDIO_I2C_ADDRESS, (uint16_t)Reg, I2C_MEMADD_SIZE_16BIT,(uint8_t*)&Value, 2, 1000); +#ifdef VERIFY_WRITTENDATA + result = (AudioCodec_Read(Reg) == Value)? 0:1; +#endif + return result; +} + +static uint16_t AudioCodec_ReadId( void ) { + return ((uint16_t)AudioCodec_Read(0x00)); +} + +static uint32_t AudioCodec_Reset( void ) { + uint32_t counter = 0; + // Reset Codec by writing in 0x0000 address register: + counter = AudioCodec_Write( 0x0000, 0x0000 ); + AudioCodec_OutputEnabled = 0; + AudioCodec_InputEnabled=0; + return counter; +} + +static uint32_t AudioCodec_Init( 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; + + // wm8994 Errata Work-Arounds + counter += AudioCodec_Write( 0x102, 0x0003); + counter += AudioCodec_Write( 0x817, 0x0000); + counter += AudioCodec_Write( 0x102, 0x0000); + + counter += AudioCodec_Write( 0x39, 0x006C); // Enable VMID soft start (fast), Start-up Bias Current Enabled + + // Enable bias generator, Enable VMID: + if( input_device > 0 ) { + counter += AudioCodec_Write( 0x01, 0x0013); + } else { + counter += AudioCodec_Write( 0x01, 0x0003); + } + + HAL_Delay(50); + + // Path Configurations for output: + if( output_device > 0 ) { + AudioCodec_OutputEnabled = 1; + + switch (output_device) { + case OUTPUT_DEVICE_SPEAKER: + counter += AudioCodec_Write( 0x05, 0x0C0C); // Enable DAC1 (Left), Enable DAC1 (Right), Disable DAC2 (Left), Disable DAC2 (Right) + counter += AudioCodec_Write( 0x601, 0x0000); // Enable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path + counter += AudioCodec_Write( 0x602, 0x0000); // Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path + counter += AudioCodec_Write( 0x604, 0x0002); // Disable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path + counter += AudioCodec_Write( 0x605, 0x0002); // Disable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path + break; + + case OUTPUT_DEVICE_HEADPHONE: + counter += AudioCodec_Write( 0x05, 0x0303); // Disable DAC1 (Left), Disable DAC1 (Right), Enable DAC2 (Left), Enable DAC2 (Right) + counter += AudioCodec_Write( 0x601, 0x0001); // Enable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path + counter += AudioCodec_Write( 0x602, 0x0001); // Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path + counter += AudioCodec_Write( 0x604, 0x0000); // Disable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path + counter += AudioCodec_Write( 0x605, 0x0000); // Disable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path + break; + + case OUTPUT_DEVICE_BOTH: + if (input_device == INPUT_DEVICE_DIGITAL_MIC1_MIC2) { + counter += AudioCodec_Write( 0x05, 0x0303 | 0x0C0C); // Enable DAC1 (Left), Enable DAC1 (Right), also Enable DAC2 (Left), Enable DAC2 (Right) + counter += AudioCodec_Write( 0x601, 0x0003); // 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 += AudioCodec_Write( 0x602, 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 += AudioCodec_Write( 0x604, 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 += AudioCodec_Write( 0x605, 0x0003); // Enable the AIF1 Timeslot 0 (Right) to DAC 2 (Right) mixer path,eEnable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path + } else { + counter += AudioCodec_Write( 0x05, 0x0303 | 0x0C0C); // Enable DAC1 (Left), Enable DAC1 (Right), also Enable DAC2 (Left), Enable DAC2 (Right) + counter += AudioCodec_Write( 0x601, 0x0001); // Enable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path + counter += AudioCodec_Write( 0x602, 0x0001); // Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path + counter += AudioCodec_Write( 0x604, 0x0002); // Enable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path + counter += AudioCodec_Write( 0x605, 0x0002); // Enable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path + } + break; + + case OUTPUT_DEVICE_AUTO : + default: + counter += AudioCodec_Write( 0x05, 0x0303); // Disable DAC1 (Left), Disable DAC1 (Right), enable DAC2 (Left), Enable DAC2 (Right) + counter += AudioCodec_Write( 0x601, 0x0001); // Enable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path + counter += AudioCodec_Write( 0x602, 0x0001); // Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path + counter += AudioCodec_Write( 0x604, 0x0000); // Disable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path + counter += AudioCodec_Write( 0x605, 0x0000); // Disable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path + break; + } + } else { + AudioCodec_OutputEnabled = 0; + } + + // Path Configurations for input + if (input_device > 0) + { + AudioCodec_InputEnabled = 1; + switch (input_device) + { + case INPUT_DEVICE_DIGITAL_MICROPHONE_2 : + counter += AudioCodec_Write( 0x04, 0x0C30); // Enable AIF1ADC2 (Left), AIF1ADC2 (Right), DMICDAT2 (Left), DMICDAT2 (Right), Left ADC, Right ADC + counter += AudioCodec_Write( 0x450, 0x00DB); // Enable AIF1 DRC2 Signal Detect & DRC in AIF1ADC2 Left/Right Timeslot 1 + counter += AudioCodec_Write( 0x02, 0x6000); // Disable IN1L, IN1R, IN2L, IN2R, Enable Thermal sensor & shutdown + counter += AudioCodec_Write( 0x608, 0x0002); // Enable the DMIC2(Left) to AIF1 Timeslot 1 (Left) mixer path + counter += AudioCodec_Write( 0x609, 0x0002); // Enable the DMIC2(Right) to AIF1 Timeslot 1 (Right) mixer path + counter += AudioCodec_Write( 0x700, 0x000E); // GPIO1 pin configuration GP1_DIR = output, GP1_FN = AIF1 DRC2 signal detect + break; + + case INPUT_DEVICE_INPUT_LINE_1 : + counter += AudioCodec_Write( 0x28, 0x0011); // IN1LN_TO_IN1L, IN1LP_TO_VMID, IN1RN_TO_IN1R, IN1RP_TO_VMID + counter += AudioCodec_Write( 0x29, 0x0035); // Disable mute on IN1L_TO_MIXINL and +30dB on IN1L PGA output + counter += AudioCodec_Write( 0x2A, 0x0035); // Disable mute on IN1R_TO_MIXINL, Gain = +30dB + counter += AudioCodec_Write( 0x04, 0x0303); // Enable AIF1ADC1 (Left), AIF1ADC1 (Right), Left ADC, Right ADC + counter += AudioCodec_Write( 0x440, 0x00DB); // Enable AIF1 DRC1 Signal Detect & DRC in AIF1ADC1 Left/Right Timeslot 0 + counter += AudioCodec_Write( 0x02, 0x6350); // Enable IN1L and IN1R, Disable IN2L and IN2R, Enable Thermal sensor & shutdown + counter += AudioCodec_Write( 0x606, 0x0002); // Enable the ADCL(Left) to AIF1 Timeslot 0 (Left) mixer path + counter += AudioCodec_Write( 0x607, 0x0002); // Enable the ADCR(Right) to AIF1 Timeslot 0 (Right) mixer path + counter += AudioCodec_Write( 0x700, 0x000D); // GPIO1 pin configuration GP1_DIR = output, GP1_FN = AIF1 DRC1 signal detect + break; + + case INPUT_DEVICE_DIGITAL_MICROPHONE_1 : + counter += AudioCodec_Write( 0x04, 0x030C); // Enable AIF1ADC1 (Left), AIF1ADC1 (Right), DMICDAT1 (Left), DMICDAT1 (Right), Left ADC, Right ADC + counter += AudioCodec_Write( 0x440, 0x00DB); // Enable AIF1 DRC2 Signal Detect & DRC in AIF1ADC1 Left/Right Timeslot 0 + counter += AudioCodec_Write( 0x02, 0x6350); // Disable IN1L, IN1R, IN2L, IN2R, Enable Thermal sensor & shutdown + counter += AudioCodec_Write( 0x606, 0x0002); // Enable the DMIC2(Left) to AIF1 Timeslot 0 (Left) mixer path + counter += AudioCodec_Write( 0x607, 0x0002); // Enable the DMIC2(Right) to AIF1 Timeslot 0 (Right) mixer path + counter += AudioCodec_Write( 0x700, 0x000D); // GPIO1 pin configuration GP1_DIR = output, GP1_FN = AIF1 DRC1 signal detect + break; + case INPUT_DEVICE_DIGITAL_MIC1_MIC2 : + counter += AudioCodec_Write( 0x04, 0x0F3C); // Enable AIF1ADC1 (Left), AIF1ADC1 (Right), DMICDAT1 (Left), DMICDAT1 (Right), Left ADC, Right ADC + counter += AudioCodec_Write( 0x450, 0x00DB); // Enable AIF1 DRC2 Signal Detect & DRC in AIF1ADC2 Left/Right Timeslot 1 + counter += AudioCodec_Write( 0x440, 0x00DB); // Enable AIF1 DRC2 Signal Detect & DRC in AIF1ADC1 Left/Right Timeslot 0 + counter += AudioCodec_Write( 0x02, 0x63A0); // Disable IN1L, IN1R, Enable IN2L, IN2R, Thermal sensor & shutdown + counter += AudioCodec_Write( 0x606, 0x0002); // Enable the DMIC2(Left) to AIF1 Timeslot 0 (Left) mixer path + counter += AudioCodec_Write( 0x607, 0x0002); // Enable the DMIC2(Right) to AIF1 Timeslot 0 (Right) mixer path + counter += AudioCodec_Write( 0x608, 0x0002); // Enable the DMIC2(Left) to AIF1 Timeslot 1 (Left) mixer path + counter += AudioCodec_Write( 0x609, 0x0002); // Enable the DMIC2(Right) to AIF1 Timeslot 1 (Right) mixer path + counter += AudioCodec_Write( 0x700, 0x000D); // GPIO1 pin configuration GP1_DIR = output, GP1_FN = AIF1 DRC1 signal detect + break; + case INPUT_DEVICE_INPUT_LINE_2 : + default: + // Actually, no other input devices supported + counter++; + break; + } + } else { + AudioCodec_InputEnabled = 0; + } + + /* + // Clock Configurations + switch (AudioFreq) + { + case AUDIO_FREQUENCY_8K: counter += AudioCodec_Write( 0x210, 0x0003); break; // AIF1 Sample Rate = 8 (KHz), ratio=256 + case AUDIO_FREQUENCY_16K: counter += AudioCodec_Write( 0x210, 0x0033); break; // AIF1 Sample Rate = 16 (KHz), ratio=256 + case AUDIO_FREQUENCY_32K: counter += AudioCodec_Write( 0x210, 0x0063); break; // AIF1 Sample Rate = 32 (KHz), ratio=256 + case AUDIO_FREQUENCY_48K: counter += AudioCodec_Write( 0x210, 0x0083); break; // AIF1 Sample Rate = 48 (KHz), ratio=256 + case AUDIO_FREQUENCY_96K: counter += AudioCodec_Write( 0x210, 0x00A3); break; // AIF1 Sample Rate = 96 (KHz), ratio=256 + case AUDIO_FREQUENCY_11K: counter += AudioCodec_Write( 0x210, 0x0013); break; // AIF1 Sample Rate = 11.025 (KHz), ratio=256 + case AUDIO_FREQUENCY_22K: counter += AudioCodec_Write( 0x210, 0x0043); break; // AIF1 Sample Rate = 22.050 (KHz), ratio=256 + case AUDIO_FREQUENCY_44K: counter += AudioCodec_Write( 0x210, 0x0073); break; // AIF1 Sample Rate = 44.1 (KHz), ratio=256 + default: counter += AudioCodec_Write( 0x210, 0x0083); break; // AIF1 Sample Rate = 48 (KHz), ratio=256 + } + */ + counter += AudioCodec_SetFrequency( AudioFreq ); + + if( input_device == INPUT_DEVICE_DIGITAL_MIC1_MIC2 ) { + counter += AudioCodec_Write( 0x300, 0x4018); // AIF1 Word Length = 16-bits, AIF1 Format = DSP mode + } else { + counter += AudioCodec_Write( 0x300, 0x4010); // AIF1 Word Length = 16-bits, AIF1 Format = I2S (Default Register Value) + } + + counter += AudioCodec_Write( 0x302, 0x0000); // slave mode + counter += AudioCodec_Write( 0x208, 0x000A); // Enable the DSP processing clock for AIF1, Enable the core clock + counter += AudioCodec_Write( 0x200, 0x0001); // Enable AIF1 Clock, AIF1 Clock Source = MCLK1 pin + if (output_device > 0) { // Audio output selected + if (output_device == OUTPUT_DEVICE_HEADPHONE) { + counter += AudioCodec_Write( 0x2D, 0x0100); // Select DAC1 (Left) to Left Headphone Output PGA (HPOUT1LVOL) path + counter += AudioCodec_Write( 0x2E, 0x0100); // Select DAC1 (Right) to Right Headphone Output PGA (HPOUT1RVOL) path + // Startup sequence for Headphone: + if( AudioCodec_ColdStartup ){ + counter += AudioCodec_Write(0x110,0x8100); + AudioCodec_ColdStartup=0; + HAL_Delay(300); + } else { // Headphone Warm Start-Up + counter += AudioCodec_Write(0x110,0x8108); + HAL_Delay(50); + } + counter += AudioCodec_Write( 0x420, 0x0000); // Soft un-Mute the AIF1 Timeslot 0 DAC1 path L&R + } + // Analog Output Configuration: + counter += AudioCodec_Write( 0x03, 0x0300); // Enable SPKRVOL PGA, Enable SPKMIXR, Enable SPKLVOL PGA, Enable SPKMIXL + counter += AudioCodec_Write( 0x22, 0x0000); // Left Speaker Mixer Volume = 0dB + counter += AudioCodec_Write( 0x23, 0x0000); // Speaker output mode = Class D, Right Speaker Mixer Volume = 0dB ((0x23, 0x0100) = class AB) + counter += AudioCodec_Write( 0x36, 0x0300); // Unmute DAC2 (Left) to Left Speaker Mixer (SPKMIXL) path, Unmute DAC2 (Right) to Right Speaker Mixer (SPKMIXR) path + counter += AudioCodec_Write( 0x01, 0x3003); // Enable bias generator, Enable VMID, Enable SPKOUTL, Enable SPKOUTR + + // Headphone/Speaker Enable: + if( input_device == INPUT_DEVICE_DIGITAL_MIC1_MIC2 ) { + counter += AudioCodec_Write( 0x51, 0x0205); // Enable Class W, Class W Envelope Tracking = AIF1 Timeslots 0 and 1 + } else { + counter += AudioCodec_Write( 0x51, 0x0005); // Enable Class W, Class W Envelope Tracking = AIF1 Timeslot 0 + } + + // Enable bias generator, Enable VMID, Enable HPOUT1 (Left) and Enable HPOUT1 (Right) input stages idem for Speaker + power_mgnt_reg_1 |= 0x0303 | 0x3003; + counter += AudioCodec_Write( 0x01, power_mgnt_reg_1); + counter += AudioCodec_Write( 0x60, 0x0022); // Enable HPOUT1 (Left) and HPOUT1 (Right) intermediate stages + counter += AudioCodec_Write( 0x4C, 0x9F25); // Enable Charge Pump + + HAL_Delay(15); + + counter += AudioCodec_Write( 0x2D, 0x0001); // Select DAC1 (Left) to Left Headphone Output PGA (HPOUT1LVOL) path + counter += AudioCodec_Write( 0x2E, 0x0001); // Select DAC1 (Right) to Right Headphone Output PGA (HPOUT1RVOL) path + counter += AudioCodec_Write( 0x03, 0x0030 | 0x0300); // Enable Left Output Mixer (MIXOUTL), Enable Right Output Mixer (MIXOUTR) idem for SPKOUTL and SPKOUTR + counter += AudioCodec_Write( 0x54, 0x0033); // Enable DC Servo and trigger start-up mode on left and right channels + + HAL_Delay(257); + + counter += AudioCodec_Write( 0x60, 0x00EE); // Enable HPOUT1 (Left) and HPOUT1 (Right) intermediate and output stages. Remove clamps + + // Unmutes: + counter += AudioCodec_Write( 0x610, 0x00C0); // Unmute DAC 1 (Left) + counter += AudioCodec_Write( 0x611, 0x00C0); // Unmute DAC 1 (Right) + counter += AudioCodec_Write( 0x420, 0x0010); // Unmute the AIF1 Timeslot 0 DAC path + counter += AudioCodec_Write( 0x612, 0x00C0); // Unmute DAC 2 (Left) + counter += AudioCodec_Write( 0x613, 0x00C0); // Unmute DAC 2 (Right) + counter += AudioCodec_Write( 0x422, 0x0010); // Unmute the AIF1 Timeslot 1 DAC2 path + + AudioCodec_SetVolume(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 += AudioCodec_Write( 0x01, power_mgnt_reg_1); + counter += AudioCodec_Write( 0x620, 0x0002); // ADC oversample enable + counter += AudioCodec_Write( 0x411, 0x3800); // AIF ADC2 HPF enable, HPF cut = voice mode 1 fc=127Hz at fs=8kHz + } else if(input_device == INPUT_DEVICE_DIGITAL_MIC1_MIC2) { + // Enable Microphone bias 1 generator, Enable VMID + power_mgnt_reg_1 |= 0x0013; + counter += AudioCodec_Write( 0x01, power_mgnt_reg_1); + counter += AudioCodec_Write( 0x620, 0x0002); // ADC oversample enable + counter += AudioCodec_Write( 0x410, 0x1800); // AIF ADC1 HPF enable, HPF cut = voice mode 1 fc=127Hz at fs=8kHz + counter += AudioCodec_Write( 0x411, 0x1800); // AIF ADC2 HPF enable, HPF cut = voice mode 1 fc=127Hz at fs=8kHz + } else if ((input_device == INPUT_DEVICE_INPUT_LINE_1) || (input_device == INPUT_DEVICE_INPUT_LINE_2)) { + counter += AudioCodec_Write( 0x18, 0x000B); // Disable mute on IN1L, IN1L Volume = +0dB + counter += AudioCodec_Write( 0x1A, 0x000B); // Disable mute on IN1R, IN1R Volume = +0dB + counter += AudioCodec_Write( 0x410, 0x1800); // AIF ADC1 HPF enable, HPF cut = hifi mode fc=4Hz at fs=48kHz + } + AudioCodec_SetVolume(Volume); + } + return counter; +} + +static uint32_t AudioCodec_SetFrequency( uint32_t AudioFreq ) { + uint32_t counter = 0; + switch (AudioFreq) { + case AUDIO_FREQUENCY_8K: counter += AudioCodec_Write( 0x210, 0x0003); break; // AIF1 Sample Rate = 8 (KHz), ratio=256 + case AUDIO_FREQUENCY_16K: counter += AudioCodec_Write( 0x210, 0x0033); break; // AIF1 Sample Rate = 16 (KHz), ratio=256 + case AUDIO_FREQUENCY_32K: counter += AudioCodec_Write( 0x210, 0x0063); break; // AIF1 Sample Rate = 32 (KHz), ratio=256 + case AUDIO_FREQUENCY_48K: counter += AudioCodec_Write( 0x210, 0x0083); break; // AIF1 Sample Rate = 48 (KHz), ratio=256 + case AUDIO_FREQUENCY_96K: counter += AudioCodec_Write( 0x210, 0x00A3); break; // AIF1 Sample Rate = 96 (KHz), ratio=256 + case AUDIO_FREQUENCY_11K: counter += AudioCodec_Write( 0x210, 0x0013); break; // AIF1 Sample Rate = 11.025 (KHz), ratio=256 + case AUDIO_FREQUENCY_22K: counter += AudioCodec_Write( 0x210, 0x0043); break; // AIF1 Sample Rate = 22.050 (KHz), ratio=256 + case AUDIO_FREQUENCY_44K: counter += AudioCodec_Write( 0x210, 0x0073); break; // AIF1 Sample Rate = 44.1 (KHz), ratio=256 + default: counter += AudioCodec_Write( 0x210, 0x0083); break; // AIF1 Sample Rate = 48 (KHz), ratio=256 + } + return counter; +} + +static uint32_t AudioCodec_SetVolume( uint8_t Volume ) { + uint32_t counter = 0; + uint8_t convertedvol = VOLUME_CONVERT(Volume); + + // Output volume + if( AudioCodec_OutputEnabled != 0 ) { + if(convertedvol > 0x3E) { + counter += AudioCodec_SetMute(AUDIO_MUTE_OFF); // Unmute audio codec + counter += AudioCodec_Write( 0x1C, 0x3F | 0x140); // Left Headphone Volume + counter += AudioCodec_Write( 0x1D, 0x3F | 0x140); // Right Headphone Volume + counter += AudioCodec_Write( 0x26, 0x3F | 0x140); // Left Speaker Volume + counter += AudioCodec_Write( 0x27, 0x3F | 0x140); // Right Speaker Volume + } else if (Volume == 0) { + counter += AudioCodec_SetMute(AUDIO_MUTE_ON); // Mute audio codec + } else { + counter += AudioCodec_SetMute(AUDIO_MUTE_OFF); // Unmute audio codec + counter += AudioCodec_Write( 0x1C, convertedvol | 0x140); // Left Headphone Volume + counter += AudioCodec_Write( 0x1D, convertedvol | 0x140); // Right Headphone Volume + counter += AudioCodec_Write( 0x26, convertedvol | 0x140); // Left Speaker Volume + counter += AudioCodec_Write( 0x27, convertedvol | 0x140); // Right Speaker Volume + } + } + + // Input volume + if( AudioCodec_InputEnabled != 0 ) { + convertedvol = VOLUME_IN_CONVERT(Volume); + counter += AudioCodec_Write( 0x400, convertedvol | 0x100); // Left AIF1 ADC1 volume + counter += AudioCodec_Write( 0x401, convertedvol | 0x100); // Right AIF1 ADC1 volume + counter += AudioCodec_Write( 0x404, convertedvol | 0x100); // Left AIF1 ADC2 volume + counter += AudioCodec_Write( 0x405, convertedvol | 0x100); // Right AIF1 ADC2 volume + } + return counter; +} + +static uint32_t AudioCodec_SetMute( uint32_t Cmd ) { + uint32_t counter = 0; + if( AudioCodec_OutputEnabled != 0 ) { + // Set the Mute mode + if(Cmd == AUDIO_MUTE_ON) { + counter += AudioCodec_Write( 0x420, 0x0200); // Soft Mute the AIF1 Timeslot 0 DAC1 path L&R + counter += AudioCodec_Write( 0x422, 0x0200); // Soft Mute the AIF1 Timeslot 1 DAC2 path L&R + } else { // AUDIO_MUTE_OFF Disable the Mute + counter += AudioCodec_Write( 0x420, 0x0010); // Unmute the AIF1 Timeslot 0 DAC1 path L&R + counter += AudioCodec_Write( 0x422, 0x0010); // Unmute the AIF1 Timeslot 1 DAC2 path L&R + } + } + return counter; +} + +static uint32_t AudioCodec_Play( void ) { + uint32_t counter = 0; + counter += AudioCodec_SetMute(AUDIO_MUTE_OFF); + return counter; +} + +static uint32_t AudioCodec_Pause( void ) { + uint32_t counter = 0; + counter += AudioCodec_SetMute(AUDIO_MUTE_ON); // Mute the output first + counter += AudioCodec_Write( 0x02, 0x01 ); // Put the Codec in Power save mode + return counter; +} + +static uint32_t AudioCodec_Resume( void ) { + uint32_t counter = 0; + counter += AudioCodec_SetMute(AUDIO_MUTE_OFF); + return counter; +} + +static uint32_t AudioCodec_Stop( uint8_t CodecPdwnMode ) { + uint32_t counter = 0; + if( AudioCodec_OutputEnabled != 0 ) { + counter += AudioCodec_SetMute(AUDIO_MUTE_ON); // Mute the output first + if( CodecPdwnMode == CODEC_PDWN_SW ) { // Only output mute required + } else { // CODEC_PDWN_HW + counter += AudioCodec_Write( 0x420, 0x0200); // Mute the AIF1 Timeslot 0 DAC1 path + counter += AudioCodec_Write( 0x422, 0x0200); // Mute the AIF1 Timeslot 1 DAC2 path + counter += AudioCodec_Write( 0x2D, 0x0000); // Disable DAC1L_TO_HPOUT1L + counter += AudioCodec_Write( 0x2E, 0x0000); // Disable DAC1R_TO_HPOUT1R + counter += AudioCodec_Write( 0x05, 0x0000); // Disable DAC1 and DAC2 + counter += AudioCodec_Write( 0x0000, 0x0000); // Reset Codec by writing in 0x0000 address register + AudioCodec_OutputEnabled = 0; + } + } + return counter; +} + +__weak void Player_CompleteTransfer_CallBack( void ) { +} +__weak void Player_HalfTransfer_CallBack( void ) { +} + + +void HAL_SAI_TxCpltCallback( SAI_HandleTypeDef *hsai ) { + Player_CompleteTransfer_CallBack(); +} + +void HAL_SAI_TxHalfCpltCallback( SAI_HandleTypeDef *hsai ) { + Player_HalfTransfer_CallBack(); +} + + + +static void Audio_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_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_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_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); + } +} + +static void Audio_SaiOutInit( SAI_HandleTypeDef *hsai, uint32_t AudioFreq) { + + // Disable SAI peripheral to allow access to SAI internal registers + __HAL_SAI_DISABLE(hsai); + + // Configure SAI_Block_x: LSBFirst: Disabled, DataSize: 16 + hsai->Init.AudioFrequency = AudioFreq; + /* + hsai->Init.MonoStereoMode = SAI_STEREOMODE; + hsai->Init.AudioFrequency = AudioFreq; + hsai->Init.AudioMode = SAI_MODEMASTER_TX; + hsai->Init.NoDivider = SAI_MASTERDIVIDER_ENABLED; + hsai->Init.Protocol = SAI_FREE_PROTOCOL; + hsai->Init.DataSize = SAI_DATASIZE_16; + hsai->Init.FirstBit = SAI_FIRSTBIT_MSB; + hsai->Init.ClockStrobing = SAI_CLOCKSTROBING_FALLINGEDGE; + hsai->Init.Synchro = SAI_ASYNCHRONOUS; + hsai->Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE; + hsai->Init.FIFOThreshold = SAI_FIFOTHRESHOLD_1QF; + hsai->Init.SynchroExt = SAI_SYNCEXT_DISABLE; + hsai->Init.CompandingMode = SAI_NOCOMPANDING; + hsai->Init.TriState = SAI_OUTPUT_NOTRELEASED; + hsai->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 */ + hsai->FrameInit.FrameLength = 64; + hsai->FrameInit.ActiveFrameLength = 32; + hsai->FrameInit.FSDefinition = SAI_FS_CHANNEL_IDENTIFICATION; + hsai->FrameInit.FSPolarity = SAI_FS_ACTIVE_LOW; + hsai->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 */ + hsai->SlotInit.FirstBitOffset = 0; + hsai->SlotInit.SlotSize = SAI_SLOTSIZE_DATASIZE; + hsai->SlotInit.SlotNumber = 4; + hsai->SlotInit.SlotActive = CODEC_AUDIOFRAME_SLOT_0123; + + HAL_SAI_Init(hsai); + + __HAL_SAI_ENABLE(hsai); +} + +uint8_t Audio_SetVolume( uint8_t Volume ) { + if( AudioCodec_SetVolume(Volume) != 0) { + return AUDIO_ERROR; + } else { + return AUDIO_OK; + } +} + +uint8_t Audio_Play( uint8_t *Buffer, uint32_t Size ) { + if( AudioCodec_Play() != 0) { + return AUDIO_ERROR; + } else { + HAL_SAI_Transmit_DMA( &AUDIO_SAI_HANDLE, Buffer, Size ); + return AUDIO_OK; + } +} + +uint8_t Audio_Pause( void ) { + if( AudioCodec_Pause() != 0 ) { + return AUDIO_ERROR; + } else { + HAL_SAI_DMAPause(&AUDIO_SAI_HANDLE); + return AUDIO_OK; + } +} + +uint8_t Audio_Resume( void ) { + if( AudioCodec_Resume() != 0) { + return AUDIO_ERROR; + } else { + HAL_SAI_DMAResume(&AUDIO_SAI_HANDLE); + return AUDIO_OK; + } +} + +uint8_t Audio_Stop( void ) { + HAL_SAI_DMAStop(&hsai_BlockA2); + uint8_t Option = CODEC_PDWN_SW; + if( AudioCodec_Stop( Option) != 0) { + return AUDIO_ERROR; + } else { + if(Option == CODEC_PDWN_HW) { + HAL_Delay(1); + } + return AUDIO_OK; + } +} + +uint8_t Audio_SetFrequency( uint32_t AudioFreq ) { + Audio_ClockConfig(&AUDIO_SAI_HANDLE, AudioFreq/2, NULL); + + __HAL_SAI_DISABLE(&AUDIO_SAI_HANDLE); + + AUDIO_SAI_HANDLE.Init.AudioFrequency = AudioFreq/2; + HAL_SAI_Init(&AUDIO_SAI_HANDLE); + + __HAL_SAI_ENABLE(&AUDIO_SAI_HANDLE); + + return AUDIO_OK; + + /* + if( AudioCodec_SetFrequency( AudioFreq ) != 0 ) { + return AUDIO_ERROR; + } else { + return AUDIO_OK; + } + */ +} + +uint8_t Audio_Init(uint16_t OutputDevice, uint8_t Volume, uint32_t AudioFreq ) { + uint8_t ret = AUDIO_ERROR; // Default: return error + uint32_t DeviceId = 0; + + Audio_ClockConfig(&AUDIO_SAI_HANDLE, AudioFreq/2, NULL); + Audio_SaiOutInit(&AUDIO_SAI_HANDLE, AudioFreq/2); + + DeviceId = AudioCodec_ReadId(); + if( DeviceId == 0x8994 ) { + AudioCodec_Reset(); + AudioCodec_Init(OutputDevice, Volume, AudioFreq); + ret = AUDIO_OK; + } + + return ret; +} diff --git a/Versuch5/Core/Src/waveplayer.c b/Versuch5/Core/Src/waveplayer.c new file mode 100644 index 0000000..de07c37 --- /dev/null +++ b/Versuch5/Core/Src/waveplayer.c @@ -0,0 +1,66 @@ +/******************************************************************* +File: waveplayer.c +Date: 29-September-2025 +Author: Peter Spindler +Description: Functions to play a wave file. Requires functions from audio.c + ********************************************************************/ +#include "stm32f7xx_hal.h" +#include "audio.h" + +#include "FreeRTOS.h" +#include "semphr.h" +#include "task.h" + +#include "ff.h" +#include "fatfs.h" + +#include "audio.h" +#include "gui.h" +#include "display.h" + +FIL WaveFile; +#define AUDIO_BUFFER_SIZE_WORDS (1024*8) +uint16_t Audio_Buffer[AUDIO_BUFFER_SIZE_WORDS]; + +void Player_Start( const char *Filename ) { + FRESULT res; + if( (res=f_open(&WaveFile, Filename , FA_READ )) != FR_OK) { + printf("Error: Can't open audio file %s: %d\n", Filename, res); + return; + } + printf("Open wavefile %s: successful\n", Filename ); + + Audio_SetFrequency(22050); + + /* ToDo: Fill audio buffer completely */ + + Audio_Play( (uint8_t*) Audio_Buffer, sizeof(Audio_Buffer)/2 ); +} + +void Player_Stop( void ) { + Audio_Stop(); + f_close(&WaveFile); +} + +// Player_HalfTransfer_CallBack: called from DMA2_Stream4-ISR if first half of audio buffer has been transferred to SAI2 +void Player_HalfTransfer_CallBack( void ) { + /* ToDo: Inform Player_Task about first half*/ +} + +// Player_CompleteTransfer_CallBack: called from DMA2_Stream4-ISR if second half of audio buffer has been transferred to SAI2 +void Player_CompleteTransfer_CallBack( void ) { + /* ToDo: Inform Player_Task about second half*/ +} + +static void Player_Task( void *arguments ) { + /* ToDo */ +} + +void Player_Init( void ) { + portENABLE_INTERRUPTS(); // Workaround to re-enable interrupts after FreeRTOS functions + // 70: initial volume, 44100: initial sampling frequency + if( Audio_Init(OUTPUT_DEVICE_AUTO,70,44100) != AUDIO_OK ) { + printf("Error: Can't init audio!\n"); + } + xTaskCreate(Player_Task,"Player", 512,NULL,0,NULL); +}