FreeTRX/MDUV380_firmware/application/source/hardware/radioHardwareInterface.c
2026-07-06 08:45:33 +02:00

528 lines
14 KiB
C

/*
* Copyright (C) 2021-2025 Roger Clark, VK3KYY / G4KYF
*
*
* 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 the copyright holder nor the names of its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. Use of this source code or binary releases for commercial purposes is strictly forbidden. This includes, without limitation,
* incorporation in a commercial product or incorporation into a product or project which allows commercial use.
*
* 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 "functions/settings.h"
#include "functions/trx.h"
#include "functions/rxPowerSaving.h"
#include "hardware/HR-C6000.h"
#include "hardware/AT1846S.h"
#if defined(USING_EXTERNAL_DEBUGGER)
#include "SeggerRTT/RTT/SEGGER_RTT.h"
#endif
#include "main.h"
#if defined(PLATFORM_MD9600)
//list of CTCSS tones supported by the HRC6000. These are used to find the index for the tone. The order is critical.
const uint16_t HRC_CTCSSTones[] =
{
6700, 7190, 7440, 7700, 7970, 8250, 8540,
8850, 9150, 9480, 9740, 10000, 10350, 10720,
11090, 11480, 11880, 12300, 12730, 13180, 13650,
14130, 14620, 15140, 15670, 16220, 16790, 17380,
17990, 18620, 19280, 20350, 21070, 21810, 22570,
23360, 24180, 25030, 6930, 6250, 15980, 16550,
17130, 17730, 18350, 18990, 19660, 19950, 20650,
22910, 25410
};
#endif
// AT-1846 native values for Rx
static uint8_t fl_l;
static uint8_t fl_h;
static uint8_t fh_l;
static uint8_t fh_h;
RadioDevice_t currentRadioDeviceId = RADIO_DEVICE_PRIMARY;
TRXDevice_t radioDevices[RADIO_DEVICE_MAX] = {
{
.currentRxFrequency = FREQUENCY_UNSET,
.currentTxFrequency = FREQUENCY_UNSET,
.lastSetTxFrequency = FREQUENCY_UNSET,
.trxCurrentBand = { RADIO_BAND_VHF, RADIO_BAND_VHF },
.trxDMRModeRx = DMR_MODE_DMO,
.trxDMRModeTx = DMR_MODE_DMO,
.currentMode = RADIO_MODE_NONE,
.txPowerLevel = POWER_UNSET,
.lastSetTxPowerLevel = POWER_UNSET,
.trxRxSignal = 0,
.trxRxNoise = 255,
.currentBandWidthIs25kHz = BANDWIDTH_12P5KHZ,
.analogSignalReceived = false,
.analogTriggeredAudio = false,
.digitalSignalReceived = false
}
};
TRXDevice_t *currentRadioDevice = &radioDevices[RADIO_DEVICE_PRIMARY];
void radioPowerOn(void)
{
//Turn off Tx Voltages to prevent transmission.
HAL_GPIO_WritePin(PA_EN_1_GPIO_Port, PA_EN_1_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(PA_EN_2_GPIO_Port, PA_EN_2_Pin, GPIO_PIN_RESET);
//Turn on all receiver voltages. (thats what the TYT firmware does, both receivers are on together)
radioSetRxLNAForDevice(currentRadioDeviceId);
HAL_GPIO_WritePin(C6000_PWD_GPIO_Port, C6000_PWD_Pin, GPIO_PIN_RESET);// Power On the C6000
//Turn on the Microphone power
HAL_GPIO_WritePin(MICPWR_SW_GPIO_Port, MICPWR_SW_Pin, GPIO_PIN_SET);
}
void radioSetRxLNAForDevice(RadioDevice_t deviceId)
{
if (radioDevices[deviceId].trxCurrentBand[TRX_RX_FREQ_BAND] == RADIO_BAND_VHF)
{
HAL_GPIO_WritePin(R5_V_SW_GPIO_Port, R5_V_SW_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(R5_U_SW_GPIO_Port, R5_U_SW_Pin, GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(R5_V_SW_GPIO_Port, R5_V_SW_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(R5_U_SW_GPIO_Port, R5_U_SW_Pin, GPIO_PIN_SET);
}
}
void radioPowerOff(bool invalidateFrequency, bool includeMic)
{
//turn off all of the transmitter and receiver voltages
HAL_GPIO_WritePin(PA_EN_1_GPIO_Port, PA_EN_1_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(PA_EN_2_GPIO_Port, PA_EN_2_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(R5_V_SW_GPIO_Port, R5_V_SW_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(R5_U_SW_GPIO_Port, R5_U_SW_Pin, GPIO_PIN_RESET);
// Some external speaker mic are problematic when the
// Mic pin is powered down, triggering some cycling PTT down events.
// So, we're keeping that pin high all the time when Rx Power Saving is running
if (includeMic)
{
HAL_GPIO_WritePin(MICPWR_SW_GPIO_Port, MICPWR_SW_Pin, GPIO_PIN_RESET);
}
HAL_GPIO_WritePin(C6000_PWD_GPIO_Port, C6000_PWD_Pin, GPIO_PIN_SET);// Power Down the C6000
// Turn off Rx in AT1846S
if (currentRadioDevice->currentBandWidthIs25kHz)
{
// 25 kHz settings
radioWriteReg2byte(0x30, 0x70, 0x06); // RX off
}
else
{
// 12.5 kHz settings
radioWriteReg2byte(0x30, 0x40, 0x06); // RX off
}
if (invalidateFrequency)
{
trxInvalidateCurrentFrequency();
}
}
void radioInit(void)
{
radioSetTRxDevice(RADIO_DEVICE_PRIMARY);
AT1846sInit();
}
void radioPostinit(void)
{
AT1846sPostInit();
}
RadioDevice_t radioSetTRxDevice(RadioDevice_t deviceId)
{
RadioDevice_t previousDeviceId = currentRadioDeviceId;
currentRadioDeviceId = deviceId;
currentRadioDevice = &radioDevices[currentRadioDeviceId];
return previousDeviceId;
}
RadioDevice_t radioGetTRxDeviceId(void)
{
return currentRadioDeviceId;
}
void radioSetBandwidth(bool Is25K)
{
AT1846sSetBandWidth(Is25K);
}
void radioSetMode(int mode) // Called withing trx.c: in task critical sections
{
if (mode == RADIO_MODE_ANALOG)
{
HRC6000SetFMRx();
}
else
{
HRC6000SetDMR();
}
AT1846sSetMode(mode);
}
void radioReadVoxAndMicStrength(void)
{
trxTxVox = adcGetVOX(); //MDUV380 Mic is not connected to AT1846 but has dedicated ADC channel like MD9600
trxTxMic = trxTxVox; //MDUV380 doesn't have separate Signals so use Vox for both.
}
static void trxUpdateAT1846SCalibration(void)
{
//Nothing needed on the MDUV380
}
void radioSetFrequency(uint32_t f_in, bool Tx)
{
// G4EML. Hack to fix the -3KHz offset on transmit on the MDUV380
// Believed to be caused by something in the AT1846S configuration.
// If we ever fix what is causing it then this hack can be removed.
if (Tx)
{
if (currentRadioDevice->currentBandWidthIs25kHz)
{
#warning Hack for FM 25khz bandwidth, somehow needed after changes to activate both AT1846S. Needs more investigation, as it may be masking another problem
f_in = f_in + 615; //add 6.15Khz to Tx frequency
}
else
{
f_in = f_in + 300; //add 3Khz to Tx frequency
}
}
//
if (currentChannelData->libreDMR_Power != 0x00)
{
currentRadioDevice->txPowerLevel = currentChannelData->libreDMR_Power - 1;
}
else
{
currentRadioDevice->txPowerLevel = nonVolatileSettings.txPowerLevel;
}
if (Tx)
{
currentRadioDevice->trxCurrentBand[TRX_TX_FREQ_BAND] = trxGetBandFromFrequency(f_in);
}
else
{
currentRadioDevice->trxCurrentBand[TRX_RX_FREQ_BAND] = trxGetBandFromFrequency(f_in);
radioSetRxLNAForDevice(currentRadioDeviceId);
}
uint32_t f = f_in * 0.16f;
fl_l = (f & 0x000000ff) >> 0;
fl_h = (f & 0x0000ff00) >> 8;
fh_l = (f & 0x00ff0000) >> 16;
fh_h = (f & 0xff000000) >> 24;
if (currentRadioDevice->currentBandWidthIs25kHz)
{
// 25 kHz settings
radioWriteReg2byte(0x30, 0x70, 0x06); // RX off
}
else
{
// 12.5 kHz settings
radioWriteReg2byte(0x30, 0x60, 0x06); // RX off
}
radioWriteReg2byte(0x05, 0x87, 0x63); // select 'normal' frequency mode
radioWriteReg2byte(0x29, fh_h, fh_l);
radioWriteReg2byte(0x2a, fl_h, fl_l);
radioWriteReg2byte(0x49, 0x0C, 0x15); // setting SQ open and shut threshold
if (currentRadioDevice->currentBandWidthIs25kHz)
{
// 25 kHz settings
radioWriteReg2byte(0x30, 0x70, 0x26); // RX on
}
else
{
// 12.5 kHz settings
radioWriteReg2byte(0x30, 0x60, 0x26); // RX on
}
trxUpdateAT1846SCalibration();
if (Tx) //We have just set the Tx frequency so we are about to transmit. Set the Tx Power Control DAC (Shared with VHF Receive Tuning)
{
dacOut(1, txDACDrivePower);
}
else
{
dacOut(1, 0);
}
}
void radioSetIF(int band, bool wide)
{
UNUSED_PARAMETER(band);
radioSetBandwidth(wide);
}
void radioSetTx(uint8_t band)
{
//Turn off receiver voltages. (thats what the TYT firmware does, both receivers are on or off together)
HAL_GPIO_WritePin(R5_V_SW_GPIO_Port, R5_V_SW_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(R5_U_SW_GPIO_Port, R5_U_SW_Pin, GPIO_PIN_RESET);
//Configure HRC-6000 for transmit
if (trxGetMode() == RADIO_MODE_ANALOG)
{
HRC6000SetFMTx();
if (currentRadioDevice->currentBandWidthIs25kHz)
{
radioWriteReg2byte(0x30, 0x70, 0x46); // 25 kHz settings // RX on
}
else
{
radioWriteReg2byte(0x30, 0x60, 0x46); // 12.5 kHz settings // RX on
}
trxSelectVoiceChannel(AT1846_VOICE_CHANNEL_MIC);// For 1750 tone burst
//trxSelectVoiceChannel(AT1846_VOICE_CHANNEL_NONE);// the MDUV380 does not use the AT1846S for modulation.
trxSetMicGainFM(nonVolatileSettings.micGainFM);
}
else
{
HRC6000SetDMR();
radioWriteReg2byte(0x30, 0x60, 0xC6); // Digital Tx
}
//Turn on Tx Voltage for the current band.
HAL_GPIO_WritePin(PA_EN_1_GPIO_Port, PA_EN_1_Pin, GPIO_PIN_SET);
if (band == RADIO_BAND_VHF)
{
HAL_GPIO_WritePin(PA_SEL_SW_GPIO_Port, PA_SEL_SW_Pin, GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(PA_SEL_SW_GPIO_Port, PA_SEL_SW_Pin, GPIO_PIN_SET);
}
//Turn on the power control circuit
HAL_GPIO_WritePin(PA_EN_2_GPIO_Port, PA_EN_2_Pin, GPIO_PIN_SET);
trxIsTransmitting = true;
}
//just enable or disable the RF output . doesn't change back to receive
void radioFastTx(bool tx)
{
if (tx)
{
radioWriteReg2byte(0x30, 0x60, 0xC6); // Digital Tx
HAL_GPIO_WritePin(PA_EN_1_GPIO_Port, PA_EN_1_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(PA_EN_2_GPIO_Port, PA_EN_2_Pin, GPIO_PIN_SET);
}
else
{
//Turn off Tx Voltages to prevent transmission.
HAL_GPIO_WritePin(PA_EN_1_GPIO_Port, PA_EN_1_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(PA_EN_2_GPIO_Port, PA_EN_2_Pin, GPIO_PIN_RESET);
if (currentRadioDevice->currentBandWidthIs25kHz)
{
// 25 kHz settings
radioWriteReg2byte(0x30, 0x70, 0x26); // RX on
}
else
{
// 12.5 kHz settings
radioWriteReg2byte(0x30, 0x60, 0x26); // RX on
}
}
}
void radioSetRx(uint8_t band)
{
//Turn off the power control circuit
//HAL_GPIO_WritePin(RF_APC_SW_GPIO_Port, RF_APC_SW_Pin, GPIO_PIN_RESET);
//Turn off Tx Voltages to prevent transmission.
HAL_GPIO_WritePin(PA_EN_1_GPIO_Port, PA_EN_1_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(PA_EN_2_GPIO_Port, PA_EN_2_Pin, GPIO_PIN_RESET);
//Turn on receiver voltages. (thats what the TYT firmware does, both receivers are on together)
radioSetRxLNAForDevice(currentRadioDeviceId);
trxIsTransmitting = false;
if (currentRadioDevice->currentBandWidthIs25kHz)
{
// 25 kHz settings
radioWriteReg2byte(0x30, 0x70, 0x26); // RX on
}
else
{
// 12.5 kHz settings
radioWriteReg2byte(0x30, 0x60, 0x26); // RX on
}
if (trxGetMode() == RADIO_MODE_ANALOG)
{
HRC6000SetFMRx();
}
else
{
HRC6000SetDMR();
}
}
void radioReadRSSIAndNoiseForBand(uint8_t band)
{
uint8_t val1, val2;
UNUSED_PARAMETER(band);// not band specific on the MDUV380
if (rxPowerSavingIsRxOn())
{
taskENTER_CRITICAL();
if (radioReadReg2byte(0x1b, &val1, &val2))
{
currentRadioDevice->trxRxSignal = val1;
currentRadioDevice->trxRxNoise = val2;
}
taskEXIT_CRITICAL();
trxDMRSynchronisedRSSIReadPending = false;
}
}
#if defined(PLATFORM_MD9600)
static int getCSSToneIndex(uint16_t tone)
{
//the TYT firmware uses a PWM output to directly generate the CTCSS Tone.
//however the HR-C6000 can also generate tones so we will use that instead
//this uses a table so we need to convert the CTCSS tone to the Table Index.
uint8_t HRCToneIndex = 1;
for (int i = 0; i < ARRAY_SIZE(HRC_CTCSSTones); i++)
{
if (HRC_CTCSSTones[i] == tone)
{
HRCToneIndex = i + 1;
break;
}
}
return HRCToneIndex;
}
#endif
void radioTxCSSOff(void)
{
AT1846sSetTxCTCSS(0);
}
void radioTxCTCSOn(uint16_t tone)
{
AT1846sSetTxCTCSS(tone);
}
void radioTxDCSOn(uint16_t code, bool inverted)
{
#if defined(PLATFORM_MDUV380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
AT1846sSetTxDCS(code, inverted);
#elif defined(PLATFORM_MD9600)
HRC6000SetTxDCS(code, inverted);
#else
#error UNSUPPORTED PLATFORM
#endif
}
void radioRxCSSOff(RadioDevice_t deviceId)
{
AT1846sSetRxCSSOff(deviceId);
}
void radioRxCTCSOn(RadioDevice_t deviceId, uint16_t tone)
{
AT1846sSetRxCTCSS(deviceId, tone);
}
void radioRxDCSOn(RadioDevice_t deviceId, uint16_t code, bool inverted)
{
AT1846sSetRxDCS(deviceId, code, inverted);
}
bool radioCheckCSS(uint16_t tone, CodeplugCSSTypes_t type)
{
return AT1846sCheckCSS(tone, type);
}
void radioSetTone1(int tonefreq)
{
if(tonefreq > 0)
{
trxSelectVoiceChannel(AT1846_VOICE_CHANNEL_TONE1); //configure AT1846S for tone
HRC6000SetMic(false); //mute the microphone while sending tone.
}
else
{
trxSelectVoiceChannel(AT1846_VOICE_CHANNEL_MIC); //reset At1846S
HRC6000SetMic(true); //enable the mic
}
AT1846sWriteTone1Reg(tonefreq * 10);
}
void radioSetMicGainFM(uint8_t gain)
{
HRC6000SetMicGainFM(gain);
}
void radioAudioAmp(bool on)
{
HAL_GPIO_WritePin(AUDIO_AMP_EN_GPIO_Port, AUDIO_AMP_EN_Pin, (on ? GPIO_PIN_SET : GPIO_PIN_RESET)); //Turn on the audio amp.
HAL_GPIO_WritePin(SPK_MUTE_GPIO_Port, SPK_MUTE_Pin, (on ? GPIO_PIN_RESET : GPIO_PIN_SET)); //Unmute the speaker
}
void radioSetAudioPath(bool fromFM)
{
HRC6000SetFmAudio(fromFM);
}
void radioSelectVoiceChannel(uint8_t channel, uint8_t *voiceGainTx, uint16_t *deviation)
{
AT1846sSelectVoiceChannel(channel, voiceGainTx, deviation);
}