diff --git a/meson.build b/meson.build index 21b535d9..6002f227 100644 --- a/meson.build +++ b/meson.build @@ -20,7 +20,8 @@ openrtx_src = ['openrtx/src/bootstrap.c', 'openrtx/src/battery.c', 'openrtx/src/graphics.c', 'openrtx/src/input.c', - 'openrtx/src/calibUtils.c'] + 'openrtx/src/calibUtils.c', + 'openrtx/src/rtx.c'] ## Replace main executable with platform test @@ -191,7 +192,7 @@ md380_src = src + stm32f405_src + ['platform/drivers/display/HX8353_MDx.c', 'platform/drivers/ADC/ADC1_MDx.c', 'platform/drivers/tones/toneGenerator_MDx.c', 'platform/drivers/baseband/pll_MD3x0.c', - 'platform/drivers/baseband/rtx_MD3x0.c', + 'platform/drivers/baseband/radio_MD3x0.c', 'platform/drivers/baseband/HR-C5000_MD3x0.c', 'platform/targets/MD-380/platform.c'] diff --git a/openrtx/include/interfaces/radio.h b/openrtx/include/interfaces/radio.h index 08f3b268..2f661d68 100644 --- a/openrtx/include/interfaces/radio.h +++ b/openrtx/include/interfaces/radio.h @@ -85,7 +85,7 @@ void radio_disableRtx(); /** * */ -void radio_updateCalibrationParams(const rtxStatus_t rtxCfg); +void radio_updateCalibrationParams(const rtxStatus_t *rtxCfg); /** * diff --git a/openrtx/include/rtx.h b/openrtx/include/rtx.h index 5cfaaf34..2147dbf3 100644 --- a/openrtx/include/rtx.h +++ b/openrtx/include/rtx.h @@ -21,10 +21,10 @@ #ifndef RTX_H #define RTX_H -#include -#include -#include #include +#include +#include +#include typedef struct { diff --git a/openrtx/src/rtx.c b/openrtx/src/rtx.c new file mode 100644 index 00000000..55eedbfa --- /dev/null +++ b/openrtx/src/rtx.c @@ -0,0 +1,295 @@ +/*************************************************************************** + * Copyright (C) 2020 by Federico Amedeo Izzo IU2NUO, * + * Niccolò Izzo IU2KIN * + * Frederik Saraci IU2NRO * + * Silvano Seva IU2KWO * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see * + ***************************************************************************/ + +#include +#include +#include +#include + +#include +#include + + +OS_MUTEX *cfgMutex; /* Mutex for incoming config messages */ +OS_Q cfgMailbox; /* Queue for incoming config messages */ + +rtxStatus_t rtxStatus; /* RTX driver status */ + +bool sqlOpen; /* Flag for squlech open/close */ + + +/* + * These functions below provide a basic API for audio path management. They + * will be removed once the audio driver is set up. + */ + +void _afCtrlInit() +{ + #if defined(PLATFORM_MD380) || defined(PLATFORM_MD390) + gpio_setMode(SPK_MUTE, OUTPUT); + gpio_setMode(AMP_EN, OUTPUT); + gpio_setMode(FM_MUTE, OUTPUT); + gpio_setMode(MIC_PWR, OUTPUT); + #elif defined(PLATFORM_GD77) || defined(PLATFORM_DM1801) + gpio_setMode(AUDIO_AMP_EN, OUTPUT); + #endif +} + +void _afCtrlSpeaker(bool enable) +{ + if(enable) + { + #if defined(PLATFORM_MD380) || defined(PLATFORM_MD390) + gpio_setPin(AMP_EN); + gpio_setPin(FM_MUTE); + gpio_clearPin(SPK_MUTE); + #elif defined(PLATFORM_GD77) || defined(PLATFORM_DM1801) + gpio_setPin(AUDIO_AMP_EN); + #endif + } + else + { + #if defined(PLATFORM_MD380) || defined(PLATFORM_MD390) + gpio_clearPin(AMP_EN); + gpio_clearPin(FM_MUTE); + gpio_setPin(SPK_MUTE); + #elif defined(PLATFORM_GD77) || defined(PLATFORM_DM1801) + gpio_clearPin(AUDIO_AMP_EN); + #endif + } +} + +void _afCtrlMic(bool enable) +{ + if(enable) + { + #if defined(PLATFORM_MD380) || defined(PLATFORM_MD390) + gpio_setPin(MIC_PWR); + #endif + } + else + { + #if defined(PLATFORM_MD380) || defined(PLATFORM_MD390) + gpio_clearPin(MIC_PWR); + #endif + } +} + +void _afCtrlTerminate() +{ + _afCtrlMic(false); + _afCtrlSpeaker(false); +} + + +void rtx_init(OS_MUTEX *m) +{ + /* Initialise mutex for configuration access */ + cfgMutex = m; + + /* Create the message queue for RTX configuration */ + OS_ERR err; + OSQCreate((OS_Q *) &cfgMailbox, + (CPU_CHAR *) "", + (OS_MSG_QTY) 1, + (OS_ERR *) &err); + + /* + * 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.rxToneEn = 0; + rtxStatus.rxTone = 0; + rtxStatus.txToneEn = 0; + rtxStatus.txTone = 0; + + sqlOpen = false; + + /* + * Initialise low-level platform-specific driver + */ + radio_init(); +} + +void rtx_terminate() +{ + radio_terminate(); +} + +void rtx_configure(const rtxStatus_t *cfg) +{ + OS_ERR err; + OSQPost((OS_Q *) &cfgMailbox, + (void *) cfg, + (OS_MSG_SIZE) sizeof(rtxStatus_t *), + (OS_OPT ) OS_OPT_POST_FIFO, + (OS_ERR *) &err); + + /* + * In case message queue is not empty, flush the old and unread configuration + * and post the new one. + */ + if(err == OS_ERR_Q_MAX) + { + OSQFlush((OS_Q *) &cfgMailbox, + (OS_ERR *) &err); + + OSQPost((OS_Q *) &cfgMailbox, + (void *) cfg, + (OS_MSG_SIZE) sizeof(rtxStatus_t *), + (OS_OPT ) OS_OPT_POST_FIFO, + (OS_ERR *) &err); + } +} + +rtxStatus_t rtx_getCurrentStatus() +{ + return rtxStatus; +} + +void rtx_taskFunc() +{ + 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); + + /* Configuration update logic */ + if((err == OS_ERR_NONE) && (msg != NULL)) + { + /* Try locking mutex for read access */ + OSMutexPend(cfgMutex, 0, OS_OPT_PEND_NON_BLOCKING, NULL, &err); + + if(err == OS_ERR_NONE) + { + /* Copy new configuration and override opStatus flags */ + uint8_t tmp = rtxStatus.opStatus; + memcpy(&rtxStatus, msg, sizeof(rtxStatus_t)); + rtxStatus.opStatus = tmp; + + /* Done, release mutex */ + OSMutexPost(cfgMutex, OS_OPT_POST_NONE, &err); + + /* Update HW configuration */ + radio_setOpmode(rtxStatus.opMode); + radio_setBandwidth(rtxStatus.bandwidth); + radio_setCSS(rtxStatus.rxTone, rtxStatus.txTone); + radio_updateCalibrationParams(&rtxStatus); + + /* Update VCO frequency, it could have been changed meanwhile */ + if(rtxStatus.opStatus == TX) + { + radio_setVcoFrequency(rtxStatus.txFrequency, true); + } + else + { + radio_setVcoFrequency(rtxStatus.rxFrequency, false); + } + } + } + + /* TODO: temporarily force to RX mode if rtx is off. */ + if(rtxStatus.opStatus == OFF) + { + radio_setVcoFrequency(rtxStatus.rxFrequency, false); + radio_enableRx(); + rtxStatus.opStatus = RX; + } + + /* RX logic */ + if(rtxStatus.opStatus == RX) + { + /* + * RSSI-based squelch mechanism, with 15 levels from -140dBm to -70dBm + */ + float rssi = rtx_getRssi(); + float squelch = -127.0f + rtxStatus.sqlLevel * 66.0f / 15.0f; + + if((sqlOpen == false) && (rssi > (squelch + 0.1f))) + { + _afCtrlSpeaker(true); + sqlOpen = true; + } + + if((sqlOpen == true) && (rssi < (squelch - 0.1f))) + { + _afCtrlSpeaker(false); + sqlOpen = false; + } + } + + /* TX logic */ + if(platform_getPttStatus() && (rtxStatus.opStatus != TX)) + { + radio_disableRtx(); + + _afCtrlMic(true); + radio_setVcoFrequency(rtxStatus.txFrequency, true); + radio_enableTx(rtxStatus.txPower, rtxStatus.txToneEn); + + rtxStatus.opStatus = TX; + } + + if(!platform_getPttStatus() && (rtxStatus.opStatus == TX)) + { + _afCtrlMic(false); + radio_disableRtx(); + + rtxStatus.opStatus = OFF; + } + + /* Led control logic */ + switch(rtxStatus.opStatus) + { + case RX: + if(sqlOpen) + platform_ledOn(GREEN); + else + platform_ledOff(GREEN); + + break; + + case TX: + platform_ledOff(GREEN); + platform_ledOn(RED); + break; + + default: + platform_ledOff(GREEN); + platform_ledOff(RED); + break; + } +} + +float rtx_getRssi() +{ + return radio_getRssi(rtxStatus.rxFrequency); +} diff --git a/platform/drivers/baseband/radio_MD3x0.c b/platform/drivers/baseband/radio_MD3x0.c index e14752f4..e6c0968e 100644 --- a/platform/drivers/baseband/radio_MD3x0.c +++ b/platform/drivers/baseband/radio_MD3x0.c @@ -39,6 +39,8 @@ uint8_t vtune_rx = 0; /* Tuning voltage for RX input filter * uint8_t txpwr_lo = 0; /* APC voltage for TX output power control, low power */ uint8_t txpwr_hi = 0; /* APC voltage for TX output power control, high power */ +enum opmode currOpMode; /* Current operating mode, needed for TX control */ + /* * Parameters for RSSI voltage (mV) to input power (dBm) conversion. * Gain is constant, while offset values are aligned to calibration frequency @@ -149,6 +151,7 @@ void radio_setBandwidth(const enum bandwidth bw) void radio_setOpmode(const enum opmode mode) { + currOpMode = mode; switch(mode) { case FM: @@ -217,6 +220,11 @@ void radio_enableTx(const float txPower, const bool enableCss) uint8_t apc = (txPower > 1.0f) ? txpwr_hi : txpwr_lo; DAC->DHR12L1 = apc * 0xFF; + if(currOpMode == FM) + { + C5000_startAnalogTx(); + } + gpio_setPin(TX_STG_EN); if(enableCss) @@ -227,36 +235,42 @@ void radio_enableTx(const float txPower, const bool enableCss) void radio_disableRtx() { - toneGen_toneOff(); + /* If we are currently transmitting, stop tone and C5000 TX */ + if(gpio_readPin(TX_STG_EN) == 1) + { + toneGen_toneOff(); + C5000_stopAnalogTx(); + } + gpio_clearPin(TX_STG_EN); gpio_clearPin(RX_STG_EN); } -void radio_updateCalibrationParams(const rtxStatus_t rtxCfg) +void radio_updateCalibrationParams(const rtxStatus_t* rtxCfg) { /* Tuning voltage for RX input filter */ - vtune_rx = interpCalParameter(rtxCfg.rxFrequency, calData->rxFreq, + vtune_rx = interpCalParameter(rtxCfg->rxFrequency, calData->rxFreq, calData->rxSensitivity, 9); /* APC voltage for TX output power control */ - txpwr_lo = interpCalParameter(rtxCfg.txFrequency, calData->txFreq, + txpwr_lo = interpCalParameter(rtxCfg->txFrequency, calData->txFreq, calData->txLowPower, 9); - txpwr_hi = interpCalParameter(rtxCfg.txFrequency, calData->txFreq, + txpwr_hi = interpCalParameter(rtxCfg->txFrequency, calData->txFreq, calData->txHighPower, 9); /* HR_C5000 modulation amplitude */ const uint8_t *Ical = calData->sendIrange; const uint8_t *Qcal = calData->sendQrange; - if(rtxCfg.opMode == FM) + if(rtxCfg->opMode == FM) { Ical = calData->analogSendIrange; Qcal = calData->analogSendQrange; } - uint8_t I = interpCalParameter(rtxCfg.txFrequency, calData->txFreq, Ical, 9); - uint8_t Q = interpCalParameter(rtxCfg.txFrequency, calData->txFreq, Qcal, 9); + uint8_t I = interpCalParameter(rtxCfg->txFrequency, calData->txFreq, Ical, 9); + uint8_t Q = interpCalParameter(rtxCfg->txFrequency, calData->txFreq, Qcal, 9); C5000_setModAmplitude(I, Q); }