first commit
This commit is contained in:
@@ -0,0 +1,702 @@
|
||||
/* -*- 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);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/* -*- 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 **
|
||||
************************************************************************************/
|
||||
|
||||
#ifndef __CODEC_H
|
||||
#define __CODEC_H
|
||||
|
||||
typedef enum
|
||||
{
|
||||
WORD_SIZE_16 = 0,
|
||||
WORD_SIZE_24 = 2,
|
||||
WORD_SIZE_32 = 3,
|
||||
} CodecSampleWidth_t;
|
||||
|
||||
#include "ui_configuration.h"
|
||||
#ifdef UI_BRD_MCHF
|
||||
#define CODEC_SPEAKER_MAX_VOLUME 16
|
||||
#else
|
||||
#define CODEC_SPEAKER_MAX_VOLUME AUDIO_GAIN_MAX
|
||||
#endif
|
||||
|
||||
// --------------------------------------------------
|
||||
|
||||
/**
|
||||
* @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);
|
||||
void Codec_MCUInterfaceInit(uint32_t AudioFreq);
|
||||
void Codec_VolumeLineOut(uint8_t txrx_mode);
|
||||
void Codec_VolumeSpkr(uint8_t vol);
|
||||
void Codec_LineInGainAdj(uint8_t gain);
|
||||
void Codec_IQInGainAdj(uint8_t gain);
|
||||
void Codec_MuteDAC(bool state);
|
||||
|
||||
uint32_t Codec_Reset(uint32_t AudioFreq);
|
||||
void Codec_RestartI2S(void);
|
||||
void Codec_TxSidetoneSetgain(uint8_t mode);
|
||||
void Codec_SwitchMicTxRxMode(uint8_t mode);
|
||||
void Codec_PrepareTx(uint8_t current_txrx_mode);
|
||||
|
||||
bool Codec_ReadyForIrqCall(void);
|
||||
#endif
|
||||
|
||||
#ifdef SDR_AMBER
|
||||
uint32_t Codec_Reset_CS4270_Codec(void);
|
||||
#endif
|
||||
@@ -0,0 +1,250 @@
|
||||
/* -*- 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_config.h"
|
||||
#include "uhsdr_board.h"
|
||||
#include "profiling.h"
|
||||
#include "uhsdr_hw_i2s.h"
|
||||
|
||||
#include "audio_driver.h"
|
||||
|
||||
#ifdef UI_BRD_MCHF
|
||||
#include "i2s.h"
|
||||
#endif
|
||||
|
||||
#ifdef UI_BRD_OVI40
|
||||
#include "sai.h"
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
IqSample_t out[2*IQ_BLOCK_SIZE];
|
||||
IqSample_t in[2*IQ_BLOCK_SIZE];
|
||||
} dma_iq_buffer_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
AudioSample_t out[2*AUDIO_BLOCK_SIZE];
|
||||
AudioSample_t in[2*AUDIO_BLOCK_SIZE];
|
||||
} dma_audio_buffer_t;
|
||||
|
||||
|
||||
// we do something tricky here:
|
||||
// if we have a single codec both buffers are in fact the same, so we use a union
|
||||
// if we have two codecs we use a struct hence two separate buffers
|
||||
|
||||
typedef
|
||||
#if CODEC_NUM == 1
|
||||
union
|
||||
#else
|
||||
struct
|
||||
#endif
|
||||
{
|
||||
dma_iq_buffer_t iq_buf;
|
||||
dma_audio_buffer_t audio_buf;
|
||||
} I2S_DmaBuffers_t;
|
||||
|
||||
static __UHSDR_DMAMEM I2S_DmaBuffers_t dma;
|
||||
|
||||
|
||||
void UhsdrHwI2s_Codec_ClearTxDmaBuffer()
|
||||
{
|
||||
memset((void*)&dma.iq_buf.out, 0, sizeof(dma.iq_buf.out));
|
||||
}
|
||||
|
||||
// #define PROFILE_APP
|
||||
static void MchfHw_Codec_HandleBlock(uint16_t which)
|
||||
{
|
||||
#ifdef PROFILE_EVENTS
|
||||
// we stop during interrupt
|
||||
// at the end we start again
|
||||
#ifdef PROFILE_APP
|
||||
profileCycleCount_stop();
|
||||
#else
|
||||
profileTimedEventStart(ProfileAudioInterrupt);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef EXEC_PROFILING
|
||||
// Profiling pin (high level)
|
||||
GPIOE->BSRRL = GPIO_Pin_10;
|
||||
#endif
|
||||
|
||||
ts.audio_int_counter++; // generating a time base for encoder handling
|
||||
|
||||
// Transfer complete interrupt
|
||||
// Point to 2nd half of buffers
|
||||
const size_t sz = IQ_BLOCK_SIZE;
|
||||
const uint16_t offset = which == 0?sz:0;
|
||||
|
||||
AudioSample_t *audio;
|
||||
IqSample_t *iq;
|
||||
|
||||
if (ts.txrx_mode != TRX_MODE_TX)
|
||||
{
|
||||
iq = &dma.iq_buf.in[offset];
|
||||
audio = &dma.audio_buf.out[offset];
|
||||
}
|
||||
else
|
||||
{
|
||||
audio = &dma.audio_buf.in[offset];
|
||||
iq = &dma.iq_buf.out[offset];
|
||||
}
|
||||
|
||||
AudioSample_t *audioDst = &dma.audio_buf.out[offset];
|
||||
|
||||
// Handle
|
||||
AudioDriver_I2SCallback(audio, iq, audioDst, sz);
|
||||
|
||||
#ifdef EXEC_PROFILING
|
||||
// Profiling pin (low level)
|
||||
GPIOE->BSRRH = GPIO_Pin_10;
|
||||
#endif
|
||||
#ifdef PROFILE_EVENTS
|
||||
// we stopped during interrupt
|
||||
// now we start again
|
||||
#ifdef PROFILE_APP
|
||||
profileCycleCount_start();
|
||||
#else
|
||||
profileTimedEventStop(ProfileAudioInterrupt);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef UI_BRD_MCHF
|
||||
/**
|
||||
* @brief HAL Handler for Codec DMA Interrupt
|
||||
*/
|
||||
void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s)
|
||||
{
|
||||
MchfHw_Codec_HandleBlock(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief HAL Handler for Codec DMA Interrupt
|
||||
*/
|
||||
void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
|
||||
{
|
||||
MchfHw_Codec_HandleBlock(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef UI_BRD_OVI40
|
||||
void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hi2s)
|
||||
{
|
||||
if (hi2s == &hsai_BlockA2)
|
||||
{
|
||||
MchfHw_Codec_HandleBlock(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hi2s)
|
||||
{
|
||||
if (hi2s == &hsai_BlockA2)
|
||||
{
|
||||
MchfHw_Codec_HandleBlock(1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(UI_BRD_OVI40) && defined(USE_32_IQ_BITS) && defined(USE_32_AUDIO_BITS)
|
||||
static void UhsdrHWI2s_Sai32Bits(SAI_HandleTypeDef* hsai)
|
||||
{
|
||||
hsai->hdmarx->Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
|
||||
hsai->hdmarx->Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
|
||||
HAL_DMA_Init(hsai->hdmarx);
|
||||
|
||||
HAL_SAI_InitProtocol(hsai, SAI_I2S_STANDARD, SAI_PROTOCOL_DATASIZE_32BIT, 2);
|
||||
}
|
||||
|
||||
static void UhsdrHWI2s_Sai24Bits(SAI_HandleTypeDef* hsai)
|
||||
{
|
||||
hsai->hdmarx->Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
|
||||
hsai->hdmarx->Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
|
||||
HAL_DMA_Init(hsai->hdmarx);
|
||||
|
||||
HAL_SAI_InitProtocol(hsai, SAI_I2S_STANDARD, SAI_PROTOCOL_DATASIZE_24BIT, 2);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void UhsdrHwI2s_SetBitWidth(void)
|
||||
{
|
||||
#if defined(USE_32_IQ_BITS)
|
||||
#if defined(UI_BRD_MCHF)
|
||||
hi2s3.Init.DataFormat = I2S_DATAFORMAT_32B;
|
||||
HAL_I2S_Init(&hi2s3);
|
||||
#endif
|
||||
#if defined(UI_BRD_OVI40)
|
||||
if(ts.codecCS4270_present)
|
||||
{
|
||||
UhsdrHWI2s_Sai24Bits(&hsai_BlockA2);
|
||||
UhsdrHWI2s_Sai24Bits(&hsai_BlockB2);
|
||||
}
|
||||
else
|
||||
{
|
||||
UhsdrHWI2s_Sai32Bits(&hsai_BlockA2);
|
||||
UhsdrHWI2s_Sai32Bits(&hsai_BlockB2);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(USE_32_AUDIO_BITS)
|
||||
#if defined(UI_BRD_OVI40)
|
||||
UhsdrHWI2s_Sai32Bits(&hsai_BlockA1);
|
||||
UhsdrHWI2s_Sai32Bits(&hsai_BlockB1);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
void UhsdrHwI2s_Codec_StartDMA()
|
||||
{
|
||||
UhsdrHwI2s_SetBitWidth();
|
||||
|
||||
#ifdef UI_BRD_MCHF
|
||||
HAL_I2SEx_TransmitReceive_DMA(&hi2s3,(uint16_t*)dma.iq_buf.out,(uint16_t*)dma.iq_buf.in,sizeof(dma.iq_buf.in)/sizeof(dma.iq_buf.in[0].l));
|
||||
#endif
|
||||
#ifdef UI_BRD_OVI40
|
||||
// we clean the buffers since we don't know if we are in a "cleaned" memory segement
|
||||
memset((void*)&dma.audio_buf,0,sizeof(dma.audio_buf));
|
||||
memset((void*)&dma.iq_buf,0,sizeof(dma.iq_buf));
|
||||
|
||||
HAL_SAI_Receive_DMA(&hsai_BlockA1,(uint8_t*)dma.audio_buf.in,sizeof(dma.audio_buf.in)/sizeof(dma.audio_buf.in[0].l));
|
||||
HAL_SAI_Transmit_DMA(&hsai_BlockB1,(uint8_t*)dma.audio_buf.out,sizeof(dma.audio_buf.out)/sizeof(dma.audio_buf.out[0].l));
|
||||
|
||||
HAL_SAI_Receive_DMA(&hsai_BlockA2,(uint8_t*)dma.iq_buf.in,sizeof(dma.iq_buf.in)/sizeof(dma.iq_buf.in[0].l));
|
||||
HAL_SAI_Transmit_DMA(&hsai_BlockB2,(uint8_t*)dma.iq_buf.out,sizeof(dma.iq_buf.out)/sizeof(dma.iq_buf.out[0].l));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void UhsdrHwI2s_Codec_StopDMA(void)
|
||||
{
|
||||
#ifdef UI_BRD_MCHF
|
||||
HAL_I2S_DMAStop(&hi2s3);
|
||||
#endif
|
||||
#ifdef UI_BRD_OVI40
|
||||
HAL_SAI_DMAStop(&hsai_BlockA1);
|
||||
HAL_SAI_DMAStop(&hsai_BlockB1);
|
||||
HAL_SAI_DMAStop(&hsai_BlockA2);
|
||||
HAL_SAI_DMAStop(&hsai_BlockB2);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/* -*- 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 **
|
||||
************************************************************************************/
|
||||
|
||||
#ifndef __MCHF_HW_I2S_H
|
||||
#define __MCHF_HW_I2S_H
|
||||
|
||||
#include "uhsdr_board_config.h"
|
||||
|
||||
void UhsdrHwI2s_Codec_StartDMA(void);
|
||||
void UhsdrHwI2s_Codec_StopDMA(void);
|
||||
|
||||
void UhsdrHwI2s_Codec_ClearTxDmaBuffer(void);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user