kopia lustrzana https://github.com/open-ham/OpenGD77
479 wiersze
12 KiB
C
479 wiersze
12 KiB
C
/*
|
|
* Copyright (C)2019 Roger Clark. VK3KYY / G4KYF
|
|
*
|
|
* 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 2 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, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
#include "hardware/HR-C6000.h"
|
|
#include "functions/settings.h"
|
|
#include "functions/sound.h"
|
|
#include "user_interface/menuSystem.h"
|
|
#include "user_interface/uiUtilities.h"
|
|
#include "user_interface/uiLocalisation.h"
|
|
|
|
#include "interfaces/clockManager.h"
|
|
|
|
typedef enum
|
|
{
|
|
TXSTOP_TIMEOUT,
|
|
TXSTOP_RX_ONLY,
|
|
TXSTOP_OUT_OF_BAND
|
|
} txTerminationReason_t;
|
|
|
|
static void updateScreen(void);
|
|
static void handleEvent(uiEvent_t *ev);
|
|
static void handleTxTermination(uiEvent_t *ev, txTerminationReason_t reason);
|
|
|
|
static const int PIT_COUNTS_PER_SECOND = 10000;
|
|
static int timeInSeconds;
|
|
static uint32_t nextSecondPIT;
|
|
static bool isShowingLastHeard;
|
|
static bool startBeepPlayed;
|
|
static uint32_t m = 0, micm = 0, mto = 0;
|
|
static uint32_t xmitErrorTimer = 0;
|
|
static bool keepScreenShownOnError = false;
|
|
static bool pttWasReleased = false;
|
|
static bool isTransmittingTone = false;
|
|
|
|
|
|
|
|
menuStatus_t menuTxScreen(uiEvent_t *ev, bool isFirstRun)
|
|
{
|
|
|
|
if (isFirstRun)
|
|
{
|
|
voicePromptsTerminate();
|
|
startBeepPlayed = false;
|
|
uiDataGlobal.Scan.active = false;
|
|
isTransmittingTone = false;
|
|
isShowingLastHeard = false;
|
|
keepScreenShownOnError = false;
|
|
timeInSeconds = 0;
|
|
pttWasReleased = false;
|
|
|
|
if (trxGetMode() == RADIO_MODE_DIGITAL)
|
|
{
|
|
clockManagerSetRunMode(kAPP_PowerModeHsrun);
|
|
}
|
|
|
|
// If the user was currently entering a new frequency and the PTT get pressed, "leave" that input screen.
|
|
if (uiDataGlobal.FreqEnter.index > 0)
|
|
{
|
|
freqEnterReset();
|
|
updateScreen();
|
|
}
|
|
|
|
if (((currentChannelData->flag4 & 0x04) == 0x00) && ((nonVolatileSettings.txFreqLimited == BAND_LIMITS_NONE) || trxCheckFrequencyInAmateurBand(currentChannelData->txFreq)))
|
|
{
|
|
nextSecondPIT = PITCounter + PIT_COUNTS_PER_SECOND;
|
|
timeInSeconds = currentChannelData->tot * 15;
|
|
|
|
LEDs_PinWrite(GPIO_LEDgreen, Pin_LEDgreen, 0);
|
|
LEDs_PinWrite(GPIO_LEDred, Pin_LEDred, 1);
|
|
|
|
txstopdelay = 0;
|
|
clearIsWakingState();
|
|
if (trxGetMode() == RADIO_MODE_ANALOG)
|
|
{
|
|
trxSetTxCSS(currentChannelData->txTone);
|
|
trxSetTX();
|
|
}
|
|
else
|
|
{
|
|
// RADIO_MODE_DIGITAL
|
|
if (!((slot_state >= DMR_STATE_REPEATER_WAKE_1) && (slot_state <= DMR_STATE_REPEATER_WAKE_3)) )
|
|
{
|
|
trxSetTX();
|
|
}
|
|
}
|
|
|
|
updateScreen();
|
|
}
|
|
else
|
|
{
|
|
handleTxTermination(ev, (((currentChannelData->flag4 & 0x04) != 0x00) ? TXSTOP_RX_ONLY : TXSTOP_OUT_OF_BAND));
|
|
}
|
|
|
|
m = micm = ev->time;
|
|
}
|
|
else
|
|
{
|
|
|
|
#if defined(PLATFORM_GD77S)
|
|
uiChannelModeHeartBeatActivityForGD77S(ev);
|
|
#endif
|
|
|
|
// Keep displaying the "RX Only" or "Out Of Band" error message
|
|
if (xmitErrorTimer > 0)
|
|
{
|
|
// Wait the voice ends, then count-down 200ms;
|
|
if (nonVolatileSettings.audioPromptMode >= AUDIO_PROMPT_MODE_VOICE_LEVEL_1)
|
|
{
|
|
if (voicePromptsIsPlaying())
|
|
{
|
|
xmitErrorTimer = (20 * 10U);
|
|
return MENU_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
xmitErrorTimer--;
|
|
|
|
if (xmitErrorTimer == 0)
|
|
{
|
|
ev->buttons &= ~BUTTON_PTT; // prevent screen lockout if the operator keeps pressing the PTT button.
|
|
}
|
|
else
|
|
{
|
|
return MENU_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (trxTransmissionEnabled && (getIsWakingState() == WAKING_MODE_NONE))
|
|
{
|
|
if (PITCounter >= nextSecondPIT)
|
|
{
|
|
if (currentChannelData->tot == 0)
|
|
{
|
|
timeInSeconds++;
|
|
}
|
|
else
|
|
{
|
|
timeInSeconds--;
|
|
if (timeInSeconds <= (nonVolatileSettings.txTimeoutBeepX5Secs * 5))
|
|
{
|
|
if ((timeInSeconds % 5) == 0)
|
|
{
|
|
soundSetMelody(MELODY_KEY_BEEP);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((currentChannelData->tot != 0) && (timeInSeconds == 0))
|
|
{
|
|
handleTxTermination(ev, TXSTOP_TIMEOUT);
|
|
keepScreenShownOnError = true;
|
|
}
|
|
else
|
|
{
|
|
if (!isShowingLastHeard)
|
|
{
|
|
updateScreen();
|
|
}
|
|
}
|
|
|
|
nextSecondPIT = PITCounter + PIT_COUNTS_PER_SECOND;
|
|
}
|
|
else
|
|
{
|
|
int mode = trxGetMode();
|
|
|
|
if (mode == RADIO_MODE_DIGITAL)
|
|
{
|
|
if ((nonVolatileSettings.beepOptions & BEEP_TX_START) &&
|
|
(startBeepPlayed == false) && (trxIsTransmitting == true)
|
|
&& (melody_play == NULL))
|
|
{
|
|
startBeepPlayed = true;// set this even if the beep is not actaully played because of the vox, as otherwise this code will get continuously run
|
|
// If VOX is running, don't send a beep as it will reset its the trigger status.
|
|
if ((voxIsEnabled() == false) || (voxIsEnabled() && (voxIsTriggered() == false)))
|
|
{
|
|
soundSetMelody(MELODY_DMR_TX_START_BEEP);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Do not update Mic level on Timeout.
|
|
if ((((currentChannelData->tot != 0) && (timeInSeconds == 0)) == false) && (ev->time - micm) > 100)
|
|
{
|
|
if (mode == RADIO_MODE_DIGITAL)
|
|
{
|
|
uiUtilityDrawDMRMicLevelBarGraph();
|
|
}
|
|
else
|
|
{
|
|
uiUtilityDrawFMMicLevelBarGraph();
|
|
}
|
|
|
|
ucRenderRows(1, 2);
|
|
micm = ev->time;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// Timeout happened, postpone going further otherwise timeout
|
|
// screen won't be visible at all.
|
|
if (((currentChannelData->tot != 0) && (timeInSeconds == 0)) || keepScreenShownOnError)
|
|
{
|
|
// Wait the voice ends, then count-down 500ms;
|
|
if (nonVolatileSettings.audioPromptMode >= AUDIO_PROMPT_MODE_VOICE_LEVEL_1)
|
|
{
|
|
if (voicePromptsIsPlaying())
|
|
{
|
|
mto = ev->time;
|
|
return MENU_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if ((ev->time - mto) < 500)
|
|
{
|
|
return MENU_STATUS_SUCCESS;
|
|
}
|
|
|
|
keepScreenShownOnError = false;
|
|
ev->buttons &= ~BUTTON_PTT; // prevent screen lockout if the operator keeps pressing the PTT button.
|
|
}
|
|
|
|
// Once the PTT key has been released, it is forbidden to re-key before the whole TX chain
|
|
// has finished (see the first statement in handleEvent())
|
|
// That's important in DMR mode, otherwise quickly press/release the PTT key will left
|
|
// the system in an unexpected state (RED led on, displayed TXScreen, but PA off).
|
|
// It doesn't have any impact on FM mode.
|
|
if (((ev->buttons & BUTTON_PTT) == 0) && (pttWasReleased == false))
|
|
{
|
|
pttWasReleased = true;
|
|
}
|
|
|
|
if ((ev->buttons & BUTTON_PTT) && pttWasReleased)
|
|
{
|
|
ev->buttons &= ~BUTTON_PTT;
|
|
}
|
|
//
|
|
|
|
|
|
// Got an event, or
|
|
if (ev->hasEvent || // PTT released, Timeout triggered,
|
|
( (((ev->buttons & BUTTON_PTT) == 0) || ((currentChannelData->tot != 0) && (timeInSeconds == 0))) ||
|
|
// or waiting for DMR ending (meanwhile, updating every 100ms)
|
|
((trxTransmissionEnabled == false) && ((ev->time - m) > 100))))
|
|
{
|
|
handleEvent(ev);
|
|
m = ev->time;
|
|
}
|
|
else
|
|
{
|
|
if ((getIsWakingState() == WAKING_MODE_FAILED) && (trxTransmissionEnabled == true))
|
|
{
|
|
trxTransmissionEnabled = false;
|
|
handleTxTermination(ev, TXSTOP_TIMEOUT);
|
|
keepScreenShownOnError = true;
|
|
}
|
|
}
|
|
}
|
|
return MENU_STATUS_SUCCESS;
|
|
}
|
|
|
|
bool menuTxScreenDisplaysLastHeard(void)
|
|
{
|
|
return isShowingLastHeard;
|
|
}
|
|
|
|
static void updateScreen(void)
|
|
{
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
if (menuDataGlobal.controlData.stack[0] == UI_VFO_MODE)
|
|
{
|
|
uiVFOModeUpdateScreen(timeInSeconds);
|
|
}
|
|
else
|
|
{
|
|
uiChannelModeUpdateScreen(timeInSeconds);
|
|
}
|
|
|
|
if (nonVolatileSettings.backlightMode != BACKLIGHT_MODE_BUTTONS)
|
|
{
|
|
displayLightOverrideTimeout(-1);
|
|
}
|
|
}
|
|
|
|
static void handleEvent(uiEvent_t *ev)
|
|
{
|
|
// Xmiting ends (normal or timeouted)
|
|
if (((ev->buttons & BUTTON_PTT) == 0)
|
|
|| ((currentChannelData->tot != 0) && (timeInSeconds == 0)))
|
|
{
|
|
if (trxTransmissionEnabled)
|
|
{
|
|
trxTransmissionEnabled = false;
|
|
isTransmittingTone = false;
|
|
|
|
if (trxGetMode() == RADIO_MODE_ANALOG)
|
|
{
|
|
// In analog mode. Stop transmitting immediately
|
|
LEDs_PinWrite(GPIO_LEDred, Pin_LEDred, 0);
|
|
|
|
// Need to wrap this in Task Critical to avoid bus contention on the I2C bus.
|
|
trxSetRxCSS(currentChannelData->rxTone);
|
|
//taskENTER_CRITICAL();
|
|
trxActivateRx();
|
|
trxIsTransmitting = false;
|
|
//taskEXIT_CRITICAL();
|
|
|
|
menuSystemPopPreviousMenu();
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN; // we need immediate redraw
|
|
}
|
|
else
|
|
{
|
|
if (isShowingLastHeard)
|
|
{
|
|
isShowingLastHeard = false;
|
|
updateScreen();
|
|
}
|
|
}
|
|
// When not in analog mode, only the trxIsTransmitting flag is cleared
|
|
// This screen keeps getting called via the handleEvent function and goes into the else clause - below.
|
|
}
|
|
else
|
|
{
|
|
// In DMR mode, wait for the DMR system to finish before exiting
|
|
if (trxIsTransmitting == false)
|
|
{
|
|
if ((nonVolatileSettings.beepOptions & BEEP_TX_STOP) && (melody_play == NULL))
|
|
{
|
|
soundSetMelody(MELODY_DMR_TX_STOP_BEEP);
|
|
}
|
|
|
|
LEDs_PinWrite(GPIO_LEDred, Pin_LEDred, 0);
|
|
|
|
// If there is a signal, lit the Green LED
|
|
if ((LEDs_PinRead(GPIO_LEDgreen, Pin_LEDgreen) == 0) && (trxCarrierDetected() || (getAudioAmpStatus() & AUDIO_AMP_MODE_RF)))
|
|
{
|
|
LEDs_PinWrite(GPIO_LEDgreen, Pin_LEDgreen, 1);
|
|
}
|
|
|
|
if (trxGetMode() == RADIO_MODE_DIGITAL)
|
|
{
|
|
clockManagerSetRunMode(kAPP_PowerModeRun);
|
|
}
|
|
|
|
menuSystemPopPreviousMenu();
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN; // we need immediate redraw
|
|
}
|
|
}
|
|
|
|
// Waiting for TX to end, no need to go further.
|
|
return;
|
|
}
|
|
|
|
// Key action while xmitting (ANALOG), Tone triggering
|
|
if (!isTransmittingTone && ((ev->buttons & BUTTON_PTT) != 0) && trxTransmissionEnabled && (trxGetMode() == RADIO_MODE_ANALOG))
|
|
{
|
|
// Send 1750Hz
|
|
if (BUTTONCHECK_DOWN(ev, BUTTON_SK2))
|
|
{
|
|
isTransmittingTone = true;
|
|
trxSetTone1(1750);
|
|
trxSelectVoiceChannel(AT1846_VOICE_CHANNEL_TONE1);
|
|
enableAudioAmp(AUDIO_AMP_MODE_RF);
|
|
GPIO_PinWrite(GPIO_RX_audio_mux, Pin_RX_audio_mux, 1);
|
|
}
|
|
else
|
|
{ // Send DTMF
|
|
int keyval = menuGetKeypadKeyValue(ev, false);
|
|
|
|
if (keyval != 99)
|
|
{
|
|
trxSetDTMF(keyval);
|
|
isTransmittingTone = true;
|
|
trxSelectVoiceChannel(AT1846_VOICE_CHANNEL_DTMF);
|
|
enableAudioAmp(AUDIO_AMP_MODE_RF);
|
|
GPIO_PinWrite(GPIO_RX_audio_mux, Pin_RX_audio_mux, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Stop xmitting Tone
|
|
if (isTransmittingTone && (BUTTONCHECK_DOWN(ev, BUTTON_SK2) == 0) && ((ev->keys.key == 0) || (ev->keys.event & KEY_MOD_UP)))
|
|
{
|
|
isTransmittingTone = false;
|
|
trxSelectVoiceChannel(AT1846_VOICE_CHANNEL_MIC);
|
|
disableAudioAmp(AUDIO_AMP_MODE_RF);
|
|
}
|
|
|
|
if ((trxGetMode() == RADIO_MODE_DIGITAL) && BUTTONCHECK_SHORTUP(ev, BUTTON_SK1) && (trxTransmissionEnabled == true))
|
|
{
|
|
isShowingLastHeard = !isShowingLastHeard;
|
|
if (isShowingLastHeard)
|
|
{
|
|
menuLastHeardInit();
|
|
menuLastHeardUpdateScreen(false, false, false);
|
|
}
|
|
else
|
|
{
|
|
updateScreen();
|
|
}
|
|
}
|
|
|
|
// Forward key events to LH screen, if shown
|
|
if (isShowingLastHeard && (ev->events & KEY_EVENT))
|
|
{
|
|
menuLastHeardHandleEvent(ev);
|
|
}
|
|
|
|
}
|
|
|
|
static void handleTxTermination(uiEvent_t *ev, txTerminationReason_t reason)
|
|
{
|
|
PTTToggledDown = false;
|
|
voxReset();
|
|
|
|
ucClearBuf();
|
|
|
|
voicePromptsTerminate();
|
|
voicePromptsInit();
|
|
|
|
ucDrawRoundRectWithDropShadow(4, 4, 120, (DISPLAY_SIZE_Y - 6), 5, true);
|
|
|
|
switch (reason)
|
|
{
|
|
case TXSTOP_RX_ONLY:
|
|
case TXSTOP_OUT_OF_BAND:
|
|
ucPrintCentered(4, currentLanguage->error, FONT_SIZE_4);
|
|
|
|
voicePromptsAppendLanguageString(¤tLanguage->error);
|
|
voicePromptsAppendPrompt(PROMPT_SILENCE);
|
|
|
|
if ((currentChannelData->flag4 & 0x04) != 0x00)
|
|
{
|
|
ucPrintCentered((DISPLAY_SIZE_Y - 24), currentLanguage->rx_only, FONT_SIZE_3);
|
|
voicePromptsAppendLanguageString(¤tLanguage->rx_only);
|
|
}
|
|
else
|
|
{
|
|
ucPrintCentered((DISPLAY_SIZE_Y - 24), currentLanguage->out_of_band, FONT_SIZE_3);
|
|
voicePromptsAppendLanguageString(¤tLanguage->out_of_band);
|
|
}
|
|
xmitErrorTimer = (100 * 10U);
|
|
break;
|
|
|
|
case TXSTOP_TIMEOUT:
|
|
ucPrintCentered(16, currentLanguage->timeout, FONT_SIZE_4);
|
|
voicePromptsAppendLanguageString(¤tLanguage->timeout);
|
|
mto = ev->time;
|
|
break;
|
|
}
|
|
|
|
ucRender();
|
|
displayLightOverrideTimeout(-1);
|
|
|
|
if (nonVolatileSettings.audioPromptMode < AUDIO_PROMPT_MODE_VOICE_LEVEL_1)
|
|
{
|
|
soundSetMelody((reason == TXSTOP_TIMEOUT) ? MELODY_TX_TIMEOUT_BEEP : MELODY_ERROR_BEEP);
|
|
}
|
|
else
|
|
{
|
|
voicePromptsPlay();
|
|
}
|
|
}
|