/* -*- 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 #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); }