From 0fee5acbf06382013855267df01b4935865130a1 Mon Sep 17 00:00:00 2001 From: Silvano Seva Date: Sat, 5 Dec 2020 14:27:10 +0100 Subject: [PATCH] Complete refactoring of both rtx API and its implementation for MD3x0 platforms --- openrtx/include/interfaces/rtx.h | 76 +++-- platform/drivers/baseband/HR-C5000_MD3x0.c | 28 +- platform/drivers/baseband/HR-C5000_MD3x0.h | 13 + platform/drivers/baseband/rtx_MD3x0.c | 368 +++++++++++++++------ platform/targets/MD-380/hwconfig.h | 1 + 5 files changed, 355 insertions(+), 131 deletions(-) diff --git a/openrtx/include/interfaces/rtx.h b/openrtx/include/interfaces/rtx.h index 596c7b43..bf301394 100644 --- a/openrtx/include/interfaces/rtx.h +++ b/openrtx/include/interfaces/rtx.h @@ -24,51 +24,69 @@ #include #include -enum funcmode +typedef struct { - OFF, - RX, - TX -}; + uint8_t opMode : 2, /**< Operating mode (FM, DMR, ...) */ + bandwidth : 2, /**< Channel bandwidth */ + txDisable : 1, /**< Disable TX operation */ + _padding : 3; -enum tone -{ - NONE, - CTCSS_67_0, - CTCSS_71_9, - CTCSS_81_5 -}; + freq_t rxFrequency; /**< RX frequency, in Hz */ + freq_t txFrequency; /**< TX frequency, in Hz */ -enum bw + float txPower; /**< TX power, in W */ + uint8_t sqlLevel; /**< Squelch opening level */ + + tone_t rxTone; /**< RX CTC/DCS tone */ + tone_t txTone; /**< TX CTC/DCS tone */ +} +rtxConfig_t; + +enum bandwidth { - BW_12_5, - BW_25 + BW_12_5 = 0, /**< 12.5kHz bandwidth */ + BW_20 = 1, /**< 20kHz bandwidth */ + BW_25 = 2 /**< 25kHz bandwidth */ }; enum opmode { - FM, - DMR + FM = 0, /**< Analog FM */ + DMR = 1 /**< DMR */ }; +enum opstatus +{ + OFF = 0, /**< OFF */ + RX = 1, /**< Receiving */ + TX = 2 /**< Transmitting */ +}; + + +/** + * Initialise rtx stage + */ void rtx_init(); +/** + * Shut down rtx stage + */ void rtx_terminate(); -void rtx_setTxFreq(freq_t freq); +/** + * Post a new RTX configuration on the internal message queue. Data structure + * \b must be heap-allocated and \b must not be modified after this function has + * been called. The driver takes care of its deallocation. + * @param cfg: pointer to a structure containing the new RTX configuration. + */ +void rtx_configure(const rtxConfig_t *cfg); -void rtx_setRxFreq(freq_t freq); - -void rtx_setFuncmode(enum funcmode mode); - -void rtx_setToneRx(enum tone t); - -void rtx_setToneTx(enum tone t); - -void rtx_setBandwidth(enum bw b); +/** + * High-level code is in charge of calling this function periodically, since it + * contains all the RTX management functionalities. + */ +void rtx_taskFunc(); float rtx_getRssi(); -void rtx_setOpmode(enum opmode mode); - #endif /* RTX_H */ diff --git a/platform/drivers/baseband/HR-C5000_MD3x0.c b/platform/drivers/baseband/HR-C5000_MD3x0.c index 7d49d6d1..3f2ebd65 100644 --- a/platform/drivers/baseband/HR-C5000_MD3x0.c +++ b/platform/drivers/baseband/HR-C5000_MD3x0.c @@ -129,12 +129,30 @@ void C5000_setModOffset(uint8_t offset) { /* * Original TYT MD-380 code does this, both for DMR and FM. + * + * References: functions @0x0803fda8 and @0x0804005c */ uint8_t offUpper = (offset < 0x80) ? 0x00 : 0x03; uint8_t offLower = 0x7F - offset; _writeReg(0x00, 0x48, offUpper); // Two-point bias, upper value _writeReg(0x00, 0x47, offLower); // Two-point bias, lower value + + _writeReg(0x00, 0x04, offLower); // Bias value for TX, Q-channel +} + +void C5000_setModAmplitude(uint8_t iAmp, uint8_t qAmp) +{ + _writeReg(0x00, 0x45, iAmp); // Mod2 magnitude (HR_C6000) + _writeReg(0x00, 0x46, qAmp); // Mod1 magnitude (HR_C6000) + +} + +void C5000_setModFactor(uint8_t mf) +{ + _writeReg(0x00, 0x35, mf); // FM modulation factor + _writeReg(0x00, 0x3F, 0x04); // FM Limiting modulation factor (HR_C6000) + } void C5000_dmrMode() @@ -229,11 +247,11 @@ void C5000_startAnalogTx() _writeReg(0x00, 0x0F, 0xC8); // ADLinVol, mic volume // _writeReg(0x00, 0x25, 0x0E); // _writeReg(0x00, 0x26, 0xFE); - _writeReg(0x00, 0x45, 0x32); // Mod2 magnitude (HR_C6000) - _writeReg(0x00, 0x46, 0x85); // Mod1 magnitude (HR_C6000) - _writeReg(0x00, 0x04, 0x1F); // Bias value for TX, Q-channel - _writeReg(0x00, 0x35, 0x1E); // FM modulation factor - _writeReg(0x00, 0x3F, 0x04); // FM Limiting modulation factor (HR_C6000) +// _writeReg(0x00, 0x45, 0x32); // Mod2 magnitude (HR_C6000) +// _writeReg(0x00, 0x46, 0x85); // Mod1 magnitude (HR_C6000) +// _writeReg(0x00, 0x04, 0x1F); // Bias value for TX, Q-channel +// _writeReg(0x00, 0x35, 0x1E); // FM modulation factor +// _writeReg(0x00, 0x3F, 0x04); // FM Limiting modulation factor (HR_C6000) _writeReg(0x00, 0x34, 0x3C); // Enable pre-emphasis, 25kHz bandwidth _writeReg(0x00, 0x3E, 0x08); // "FM Modulation frequency deviation coefficient at the receiving end" (HR_C6000) _writeReg(0x00, 0x37, 0xC2); // Unknown register diff --git a/platform/drivers/baseband/HR-C5000_MD3x0.h b/platform/drivers/baseband/HR-C5000_MD3x0.h index 80959daa..26835a7c 100644 --- a/platform/drivers/baseband/HR-C5000_MD3x0.h +++ b/platform/drivers/baseband/HR-C5000_MD3x0.h @@ -51,6 +51,19 @@ void C5000_terminate(); */ void C5000_setModOffset(uint8_t offset); +/** + * Set values for two-point modulation amplitude adjustment. These values + * usually are stored in radio calibration data. + * @param iMag: value for modulation offset adjustment. + */ +void C5000_setModAmplitude(uint8_t iAmp, uint8_t qAmp); + +/** + * Set value for FM-mode modulation factor, a value dependent on bandwidth. + * @param mf: value for FM modulation factor. + */ +void C5000_setModFactor(uint8_t mf); + /** * Configure HR_C5000 chipset for DMR operation. */ diff --git a/platform/drivers/baseband/rtx_MD3x0.c b/platform/drivers/baseband/rtx_MD3x0.c index e89eba35..018f1b62 100644 --- a/platform/drivers/baseband/rtx_MD3x0.c +++ b/platform/drivers/baseband/rtx_MD3x0.c @@ -18,28 +18,190 @@ * along with this program; if not, see * ***************************************************************************/ -#include +#include +#include #include +#include +#include +#include +#include +#include #include -#include "rtx.h" -#include "pll_MD3x0.h" -#include "ADC1_MDxx380.h" +#include +#include #include "HR-C5000_MD3x0.h" +#include "pll_MD3x0.h" -const freq_t IF_FREQ = 49950000.0f; /* Intermediate frequency: 49.95MHz */ +#include "toneGenerator_MDx.h" +#include -freq_t rxFreq = 430000000.0f; -freq_t txFreq = 430000000.0f; - -void _setApcTv(uint16_t value) +struct rtxStatus_t { - DAC->DHR12R1 = value; + uint8_t opMode : 2, /**< Operating mode (FM, DMR, ...) */ + bandwidth : 2, /**< Channel bandwidth */ + txDisable : 1, /**< Disable TX operation */ + opStatus : 3; /**< RTX status (OFF, RX, TX) */ + + freq_t rxFrequency; /**< RX frequency, in Hz */ + freq_t txFrequency; /**< TX frequency, in Hz */ + + float txPower; /**< TX power, in W */ + uint8_t sqlLevel; /**< Squelch opening level */ + + tone_t rxTone; /**< RX CTC/DCS tone */ + tone_t txTone; /**< TX CTC/DCS tone */ } +rtxStatus; + +OS_Q cfgMailbox; +const md3x0Calib_t *calData; +const freq_t IF_FREQ = 49950000; /* Intermediate frequency: 49.95MHz */ + +static void printUnsignedInt(unsigned int x) +{ + static const char hexdigits[]="0123456789abcdef"; + char result[]="0x........\r\n"; + for(int i=9;i>=2;i--) + { + result[i]=hexdigits[x & 0xf]; + x>>=4; + } + puts(result); +} + +/* + * Helper functions to reduce code mess. They all access 'rtxStatus' + * internally. + */ +void _setBandwidth() +{ + /* Override bandwidth configuration for digital modes */ + uint8_t bandwidth = BW_12_5; + if(rtxStatus.opMode == FM) bandwidth = rtxStatus.bandwidth; + + switch(bandwidth) + { + case BW_12_5: + gpio_setPin(WN_SW); + C5000_setModFactor(0x1E); + break; + + case BW_20: + gpio_clearPin(WN_SW); + C5000_setModFactor(0x30); + break; + + case BW_25: + gpio_clearPin(WN_SW); + C5000_setModFactor(0x3C); + break; + + default: + break; + } +} + +void _setOpMode() +{ + switch(rtxStatus.opMode) + { + case FM: + gpio_clearPin(DMR_SW); + gpio_setPin(FM_SW); + C5000_fmMode(); + break; + + case DMR: + gpio_clearPin(FM_SW); + gpio_setPin(DMR_SW); + //C5000_dmrMode(); + break; + + default: + break; + } +} + +void _enableTxStage() +{ +// if(rtxStatus.txDisable == 1) return; + + gpio_clearPin(RX_STG_EN); + + gpio_setPin(RF_APC_SW); + gpio_clearPin(VCOVCC_SW); + + pll_setFrequency(rtxStatus.txFrequency, 5); + + /* + * Set transmit power. Initial setting is 1W, overridden to 5W if tx power + * is greater than 1W. + * TODO: increase granularity + */ + const uint8_t *paramPtr = calData->txLowPower; + if(rtxStatus.txPower > 1.0f) paramPtr = calData->txHighPower; + uint8_t apc = interpCalParameter(rtxStatus.txFrequency, calData->txFreq, + paramPtr, 9); + DAC->DHR12L1 = apc * 0xFF; + + gpio_setPin(TX_STG_EN); + rtxStatus.opStatus = TX; +} + +void _enableRxStage() +{ + gpio_clearPin(TX_STG_EN); + + gpio_clearPin(RF_APC_SW); + gpio_setPin(VCOVCC_SW); + + pll_setFrequency(((float) rtxStatus.rxFrequency - IF_FREQ), 5); + + /* Set tuning voltage for input filter */ + uint8_t tuneVoltage = interpCalParameter(rtxStatus.rxFrequency, + calData->rxFreq, + calData->rxSensitivity, 9); + DAC->DHR12L1 = tuneVoltage * 0xFF; + + gpio_setPin(RX_STG_EN); + rtxStatus.opStatus = RX; +} + +void _disableRtxStages() +{ + gpio_clearPin(TX_STG_EN); + gpio_clearPin(RX_STG_EN); + rtxStatus.opStatus = OFF; +} + +void _updateC5000IQparams() +{ + const uint8_t *Ical = calData->sendIrange; + if(rtxStatus.opMode == FM) Ical = calData->analogSendIrange; + uint8_t I = interpCalParameter(rtxStatus.txFrequency, calData->txFreq, Ical, + 9); + + const uint8_t *Qcal = calData->sendQrange; + if(rtxStatus.opMode == FM) Qcal = calData->analogSendQrange; + uint8_t Q = interpCalParameter(rtxStatus.txFrequency, calData->txFreq, Qcal, + 9); + + C5000_setModAmplitude(I, Q); +} + + void rtx_init() { + /* Create the message queue for RTX configuration */ + OS_ERR err; + OSQCreate((OS_Q *) &cfgMailbox, + (CPU_CHAR *) "", + (OS_MSG_QTY) 1, + (OS_ERR *) &err); + /* - * Configure GPIOs + * Configure RTX GPIOs */ gpio_setMode(PLL_PWR, OUTPUT); gpio_setMode(VCOVCC_SW, OUTPUT); @@ -59,6 +221,19 @@ void rtx_init() gpio_clearPin(TX_STG_EN); /* Disable TX power stage */ gpio_clearPin(RX_STG_EN); /* Disable RX input stage */ + /* + * Configure audio control GPIOs + */ + gpio_setMode(SPK_MUTE, OUTPUT); + gpio_setMode(AMP_EN, OUTPUT); + gpio_setMode(FM_MUTE, OUTPUT); + gpio_setMode(MIC_PWR, OUTPUT); + + gpio_setPin(MIC_PWR); /* Turn on microphone */ + gpio_setPin(AMP_EN); /* Turn on audio amplifier */ + gpio_setPin(FM_MUTE); /* Unmute path from AF_out to amplifier */ + gpio_clearPin(SPK_MUTE); /* Mute speaker */ + /* * Configure and enble DAC */ @@ -70,6 +245,11 @@ void rtx_init() DAC->DHR12R2 = 0; DAC->DHR12R1 = 0; + /* + * Load calibration data + */ + calData = ((const md3x0Calib_t *) platform_getCalibrationData()); + /* * Enable and configure PLL */ @@ -84,9 +264,22 @@ void rtx_init() /* * Modulation bias settings */ - const uint8_t mod2_bias = 0x60; /* TODO use calibration */ - DAC->DHR12R2 = mod2_bias*4 + 0x600; /* Original FW does this */ - C5000_setModOffset(mod2_bias); + DAC->DHR12R2 = (calData->freqAdjustMid)*4 + 0x600; /* Original FW does this */ + C5000_setModOffset(calData->freqAdjustMid); + + /* + * Default initialisation for rtx status + */ + rtxStatus.opMode = FM; + rtxStatus.bandwidth = BW_25; + rtxStatus.txDisable = 0; + rtxStatus.opStatus = OFF; + rtxStatus.rxFrequency = 430000000; + rtxStatus.txFrequency = 430000000; + rtxStatus.txPower = 0.0f; + rtxStatus.sqlLevel = 1; + rtxStatus.rxTone = 0; + rtxStatus.txTone = 0; } void rtx_terminate() @@ -100,109 +293,90 @@ void rtx_terminate() gpio_clearPin(TX_STG_EN); /* Disable TX power stage */ gpio_clearPin(RX_STG_EN); /* Disable RX input stage */ + gpio_clearPin(MIC_PWR); /* Turn off microphone */ + gpio_clearPin(AMP_EN); /* Turn off audio amplifier */ + gpio_clearPin(FM_MUTE); /* Mute path from AF_out to amplifier */ + DAC->DHR12R2 = 0; DAC->DHR12R1 = 0; RCC->APB1ENR &= ~RCC_APB1ENR_DACEN; + } -void rtx_setTxFreq(freq_t freq) +void rtx_configure(const rtxConfig_t *cfg) { - txFreq = freq; -} + OS_ERR err; + OSQPost((OS_Q *) &cfgMailbox, + (void *) cfg, + (OS_MSG_SIZE) sizeof(rtxConfig_t *), + (OS_OPT ) OS_OPT_POST_FIFO, + (OS_ERR *) &err); -void rtx_setRxFreq(freq_t freq) -{ - rxFreq = freq; -} - -void rtx_setFuncmode(enum funcmode mode) -{ - switch(mode) + /* + * In case message queue is not empty, flush the old and unread configuration + * and post the new one. + */ + if(err == OS_ERR_Q_MAX) { - case OFF: - gpio_clearPin(TX_STG_EN); - gpio_clearPin(RX_STG_EN); - break; + OSQFlush((OS_Q *) &cfgMailbox, + (OS_ERR *) &err); - case RX: - gpio_clearPin(TX_STG_EN); - - gpio_clearPin(RF_APC_SW); - gpio_setPin(VCOVCC_SW); - pll_setFrequency(rxFreq - IF_FREQ, 5); - _setApcTv(0x956); /* TODO use calibration */ - - gpio_setPin(RX_STG_EN); - break; - - case TX: - gpio_clearPin(RX_STG_EN); - - gpio_setPin(RF_APC_SW); - gpio_clearPin(VCOVCC_SW); - pll_setFrequency(txFreq, 5); - - _setApcTv(0x02); - - gpio_setPin(TX_STG_EN); - break; - - default: - /* TODO */ - break; + OSQPost((OS_Q *) &cfgMailbox, + (void *) cfg, + (OS_MSG_SIZE) sizeof(rtxConfig_t *), + (OS_OPT ) OS_OPT_POST_FIFO, + (OS_ERR *) &err); } } -void rtx_setToneRx(enum tone t) +void rtx_taskFunc() { - /* TODO */ -} + OS_ERR err; + OS_MSG_SIZE size; + void *msg = OSQPend((OS_Q *) &cfgMailbox, + (OS_TICK ) 0, + (OS_OPT ) OS_OPT_PEND_NON_BLOCKING, + (OS_MSG_SIZE *) &size, + (CPU_TS *) NULL, + (OS_ERR *) &err); -void rtx_setToneTx(enum tone t) -{ - /* TODO */ -} - -void rtx_setBandwidth(enum bw b) -{ - switch(b) + if((err == OS_ERR_NONE) && (msg != NULL)) { - case BW_25: - gpio_clearPin(WN_SW); - break; + uint8_t tmp = rtxStatus.opStatus; + memcpy(&rtxStatus, msg, sizeof(rtxConfig_t)); + free(msg); + rtxStatus.opStatus = tmp; - case BW_12_5: - gpio_setPin(WN_SW); - break; + _setOpMode(); + _setBandwidth(); + _updateC5000IQparams(); - default: - /* TODO */ - break; + /* TODO: temporarily force to RX mode if rtx is off. */ + if(rtxStatus.opStatus == OFF) _enableRx(); + } + + if(platform_getPttStatus() && (rtxStatus.opStatus != TX)) + { + _disableRtxStages(); + toneGen_toneOn(); + gpio_setPin(MIC_PWR); + C5000_startAnalogTx(); + _enableTxStage(); + platform_ledOn(RED); + } + + if(!platform_getPttStatus() && (rtxStatus.opStatus == TX)) + { + _disableRtxStages(); + gpio_clearPin(MIC_PWR); + toneGen_toneOff(); + C5000_stopAnalogTx(); + _enableRxStage(); + platform_ledOff(RED); } } float rtx_getRssi() { - return adc1_getMeasurement(1); -} - -void rtx_setOpmode(enum opmode mode) -{ - switch(mode) - { - case FM: - gpio_clearPin(DMR_SW); - gpio_setPin(FM_SW); - C5000_fmMode(); - break; - - case DMR: - gpio_clearPin(FM_SW); - gpio_setPin(DMR_SW); - break; - - default: - /* TODO */ - break; - } + return 0; } diff --git a/platform/targets/MD-380/hwconfig.h b/platform/targets/MD-380/hwconfig.h index a4cd2a16..237f0b47 100644 --- a/platform/targets/MD-380/hwconfig.h +++ b/platform/targets/MD-380/hwconfig.h @@ -117,5 +117,6 @@ #define AMP_EN GPIOB,9 #define SPK_MUTE GPIOB,8 #define FM_MUTE GPIOE,13 +#define MIC_PWR GPIOA,14 #endif