Add received files waveplayer, audio

This commit is contained in:
jonny 2025-12-15 19:02:07 +01:00
parent c7b1f0c737
commit 747995010b
4 changed files with 750 additions and 0 deletions

49
Versuch5/Core/Inc/audio.h Normal file
View File

@ -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

View File

@ -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

624
Versuch5/Core/Src/audio.c Normal file
View File

@ -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 <stdio.h>
#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;
}

View File

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