mirror of
https://github.com/OneOfEleven/uv-k5-firmware-custom.git
synced 2025-06-19 06:39:49 +03:00
Initial commit
This commit is contained in:
302
app/action.c
Normal file
302
app/action.c
Normal file
@ -0,0 +1,302 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "app/action.h"
|
||||
#include "app/app.h"
|
||||
#include "app/dtmf.h"
|
||||
#include "app/fm.h"
|
||||
#include "app/scanner.h"
|
||||
#include "audio.h"
|
||||
#include "bsp/dp32g030/gpio.h"
|
||||
#include "driver/bk1080.h"
|
||||
#include "driver/bk4819.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "functions.h"
|
||||
#include "misc.h"
|
||||
#include "settings.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
static void ACTION_FlashLight(void)
|
||||
{
|
||||
switch (gFlashLightState)
|
||||
{
|
||||
case 0:
|
||||
gFlashLightState++;
|
||||
GPIO_SetBit(&GPIOC->DATA, GPIOC_PIN_FLASHLIGHT);
|
||||
break;
|
||||
case 1:
|
||||
gFlashLightState++;
|
||||
break;
|
||||
default:
|
||||
gFlashLightState = 0;
|
||||
GPIO_ClearBit(&GPIOC->DATA, GPIOC_PIN_FLASHLIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
void ACTION_Power(void)
|
||||
{
|
||||
if (++gTxVfo->OUTPUT_POWER > OUTPUT_POWER_HIGH)
|
||||
gTxVfo->OUTPUT_POWER = OUTPUT_POWER_LOW;
|
||||
|
||||
gRequestSaveChannel = 1;
|
||||
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_POWER;
|
||||
#endif
|
||||
|
||||
gRequestDisplayScreen = gScreenToDisplay;
|
||||
}
|
||||
|
||||
static void ACTION_Monitor(void)
|
||||
{
|
||||
if (gCurrentFunction != FUNCTION_MONITOR)
|
||||
{
|
||||
RADIO_SelectVfos();
|
||||
|
||||
#ifndef DISABLE_NOAA
|
||||
if (gRxVfo->CHANNEL_SAVE >= NOAA_CHANNEL_FIRST && gIsNoaaMode)
|
||||
gNoaaChannel = gRxVfo->CHANNEL_SAVE - NOAA_CHANNEL_FIRST;
|
||||
#endif
|
||||
|
||||
RADIO_SetupRegisters(true);
|
||||
APP_StartListening(FUNCTION_MONITOR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gScanState != SCAN_OFF)
|
||||
{
|
||||
ScanPauseDelayIn10msec = 500;
|
||||
gScheduleScanListen = false;
|
||||
gScanPauseMode = true;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_NOAA
|
||||
if (gEeprom.DUAL_WATCH == DUAL_WATCH_OFF && gIsNoaaMode)
|
||||
{
|
||||
gNOAA_Countdown = 500;
|
||||
gScheduleNOAA = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
RADIO_SetupRegisters(true);
|
||||
|
||||
if (gFmRadioMode)
|
||||
{
|
||||
FM_Start();
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
}
|
||||
else
|
||||
gRequestDisplayScreen = gScreenToDisplay;
|
||||
}
|
||||
|
||||
void ACTION_Scan(bool bRestart)
|
||||
{
|
||||
if (gFmRadioMode)
|
||||
{
|
||||
if (gCurrentFunction != FUNCTION_RECEIVE && gCurrentFunction != FUNCTION_MONITOR && gCurrentFunction != FUNCTION_TRANSMIT)
|
||||
{
|
||||
uint16_t Frequency;
|
||||
|
||||
GUI_SelectNextDisplay(DISPLAY_FM);
|
||||
if (gFM_ScanState != FM_SCAN_OFF)
|
||||
{
|
||||
FM_PlayAndUpdate();
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_SCANNING_STOP;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bRestart)
|
||||
{
|
||||
gFM_AutoScan = true;
|
||||
gFM_ChannelPosition = 0;
|
||||
FM_EraseChannels();
|
||||
Frequency = gEeprom.FM_LowerLimit;
|
||||
}
|
||||
else
|
||||
{
|
||||
gFM_AutoScan = false;
|
||||
gFM_ChannelPosition = 0;
|
||||
Frequency = gEeprom.FM_FrequencyPlaying;
|
||||
}
|
||||
BK1080_GetFrequencyDeviation(Frequency);
|
||||
FM_Tune(Frequency, 1, bRestart);
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_SCANNING_BEGIN;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
if (gScreenToDisplay != DISPLAY_SCANNER)
|
||||
{
|
||||
RADIO_SelectVfos();
|
||||
|
||||
#ifndef DISABLE_NOAA
|
||||
if (IS_NOT_NOAA_CHANNEL(gRxVfo->CHANNEL_SAVE))
|
||||
#endif
|
||||
{
|
||||
GUI_SelectNextDisplay(DISPLAY_MAIN);
|
||||
if (gScanState != SCAN_OFF)
|
||||
{
|
||||
SCANNER_Stop();
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_SCANNING_STOP;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
CHANNEL_Next(true, 1);
|
||||
#ifndef DISABLE_VOICE
|
||||
AUDIO_SetVoiceID(0, VOICE_ID_SCANNING_BEGIN);
|
||||
AUDIO_PlaySingleVoice(true);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ACTION_Vox(void)
|
||||
{
|
||||
gEeprom.VOX_SWITCH = !gEeprom.VOX_SWITCH;
|
||||
gRequestSaveSettings = true;
|
||||
gFlagReconfigureVfos = true;
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_VOX;
|
||||
#endif
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
|
||||
static void ACTION_AlarmOr1750(bool b1750)
|
||||
{
|
||||
gInputBoxIndex = 0;
|
||||
gAlarmState = b1750 ? ALARM_STATE_TX1750 : ALARM_STATE_TXALARM;
|
||||
gAlarmRunningCounter = 0;
|
||||
gFlagPrepareTX = true;
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
}
|
||||
|
||||
void ACTION_FM(void)
|
||||
{
|
||||
if (gCurrentFunction != FUNCTION_TRANSMIT && gCurrentFunction != FUNCTION_MONITOR)
|
||||
{
|
||||
if (gFmRadioMode)
|
||||
{
|
||||
FM_TurnOff();
|
||||
gInputBoxIndex = 0;
|
||||
gVoxResumeCountdown = 80;
|
||||
gFlagReconfigureVfos = true;
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
return;
|
||||
}
|
||||
|
||||
RADIO_SelectVfos();
|
||||
RADIO_SetupRegisters(true);
|
||||
FM_Start();
|
||||
gInputBoxIndex = 0;
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
}
|
||||
}
|
||||
|
||||
void ACTION_Handle(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
uint8_t Short;
|
||||
uint8_t Long;
|
||||
|
||||
if (gScreenToDisplay == DISPLAY_MAIN && gDTMF_InputMode)
|
||||
{
|
||||
if (Key == KEY_SIDE1 && !bKeyHeld && bKeyPressed)
|
||||
{
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
if (gDTMF_InputIndex)
|
||||
{
|
||||
gDTMF_InputBox[--gDTMF_InputIndex] = '-';
|
||||
if (gDTMF_InputIndex)
|
||||
{
|
||||
gPttWasReleased = true;
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_CANCEL;
|
||||
#endif
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
gDTMF_InputMode = false;
|
||||
}
|
||||
|
||||
gPttWasReleased = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Key == KEY_SIDE1)
|
||||
{
|
||||
Short = gEeprom.KEY_1_SHORT_PRESS_ACTION;
|
||||
Long = gEeprom.KEY_1_LONG_PRESS_ACTION;
|
||||
}
|
||||
else
|
||||
{
|
||||
Short = gEeprom.KEY_2_SHORT_PRESS_ACTION;
|
||||
Long = gEeprom.KEY_2_LONG_PRESS_ACTION;
|
||||
}
|
||||
|
||||
if (!bKeyHeld && bKeyPressed)
|
||||
{
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bKeyHeld || bKeyPressed)
|
||||
{
|
||||
if (!bKeyHeld)
|
||||
return;
|
||||
|
||||
Short = Long;
|
||||
if (!bKeyPressed)
|
||||
return;
|
||||
}
|
||||
|
||||
switch (Short)
|
||||
{
|
||||
case 1:
|
||||
ACTION_FlashLight();
|
||||
break;
|
||||
case 2:
|
||||
ACTION_Power();
|
||||
break;
|
||||
case 3:
|
||||
ACTION_Monitor();
|
||||
break;
|
||||
case 4:
|
||||
ACTION_Scan(true);
|
||||
break;
|
||||
case 5:
|
||||
ACTION_Vox();
|
||||
break;
|
||||
case 6:
|
||||
ACTION_AlarmOr1750(false);
|
||||
break;
|
||||
case 7:
|
||||
ACTION_FM();
|
||||
break;
|
||||
case 8:
|
||||
ACTION_AlarmOr1750(true);
|
||||
break;
|
||||
}
|
||||
}
|
33
app/action.h
Normal file
33
app/action.h
Normal file
@ -0,0 +1,33 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef APP_ACTION_H
|
||||
#define APP_ACTION_H
|
||||
|
||||
#include "driver/keyboard.h"
|
||||
|
||||
//static void ACTION_FlashLight(void)
|
||||
void ACTION_Power(void);
|
||||
//static void ACTION_Monitor(void)
|
||||
void ACTION_Scan(bool bFlag);
|
||||
void ACTION_Vox(void);
|
||||
//static void ACTION_AlarmOr1750(bool b1750)
|
||||
void ACTION_FM(void);
|
||||
|
||||
void ACTION_Handle(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld);
|
||||
|
||||
#endif
|
||||
|
231
app/aircopy.c
Normal file
231
app/aircopy.c
Normal file
@ -0,0 +1,231 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef DISABLE_AIRCOPY
|
||||
|
||||
#include "app/aircopy.h"
|
||||
#include "audio.h"
|
||||
#include "driver/bk4819.h"
|
||||
#include "driver/crc.h"
|
||||
#include "driver/eeprom.h"
|
||||
#include "frequencies.h"
|
||||
#include "misc.h"
|
||||
#include "radio.h"
|
||||
#include "ui/helper.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
static const uint16_t Obfuscation[8] = {0x6C16, 0xE614, 0x912E, 0x400D, 0x3521, 0x40D5, 0x0313, 0x80E9};
|
||||
|
||||
AIRCOPY_State_t gAircopyState;
|
||||
uint16_t gAirCopyBlockNumber;
|
||||
uint16_t gErrorsDuringAirCopy;
|
||||
uint8_t gAirCopyIsSendMode;
|
||||
|
||||
uint16_t g_FSK_Buffer[36];
|
||||
|
||||
void AIRCOPY_SendMessage(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
g_FSK_Buffer[1] = (gAirCopyBlockNumber & 0x3FF) << 6;
|
||||
|
||||
EEPROM_ReadBuffer(g_FSK_Buffer[1], &g_FSK_Buffer[2], 64);
|
||||
|
||||
g_FSK_Buffer[34] = CRC_Calculate(&g_FSK_Buffer[1], 2 + 64);
|
||||
|
||||
for (i = 0; i < 34; i++)
|
||||
g_FSK_Buffer[i + 1] ^= Obfuscation[i % 8];
|
||||
|
||||
if (++gAirCopyBlockNumber >= 0x78)
|
||||
gAircopyState = AIRCOPY_COMPLETE;
|
||||
|
||||
RADIO_SetTxParameters();
|
||||
|
||||
BK4819_SendFSKData(g_FSK_Buffer);
|
||||
BK4819_SetupPowerAmplifier(0, 0);
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO5_PIN1, false);
|
||||
|
||||
gAircopySendCountdown = 30;
|
||||
}
|
||||
|
||||
void AIRCOPY_StorePacket(void)
|
||||
{
|
||||
uint16_t Status;
|
||||
|
||||
if (gFSKWriteIndex < 36)
|
||||
return;
|
||||
|
||||
gFSKWriteIndex = 0;
|
||||
gUpdateDisplay = true;
|
||||
Status = BK4819_ReadRegister(BK4819_REG_0B);
|
||||
BK4819_PrepareFSKReceive();
|
||||
|
||||
// Doc says bit 4 should be 1 = CRC OK, 0 = CRC FAIL, but original firmware checks for FAIL.
|
||||
|
||||
if ((Status & 0x0010U) == 0 && g_FSK_Buffer[0] == 0xABCD && g_FSK_Buffer[35] == 0xDCBA)
|
||||
{
|
||||
uint16_t CRC;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < 34; i++)
|
||||
g_FSK_Buffer[i + 1] ^= Obfuscation[i % 8];
|
||||
|
||||
CRC = CRC_Calculate(&g_FSK_Buffer[1], 2 + 64);
|
||||
if (g_FSK_Buffer[34] == CRC)
|
||||
{
|
||||
const uint16_t *pData;
|
||||
uint16_t Offset;
|
||||
|
||||
Offset = g_FSK_Buffer[1];
|
||||
if (Offset < 0x1E00)
|
||||
{
|
||||
pData = &g_FSK_Buffer[2];
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
EEPROM_WriteBuffer(Offset, pData);
|
||||
pData += 4;
|
||||
Offset += 8;
|
||||
}
|
||||
|
||||
if (Offset == 0x1E00)
|
||||
gAircopyState = AIRCOPY_COMPLETE;
|
||||
|
||||
gAirCopyBlockNumber++;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
gErrorsDuringAirCopy++;
|
||||
}
|
||||
|
||||
static void AIRCOPY_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if (!bKeyHeld && bKeyPressed)
|
||||
{
|
||||
uint32_t Frequency;
|
||||
unsigned int i;
|
||||
|
||||
INPUTBOX_Append(Key);
|
||||
gRequestDisplayScreen = DISPLAY_AIRCOPY;
|
||||
if (gInputBoxIndex < 6)
|
||||
{
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
gInputBoxIndex = 0;
|
||||
|
||||
NUMBER_Get(gInputBox, &Frequency);
|
||||
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
if (Frequency >= gLowerLimitFrequencyBandTable[i] && Frequency <= gUpperLimitFrequencyBandTable[i])
|
||||
{
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
#endif
|
||||
gRxVfo->Band = i;
|
||||
Frequency += 75;
|
||||
Frequency = FREQUENCY_FloorToStep(Frequency, gRxVfo->StepFrequency, 0);
|
||||
gRxVfo->ConfigRX.Frequency = Frequency;
|
||||
gRxVfo->ConfigTX.Frequency = Frequency;
|
||||
RADIO_ConfigureSquelchAndOutputPower(gRxVfo);
|
||||
gCurrentVfo = gRxVfo;
|
||||
RADIO_SetupRegisters(true);
|
||||
BK4819_SetupAircopy();
|
||||
BK4819_ResetFSK();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_AIRCOPY;
|
||||
}
|
||||
}
|
||||
|
||||
static void AIRCOPY_Key_EXIT(bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if (!bKeyHeld && bKeyPressed)
|
||||
{
|
||||
if (gInputBoxIndex == 0)
|
||||
{
|
||||
gFSKWriteIndex = 0;
|
||||
gAirCopyBlockNumber = 0;
|
||||
gErrorsDuringAirCopy = 0;
|
||||
gInputBoxIndex = 0;
|
||||
gAirCopyIsSendMode = 0;
|
||||
|
||||
BK4819_PrepareFSKReceive();
|
||||
|
||||
gAircopyState = AIRCOPY_TRANSFER;
|
||||
}
|
||||
else
|
||||
gInputBox[--gInputBoxIndex] = 10;
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_AIRCOPY;
|
||||
}
|
||||
}
|
||||
|
||||
static void AIRCOPY_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if (!bKeyHeld && bKeyPressed)
|
||||
{
|
||||
gFSKWriteIndex = 0;
|
||||
gAirCopyBlockNumber = 0;
|
||||
gInputBoxIndex = 0;
|
||||
gAirCopyIsSendMode = 1;
|
||||
g_FSK_Buffer[0] = 0xABCD;
|
||||
g_FSK_Buffer[1] = 0;
|
||||
g_FSK_Buffer[35] = 0xDCBA;
|
||||
|
||||
AIRCOPY_SendMessage();
|
||||
GUI_DisplayScreen();
|
||||
|
||||
gAircopyState = AIRCOPY_TRANSFER;
|
||||
}
|
||||
}
|
||||
|
||||
void AIRCOPY_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
switch (Key)
|
||||
{
|
||||
case KEY_0:
|
||||
case KEY_1:
|
||||
case KEY_2:
|
||||
case KEY_3:
|
||||
case KEY_4:
|
||||
case KEY_5:
|
||||
case KEY_6:
|
||||
case KEY_7:
|
||||
case KEY_8:
|
||||
case KEY_9:
|
||||
AIRCOPY_Key_DIGITS(Key, bKeyPressed, bKeyHeld);
|
||||
break;
|
||||
case KEY_MENU:
|
||||
AIRCOPY_Key_MENU(bKeyPressed, bKeyHeld);
|
||||
break;
|
||||
case KEY_EXIT:
|
||||
AIRCOPY_Key_EXIT(bKeyPressed, bKeyHeld);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
47
app/aircopy.h
Normal file
47
app/aircopy.h
Normal file
@ -0,0 +1,47 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef APP_AIRCOPY_H
|
||||
#define APP_AIRCOPY_H
|
||||
|
||||
#ifndef DISABLE_AIRCOPY
|
||||
|
||||
#include "driver/keyboard.h"
|
||||
|
||||
enum AIRCOPY_State_t
|
||||
{
|
||||
AIRCOPY_READY = 0,
|
||||
AIRCOPY_TRANSFER,
|
||||
AIRCOPY_COMPLETE
|
||||
};
|
||||
|
||||
typedef enum AIRCOPY_State_t AIRCOPY_State_t;
|
||||
|
||||
extern AIRCOPY_State_t gAircopyState;
|
||||
extern uint16_t gAirCopyBlockNumber;
|
||||
extern uint16_t gErrorsDuringAirCopy;
|
||||
extern uint8_t gAirCopyIsSendMode;
|
||||
|
||||
extern uint16_t g_FSK_Buffer[36];
|
||||
|
||||
void AIRCOPY_SendMessage(void);
|
||||
void AIRCOPY_StorePacket(void);
|
||||
void AIRCOPY_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
34
app/app.h
Normal file
34
app/app.h
Normal file
@ -0,0 +1,34 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef APP_APP_H
|
||||
#define APP_APP_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "functions.h"
|
||||
#include "radio.h"
|
||||
|
||||
void APP_EndTransmission(void);
|
||||
void CHANNEL_Next(bool bFlag, int8_t Direction);
|
||||
void APP_StartListening(FUNCTION_Type_t Function);
|
||||
void APP_SetFrequencyByStep(VFO_Info_t *pInfo, int8_t Step);
|
||||
|
||||
void APP_Update(void);
|
||||
void APP_TimeSlice10ms(void);
|
||||
void APP_TimeSlice500ms(void);
|
||||
|
||||
#endif
|
||||
|
371
app/dtmf.c
Normal file
371
app/dtmf.c
Normal file
@ -0,0 +1,371 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "app/fm.h"
|
||||
#include "app/scanner.h"
|
||||
#include "bsp/dp32g030/gpio.h"
|
||||
#include "driver/bk4819.h"
|
||||
#include "driver/eeprom.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/system.h"
|
||||
#include "dtmf.h"
|
||||
#include "external/printf/printf.h"
|
||||
#include "misc.h"
|
||||
#include "settings.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
char gDTMF_String[15];
|
||||
char gDTMF_InputBox[15];
|
||||
char gDTMF_Received[16];
|
||||
bool gIsDtmfContactValid;
|
||||
char gDTMF_ID[4];
|
||||
char gDTMF_Caller[4];
|
||||
char gDTMF_Callee[4];
|
||||
DTMF_State_t gDTMF_State;
|
||||
bool gDTMF_DecodeRing;
|
||||
uint8_t gDTMF_DecodeRingCountdown;
|
||||
uint8_t gDTMFChosenContact;
|
||||
uint8_t gDTMF_WriteIndex;
|
||||
uint8_t gDTMF_PreviousIndex;
|
||||
uint8_t gDTMF_AUTO_RESET_TIME;
|
||||
uint8_t gDTMF_InputIndex;
|
||||
bool gDTMF_InputMode;
|
||||
uint8_t gDTMF_RecvTimeout;
|
||||
DTMF_CallState_t gDTMF_CallState;
|
||||
DTMF_ReplyState_t gDTMF_ReplyState;
|
||||
DTMF_CallMode_t gDTMF_CallMode;
|
||||
bool gDTMF_IsTx;
|
||||
uint8_t gDTMF_TxStopCountdown;
|
||||
bool gDTMF_IsGroupCall;
|
||||
|
||||
bool DTMF_ValidateCodes(char *pCode, uint8_t Size)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
if (pCode[0] == 0xFF || pCode[0] == 0)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < Size; i++)
|
||||
{
|
||||
if (pCode[i] == 0xFF || pCode[i] == 0)
|
||||
{
|
||||
pCode[i] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((pCode[i] < '0' || pCode[i] > '9') && (pCode[i] < 'A' || pCode[i] > 'D') && pCode[i] != '*' && pCode[i] != '#')
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DTMF_GetContact(uint8_t Index, char *pContact)
|
||||
{
|
||||
EEPROM_ReadBuffer(0x1C00 + (Index * 16), pContact, 16);
|
||||
return ((pContact[0] - ' ') >= 0x5F) ? false : true;
|
||||
}
|
||||
|
||||
bool DTMF_FindContact(const char *pContact, char *pResult)
|
||||
{
|
||||
char Contact [16];
|
||||
uint8_t i, j;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
if (!DTMF_GetContact(i, Contact))
|
||||
return false;
|
||||
|
||||
for (j = 0; j < 3; j++)
|
||||
if (pContact[j] != Contact[j + 8])
|
||||
break;
|
||||
|
||||
if (j == 3)
|
||||
{
|
||||
memcpy(pResult, Contact, 8);
|
||||
pResult[8] = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
char DTMF_GetCharacter(uint8_t Code)
|
||||
{
|
||||
switch (Code)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
return '0' + (char)Code;
|
||||
case 10:
|
||||
return 'A';
|
||||
case 11:
|
||||
return 'B';
|
||||
case 12:
|
||||
return 'C';
|
||||
case 13:
|
||||
return 'D';
|
||||
case 14:
|
||||
return '*';
|
||||
case 15:
|
||||
return '#';
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
bool DTMF_CompareMessage(const char *pMsg, const char *pTemplate, uint8_t Size, bool bCheckGroup)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < Size; i++)
|
||||
{
|
||||
if (pMsg[i] != pTemplate[i])
|
||||
{
|
||||
if (!bCheckGroup || pMsg[i] != gEeprom.DTMF_GROUP_CALL_CODE)
|
||||
return false;
|
||||
gDTMF_IsGroupCall = true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DTMF_CheckGroupCall(const char *pMsg, uint32_t Size)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < Size; i++)
|
||||
if (pMsg[i] == gEeprom.DTMF_GROUP_CALL_CODE)
|
||||
break;
|
||||
|
||||
return (i != Size) ? true : false;
|
||||
}
|
||||
|
||||
void DTMF_Append(char Code)
|
||||
{
|
||||
if (gDTMF_InputIndex == 0)
|
||||
{
|
||||
memset(gDTMF_InputBox, '-', sizeof(gDTMF_InputBox));
|
||||
gDTMF_InputBox[14] = 0;
|
||||
}
|
||||
else
|
||||
if (gDTMF_InputIndex >= sizeof(gDTMF_InputBox))
|
||||
return;
|
||||
|
||||
gDTMF_InputBox[gDTMF_InputIndex++] = Code;
|
||||
}
|
||||
|
||||
void DTMF_HandleRequest(void)
|
||||
{
|
||||
char String[20];
|
||||
uint8_t Offset;
|
||||
|
||||
if (!gDTMF_RequestPending)
|
||||
return;
|
||||
|
||||
gDTMF_RequestPending = false;
|
||||
|
||||
if (gScanState != SCAN_OFF || gCssScanMode != CSS_SCAN_MODE_OFF)
|
||||
return;
|
||||
|
||||
if (!gRxVfo->DTMF_DECODING_ENABLE && !gSetting_KILLED)
|
||||
return;
|
||||
|
||||
if (gDTMF_WriteIndex >= 9)
|
||||
{
|
||||
Offset = gDTMF_WriteIndex - 9;
|
||||
sprintf(String, "%s%c%s", gEeprom.ANI_DTMF_ID, gEeprom.DTMF_SEPARATE_CODE, gEeprom.KILL_CODE);
|
||||
if (DTMF_CompareMessage(gDTMF_Received + Offset, String, 9, true))
|
||||
{
|
||||
if (gEeprom.PERMIT_REMOTE_KILL)
|
||||
{
|
||||
gSetting_KILLED = true;
|
||||
SETTINGS_SaveSettings();
|
||||
gDTMF_ReplyState = DTMF_REPLY_AB;
|
||||
if (gFmRadioMode)
|
||||
{
|
||||
FM_TurnOff();
|
||||
GUI_SelectNextDisplay(DISPLAY_MAIN);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gDTMF_ReplyState = DTMF_REPLY_NONE;
|
||||
}
|
||||
|
||||
gDTMF_CallState = DTMF_CALL_STATE_NONE;
|
||||
|
||||
gUpdateDisplay = true;
|
||||
gUpdateStatus = true;
|
||||
return;
|
||||
}
|
||||
|
||||
sprintf(String, "%s%c%s", gEeprom.ANI_DTMF_ID, gEeprom.DTMF_SEPARATE_CODE, gEeprom.REVIVE_CODE);
|
||||
if (DTMF_CompareMessage(gDTMF_Received + Offset, String, 9, true))
|
||||
{
|
||||
gSetting_KILLED = false;
|
||||
SETTINGS_SaveSettings();
|
||||
gDTMF_ReplyState = DTMF_REPLY_AB;
|
||||
gDTMF_CallState = DTMF_CALL_STATE_NONE;
|
||||
gUpdateDisplay = true;
|
||||
gUpdateStatus = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (gDTMF_WriteIndex >= 2)
|
||||
{
|
||||
if (DTMF_CompareMessage(gDTMF_Received + (gDTMF_WriteIndex - 2), "AB", 2, true))
|
||||
{
|
||||
gDTMF_State = DTMF_STATE_TX_SUCC;
|
||||
gUpdateDisplay = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (gDTMF_CallState == DTMF_CALL_STATE_CALL_OUT && gDTMF_CallMode == DTMF_CALL_MODE_NOT_GROUP && gDTMF_WriteIndex >= 9)
|
||||
{
|
||||
Offset = gDTMF_WriteIndex - 9;
|
||||
sprintf(String, "%s%c%s", gDTMF_String, gEeprom.DTMF_SEPARATE_CODE, "AAAAA");
|
||||
if (DTMF_CompareMessage(gDTMF_Received + Offset, String, 9, false))
|
||||
{
|
||||
gDTMF_State = DTMF_STATE_CALL_OUT_RSP;
|
||||
gUpdateDisplay = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (gSetting_KILLED || gDTMF_CallState != DTMF_CALL_STATE_NONE)
|
||||
return;
|
||||
|
||||
if (gDTMF_WriteIndex >= 7)
|
||||
{
|
||||
Offset = gDTMF_WriteIndex - 7;
|
||||
sprintf(String, "%s%c", gEeprom.ANI_DTMF_ID, gEeprom.DTMF_SEPARATE_CODE);
|
||||
gDTMF_IsGroupCall = false;
|
||||
if (DTMF_CompareMessage(gDTMF_Received + Offset, String, 4, true))
|
||||
{
|
||||
gDTMF_CallState = DTMF_CALL_STATE_RECEIVED;
|
||||
memcpy(gDTMF_Callee, gDTMF_Received + Offset, 3);
|
||||
memcpy(gDTMF_Caller, gDTMF_Received + Offset + 4, 3);
|
||||
|
||||
gUpdateDisplay = true;
|
||||
|
||||
switch (gEeprom.DTMF_DECODE_RESPONSE)
|
||||
{
|
||||
case 3:
|
||||
gDTMF_DecodeRing = true;
|
||||
gDTMF_DecodeRingCountdown = 20;
|
||||
// Fallthrough
|
||||
case 2:
|
||||
gDTMF_ReplyState = DTMF_REPLY_AAAAA;
|
||||
break;
|
||||
case 1:
|
||||
gDTMF_DecodeRing = true;
|
||||
gDTMF_DecodeRingCountdown = 20;
|
||||
break;
|
||||
default:
|
||||
gDTMF_DecodeRing = false;
|
||||
gDTMF_ReplyState = DTMF_REPLY_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (gDTMF_IsGroupCall)
|
||||
gDTMF_ReplyState = DTMF_REPLY_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DTMF_Reply(void)
|
||||
{
|
||||
char String[20];
|
||||
const char *pString;
|
||||
uint16_t Delay;
|
||||
|
||||
switch (gDTMF_ReplyState)
|
||||
{
|
||||
case DTMF_REPLY_ANI:
|
||||
if (gDTMF_CallMode == DTMF_CALL_MODE_DTMF)
|
||||
{
|
||||
pString = gDTMF_String;
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(String, "%s%c%s", gDTMF_String, gEeprom.DTMF_SEPARATE_CODE, gEeprom.ANI_DTMF_ID);
|
||||
pString = String;
|
||||
}
|
||||
break;
|
||||
|
||||
case DTMF_REPLY_AB:
|
||||
pString = "AB";
|
||||
break;
|
||||
|
||||
case DTMF_REPLY_AAAAA:
|
||||
sprintf(String, "%s%c%s", gEeprom.ANI_DTMF_ID, gEeprom.DTMF_SEPARATE_CODE, "AAAAA");
|
||||
pString = String;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (gDTMF_CallState != DTMF_CALL_STATE_NONE || (gCurrentVfo->DTMF_PTT_ID_TX_MODE != PTT_ID_BOT && gCurrentVfo->DTMF_PTT_ID_TX_MODE != PTT_ID_BOTH))
|
||||
{
|
||||
gDTMF_ReplyState = DTMF_REPLY_NONE;
|
||||
return;
|
||||
}
|
||||
pString = gEeprom.DTMF_UP_CODE;
|
||||
break;
|
||||
}
|
||||
|
||||
gDTMF_ReplyState = DTMF_REPLY_NONE;
|
||||
|
||||
Delay = gEeprom.DTMF_PRELOAD_TIME;
|
||||
if (gEeprom.DTMF_SIDE_TONE)
|
||||
{
|
||||
GPIO_SetBit(&GPIOC->DATA, GPIOC_PIN_AUDIO_PATH);
|
||||
gEnableSpeaker = true;
|
||||
|
||||
Delay = gEeprom.DTMF_PRELOAD_TIME;
|
||||
if (gEeprom.DTMF_PRELOAD_TIME < 60)
|
||||
Delay = 60;
|
||||
}
|
||||
SYSTEM_DelayMs(Delay);
|
||||
|
||||
BK4819_EnterDTMF_TX(gEeprom.DTMF_SIDE_TONE);
|
||||
|
||||
BK4819_PlayDTMFString(
|
||||
pString,
|
||||
1,
|
||||
gEeprom.DTMF_FIRST_CODE_PERSIST_TIME,
|
||||
gEeprom.DTMF_HASH_CODE_PERSIST_TIME,
|
||||
gEeprom.DTMF_CODE_PERSIST_TIME,
|
||||
gEeprom.DTMF_CODE_INTERVAL_TIME);
|
||||
|
||||
GPIO_ClearBit(&GPIOC->DATA, GPIOC_PIN_AUDIO_PATH);
|
||||
|
||||
gEnableSpeaker = false;
|
||||
|
||||
BK4819_ExitDTMF_TX(false);
|
||||
}
|
||||
|
90
app/dtmf.h
Normal file
90
app/dtmf.h
Normal file
@ -0,0 +1,90 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef DTMF_H
|
||||
#define DTMF_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum DTMF_State_t {
|
||||
DTMF_STATE_0 = 0U,
|
||||
DTMF_STATE_TX_SUCC = 1U,
|
||||
DTMF_STATE_CALL_OUT_RSP = 2U,
|
||||
};
|
||||
|
||||
typedef enum DTMF_State_t DTMF_State_t;
|
||||
|
||||
enum DTMF_CallState_t {
|
||||
DTMF_CALL_STATE_NONE = 0U,
|
||||
DTMF_CALL_STATE_CALL_OUT = 1U,
|
||||
DTMF_CALL_STATE_RECEIVED = 2U,
|
||||
};
|
||||
|
||||
typedef enum DTMF_CallState_t DTMF_CallState_t;
|
||||
|
||||
enum DTMF_ReplyState_t {
|
||||
DTMF_REPLY_NONE = 0U,
|
||||
DTMF_REPLY_ANI = 1U,
|
||||
DTMF_REPLY_AB = 2U,
|
||||
DTMF_REPLY_AAAAA = 3U,
|
||||
};
|
||||
|
||||
typedef enum DTMF_ReplyState_t DTMF_ReplyState_t;
|
||||
|
||||
enum DTMF_CallMode_t {
|
||||
DTMF_CALL_MODE_NOT_GROUP = 0U,
|
||||
DTMF_CALL_MODE_GROUP = 1U,
|
||||
DTMF_CALL_MODE_DTMF = 2U,
|
||||
};
|
||||
|
||||
typedef enum DTMF_CallMode_t DTMF_CallMode_t;
|
||||
|
||||
extern char gDTMF_String[15];
|
||||
extern char gDTMF_InputBox[15];
|
||||
extern char gDTMF_Received[16];
|
||||
extern bool gIsDtmfContactValid;
|
||||
extern char gDTMF_ID[4];
|
||||
extern char gDTMF_Caller[4];
|
||||
extern char gDTMF_Callee[4];
|
||||
extern DTMF_State_t gDTMF_State;
|
||||
extern bool gDTMF_DecodeRing;
|
||||
extern uint8_t gDTMF_DecodeRingCountdown;
|
||||
extern uint8_t gDTMFChosenContact;
|
||||
extern uint8_t gDTMF_WriteIndex;
|
||||
extern uint8_t gDTMF_PreviousIndex;
|
||||
extern uint8_t gDTMF_AUTO_RESET_TIME;
|
||||
extern uint8_t gDTMF_InputIndex;
|
||||
extern bool gDTMF_InputMode;
|
||||
extern uint8_t gDTMF_RecvTimeout;
|
||||
extern DTMF_CallState_t gDTMF_CallState;
|
||||
extern DTMF_ReplyState_t gDTMF_ReplyState;
|
||||
extern DTMF_CallMode_t gDTMF_CallMode;
|
||||
extern bool gDTMF_IsTx;
|
||||
extern uint8_t gDTMF_TxStopCountdown;
|
||||
|
||||
bool DTMF_ValidateCodes(char *pCode, uint8_t Size);
|
||||
bool DTMF_GetContact(uint8_t Index, char *pContact);
|
||||
bool DTMF_FindContact(const char *pContact, char *pResult);
|
||||
char DTMF_GetCharacter(uint8_t Code);
|
||||
bool DTMF_CompareMessage(const char *pDTMF, const char *pTemplate, uint8_t Size, bool bFlag);
|
||||
bool DTMF_CheckGroupCall(const char *pDTMF, uint32_t Size);
|
||||
void DTMF_Append(char Code);
|
||||
void DTMF_HandleRequest(void);
|
||||
void DTMF_Reply(void);
|
||||
|
||||
#endif
|
||||
|
563
app/fm.c
Normal file
563
app/fm.c
Normal file
@ -0,0 +1,563 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "app/action.h"
|
||||
#include "app/fm.h"
|
||||
#include "app/generic.h"
|
||||
#include "audio.h"
|
||||
#include "bsp/dp32g030/gpio.h"
|
||||
#include "driver/bk1080.h"
|
||||
#include "driver/eeprom.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "functions.h"
|
||||
#include "misc.h"
|
||||
#include "settings.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
uint16_t gFM_Channels[20];
|
||||
bool gFmRadioMode;
|
||||
uint8_t gFmRadioCountdown;
|
||||
volatile uint16_t gFmPlayCountdown;
|
||||
volatile int8_t gFM_ScanState;
|
||||
bool gFM_AutoScan;
|
||||
uint8_t gFM_ChannelPosition;
|
||||
bool gFM_FoundFrequency;
|
||||
bool gFM_AutoScan;
|
||||
uint8_t gFM_ResumeCountdown;
|
||||
uint16_t gFM_RestoreCountdown;
|
||||
|
||||
bool FM_CheckValidChannel(uint8_t Channel)
|
||||
{
|
||||
if (Channel < 20 && (gFM_Channels[Channel] >= 760 && gFM_Channels[Channel] < 1080)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t FM_FindNextChannel(uint8_t Channel, uint8_t Direction)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < 20; i++) {
|
||||
Channel %= 20;
|
||||
if (FM_CheckValidChannel(Channel)) {
|
||||
return Channel;
|
||||
}
|
||||
Channel += Direction;
|
||||
}
|
||||
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
int FM_ConfigureChannelState(void)
|
||||
{
|
||||
uint8_t Channel;
|
||||
|
||||
gEeprom.FM_FrequencyPlaying = gEeprom.FM_SelectedFrequency;
|
||||
if (gEeprom.FM_IsMrMode) {
|
||||
Channel = FM_FindNextChannel(gEeprom.FM_SelectedChannel, FM_CHANNEL_UP);
|
||||
if (Channel == 0xFF) {
|
||||
gEeprom.FM_IsMrMode = false;
|
||||
return -1;
|
||||
}
|
||||
gEeprom.FM_SelectedChannel = Channel;
|
||||
gEeprom.FM_FrequencyPlaying = gFM_Channels[Channel];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FM_TurnOff(void)
|
||||
{
|
||||
gFmRadioMode = false;
|
||||
gFM_ScanState = FM_SCAN_OFF;
|
||||
gFM_RestoreCountdown = 0;
|
||||
GPIO_ClearBit(&GPIOC->DATA, GPIOC_PIN_AUDIO_PATH);
|
||||
gEnableSpeaker = false;
|
||||
BK1080_Init(0, false);
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
|
||||
void FM_EraseChannels(void)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t Template[8];
|
||||
|
||||
memset(Template, 0xFF, sizeof(Template));
|
||||
for (i = 0; i < 5; i++) {
|
||||
EEPROM_WriteBuffer(0x0E40 + (i * 8), Template);
|
||||
}
|
||||
|
||||
memset(gFM_Channels, 0xFF, sizeof(gFM_Channels));
|
||||
}
|
||||
|
||||
void FM_Tune(uint16_t Frequency, int8_t Step, bool bFlag)
|
||||
{
|
||||
GPIO_ClearBit(&GPIOC->DATA, GPIOC_PIN_AUDIO_PATH);
|
||||
gEnableSpeaker = false;
|
||||
if (gFM_ScanState == FM_SCAN_OFF) {
|
||||
gFmPlayCountdown = 120;
|
||||
} else {
|
||||
gFmPlayCountdown = 10;
|
||||
}
|
||||
gScheduleFM = false;
|
||||
gFM_FoundFrequency = false;
|
||||
gAskToSave = false;
|
||||
gAskToDelete = false;
|
||||
gEeprom.FM_FrequencyPlaying = Frequency;
|
||||
if (!bFlag) {
|
||||
Frequency += Step;
|
||||
if (Frequency < gEeprom.FM_LowerLimit) {
|
||||
Frequency = gEeprom.FM_UpperLimit;
|
||||
} else if (Frequency > gEeprom.FM_UpperLimit) {
|
||||
Frequency = gEeprom.FM_LowerLimit;
|
||||
}
|
||||
gEeprom.FM_FrequencyPlaying = Frequency;
|
||||
}
|
||||
|
||||
gFM_ScanState = Step;
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying);
|
||||
}
|
||||
|
||||
void FM_PlayAndUpdate(void)
|
||||
{
|
||||
gFM_ScanState = FM_SCAN_OFF;
|
||||
if (gFM_AutoScan) {
|
||||
gEeprom.FM_IsMrMode = true;
|
||||
gEeprom.FM_SelectedChannel = 0;
|
||||
}
|
||||
FM_ConfigureChannelState();
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying);
|
||||
SETTINGS_SaveFM();
|
||||
gFmPlayCountdown = 0;
|
||||
gScheduleFM = false;
|
||||
gAskToSave = false;
|
||||
GPIO_SetBit(&GPIOC->DATA, GPIOC_PIN_AUDIO_PATH);
|
||||
gEnableSpeaker = true;
|
||||
}
|
||||
|
||||
int FM_CheckFrequencyLock(uint16_t Frequency, uint16_t LowerLimit)
|
||||
{
|
||||
uint16_t Test2;
|
||||
uint16_t Deviation;
|
||||
int ret = -1;
|
||||
|
||||
Test2 = BK1080_ReadRegister(BK1080_REG_07);
|
||||
// This is supposed to be a signed value, but above function is unsigned
|
||||
Deviation = BK1080_REG_07_GET_FREQD(Test2);
|
||||
|
||||
if (BK1080_REG_07_GET_SNR(Test2) >= 2) {
|
||||
uint16_t Status;
|
||||
|
||||
Status = BK1080_ReadRegister(BK1080_REG_10);
|
||||
if ((Status & BK1080_REG_10_MASK_AFCRL) == BK1080_REG_10_AFCRL_NOT_RAILED && BK1080_REG_10_GET_RSSI(Status) >= 10) {
|
||||
// if (Deviation > -281 && Deviation < 280)
|
||||
if (Deviation < 280 || Deviation > 3815) {
|
||||
// not BLE(less than or equal)
|
||||
if (Frequency > LowerLimit && (Frequency - BK1080_BaseFrequency) == 1) {
|
||||
if (BK1080_FrequencyDeviation & 0x800) {
|
||||
goto Bail;
|
||||
}
|
||||
if (BK1080_FrequencyDeviation < 20) {
|
||||
goto Bail;
|
||||
}
|
||||
}
|
||||
// not BLT(less than)
|
||||
if (Frequency >= LowerLimit && (BK1080_BaseFrequency - Frequency) == 1) {
|
||||
if ((BK1080_FrequencyDeviation & 0x800) == 0) {
|
||||
goto Bail;
|
||||
}
|
||||
// if (BK1080_FrequencyDeviation > -21) {
|
||||
if (BK1080_FrequencyDeviation > 4075) {
|
||||
goto Bail;
|
||||
}
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bail:
|
||||
BK1080_FrequencyDeviation = Deviation;
|
||||
BK1080_BaseFrequency = Frequency;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void FM_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
#define STATE_FREQ_MODE 0
|
||||
#define STATE_MR_MODE 1
|
||||
#define STATE_SAVE 2
|
||||
|
||||
if (!bKeyHeld && bKeyPressed) {
|
||||
if (!gWasFKeyPressed) {
|
||||
uint8_t State;
|
||||
|
||||
if (gAskToDelete) {
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
if (gAskToSave) {
|
||||
State = STATE_SAVE;
|
||||
} else {
|
||||
if (gFM_ScanState != FM_SCAN_OFF) {
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
if (gEeprom.FM_IsMrMode) {
|
||||
State = STATE_MR_MODE;
|
||||
} else {
|
||||
State = STATE_FREQ_MODE;
|
||||
}
|
||||
}
|
||||
INPUTBOX_Append(Key);
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
if (State == STATE_FREQ_MODE) {
|
||||
if (gInputBoxIndex == 1) {
|
||||
if (gInputBox[0] > 1) {
|
||||
gInputBox[1] = gInputBox[0];
|
||||
gInputBox[0] = 0;
|
||||
gInputBoxIndex = 2;
|
||||
}
|
||||
} else if (gInputBoxIndex > 3) {
|
||||
uint32_t Frequency;
|
||||
|
||||
gInputBoxIndex = 0;
|
||||
NUMBER_Get(gInputBox, &Frequency);
|
||||
Frequency = Frequency / 10000;
|
||||
if (Frequency < gEeprom.FM_LowerLimit || gEeprom.FM_UpperLimit < Frequency) {
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
return;
|
||||
}
|
||||
gEeprom.FM_SelectedFrequency = (uint16_t)Frequency;
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
#endif
|
||||
gEeprom.FM_FrequencyPlaying = gEeprom.FM_SelectedFrequency;
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying);
|
||||
gRequestSaveFM = true;
|
||||
return;
|
||||
}
|
||||
} else if (gInputBoxIndex == 2) {
|
||||
uint8_t Channel;
|
||||
|
||||
gInputBoxIndex = 0;
|
||||
Channel = ((gInputBox[0] * 10) + gInputBox[1]) - 1;
|
||||
if (State == STATE_MR_MODE) {
|
||||
if (FM_CheckValidChannel(Channel))
|
||||
{
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
#endif
|
||||
gEeprom.FM_SelectedChannel = Channel;
|
||||
gEeprom.FM_FrequencyPlaying = gFM_Channels[Channel];
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying);
|
||||
gRequestSaveFM = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (Channel < 20)
|
||||
{
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
#endif
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
gInputBoxIndex = 0;
|
||||
gFM_ChannelPosition = Channel;
|
||||
return;
|
||||
}
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
gWasFKeyPressed = false;
|
||||
gUpdateStatus = true;
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
switch (Key) {
|
||||
case KEY_0:
|
||||
ACTION_FM();
|
||||
break;
|
||||
|
||||
case KEY_1:
|
||||
gEeprom.FM_IsMrMode = !gEeprom.FM_IsMrMode;
|
||||
if (!FM_ConfigureChannelState()) {
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying);
|
||||
gRequestSaveFM = true;
|
||||
} else {
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case KEY_2:
|
||||
ACTION_Scan(true);
|
||||
break;
|
||||
|
||||
case KEY_3:
|
||||
ACTION_Scan(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void FM_Key_EXIT(bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if (bKeyHeld) {
|
||||
return;
|
||||
}
|
||||
if (!bKeyPressed) {
|
||||
return;
|
||||
}
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
if (gFM_ScanState == FM_SCAN_OFF) {
|
||||
if (gInputBoxIndex == 0) {
|
||||
if (!gAskToSave && !gAskToDelete) {
|
||||
ACTION_FM();
|
||||
return;
|
||||
}
|
||||
gAskToSave = false;
|
||||
gAskToDelete = false;
|
||||
} else {
|
||||
gInputBoxIndex--;
|
||||
gInputBox[gInputBoxIndex] = 10;
|
||||
if (gInputBoxIndex) {
|
||||
if (gInputBoxIndex != 1) {
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
return;
|
||||
}
|
||||
if (gInputBox[0] != 0) {
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
return;
|
||||
}
|
||||
}
|
||||
gInputBoxIndex = 0;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_CANCEL;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
FM_PlayAndUpdate();
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_SCANNING_STOP;
|
||||
#endif
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
}
|
||||
|
||||
static void FM_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if (bKeyHeld) {
|
||||
return;
|
||||
}
|
||||
if (!bKeyPressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
if (gFM_ScanState == FM_SCAN_OFF) {
|
||||
if (!gEeprom.FM_IsMrMode) {
|
||||
if (gAskToSave) {
|
||||
gFM_Channels[gFM_ChannelPosition] = gEeprom.FM_FrequencyPlaying;
|
||||
gAskToSave = false;
|
||||
gRequestSaveFM = true;
|
||||
} else {
|
||||
gAskToSave = true;
|
||||
}
|
||||
} else {
|
||||
if (gAskToDelete) {
|
||||
gFM_Channels[gEeprom.FM_SelectedChannel] = 0xFFFF;
|
||||
FM_ConfigureChannelState();
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying);
|
||||
gRequestSaveFM = true;
|
||||
gAskToDelete = false;
|
||||
} else {
|
||||
gAskToDelete = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (gFM_AutoScan || !gFM_FoundFrequency) {
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
gInputBoxIndex = 0;
|
||||
return;
|
||||
} else if (gAskToSave) {
|
||||
gFM_Channels[gFM_ChannelPosition] = gEeprom.FM_FrequencyPlaying;
|
||||
gAskToSave = false;
|
||||
gRequestSaveFM = true;
|
||||
} else {
|
||||
gAskToSave = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void FM_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Step)
|
||||
{
|
||||
if (bKeyHeld || !bKeyPressed) {
|
||||
if (gInputBoxIndex) {
|
||||
return;
|
||||
}
|
||||
if (!bKeyPressed) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (gInputBoxIndex) {
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
}
|
||||
if (gAskToSave) {
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
gFM_ChannelPosition = NUMBER_AddWithWraparound(gFM_ChannelPosition, Step, 0, 19);
|
||||
return;
|
||||
}
|
||||
if (gFM_ScanState != FM_SCAN_OFF) {
|
||||
if (gFM_AutoScan) {
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
FM_Tune(gEeprom.FM_FrequencyPlaying, Step, false);
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
return;
|
||||
}
|
||||
if (gEeprom.FM_IsMrMode) {
|
||||
uint8_t Channel;
|
||||
|
||||
Channel = FM_FindNextChannel(gEeprom.FM_SelectedChannel + Step, Step);
|
||||
if (Channel == 0xFF || gEeprom.FM_SelectedChannel == Channel) {
|
||||
goto Bail;
|
||||
}
|
||||
gEeprom.FM_SelectedChannel = Channel;
|
||||
gEeprom.FM_FrequencyPlaying = gFM_Channels[Channel];
|
||||
} else {
|
||||
uint16_t Frequency;
|
||||
|
||||
Frequency = gEeprom.FM_SelectedFrequency + Step;
|
||||
if (Frequency < gEeprom.FM_LowerLimit) {
|
||||
Frequency = gEeprom.FM_UpperLimit;
|
||||
} else if (Frequency > gEeprom.FM_UpperLimit) {
|
||||
Frequency = gEeprom.FM_LowerLimit;
|
||||
}
|
||||
gEeprom.FM_FrequencyPlaying = Frequency;
|
||||
gEeprom.FM_SelectedFrequency = gEeprom.FM_FrequencyPlaying;
|
||||
}
|
||||
gRequestSaveFM = true;
|
||||
|
||||
Bail:
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying);
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
}
|
||||
|
||||
void FM_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
switch (Key) {
|
||||
case KEY_0: case KEY_1: case KEY_2: case KEY_3:
|
||||
case KEY_4: case KEY_5: case KEY_6: case KEY_7:
|
||||
case KEY_8: case KEY_9:
|
||||
FM_Key_DIGITS(Key, bKeyPressed, bKeyHeld);
|
||||
break;
|
||||
case KEY_MENU:
|
||||
FM_Key_MENU(bKeyPressed, bKeyHeld);
|
||||
return;
|
||||
case KEY_UP:
|
||||
FM_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
FM_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
|
||||
break;;
|
||||
case KEY_EXIT:
|
||||
FM_Key_EXIT(bKeyPressed, bKeyHeld);
|
||||
break;
|
||||
case KEY_F:
|
||||
GENERIC_Key_F(bKeyPressed, bKeyHeld);
|
||||
break;
|
||||
case KEY_PTT:
|
||||
GENERIC_Key_PTT(bKeyPressed);
|
||||
break;
|
||||
default:
|
||||
if (!bKeyHeld && bKeyPressed) {
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FM_Play(void)
|
||||
{
|
||||
if (!FM_CheckFrequencyLock(gEeprom.FM_FrequencyPlaying, gEeprom.FM_LowerLimit)) {
|
||||
if (!gFM_AutoScan) {
|
||||
gFmPlayCountdown = 0;
|
||||
gFM_FoundFrequency = true;
|
||||
if (!gEeprom.FM_IsMrMode) {
|
||||
gEeprom.FM_SelectedFrequency = gEeprom.FM_FrequencyPlaying;
|
||||
}
|
||||
GPIO_SetBit(&GPIOC->DATA, GPIOC_PIN_AUDIO_PATH);
|
||||
gEnableSpeaker = true;
|
||||
GUI_SelectNextDisplay(DISPLAY_FM);
|
||||
return;
|
||||
}
|
||||
if (gFM_ChannelPosition < 20) {
|
||||
gFM_Channels[gFM_ChannelPosition++] = gEeprom.FM_FrequencyPlaying;
|
||||
}
|
||||
if (gFM_ChannelPosition >= 20) {
|
||||
FM_PlayAndUpdate();
|
||||
GUI_SelectNextDisplay(DISPLAY_FM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (gFM_AutoScan && gEeprom.FM_FrequencyPlaying >= gEeprom.FM_UpperLimit) {
|
||||
FM_PlayAndUpdate();
|
||||
} else {
|
||||
FM_Tune(gEeprom.FM_FrequencyPlaying, gFM_ScanState, false);
|
||||
}
|
||||
|
||||
GUI_SelectNextDisplay(DISPLAY_FM);
|
||||
}
|
||||
|
||||
void FM_Start(void)
|
||||
{
|
||||
gFmRadioMode = true;
|
||||
gFM_ScanState = FM_SCAN_OFF;
|
||||
gFM_RestoreCountdown = 0;
|
||||
BK1080_Init(gEeprom.FM_FrequencyPlaying, true);
|
||||
GPIO_SetBit(&GPIOC->DATA, GPIOC_PIN_AUDIO_PATH);
|
||||
gEnableSpeaker = true;
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
|
59
app/fm.h
Normal file
59
app/fm.h
Normal file
@ -0,0 +1,59 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef APP_FM_H
|
||||
#define APP_FM_H
|
||||
|
||||
#include "driver/keyboard.h"
|
||||
|
||||
#define FM_CHANNEL_UP 0x01
|
||||
#define FM_CHANNEL_DOWN 0xFF
|
||||
|
||||
enum {
|
||||
FM_SCAN_OFF = 0U,
|
||||
};
|
||||
|
||||
extern uint16_t gFM_Channels[20];
|
||||
extern bool gFmRadioMode;
|
||||
extern uint8_t gFmRadioCountdown;
|
||||
extern volatile uint16_t gFmPlayCountdown;
|
||||
extern volatile int8_t gFM_ScanState;
|
||||
extern bool gFM_AutoScan;
|
||||
extern uint8_t gFM_ChannelPosition;
|
||||
// Doubts about whether this should be signed or not.
|
||||
extern uint16_t gFM_FrequencyDeviation;
|
||||
extern bool gFM_FoundFrequency;
|
||||
extern bool gFM_AutoScan;
|
||||
extern uint8_t gFM_ResumeCountdown;
|
||||
extern uint16_t gFM_RestoreCountdown;
|
||||
|
||||
bool FM_CheckValidChannel(uint8_t Channel);
|
||||
uint8_t FM_FindNextChannel(uint8_t Channel, uint8_t Direction);
|
||||
int FM_ConfigureChannelState(void);
|
||||
void FM_TurnOff(void);
|
||||
void FM_EraseChannels(void);
|
||||
|
||||
void FM_Tune(uint16_t Frequency, int8_t Step, bool bFlag);
|
||||
void FM_PlayAndUpdate(void);
|
||||
int FM_CheckFrequencyLock(uint16_t Frequency, uint16_t LowerLimit);
|
||||
|
||||
void FM_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld);
|
||||
|
||||
void FM_Play(void);
|
||||
void FM_Start(void);
|
||||
|
||||
#endif
|
||||
|
223
app/generic.c
Normal file
223
app/generic.c
Normal file
@ -0,0 +1,223 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/fm.h"
|
||||
#include "app/generic.h"
|
||||
#include "app/menu.h"
|
||||
#include "app/scanner.h"
|
||||
#include "audio.h"
|
||||
#include "driver/keyboard.h"
|
||||
#include "dtmf.h"
|
||||
#include "external/printf/printf.h"
|
||||
#include "functions.h"
|
||||
#include "misc.h"
|
||||
#include "settings.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
void GENERIC_Key_F(bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if (gInputBoxIndex)
|
||||
{
|
||||
if (!bKeyHeld && bKeyPressed)
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bKeyHeld || !bKeyPressed)
|
||||
{
|
||||
if (bKeyHeld || bKeyPressed)
|
||||
{
|
||||
if (!bKeyHeld)
|
||||
return;
|
||||
|
||||
if (!bKeyPressed)
|
||||
return;
|
||||
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = gEeprom.KEY_LOCK ? VOICE_ID_UNLOCK : VOICE_ID_LOCK;
|
||||
#endif
|
||||
|
||||
gEeprom.KEY_LOCK = !gEeprom.KEY_LOCK;
|
||||
gRequestSaveSettings = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((gFmRadioMode || gScreenToDisplay != DISPLAY_MAIN) && gScreenToDisplay != DISPLAY_FM)
|
||||
return;
|
||||
|
||||
gWasFKeyPressed = !gWasFKeyPressed;
|
||||
|
||||
#ifndef DISABLE_VOICE
|
||||
if (!gWasFKeyPressed)
|
||||
gAnotherVoiceID = VOICE_ID_CANCEL;
|
||||
#endif
|
||||
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gScreenToDisplay != DISPLAY_FM)
|
||||
{
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (gFM_ScanState == FM_SCAN_OFF)
|
||||
{
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
gBeepToPlay = BEEP_440HZ_500MS;
|
||||
gPttWasReleased = true;
|
||||
}
|
||||
}
|
||||
|
||||
void GENERIC_Key_PTT(bool bKeyPressed)
|
||||
{
|
||||
gInputBoxIndex = 0;
|
||||
|
||||
if (!bKeyPressed)
|
||||
{
|
||||
if (gScreenToDisplay == DISPLAY_MAIN)
|
||||
{
|
||||
if (gCurrentFunction == FUNCTION_TRANSMIT)
|
||||
{
|
||||
if (gFlagEndTransmission)
|
||||
{
|
||||
FUNCTION_Select(FUNCTION_FOREGROUND);
|
||||
}
|
||||
else
|
||||
{
|
||||
APP_EndTransmission();
|
||||
|
||||
if (gEeprom.REPEATER_TAIL_TONE_ELIMINATION == 0)
|
||||
FUNCTION_Select(FUNCTION_FOREGROUND);
|
||||
else
|
||||
gRTTECountdown = gEeprom.REPEATER_TAIL_TONE_ELIMINATION * 10;
|
||||
}
|
||||
|
||||
gFlagEndTransmission = false;
|
||||
gVOX_NoiseDetected = false;
|
||||
}
|
||||
|
||||
RADIO_SetVfoState(VFO_STATE_NORMAL);
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
return;
|
||||
}
|
||||
|
||||
gInputBoxIndex = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (gScanState != SCAN_OFF)
|
||||
{
|
||||
SCANNER_Stop();
|
||||
|
||||
gPttDebounceCounter = 0;
|
||||
gPttIsPressed = false;
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
return;
|
||||
}
|
||||
|
||||
if (gFM_ScanState == FM_SCAN_OFF)
|
||||
{
|
||||
if (gCssScanMode == CSS_SCAN_MODE_OFF)
|
||||
{
|
||||
if (gScreenToDisplay == DISPLAY_MENU || gScreenToDisplay == DISPLAY_FM)
|
||||
{
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
gInputBoxIndex = 0;
|
||||
gPttIsPressed = false;
|
||||
gPttDebounceCounter = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (gScreenToDisplay != DISPLAY_SCANNER)
|
||||
{
|
||||
if (gCurrentFunction == FUNCTION_TRANSMIT && gRTTECountdown == 0)
|
||||
{
|
||||
gInputBoxIndex = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
gFlagPrepareTX = true;
|
||||
|
||||
if (gDTMF_InputMode)
|
||||
{
|
||||
if (gDTMF_InputIndex || gDTMF_PreviousIndex)
|
||||
{
|
||||
if (gDTMF_InputIndex == 0)
|
||||
gDTMF_InputIndex = gDTMF_PreviousIndex;
|
||||
|
||||
gDTMF_InputBox[gDTMF_InputIndex] = 0;
|
||||
|
||||
if (gDTMF_InputIndex == 3)
|
||||
gDTMF_CallMode = DTMF_CheckGroupCall(gDTMF_InputBox, 3);
|
||||
else
|
||||
gDTMF_CallMode = DTMF_CALL_MODE_DTMF;
|
||||
|
||||
sprintf(gDTMF_String, "%s", gDTMF_InputBox);
|
||||
|
||||
gDTMF_PreviousIndex = gDTMF_InputIndex;
|
||||
gDTMF_ReplyState = DTMF_REPLY_ANI;
|
||||
gDTMF_State = DTMF_STATE_0;
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
|
||||
gDTMF_InputMode = false;
|
||||
gDTMF_InputIndex = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
gFlagPrepareTX = true;
|
||||
gInputBoxIndex = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
gEeprom.CROSS_BAND_RX_TX = gBackupCROSS_BAND_RX_TX;
|
||||
gUpdateStatus = true;
|
||||
gFlagStopScan = true;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
gFlagResetVfos = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
MENU_StopCssScan();
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FM_PlayAndUpdate();
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_SCANNING_STOP;
|
||||
#endif
|
||||
|
||||
gPttWasPressed = true;
|
||||
}
|
||||
|
26
app/generic.h
Normal file
26
app/generic.h
Normal file
@ -0,0 +1,26 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef APP_GENERIC_H
|
||||
#define APP_GENERIC_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void GENERIC_Key_F(bool bKeyPressed, bool bKeyHeld);
|
||||
void GENERIC_Key_PTT(bool bKeyPressed);
|
||||
|
||||
#endif
|
||||
|
621
app/main.c
Normal file
621
app/main.c
Normal file
@ -0,0 +1,621 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "app/action.h"
|
||||
#include "app/app.h"
|
||||
#include "app/fm.h"
|
||||
#include "app/generic.h"
|
||||
#include "app/main.h"
|
||||
#include "app/scanner.h"
|
||||
#include "audio.h"
|
||||
#include "dtmf.h"
|
||||
#include "frequencies.h"
|
||||
#include "misc.h"
|
||||
#include "radio.h"
|
||||
#include "settings.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
uint8_t Band;
|
||||
uint8_t Vfo = gEeprom.TX_CHANNEL;
|
||||
|
||||
if (bKeyHeld)
|
||||
return;
|
||||
|
||||
if (!bKeyPressed)
|
||||
return;
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
if (!gWasFKeyPressed)
|
||||
{
|
||||
INPUTBOX_Append(Key);
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
|
||||
if (IS_MR_CHANNEL(gTxVfo->CHANNEL_SAVE))
|
||||
{
|
||||
uint16_t Channel;
|
||||
|
||||
if (gInputBoxIndex != 3)
|
||||
{
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
#endif
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
return;
|
||||
}
|
||||
|
||||
gInputBoxIndex = 0;
|
||||
|
||||
Channel = ((gInputBox[0] * 100) + (gInputBox[1] * 10) + gInputBox[2]) - 1;
|
||||
if (!RADIO_CheckValidChannel(Channel, false, 0))
|
||||
{
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
#endif
|
||||
gEeprom.MrChannel[Vfo] = (uint8_t)Channel;
|
||||
gEeprom.ScreenChannel[Vfo] = (uint8_t)Channel;
|
||||
gRequestSaveVFO = true;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_NOAA
|
||||
if (IS_NOT_NOAA_CHANNEL(gTxVfo->CHANNEL_SAVE))
|
||||
#endif
|
||||
{
|
||||
uint32_t Frequency;
|
||||
|
||||
if (gInputBoxIndex < 6)
|
||||
{
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
gInputBoxIndex = 0;
|
||||
|
||||
NUMBER_Get(gInputBox, &Frequency);
|
||||
|
||||
if (gSetting_350EN || (4999990 < (Frequency - 35000000)))
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
if (Frequency <= gUpperLimitFrequencyBandTable[i] && (gLowerLimitFrequencyBandTable[i] <= Frequency))
|
||||
{
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
#endif
|
||||
if (gTxVfo->Band != i)
|
||||
{
|
||||
gTxVfo->Band = i;
|
||||
gEeprom.ScreenChannel[Vfo] = i + FREQ_CHANNEL_FIRST;
|
||||
gEeprom.FreqChannel[Vfo] = i + FREQ_CHANNEL_FIRST;
|
||||
|
||||
SETTINGS_SaveVfoIndices();
|
||||
RADIO_ConfigureChannel(Vfo, 2);
|
||||
}
|
||||
|
||||
Frequency += 75;
|
||||
|
||||
gTxVfo->ConfigRX.Frequency = FREQUENCY_FloorToStep(
|
||||
Frequency,
|
||||
gTxVfo->StepFrequency,
|
||||
gLowerLimitFrequencyBandTable[gTxVfo->Band]);
|
||||
|
||||
gRequestSaveChannel = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifndef DISABLE_NOAA
|
||||
else
|
||||
{
|
||||
uint8_t Channel;
|
||||
|
||||
if (gInputBoxIndex != 2)
|
||||
{
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
#endif
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
return;
|
||||
}
|
||||
|
||||
gInputBoxIndex = 0;
|
||||
|
||||
Channel = (gInputBox[0] * 10) + gInputBox[1];
|
||||
if (Channel >= 1 && Channel <= 10)
|
||||
{
|
||||
Channel += NOAA_CHANNEL_FIRST;
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
#endif
|
||||
gEeprom.NoaaChannel[Vfo] = Channel;
|
||||
gEeprom.ScreenChannel[Vfo] = Channel;
|
||||
gRequestSaveVFO = true;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
gWasFKeyPressed = false;
|
||||
gUpdateStatus = true;
|
||||
|
||||
switch (Key)
|
||||
{
|
||||
case KEY_0:
|
||||
ACTION_FM();
|
||||
break;
|
||||
|
||||
case KEY_1:
|
||||
if (!IS_FREQ_CHANNEL(gTxVfo->CHANNEL_SAVE))
|
||||
{
|
||||
gWasFKeyPressed = false;
|
||||
gUpdateStatus = true;
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
Band = gTxVfo->Band + 1;
|
||||
if (gSetting_350EN || Band != BAND5_350MHz)
|
||||
{
|
||||
if (BAND7_470MHz < Band)
|
||||
Band = BAND1_50MHz;
|
||||
}
|
||||
else
|
||||
Band = BAND6_400MHz;
|
||||
gTxVfo->Band = Band;
|
||||
|
||||
gEeprom.ScreenChannel[Vfo] = FREQ_CHANNEL_FIRST + Band;
|
||||
gEeprom.FreqChannel[Vfo] = FREQ_CHANNEL_FIRST + Band;
|
||||
gRequestSaveVFO = true;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
break;
|
||||
|
||||
case KEY_2:
|
||||
if (gEeprom.CROSS_BAND_RX_TX == CROSS_BAND_CHAN_A)
|
||||
gEeprom.CROSS_BAND_RX_TX = CROSS_BAND_CHAN_B;
|
||||
else
|
||||
if (gEeprom.CROSS_BAND_RX_TX == CROSS_BAND_CHAN_B)
|
||||
gEeprom.CROSS_BAND_RX_TX = CROSS_BAND_CHAN_A;
|
||||
else
|
||||
if (gEeprom.DUAL_WATCH == DUAL_WATCH_CHAN_A)
|
||||
gEeprom.DUAL_WATCH = DUAL_WATCH_CHAN_B;
|
||||
else
|
||||
if (gEeprom.DUAL_WATCH == DUAL_WATCH_CHAN_B)
|
||||
gEeprom.DUAL_WATCH = DUAL_WATCH_CHAN_A;
|
||||
else
|
||||
gEeprom.TX_CHANNEL = (Vfo == 0);
|
||||
|
||||
gRequestSaveSettings = 1;
|
||||
gFlagReconfigureVfos = true;
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
break;
|
||||
|
||||
case KEY_3:
|
||||
#ifndef DISABLE_NOAA
|
||||
if (gEeprom.VFO_OPEN && IS_NOT_NOAA_CHANNEL(gTxVfo->CHANNEL_SAVE))
|
||||
#else
|
||||
if (gEeprom.VFO_OPEN)
|
||||
#endif
|
||||
{
|
||||
uint8_t Channel;
|
||||
|
||||
if (IS_MR_CHANNEL(gTxVfo->CHANNEL_SAVE))
|
||||
{
|
||||
gEeprom.ScreenChannel[Vfo] = gEeprom.FreqChannel[gEeprom.TX_CHANNEL];
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_FREQUENCY_MODE;
|
||||
#endif
|
||||
gRequestSaveVFO = true;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
break;
|
||||
}
|
||||
Channel = RADIO_FindNextChannel(gEeprom.MrChannel[gEeprom.TX_CHANNEL], 1, false, 0);
|
||||
if (Channel != 0xFF)
|
||||
{
|
||||
gEeprom.ScreenChannel[Vfo] = Channel;
|
||||
#ifndef DISABLE_VOICE
|
||||
AUDIO_SetVoiceID(0, VOICE_ID_CHANNEL_MODE);
|
||||
AUDIO_SetDigitVoice(1, Channel + 1);
|
||||
gAnotherVoiceID = (VOICE_ID_t)0xFE;
|
||||
#endif
|
||||
gRequestSaveVFO = true;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
break;
|
||||
}
|
||||
}
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
break;
|
||||
|
||||
case KEY_4:
|
||||
gWasFKeyPressed = false;
|
||||
gUpdateStatus = true;
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
gFlagStartScan = true;
|
||||
gScanSingleFrequency = false;
|
||||
gBackupCROSS_BAND_RX_TX = gEeprom.CROSS_BAND_RX_TX;
|
||||
gEeprom.CROSS_BAND_RX_TX = CROSS_BAND_OFF;
|
||||
break;
|
||||
|
||||
case KEY_5:
|
||||
// TODO: something wrong here !!
|
||||
#ifndef DISABLE_NOAA
|
||||
if (IS_NOT_NOAA_CHANNEL(gTxVfo->CHANNEL_SAVE))
|
||||
gEeprom.ScreenChannel[Vfo] = gEeprom.NoaaChannel[gEeprom.TX_CHANNEL];
|
||||
else
|
||||
{
|
||||
gEeprom.ScreenChannel[Vfo] = gEeprom.FreqChannel[gEeprom.TX_CHANNEL];
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_FREQUENCY_MODE;
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
//gEeprom.ScreenChannel[Vfo] = gEeprom.NoaaChannel[gEeprom.TX_CHANNEL];
|
||||
gEeprom.ScreenChannel[Vfo] = gEeprom.FreqChannel[gEeprom.TX_CHANNEL];
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_FREQUENCY_MODE;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
gRequestSaveVFO = true;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
break;
|
||||
|
||||
case KEY_6:
|
||||
ACTION_Power();
|
||||
break;
|
||||
|
||||
case KEY_7:
|
||||
ACTION_Vox();
|
||||
break;
|
||||
|
||||
case KEY_8:
|
||||
gTxVfo->FrequencyReverse = gTxVfo->FrequencyReverse == false;
|
||||
gRequestSaveChannel = 1;
|
||||
break;
|
||||
|
||||
case KEY_9:
|
||||
if (RADIO_CheckValidChannel(gEeprom.CHAN_1_CALL, false, 0))
|
||||
{
|
||||
gEeprom.MrChannel[Vfo] = gEeprom.CHAN_1_CALL;
|
||||
gEeprom.ScreenChannel[Vfo] = gEeprom.CHAN_1_CALL;
|
||||
#ifndef DISABLE_VOICE
|
||||
AUDIO_SetVoiceID(0, VOICE_ID_CHANNEL_MODE);
|
||||
AUDIO_SetDigitVoice(1, gEeprom.CHAN_1_CALL + 1);
|
||||
gAnotherVoiceID = (VOICE_ID_t)0xFE;
|
||||
#endif
|
||||
gRequestSaveVFO = true;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
break;
|
||||
}
|
||||
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
break;
|
||||
|
||||
default:
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
gUpdateStatus = true;
|
||||
gWasFKeyPressed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void MAIN_Key_EXIT(bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if (!bKeyHeld && bKeyPressed)
|
||||
{
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
if (!gFmRadioMode)
|
||||
{
|
||||
if (gScanState == SCAN_OFF)
|
||||
{
|
||||
if (gInputBoxIndex == 0)
|
||||
return;
|
||||
gInputBox[--gInputBoxIndex] = 10;
|
||||
#ifndef DISABLE_VOICE
|
||||
if (gInputBoxIndex == 0)
|
||||
gAnotherVoiceID = VOICE_ID_CANCEL;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
SCANNER_Stop();
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_SCANNING_STOP;
|
||||
#endif
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
return;
|
||||
}
|
||||
ACTION_FM();
|
||||
}
|
||||
}
|
||||
|
||||
static void MAIN_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if (!bKeyHeld && bKeyPressed)
|
||||
{
|
||||
bool bFlag;
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
bFlag = (gInputBoxIndex == 0);
|
||||
gInputBoxIndex = 0;
|
||||
if (bFlag)
|
||||
{
|
||||
gFlagRefreshSetting = true;
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_MENU;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void MAIN_Key_STAR(bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if (gInputBoxIndex)
|
||||
{
|
||||
if (!bKeyHeld && bKeyPressed)
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bKeyHeld || !bKeyPressed)
|
||||
{
|
||||
if (bKeyHeld || bKeyPressed)
|
||||
{
|
||||
if (!bKeyHeld)
|
||||
return;
|
||||
|
||||
if (!bKeyPressed)
|
||||
return;
|
||||
|
||||
ACTION_Scan(false);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_NOAA
|
||||
if (gScanState == SCAN_OFF && IS_NOT_NOAA_CHANNEL(gTxVfo->CHANNEL_SAVE))
|
||||
#else
|
||||
if (gScanState == SCAN_OFF)
|
||||
#endif
|
||||
{
|
||||
gDTMF_InputMode = true;
|
||||
memcpy(gDTMF_InputBox, gDTMF_String, 15);
|
||||
gDTMF_InputIndex = 0;
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
if (!gWasFKeyPressed)
|
||||
{
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
gWasFKeyPressed = false;
|
||||
gUpdateStatus = true;
|
||||
|
||||
#ifndef DISABLE_NOAA
|
||||
if (IS_NOT_NOAA_CHANNEL(gTxVfo->CHANNEL_SAVE))
|
||||
{
|
||||
gFlagStartScan = true;
|
||||
gScanSingleFrequency = true;
|
||||
gBackupCROSS_BAND_RX_TX = gEeprom.CROSS_BAND_RX_TX;
|
||||
gEeprom.CROSS_BAND_RX_TX = CROSS_BAND_OFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
}
|
||||
#else
|
||||
gFlagStartScan = true;
|
||||
gScanSingleFrequency = true;
|
||||
gBackupCROSS_BAND_RX_TX = gEeprom.CROSS_BAND_RX_TX;
|
||||
gEeprom.CROSS_BAND_RX_TX = CROSS_BAND_OFF;
|
||||
#endif
|
||||
|
||||
gPttWasReleased = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void MAIN_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
{
|
||||
uint8_t Channel = gEeprom.ScreenChannel[gEeprom.TX_CHANNEL];
|
||||
|
||||
if (bKeyHeld || !bKeyPressed)
|
||||
{
|
||||
if (gInputBoxIndex)
|
||||
return;
|
||||
|
||||
if (!bKeyPressed)
|
||||
{
|
||||
if (!bKeyHeld)
|
||||
return;
|
||||
|
||||
if (IS_FREQ_CHANNEL(Channel))
|
||||
return;
|
||||
|
||||
#ifndef DISABLE_VOICE
|
||||
AUDIO_SetDigitVoice(0, gTxVfo->CHANNEL_SAVE + 1);
|
||||
gAnotherVoiceID = (VOICE_ID_t)0xFE;
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gInputBoxIndex)
|
||||
{
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
}
|
||||
|
||||
if (gScanState == SCAN_OFF)
|
||||
{
|
||||
#ifndef DISABLE_NOAA
|
||||
if (IS_NOT_NOAA_CHANNEL(Channel))
|
||||
#endif
|
||||
{
|
||||
uint8_t Next;
|
||||
|
||||
if (IS_FREQ_CHANNEL(Channel))
|
||||
{
|
||||
APP_SetFrequencyByStep(gTxVfo, Direction);
|
||||
gRequestSaveChannel = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
Next = RADIO_FindNextChannel(Channel + Direction, Direction, false, 0);
|
||||
if (Next == 0xFF)
|
||||
return;
|
||||
|
||||
if (Channel == Next)
|
||||
return;
|
||||
|
||||
gEeprom.MrChannel[gEeprom.TX_CHANNEL] = Next;
|
||||
gEeprom.ScreenChannel[gEeprom.TX_CHANNEL] = Next;
|
||||
|
||||
if (!bKeyHeld)
|
||||
{
|
||||
#ifndef DISABLE_VOICE
|
||||
AUDIO_SetDigitVoice(0, Next + 1);
|
||||
gAnotherVoiceID = (VOICE_ID_t)0xFE;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#ifndef DISABLE_NOAA
|
||||
else
|
||||
{
|
||||
Channel = NOAA_CHANNEL_FIRST + NUMBER_AddWithWraparound(gEeprom.ScreenChannel[gEeprom.TX_CHANNEL] - NOAA_CHANNEL_FIRST, Direction, 0, 9);
|
||||
gEeprom.NoaaChannel[gEeprom.TX_CHANNEL] = Channel;
|
||||
gEeprom.ScreenChannel[gEeprom.TX_CHANNEL] = Channel;
|
||||
}
|
||||
#endif
|
||||
|
||||
gRequestSaveVFO = true;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
return;
|
||||
}
|
||||
|
||||
CHANNEL_Next(false, Direction);
|
||||
|
||||
gPttWasReleased = true;
|
||||
}
|
||||
|
||||
void MAIN_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if (gFmRadioMode && Key != KEY_PTT && Key != KEY_EXIT)
|
||||
{
|
||||
if (!bKeyHeld && bKeyPressed)
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (gDTMF_InputMode && !bKeyHeld && bKeyPressed)
|
||||
{
|
||||
const char Character = DTMF_GetCharacter(Key);
|
||||
if (Character != 0xFF)
|
||||
{
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
DTMF_Append(Character);
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
gPttWasReleased = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: ???
|
||||
if (KEY_PTT < Key)
|
||||
{
|
||||
Key = KEY_SIDE2;
|
||||
}
|
||||
|
||||
switch (Key)
|
||||
{
|
||||
case KEY_0:
|
||||
case KEY_1:
|
||||
case KEY_2:
|
||||
case KEY_3:
|
||||
case KEY_4:
|
||||
case KEY_5:
|
||||
case KEY_6:
|
||||
case KEY_7:
|
||||
case KEY_8:
|
||||
case KEY_9:
|
||||
MAIN_Key_DIGITS(Key, bKeyPressed, bKeyHeld);
|
||||
break;
|
||||
case KEY_MENU:
|
||||
MAIN_Key_MENU(bKeyPressed, bKeyHeld);
|
||||
break;
|
||||
case KEY_UP:
|
||||
MAIN_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
MAIN_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
|
||||
break;
|
||||
case KEY_EXIT:
|
||||
MAIN_Key_EXIT(bKeyPressed, bKeyHeld);
|
||||
break;
|
||||
case KEY_STAR:
|
||||
MAIN_Key_STAR(bKeyPressed, bKeyHeld);
|
||||
break;
|
||||
case KEY_F:
|
||||
GENERIC_Key_F(bKeyPressed, bKeyHeld);
|
||||
break;
|
||||
case KEY_PTT:
|
||||
GENERIC_Key_PTT(bKeyPressed);
|
||||
break;
|
||||
default:
|
||||
if (!bKeyHeld && bKeyPressed)
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
break;
|
||||
}
|
||||
}
|
25
app/main.h
Normal file
25
app/main.h
Normal file
@ -0,0 +1,25 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef APP_MAIN_H
|
||||
#define APP_MAIN_H
|
||||
|
||||
#include "driver/keyboard.h"
|
||||
|
||||
void MAIN_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld);
|
||||
|
||||
#endif
|
||||
|
1295
app/menu.c
Normal file
1295
app/menu.c
Normal file
File diff suppressed because it is too large
Load Diff
32
app/menu.h
Normal file
32
app/menu.h
Normal file
@ -0,0 +1,32 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef APP_MENU_H
|
||||
#define APP_MENU_H
|
||||
|
||||
#include "driver/keyboard.h"
|
||||
|
||||
int MENU_GetLimits(uint8_t Cursor, uint8_t *pMin, uint8_t *pMax);
|
||||
void MENU_AcceptSetting(void);
|
||||
void MENU_SelectNextCode(void);
|
||||
void MENU_ShowCurrentSetting(void);
|
||||
void MENU_StartCssScan(int8_t Direction);
|
||||
void MENU_StopCssScan(void);
|
||||
|
||||
void MENU_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld);
|
||||
|
||||
#endif
|
||||
|
445
app/scanner.c
Normal file
445
app/scanner.c
Normal file
@ -0,0 +1,445 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "app/generic.h"
|
||||
#include "app/scanner.h"
|
||||
#include "audio.h"
|
||||
#include "driver/bk4819.h"
|
||||
#include "frequencies.h"
|
||||
#include "misc.h"
|
||||
#include "radio.h"
|
||||
#include "settings.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
DCS_CodeType_t gScanCssResultType;
|
||||
uint8_t gScanCssResultCode;
|
||||
bool gFlagStartScan;
|
||||
bool gFlagStopScan;
|
||||
bool gScanSingleFrequency;
|
||||
uint8_t gScannerEditState;
|
||||
uint8_t gScanChannel;
|
||||
uint32_t gScanFrequency;
|
||||
bool gScanPauseMode;
|
||||
SCAN_CssState_t gScanCssState;
|
||||
volatile bool gScheduleScanListen = true;
|
||||
volatile uint16_t ScanPauseDelayIn10msec;
|
||||
uint8_t gScanProgressIndicator;
|
||||
uint8_t gScanHitCount;
|
||||
bool gScanUseCssResult;
|
||||
uint8_t gScanState;
|
||||
bool bScanKeepFrequency;
|
||||
|
||||
static void SCANNER_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if (!bKeyHeld && bKeyPressed)
|
||||
{
|
||||
if (gScannerEditState == 1)
|
||||
{
|
||||
uint16_t Channel;
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
INPUTBOX_Append(Key);
|
||||
gRequestDisplayScreen = DISPLAY_SCANNER;
|
||||
|
||||
if (gInputBoxIndex < 3)
|
||||
{
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
gInputBoxIndex = 0;
|
||||
Channel = ((gInputBox[0] * 100) + (gInputBox[1] * 10) + gInputBox[2]) - 1;
|
||||
|
||||
if (IS_MR_CHANNEL(Channel))
|
||||
{
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
#endif
|
||||
gShowChPrefix = RADIO_CheckValidChannel(Channel, false, 0);
|
||||
gScanChannel = (uint8_t)Channel;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
}
|
||||
}
|
||||
|
||||
static void SCANNER_Key_EXIT(bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if (!bKeyHeld && bKeyPressed)
|
||||
{
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
switch (gScannerEditState)
|
||||
{
|
||||
case 0:
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
gEeprom.CROSS_BAND_RX_TX = gBackupCROSS_BAND_RX_TX;
|
||||
gUpdateStatus = true;
|
||||
gFlagStopScan = true;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
gFlagResetVfos = true;
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_CANCEL;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (gInputBoxIndex)
|
||||
{
|
||||
gInputBox[--gInputBoxIndex] = 10;
|
||||
gRequestDisplayScreen = DISPLAY_SCANNER;
|
||||
break;
|
||||
}
|
||||
// Fallthrough
|
||||
|
||||
case 2:
|
||||
gScannerEditState = 0;
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_CANCEL;
|
||||
#endif
|
||||
gRequestDisplayScreen = DISPLAY_SCANNER;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void SCANNER_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
uint8_t Channel;
|
||||
|
||||
if (bKeyHeld)
|
||||
return;
|
||||
|
||||
if (!bKeyPressed)
|
||||
return;
|
||||
|
||||
if (gScanCssState == SCAN_CSS_STATE_OFF && !gScanSingleFrequency)
|
||||
{
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (gScanCssState == SCAN_CSS_STATE_SCANNING)
|
||||
{
|
||||
if (gScanSingleFrequency)
|
||||
{
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (gScanCssState == SCAN_CSS_STATE_FAILED)
|
||||
{
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
switch (gScannerEditState)
|
||||
{
|
||||
case 0:
|
||||
if (!gScanSingleFrequency)
|
||||
{
|
||||
uint32_t Freq250;
|
||||
uint32_t Freq625;
|
||||
int16_t Delta250;
|
||||
int16_t Delta625;
|
||||
|
||||
Freq250 = FREQUENCY_FloorToStep(gScanFrequency, 250, 0);
|
||||
Freq625 = FREQUENCY_FloorToStep(gScanFrequency, 625, 0);
|
||||
Delta250 = (short)gScanFrequency - (short)Freq250;
|
||||
|
||||
if (125 < Delta250)
|
||||
{
|
||||
Delta250 = 250 - Delta250;
|
||||
Freq250 += 250;
|
||||
}
|
||||
|
||||
Delta625 = (short)gScanFrequency - (short)Freq625;
|
||||
|
||||
if (312 < Delta625)
|
||||
{
|
||||
Delta625 = 625 - Delta625;
|
||||
Freq625 += 625;
|
||||
}
|
||||
|
||||
if (Delta625 < Delta250)
|
||||
{
|
||||
gStepSetting = STEP_6_25kHz;
|
||||
gScanFrequency = Freq625;
|
||||
}
|
||||
else
|
||||
{
|
||||
gStepSetting = STEP_2_5kHz;
|
||||
gScanFrequency = Freq250;
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_MR_CHANNEL(gTxVfo->CHANNEL_SAVE))
|
||||
{
|
||||
gScannerEditState = 1;
|
||||
gScanChannel = gTxVfo->CHANNEL_SAVE;
|
||||
gShowChPrefix = RADIO_CheckValidChannel(gTxVfo->CHANNEL_SAVE, false, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
gScannerEditState = 2;
|
||||
}
|
||||
|
||||
gScanCssState = SCAN_CSS_STATE_FOUND;
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_MEMORY_CHANNEL;
|
||||
#endif
|
||||
gRequestDisplayScreen = DISPLAY_SCANNER;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (gInputBoxIndex == 0)
|
||||
{
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
gRequestDisplayScreen = DISPLAY_SCANNER;
|
||||
gScannerEditState = 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (!gScanSingleFrequency)
|
||||
{
|
||||
RADIO_InitInfo(gTxVfo, gTxVfo->CHANNEL_SAVE, FREQUENCY_GetBand(gScanFrequency), gScanFrequency);
|
||||
|
||||
if (gScanUseCssResult)
|
||||
{
|
||||
gTxVfo->ConfigRX.CodeType = gScanCssResultType;
|
||||
gTxVfo->ConfigRX.Code = gScanCssResultCode;
|
||||
}
|
||||
|
||||
gTxVfo->ConfigTX = gTxVfo->ConfigRX;
|
||||
gTxVfo->STEP_SETTING = gStepSetting;
|
||||
}
|
||||
else
|
||||
{
|
||||
RADIO_ConfigureChannel(0, 2);
|
||||
RADIO_ConfigureChannel(1, 2);
|
||||
gTxVfo->ConfigRX.CodeType = gScanCssResultType;
|
||||
gTxVfo->ConfigRX.Code = gScanCssResultCode;
|
||||
gTxVfo->ConfigTX.CodeType = gScanCssResultType;
|
||||
gTxVfo->ConfigTX.Code = gScanCssResultCode;
|
||||
}
|
||||
|
||||
if (IS_MR_CHANNEL(gTxVfo->CHANNEL_SAVE))
|
||||
{
|
||||
Channel = gScanChannel;
|
||||
gEeprom.MrChannel[gEeprom.TX_CHANNEL] = Channel;
|
||||
}
|
||||
else
|
||||
{
|
||||
Channel = gTxVfo->Band + FREQ_CHANNEL_FIRST;
|
||||
gEeprom.FreqChannel[gEeprom.TX_CHANNEL] = Channel;
|
||||
}
|
||||
|
||||
gTxVfo->CHANNEL_SAVE = Channel;
|
||||
gEeprom.ScreenChannel[gEeprom.TX_CHANNEL] = Channel;
|
||||
#ifndef DISABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_CONFIRM;
|
||||
#endif
|
||||
gRequestDisplayScreen = DISPLAY_SCANNER;
|
||||
gRequestSaveChannel = 2;
|
||||
gScannerEditState = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void SCANNER_Key_STAR(bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if ((!bKeyHeld) && (bKeyPressed))
|
||||
{
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
gFlagStartScan = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void SCANNER_Key_UP_DOWN(bool bKeyPressed, bool pKeyHeld, int8_t Direction)
|
||||
{
|
||||
if (pKeyHeld)
|
||||
{
|
||||
if (!bKeyPressed)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!bKeyPressed)
|
||||
return;
|
||||
|
||||
gInputBoxIndex = 0;
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
}
|
||||
|
||||
if (gScannerEditState == 1)
|
||||
{
|
||||
gScanChannel = NUMBER_AddWithWraparound(gScanChannel, Direction, 0, 199);
|
||||
gShowChPrefix = RADIO_CheckValidChannel(gScanChannel, false, 0);
|
||||
gRequestDisplayScreen = DISPLAY_SCANNER;
|
||||
}
|
||||
else
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
}
|
||||
|
||||
void SCANNER_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
switch (Key)
|
||||
{
|
||||
case KEY_0:
|
||||
case KEY_1:
|
||||
case KEY_2:
|
||||
case KEY_3:
|
||||
case KEY_4:
|
||||
case KEY_5:
|
||||
case KEY_6:
|
||||
case KEY_7:
|
||||
case KEY_8:
|
||||
case KEY_9:
|
||||
SCANNER_Key_DIGITS(Key, bKeyPressed, bKeyHeld);
|
||||
break;
|
||||
case KEY_MENU:
|
||||
SCANNER_Key_MENU(bKeyPressed, bKeyHeld);
|
||||
break;
|
||||
case KEY_UP:
|
||||
SCANNER_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
SCANNER_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
|
||||
break;
|
||||
case KEY_EXIT:
|
||||
SCANNER_Key_EXIT(bKeyPressed, bKeyHeld);
|
||||
break;
|
||||
case KEY_STAR:
|
||||
SCANNER_Key_STAR(bKeyPressed, bKeyHeld);
|
||||
break;
|
||||
case KEY_PTT:
|
||||
GENERIC_Key_PTT(bKeyPressed);
|
||||
break;
|
||||
default:
|
||||
if (!bKeyHeld && bKeyPressed)
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SCANNER_Start(void)
|
||||
{
|
||||
uint8_t BackupStep;
|
||||
uint16_t BackupFrequency;
|
||||
|
||||
BK4819_StopScan();
|
||||
RADIO_SelectVfos();
|
||||
|
||||
#ifndef DISABLE_NOAA
|
||||
if (IS_NOAA_CHANNEL(gRxVfo->CHANNEL_SAVE))
|
||||
gRxVfo->CHANNEL_SAVE = FREQ_CHANNEL_FIRST + 5;
|
||||
#endif
|
||||
|
||||
BackupStep = gRxVfo->STEP_SETTING;
|
||||
BackupFrequency = gRxVfo->StepFrequency;
|
||||
|
||||
RADIO_InitInfo(gRxVfo, gRxVfo->CHANNEL_SAVE, gRxVfo->Band, gRxVfo->pRX->Frequency);
|
||||
|
||||
gRxVfo->STEP_SETTING = BackupStep;
|
||||
gRxVfo->StepFrequency = BackupFrequency;
|
||||
|
||||
RADIO_SetupRegisters(true);
|
||||
|
||||
#ifndef DISABLE_NOAA
|
||||
gIsNoaaMode = false;
|
||||
#endif
|
||||
|
||||
if (gScanSingleFrequency)
|
||||
{
|
||||
gScanCssState = SCAN_CSS_STATE_SCANNING;
|
||||
gScanFrequency = gRxVfo->pRX->Frequency;
|
||||
gStepSetting = gRxVfo->STEP_SETTING;
|
||||
BK4819_PickRXFilterPathBasedOnFrequency(gScanFrequency);
|
||||
BK4819_SetScanFrequency(gScanFrequency);
|
||||
}
|
||||
else
|
||||
{
|
||||
gScanCssState = SCAN_CSS_STATE_OFF;
|
||||
gScanFrequency = 0xFFFFFFFF;
|
||||
BK4819_PickRXFilterPathBasedOnFrequency(0xFFFFFFFF);
|
||||
BK4819_EnableFrequencyScan();
|
||||
}
|
||||
|
||||
gScanDelay = 21;
|
||||
gScanCssResultCode = 0xFF;
|
||||
gScanCssResultType = 0xFF;
|
||||
gScanHitCount = 0;
|
||||
gScanUseCssResult = false;
|
||||
gDTMF_RequestPending = false;
|
||||
g_CxCSS_TAIL_Found = false;
|
||||
g_CDCSS_Lost = false;
|
||||
gCDCSSCodeType = 0;
|
||||
g_CTCSS_Lost = false;
|
||||
g_VOX_Lost = false;
|
||||
g_SquelchLost = false;
|
||||
gScannerEditState = 0;
|
||||
gScanProgressIndicator = 0;
|
||||
}
|
||||
|
||||
void SCANNER_Stop(void)
|
||||
{
|
||||
uint8_t Previous = gRestoreMrChannel;
|
||||
|
||||
gScanState = SCAN_OFF;
|
||||
|
||||
if (!bScanKeepFrequency)
|
||||
{
|
||||
if (IS_MR_CHANNEL(gNextMrChannel))
|
||||
{
|
||||
gEeprom.MrChannel[gEeprom.RX_CHANNEL] = gRestoreMrChannel;
|
||||
gEeprom.ScreenChannel[gEeprom.RX_CHANNEL] = Previous;
|
||||
RADIO_ConfigureChannel(gEeprom.RX_CHANNEL, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
gRxVfo->ConfigRX.Frequency = gRestoreFrequency;
|
||||
RADIO_ApplyOffset(gRxVfo);
|
||||
RADIO_ConfigureSquelchAndOutputPower(gRxVfo);
|
||||
}
|
||||
RADIO_SetupRegisters(true);
|
||||
gUpdateDisplay = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IS_MR_CHANNEL(gRxVfo->CHANNEL_SAVE))
|
||||
{
|
||||
RADIO_ApplyOffset(gRxVfo);
|
||||
RADIO_ConfigureSquelchAndOutputPower(gRxVfo);
|
||||
SETTINGS_SaveChannel(gRxVfo->CHANNEL_SAVE, gEeprom.RX_CHANNEL, gRxVfo, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
SETTINGS_SaveVfoIndices();
|
||||
}
|
59
app/scanner.h
Normal file
59
app/scanner.h
Normal file
@ -0,0 +1,59 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef APP_SCANNER_H
|
||||
#define APP_SCANNER_H
|
||||
|
||||
#include "dcs.h"
|
||||
#include "driver/keyboard.h"
|
||||
|
||||
enum SCAN_CssState_t {
|
||||
SCAN_CSS_STATE_OFF = 0U,
|
||||
SCAN_CSS_STATE_SCANNING = 1U,
|
||||
SCAN_CSS_STATE_FOUND = 2U,
|
||||
SCAN_CSS_STATE_FAILED = 3U,
|
||||
};
|
||||
|
||||
typedef enum SCAN_CssState_t SCAN_CssState_t;
|
||||
|
||||
enum {
|
||||
SCAN_OFF = 0U,
|
||||
};
|
||||
|
||||
extern DCS_CodeType_t gScanCssResultType;
|
||||
extern uint8_t gScanCssResultCode;
|
||||
extern bool gFlagStartScan;
|
||||
extern bool gFlagStopScan;
|
||||
extern bool gScanSingleFrequency;
|
||||
extern uint8_t gScannerEditState;
|
||||
extern uint8_t gScanChannel;
|
||||
extern uint32_t gScanFrequency;
|
||||
extern bool gScanPauseMode;
|
||||
extern SCAN_CssState_t gScanCssState;
|
||||
extern volatile bool gScheduleScanListen;
|
||||
extern volatile uint16_t ScanPauseDelayIn10msec;
|
||||
extern uint8_t gScanProgressIndicator;
|
||||
extern uint8_t gScanHitCount;
|
||||
extern bool gScanUseCssResult;
|
||||
extern uint8_t gScanState;
|
||||
extern bool bScanKeepFrequency;
|
||||
|
||||
void SCANNER_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld);
|
||||
void SCANNER_Start(void);
|
||||
void SCANNER_Stop(void);
|
||||
|
||||
#endif
|
||||
|
520
app/uart.c
Normal file
520
app/uart.c
Normal file
@ -0,0 +1,520 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "app/fm.h"
|
||||
#include "app/uart.h"
|
||||
#include "board.h"
|
||||
#include "bsp/dp32g030/dma.h"
|
||||
#include "bsp/dp32g030/gpio.h"
|
||||
#include "driver/aes.h"
|
||||
#include "driver/bk4819.h"
|
||||
#include "driver/crc.h"
|
||||
#include "driver/eeprom.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/uart.h"
|
||||
#include "functions.h"
|
||||
#include "misc.h"
|
||||
#include "settings.h"
|
||||
#include "sram-overlay.h"
|
||||
#include "version.h"
|
||||
|
||||
#define DMA_INDEX(x, y) (((x) + (y)) % sizeof(UART_DMA_Buffer))
|
||||
|
||||
typedef struct {
|
||||
uint16_t ID;
|
||||
uint16_t Size;
|
||||
} Header_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t Padding[2];
|
||||
uint16_t ID;
|
||||
} Footer_t;
|
||||
|
||||
typedef struct {
|
||||
Header_t Header;
|
||||
uint32_t Timestamp;
|
||||
} CMD_0514_t;
|
||||
|
||||
typedef struct {
|
||||
Header_t Header;
|
||||
struct {
|
||||
char Version[16];
|
||||
bool bHasCustomAesKey;
|
||||
bool bIsInLockScreen;
|
||||
uint8_t Padding[2];
|
||||
uint32_t Challenge[4];
|
||||
} Data;
|
||||
} REPLY_0514_t;
|
||||
|
||||
typedef struct {
|
||||
Header_t Header;
|
||||
uint16_t Offset;
|
||||
uint8_t Size;
|
||||
uint8_t Padding;
|
||||
uint32_t Timestamp;
|
||||
} CMD_051B_t;
|
||||
|
||||
typedef struct {
|
||||
Header_t Header;
|
||||
struct {
|
||||
uint16_t Offset;
|
||||
uint8_t Size;
|
||||
uint8_t Padding;
|
||||
uint8_t Data[128];
|
||||
} Data;
|
||||
} REPLY_051B_t;
|
||||
|
||||
typedef struct {
|
||||
Header_t Header;
|
||||
uint16_t Offset;
|
||||
uint8_t Size;
|
||||
bool bAllowPassword;
|
||||
uint32_t Timestamp;
|
||||
uint8_t Data[0];
|
||||
} CMD_051D_t;
|
||||
|
||||
typedef struct {
|
||||
Header_t Header;
|
||||
struct {
|
||||
uint16_t Offset;
|
||||
} Data;
|
||||
} REPLY_051D_t;
|
||||
|
||||
typedef struct {
|
||||
Header_t Header;
|
||||
struct {
|
||||
uint16_t RSSI;
|
||||
uint8_t ExNoiseIndicator;
|
||||
uint8_t GlitchIndicator;
|
||||
} Data;
|
||||
} REPLY_0527_t;
|
||||
|
||||
typedef struct {
|
||||
Header_t Header;
|
||||
struct {
|
||||
uint16_t Voltage;
|
||||
uint16_t Current;
|
||||
} Data;
|
||||
} REPLY_0529_t;
|
||||
|
||||
typedef struct {
|
||||
Header_t Header;
|
||||
uint32_t Response[4];
|
||||
} CMD_052D_t;
|
||||
|
||||
typedef struct {
|
||||
Header_t Header;
|
||||
struct {
|
||||
bool bIsLocked;
|
||||
uint8_t Padding[3];
|
||||
} Data;
|
||||
} REPLY_052D_t;
|
||||
|
||||
typedef struct {
|
||||
Header_t Header;
|
||||
uint32_t Timestamp;
|
||||
} CMD_052F_t;
|
||||
|
||||
static const uint8_t Obfuscation[16] = { 0x16, 0x6C, 0x14, 0xE6, 0x2E, 0x91, 0x0D, 0x40, 0x21, 0x35, 0xD5, 0x40, 0x13, 0x03, 0xE9, 0x80 };
|
||||
|
||||
static union {
|
||||
uint8_t Buffer[256];
|
||||
struct {
|
||||
Header_t Header;
|
||||
uint8_t Data[252];
|
||||
};
|
||||
} UART_Command;
|
||||
|
||||
static uint32_t Timestamp;
|
||||
static uint16_t gUART_WriteIndex;
|
||||
static bool bIsEncrypted = true;
|
||||
|
||||
static void SendReply(void *pReply, uint16_t Size)
|
||||
{
|
||||
Header_t Header;
|
||||
Footer_t Footer;
|
||||
uint8_t *pBytes;
|
||||
uint16_t i;
|
||||
|
||||
if (bIsEncrypted) {
|
||||
pBytes = (uint8_t *)pReply;
|
||||
for (i = 0; i < Size; i++) {
|
||||
pBytes[i] ^= Obfuscation[i % 16];
|
||||
}
|
||||
}
|
||||
|
||||
Header.ID = 0xCDAB;
|
||||
Header.Size = Size;
|
||||
UART_Send(&Header, sizeof(Header));
|
||||
UART_Send(pReply, Size);
|
||||
if (bIsEncrypted) {
|
||||
Footer.Padding[0] = Obfuscation[(Size + 0) % 16] ^ 0xFF;
|
||||
Footer.Padding[1] = Obfuscation[(Size + 1) % 16] ^ 0xFF;
|
||||
} else {
|
||||
Footer.Padding[0] = 0xFF;
|
||||
Footer.Padding[1] = 0xFF;
|
||||
}
|
||||
Footer.ID = 0xBADC;
|
||||
|
||||
UART_Send(&Footer, sizeof(Footer));
|
||||
}
|
||||
|
||||
static void SendVersion(void)
|
||||
{
|
||||
REPLY_0514_t Reply;
|
||||
|
||||
Reply.Header.ID = 0x0515;
|
||||
Reply.Header.Size = sizeof(Reply.Data);
|
||||
strcpy(Reply.Data.Version, Version);
|
||||
Reply.Data.bHasCustomAesKey = bHasCustomAesKey;
|
||||
Reply.Data.bIsInLockScreen = bIsInLockScreen;
|
||||
Reply.Data.Challenge[0] = gChallenge[0];
|
||||
Reply.Data.Challenge[1] = gChallenge[1];
|
||||
Reply.Data.Challenge[2] = gChallenge[2];
|
||||
Reply.Data.Challenge[3] = gChallenge[3];
|
||||
|
||||
SendReply(&Reply, sizeof(Reply));
|
||||
}
|
||||
|
||||
static bool IsBadChallenge(const uint32_t *pKey, const uint32_t *pIn, const uint32_t *pResponse)
|
||||
{
|
||||
uint8_t i;
|
||||
uint32_t IV[4];
|
||||
|
||||
IV[0] = 0;
|
||||
IV[1] = 0;
|
||||
IV[2] = 0;
|
||||
IV[3] = 0;
|
||||
AES_Encrypt(pKey, IV, pIn, IV, true);
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (IV[i] != pResponse[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void CMD_0514(const uint8_t *pBuffer)
|
||||
{
|
||||
const CMD_0514_t *pCmd = (const CMD_0514_t *)pBuffer;
|
||||
|
||||
Timestamp = pCmd->Timestamp;
|
||||
gFmRadioCountdown = 4;
|
||||
GPIO_ClearBit(&GPIOB->DATA, GPIOB_PIN_BACKLIGHT);
|
||||
SendVersion();
|
||||
}
|
||||
|
||||
static void CMD_051B(const uint8_t *pBuffer)
|
||||
{
|
||||
const CMD_051B_t *pCmd = (const CMD_051B_t *)pBuffer;
|
||||
REPLY_051B_t Reply;
|
||||
bool bLocked = false;
|
||||
|
||||
if (pCmd->Timestamp != Timestamp) {
|
||||
return;
|
||||
}
|
||||
|
||||
gFmRadioCountdown = 4;
|
||||
memset(&Reply, 0, sizeof(Reply));
|
||||
Reply.Header.ID = 0x051C;
|
||||
Reply.Header.Size = pCmd->Size + 4;
|
||||
Reply.Data.Offset = pCmd->Offset;
|
||||
Reply.Data.Size = pCmd->Size;
|
||||
|
||||
if (bHasCustomAesKey) {
|
||||
bLocked = gIsLocked;
|
||||
}
|
||||
|
||||
if (!bLocked) {
|
||||
EEPROM_ReadBuffer(pCmd->Offset, Reply.Data.Data, pCmd->Size);
|
||||
}
|
||||
|
||||
SendReply(&Reply, pCmd->Size + 8);
|
||||
}
|
||||
|
||||
static void CMD_051D(const uint8_t *pBuffer)
|
||||
{
|
||||
const CMD_051D_t *pCmd = (const CMD_051D_t *)pBuffer;
|
||||
REPLY_051D_t Reply;
|
||||
bool bReloadEeprom;
|
||||
bool bIsLocked;
|
||||
|
||||
if (pCmd->Timestamp != Timestamp) {
|
||||
return;
|
||||
}
|
||||
|
||||
bReloadEeprom = false;
|
||||
|
||||
gFmRadioCountdown = 4;
|
||||
Reply.Header.ID = 0x051E;
|
||||
Reply.Header.Size = sizeof(Reply.Data);
|
||||
Reply.Data.Offset = pCmd->Offset;
|
||||
|
||||
bIsLocked = bHasCustomAesKey;
|
||||
if (bHasCustomAesKey) {
|
||||
bIsLocked = gIsLocked;
|
||||
}
|
||||
|
||||
if (!bIsLocked) {
|
||||
uint16_t i;
|
||||
|
||||
for (i = 0; i < (pCmd->Size / 8U); i++) {
|
||||
uint16_t Offset = pCmd->Offset + (i * 8U);
|
||||
|
||||
if (Offset >= 0x0F30 && Offset < 0x0F40) {
|
||||
if (!gIsLocked) {
|
||||
bReloadEeprom = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((Offset < 0x0E98 || Offset >= 0x0EA0) || !bIsInLockScreen || pCmd->bAllowPassword) {
|
||||
EEPROM_WriteBuffer(Offset, &pCmd->Data[i * 8U]);
|
||||
}
|
||||
}
|
||||
|
||||
if (bReloadEeprom) {
|
||||
BOARD_EEPROM_Init();
|
||||
}
|
||||
}
|
||||
|
||||
SendReply(&Reply, sizeof(Reply));
|
||||
}
|
||||
|
||||
static void CMD_0527(void)
|
||||
{
|
||||
REPLY_0527_t Reply;
|
||||
|
||||
Reply.Header.ID = 0x0528;
|
||||
Reply.Header.Size = sizeof(Reply.Data);
|
||||
Reply.Data.RSSI = BK4819_ReadRegister(BK4819_REG_67) & 0x01FF;
|
||||
Reply.Data.ExNoiseIndicator = BK4819_ReadRegister(BK4819_REG_65) & 0x007F;
|
||||
Reply.Data.GlitchIndicator = BK4819_ReadRegister(BK4819_REG_63);
|
||||
|
||||
SendReply(&Reply, sizeof(Reply));
|
||||
}
|
||||
|
||||
static void CMD_0529(void)
|
||||
{
|
||||
REPLY_0529_t Reply;
|
||||
|
||||
Reply.Header.ID = 0x52A;
|
||||
Reply.Header.Size = sizeof(Reply.Data);
|
||||
// Original doesn't actually send current!
|
||||
BOARD_ADC_GetBatteryInfo(&Reply.Data.Voltage, &Reply.Data.Current);
|
||||
SendReply(&Reply, sizeof(Reply));
|
||||
}
|
||||
|
||||
static void CMD_052D(const uint8_t *pBuffer)
|
||||
{
|
||||
const CMD_052D_t *pCmd = (const CMD_052D_t *)pBuffer;
|
||||
REPLY_052D_t Reply;
|
||||
bool bIsLocked;
|
||||
|
||||
gFmRadioCountdown = 4;
|
||||
Reply.Header.ID = 0x052E;
|
||||
Reply.Header.Size = sizeof(Reply.Data);
|
||||
|
||||
bIsLocked = bHasCustomAesKey;
|
||||
|
||||
if (!bIsLocked) {
|
||||
bIsLocked = IsBadChallenge(gCustomAesKey, gChallenge, pCmd->Response);
|
||||
}
|
||||
if (!bIsLocked) {
|
||||
bIsLocked = IsBadChallenge(gDefaultAesKey, gChallenge, pCmd->Response);
|
||||
if (bIsLocked) {
|
||||
gTryCount++;
|
||||
}
|
||||
}
|
||||
if (gTryCount < 3) {
|
||||
if (!bIsLocked) {
|
||||
gTryCount = 0;
|
||||
}
|
||||
} else {
|
||||
gTryCount = 3;
|
||||
bIsLocked = true;
|
||||
}
|
||||
gIsLocked = bIsLocked;
|
||||
Reply.Data.bIsLocked = bIsLocked;
|
||||
SendReply(&Reply, sizeof(Reply));
|
||||
}
|
||||
|
||||
static void CMD_052F(const uint8_t *pBuffer)
|
||||
{
|
||||
const CMD_052F_t *pCmd = (const CMD_052F_t *)pBuffer;
|
||||
|
||||
gEeprom.DUAL_WATCH = DUAL_WATCH_OFF;
|
||||
gEeprom.CROSS_BAND_RX_TX = CROSS_BAND_OFF;
|
||||
gEeprom.RX_CHANNEL = 0;
|
||||
gEeprom.DTMF_SIDE_TONE = false;
|
||||
gEeprom.VfoInfo[0].FrequencyReverse = false;
|
||||
gEeprom.VfoInfo[0].pRX = &gEeprom.VfoInfo[0].ConfigRX;
|
||||
gEeprom.VfoInfo[0].pTX = &gEeprom.VfoInfo[0].ConfigTX;
|
||||
gEeprom.VfoInfo[0].FREQUENCY_DEVIATION_SETTING = FREQUENCY_DEVIATION_OFF;
|
||||
gEeprom.VfoInfo[0].DTMF_PTT_ID_TX_MODE = PTT_ID_OFF;
|
||||
gEeprom.VfoInfo[0].DTMF_DECODING_ENABLE = false;
|
||||
|
||||
#ifndef DISABLE_NOAA
|
||||
gIsNoaaMode = false;
|
||||
#endif
|
||||
|
||||
if (gCurrentFunction == FUNCTION_POWER_SAVE)
|
||||
FUNCTION_Select(FUNCTION_FOREGROUND);
|
||||
|
||||
Timestamp = pCmd->Timestamp;
|
||||
|
||||
GPIO_ClearBit(&GPIOB->DATA, GPIOB_PIN_BACKLIGHT);
|
||||
|
||||
SendVersion();
|
||||
}
|
||||
|
||||
bool UART_IsCommandAvailable(void)
|
||||
{
|
||||
uint16_t DmaLength;
|
||||
uint16_t CommandLength;
|
||||
uint16_t Index;
|
||||
uint16_t TailIndex;
|
||||
uint16_t Size;
|
||||
uint16_t CRC;
|
||||
uint16_t i;
|
||||
|
||||
DmaLength = DMA_CH0->ST & 0xFFFU;
|
||||
while (1)
|
||||
{
|
||||
if (gUART_WriteIndex == DmaLength)
|
||||
return false;
|
||||
|
||||
while (gUART_WriteIndex != DmaLength && UART_DMA_Buffer[gUART_WriteIndex] != 0xABU)
|
||||
gUART_WriteIndex = DMA_INDEX(gUART_WriteIndex, 1);
|
||||
|
||||
if (gUART_WriteIndex == DmaLength)
|
||||
return false;
|
||||
|
||||
if (gUART_WriteIndex < DmaLength)
|
||||
CommandLength = DmaLength - gUART_WriteIndex;
|
||||
else
|
||||
CommandLength = (DmaLength + sizeof(UART_DMA_Buffer)) - gUART_WriteIndex;
|
||||
|
||||
if (CommandLength < 8)
|
||||
return 0;
|
||||
|
||||
if (UART_DMA_Buffer[DMA_INDEX(gUART_WriteIndex, 1)] == 0xCD)
|
||||
break;
|
||||
|
||||
gUART_WriteIndex = DMA_INDEX(gUART_WriteIndex, 1);
|
||||
}
|
||||
|
||||
Index = DMA_INDEX(gUART_WriteIndex, 2);
|
||||
Size = (UART_DMA_Buffer[DMA_INDEX(Index, 1)] << 8) | UART_DMA_Buffer[Index];
|
||||
|
||||
if ((Size + 8) > sizeof(UART_DMA_Buffer))
|
||||
{
|
||||
gUART_WriteIndex = DmaLength;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CommandLength < (Size + 8))
|
||||
return false;
|
||||
|
||||
Index = DMA_INDEX(Index, 2);
|
||||
TailIndex = DMA_INDEX(Index, Size + 2);
|
||||
|
||||
if (UART_DMA_Buffer[TailIndex] != 0xDC || UART_DMA_Buffer[DMA_INDEX(TailIndex, 1)] != 0xBA)
|
||||
{
|
||||
gUART_WriteIndex = DmaLength;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TailIndex < Index)
|
||||
{
|
||||
const uint16_t ChunkSize = sizeof(UART_DMA_Buffer) - Index;
|
||||
memcpy(UART_Command.Buffer, UART_DMA_Buffer + Index, ChunkSize);
|
||||
memcpy(UART_Command.Buffer + ChunkSize, UART_DMA_Buffer, TailIndex);
|
||||
}
|
||||
else
|
||||
memcpy(UART_Command.Buffer, UART_DMA_Buffer + Index, TailIndex - Index);
|
||||
|
||||
TailIndex = DMA_INDEX(TailIndex, 2);
|
||||
if (TailIndex < gUART_WriteIndex)
|
||||
{
|
||||
memset(UART_DMA_Buffer + gUART_WriteIndex, 0, sizeof(UART_DMA_Buffer) - gUART_WriteIndex);
|
||||
memset(UART_DMA_Buffer, 0, TailIndex);
|
||||
}
|
||||
else
|
||||
memset(UART_DMA_Buffer + gUART_WriteIndex, 0, TailIndex - gUART_WriteIndex);
|
||||
|
||||
gUART_WriteIndex = TailIndex;
|
||||
|
||||
if (UART_Command.Header.ID == 0x0514)
|
||||
bIsEncrypted = false;
|
||||
|
||||
if (UART_Command.Header.ID == 0x6902)
|
||||
bIsEncrypted = true;
|
||||
|
||||
if (bIsEncrypted)
|
||||
for (i = 0; i < Size + 2; i++)
|
||||
UART_Command.Buffer[i] ^= Obfuscation[i % 16];
|
||||
|
||||
CRC = UART_Command.Buffer[Size] | (UART_Command.Buffer[Size + 1] << 8);
|
||||
|
||||
return (CRC_Calculate(UART_Command.Buffer, Size) != CRC) ? false : true;
|
||||
}
|
||||
|
||||
void UART_HandleCommand(void)
|
||||
{
|
||||
switch (UART_Command.Header.ID)
|
||||
{
|
||||
case 0x0514:
|
||||
CMD_0514(UART_Command.Buffer);
|
||||
break;
|
||||
|
||||
case 0x051B:
|
||||
CMD_051B(UART_Command.Buffer);
|
||||
break;
|
||||
|
||||
case 0x051D:
|
||||
CMD_051D(UART_Command.Buffer);
|
||||
break;
|
||||
|
||||
case 0x051F: // Not implementing non-authentic command
|
||||
break;
|
||||
|
||||
case 0x0521: // Not implementing non-authentic command
|
||||
break;
|
||||
|
||||
case 0x0527:
|
||||
CMD_0527();
|
||||
break;
|
||||
|
||||
case 0x0529:
|
||||
CMD_0529();
|
||||
break;
|
||||
|
||||
case 0x052D:
|
||||
CMD_052D(UART_Command.Buffer);
|
||||
break;
|
||||
|
||||
case 0x052F:
|
||||
CMD_052F(UART_Command.Buffer);
|
||||
break;
|
||||
|
||||
case 0x05DD:
|
||||
overlay_FLASH_RebootToBootloader();
|
||||
break;
|
||||
}
|
||||
}
|
26
app/uart.h
Normal file
26
app/uart.h
Normal file
@ -0,0 +1,26 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef APP_UART_H
|
||||
#define APP_UART_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
bool UART_IsCommandAvailable(void);
|
||||
void UART_HandleCommand(void);
|
||||
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user