UHSDR/UHSDR-active-devel/mchf-eclipse/drivers/audio/codec/codec.c
2022-08-24 08:41:00 +02:00

703 lines
25 KiB
C

/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
// Common
#include "uhsdr_board.h"
#include "audio_driver.h"
#include "radio_management.h"
#include <stdio.h>
#include "uhsdr_hw_i2c.h"
#include "codec.h"
#include "uhsdr_mcu.h"
// I2C addresses
#define W8731_ADDR_0 0x1A // CS = 0, MODE to GND
#define W8731_ADDR_1 0x1B // CS = 1, MODE to GND
// The 7 bits Codec address (sent through I2C interface)
#define CODEC_ADDRESS (W8731_ADDR_0<<1)
// Registers
#define W8731_LEFT_LINE_IN 0x00 // 0000000
#define W8731_RIGHT_LINE_IN 0x01 // 0000001
#define W8731_LEFT_HEADPH_OUT 0x02 // 0000010
#define W8731_RIGHT_HEADPH_OUT 0x03 // 0000011
#define W8731_ANLG_AU_PATH_CNTR 0x04 // 0000100
#define W8731_DIGI_AU_PATH_CNTR 0x05 // 0000101
#define W8731_POWER_DOWN_CNTR 0x06 // 0000110
#define W8731_DIGI_AU_INTF_FORMAT 0x07 // 0000111
#define W8731_SAMPLING_CNTR 0x08 // 0001000
#define W8731_ACTIVE_CNTR 0x09 // 0001001
#define W8731_RESET 0x0F // 0001111
// -------------------------------------------------
//#define W8731_DEEMPH_CNTR 0x06 // WM8731 codec De-emphasis enabled
#define W8731_DEEMPH_CNTR 0x00 // WM8731 codec De-emphasis disabled
#define W8731_HEADPH_OUT_ZCEN 0x0080 // bit 7 W8731_LEFT_HEADPH_OUT / W8731_RIGHT_HEADPH_OUT
#define W8731_HEADPH_OUT_HPBOTH 0x0100 // bit 8 W8731_LEFT_HEADPH_OUT / W8731_RIGHT_HEADPH_OUT
#define W8731_LINE_IN_LRBOTH 0x0100 // bit 8 W8731_LEFT_LINE_IN_OUT / W8731_RIGHT_LINE_IN
#define W8731_ANLG_AU_PATH_CNTR_DACSEL (0x10)
#define W8731_ANLG_AU_PATH_CNTR_INSEL_MIC (0x04)
#define W8731_ANLG_AU_PATH_CNTR_INSEL_LINE (0x00)
#define W8731_ANLG_AU_PATH_CNTR_MUTEMIC (0x02)
#define W8731_ANLG_AU_PATH_CNTR_MICBBOOST (0x01)
#define W8731_DIGI_AU_INTF_FORMAT_PHILIPS 0x02
#define W8731_DIGI_AU_INTF_FORMAT_PCM 0x00
#define W8731_DIGI_AU_INTF_FORMAT_16B (0x0 << 2)
#define W8731_DIGI_AU_INTF_FORMAT_20B (0x1 << 2)
#define W8731_DIGI_AU_INTF_FORMAT_24B (0x2 << 2)
#define W8731_DIGI_AU_INTF_FORMAT_32B (0x3 << 2)
#define W8731_DIGI_AU_INTF_FORMAT_I2S_PROTO W8731_DIGI_AU_INTF_FORMAT_PHILIPS
#define W8731_POWER_DOWN_CNTR_POWEROFF (0x80)
#define W8731_POWER_DOWN_CNTR_CLKOUTPD (0x40)
#define W8731_POWER_DOWN_CNTR_OSCPD (0x20)
#define W8731_POWER_DOWN_CNTR_OUTPD (0x10)
#define W8731_POWER_DOWN_CNTR_DACPD (0x08)
#define W8731_POWER_DOWN_CNTR_ADCPD (0x04)
#define W8731_POWER_DOWN_CNTR_MICPD (0x02)
#define W8731_POWER_DOWN_CNTR_LINEPD (0x01)
#define W8731_SAMPLING_CNTR_BOSR (0x0002)
#define W8731_SAMPLING_CNTR_96K (0x0007 << 2)
#define W8731_SAMPLING_CNTR_48K (0x0000 << 2)
#define W8731_SAMPLING_CNTR_32K (0x0006 << 2)
#define W8731_SAMPLING_CNTR_8K (0x0003 << 2)
#define W8731_VOL_MAX 0x50
#define W8731_POWER_DOWN_CNTR_MCHF_ALL_ON (W8731_POWER_DOWN_CNTR_CLKOUTPD|W8731_POWER_DOWN_CNTR_OSCPD)
// all on but osc and out, since we don't need it, clock comes from STM
#define W8731_POWER_DOWN_CNTR_MCHF_MIC_OFF (W8731_POWER_DOWN_CNTR_CLKOUTPD|W8731_POWER_DOWN_CNTR_OSCPD|W8731_POWER_DOWN_CNTR_MICPD)
typedef struct
{
bool present;
} mchf_codec_t;
__IO mchf_codec_t mchf_codecs[CODEC_NUM];
// FIXME: for now we use 32bits transfer size, does not change the ADC/DAC resolution
// which is 24 bits in any case. We should reduce finally to 24bits (which requires also the I2S/SAI peripheral to
// use 24bits)
#if defined(USE_32_IQ_BITS)
#define IQ_WORD_SIZE WORD_SIZE_32
#else
#define IQ_WORD_SIZE WORD_SIZE_16
#endif
#if defined(USE_32_AUDIO_BITS)
#define AUDIO_WORD_SIZE WORD_SIZE_32
#else
#define AUDIO_WORD_SIZE WORD_SIZE_16
#endif
#ifdef UI_BRD_OVI40
#include "dac.h"
/**
* @brief controls volume on "external" PA via DAC
* @param vol volume in range of 0 to CODEC_SPEAKER_MAX_VOLUME
*/
static void AudioPA_Volume(uint8_t vol)
{
uint32_t lv = vol>CODEC_SPEAKER_MAX_VOLUME?CODEC_SPEAKER_MAX_VOLUME:vol;
HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_R, (lv * 4095)/CODEC_SPEAKER_MAX_VOLUME);
}
/**
* @brief controls sound delivery on "external" PA via DAC
* @param enable true == amplification, false == powerdown
*/
static void AudioPA_Enable(bool enable)
{
if (enable)
{
GPIO_SetBits(AUDIO_PA_EN_PIO,AUDIO_PA_EN);
}
else
{
GPIO_ResetBits(AUDIO_PA_EN_PIO,AUDIO_PA_EN);
}
}
#endif
/**
* @brief writes 16 bit data word to codec register
* @returns I2C error code
*/
static uint32_t Codec_WriteRegister(I2C_HandleTypeDef* hi2c, uint8_t RegisterAddr, uint16_t RegisterValue)
{
// Assemble 2-byte data in WM8731 format
uint8_t Byte1 = ((RegisterAddr<<1)&0xFE) | ((RegisterValue>>8)&0x01);
uint8_t Byte2 = RegisterValue&0xFF;
return UhsdrHw_I2C_WriteRegister(hi2c, CODEC_ADDRESS, Byte1, 1, Byte2);
}
static uint32_t Codec_ResetCodec(I2C_HandleTypeDef* hi2c, uint32_t AudioFreq, CodecSampleWidth_t word_size)
{
uint32_t retval = HAL_OK;
retval = Codec_WriteRegister(hi2c, W8731_RESET, 0);
// Reset register
if( retval == HAL_OK)
{
// Reg 00: Left Line In (0dB, mute off)
Codec_WriteRegister(hi2c, W8731_LEFT_LINE_IN,0x001F);
// Reg 01: Right Line In (0dB, mute off)
Codec_WriteRegister(hi2c, W8731_RIGHT_LINE_IN,0x001F);
// Reg 02: Left Headphone out (0dB)
//Codec_WriteRegister(0x02,0x0079);
// Reg 03: Right Headphone out (0dB)
//Codec_WriteRegister(0x03,0x0079);
// Reg 04: Analog Audio Path Control (DAC sel, ADC line, Mute Mic)
Codec_WriteRegister(hi2c, W8731_ANLG_AU_PATH_CNTR,
W8731_ANLG_AU_PATH_CNTR_DACSEL |
W8731_ANLG_AU_PATH_CNTR_INSEL_LINE |
W8731_ANLG_AU_PATH_CNTR_MUTEMIC);
// Reg 05: Digital Audio Path Control(all filters disabled)
// De-emphasis control, bx11x - 48kHz
// bx00x - off
// DAC soft mute b1xxx - mute on
// b0xxx - mute off
//
Codec_WriteRegister(hi2c, W8731_DIGI_AU_PATH_CNTR,W8731_DEEMPH_CNTR);
// Reg 06: Power Down Control (Clk off, Osc off, Mic off))
Codec_WriteRegister(hi2c, W8731_POWER_DOWN_CNTR,W8731_POWER_DOWN_CNTR_MCHF_MIC_OFF);
// Reg 07: Digital Audio Interface Format (i2s, 16/32 bit, slave)
uint16_t size_reg_val;
switch(word_size)
{
case WORD_SIZE_32:
size_reg_val = W8731_DIGI_AU_INTF_FORMAT_32B;
break;
case WORD_SIZE_24:
size_reg_val = W8731_DIGI_AU_INTF_FORMAT_24B;
break;
case WORD_SIZE_16:
default:
size_reg_val = W8731_DIGI_AU_INTF_FORMAT_16B;
break;
}
Codec_WriteRegister(hi2c, W8731_DIGI_AU_INTF_FORMAT,W8731_DIGI_AU_INTF_FORMAT_I2S_PROTO|size_reg_val);
// Reg 08: Sampling Control (Normal, 256x, 48k ADC/DAC)
// master clock: 12.288 Mhz
uint16_t samp_reg_val;
switch (AudioFreq)
{
case 32000:
samp_reg_val = W8731_SAMPLING_CNTR_32K;
break;
case 8000:
samp_reg_val = W8731_SAMPLING_CNTR_8K;
break;
case 96000:
samp_reg_val = W8731_SAMPLING_CNTR_96K;
break;
case 48000:
default:
samp_reg_val = W8731_SAMPLING_CNTR_48K;
break;
}
Codec_WriteRegister(hi2c, W8731_SAMPLING_CNTR,samp_reg_val);
// Reg 09: Active Control
// and now we start the Codec Digital Interface
Codec_WriteRegister(hi2c, W8731_ACTIVE_CNTR,0x0001);
}
return retval;
}
#ifdef SDR_AMBER
#ifndef UI_BRD_MCHF
uint32_t Codec_Reset_CS4270_Codec()
{
uint32_t retval = HAL_OK;
// uint32_t AudioFreq = ts.samp_rate;
// CodecSampleWidth_t word_size = IQ_WORD_SIZE;
GPIO_ResetBits(BAND3_PIO, BAND3); // this port is using for CS4270 codec RESET in TRX Amber
non_os_delay(); // we can't use HAL_Delay here
GPIO_SetBits(BAND3_PIO, BAND3);
retval = Board_AmberCS4270_Write(0x02, 0x01); // total codec powerdown
if(retval != HAL_OK)
{
return retval;
}
non_os_delay();
retval = Board_AmberCS4270_Write(0x02, 0x00); // total codec powerup
if(retval == HAL_OK)
{
// Board_AmberCS4270_Write(0x02, 0x02); // DAC powerdown, we need RESET just on RX
Board_AmberCS4270_Write(0x03, 0x00); // slave, divide 1
Board_AmberCS4270_Write(0x04, 0x09); // I2S for DAC & ADC
Board_AmberCS4270_Write(0x05, 0x80); // DACs single volume
Board_AmberCS4270_Write(0x06, 0x00); // Automute off
Board_AmberCS4270_Write(0x07, 0x00); // DACs max volume
}
return retval;
}
#endif
#endif
/**
* @brief initializes codec
* @param AudioFreq sample rate in Hertz
* @param word_size should be set to WORD_SIZE_16, since we have not yet implemented any other word_size
*/
uint32_t Codec_Reset(uint32_t AudioFreq)
{
uint32_t retval;
#ifdef UI_BRD_MCHF
retval = Codec_ResetCodec(CODEC_I2C, AudioFreq, IQ_WORD_SIZE);
#else
ts.codecWM8731_Audio_present = false;
retval = Codec_ResetCodec(CODEC_ANA_I2C, AudioFreq, AUDIO_WORD_SIZE);
if (retval == 0)
{
ts.codecWM8731_Audio_present = true;
mchf_codecs[1].present = true;
retval = Codec_ResetCodec(CODEC_IQ_I2C, AudioFreq, IQ_WORD_SIZE);
}
#endif
if (retval == 0)
{
mchf_codecs[0].present = true;
#ifdef UI_BRD_OVI40
AudioPA_Enable(true);
#endif
Codec_VolumeSpkr(0); // mute speaker
Codec_VolumeLineOut(ts.txrx_mode); // configure lineout according to mode
}
#if defined(SDR_AMBER) && defined(UI_BRD_OVI40)
else
{
ts.codecCS4270_present = Codec_Reset_CS4270_Codec() == HAL_OK;
if(ts.codecCS4270_present)
{
AudioPA_Enable(true);
}
}
#endif
return retval;
}
/**
* @brief Call this if the twin peaks happen, this restarts the I2S audio stream and it may fix the issue
*/
void Codec_RestartI2S(void)
{
#if !defined(UI_BRD_MCHF) && defined(SDR_AMBER)
if(ts.codecCS4270_present)
{
Codec_Reset_CS4270_Codec();
}
else
{
#endif
// Reg 09: Active Control
Codec_WriteRegister(CODEC_IQ_I2C, W8731_ACTIVE_CNTR,0x0000);
non_os_delay(); // we can't use HAL_Delay here, since our audio interrupt has higher priority which stops the ticks.
// Reg 09: Active Control
Codec_WriteRegister(CODEC_IQ_I2C, W8731_ACTIVE_CNTR,0x0001);
#if !defined(UI_BRD_MCHF) && defined(SDR_AMBER)
}
#endif
}
/**
* @brief This enables the microphone if in TX and sets gain, does nothing in RX or if audio_source is not microphone
* @param txrx_mode the mode for which it should be configured
*/
void Codec_SwitchMicTxRxMode(uint8_t txrx_mode)
{
// only adjust the hardware if in TX txrx_mode with mic selected (it will kill RX otherwise!)
if(txrx_mode == TRX_MODE_TX && ts.tx_audio_source == TX_AUDIO_MIC)
{
// Set up microphone gain and adjust mic boost accordingly
// Reg 04: Analog Audio Path Control (DAC sel, ADC Mic, Mic on)
// non_os_delay();
if(ts.tx_gain[TX_AUDIO_MIC] > 50) // actively adjust microphone gain and microphone boost
{
Codec_WriteRegister(CODEC_ANA_I2C, W8731_ANLG_AU_PATH_CNTR,
W8731_ANLG_AU_PATH_CNTR_DACSEL |
W8731_ANLG_AU_PATH_CNTR_INSEL_MIC|
W8731_ANLG_AU_PATH_CNTR_MICBBOOST); // mic boost on
ts.tx_mic_gain_mult = (ts.tx_gain[TX_AUDIO_MIC] - 35)/3; // above 50, rescale software amplification
}
else
{
Codec_WriteRegister(CODEC_ANA_I2C, W8731_ANLG_AU_PATH_CNTR,
W8731_ANLG_AU_PATH_CNTR_DACSEL |
W8731_ANLG_AU_PATH_CNTR_INSEL_MIC); // mic boost off
ts.tx_mic_gain_mult = ts.tx_gain[TX_AUDIO_MIC];
}
}
}
static bool is_microphone_active(void)
{
return ts.tx_audio_source == TX_AUDIO_MIC && (ts.dmod_mode != DEMOD_CW && is_demod_rtty() == false && is_demod_psk() == false);
}
/**
* @brief sets certain settings in preparation for smooth TX switching, call before actual switch function is called
* @param current_txrx_mode the current mode, not the future mode (this is assumed to be TRX_MODE_TX)
*/
void Codec_PrepareTx(uint8_t current_txrx_mode)
{
Codec_LineInGainAdj(0); // yes - momentarily mute LINE IN audio if in LINE IN mode until we have switched to TX
bool uses_mic_input = is_microphone_active();
if (uses_mic_input) // we are in MIC IN mode
{
ts.tx_mic_gain_mult = 0; // momentarily set the mic gain to zero while we go to TX
Codec_WriteRegister(CODEC_ANA_I2C, W8731_ANLG_AU_PATH_CNTR,
W8731_ANLG_AU_PATH_CNTR_DACSEL
| W8731_ANLG_AU_PATH_CNTR_INSEL_LINE
| W8731_ANLG_AU_PATH_CNTR_MUTEMIC);
// Mute the microphone with the CODEC (this does so without a CLICK) and remain/switch line in on
Codec_WriteRegister(CODEC_ANA_I2C, W8731_POWER_DOWN_CNTR,
W8731_POWER_DOWN_CNTR_MCHF_ALL_ON);
// now we power on all amps including the mic preamp and bias
}
// Is translate mode active and we have NOT already muted the audio output?
if ((ts.iq_freq_mode) && (current_txrx_mode == TRX_MODE_RX))
{
Codec_VolumeSpkr(0);
Codec_VolumeLineOut(TRX_MODE_TX); // yes - mute the audio codec to suppress an approx. 6 kHz chirp when going in to TX mode
}
if (uses_mic_input)
{
HAL_Delay(10);
// pause an instant because the codec chip has its own delay before tasks complete when we use the microphone input!
// otherwise audible noise will be transmitted
}
}
/**
* @brief setups up the codec according to tx/rx mode and selected sources
* @param txrx_mode the mode for which it should be configured
*
*/
void Codec_SwitchTxRxMode(uint8_t txrx_mode)
{
// First step - mute sound
Codec_VolumeSpkr(0);
Codec_VolumeLineOut(txrx_mode);
if(txrx_mode == TRX_MODE_RX)
{
// Mute line input
Codec_LineInGainAdj(0);
// Reg 04: Analog Audio Path Control (DAC sel, ADC line, Mute Mic)
Codec_WriteRegister(CODEC_ANA_I2C, W8731_ANLG_AU_PATH_CNTR,
W8731_ANLG_AU_PATH_CNTR_DACSEL|
W8731_ANLG_AU_PATH_CNTR_INSEL_LINE|
W8731_ANLG_AU_PATH_CNTR_MUTEMIC);
// Reg 06: Power Down Control (Clk off, Osc off, Mic Off)
// COMMENT: It would be tempting to set bit 1 "MICPD" of "W8731_POWER_DOWN_CTR" to zero to disable mic power down
// and maintain microphone bias during receive, but this seems to cause problems on receive (e.g. deafness) even
// if the microphone is muted and "mic boost" is disabled. (KA7OEI 20151030)
Codec_WriteRegister(CODEC_ANA_I2C, W8731_POWER_DOWN_CNTR,W8731_POWER_DOWN_CNTR_MCHF_MIC_OFF); // turn off mic bias
}
else // It is transmit
{
if(RadioManagement_UsesTxSidetone())
{
Codec_TxSidetoneSetgain(txrx_mode); // set sidetone level
}
else // Not CW or TUNE mode
{
// Select source or leave it as it is
// PHONE out is muted, normal exit routed to TX modulator
// input audio is routed via 4066 switch
if(ts.tx_audio_source == TX_AUDIO_MIC)
{
// now enabled the analog path according to gain settings
// with or without boost
Codec_SwitchMicTxRxMode(txrx_mode);
}
else if (ts.tx_audio_source != TX_AUDIO_DIG || ts.tx_audio_source != TX_AUDIO_DIGIQ)
{
// we change gain only if it is not a digital tx input source
Codec_LineInGainAdj(ts.tx_gain[ts.tx_audio_source]);
// set LINE input gain if in LINE in mode
}
}
}
}
/**
* @brief calculates and sets sidetone gain based on tx power factor
*
* This calculates the relative level of the sidetone and sets the headphone gain appropriately
* to keep the sidetone level more or less the same.
* This seems to be slightly "off", particularly at the extremes of high and low
* transmit power levels - this needs to be looked into...
*
*/
void Codec_TxSidetoneSetgain(uint8_t txrx_mode)
{
// Note that this function is called from places OTHER than Codec_RX_TX(), above!
if(txrx_mode == TRX_MODE_TX) // bail out if not in transmit mode
{
float32_t vcalc = 0;
if(ts.cw_sidetone_gain) // calculate if the sidetone gain is non-zero
{
float32_t pf = ts.tx_power_factor; // get TX scaling power factor
if ( pf == 0 )
{
pf = 0.001; // Almost zero but prevent from NoNe (1/0) in the next equation.
}
float32_t signal_level_db = 10* log10f(1/(pf*pf));
// we invert the square of power_factor (aka the signal energy)
// since we are calculating attenuation of the original signal (assuming normalization to 1.0)
// get the log
// and multiple with 10 to convert to deciBels
float32_t sidetone_level_db = 6.0 *((float32_t)ts.cw_sidetone_gain-5);
// get the sidetone gain (level) setting
// offset by # of dB the desired sidetone gain
vcalc = signal_level_db + sidetone_level_db; // add the calculated gain to the desired sidetone gain
if(vcalc > 127) // enforce limits of calculation to range of attenuator
{
vcalc = 127;
}
else if (vcalc < 0)
{
vcalc = 0;
}
}
Codec_VolumeSpkr(vcalc/5); // divide by 5 to convert decibel to volume control steps
}
}
/**
* @brief audio volume control in TX and RX modes for speaker [left headphone]
* @param vol speaker / headphone volume in range [0 - CODEC_SPEAKER_MAX_VOLUME], unit is dB, 0 represents muting, one increment represents 5db
*/
void Codec_VolumeSpkr(uint8_t vol)
{
#ifdef UI_BRD_MCHF
uint32_t lv = vol*5>W8731_VOL_MAX?W8731_VOL_MAX:vol*5;
// limit max value to 80
lv += 0x2F; // volume offset, all lower values including 0x2F represent muting
// Reg 02: Speaker - variable volume, change at zero crossing in order to prevent audible clicks
// Codec_WriteRegister(W8731_LEFT_HEADPH_OUT,lv); // (lv | W8731_HEADPH_OUT_ZCEN));
Codec_WriteRegister(CODEC_ANA_I2C, W8731_LEFT_HEADPH_OUT,(lv | W8731_HEADPH_OUT_ZCEN));
#else
// external PA Control
AudioPA_Volume(vol);
#endif
}
/**
* @brief audio volume control in TX and RX modes for lineout [right headphone]
*
* At RX Lineout is always on with constant level (control via ts.lineout_gain)
* At TX only if no frequency translation is active AND TX lineout mute is not set OR in CW
* (because we send always without frequency translation in CW)
*
* @param txrx_mode txrx for which volume is to be set
*/
void Codec_VolumeLineOut(uint8_t txrx_mode)
{
uint16_t lov = ts.lineout_gain + 0x2F;
// Selectively mute "Right Headphone" output (LINE OUT) depending on transceiver configuration
#ifdef UI_BRD_MCHF
// only needed if we have MCHF_UI which shares IQ and Audio on same codec
// we can only "listen to the transmit output if we are send
if (
(txrx_mode == TRX_MODE_TX)
&&
((ts.flags1 & FLAGS1_MUTE_LINEOUT_TX) || (ts.iq_freq_mode && ts.dmod_mode != DEMOD_CW))
)
{
// at CW we transmit without translation, no matter what the iq_freq_mode for RX is
// is translate mode active OR translate mode OFF but LINE OUT to be muted during transmit
Codec_WriteRegister(CODEC_ANA_I2C, W8731_RIGHT_HEADPH_OUT,0); // yes - mute LINE OUT during transmit
}
else // receive mode - LINE OUT always enabled
{
Codec_WriteRegister(CODEC_ANA_I2C, W8731_RIGHT_HEADPH_OUT,lov); // value selected for 0.5VRMS at AGC setting
}
#elif defined(UI_BRD_OVI40)
UNUSED(txrx_mode);
// we have a special shared lineout/headphone on the OVI40.
// And since we have a dedidacted IQ codec, there is no need to switch of the lineout or headphones here
Codec_WriteRegister(CODEC_ANA_I2C, W8731_RIGHT_HEADPH_OUT, lov | W8731_HEADPH_OUT_ZCEN | W8731_HEADPH_OUT_HPBOTH ); // value selected for 0.5VRMS at AGC setting
#endif
}
/**
* @brief mute the Codecs Digital to Analog Converter Output
* @param state true -> mute, false -> unmute
*/
void Codec_MuteDAC(bool state)
{
if(state)
{
Codec_WriteRegister(CODEC_ANA_I2C, W8731_DIGI_AU_PATH_CNTR,(W8731_DEEMPH_CNTR|0x08)); // mute
}
else
{
Codec_WriteRegister(CODEC_ANA_I2C, W8731_DIGI_AU_PATH_CNTR,(W8731_DEEMPH_CNTR)); // mute off
}
}
/**
* @brief Sets the Codec WM8371 line input gain for both channels
* @param gain in range of [0-255]
*/
static void Codec_InGainAdj(I2C_HandleTypeDef* hi2c, uint16_t gain)
{
// Use Reg 00: Left Line In, set flag to adjust gain of both channels simultaneously
Codec_WriteRegister(hi2c, W8731_LEFT_LINE_IN, gain | W8731_LINE_IN_LRBOTH);
}
/**
* @brief Sets the Codec WM8371 line input gain for both audio in channels
* @param gain in range of [0-255]
*/
void Codec_LineInGainAdj(uint8_t gain)
{
Codec_InGainAdj(CODEC_ANA_I2C, gain);
}
/**
* @brief Sets the Codec WM8371 line input gain for IQ in (both channels)
* @param gain in range of [0-255]
*/
void Codec_IQInGainAdj(uint8_t gain)
{
#ifdef SDR_AMBER
if(ts.codecCS4270_present) // simulation of level control at the ADC input, which is absent in the CS4270 codec
{
float32_t gain_t;
switch(gain)
{
case 0: gain_t = 0.0188; break; // -34.5 dB
case 1: gain_t = 0.0224; break; // -33.0 dB
case 2: gain_t = 0.0266; break; // -31.5 dB
case 3: gain_t = 0.0316; break; // -30.0 dB
case 4: gain_t = 0.0376; break; // -28.5 dB
case 5: gain_t = 0.0447; break; // -27.0 dB
case 6: gain_t = 0.0531; break; // -25.5 dB
case 7: gain_t = 0.0631; break; // -24.0 dB
case 8: gain_t = 0.0750; break; // -22.5 dB
case 9: gain_t = 0.0891; break; // -21.0 dB
case 10: gain_t = 0.1060; break; // -19.5 dB
case 11: gain_t = 0.1260; break; // -18.0 dB
case 12: gain_t = 0.1500; break; // -16.5 dB
case 13: gain_t = 0.1780; break; // -15.0 dB
case 14: gain_t = 0.2110; break; // -13.5 dB
case 15: gain_t = 0.2510; break; // -12.0 dB
case 16: gain_t = 0.2990; break; // -10.5 dB
case 17: gain_t = 0.3550; break; // -9.0 dB
case 18: gain_t = 0.4220; break; // -7.5 dB
case 19: gain_t = 0.5010; break; // -6.0 dB
case 20: gain_t = 0.5960; break; // -4.5 dB
case 21: gain_t = 0.7080; break; // -3.0 dB
case 22: gain_t = 0.8410; break; // -1.5 dB
case 23: gain_t = 1.0000; break; // 0 dB
case 24: gain_t = 1.1900; break; // 1.5 dB
case 25: gain_t = 1.4100; break; // 3.0 dB
case 26: gain_t = 1.6800; break; // 4.5 dB
case 27: gain_t = 2.0000; break; // 6.0 dB
case 28: gain_t = 2.3700; break; // 7.5 dB
case 29: gain_t = 2.8200; break; // 9.0 dB
case 30: gain_t = 3.3500; break; // 10.5 dB
case 31: gain_t = 4.0000; break; // 12.0 dB
}
ts.rf_gain_codecCS4270 = gain_t;
}
else
{
#endif
Codec_InGainAdj(CODEC_IQ_I2C, gain);
#ifdef SDR_AMBER
}
#endif
}
/**
* @brief Checks if all codec resources are available for switching
* It basically checks if the I2C is currently in use
* This function must be called before changing the oscillator in interrupts
* otherwise deadlocks may happen
* @return true if it is safe to call codec functions in an interrupt
*/
bool Codec_ReadyForIrqCall(void)
{
return (CODEC_ANA_I2C->Lock == HAL_UNLOCKED) && (CODEC_IQ_I2C->Lock == HAL_UNLOCKED);
}