mirror of
https://github.com/armel/uv-k1-k5v3-firmware-custom.git
synced 2026-10-04 12:07:24 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
504a2b0fb0 | ||
|
|
16b07c6374 | ||
|
|
f5b20d4f80 | ||
|
|
ae14bf5df3 | ||
|
|
9dc85852ba | ||
|
|
f7d29f6a14 | ||
|
|
1184618731 | ||
|
|
3d15d59ce0 | ||
|
|
15fccc31d6 | ||
|
|
48f753caaf | ||
|
|
edc4673983 | ||
|
|
9e03f02296 | ||
|
|
25ea1f827b | ||
|
|
8f11a32c9d | ||
|
|
576b8c552f | ||
|
|
85db5a2394 | ||
|
|
98989d4f0a | ||
|
|
8c976cfffd | ||
|
|
0ce5422db3 | ||
|
|
d939009949 | ||
|
|
50efade5c4 | ||
|
|
682a59fc0e | ||
|
|
3d3f370b86 | ||
|
|
eebeba09db | ||
|
|
6cceb26814 | ||
|
|
a77b132fa5 | ||
|
|
86aa05f1ec | ||
|
|
953f579adb | ||
|
|
d429b3afe8 | ||
|
|
a65a4f601a | ||
|
|
546cd35681 | ||
|
|
8ecf976067 | ||
|
|
3e54dc1c00 | ||
|
|
9eb9a9da5c | ||
|
|
a74529b9ec | ||
|
|
7ccb53572a | ||
|
|
c515188a06 | ||
|
|
cdcb950ccd | ||
|
|
13ca3b8a39 | ||
|
|
34acff0fbf | ||
|
|
fa75ab4243 | ||
|
|
1589d8bdd7 | ||
|
|
a5597a1ab9 | ||
|
|
955b245e66 | ||
|
|
2c4bca4202 | ||
|
|
6c4c240e9a | ||
|
|
e21d472609 | ||
|
|
8f89312e99 | ||
|
|
ec9fdd17bf | ||
|
|
1ed550fb92 | ||
|
|
7d69b74b08 | ||
|
|
f50a29ae08 | ||
|
|
7a4226f483 | ||
|
|
4523108293 | ||
|
|
c92e63da0d | ||
|
|
33d949a4be | ||
|
|
a9ff58e4a7 | ||
|
|
b8936ace0e | ||
|
|
59ed9cd827 | ||
|
|
a0e4d2708b | ||
|
|
c0c1d9881c | ||
|
|
c1d5b7bd55 | ||
|
|
932d6e954a | ||
|
|
3e6b169b7b | ||
|
|
ec4ea28325 | ||
|
|
5157dc573b | ||
|
|
a0aefae01b | ||
|
|
46cf7d7d57 | ||
|
|
9a7b199bb9 | ||
|
|
1e03dc4af0 | ||
|
|
673d1b57ab | ||
|
|
6ae360d029 | ||
|
|
d5a29aa241 | ||
|
|
e0154de186 | ||
|
|
915083522d | ||
|
|
c6e7ac33e2 | ||
|
|
bc494a4526 | ||
|
|
1ee871cc79 | ||
|
|
b83e4c2491 | ||
|
|
c1f8e945c9 | ||
|
|
9013f83734 | ||
|
|
a46604d8f3 | ||
|
|
037de071eb | ||
|
|
2c116f9503 | ||
|
|
50681ef90b | ||
|
|
0501422c64 | ||
|
|
786318453f | ||
|
|
61662f3965 | ||
|
|
b3cfda76e5 | ||
|
|
a20f1ce28e | ||
|
|
8a292755b0 | ||
|
|
915fc6aa90 | ||
|
|
cac63f978e | ||
|
|
d5edba1fc2 | ||
|
|
2f0852cd03 | ||
|
|
a65f84305e | ||
|
|
1c0d5ea628 | ||
|
|
a0a5b93408 | ||
|
|
e8cb32a4ca | ||
|
|
706dea756c | ||
|
|
1ea8aba88e | ||
|
|
c2d6a4b751 | ||
|
|
db61bce014 | ||
|
|
a3c03f18f0 | ||
|
|
8977a0a112 | ||
|
|
09a5e3c1e7 | ||
|
|
4fa821e15f | ||
|
|
c9a383919a | ||
|
|
e8ba069000 | ||
|
|
acd2c340ea | ||
|
|
24fd6fd9b1 | ||
|
|
86bb710ca4 | ||
|
|
7148c15f98 | ||
|
|
2d7093651d | ||
|
|
874e5aac4b | ||
|
|
8051605c16 | ||
|
|
134624825f | ||
|
|
7d0fcc8415 | ||
|
|
c27b72d6bc | ||
|
|
066c0d0e14 | ||
|
|
e77b87f1f2 | ||
|
|
2dd5e1a509 | ||
|
|
ab9e97805e | ||
|
|
65c584ff7d | ||
|
|
4c76ca1fb4 | ||
|
|
094896b5f1 | ||
|
|
669dad5b2e | ||
|
|
b9902e0c49 | ||
|
|
0a955c1f15 | ||
|
|
9f649b5f5b | ||
|
|
bb992ca85d | ||
|
|
679cf4ea1f | ||
|
|
0004074ffe | ||
|
|
35e5d8df90 | ||
|
|
fa0de5f576 | ||
|
|
4162618473 | ||
|
|
284af7ca1d | ||
|
|
c6f1f7b483 | ||
|
|
6cea90d507 | ||
|
|
46608af767 | ||
|
|
24167a7902 | ||
|
|
e300c4e7e3 | ||
|
|
1a815b315f | ||
|
|
a7fed2c2d8 | ||
|
|
9e92037674 | ||
|
|
e536725731 | ||
|
|
f59370cee6 | ||
|
|
bdaf320c08 | ||
|
|
fb60d6f7fd | ||
|
|
22295eedcf | ||
|
|
21c215e9fd | ||
|
|
0061613883 | ||
|
|
aecbcc6409 | ||
|
|
642dcc9a43 | ||
|
|
2b05990a2c | ||
|
|
975084baca | ||
|
|
775cd7751a | ||
|
|
c5da503cb3 | ||
|
|
5f8a492f4f | ||
|
|
8dd5da195e | ||
|
|
61aa14cd86 | ||
|
|
86a6bac145 | ||
|
|
26616c506e | ||
|
|
f8c48ceb8a | ||
|
|
3d739c642e | ||
|
|
25b96be1a3 | ||
|
|
397ef318f8 | ||
|
|
fbb06fed05 | ||
|
|
064a203db0 | ||
|
|
5987ec7b64 | ||
|
|
d72524879b | ||
|
|
db46cea0eb | ||
|
|
8f75d6813b | ||
|
|
1f577ec82e | ||
|
|
1f801757e9 | ||
|
|
4588e58af5 | ||
|
|
6fe1bec80c | ||
|
|
a2735b1167 | ||
|
|
249b0b2bb7 | ||
|
|
62320f5fbe | ||
|
|
cebbda72ec | ||
|
|
c51033db77 | ||
|
|
f863c41d40 | ||
|
|
ab7b4ca094 | ||
|
|
179c271618 | ||
|
|
2e6dfe3bc7 | ||
|
|
122cf48d3e | ||
|
|
dea747c628 | ||
|
|
bbbc553064 | ||
|
|
43c40ae9a0 | ||
|
|
6fdb17d64a | ||
|
|
aa69d698a7 | ||
|
|
f4a32b4350 | ||
|
|
688b3a5c77 | ||
|
|
ff336cd5d9 | ||
|
|
85dae05ad3 | ||
|
|
2939620066 | ||
|
|
0b3a390cb9 | ||
|
|
3d4ffd7900 | ||
|
|
06907f9b67 | ||
|
|
7c343a2b3e | ||
|
|
b4f66e9bf3 | ||
|
|
138e078984 | ||
|
|
e28177cb9b | ||
|
|
a1f2f0cdb9 | ||
|
|
e77e558d64 | ||
|
|
7d5e5b7d97 | ||
|
|
315d7a6b59 | ||
|
|
67231fd326 | ||
|
|
f4d8abd7d9 | ||
|
|
9574376d38 | ||
|
|
f2666a6248 | ||
|
|
ee5878d641 | ||
|
|
470d1959ba | ||
|
|
f5d6f5782b | ||
|
|
5cb6bbdf7a | ||
|
|
9f0547f6af | ||
|
|
fcf94e34ce | ||
|
|
5d88fda158 | ||
|
|
7b7186e293 | ||
|
|
d83c6451c8 | ||
|
|
eaa05f107e | ||
|
|
d0fdca4dc1 | ||
|
|
37f18972e7 | ||
|
|
7471ac90c5 | ||
|
|
d826cbd138 | ||
|
|
5293a8526d | ||
|
|
74aa7b354e | ||
|
|
ffddf9a9b3 | ||
|
|
77785d14c2 | ||
|
|
f715f776a4 |
No files matched your search
+4
-2
@@ -7,10 +7,12 @@ firmware
|
||||
.cache
|
||||
compile_commands.json
|
||||
.vscode
|
||||
.claude
|
||||
/docs
|
||||
k5_eeprom.raw
|
||||
|
||||
/build
|
||||
/serialtool/__pycache__
|
||||
__pycache__/
|
||||
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
AGENTS.md
|
||||
+21
-1
@@ -78,6 +78,7 @@ endif()
|
||||
target_compile_definitions(App INTERFACE
|
||||
"AUTHOR_STRING=\"${AUTHOR_STRING}\""
|
||||
"VERSION_STRING=\"${VERSION_STRING}\""
|
||||
"BUILD_COMMIT=\"${BUILD_COMMIT}\""
|
||||
)
|
||||
|
||||
if(NOT SQL_TONE)
|
||||
@@ -122,7 +123,7 @@ enable_feature(ENABLE_FMRADIO
|
||||
ui/fmradio.c
|
||||
)
|
||||
enable_feature(ENABLE_AIRCOPY
|
||||
app/aircopy.c
|
||||
app/aircopy.c
|
||||
ui/aircopy.c
|
||||
)
|
||||
enable_feature(ENABLE_NOAA)
|
||||
@@ -161,6 +162,7 @@ enable_feature(ENABLE_SQUELCH_MORE_SENSITIVE)
|
||||
enable_feature(ENABLE_FASTER_CHANNEL_SCAN)
|
||||
enable_feature(ENABLE_RSSI_BAR)
|
||||
enable_feature(ENABLE_AUDIO_BAR)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_AUDIO_SCOPE)
|
||||
enable_feature(ENABLE_COPY_CHAN_TO_VFO)
|
||||
enable_feature(ENABLE_REDUCE_LOW_MID_TX_POWER)
|
||||
enable_feature(ENABLE_BYP_RAW_DEMODULATORS)
|
||||
@@ -193,6 +195,10 @@ enable_feature(ENABLE_FEAT_F4HWN_RESUME_STATE)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_NARROWER)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_INV)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_CTR)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_SCAN_PROGRESS)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_SCAN_FASTER)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_SCAN_RSSI)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_RESCUE_OPS)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_VOL)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_AUDIO)
|
||||
@@ -202,6 +208,20 @@ enable_feature(ENABLE_FEAT_F4HWN_GMRS_FRS_MURS)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_CA)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_DEBUG)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_MEM)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_BEAM
|
||||
app/beam.c
|
||||
)
|
||||
|
||||
if(ENABLE_FEAT_F4HWN_BEAM AND NOT ENABLE_AIRCOPY)
|
||||
message(FATAL_ERROR "ENABLE_FEAT_F4HWN_BEAM requires ENABLE_AIRCOPY (it reuses g_FSK_Buffer, AIRCOPY_Obfuscate and the FSK packet plumbing).")
|
||||
endif()
|
||||
enable_feature(ENABLE_FEAT_F4HWN_QRCODE)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_LOGO)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_LOGO_SAV)
|
||||
|
||||
if(ENABLE_FEAT_F4HWN_LOGO_SAV AND NOT ENABLE_FEAT_F4HWN_LOGO)
|
||||
message(FATAL_ERROR "ENABLE_FEAT_F4HWN_LOGO_SAV requires ENABLE_FEAT_F4HWN_LOGO.")
|
||||
endif()
|
||||
|
||||
# ---- DEBUGGING ----
|
||||
|
||||
|
||||
+93
-82
@@ -40,10 +40,14 @@
|
||||
#include "misc.h"
|
||||
#include "settings.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/main.h"
|
||||
#include "ui/ui.h"
|
||||
#ifdef ENABLE_REGA
|
||||
#include "app/rega.h"
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
#include "app/beam.h"
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_FMRADIO)
|
||||
static void ACTION_Scan_FM(bool bRestart);
|
||||
@@ -131,6 +135,9 @@ void (*action_opt_table[])(void) = {
|
||||
[ACTION_OPT_REGA_ALARM] = &ACTION_RegaAlarm,
|
||||
[ACTION_OPT_REGA_TEST] = &ACTION_RegaTest,
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
[ACTION_OPT_BEAM] = &ACTION_Beam,
|
||||
#endif
|
||||
};
|
||||
|
||||
static_assert(ARRAY_SIZE(action_opt_table) == ACTION_OPT_LEN);
|
||||
@@ -212,7 +219,7 @@ void ACTION_Scan(bool bRestart)
|
||||
DTMF_clear_RX();
|
||||
#endif
|
||||
gDTMF_RX_live_timeout = 0;
|
||||
memset(gDTMF_RX_live, 0, sizeof(gDTMF_RX_live));
|
||||
DTMF_clear_input_box_memory();
|
||||
|
||||
RADIO_SelectVfos();
|
||||
|
||||
@@ -237,15 +244,14 @@ void ACTION_Scan(bool bRestart)
|
||||
|
||||
// channel mode. Keep scanning but toggle between scan lists
|
||||
RADIO_NextValidList(1);
|
||||
UI_MAIN_NotifyScanProgressDataChanged();
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
SETTINGS_WriteCurrentState();
|
||||
#endif
|
||||
|
||||
// jump to the next channel
|
||||
CHFRSCANNER_Start(false, gScanStateDir);
|
||||
gScanPauseDelayIn_10ms = 1;
|
||||
gScheduleScanListen = false;
|
||||
CHFRSCANNER_ManualResume(gScanStateDir);
|
||||
} else {
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
if(gScanRangeStart == 0) // No ScanRange
|
||||
@@ -290,6 +296,8 @@ void ACTION_SwitchDemodul(void)
|
||||
|
||||
void ACTION_Handle(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
HideFKeyIcon();
|
||||
|
||||
if (gScreenToDisplay == DISPLAY_MAIN && gDTMF_InputMode){
|
||||
// entering DTMF code
|
||||
|
||||
@@ -319,45 +327,73 @@ void ACTION_Handle(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
return;
|
||||
}
|
||||
|
||||
enum ACTION_OPT_t funcShort = ACTION_OPT_NONE;
|
||||
enum ACTION_OPT_t funcLong = ACTION_OPT_NONE;
|
||||
enum ACTION_OPT_t func = ACTION_OPT_NONE;
|
||||
switch(Key) {
|
||||
case KEY_SIDE1:
|
||||
funcShort = gEeprom.KEY_1_SHORT_PRESS_ACTION;
|
||||
funcLong = gEeprom.KEY_1_LONG_PRESS_ACTION;
|
||||
if (bKeyHeld)
|
||||
func = gEeprom.KEY_1_LONG_PRESS_ACTION;
|
||||
else
|
||||
func = gEeprom.KEY_1_SHORT_PRESS_ACTION;
|
||||
break;
|
||||
case KEY_SIDE2:
|
||||
funcShort = gEeprom.KEY_2_SHORT_PRESS_ACTION;
|
||||
funcLong = gEeprom.KEY_2_LONG_PRESS_ACTION;
|
||||
if (bKeyHeld)
|
||||
func = gEeprom.KEY_2_LONG_PRESS_ACTION;
|
||||
else
|
||||
func = gEeprom.KEY_2_SHORT_PRESS_ACTION;
|
||||
break;
|
||||
case KEY_MENU:
|
||||
funcLong = gEeprom.KEY_M_LONG_PRESS_ACTION;
|
||||
if (bKeyHeld)
|
||||
func = gEeprom.KEY_M_LONG_PRESS_ACTION;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!bKeyHeld && bKeyPressed) // button pushed
|
||||
{
|
||||
if (bKeyHeld != bKeyPressed) { // button pushed or released after hold
|
||||
// (!bKeyHeld && bKeyPressed) or (bKeyHeld && !bKeyPressed)
|
||||
return;
|
||||
}
|
||||
|
||||
// held or released beyond this point
|
||||
// held or released after short press
|
||||
|
||||
if(!(bKeyHeld && !bKeyPressed)) // don't beep on released after hold
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
if (gFmRadioMode) { // do not run these actions in FM radio mode
|
||||
switch (func) {
|
||||
case ACTION_OPT_POWER:
|
||||
case ACTION_OPT_MONITOR:
|
||||
case ACTION_OPT_A_B:
|
||||
case ACTION_OPT_VFO_MR:
|
||||
case ACTION_OPT_SWITCH_DEMODUL:
|
||||
#ifdef ENABLE_VOX
|
||||
case ACTION_OPT_VOX:
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
case ACTION_OPT_RXMODE:
|
||||
case ACTION_OPT_MAINONLY:
|
||||
case ACTION_OPT_WN:
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
case ACTION_OPT_RXA:
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
case ACTION_OPT_POWER_HIGH:
|
||||
case ACTION_OPT_REMOVE_OFFSET:
|
||||
#endif
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
case ACTION_OPT_BEAM:
|
||||
#endif
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
|
||||
if (bKeyHeld || bKeyPressed) // held
|
||||
{
|
||||
funcShort = funcLong;
|
||||
|
||||
if (!bKeyPressed) //ignore release if held
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// held or released after short press beyond this point
|
||||
|
||||
action_opt_table[funcShort]();
|
||||
action_opt_table[func]();
|
||||
}
|
||||
|
||||
|
||||
@@ -537,7 +573,11 @@ void ACTION_MainOnly(void)
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
void ACTION_RxA(void)
|
||||
{
|
||||
gSetting_set_audio = (gSetting_set_audio + 1) % 5;
|
||||
if(gRxVfo->Modulation == MODULATION_AM)
|
||||
gSetting_set_audio_am = (gSetting_set_audio_am + 1) % 3;
|
||||
else if (gRxVfo->Modulation == MODULATION_FM)
|
||||
gSetting_set_audio_fm = (gSetting_set_audio_fm + 1) % 5;
|
||||
|
||||
RADIO_SetModulation(gRxVfo->Modulation);
|
||||
}
|
||||
#endif
|
||||
@@ -545,64 +585,36 @@ void ACTION_RxA(void)
|
||||
void ACTION_Ptt(void)
|
||||
{
|
||||
gSetting_set_ptt_session = !gSetting_set_ptt_session;
|
||||
|
||||
ACTION_Update();
|
||||
}
|
||||
|
||||
void ACTION_Wn(void)
|
||||
{
|
||||
if (gRxVfo->Modulation == MODULATION_AM)
|
||||
const bool isRx = FUNCTION_IsRx();
|
||||
VFO_Info_t *pVfo = isRx ? gRxVfo : gTxVfo;
|
||||
|
||||
pVfo->CHANNEL_BANDWIDTH = !pVfo->CHANNEL_BANDWIDTH;
|
||||
|
||||
if (pVfo->Modulation == MODULATION_AM)
|
||||
{
|
||||
BK4819_SetFilterBandwidth(BK4819_FILTER_BW_AM, true);
|
||||
BK4819_SetFilterBandwidth(RADIO_GetAMFilterBandwidth(pVfo), true);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t bw = pVfo->CHANNEL_BANDWIDTH;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
bool narrower = 0;
|
||||
if (FUNCTION_IsRx())
|
||||
if (isRx && bw == BANDWIDTH_NARROW && gSetting_set_nfm == 1)
|
||||
{
|
||||
gRxVfo->CHANNEL_BANDWIDTH = (gRxVfo->CHANNEL_BANDWIDTH == 0) ? 1: 0;
|
||||
if(gRxVfo->CHANNEL_BANDWIDTH == BANDWIDTH_NARROW && gSetting_set_nfm == 1)
|
||||
{
|
||||
narrower = 1;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_AM_FIX
|
||||
BK4819_SetFilterBandwidth(gRxVfo->CHANNEL_BANDWIDTH + narrower, true);
|
||||
#else
|
||||
BK4819_SetFilterBandwidth(gRxVfo->CHANNEL_BANDWIDTH + narrower, false);
|
||||
#endif
|
||||
bw++;
|
||||
}
|
||||
else
|
||||
{
|
||||
gTxVfo->CHANNEL_BANDWIDTH = (gTxVfo->CHANNEL_BANDWIDTH == 0) ? 1: 0;
|
||||
if(gTxVfo->CHANNEL_BANDWIDTH == BANDWIDTH_NARROW && gSetting_set_nfm == 1)
|
||||
{
|
||||
narrower = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_AM_FIX
|
||||
BK4819_SetFilterBandwidth(gTxVfo->CHANNEL_BANDWIDTH, true);
|
||||
#else
|
||||
BK4819_SetFilterBandwidth(gTxVfo->CHANNEL_BANDWIDTH, false);
|
||||
#endif
|
||||
}
|
||||
#ifdef ENABLE_AM_FIX
|
||||
BK4819_SetFilterBandwidth(bw, true);
|
||||
#else
|
||||
if (FUNCTION_IsRx())
|
||||
{
|
||||
gRxVfo->CHANNEL_BANDWIDTH = (gRxVfo->CHANNEL_BANDWIDTH == 0) ? 1: 0;
|
||||
#ifdef ENABLE_AM_FIX
|
||||
BK4819_SetFilterBandwidth(gRxVfo->CHANNEL_BANDWIDTH, true);
|
||||
#else
|
||||
BK4819_SetFilterBandwidth(gRxVfo->CHANNEL_BANDWIDTH, false);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
gTxVfo->CHANNEL_BANDWIDTH = (gTxVfo->CHANNEL_BANDWIDTH == 0) ? 1: 0;
|
||||
#ifdef ENABLE_AM_FIX
|
||||
BK4819_SetFilterBandwidth(gTxVfo->CHANNEL_BANDWIDTH, true);
|
||||
#else
|
||||
BK4819_SetFilterBandwidth(gTxVfo->CHANNEL_BANDWIDTH, false);
|
||||
#endif
|
||||
}
|
||||
BK4819_SetFilterBandwidth(bw, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -649,26 +661,25 @@ void ACTION_Mute(void)
|
||||
BK1080_WriteRegister(BK1080_REG_05_SYSTEM_CONFIGURATION2, gMute ? 0x0A10 : 0x0A1F);
|
||||
#endif
|
||||
gEeprom.VOLUME_GAIN = gMute ? 0 : gEeprom.VOLUME_GAIN_BACKUP;
|
||||
BK4819_WriteRegister(BK4819_REG_48,
|
||||
(11u << 12) | // ??? .. 0 ~ 15, doesn't seem to make any difference
|
||||
(0u << 10) | // AF Rx Gain-1
|
||||
(gEeprom.VOLUME_GAIN << 4) | // AF Rx Gain-2
|
||||
(gEeprom.DAC_GAIN << 0)); // AF DAC Gain (after Gain-1 and Gain-2)
|
||||
BK4819_SetRxAudioGain();
|
||||
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
void ACTION_ToggleVfoSetting(bool *setting) {
|
||||
*setting = !(*setting);
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
}
|
||||
|
||||
void ACTION_Power_High(void)
|
||||
{
|
||||
gPowerHigh = !gPowerHigh;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
ACTION_ToggleVfoSetting(&gPowerHigh);
|
||||
}
|
||||
|
||||
void ACTION_Remove_Offset(void)
|
||||
{
|
||||
gRemoveOffset = !gRemoveOffset;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
ACTION_ToggleVfoSetting(&gRemoveOffset);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
+66
-110
@@ -39,7 +39,7 @@ static const uint16_t Obfuscation[8] = { 0x6C16, 0xE614, 0x912E, 0x400D, 0x3521,
|
||||
AIRCOPY_State_t gAircopyState;
|
||||
uint16_t gAirCopyBlockNumber;
|
||||
uint16_t gErrorsDuringAirCopy;
|
||||
uint8_t gAirCopyIsSendMode;
|
||||
bool gAirCopyIsSendMode;
|
||||
|
||||
uint16_t g_FSK_Buffer[36];
|
||||
|
||||
@@ -90,12 +90,14 @@ DECLARE_AIRCOPY_BANK(1)
|
||||
static const AIRCOPY_Segment_t AIRCOPY_Segments_Settings[] = {
|
||||
{ 0xA000, 0xA170, AIRCOPY_WRITE_BYTES },
|
||||
{ 0x880E, 0x886E, AIRCOPY_WRITE_BYTES },
|
||||
{ 0x9000, 0x90D6, AIRCOPY_WRITE_BYTES }, // VFO area (so scan-range source frequencies are replicated)
|
||||
};
|
||||
|
||||
// total_blocks = ceil(0x170/64) + ceil(0x60/64) + ceil(0xD6/64) = 6 + 2 + 4 = 12
|
||||
static const AIRCOPY_TransferMap_t AIRCOPY_Map_Settings = {
|
||||
.segments = AIRCOPY_Segments_Settings,
|
||||
.num_segments = 2,
|
||||
.total_blocks = 8
|
||||
.num_segments = 3,
|
||||
.total_blocks = 12
|
||||
};
|
||||
|
||||
// Finally
|
||||
@@ -139,7 +141,7 @@ static void AIRCOPY_clear()
|
||||
crc[i] = 0;
|
||||
}
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot(true);
|
||||
SCREENSHOT_Update(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -158,8 +160,10 @@ static inline const AIRCOPY_Segment_t *AIRCOPY_FindSegmentForOffset(uint16_t off
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void AIRCOPY_CheckComplete(void)
|
||||
static inline void AIRCOPY_CheckComplete(uint16_t *num)
|
||||
{
|
||||
*num = *num + 1;
|
||||
|
||||
const AIRCOPY_TransferMap_t *map = AIRCOPY_GetCurrentMap();
|
||||
uint16_t done = gAirCopyBlockNumber + gErrorsDuringAirCopy;
|
||||
|
||||
@@ -167,11 +171,18 @@ static inline void AIRCOPY_CheckComplete(void)
|
||||
{
|
||||
gAircopyState = AIRCOPY_COMPLETE;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot(false);
|
||||
SCREENSHOT_Update(false);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void AIRCOPY_Obfuscate(unsigned int count)
|
||||
{
|
||||
for (unsigned int i = 0; i < count; i++) {
|
||||
g_FSK_Buffer[i + 1] ^= Obfuscation[i % 8];
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Send/Receive Functions
|
||||
// ============================================================================
|
||||
@@ -212,7 +223,7 @@ bool AIRCOPY_SendMessage(void)
|
||||
if (CurrentSegmentIndex >= map->num_segments) {
|
||||
gAircopyState = AIRCOPY_COMPLETE;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot(false);
|
||||
SCREENSHOT_Update(false);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
@@ -223,9 +234,7 @@ bool AIRCOPY_SendMessage(void)
|
||||
|
||||
g_FSK_Buffer[34] = CRC_Calculate(&g_FSK_Buffer[1], 2 + 64);
|
||||
|
||||
for (unsigned int i = 0; i < 34; i++) {
|
||||
g_FSK_Buffer[i + 1] ^= Obfuscation[i % 8];
|
||||
}
|
||||
AIRCOPY_Obfuscate(34);
|
||||
|
||||
RADIO_SetTxParameters();
|
||||
|
||||
@@ -252,93 +261,61 @@ void AIRCOPY_StorePacket(void)
|
||||
BK4819_PrepareFSKReceive();
|
||||
|
||||
if ((Status & 0x0010U) != 0 || g_FSK_Buffer[0] != 0xABCD || g_FSK_Buffer[35] != 0xDCBA) {
|
||||
gErrorsDuringAirCopy++;
|
||||
|
||||
BK4819_ResetFSK(); // <- important
|
||||
BK4819_PrepareFSKReceive(); // <- re-arm proprement
|
||||
|
||||
AIRCOPY_CheckComplete();
|
||||
AIRCOPY_CheckComplete(&gErrorsDuringAirCopy);
|
||||
return;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < 34; i++) {
|
||||
g_FSK_Buffer[i + 1] ^= Obfuscation[i % 8];
|
||||
}
|
||||
AIRCOPY_Obfuscate(34);
|
||||
|
||||
uint16_t Crc = CRC_Calculate(&g_FSK_Buffer[1], 2 + 64);
|
||||
if (g_FSK_Buffer[34] != Crc) {
|
||||
gErrorsDuringAirCopy++;
|
||||
AIRCOPY_CheckComplete();
|
||||
AIRCOPY_CheckComplete(&gErrorsDuringAirCopy);
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t Offset = g_FSK_Buffer[1];
|
||||
const AIRCOPY_TransferMap_t *map = AIRCOPY_GetCurrentMap();
|
||||
|
||||
// Validate offset is in the map
|
||||
bool validOffset = false;
|
||||
for (uint16_t i = 0; i < map->num_segments; i++) {
|
||||
if (Offset >= map->segments[i].start_offset && Offset < map->segments[i].end_offset) {
|
||||
validOffset = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!validOffset) {
|
||||
gErrorsDuringAirCopy++;
|
||||
AIRCOPY_CheckComplete();
|
||||
return;
|
||||
}
|
||||
|
||||
const AIRCOPY_Segment_t *seg = AIRCOPY_FindSegmentForOffset(Offset);
|
||||
|
||||
|
||||
if (seg == NULL) {
|
||||
gErrorsDuringAirCopy++;
|
||||
AIRCOPY_CheckComplete();
|
||||
AIRCOPY_CheckComplete(&gErrorsDuringAirCopy);
|
||||
return;
|
||||
}
|
||||
|
||||
if (seg->write_mode == AIRCOPY_WRITE_BYTES)
|
||||
{
|
||||
/* Raw bytes stream, written in 8-byte EEPROM chunks */
|
||||
const uint8_t *p8 = (const uint8_t *)&g_FSK_Buffer[2];
|
||||
const uint8_t *pData = (const uint8_t *)&g_FSK_Buffer[2];
|
||||
|
||||
for (unsigned int i = 0; i < 8; i++)
|
||||
{
|
||||
EEPROM_WriteBuffer(Offset + (i * 8), p8 + (i * 8));
|
||||
}
|
||||
}
|
||||
else
|
||||
for (unsigned int i = 0; i < 8; i++)
|
||||
{
|
||||
/* Structured data path (kept as-is) */
|
||||
const uint16_t *pData = &g_FSK_Buffer[2];
|
||||
|
||||
for (unsigned int i = 0; i < 8; i++)
|
||||
{
|
||||
EEPROM_WriteBuffer(Offset, pData);
|
||||
pData += 4;
|
||||
Offset += 8;
|
||||
}
|
||||
EEPROM_WriteBuffer(Offset + (i * 8), pData + (i * 8));
|
||||
}
|
||||
|
||||
gAirCopyBlockNumber++;
|
||||
AIRCOPY_CheckComplete();
|
||||
AIRCOPY_CheckComplete(&gAirCopyBlockNumber);
|
||||
}
|
||||
|
||||
static void AIRCOPY_InitTransfer(bool isSendMode)
|
||||
{
|
||||
gAircopyStep = 1;
|
||||
gFSKWriteIndex = 0;
|
||||
gAirCopyBlockNumber = 0;
|
||||
gInputBoxIndex = 0;
|
||||
gAirCopyIsSendMode = isSendMode;
|
||||
|
||||
AIRCOPY_clear();
|
||||
|
||||
gAircopyState = AIRCOPY_TRANSFER;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Key Processing
|
||||
// ============================================================================
|
||||
|
||||
static void AIRCOPY_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
static void AIRCOPY_Key_DIGITS(KEY_Code_t Key)
|
||||
{
|
||||
if (bKeyHeld || !bKeyPressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
INPUTBOX_Append(Key);
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_AIRCOPY;
|
||||
|
||||
if (gInputBoxIndex < 6) {
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
@@ -373,64 +350,32 @@ static void AIRCOPY_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
BK4819_ResetFSK();
|
||||
return;
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_AIRCOPY;
|
||||
}
|
||||
|
||||
static void AIRCOPY_Key_EXIT(bool bKeyPressed, bool bKeyHeld)
|
||||
static void AIRCOPY_Key_EXIT()
|
||||
{
|
||||
if (bKeyHeld || !bKeyPressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gInputBoxIndex == 0) {
|
||||
gAircopyStep = 1;
|
||||
gFSKWriteIndex = 0;
|
||||
gAirCopyBlockNumber = 0;
|
||||
gInputBoxIndex = 0;
|
||||
AIRCOPY_InitTransfer(0); // Mode: Receive
|
||||
gErrorsDuringAirCopy = lErrorsDuringAirCopy = 0;
|
||||
gAirCopyIsSendMode = 0;
|
||||
|
||||
AIRCOPY_clear();
|
||||
|
||||
BK4819_PrepareFSKReceive();
|
||||
|
||||
gAircopyState = AIRCOPY_TRANSFER;
|
||||
|
||||
} else {
|
||||
gInputBox[--gInputBoxIndex] = 10;
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_AIRCOPY;
|
||||
}
|
||||
|
||||
static void AIRCOPY_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
static void AIRCOPY_Key_MENU()
|
||||
{
|
||||
if (bKeyHeld || !bKeyPressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
gAircopyStep = 1;
|
||||
gFSKWriteIndex = 0;
|
||||
gAirCopyBlockNumber = 0;
|
||||
gInputBoxIndex = 0;
|
||||
gAirCopyIsSendMode = 1;
|
||||
AIRCOPY_InitTransfer(1); // Mode: Send
|
||||
|
||||
g_FSK_Buffer[0] = 0xABCD;
|
||||
g_FSK_Buffer[1] = 0;
|
||||
g_FSK_Buffer[35] = 0xDCBA;
|
||||
|
||||
AIRCOPY_clear();
|
||||
|
||||
GUI_DisplayScreen();
|
||||
|
||||
gAircopyState = AIRCOPY_TRANSFER;
|
||||
}
|
||||
|
||||
static void AIRCOPY_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
static void AIRCOPY_Key_UP_DOWN(int8_t Direction)
|
||||
{
|
||||
if (bKeyHeld || !bKeyPressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gEeprom.SET_NAV) {
|
||||
Direction = -Direction;
|
||||
}
|
||||
@@ -444,29 +389,40 @@ static void AIRCOPY_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Directio
|
||||
gAircopyCurrentMapIndex = (gAircopyCurrentMapIndex + AIRCOPY_NUM_MAPS - 1) % AIRCOPY_NUM_MAPS;
|
||||
break;
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_AIRCOPY;
|
||||
}
|
||||
|
||||
void AIRCOPY_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if (bKeyHeld || !bKeyPressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Key != KEY_PTT) {
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
}
|
||||
|
||||
switch (Key) {
|
||||
case KEY_0...KEY_9:
|
||||
AIRCOPY_Key_DIGITS(Key, bKeyPressed, bKeyHeld);
|
||||
AIRCOPY_Key_DIGITS(Key);
|
||||
break;
|
||||
case KEY_MENU:
|
||||
AIRCOPY_Key_MENU(bKeyPressed, bKeyHeld);
|
||||
AIRCOPY_Key_MENU();
|
||||
break;
|
||||
case KEY_EXIT:
|
||||
AIRCOPY_Key_EXIT(bKeyPressed, bKeyHeld);
|
||||
AIRCOPY_Key_EXIT();
|
||||
break;
|
||||
case KEY_UP:
|
||||
case KEY_DOWN:
|
||||
AIRCOPY_Key_UP_DOWN(bKeyPressed, bKeyHeld, Key == KEY_UP ? 1 : -1);
|
||||
AIRCOPY_Key_UP_DOWN(Key == KEY_UP ? 1 : -1);
|
||||
break;
|
||||
case KEY_PTT:
|
||||
break;
|
||||
default:
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
break;
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_AIRCOPY;
|
||||
}
|
||||
|
||||
#endif
|
||||
+5
-1
@@ -92,7 +92,7 @@ typedef enum {
|
||||
extern AIRCOPY_State_t gAircopyState;
|
||||
extern uint16_t gAirCopyBlockNumber;
|
||||
extern uint16_t gErrorsDuringAirCopy;
|
||||
extern uint8_t gAirCopyIsSendMode;
|
||||
extern bool gAirCopyIsSendMode;
|
||||
|
||||
extern uint16_t g_FSK_Buffer[36];
|
||||
|
||||
@@ -106,5 +106,9 @@ void AIRCOPY_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld);
|
||||
|
||||
const AIRCOPY_TransferMap_t* AIRCOPY_GetCurrentMap(void);
|
||||
|
||||
// XOR-obfuscate `count` words of g_FSK_Buffer starting at index 1.
|
||||
// Self-inverse: applying twice restores the original buffer.
|
||||
void AIRCOPY_Obfuscate(unsigned int count);
|
||||
|
||||
#endif // ENABLE_AIRCOPY
|
||||
#endif // APP_AIRCOPY_H
|
||||
+490
-145
@@ -24,6 +24,9 @@
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
#include "app/aircopy.h"
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
#include "app/beam.h"
|
||||
#endif
|
||||
#include "app/app.h"
|
||||
#include "app/chFrScanner.h"
|
||||
#include "app/dtmf.h"
|
||||
@@ -69,11 +72,13 @@
|
||||
#include "sram-overlay.h"
|
||||
#endif
|
||||
#include "ui/battery.h"
|
||||
#include "ui/helper.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/main.h"
|
||||
#include "ui/menu.h"
|
||||
#include "ui/status.h"
|
||||
#include "ui/ui.h"
|
||||
#include "ui/welcome.h"
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
#include "screenshot.h"
|
||||
@@ -83,6 +88,19 @@ static bool flagSaveVfo;
|
||||
static bool flagSaveSettings;
|
||||
static bool flagSaveChannel;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SLEEP
|
||||
static KEY_Code_t gSleepWakeKey = KEY_INVALID;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
static bool gScreenSaverDisplayed;
|
||||
static uint8_t gScreenSaverTick;
|
||||
static uint16_t gMatrixRandom = 0xACE1;
|
||||
static uint8_t gMatrixHeads[32];
|
||||
static uint8_t gMatrixSpeeds[32];
|
||||
static KEY_Code_t gScreenSaverWakeKey = KEY_INVALID;
|
||||
#endif
|
||||
|
||||
static void ProcessKey(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld);
|
||||
|
||||
|
||||
@@ -107,11 +125,192 @@ static_assert(ARRAY_SIZE(ProcessKeysFunctions) == DISPLAY_N_ELEM-1);
|
||||
static_assert(ARRAY_SIZE(ProcessKeysFunctions) == DISPLAY_N_ELEM);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
static uint8_t ScreenSaverRandom(void)
|
||||
{
|
||||
gMatrixRandom = (uint16_t)(gMatrixRandom * 2053u + 13849u);
|
||||
return (uint8_t)(gMatrixRandom >> 8);
|
||||
}
|
||||
|
||||
static void ScreenSaverSetPixel(uint8_t x, uint8_t y, bool fill)
|
||||
{
|
||||
const uint8_t pattern = 1u << (y & 7u);
|
||||
|
||||
uint8_t *target = (y < 8) ? &gStatusLine[x] : &gFrameBuffer[(y >> 3) - 1][x];
|
||||
|
||||
if (fill)
|
||||
*target |= pattern;
|
||||
else
|
||||
*target &= (uint8_t)~pattern;
|
||||
}
|
||||
|
||||
static void ScreenSaverDrawMatrixGlyph(uint8_t x, int16_t y, uint8_t glyph)
|
||||
{
|
||||
static const uint8_t glyphs[][3] = {
|
||||
{0x1F, 0x11, 0x1F},
|
||||
{0x15, 0x1F, 0x15},
|
||||
{0x1D, 0x15, 0x17},
|
||||
{0x17, 0x15, 0x1D},
|
||||
{0x1F, 0x04, 0x1F},
|
||||
{0x1B, 0x15, 0x1B},
|
||||
{0x0E, 0x11, 0x0E},
|
||||
{0x11, 0x0A, 0x04}
|
||||
};
|
||||
|
||||
for (uint8_t dx = 0; dx < 3; dx++) {
|
||||
uint8_t pixels = glyphs[glyph & 7u][dx];
|
||||
for (uint8_t dy = 0; dy < 5; dy++) {
|
||||
const int16_t py = y + dy;
|
||||
if ((pixels & (1u << dy)) && py >= 0 && py < LCD_HEIGHT)
|
||||
ScreenSaverSetPixel(x + dx, (uint8_t)py, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ScreenSaverRenderMatrix(bool reset)
|
||||
{
|
||||
if (reset) {
|
||||
for (uint8_t i = 0; i < ARRAY_SIZE(gMatrixHeads); i++) {
|
||||
gMatrixHeads[i] = ScreenSaverRandom() % 112u;
|
||||
gMatrixSpeeds[i] = 1u + (ScreenSaverRandom() % 3u);
|
||||
}
|
||||
} else {
|
||||
for (uint8_t i = 0; i < ARRAY_SIZE(gMatrixHeads); i++) {
|
||||
gMatrixHeads[i] += gMatrixSpeeds[i];
|
||||
if (gMatrixHeads[i] > 112u)
|
||||
gMatrixHeads[i] = ScreenSaverRandom() & 0x0Fu;
|
||||
}
|
||||
}
|
||||
|
||||
UI_StatusClear();
|
||||
UI_DisplayClear();
|
||||
|
||||
for (uint8_t col = 0; col < ARRAY_SIZE(gMatrixHeads); col++) {
|
||||
const uint8_t x = col * 4u;
|
||||
for (uint8_t trail = 0; trail < 8; trail++) {
|
||||
const int16_t y = (int16_t)gMatrixHeads[col] - ((int16_t)trail * 7);
|
||||
const uint8_t glyph = (uint8_t)(col + trail + gMatrixHeads[col]);
|
||||
|
||||
if (trail == 0) {
|
||||
ScreenSaverDrawMatrixGlyph(x, y, glyph);
|
||||
} else if ((trail < 4) || ((glyph & 1u) != 0)) {
|
||||
ScreenSaverDrawMatrixGlyph(x, y, glyph);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ST7565_BlitStatusLine();
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
|
||||
static void ScreenSaverScrollLogoLine(uint8_t *line)
|
||||
{
|
||||
const uint8_t first = line[0];
|
||||
|
||||
memmove(line, line + 1, LCD_WIDTH - 1);
|
||||
line[LCD_WIDTH - 1] = first;
|
||||
}
|
||||
|
||||
static void ScreenSaverRenderLogoPlus(bool reset)
|
||||
{
|
||||
if (reset) {
|
||||
UI_DisplayLogo();
|
||||
return;
|
||||
}
|
||||
|
||||
ScreenSaverScrollLogoLine(gStatusLine);
|
||||
for (uint8_t line = 0; line < FRAME_LINES; line++)
|
||||
ScreenSaverScrollLogoLine(gFrameBuffer[line]);
|
||||
|
||||
ST7565_BlitStatusLine();
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
|
||||
static void ScreenSaverUpdateViewer(void)
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
SCREENSHOT_Update(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool ScreenSaverCanDisplay(void)
|
||||
{
|
||||
if (gSetting_set_sav == SET_SAV_OFF ||
|
||||
gEeprom.BACKLIGHT_TIME == 0 ||
|
||||
gEeprom.BACKLIGHT_TIME >= 61 ||
|
||||
gScreenSaverDisplayed ||
|
||||
gCurrentFunction == FUNCTION_TRANSMIT ||
|
||||
FUNCTION_IsRx() ||
|
||||
gPttIsPressed
|
||||
#ifdef ENABLE_FMRADIO
|
||||
|| (gFM_ScanState != FM_SCAN_OFF && !gFM_FoundFrequency)
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
|| gBeamActive
|
||||
#endif
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gScreenToDisplay == DISPLAY_MAIN)
|
||||
return true;
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
if (gScreenToDisplay == DISPLAY_FM)
|
||||
return true;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ScreenSaverTryDisplay(void)
|
||||
{
|
||||
if (!ScreenSaverCanDisplay())
|
||||
return;
|
||||
|
||||
if (gSetting_set_sav == SET_SAV_LOGO)
|
||||
UI_DisplayLogo();
|
||||
else if (gSetting_set_sav == SET_SAV_LOGO_PLUS)
|
||||
ScreenSaverRenderLogoPlus(true);
|
||||
else if (gSetting_set_sav == SET_SAV_MATRIX)
|
||||
ScreenSaverRenderMatrix(true);
|
||||
|
||||
gScreenSaverDisplayed = true;
|
||||
gScreenSaverTick = 0;
|
||||
gUpdateDisplay = false;
|
||||
gUpdateStatus = false;
|
||||
ScreenSaverUpdateViewer();
|
||||
}
|
||||
|
||||
static void ScreenSaverExit(void)
|
||||
{
|
||||
if (gScreenSaverDisplayed) {
|
||||
gScreenSaverDisplayed = false;
|
||||
gUpdateDisplay = true;
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool APP_IsScreenSaverDisplayed(void)
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
return gScreenSaverDisplayed;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void CheckForIncoming(void)
|
||||
{
|
||||
if (!g_SquelchLost)
|
||||
return; // squelch is closed
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
ScreenSaverExit();
|
||||
#endif
|
||||
|
||||
// squelch is open
|
||||
|
||||
if (gScanStateDir == SCAN_OFF)
|
||||
@@ -492,6 +691,10 @@ void APP_StartListening(FUNCTION_Type_t function)
|
||||
{
|
||||
const unsigned int vfo = gEeprom.RX_VFO;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
ScreenSaverExit();
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RX_TX_TIMER
|
||||
gRxTimerCountdown_500ms = 7200;
|
||||
#endif
|
||||
@@ -535,7 +738,17 @@ void APP_StartListening(FUNCTION_Type_t function)
|
||||
gEeprom.DUAL_WATCH != DUAL_WATCH_OFF)
|
||||
{ // not scanning, dual watch is enabled
|
||||
|
||||
gDualWatchCountdown_10ms = dual_watch_count_after_2_10ms;
|
||||
//gDualWatchCountdown_10ms = dual_watch_count_after_2_10ms;
|
||||
|
||||
const bool isMainTxDualRx =
|
||||
(gEeprom.DUAL_WATCH != DUAL_WATCH_OFF) &&
|
||||
(gEeprom.CROSS_BAND_RX_TX != CROSS_BAND_OFF);
|
||||
|
||||
// Use a short hold only for MAIN TX DUAL RX, keep legacy hold otherwise
|
||||
gDualWatchCountdown_10ms = isMainTxDualRx
|
||||
? dual_watch_count_after_2_10ms / 4 // Short timer = 420 / 4 ...
|
||||
: dual_watch_count_after_2_10ms;
|
||||
|
||||
gScheduleDualWatch = false;
|
||||
|
||||
// when crossband is active only the main VFO should be used for TX
|
||||
@@ -547,11 +760,7 @@ void APP_StartListening(FUNCTION_Type_t function)
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
|
||||
BK4819_WriteRegister(BK4819_REG_48,
|
||||
(11u << 12) | // ??? .. 0 to 15, doesn't seem to make any difference
|
||||
( 0u << 10) | // AF Rx Gain-1
|
||||
(gEeprom.VOLUME_GAIN << 4) | // AF Rx Gain-2
|
||||
(gEeprom.DAC_GAIN << 0)); // AF DAC Gain (after Gain-1 and Gain-2)
|
||||
BK4819_SetRxAudioGain();
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
if (gVoiceWriteIndex == 0) // AM/FM RX mode will be set when the voice has finished
|
||||
@@ -790,6 +999,25 @@ static void CheckRadioInterrupts(void)
|
||||
AIRCOPY_StorePacket();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
if ((interrupts.fskFifoAlmostFull || interrupts.fskRxFinied) &&
|
||||
gBeamActive &&
|
||||
gBeamMode == BEAM_MODE_RX &&
|
||||
(gBeamStatus == BEAM_STATUS_RX_WAIT || gBeamStatus == BEAM_STATUS_ERROR))
|
||||
{
|
||||
const unsigned int wordsToRead = interrupts.fskRxFinied ? (36 - gFSKWriteIndex) : 4;
|
||||
for (unsigned int i = 0; i < wordsToRead; i++) {
|
||||
const uint16_t word = BK4819_ReadRegister(BK4819_REG_5F);
|
||||
if (gFSKWriteIndex < 36)
|
||||
g_FSK_Buffer[gFSKWriteIndex++] = word;
|
||||
}
|
||||
|
||||
gBeamRxWordCount = gFSKWriteIndex;
|
||||
gUpdateDisplay = true;
|
||||
BEAM_StorePacket();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -806,6 +1034,22 @@ void APP_EndTransmission(void)
|
||||
}
|
||||
}
|
||||
|
||||
void APP_HandleEndTransmission(void) {
|
||||
if (gFlagEndTransmission) {
|
||||
FUNCTION_Select(FUNCTION_FOREGROUND);
|
||||
}
|
||||
else {
|
||||
APP_EndTransmission();
|
||||
|
||||
if (gEeprom.REPEATER_TAIL_TONE_ELIMINATION == 0)
|
||||
FUNCTION_Select(FUNCTION_FOREGROUND);
|
||||
else
|
||||
gRTTECountdown_10ms = gEeprom.REPEATER_TAIL_TONE_ELIMINATION * 10;
|
||||
}
|
||||
|
||||
gFlagEndTransmission = false;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_VOX
|
||||
static void HandleVox(void)
|
||||
{
|
||||
@@ -841,24 +1085,10 @@ static void HandleVox(void)
|
||||
gVOX_NoiseDetected = false;
|
||||
|
||||
if (gCurrentFunction == FUNCTION_TRANSMIT && !gPttIsPressed && !gVOX_NoiseDetected) {
|
||||
if (gFlagEndTransmission) {
|
||||
//if (gCurrentFunction != FUNCTION_FOREGROUND)
|
||||
FUNCTION_Select(FUNCTION_FOREGROUND);
|
||||
}
|
||||
else {
|
||||
APP_EndTransmission();
|
||||
|
||||
if (gEeprom.REPEATER_TAIL_TONE_ELIMINATION == 0) {
|
||||
//if (gCurrentFunction != FUNCTION_FOREGROUND)
|
||||
FUNCTION_Select(FUNCTION_FOREGROUND);
|
||||
}
|
||||
else
|
||||
gRTTECountdown_10ms = gEeprom.REPEATER_TAIL_TONE_ELIMINATION * 10;
|
||||
}
|
||||
APP_HandleEndTransmission();
|
||||
|
||||
gUpdateStatus = true;
|
||||
gUpdateDisplay = true;
|
||||
gFlagEndTransmission = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -882,6 +1112,12 @@ static void HandleVox(void)
|
||||
|
||||
void APP_Update(void)
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
// Parse incoming packets on every tick so serial keys are never missed,
|
||||
// regardless of whether the screen needs redrawing.
|
||||
SCREENSHOT_ParseInput();
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
if (gFlagPlayQueuedVoice) {
|
||||
AUDIO_PlayQueuedVoice();
|
||||
@@ -980,7 +1216,7 @@ void APP_Update(void)
|
||||
|
||||
APP_EndTransmission();
|
||||
|
||||
AUDIO_PlayBeep(BEEP_880HZ_60MS_DOUBLE_BEEP);
|
||||
AUDIO_PlayBeep(BEEP_880HZ_60MS_TRIPLE_BEEP);
|
||||
|
||||
RADIO_SetVfoState(VFO_STATE_TIMEOUT);
|
||||
|
||||
@@ -1030,6 +1266,9 @@ void APP_Update(void)
|
||||
&& gScanStateDir == SCAN_OFF
|
||||
&& !gPttIsPressed
|
||||
&& gCurrentFunction != FUNCTION_POWER_SAVE
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
&& !gBeamActive
|
||||
#endif
|
||||
#ifdef ENABLE_VOICE
|
||||
&& gVoiceWriteIndex == 0
|
||||
#endif
|
||||
@@ -1108,7 +1347,11 @@ void APP_Update(void)
|
||||
|
||||
if (gEeprom.DUAL_WATCH != DUAL_WATCH_OFF &&
|
||||
gScanStateDir == SCAN_OFF &&
|
||||
!gCssBackgroundScan)
|
||||
!gCssBackgroundScan
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
&& !gBeamActive
|
||||
#endif
|
||||
)
|
||||
{ // dual watch mode, toggle between the two VFO's
|
||||
DualwatchAlternate();
|
||||
goToSleep = false;
|
||||
@@ -1119,7 +1362,11 @@ void APP_Update(void)
|
||||
gPowerSave_10ms = power_save1_10ms; // come back here in a bit
|
||||
gRxIdleMode = false; // RX is awake
|
||||
}
|
||||
else if (gEeprom.DUAL_WATCH == DUAL_WATCH_OFF || gScanStateDir != SCAN_OFF || gCssBackgroundScan || goToSleep)
|
||||
else if (
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
!gBeamActive &&
|
||||
#endif
|
||||
(gEeprom.DUAL_WATCH == DUAL_WATCH_OFF || gScanStateDir != SCAN_OFF || gCssBackgroundScan || goToSleep))
|
||||
{ // dual watch mode off or scanning or rssi update request
|
||||
// go back to sleep
|
||||
|
||||
@@ -1138,7 +1385,11 @@ void APP_Update(void)
|
||||
// Authentic device checked removed
|
||||
|
||||
}
|
||||
else {
|
||||
else
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
if (!gBeamActive)
|
||||
#endif
|
||||
{
|
||||
// toggle between the two VFO's
|
||||
DualwatchAlternate();
|
||||
gPowerSave_10ms = power_save1_10ms;
|
||||
@@ -1149,8 +1400,21 @@ void APP_Update(void)
|
||||
}
|
||||
}
|
||||
|
||||
void StopTransmitting(void) {
|
||||
ProcessKey(KEY_PTT, false, false);
|
||||
gPttIsPressed = false;
|
||||
if (gKeyReading1 != KEY_INVALID)
|
||||
gPttWasReleased = true;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
#if defined(ENABLE_FEAT_F4HWN_CTR) || defined(ENABLE_FEAT_F4HWN_INV)
|
||||
ST7565_ContrastAndInv();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// called every 10ms
|
||||
static void CheckKeys(void)
|
||||
void CheckKeys(void)
|
||||
{
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
if(gSetting_KILLED){
|
||||
@@ -1165,75 +1429,63 @@ static void CheckKeys(void)
|
||||
#endif
|
||||
|
||||
// -------------------- PTT ------------------------
|
||||
const bool serialConfigInProgress = SerialConfigInProgress();
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
const bool isPressed = GPIO_IsPttPressed() && !serialConfigInProgress;
|
||||
#else
|
||||
const bool isPressed = !GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT) && !serialConfigInProgress;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
if (gSetting_set_ptt_session)
|
||||
{
|
||||
if (GPIO_IsPttPressed() && !SerialConfigInProgress() && gPttOnePushCounter == 0)
|
||||
{ // PTT pressed
|
||||
if (++gPttDebounceCounter >= 3) // 30ms
|
||||
{ // start transmitting
|
||||
boot_counter_10ms = 0;
|
||||
if ((isPressed && (gPttOnePushCounter == 0 || gPttOnePushCounter == 2)) ||
|
||||
(!isPressed && (gPttOnePushCounter == 1 || gPttOnePushCounter == 3)) ||
|
||||
serialConfigInProgress)
|
||||
{
|
||||
if (++gPttDebounceCounter >= 3 || (serialConfigInProgress && gPttOnePushCounter > 0))
|
||||
{
|
||||
gPttDebounceCounter = 0;
|
||||
gPttIsPressed = true;
|
||||
gPttOnePushCounter = 1;
|
||||
ProcessKey(KEY_PTT, true, false);
|
||||
|
||||
if (gPttOnePushCounter == 0)
|
||||
{ // start transmitting
|
||||
boot_counter_10ms = 0;
|
||||
gPttIsPressed = true;
|
||||
gPttOnePushCounter = 1;
|
||||
ProcessKey(KEY_PTT, true, false);
|
||||
}
|
||||
else if (gPttOnePushCounter == 3 || serialConfigInProgress)
|
||||
{ // stop transmitting
|
||||
StopTransmitting();
|
||||
gPttOnePushCounter = 0;
|
||||
}
|
||||
else
|
||||
gPttOnePushCounter++;
|
||||
}
|
||||
}
|
||||
else if ((!GPIO_IsPttPressed() || SerialConfigInProgress()) && gPttOnePushCounter == 1)
|
||||
{
|
||||
// PTT released or serial comms config in progress
|
||||
if (++gPttDebounceCounter >= 3 || SerialConfigInProgress()) // 30ms
|
||||
{ // stop transmitting
|
||||
gPttOnePushCounter = 2;
|
||||
}
|
||||
}
|
||||
else if (GPIO_IsPttPressed() && !SerialConfigInProgress() && gPttOnePushCounter == 2)
|
||||
{ // PTT pressed again
|
||||
if (++gPttDebounceCounter >= 3 || SerialConfigInProgress()) // 30ms
|
||||
{ // stop transmitting
|
||||
gPttOnePushCounter = 3;
|
||||
}
|
||||
}
|
||||
else if ((!GPIO_IsPttPressed() || SerialConfigInProgress()) && gPttOnePushCounter == 3)
|
||||
{ // PTT released or serial comms config in progress
|
||||
if (++gPttDebounceCounter >= 3 || SerialConfigInProgress()) // 30ms
|
||||
{ // stop transmitting
|
||||
ProcessKey(KEY_PTT, false, false);
|
||||
gPttIsPressed = false;
|
||||
if (gKeyReading1 != KEY_INVALID)
|
||||
gPttWasReleased = true;
|
||||
gPttOnePushCounter = 0;
|
||||
#if defined(ENABLE_FEAT_F4HWN_CTR) || defined(ENABLE_FEAT_F4HWN_INV)
|
||||
ST7565_ContrastAndInv();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
gPttDebounceCounter = 0;
|
||||
|
||||
//gDebug = gPttOnePushCounter;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (gPttIsPressed)
|
||||
{
|
||||
if (!GPIO_IsPttPressed() || SerialConfigInProgress())
|
||||
if (!isPressed)
|
||||
{ // PTT released or serial comms config in progress
|
||||
if (++gPttDebounceCounter >= 3 || SerialConfigInProgress()) // 30ms
|
||||
if (++gPttDebounceCounter >= 3 || serialConfigInProgress) // 30ms
|
||||
{ // stop transmitting
|
||||
ProcessKey(KEY_PTT, false, false);
|
||||
gPttIsPressed = false;
|
||||
if (gKeyReading1 != KEY_INVALID)
|
||||
gPttWasReleased = true;
|
||||
#if defined(ENABLE_FEAT_F4HWN_CTR) || defined(ENABLE_FEAT_F4HWN_INV)
|
||||
ST7565_ContrastAndInv();
|
||||
#endif
|
||||
gPttDebounceCounter = 0;
|
||||
StopTransmitting();
|
||||
}
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
gPttDebounceCounter = 0;
|
||||
}
|
||||
else if (GPIO_IsPttPressed() && !SerialConfigInProgress())
|
||||
else if (isPressed)
|
||||
{ // PTT pressed
|
||||
if (++gPttDebounceCounter >= 3) // 30ms
|
||||
{ // start transmitting
|
||||
@@ -1243,38 +1495,9 @@ static void CheckKeys(void)
|
||||
ProcessKey(KEY_PTT, true, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
gPttDebounceCounter = 0;
|
||||
}
|
||||
#else
|
||||
if (gPttIsPressed)
|
||||
{
|
||||
if (GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT) || SerialConfigInProgress())
|
||||
{ // PTT released or serial comms config in progress
|
||||
if (++gPttDebounceCounter >= 3 || SerialConfigInProgress()) // 30ms
|
||||
{ // stop transmitting
|
||||
ProcessKey(KEY_PTT, false, false);
|
||||
gPttIsPressed = false;
|
||||
if (gKeyReading1 != KEY_INVALID)
|
||||
gPttWasReleased = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
gPttDebounceCounter = 0;
|
||||
}
|
||||
else if (!GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT) && !SerialConfigInProgress())
|
||||
{ // PTT pressed
|
||||
if (++gPttDebounceCounter >= 3) // 30ms
|
||||
{ // start transmitting
|
||||
boot_counter_10ms = 0;
|
||||
gPttDebounceCounter = 0;
|
||||
gPttIsPressed = true;
|
||||
ProcessKey(KEY_PTT, true, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
gPttDebounceCounter = 0;
|
||||
#endif
|
||||
|
||||
// --------------------- OTHER KEYS ----------------------------
|
||||
|
||||
@@ -1348,9 +1571,9 @@ void APP_TimeSlice10ms(void)
|
||||
{
|
||||
gNextTimeslice = false;
|
||||
|
||||
if (gScheduleVfoSave) {
|
||||
SETTINGS_SaveVfoIndicesFlush();
|
||||
}
|
||||
SETTINGS_SaveVfoIndicesFlush();
|
||||
|
||||
BACKLIGHT_Update();
|
||||
|
||||
gFlashLightBlinkCounter++;
|
||||
|
||||
@@ -1376,17 +1599,53 @@ void APP_TimeSlice10ms(void)
|
||||
|
||||
if (gCurrentFunction == FUNCTION_TRANSMIT)
|
||||
{ // transmitting
|
||||
#ifdef ENABLE_AUDIO_BAR
|
||||
#if defined(ENABLE_AUDIO_BAR) && !defined(ENABLE_FEAT_F4HWN_AUDIO_SCOPE)
|
||||
if (gSetting_mic_bar && (gFlashLightBlinkCounter % (150 / 10)) == 0) // once every 150ms
|
||||
UI_DisplayAudioBar();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO_SCOPE
|
||||
if (gSetting_mic_bar && (gFlashLightBlinkCounter % (20 / 10)) == 0) // once every 20ms
|
||||
// Sample audio amplitude and refresh display during TX only (FM RX has no usable audio register)
|
||||
UI_DisplayAudioScope();
|
||||
#endif
|
||||
|
||||
bool gUpdateDisplayCurrent = gUpdateDisplay;
|
||||
bool gUpdateStatusCurrent = gUpdateStatus;
|
||||
|
||||
if (gUpdateDisplayCurrent) {
|
||||
gUpdateDisplay = false;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
bool screenSaverRendered = false;
|
||||
|
||||
if (gScreenSaverDisplayed) {
|
||||
if (gUpdateDisplayCurrent) {
|
||||
gUpdateDisplayCurrent = false;
|
||||
} else if (gUpdateStatusCurrent) {
|
||||
gUpdateStatusCurrent = false;
|
||||
gUpdateStatus = false;
|
||||
}
|
||||
|
||||
if (gSetting_set_sav == SET_SAV_MATRIX) {
|
||||
if (++gScreenSaverTick >= 8u) {
|
||||
gScreenSaverTick = 0;
|
||||
ScreenSaverRenderMatrix(false);
|
||||
screenSaverRendered = true;
|
||||
}
|
||||
} else if (gSetting_set_sav == SET_SAV_LOGO_PLUS) {
|
||||
if (++gScreenSaverTick >= 16u) {
|
||||
gScreenSaverTick = 0;
|
||||
ScreenSaverRenderLogoPlus(false);
|
||||
screenSaverRendered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gUpdateDisplayCurrent) {
|
||||
GUI_DisplayScreen();
|
||||
}
|
||||
|
||||
@@ -1395,8 +1654,12 @@ void APP_TimeSlice10ms(void)
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
if (gUpdateDisplayCurrent || gUpdateStatusCurrent) {
|
||||
getScreenShot(false);
|
||||
if (gUpdateDisplayCurrent || gUpdateStatusCurrent
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
|| screenSaverRendered
|
||||
#endif
|
||||
) {
|
||||
SCREENSHOT_Update(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1443,8 +1706,7 @@ void APP_TimeSlice10ms(void)
|
||||
if (gAlarmState == ALARM_STATE_TXALARM) {
|
||||
gAlarmState = ALARM_STATE_SITE_ALARM;
|
||||
|
||||
if(gEeprom.TAIL_TONE_ELIMINATION)
|
||||
RADIO_SendCssTail();
|
||||
RADIO_SendCssTail();
|
||||
BK4819_SetupPowerAmplifier(0, 0);
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO1_PIN29_PA_ENABLE, false);
|
||||
BK4819_Enable_AfDac_DiscMode_TxDsp();
|
||||
@@ -1493,6 +1755,10 @@ void APP_TimeSlice10ms(void)
|
||||
|
||||
SCANNER_TimeSlice10ms();
|
||||
|
||||
#if defined(ENABLE_SCAN_RANGES) && defined(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE) && ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE
|
||||
CHFRSCANNER_UpdateCssDetection();
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
if (gScreenToDisplay == DISPLAY_AIRCOPY && gAircopyState == AIRCOPY_TRANSFER && gAirCopyIsSendMode == 1) {
|
||||
if (!AIRCOPY_SendMessage()) {
|
||||
@@ -1509,17 +1775,16 @@ void cancelUserInputModes(void)
|
||||
if (gDTMF_InputMode || gDTMF_InputBox_Index > 0)
|
||||
{
|
||||
DTMF_clear_input_box();
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
gUpdateDisplay = true;
|
||||
}
|
||||
|
||||
if (gWasFKeyPressed || gKeyInputCountdown > 0 || gInputBoxIndex > 0)
|
||||
{
|
||||
gWasFKeyPressed = false;
|
||||
HideFKeyIcon();
|
||||
|
||||
gInputBoxIndex = 0;
|
||||
gKeyInputCountdown = 0;
|
||||
gUpdateStatus = true;
|
||||
gUpdateDisplay = true;
|
||||
}
|
||||
}
|
||||
@@ -1536,12 +1801,27 @@ void APP_TimeSlice500ms(void)
|
||||
if (--gKeypadLocked == 0)
|
||||
gUpdateDisplay = true;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RX_TX_TIMER
|
||||
if (gSetting_set_tmr && (gCurrentFunction == FUNCTION_TRANSMIT || FUNCTION_IsRx())) {
|
||||
const uint16_t timerCountdown = (gCurrentFunction == FUNCTION_TRANSMIT)
|
||||
? gTxTimerCountdown_500ms
|
||||
: gRxTimerCountdown_500ms;
|
||||
|
||||
if ((timerCountdown & 1u) != 0u)
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gKeyInputCountdown > 0)
|
||||
{
|
||||
if (--gKeyInputCountdown == 0)
|
||||
{
|
||||
|
||||
if (IS_MR_CHANNEL(gTxVfo->CHANNEL_SAVE) && (gInputBoxIndex > 0 && gInputBoxIndex < 4))
|
||||
if (IS_MR_CHANNEL(gTxVfo->CHANNEL_SAVE) && (gInputBoxIndex > 0 && gInputBoxIndex < 4)
|
||||
#ifdef ENABLE_FMRADIO
|
||||
&& (!gFmRadioMode)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
channelMoveSwitch();
|
||||
|
||||
@@ -1551,6 +1831,7 @@ void APP_TimeSlice500ms(void)
|
||||
}
|
||||
|
||||
cancelUserInputModes();
|
||||
gHasVfoBackup = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1565,7 +1846,7 @@ void APP_TimeSlice500ms(void)
|
||||
{
|
||||
if (gDTMF_RX_live[0] != 0)
|
||||
{
|
||||
memset(gDTMF_RX_live, 0, sizeof(gDTMF_RX_live));
|
||||
DTMF_clear_input_box_memory();
|
||||
gUpdateDisplay = true;
|
||||
}
|
||||
}
|
||||
@@ -1593,29 +1874,42 @@ void APP_TimeSlice500ms(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
const int m = UI_MENU_GetCurrentMenuId();
|
||||
|
||||
if (gBacklightCountdown_500ms > 0 && !gAskToSave && !gCssBackgroundScan
|
||||
// don't turn off backlight if user is in backlight menu option
|
||||
&& !(gScreenToDisplay == DISPLAY_MENU && (UI_MENU_GetCurrentMenuId() == MENU_ABR || UI_MENU_GetCurrentMenuId() == MENU_ABR_MAX))
|
||||
&& !(gScreenToDisplay == DISPLAY_MENU && (m == MENU_ABR || m == MENU_ABR_MAX || m == MENU_ABR_MIN))
|
||||
&& --gBacklightCountdown_500ms == 0
|
||||
&& gEeprom.BACKLIGHT_TIME < 61
|
||||
) {
|
||||
BACKLIGHT_TurnOff();
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
ScreenSaverTryDisplay();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SLEEP
|
||||
if (gSleepModeCountdown_500ms == gSetting_set_off * 120 && gWakeUp) {
|
||||
//ST7565_Init();
|
||||
ST7565_FixInterfGlitch();
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
ScreenSaverExit();
|
||||
#endif
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO5_PIN1_RED, false);
|
||||
gPowerSave_10ms = gEeprom.BATTERY_SAVE * 10;
|
||||
gWakeUp = false;
|
||||
gUpdateDisplay = true;
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
|
||||
if(gCurrentFunction != FUNCTION_TRANSMIT && !FUNCTION_IsRx()
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
if(gCurrentFunction != FUNCTION_TRANSMIT && !FUNCTION_IsRx() && gScreenToDisplay != DISPLAY_AIRCOPY)
|
||||
#else
|
||||
if(gCurrentFunction != FUNCTION_TRANSMIT && !FUNCTION_IsRx())
|
||||
&& gScreenToDisplay != DISPLAY_AIRCOPY
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
&& !gBeamActive
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (gSleepModeCountdown_500ms > 0 && --gSleepModeCountdown_500ms == 0) {
|
||||
gBacklightCountdown_500ms = 0;
|
||||
@@ -1701,6 +1995,9 @@ void APP_TimeSlice500ms(void)
|
||||
#endif
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
&& gScreenToDisplay != DISPLAY_AIRCOPY
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
&& !gBeamActive
|
||||
#endif
|
||||
) {
|
||||
if (gEeprom.AUTO_KEYPAD_LOCK && gKeyLockCountdown > 0 && !gDTMF_InputMode
|
||||
@@ -1713,8 +2010,10 @@ void APP_TimeSlice500ms(void)
|
||||
if (exit_menu) {
|
||||
gMenuCountdown = 0;
|
||||
|
||||
if (gEeprom.BACKLIGHT_TIME == 0) {
|
||||
BACKLIGHT_TurnOff();
|
||||
const int m = UI_MENU_GetCurrentMenuId();
|
||||
|
||||
if (gScreenToDisplay == DISPLAY_MENU && (m == MENU_ABR || m == MENU_ABR_MAX || m == MENU_ABR_MIN)) {
|
||||
BACKLIGHT_TurnOn();
|
||||
}
|
||||
|
||||
if (gInputBoxIndex > 0 || gDTMF_InputMode) {
|
||||
@@ -1732,13 +2031,12 @@ void APP_TimeSlice500ms(void)
|
||||
*/
|
||||
DTMF_clear_input_box();
|
||||
|
||||
gWasFKeyPressed = false;
|
||||
HideFKeyIcon();
|
||||
gInputBoxIndex = 0;
|
||||
|
||||
gAskToSave = false;
|
||||
gAskToDelete = false;
|
||||
|
||||
gUpdateStatus = true;
|
||||
gUpdateDisplay = true;
|
||||
|
||||
GUI_DisplayType_t disp = DISPLAY_INVALID;
|
||||
@@ -1774,6 +2072,17 @@ void APP_TimeSlice500ms(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
if (gBacklightCountdown_500ms == 0 &&
|
||||
gEeprom.BACKLIGHT_TIME > 0 &&
|
||||
gEeprom.BACKLIGHT_TIME < 61 &&
|
||||
!gAskToSave &&
|
||||
!gCssBackgroundScan)
|
||||
{
|
||||
ScreenSaverTryDisplay();
|
||||
}
|
||||
#endif
|
||||
|
||||
BATTERY_TimeSlice500ms();
|
||||
SCANNER_TimeSlice500ms();
|
||||
UI_MAIN_TimeSlice500ms();
|
||||
@@ -1835,24 +2144,53 @@ static void ALARM_Off(void)
|
||||
|
||||
static void ProcessKey(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_SLEEP
|
||||
#ifdef ENABLE_FEAT_F4HWN_SLEEP
|
||||
if (gSleepWakeKey != KEY_INVALID) {
|
||||
if (Key == gSleepWakeKey && !bKeyPressed)
|
||||
gSleepWakeKey = KEY_INVALID;
|
||||
return;
|
||||
}
|
||||
|
||||
if(gWakeUp)
|
||||
{
|
||||
if(!bKeyPressed || Key == KEY_PTT)
|
||||
if(bKeyPressed || Key == KEY_PTT)
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
ScreenSaverExit();
|
||||
#endif
|
||||
BACKLIGHT_TurnOn();
|
||||
|
||||
if(Key != KEY_PTT)
|
||||
{
|
||||
Key = KEY_INVALID;
|
||||
gSleepWakeKey = Key;
|
||||
gBeepToPlay = BEEP_NONE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if(Key != KEY_PTT)
|
||||
{
|
||||
Key = KEY_INVALID;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
if (gScreenSaverWakeKey != KEY_INVALID) {
|
||||
if (Key == gScreenSaverWakeKey && !bKeyPressed)
|
||||
gScreenSaverWakeKey = KEY_INVALID;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bKeyPressed && gScreenSaverDisplayed) {
|
||||
ScreenSaverExit();
|
||||
BACKLIGHT_TurnOn();
|
||||
if (Key != KEY_PTT) {
|
||||
gScreenSaverWakeKey = Key;
|
||||
gBeepToPlay = BEEP_NONE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (Key == KEY_EXIT && !BACKLIGHT_IsOn() && gEeprom.BACKLIGHT_TIME > 0)
|
||||
{ // just turn the light on for now so the user can see what's what
|
||||
@@ -1910,14 +2248,14 @@ static void ProcessKey(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
if (Key == KEY_EXIT && bKeyHeld) { // exit key held pressed
|
||||
// clear the live DTMF decoder
|
||||
if (gDTMF_RX_live[0] != 0) {
|
||||
memset(gDTMF_RX_live, 0, sizeof(gDTMF_RX_live));
|
||||
DTMF_clear_input_box_memory();
|
||||
gDTMF_RX_live_timeout = 0;
|
||||
gUpdateDisplay = true;
|
||||
}
|
||||
|
||||
// cancel user input
|
||||
cancelUserInputModes();
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
if (gMonitor)
|
||||
ACTION_Monitor(); //turn off the monitor
|
||||
@@ -1946,12 +2284,10 @@ static void ProcessKey(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
bool lowBatPopup = gLowBattery && !gLowBatteryConfirmed && gScreenToDisplay == DISPLAY_MAIN;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN // Disable PTT if KEY_LOCK
|
||||
bool lck_condition = false;
|
||||
bool lck_condition = (gEeprom.KEY_LOCK || lowBatPopup) && gCurrentFunction != FUNCTION_TRANSMIT;
|
||||
|
||||
if(gSetting_set_lck)
|
||||
lck_condition = (gEeprom.KEY_LOCK || lowBatPopup) && gCurrentFunction != FUNCTION_TRANSMIT;
|
||||
else
|
||||
lck_condition = (gEeprom.KEY_LOCK || lowBatPopup) && gCurrentFunction != FUNCTION_TRANSMIT && Key != KEY_PTT;
|
||||
if(!gSetting_set_lck)
|
||||
lck_condition = lck_condition && Key != KEY_PTT;
|
||||
|
||||
if (lck_condition)
|
||||
#else
|
||||
@@ -2030,8 +2366,7 @@ static void ProcessKey(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
if (gWasFKeyPressed && (Key == KEY_PTT || Key == KEY_EXIT || Key == KEY_SIDE1 || Key == KEY_SIDE2)) {
|
||||
#endif
|
||||
// cancel the F-key
|
||||
gWasFKeyPressed = false;
|
||||
gUpdateStatus = true;
|
||||
HideFKeyIcon();
|
||||
}
|
||||
|
||||
if (bFlag) {
|
||||
@@ -2093,7 +2428,8 @@ static void ProcessKey(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
}
|
||||
}
|
||||
#if defined(ENABLE_ALARM) || defined(ENABLE_TX1750)
|
||||
else if ((!bKeyHeld && bKeyPressed) || (gAlarmState == ALARM_STATE_TX1750 && bKeyHeld && !bKeyPressed)) {
|
||||
// else if ((!bKeyHeld && bKeyPressed) || (gAlarmState == ALARM_STATE_TX1750 && bKeyHeld && !bKeyPressed)) {
|
||||
else if ((bKeyHeld != bKeyPressed) && (gAlarmState == ALARM_STATE_TX1750 || bKeyPressed)) {
|
||||
ALARM_Off();
|
||||
|
||||
if (gEeprom.REPEATER_TAIL_TONE_ELIMINATION == 0)
|
||||
@@ -2108,12 +2444,21 @@ static void ProcessKey(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (gScreenToDisplay != DISPLAY_INVALID && (
|
||||
else if (
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
gBeamActive ||
|
||||
#endif
|
||||
(gScreenToDisplay != DISPLAY_INVALID && (
|
||||
(Key != KEY_SIDE1 && Key != KEY_SIDE2)
|
||||
#ifdef ENABLE_FEAT_F4HWN // For F + SIDE1 or F + SIDE2
|
||||
|| (gWasFKeyPressed && (Key == KEY_SIDE1 || Key == KEY_SIDE2))
|
||||
#endif
|
||||
)) {
|
||||
))) {
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
if (gBeamActive)
|
||||
BEAM_ProcessKeys(Key, bKeyPressed, bKeyHeld);
|
||||
else
|
||||
#endif
|
||||
ProcessKeysFunctions[gScreenToDisplay](Key, bKeyPressed, bKeyHeld);
|
||||
}
|
||||
else if (!SCANNER_IsScanning()
|
||||
|
||||
+2
-1
@@ -24,12 +24,13 @@
|
||||
#include "radio.h"
|
||||
|
||||
void APP_EndTransmission(void);
|
||||
void APP_HandleEndTransmission(void);
|
||||
void APP_StartListening(FUNCTION_Type_t function);
|
||||
uint32_t APP_SetFreqByStepAndLimits(VFO_Info_t *pInfo, int8_t direction, uint32_t lower, uint32_t upper);
|
||||
uint32_t APP_SetFrequencyByStep(VFO_Info_t *pInfo, int8_t direction);
|
||||
void APP_Update(void);
|
||||
void APP_TimeSlice10ms(void);
|
||||
void APP_TimeSlice500ms(void);
|
||||
bool APP_IsScreenSaverDisplayed(void);
|
||||
|
||||
#endif
|
||||
|
||||
+325
@@ -0,0 +1,325 @@
|
||||
/* Copyright 2026 Armel F4HWN
|
||||
* https://github.com/armel
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "app/aircopy.h"
|
||||
#include "app/beam.h"
|
||||
#include "audio.h"
|
||||
#include "driver/bk4819.h"
|
||||
#include "driver/crc.h"
|
||||
#include "driver/st7565.h"
|
||||
#include "frequencies.h"
|
||||
#include "misc.h"
|
||||
#include "radio.h"
|
||||
#include "settings.h"
|
||||
#include "ui/main.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
#define BEAM_PACKET_MAGIC 0xBEA5u
|
||||
#define BEAM_PACKET_VERSION 2u
|
||||
|
||||
// One byte per field instead of bit-packing — payload still fits in 64 bytes,
|
||||
// and avoids costly shift/mask/store sequences on Cortex-M.
|
||||
typedef struct {
|
||||
uint16_t magic;
|
||||
uint8_t version;
|
||||
uint8_t _pad;
|
||||
uint32_t rx_frequency;
|
||||
uint32_t tx_offset_frequency;
|
||||
uint8_t rx_code;
|
||||
uint8_t tx_code;
|
||||
uint8_t rx_codetype;
|
||||
uint8_t tx_codetype;
|
||||
uint8_t modulation;
|
||||
uint8_t tx_offset_direction;
|
||||
uint8_t tx_lock;
|
||||
uint8_t busy_channel_lock;
|
||||
uint8_t output_power;
|
||||
uint8_t channel_bandwidth;
|
||||
uint8_t frequency_reverse;
|
||||
uint8_t dtmf_ptt_id_mode;
|
||||
uint8_t dtmf_decoding_enable;
|
||||
uint8_t step_setting;
|
||||
uint8_t scrambling_type;
|
||||
uint8_t band;
|
||||
uint8_t scanlist;
|
||||
uint8_t compander;
|
||||
char name[16];
|
||||
} BEAM_Payload_t;
|
||||
|
||||
static_assert(sizeof(BEAM_Payload_t) <= 64);
|
||||
|
||||
BEAM_Mode_t gBeamMode = BEAM_MODE_TX;
|
||||
BEAM_Status_t gBeamStatus = BEAM_STATUS_READY;
|
||||
uint16_t gBeamCopiedChannel = 0xFFFFu;
|
||||
uint8_t gBeamRxWordCount;
|
||||
bool gBeamActive;
|
||||
|
||||
static VFO_Info_t gBeamRadioVfo;
|
||||
|
||||
static void BEAM_SetRadioToBeamFrequency(void)
|
||||
{
|
||||
const uint16_t channel = FREQ_CHANNEL_FIRST + BAND6_400MHz;
|
||||
|
||||
RADIO_InitInfo(&gBeamRadioVfo, channel, DEFAULT_FREQ);
|
||||
gBeamRadioVfo.CHANNEL_BANDWIDTH = BANDWIDTH_NARROW;
|
||||
gBeamRadioVfo.OUTPUT_POWER = OUTPUT_POWER_LOW1;
|
||||
RADIO_ConfigureSquelchAndOutputPower(&gBeamRadioVfo);
|
||||
|
||||
gRxVfo = &gBeamRadioVfo;
|
||||
gTxVfo = &gBeamRadioVfo;
|
||||
gCurrentVfo = gRxVfo;
|
||||
RADIO_SetupRegisters(true);
|
||||
BK4819_SetupAircopy();
|
||||
BK4819_ResetFSK();
|
||||
}
|
||||
|
||||
static void BEAM_SendPacket(void)
|
||||
{
|
||||
memset(g_FSK_Buffer, 0, sizeof(g_FSK_Buffer));
|
||||
g_FSK_Buffer[0] = 0xABCDu;
|
||||
|
||||
BEAM_Payload_t * const payload = (BEAM_Payload_t *)&g_FSK_Buffer[2];
|
||||
|
||||
const VFO_Info_t *vfo = &gEeprom.VfoInfo[gEeprom.TX_VFO];
|
||||
|
||||
payload->magic = BEAM_PACKET_MAGIC;
|
||||
payload->version = BEAM_PACKET_VERSION;
|
||||
payload->rx_frequency = vfo->freq_config_RX.Frequency;
|
||||
payload->tx_offset_frequency = vfo->TX_OFFSET_FREQUENCY;
|
||||
payload->rx_code = vfo->freq_config_RX.Code;
|
||||
payload->tx_code = vfo->freq_config_TX.Code;
|
||||
payload->rx_codetype = vfo->freq_config_RX.CodeType;
|
||||
payload->tx_codetype = vfo->freq_config_TX.CodeType;
|
||||
payload->modulation = vfo->Modulation;
|
||||
payload->tx_offset_direction = vfo->TX_OFFSET_FREQUENCY_DIRECTION;
|
||||
payload->tx_lock = vfo->TX_LOCK;
|
||||
payload->busy_channel_lock = vfo->BUSY_CHANNEL_LOCK;
|
||||
payload->output_power = vfo->OUTPUT_POWER;
|
||||
payload->channel_bandwidth = vfo->CHANNEL_BANDWIDTH;
|
||||
payload->frequency_reverse = vfo->FrequencyReverse;
|
||||
payload->dtmf_ptt_id_mode = vfo->DTMF_PTT_ID_TX_MODE;
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
payload->dtmf_decoding_enable = vfo->DTMF_DECODING_ENABLE;
|
||||
#endif
|
||||
payload->step_setting = vfo->STEP_SETTING;
|
||||
payload->scrambling_type = vfo->SCRAMBLING_TYPE;
|
||||
payload->band = vfo->Band;
|
||||
payload->scanlist = vfo->SCANLIST_PARTICIPATION;
|
||||
payload->compander = vfo->Compander;
|
||||
|
||||
if (IS_MR_CHANNEL(vfo->CHANNEL_SAVE)) {
|
||||
SETTINGS_FetchChannelName(payload->name, vfo->CHANNEL_SAVE);
|
||||
} else {
|
||||
memcpy(payload->name, vfo->Name, sizeof(vfo->Name));
|
||||
}
|
||||
|
||||
g_FSK_Buffer[34] = CRC_Calculate(&g_FSK_Buffer[1], 2 + 64);
|
||||
g_FSK_Buffer[35] = 0xDCBAu;
|
||||
|
||||
AIRCOPY_Obfuscate(32);
|
||||
|
||||
// Show "SENDING" before the blocking FSK transmission so the user gets
|
||||
// immediate visual feedback instead of a frozen "BEAM TX" screen.
|
||||
gBeamStatus = BEAM_STATUS_TX_WAIT;
|
||||
UI_DisplayMain();
|
||||
ST7565_BlitFullScreen();
|
||||
|
||||
RADIO_SetTxParameters();
|
||||
BK4819_SendFSKData(g_FSK_Buffer);
|
||||
BK4819_SetupPowerAmplifier(0, 0);
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO1_PIN29_PA_ENABLE, false);
|
||||
|
||||
RADIO_SelectVfos();
|
||||
RADIO_SetupRegisters(true);
|
||||
gBeamStatus = BEAM_STATUS_TX_DONE;
|
||||
gBeepToPlay = BEEP_880HZ_60MS_TRIPLE_BEEP;
|
||||
gUpdateDisplay = true;
|
||||
}
|
||||
|
||||
static void BEAM_SavePayloadToFirstFreeChannel(const BEAM_Payload_t *payload)
|
||||
{
|
||||
uint16_t channel = 0xFFFFu;
|
||||
|
||||
for (uint16_t c = MR_CHANNEL_FIRST; IS_MR_CHANNEL(c); c++) {
|
||||
if (!RADIO_CheckValidChannel(c, false, 0)) {
|
||||
channel = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (channel == 0xFFFFu) {
|
||||
gBeamStatus = BEAM_STATUS_RX_FULL;
|
||||
gUpdateDisplay = true;
|
||||
return;
|
||||
}
|
||||
|
||||
VFO_Info_t vfo;
|
||||
RADIO_InitInfo(&vfo, channel, payload->rx_frequency);
|
||||
|
||||
// CRC + magic + version already validate the payload — no need to clamp fields.
|
||||
vfo.TX_OFFSET_FREQUENCY = payload->tx_offset_frequency;
|
||||
vfo.freq_config_RX.Code = payload->rx_code;
|
||||
vfo.freq_config_TX.Code = payload->tx_code;
|
||||
vfo.freq_config_RX.CodeType = payload->rx_codetype;
|
||||
vfo.freq_config_TX.CodeType = payload->tx_codetype;
|
||||
vfo.TX_OFFSET_FREQUENCY_DIRECTION = payload->tx_offset_direction;
|
||||
vfo.Modulation = payload->modulation;
|
||||
vfo.TX_LOCK = payload->tx_lock;
|
||||
vfo.BUSY_CHANNEL_LOCK = payload->busy_channel_lock;
|
||||
vfo.OUTPUT_POWER = payload->output_power;
|
||||
vfo.CHANNEL_BANDWIDTH = payload->channel_bandwidth;
|
||||
vfo.FrequencyReverse = payload->frequency_reverse;
|
||||
vfo.DTMF_PTT_ID_TX_MODE = payload->dtmf_ptt_id_mode;
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
vfo.DTMF_DECODING_ENABLE = payload->dtmf_decoding_enable;
|
||||
#endif
|
||||
vfo.STEP_SETTING = payload->step_setting;
|
||||
vfo.StepFrequency = gStepFrequencyTable[vfo.STEP_SETTING];
|
||||
vfo.SCRAMBLING_TYPE = payload->scrambling_type;
|
||||
vfo.Band = payload->band;
|
||||
vfo.SCANLIST_PARTICIPATION = payload->scanlist;
|
||||
vfo.Compander = payload->compander;
|
||||
|
||||
memcpy(vfo.Name, payload->name, sizeof(vfo.Name));
|
||||
vfo.Name[sizeof(vfo.Name) - 1] = '\0';
|
||||
|
||||
RADIO_ApplyOffset(&vfo);
|
||||
RADIO_ConfigureSquelchAndOutputPower(&vfo);
|
||||
|
||||
SETTINGS_SaveChannel(channel, gEeprom.TX_VFO, &vfo, 3);
|
||||
SETTINGS_SaveChannelName(channel, vfo.Name);
|
||||
|
||||
gBeamCopiedChannel = channel;
|
||||
gBeamStatus = BEAM_STATUS_RX_SAVED;
|
||||
gUpdateDisplay = true;
|
||||
BACKLIGHT_TurnOn();
|
||||
return;
|
||||
}
|
||||
|
||||
static void BEAM_KeyMenu(void)
|
||||
{
|
||||
BEAM_SetRadioToBeamFrequency();
|
||||
|
||||
if (gBeamMode == BEAM_MODE_TX) {
|
||||
BEAM_SendPacket();
|
||||
} else {
|
||||
gBeamStatus = BEAM_STATUS_RX_WAIT;
|
||||
gBeamCopiedChannel = 0xFFFFu;
|
||||
gBeamRxWordCount = 0;
|
||||
gFSKWriteIndex = 0;
|
||||
BK4819_PrepareFSKReceive();
|
||||
}
|
||||
}
|
||||
|
||||
static void BEAM_KeyExit(void)
|
||||
{
|
||||
BK4819_ResetFSK();
|
||||
|
||||
if (gBeamMode == BEAM_MODE_RX && gBeamCopiedChannel != 0xFFFFu) {
|
||||
gEeprom.MrChannel[gEeprom.TX_VFO] = gBeamCopiedChannel;
|
||||
gEeprom.ScreenChannel[gEeprom.TX_VFO] = gBeamCopiedChannel;
|
||||
RADIO_ConfigureChannel(gEeprom.TX_VFO, VFO_CONFIGURE_RELOAD);
|
||||
}
|
||||
|
||||
RADIO_SelectVfos();
|
||||
RADIO_SetupRegisters(true);
|
||||
|
||||
GUI_SelectNextDisplay(DISPLAY_MAIN);
|
||||
gBeamActive = false;
|
||||
}
|
||||
|
||||
void ACTION_Beam(void)
|
||||
{
|
||||
gBeamMode = BEAM_MODE_TX;
|
||||
gBeamStatus = BEAM_STATUS_READY;
|
||||
gBeamCopiedChannel = 0xFFFFu;
|
||||
gBeamRxWordCount = 0;
|
||||
gBeamActive = true;
|
||||
GUI_SelectNextDisplay(DISPLAY_MAIN);
|
||||
}
|
||||
|
||||
void BEAM_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if (bKeyHeld || !bKeyPressed)
|
||||
return;
|
||||
|
||||
if (Key != KEY_PTT)
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
switch (Key) {
|
||||
case KEY_UP:
|
||||
case KEY_DOWN:
|
||||
gBeamMode ^= 1; // (gBeamMode == BEAM_MODE_TX) ? BEAM_MODE_RX : BEAM_MODE_TX
|
||||
gBeamStatus = BEAM_STATUS_READY;
|
||||
gBeamRxWordCount = 0;
|
||||
break;
|
||||
case KEY_MENU:
|
||||
BEAM_KeyMenu();
|
||||
break;
|
||||
case KEY_EXIT:
|
||||
BEAM_KeyExit();
|
||||
return;
|
||||
case KEY_PTT:
|
||||
break;
|
||||
default:
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
break;
|
||||
}
|
||||
|
||||
gUpdateDisplay = true;
|
||||
}
|
||||
|
||||
void BEAM_StorePacket(void)
|
||||
{
|
||||
if (gFSKWriteIndex < 36)
|
||||
return;
|
||||
|
||||
gBeamRxWordCount = gFSKWriteIndex;
|
||||
gFSKWriteIndex = 0;
|
||||
|
||||
const uint16_t Status = BK4819_ReadRegister(BK4819_REG_0B);
|
||||
BK4819_PrepareFSKReceive();
|
||||
|
||||
if ((Status & 0x0010U) != 0 || g_FSK_Buffer[0] != 0xABCDu || g_FSK_Buffer[35] != 0xDCBAu)
|
||||
goto error;
|
||||
|
||||
AIRCOPY_Obfuscate(32);
|
||||
|
||||
if (g_FSK_Buffer[34] != CRC_Calculate(&g_FSK_Buffer[1], 2 + 64))
|
||||
goto error;
|
||||
|
||||
BEAM_Payload_t * const payload = (BEAM_Payload_t *)&g_FSK_Buffer[2];
|
||||
|
||||
if (payload->magic != BEAM_PACKET_MAGIC || payload->version != BEAM_PACKET_VERSION)
|
||||
goto error;
|
||||
|
||||
BEAM_SavePayloadToFirstFreeChannel(payload);
|
||||
BK4819_ResetFSK();
|
||||
return;
|
||||
|
||||
error:
|
||||
gBeamStatus = BEAM_STATUS_ERROR;
|
||||
gBeamRxWordCount = 0;
|
||||
gUpdateDisplay = true;
|
||||
BACKLIGHT_TurnOn();
|
||||
}
|
||||
|
||||
#endif // ENABLE_FEAT_F4HWN_BEAM
|
||||
@@ -0,0 +1,53 @@
|
||||
/* Copyright 2026 Armel F4HWN
|
||||
* https://github.com/armel
|
||||
*
|
||||
* 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_BEAM_H
|
||||
#define APP_BEAM_H
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "driver/keyboard.h"
|
||||
|
||||
typedef enum {
|
||||
BEAM_MODE_TX = 0,
|
||||
BEAM_MODE_RX
|
||||
} BEAM_Mode_t;
|
||||
|
||||
typedef enum {
|
||||
BEAM_STATUS_READY = 0,
|
||||
BEAM_STATUS_TX_DONE,
|
||||
BEAM_STATUS_RX_WAIT,
|
||||
BEAM_STATUS_RX_SAVED,
|
||||
BEAM_STATUS_RX_FULL,
|
||||
BEAM_STATUS_ERROR,
|
||||
BEAM_STATUS_TX_WAIT
|
||||
} BEAM_Status_t;
|
||||
|
||||
extern BEAM_Mode_t gBeamMode;
|
||||
extern BEAM_Status_t gBeamStatus;
|
||||
extern uint16_t gBeamCopiedChannel;
|
||||
extern uint8_t gBeamRxWordCount;
|
||||
extern bool gBeamActive;
|
||||
|
||||
void ACTION_Beam(void);
|
||||
void BEAM_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld);
|
||||
void BEAM_StorePacket(void);
|
||||
|
||||
#endif // ENABLE_FEAT_F4HWN_BEAM
|
||||
#endif // APP_BEAM_H
|
||||
+117
-228
@@ -25,9 +25,8 @@ static uint8_t blockAnim = 0;
|
||||
|
||||
static bool isInitialized = false;
|
||||
bool isPaused = false;
|
||||
bool isBeep = false;
|
||||
|
||||
uint8_t levelCountBreackout = 1;
|
||||
uint8_t levelCountBreakout = 1;
|
||||
|
||||
uint16_t tone = 0;
|
||||
uint16_t score = 0;
|
||||
@@ -42,115 +41,7 @@ Brick brick[BRICK_NUMBER];
|
||||
Racket racket;
|
||||
Ball ball;
|
||||
|
||||
const uint8_t BITMAP_block[4][15] =
|
||||
{
|
||||
{
|
||||
0b00011110,
|
||||
0b00110001,
|
||||
0b00101001,
|
||||
0b00100101,
|
||||
0b00100011,
|
||||
0b00110001,
|
||||
0b00101001,
|
||||
0b00100101,
|
||||
0b00100011,
|
||||
0b00110001,
|
||||
0b00101001,
|
||||
0b00100101,
|
||||
0b00100011,
|
||||
0b00110001,
|
||||
0b00011110,
|
||||
},
|
||||
{
|
||||
0b00011110,
|
||||
0b00101001,
|
||||
0b00100101,
|
||||
0b00100011,
|
||||
0b00110001,
|
||||
0b00101001,
|
||||
0b00100101,
|
||||
0b00100011,
|
||||
0b00110001,
|
||||
0b00101001,
|
||||
0b00100101,
|
||||
0b00100011,
|
||||
0b00110001,
|
||||
0b00101001,
|
||||
0b00011110,
|
||||
},
|
||||
{
|
||||
0b00011110,
|
||||
0b00100101,
|
||||
0b00100011,
|
||||
0b00110001,
|
||||
0b00101001,
|
||||
0b00100101,
|
||||
0b00100011,
|
||||
0b00110001,
|
||||
0b00101001,
|
||||
0b00100101,
|
||||
0b00100011,
|
||||
0b00110001,
|
||||
0b00101001,
|
||||
0b00100101,
|
||||
0b00011110,
|
||||
},
|
||||
{
|
||||
0b00011110,
|
||||
0b00100011,
|
||||
0b00110001,
|
||||
0b00101001,
|
||||
0b00100101,
|
||||
0b00100011,
|
||||
0b00110001,
|
||||
0b00101001,
|
||||
0b00100101,
|
||||
0b00100011,
|
||||
0b00110001,
|
||||
0b00101001,
|
||||
0b00100101,
|
||||
0b00100011,
|
||||
0b00011110,
|
||||
}
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_blockOn[15] =
|
||||
{
|
||||
0b00011110,
|
||||
0b00111111,
|
||||
0b00111111,
|
||||
0b00111111,
|
||||
0b00111111,
|
||||
0b00111111,
|
||||
0b00111111,
|
||||
0b00111111,
|
||||
0b00111111,
|
||||
0b00111111,
|
||||
0b00111111,
|
||||
0b00111111,
|
||||
0b00111111,
|
||||
0b00111111,
|
||||
0b00011110,
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_blockEmpty[15] =
|
||||
{
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
};
|
||||
static const uint8_t BRICK_ANIM_PATTERNS[4] = {0b00110001, 0b00101001, 0b00100101, 0b00100011};
|
||||
|
||||
// Initialise seed
|
||||
void srand_custom(uint32_t seed) {
|
||||
@@ -172,18 +63,16 @@ int randInt(int min, int max) {
|
||||
void reset(void)
|
||||
{
|
||||
ballCount = BALL_NUMBER;
|
||||
levelCountBreackout = 1;
|
||||
levelCountBreakout = 1;
|
||||
score = 0;
|
||||
}
|
||||
|
||||
// PlayBeep
|
||||
void playBeep(uint16_t tone)
|
||||
{
|
||||
BK4819_PlayTone(tone, true); // 500 Hz ON
|
||||
BK4819_PrepareToPlayTone(true);
|
||||
AUDIO_AudioPathOn();
|
||||
BK4819_ExitTxMute();
|
||||
SYSTEM_DelayMs(100);
|
||||
BK4819_EnterTxMute();
|
||||
BK4819_PlayToneRaw(tone, 100);
|
||||
AUDIO_AudioPathOff();
|
||||
}
|
||||
|
||||
@@ -191,10 +80,10 @@ void playBeep(uint16_t tone)
|
||||
void drawScore()
|
||||
{
|
||||
// Clean status line
|
||||
memset(gStatusLine, 0, sizeof(gStatusLine));
|
||||
UI_StatusClear();
|
||||
|
||||
// Level
|
||||
sprintf(str, "Level %02u", levelCountBreackout);
|
||||
sprintf(str, "Level %02u", levelCountBreakout);
|
||||
GUI_DisplaySmallest(str, 0, 1, true, true);
|
||||
|
||||
// Ball
|
||||
@@ -206,6 +95,12 @@ void drawScore()
|
||||
GUI_DisplaySmallest(str, 88, 1, true, true);
|
||||
}
|
||||
|
||||
// Render the ball
|
||||
void renderBall(bool state) {
|
||||
UI_DrawRectangleBuffer(gFrameBuffer, ball.x, ball.y, ball.x + ball.w - 1, ball.y + ball.h - 1, state);
|
||||
UI_DrawLineBuffer(gFrameBuffer, ball.x - 1, ball.y + 1, ball.x + ball.w, ball.y + 1, state);
|
||||
}
|
||||
|
||||
// Init ball
|
||||
void initBall() {
|
||||
ball.x = 62;
|
||||
@@ -215,21 +110,25 @@ void initBall() {
|
||||
ball.dx = 0;
|
||||
ball.dy = 1;
|
||||
|
||||
UI_DrawRectangleBuffer(gFrameBuffer, ball.x, ball.y, ball.x + ball.w -1, ball.y + ball.h - 1, true);
|
||||
UI_DrawLineBuffer(gFrameBuffer, ball.x - 1, ball.y + 1, ball.x + ball.w, ball.y + 1, true);
|
||||
renderBall(true);
|
||||
}
|
||||
|
||||
// Calculate the direction of the bounced ball
|
||||
void directionBall(int16_t x, uint8_t w, int8_t num) {
|
||||
ball.dx = map(x + w - ball.x, 0, w, num, -num);
|
||||
ball.dy *= -1;
|
||||
}
|
||||
|
||||
// Draw ball
|
||||
void drawBall() {
|
||||
UI_DrawRectangleBuffer(gFrameBuffer, ball.x, ball.y, ball.x + ball.w -1, ball.y + ball.h -1, false);
|
||||
UI_DrawLineBuffer(gFrameBuffer, ball.x - 1, ball.y + 1, ball.x + ball.w, ball.y + 1, false);
|
||||
renderBall(false);
|
||||
|
||||
ball.x += ball.dx;
|
||||
ball.y += ball.dy;
|
||||
|
||||
if (ball.y <= 0) // Up
|
||||
{
|
||||
ball.dx = map(randInt(0, 7), 0, 7, 3, -3);
|
||||
ball.dx = randInt(-3, 3);
|
||||
ball.dy = 1;
|
||||
}
|
||||
else if (ball.x <= 2) // Left
|
||||
@@ -243,9 +142,7 @@ void drawBall() {
|
||||
// And now Down...
|
||||
if (ball.y == 47) {
|
||||
if (ball.x + 1 >= racket.x && ball.x - 1 <= racket.x + racket.w) {
|
||||
ball.dx = map(racket.x + racket.w - ball.x, 0, racket.w, 3, -3);
|
||||
ball.dy *= -1;
|
||||
isBeep = true;
|
||||
directionBall(racket.x, racket.w, 3);
|
||||
tone = 400;
|
||||
}
|
||||
}
|
||||
@@ -253,7 +150,6 @@ void drawBall() {
|
||||
ballCount--;
|
||||
UI_DisplayClear();
|
||||
drawScore();
|
||||
isBeep = true;
|
||||
tone = 800;
|
||||
if (ballCount < 0) {
|
||||
reset();
|
||||
@@ -261,15 +157,13 @@ void drawBall() {
|
||||
drawWall();
|
||||
|
||||
isPaused = true;
|
||||
UI_PrintStringSmallBold("GAME", 32, 0, 4);
|
||||
UI_PrintStringSmallBold("OVER", 66, 0, 4);
|
||||
UI_PrintStringSmallBold("GAME OVER", 0, LCD_WIDTH - 1, 4);
|
||||
}
|
||||
initRacket();
|
||||
initBall();
|
||||
}
|
||||
|
||||
UI_DrawRectangleBuffer(gFrameBuffer, ball.x, ball.y, ball.x + ball.w -1, ball.y + ball.h - 1, true);
|
||||
UI_DrawLineBuffer(gFrameBuffer, ball.x - 1, ball.y + 1, ball.x + ball.w, ball.y + 1, true);
|
||||
renderBall(true);
|
||||
}
|
||||
|
||||
// Init wall
|
||||
@@ -289,7 +183,6 @@ void initWall() {
|
||||
brick[i].y = -8 + 8 * k;
|
||||
brick[i].w = 14;
|
||||
brick[i].h = 5;
|
||||
brick[i].s = randInt(0, 3);
|
||||
brick[i].destroy = false;
|
||||
|
||||
j++;
|
||||
@@ -302,6 +195,11 @@ void drawWall() {
|
||||
|
||||
for (i = 0; i < BRICK_NUMBER; i++) {
|
||||
if (brick[i].destroy == false) {
|
||||
uint8_t *fb_ptr = gFrameBuffer[brick[i].y / 8] + brick[i].x;
|
||||
|
||||
fb_ptr[0] = 0b00011110;
|
||||
fb_ptr[14] = 0b00011110;
|
||||
|
||||
if ((ball.x + 1 >= brick[i].x &&
|
||||
ball.x - 1 <= brick[i].x + brick[i].w) &&
|
||||
((ball.y + 1 >= brick[i].y &&
|
||||
@@ -309,32 +207,37 @@ void drawWall() {
|
||||
brick[i].destroy = true;
|
||||
score++;
|
||||
|
||||
ball.dx = map(brick[i].x + brick[i].w - ball.x, 0, brick[i].w, 2, -2);
|
||||
ball.dy *= -1;
|
||||
directionBall(brick[i].x, brick[i].w, 2);
|
||||
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, true);
|
||||
memcpy(gFrameBuffer[brick[i].y / 8] + brick[i].x, BITMAP_blockOn, sizeof(BITMAP_blockOn));
|
||||
memset(fb_ptr + 1, 0b00111111, 13);
|
||||
ST7565_BlitLine(brick[i].y / 8);
|
||||
playBeep(600);
|
||||
memcpy(gFrameBuffer[brick[i].y / 8] + brick[i].x, BITMAP_blockEmpty, sizeof(BITMAP_blockEmpty));
|
||||
memset(fb_ptr + 0, 0b00000000, 15);
|
||||
ST7565_BlitLine(brick[i].y / 8);
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false);
|
||||
|
||||
if (score % BRICK_NUMBER == 0) {
|
||||
levelCountBreackout++;
|
||||
levelCountBreakout++;
|
||||
ballCount++;
|
||||
initWall();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (brick[i].destroy == false) {
|
||||
memcpy(gFrameBuffer[brick[i].y / 8] + brick[i].x, BITMAP_block[(brick[i].s + blockAnim) % 4], sizeof(BITMAP_block[(brick[i].s + blockAnim) % 4]));
|
||||
} else {
|
||||
for(uint8_t k = 0; k < 13; k++) {
|
||||
fb_ptr[k + 1] = BRICK_ANIM_PATTERNS[(blockAnim + k) % 4];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Render the racket shape
|
||||
void renderRacket(int x, bool state) {
|
||||
UI_DrawRectangleBuffer(gFrameBuffer, x + 1, racket.y, x + racket.w - 2, racket.y + racket.h, state);
|
||||
UI_DrawLineBuffer(gFrameBuffer, x, racket.y + 1, x + racket.w - 1, racket.y + 1, state);
|
||||
}
|
||||
|
||||
// Init racket
|
||||
void initRacket() {
|
||||
racket.w = 24;
|
||||
@@ -343,18 +246,15 @@ void initRacket() {
|
||||
racket.h = 2;
|
||||
racket.p = racket.x;
|
||||
|
||||
UI_DrawRectangleBuffer(gFrameBuffer, racket.x + 1, racket.y, racket.x + racket.w - 2, racket.y + racket.h, true);
|
||||
UI_DrawLineBuffer(gFrameBuffer, racket.x, racket.y + 1, racket.x + racket.w - 1, racket.y + 1, true);
|
||||
renderRacket(racket.x, true);
|
||||
}
|
||||
|
||||
// Draw racket
|
||||
void drawRacket() {
|
||||
if (racket.p != racket.x) {
|
||||
UI_DrawRectangleBuffer(gFrameBuffer, racket.p + 1, racket.y, racket.p + racket.w - 2, racket.y + racket.h, false);
|
||||
UI_DrawLineBuffer(gFrameBuffer, racket.p, racket.y + 1, racket.p + racket.w - 1, racket.y + 1, false);
|
||||
renderRacket(racket.p, false);
|
||||
racket.p = racket.x;
|
||||
UI_DrawRectangleBuffer(gFrameBuffer, racket.x + 1, racket.y, racket.x + racket.w - 2, racket.y + racket.h, true);
|
||||
UI_DrawLineBuffer(gFrameBuffer, racket.x, racket.y + 1, racket.x + racket.w - 1, racket.y + 1, true);
|
||||
renderRacket(racket.x, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,7 +282,7 @@ static void OnKeyDown(uint8_t key)
|
||||
kbd.counter = 0;
|
||||
if(isPaused)
|
||||
{
|
||||
UI_PrintStringSmallBold("PAUSE", 0, 128, 4);
|
||||
UI_PrintStringSmallBold("PAUSE", 0, LCD_WIDTH - 1, 4);
|
||||
}
|
||||
break;
|
||||
case KEY_EXIT:
|
||||
@@ -401,46 +301,31 @@ static void OnKeyDown(uint8_t key)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Key
|
||||
static KEY_Code_t GetKey()
|
||||
{
|
||||
KEY_Code_t btn = KEYBOARD_Poll();
|
||||
if (btn == KEY_INVALID && GPIO_IsPttPressed())
|
||||
{
|
||||
btn = KEY_PTT;
|
||||
}
|
||||
return btn;
|
||||
}
|
||||
|
||||
// HandleUserInput
|
||||
static bool HandleUserInput()
|
||||
{
|
||||
// Store previous key state
|
||||
kbd.prev = kbd.current;
|
||||
|
||||
// Get the current key
|
||||
kbd.current = GetKey();
|
||||
|
||||
// Detect valid key press continuation (same key still pressed)
|
||||
if (kbd.current != KEY_INVALID && kbd.current == kbd.prev)
|
||||
{
|
||||
kbd.counter = 1;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
// Parse incoming packets on every tick so serial keys are never missed,
|
||||
// regardless of whether the screen needs redrawing.
|
||||
SCREENSHOT_ParseInput();
|
||||
#endif
|
||||
|
||||
kbd.prev = kbd.current;
|
||||
kbd.current = KEYBOARD_GetKey();
|
||||
|
||||
if (kbd.current == KEY_INVALID)
|
||||
return true;
|
||||
|
||||
if (
|
||||
// Movement keys: dispatch on every tick while held
|
||||
(kbd.current == KEY_UP || kbd.current == KEY_DOWN ||
|
||||
kbd.current == KEY_4 || kbd.current == KEY_0) ||
|
||||
// Action keys (MENU, EXIT): dispatch only on rising edge
|
||||
(kbd.current != kbd.prev)
|
||||
){
|
||||
OnKeyDown(kbd.current);
|
||||
|
||||
// Special handling for MENU key
|
||||
if(kbd.current == KEY_MENU)
|
||||
{
|
||||
kbd.counter = 0;
|
||||
SYSTEM_DelayMs(250);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
kbd.counter = 0;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -448,62 +333,66 @@ static bool HandleUserInput()
|
||||
static void Tick()
|
||||
{
|
||||
HandleUserInput();
|
||||
HandleUserInput();
|
||||
}
|
||||
|
||||
// APP_RunBreakout
|
||||
void APP_RunBreakout(void) {
|
||||
static uint8_t swap = 0;
|
||||
static uint8_t swap = 0;
|
||||
|
||||
// Init seed
|
||||
srand_custom(BK4819_ReadRegister(BK4819_REG_67) & 0x01FF * gBatteryVoltageAverage * gEeprom.VfoInfo[0].pRX->Frequency);
|
||||
// Init seed
|
||||
srand_custom(BK4819_ReadRegister(BK4819_REG_67) & 0x01FF * gBatteryVoltageAverage * gEeprom.VfoInfo[0].pRX->Frequency);
|
||||
|
||||
// Init led
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false);
|
||||
// Init led
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false);
|
||||
|
||||
// Init game
|
||||
UI_DisplayClear();
|
||||
reset();
|
||||
initWall();
|
||||
initRacket();
|
||||
initBall();
|
||||
memset(gStatusLine, 0, sizeof(gStatusLine));
|
||||
isInitialized = true;
|
||||
// Finish the brightness fade if it is in progress
|
||||
BACKLIGHT_UpdateTickless();
|
||||
|
||||
while(isInitialized)
|
||||
// Init game
|
||||
UI_DisplayClear();
|
||||
reset();
|
||||
initWall();
|
||||
initRacket();
|
||||
initBall();
|
||||
UI_StatusClear();
|
||||
isInitialized = true;
|
||||
|
||||
while(isInitialized)
|
||||
{
|
||||
Tick();
|
||||
if(!isPaused)
|
||||
{
|
||||
Tick();
|
||||
if(!isPaused)
|
||||
if(swap == 0)
|
||||
{
|
||||
if(swap == 0)
|
||||
{
|
||||
blockAnim = (blockAnim + 1) % 4;
|
||||
|
||||
// For screenshot
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
swap = (swap + 1) % 4;
|
||||
|
||||
drawScore();
|
||||
drawWall();
|
||||
drawRacket();
|
||||
drawBall();
|
||||
|
||||
if(isBeep)
|
||||
{
|
||||
playBeep(tone);
|
||||
isBeep = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
SYSTEM_DelayMs(40 - MIN(levelCountBreackout - 1, 20)); // Add more fun...
|
||||
}
|
||||
blockAnim = (blockAnim + 1) % 4;
|
||||
}
|
||||
|
||||
swap = (swap + 1) % 4;
|
||||
|
||||
ST7565_BlitStatusLine(); // Blank status line
|
||||
ST7565_BlitFullScreen();
|
||||
drawScore();
|
||||
drawWall();
|
||||
drawRacket();
|
||||
drawBall();
|
||||
|
||||
if(tone != 0)
|
||||
{
|
||||
playBeep(tone);
|
||||
tone = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
SYSTEM_DelayMs(40 - MIN(levelCountBreakout - 1, 20)); // Add more fun...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ST7565_BlitStatusLine(); // Blank status line
|
||||
ST7565_BlitFullScreen();
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
if(isPaused || swap == 0)
|
||||
{
|
||||
SCREENSHOT_Update(false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,6 @@ typedef struct {
|
||||
uint8_t y; // y
|
||||
uint8_t w; // width
|
||||
uint8_t h; // height
|
||||
uint8_t s; // style
|
||||
bool destroy; // active, if true, check this button, else bypass
|
||||
} Brick;
|
||||
|
||||
|
||||
+833
-5
@@ -1,9 +1,17 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/chFrScanner.h"
|
||||
#include "audio.h"
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
#include "driver/systick.h"
|
||||
#endif
|
||||
#include "functions.h"
|
||||
#include "misc.h"
|
||||
#include "settings.h"
|
||||
#include "ui/main.h"
|
||||
//#include "debugging.h"
|
||||
|
||||
int8_t gScanStateDir;
|
||||
@@ -13,6 +21,29 @@ bool gScanPauseMode;
|
||||
#ifdef ENABLE_SCAN_RANGES
|
||||
uint32_t gScanRangeStart;
|
||||
uint32_t gScanRangeStop;
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE) && ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE
|
||||
DCS_CodeType_t gScanRangeCssType = CODE_TYPE_OFF;
|
||||
uint8_t gScanRangeCssCode = 0xFF;
|
||||
static uint8_t scanRangeCssCandidate = 0xFF;
|
||||
static uint8_t scanRangeCssHitCount = 0;
|
||||
#endif
|
||||
|
||||
#define SCAN_RANGE_SKIP_MAX 32
|
||||
#if (SCAN_RANGE_SKIP_MAX & (SCAN_RANGE_SKIP_MAX - 1)) != 0
|
||||
#error SCAN_RANGE_SKIP_MAX must be a power of two
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint16_t sample[SCAN_RANGE_SKIP_MAX];
|
||||
uint32_t start;
|
||||
uint32_t stop;
|
||||
uint16_t step;
|
||||
uint8_t count;
|
||||
uint8_t next;
|
||||
} ScanRangeSkipList_t;
|
||||
|
||||
static ScanRangeSkipList_t scanRangeSkip;
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
@@ -36,6 +67,687 @@ uint8_t initialCROSS_BAND_RX_TX;
|
||||
|
||||
static void NextFreqChannel(void);
|
||||
static void NextMemChannel(void);
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
static void ScanFastResetState(void);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
static bool ScanFastEnabled(void)
|
||||
{
|
||||
return gSetting_set_scn;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SCAN_RANGES
|
||||
static void ScanRangeSkipClear(void)
|
||||
{
|
||||
scanRangeSkip.count = 0;
|
||||
scanRangeSkip.next = 0;
|
||||
}
|
||||
|
||||
static void ScanRangeSkipSync(void)
|
||||
{
|
||||
const uint16_t step = gRxVfo->StepFrequency;
|
||||
|
||||
if (scanRangeSkip.start == gScanRangeStart &&
|
||||
scanRangeSkip.stop == gScanRangeStop &&
|
||||
scanRangeSkip.step == step)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ScanRangeSkipClear();
|
||||
scanRangeSkip.start = gScanRangeStart;
|
||||
scanRangeSkip.stop = gScanRangeStop;
|
||||
scanRangeSkip.step = step;
|
||||
}
|
||||
|
||||
static uint16_t ScanRangeSkipSampleForFrequency(uint32_t frequency)
|
||||
{
|
||||
const uint16_t step = gRxVfo->StepFrequency;
|
||||
if (!gScanRangeStart || step == 0 || frequency < gScanRangeStart || frequency > gScanRangeStop)
|
||||
return 0;
|
||||
|
||||
const uint32_t sample = (frequency - gScanRangeStart) / step;
|
||||
if (sample >= 0xFFFFu)
|
||||
return 0;
|
||||
|
||||
return (uint16_t)(sample + 1);
|
||||
}
|
||||
|
||||
static bool ScanRangeSkipContainsFrequency(uint32_t frequency)
|
||||
{
|
||||
const uint16_t sample = ScanRangeSkipSampleForFrequency(frequency);
|
||||
if (sample == 0)
|
||||
return false;
|
||||
|
||||
for (uint8_t i = 0; i < scanRangeSkip.count; ++i)
|
||||
if (scanRangeSkip.sample[i] == sample)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint32_t ScanRangeNextFrequency(void)
|
||||
{
|
||||
uint32_t frequency;
|
||||
uint8_t guard = SCAN_RANGE_SKIP_MAX + 1;
|
||||
|
||||
ScanRangeSkipSync();
|
||||
|
||||
do
|
||||
{
|
||||
frequency = APP_SetFreqByStepAndLimits(gRxVfo, gScanStateDir, gScanRangeStart, gScanRangeStop);
|
||||
gRxVfo->freq_config_RX.Frequency = frequency;
|
||||
} while (--guard && ScanRangeSkipContainsFrequency(frequency));
|
||||
|
||||
return frequency;
|
||||
}
|
||||
|
||||
bool CHFRSCANNER_ExcludeCurrentScanRange(void)
|
||||
{
|
||||
ScanRangeSkipSync();
|
||||
|
||||
uint16_t sample = ScanRangeSkipSampleForFrequency(lastFoundFrqOrChan);
|
||||
if (sample == 0)
|
||||
sample = ScanRangeSkipSampleForFrequency(gRxVfo->freq_config_RX.Frequency);
|
||||
|
||||
if (sample == 0)
|
||||
return false;
|
||||
|
||||
for (uint8_t i = 0; i < scanRangeSkip.count; ++i)
|
||||
if (scanRangeSkip.sample[i] == sample)
|
||||
return true;
|
||||
|
||||
scanRangeSkip.sample[scanRangeSkip.next] = sample;
|
||||
scanRangeSkip.next = (scanRangeSkip.next + 1) & (SCAN_RANGE_SKIP_MAX - 1);
|
||||
if (scanRangeSkip.count < SCAN_RANGE_SKIP_MAX)
|
||||
scanRangeSkip.count++;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
ScanFastResetState();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CHFRSCANNER_HasScanRangeExcludedOrdinal(uint32_t first_ordinal, uint32_t last_ordinal)
|
||||
{
|
||||
if (first_ordinal == 0)
|
||||
first_ordinal = 1;
|
||||
if (last_ordinal < first_ordinal)
|
||||
return false;
|
||||
if (first_ordinal > 0xFFFFu)
|
||||
return false;
|
||||
if (last_ordinal > 0xFFFFu)
|
||||
last_ordinal = 0xFFFFu;
|
||||
|
||||
for (uint8_t i = 0; i < scanRangeSkip.count; ++i) {
|
||||
const uint16_t sample = scanRangeSkip.sample[i];
|
||||
if (sample >= first_ordinal && sample <= last_ordinal)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE) && ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE
|
||||
void CHFRSCANNER_UpdateCssDetection(void)
|
||||
{
|
||||
if (!gScanRangeStart || !FUNCTION_IsRx())
|
||||
{
|
||||
gScanRangeCssType = CODE_TYPE_OFF;
|
||||
gScanRangeCssCode = 0xFF;
|
||||
scanRangeCssCandidate = 0xFF;
|
||||
scanRangeCssHitCount = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t cdcssFreq;
|
||||
uint16_t ctcssFreq;
|
||||
const BK4819_CssScanResult_t result = BK4819_GetCxCSSScanResult(&cdcssFreq, &ctcssFreq);
|
||||
|
||||
if (result == BK4819_CSS_RESULT_CDCSS)
|
||||
{
|
||||
const uint8_t Code = DCS_GetCdcssCode(cdcssFreq);
|
||||
if (Code != 0xFF && Code != gScanRangeCssCode)
|
||||
{
|
||||
gScanRangeCssType = CODE_TYPE_DIGITAL;
|
||||
gScanRangeCssCode = Code;
|
||||
scanRangeCssCandidate = 0xFF;
|
||||
scanRangeCssHitCount = 0;
|
||||
gUpdateDisplay = true;
|
||||
}
|
||||
}
|
||||
else if (result == BK4819_CSS_RESULT_CTCSS)
|
||||
{
|
||||
const uint8_t Code = DCS_GetCtcssCode(ctcssFreq);
|
||||
if (Code != 0xFF)
|
||||
{
|
||||
if (Code == scanRangeCssCandidate)
|
||||
{
|
||||
if (++scanRangeCssHitCount >= 2 && Code != gScanRangeCssCode)
|
||||
{
|
||||
gScanRangeCssType = CODE_TYPE_CONTINUOUS_TONE;
|
||||
gScanRangeCssCode = Code;
|
||||
gUpdateDisplay = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
scanRangeCssCandidate = Code;
|
||||
scanRangeCssHitCount = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static void CHFRSCANNER_AbortActiveReception(void)
|
||||
{
|
||||
if (!FUNCTION_IsRx())
|
||||
return;
|
||||
|
||||
AUDIO_AudioPathOff();
|
||||
gEnableSpeaker = false;
|
||||
gMonitor = false;
|
||||
gRxReceptionMode = RX_MODE_NONE;
|
||||
gScanPauseMode = false;
|
||||
|
||||
FUNCTION_Init();
|
||||
FUNCTION_Select(FUNCTION_FOREGROUND);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
#define SCAN_FAST_PRECHECK_STEPS 6
|
||||
#define SCAN_FAST_RSSI_MARGIN 16
|
||||
#define SCAN_FAST_SQUELCH_MARGIN 8
|
||||
#define SCAN_FAST_WEAK_MARGIN 8
|
||||
#define SCAN_FAST_RECHECK_DELAY_US 350
|
||||
#define SCAN_FAST_FINE_STEP_LIMIT 250
|
||||
#define SCAN_FAST_FINE_REFINE_SPAN 1000
|
||||
#define SCAN_FAST_FINE_REFINE_MAX 80
|
||||
#define SCAN_FAST_FINE_RSSI_DROP 8
|
||||
#define SCAN_FAST_RSSI_MAX 65535u
|
||||
// Settle loop guard: at most this many 1us waits while the BK4819 glitch
|
||||
// indicator stays above SCAN_FAST_GLITCH_THRESHOLD. Caps the worst-case
|
||||
// settling time per step at ~50us before we read the RSSI anyway.
|
||||
#define SCAN_FAST_GLITCH_GUARD_MAX 50
|
||||
#define SCAN_FAST_GLITCH_THRESHOLD 200
|
||||
// HF/VHF boundary in Hz: BK4819_PickRXFilterPathBasedOnFrequency() switches
|
||||
// the front-end filter path here, so we only re-run that (relatively
|
||||
// expensive) call when we actually cross the boundary.
|
||||
#define SCAN_FAST_HF_VHF_BOUNDARY_HZ 28000000u
|
||||
|
||||
typedef enum {
|
||||
SCAN_FAST_DISABLED,
|
||||
SCAN_FAST_QUIET_BATCH,
|
||||
SCAN_FAST_CANDIDATE
|
||||
} scan_fast_result_t;
|
||||
|
||||
static uint16_t scanFastReg30;
|
||||
static uint16_t scanFastNoiseFloor = SCAN_FAST_RSSI_MAX;
|
||||
static uint32_t scanFastPrevFrequency;
|
||||
static bool scanFastLastFullTuneCandidate;
|
||||
static VFO_Info_t scanFastDisplayVfo;
|
||||
static bool scanFastDisplayVfoValid;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_RSSI
|
||||
static uint16_t scanRssiSparkline[CHFRSCANNER_RSSI_SPARKLINE_WIDTH];
|
||||
static uint8_t scanRssiSparklineWrite;
|
||||
static uint8_t scanRssiSparklineCount;
|
||||
|
||||
static void ScanRssiSparklineReset(void)
|
||||
{
|
||||
memset(scanRssiSparkline, 0, sizeof(scanRssiSparkline));
|
||||
scanRssiSparklineWrite = 0;
|
||||
scanRssiSparklineCount = 0;
|
||||
}
|
||||
|
||||
static void ScanRssiSparklinePush(uint16_t rssi)
|
||||
{
|
||||
scanRssiSparkline[scanRssiSparklineWrite] = rssi;
|
||||
scanRssiSparklineWrite++;
|
||||
if (scanRssiSparklineWrite >= CHFRSCANNER_RSSI_SPARKLINE_WIDTH)
|
||||
scanRssiSparklineWrite = 0;
|
||||
|
||||
if (scanRssiSparklineCount < CHFRSCANNER_RSSI_SPARKLINE_WIDTH)
|
||||
scanRssiSparklineCount++;
|
||||
}
|
||||
|
||||
bool CHFRSCANNER_HasScanRssiSparkline(void)
|
||||
{
|
||||
return gScanStateDir != SCAN_OFF && scanRssiSparklineCount > 1;
|
||||
}
|
||||
|
||||
uint8_t CHFRSCANNER_GetScanRssiSparklineLevel(uint8_t index)
|
||||
{
|
||||
uint16_t minRssi = SCAN_FAST_RSSI_MAX;
|
||||
uint16_t rssi = 0;
|
||||
uint8_t oldest;
|
||||
|
||||
if (index >= CHFRSCANNER_RSSI_SPARKLINE_WIDTH || scanRssiSparklineCount == 0)
|
||||
return 0;
|
||||
|
||||
if (index < CHFRSCANNER_RSSI_SPARKLINE_WIDTH - scanRssiSparklineCount)
|
||||
return 0;
|
||||
|
||||
oldest = (uint8_t)((scanRssiSparklineWrite + CHFRSCANNER_RSSI_SPARKLINE_WIDTH - scanRssiSparklineCount)
|
||||
% CHFRSCANNER_RSSI_SPARKLINE_WIDTH);
|
||||
index -= (uint8_t)(CHFRSCANNER_RSSI_SPARKLINE_WIDTH - scanRssiSparklineCount);
|
||||
|
||||
for (uint8_t i = 0; i < scanRssiSparklineCount; i++)
|
||||
{
|
||||
const uint16_t sample = scanRssiSparkline[(oldest + i) % CHFRSCANNER_RSSI_SPARKLINE_WIDTH];
|
||||
if (sample != 0 && sample < minRssi)
|
||||
minRssi = sample;
|
||||
}
|
||||
|
||||
rssi = scanRssiSparkline[(oldest + index) % CHFRSCANNER_RSSI_SPARKLINE_WIDTH];
|
||||
if (rssi == 0 || minRssi == SCAN_FAST_RSSI_MAX || rssi <= minRssi + 2)
|
||||
return 0;
|
||||
|
||||
// 48 RSSI units ~= 24 dB. This keeps quiet jitter low while strong hits pop.
|
||||
return (uint8_t)MIN(((uint32_t)(rssi - minRssi) * 5u + 24u) / 48u, 5u);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ScanFastResetState(void)
|
||||
{
|
||||
// Called on every scan (re)start, after a reception, and on each
|
||||
// wraparound to the start of the channel list / range. The noise
|
||||
// floor is re-warmed up from current conditions instead of carrying
|
||||
// stale calibration into a later sweep.
|
||||
scanFastNoiseFloor = SCAN_FAST_RSSI_MAX;
|
||||
scanFastPrevFrequency = 0;
|
||||
scanFastLastFullTuneCandidate = false;
|
||||
scanFastDisplayVfoValid = false;
|
||||
}
|
||||
|
||||
static void ScanFastResetNoiseFloor(void)
|
||||
{
|
||||
scanFastNoiseFloor = SCAN_FAST_RSSI_MAX;
|
||||
}
|
||||
|
||||
static uint16_t ScanFastReadRssi(void)
|
||||
{
|
||||
uint8_t guard = SCAN_FAST_GLITCH_GUARD_MAX;
|
||||
while (guard-- && BK4819_GetGlitchIndicator() >= SCAN_FAST_GLITCH_THRESHOLD)
|
||||
{
|
||||
SYSTICK_DelayUs(1);
|
||||
}
|
||||
|
||||
// Discard first read: after fast tuning the RSSI/AGC value may still be stale.
|
||||
BK4819_GetRSSI();
|
||||
return BK4819_GetRSSI();
|
||||
}
|
||||
|
||||
static void ScanFastTune(uint32_t frequency)
|
||||
{
|
||||
if (scanFastPrevFrequency == 0 ||
|
||||
((frequency < SCAN_FAST_HF_VHF_BOUNDARY_HZ) !=
|
||||
(scanFastPrevFrequency < SCAN_FAST_HF_VHF_BOUNDARY_HZ)))
|
||||
{
|
||||
BK4819_PickRXFilterPathBasedOnFrequency(frequency);
|
||||
}
|
||||
|
||||
scanFastPrevFrequency = frequency;
|
||||
BK4819_SetFrequency(frequency);
|
||||
BK4819_WriteRegister(BK4819_REG_30, 0);
|
||||
BK4819_WriteRegister(BK4819_REG_30, scanFastReg30);
|
||||
}
|
||||
|
||||
const VFO_Info_t *CHFRSCANNER_GetScanDisplayVfo(void)
|
||||
{
|
||||
if (!ScanFastEnabled() || !scanFastDisplayVfoValid || gScanStateDir == SCAN_OFF || FUNCTION_IsRx())
|
||||
return NULL;
|
||||
|
||||
return &scanFastDisplayVfo;
|
||||
}
|
||||
|
||||
static bool ScanFastUpdateDisplayVfo(uint16_t channel, uint32_t *frequency, ModulationMode_t *modulation)
|
||||
{
|
||||
ChannelScanDisplayInfo_t info;
|
||||
|
||||
if (!SETTINGS_FetchChannelScanDisplayInfo(channel, &info))
|
||||
{
|
||||
scanFastDisplayVfoValid = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
scanFastDisplayVfo = gEeprom.VfoInfo[gEeprom.RX_VFO];
|
||||
|
||||
scanFastDisplayVfo.CHANNEL_SAVE = channel;
|
||||
scanFastDisplayVfo.freq_config_RX = info.rx;
|
||||
scanFastDisplayVfo.freq_config_TX = info.tx;
|
||||
scanFastDisplayVfo.TX_OFFSET_FREQUENCY = info.offset;
|
||||
scanFastDisplayVfo.StepFrequency = info.stepFrequency;
|
||||
scanFastDisplayVfo.STEP_SETTING = info.stepSetting;
|
||||
scanFastDisplayVfo.Modulation = info.modulation;
|
||||
scanFastDisplayVfo.TX_OFFSET_FREQUENCY_DIRECTION = info.txOffsetFrequencyDirection;
|
||||
scanFastDisplayVfo.OUTPUT_POWER = info.outputPower;
|
||||
scanFastDisplayVfo.FrequencyReverse = info.frequencyReverse;
|
||||
scanFastDisplayVfo.CHANNEL_BANDWIDTH = info.channelBandwidth;
|
||||
scanFastDisplayVfo.BUSY_CHANNEL_LOCK = info.busyChannelLock;
|
||||
scanFastDisplayVfo.TX_LOCK = info.txLock;
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
scanFastDisplayVfo.DTMF_DECODING_ENABLE = info.dtmfDecodingEnable;
|
||||
#endif
|
||||
scanFastDisplayVfo.DTMF_PTT_ID_TX_MODE = info.dtmfPttIdTxMode;
|
||||
|
||||
if (!scanFastDisplayVfo.FrequencyReverse)
|
||||
{
|
||||
scanFastDisplayVfo.pRX = &scanFastDisplayVfo.freq_config_RX;
|
||||
scanFastDisplayVfo.pTX = &scanFastDisplayVfo.freq_config_TX;
|
||||
}
|
||||
else
|
||||
{
|
||||
scanFastDisplayVfo.pRX = &scanFastDisplayVfo.freq_config_TX;
|
||||
scanFastDisplayVfo.pTX = &scanFastDisplayVfo.freq_config_RX;
|
||||
}
|
||||
|
||||
scanFastDisplayVfoValid = true;
|
||||
|
||||
if (frequency)
|
||||
*frequency = info.rx.Frequency;
|
||||
if (modulation)
|
||||
*modulation = info.modulation;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint16_t ScanFastSaturatingAdd(uint16_t value, uint16_t add)
|
||||
{
|
||||
return (value > SCAN_FAST_RSSI_MAX - add) ? SCAN_FAST_RSSI_MAX : (uint16_t)(value + add);
|
||||
}
|
||||
|
||||
static uint16_t ScanFastSaturatingSub(uint16_t value, uint16_t sub)
|
||||
{
|
||||
return (value > sub) ? (uint16_t)(value - sub) : 0;
|
||||
}
|
||||
|
||||
static uint16_t ScanFastGetNoiseTrigger(void)
|
||||
{
|
||||
return ScanFastSaturatingAdd(scanFastNoiseFloor, SCAN_FAST_RSSI_MARGIN);
|
||||
}
|
||||
|
||||
static uint16_t ScanFastGetSquelchTrigger(void)
|
||||
{
|
||||
return ScanFastSaturatingSub(gRxVfo->SquelchOpenRSSIThresh, SCAN_FAST_SQUELCH_MARGIN);
|
||||
}
|
||||
|
||||
static bool ScanFastIsNearCandidate(uint16_t rssi)
|
||||
{
|
||||
const uint16_t rssiWithMargin = ScanFastSaturatingAdd(rssi, SCAN_FAST_WEAK_MARGIN);
|
||||
const uint16_t squelchTrigger = ScanFastGetSquelchTrigger();
|
||||
|
||||
if (scanFastNoiseFloor == SCAN_FAST_RSSI_MAX)
|
||||
return gRxVfo->SquelchOpenRSSIThresh > 0 &&
|
||||
rssiWithMargin >= gRxVfo->SquelchOpenRSSIThresh;
|
||||
|
||||
return rssiWithMargin >= ScanFastGetNoiseTrigger() &&
|
||||
rssiWithMargin >= squelchTrigger;
|
||||
}
|
||||
|
||||
static uint16_t ScanFastReadCandidateRssi(void)
|
||||
{
|
||||
uint16_t rssi = ScanFastReadRssi();
|
||||
|
||||
if (ScanFastIsNearCandidate(rssi))
|
||||
{
|
||||
SYSTICK_DelayUs(SCAN_FAST_RECHECK_DELAY_US);
|
||||
|
||||
const uint16_t retryRssi = ScanFastReadRssi();
|
||||
if (retryRssi > rssi)
|
||||
rssi = retryRssi;
|
||||
}
|
||||
|
||||
return rssi;
|
||||
}
|
||||
|
||||
static bool ScanFastIsCandidate(uint16_t rssi)
|
||||
{
|
||||
const uint16_t squelchTrigger = ScanFastGetSquelchTrigger();
|
||||
|
||||
if (scanFastNoiseFloor == SCAN_FAST_RSSI_MAX)
|
||||
{
|
||||
scanFastNoiseFloor = rssi;
|
||||
return gRxVfo->SquelchOpenRSSIThresh > 0 &&
|
||||
rssi >= gRxVfo->SquelchOpenRSSIThresh;
|
||||
}
|
||||
|
||||
const uint16_t noiseTrigger = ScanFastGetNoiseTrigger();
|
||||
const uint16_t rssiWithMargin = ScanFastSaturatingAdd(rssi, SCAN_FAST_WEAK_MARGIN);
|
||||
|
||||
if ((rssi >= noiseTrigger && rssi >= squelchTrigger) ||
|
||||
(rssiWithMargin >= noiseTrigger && rssiWithMargin >= squelchTrigger))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (rssi < scanFastNoiseFloor)
|
||||
scanFastNoiseFloor = rssi;
|
||||
else
|
||||
scanFastNoiseFloor = (uint16_t)((7u * scanFastNoiseFloor + rssi + 4u) >> 3);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ScanFastApplyChannelShape(ModulationMode_t modulation)
|
||||
{
|
||||
const bool modulationChanged = gRxVfo->Modulation != modulation;
|
||||
const bool bandwidthChanged = gRxVfo->CHANNEL_BANDWIDTH != BANDWIDTH_WIDE;
|
||||
|
||||
if (!modulationChanged && !bandwidthChanged)
|
||||
return;
|
||||
|
||||
gRxVfo->Modulation = modulation;
|
||||
gRxVfo->CHANNEL_BANDWIDTH = BANDWIDTH_WIDE;
|
||||
|
||||
if (modulationChanged)
|
||||
RADIO_SetModulation(modulation);
|
||||
|
||||
if (modulation == MODULATION_AM)
|
||||
{
|
||||
BK4819_SetFilterBandwidth(RADIO_GetAMFilterBandwidth(gRxVfo), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef ENABLE_AM_FIX
|
||||
BK4819_SetFilterBandwidth(BK4819_FILTER_BW_WIDE, true);
|
||||
#else
|
||||
BK4819_SetFilterBandwidth(BK4819_FILTER_BW_WIDE, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (modulationChanged)
|
||||
{
|
||||
// AM and FM use different demod/AGC profiles, so their RSSI
|
||||
// baselines are not directly comparable. Relearn the floor after
|
||||
// crossing that boundary instead of treating the next FM block as
|
||||
// a wall of candidates.
|
||||
ScanFastResetNoiseFloor();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN_SCAN_FASTER) && defined(ENABLE_SCAN_RANGES)
|
||||
static void ScanRangeFastRefineCandidate(uint16_t firstRssi)
|
||||
{
|
||||
const uint16_t step = gRxVfo->StepFrequency;
|
||||
if (step == 0 || step >= SCAN_FAST_FINE_STEP_LIMIT)
|
||||
return;
|
||||
|
||||
uint16_t maxSteps = SCAN_FAST_FINE_REFINE_SPAN / step;
|
||||
if (maxSteps == 0)
|
||||
maxSteps = 1;
|
||||
if (maxSteps > SCAN_FAST_FINE_REFINE_MAX)
|
||||
maxSteps = SCAN_FAST_FINE_REFINE_MAX;
|
||||
|
||||
uint16_t bestRssi = firstRssi;
|
||||
uint32_t bestFrequency = gRxVfo->freq_config_RX.Frequency;
|
||||
uint8_t fallingSteps = 0;
|
||||
|
||||
for (uint16_t i = 0; i < maxSteps; ++i)
|
||||
{
|
||||
const uint32_t prevRxFrequency = gRxVfo->pRX->Frequency;
|
||||
|
||||
gRxVfo->freq_config_RX.Frequency = ScanRangeNextFrequency();
|
||||
RADIO_ApplyOffset(gRxVfo);
|
||||
|
||||
const uint32_t freq = gRxVfo->pRX->Frequency;
|
||||
if ((gScanStateDir > 0 && freq < prevRxFrequency) ||
|
||||
(gScanStateDir < 0 && freq > prevRxFrequency))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ScanFastTune(freq);
|
||||
|
||||
const uint16_t rssi = ScanFastReadRssi();
|
||||
if (rssi > bestRssi)
|
||||
{
|
||||
bestRssi = rssi;
|
||||
bestFrequency = gRxVfo->freq_config_RX.Frequency;
|
||||
fallingSteps = 0;
|
||||
}
|
||||
else if (bestRssi > rssi && bestRssi - rssi >= SCAN_FAST_FINE_RSSI_DROP)
|
||||
{
|
||||
if (++fallingSteps >= 3)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gRxVfo->freq_config_RX.Frequency = bestFrequency;
|
||||
RADIO_ApplyOffset(gRxVfo);
|
||||
ScanFastTune(gRxVfo->pRX->Frequency);
|
||||
}
|
||||
|
||||
static scan_fast_result_t ScanRangeFastPrecheck(void)
|
||||
{
|
||||
if (!gScanRangeStart)
|
||||
return SCAN_FAST_DISABLED;
|
||||
|
||||
if (gRxVfo->SquelchOpenRSSIThresh == 0)
|
||||
return SCAN_FAST_DISABLED;
|
||||
|
||||
// Mute AF DAC during the precheck sweep: avoids audio glitches and
|
||||
// saves a few uA on each silent step. The bit is restored on the real
|
||||
// tune by RADIO_SetupRegisters() in NextFreqChannel().
|
||||
scanFastReg30 = BK4819_ReadRegister(BK4819_REG_30) & ~BK4819_REG_30_MASK_ENABLE_AF_DAC;
|
||||
|
||||
for (uint8_t i = 0; i < SCAN_FAST_PRECHECK_STEPS; ++i)
|
||||
{
|
||||
gRxVfo->freq_config_RX.Frequency = ScanRangeNextFrequency();
|
||||
RADIO_ApplyOffset(gRxVfo);
|
||||
|
||||
const uint32_t freq = gRxVfo->pRX->Frequency;
|
||||
|
||||
// Detect wraparound: scanning forward but the new freq is lower
|
||||
// than the previous one (or scanning backward but it's higher)
|
||||
// means the range has wrapped from stop back to start. Reset the
|
||||
// noise floor so the new pass re-warms up from current conditions.
|
||||
if (scanFastPrevFrequency != 0 &&
|
||||
((gScanStateDir > 0 && freq < scanFastPrevFrequency) ||
|
||||
(gScanStateDir < 0 && freq > scanFastPrevFrequency)))
|
||||
{
|
||||
ScanFastResetState();
|
||||
}
|
||||
|
||||
ScanFastTune(freq);
|
||||
|
||||
const uint16_t rssi = ScanFastReadCandidateRssi();
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_RSSI
|
||||
ScanRssiSparklinePush(rssi);
|
||||
#endif
|
||||
if (ScanFastIsCandidate(rssi))
|
||||
{
|
||||
ScanRangeFastRefineCandidate(rssi);
|
||||
return SCAN_FAST_CANDIDATE;
|
||||
}
|
||||
}
|
||||
|
||||
return SCAN_FAST_QUIET_BATCH;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
static bool MemChannelFastPrecheck(uint16_t channel)
|
||||
{
|
||||
uint32_t frequency;
|
||||
ModulationMode_t modulation;
|
||||
|
||||
if (gRxVfo->SquelchOpenRSSIThresh == 0)
|
||||
{
|
||||
scanFastLastFullTuneCandidate = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!ScanFastUpdateDisplayVfo(channel, &frequency, &modulation))
|
||||
{
|
||||
scanFastLastFullTuneCandidate = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
ScanFastApplyChannelShape(modulation);
|
||||
|
||||
// Mute AF DAC for the same reason as in ScanRangeFastPrecheck().
|
||||
scanFastReg30 = BK4819_ReadRegister(BK4819_REG_30) & ~BK4819_REG_30_MASK_ENABLE_AF_DAC;
|
||||
ScanFastTune(frequency);
|
||||
|
||||
const uint16_t rssi = ScanFastReadCandidateRssi();
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_RSSI
|
||||
ScanRssiSparklinePush(rssi);
|
||||
#endif
|
||||
|
||||
if (ScanFastIsCandidate(rssi))
|
||||
{
|
||||
scanFastLastFullTuneCandidate = true;
|
||||
return true; // signal detected: let the full tune path follow
|
||||
}
|
||||
|
||||
// No signal here: still mirror the probed frequency in the VFO so the
|
||||
// status line (channel name + frequency) keeps in sync as we skip.
|
||||
// RADIO_ConfigureChannel() will overwrite these values cleanly when a
|
||||
// candidate is eventually retained.
|
||||
scanFastLastFullTuneCandidate = false;
|
||||
gRxVfo->freq_config_RX.Frequency = frequency;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void AdvanceMemScanList(const bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
if (++currentScanList >= SCAN_NEXT_NUM)
|
||||
currentScanList = SCAN_NEXT_CHAN_SCANLIST1;
|
||||
}
|
||||
|
||||
static void SetMemScanProgressChannel(uint16_t channel)
|
||||
{
|
||||
gEeprom.MrChannel[ gEeprom.RX_VFO] = channel;
|
||||
gEeprom.ScreenChannel[gEeprom.RX_VFO] = channel;
|
||||
gRxVfo->CHANNEL_SAVE = channel;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN_RESUME_STATE) || defined(ENABLE_SCAN_RANGES)
|
||||
void CHFRSCANNER_ScanRange(void) {
|
||||
if (gScanRangeStart) {
|
||||
gScanRangeStart = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
gScanRangeStart = gTxVfo->pRX->Frequency;
|
||||
gScanRangeStop = gEeprom.VfoInfo[!gEeprom.TX_VFO].freq_config_RX.Frequency;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
ScanFastResetState();
|
||||
#endif
|
||||
if(gScanRangeStart > gScanRangeStop)
|
||||
SWAP(gScanRangeStart, gScanRangeStop);
|
||||
|
||||
ScanRangeSkipSync();
|
||||
}
|
||||
#endif
|
||||
|
||||
void CHFRSCANNER_Start(const bool storeBackupSettings, const int8_t scan_direction)
|
||||
{
|
||||
@@ -46,16 +758,24 @@ void CHFRSCANNER_Start(const bool storeBackupSettings, const int8_t scan_directi
|
||||
}
|
||||
|
||||
RADIO_SelectVfos();
|
||||
CHFRSCANNER_AbortActiveReception();
|
||||
|
||||
gNextMrChannel = gRxVfo->CHANNEL_SAVE;
|
||||
currentScanList = SCAN_NEXT_CHAN_SCANLIST1;
|
||||
gScanStateDir = scan_direction;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_RSSI
|
||||
ScanRssiSparklineReset();
|
||||
#endif
|
||||
ScanFastResetState();
|
||||
#endif
|
||||
|
||||
if (IS_MR_CHANNEL(gNextMrChannel))
|
||||
{
|
||||
|
||||
if(!RADIO_CheckValidList(gEeprom.SCAN_LIST_DEFAULT)) {
|
||||
RADIO_NextValidList(1);
|
||||
UI_MAIN_NotifyScanProgressDataChanged();
|
||||
}
|
||||
|
||||
// channel mode
|
||||
@@ -84,6 +804,16 @@ void CHFRSCANNER_Start(const bool storeBackupSettings, const int8_t scan_directi
|
||||
gScanPauseMode = false;
|
||||
}
|
||||
|
||||
void CHFRSCANNER_ManualResume(const int8_t scan_direction)
|
||||
{
|
||||
CHFRSCANNER_Start(false, scan_direction);
|
||||
|
||||
gScanPauseDelayIn_10ms = (gRxVfo->SquelchOpenRSSIThresh == 0)
|
||||
? scan_pause_delay_in_3_10ms
|
||||
: 1;
|
||||
gScheduleScanListen = false;
|
||||
}
|
||||
|
||||
/*
|
||||
void CHFRSCANNER_ContinueScanning(void)
|
||||
{
|
||||
@@ -110,6 +840,18 @@ void CHFRSCANNER_ContinueScanning(void)
|
||||
|
||||
void CHFRSCANNER_ContinueScanning(void)
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
if (scanFastLastFullTuneCandidate &&
|
||||
gCurrentFunction != FUNCTION_INCOMING &&
|
||||
!g_SquelchLost)
|
||||
{
|
||||
// A rejected full-tune candidate is just a false RSSI hit. Keep the
|
||||
// learned floor; resetting here can make the next channel blind when
|
||||
// it is the real signal, especially while scanning down.
|
||||
scanFastLastFullTuneCandidate = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gCurrentFunction == FUNCTION_INCOMING &&
|
||||
(IS_FREQ_CHANNEL(gNextMrChannel) || gCurrentCodeType == CODE_TYPE_OFF))
|
||||
{
|
||||
@@ -127,6 +869,15 @@ void CHFRSCANNER_ContinueScanning(void)
|
||||
|
||||
void CHFRSCANNER_Found(void)
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
// After a real reception the BK4819 AGC has shifted, biasing the next
|
||||
// few RSSI readings high. Reset the precheck state so it warms up from
|
||||
// the current noise floor instead of carrying stale calibration into
|
||||
// the post-reception scan, which otherwise turns nearly every channel
|
||||
// into a CANDIDATE and erases the speed gain.
|
||||
ScanFastResetState();
|
||||
#endif
|
||||
|
||||
if (gEeprom.SCAN_RESUME_MODE > 80) {
|
||||
if (!gScanPauseMode) {
|
||||
gScanPauseDelayIn_10ms = scan_pause_delay_in_5_10ms * (gEeprom.SCAN_RESUME_MODE - 80) * 5;
|
||||
@@ -199,6 +950,9 @@ void CHFRSCANNER_Stop(void)
|
||||
}
|
||||
|
||||
gScanStateDir = SCAN_OFF;
|
||||
#if defined(ENABLE_FEAT_F4HWN_SCAN_FASTER) && defined(ENABLE_FEAT_F4HWN_SCAN_RSSI)
|
||||
ScanRssiSparklineReset();
|
||||
#endif
|
||||
|
||||
const uint32_t chFr = gScanKeepResult ? lastFoundFrqOrChan : initialFrqOrChan;
|
||||
const bool channelChanged = chFr != initialFrqOrChan;
|
||||
@@ -234,11 +988,46 @@ static void NextFreqChannel(void)
|
||||
{
|
||||
#ifdef ENABLE_SCAN_RANGES
|
||||
if(gScanRangeStart) {
|
||||
gRxVfo->freq_config_RX.Frequency = APP_SetFreqByStepAndLimits(gRxVfo, gScanStateDir, gScanRangeStart, gScanRangeStop);
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
if (ScanFastEnabled())
|
||||
{
|
||||
const scan_fast_result_t fastResult = ScanRangeFastPrecheck();
|
||||
|
||||
if (fastResult == SCAN_FAST_QUIET_BATCH)
|
||||
{
|
||||
scanFastLastFullTuneCandidate = false;
|
||||
gScanPauseDelayIn_10ms = 1;
|
||||
gUpdateDisplay = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (fastResult == SCAN_FAST_DISABLED)
|
||||
{
|
||||
scanFastLastFullTuneCandidate = false;
|
||||
gRxVfo->freq_config_RX.Frequency = ScanRangeNextFrequency();
|
||||
}
|
||||
else
|
||||
{
|
||||
scanFastLastFullTuneCandidate = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
scanFastLastFullTuneCandidate = false;
|
||||
gRxVfo->freq_config_RX.Frequency = ScanRangeNextFrequency();
|
||||
}
|
||||
#else
|
||||
gRxVfo->freq_config_RX.Frequency = ScanRangeNextFrequency();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
scanFastLastFullTuneCandidate = false;
|
||||
#endif
|
||||
gRxVfo->freq_config_RX.Frequency = APP_SetFrequencyByStep(gRxVfo, gScanStateDir);
|
||||
}
|
||||
|
||||
RADIO_ApplyOffset(gRxVfo);
|
||||
RADIO_ConfigureSquelchAndOutputPower(gRxVfo);
|
||||
@@ -339,23 +1128,58 @@ static void NextMemChannel(void)
|
||||
}
|
||||
|
||||
if (!enabled || chan == 0xFFFF)
|
||||
{
|
||||
{
|
||||
const uint16_t searchStart = gNextMrChannel;
|
||||
chan = RADIO_FindNextChannel(gNextMrChannel + gScanStateDir, gScanStateDir, true, gEeprom.SCAN_LIST_DEFAULT);
|
||||
if (chan == 0xFFFF)
|
||||
{ // no valid channel found
|
||||
{ // no valid channel found -> wrapping back to the first channel
|
||||
chan = MR_CHANNEL_FIRST;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
// Wraparound: re-warm the precheck noise floor on the new pass
|
||||
// so it tracks current RF conditions instead of an EMA that
|
||||
// accumulated drift over the previous full sweep.
|
||||
ScanFastResetState();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
else if ((gScanStateDir > 0 && chan < searchStart) ||
|
||||
(gScanStateDir < 0 && chan > searchStart))
|
||||
{
|
||||
// RADIO_FindNextChannel() wraps internally, so 0xFFFF is not
|
||||
// returned on a normal full-sweep wrap. Detect that transition
|
||||
// here and restart the RSSI floor learning for the new pass.
|
||||
ScanFastResetState();
|
||||
}
|
||||
#endif
|
||||
|
||||
gNextMrChannel = chan;
|
||||
|
||||
//sprintf(str, "----> Chan %d\n", chan + 1);
|
||||
//LogUart(str);
|
||||
}
|
||||
|
||||
if (gNextMrChannel != prev_chan)
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
SetMemScanProgressChannel(gNextMrChannel);
|
||||
|
||||
if (ScanFastEnabled() && !MemChannelFastPrecheck(gNextMrChannel))
|
||||
{
|
||||
gScanPauseDelayIn_10ms = 1;
|
||||
gUpdateDisplay = true;
|
||||
AdvanceMemScanList(enabled);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gNextMrChannel != prev_chan
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
|| scanFastLastFullTuneCandidate
|
||||
#endif
|
||||
)
|
||||
{
|
||||
#ifndef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
gEeprom.MrChannel[ gEeprom.RX_VFO] = gNextMrChannel;
|
||||
gEeprom.ScreenChannel[gEeprom.RX_VFO] = gNextMrChannel;
|
||||
#endif
|
||||
|
||||
RADIO_ConfigureChannel(gEeprom.RX_VFO, VFO_CONFIGURE_RELOAD);
|
||||
RADIO_SetupRegisters(true);
|
||||
@@ -369,7 +1193,11 @@ static void NextMemChannel(void)
|
||||
gScanPauseDelayIn_10ms = scan_pause_delay_in_3_10ms;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
AdvanceMemScanList(enabled);
|
||||
#else
|
||||
if (enabled)
|
||||
if (++currentScanList >= SCAN_NEXT_NUM)
|
||||
currentScanList = SCAN_NEXT_CHAN_SCANLIST1; // back round we go
|
||||
#endif
|
||||
}
|
||||
+26
-1
@@ -4,6 +4,8 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "radio.h"
|
||||
|
||||
// scan direction, if not equal SCAN_OFF indicates
|
||||
// that we are in a process of scanning channels/frequencies
|
||||
extern int8_t gScanStateDir;
|
||||
@@ -13,16 +15,39 @@ extern bool gScanPauseMode;
|
||||
#ifdef ENABLE_SCAN_RANGES
|
||||
extern uint32_t gScanRangeStart;
|
||||
extern uint32_t gScanRangeStop;
|
||||
#if defined(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE) && ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE
|
||||
extern DCS_CodeType_t gScanRangeCssType;
|
||||
extern uint8_t gScanRangeCssCode;
|
||||
#endif
|
||||
|
||||
bool CHFRSCANNER_ExcludeCurrentScanRange(void);
|
||||
bool CHFRSCANNER_HasScanRangeExcludedOrdinal(uint32_t first_ordinal, uint32_t last_ordinal);
|
||||
#if defined(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE) && ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE
|
||||
void CHFRSCANNER_UpdateCssDetection(void);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void CHFRSCANNER_Found(void);
|
||||
void CHFRSCANNER_Stop(void);
|
||||
void CHFRSCANNER_Start(const bool storeBackupSettings, const int8_t scan_direction);
|
||||
void CHFRSCANNER_ManualResume(const int8_t scan_direction);
|
||||
void CHFRSCANNER_ContinueScanning(void);
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
const VFO_Info_t *CHFRSCANNER_GetScanDisplayVfo(void);
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_RSSI
|
||||
bool CHFRSCANNER_HasScanRssiSparkline(void);
|
||||
uint8_t CHFRSCANNER_GetScanRssiSparklineLevel(uint8_t index);
|
||||
#define CHFRSCANNER_RSSI_SPARKLINE_WIDTH 24u
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN_RESUME_STATE) || defined(ENABLE_SCAN_RANGES)
|
||||
void CHFRSCANNER_ScanRange(void);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
extern uint32_t lastFoundFrqOrChan;
|
||||
extern uint32_t lastFoundFrqOrChanOld;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "functions.h"
|
||||
#include "misc.h"
|
||||
#include "settings.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
void COMMON_KeypadLockToggle()
|
||||
@@ -29,6 +30,11 @@ void COMMON_SwitchVFOs()
|
||||
#endif
|
||||
gEeprom.TX_VFO ^= 1;
|
||||
|
||||
if (gInputBoxIndex > 0) {
|
||||
gInputBoxIndex = 0;
|
||||
gHasVfoBackup = false;
|
||||
}
|
||||
|
||||
if (gEeprom.CROSS_BAND_RX_TX != CROSS_BAND_OFF)
|
||||
gEeprom.CROSS_BAND_RX_TX = gEeprom.TX_VFO + 1;
|
||||
if (gEeprom.DUAL_WATCH != DUAL_WATCH_OFF)
|
||||
@@ -49,6 +55,11 @@ void COMMON_SwitchVFOMode()
|
||||
if (gEeprom.VFO_OPEN)
|
||||
#endif
|
||||
{
|
||||
if (gInputBoxIndex > 0) {
|
||||
gInputBoxIndex = 0;
|
||||
gHasVfoBackup = false;
|
||||
}
|
||||
|
||||
if (IS_MR_CHANNEL(gTxVfo->CHANNEL_SAVE))
|
||||
{ // swap to frequency mode
|
||||
gEeprom.ScreenChannel[gEeprom.TX_VFO] = gEeprom.FreqChannel[gEeprom.TX_VFO];
|
||||
|
||||
+5
-1
@@ -212,6 +212,10 @@ DTMF_CallMode_t DTMF_CheckGroupCall(const char *pMsg, const unsigned int size)
|
||||
}
|
||||
#endif
|
||||
|
||||
void DTMF_clear_input_box_memory() {
|
||||
memset(gDTMF_RX_live, 0, sizeof(gDTMF_RX_live));
|
||||
}
|
||||
|
||||
void DTMF_clear_input_box(void)
|
||||
{
|
||||
memset(gDTMF_InputBox, 0, sizeof(gDTMF_InputBox));
|
||||
@@ -470,7 +474,7 @@ void DTMF_Reply(void)
|
||||
if (pString == NULL)
|
||||
return;
|
||||
|
||||
Delay = (gEeprom.DTMF_PRELOAD_TIME < 200) ? 200 : gEeprom.DTMF_PRELOAD_TIME;
|
||||
Delay = MAX(gEeprom.DTMF_PRELOAD_TIME, 200);
|
||||
|
||||
if (gEeprom.DTMF_SIDE_TONE)
|
||||
{ // the user will also hear the transmitted tones
|
||||
|
||||
@@ -82,6 +82,7 @@ extern DTMF_ReplyState_t gDTMF_ReplyState;
|
||||
|
||||
bool DTMF_ValidateCodes(char *pCode, const unsigned int size);
|
||||
char DTMF_GetCharacter(const unsigned int code);
|
||||
void DTMF_clear_input_box_memory(void);
|
||||
void DTMF_clear_input_box(void);
|
||||
void DTMF_Append(const char code);
|
||||
void DTMF_Reply(void);
|
||||
|
||||
+19
-13
@@ -51,20 +51,26 @@ static inline void Flashlight_Toggle(){ GPIO_TogglePin(GPIO_PIN_FLASHLIGHT); }
|
||||
|
||||
void ACTION_FlashLight(void)
|
||||
{
|
||||
switch (gFlashLightState) {
|
||||
case FLASHLIGHT_OFF:
|
||||
gFlashLightState++;
|
||||
Flashlight_TurnOn();
|
||||
break;
|
||||
case FLASHLIGHT_ON:
|
||||
case FLASHLIGHT_BLINK:
|
||||
gFlashLightState++;
|
||||
break;
|
||||
case FLASHLIGHT_SOS:
|
||||
default:
|
||||
gFlashLightState = 0;
|
||||
Flashlight_TurnOff();
|
||||
// switch (gFlashLightState) {
|
||||
// case FLASHLIGHT_OFF:
|
||||
// Flashlight_TurnOn();
|
||||
// break;
|
||||
// case FLASHLIGHT_ON:
|
||||
// case FLASHLIGHT_BLINK:
|
||||
// break;
|
||||
// case FLASHLIGHT_SOS:
|
||||
// default:
|
||||
// Flashlight_TurnOff();
|
||||
// }
|
||||
|
||||
if(gFlashLightState == FLASHLIGHT_OFF) {
|
||||
Flashlight_TurnOn();
|
||||
}
|
||||
else if (gFlashLightState == FLASHLIGHT_SOS) {
|
||||
Flashlight_TurnOff();
|
||||
}
|
||||
|
||||
gFlashLightState = (gFlashLightState + 1) % 4;
|
||||
}
|
||||
#else
|
||||
void ACTION_FlashLight(void)
|
||||
|
||||
+78
-36
@@ -40,7 +40,6 @@ volatile int8_t gFM_ScanState;
|
||||
bool gFM_AutoScan;
|
||||
uint8_t gFM_ChannelPosition;
|
||||
bool gFM_FoundFrequency;
|
||||
bool gFM_AutoScan;
|
||||
uint16_t gFM_RestoreCountdown_10ms;
|
||||
|
||||
|
||||
@@ -95,6 +94,11 @@ int FM_ConfigureChannelState(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FM_SetFrequency(void)
|
||||
{
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
|
||||
}
|
||||
|
||||
void FM_TurnOff(void)
|
||||
{
|
||||
gFmRadioMode = false;
|
||||
@@ -129,6 +133,18 @@ void FM_EraseChannels(void)
|
||||
memset(gFM_Channels, 0xFF, sizeof(gFM_Channels));
|
||||
}
|
||||
|
||||
uint16_t FM_WrapFrequency(uint16_t Frequency) {
|
||||
const uint16_t freqLoLimit = BK1080_GetFreqLoLimit(gEeprom.FM_Band);
|
||||
const uint16_t freqHiLimit = BK1080_GetFreqHiLimit(gEeprom.FM_Band);
|
||||
|
||||
if (Frequency < freqLoLimit)
|
||||
return freqHiLimit;
|
||||
else if (Frequency > freqHiLimit)
|
||||
return freqLoLimit;
|
||||
|
||||
return Frequency;
|
||||
}
|
||||
|
||||
void FM_Tune(uint16_t Frequency, int8_t Step, bool bFlag)
|
||||
{
|
||||
AUDIO_AudioPathOff();
|
||||
@@ -145,17 +161,19 @@ void FM_Tune(uint16_t Frequency, int8_t Step, bool bFlag)
|
||||
|
||||
if (!bFlag) {
|
||||
Frequency += Step;
|
||||
if (Frequency < BK1080_GetFreqLoLimit(gEeprom.FM_Band))
|
||||
Frequency = BK1080_GetFreqHiLimit(gEeprom.FM_Band);
|
||||
else if (Frequency > BK1080_GetFreqHiLimit(gEeprom.FM_Band))
|
||||
Frequency = BK1080_GetFreqLoLimit(gEeprom.FM_Band);
|
||||
Frequency = FM_WrapFrequency(Frequency);
|
||||
|
||||
gEeprom.FM_FrequencyPlaying = Frequency;
|
||||
}
|
||||
|
||||
gFM_ScanState = Step;
|
||||
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
|
||||
FM_SetFrequency();
|
||||
}
|
||||
|
||||
void FM_AudioPathOn(void) {
|
||||
AUDIO_AudioPathOn();
|
||||
gEnableSpeaker = true;
|
||||
}
|
||||
|
||||
void FM_PlayAndUpdate(void)
|
||||
@@ -168,16 +186,15 @@ void FM_PlayAndUpdate(void)
|
||||
}
|
||||
|
||||
FM_ConfigureChannelState();
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
|
||||
FM_SetFrequency();
|
||||
SETTINGS_SaveFM();
|
||||
|
||||
gFmPlayCountdown_10ms = 0;
|
||||
gScheduleFM = false;
|
||||
gAskToSave = false;
|
||||
|
||||
AUDIO_AudioPathOn();
|
||||
|
||||
gEnableSpeaker = true;
|
||||
BACKLIGHT_TurnOn();
|
||||
FM_AudioPathOn();
|
||||
}
|
||||
|
||||
int FM_CheckFrequencyLock(uint16_t Frequency, uint16_t LowerLimit)
|
||||
@@ -248,6 +265,7 @@ static void Key_DIGITS(KEY_Code_t Key, uint8_t state)
|
||||
}
|
||||
|
||||
INPUTBOX_Append(Key);
|
||||
gKeyInputCountdown = key_input_timeout_500ms;
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
|
||||
@@ -263,6 +281,8 @@ static void Key_DIGITS(KEY_Code_t Key, uint8_t state)
|
||||
uint32_t Frequency;
|
||||
|
||||
gInputBoxIndex = 0;
|
||||
gKeyInputCountdown = 1;
|
||||
|
||||
Frequency = StrToUL(INPUTBOX_GetAscii());
|
||||
|
||||
if (Frequency < BK1080_GetFreqLoLimit(gEeprom.FM_Band) || BK1080_GetFreqHiLimit(gEeprom.FM_Band) < Frequency) {
|
||||
@@ -276,7 +296,7 @@ static void Key_DIGITS(KEY_Code_t Key, uint8_t state)
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
#endif
|
||||
gEeprom.FM_FrequencyPlaying = gEeprom.FM_SelectedFrequency;
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
|
||||
FM_SetFrequency();
|
||||
gRequestSaveFM = true;
|
||||
return;
|
||||
}
|
||||
@@ -285,6 +305,8 @@ static void Key_DIGITS(KEY_Code_t Key, uint8_t state)
|
||||
uint8_t Channel;
|
||||
|
||||
gInputBoxIndex = 0;
|
||||
gKeyInputCountdown = 1;
|
||||
|
||||
Channel = ((gInputBox[0] * 10) + gInputBox[1]) - 1;
|
||||
|
||||
if (State == STATE_MR_MODE) {
|
||||
@@ -294,7 +316,7 @@ static void Key_DIGITS(KEY_Code_t Key, uint8_t state)
|
||||
#endif
|
||||
gEeprom.FM_SelectedChannel = Channel;
|
||||
gEeprom.FM_FrequencyPlaying = gFM_Channels[Channel];
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
|
||||
FM_SetFrequency();
|
||||
gRequestSaveFM = true;
|
||||
return;
|
||||
}
|
||||
@@ -327,8 +349,7 @@ static void Key_FUNC(KEY_Code_t Key, uint8_t state)
|
||||
bool autoScan = gWasFKeyPressed || (state == BUTTON_EVENT_HELD);
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
gWasFKeyPressed = false;
|
||||
gUpdateStatus = true;
|
||||
HideFKeyIcon();
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
|
||||
switch (Key) {
|
||||
@@ -350,7 +371,7 @@ static void Key_FUNC(KEY_Code_t Key, uint8_t state)
|
||||
gEeprom.FM_IsMrMode = !gEeprom.FM_IsMrMode;
|
||||
|
||||
if (!FM_ConfigureChannelState()) {
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
|
||||
FM_SetFrequency();
|
||||
gRequestSaveFM = true;
|
||||
}
|
||||
else
|
||||
@@ -378,8 +399,14 @@ static void Key_FUNC(KEY_Code_t Key, uint8_t state)
|
||||
|
||||
static void Key_EXIT(uint8_t state)
|
||||
{
|
||||
if (state != BUTTON_EVENT_SHORT)
|
||||
return;
|
||||
if (gInputBoxIndex) {
|
||||
if (state != BUTTON_EVENT_SHORT)
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (state != BUTTON_EVENT_PRESSED)
|
||||
return;
|
||||
}
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
@@ -395,6 +422,7 @@ static void Key_EXIT(uint8_t state)
|
||||
}
|
||||
else {
|
||||
gInputBox[--gInputBoxIndex] = 10;
|
||||
gKeyInputCountdown = key_input_timeout_500ms;
|
||||
|
||||
if (gInputBoxIndex) {
|
||||
if (gInputBoxIndex != 1) {
|
||||
@@ -426,14 +454,25 @@ static void Key_EXIT(uint8_t state)
|
||||
|
||||
static void Key_MENU(uint8_t state)
|
||||
{
|
||||
if (state != BUTTON_EVENT_SHORT)
|
||||
if (state == BUTTON_EVENT_HELD) {
|
||||
ACTION_Handle(KEY_MENU, true, true);
|
||||
return;
|
||||
|
||||
}
|
||||
else if (state != BUTTON_EVENT_SHORT) {
|
||||
return;
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
HideFKeyIcon();
|
||||
|
||||
if (gFM_ScanState == FM_SCAN_OFF) {
|
||||
if (gInputBoxIndex) {
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gEeprom.FM_IsMrMode) {
|
||||
if (gAskToSave) {
|
||||
gFM_Channels[gFM_ChannelPosition] = gEeprom.FM_FrequencyPlaying;
|
||||
@@ -446,7 +485,7 @@ static void Key_MENU(uint8_t state)
|
||||
gFM_Channels[gEeprom.FM_SelectedChannel] = 0xFFFF;
|
||||
|
||||
FM_ConfigureChannelState();
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
|
||||
FM_SetFrequency();
|
||||
|
||||
gRequestSaveFM = true;
|
||||
}
|
||||
@@ -470,6 +509,8 @@ static void Key_MENU(uint8_t state)
|
||||
|
||||
static void Key_UP_DOWN(uint8_t state, int8_t Step)
|
||||
{
|
||||
HideFKeyIcon();
|
||||
|
||||
if (state == BUTTON_EVENT_PRESSED) {
|
||||
if (gInputBoxIndex) {
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
@@ -513,10 +554,7 @@ static void Key_UP_DOWN(uint8_t state, int8_t Step)
|
||||
else {
|
||||
uint16_t Frequency = gEeprom.FM_SelectedFrequency + Step;
|
||||
|
||||
if (Frequency < BK1080_GetFreqLoLimit(gEeprom.FM_Band))
|
||||
Frequency = BK1080_GetFreqHiLimit(gEeprom.FM_Band);
|
||||
else if (Frequency > BK1080_GetFreqHiLimit(gEeprom.FM_Band))
|
||||
Frequency = BK1080_GetFreqLoLimit(gEeprom.FM_Band);
|
||||
Frequency = FM_WrapFrequency(Frequency);
|
||||
|
||||
gEeprom.FM_FrequencyPlaying = Frequency;
|
||||
gEeprom.FM_SelectedFrequency = gEeprom.FM_FrequencyPlaying;
|
||||
@@ -525,7 +563,7 @@ static void Key_UP_DOWN(uint8_t state, int8_t Step)
|
||||
gRequestSaveFM = true;
|
||||
|
||||
Bail:
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
|
||||
FM_SetFrequency();
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
}
|
||||
@@ -557,9 +595,14 @@ void FM_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
case KEY_PTT:
|
||||
GENERIC_Key_PTT(bKeyPressed);
|
||||
break;
|
||||
default:
|
||||
if (!bKeyHeld && bKeyPressed)
|
||||
case KEY_SIDE1:
|
||||
case KEY_SIDE2:
|
||||
if (state != BUTTON_EVENT_PRESSED) {
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
HideFKeyIcon();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -574,20 +617,19 @@ void FM_Play(void)
|
||||
if (!gEeprom.FM_IsMrMode)
|
||||
gEeprom.FM_SelectedFrequency = gEeprom.FM_FrequencyPlaying;
|
||||
|
||||
AUDIO_AudioPathOn();
|
||||
gEnableSpeaker = true;
|
||||
BACKLIGHT_TurnOn();
|
||||
FM_AudioPathOn();
|
||||
|
||||
GUI_SelectNextDisplay(DISPLAY_FM);
|
||||
return;
|
||||
goto Display;
|
||||
}
|
||||
|
||||
if (gFM_ChannelPosition < FM_CHANNELS_MAX)
|
||||
gFM_Channels[gFM_ChannelPosition++] = gEeprom.FM_FrequencyPlaying;
|
||||
|
||||
|
||||
if (gFM_ChannelPosition >= FM_CHANNELS_MAX) {
|
||||
FM_PlayAndUpdate();
|
||||
GUI_SelectNextDisplay(DISPLAY_FM);
|
||||
return;
|
||||
|
||||
goto Display;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -596,6 +638,7 @@ void FM_Play(void)
|
||||
else
|
||||
FM_Tune(gEeprom.FM_FrequencyPlaying, gFM_ScanState, false);
|
||||
|
||||
Display:
|
||||
GUI_SelectNextDisplay(DISPLAY_FM);
|
||||
}
|
||||
|
||||
@@ -610,9 +653,8 @@ void FM_Start(void)
|
||||
// Disable UHF LNA, enable VHF LNA
|
||||
BK4819_PickRXFilterPathBasedOnFrequency(10320000); // 103.2 MHz < 280 MHz
|
||||
|
||||
AUDIO_AudioPathOn();
|
||||
FM_AudioPathOn();
|
||||
|
||||
gEnableSpeaker = true;
|
||||
gUpdateStatus = true;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
|
||||
+2
-14
@@ -91,7 +91,7 @@ void GENERIC_Key_F(bool bKeyPressed, bool bKeyHeld)
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
gBeepToPlay = BEEP_440HZ_500MS;
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
gPttWasReleased = true;
|
||||
}
|
||||
}
|
||||
@@ -103,20 +103,8 @@ void GENERIC_Key_PTT(bool bKeyPressed)
|
||||
if (!bKeyPressed || SerialConfigInProgress())
|
||||
{ // PTT released
|
||||
if (gCurrentFunction == FUNCTION_TRANSMIT) {
|
||||
// we are transmitting .. stop
|
||||
if (gFlagEndTransmission) {
|
||||
FUNCTION_Select(FUNCTION_FOREGROUND);
|
||||
}
|
||||
else {
|
||||
APP_EndTransmission();
|
||||
APP_HandleEndTransmission();
|
||||
|
||||
if (gEeprom.REPEATER_TAIL_TONE_ELIMINATION == 0)
|
||||
FUNCTION_Select(FUNCTION_FOREGROUND);
|
||||
else
|
||||
gRTTECountdown_10ms = gEeprom.REPEATER_TAIL_TONE_ELIMINATION * 10;
|
||||
}
|
||||
|
||||
gFlagEndTransmission = false;
|
||||
#ifdef ENABLE_VOX
|
||||
gVOX_NoiseDetected = false;
|
||||
#endif
|
||||
|
||||
+137
-157
@@ -44,6 +44,7 @@
|
||||
#include "radio.h"
|
||||
#include "settings.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/main.h"
|
||||
#include "ui/ui.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -51,7 +52,26 @@
|
||||
static VFO_Info_t gVfoBackup;
|
||||
static uint16_t gScreenChannelBackup = 0;
|
||||
static uint16_t gFreqChannelBackup = 0;
|
||||
static bool gHasVfoBackup = false;
|
||||
|
||||
static void VFO_RestoreBackup(void) {
|
||||
if (gHasVfoBackup) {
|
||||
const uint8_t Vfo = gEeprom.TX_VFO;
|
||||
|
||||
// Restore indices
|
||||
gEeprom.ScreenChannel[Vfo] = gScreenChannelBackup;
|
||||
gEeprom.FreqChannel[Vfo] = gFreqChannelBackup;
|
||||
|
||||
// Restore full VFO
|
||||
memcpy(gTxVfo, &gVfoBackup, sizeof(VFO_Info_t));
|
||||
|
||||
// Save and apply
|
||||
SETTINGS_SaveVfoIndices();
|
||||
RADIO_ConfigureSquelchAndOutputPower(gTxVfo);
|
||||
RADIO_SetupRegisters(true);
|
||||
|
||||
gHasVfoBackup = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void toggle_chan_scanlist(void)
|
||||
{ // toggle the selected channels scanlist setting
|
||||
@@ -61,10 +81,7 @@ static void toggle_chan_scanlist(void)
|
||||
|
||||
if(!IS_MR_CHANNEL(gTxVfo->CHANNEL_SAVE)) {
|
||||
#ifdef ENABLE_SCAN_RANGES
|
||||
gScanRangeStart = gScanRangeStart ? 0 : gTxVfo->pRX->Frequency;
|
||||
gScanRangeStop = gEeprom.VfoInfo[!gEeprom.TX_VFO].freq_config_RX.Frequency;
|
||||
if(gScanRangeStart > gScanRangeStop)
|
||||
SWAP(gScanRangeStart, gScanRangeStop);
|
||||
CHFRSCANNER_ScanRange();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
@@ -99,16 +116,14 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
|
||||
{
|
||||
uint8_t Vfo = gEeprom.TX_VFO;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
if(gEeprom.MENU_LOCK == true) {
|
||||
if(Key == 2) { // Enable A/B only
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
COMMON_SwitchVFOs();
|
||||
if (beep)
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
}
|
||||
if (beep)
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
return; // prevent F function if MENU LOCK is true
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
if(gEeprom.MENU_LOCK == true && Key != 2) {
|
||||
HideFKeyIcon();
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -126,9 +141,7 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
|
||||
|
||||
case KEY_1:
|
||||
if (!IS_FREQ_CHANNEL(gTxVfo->CHANNEL_SAVE)) {
|
||||
gWasFKeyPressed = false;
|
||||
gUpdateStatus = true;
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
HideFKeyIcon();
|
||||
|
||||
#ifdef ENABLE_COPY_CHAN_TO_VFO
|
||||
if (!gEeprom.VFO_OPEN || gCssBackgroundScan) {
|
||||
@@ -192,9 +205,6 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
|
||||
if (beep)
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
break;
|
||||
|
||||
case KEY_2:
|
||||
@@ -202,8 +212,6 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
#endif
|
||||
COMMON_SwitchVFOs();
|
||||
if (beep)
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
break;
|
||||
|
||||
case KEY_3:
|
||||
@@ -211,19 +219,14 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
#endif
|
||||
COMMON_SwitchVFOMode();
|
||||
if (beep)
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
break;
|
||||
|
||||
case KEY_4:
|
||||
gWasFKeyPressed = false;
|
||||
HideFKeyIcon();
|
||||
|
||||
gBackup_CROSS_BAND_RX_TX = gEeprom.CROSS_BAND_RX_TX;
|
||||
gEeprom.CROSS_BAND_RX_TX = CROSS_BAND_OFF;
|
||||
gUpdateStatus = true;
|
||||
if (beep)
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
gEeprom.CROSS_BAND_RX_TX = CROSS_BAND_OFF;
|
||||
|
||||
SCANNER_Start(false);
|
||||
gRequestDisplayScreen = DISPLAY_SCANNER;
|
||||
@@ -259,34 +262,51 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
|
||||
break;
|
||||
|
||||
case KEY_7:
|
||||
#ifdef ENABLE_VOX
|
||||
ACTION_Vox();
|
||||
//#else
|
||||
// toggle_chan_scanlist();
|
||||
#ifdef ENABLE_FEAT_F4HWN_GAME
|
||||
if (!beep) {
|
||||
APP_RunBreakout();
|
||||
} else {
|
||||
#endif
|
||||
#ifdef ENABLE_VOX
|
||||
ACTION_Vox();
|
||||
//#else
|
||||
// toggle_chan_scanlist();
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_GAME
|
||||
}
|
||||
#endif
|
||||
|
||||
break;
|
||||
|
||||
case KEY_8:
|
||||
gTxVfo->FrequencyReverse = gTxVfo->FrequencyReverse == false;
|
||||
gRequestSaveChannel = 1;
|
||||
if (!beep) {
|
||||
ACTION_BackLightOnDemand();
|
||||
}
|
||||
else {
|
||||
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;
|
||||
if (!beep) {
|
||||
ACTION_BackLight();
|
||||
}
|
||||
else {
|
||||
if (RADIO_CheckValidChannel(gEeprom.CHAN_1_CALL, false, 0)) {
|
||||
gEeprom.MrChannel[Vfo] = gEeprom.CHAN_1_CALL;
|
||||
gEeprom.ScreenChannel[Vfo] = gEeprom.CHAN_1_CALL;
|
||||
#ifdef ENABLE_VOICE
|
||||
AUDIO_SetVoiceID(0, VOICE_ID_CHANNEL_MODE);
|
||||
AUDIO_SetDigitVoice(1, gEeprom.CHAN_1_CALL + 1);
|
||||
gAnotherVoiceID = (VOICE_ID_t)0xFE;
|
||||
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;
|
||||
gRequestSaveVFO = true;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
}
|
||||
}
|
||||
|
||||
if (beep)
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN // Set Squelch F + UP or Down and Step F + SIDE1 or F + SIDE2
|
||||
@@ -297,19 +317,24 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
|
||||
|
||||
if (gScanStateDir != SCAN_OFF) {
|
||||
RADIO_NextValidList(isKeyUp ? 1 : -1);
|
||||
UI_MAIN_NotifyScanProgressDataChanged();
|
||||
} else {
|
||||
// Adjust squelch: UP increments, DOWN decrements
|
||||
if (gSquelchLevelOriginal == 10)
|
||||
gSquelchLevelOriginal = gEeprom.SQUELCH_LEVEL;
|
||||
|
||||
if (isKeyUp) {
|
||||
gEeprom.SQUELCH_LEVEL = (gEeprom.SQUELCH_LEVEL < 9) ? gEeprom.SQUELCH_LEVEL + 1 : 9;
|
||||
if (gEeprom.SQUELCH_LEVEL < 9) gEeprom.SQUELCH_LEVEL++;
|
||||
} else {
|
||||
gEeprom.SQUELCH_LEVEL = (gEeprom.SQUELCH_LEVEL > 0) ? gEeprom.SQUELCH_LEVEL - 1 : 0;
|
||||
if (gEeprom.SQUELCH_LEVEL > 0) gEeprom.SQUELCH_LEVEL--;
|
||||
}
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
}
|
||||
|
||||
gWasFKeyPressed = false;
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case KEY_SIDE1:
|
||||
case KEY_SIDE2:
|
||||
{
|
||||
@@ -327,13 +352,13 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
|
||||
}
|
||||
|
||||
gWasFKeyPressed = false;
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
gUpdateStatus = true;
|
||||
gWasFKeyPressed = false;
|
||||
HideFKeyIcon();
|
||||
|
||||
if (beep)
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
@@ -423,8 +448,7 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
}
|
||||
|
||||
gWasFKeyPressed = false;
|
||||
gUpdateStatus = true;
|
||||
HideFKeyIcon();
|
||||
|
||||
processFKeyFunction(Key, true);
|
||||
}
|
||||
@@ -476,6 +500,7 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
if (value == 0)
|
||||
{
|
||||
gEeprom.SCAN_LIST_DEFAULT = MR_CHANNELS_LIST + 1;
|
||||
UI_MAIN_NotifyScanProgressDataChanged();
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
SETTINGS_WriteCurrentState();
|
||||
#endif
|
||||
@@ -494,6 +519,7 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
RADIO_NextValidList(1);
|
||||
}
|
||||
UI_MAIN_NotifyScanProgressDataChanged();
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
SETTINGS_WriteCurrentState();
|
||||
@@ -550,7 +576,13 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
return;
|
||||
}
|
||||
|
||||
gKeyInputCountdown = (gInputBoxIndex == totalDigits) ? (key_input_timeout_500ms / 16) : (key_input_timeout_500ms / 3);
|
||||
gKeyInputCountdown = key_input_timeout_500ms / (gInputBoxIndex >= totalDigits ? 16 : 3);
|
||||
|
||||
if (gInputBoxIndex > totalDigits) {
|
||||
gInputBoxIndex = totalDigits;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const char *inputStr = INPUTBOX_GetAscii();
|
||||
uint8_t inputLength = gInputBoxIndex;
|
||||
@@ -558,11 +590,8 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
// convert to int
|
||||
uint32_t inputFreq = StrToUL(inputStr);
|
||||
|
||||
// how many zero to add
|
||||
uint8_t zerosToAdd = totalDigits - inputLength;
|
||||
|
||||
// add missing zero
|
||||
for (uint8_t i = 0; i < zerosToAdd; i++) {
|
||||
for (uint8_t i = 0; i < totalDigits - inputLength; i++) {
|
||||
inputFreq *= 10;
|
||||
}
|
||||
|
||||
@@ -619,6 +648,7 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
}
|
||||
|
||||
gInputBoxIndex = 0;
|
||||
gHasVfoBackup = false;
|
||||
|
||||
uint8_t Channel = (gInputBox[0] * 10) + gInputBox[1];
|
||||
if (Channel >= 1 && Channel <= ARRAY_SIZE(NoaaFrequencyTable)) {
|
||||
@@ -640,31 +670,7 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
return;
|
||||
}
|
||||
|
||||
gWasFKeyPressed = false;
|
||||
gUpdateStatus = true;
|
||||
|
||||
if(Key == 8)
|
||||
{
|
||||
ACTION_BackLightOnDemand();
|
||||
return;
|
||||
}
|
||||
else if(Key == 9)
|
||||
{
|
||||
ACTION_BackLight();
|
||||
return;
|
||||
}
|
||||
#ifdef ENABLE_FEAT_F4HWN_GAME
|
||||
else if(Key == 7)
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
if(gEeprom.MENU_LOCK == true) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
APP_RunBreakout();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
HideFKeyIcon();
|
||||
|
||||
processFKeyFunction(Key, false);
|
||||
}
|
||||
@@ -678,35 +684,10 @@ static void MAIN_Key_EXIT(bool bKeyPressed, bool bKeyHeld)
|
||||
|
||||
if (bKeyHeld) { // exit key held down
|
||||
if (bKeyPressed) {
|
||||
if (gInputBoxIndex > 0 || gDTMF_InputBox_Index > 0 || gDTMF_InputMode)
|
||||
{ // cancel key input mode (channel/frequency entry)
|
||||
// Restore full VFO state on long press EXIT
|
||||
VFO_RestoreBackup();
|
||||
|
||||
// Restore full VFO state on long press EXIT
|
||||
if (gHasVfoBackup) {
|
||||
const uint8_t Vfo = gEeprom.TX_VFO;
|
||||
|
||||
// Restore indices
|
||||
gEeprom.ScreenChannel[Vfo] = gScreenChannelBackup;
|
||||
gEeprom.FreqChannel[Vfo] = gFreqChannelBackup;
|
||||
|
||||
// Restore full VFO
|
||||
memcpy(gTxVfo, &gVfoBackup, sizeof(VFO_Info_t));
|
||||
|
||||
// Save and apply
|
||||
SETTINGS_SaveVfoIndices();
|
||||
RADIO_ConfigureSquelchAndOutputPower(gTxVfo);
|
||||
RADIO_SetupRegisters(true);
|
||||
|
||||
gHasVfoBackup = false;
|
||||
}
|
||||
|
||||
gDTMF_InputMode = false;
|
||||
gDTMF_InputBox_Index = 0;
|
||||
memset(gDTMF_String, 0, sizeof(gDTMF_String));
|
||||
gInputBoxIndex = 0;
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
}
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -731,23 +712,8 @@ static void MAIN_Key_EXIT(bool bKeyPressed, bool bKeyHeld)
|
||||
gInputBox[--gInputBoxIndex] = 10;
|
||||
|
||||
// Restore full VFO state when back to 0
|
||||
if (gInputBoxIndex == 0 && gHasVfoBackup) {
|
||||
const uint8_t Vfo = gEeprom.TX_VFO;
|
||||
|
||||
// Restore indices
|
||||
gEeprom.ScreenChannel[Vfo] = gScreenChannelBackup;
|
||||
gEeprom.FreqChannel[Vfo] = gFreqChannelBackup;
|
||||
|
||||
// Restore full VFO
|
||||
memcpy(gTxVfo, &gVfoBackup, sizeof(VFO_Info_t));
|
||||
|
||||
// Save and apply
|
||||
SETTINGS_SaveVfoIndices();
|
||||
RADIO_ConfigureSquelchAndOutputPower(gTxVfo);
|
||||
RADIO_SetupRegisters(true);
|
||||
|
||||
gHasVfoBackup = false;
|
||||
}
|
||||
if (gInputBoxIndex == 0)
|
||||
VFO_RestoreBackup();
|
||||
|
||||
gKeyInputCountdown = key_input_timeout_500ms;
|
||||
channelMoveSwitch();
|
||||
@@ -786,15 +752,30 @@ static void MAIN_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
if (bKeyPressed) { // long press MENU key
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
// Exclude channel
|
||||
// Exclude current scan entry
|
||||
if(gScanStateDir != SCAN_OFF)
|
||||
{
|
||||
if(FUNCTION_IsRx() || gScanPauseDelayIn_10ms > 9)
|
||||
{
|
||||
#ifdef ENABLE_SCAN_RANGES
|
||||
if(gScanRangeStart && !IS_MR_CHANNEL(gNextMrChannel))
|
||||
{
|
||||
if(CHFRSCANNER_ExcludeCurrentScanRange())
|
||||
{
|
||||
UI_MAIN_NotifyScanProgressDataChanged();
|
||||
lastFoundFrqOrChan = lastFoundFrqOrChanOld;
|
||||
CHFRSCANNER_ContinueScanning();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
ChannelAttributes_t *att = MR_GetChannelAttributes(lastFoundFrqOrChan);
|
||||
att->exclude = true;
|
||||
|
||||
MR_SaveChannelAttributesToFlash(lastFoundFrqOrChan, att);
|
||||
UI_MAIN_NotifyScanProgressDataChanged();
|
||||
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
gFlagResetVfos = true;
|
||||
@@ -839,7 +820,11 @@ static void MAIN_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
if(gEeprom.MENU_LOCK == false) {
|
||||
if(gEeprom.MENU_LOCK == true) {
|
||||
HideFKeyIcon();
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
gFlagRefreshSetting = true;
|
||||
@@ -847,10 +832,6 @@ static void MAIN_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_MENU;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
@@ -860,13 +841,6 @@ static void MAIN_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
|
||||
static void MAIN_Key_STAR(bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
if(gEeprom.MENU_LOCK == true) {
|
||||
return; // prevent F function if MENU LOCK is true
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gCurrentFunction == FUNCTION_TRANSMIT)
|
||||
return;
|
||||
|
||||
@@ -876,6 +850,19 @@ static void MAIN_Key_STAR(bool bKeyPressed, bool bKeyHeld)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bKeyHeld && bKeyPressed) { // star key pressed
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL; // beep when key is pressed
|
||||
return; // don't use the key till it's released
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
if(gEeprom.MENU_LOCK == true) {
|
||||
HideFKeyIcon();
|
||||
|
||||
return; // prevent F function if MENU LOCK is true
|
||||
}
|
||||
#endif
|
||||
|
||||
if (bKeyHeld && !gWasFKeyPressed){ // long press
|
||||
if (!bKeyPressed) // released
|
||||
return;
|
||||
@@ -898,12 +885,6 @@ static void MAIN_Key_STAR(bool bKeyPressed, bool bKeyHeld)
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bKeyPressed) { // just pressed
|
||||
return;
|
||||
}
|
||||
|
||||
// just released
|
||||
|
||||
if (!gWasFKeyPressed) // pressed without the F-key
|
||||
{
|
||||
@@ -916,7 +897,6 @@ static void MAIN_Key_STAR(bool bKeyPressed, bool bKeyHeld)
|
||||
#endif
|
||||
)
|
||||
{ // start entering a DTMF string
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
memcpy(gDTMF_InputBox, gDTMF_String, MIN(sizeof(gDTMF_InputBox), sizeof(gDTMF_String) - 1));
|
||||
gDTMF_InputBox_Index = 0;
|
||||
gDTMF_InputMode = true;
|
||||
@@ -958,7 +938,7 @@ static void MAIN_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN // Set Squelch F + UP or Down
|
||||
if(gWasFKeyPressed) {
|
||||
processFKeyFunction(Direction == 1 ? KEY_UP : KEY_DOWN, false);
|
||||
processFKeyFunction(Direction == 1 ? KEY_UP : KEY_DOWN, true);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -970,10 +950,12 @@ static void MAIN_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
|
||||
uint16_t Channel = gEeprom.ScreenChannel[gEeprom.TX_VFO];
|
||||
|
||||
if (bKeyHeld || !bKeyPressed) { // key held or released
|
||||
if (gInputBoxIndex > 0)
|
||||
gInputBoxIndex = 0;
|
||||
if (gInputBoxIndex > 0) {
|
||||
gInputBoxIndex = 0;
|
||||
gHasVfoBackup = false;
|
||||
}
|
||||
|
||||
if (bKeyHeld || !bKeyPressed) { // key held or released
|
||||
if (!bKeyPressed) {
|
||||
if (!bKeyHeld || IS_FREQ_CHANNEL(Channel))
|
||||
return;
|
||||
@@ -1038,9 +1020,7 @@ static void MAIN_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
}
|
||||
|
||||
// jump to the next channel
|
||||
CHFRSCANNER_Start(false, Direction);
|
||||
gScanPauseDelayIn_10ms = 1;
|
||||
gScheduleScanListen = false;
|
||||
CHFRSCANNER_ManualResume(Direction);
|
||||
|
||||
gPttWasReleased = true;
|
||||
}
|
||||
@@ -1106,4 +1086,4 @@ void MAIN_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
+296
-113
@@ -222,6 +222,13 @@ int MENU_GetLimits(uint8_t menu_id, int32_t *pMin, int32_t *pMax)
|
||||
*pMax = ARRAY_SIZE(gSubMenu_RX_TX) - 1;
|
||||
break;
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN) && defined(ENABLE_FEAT_F4HWN_LOGO_SAV)
|
||||
case MENU_SET_SAV:
|
||||
//*pMin = 0;
|
||||
*pMax = SET_SAV_LEN - 1;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
#ifdef ENABLE_AM_FIX
|
||||
case MENU_AM_FIX:
|
||||
@@ -426,10 +433,21 @@ int MENU_GetLimits(uint8_t menu_id, int32_t *pMin, int32_t *pMax)
|
||||
//*pMin = 0;
|
||||
*pMax = ARRAY_SIZE(gSubMenu_SET_MET) - 1;
|
||||
break;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
case MENU_SET_SCN:
|
||||
//*pMin = 0;
|
||||
*pMax = ARRAY_SIZE(gSubMenu_SET_SCN) - 1;
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
case MENU_SET_AUD:
|
||||
//*pMin = 0;
|
||||
*pMax = ARRAY_SIZE(gSubMenu_SET_AUD) - 1;
|
||||
if(gTxVfo->Modulation == MODULATION_AM)
|
||||
*pMax = ARRAY_SIZE(gSubMenu_SET_AUD_AM) - 1;
|
||||
else if (gTxVfo->Modulation == MODULATION_USB)
|
||||
*pMax = 0;
|
||||
else
|
||||
*pMax = ARRAY_SIZE(gSubMenu_SET_AUD_FM) - 1;
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
@@ -452,6 +470,28 @@ int MENU_GetLimits(uint8_t menu_id, int32_t *pMin, int32_t *pMax)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
case MENU_VOL: {
|
||||
// SysInf paginates:
|
||||
// page 0 = identity,
|
||||
// page 1 = build date/time,
|
||||
// page 2 = battery,
|
||||
// +1 if F4HWN_MEM (Flash/SRAM),
|
||||
// +2 if F4HWN_QRCODE (Code QR + Wiki QR).
|
||||
int32_t vol_max = 0;
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
vol_max += 2;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_MEM
|
||||
vol_max += 1;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_QRCODE
|
||||
vol_max += 2;
|
||||
#endif
|
||||
if (vol_max == 0) return -1;
|
||||
*pMax = vol_max;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
@@ -480,6 +520,9 @@ void MENU_AcceptSetting(void)
|
||||
case MENU_SQL:
|
||||
gEeprom.SQUELCH_LEVEL = gSubMenuSelection;
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
gSquelchLevelOriginal = 10;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case MENU_STEP:
|
||||
@@ -586,7 +629,7 @@ void MENU_AcceptSetting(void)
|
||||
|
||||
case MENU_MEM_NAME:
|
||||
for (int i = 9; i >= 0; i--) {
|
||||
if (edit[i] != ' ' && edit[i] != '_' && edit[i] != 0x00 && edit[i] != 0xff)
|
||||
if (edit[i] != ' ' && edit[i] != 0x00 && edit[i] != 0xff)
|
||||
break;
|
||||
edit[i] = ' ';
|
||||
}
|
||||
@@ -770,7 +813,7 @@ void MENU_AcceptSetting(void)
|
||||
case MENU_D_LIVE_DEC:
|
||||
gSetting_live_DTMF_decoder = gSubMenuSelection;
|
||||
gDTMF_RX_live_timeout = 0;
|
||||
memset(gDTMF_RX_live, 0, sizeof(gDTMF_RX_live));
|
||||
DTMF_clear_input_box_memory();
|
||||
if (!gSetting_live_DTMF_decoder)
|
||||
BK4819_DisableDTMF();
|
||||
gFlagReconfigureVfos = true;
|
||||
@@ -959,10 +1002,19 @@ void MENU_AcceptSetting(void)
|
||||
case MENU_SET_GUI:
|
||||
gSetting_set_gui = gSubMenuSelection;
|
||||
break;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
case MENU_SET_SCN:
|
||||
gSetting_set_scn = gSubMenuSelection;
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
case MENU_SET_AUD:
|
||||
gSetting_set_audio = gSubMenuSelection;
|
||||
RADIO_SetModulation(gRxVfo->Modulation);
|
||||
if(gTxVfo->Modulation == MODULATION_AM)
|
||||
gSetting_set_audio_am = gSubMenuSelection;
|
||||
else if (gTxVfo->Modulation == MODULATION_FM)
|
||||
gSetting_set_audio_fm = gSubMenuSelection;
|
||||
|
||||
RADIO_SetModulation(gTxVfo->Modulation);
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
@@ -985,6 +1037,11 @@ void MENU_AcceptSetting(void)
|
||||
case MENU_SET_TMR:
|
||||
gSetting_set_tmr = gSubMenuSelection;
|
||||
break;
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
case MENU_SET_SAV:
|
||||
gSetting_set_sav = gSubMenuSelection;
|
||||
break;
|
||||
#endif
|
||||
case MENU_TX_LOCK:
|
||||
gTxVfo->TX_LOCK = gSubMenuSelection;
|
||||
gRequestSaveChannel = 1;
|
||||
@@ -1018,6 +1075,11 @@ void MENU_ShowCurrentSetting(void)
|
||||
gSubMenuSelection = gEeprom.SQUELCH_LEVEL;
|
||||
break;
|
||||
|
||||
case MENU_VOL:
|
||||
// SysInf is paginated; always start on page 0 (identity).
|
||||
gSubMenuSelection = 0;
|
||||
break;
|
||||
|
||||
case MENU_STEP:
|
||||
gSubMenuSelection = FREQUENCY_GetSortedIdxFromStepIdx(gTxVfo->STEP_SETTING);
|
||||
break;
|
||||
@@ -1414,9 +1476,19 @@ void MENU_ShowCurrentSetting(void)
|
||||
case MENU_SET_GUI:
|
||||
gSubMenuSelection = gSetting_set_gui;
|
||||
break;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
case MENU_SET_SCN:
|
||||
gSubMenuSelection = gSetting_set_scn;
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
case MENU_SET_AUD:
|
||||
gSubMenuSelection = gSetting_set_audio;
|
||||
if(gTxVfo->Modulation == MODULATION_AM)
|
||||
gSubMenuSelection = gSetting_set_audio_am;
|
||||
else if (gTxVfo->Modulation == MODULATION_USB)
|
||||
gSubMenuSelection = 0;
|
||||
else
|
||||
gSubMenuSelection = gSetting_set_audio_fm;
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
@@ -1437,6 +1509,11 @@ void MENU_ShowCurrentSetting(void)
|
||||
case MENU_SET_TMR:
|
||||
gSubMenuSelection = gSetting_set_tmr;
|
||||
break;
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
case MENU_SET_SAV:
|
||||
gSubMenuSelection = gSetting_set_sav;
|
||||
break;
|
||||
#endif
|
||||
case MENU_TX_LOCK:
|
||||
gSubMenuSelection = gTxVfo->TX_LOCK;
|
||||
break;
|
||||
@@ -1447,6 +1524,29 @@ void MENU_ShowCurrentSetting(void)
|
||||
}
|
||||
}
|
||||
|
||||
static KEY_Code_t edit_last_key = 255;
|
||||
static uint8_t edit_char_index = 0;
|
||||
|
||||
static const char* const char_map[10] = {
|
||||
" 0", // KEY_0
|
||||
".,-()@/\\+=*#<>[]~1", // KEY_1
|
||||
"abc2", // KEY_2
|
||||
"def3", // KEY_3
|
||||
"ghi4", // KEY_4
|
||||
"jkl5", // KEY_5
|
||||
"mno6", // KEY_6
|
||||
"pqrs7", // KEY_7
|
||||
"tuv8", // KEY_8
|
||||
"wxyz9" // KEY_9
|
||||
};
|
||||
|
||||
static bool MENU_IsEditingName() {
|
||||
return !gCssBackgroundScan
|
||||
&& UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME
|
||||
&& gIsInSubMenu
|
||||
&& edit_index >= 0;
|
||||
}
|
||||
|
||||
static void MENU_Key_0_to_9(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
uint8_t Offset;
|
||||
@@ -1454,33 +1554,55 @@ static void MENU_Key_0_to_9(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
int32_t Max;
|
||||
uint16_t Value = 0;
|
||||
|
||||
if (bKeyHeld || !bKeyPressed)
|
||||
if (!bKeyPressed)
|
||||
return;
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME && edit_index >= 0)
|
||||
{ // currently editing the channel name
|
||||
if (edit_index >= 10)
|
||||
return;
|
||||
|
||||
if (edit_index < 10)
|
||||
uint8_t key_idx = Key - KEY_0;
|
||||
|
||||
if (bKeyHeld)
|
||||
{
|
||||
if (Key <= KEY_9)
|
||||
edit[edit_index] = '0' + key_idx;
|
||||
edit_last_key = 255;
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Key != edit_last_key)
|
||||
{
|
||||
edit_last_key = Key;
|
||||
edit_char_index = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
edit_char_index++;
|
||||
if (char_map[key_idx][edit_char_index] == '\0')
|
||||
{
|
||||
edit[edit_index] = '0' + Key - KEY_0;
|
||||
|
||||
if (++edit_index >= 10)
|
||||
{ // exit edit
|
||||
gFlagAcceptSetting = false;
|
||||
gAskForConfirmation = 1;
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
edit_char_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
char c = char_map[key_idx][edit_char_index];
|
||||
if (edit_is_uppercase && c >= 'a' && c <= 'z')
|
||||
{
|
||||
c -= 32;
|
||||
}
|
||||
edit[edit_index] = c;
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bKeyHeld)
|
||||
return;
|
||||
|
||||
INPUTBOX_Append(Key);
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
@@ -1547,12 +1669,14 @@ static void MENU_Key_0_to_9(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
return;
|
||||
}
|
||||
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_MEM_CH ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_DEL_CH ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_1_CALL ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_S_PRI_CH_1 ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_S_PRI_CH_2 ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME)
|
||||
const int m = UI_MENU_GetCurrentMenuId();
|
||||
|
||||
if (m == MENU_MEM_CH ||
|
||||
m == MENU_DEL_CH ||
|
||||
m == MENU_1_CALL ||
|
||||
m == MENU_S_PRI_CH_1 ||
|
||||
m == MENU_S_PRI_CH_2 ||
|
||||
m == MENU_MEM_NAME)
|
||||
{ // enter 4-digit channel number
|
||||
|
||||
if (gInputBoxIndex < 4)
|
||||
@@ -1567,7 +1691,7 @@ static void MENU_Key_0_to_9(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
gInputBoxIndex = 0;
|
||||
|
||||
//Value = ((gInputBox[0] * 100) + (gInputBox[1] * 10) + gInputBox[2]) - 1;
|
||||
Value = ((gInputBox[0] * 1000) + (gInputBox[1] * 100) + (gInputBox[2] * 10) + gInputBox[3]) - 1;
|
||||
Value = (((gInputBox[0] * 10 + gInputBox[1]) * 10 + gInputBox[2]) * 10 + gInputBox[3]) - 1;
|
||||
|
||||
if (IS_MR_CHANNEL(Value))
|
||||
{
|
||||
@@ -1624,6 +1748,53 @@ static void MENU_Key_0_to_9(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
|
||||
static void MENU_Key_EXIT(bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if (MENU_IsEditingName())
|
||||
{
|
||||
if (!bKeyPressed)
|
||||
{
|
||||
if (bKeyHeld)
|
||||
return; // release after a long press, keep editing
|
||||
|
||||
if (edit_index == 0)
|
||||
goto Skip;
|
||||
|
||||
if (edit_index > 0)
|
||||
{ // step back one character while editing the channel name
|
||||
edit_index--;
|
||||
edit_last_key = 255;
|
||||
gAskForConfirmation = 0;
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bKeyHeld)
|
||||
{ // wait to see if the user wants a short exit or a long backspace
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
Skip:
|
||||
|
||||
/* Backlight related menus set full brightness. Set it back to the configured value,
|
||||
just in case we are editing from one of them. */
|
||||
BACKLIGHT_TurnOn();
|
||||
|
||||
gAskForConfirmation = 0;
|
||||
gIsInSubMenu = false;
|
||||
gInputBoxIndex = 0;
|
||||
gFlagRefreshSetting = true;
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_CANCEL;
|
||||
#endif
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (bKeyHeld || !bKeyPressed)
|
||||
return;
|
||||
|
||||
@@ -1631,29 +1802,22 @@ static void MENU_Key_EXIT(bool bKeyPressed, bool bKeyHeld)
|
||||
|
||||
if (!gCssBackgroundScan)
|
||||
{
|
||||
/* Backlight related menus set full brightness. Set it back to the configured value,
|
||||
just in case we are exiting from one of them. */
|
||||
BACKLIGHT_TurnOn();
|
||||
|
||||
if (gIsInSubMenu)
|
||||
{
|
||||
if (gInputBoxIndex == 0 || UI_MENU_GetCurrentMenuId() != MENU_OFFSET)
|
||||
{
|
||||
gAskForConfirmation = 0;
|
||||
gIsInSubMenu = false;
|
||||
gInputBoxIndex = 0;
|
||||
gFlagRefreshSetting = true;
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_CANCEL;
|
||||
#endif
|
||||
goto Skip;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Backlight related menus set full brightness. Set it back to the configured value,
|
||||
just in case we are exiting from one of them. */
|
||||
BACKLIGHT_TurnOn();
|
||||
|
||||
gInputBox[--gInputBoxIndex] = 10;
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
}
|
||||
|
||||
// ***********************
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1684,27 +1848,29 @@ static void MENU_Key_EXIT(bool bKeyPressed, bool bKeyHeld)
|
||||
|
||||
static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld)
|
||||
{
|
||||
if (bKeyHeld || !bKeyPressed)
|
||||
if (!bKeyPressed || (bKeyHeld && (!MENU_IsEditingName() || gAskForConfirmation)))
|
||||
return;
|
||||
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
|
||||
if (!gIsInSubMenu)
|
||||
{
|
||||
const int m = UI_MENU_GetCurrentMenuId();
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
if (UI_MENU_GetCurrentMenuId() != MENU_SCR)
|
||||
if (m != MENU_SCR)
|
||||
gAnotherVoiceID = MenuList[gMenuCursor].voice_id;
|
||||
#endif
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_UPCODE
|
||||
|| UI_MENU_GetCurrentMenuId() == MENU_DWCODE
|
||||
if (m == MENU_UPCODE
|
||||
|| m == MENU_DWCODE
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
|| UI_MENU_GetCurrentMenuId() == MENU_ANI_ID
|
||||
|| m == MENU_ANI_ID
|
||||
#endif
|
||||
)
|
||||
return;
|
||||
#if 1
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_DEL_CH || UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME)
|
||||
if (m == MENU_DEL_CH || m == MENU_MEM_NAME)
|
||||
if (!RADIO_CheckValidChannel(gSubMenuSelection, false, 0))
|
||||
return; // invalid channel
|
||||
#endif
|
||||
@@ -1712,7 +1878,7 @@ static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld)
|
||||
gAskForConfirmation = 0;
|
||||
gIsInSubMenu = true;
|
||||
|
||||
// if (UI_MENU_GetCurrentMenuId() != MENU_D_LIST)
|
||||
// if (m != MENU_D_LIST)
|
||||
{
|
||||
gInputBoxIndex = 0;
|
||||
edit_index = -1;
|
||||
@@ -1730,12 +1896,18 @@ static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld)
|
||||
|
||||
SETTINGS_FetchChannelName(edit, gSubMenuSelection);
|
||||
|
||||
// pad the channel name out with '_'
|
||||
edit_index = strlen(edit);
|
||||
while (edit_index < 10)
|
||||
edit[edit_index++] = '_';
|
||||
edit[edit_index] = 0;
|
||||
// pad the channel name out with ' '
|
||||
size_t len = strlen(edit);
|
||||
if (len < 10)
|
||||
{
|
||||
memset(edit + len, ' ', 10 - len);
|
||||
edit[10] = '\0';
|
||||
}
|
||||
|
||||
edit_index = 0; // 'edit_index' is going to be used as the cursor position
|
||||
edit_last_key = 255;
|
||||
edit_char_index = 0;
|
||||
edit_is_uppercase = false;
|
||||
|
||||
// make a copy so we can test for change when exiting the menu item
|
||||
memcpy(edit_original, edit, sizeof(edit_original));
|
||||
@@ -1745,9 +1917,14 @@ static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld)
|
||||
else
|
||||
if (edit_index >= 0 && edit_index < 10)
|
||||
{ // editing the channel name characters
|
||||
edit_last_key = 255;
|
||||
|
||||
if (++edit_index < 10)
|
||||
return; // next char
|
||||
if (bKeyHeld) {
|
||||
edit_index = 10;
|
||||
}
|
||||
else if (++edit_index < 10) {
|
||||
return;
|
||||
}
|
||||
|
||||
// exit
|
||||
gFlagAcceptSetting = false;
|
||||
@@ -1763,10 +1940,12 @@ static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld)
|
||||
|
||||
if (gIsInSubMenu)
|
||||
{
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_RESET ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_MEM_CH ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_DEL_CH ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME)
|
||||
const int m = UI_MENU_GetCurrentMenuId();
|
||||
|
||||
if (m == MENU_RESET ||
|
||||
m == MENU_MEM_CH ||
|
||||
m == MENU_DEL_CH ||
|
||||
m == MENU_MEM_NAME)
|
||||
{
|
||||
switch (gAskForConfirmation)
|
||||
{
|
||||
@@ -1779,7 +1958,7 @@ static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld)
|
||||
|
||||
UI_DisplayMenu();
|
||||
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_RESET)
|
||||
if (m == MENU_RESET)
|
||||
{
|
||||
#ifdef ENABLE_VOICE
|
||||
AUDIO_SetVoiceID(0, VOICE_ID_CONFIRM);
|
||||
@@ -1821,7 +2000,7 @@ static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld)
|
||||
|
||||
static void MENU_Key_STAR(const bool bKeyPressed, const bool bKeyHeld)
|
||||
{
|
||||
if (bKeyHeld || !bKeyPressed)
|
||||
if (!bKeyPressed)
|
||||
return;
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
@@ -1831,13 +2010,8 @@ static void MENU_Key_STAR(const bool bKeyPressed, const bool bKeyHeld)
|
||||
|
||||
if (edit_index < 10)
|
||||
{
|
||||
edit[edit_index] = '-';
|
||||
|
||||
if (++edit_index >= 10)
|
||||
{ // exit edit
|
||||
gFlagAcceptSetting = false;
|
||||
gAskForConfirmation = 1;
|
||||
}
|
||||
edit[edit_index] = !bKeyHeld ? '-' : '*';
|
||||
edit_last_key = 255;
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
}
|
||||
@@ -1845,6 +2019,9 @@ static void MENU_Key_STAR(const bool bKeyPressed, const bool bKeyHeld)
|
||||
return;
|
||||
}
|
||||
|
||||
if (bKeyHeld)
|
||||
return;
|
||||
|
||||
RADIO_SelectVfos();
|
||||
|
||||
#ifdef ENABLE_NOAA
|
||||
@@ -1853,7 +2030,8 @@ static void MENU_Key_STAR(const bool bKeyPressed, const bool bKeyHeld)
|
||||
if (gRxVfo->Modulation == MODULATION_FM)
|
||||
#endif
|
||||
{
|
||||
if ((UI_MENU_GetCurrentMenuId() == MENU_R_CTCS || UI_MENU_GetCurrentMenuId() == MENU_R_DCS) && gIsInSubMenu)
|
||||
const int m = UI_MENU_GetCurrentMenuId();
|
||||
if ((m == MENU_R_CTCS || m == MENU_R_DCS) && gIsInSubMenu)
|
||||
{ // scan CTCSS or DCS to find the tone/code of the incoming signal
|
||||
if (!SCANNER_IsScanning())
|
||||
MENU_StartCssScan();
|
||||
@@ -1874,15 +2052,23 @@ static void MENU_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
uint16_t Channel;
|
||||
bool bCheckScanList;
|
||||
|
||||
if (!bKeyPressed)
|
||||
return;
|
||||
|
||||
if (!bKeyHeld) {
|
||||
gInputBoxIndex = 0;
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
}
|
||||
|
||||
if (!gEeprom.SET_NAV && gIsInSubMenu) {
|
||||
Direction = -Direction;
|
||||
}
|
||||
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME && gIsInSubMenu && edit_index >= 0)
|
||||
{ // change the character
|
||||
if (bKeyPressed && edit_index < 10 && Direction != 0)
|
||||
if (edit_index < 10 && Direction != 0)
|
||||
{
|
||||
const char unwanted[] = "$%&!\"':;?^`|{}";
|
||||
const char unwanted[] = "$%&!\"':;?^`|{}_";
|
||||
char c = edit[edit_index] + Direction;
|
||||
unsigned int i = 0;
|
||||
while (i < sizeof(unwanted) && c >= 32 && c <= 126)
|
||||
@@ -1894,25 +2080,13 @@ static void MENU_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
}
|
||||
}
|
||||
edit[edit_index] = (c < 32) ? 126 : (c > 126) ? 32 : c;
|
||||
edit_last_key = 255;
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bKeyHeld)
|
||||
{
|
||||
if (!bKeyPressed)
|
||||
return;
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
gInputBoxIndex = 0;
|
||||
}
|
||||
else
|
||||
if (!bKeyPressed)
|
||||
return;
|
||||
|
||||
if (SCANNER_IsScanning()) {
|
||||
return;
|
||||
}
|
||||
@@ -1925,9 +2099,11 @@ static void MENU_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
|
||||
if (UI_MENU_GetCurrentMenuId() != MENU_ABR
|
||||
&& UI_MENU_GetCurrentMenuId() != MENU_ABR_MIN
|
||||
&& UI_MENU_GetCurrentMenuId() != MENU_ABR_MAX
|
||||
const int m = UI_MENU_GetCurrentMenuId();
|
||||
|
||||
if (m != MENU_ABR
|
||||
&& m != MENU_ABR_MIN
|
||||
&& m != MENU_ABR_MAX
|
||||
&& gEeprom.BACKLIGHT_TIME == 0) // backlight always off and not in the backlight menu
|
||||
{
|
||||
BACKLIGHT_TurnOff();
|
||||
@@ -1953,8 +2129,10 @@ static void MENU_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
}
|
||||
|
||||
VFO = 0;
|
||||
|
||||
const int m = UI_MENU_GetCurrentMenuId();
|
||||
|
||||
switch (UI_MENU_GetCurrentMenuId())
|
||||
switch (m)
|
||||
{
|
||||
case MENU_DEL_CH:
|
||||
case MENU_1_CALL:
|
||||
@@ -1970,19 +2148,21 @@ static void MENU_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
return;
|
||||
}
|
||||
|
||||
if(UI_MENU_GetCurrentMenuId() == MENU_S_PRI_CH_1 || UI_MENU_GetCurrentMenuId() == MENU_S_PRI_CH_2)
|
||||
if(m == MENU_S_PRI_CH_1 || m == MENU_S_PRI_CH_2)
|
||||
{
|
||||
static int16_t last;
|
||||
|
||||
if(Direction > 0 && gSubMenuSelection == MR_CHANNELS_MAX)
|
||||
{
|
||||
gSubMenuSelection = -1;
|
||||
last = -1;
|
||||
}
|
||||
else if(Direction < 0 && gSubMenuSelection == MR_CHANNELS_MAX)
|
||||
{
|
||||
gSubMenuSelection = MR_CHANNELS_MAX;
|
||||
last = MR_CHANNELS_MAX;
|
||||
if(gSubMenuSelection == MR_CHANNELS_MAX) {
|
||||
if(Direction > 0)
|
||||
{
|
||||
gSubMenuSelection = -1;
|
||||
last = -1;
|
||||
}
|
||||
else if(Direction < 0)
|
||||
{
|
||||
gSubMenuSelection = MR_CHANNELS_MAX;
|
||||
last = MR_CHANNELS_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
Channel = RADIO_FindNextChannel(gSubMenuSelection + Direction, Direction, bCheckScanList, VFO);
|
||||
@@ -2037,19 +2217,20 @@ void MENU_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
case KEY_F:
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME && edit_index >= 0)
|
||||
{ // currently editing the channel name
|
||||
if (!bKeyHeld && bKeyPressed)
|
||||
if (!bKeyPressed)
|
||||
break;
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
if (edit_index < 10)
|
||||
{
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
if (edit_index < 10)
|
||||
{
|
||||
edit[edit_index] = ' ';
|
||||
if (++edit_index >= 10)
|
||||
{ // exit edit
|
||||
gFlagAcceptSetting = false;
|
||||
gAskForConfirmation = 1;
|
||||
}
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
}
|
||||
if (bKeyHeld)
|
||||
edit[edit_index] = '#';
|
||||
|
||||
edit_is_uppercase = !edit_is_uppercase;
|
||||
edit_last_key = 255;
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2067,11 +2248,13 @@ void MENU_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
|
||||
if (gScreenToDisplay == DISPLAY_MENU)
|
||||
{
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_VOL ||
|
||||
const int m = UI_MENU_GetCurrentMenuId();
|
||||
|
||||
if (m == MENU_VOL ||
|
||||
#ifdef ENABLE_F_CAL_MENU
|
||||
UI_MENU_GetCurrentMenuId() == MENU_F_CALI ||
|
||||
m == MENU_F_CALI ||
|
||||
#endif
|
||||
UI_MENU_GetCurrentMenuId() == MENU_BATCAL)
|
||||
m == MENU_BATCAL)
|
||||
{
|
||||
gMenuCountdown = menu_timeout_long_500ms;
|
||||
}
|
||||
|
||||
+12
-15
@@ -62,7 +62,7 @@ static void SCANNER_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
gInputBoxIndex = 0;
|
||||
|
||||
// uint16_t chan = ((gInputBox[0] * 100) + (gInputBox[1] * 10) + gInputBox[2]) - 1;
|
||||
uint16_t chan = ((gInputBox[0] * 1000) + (gInputBox[1] * 100) + (gInputBox[2] * 10) + gInputBox[3]) - 1;
|
||||
uint16_t chan = (((gInputBox[0] * 10 + gInputBox[1]) * 10 + gInputBox[2]) * 10 + gInputBox[3]) - 1;
|
||||
if (IS_MR_CHANNEL(chan)) {
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
@@ -243,16 +243,13 @@ static void SCANNER_Key_STAR(bool bKeyPressed, bool bKeyHeld)
|
||||
return;
|
||||
}
|
||||
|
||||
static void SCANNER_Key_UP_DOWN(bool bKeyPressed, bool pKeyHeld, int8_t Direction)
|
||||
static void SCANNER_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
{
|
||||
if (pKeyHeld) {
|
||||
if (!bKeyPressed)
|
||||
return;
|
||||
if (!bKeyPressed) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (!bKeyPressed)
|
||||
return;
|
||||
|
||||
if (!bKeyHeld) {
|
||||
gInputBoxIndex = 0;
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
}
|
||||
@@ -266,7 +263,7 @@ static void SCANNER_Key_UP_DOWN(bool bKeyPressed, bool pKeyHeld, int8_t Directio
|
||||
gShowChPrefix = RADIO_CheckValidChannel(gScanChannel, false, 0);
|
||||
gRequestDisplayScreen = DISPLAY_SCANNER;
|
||||
}
|
||||
else
|
||||
else if (!bKeyHeld)
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
}
|
||||
|
||||
@@ -345,7 +342,7 @@ void SCANNER_Start(bool singleFreq)
|
||||
gScanFrequency = 0xFFFFFFFF;
|
||||
|
||||
BK4819_PickRXFilterPathBasedOnFrequency(gScanFrequency);
|
||||
BK4819_EnableFrequencyScan();
|
||||
BK4819_SetFrequencyScan(true);
|
||||
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
@@ -418,10 +415,10 @@ void SCANNER_TimeSlice10ms(void)
|
||||
else
|
||||
scanHitCount = 0;
|
||||
|
||||
BK4819_DisableFrequencyScan();
|
||||
BK4819_SetFrequencyScan(false);
|
||||
|
||||
if (scanHitCount < 3) {
|
||||
BK4819_EnableFrequencyScan();
|
||||
BK4819_SetFrequencyScan(true);
|
||||
}
|
||||
else {
|
||||
BK4819_SetScanFrequency(gScanFrequency);
|
||||
@@ -509,8 +506,8 @@ void SCANNER_TimeSlice500ms(void)
|
||||
if (SCANNER_IsScanning() && gScannerSaveState == SCAN_SAVE_NO_PROMPT && gScanCssState < SCAN_CSS_STATE_FOUND) {
|
||||
gScanProgressIndicator++;
|
||||
#ifndef ENABLE_NO_CODE_SCAN_TIMEOUT
|
||||
if (gScanProgressIndicator > 32) {
|
||||
if (gScanCssState == SCAN_CSS_STATE_SCANNING && !gScanSingleFrequency)
|
||||
if (gScanCssState == SCAN_CSS_STATE_SCANNING && gScanProgressIndicator > 32) {
|
||||
if (!gScanSingleFrequency)
|
||||
gScanCssState = SCAN_CSS_STATE_FOUND;
|
||||
else
|
||||
gScanCssState = SCAN_CSS_STATE_FAILED;
|
||||
@@ -528,4 +525,4 @@ void SCANNER_TimeSlice500ms(void)
|
||||
bool SCANNER_IsScanning(void)
|
||||
{
|
||||
return gCssBackgroundScan || (gScreenToDisplay == DISPLAY_SCANNER);
|
||||
}
|
||||
}
|
||||
+1130
-322
File diff suppressed because it is too large.
Load diff
+20
-13
@@ -42,7 +42,8 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
static const uint8_t DrawingEndY = 40;
|
||||
static const uint8_t DrawingEndY = 40;
|
||||
static const uint8_t DrawingTopY = 8; // Reserve top 8px for frequency display (gFrameBuffer[0])
|
||||
|
||||
static const uint8_t U8RssiMap[] = {
|
||||
121,
|
||||
@@ -78,29 +79,35 @@ static const uint16_t scanStepValues[] = {
|
||||
static const uint16_t scanStepBWRegValues[] = {
|
||||
// RX RXw TX BW
|
||||
// 0b0 000 000 001 01 1000
|
||||
// 1
|
||||
// 1 (S_STEP_0_01kHz, index 0)
|
||||
0b0000000001011000, // 6.25
|
||||
// 10
|
||||
// 10 (S_STEP_0_1kHz, index 1)
|
||||
0b0000000001011000, // 6.25
|
||||
// 50
|
||||
// 50 (S_STEP_0_5kHz, index 2)
|
||||
0b0000000001011000, // 6.25
|
||||
// 100
|
||||
// 100 (S_STEP_1_0kHz, index 3)
|
||||
0b0000000001011000, // 6.25
|
||||
// 250
|
||||
// 250 (S_STEP_2_5kHz, index 4)
|
||||
0b0000000001011000, // 6.25
|
||||
// 500
|
||||
// 500 (S_STEP_5_0kHz, index 5)
|
||||
0b0010010001011000, // 6.25
|
||||
// 625
|
||||
// 625 (S_STEP_6_25kHz, index 6)
|
||||
0b0100100001011000, // 6.25
|
||||
// 833
|
||||
// 833 (S_STEP_8_33kHz, index 7)
|
||||
0b0110110001001000, // 6.25
|
||||
// 1000
|
||||
// 1000 (S_STEP_10_0kHz, index 8)
|
||||
0b0110110001001000, // 6.25
|
||||
// 1250
|
||||
// 1250 (S_STEP_12_5kHz, index 9)
|
||||
0b0111111100001000, // 6.25
|
||||
// 2500
|
||||
// 1500 (S_STEP_15_0kHz, index 10)
|
||||
0b0011011000101000, // 25
|
||||
// 10000
|
||||
// 2000 (S_STEP_20_0kHz, index 11)
|
||||
0b0011011000101000, // 25
|
||||
// 2500 (S_STEP_25_0kHz, index 12)
|
||||
0b0011011000101000, // 25
|
||||
// 5000 (S_STEP_50_0kHz, index 13)
|
||||
0b0011011000101000, // 25
|
||||
// 10000 (S_STEP_100_0kHz, index 14)
|
||||
0b0011011000101000, // 25
|
||||
};
|
||||
|
||||
|
||||
+5
-5
@@ -347,9 +347,9 @@ static void CMD_0514(uint32_t Port, const uint8_t *pBuffer)
|
||||
#endif
|
||||
|
||||
gSerialConfigCountDown_500ms = 12; // 6 sec
|
||||
|
||||
// turn the LCD backlight off
|
||||
BACKLIGHT_TurnOff();
|
||||
|
||||
if (gEeprom.BACKLIGHT_TIME < 61) // backlight is set to be always on
|
||||
BACKLIGHT_TurnOff(); // turn the LCD backlight off
|
||||
|
||||
SendVersion(Port);
|
||||
}
|
||||
@@ -593,8 +593,8 @@ static void CMD_052F(uint32_t Port, const uint8_t *pBuffer)
|
||||
}
|
||||
#endif
|
||||
|
||||
// turn the LCD backlight off
|
||||
BACKLIGHT_TurnOff();
|
||||
if (gEeprom.BACKLIGHT_TIME < 61) // backlight is set to be always on
|
||||
BACKLIGHT_TurnOff(); // turn the LCD backlight off
|
||||
|
||||
SendVersion(Port);
|
||||
}
|
||||
|
||||
+37
-101
@@ -33,38 +33,43 @@
|
||||
#include "settings.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
static const uint16_t BEEP_Classic_array[][3] = { /* Tone Duration Repeats */
|
||||
[BEEP_NONE] = {0, 0, 0 },
|
||||
[BEEP_1KHZ_60MS_OPTIONAL] = {1000, 60, 1 },
|
||||
[BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL] = {500, 60, 2 },
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
[BEEP_880HZ_200MS] = {880, 200, 1 },
|
||||
[BEEP_880HZ_500MS] = {880, 500, 1 },
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
[BEEP_400HZ_30MS] = {400, 30, 1 },
|
||||
[BEEP_500HZ_30MS] = {500, 30, 1 },
|
||||
[BEEP_600HZ_30MS] = {600, 30, 1 },
|
||||
#endif
|
||||
[BEEP_880HZ_60MS_TRIPLE_BEEP] = {880, 60, 3 }
|
||||
};
|
||||
|
||||
BEEP_Type_t gBeepToPlay = BEEP_NONE;
|
||||
|
||||
void AUDIO_PlayBeep(BEEP_Type_t Beep)
|
||||
{
|
||||
|
||||
if (Beep != BEEP_880HZ_60MS_DOUBLE_BEEP &&
|
||||
Beep != BEEP_500HZ_60MS_DOUBLE_BEEP &&
|
||||
Beep != BEEP_440HZ_500MS &&
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
Beep != BEEP_880HZ_200MS &&
|
||||
Beep != BEEP_880HZ_500MS &&
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
Beep != BEEP_400HZ_30MS &&
|
||||
Beep != BEEP_500HZ_30MS &&
|
||||
Beep != BEEP_600HZ_30MS &&
|
||||
#endif
|
||||
!gEeprom.BEEP_CONTROL)
|
||||
if (Beep == BEEP_NONE)
|
||||
return;
|
||||
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
if (gScreenToDisplay == DISPLAY_AIRCOPY)
|
||||
if ((Beep == BEEP_1KHZ_60MS_OPTIONAL ||
|
||||
Beep == BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL) &&
|
||||
!gEeprom.BEEP_CONTROL)
|
||||
return;
|
||||
#endif
|
||||
|
||||
|
||||
if (gCurrentFunction == FUNCTION_RECEIVE)
|
||||
return;
|
||||
|
||||
if (gCurrentFunction == FUNCTION_MONITOR)
|
||||
return;
|
||||
|
||||
if (Beep >= ARRAY_SIZE(BEEP_Classic_array))
|
||||
return;
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
if (gFmRadioMode)
|
||||
BK1080_Mute(true);
|
||||
@@ -79,49 +84,14 @@ void AUDIO_PlayBeep(BEEP_Type_t Beep)
|
||||
|
||||
uint16_t ToneConfig = BK4819_ReadRegister(BK4819_REG_71);
|
||||
|
||||
uint16_t ToneFrequency;
|
||||
switch (Beep)
|
||||
{
|
||||
default:
|
||||
case BEEP_NONE:
|
||||
ToneFrequency = 220;
|
||||
break;
|
||||
case BEEP_1KHZ_60MS_OPTIONAL:
|
||||
ToneFrequency = 1000;
|
||||
break;
|
||||
case BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL:
|
||||
case BEEP_500HZ_60MS_DOUBLE_BEEP:
|
||||
ToneFrequency = 500;
|
||||
break;
|
||||
case BEEP_440HZ_500MS:
|
||||
ToneFrequency = 440;
|
||||
break;
|
||||
case BEEP_880HZ_60MS_DOUBLE_BEEP:
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
case BEEP_880HZ_200MS:
|
||||
case BEEP_880HZ_500MS:
|
||||
#endif
|
||||
ToneFrequency = 880;
|
||||
break;
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
case BEEP_400HZ_30MS:
|
||||
ToneFrequency = 400;
|
||||
break;
|
||||
case BEEP_500HZ_30MS:
|
||||
ToneFrequency = 500;
|
||||
break;
|
||||
case BEEP_600HZ_30MS:
|
||||
ToneFrequency = 600;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
if(Beep == BEEP_400HZ_30MS || Beep == BEEP_500HZ_30MS || Beep == BEEP_600HZ_30MS)
|
||||
{
|
||||
BK4819_WriteRegister(BK4819_REG_70, BK4819_REG_70_ENABLE_TONE1 | ((1 & 0x7f) << BK4819_REG_70_SHIFT_TONE1_TUNING_GAIN));
|
||||
}
|
||||
#endif
|
||||
|
||||
BK4819_PlayTone(ToneFrequency, true);
|
||||
BK4819_PrepareToPlayTone(true);
|
||||
|
||||
SYSTEM_DelayMs(2);
|
||||
|
||||
@@ -129,52 +99,12 @@ void AUDIO_PlayBeep(BEEP_Type_t Beep)
|
||||
|
||||
SYSTEM_DelayMs(60);
|
||||
|
||||
uint16_t Duration;
|
||||
switch (Beep)
|
||||
{
|
||||
case BEEP_880HZ_60MS_DOUBLE_BEEP:
|
||||
BK4819_ExitTxMute();
|
||||
SYSTEM_DelayMs(60);
|
||||
BK4819_EnterTxMute();
|
||||
SYSTEM_DelayMs(20);
|
||||
[[fallthrough]];
|
||||
case BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL:
|
||||
case BEEP_500HZ_60MS_DOUBLE_BEEP:
|
||||
BK4819_ExitTxMute();
|
||||
SYSTEM_DelayMs(60);
|
||||
BK4819_EnterTxMute();
|
||||
SYSTEM_DelayMs(20);
|
||||
[[fallthrough]];
|
||||
case BEEP_1KHZ_60MS_OPTIONAL:
|
||||
BK4819_ExitTxMute();
|
||||
Duration = 60;
|
||||
break;
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
case BEEP_400HZ_30MS:
|
||||
case BEEP_500HZ_30MS:
|
||||
case BEEP_600HZ_30MS:
|
||||
BK4819_ExitTxMute();
|
||||
Duration = 30;
|
||||
break;
|
||||
#endif
|
||||
case BEEP_440HZ_500MS:
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
case BEEP_880HZ_200MS:
|
||||
BK4819_ExitTxMute();
|
||||
Duration = 200;
|
||||
break;
|
||||
case BEEP_880HZ_500MS:
|
||||
#endif
|
||||
default:
|
||||
BK4819_ExitTxMute();
|
||||
Duration = 500;
|
||||
break;
|
||||
for (uint8_t i = 0; i < BEEP_Classic_array[Beep][BEEP_REPEATS]; i++) {
|
||||
BK4819_PlayToneRaw( BEEP_Classic_array[Beep][BEEP_TONE],
|
||||
BEEP_Classic_array[Beep][BEEP_DURATION]);
|
||||
SYSTEM_DelayMs(20);
|
||||
}
|
||||
|
||||
SYSTEM_DelayMs(Duration);
|
||||
BK4819_EnterTxMute();
|
||||
SYSTEM_DelayMs(20);
|
||||
|
||||
AUDIO_AudioPathOff();
|
||||
|
||||
SYSTEM_DelayMs(5);
|
||||
@@ -182,11 +112,18 @@ void AUDIO_PlayBeep(BEEP_Type_t Beep)
|
||||
SYSTEM_DelayMs(5);
|
||||
BK4819_WriteRegister(BK4819_REG_71, ToneConfig);
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
const bool isFmRadio = gFmRadioMode;
|
||||
|
||||
if (isFmRadio)
|
||||
SYSTEM_DelayMs(10);
|
||||
#endif
|
||||
|
||||
if (gEnableSpeaker)
|
||||
AUDIO_AudioPathOn();
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
if (gFmRadioMode)
|
||||
if (isFmRadio)
|
||||
BK1080_Mute(false);
|
||||
#endif
|
||||
|
||||
@@ -196,7 +133,6 @@ void AUDIO_PlayBeep(BEEP_Type_t Beep)
|
||||
#ifdef ENABLE_VOX
|
||||
gVoxResumeCountdown = 80;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
|
||||
+8
-3
@@ -22,23 +22,28 @@
|
||||
|
||||
#include "driver/gpio.h"
|
||||
|
||||
enum {
|
||||
BEEP_TONE = 0,
|
||||
BEEP_DURATION,
|
||||
BEEP_REPEATS
|
||||
};
|
||||
|
||||
|
||||
enum BEEP_Type_t
|
||||
{
|
||||
BEEP_NONE = 0,
|
||||
BEEP_1KHZ_60MS_OPTIONAL,
|
||||
BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL,
|
||||
BEEP_440HZ_500MS,
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
BEEP_880HZ_200MS,
|
||||
BEEP_880HZ_500MS,
|
||||
#endif
|
||||
BEEP_500HZ_60MS_DOUBLE_BEEP,
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
BEEP_400HZ_30MS,
|
||||
BEEP_500HZ_30MS,
|
||||
BEEP_600HZ_30MS,
|
||||
#endif
|
||||
BEEP_880HZ_60MS_DOUBLE_BEEP
|
||||
BEEP_880HZ_60MS_TRIPLE_BEEP
|
||||
};
|
||||
|
||||
typedef enum BEEP_Type_t BEEP_Type_t;
|
||||
|
||||
@@ -57,6 +57,21 @@ const uint8_t gFontLight[9] =
|
||||
0b00001100,
|
||||
};
|
||||
|
||||
// Same bulb outline as gFontLight with the inner filament removed,
|
||||
// used when the manual backlight is currently off
|
||||
const uint8_t gFontLightOff[9] =
|
||||
{
|
||||
0b00001100,
|
||||
0b00010010,
|
||||
0b00100001,
|
||||
0b01100001,
|
||||
0b01100001,
|
||||
0b01100001,
|
||||
0b00100001,
|
||||
0b00010010,
|
||||
0b00001100,
|
||||
};
|
||||
|
||||
const uint8_t gFontMute[12] =
|
||||
{
|
||||
0b00011100,
|
||||
|
||||
@@ -12,6 +12,7 @@ extern const uint8_t gFontS[6];
|
||||
|
||||
extern const uint8_t gFontKeyLock[9];
|
||||
extern const uint8_t gFontLight[9];
|
||||
extern const uint8_t gFontLightOff[9];
|
||||
extern const uint8_t gFontMute[12];
|
||||
|
||||
extern const uint8_t gFontXB[2][6];
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include "dcs.h"
|
||||
#include "misc.h"
|
||||
|
||||
@@ -26,6 +27,12 @@ const uint16_t CTCSS_Options[50] = {
|
||||
2035, 2065, 2107, 2181, 2257, 2291, 2336, 2418, 2503, 2541
|
||||
};
|
||||
|
||||
// Indices in CTCSS_Options for the 12 non-homologated tones.
|
||||
static const uint8_t CTCSS_ExtraIdx[12] = {
|
||||
1, 26, 28, 30, 32, 34, 36, 38, 39, 41,
|
||||
45, 49
|
||||
};
|
||||
|
||||
const uint16_t DCS_Options[104] = {
|
||||
0x0013, 0x0015, 0x0016, 0x0019, 0x001A, 0x001E, 0x0023, 0x0027,
|
||||
0x0029, 0x002B, 0x002C, 0x0035, 0x0039, 0x003A, 0x003B, 0x003C,
|
||||
@@ -42,6 +49,13 @@ const uint16_t DCS_Options[104] = {
|
||||
0x01C3, 0x01CA, 0x01D3, 0x01D9, 0x01DA, 0x01DC, 0x01E3, 0x01EC,
|
||||
};
|
||||
|
||||
// Indices in DCS_Options for the 21 non-PMR446 DCS codes.
|
||||
static const uint8_t DCS_ExtraIdx[21] = {
|
||||
5, 9, 19, 25, 34, 36, 41, 43, 44, 48,
|
||||
50, 54, 56, 60, 71, 72, 73, 74, 75, 82,
|
||||
83
|
||||
};
|
||||
|
||||
static uint32_t DCS_CalculateGolay(uint32_t CodeWord)
|
||||
{
|
||||
unsigned int i;
|
||||
@@ -108,3 +122,40 @@ uint8_t DCS_GetCtcssCode(int Code)
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static uint8_t DCS_GetApprovedIndex(uint8_t Option, size_t options_size, size_t extraidx_size, const uint8_t *extraidx)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int extra_pos = 0;
|
||||
uint8_t approved_index = 0;
|
||||
|
||||
if (Option >= options_size)
|
||||
return 0xFF;
|
||||
|
||||
for (i = 0; i < options_size; i++) {
|
||||
const bool is_extra = extra_pos < extraidx_size &&
|
||||
i == extraidx[extra_pos];
|
||||
|
||||
if (is_extra) {
|
||||
extra_pos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == Option)
|
||||
return approved_index;
|
||||
|
||||
approved_index++;
|
||||
}
|
||||
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
uint8_t DCS_GetCtcssApprovedIndex(uint8_t Option)
|
||||
{
|
||||
return DCS_GetApprovedIndex(Option, ARRAY_SIZE(CTCSS_Options), ARRAY_SIZE(CTCSS_ExtraIdx), CTCSS_ExtraIdx);
|
||||
}
|
||||
|
||||
uint8_t DCS_GetDcsApprovedIndex(uint8_t Option)
|
||||
{
|
||||
return DCS_GetApprovedIndex(Option, ARRAY_SIZE(DCS_Options), ARRAY_SIZE(DCS_ExtraIdx), DCS_ExtraIdx);
|
||||
}
|
||||
@@ -40,6 +40,7 @@ extern const uint16_t DCS_Options[104];
|
||||
uint32_t DCS_GetGolayCodeWord(DCS_CodeType_t CodeType, uint8_t Option);
|
||||
uint8_t DCS_GetCdcssCode(uint32_t Code);
|
||||
uint8_t DCS_GetCtcssCode(int Code);
|
||||
uint8_t DCS_GetCtcssApprovedIndex(uint8_t Option);
|
||||
uint8_t DCS_GetDcsApprovedIndex(uint8_t Option);
|
||||
|
||||
#endif
|
||||
|
||||
+80
-38
@@ -31,8 +31,10 @@
|
||||
#include "misc.h"
|
||||
#endif
|
||||
|
||||
#define PWM_FREQ 320
|
||||
#define DUTY_CYCLE_LEVELS 64
|
||||
#define PWM_FREQ 4000
|
||||
// 32 levels keep every step of the value[] table distinct after
|
||||
// quantization while halving the DMA duty cycle buffer (128 bytes saved)
|
||||
#define DUTY_CYCLE_LEVELS 32
|
||||
|
||||
#define DUTY_CYCLE_ON_VALUE GPIO_PIN_MASK(GPIO_PIN_BACKLIGHT)
|
||||
#define DUTY_CYCLE_OFF_VALUE (DUTY_CYCLE_ON_VALUE << 16)
|
||||
@@ -44,14 +46,20 @@ static uint32_t dutyCycle[DUTY_CYCLE_LEVELS];
|
||||
|
||||
// this is decremented once every 500ms
|
||||
uint16_t gBacklightCountdown_500ms = 0;
|
||||
bool gUpdateBacklight = false;
|
||||
bool backlightOn;
|
||||
|
||||
static uint8_t currentIndex = 0;
|
||||
static int16_t currentBrightness = 0;
|
||||
static int16_t targetBrightness = 0;
|
||||
static int16_t fadeStep = 0;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
const uint8_t value[] = {
|
||||
0, // 0 off
|
||||
8, // 1 visible in the dark
|
||||
14, // 2
|
||||
22, // 3
|
||||
16, // 2
|
||||
24, // 3
|
||||
32, // 4
|
||||
48, // 5
|
||||
72, // 6
|
||||
@@ -101,15 +109,26 @@ void BACKLIGHT_InitHardware()
|
||||
|
||||
static void BACKLIGHT_Sound(void)
|
||||
{
|
||||
if (gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_SOUND || gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_ALL)
|
||||
if (gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_SOUND ||
|
||||
gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_ALL
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO
|
||||
|| gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_LOGO
|
||||
#endif
|
||||
)
|
||||
{
|
||||
AUDIO_PlayBeep(BEEP_880HZ_60MS_DOUBLE_BEEP);
|
||||
AUDIO_PlayBeep(BEEP_880HZ_60MS_DOUBLE_BEEP);
|
||||
AUDIO_PlayBeep(BEEP_880HZ_60MS_TRIPLE_BEEP);
|
||||
AUDIO_PlayBeep(BEEP_880HZ_60MS_TRIPLE_BEEP);
|
||||
}
|
||||
|
||||
gK5startup = false;
|
||||
}
|
||||
|
||||
void BACKLIGHT_UpdateTickless(void) {
|
||||
while(gUpdateBacklight) {
|
||||
BACKLIGHT_Update();
|
||||
SYSTEM_DelayMs(10);
|
||||
}
|
||||
}
|
||||
|
||||
void BACKLIGHT_TurnOn(void)
|
||||
{
|
||||
@@ -134,27 +153,23 @@ void BACKLIGHT_TurnOn(void)
|
||||
|
||||
backlightOn = true;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
if(gK5startup == true) {
|
||||
#if defined(ENABLE_FMRADIO) && defined(ENABLE_SPECTRUM)
|
||||
BACKLIGHT_SetBrightness(gEeprom.BACKLIGHT_MAX);
|
||||
#else
|
||||
for(uint8_t i = 0; i <= gEeprom.BACKLIGHT_MAX; i++)
|
||||
{
|
||||
BACKLIGHT_SetBrightness(i);
|
||||
SYSTEM_DelayMs(50);
|
||||
}
|
||||
#endif
|
||||
|
||||
BACKLIGHT_Sound();
|
||||
}
|
||||
else
|
||||
{
|
||||
BACKLIGHT_SetBrightness(gEeprom.BACKLIGHT_MAX);
|
||||
}
|
||||
#else
|
||||
BACKLIGHT_SetBrightness(gEeprom.BACKLIGHT_MAX);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
if(gK5startup == true)
|
||||
#else
|
||||
static bool startup = true;
|
||||
|
||||
if(startup)
|
||||
#endif
|
||||
{
|
||||
BACKLIGHT_UpdateTickless();
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
BACKLIGHT_Sound();
|
||||
#else
|
||||
startup = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
switch (gEeprom.BACKLIGHT_TIME) {
|
||||
default:
|
||||
@@ -190,18 +205,11 @@ bool BACKLIGHT_IsOn()
|
||||
return backlightOn;
|
||||
}
|
||||
|
||||
static uint8_t currentBrightness = 0;
|
||||
|
||||
void BACKLIGHT_SetBrightness(uint8_t brigtness)
|
||||
static void BACKLIGHT_SetHardwareBrightness(uint8_t brightness)
|
||||
{
|
||||
// printf("BL: %d\n", brigtness);
|
||||
|
||||
if (currentBrightness == brigtness)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (0 == brigtness)
|
||||
if (0 == brightness)
|
||||
{
|
||||
LL_TIM_DisableCounter(TIMx);
|
||||
LL_DMA_DisableChannel(DMA1, DMA_CHANNEL);
|
||||
@@ -210,7 +218,7 @@ void BACKLIGHT_SetBrightness(uint8_t brigtness)
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint32_t level = (uint32_t)(value[brigtness]) * DUTY_CYCLE_LEVELS / 255;
|
||||
const uint32_t level = (uint32_t)(brightness) * DUTY_CYCLE_LEVELS / 255;
|
||||
if (level >= DUTY_CYCLE_LEVELS)
|
||||
{
|
||||
LL_TIM_DisableCounter(TIMx);
|
||||
@@ -231,11 +239,45 @@ void BACKLIGHT_SetBrightness(uint8_t brigtness)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentBrightness = brigtness;
|
||||
void BACKLIGHT_Update(void)
|
||||
{
|
||||
if (gUpdateBacklight) {
|
||||
currentBrightness += fadeStep;
|
||||
|
||||
if ((fadeStep > 0 && currentBrightness >= targetBrightness) ||
|
||||
(fadeStep < 0 && currentBrightness <= targetBrightness)) {
|
||||
currentBrightness = targetBrightness;
|
||||
gUpdateBacklight = false;
|
||||
}
|
||||
|
||||
BACKLIGHT_SetHardwareBrightness((uint8_t)currentBrightness);
|
||||
}
|
||||
}
|
||||
|
||||
void BACKLIGHT_SetBrightness(uint8_t targetIndex)
|
||||
{
|
||||
if (currentIndex == targetIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentIndex = targetIndex;
|
||||
targetBrightness = value[targetIndex];
|
||||
|
||||
int16_t diff = targetBrightness - currentBrightness;
|
||||
|
||||
if (diff == 0) {
|
||||
gUpdateBacklight = false;
|
||||
return;
|
||||
}
|
||||
|
||||
fadeStep = diff > 0 ? -(-diff >> 4) : diff >> 4;
|
||||
|
||||
gUpdateBacklight = true;
|
||||
}
|
||||
|
||||
uint8_t BACKLIGHT_GetBrightness(void)
|
||||
{
|
||||
return currentBrightness;
|
||||
return currentIndex;
|
||||
}
|
||||
@@ -40,10 +40,12 @@ typedef enum {
|
||||
#endif
|
||||
|
||||
void BACKLIGHT_InitHardware();
|
||||
void BACKLIGHT_UpdateTickless(void);
|
||||
void BACKLIGHT_TurnOn();
|
||||
void BACKLIGHT_TurnOff();
|
||||
bool BACKLIGHT_IsOn();
|
||||
void BACKLIGHT_SetBrightness(uint8_t brigtness);
|
||||
void BACKLIGHT_Update(void);
|
||||
uint8_t BACKLIGHT_GetBrightness(void);
|
||||
|
||||
#endif
|
||||
+8
-2
@@ -105,7 +105,9 @@ void BK4819_SetCompander(const unsigned int mode);
|
||||
void BK4819_DisableVox(void);
|
||||
void BK4819_DisableDTMF(void);
|
||||
void BK4819_EnableDTMF(void);
|
||||
void BK4819_PrepareToPlayTone(bool bTuningGainSwitch);
|
||||
void BK4819_PlayTone(uint16_t Frequency, bool bTuningGainSwitch);
|
||||
void BK4819_PlayToneRaw(const unsigned int tone_Hz, const unsigned int delay);
|
||||
void BK4819_PlaySingleTone(const unsigned int tone_Hz, const unsigned int delay, const unsigned int level, const bool play_speaker);
|
||||
void BK4819_EnterTxMute(void);
|
||||
void BK4819_ExitTxMute(void);
|
||||
@@ -116,6 +118,10 @@ void BK4819_TurnsOffTones_TurnsOnRX(void);
|
||||
#endif
|
||||
void BK4819_ResetFSK(void);
|
||||
void BK4819_Idle(void);
|
||||
#ifdef ENABLE_BYP_RAW_DEMODULATORS
|
||||
void BK4819_EnterBypass(void);
|
||||
void BK4819_EnterRaw(void);
|
||||
#endif
|
||||
void BK4819_ExitBypass(void);
|
||||
void BK4819_PrepareTransmit(void);
|
||||
void BK4819_TxOn_Beep(void);
|
||||
@@ -143,11 +149,11 @@ uint8_t BK4819_GetGlitchIndicator(void);
|
||||
uint8_t BK4819_GetExNoiceIndicator(void);
|
||||
uint16_t BK4819_GetVoiceAmplitudeOut(void);
|
||||
uint8_t BK4819_GetAfTxRx(void);
|
||||
void BK4819_SetRxAudioGain(void);
|
||||
|
||||
bool BK4819_GetFrequencyScanResult(uint32_t *pFrequency);
|
||||
BK4819_CssScanResult_t BK4819_GetCxCSSScanResult(uint32_t *pCdcssFreq, uint16_t *pCtcssFreq);
|
||||
void BK4819_DisableFrequencyScan(void);
|
||||
void BK4819_EnableFrequencyScan(void);
|
||||
void BK4819_SetFrequencyScan(bool enable);
|
||||
void BK4819_SetScanFrequency(uint32_t Frequency);
|
||||
|
||||
void BK4819_Disable(void);
|
||||
|
||||
+219
-232
@@ -39,6 +39,16 @@ static const uint16_t FSK_RogerTable[7] = {0xF1A2, 0x7446, 0x61A4, 0x6544, 0x4E8
|
||||
|
||||
static uint16_t gBK4819_GpioOutState;
|
||||
|
||||
#define SHORT_DELAY() \
|
||||
__asm volatile("nop\n nop\n nop\n nop\n nop\n" \
|
||||
"nop\n nop\n nop\n nop\n nop\n" \
|
||||
"nop\n nop\n nop\n nop\n nop\n" \
|
||||
"nop\n nop\n nop\n nop\n nop\n" \
|
||||
"nop\n nop\n nop\n nop\n nop\n" \
|
||||
"nop\n nop\n nop\n nop\n nop\n" \
|
||||
"nop\n nop\n nop\n nop\n nop\n" \
|
||||
"nop\n nop\n nop\n nop\n nop\n")
|
||||
|
||||
bool gRxIdleMode;
|
||||
|
||||
static inline void CS_Assert()
|
||||
@@ -197,63 +207,78 @@ static uint16_t BK4819_ReadU16(void)
|
||||
uint16_t Value;
|
||||
|
||||
SDA_SetDir(false);
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
Value = 0;
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
Value <<= 1;
|
||||
Value |= SDA_ReadInput();
|
||||
SCL_Set();
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
SCL_Reset();
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
}
|
||||
SDA_SetDir(true);
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
static uint16_t reg_30_cache = 0xFFFF;
|
||||
static uint16_t reg_47_cache = 0xFFFF;
|
||||
|
||||
uint16_t BK4819_ReadRegister(BK4819_REGISTER_t Register)
|
||||
{
|
||||
uint16_t Value;
|
||||
|
||||
CS_Release();
|
||||
SCL_Reset();
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
|
||||
CS_Assert();
|
||||
BK4819_WriteU8(Register | 0x80);
|
||||
Value = BK4819_ReadU16();
|
||||
CS_Release();
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
|
||||
SCL_Set();
|
||||
SDA_Set();
|
||||
|
||||
if (Register == BK4819_REG_30)
|
||||
reg_30_cache = Value;
|
||||
else if (Register == BK4819_REG_47)
|
||||
reg_47_cache = Value;
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
void BK4819_WriteRegister(BK4819_REGISTER_t Register, uint16_t Data)
|
||||
{
|
||||
if (Register == BK4819_REG_30)
|
||||
{
|
||||
if (Data == reg_30_cache)
|
||||
return;
|
||||
reg_30_cache = Data;
|
||||
}
|
||||
else if (Register == BK4819_REG_47)
|
||||
{
|
||||
if (Data == reg_47_cache)
|
||||
return;
|
||||
reg_47_cache = Data;
|
||||
}
|
||||
|
||||
CS_Release();
|
||||
SCL_Reset();
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
|
||||
CS_Assert();
|
||||
BK4819_WriteU8(Register);
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
|
||||
BK4819_WriteU16(Data);
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
|
||||
CS_Release();
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
|
||||
SCL_Set();
|
||||
SDA_Set();
|
||||
@@ -271,14 +296,14 @@ void BK4819_WriteU8(uint8_t Data)
|
||||
else
|
||||
SDA_Set();
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
SCL_Set();
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
|
||||
Data <<= 1;
|
||||
|
||||
SCL_Reset();
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,14 +319,14 @@ void BK4819_WriteU16(uint16_t Data)
|
||||
else
|
||||
SDA_Set();
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
SCL_Set();
|
||||
|
||||
Data <<= 1;
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
SCL_Reset();
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,45 +359,9 @@ void BK4819_SetAGC(bool enable)
|
||||
|
||||
void BK4819_InitAGC(bool amModulation)
|
||||
{
|
||||
// REG_10, REG_11, REG_12 REG_13, REG_14
|
||||
//
|
||||
// Rx AGC Gain Table[]. (Index Max->Min is 3,2,1,0,-1)
|
||||
//
|
||||
// <15:10> ???
|
||||
//
|
||||
// <9:8> LNA Gain Short
|
||||
// 3 = 0dB <<< 1o11 read from spectrum reference manual
|
||||
// 2 = -24dB -19 -11
|
||||
// 1 = -30dB -24 -16
|
||||
// 0 = -33dB -28 -19
|
||||
//
|
||||
// <7:5> LNA Gain
|
||||
// 7 = 0dB
|
||||
// 6 = -2dB
|
||||
// 5 = -4dB
|
||||
// 4 = -6dB
|
||||
// 3 = -9dB
|
||||
// 2 = -14dB <<<
|
||||
// 1 = -19dB
|
||||
// 0 = -24dB
|
||||
//
|
||||
// <4:3> MIXER Gain
|
||||
// 3 = 0dB <<<
|
||||
// 2 = -3dB
|
||||
// 1 = -6dB
|
||||
// 0 = -8dB
|
||||
//
|
||||
// <2:0> PGA Gain
|
||||
// 7 = 0dB
|
||||
// 6 = -3dB <<<
|
||||
// 5 = -6dB
|
||||
// 4 = -9dB
|
||||
// 3 = -15dB
|
||||
// 2 = -21dB
|
||||
// 1 = -27dB
|
||||
// 0 = -33dB
|
||||
//
|
||||
|
||||
/*
|
||||
// Old strategy
|
||||
BK4819_WriteRegister(BK4819_REG_13, 0x03BE); // 0x03BE / 000000 11 101 11 110 / -7dB
|
||||
BK4819_WriteRegister(BK4819_REG_12, 0x037B); // 0x037B / 000000 11 011 11 011 / -24dB
|
||||
BK4819_WriteRegister(BK4819_REG_11, 0x027B); // 0x027B / 000000 10 011 11 011 / -43dB
|
||||
@@ -387,7 +376,21 @@ void BK4819_InitAGC(bool amModulation)
|
||||
}
|
||||
|
||||
BK4819_WriteRegister(BK4819_REG_7B, 0x8420);
|
||||
*/
|
||||
|
||||
// New strategy
|
||||
// Keep the runtime AGC profile aligned with the stock BK4829 firmware
|
||||
// until modulation-specific AGC changes are proven on hardware.
|
||||
|
||||
(void)amModulation;
|
||||
|
||||
BK4819_WriteRegister(BK4819_REG_10, 0x0318);
|
||||
BK4819_WriteRegister(BK4819_REG_11, 0x033A);
|
||||
BK4819_WriteRegister(BK4819_REG_12, 0x03DB);
|
||||
BK4819_WriteRegister(BK4819_REG_13, 0x03DF);
|
||||
BK4819_WriteRegister(BK4819_REG_14, 0x0210);
|
||||
BK4819_WriteRegister(BK4819_REG_49, 0x2AB2);
|
||||
BK4819_WriteRegister(BK4819_REG_7B, 0x73DC);
|
||||
}
|
||||
|
||||
int8_t BK4819_GetRxGain_dB(void)
|
||||
@@ -625,100 +628,91 @@ void BK4819_EnableVox(uint16_t VoxEnableThreshold, uint16_t VoxDisableThreshold)
|
||||
|
||||
void BK4819_SetFilterBandwidth(const BK4819_FilterBandwidth_t Bandwidth, const bool weak_no_different)
|
||||
{
|
||||
// REG_43
|
||||
// <15> 0 ???
|
||||
(void)weak_no_different;
|
||||
|
||||
// REG_43 on BK4829, per BK4829 Registers Table.
|
||||
//
|
||||
// <14:12> 4 RF filter bandwidth
|
||||
// 0 = 1.7 kHz
|
||||
// 1 = 2.0 kHz
|
||||
// 2 = 2.5 kHz
|
||||
// 3 = 3.0 kHz
|
||||
// 4 = 3.75 kHz
|
||||
// 5 = 4.0 kHz
|
||||
// 6 = 4.25 kHz
|
||||
// 7 = 4.5 kHz
|
||||
// if <5> == 1, RF filter bandwidth * 2
|
||||
// <14:12> RF filter bandwidth (Apass = 0.1dB)
|
||||
// 000 = 2.0 kHz
|
||||
// 001 = 2.5 kHz
|
||||
// 010 = 3.0 kHz
|
||||
// 011 = 3.5 kHz
|
||||
// 100 = 4.0 kHz
|
||||
// 101 = 4.5 kHz
|
||||
// 110 = 5.0 kHz
|
||||
// 111 = 5.5 kHz
|
||||
// if REG_43<5> == 1, RF filter bandwidth *= 2
|
||||
//
|
||||
// <11:9> 0 RF filter bandwidth when signal is weak
|
||||
// 0 = 1.7 kHz
|
||||
// 1 = 2.0 kHz
|
||||
// 2 = 2.5 kHz
|
||||
// 3 = 3.0 kHz
|
||||
// 4 = 3.75 kHz
|
||||
// 5 = 4.0 kHz
|
||||
// 6 = 4.25 kHz
|
||||
// 7 = 4.5 kHz
|
||||
// if <5> == 1, RF filter bandwidth * 2
|
||||
// <11:9> RF filter bandwidth when signal is weak (Apass = 0.1dB)
|
||||
// 000 = 2.0 kHz
|
||||
// 001 = 2.5 kHz
|
||||
// 010 = 3.0 kHz
|
||||
// 011 = 3.5 kHz
|
||||
// 100 = 4.0 kHz
|
||||
// 101 = 4.5 kHz
|
||||
// 110 = 5.0 kHz
|
||||
// 111 = 5.5 kHz
|
||||
// if REG_43<5> == 1, RF filter bandwidth *= 2
|
||||
//
|
||||
// <8:6> 1 AFTxLPF2 filter Band Width
|
||||
// 1 = 2.5 kHz (for 12.5k channel space)
|
||||
// 2 = 2.75 kHz
|
||||
// 0 = 3.0 kHz (for 25k channel space)
|
||||
// 3 = 3.5 kHz
|
||||
// 4 = 4.5 kHz
|
||||
// 5 = 4.25 kHz
|
||||
// 6 = 4.0 kHz
|
||||
// 7 = 3.75 kHz
|
||||
// <8:6> AF Tx LPF2 filter bandwidth (Apass = 1dB)
|
||||
// 100 = 5.5 kHz
|
||||
// 101 = 5.0 kHz
|
||||
// 110 = 4.5 kHz
|
||||
// 111 = 4.0 kHz
|
||||
// 000 = 3.0 kHz
|
||||
// 001 = 2.5 kHz
|
||||
// 010 = 2.75 kHz
|
||||
// 011 = 3.5 kHz
|
||||
//
|
||||
// <5:4> 0 BW Mode Selection
|
||||
// 0 = 12.5k
|
||||
// 1 = 6.25k
|
||||
// 2 = 25k/20k
|
||||
// <5:4> BW mode selection
|
||||
// 00 = 12.5k
|
||||
// 01 = 6.25k
|
||||
// 10 = 25k/20k
|
||||
//
|
||||
// <3> 1 ???
|
||||
// <2> gain after FM demodulation
|
||||
// 0 = 0 dB
|
||||
// 1 = 6 dB
|
||||
//
|
||||
// <2> 0 Gain after FM Demodulation
|
||||
// 0 = 0dB
|
||||
// 1 = 6dB
|
||||
//
|
||||
// <1:0> 0 ???
|
||||
// <3> and <1:0> remain undocumented here.
|
||||
|
||||
uint16_t val = 0;
|
||||
switch (Bandwidth)
|
||||
{
|
||||
case BK4819_FILTER_BW_WIDE: // 25kHz
|
||||
// if (weak_no_different) {
|
||||
// // make the RX bandwidth the same with weak signals
|
||||
// val = 0x3628; // Old value 0x49a8 < v3.6
|
||||
// } else {
|
||||
// // with weak RX signals the RX bandwidth is reduced
|
||||
// val = 0x3428; // Old value 0x45a8 < v3.6
|
||||
// }
|
||||
// 0x3028 = (0b011 << 12) | (0b000 << 9) | (0b000 << 6) |
|
||||
// (0b10 << 4) | (1 << 3)
|
||||
// = RF 3.5 kHz and weak-RF 2.0 kHz, both doubled because
|
||||
// bit<5> is set in 25k/20k mode => RF 7.0 kHz, weak-RF
|
||||
// 4.0 kHz, AF Tx LPF2 3.0 kHz, FM gain 0 dB.
|
||||
val = 0x3028;
|
||||
break;
|
||||
|
||||
case BK4819_FILTER_BW_NARROW: // 12.5kHz
|
||||
// if (weak_no_different) {
|
||||
// // make the RX bandwidth the same with weak signals
|
||||
// val = 0x3648; // Old value 0x4808 < v3.6
|
||||
// } else {
|
||||
// // with weak RX signals the RX bandwidth is reduced
|
||||
// val = 0x3448; // Old value 0x4408 < v3.6
|
||||
// }
|
||||
// 0x4048 = (0b100 << 12) | (0b000 << 9) | (0b001 << 6) |
|
||||
// (0b00 << 4) | (1 << 3)
|
||||
// = RF 4.0 kHz, weak-RF 2.0 kHz, AF Tx LPF2 2.5 kHz,
|
||||
// 12.5k mode, FM gain 0 dB.
|
||||
val = 0x4048;
|
||||
break;
|
||||
|
||||
case BK4819_FILTER_BW_NARROWER: // 6.25kHz
|
||||
// if (weak_no_different) {
|
||||
// // make the RX bandwidth the same with weak signals
|
||||
// val = 0x1348; // Old value 0x3658 < v3.6
|
||||
// } else {
|
||||
// // with weak RX signals the RX bandwidth is reduced
|
||||
// val = 0x1148; // Old value 0x3658 < v3.6
|
||||
// }
|
||||
// 0x205C = (0b010 << 12) | (0b000 << 9) | (0b001 << 6) |
|
||||
// (0b01 << 4) | (1 << 3) | (1 << 2)
|
||||
// = RF 3.0 kHz, weak-RF 2.0 kHz, AF Tx LPF2 2.5 kHz,
|
||||
// 6.25k mode, FM gain +6 dB.
|
||||
val = 0x205C;
|
||||
break;
|
||||
|
||||
case BK4819_FILTER_BW_AM: // 8.33kHz
|
||||
// if (weak_no_different) {
|
||||
// // make the RX bandwidth the same with weak signals
|
||||
// val = 0x4858;
|
||||
// } else {
|
||||
// // with weak RX signals the RX bandwidth is reduced
|
||||
// val = 0x4458;
|
||||
// }
|
||||
case BK4819_FILTER_BW_AM: // Stock AM preset
|
||||
// 0x345C = (0b011 << 12) | (0b010 << 9) | (0b001 << 6) |
|
||||
// (0b01 << 4) | (1 << 3) | (1 << 2)
|
||||
// = RF 3.5 kHz, weak-RF 3.0 kHz, AF Tx LPF2 2.5 kHz,
|
||||
// 6.25k mode, bit<2> set. The "8.33kHz AM" label used in
|
||||
// the codebase is functional/empirical; it does not come
|
||||
// directly from the BW mode bits of REG_43.
|
||||
val = 0x345C;
|
||||
break;
|
||||
|
||||
default:
|
||||
val = 0x5C;
|
||||
break;
|
||||
@@ -797,21 +791,20 @@ void BK4819_SetupSquelch(
|
||||
// <13:11> 5 Squelch = open Delay Setting
|
||||
// 0 ~ 7
|
||||
//
|
||||
// <10:9> 7 Squelch = close Delay Setting
|
||||
// <10:9> 3 Squelch = close Delay Setting
|
||||
// 0 ~ 3
|
||||
//
|
||||
// <8> 0 ???
|
||||
// <8> 1 ???
|
||||
//
|
||||
// <7:0> 8 Glitch threshold for Squelch = open
|
||||
// 0 ~ 255
|
||||
//
|
||||
BK4819_WriteRegister(BK4819_REG_4E, // 01 101 11 1 00000000
|
||||
|
||||
// original (*)
|
||||
(1u << 14) | // 1 ???
|
||||
(5u << 11) | // *5 squelch = open delay .. 0 ~ 7
|
||||
(6u << 9) | // *3 squelch = close delay .. 0 ~ 3
|
||||
SquelchOpenGlitchThresh); // 0 ~ 255
|
||||
BK4819_WriteRegister(BK4819_REG_4E,
|
||||
(1u << 14) | // 1 ???
|
||||
(5u << 11) | // 5 squelch = open delay .. 0 ~ 7
|
||||
(3u << 9) | // 3 squelch = close delay .. 0 ~ 3
|
||||
(1u << 8) | // 1 ??? (matches stock BK4829)
|
||||
SquelchOpenGlitchThresh); // 0 ~ 255
|
||||
|
||||
|
||||
// REG_4F
|
||||
@@ -1038,7 +1031,7 @@ void BK4819_EnableDTMF(void)
|
||||
(15u << BK4819_REG_24_SHIFT_MAX_SYMBOLS)); // 0 ~ 15
|
||||
}
|
||||
|
||||
void BK4819_PlayTone(uint16_t Frequency, bool bTuningGainSwitch)
|
||||
void BK4819_PrepareToPlayTone(bool bTuningGainSwitch)
|
||||
{
|
||||
uint16_t ToneConfig = BK4819_REG_70_ENABLE_TONE1;
|
||||
|
||||
@@ -1053,10 +1046,23 @@ void BK4819_PlayTone(uint16_t Frequency, bool bTuningGainSwitch)
|
||||
|
||||
BK4819_WriteRegister(BK4819_REG_30, 0);
|
||||
BK4819_WriteRegister(BK4819_REG_30, BK4819_REG_30_ENABLE_AF_DAC | BK4819_REG_30_ENABLE_DISC_MODE | BK4819_REG_30_ENABLE_TX_DSP);
|
||||
}
|
||||
|
||||
void BK4819_PlayTone(uint16_t Frequency, bool bTuningGainSwitch)
|
||||
{
|
||||
BK4819_PrepareToPlayTone(bTuningGainSwitch);
|
||||
|
||||
BK4819_WriteRegister(BK4819_REG_71, scale_freq(Frequency));
|
||||
}
|
||||
|
||||
void BK4819_PlayToneRaw(const unsigned int tone_Hz, const unsigned int delay) {
|
||||
BK4819_WriteRegister(BK4819_REG_71, scale_freq(tone_Hz));
|
||||
|
||||
BK4819_ExitTxMute();
|
||||
SYSTEM_DelayMs(delay);
|
||||
BK4819_EnterTxMute();
|
||||
}
|
||||
|
||||
// level 0 ~ 127
|
||||
void BK4819_PlaySingleTone(const unsigned int tone_Hz, const unsigned int delay, const unsigned int level, const bool play_speaker)
|
||||
{
|
||||
@@ -1076,11 +1082,7 @@ void BK4819_PlaySingleTone(const unsigned int tone_Hz, const unsigned int delay,
|
||||
BK4819_EnableTXLink();
|
||||
SYSTEM_DelayMs(50);
|
||||
|
||||
BK4819_WriteRegister(BK4819_REG_71, scale_freq(tone_Hz));
|
||||
|
||||
BK4819_ExitTxMute();
|
||||
SYSTEM_DelayMs(delay);
|
||||
BK4819_EnterTxMute();
|
||||
BK4819_PlayToneRaw(tone_Hz, delay);
|
||||
|
||||
if (play_speaker)
|
||||
{
|
||||
@@ -1154,45 +1156,62 @@ void BK4819_Idle(void)
|
||||
BK4819_WriteRegister(BK4819_REG_30, 0x0000);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_BYP_RAW_DEMODULATORS
|
||||
void BK4819_EnterBypass(void)
|
||||
{
|
||||
// Keep AF output on normal path (REG_47 mode 0x1 is documented on BK4829).
|
||||
BK4819_SetAF(BK4819_AF_FM);
|
||||
|
||||
// Bypass all AF filters (Rx + Tx) as recommended for digital bypass mode.
|
||||
// REG_2B:
|
||||
// bit10: Disable AF Rx HPF300
|
||||
// bit 9: Disable AF Rx LPF3K
|
||||
// bit 8: Disable AF Rx de-emphasis
|
||||
// bit 2: Disable AF Tx HPF300
|
||||
// bit 1: Disable AF Tx LPF1
|
||||
// bit 0: Disable AF Tx pre-emphasis
|
||||
uint16_t reg2b = BK4819_ReadRegister(BK4819_REG_2B);
|
||||
reg2b |= (1u << 10) | (1u << 9) | (1u << 8) | (1u << 2) | (1u << 1) | (1u << 0);
|
||||
BK4819_WriteRegister(BK4819_REG_2B, reg2b);
|
||||
|
||||
// Keep AGC/AFC behavior aligned with FM-like bypass experiments.
|
||||
BK4819_SetRegValue(afcDisableRegSpec, false);
|
||||
}
|
||||
|
||||
void BK4819_EnterRaw(void)
|
||||
{
|
||||
// Keep AF output on a documented mode.
|
||||
BK4819_SetAF(BK4819_AF_FM);
|
||||
|
||||
// RAW profile: bypass RX AF filters only, keep TX filter path unchanged.
|
||||
// REG_2B:
|
||||
// bit10: Disable AF Rx HPF300
|
||||
// bit 9: Disable AF Rx LPF3K
|
||||
// bit 8: Disable AF Rx de-emphasis
|
||||
uint16_t reg2b = BK4819_ReadRegister(BK4819_REG_2B);
|
||||
reg2b |= (1u << 10) | (1u << 9) | (1u << 8);
|
||||
reg2b &= ~((1u << 2) | (1u << 1) | (1u << 0));
|
||||
BK4819_WriteRegister(BK4819_REG_2B, reg2b);
|
||||
|
||||
// RAW profile keeps AFC disabled to preserve discriminator-like behavior.
|
||||
BK4819_SetRegValue(afcDisableRegSpec, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
void BK4819_ExitBypass(void)
|
||||
{
|
||||
BK4819_SetAF(BK4819_AF_MUTE);
|
||||
|
||||
// REG_7E
|
||||
//
|
||||
// <15> 0 AGC fix mode
|
||||
// 1 = fix
|
||||
// 0 = auto
|
||||
//
|
||||
// <14:12> 3 AGC fix index
|
||||
// 3 ( 3) = max
|
||||
// 2 ( 2)
|
||||
// 1 ( 1)
|
||||
// 0 ( 0)
|
||||
// 7 (-1)
|
||||
// 6 (-2)
|
||||
// 5 (-3)
|
||||
// 4 (-4) = min
|
||||
//
|
||||
// <11:6> 0 ???
|
||||
//
|
||||
// <5:3> 5 DC filter band width for Tx (MIC In)
|
||||
// 0 ~ 7
|
||||
// 0 = bypass DC filter
|
||||
//
|
||||
// <2:0> 6 DC filter band width for Rx (I.F In)
|
||||
// 0 ~ 7
|
||||
// 0 = bypass DC filter
|
||||
//
|
||||
#ifdef ENABLE_BYP_RAW_DEMODULATORS
|
||||
// Restore normal AF filters.
|
||||
uint16_t reg2b = BK4819_ReadRegister(BK4819_REG_2B);
|
||||
reg2b &= ~((1u << 10) | (1u << 9) | (1u << 8) | (1u << 2) | (1u << 1) | (1u << 0));
|
||||
BK4819_WriteRegister(BK4819_REG_2B, reg2b);
|
||||
#endif
|
||||
|
||||
// Keep existing REG_7E logic from your current implementation.
|
||||
uint16_t regVal = BK4819_ReadRegister(BK4819_REG_7E);
|
||||
|
||||
// 0x302E / 0 011 000000 101 110
|
||||
BK4819_WriteRegister(BK4819_REG_7E, (regVal & ~(0b111 << 3))
|
||||
|
||||
| (5u << 3) // 5 DC Filter band width for Tx (MIC In)
|
||||
|
||||
);
|
||||
BK4819_WriteRegister(BK4819_REG_7E, (regVal & ~(0b111 << 3)) | (5u << 3));
|
||||
}
|
||||
|
||||
void BK4819_PrepareTransmit(void)
|
||||
@@ -1447,24 +1466,15 @@ void BK4819_GenTail(uint8_t Tail)
|
||||
// freq(Hz) * 20.64888 for XTAL 13M/26M or
|
||||
// freq(Hz)*20.97152 for XTAL 12.8M/19.2M/25.6M/38.4M
|
||||
|
||||
switch (Tail)
|
||||
{
|
||||
case 0: // 134.4Hz CTCSS Tail
|
||||
BK4819_WriteRegister(BK4819_REG_52, 0x828F); // 1 00 0 001010 001111
|
||||
break;
|
||||
case 1: // 120° phase shift
|
||||
BK4819_WriteRegister(BK4819_REG_52, 0xA28F); // 1 01 0 001010 001111
|
||||
break;
|
||||
case 2: // 180° phase shift
|
||||
BK4819_WriteRegister(BK4819_REG_52, 0xC28F); // 1 10 0 001010 001111
|
||||
break;
|
||||
case 3: // 240° phase shift
|
||||
BK4819_WriteRegister(BK4819_REG_52, 0xE28F); // 1 11 0 001010 001111
|
||||
break;
|
||||
case 4: // 55Hz tone freq
|
||||
BK4819_WriteRegister(BK4819_REG_07, 0x046f); // 0 00 0 010001 101111
|
||||
break;
|
||||
}
|
||||
if (Tail <= 3)
|
||||
// 0: 134.4Hz CTCSS Tail
|
||||
// 1: 120° phase shift
|
||||
// 2: 180° phase shift
|
||||
// 3: 240° phase shift
|
||||
BK4819_WriteRegister(BK4819_REG_52, 0x028F | (Tail << 13));
|
||||
else if (Tail == 4)
|
||||
// 4: 55Hz tone freq
|
||||
BK4819_WriteRegister(BK4819_REG_07, 0x046F);
|
||||
}
|
||||
|
||||
void BK4819_PlayCDCSSTail(void)
|
||||
@@ -1546,6 +1556,14 @@ uint8_t BK4819_GetAfTxRx(void)
|
||||
return BK4819_ReadRegister(BK4819_REG_6F) & 0x003F;
|
||||
}
|
||||
|
||||
void BK4819_SetRxAudioGain(void) {
|
||||
BK4819_WriteRegister(BK4819_REG_48,
|
||||
(11u << 12) | // ??? .. 0 ~ 15, doesn't seem to make any difference
|
||||
( 0u << 10) | // AF Rx Gain-1
|
||||
(gEeprom.VOLUME_GAIN << 4) | // AF Rx Gain-2
|
||||
(gEeprom.DAC_GAIN << 0)); // AF DAC Gain (after Gain-1 and Gain-2)
|
||||
}
|
||||
|
||||
bool BK4819_GetFrequencyScanResult(uint32_t *pFrequency)
|
||||
{
|
||||
const uint16_t High = BK4819_ReadRegister(BK4819_REG_0D);
|
||||
@@ -1581,7 +1599,7 @@ BK4819_CssScanResult_t BK4819_GetCxCSSScanResult(uint32_t *pCdcssFreq, uint16_t
|
||||
return BK4819_CSS_RESULT_NOT_FOUND;
|
||||
}
|
||||
|
||||
void BK4819_DisableFrequencyScan(void)
|
||||
void BK4819_SetFrequencyScan(bool enable)
|
||||
{
|
||||
// REG_32
|
||||
//
|
||||
@@ -1597,32 +1615,10 @@ void BK4819_DisableFrequencyScan(void)
|
||||
// 1 = enable
|
||||
// 0 = disable
|
||||
//
|
||||
BK4819_WriteRegister(BK4819_REG_32, // 0x0244); // 00 0000100100010 0
|
||||
BK4819_WriteRegister(BK4819_REG_32,
|
||||
( 0u << 14) | // 0 frequency scan Time
|
||||
(290u << 1) | // ???
|
||||
( 0u << 0)); // 0 frequency scan enable
|
||||
}
|
||||
|
||||
void BK4819_EnableFrequencyScan(void)
|
||||
{
|
||||
// REG_32
|
||||
//
|
||||
// <15:14> 0 frequency scan time
|
||||
// 0 = 0.2 sec
|
||||
// 1 = 0.4 sec
|
||||
// 2 = 0.8 sec
|
||||
// 3 = 1.6 sec
|
||||
//
|
||||
// <13:1> ???
|
||||
//
|
||||
// <0> 0 frequency scan enable
|
||||
// 1 = enable
|
||||
// 0 = disable
|
||||
//
|
||||
BK4819_WriteRegister(BK4819_REG_32, // 0x0245); // 00 0000100100010 1
|
||||
( 0u << 14) | // 0 frequency scan time
|
||||
(290u << 1) | // ???
|
||||
( 1u << 0)); // 1 frequency scan enable
|
||||
(enable ? 1u : 0u)); // frequency scan (1: enable | 0: disable)
|
||||
}
|
||||
|
||||
void BK4819_SetScanFrequency(uint32_t Frequency)
|
||||
@@ -1686,7 +1682,7 @@ void BK4819_Disable(void)
|
||||
|
||||
void BK4819_StopScan(void)
|
||||
{
|
||||
BK4819_DisableFrequencyScan();
|
||||
BK4819_SetFrequencyScan(false);
|
||||
BK4819_Disable();
|
||||
}
|
||||
|
||||
@@ -1780,17 +1776,8 @@ static void BK4819_PlayRogerNormal(void)
|
||||
BK4819_EnableTXLink();
|
||||
SYSTEM_DelayMs(50);
|
||||
|
||||
BK4819_WriteRegister(BK4819_REG_71, scale_freq(tone1_Hz));
|
||||
|
||||
BK4819_ExitTxMute();
|
||||
SYSTEM_DelayMs(80);
|
||||
BK4819_EnterTxMute();
|
||||
|
||||
BK4819_WriteRegister(BK4819_REG_71, scale_freq(tone2_Hz));
|
||||
|
||||
BK4819_ExitTxMute();
|
||||
SYSTEM_DelayMs(80);
|
||||
BK4819_EnterTxMute();
|
||||
BK4819_PlayToneRaw(tone1_Hz, 80);
|
||||
BK4819_PlayToneRaw(tone2_Hz, 80);
|
||||
|
||||
BK4819_WriteRegister(BK4819_REG_70, 0x0000);
|
||||
BK4819_WriteRegister(BK4819_REG_30, 0xC1FE); // 1 1 0000 0 1 1111 1 1 1 0
|
||||
|
||||
@@ -59,13 +59,19 @@ static const AddrMapping_t ADDR_MAPPINGS[] = {
|
||||
// MR FM * 128 Bytes (0x003000) 0x00A028 -> 0x00A0A8
|
||||
// Settings * 80 Bytes (0x007000) 0x00A0A8 -> 0x00A0F8
|
||||
// Settings * 56 Bytes (0x008000) 0x00A0F8 -> 0x00A130
|
||||
// Settings Scanlist * 8 Bytes (0x009000) 0x00A130 -> 0x00A140
|
||||
// Settings AES * 16 Bytes (0x00A000) 0x00A140 -> 0x00A150
|
||||
// Settings Scanlist * 8 Bytes (0x009000) 0x00A130 -> 0x00A138
|
||||
// Settings AES * 16 Bytes (0x00A000) 0x00A138 -> 0x00A148
|
||||
// Settings Spectrum * 8 Bytes 0x00A148 -> 0x00A150
|
||||
// Settings * 8 Bytes (0x00B000) 0x00A150 -> 0x00A158
|
||||
// Settings F4HWN * 8 Bytes (0x00C000) 0x00A158 -> 0x00A160
|
||||
// Settings Version * 16 Bytes 0x00A160 -> 0x00A170
|
||||
|
||||
_MK_MAPPING(0x010000, 0x00B000, 0x00B200), // Calibration 512 Bytes!!!
|
||||
|
||||
_MK_MAPPING(0x011000, 0x00C000, 0x00D000), // Boot Logo sector (4 KB):
|
||||
// [0x00..0x07] 8-byte header (reserved)
|
||||
// [0x08..0x407] 128x64 monochrome bitmap, 1024 Bytes
|
||||
// ST7565-native: 8 pages * 128 columns, column-major LSB-top
|
||||
};
|
||||
|
||||
static void AddrTranslate(uint16_t EEPROM_Addr, uint16_t Size, uint32_t *PY25Q16_Addr_out, uint16_t *Size_out, bool *End_out);
|
||||
|
||||
+67
-10
@@ -36,30 +36,72 @@ bool gWasFKeyPressed = false;
|
||||
// Must exceed key_repeat_delay_10ms (40) to trigger ProcessKey(key, true, true).
|
||||
#define SERIAL_KEY_LONG_POLLS 45
|
||||
|
||||
// Packet types for serial key injection (K5Viewer → radio)
|
||||
#define SERIAL_KEY_TYPE 0x03
|
||||
#define SERIAL_KEY_TYPE_LONG 0x04
|
||||
|
||||
volatile KEY_Code_t gKeyFromSerial = KEY_INVALID;
|
||||
static uint8_t gSerialKeyHoldCount = 0;
|
||||
static uint8_t gSerialKeyLong = 0; // 0 = short press, 1 = long press
|
||||
|
||||
// Inject a short press from serial (UART or VCP).
|
||||
// Inject a short or long press from serial (UART or VCP).
|
||||
// KEY_PTT is explicitly blocked — PTT release cannot be guaranteed over serial.
|
||||
void KEYBOARD_InjectKey(uint8_t keyCode)
|
||||
static inline void KEYBOARD_InjectKey(uint8_t keyCode, bool keyLong)
|
||||
{
|
||||
if (keyCode < KEY_INVALID && keyCode != KEY_PTT) {
|
||||
gKeyFromSerial = (KEY_Code_t)keyCode;
|
||||
gSerialKeyHoldCount = 0;
|
||||
gSerialKeyLong = 0;
|
||||
gSerialKeyLong = keyLong;
|
||||
}
|
||||
}
|
||||
|
||||
// Inject a long press from serial (UART or VCP).
|
||||
// KEY_PTT is explicitly blocked — PTT release cannot be guaranteed over serial.
|
||||
void KEYBOARD_InjectKeyLong(uint8_t keyCode)
|
||||
bool KEYBOARD_ProcessProtocolByte(ParseState_t *state, uint8_t b)
|
||||
{
|
||||
if (keyCode < KEY_INVALID && keyCode != KEY_PTT) {
|
||||
gKeyFromSerial = (KEY_Code_t)keyCode;
|
||||
gSerialKeyHoldCount = 0;
|
||||
gSerialKeyLong = 1;
|
||||
bool connected = false;
|
||||
|
||||
switch (*state)
|
||||
{
|
||||
case STATE_IDLE:
|
||||
if (b == 0x55) *state = STATE_KA_1;
|
||||
else if (b == 0xAA) *state = STATE_KEY_1;
|
||||
break;
|
||||
|
||||
case STATE_KA_1:
|
||||
*state = (b == 0xAA) ? STATE_KA_2 : STATE_IDLE;
|
||||
break;
|
||||
|
||||
case STATE_KA_2:
|
||||
*state = (b == 0x00) ? STATE_KA_3 : STATE_IDLE;
|
||||
break;
|
||||
|
||||
case STATE_KA_3:
|
||||
if (b == 0x00) connected = true;
|
||||
*state = STATE_IDLE;
|
||||
break;
|
||||
|
||||
case STATE_KEY_1:
|
||||
*state = (b == 0x55) ? STATE_KEY_2 : STATE_IDLE;
|
||||
break;
|
||||
|
||||
case STATE_KEY_2:
|
||||
if (b == SERIAL_KEY_TYPE) *state = STATE_KEY_3;
|
||||
else if (b == SERIAL_KEY_TYPE_LONG) *state = STATE_KEY_3L;
|
||||
else *state = STATE_IDLE;
|
||||
break;
|
||||
|
||||
case STATE_KEY_3:
|
||||
case STATE_KEY_3L:
|
||||
KEYBOARD_InjectKey(b, *state == STATE_KEY_3L);
|
||||
connected = true;
|
||||
*state = STATE_IDLE;
|
||||
break;
|
||||
|
||||
default:
|
||||
*state = STATE_IDLE;
|
||||
break;
|
||||
}
|
||||
|
||||
return connected;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -208,4 +250,19 @@ KEY_Code_t KEYBOARD_Poll(void)
|
||||
GPIO_SetOutputPin(PIN_COLS);
|
||||
|
||||
return Key;
|
||||
}
|
||||
|
||||
KEY_Code_t KEYBOARD_GetKey(void)
|
||||
{
|
||||
KEY_Code_t btn = KEYBOARD_Poll();
|
||||
if (btn == KEY_INVALID && GPIO_IsPttPressed())
|
||||
{
|
||||
btn = KEY_PTT;
|
||||
}
|
||||
return btn;
|
||||
}
|
||||
|
||||
void HideFKeyIcon(void) {
|
||||
gWasFKeyPressed = false;
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
+15
-5
@@ -45,6 +45,17 @@ enum KEY_Code_e {
|
||||
};
|
||||
typedef enum KEY_Code_e KEY_Code_t;
|
||||
|
||||
typedef enum {
|
||||
STATE_IDLE = 0,
|
||||
STATE_KA_1,
|
||||
STATE_KA_2,
|
||||
STATE_KA_3,
|
||||
STATE_KEY_1,
|
||||
STATE_KEY_2,
|
||||
STATE_KEY_3,
|
||||
STATE_KEY_3L,
|
||||
} ParseState_t;
|
||||
|
||||
extern KEY_Code_t gKeyReading0;
|
||||
extern KEY_Code_t gKeyReading1;
|
||||
extern uint16_t gDebounceCounter;
|
||||
@@ -54,13 +65,12 @@ extern bool gWasFKeyPressed;
|
||||
// Serial-injected key (written by UART/VCP parser, consumed by KEYBOARD_Poll).
|
||||
extern volatile KEY_Code_t gKeyFromSerial;
|
||||
|
||||
// Inject a short press received from serial (UART or VCP).
|
||||
void KEYBOARD_InjectKey(uint8_t keyCode);
|
||||
|
||||
// Inject a long press received from serial (UART or VCP).
|
||||
void KEYBOARD_InjectKeyLong(uint8_t keyCode);
|
||||
bool KEYBOARD_ProcessProtocolByte(ParseState_t *state, uint8_t b);
|
||||
#endif
|
||||
|
||||
KEY_Code_t KEYBOARD_Poll(void);
|
||||
KEY_Code_t KEYBOARD_GetKey(void);
|
||||
|
||||
void HideFKeyIcon(void);
|
||||
|
||||
#endif
|
||||
+1
-1
@@ -165,7 +165,7 @@ void ST7565_DrawLine(const unsigned int Column, const unsigned int Line, const u
|
||||
{
|
||||
ST7565_BlitScreen(line + 1);
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot(true); // Force immediate capture
|
||||
SCREENSHOT_Update(true); // Force immediate capture
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+14
-37
@@ -25,9 +25,6 @@
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
#include "driver/keyboard.h"
|
||||
// Packet types for serial key injection (K5Viewer → radio)
|
||||
#define UART_TYPE_KEY 0x03
|
||||
#define UART_TYPE_KEY_LONG 0x04
|
||||
#endif
|
||||
|
||||
#define USARTx USART1
|
||||
@@ -155,44 +152,24 @@ void UART_LogSend(const void *pBuffer, uint32_t Size)
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
bool UART_IsCableConnected(void) {
|
||||
static uint8_t read_ptr = 0;
|
||||
static ParseState_t state = STATE_IDLE;
|
||||
|
||||
bool connected = false;
|
||||
|
||||
for (size_t i = 0; i < sizeof(UART_DMA_Buffer); i++) {
|
||||
uint8_t b = UART_DMA_Buffer[i];
|
||||
// DMA write position: NbData counts DOWN from 256
|
||||
uint8_t write_ptr = (uint8_t)(sizeof(UART_DMA_Buffer) -
|
||||
LL_DMA_GetDataLength(DMA1, DMA_CHANNEL));
|
||||
|
||||
if (b == 0x55) {
|
||||
// Keepalive byte — viewer is alive
|
||||
UART_DMA_Buffer[i] = 0x00;
|
||||
uint8_t processed = 0;
|
||||
while (read_ptr != write_ptr && processed < sizeof(UART_DMA_Buffer))
|
||||
{
|
||||
uint8_t b = UART_DMA_Buffer[read_ptr++];
|
||||
// read_ptr wraps naturally at 256 since it's uint8_t
|
||||
processed++;
|
||||
|
||||
if(KEYBOARD_ProcessProtocolByte(&state, b))
|
||||
connected = true;
|
||||
}
|
||||
else if (b == 0xAA) {
|
||||
// Possible start of a key packet: 0xAA 0x55 <type> <keycode>
|
||||
// type 0x03 = short press, 0x04 = long press
|
||||
size_t i1 = (i + 1) % sizeof(UART_DMA_Buffer);
|
||||
size_t i2 = (i + 2) % sizeof(UART_DMA_Buffer);
|
||||
size_t i3 = (i + 3) % sizeof(UART_DMA_Buffer);
|
||||
|
||||
if (UART_DMA_Buffer[i1] == 0x55 &&
|
||||
(UART_DMA_Buffer[i2] == UART_TYPE_KEY ||
|
||||
UART_DMA_Buffer[i2] == UART_TYPE_KEY_LONG))
|
||||
{
|
||||
uint8_t type = UART_DMA_Buffer[i2];
|
||||
uint8_t keyCode = UART_DMA_Buffer[i3];
|
||||
|
||||
// Consume all 4 bytes
|
||||
UART_DMA_Buffer[i] = 0x00;
|
||||
UART_DMA_Buffer[i1] = 0x00;
|
||||
UART_DMA_Buffer[i2] = 0x00;
|
||||
UART_DMA_Buffer[i3] = 0x00;
|
||||
|
||||
if (type == UART_TYPE_KEY_LONG)
|
||||
KEYBOARD_InjectKeyLong(keyCode);
|
||||
else
|
||||
KEYBOARD_InjectKey(keyCode);
|
||||
|
||||
connected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return connected;
|
||||
|
||||
+7
-64
@@ -20,9 +20,6 @@
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
#include "driver/keyboard.h"
|
||||
// Packet types for serial key injection (K5Viewer → radio)
|
||||
#define VCP_TYPE_KEY 0x03
|
||||
#define VCP_TYPE_KEY_LONG 0x04
|
||||
#endif
|
||||
|
||||
uint8_t VCP_RxBuf[VCP_RX_BUF_SIZE];
|
||||
@@ -63,25 +60,14 @@ bool VCP_ScreenshotPing(void)
|
||||
// KEY_1 → 0x55 → KEY_2 (else IDLE)
|
||||
// KEY_2 → 0x03 → KEY_3 (short press, else check long)
|
||||
// KEY_2 → 0x04 → KEY_3L (long press, else IDLE)
|
||||
// KEY_3 → <b> → InjectKey(b), IDLE
|
||||
// KEY_3L → <b> → InjectKeyLong(b), IDLE
|
||||
// KEY_3 → <b> → KEYBOARD_InjectKey(b, false), IDLE
|
||||
// KEY_3L → <b> → KEYBOARD_InjectKey(b, true), IDLE
|
||||
|
||||
typedef enum {
|
||||
STATE_IDLE = 0,
|
||||
STATE_KA_1,
|
||||
STATE_KA_2,
|
||||
STATE_KA_3,
|
||||
STATE_KEY_1,
|
||||
STATE_KEY_2,
|
||||
STATE_KEY_3,
|
||||
STATE_KEY_3L,
|
||||
} ParseState_t;
|
||||
|
||||
static uint32_t read_ptr = 0;
|
||||
static uint8_t read_ptr = 0;
|
||||
static ParseState_t state = STATE_IDLE;
|
||||
|
||||
bool connected = false;
|
||||
uint32_t write_ptr = VCP_RxBufPointer; // snapshot once — ISR may update concurrently
|
||||
uint8_t write_ptr = VCP_RxBufPointer; // snapshot once — ISR may update concurrently
|
||||
|
||||
// Cap bytes processed per call to VCP_RX_BUF_SIZE.
|
||||
// Prevents unbounded loop if the ISR write pointer laps read_ptr
|
||||
@@ -91,57 +77,14 @@ bool VCP_ScreenshotPing(void)
|
||||
while (read_ptr != write_ptr && processed < VCP_RX_BUF_SIZE)
|
||||
{
|
||||
uint8_t b = VCP_RxBuf[read_ptr];
|
||||
|
||||
read_ptr++;
|
||||
if (read_ptr >= VCP_RX_BUF_SIZE)
|
||||
read_ptr = 0;
|
||||
processed++;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case STATE_IDLE:
|
||||
if (b == 0x55) state = STATE_KA_1;
|
||||
else if (b == 0xAA) state = STATE_KEY_1;
|
||||
break;
|
||||
|
||||
case STATE_KA_1:
|
||||
state = (b == 0xAA) ? STATE_KA_2 : STATE_IDLE;
|
||||
break;
|
||||
|
||||
case STATE_KA_2:
|
||||
state = (b == 0x00) ? STATE_KA_3 : STATE_IDLE;
|
||||
break;
|
||||
|
||||
case STATE_KA_3:
|
||||
if (b == 0x00) connected = true;
|
||||
state = STATE_IDLE;
|
||||
break;
|
||||
|
||||
case STATE_KEY_1:
|
||||
state = (b == 0x55) ? STATE_KEY_2 : STATE_IDLE;
|
||||
break;
|
||||
|
||||
case STATE_KEY_2:
|
||||
if (b == VCP_TYPE_KEY) state = STATE_KEY_3;
|
||||
else if (b == VCP_TYPE_KEY_LONG) state = STATE_KEY_3L;
|
||||
else state = STATE_IDLE;
|
||||
break;
|
||||
|
||||
case STATE_KEY_3:
|
||||
KEYBOARD_InjectKey(b);
|
||||
connected = true;
|
||||
state = STATE_IDLE;
|
||||
break;
|
||||
|
||||
case STATE_KEY_3L:
|
||||
KEYBOARD_InjectKeyLong(b);
|
||||
connected = true;
|
||||
state = STATE_IDLE;
|
||||
break;
|
||||
|
||||
default:
|
||||
state = STATE_IDLE;
|
||||
break;
|
||||
}
|
||||
if(KEYBOARD_ProcessProtocolByte(&state, b))
|
||||
connected = true;
|
||||
}
|
||||
|
||||
return connected;
|
||||
|
||||
+1
-1
@@ -362,7 +362,7 @@ const uint8_t gFontSmall[95-1][6] =
|
||||
{0x20, 0x40, 0x40, 0x3D, 0x00, 0x00}, // 'j'
|
||||
{0x00, 0x7F, 0x10, 0x28, 0x44, 0x00}, // 'k'
|
||||
{0x00, 0x00, 0x3F, 0x40, 0x00, 0x00}, // 'l'
|
||||
{0x7C, 0x08, 0x10, 0x10, 0x08, 0x7C}, // 'm'
|
||||
{0x7C, 0x04, 0x7C, 0x04, 0x78, 0x00}, // 'm'
|
||||
{0x7C, 0x04, 0x04, 0x04, 0x78, 0x00}, // 'n'
|
||||
{0x38, 0x44, 0x44, 0x44, 0x38, 0x00}, // 'o'
|
||||
{0x7C, 0x14, 0x14, 0x14, 0x08, 0x00}, // 'p'
|
||||
|
||||
+5
-8
@@ -163,11 +163,8 @@ int32_t TX_freq_check(const uint32_t Frequency)
|
||||
{ // return '0' if TX frequency is allowed
|
||||
// otherwise return '-1'
|
||||
|
||||
if (Frequency < frequencyBandTable[0].lower || Frequency > frequencyBandTable[BAND_N_ELEM - 1].upper)
|
||||
return -1; // not allowed outside this range
|
||||
|
||||
if (Frequency >= BX4819_band1.upper && Frequency < BX4819_band2.lower)
|
||||
return -1; // BX chip does not work in this range
|
||||
if (RX_freq_check(Frequency))
|
||||
return -1;
|
||||
|
||||
switch (gSetting_F_LOCK)
|
||||
{
|
||||
@@ -280,10 +277,10 @@ int32_t RX_freq_check(const uint32_t Frequency)
|
||||
// otherwise return '-1'
|
||||
|
||||
if (Frequency < frequencyBandTable[0].lower || Frequency > frequencyBandTable[BAND_N_ELEM - 1].upper)
|
||||
return -1;
|
||||
return -1; // not allowed outside this range
|
||||
|
||||
if (Frequency >= BX4819_band1.upper && Frequency < BX4819_band2.lower)
|
||||
return -1;
|
||||
return -1; // BX chip does not work in this range
|
||||
|
||||
return 0; // OK frequency
|
||||
return 0; // OK frequency
|
||||
}
|
||||
+1
-1
@@ -151,7 +151,7 @@ void FUNCTION_Transmit()
|
||||
|
||||
// clear the DTMF RX live decoder buffer
|
||||
gDTMF_RX_live_timeout = 0;
|
||||
memset(gDTMF_RX_live, 0, sizeof(gDTMF_RX_live));
|
||||
DTMF_clear_input_box_memory();
|
||||
|
||||
#if defined(ENABLE_FMRADIO)
|
||||
if (gFmRadioMode)
|
||||
|
||||
@@ -228,7 +228,7 @@ void BATTERY_TimeSlice500ms(void)
|
||||
|
||||
if (lowBatteryCountdown < lowBatteryPeriod) {
|
||||
if (lowBatteryCountdown == lowBatteryPeriod-1 && !gChargingWithTypeC && !gLowBatteryConfirmed) {
|
||||
AUDIO_PlayBeep(BEEP_500HZ_60MS_DOUBLE_BEEP);
|
||||
AUDIO_PlayBeep(BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -241,7 +241,7 @@ void BATTERY_TimeSlice500ms(void)
|
||||
|
||||
// not on charge
|
||||
if (!gLowBatteryConfirmed) {
|
||||
AUDIO_PlayBeep(BEEP_500HZ_60MS_DOUBLE_BEEP);
|
||||
AUDIO_PlayBeep(BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL);
|
||||
#ifdef ENABLE_VOICE
|
||||
AUDIO_SetVoiceID(0, VOICE_ID_LOW_VOLTAGE);
|
||||
#endif
|
||||
|
||||
+6
-6
@@ -73,12 +73,14 @@ BOOT_Mode_t BOOT_GetMode(void)
|
||||
|
||||
void BOOT_ProcessMode(BOOT_Mode_t Mode)
|
||||
{
|
||||
GUI_DisplayType_t display = DISPLAY_MAIN;
|
||||
|
||||
if (Mode == BOOT_MODE_F_LOCK)
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
gEeprom.CURRENT_STATE = 0; // Don't resume is active...
|
||||
#endif
|
||||
GUI_SelectNextDisplay(DISPLAY_MENU);
|
||||
display = DISPLAY_MENU;
|
||||
}
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
else
|
||||
@@ -119,11 +121,9 @@ void BOOT_ProcessMode(BOOT_Mode_t Mode)
|
||||
gEeprom.CURRENT_STATE = 0; // Don't resume is active...
|
||||
#endif
|
||||
|
||||
GUI_SelectNextDisplay(DISPLAY_AIRCOPY);
|
||||
display = DISPLAY_AIRCOPY;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
GUI_SelectNextDisplay(DISPLAY_MAIN);
|
||||
}
|
||||
|
||||
GUI_SelectNextDisplay(display);
|
||||
}
|
||||
+26
-69
@@ -157,11 +157,7 @@ void Main(void)
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
gEeprom.KEY_LOCK = 0;
|
||||
SETTINGS_SaveSettings();
|
||||
gMenuCursor = MENU_ITEMS;
|
||||
|
||||
#ifdef ENABLE_NOAA
|
||||
gMenuCursor += 1; // move to hidden section, fix me if change... !!!
|
||||
#endif
|
||||
gMenuCursor = UI_MENU_GetMenuIdx(FIRST_HIDDEN_MENU_ITEM);
|
||||
gSubMenuSelection = gSetting_F_LOCK;
|
||||
#endif
|
||||
}
|
||||
@@ -277,77 +273,38 @@ void Main(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
if(gEeprom.CURRENT_STATE == 2 || gEeprom.CURRENT_STATE == 5)
|
||||
{
|
||||
gScanRangeStart = gScanRangeStart ? 0 : gTxVfo->pRX->Frequency;
|
||||
gScanRangeStop = gEeprom.VfoInfo[!gEeprom.TX_VFO].freq_config_RX.Frequency;
|
||||
if(gScanRangeStart > gScanRangeStop)
|
||||
{
|
||||
SWAP(gScanRangeStart, gScanRangeStop);
|
||||
}
|
||||
}
|
||||
switch (gEeprom.CURRENT_STATE) {
|
||||
case 1:
|
||||
gEeprom.SCAN_LIST_DEFAULT = gEeprom.CURRENT_LIST;
|
||||
CHFRSCANNER_Start(true, SCAN_FWD);
|
||||
break;
|
||||
if (gEeprom.CURRENT_STATE == 2 || gEeprom.CURRENT_STATE == 5)
|
||||
CHFRSCANNER_ScanRange();
|
||||
|
||||
case 2:
|
||||
CHFRSCANNER_Start(true, SCAN_FWD);
|
||||
break;
|
||||
switch (gEeprom.CURRENT_STATE) {
|
||||
case 1:
|
||||
gEeprom.SCAN_LIST_DEFAULT = gEeprom.CURRENT_LIST;
|
||||
CHFRSCANNER_Start(true, SCAN_FWD);
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
case 3:
|
||||
ACTION_FM();
|
||||
GUI_SelectNextDisplay(gRequestDisplayScreen);
|
||||
break;
|
||||
#endif
|
||||
case 2:
|
||||
CHFRSCANNER_Start(true, SCAN_FWD);
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_SPECTRUM
|
||||
case 4:
|
||||
APP_RunSpectrum();
|
||||
break;
|
||||
case 5:
|
||||
APP_RunSpectrum();
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_FMRADIO
|
||||
case 3:
|
||||
ACTION_FM();
|
||||
GUI_SelectNextDisplay(gRequestDisplayScreen);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
// No action for CURRENT_STATE == 0 or other unexpected values
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
#ifdef ENABLE_SPECTRUM
|
||||
case 4:
|
||||
case 5:
|
||||
APP_RunSpectrum();
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
if (gEeprom.CURRENT_STATE == 2 || gEeprom.CURRENT_STATE == 5) {
|
||||
gScanRangeStart = gScanRangeStart ? 0 : gTxVfo->pRX->Frequency;
|
||||
gScanRangeStop = gEeprom.VfoInfo[!gEeprom.TX_VFO].freq_config_RX.Frequency;
|
||||
if (gScanRangeStart > gScanRangeStop) {
|
||||
SWAP(gScanRangeStart, gScanRangeStop);
|
||||
}
|
||||
default:
|
||||
// No action for CURRENT_STATE == 0 or other unexpected values
|
||||
break;
|
||||
}
|
||||
|
||||
if (gEeprom.CURRENT_STATE == 1) {
|
||||
gEeprom.SCAN_LIST_DEFAULT = gEeprom.CURRENT_LIST;
|
||||
}
|
||||
|
||||
if (gEeprom.CURRENT_STATE == 1 || gEeprom.CURRENT_STATE == 2) {
|
||||
CHFRSCANNER_Start(true, SCAN_FWD);
|
||||
}
|
||||
#ifdef ENABLE_FMRADIO
|
||||
else if (gEeprom.CURRENT_STATE == 3) {
|
||||
ACTION_FM();
|
||||
GUI_SelectNextDisplay(gRequestDisplayScreen);
|
||||
}
|
||||
#endif
|
||||
#ifdef ENABLE_SPECTRUM
|
||||
else if (gEeprom.CURRENT_STATE == 4 || gEeprom.CURRENT_STATE == 5) {
|
||||
APP_RunSpectrum();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
while (true) {
|
||||
|
||||
+24
-2
@@ -116,6 +116,10 @@ enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
|
||||
bool gWakeUp = false;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
bool gSetting_set_scn = 1;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
uint8_t gSetting_set_pwr = 1;
|
||||
bool gSetting_set_ptt = 0;
|
||||
@@ -127,11 +131,15 @@ enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
|
||||
bool gSetting_set_met = 0;
|
||||
bool gSetting_set_gui = 0;
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
uint8_t gSetting_set_audio = 0;
|
||||
uint8_t gSetting_set_audio_fm = 0;
|
||||
uint8_t gSetting_set_audio_am = 0;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
bool gSetting_set_nfm = 0;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
uint8_t gSetting_set_sav = SET_SAV_OFF;
|
||||
#endif
|
||||
bool gSetting_set_tmr = 0;
|
||||
bool gSetting_set_ptt_session;
|
||||
#ifdef ENABLE_FEAT_F4HWN_DEBUG
|
||||
@@ -242,6 +250,7 @@ volatile uint16_t gScanPauseDelayIn_10ms;
|
||||
uint16_t gMenuCountdown;
|
||||
bool gPttWasReleased;
|
||||
bool gPttWasPressed;
|
||||
bool gHasVfoBackup;
|
||||
uint8_t gKeypadLocked;
|
||||
bool gFlagReconfigureVfos;
|
||||
uint8_t gVfoConfigureMode;
|
||||
@@ -329,6 +338,7 @@ uint8_t gIsLocked = 0xFF;
|
||||
bool gMute = false;
|
||||
uint8_t gBacklightTimeOriginal;
|
||||
uint8_t gBacklightBrightnessOld;
|
||||
uint8_t gSquelchLevelOriginal = 10;
|
||||
uint8_t gPttOnePushCounter = 0;
|
||||
uint32_t gBlinkCounter = 0;
|
||||
|
||||
@@ -634,4 +644,16 @@ void MR_PrintCacheStats(void)
|
||||
// cache_hits, cache_misses, MR_GetCacheHitRate());
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
bool SCREENSHOT_IsLocked(void)
|
||||
{
|
||||
if (gUART_LockScreenshot > 0) {
|
||||
gUART_LockScreenshot--;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
+24
-4
@@ -39,8 +39,6 @@
|
||||
#define FM_CHANNELS_MAX 48
|
||||
#define MR_CHANNELS_MAX 1024
|
||||
#define MR_CHANNELS_LIST 24
|
||||
#define MENU_ITEMS 69
|
||||
|
||||
// CACHE-BASED OPTIMIZATION: Only keep active channels in RAM
|
||||
// Full array stays in EEPROM, cache holds ~10 most-used channels
|
||||
#define MR_CHANNELS_CACHE_SIZE 10
|
||||
@@ -178,7 +176,21 @@ extern enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
|
||||
extern bool gWakeUp;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
extern bool gSetting_set_scn;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
enum SET_SAV_t {
|
||||
SET_SAV_OFF,
|
||||
SET_SAV_LOGO,
|
||||
SET_SAV_LOGO_PLUS,
|
||||
SET_SAV_MATRIX,
|
||||
SET_SAV_LEN
|
||||
};
|
||||
#endif
|
||||
|
||||
extern uint8_t gSetting_set_pwr;
|
||||
extern bool gSetting_set_ptt;
|
||||
extern uint8_t gSetting_set_tot;
|
||||
@@ -189,11 +201,15 @@ extern enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
|
||||
extern bool gSetting_set_met;
|
||||
extern bool gSetting_set_gui;
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
extern uint8_t gSetting_set_audio;
|
||||
extern uint8_t gSetting_set_audio_fm;
|
||||
extern uint8_t gSetting_set_audio_am;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
extern bool gSetting_set_nfm;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
extern uint8_t gSetting_set_sav;
|
||||
#endif
|
||||
extern bool gSetting_set_tmr;
|
||||
extern bool gSetting_set_ptt_session;
|
||||
#ifdef ENABLE_FEAT_F4HWN_DEBUG
|
||||
@@ -323,6 +339,8 @@ extern volatile bool gTxTimeoutReached;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
extern volatile uint8_t gUART_LockScreenshot; // lock screenshot if Chirp is used
|
||||
extern bool gUSB_ScreenshotEnabled;
|
||||
|
||||
bool SCREENSHOT_IsLocked(void);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -366,6 +384,7 @@ extern AlarmState_t gAlarmState;
|
||||
extern uint16_t gMenuCountdown;
|
||||
extern bool gPttWasReleased;
|
||||
extern bool gPttWasPressed;
|
||||
extern bool gHasVfoBackup;
|
||||
extern bool gFlagReconfigureVfos;
|
||||
extern uint8_t gVfoConfigureMode;
|
||||
extern bool gFlagResetVfos;
|
||||
@@ -450,6 +469,7 @@ extern volatile uint8_t boot_counter_10ms;
|
||||
extern bool gMute;
|
||||
extern uint8_t gBacklightTimeOriginal;
|
||||
extern uint8_t gBacklightBrightnessOld;
|
||||
extern uint8_t gSquelchLevelOriginal;
|
||||
extern uint8_t gPttOnePushCounter;
|
||||
extern uint32_t gBlinkCounter;
|
||||
|
||||
@@ -467,4 +487,4 @@ void FUNCTION_NOP();
|
||||
|
||||
static inline bool SerialConfigInProgress() { return gSerialConfigCountDown_500ms != 0; }
|
||||
|
||||
#endif
|
||||
#endif
|
||||
+235
-232
@@ -54,36 +54,67 @@ const char gModulationStr[MODULATION_UKNOWN][4] = {
|
||||
};
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
static void AUDIO_ApplyProfile(uint8_t profile)
|
||||
|
||||
// About BK4819_WriteRegister(0x2b, val) experimentation...
|
||||
//
|
||||
// 0x000: 300 Hz high-pass filter enabled, 3 kHz low-pass filter enabled, de-emphasis enabled.
|
||||
// Audio impression: the most "classic radio" tuning, more filtered and smoother.
|
||||
//
|
||||
// 0x300: 300 Hz high-pass filter enabled, 3 kHz low-pass filter disabled, de-emphasis disabled.
|
||||
// Audio impression: clearer, brighter, and more open, while still cutting low frequencies.
|
||||
//
|
||||
// 0x400: 300 Hz high-pass filter disabled, 3 kHz low-pass filter enabled, de-emphasis enabled.
|
||||
// Audio impression: fuller low end, but still softened by de-emphasis and upper-frequency limiting.
|
||||
//
|
||||
// 0x500: 300 Hz high-pass filter disabled, 3 kHz low-pass filter enabled, de-emphasis disabled.
|
||||
// Audio impression: fuller bass, more direct sound, while still keeping the 3 kHz top-end limit.
|
||||
|
||||
static void AUDIO_ApplyFMProfile(uint8_t profile)
|
||||
{ // | 0x54 || 0x55 |
|
||||
static const uint16_t fm_profiles[][2] = {
|
||||
{0x9009, 0x3200}, // 0: FLAT
|
||||
{0x9009, 0x33A9}, // 1: CLEAN
|
||||
{0x9009, 0x3600}, // 2: MID
|
||||
{0x8546, 0x3AF0}, // 3: BOOST
|
||||
{0x8566, 0x3D00} // 4: MAX
|
||||
};
|
||||
|
||||
if (profile >= ARRAY_SIZE(fm_profiles))
|
||||
profile = 0;
|
||||
|
||||
BK4819_WriteRegister(0x54, fm_profiles[profile][0]);
|
||||
BK4819_WriteRegister(0x55, fm_profiles[profile][1]);
|
||||
}
|
||||
|
||||
static void AUDIO_ApplyAMProfile(uint8_t profile)
|
||||
{ // | 0x2b || 0x2f || 0x54 || 0x55 |
|
||||
static const uint16_t am_profiles[][4] = {
|
||||
// SHARP (ALPHA test profile) - Narrow IF filter (REG54 bits[14:8]=0, bits[7:0]=9), low IF gain (REG55 bits[11:8]=1, ref=169)
|
||||
// Selective and crisp, best adjacent channel rejection, may sound harsh on strong signals
|
||||
{0x0300, 0x9990, 0x9009, 0x31A9},
|
||||
|
||||
// STOCK - Narrow IF filter (REG54 bits[14:8]=0, bits[7:0]=9), moderate IF gain (REG55 bits[11:8]=4, ref=180)
|
||||
// Selective filter with balanced gain, punchy and detailed, good compromise between rejection and sensitivity
|
||||
{0x0500, 0x9990, 0x9009, 0x31A9},
|
||||
|
||||
// OPEN (BRAVO test profile) - Medium-wide IF filter (REG54 bits[14:8]=8, bits[7:0]=70), high IF gain (REG55 bits[11:8]=8, ref=192)
|
||||
// Wide and pleasant, better sensitivity on weak signals, may struggle with adjacent channel interference
|
||||
{0x0300, 0x9990, 0x8846, 0x38C0}
|
||||
};
|
||||
|
||||
if (profile >= ARRAY_SIZE(am_profiles))
|
||||
profile = 0;
|
||||
|
||||
BK4819_WriteRegister(0x2b, am_profiles[profile][0]);
|
||||
BK4819_WriteRegister(0x2f, am_profiles[profile][1]);
|
||||
BK4819_WriteRegister(0x54, am_profiles[profile][2]);
|
||||
BK4819_WriteRegister(0x55, am_profiles[profile][3]);
|
||||
}
|
||||
|
||||
static void AUDIO_ApplyUSBProfile(void)
|
||||
{
|
||||
switch (profile)
|
||||
{
|
||||
default:
|
||||
case 0: // FLAT
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x3200);
|
||||
break;
|
||||
|
||||
case 1: // CLEAN
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x33A9);
|
||||
break;
|
||||
|
||||
case 2: // MID
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x3600);
|
||||
break;
|
||||
|
||||
case 3: // BOOST
|
||||
BK4819_WriteRegister(0x54, 0x8546);
|
||||
BK4819_WriteRegister(0x55, 0x3AF0);
|
||||
break;
|
||||
|
||||
case 4: // MAX
|
||||
BK4819_WriteRegister(0x54, 0x8566);
|
||||
BK4819_WriteRegister(0x55, 0x3D00);
|
||||
break;
|
||||
}
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x31A9);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -202,6 +233,24 @@ void RADIO_InitInfo(VFO_Info_t *pInfo, const uint16_t ChannelSave, const uint32_
|
||||
RADIO_ConfigureSquelchAndOutputPower(pInfo);
|
||||
}
|
||||
|
||||
void RADIO_ValidateAndSetCode(FREQ_Config_t *pFreq_Config, uint8_t tmp) {
|
||||
switch (pFreq_Config->CodeType) {
|
||||
default:
|
||||
case CODE_TYPE_OFF:
|
||||
pFreq_Config->CodeType = CODE_TYPE_OFF;
|
||||
tmp = 0;
|
||||
break;
|
||||
|
||||
case CODE_TYPE_CONTINUOUS_TONE:
|
||||
case CODE_TYPE_DIGITAL:
|
||||
case CODE_TYPE_REVERSE_DIGITAL:
|
||||
if (tmp > ((pFreq_Config->CodeType == CODE_TYPE_CONTINUOUS_TONE ? ARRAY_SIZE(CTCSS_Options) : ARRAY_SIZE(DCS_Options)) - 1))
|
||||
tmp = 0;
|
||||
break;
|
||||
}
|
||||
pFreq_Config->Code = tmp;
|
||||
}
|
||||
|
||||
void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure)
|
||||
{
|
||||
VFO_Info_t *pVfo = &gEeprom.VfoInfo[VFO];
|
||||
@@ -275,7 +324,7 @@ void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure
|
||||
}
|
||||
|
||||
pVfo->Band = band;
|
||||
pVfo->SCANLIST_PARTICIPATION = bParticipation;
|
||||
pVfo->SCANLIST_PARTICIPATION = bParticipation;
|
||||
pVfo->CHANNEL_SAVE = channel;
|
||||
|
||||
uint32_t base;
|
||||
@@ -320,49 +369,8 @@ void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure
|
||||
pVfo->freq_config_RX.CodeType = (data[2] >> 0) & 0x0F;
|
||||
pVfo->freq_config_TX.CodeType = (data[2] >> 4) & 0x0F;
|
||||
|
||||
tmp = data[0];
|
||||
switch (pVfo->freq_config_RX.CodeType)
|
||||
{
|
||||
default:
|
||||
case CODE_TYPE_OFF:
|
||||
pVfo->freq_config_RX.CodeType = CODE_TYPE_OFF;
|
||||
tmp = 0;
|
||||
break;
|
||||
|
||||
case CODE_TYPE_CONTINUOUS_TONE:
|
||||
if (tmp > (ARRAY_SIZE(CTCSS_Options) - 1))
|
||||
tmp = 0;
|
||||
break;
|
||||
|
||||
case CODE_TYPE_DIGITAL:
|
||||
case CODE_TYPE_REVERSE_DIGITAL:
|
||||
if (tmp > (ARRAY_SIZE(DCS_Options) - 1))
|
||||
tmp = 0;
|
||||
break;
|
||||
}
|
||||
pVfo->freq_config_RX.Code = tmp;
|
||||
|
||||
tmp = data[1];
|
||||
switch (pVfo->freq_config_TX.CodeType)
|
||||
{
|
||||
default:
|
||||
case CODE_TYPE_OFF:
|
||||
pVfo->freq_config_TX.CodeType = CODE_TYPE_OFF;
|
||||
tmp = 0;
|
||||
break;
|
||||
|
||||
case CODE_TYPE_CONTINUOUS_TONE:
|
||||
if (tmp > (ARRAY_SIZE(CTCSS_Options) - 1))
|
||||
tmp = 0;
|
||||
break;
|
||||
|
||||
case CODE_TYPE_DIGITAL:
|
||||
case CODE_TYPE_REVERSE_DIGITAL:
|
||||
if (tmp > (ARRAY_SIZE(DCS_Options) - 1))
|
||||
tmp = 0;
|
||||
break;
|
||||
}
|
||||
pVfo->freq_config_TX.Code = tmp;
|
||||
RADIO_ValidateAndSetCode(&pVfo->freq_config_RX, data[0]);
|
||||
RADIO_ValidateAndSetCode(&pVfo->freq_config_TX, data[1]);
|
||||
|
||||
if (data[4] == 0xFF)
|
||||
{
|
||||
@@ -502,17 +510,20 @@ void RADIO_ConfigureSquelchAndOutputPower(VFO_Info_t *pInfo)
|
||||
}
|
||||
else
|
||||
{ // squelch >= 1
|
||||
Base += gEeprom.SQUELCH_LEVEL; // my eeprom squelch-1
|
||||
// VHF UHF
|
||||
PY25Q16_ReadBuffer(Base + 0x00, &pInfo->SquelchOpenRSSIThresh, 1); // 50 10
|
||||
PY25Q16_ReadBuffer(Base + 0x10, &pInfo->SquelchCloseRSSIThresh, 1); // 40 5
|
||||
|
||||
PY25Q16_ReadBuffer(Base + 0x20, &pInfo->SquelchOpenNoiseThresh, 1); // 65 90
|
||||
PY25Q16_ReadBuffer(Base + 0x30, &pInfo->SquelchCloseNoiseThresh, 1); // 70 100
|
||||
|
||||
PY25Q16_ReadBuffer(Base + 0x40, &pInfo->SquelchCloseGlitchThresh, 1); // 90 90
|
||||
PY25Q16_ReadBuffer(Base + 0x50, &pInfo->SquelchOpenGlitchThresh, 1); // 100 100
|
||||
Base += gEeprom.SQUELCH_LEVEL; // my eeprom squelch-1
|
||||
// VHF UHF
|
||||
uint8_t *sq_ptrs[6] = {
|
||||
&pInfo->SquelchOpenRSSIThresh, // 50 10
|
||||
&pInfo->SquelchCloseRSSIThresh, // 40 5
|
||||
&pInfo->SquelchOpenNoiseThresh, // 65 90
|
||||
&pInfo->SquelchCloseNoiseThresh, // 70 100
|
||||
&pInfo->SquelchCloseGlitchThresh, // 90 90
|
||||
&pInfo->SquelchOpenGlitchThresh // 100 100
|
||||
};
|
||||
|
||||
for(uint8_t i = 0; i < 6; i++) {
|
||||
PY25Q16_ReadBuffer(Base + (i * 0x10), sq_ptrs[i], 1);
|
||||
}
|
||||
|
||||
uint16_t noise_open = pInfo->SquelchOpenNoiseThresh;
|
||||
uint16_t noise_close = pInfo->SquelchCloseNoiseThresh;
|
||||
@@ -536,14 +547,14 @@ void RADIO_ConfigureSquelchAndOutputPower(VFO_Info_t *pInfo)
|
||||
if (glitch_close == glitch_open && glitch_close <= 253)
|
||||
glitch_close += 2;
|
||||
|
||||
pInfo->SquelchOpenRSSIThresh = (rssi_open > 255) ? 255 : rssi_open;
|
||||
pInfo->SquelchCloseRSSIThresh = (rssi_close > 255) ? 255 : rssi_close;
|
||||
pInfo->SquelchOpenGlitchThresh = (glitch_open > 255) ? 255 : glitch_open;
|
||||
pInfo->SquelchCloseGlitchThresh = (glitch_close > 255) ? 255 : glitch_close;
|
||||
pInfo->SquelchOpenRSSIThresh = MIN(rssi_open, 255);
|
||||
pInfo->SquelchCloseRSSIThresh = MIN(rssi_close, 255);
|
||||
pInfo->SquelchOpenGlitchThresh = MIN(glitch_open, 255);
|
||||
pInfo->SquelchCloseGlitchThresh = MIN(glitch_close, 255);
|
||||
#endif
|
||||
|
||||
pInfo->SquelchOpenNoiseThresh = (noise_open > 127) ? 127 : noise_open;
|
||||
pInfo->SquelchCloseNoiseThresh = (noise_close > 127) ? 127 : noise_close;
|
||||
pInfo->SquelchOpenNoiseThresh = MIN(noise_open, 127);
|
||||
pInfo->SquelchCloseNoiseThresh = MIN(noise_close, 127);
|
||||
}
|
||||
|
||||
// *******************************
|
||||
@@ -574,30 +585,15 @@ void RADIO_ConfigureSquelchAndOutputPower(VFO_Info_t *pInfo)
|
||||
uint8_t Op = 0; // Low eeprom calibration data
|
||||
uint8_t currentPower = pInfo->OUTPUT_POWER;
|
||||
|
||||
if(currentPower == OUTPUT_POWER_USER)
|
||||
{
|
||||
if(gSetting_set_pwr == 5)
|
||||
{
|
||||
Op = 1; // Mid eeprom calibration data
|
||||
}
|
||||
else if(gSetting_set_pwr == 6)
|
||||
{
|
||||
Op = 2; // High eeprom calibration data
|
||||
}
|
||||
if (currentPower == OUTPUT_POWER_USER)
|
||||
currentPower = gSetting_set_pwr;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentPower == OUTPUT_POWER_MID)
|
||||
{
|
||||
Op = 1; // Mid eeprom calibration data
|
||||
}
|
||||
else if(currentPower == OUTPUT_POWER_HIGH)
|
||||
{
|
||||
Op = 2; // High eeprom calibration data
|
||||
}
|
||||
currentPower--;
|
||||
}
|
||||
|
||||
if (currentPower == 5)
|
||||
Op = 1; // Mid eeprom calibration data
|
||||
else if (currentPower == 6)
|
||||
Op = 2; // High eeprom calibration data
|
||||
|
||||
PY25Q16_ReadBuffer(0x100D0 + (Band * 16) + (Op * 3), Txp, 3);
|
||||
|
||||
@@ -705,18 +701,11 @@ void RADIO_ApplyOffset(VFO_Info_t *pInfo)
|
||||
{
|
||||
uint32_t Frequency = pInfo->freq_config_RX.Frequency;
|
||||
|
||||
switch (pInfo->TX_OFFSET_FREQUENCY_DIRECTION)
|
||||
{
|
||||
case TX_OFFSET_FREQUENCY_DIRECTION_OFF:
|
||||
break;
|
||||
case TX_OFFSET_FREQUENCY_DIRECTION_ADD:
|
||||
Frequency += pInfo->TX_OFFSET_FREQUENCY;
|
||||
break;
|
||||
case TX_OFFSET_FREQUENCY_DIRECTION_SUB:
|
||||
Frequency -= pInfo->TX_OFFSET_FREQUENCY;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pInfo->TX_OFFSET_FREQUENCY_DIRECTION == TX_OFFSET_FREQUENCY_DIRECTION_ADD)
|
||||
Frequency += pInfo->TX_OFFSET_FREQUENCY;
|
||||
else if (pInfo->TX_OFFSET_FREQUENCY_DIRECTION == TX_OFFSET_FREQUENCY_DIRECTION_SUB)
|
||||
Frequency -= pInfo->TX_OFFSET_FREQUENCY;
|
||||
|
||||
pInfo->freq_config_TX.Frequency = Frequency;
|
||||
}
|
||||
|
||||
@@ -740,6 +729,12 @@ void RADIO_SelectVfos(void)
|
||||
RADIO_SelectCurrentVfo();
|
||||
}
|
||||
|
||||
BK4819_FilterBandwidth_t RADIO_GetAMFilterBandwidth(const VFO_Info_t *pVfo)
|
||||
{
|
||||
// On BK4829, AM "wide" intentionally reuses the wider RF filter preset.
|
||||
return (pVfo->CHANNEL_BANDWIDTH == BANDWIDTH_WIDE) ? BK4819_FILTER_BW_WIDE : BK4819_FILTER_BW_AM;
|
||||
}
|
||||
|
||||
void RADIO_SetupRegisters(bool switchToForeground)
|
||||
{
|
||||
BK4819_FilterBandwidth_t Bandwidth = gRxVfo->CHANNEL_BANDWIDTH;
|
||||
@@ -758,7 +753,7 @@ void RADIO_SetupRegisters(bool switchToForeground)
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false);
|
||||
|
||||
if (gRxVfo->Modulation == MODULATION_AM)
|
||||
BK4819_SetFilterBandwidth(BK4819_FILTER_BW_AM, true);
|
||||
BK4819_SetFilterBandwidth(RADIO_GetAMFilterBandwidth(gRxVfo), true);
|
||||
else
|
||||
{
|
||||
switch (Bandwidth)
|
||||
@@ -810,6 +805,9 @@ void RADIO_SetupRegisters(bool switchToForeground)
|
||||
#endif
|
||||
BK4819_SetFrequency(Frequency);
|
||||
|
||||
// Keep the demodulator in sync when retuning without entering RX audio.
|
||||
RADIO_SetModulation(gRxVfo->Modulation);
|
||||
|
||||
BK4819_SetupSquelch(
|
||||
gRxVfo->SquelchOpenRSSIThresh, gRxVfo->SquelchCloseRSSIThresh,
|
||||
gRxVfo->SquelchOpenNoiseThresh, gRxVfo->SquelchCloseNoiseThresh,
|
||||
@@ -822,12 +820,7 @@ void RADIO_SetupRegisters(bool switchToForeground)
|
||||
|
||||
// AF RX Gain and DAC
|
||||
//BK4819_WriteRegister(BK4819_REG_48, 0xB3A8); // 1011 00 111010 1000
|
||||
BK4819_WriteRegister(BK4819_REG_48,
|
||||
(11u << 12) | // ??? .. 0 ~ 15, doesn't seem to make any difference
|
||||
( 0u << 10) | // AF Rx Gain-1
|
||||
(gEeprom.VOLUME_GAIN << 4) | // AF Rx Gain-2
|
||||
(gEeprom.DAC_GAIN << 0)); // AF DAC Gain (after Gain-1 and Gain-2)
|
||||
|
||||
BK4819_SetRxAudioGain();
|
||||
|
||||
uint16_t InterruptMask = BK4819_REG_3F_SQUELCH_FOUND | BK4819_REG_3F_SQUELCH_LOST;
|
||||
|
||||
@@ -847,7 +840,7 @@ void RADIO_SetupRegisters(bool switchToForeground)
|
||||
BK4819_SetCTCSSFrequency(SQL_TONE);
|
||||
BK4819_SetTailDetection(SQL_TONE); // Default 550 = QS's 55Hz tone method
|
||||
|
||||
InterruptMask = BK4819_REG_3F_CxCSS_TAIL | BK4819_REG_3F_SQUELCH_FOUND | BK4819_REG_3F_SQUELCH_LOST;
|
||||
InterruptMask |= BK4819_REG_3F_CxCSS_TAIL;
|
||||
break;
|
||||
|
||||
case CODE_TYPE_CONTINUOUS_TONE:
|
||||
@@ -859,24 +852,17 @@ void RADIO_SetupRegisters(bool switchToForeground)
|
||||
// BK4819_SetTailDetection(CTCSS_Options[Code]);
|
||||
//#endif
|
||||
|
||||
InterruptMask = 0
|
||||
| BK4819_REG_3F_CxCSS_TAIL
|
||||
| BK4819_REG_3F_CTCSS_FOUND
|
||||
| BK4819_REG_3F_CTCSS_LOST
|
||||
| BK4819_REG_3F_SQUELCH_FOUND
|
||||
| BK4819_REG_3F_SQUELCH_LOST;
|
||||
|
||||
InterruptMask |= BK4819_REG_3F_CxCSS_TAIL
|
||||
| BK4819_REG_3F_CTCSS_FOUND
|
||||
| BK4819_REG_3F_CTCSS_LOST;
|
||||
break;
|
||||
|
||||
case CODE_TYPE_DIGITAL:
|
||||
case CODE_TYPE_REVERSE_DIGITAL:
|
||||
BK4819_SetCDCSSCodeWord(DCS_GetGolayCodeWord(CodeType, Code));
|
||||
InterruptMask = 0
|
||||
| BK4819_REG_3F_CxCSS_TAIL
|
||||
| BK4819_REG_3F_CDCSS_FOUND
|
||||
| BK4819_REG_3F_CDCSS_LOST
|
||||
| BK4819_REG_3F_SQUELCH_FOUND
|
||||
| BK4819_REG_3F_SQUELCH_LOST;
|
||||
InterruptMask |= BK4819_REG_3F_CxCSS_TAIL
|
||||
| BK4819_REG_3F_CDCSS_FOUND
|
||||
| BK4819_REG_3F_CDCSS_LOST;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -894,11 +880,8 @@ void RADIO_SetupRegisters(bool switchToForeground)
|
||||
else
|
||||
{
|
||||
BK4819_SetCTCSSFrequency(2625);
|
||||
InterruptMask = 0
|
||||
| BK4819_REG_3F_CTCSS_FOUND
|
||||
| BK4819_REG_3F_CTCSS_LOST
|
||||
| BK4819_REG_3F_SQUELCH_FOUND
|
||||
| BK4819_REG_3F_SQUELCH_LOST;
|
||||
InterruptMask |= BK4819_REG_3F_CTCSS_FOUND
|
||||
| BK4819_REG_3F_CTCSS_LOST;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -926,8 +909,8 @@ void RADIO_SetupRegisters(bool switchToForeground)
|
||||
BK4819_EnableDTMF();
|
||||
InterruptMask |= BK4819_REG_3F_DTMF_5TONE_FOUND;
|
||||
|
||||
//RADIO_SetupAGC(gRxVfo->Modulation == MODULATION_AM, false);
|
||||
RADIO_SetupAGC(false, false);
|
||||
RADIO_SetupAGC(gRxVfo->Modulation == MODULATION_AM, false);
|
||||
//RADIO_SetupAGC(false, false);
|
||||
|
||||
// enable/disable BK4819 selected interrupts
|
||||
BK4819_WriteRegister(BK4819_REG_3F, InterruptMask);
|
||||
@@ -1056,6 +1039,30 @@ void RADIO_SetTxParameters(void)
|
||||
|
||||
void RADIO_SetModulation(ModulationMode_t modulation)
|
||||
{
|
||||
#ifdef ENABLE_BYP_RAW_DEMODULATORS
|
||||
if (modulation == MODULATION_BYP || modulation == MODULATION_RAW) {
|
||||
uint16_t reg_3d_val = 0x0000;
|
||||
|
||||
if (modulation == MODULATION_BYP) {
|
||||
// BYP on BK4829 uses full audio bypass profile.
|
||||
BK4819_EnterBypass();
|
||||
reg_3d_val = 0x2AAB;
|
||||
} else {
|
||||
// RAW on BK4829 uses RX-only filter bypass profile.
|
||||
BK4819_EnterRaw();
|
||||
// reg_3d_val = 0x0000;
|
||||
}
|
||||
|
||||
BK4819_SetRegValue(afDacGainRegSpec, 0xF);
|
||||
BK4819_WriteRegister(BK4819_REG_3D, reg_3d_val);
|
||||
RADIO_SetupAGC(false, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure we always leave bypass / raw mode before applying normal modulation settings.
|
||||
BK4819_ExitBypass();
|
||||
#endif
|
||||
|
||||
BK4819_AF_Type_t mod;
|
||||
switch(modulation) {
|
||||
default:
|
||||
@@ -1069,93 +1076,87 @@ void RADIO_SetModulation(ModulationMode_t modulation)
|
||||
mod = BK4819_AF_BASEBAND2;
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_BYP_RAW_DEMODULATORS
|
||||
case MODULATION_BYP:
|
||||
mod = BK4819_AF_UNKNOWN3;
|
||||
break;
|
||||
case MODULATION_RAW:
|
||||
mod = BK4819_AF_BASEBAND1;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
BK4819_SetAF(mod);
|
||||
|
||||
// HACK, FIXME:
|
||||
//
|
||||
// What follows is a direct copy of the AM enable/disable code from
|
||||
// the original UV-K1 firmware. It is not clear why these specific register
|
||||
// values are used for AM all of a sudden instead of the AF setting like on
|
||||
// the BK4819, nor what exactly they do.
|
||||
// So for now we just keep it as is to maintain compatibility.
|
||||
//
|
||||
if (modulation != MODULATION_AM)
|
||||
{
|
||||
uint16_t uVar1 = BK4819_ReadRegister(0x31);
|
||||
BK4819_WriteRegister(0x31,uVar1 & 0xfffffffe);
|
||||
BK4819_WriteRegister(0x42,0x6b5a);
|
||||
BK4819_WriteRegister(0x2a,0x7400);
|
||||
BK4819_WriteRegister(0x2b,0);
|
||||
BK4819_WriteRegister(0x2f,0x9890);
|
||||
//BK4819_WriteRegister(0x54, 0x9009);
|
||||
//BK4819_WriteRegister(0x55, 0x31a9);
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
AUDIO_ApplyProfile(gSetting_set_audio);
|
||||
#else
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x31a9);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t uVar1 = BK4819_ReadRegister(0x31);
|
||||
BK4819_WriteRegister(0x31,uVar1 | 1);
|
||||
BK4819_WriteRegister(0x42,0x6f5c);
|
||||
BK4819_WriteRegister(0x2a,0x7434);
|
||||
BK4819_WriteRegister(0x2b,0x300);
|
||||
BK4819_WriteRegister(0x2f,0x9990);
|
||||
//BK4819_WriteRegister(0x54, 0x9775);
|
||||
//BK4819_WriteRegister(0x55, 0x32c6);
|
||||
|
||||
BK4819_WriteRegister(0x54, 0x8846);
|
||||
BK4819_WriteRegister(0x55, 0x38C0);
|
||||
uint16_t uVar1 = BK4819_ReadRegister(0x31);
|
||||
|
||||
BK4819_SetFilterBandwidth(BK4819_FILTER_BW_AM, true);
|
||||
switch (modulation)
|
||||
{
|
||||
case MODULATION_AM:
|
||||
{
|
||||
BK4819_WriteRegister(0x31, uVar1 | 1); // AM Demodulation Enable
|
||||
BK4819_WriteRegister(0x42, 0x6f5c);
|
||||
BK4819_WriteRegister(0x2a, 0x7434);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
AUDIO_ApplyAMProfile(gSetting_set_audio_am);
|
||||
#else
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x31a9);
|
||||
#endif
|
||||
|
||||
BK4819_SetFilterBandwidth(RADIO_GetAMFilterBandwidth(gRxVfo), true);
|
||||
break;
|
||||
}
|
||||
|
||||
case MODULATION_USB:
|
||||
case MODULATION_FM:
|
||||
default:
|
||||
{
|
||||
BK4819_WriteRegister(0x31, uVar1 & 0xfffe); // AM Demodulation Disable
|
||||
BK4819_WriteRegister(0x42, 0x6b5a);
|
||||
BK4819_WriteRegister(0x2a, 0x7400);
|
||||
BK4819_WriteRegister(0x2b, 0x0000);
|
||||
BK4819_WriteRegister(0x2f, 0x9890);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
if (modulation == MODULATION_USB)
|
||||
AUDIO_ApplyUSBProfile();
|
||||
else
|
||||
AUDIO_ApplyFMProfile(gSetting_set_audio_fm);
|
||||
#else
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x31a9);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BK4819_SetRegValue(afDacGainRegSpec, 0xF);
|
||||
BK4819_WriteRegister(BK4819_REG_3D, modulation == MODULATION_USB ? 0 : 0x2AAB);
|
||||
BK4819_SetRegValue(afcDisableRegSpec, modulation != MODULATION_FM);
|
||||
|
||||
//RADIO_SetupAGC(modulation == MODULATION_AM, false);
|
||||
RADIO_SetupAGC(false, false);
|
||||
RADIO_SetupAGC(modulation == MODULATION_AM, false);
|
||||
}
|
||||
|
||||
void RADIO_SetupAGC(bool listeningAM, bool disable)
|
||||
{
|
||||
static uint8_t lastSettings;
|
||||
static uint8_t lastSettings = 0xFF;
|
||||
uint8_t newSettings = (listeningAM << 1) | disable;
|
||||
if(lastSettings == newSettings)
|
||||
if (lastSettings == newSettings)
|
||||
return;
|
||||
lastSettings = newSettings;
|
||||
|
||||
|
||||
if(!listeningAM) { // if not actively listening AM we don't need any AM specific regulation
|
||||
BK4819_SetAGC(!disable);
|
||||
BK4819_InitAGC(false);
|
||||
}
|
||||
else {
|
||||
#ifdef ENABLE_AM_FIX
|
||||
if(gSetting_AM_fix) { // if AM fix active lock AGC so AM-fix can do it's job
|
||||
BK4819_SetAGC(0);
|
||||
AM_fix_enable(!disable);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
BK4819_SetAGC(!disable);
|
||||
BK4819_InitAGC(true);
|
||||
}
|
||||
if (listeningAM && gSetting_AM_fix) {
|
||||
BK4819_SetAGC(0);
|
||||
AM_fix_enable(!disable);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
BK4819_SetAGC(!disable);
|
||||
BK4819_InitAGC(listeningAM);
|
||||
}
|
||||
|
||||
void RADIO_SetVfoState(VfoState_t State)
|
||||
@@ -1201,16 +1202,12 @@ void RADIO_PrepareTX(void)
|
||||
|
||||
RADIO_SelectCurrentVfo();
|
||||
|
||||
if(TX_freq_check(gCurrentVfo->pTX->Frequency) != 0
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
if(TX_freq_check(gCurrentVfo->pTX->Frequency) != 0 && gCurrentVfo->TX_LOCK == true
|
||||
#if defined(ENABLE_ALARM) || defined(ENABLE_TX1750)
|
||||
&& gAlarmState != ALARM_STATE_SITE_ALARM
|
||||
#endif
|
||||
#else
|
||||
if(TX_freq_check(gCurrentVfo->pTX->Frequency) != 0
|
||||
#if defined(ENABLE_ALARM) || defined(ENABLE_TX1750)
|
||||
&& gAlarmState != ALARM_STATE_SITE_ALARM
|
||||
#endif
|
||||
&& gCurrentVfo->TX_LOCK == true
|
||||
#endif
|
||||
#if defined(ENABLE_ALARM) || defined(ENABLE_TX1750)
|
||||
&& gAlarmState != ALARM_STATE_SITE_ALARM
|
||||
#endif
|
||||
){
|
||||
// TX frequency not allowed
|
||||
@@ -1229,6 +1226,12 @@ void RADIO_PrepareTX(void)
|
||||
// over voltage .. this is being a pain
|
||||
State = VFO_STATE_VOLTAGE_HIGH;
|
||||
}
|
||||
#ifdef ENABLE_BYP_RAW_DEMODULATORS
|
||||
else if (gCurrentVfo->Modulation == MODULATION_BYP || gCurrentVfo->Modulation == MODULATION_RAW) {
|
||||
// BYP/RAW are receive-only modes.
|
||||
State = VFO_STATE_TX_DISABLE;
|
||||
}
|
||||
#endif
|
||||
#ifndef ENABLE_TX_WHEN_AM
|
||||
else if (gCurrentVfo->Modulation != MODULATION_FM) {
|
||||
// not allowed to TX if in AM mode
|
||||
@@ -1308,17 +1311,19 @@ void RADIO_PrepareTX(void)
|
||||
|
||||
void RADIO_SendCssTail(void)
|
||||
{
|
||||
switch (gCurrentVfo->pTX->CodeType) {
|
||||
case CODE_TYPE_DIGITAL:
|
||||
case CODE_TYPE_REVERSE_DIGITAL:
|
||||
BK4819_PlayCDCSSTail();
|
||||
break;
|
||||
default:
|
||||
BK4819_PlayCTCSSTail();
|
||||
break;
|
||||
}
|
||||
if (gEeprom.TAIL_TONE_ELIMINATION) {
|
||||
switch (gCurrentVfo->pTX->CodeType) {
|
||||
case CODE_TYPE_DIGITAL:
|
||||
case CODE_TYPE_REVERSE_DIGITAL:
|
||||
BK4819_PlayCDCSSTail();
|
||||
break;
|
||||
default:
|
||||
BK4819_PlayCTCSSTail();
|
||||
break;
|
||||
}
|
||||
|
||||
SYSTEM_DelayMs(200);
|
||||
SYSTEM_DelayMs(200);
|
||||
}
|
||||
}
|
||||
|
||||
void RADIO_SendEndOfTransmission(void)
|
||||
@@ -1327,8 +1332,7 @@ void RADIO_SendEndOfTransmission(void)
|
||||
DTMF_SendEndOfTransmission();
|
||||
|
||||
// send the CTCSS/DCS tail tone - allows the receivers to mute the usual FM squelch tail/crash
|
||||
if(gEeprom.TAIL_TONE_ELIMINATION)
|
||||
RADIO_SendCssTail();
|
||||
RADIO_SendCssTail();
|
||||
RADIO_SetupRegisters(false);
|
||||
}
|
||||
|
||||
@@ -1338,7 +1342,6 @@ void RADIO_PrepareCssTX(void)
|
||||
|
||||
SYSTEM_DelayMs(200);
|
||||
|
||||
if(gEeprom.TAIL_TONE_ELIMINATION)
|
||||
RADIO_SendCssTail();
|
||||
RADIO_SendCssTail();
|
||||
RADIO_SetupRegisters(true);
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "dcs.h"
|
||||
#include "driver/bk4819.h"
|
||||
#include "frequencies.h"
|
||||
|
||||
enum {
|
||||
@@ -155,12 +156,14 @@ uint16_t RADIO_FindNextChannel(uint16_t ChNum, int8_t Direction, bool bCheckScan
|
||||
void RADIO_InitInfo(VFO_Info_t *pInfo, const uint16_t ChannelSave, const uint32_t Frequency);
|
||||
void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure);
|
||||
void RADIO_ConfigureSquelchAndOutputPower(VFO_Info_t *pInfo);
|
||||
void RADIO_ValidateAndSetCode(FREQ_Config_t *pFreq_Config, uint8_t tmp);
|
||||
void RADIO_ApplyOffset(VFO_Info_t *pInfo);
|
||||
void RADIO_SelectVfos(void);
|
||||
void RADIO_SetupRegisters(bool switchToForeground);
|
||||
#ifdef ENABLE_NOAA
|
||||
void RADIO_ConfigureNOAA(void);
|
||||
#endif
|
||||
BK4819_FilterBandwidth_t RADIO_GetAMFilterBandwidth(const VFO_Info_t *pVfo);
|
||||
void RADIO_SetTxParameters(void);
|
||||
void RADIO_SetupAGC(bool listeningAM, bool disable);
|
||||
void RADIO_SetModulation(ModulationMode_t modulation);
|
||||
|
||||
@@ -98,6 +98,38 @@ void SysTick_Handler(void)
|
||||
if (gCurrentFunction != FUNCTION_MONITOR && gCurrentFunction != FUNCTION_TRANSMIT)
|
||||
DECREMENT_AND_TRIGGER(gScanPauseDelayIn_10ms, gScheduleScanListen);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
// Scan stall watchdog: if the scan-resume countdown has expired
|
||||
// (gScanPauseDelayIn_10ms == 0) but no resume was actually scheduled
|
||||
// (gScheduleScanListen == false) and no real reception is happening
|
||||
// (squelch closed -> green LED off, not in RX/TX/MONITOR), the scan
|
||||
// loop is stuck. Force a resume after this many 10ms ticks. The
|
||||
// gScanPauseDelayIn_10ms == 0 guard ensures we never override the
|
||||
// user-configured ScnRev (SCAN_RESUME_MODE) pause: while that pause
|
||||
// is decrementing, the watchdog stays asleep.
|
||||
#define SCAN_FAST_STALL_WATCHDOG_10ms 25 // 250 ms
|
||||
static uint16_t scanFastStallCounter;
|
||||
|
||||
if (gSetting_set_scn
|
||||
&& gScanStateDir != SCAN_OFF
|
||||
&& gScanPauseDelayIn_10ms == 0
|
||||
&& !gScheduleScanListen
|
||||
&& !g_SquelchLost
|
||||
&& gCurrentFunction != FUNCTION_RECEIVE
|
||||
&& gCurrentFunction != FUNCTION_TRANSMIT
|
||||
&& gCurrentFunction != FUNCTION_MONITOR)
|
||||
{
|
||||
if (++scanFastStallCounter >= SCAN_FAST_STALL_WATCHDOG_10ms) {
|
||||
scanFastStallCounter = 0;
|
||||
gScheduleScanListen = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
scanFastStallCounter = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
DECREMENT_AND_TRIGGER(gTailNoteEliminationCountdown_10ms, gFlagTailNoteEliminationComplete);
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
|
||||
+105
-87
@@ -21,7 +21,49 @@
|
||||
#include "driver/vcp.h"
|
||||
#include "driver/keyboard.h"
|
||||
|
||||
static void Screenshot_Send(const uint8_t *buf, uint16_t len)
|
||||
// SRAM optimization: minimize static allocations
|
||||
// - previousHash: one fingerprint per 8-byte chunk instead of a full
|
||||
// 1024-byte copy of the previous frame (chunks are only compared,
|
||||
// never retransmitted from history). A 16-bit fingerprint keeps the
|
||||
// collision odds at ~1/65536 per chunk, so a stale chunk slipping
|
||||
// through is rare; the forcedBlock rotation repairs that residual
|
||||
// within at most 128 frames.
|
||||
// Stack optimization: chunks are computed on demand straight from
|
||||
// gStatusLine/gFrameBuffer instead of building the whole 1024-byte
|
||||
// frame on the stack. Changed chunks are tracked in a 16-byte bitmap.
|
||||
// Peak stack drops from ~1.3 KB to ~40 bytes, at the cost of
|
||||
// transforming each transmitted chunk twice (negligible next to the
|
||||
// blocking UART transfer).
|
||||
static uint16_t previousHash[128];
|
||||
static uint8_t forcedBlock = 0;
|
||||
static uint8_t keepAlive = 3;
|
||||
|
||||
// FNV-1a over one 8-byte chunk, folded to 16 bits
|
||||
static uint16_t SCREENSHOT_Hash(const uint8_t *data)
|
||||
{
|
||||
uint32_t h = 2166136261u;
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
h = (h ^ data[i]) * 16777619u;
|
||||
}
|
||||
return (uint16_t)(h ^ (h >> 16));
|
||||
}
|
||||
|
||||
void SCREENSHOT_ParseInput(void)
|
||||
{
|
||||
if (SCREENSHOT_IsLocked())
|
||||
return;
|
||||
|
||||
if (UART_IsCableConnected()) {
|
||||
keepAlive = 15;
|
||||
gUSB_ScreenshotEnabled = false;
|
||||
}
|
||||
else if (VCP_ScreenshotPing()) {
|
||||
keepAlive = 15;
|
||||
gUSB_ScreenshotEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void SCREENSHOT_Send(const uint8_t *buf, uint16_t len)
|
||||
{
|
||||
if (gUSB_ScreenshotEnabled) {
|
||||
cdc_acm_data_send_with_dtr(buf, len);
|
||||
@@ -30,93 +72,69 @@ static void Screenshot_Send(const uint8_t *buf, uint16_t len)
|
||||
}
|
||||
}
|
||||
|
||||
// SRAM optimization: minimize static allocations
|
||||
// - previousFrame: 1024 bytes (REQUIRED - need to compare for delta)
|
||||
// - No currentFrame or deltaFrame static buffers
|
||||
static uint8_t previousFrame[1024] = {0};
|
||||
static uint8_t forcedBlock = 0;
|
||||
static uint8_t keepAlive = 10;
|
||||
enum {
|
||||
SCREENSHOT_CHUNK_SIZE = 8,
|
||||
SCREENSHOT_CHUNKS_PER_LINE = 16,
|
||||
SCREENSHOT_HALF_LINE_COLUMNS = LCD_WIDTH / 2,
|
||||
};
|
||||
|
||||
void getScreenShot(bool force)
|
||||
// Compute one 8-byte output chunk directly from the display buffers.
|
||||
// Frame layout: per line (status + 7 frame lines), 8 bit layers of
|
||||
// 16 bytes each. Each bit layer is split into two 64-column chunks.
|
||||
static void SCREENSHOT_Chunk(uint8_t chunkIdx, uint8_t *dest)
|
||||
{
|
||||
// Build frame in a temporary stack buffer
|
||||
// This is 1024 bytes but it's temporary and gets freed after the function
|
||||
uint8_t frameBuffer[1024];
|
||||
uint16_t index = 0;
|
||||
uint8_t acc = 0;
|
||||
uint8_t bitCount = 0;
|
||||
const uint8_t chunkInLine = chunkIdx % SCREENSHOT_CHUNKS_PER_LINE;
|
||||
const uint8_t line = chunkIdx / SCREENSHOT_CHUNKS_PER_LINE;
|
||||
const uint8_t bit = chunkInLine / 2;
|
||||
const uint8_t columnBase = (chunkInLine % 2) * SCREENSHOT_HALF_LINE_COLUMNS;
|
||||
const uint8_t *src = (line == 0 ? gStatusLine : gFrameBuffer[line - 1])
|
||||
+ columnBase;
|
||||
|
||||
if (gUART_LockScreenshot > 0) {
|
||||
gUART_LockScreenshot--;
|
||||
for (uint8_t j = 0; j < SCREENSHOT_CHUNK_SIZE; j++) {
|
||||
uint8_t acc = 0;
|
||||
for (uint8_t k = 0; k < SCREENSHOT_CHUNK_SIZE; k++) {
|
||||
if (src[j * SCREENSHOT_CHUNK_SIZE + k] & (1 << bit)) acc |= (1 << k);
|
||||
}
|
||||
dest[j] = gSetting_set_inv ? ~acc : acc;
|
||||
}
|
||||
}
|
||||
|
||||
void SCREENSHOT_Update(bool force)
|
||||
{
|
||||
static bool wasConnected = false;
|
||||
|
||||
if (SCREENSHOT_IsLocked())
|
||||
return;
|
||||
}
|
||||
|
||||
if (UART_IsCableConnected()) {
|
||||
keepAlive = 10;
|
||||
gUSB_ScreenshotEnabled = false;
|
||||
}
|
||||
|
||||
if (VCP_ScreenshotPing()) {
|
||||
keepAlive = 10;
|
||||
gUSB_ScreenshotEnabled = true;
|
||||
}
|
||||
|
||||
if (keepAlive > 0) {
|
||||
if (--keepAlive == 0) return;
|
||||
if (--keepAlive == 0) {
|
||||
// Connection just lost → reset state for next reconnection
|
||||
wasConnected = false;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// ==== BUILD FRAME ONCE ====
|
||||
// Status line: 8 bit layers × 128 columns
|
||||
for (uint8_t b = 0; b < 8; b++) {
|
||||
for (uint8_t i = 0; i < 128; i++) {
|
||||
uint8_t bit = (gStatusLine[i] >> b) & 0x01;
|
||||
acc |= (bit << bitCount++);
|
||||
if (bitCount == 8) {
|
||||
frameBuffer[index++] = acc;
|
||||
acc = 0;
|
||||
bitCount = 0;
|
||||
}
|
||||
}
|
||||
// Connection is alive — detect reconnection and force full frame
|
||||
if (!wasConnected) {
|
||||
force = true;
|
||||
wasConnected = true;
|
||||
}
|
||||
|
||||
// Frame buffer: 7 lines × 8 bit layers × 128 columns
|
||||
for (uint8_t l = 0; l < 7; l++) {
|
||||
for (uint8_t b = 0; b < 8; b++) {
|
||||
for (uint8_t i = 0; i < 128; i++) {
|
||||
uint8_t bit = (gFrameBuffer[l][i] >> b) & 0x01;
|
||||
acc |= (bit << bitCount++);
|
||||
if (bitCount == 8) {
|
||||
frameBuffer[index++] = acc;
|
||||
acc = 0;
|
||||
bitCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bitCount > 0)
|
||||
frameBuffer[index++] = acc;
|
||||
|
||||
if (index != 1024)
|
||||
return;
|
||||
|
||||
// ==== FIRST PASS: Count changed chunks ====
|
||||
uint16_t deltaLen = 0;
|
||||
uint8_t changedChunks[128]; // List of changed chunk indices
|
||||
uint8_t changedCount = 0;
|
||||
uint8_t changedBitmap[16] = {0}; // 1 bit per chunk
|
||||
uint8_t chunk[9]; // [0] = index, [1..8] = payload
|
||||
|
||||
for (uint8_t chunk = 0; chunk < 128; chunk++) {
|
||||
uint8_t *cur = &frameBuffer[chunk * 8];
|
||||
uint8_t *prev = &previousFrame[chunk * 8];
|
||||
for (uint8_t chunkIdx = 0; chunkIdx < 128; chunkIdx++) {
|
||||
SCREENSHOT_Chunk(chunkIdx, &chunk[1]);
|
||||
|
||||
bool changed = memcmp(cur, prev, 8) != 0;
|
||||
bool isForced = (chunk == forcedBlock);
|
||||
bool fullUpdate = force;
|
||||
bool changed = SCREENSHOT_Hash(&chunk[1]) != previousHash[chunkIdx];
|
||||
bool isForced = (chunkIdx == forcedBlock);
|
||||
|
||||
if (changed || isForced || fullUpdate) {
|
||||
changedChunks[changedCount++] = chunk;
|
||||
if (changed || isForced || force) {
|
||||
changedBitmap[chunkIdx >> 3] |= 1 << (chunkIdx & 7);
|
||||
deltaLen += 9;
|
||||
}
|
||||
}
|
||||
@@ -135,7 +153,7 @@ void getScreenShot(bool force)
|
||||
// New format: sends 0xFF before header
|
||||
// Old format: doesn't exist, so viewers can differentiate
|
||||
uint8_t versionMarker = 0xFF;
|
||||
Screenshot_Send(&versionMarker, 1);
|
||||
SCREENSHOT_Send(&versionMarker, 1);
|
||||
|
||||
// ==== Send header ====
|
||||
uint8_t header[5] = {
|
||||
@@ -144,25 +162,25 @@ void getScreenShot(bool force)
|
||||
(uint8_t)(deltaLen & 0xFF)
|
||||
};
|
||||
|
||||
Screenshot_Send(header, 5);
|
||||
SCREENSHOT_Send(header, 5);
|
||||
|
||||
// ==== SECOND PASS: Send only changed chunks ====
|
||||
uint8_t chunk[9];
|
||||
|
||||
for (uint8_t i = 0; i < changedCount; i++) {
|
||||
uint8_t chunkIdx = changedChunks[i];
|
||||
uint8_t *cur = &frameBuffer[chunkIdx * 8];
|
||||
uint8_t *prev = &previousFrame[chunkIdx * 8];
|
||||
for (uint8_t chunkIdx = 0; chunkIdx < 128; chunkIdx++) {
|
||||
if (!(changedBitmap[chunkIdx >> 3] & (1 << (chunkIdx & 7))))
|
||||
continue;
|
||||
|
||||
chunk[0] = chunkIdx;
|
||||
memcpy(&chunk[1], cur, 8);
|
||||
|
||||
Screenshot_Send(chunk, 9);
|
||||
|
||||
// Update previousFrame for next comparison
|
||||
memcpy(prev, cur, 8);
|
||||
SCREENSHOT_Chunk(chunkIdx, &chunk[1]);
|
||||
|
||||
SCREENSHOT_Send(chunk, 9);
|
||||
|
||||
// Update the fingerprint only once the chunk is actually sent,
|
||||
// so chunks skipped by an early return stay marked as changed.
|
||||
// Hashing the recomputed payload also keeps the fingerprint in
|
||||
// sync if the display buffer changed between the two passes.
|
||||
previousHash[chunkIdx] = SCREENSHOT_Hash(&chunk[1]);
|
||||
}
|
||||
|
||||
uint8_t end = 0x0A;
|
||||
Screenshot_Send(&end, 1);
|
||||
}
|
||||
SCREENSHOT_Send(&end, 1);
|
||||
}
|
||||
+2
-1
@@ -17,6 +17,7 @@
|
||||
#ifndef SCREENSHOT_H
|
||||
#define SCREENSHOT_H
|
||||
|
||||
void getScreenShot(bool force);
|
||||
void SCREENSHOT_Update(bool force);
|
||||
void SCREENSHOT_ParseInput(void);
|
||||
|
||||
#endif
|
||||
+207
-56
@@ -30,6 +30,23 @@
|
||||
|
||||
EEPROM_Config_t gEeprom = { 0 };
|
||||
|
||||
// Load a DTMF code from EEPROM, falling back to default_val if invalid.
|
||||
static void SETTINGS_LoadEepromDtmf(uint32_t addr, char *dest, size_t size, const char *default_val)
|
||||
{
|
||||
uint8_t buf[16];
|
||||
|
||||
if (size > sizeof(buf))
|
||||
size = sizeof(buf);
|
||||
|
||||
PY25Q16_ReadBuffer(addr, buf, size);
|
||||
|
||||
if (DTMF_ValidateCodes((char *)buf, size)) {
|
||||
memcpy(dest, buf, size);
|
||||
} else {
|
||||
strcpy(dest, default_val);
|
||||
}
|
||||
}
|
||||
|
||||
void SETTINGS_InitEEPROM(void)
|
||||
{
|
||||
uint8_t Data[16] = {0};
|
||||
@@ -100,16 +117,29 @@ void SETTINGS_InitEEPROM(void)
|
||||
|
||||
// 5. Reset dBmCorrTable
|
||||
int8_t buf[7];
|
||||
for (uint8_t i = 0; i < 7; i++)
|
||||
buf[i] = dBmCorrTable[i]; // values from misc.c
|
||||
PY25Q16_WriteBuffer(0x00A0B9, buf, 7, false);
|
||||
PY25Q16_ReadBuffer(0x00A0B9, (uint8_t *)buf, 7);
|
||||
|
||||
needsWrite = true;
|
||||
for (uint8_t i = 0; i < 7; i++) {
|
||||
if ((uint8_t)buf[i] != 0xFF) {
|
||||
needsWrite = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (needsWrite) {
|
||||
for (uint8_t i = 0; i < 7; i++)
|
||||
buf[i] = dBmCorrTable[i];
|
||||
PY25Q16_WriteBuffer(0x00A0B9, buf, 7, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 0E70..0E77
|
||||
PY25Q16_ReadBuffer(0x00A000, Data, 8);
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
gSetting_set_audio = (Data[0] < 5) ? Data[0] : 0;
|
||||
gSetting_set_audio_fm = ((Data[0] & 0x0F) < 5) ? (Data[0] & 0x0F) : 0;
|
||||
gSetting_set_audio_am = (((Data[0] >> 4) & 0x0F) < 3) ? ((Data[0] >> 4) & 0x0F) : 0;
|
||||
#endif
|
||||
gEeprom.SQUELCH_LEVEL = (Data[1] < 10) ? Data[1] : 1;
|
||||
gEeprom.TX_TIMEOUT_TIMER = (Data[2] > 4 && Data[2] < 180) ? Data[2] : 11;
|
||||
@@ -205,9 +235,12 @@ gEeprom.FreqChannel[1] = IS_FREQ_CHANNEL(Data16[5]) ? Data16[5] : (FREQ_CHANNE
|
||||
|
||||
gEeprom.FM_Band = fmCfg.band;
|
||||
//gEeprom.FM_Space = fmCfg.space;
|
||||
|
||||
uint16_t freqLoLimit = BK1080_GetFreqLoLimit(gEeprom.FM_Band);
|
||||
|
||||
gEeprom.FM_SelectedFrequency =
|
||||
(fmCfg.selFreq >= BK1080_GetFreqLoLimit(gEeprom.FM_Band) && fmCfg.selFreq <= BK1080_GetFreqHiLimit(gEeprom.FM_Band)) ?
|
||||
fmCfg.selFreq : BK1080_GetFreqLoLimit(gEeprom.FM_Band);
|
||||
(fmCfg.selFreq >= freqLoLimit && fmCfg.selFreq <= BK1080_GetFreqHiLimit(gEeprom.FM_Band)) ?
|
||||
fmCfg.selFreq : freqLoLimit;
|
||||
|
||||
gEeprom.FM_SelectedChannel = fmCfg.selChn;
|
||||
gEeprom.FM_IsMrMode = fmCfg.isMrMode;
|
||||
@@ -281,51 +314,25 @@ gEeprom.FreqChannel[1] = IS_FREQ_CHANNEL(Data16[5]) ? Data16[5] : (FREQ_CHANNE
|
||||
PY25Q16_ReadBuffer(0x00A0A8 + 0x48, Data, 8);
|
||||
gEeprom.DTMF_CODE_PERSIST_TIME = (Data[0] < 101) ? Data[0] * 10 : 100;
|
||||
gEeprom.DTMF_CODE_INTERVAL_TIME = (Data[1] < 101) ? Data[1] * 10 : 100;
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
gEeprom.PERMIT_REMOTE_KILL = (Data[2] < 2) ? Data[2] : true;
|
||||
|
||||
// 0EE0..0EE7
|
||||
|
||||
PY25Q16_ReadBuffer(0x00A0F8, Data, sizeof(gEeprom.ANI_DTMF_ID));
|
||||
if (DTMF_ValidateCodes((char *)Data, sizeof(gEeprom.ANI_DTMF_ID))) {
|
||||
memcpy(gEeprom.ANI_DTMF_ID, Data, sizeof(gEeprom.ANI_DTMF_ID));
|
||||
} else {
|
||||
strcpy(gEeprom.ANI_DTMF_ID, "123");
|
||||
}
|
||||
|
||||
SETTINGS_LoadEepromDtmf(0x00A0F8, gEeprom.ANI_DTMF_ID, sizeof(gEeprom.ANI_DTMF_ID), "123");
|
||||
|
||||
// 0EE8..0EEF
|
||||
PY25Q16_ReadBuffer(0x00A0F8 + 0x8, Data, sizeof(gEeprom.KILL_CODE));
|
||||
if (DTMF_ValidateCodes((char *)Data, sizeof(gEeprom.KILL_CODE))) {
|
||||
memcpy(gEeprom.KILL_CODE, Data, sizeof(gEeprom.KILL_CODE));
|
||||
} else {
|
||||
strcpy(gEeprom.KILL_CODE, "ABCD9");
|
||||
}
|
||||
SETTINGS_LoadEepromDtmf(0x00A0F8 + 0x08, gEeprom.KILL_CODE, sizeof(gEeprom.KILL_CODE), "ABCD9");
|
||||
|
||||
// 0EF0..0EF7
|
||||
PY25Q16_ReadBuffer(0x00A0F8 + 0x10, Data, sizeof(gEeprom.REVIVE_CODE));
|
||||
if (DTMF_ValidateCodes((char *)Data, sizeof(gEeprom.REVIVE_CODE))) {
|
||||
memcpy(gEeprom.REVIVE_CODE, Data, sizeof(gEeprom.REVIVE_CODE));
|
||||
} else {
|
||||
strcpy(gEeprom.REVIVE_CODE, "9DCBA");
|
||||
}
|
||||
SETTINGS_LoadEepromDtmf(0x00A0F8 + 0x10, gEeprom.REVIVE_CODE, sizeof(gEeprom.REVIVE_CODE), "9DCBA");
|
||||
#endif
|
||||
|
||||
// 0EF8..0F07
|
||||
PY25Q16_ReadBuffer(0x00A0F8 + 0x18, Data, sizeof(gEeprom.DTMF_UP_CODE));
|
||||
if (DTMF_ValidateCodes((char *)Data, sizeof(gEeprom.DTMF_UP_CODE))) {
|
||||
memcpy(gEeprom.DTMF_UP_CODE, Data, sizeof(gEeprom.DTMF_UP_CODE));
|
||||
} else {
|
||||
strcpy(gEeprom.DTMF_UP_CODE, "12345");
|
||||
}
|
||||
SETTINGS_LoadEepromDtmf(0x00A0F8 + 0x18, gEeprom.DTMF_UP_CODE, sizeof(gEeprom.DTMF_UP_CODE), "12345");
|
||||
|
||||
// 0F08..0F17
|
||||
PY25Q16_ReadBuffer(0x00A0F8 + 0x28, Data, sizeof(gEeprom.DTMF_DOWN_CODE));
|
||||
if (DTMF_ValidateCodes((char *)Data, sizeof(gEeprom.DTMF_DOWN_CODE))) {
|
||||
memcpy(gEeprom.DTMF_DOWN_CODE, Data, sizeof(gEeprom.DTMF_DOWN_CODE));
|
||||
} else {
|
||||
strcpy(gEeprom.DTMF_DOWN_CODE, "54321");
|
||||
}
|
||||
SETTINGS_LoadEepromDtmf(0x00A0F8 + 0x28, gEeprom.DTMF_DOWN_CODE, sizeof(gEeprom.DTMF_DOWN_CODE), "54321");
|
||||
|
||||
// 0F18..0F1F
|
||||
PY25Q16_ReadBuffer(0x00A130, Data, 8);
|
||||
@@ -447,8 +454,20 @@ gEeprom.FreqChannel[1] = IS_FREQ_CHANNEL(Data16[5]) ? Data16[5] : (FREQ_CHANNE
|
||||
// 1FF0..0x1FF7
|
||||
// TODO: address TBD
|
||||
PY25Q16_ReadBuffer(0x00A158, Data, 8);
|
||||
const uint8_t set_ptt_scn_sav = Data[7] & 0x0F;
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
const bool set_ptt_scn_sav_valid = set_ptt_scn_sav < (SET_SAV_LEN << 2);
|
||||
#else
|
||||
const bool set_ptt_scn_sav_valid = set_ptt_scn_sav < 4;
|
||||
#endif
|
||||
gSetting_set_pwr = (((Data[7] & 0xF0) >> 4) < 7) ? ((Data[7] & 0xF0) >> 4) : 0;
|
||||
gSetting_set_ptt = (((Data[7] & 0x0F)) < 2) ? ((Data[7] & 0x0F)) : 0;
|
||||
gSetting_set_ptt = set_ptt_scn_sav_valid ? (set_ptt_scn_sav & 0x01) : 0;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
gSetting_set_scn = set_ptt_scn_sav_valid ? ((set_ptt_scn_sav & 0x02) == 0) : 1;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
gSetting_set_sav = set_ptt_scn_sav_valid ? ((set_ptt_scn_sav >> 2) & 0x03) : SET_SAV_OFF;
|
||||
#endif
|
||||
|
||||
gSetting_set_tot = (((Data[6] & 0xF0) >> 4) < 4) ? ((Data[6] & 0xF0) >> 4) : 0;
|
||||
gSetting_set_eot = (((Data[6] & 0x0F)) < 4) ? ((Data[6] & 0x0F)) : 0;
|
||||
@@ -488,10 +507,6 @@ gEeprom.FreqChannel[1] = IS_FREQ_CHANNEL(Data16[5]) ? Data16[5] : (FREQ_CHANNE
|
||||
gSetting_set_off = (Data[4] >> 1) > 120 ? 60 : (Data[4] >> 1);
|
||||
#endif
|
||||
|
||||
// Warning
|
||||
// Be aware, Data[3] is use by Spectrum
|
||||
// Warning
|
||||
|
||||
// And set special session settings for actions
|
||||
gSetting_set_ptt_session = gSetting_set_ptt;
|
||||
gEeprom.KEY_LOCK_PTT = gSetting_set_lck;
|
||||
@@ -576,6 +591,124 @@ uint32_t SETTINGS_FetchChannelFrequency(const uint16_t channel)
|
||||
return info.frequency;
|
||||
}
|
||||
|
||||
bool SETTINGS_FetchChannelScanInfo(const uint16_t channel, uint32_t *frequency, ModulationMode_t *modulation)
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint32_t frequency;
|
||||
uint32_t offset;
|
||||
uint8_t settings[4];
|
||||
} __attribute__((packed)) info;
|
||||
|
||||
PY25Q16_ReadBuffer(channel * 16, &info, sizeof(info));
|
||||
|
||||
if (frequency)
|
||||
*frequency = info.frequency;
|
||||
|
||||
if (modulation)
|
||||
{
|
||||
uint8_t mode = info.settings[3] >> 4;
|
||||
if (mode >= MODULATION_UKNOWN)
|
||||
mode = MODULATION_FM;
|
||||
*modulation = (ModulationMode_t)mode;
|
||||
}
|
||||
|
||||
return info.frequency != 0 && info.frequency != 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
bool SETTINGS_FetchChannelScanDisplayInfo(const uint16_t channel, ChannelScanDisplayInfo_t *info)
|
||||
{
|
||||
if (info == NULL)
|
||||
return false;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t frequency;
|
||||
uint32_t offset;
|
||||
uint8_t data[8];
|
||||
} __attribute__((packed)) raw;
|
||||
|
||||
PY25Q16_ReadBuffer(channel * 16, &raw, sizeof(raw));
|
||||
|
||||
if (raw.frequency == 0 || raw.frequency == 0xFFFFFFFF)
|
||||
return false;
|
||||
|
||||
memset(info, 0, sizeof(*info));
|
||||
|
||||
info->rx.Frequency = raw.frequency;
|
||||
info->tx.Frequency = raw.frequency;
|
||||
info->offset = (raw.offset >= _1GHz_in_KHz) ? (_1GHz_in_KHz / 100) : raw.offset;
|
||||
|
||||
info->rx.CodeType = (raw.data[2] >> 0) & 0x0F;
|
||||
info->tx.CodeType = (raw.data[2] >> 4) & 0x0F;
|
||||
RADIO_ValidateAndSetCode(&info->rx, raw.data[0]);
|
||||
RADIO_ValidateAndSetCode(&info->tx, raw.data[1]);
|
||||
|
||||
uint8_t tmp = raw.data[3] & 0x0F;
|
||||
if (tmp > TX_OFFSET_FREQUENCY_DIRECTION_SUB)
|
||||
tmp = TX_OFFSET_FREQUENCY_DIRECTION_OFF;
|
||||
info->txOffsetFrequencyDirection = tmp;
|
||||
|
||||
tmp = raw.data[3] >> 4;
|
||||
if (tmp >= MODULATION_UKNOWN)
|
||||
tmp = MODULATION_FM;
|
||||
info->modulation = (ModulationMode_t)tmp;
|
||||
|
||||
tmp = raw.data[6];
|
||||
if (tmp >= STEP_N_ELEM)
|
||||
tmp = STEP_12_5kHz;
|
||||
info->stepSetting = (STEP_Setting_t)tmp;
|
||||
info->stepFrequency = gStepFrequencyTable[tmp];
|
||||
|
||||
if (raw.data[4] == 0xFF)
|
||||
{
|
||||
info->frequencyReverse = false;
|
||||
info->channelBandwidth = BANDWIDTH_WIDE;
|
||||
info->outputPower = OUTPUT_POWER_LOW1;
|
||||
info->busyChannelLock = false;
|
||||
info->txLock = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint8_t d4 = raw.data[4];
|
||||
info->frequencyReverse = !!((d4 >> 0) & 1u);
|
||||
info->channelBandwidth = !!((d4 >> 1) & 1u);
|
||||
info->outputPower = ((d4 >> 2) & 7u);
|
||||
info->busyChannelLock = !!((d4 >> 5) & 1u);
|
||||
info->txLock = !!((d4 >> 6) & 1u);
|
||||
}
|
||||
|
||||
switch (info->txOffsetFrequencyDirection)
|
||||
{
|
||||
case TX_OFFSET_FREQUENCY_DIRECTION_ADD:
|
||||
info->tx.Frequency = raw.frequency + info->offset;
|
||||
break;
|
||||
case TX_OFFSET_FREQUENCY_DIRECTION_SUB:
|
||||
info->tx.Frequency = raw.frequency - info->offset;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (raw.data[5] == 0xFF)
|
||||
{
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
info->dtmfDecodingEnable = false;
|
||||
#endif
|
||||
info->dtmfPttIdTxMode = PTT_ID_OFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
info->dtmfDecodingEnable = (raw.data[5] >> 0) & 1u;
|
||||
#endif
|
||||
const uint8_t pttId = (raw.data[5] >> 1) & 7u;
|
||||
info->dtmfPttIdTxMode = pttId < ARRAY_SIZE(gSubMenu_PTT_ID) ? pttId : PTT_ID_OFF;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SETTINGS_FetchChannelName(char *s, const uint16_t channel)
|
||||
{
|
||||
if (s == NULL)
|
||||
@@ -605,16 +738,20 @@ void SETTINGS_FetchChannelName(char *s, const uint16_t channel)
|
||||
|
||||
void SETTINGS_FactoryReset(bool bIsAll)
|
||||
{
|
||||
PY25Q16_SectorErase(0x000000);
|
||||
PY25Q16_SectorErase(0x001000);
|
||||
PY25Q16_SectorErase(0x002000);
|
||||
PY25Q16_SectorErase(0x003000);
|
||||
PY25Q16_SectorErase(0x004000);
|
||||
PY25Q16_SectorErase(0x005000);
|
||||
PY25Q16_SectorErase(0x006000);
|
||||
PY25Q16_SectorErase(0x007000);
|
||||
PY25Q16_SectorErase(0x008000);
|
||||
PY25Q16_SectorErase(0x009000);
|
||||
// PY25Q16_SectorErase(0x000000);
|
||||
// PY25Q16_SectorErase(0x001000);
|
||||
// PY25Q16_SectorErase(0x002000);
|
||||
// PY25Q16_SectorErase(0x003000);
|
||||
// PY25Q16_SectorErase(0x004000);
|
||||
// PY25Q16_SectorErase(0x005000);
|
||||
// PY25Q16_SectorErase(0x006000);
|
||||
// PY25Q16_SectorErase(0x007000);
|
||||
// PY25Q16_SectorErase(0x008000);
|
||||
// PY25Q16_SectorErase(0x009000);
|
||||
|
||||
for (uint32_t addr = 0x000000; addr <= 0x009000; addr += 0x1000) {
|
||||
PY25Q16_SectorErase(addr);
|
||||
}
|
||||
|
||||
// 0d60 - 0e30
|
||||
if (bIsAll)
|
||||
@@ -748,9 +885,14 @@ void SETTINGS_SaveSettings(void)
|
||||
// 0x0E70
|
||||
State = SecBuf;
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
State[0] = gSetting_set_audio;
|
||||
State[0] = (gSetting_set_audio_fm & 0x0F) | ((gSetting_set_audio_am & 0x0F) << 4);
|
||||
#endif
|
||||
State[1] = gEeprom.SQUELCH_LEVEL;
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
if (gSquelchLevelOriginal < 10)
|
||||
State[1] = gSquelchLevelOriginal;
|
||||
else
|
||||
#endif
|
||||
State[1] = gEeprom.SQUELCH_LEVEL;
|
||||
State[2] = gEeprom.TX_TIMEOUT_TIMER;
|
||||
#ifdef ENABLE_NOAA
|
||||
State[3] = gEeprom.NOAA_AUTO_SCAN;
|
||||
@@ -997,7 +1139,16 @@ void SETTINGS_SaveSettings(void)
|
||||
|
||||
State[5] = ((tmp << 4) | (gSetting_set_ctr & 0x0F));
|
||||
State[6] = ((gSetting_set_tot << 4) | (gSetting_set_eot & 0x0F));
|
||||
State[7] = ((gSetting_set_pwr << 4) | (gSetting_set_ptt & 0x0F));
|
||||
uint8_t set_ptt_scn_sav = gSetting_set_ptt & 0x01;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
if (!gSetting_set_scn)
|
||||
set_ptt_scn_sav |= 0x02;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
set_ptt_scn_sav |= (gSetting_set_sav & 0x03) << 2;
|
||||
#endif
|
||||
|
||||
State[7] = ((gSetting_set_pwr << 4) | set_ptt_scn_sav);
|
||||
|
||||
gEeprom.KEY_LOCK_PTT = gSetting_set_lck;
|
||||
|
||||
|
||||
+28
-1
@@ -34,7 +34,10 @@ enum POWER_OnDisplayMode_t {
|
||||
#endif
|
||||
POWER_ON_DISPLAY_MODE_MESSAGE,
|
||||
POWER_ON_DISPLAY_MODE_VOLTAGE,
|
||||
POWER_ON_DISPLAY_MODE_NONE
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO
|
||||
POWER_ON_DISPLAY_MODE_LOGO,
|
||||
#endif
|
||||
POWER_ON_DISPLAY_MODE_NONE,
|
||||
};
|
||||
typedef enum POWER_OnDisplayMode_t POWER_OnDisplayMode_t;
|
||||
|
||||
@@ -127,6 +130,9 @@ enum ACTION_OPT_t {
|
||||
#ifdef ENABLE_REGA
|
||||
ACTION_OPT_REGA_ALARM,
|
||||
ACTION_OPT_REGA_TEST,
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
ACTION_OPT_BEAM,
|
||||
#endif
|
||||
ACTION_OPT_LEN
|
||||
};
|
||||
@@ -307,9 +313,30 @@ typedef struct {
|
||||
|
||||
extern EEPROM_Config_t gEeprom;
|
||||
|
||||
typedef struct {
|
||||
FREQ_Config_t rx;
|
||||
FREQ_Config_t tx;
|
||||
uint32_t offset;
|
||||
uint16_t stepFrequency;
|
||||
STEP_Setting_t stepSetting;
|
||||
ModulationMode_t modulation;
|
||||
uint8_t txOffsetFrequencyDirection;
|
||||
uint8_t outputPower;
|
||||
bool frequencyReverse;
|
||||
uint8_t channelBandwidth;
|
||||
uint8_t busyChannelLock;
|
||||
uint8_t txLock;
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
uint8_t dtmfDecodingEnable;
|
||||
#endif
|
||||
PTT_ID_t dtmfPttIdTxMode;
|
||||
} ChannelScanDisplayInfo_t;
|
||||
|
||||
void SETTINGS_InitEEPROM(void);
|
||||
void SETTINGS_LoadCalibration(void);
|
||||
uint32_t SETTINGS_FetchChannelFrequency(const uint16_t channel);
|
||||
bool SETTINGS_FetchChannelScanInfo(const uint16_t channel, uint32_t *frequency, ModulationMode_t *modulation);
|
||||
bool SETTINGS_FetchChannelScanDisplayInfo(const uint16_t channel, ChannelScanDisplayInfo_t *info);
|
||||
void SETTINGS_FetchChannelName(char *s, const uint16_t channel);
|
||||
void SETTINGS_FactoryReset(bool bIsAll);
|
||||
#ifdef ENABLE_FMRADIO
|
||||
|
||||
+45
-52
@@ -37,9 +37,8 @@ static int get_bit(uint8_t* array, int bit_index) {
|
||||
|
||||
void UI_DisplayAircopy(void)
|
||||
{
|
||||
char String[16] = { 0 };
|
||||
char *pPrintStr = { 0 };
|
||||
uint16_t percent;
|
||||
char String[16];
|
||||
char *pPrintStr;
|
||||
|
||||
UI_DisplayClear();
|
||||
|
||||
@@ -60,15 +59,13 @@ void UI_DisplayAircopy(void)
|
||||
// show the remaining 2 small frequency digits
|
||||
UI_PrintStringSmallNormal(String + 7, 97, 0, 3);
|
||||
String[7] = 0;
|
||||
// show the main large frequency digits
|
||||
UI_DisplayFrequency(String, 16, 2, false);
|
||||
} else {
|
||||
const char *ascii = INPUTBOX_GetAscii();
|
||||
sprintf(String, "%.3s.%.3s", ascii, ascii + 3);
|
||||
UI_DisplayFrequency(String, 16, 2, false);
|
||||
}
|
||||
|
||||
memset(String, 0, sizeof(String));
|
||||
// show the main large frequency digits
|
||||
UI_DisplayFrequency(String, 16, 2, false);
|
||||
|
||||
// Get the current map and calculate percentage based on its total blocks
|
||||
const AIRCOPY_TransferMap_t *currentMap = AIRCOPY_GetCurrentMap();
|
||||
@@ -78,47 +75,44 @@ void UI_DisplayAircopy(void)
|
||||
if (doneBlocks > currentMap->total_blocks)
|
||||
doneBlocks = currentMap->total_blocks;
|
||||
|
||||
percent = (doneBlocks * 10000) / currentMap->total_blocks;
|
||||
|
||||
if (gAirCopyIsSendMode == 0) {
|
||||
sprintf(String, "RCV:%02u.%02u%% E:%d", percent / 100, percent % 100, gErrorsDuringAirCopy);
|
||||
} else if (gAirCopyIsSendMode == 1) {
|
||||
sprintf(String, "SND:%02u.%02u%%", percent / 100, percent % 100);
|
||||
}
|
||||
|
||||
// Draw gauge
|
||||
if(gAircopyStep != 0)
|
||||
{
|
||||
UI_PrintString(String, 2, 127, 5, 8);
|
||||
|
||||
gFrameBuffer[4][1] = 0x3c;
|
||||
gFrameBuffer[4][2] = 0x42;
|
||||
|
||||
for(uint8_t i = 1; i <= AIRCOPY_BAR_WIDTH + 2; i++)
|
||||
{
|
||||
gFrameBuffer[4][2 + i] = 0x81;
|
||||
}
|
||||
|
||||
gFrameBuffer[4][125] = 0x42;
|
||||
gFrameBuffer[4][126] = 0x3c;
|
||||
}
|
||||
|
||||
// Draw memory selection
|
||||
if(gAircopyState == AIRCOPY_READY)
|
||||
if (gAircopyState == AIRCOPY_READY)
|
||||
{
|
||||
doneBlocks = 0;
|
||||
|
||||
memset(gFrameBuffer[5], 0, 128);
|
||||
memset(gFrameBuffer[6], 0, 128);
|
||||
|
||||
if(gAircopyCurrentMapIndex < AIRCOPY_NUM_BANKS) {
|
||||
sprintf(String, "MEM %03u - %03u%", (gAircopyCurrentMapIndex * 128) + 1, (gAircopyCurrentMapIndex + 1) * 128);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(String, "Settings");
|
||||
sprintf(String, "MEM %03u - %03u", (gAircopyCurrentMapIndex * 128) + 1, (gAircopyCurrentMapIndex + 1) * 128);
|
||||
} else {
|
||||
strcpy(String, "Settings");
|
||||
}
|
||||
UI_PrintString(String, 2, 127, 5, 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t percent = (doneBlocks * 10000) / currentMap->total_blocks;
|
||||
|
||||
if (gAirCopyIsSendMode == 0) {
|
||||
sprintf(String, "RCV:%02u.%02u%% E:%d", percent / 100, percent % 100, gErrorsDuringAirCopy);
|
||||
} else {
|
||||
sprintf(String, "SND:%02u.%02u%%", percent / 100, percent % 100);
|
||||
}
|
||||
|
||||
// Draw gauge
|
||||
if(gAircopyStep != 0)
|
||||
{
|
||||
UI_PrintString(String, 2, 127, 5, 8);
|
||||
|
||||
gFrameBuffer[4][1] = 0x3c;
|
||||
gFrameBuffer[4][2] = 0x42;
|
||||
|
||||
for(uint8_t i = 1; i <= AIRCOPY_BAR_WIDTH + 2; i++)
|
||||
{
|
||||
gFrameBuffer[4][2 + i] = 0x81;
|
||||
}
|
||||
|
||||
gFrameBuffer[4][125] = 0x42;
|
||||
gFrameBuffer[4][126] = 0x3c;
|
||||
}
|
||||
}
|
||||
|
||||
if (doneBlocks > 0)
|
||||
@@ -131,19 +125,12 @@ void UI_DisplayAircopy(void)
|
||||
lErrorsDuringAirCopy = gErrorsDuringAirCopy;
|
||||
}
|
||||
|
||||
const AIRCOPY_TransferMap_t *currentMap = AIRCOPY_GetCurrentMap();
|
||||
|
||||
uint16_t total = currentMap->total_blocks;
|
||||
uint16_t done = gAirCopyBlockNumber + gErrorsDuringAirCopy;
|
||||
|
||||
if (done > total) done = total;
|
||||
uint16_t b = 0;
|
||||
uint16_t fraction_accumulator = 0;
|
||||
|
||||
for (uint8_t col = 0; col < AIRCOPY_BAR_WIDTH; col++)
|
||||
{
|
||||
/* Map column [0..BAR_WIDTH-1] to block [0..total-1] */
|
||||
uint16_t b = (uint16_t)((col * (uint32_t)total) / AIRCOPY_BAR_WIDTH);
|
||||
|
||||
bool processed = (b < done);
|
||||
bool processed = (b < doneBlocks);
|
||||
bool error = processed && get_bit(crc, b);
|
||||
|
||||
if (!processed)
|
||||
@@ -152,8 +139,14 @@ void UI_DisplayAircopy(void)
|
||||
gFrameBuffer[4][col + 4] = 0x81; // error gap (intentional hole)
|
||||
else
|
||||
gFrameBuffer[4][col + 4] = 0xBD; // ok filled
|
||||
}
|
||||
|
||||
// DDA/Bresenham algorythm
|
||||
fraction_accumulator += currentMap->total_blocks;
|
||||
while (fraction_accumulator >= AIRCOPY_BAR_WIDTH) {
|
||||
fraction_accumulator -= AIRCOPY_BAR_WIDTH;
|
||||
b++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ST7565_BlitFullScreen();
|
||||
|
||||
+5
-2
@@ -31,10 +31,14 @@
|
||||
|
||||
void UI_DisplayFM(void)
|
||||
{
|
||||
char String[16] = {0};
|
||||
char String[16];
|
||||
char *pPrintStr = String;
|
||||
UI_DisplayClear();
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
UI_DisplayUnlockKeyboard(5);
|
||||
#endif
|
||||
|
||||
UI_PrintString("FM", 2, 0, 0, 8);
|
||||
|
||||
sprintf(String, "%d%s-%dM",
|
||||
@@ -76,7 +80,6 @@ void UI_DisplayFM(void)
|
||||
|
||||
UI_PrintString(pPrintStr, 0, 127, 3, 10); // memory, vfo, scan
|
||||
|
||||
memset(String, 0, sizeof(String));
|
||||
if (gAskToSave || (gEeprom.FM_IsMrMode && gInputBoxIndex > 0)) {
|
||||
UI_GenerateChannelString(String, gFM_ChannelPosition);
|
||||
} else if (gAskToDelete) {
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "ui/helper.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "misc.h"
|
||||
#include "settings.h"
|
||||
|
||||
|
||||
void UI_GenerateChannelString(char *pString, const uint16_t Channel)
|
||||
@@ -39,6 +40,8 @@ void UI_GenerateChannelString(char *pString, const uint16_t Channel)
|
||||
pString[2] = '-';
|
||||
for (i = 0; i < 2; i++)
|
||||
pString[i + 3] = (gInputBox[i] == 10) ? '-' : gInputBox[i] + '0';
|
||||
|
||||
pString[5] = 0;
|
||||
}
|
||||
|
||||
void UI_GenerateChannelStringEx(char *pString, const bool bShowPrefix, const uint16_t ChannelNumber)
|
||||
@@ -321,6 +324,50 @@ static void sort(int16_t *a, int16_t *b)
|
||||
x += 4;
|
||||
}
|
||||
}
|
||||
|
||||
void GUI_DisplaySmallestInverse(const char *pString, uint8_t x, uint8_t Line,
|
||||
bool statusbar, bool fill, uint8_t end)
|
||||
{
|
||||
// First draw the string normally
|
||||
GUI_DisplaySmallest(pString, x, (Line * 8) + 1, statusbar, fill);
|
||||
|
||||
// Now invert the framebuffer/statusline bits for the rendered area
|
||||
uint8_t start = (x - 2);
|
||||
uint8_t *buffer = statusbar ? gStatusLine : gFrameBuffer[Line];
|
||||
|
||||
buffer[start] ^= 0x3E;
|
||||
for (uint8_t i = start + 1; i < end; i++) {
|
||||
buffer[i] ^= 0x7F;
|
||||
}
|
||||
buffer[end] ^= 0x3E;
|
||||
}
|
||||
|
||||
void UI_DisplayUnlockKeyboard(uint8_t shift) {
|
||||
if (gEeprom.KEY_LOCK && gKeypadLocked > 0)
|
||||
{ // tell user how to unlock the keyboard
|
||||
|
||||
//memcpy(gFrameBuffer[shift] + 2, gFontKeyLock, sizeof(gFontKeyLock));
|
||||
UI_PrintStringSmallBold("UNLOCK KEYBOARD", 12, 0, shift);
|
||||
//memcpy(gFrameBuffer[shift] + 120, gFontKeyLock, sizeof(gFontKeyLock));
|
||||
|
||||
/*
|
||||
for (uint8_t i = 12; i < 116; i++)
|
||||
{
|
||||
gFrameBuffer[shift][i] ^= 0xFF;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
bool IsEmptyName(const char *name, uint8_t len) {
|
||||
if (name[0] == '\0' || name[0] == '\xff')
|
||||
return true;
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
if (name[i] != ' ' && name[i] != '\xff' && name[i] != '\0')
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void UI_DrawLineBuffer(uint8_t (*buffer)[128], int16_t x1, int16_t y1, int16_t x2, int16_t y2, bool black)
|
||||
@@ -378,3 +425,8 @@ void UI_DisplayClear()
|
||||
{
|
||||
memset(gFrameBuffer, 0, sizeof(gFrameBuffer));
|
||||
}
|
||||
|
||||
void UI_StatusClear()
|
||||
{
|
||||
memset(gStatusLine, 0, sizeof(gStatusLine));
|
||||
}
|
||||
@@ -38,10 +38,14 @@ void UI_DrawPixelBuffer(uint8_t (*buffer)[128], uint8_t x, uint8_t y, bool black
|
||||
void PutPixel(uint8_t x, uint8_t y, bool fill);
|
||||
void PutPixelStatus(uint8_t x, uint8_t y, bool fill);
|
||||
void GUI_DisplaySmallest(const char *pString, uint8_t x, uint8_t y, bool statusbar, bool fill);
|
||||
void GUI_DisplaySmallestInverse(const char *pString, uint8_t x, uint8_t Line, bool statusbar, bool fill, uint8_t endX);
|
||||
void UI_DisplayUnlockKeyboard(uint8_t shift);
|
||||
bool IsEmptyName(const char *name, uint8_t len);
|
||||
#endif
|
||||
void UI_DrawLineBuffer(uint8_t (*buffer)[128], int16_t x1, int16_t y1, int16_t x2, int16_t y2, bool black);
|
||||
void UI_DrawRectangleBuffer(uint8_t (*buffer)[128], int16_t x1, int16_t y1, int16_t x2, int16_t y2, bool black);
|
||||
|
||||
void UI_DisplayClear();
|
||||
void UI_StatusClear();
|
||||
|
||||
#endif
|
||||
@@ -41,4 +41,19 @@ const char* INPUTBOX_GetAscii()
|
||||
inputBoxAscii[i] = (c==10)? '-' : '0' + c;
|
||||
}
|
||||
return inputBoxAscii;
|
||||
}
|
||||
|
||||
const char* INPUTBOX_GetAsciiAlignRight() {
|
||||
int targetPos = 7;
|
||||
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
if (gInputBox[i] != 10) {
|
||||
inputBoxAscii[targetPos--] = '0' + gInputBox[i];
|
||||
}
|
||||
}
|
||||
|
||||
while(targetPos >= 0)
|
||||
inputBoxAscii[targetPos--] = '-';
|
||||
|
||||
return inputBoxAscii;
|
||||
}
|
||||
@@ -25,7 +25,9 @@ extern char gInputBox[8];
|
||||
extern uint8_t gInputBoxIndex;
|
||||
|
||||
void INPUTBOX_Append(const KEY_Code_t Digit);
|
||||
|
||||
const char* INPUTBOX_GetAscii();
|
||||
const char* INPUTBOX_GetAsciiAlignRight();
|
||||
|
||||
#endif
|
||||
|
||||
+2
-11
@@ -34,7 +34,7 @@ static void Render(void)
|
||||
unsigned int i;
|
||||
char String[7];
|
||||
|
||||
memset(gStatusLine, 0, sizeof(gStatusLine));
|
||||
UI_StatusClear();
|
||||
UI_DisplayClear();
|
||||
|
||||
UI_PrintString("PASSWORD", 0, 127, 1, 10);
|
||||
@@ -80,16 +80,7 @@ void UI_DisplayLock(void)
|
||||
|
||||
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:
|
||||
case KEY_0...KEY_9:
|
||||
INPUTBOX_Append(Key - KEY_0);
|
||||
|
||||
if (gInputBoxIndex < 6) // 6 frequency digits
|
||||
|
||||
+878
-122
File diff suppressed because it is too large.
Load diff
+19
-1
@@ -17,14 +17,21 @@
|
||||
#ifndef UI_MAIN_H
|
||||
#define UI_MAIN_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum center_line_t {
|
||||
CENTER_LINE_NONE = 0,
|
||||
CENTER_LINE_IN_USE,
|
||||
CENTER_LINE_SCAN_PROGRESS,
|
||||
CENTER_LINE_AUDIO_BAR,
|
||||
CENTER_LINE_AUDIO_SCOPE,
|
||||
CENTER_LINE_RSSI,
|
||||
CENTER_LINE_AM_FIX_DATA,
|
||||
CENTER_LINE_DTMF_DEC,
|
||||
CENTER_LINE_CHARGE_DATA
|
||||
CENTER_LINE_CHARGE_DATA,
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
CENTER_LINE_BEAM
|
||||
#endif
|
||||
};
|
||||
|
||||
enum Vfo_txtr_mode{
|
||||
@@ -37,10 +44,21 @@ typedef enum center_line_t center_line_t;
|
||||
|
||||
extern center_line_t center_line;
|
||||
|
||||
#ifdef ENABLE_AUDIO_BAR
|
||||
void UI_DisplayAudioBar(void);
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO_SCOPE
|
||||
void UI_DisplayAudioScope(void);
|
||||
#endif
|
||||
void UI_MAIN_TimeSlice500ms(void);
|
||||
void UI_DisplayMain(void);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_PROGRESS
|
||||
void UI_MAIN_NotifyScanProgressDataChanged(void);
|
||||
#else
|
||||
static inline void UI_MAIN_NotifyScanProgressDataChanged(void) {}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_AGC_SHOW_DATA
|
||||
void UI_MAIN_PrintAGC(bool force);
|
||||
#endif
|
||||
|
||||
+355
-143
@@ -27,6 +27,7 @@
|
||||
#include "../driver/eeprom.h"
|
||||
#include "../driver/st7565.h"
|
||||
#include "../external/printf/printf.h"
|
||||
#include "../font.h"
|
||||
#include "../frequencies.h"
|
||||
#include "../helper/battery.h"
|
||||
#include "../misc.h"
|
||||
@@ -40,6 +41,7 @@
|
||||
#include "inputbox.h"
|
||||
#include "menu.h"
|
||||
#include "ui.h"
|
||||
#include "welcome.h"
|
||||
|
||||
|
||||
const t_menu_item MenuList[] =
|
||||
@@ -166,6 +168,12 @@ const t_menu_item MenuList[] =
|
||||
#ifdef ENABLE_NOAA
|
||||
{"SetNWR", MENU_NOAA_S },
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
{"SetScn", MENU_SET_SCN },
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
{"SetSav", MENU_SET_SAV },
|
||||
#endif
|
||||
#endif
|
||||
// hidden menu items from here on
|
||||
// enabled if pressing both the PTT and upper side button at power-on
|
||||
@@ -192,7 +200,7 @@ const t_menu_item MenuList[] =
|
||||
|
||||
const uint8_t FIRST_HIDDEN_MENU_ITEM = MENU_F_LOCK;
|
||||
|
||||
const char gSubMenu_TXP[][6] =
|
||||
const char* const gSubMenu_TXP[] =
|
||||
{
|
||||
"USER",
|
||||
"LOW 1",
|
||||
@@ -204,29 +212,26 @@ const char gSubMenu_TXP[][6] =
|
||||
"HIGH"
|
||||
};
|
||||
|
||||
const char gSubMenu_SFT_D[][4] =
|
||||
const char* const gSubMenu_SFT_D[] =
|
||||
{
|
||||
"OFF",
|
||||
"+",
|
||||
"-"
|
||||
};
|
||||
|
||||
const char gSubMenu_W_N[][7] =
|
||||
const char* const gSubMenu_W_N[] =
|
||||
{
|
||||
"WIDE",
|
||||
"NARROW"
|
||||
};
|
||||
|
||||
const char gSubMenu_OFF_ON[][4] =
|
||||
const char* const gSubMenu_OFF_ON[] =
|
||||
{
|
||||
"OFF",
|
||||
"ON"
|
||||
};
|
||||
|
||||
const char gSubMenu_NA[4] =
|
||||
{
|
||||
"N/A"
|
||||
};
|
||||
const char* gSubMenu_NA = "N/A";
|
||||
|
||||
const char* const gSubMenu_RXMode[] =
|
||||
{
|
||||
@@ -237,7 +242,7 @@ const char* const gSubMenu_RXMode[] =
|
||||
};
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
const char gSubMenu_VOICE[][4] =
|
||||
const char* const gSubMenu_VOICE[] =
|
||||
{
|
||||
"OFF",
|
||||
"CHI",
|
||||
@@ -254,7 +259,7 @@ const char* const gSubMenu_MDF[] =
|
||||
};
|
||||
|
||||
#ifdef ENABLE_ALARM
|
||||
const char gSubMenu_AL_MOD[][5] =
|
||||
const char* const gSubMenu_AL_MOD[] =
|
||||
{
|
||||
"SITE",
|
||||
"TONE"
|
||||
@@ -262,7 +267,7 @@ const char* const gSubMenu_MDF[] =
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
const char gSubMenu_D_RSP[][11] =
|
||||
const char* const gSubMenu_D_RSP[] =
|
||||
{
|
||||
"DO\nNOTHING",
|
||||
"RING",
|
||||
@@ -280,7 +285,7 @@ const char* const gSubMenu_PTT_ID[] =
|
||||
"APOLLO\nQUINDAR"
|
||||
};
|
||||
|
||||
const char gSubMenu_PONMSG[][8] =
|
||||
const char* const gSubMenu_PONMSG[] =
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
"ALL",
|
||||
@@ -290,23 +295,36 @@ const char gSubMenu_PONMSG[][8] =
|
||||
#endif
|
||||
"MESSAGE",
|
||||
"VOLTAGE",
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO
|
||||
"LOGO",
|
||||
#endif
|
||||
"NONE"
|
||||
};
|
||||
|
||||
const char gSubMenu_ROGER[][6] =
|
||||
#if defined(ENABLE_FEAT_F4HWN) && defined(ENABLE_FEAT_F4HWN_LOGO_SAV)
|
||||
const char* const gSubMenu_SET_SAV[] =
|
||||
{
|
||||
"OFF",
|
||||
"LOGO",
|
||||
"LOGO+",
|
||||
"MATRIX"
|
||||
};
|
||||
#endif
|
||||
|
||||
const char* const gSubMenu_ROGER[] =
|
||||
{
|
||||
"OFF",
|
||||
"ROGER",
|
||||
"MDC"
|
||||
};
|
||||
|
||||
const char gSubMenu_RESET[][4] =
|
||||
const char* const gSubMenu_RESET[] =
|
||||
{
|
||||
"VFO",
|
||||
"ALL"
|
||||
};
|
||||
|
||||
const char * const gSubMenu_F_LOCK[] =
|
||||
const char* const gSubMenu_F_LOCK[] =
|
||||
{
|
||||
"DEFAULT+\n137-174\n400-470",
|
||||
"FCC HAM\n144-148\n420-450",
|
||||
@@ -327,7 +345,7 @@ const char * const gSubMenu_F_LOCK[] =
|
||||
"UNLOCK\nALL",
|
||||
};
|
||||
|
||||
const char gSubMenu_RX_TX[][6] =
|
||||
const char* const gSubMenu_RX_TX[] =
|
||||
{
|
||||
"OFF",
|
||||
"TX",
|
||||
@@ -335,14 +353,14 @@ const char gSubMenu_RX_TX[][6] =
|
||||
"TX/RX"
|
||||
};
|
||||
|
||||
const char gSubMenu_BAT_TXT[][8] =
|
||||
const char* const gSubMenu_BAT_TXT[] =
|
||||
{
|
||||
"NONE",
|
||||
"VOLTAGE",
|
||||
"PERCENT"
|
||||
};
|
||||
|
||||
const char gSubMenu_BATTYP[][12] =
|
||||
const char* const gSubMenu_BATTYP[] =
|
||||
{
|
||||
"1600mAh K5",
|
||||
"2200mAh K5",
|
||||
@@ -351,14 +369,14 @@ const char gSubMenu_BATTYP[][12] =
|
||||
"2500mAh K1"
|
||||
};
|
||||
|
||||
const char gSubMenu_SET_NAV[][17] =
|
||||
const char* const gSubMenu_SET_NAV[] =
|
||||
{
|
||||
"LEFT\nRIGHT\nUV-K1",
|
||||
"UP\nDOWN\nUV-K5(8)",
|
||||
};
|
||||
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
const char gSubMenu_SCRAMBLER[][7] =
|
||||
const char* const gSubMenu_SCRAMBLER[] =
|
||||
{
|
||||
"OFF",
|
||||
"2600Hz",
|
||||
@@ -375,7 +393,7 @@ const char gSubMenu_SCRAMBLER[][7] =
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
const char gSubMenu_SET_PWR[][6] =
|
||||
const char* const gSubMenu_SET_PWR[] =
|
||||
{
|
||||
"< 20m",
|
||||
"125m",
|
||||
@@ -386,13 +404,13 @@ const char gSubMenu_SCRAMBLER[][7] =
|
||||
"5"
|
||||
};
|
||||
|
||||
const char gSubMenu_SET_PTT[][8] =
|
||||
const char* const gSubMenu_SET_PTT[] =
|
||||
{
|
||||
"CLASSIC",
|
||||
"ONEPUSH"
|
||||
};
|
||||
|
||||
const char gSubMenu_SET_TOT[][7] = // Use by SET_EOT too
|
||||
const char* const gSubMenu_SET_TOT[] =
|
||||
{
|
||||
"OFF",
|
||||
"SOUND",
|
||||
@@ -400,20 +418,28 @@ const char gSubMenu_SCRAMBLER[][7] =
|
||||
"ALL"
|
||||
};
|
||||
|
||||
const char gSubMenu_SET_LCK[][9] =
|
||||
const char* const gSubMenu_SET_LCK[] =
|
||||
{
|
||||
"KEYS",
|
||||
"KEYS+PTT"
|
||||
};
|
||||
|
||||
const char gSubMenu_SET_MET[][8] =
|
||||
const char* const gSubMenu_SET_MET[] =
|
||||
{
|
||||
"TINY",
|
||||
"CLASSIC"
|
||||
};
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
const char* const gSubMenu_SET_SCN[] =
|
||||
{
|
||||
"NORMAL",
|
||||
"FAST"
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
const char gSubMenu_SET_AUD[][6] =
|
||||
const char* const gSubMenu_SET_AUD_FM[] =
|
||||
{
|
||||
"FLAT",
|
||||
"CLEAN",
|
||||
@@ -421,10 +447,17 @@ const char gSubMenu_SCRAMBLER[][7] =
|
||||
"BOOST",
|
||||
"MAX"
|
||||
};
|
||||
|
||||
const char* const gSubMenu_SET_AUD_AM[] =
|
||||
{
|
||||
"SHARP",
|
||||
"STOCK",
|
||||
"OPEN"
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
const char gSubMenu_SET_NFM[][9] =
|
||||
const char* const gSubMenu_SET_NFM[] =
|
||||
{
|
||||
"NARROW",
|
||||
"NARROWER"
|
||||
@@ -432,7 +465,7 @@ const char gSubMenu_SCRAMBLER[][7] =
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
const char gSubMenu_SET_KEY[][9] =
|
||||
const char* const gSubMenu_SET_KEY[] =
|
||||
{
|
||||
"KEY_MENU",
|
||||
"KEY_UP",
|
||||
@@ -473,7 +506,7 @@ const t_sidefunction gSubMenu_SIDEFUNCTIONS[] =
|
||||
{"VFO\nMEM", ACTION_OPT_VFO_MR},
|
||||
{"MODE", ACTION_OPT_SWITCH_DEMODUL},
|
||||
#ifdef ENABLE_BLMIN_TMP_OFF
|
||||
{"BLMIN\nTMP OFF", ACTION_OPT_BLMIN_TMP_OFF}, //BackLight Minimum Temporay OFF
|
||||
{"BLMIN\nTMP OFF", ACTION_OPT_BLMIN_TMP_OFF}, //BackLight Minimum Temporary OFF
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
{"RX MODE", ACTION_OPT_RXMODE},
|
||||
@@ -488,6 +521,9 @@ const t_sidefunction gSubMenu_SIDEFUNCTIONS[] =
|
||||
{"POWER\nHIGH", ACTION_OPT_POWER_HIGH},
|
||||
{"REMOVE\nOFFSET", ACTION_OPT_REMOVE_OFFSET},
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
{"BEAM", ACTION_OPT_BEAM},
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -516,6 +552,64 @@ int32_t gSubMenuSelection;
|
||||
char edit_original[17]; // a copy of the text before editing so that we can easily test for changes/difference
|
||||
char edit[17];
|
||||
int edit_index;
|
||||
bool edit_is_uppercase = false;
|
||||
|
||||
static void UI_MENU_DrawTopRightRoundedBadge(const char *text, const uint8_t line, const bool center_in_area, const uint8_t area_x1, const uint8_t area_x2)
|
||||
{
|
||||
const size_t length = strlen(text);
|
||||
const size_t char_pitch = ARRAY_SIZE(gFontSmall[0]) + 1u;
|
||||
const size_t text_width = length * char_pitch;
|
||||
const size_t capsule_span = text_width + 1u; // matches UI_PrintStringSmallNormalInverse x_end computation
|
||||
uint8_t text_x;
|
||||
|
||||
if (length == 0 || line == 0 || line >= FRAME_LINES) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (center_in_area && area_x2 > area_x1 + 2u) {
|
||||
const uint8_t min_x = area_x1 + 1u;
|
||||
uint8_t max_x;
|
||||
const uint8_t area_width = area_x2 - area_x1 + 1u;
|
||||
|
||||
if (capsule_span >= area_width) {
|
||||
text_x = min_x;
|
||||
} else {
|
||||
text_x = (uint8_t)(area_x1 + ((area_width - capsule_span) / 2u));
|
||||
}
|
||||
|
||||
if (area_x2 > capsule_span) {
|
||||
max_x = (uint8_t)(area_x2 - capsule_span);
|
||||
} else {
|
||||
max_x = min_x;
|
||||
}
|
||||
|
||||
if (max_x < min_x) {
|
||||
max_x = min_x;
|
||||
}
|
||||
if (text_x < min_x) {
|
||||
text_x = min_x;
|
||||
} else if (text_x > max_x) {
|
||||
text_x = max_x;
|
||||
}
|
||||
} else {
|
||||
if (capsule_span >= (LCD_WIDTH - 3u)) {
|
||||
text_x = 1u;
|
||||
} else {
|
||||
const uint8_t global_shift_right = 1u;
|
||||
const uint8_t base_text_x = (uint8_t)(LCD_WIDTH - capsule_span - 3u);
|
||||
const uint8_t max_text_x = (uint8_t)(LCD_WIDTH - capsule_span - 1u);
|
||||
const uint16_t shifted_x = (uint16_t)base_text_x + global_shift_right;
|
||||
|
||||
if (shifted_x > max_text_x) {
|
||||
text_x = max_text_x;
|
||||
} else {
|
||||
text_x = (uint8_t)shifted_x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UI_PrintStringSmallNormalInverse(text, text_x, 0, line);
|
||||
}
|
||||
|
||||
void UI_DisplayMenu(void)
|
||||
{
|
||||
@@ -524,6 +618,9 @@ void UI_DisplayMenu(void)
|
||||
const unsigned int menu_item_x2 = LCD_WIDTH - 1;
|
||||
unsigned int i;
|
||||
char String[64]; // bigger cuz we can now do multi-line in one string (use '\n' char)
|
||||
char top_right_badge[16];
|
||||
|
||||
const int m = UI_MENU_GetCurrentMenuId();
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
char Contact[16];
|
||||
@@ -571,57 +668,56 @@ void UI_DisplayMenu(void)
|
||||
#else
|
||||
{ // new menu layout .. experimental & unfinished
|
||||
const int menu_index = gMenuCursor; // current selected menu item
|
||||
i = 1;
|
||||
const int menu_count = (int)gMenuListCount;
|
||||
|
||||
if (!gIsInSubMenu) {
|
||||
while (i < 2)
|
||||
{ // leading menu items - small text
|
||||
const int k = menu_index + i - 2;
|
||||
if (k < 0)
|
||||
UI_PrintStringSmallNormal(MenuList[gMenuListCount + k].name, 0, 0, i); // wrap-a-round
|
||||
else if (k >= 0 && k < (int)gMenuListCount)
|
||||
UI_PrintStringSmallNormal(MenuList[k].name, 0, 0, i);
|
||||
i++;
|
||||
}
|
||||
if (menu_index >= 0 && menu_index < menu_count)
|
||||
{
|
||||
if (!gIsInSubMenu)
|
||||
{
|
||||
// leading menu items - small text
|
||||
int prev_index = menu_index - 1;
|
||||
if (prev_index < 0) {
|
||||
prev_index = menu_count - 1;
|
||||
}
|
||||
UI_PrintStringSmallNormal(MenuList[prev_index].name, 0, 0, 1);
|
||||
|
||||
// current menu item - keep big n fat
|
||||
if (menu_index >= 0 && menu_index < (int)gMenuListCount)
|
||||
// current menu item - keep big n fat
|
||||
UI_PrintString(MenuList[menu_index].name, 0, 0, 2, 8);
|
||||
i++;
|
||||
|
||||
while (i < 4)
|
||||
{ // trailing menu item - small text
|
||||
const int k = menu_index + i - 2;
|
||||
if (k >= 0 && k < (int)gMenuListCount)
|
||||
UI_PrintStringSmallNormal(MenuList[k].name, 0, 0, 1 + i);
|
||||
else if (k >= (int)gMenuListCount)
|
||||
UI_PrintStringSmallNormal(MenuList[gMenuListCount - k].name, 0, 0, 1 + i); // wrap-a-round
|
||||
i++;
|
||||
// trailing menu item - small text
|
||||
int next_index = menu_index + 1;
|
||||
if (next_index >= menu_count) {
|
||||
next_index = 0;
|
||||
}
|
||||
UI_PrintStringSmallNormal(MenuList[next_index].name, 0, 0, 4);
|
||||
|
||||
|
||||
// draw the menu index number/count
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
sprintf(String, "%2u.%u", 1 + menu_index, menu_count);
|
||||
UI_PrintStringSmallNormal(String, 2, 0, 6);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// current menu item
|
||||
// strcat(String, ":");
|
||||
UI_PrintString(MenuList[menu_index].name, 0, 0, 0, 8);
|
||||
// UI_PrintStringSmallNormal(String, 0, 0, 0);
|
||||
}
|
||||
|
||||
// draw the menu index number/count
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
sprintf(String, "%2u.%u", 1 + gMenuCursor, gMenuListCount);
|
||||
UI_PrintStringSmallNormal(String, 2, 0, 6);
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
sprintf(String, "%02u/%u", 1 + menu_index, menu_count);
|
||||
UI_PrintStringSmallNormal(String, 6, 0, 6);
|
||||
#endif
|
||||
}
|
||||
else if (menu_index >= 0 && menu_index < (int)gMenuListCount)
|
||||
{ // current menu item
|
||||
// strcat(String, ":");
|
||||
UI_PrintString(MenuList[menu_index].name, 0, 0, 0, 8);
|
||||
// UI_PrintStringSmallNormal(String, 0, 0, 0);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
sprintf(String, "%02u/%u", 1 + gMenuCursor, gMenuListCount);
|
||||
UI_PrintStringSmallNormal(String, 6, 0, 6);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
// **************
|
||||
|
||||
memset(String, 0, sizeof(String));
|
||||
String[0] = '\0';
|
||||
top_right_badge[0] = '\0';
|
||||
|
||||
bool already_printed = false;
|
||||
|
||||
@@ -637,7 +733,7 @@ void UI_DisplayMenu(void)
|
||||
uint8_t gaugeMax = 0;
|
||||
//#endif
|
||||
|
||||
switch (UI_MENU_GetCurrentMenuId())
|
||||
switch (m)
|
||||
{
|
||||
case MENU_SQL:
|
||||
sprintf(String, "%d", gSubMenuSelection);
|
||||
@@ -835,6 +931,12 @@ void UI_DisplayMenu(void)
|
||||
strcpy(String, gSubMenu_OFF_ON[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN) && defined(ENABLE_FEAT_F4HWN_LOGO_SAV)
|
||||
case MENU_SET_SAV:
|
||||
strcpy(String, gSubMenu_SET_SAV[gSubMenuSelection]);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case MENU_MEM_CH:
|
||||
case MENU_1_CALL:
|
||||
case MENU_DEL_CH:
|
||||
@@ -858,7 +960,7 @@ void UI_DisplayMenu(void)
|
||||
{ // show the frequency so that the user knows the channels frequency
|
||||
const uint32_t frequency = SETTINGS_FetchChannelFrequency(gSubMenuSelection);
|
||||
sprintf(String, "%u.%05u", frequency / 100000, frequency % 100000);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 4, 8);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 5, 8);
|
||||
}
|
||||
|
||||
SETTINGS_FetchChannelName(String, gSubMenuSelection);
|
||||
@@ -892,15 +994,34 @@ void UI_DisplayMenu(void)
|
||||
{ // show the channel name being edited
|
||||
//UI_PrintString(edit, menu_item_x1, 0, 2, 8);
|
||||
UI_PrintString(edit, menu_item_x1, menu_item_x2, 2, 8);
|
||||
if (edit_index < 10)
|
||||
//UI_PrintString("^", menu_item_x1 + (8 * edit_index), 0, 4, 8); // show the cursor
|
||||
UI_PrintString("^", menu_item_x1 - 1 + (8 * edit_index),0, 4, 8); // show the cursor
|
||||
if (edit_index < 10) {
|
||||
// UI_PrintString("^", menu_item_x1 - 1 + (8 * edit_index),0, 4, 8); // show the cursor
|
||||
uint8_t x = menu_item_x1 - 1;
|
||||
for (uint8_t i = 0; i < 10; i++)
|
||||
{
|
||||
if (i != edit_index)
|
||||
{
|
||||
if (edit[i] != 'g' && edit[i] != 'j')
|
||||
{
|
||||
UI_DrawLineBuffer(gFrameBuffer, x, 29, x + 6, 29, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UI_DrawLineBuffer(gFrameBuffer, x + 2, 30, x + 4, 30, 1);
|
||||
UI_DrawPixelBuffer(gFrameBuffer, x + 3, 29, 1);
|
||||
}
|
||||
x += 8;
|
||||
}
|
||||
|
||||
UI_PrintStringSmallNormal(edit_is_uppercase ? "ABC" : "abc", 77, 0, 4);
|
||||
}
|
||||
}
|
||||
|
||||
if (!gAskForConfirmation)
|
||||
{ // show the frequency so that the user knows the channels frequency
|
||||
sprintf(String, "%u.%05u", frequency / 100000, frequency % 100000);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 4 + (gIsInSubMenu && edit_index >= 0), 8);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 5, 8);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -968,34 +1089,22 @@ void UI_DisplayMenu(void)
|
||||
break;
|
||||
|
||||
case MENU_LIST_CH:
|
||||
case MENU_S_LIST:
|
||||
if (gSubMenuSelection == MR_CHANNELS_LIST + 1)
|
||||
strcpy(String, "ALL");
|
||||
else if (gSubMenuSelection == 0)
|
||||
else if (gSubMenuSelection == 0 && m == MENU_LIST_CH)
|
||||
strcpy(String, "OFF");
|
||||
else {
|
||||
const char *name = gListName[gSubMenuSelection - 1];
|
||||
|
||||
// If first character is empty/invalid, display "N/A"
|
||||
if (name[0] == '\0' || name[0] == '\xff' || name[0] == ' ')
|
||||
if (IsEmptyName(name, sizeof(gListName[0])))
|
||||
sprintf(String, "%02u", gSubMenuSelection);
|
||||
else
|
||||
sprintf(String, "%02u (%.3s)", gSubMenuSelection, name);
|
||||
}
|
||||
break;
|
||||
|
||||
case MENU_S_LIST:
|
||||
if (gSubMenuSelection == MR_CHANNELS_LIST + 1)
|
||||
strcpy(String, "ALL");
|
||||
else {
|
||||
const char *name = gListName[gSubMenuSelection - 1];
|
||||
// If first character is empty/invalid, display "N/A"
|
||||
if (name[0] == '\0' || name[0] == '\xff' || name[0] == ' ')
|
||||
sprintf(String, "%02u", gSubMenuSelection);
|
||||
else
|
||||
sprintf(String, "%02u (%.3s)", gSubMenuSelection, name);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
#ifdef ENABLE_ALARM
|
||||
case MENU_AL_MOD:
|
||||
sprintf(String, gSubMenu_AL_MOD[gSubMenuSelection]);
|
||||
@@ -1008,11 +1117,19 @@ void UI_DisplayMenu(void)
|
||||
break;
|
||||
#endif
|
||||
case MENU_UPCODE:
|
||||
sprintf(String, "%.8s\n%.8s", gEeprom.DTMF_UP_CODE, gEeprom.DTMF_UP_CODE + 8);
|
||||
if (gEeprom.DTMF_UP_CODE[8] != '\0' && gEeprom.DTMF_UP_CODE[8] != 0xFF) {
|
||||
sprintf(String, "%.8s\n%.8s", gEeprom.DTMF_UP_CODE, gEeprom.DTMF_UP_CODE + 8);
|
||||
} else {
|
||||
sprintf(String, "%.8s", gEeprom.DTMF_UP_CODE);
|
||||
}
|
||||
break;
|
||||
|
||||
case MENU_DWCODE:
|
||||
sprintf(String, "%.8s\n%.8s", gEeprom.DTMF_DOWN_CODE, gEeprom.DTMF_DOWN_CODE + 8);
|
||||
if (gEeprom.DTMF_DOWN_CODE[8] != '\0' && gEeprom.DTMF_DOWN_CODE[8] != 0xFF) {
|
||||
sprintf(String, "%.8s\n%.8s", gEeprom.DTMF_DOWN_CODE, gEeprom.DTMF_DOWN_CODE + 8);
|
||||
} else {
|
||||
sprintf(String, "%.8s", gEeprom.DTMF_DOWN_CODE);
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
@@ -1054,18 +1171,98 @@ void UI_DisplayMenu(void)
|
||||
strcpy(String, gSubMenu_ROGER[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
case MENU_VOL:
|
||||
case MENU_VOL: {
|
||||
// SysInf is paginated. Pages appear in this order, only when their
|
||||
// feature flag is enabled:
|
||||
// 0 -> identity
|
||||
// next -> Build date/time (ENABLE_FEAT_F4HWN)
|
||||
// next -> Battery (ENABLE_FEAT_F4HWN)
|
||||
// next -> Flash / SRAM usage (ENABLE_FEAT_F4HWN_MEM)
|
||||
// next, +1 -> CODE / WIKI QR codes (ENABLE_FEAT_F4HWN_QRCODE)
|
||||
// In non-F4HWN builds, page 0 keeps the old battery-voltage display.
|
||||
const uint8_t page = (uint8_t)gSubMenuSelection;
|
||||
uint8_t p = 0;
|
||||
|
||||
if (page == p++) {
|
||||
// Page 0: firmware identity.
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
sprintf(String, "%s\n%s",
|
||||
AUTHOR_STRING_2,
|
||||
VERSION_STRING_2
|
||||
);
|
||||
sprintf(String, "%s\n%s", AUTHOR_STRING_2, VERSION_STRING_2);
|
||||
UI_PrintStringSmallNormal(Edition, menu_item_x1 - 1, menu_item_x2, 6);
|
||||
#else
|
||||
sprintf(String, "%u.%02uV\n%u%%",
|
||||
gBatteryVoltageAverage / 100, gBatteryVoltageAverage % 100,
|
||||
BATTERY_VoltsToPercent(gBatteryVoltageAverage));
|
||||
sprintf(String, "%u.%02uV\n%u%%",
|
||||
gBatteryVoltageAverage / 100, gBatteryVoltageAverage % 100,
|
||||
BATTERY_VoltsToPercent(gBatteryVoltageAverage));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
if (page == p++) {
|
||||
strcpy(top_right_badge, "BUILD");
|
||||
UI_PrintStringSmallNormal(BuildDate, menu_item_x1 - 1, menu_item_x2, 3);
|
||||
UI_PrintStringSmallNormal(BuildTime, menu_item_x1 - 1, menu_item_x2, 4);
|
||||
UI_PrintStringSmallNormal(BuildCommit, menu_item_x1 - 1, menu_item_x2, 6);
|
||||
|
||||
already_printed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (page == p++) {
|
||||
char val[16];
|
||||
|
||||
strcpy(top_right_badge, "BATTERY");
|
||||
|
||||
sprintf(val, "%u.%02uV %u%%",
|
||||
gBatteryVoltageAverage / 100, gBatteryVoltageAverage % 100,
|
||||
BATTERY_VoltsToPercent(gBatteryVoltageAverage));
|
||||
UI_PrintStringSmallNormal(val, menu_item_x1 - 1, menu_item_x2, 3);
|
||||
|
||||
UI_PrintStringSmallNormal(gSubMenu_BATTYP[gEeprom.BATTERY_TYPE], menu_item_x1 - 1, menu_item_x2, 5);
|
||||
|
||||
already_printed = true;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_MEM
|
||||
if (page == p++) {
|
||||
uint16_t flash_pct = 0;
|
||||
uint16_t ram_pct = 0;
|
||||
UI_GetMemPercents(&flash_pct, &ram_pct);
|
||||
|
||||
char val[16];
|
||||
|
||||
// MEMORY title capsule centered in right zone, fb line 1.
|
||||
strcpy(top_right_badge, "MEMORY");
|
||||
|
||||
// Flash + SRAM values stacked below, normal small font, with a fb-line of breathing space.
|
||||
sprintf(val, "FLASH %u.%u%%",
|
||||
(unsigned)(flash_pct / 100), (unsigned)((flash_pct / 10) % 10));
|
||||
UI_PrintStringSmallNormal(val, menu_item_x1 - 1, menu_item_x2, 3);
|
||||
|
||||
sprintf(val, "SRAM %u.%u%%",
|
||||
(unsigned)(ram_pct / 100), (unsigned)((ram_pct / 10) % 10));
|
||||
UI_PrintStringSmallNormal(val, menu_item_x1 - 1, menu_item_x2, 5);
|
||||
|
||||
already_printed = true;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_QRCODE
|
||||
// Right zone: x=49..127 (79 px). QR centered at x=72..104.
|
||||
// Capsule label above QR (small-font Inverse style at fb line 1).
|
||||
if (page == p || page == p + 1) {
|
||||
const bool is_wiki = (page == (p + 1));
|
||||
|
||||
strcpy(top_right_badge, is_wiki ? "WIKI" : "CODE");
|
||||
UI_DrawQRCode(is_wiki, 72, 28);
|
||||
|
||||
already_printed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
p += 2;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
case MENU_RESET:
|
||||
strcpy(String, gSubMenu_RESET[gSubMenuSelection]);
|
||||
@@ -1192,9 +1389,26 @@ void UI_DisplayMenu(void)
|
||||
strcpy(String, gSubMenu_SET_MET[gSubMenuSelection]); // Same as SET_MET
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
case MENU_SET_SCN:
|
||||
strcpy(String, gSubMenu_SET_SCN[gSubMenuSelection]);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
case MENU_SET_AUD:
|
||||
strcpy(String, gSubMenu_SET_AUD[gSubMenuSelection]);
|
||||
if(gTxVfo->Modulation == MODULATION_AM) {
|
||||
strcpy(String, gSubMenu_SET_AUD_AM[gSubMenuSelection]);
|
||||
strcpy(top_right_badge, "AM");
|
||||
}
|
||||
else if (gTxVfo->Modulation == MODULATION_USB) {
|
||||
strcpy(String, "USB");
|
||||
strcpy(top_right_badge, "USB");
|
||||
}
|
||||
else {
|
||||
strcpy(String, gSubMenu_SET_AUD_FM[gSubMenuSelection]);
|
||||
strcpy(top_right_badge, "FM");
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
@@ -1220,12 +1434,8 @@ void UI_DisplayMenu(void)
|
||||
gaugeMax = 63;
|
||||
//#endif
|
||||
}
|
||||
gEeprom.VOLUME_GAIN = gSubMenuSelection;
|
||||
BK4819_WriteRegister(BK4819_REG_48,
|
||||
(11u << 12) | // ??? .. 0 ~ 15, doesn't seem to make any difference
|
||||
( 0u << 10) | // AF Rx Gain-1
|
||||
(gEeprom.VOLUME_GAIN << 4) | // AF Rx Gain-2
|
||||
(gEeprom.DAC_GAIN << 0)); // AF DAC Gain (after Gain-1 and Gain-2)
|
||||
// gEeprom.VOLUME_GAIN = gSubMenuSelection;
|
||||
BK4819_SetRxAudioGain();
|
||||
break;
|
||||
#endif
|
||||
|
||||
@@ -1253,7 +1463,7 @@ void UI_DisplayMenu(void)
|
||||
unsigned int len = strlen(String);
|
||||
bool small = false;
|
||||
|
||||
if (len > 0)
|
||||
if (String[0] != '\0')
|
||||
{
|
||||
// count number of lines
|
||||
for (i = 0; i < len; i++)
|
||||
@@ -1282,23 +1492,6 @@ void UI_DisplayMenu(void)
|
||||
|
||||
y = (small ? 3 : 2) - (lines / 2);
|
||||
|
||||
// only for SysInf
|
||||
if(UI_MENU_GetCurrentMenuId() == MENU_VOL)
|
||||
{
|
||||
sprintf(edit, "%u.%02uV %u%%",
|
||||
gBatteryVoltageAverage / 100, gBatteryVoltageAverage % 100,
|
||||
BATTERY_VoltsToPercent(gBatteryVoltageAverage)
|
||||
);
|
||||
|
||||
UI_PrintStringSmallNormal(edit, 54, 127, 1);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
UI_PrintStringSmallNormal(Edition, 54, 127, 6);
|
||||
#endif
|
||||
|
||||
y = 2;
|
||||
}
|
||||
|
||||
// draw the text lines
|
||||
for (i = 0; i < len && lines > 0; lines--)
|
||||
{
|
||||
@@ -1320,16 +1513,16 @@ void UI_DisplayMenu(void)
|
||||
}
|
||||
}
|
||||
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_S_PRI_CH_1 || UI_MENU_GetCurrentMenuId() == MENU_S_PRI_CH_2)
|
||||
if (m == MENU_S_PRI_CH_1 || m == MENU_S_PRI_CH_2)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
if ((UI_MENU_GetCurrentMenuId() == MENU_R_CTCS || UI_MENU_GetCurrentMenuId() == MENU_R_DCS) && gCssBackgroundScan)
|
||||
if ((m == MENU_R_CTCS || m == MENU_R_DCS) && gCssBackgroundScan)
|
||||
UI_PrintString("SCAN", menu_item_x1, menu_item_x2, 4, 8);
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_D_LIST && gIsDtmfContactValid) {
|
||||
if (m == MENU_D_LIST && gIsDtmfContactValid) {
|
||||
Contact[11] = 0;
|
||||
memcpy(&gDTMF_ID, Contact + 8, 4);
|
||||
sprintf(String, "ID:%4s", gDTMF_ID);
|
||||
@@ -1337,26 +1530,45 @@ void UI_DisplayMenu(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_R_CTCS ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_T_CTCS ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_R_DCS ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_T_DCS
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
|| UI_MENU_GetCurrentMenuId() == MENU_D_LIST
|
||||
#endif
|
||||
) {
|
||||
sprintf(String, "%2d", gSubMenuSelection);
|
||||
UI_PrintStringSmallNormal(String, 105, 0, 0);
|
||||
const bool is_ctcs = (m == MENU_R_CTCS || m == MENU_T_CTCS);
|
||||
const bool is_dcs = (m == MENU_R_DCS || m == MENU_T_DCS);
|
||||
|
||||
if (is_ctcs || is_dcs) {
|
||||
if (gSubMenuSelection == 0) {
|
||||
strcpy(top_right_badge, is_ctcs ? "00/00" : "000/00");
|
||||
} else {
|
||||
const uint8_t approved_index = is_ctcs ?
|
||||
DCS_GetCtcssApprovedIndex(gSubMenuSelection - 1) :
|
||||
DCS_GetDcsApprovedIndex(gSubMenuSelection - 1);
|
||||
|
||||
const uint8_t width = is_ctcs ? 2 : 3;
|
||||
|
||||
if (approved_index != 0xFF) {
|
||||
sprintf(top_right_badge, "%0*u/%02u", width, (unsigned)gSubMenuSelection, (unsigned)approved_index + 1);
|
||||
} else {
|
||||
sprintf(top_right_badge, "%0*u/--", width, (unsigned)gSubMenuSelection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((UI_MENU_GetCurrentMenuId() == MENU_RESET ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_MEM_CH ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_DEL_CH) && gAskForConfirmation)
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
if (m == MENU_D_LIST) {
|
||||
sprintf(top_right_badge, "%03d", gSubMenuSelection);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (top_right_badge[0] != '\0') {
|
||||
UI_MENU_DrawTopRightRoundedBadge(top_right_badge, 1, true, menu_item_x1, menu_item_x2);
|
||||
}
|
||||
|
||||
if ((m == MENU_RESET ||
|
||||
m == MENU_MEM_CH ||
|
||||
m == MENU_MEM_NAME ||
|
||||
m == MENU_DEL_CH) && gAskForConfirmation)
|
||||
{ // display confirmation
|
||||
char *pPrintStr = (gAskForConfirmation == 1) ? "SURE?" : "WAIT!";
|
||||
UI_PrintString(pPrintStr, menu_item_x1, menu_item_x2, 5, 8);
|
||||
}
|
||||
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
}
|
||||
+56
-32
@@ -23,7 +23,7 @@
|
||||
#include "audio.h" // VOICE_ID_t
|
||||
#include "settings.h"
|
||||
|
||||
typedef struct {
|
||||
typedef struct __attribute__((packed)) {
|
||||
const char name[7]; // menu display area only has room for 6 characters
|
||||
uint8_t menu_id;
|
||||
} t_menu_item;
|
||||
@@ -137,6 +137,9 @@ enum
|
||||
MENU_SET_MET,
|
||||
MENU_SET_GUI,
|
||||
MENU_SET_TMR,
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
MENU_SET_SCN,
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
MENU_SET_NFM,
|
||||
#endif
|
||||
@@ -160,68 +163,88 @@ enum
|
||||
MENU_F2SHRT,
|
||||
MENU_F2LONG,
|
||||
MENU_MLONG,
|
||||
MENU_BATTYP
|
||||
MENU_BATTYP,
|
||||
#if defined(ENABLE_FEAT_F4HWN) && defined(ENABLE_FEAT_F4HWN_LOGO_SAV)
|
||||
MENU_SET_SAV
|
||||
#endif
|
||||
};
|
||||
|
||||
extern const uint8_t FIRST_HIDDEN_MENU_ITEM;
|
||||
extern const t_menu_item MenuList[];
|
||||
|
||||
extern const char gSubMenu_TXP[8][6];
|
||||
extern const char gSubMenu_SFT_D[3][4];
|
||||
extern const char gSubMenu_W_N[2][7];
|
||||
extern const char gSubMenu_OFF_ON[2][4];
|
||||
extern const char gSubMenu_NA[4];
|
||||
extern const char gSubMenu_TOT[11][7];
|
||||
extern const char* const gSubMenu_RXMode[4];
|
||||
extern const char* const gSubMenu_TXP[8];
|
||||
extern const char* const gSubMenu_SFT_D[3];
|
||||
extern const char* const gSubMenu_W_N[2];
|
||||
extern const char* const gSubMenu_OFF_ON[2];
|
||||
extern const char* gSubMenu_NA;
|
||||
extern const char* const gSubMenu_TOT[11];
|
||||
extern const char* const gSubMenu_RXMode[4];
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
extern const char gSubMenu_VOICE[3][4];
|
||||
extern const char* const gSubMenu_VOICE[3];
|
||||
#endif
|
||||
extern const char* const gSubMenu_MDF[4];
|
||||
extern const char* const gSubMenu_MDF[4];
|
||||
#ifdef ENABLE_ALARM
|
||||
extern const char gSubMenu_AL_MOD[2][5];
|
||||
extern const char* const gSubMenu_AL_MOD[2];
|
||||
#endif
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
extern const char gSubMenu_D_RSP[4][11];
|
||||
extern const char* const gSubMenu_D_RSP[4];
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
extern const char gSubMenu_SET_PWR[7][6];
|
||||
extern const char gSubMenu_SET_PTT[2][8];
|
||||
extern const char gSubMenu_SET_TOT[4][7];
|
||||
extern const char gSubMenu_SET_LCK[2][9];
|
||||
extern const char gSubMenu_SET_MET[2][8];
|
||||
extern const char* const gSubMenu_SET_PWR[7];
|
||||
extern const char* const gSubMenu_SET_PTT[2];
|
||||
extern const char* const gSubMenu_SET_TOT[4];
|
||||
extern const char* const gSubMenu_SET_LCK[2];
|
||||
extern const char* const gSubMenu_SET_MET[2];
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
extern const char* const gSubMenu_SET_SCN[2];
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
extern const char gSubMenu_SET_NFM[2][9];
|
||||
extern const char* const gSubMenu_SET_NFM[2];
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
extern const char gSubMenu_SET_KEY[][9];
|
||||
extern const char* const gSubMenu_SET_KEY[5];
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
extern const char gSubMenu_SET_AUD[5][6];
|
||||
extern const char* const gSubMenu_SET_AUD_FM[5];
|
||||
extern const char* const gSubMenu_SET_AUD_AM[3];
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
extern const char* const gSubMenu_SET_SAV[];
|
||||
#endif
|
||||
#endif
|
||||
|
||||
extern const char* const gSubMenu_PTT_ID[5];
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
extern const char gSubMenu_PONMSG[5][8];
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO
|
||||
extern const char* const gSubMenu_PONMSG[6];
|
||||
#else
|
||||
extern const char* const gSubMenu_PONMSG[5];
|
||||
#endif
|
||||
#else
|
||||
extern const char gSubMenu_PONMSG[4][8];
|
||||
extern const char* const gSubMenu_PONMSG[4];
|
||||
#endif
|
||||
extern const char gSubMenu_ROGER[3][6];
|
||||
extern const char gSubMenu_RESET[2][4];
|
||||
extern const char* const gSubMenu_F_LOCK[F_LOCK_LEN];
|
||||
extern const char gSubMenu_RX_TX[4][6];
|
||||
extern const char gSubMenu_BAT_TXT[3][8];
|
||||
extern const char gSubMenu_BATTYP[5][12];
|
||||
|
||||
extern const char* const gSubMenu_ROGER[3];
|
||||
extern const char* const gSubMenu_RESET[2];
|
||||
extern const char* const gSubMenu_F_LOCK[F_LOCK_LEN];
|
||||
extern const char* const gSubMenu_RX_TX[4];
|
||||
extern const char* const gSubMenu_BAT_TXT[3];
|
||||
extern const char* const gSubMenu_BATTYP[5];
|
||||
extern const char* const gSubMenu_SET_NAV[2];
|
||||
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
extern const char gSubMenu_SCRAMBLER[11][7];
|
||||
extern const char* const gSubMenu_SCRAMBLER[11];
|
||||
#endif
|
||||
|
||||
typedef struct {char* name; uint8_t id;} t_sidefunction;
|
||||
typedef struct /* __attribute__((packed)) */ {
|
||||
const char* name;
|
||||
uint8_t id;
|
||||
} t_sidefunction;
|
||||
|
||||
extern const uint8_t gSubMenu_SIDEFUNCTIONS_size;
|
||||
extern const t_sidefunction gSubMenu_SIDEFUNCTIONS[];
|
||||
extern const t_sidefunction gSubMenu_SIDEFUNCTIONS[];
|
||||
|
||||
extern bool gIsInSubMenu;
|
||||
|
||||
@@ -232,6 +255,7 @@ extern int32_t gSubMenuSelection;
|
||||
extern char edit_original[17];
|
||||
extern char edit[17];
|
||||
extern int edit_index;
|
||||
extern bool edit_is_uppercase;
|
||||
|
||||
void UI_DisplayMenu(void);
|
||||
int UI_MENU_GetCurrentMenuId();
|
||||
|
||||
+34
-36
@@ -26,58 +26,56 @@
|
||||
|
||||
void UI_DisplayScanner(void)
|
||||
{
|
||||
char String[16] = {0};
|
||||
char String[16];
|
||||
char *pPrintStr = String;
|
||||
bool bCentered;
|
||||
uint8_t Start;
|
||||
|
||||
UI_DisplayClear();
|
||||
|
||||
if (gScanSingleFrequency || (gScanCssState != SCAN_CSS_STATE_OFF && gScanCssState != SCAN_CSS_STATE_FAILED)) {
|
||||
sprintf(String, "FREQ:%u.%05u", gScanFrequency / 100000, gScanFrequency % 100000);
|
||||
// 1st line
|
||||
if (gScannerSaveState == SCAN_SAVE_CHANNEL) {
|
||||
pPrintStr = "Save?";
|
||||
} else if (gScannerSaveState == SCAN_SAVE_CHAN_SEL) {
|
||||
strcpy(String, "Save:");
|
||||
UI_GenerateChannelStringEx(String + 5, gShowChPrefix, gScanChannel);
|
||||
pPrintStr = String;
|
||||
} else if ((gScanCssState < SCAN_CSS_STATE_FOUND) && ((gScanProgressIndicator & 1u) != 0)) {
|
||||
pPrintStr = "";
|
||||
} else if (gScanCssState == SCAN_CSS_STATE_OFF) {
|
||||
pPrintStr = "Search Freq";
|
||||
} else if (gScanCssState == SCAN_CSS_STATE_SCANNING) {
|
||||
pPrintStr = "Search Tone";
|
||||
} else if (gScanCssState == SCAN_CSS_STATE_FOUND) {
|
||||
pPrintStr = "Scan Complete";
|
||||
} else {
|
||||
pPrintStr = "FREQ:**.*****";
|
||||
pPrintStr = "Scan Failed";
|
||||
}
|
||||
|
||||
UI_PrintString(pPrintStr, 2, 0, 1, 8);
|
||||
|
||||
if (gScanCssState < SCAN_CSS_STATE_FOUND || !gScanUseCssResult) {
|
||||
pPrintStr = "CTC:******";
|
||||
// 2nd line
|
||||
if (gScanSingleFrequency || (gScanCssState != SCAN_CSS_STATE_OFF && gScanCssState != SCAN_CSS_STATE_FAILED)) {
|
||||
sprintf(String, "Freq:%u.%05u", gScanFrequency / 100000, gScanFrequency % 100000);
|
||||
pPrintStr = String;
|
||||
} else {
|
||||
pPrintStr = "Freq:---.-----";
|
||||
}
|
||||
|
||||
UI_PrintString(pPrintStr, 2, 0, 3, 8);
|
||||
|
||||
// 3rd line
|
||||
if (gScanCssState < SCAN_CSS_STATE_FOUND) {
|
||||
pPrintStr = "Tone:---";
|
||||
} else if (!gScanUseCssResult) {
|
||||
pPrintStr = "Tone:None";
|
||||
} else if (gScanCssResultType == CODE_TYPE_CONTINUOUS_TONE) {
|
||||
sprintf(String, "CTC:%u.%uHz", CTCSS_Options[gScanCssResultCode] / 10, CTCSS_Options[gScanCssResultCode] % 10);
|
||||
sprintf(String, "CTCSS:%u.%uHz", CTCSS_Options[gScanCssResultCode] / 10, CTCSS_Options[gScanCssResultCode] % 10);
|
||||
pPrintStr = String;
|
||||
} else {
|
||||
sprintf(String, "DCS:D%03oN", DCS_Options[gScanCssResultCode]);
|
||||
pPrintStr = String;
|
||||
}
|
||||
|
||||
UI_PrintString(pPrintStr, 2, 0, 3, 8);
|
||||
memset(String, 0, sizeof(String));
|
||||
if (gScannerSaveState == SCAN_SAVE_CHANNEL) {
|
||||
pPrintStr = "SAVE?";
|
||||
Start = 0;
|
||||
bCentered = 1;
|
||||
} else {
|
||||
Start = 2;
|
||||
bCentered = 0;
|
||||
|
||||
if (gScannerSaveState == SCAN_SAVE_CHAN_SEL) {
|
||||
strcpy(String, "SAVE:");
|
||||
UI_GenerateChannelStringEx(String + 5, gShowChPrefix, gScanChannel);
|
||||
pPrintStr = String;
|
||||
} else if (gScanCssState < SCAN_CSS_STATE_FOUND) {
|
||||
strcpy(String, "SCAN");
|
||||
memset(String + 4, '.', (gScanProgressIndicator & 7) + 1);
|
||||
pPrintStr = String;
|
||||
} else if (gScanCssState == SCAN_CSS_STATE_FOUND) {
|
||||
pPrintStr = "SCAN CMP.";
|
||||
} else {
|
||||
pPrintStr = "SCAN FAIL.";
|
||||
}
|
||||
}
|
||||
|
||||
UI_PrintString(pPrintStr, Start, bCentered ? 127 : 0, 5, 8);
|
||||
|
||||
UI_PrintString(pPrintStr, 2, 0, 5, 8);
|
||||
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
+74
-70
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/chFrScanner.h"
|
||||
#ifdef ENABLE_FMRADIO
|
||||
#include "app/fm.h"
|
||||
@@ -43,26 +44,35 @@ static void convertTime(uint8_t *line, uint8_t type)
|
||||
uint8_t m = t / 60;
|
||||
uint8_t s = t - (m * 60); // Replace modulo with subtraction for efficiency
|
||||
|
||||
gStatusLine[0] = gStatusLine[7] = gStatusLine[14] = 0x00; // Quick fix on display (on scanning I, II, etc.)
|
||||
|
||||
char str[6];
|
||||
sprintf(str, "%02u:%02u", m, s);
|
||||
UI_PrintStringSmallBufferNormal(str, line);
|
||||
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void UI_DisplayStatus()
|
||||
{
|
||||
char str[8] = "";
|
||||
|
||||
gUpdateStatus = false;
|
||||
memset(gStatusLine, 0, sizeof(gStatusLine));
|
||||
|
||||
if (APP_IsScreenSaverDisplayed())
|
||||
return;
|
||||
|
||||
UI_StatusClear();
|
||||
|
||||
uint8_t *line = gStatusLine;
|
||||
unsigned int x = 0;
|
||||
unsigned int x1 = 0;
|
||||
|
||||
char str[8] = "";
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN_RX_TX_TIMER) && !defined(ENABLE_FEAT_F4HWN_DEBUG)
|
||||
bool isTransmit = gCurrentFunction == FUNCTION_TRANSMIT;
|
||||
if (gSetting_set_tmr && (isTransmit || FUNCTION_IsRx())) {
|
||||
convertTime(line, !isTransmit);
|
||||
x += 39;
|
||||
} else {
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NOAA
|
||||
// NOAA indicator
|
||||
@@ -82,7 +92,7 @@ void UI_DisplayStatus()
|
||||
x += 8;
|
||||
#endif
|
||||
|
||||
unsigned int x1 = x;
|
||||
x1 = x;
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
if (gSetting_KILLED) {
|
||||
@@ -94,39 +104,35 @@ void UI_DisplayStatus()
|
||||
{ // SCAN indicator
|
||||
if (gScanStateDir != SCAN_OFF || SCANNER_IsScanning()) {
|
||||
if (IS_MR_CHANNEL(gNextMrChannel) && !SCANNER_IsScanning()) { // channel mode
|
||||
|
||||
uint8_t end = 0;
|
||||
|
||||
if(gEeprom.SCAN_LIST_DEFAULT == MR_CHANNELS_LIST + 1)
|
||||
{
|
||||
if(gEeprom.SCAN_LIST_ENABLED==1) {
|
||||
sprintf(str, "%s+", "ALL");
|
||||
end = 18;
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(str, "%s", "ALL");
|
||||
end = 14;
|
||||
}
|
||||
strcpy(str, "ALL");
|
||||
end = 14;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *name = gListName[gEeprom.SCAN_LIST_DEFAULT - 1];
|
||||
|
||||
// Check if name is valid
|
||||
bool nameValid = (name[0] != '\0' && name[0] != '\xff' && name[0] != ' ');
|
||||
|
||||
// Format the string
|
||||
if (nameValid) {
|
||||
sprintf(str, "%.3s%s", name, gEeprom.SCAN_LIST_ENABLED ? "+" : "");
|
||||
end = gEeprom.SCAN_LIST_ENABLED ? 18 : 14;
|
||||
}
|
||||
else {
|
||||
sprintf(str, "%02d%s", gEeprom.SCAN_LIST_DEFAULT, gEeprom.SCAN_LIST_ENABLED ? "+" : "");
|
||||
end = gEeprom.SCAN_LIST_ENABLED ? 14 : 10;
|
||||
if (!IsEmptyName(name, sizeof(gListName[0]))) {
|
||||
sprintf(str, "%.3s", name);
|
||||
end = 14;
|
||||
} else {
|
||||
sprintf(str, "%02d", gEeprom.SCAN_LIST_DEFAULT);
|
||||
end = 10;
|
||||
}
|
||||
}
|
||||
|
||||
if (gEeprom.SCAN_LIST_ENABLED) {
|
||||
strcat(str, "+");
|
||||
end += 4;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
GUI_DisplaySmallestInverse(str, 2, 0, true, true, end);
|
||||
#else
|
||||
GUI_DisplaySmallest(str, 2, 1, true, true);
|
||||
|
||||
gStatusLine[0] ^= 0x3E;
|
||||
@@ -135,6 +141,7 @@ void UI_DisplayStatus()
|
||||
gStatusLine[x] ^= 0x7F;
|
||||
}
|
||||
gStatusLine[end] ^= 0x3E;
|
||||
#endif
|
||||
}
|
||||
else { // frequency mode
|
||||
memcpy(line + x + 1, gFontS, sizeof(gFontS));
|
||||
@@ -164,48 +171,49 @@ void UI_DisplayStatus()
|
||||
#endif
|
||||
|
||||
if(!SCANNER_IsScanning()) {
|
||||
#ifdef ENABLE_FEAT_F4HWN_RX_TX_TIMER
|
||||
if(gCurrentFunction == FUNCTION_TRANSMIT && gSetting_set_tmr == true)
|
||||
{
|
||||
convertTime(line, 0);
|
||||
}
|
||||
else if(FUNCTION_IsRx() && gSetting_set_tmr == true)
|
||||
{
|
||||
convertTime(line, 1);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if(!gAirCopyBootMode) {
|
||||
const void *src = NULL; // Pointer to the font/bitmap to copy
|
||||
size_t sSize = 0; // Size of the font/bitmap
|
||||
uint8_t sOff = 2; // Offset relative to the reference position
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
if(gEeprom.MENU_LOCK == true) {
|
||||
memcpy(line + x + 2, gFontRO, sizeof(gFontRO));
|
||||
}
|
||||
else
|
||||
if (gEeprom.MENU_LOCK) {
|
||||
src = gFontRO;
|
||||
sSize = sizeof(gFontRO);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#endif
|
||||
uint8_t dw = (gEeprom.DUAL_WATCH != DUAL_WATCH_OFF) + (gEeprom.CROSS_BAND_RX_TX != CROSS_BAND_OFF) * 2;
|
||||
if(dw == 1 || dw == 3) { // DWR - dual watch + respond
|
||||
if(gDualWatchActive)
|
||||
memcpy(line + x + (dw==1?0:2), gFontDWR, sizeof(gFontDWR) - (dw==1?0:5));
|
||||
else
|
||||
memcpy(line + x + 3, gFontHold, sizeof(gFontHold));
|
||||
uint8_t xb = (gEeprom.CROSS_BAND_RX_TX != CROSS_BAND_OFF);
|
||||
|
||||
if (gEeprom.DUAL_WATCH != DUAL_WATCH_OFF) {
|
||||
if (gDualWatchActive) { // DWR - dual watch + respond
|
||||
src = gFontDWR;
|
||||
sOff = xb ? 2 : 0;
|
||||
sSize = sizeof(gFontDWR) - (xb ? 5 : 0);
|
||||
} else {
|
||||
src = gFontHold;
|
||||
sOff = 3;
|
||||
sSize = sizeof(gFontHold);
|
||||
}
|
||||
} else {
|
||||
src = xb ? gFontXB : gFontMO; // XB - crossband
|
||||
sSize = xb ? sizeof(gFontXB) : sizeof(gFontMO); // MO - main only
|
||||
}
|
||||
else if(dw == 2) { // XB - crossband
|
||||
memcpy(line + x + 2, gFontXB, sizeof(gFontXB));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!gAirCopyBootMode)
|
||||
memcpy(line + x + 2, gFontMO, sizeof(gFontMO));
|
||||
}
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
}
|
||||
#endif
|
||||
|
||||
// Perform the memcpy if a source was selected
|
||||
if (src) {
|
||||
memcpy(line + x + sOff, src, sSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
x += sizeof(gFontDWR) + 3;
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN_RX_TX_TIMER) && !defined(ENABLE_FEAT_F4HWN_DEBUG)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_VOX
|
||||
// VOX indicator
|
||||
if (gEeprom.VOX_SWITCH) {
|
||||
@@ -242,15 +250,8 @@ void UI_DisplayStatus()
|
||||
size = sizeof(gFontKeyLock);
|
||||
}
|
||||
else if (gWasFKeyPressed) {
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
if (!gEeprom.MENU_LOCK) {
|
||||
src = gFontF;
|
||||
size = sizeof(gFontF);
|
||||
}
|
||||
#else
|
||||
src = gFontF;
|
||||
size = sizeof(gFontF);
|
||||
#endif
|
||||
}
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
else if (gMute) {
|
||||
@@ -259,7 +260,10 @@ void UI_DisplayStatus()
|
||||
}
|
||||
#endif
|
||||
else if (gBackLight) {
|
||||
src = gFontLight;
|
||||
// Distinguish the manual backlight sub-state: BACKLIGHT_TIME == 0
|
||||
// means the light is currently off (hollow bulb), otherwise it is
|
||||
// forced on (bulb with filament). Both glyphs share the same size.
|
||||
src = (gEeprom.BACKLIGHT_TIME == 0) ? gFontLightOff : gFontLight;
|
||||
size = sizeof(gFontLight);
|
||||
}
|
||||
#ifdef ENABLE_FEAT_F4HWN_CHARGING_C
|
||||
@@ -288,7 +292,7 @@ void UI_DisplayStatus()
|
||||
break;
|
||||
|
||||
case 1: // voltage
|
||||
const uint16_t voltage = (gBatteryVoltageAverage <= 999) ? gBatteryVoltageAverage : 999; // limit to 9.99V
|
||||
const uint16_t voltage = MIN(gBatteryVoltageAverage, 999); // limit to 9.99V
|
||||
sprintf(str, "%u.%02u", voltage / 100, voltage % 100);
|
||||
break;
|
||||
|
||||
|
||||
+1
-2
@@ -94,9 +94,8 @@ void GUI_SelectNextDisplay(GUI_DisplayType_t Display)
|
||||
gAskForConfirmation = 0;
|
||||
gAskToSave = false;
|
||||
gAskToDelete = false;
|
||||
gWasFKeyPressed = false;
|
||||
|
||||
gUpdateStatus = true;
|
||||
HideFKeyIcon();
|
||||
}
|
||||
|
||||
gScreenToDisplay = Display;
|
||||
|
||||
+152
-19
@@ -33,6 +33,80 @@
|
||||
#include "screenshot.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO
|
||||
// Boot logo storage in PY25Q16 external flash, aligned on a 4 KB sector,
|
||||
// placed past the calibration zone (0x010000-0x010200).
|
||||
//
|
||||
// Layout inside the sector starting at LOGO_FLASH_ADDR:
|
||||
// [0x00..0x07] : 8-byte header (reserved for future magic/version/flags)
|
||||
// [0x08..0x407]: 128x64 monochrome bitmap (1024 B)
|
||||
// ST7565-native: 8 pages * 128 columns, column-major LSB-top
|
||||
#define LOGO_FLASH_ADDR 0x011000
|
||||
#define LOGO_HEADER_SIZE 8
|
||||
#define LOGO_BITMAP_ADDR (LOGO_FLASH_ADDR + LOGO_HEADER_SIZE)
|
||||
|
||||
static void UI_LoadLogo(void)
|
||||
{
|
||||
// Skip 8-byte header, then read 128x64 bitmap (1024 B):
|
||||
// page 0 -> gStatusLine, pages 1..7 -> gFrameBuffer.
|
||||
PY25Q16_ReadBuffer(LOGO_BITMAP_ADDR, gStatusLine, sizeof(gStatusLine));
|
||||
PY25Q16_ReadBuffer(LOGO_BITMAP_ADDR + sizeof(gStatusLine), gFrameBuffer, sizeof(gFrameBuffer));
|
||||
}
|
||||
|
||||
void UI_DisplayLogo(void)
|
||||
{
|
||||
UI_LoadLogo();
|
||||
ST7565_BlitStatusLine();
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_QRCODE
|
||||
// QR code (version 4, 33x33 modules, EC level L) encoding:
|
||||
// https://github.com/armel/uv-k1-k5v3-firmware-custom
|
||||
// Stored in framebuffer column-major format: 5 fb-lines x 33 columns.
|
||||
// Each byte packs 8 vertical pixels (bit 0 = top). Last fb-line uses
|
||||
// only bit 0 (row 32); bits 1..7 are always 0.
|
||||
//
|
||||
// static const uint8_t BITMAP_QR_GitHub[5][33] = {
|
||||
// { 0x7F, 0x41, 0x5D, 0x5D, 0x5D, 0x41, 0x7F, 0x00, 0x6A, 0xB8, 0xCB, 0xA0, 0x6D, 0x07, 0xCB, 0x1F, 0xD2, 0x18, 0x59, 0x15, 0x79, 0x86, 0xCE, 0x15, 0x43, 0x00, 0x7F, 0x41, 0x5D, 0x5D, 0x5D, 0x41, 0x7F },
|
||||
// { 0x87, 0xA3, 0x69, 0xC3, 0x19, 0x0E, 0x55, 0x1F, 0x43, 0x11, 0x16, 0xC1, 0x5A, 0x0E, 0x96, 0x3E, 0xA5, 0x15, 0x06, 0x2A, 0xFE, 0xCE, 0xCA, 0x3A, 0x70, 0xD9, 0xEA, 0xF5, 0x5C, 0x15, 0x8A, 0x67, 0x22 },
|
||||
// { 0xE0, 0x0B, 0x1D, 0x28, 0xF5, 0x87, 0x55, 0xEB, 0xA8, 0x11, 0xA3, 0xC1, 0x5A, 0x0E, 0x96, 0x3E, 0xA5, 0x15, 0x84, 0x2B, 0x72, 0xE8, 0xE9, 0x23, 0x11, 0xCD, 0xE6, 0xC1, 0x91, 0xE6, 0x88, 0x77, 0x22 },
|
||||
// { 0xFD, 0x04, 0x75, 0x75, 0x75, 0x04, 0xFD, 0x01, 0xF7, 0xE0, 0xD6, 0xC1, 0x5A, 0x0E, 0x96, 0x3E, 0xA5, 0x37, 0x22, 0x2B, 0xEA, 0xAA, 0xA7, 0x8D, 0x5F, 0x31, 0x55, 0xB1, 0x3F, 0xCE, 0xCA, 0x2C, 0x2B },
|
||||
// { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00 }
|
||||
// };
|
||||
|
||||
static const uint8_t BITMAP_QR_GitHub_Compressed[137] = {
|
||||
0x7F, 0x41, 0x5D, 0x5D, 0x5D, 0x41, 0x7F, 0x00, 0x6A, 0xB8, 0xCB, 0xA0, 0x6D, 0x07, 0xCB, 0x1F, 0xD2, 0x18, 0x59, 0x15, 0x79, 0x86, 0xCE, 0x15, 0x43, 0x00, 0x7F, 0x41, 0x5D, 0x5D, 0x5D, 0x41, 0x7F,
|
||||
0x87, 0xA3, 0x69, 0xC3, 0x19, 0x0E, 0x55, 0x1F, 0x43, 0x11, 0x16, 0xC1, 0x5A, 0x0E, 0x96, 0x3E, 0xA5, 0x15, 0x06, 0x2A, 0xFE, 0xCE, 0xCA, 0x3A, 0x70, 0xD9, 0xEA, 0xF5, 0x5C, 0x15, 0x8A, 0x67, 0x22,
|
||||
0xE0, 0x0B, 0x1D, 0x28, 0xF5, 0x87, 0x55, 0xEB, 0xA8, 0x11, 0xA3, 0xC1, 0x5A, 0x0E, 0x96, 0x3E, 0xA5, 0x15, 0x84, 0x2B, 0x72, 0xE8, 0xE9, 0x23, 0x11, 0xCD, 0xE6, 0xC1, 0x91, 0xE6, 0x88, 0x77, 0x22,
|
||||
0xFD, 0x04, 0x75, 0x75, 0x75, 0x04, 0xFD, 0x01, 0xF7, 0xE0, 0xD6, 0xC1, 0x5A, 0x0E, 0x96, 0x3E, 0xA5, 0x37, 0x22, 0x2B, 0xEA, 0xAA, 0xA7, 0x8D, 0x5F, 0x31, 0x55, 0xB1, 0x3F, 0xCE, 0xCA, 0x2C, 0x2B,
|
||||
0x7F, 0x09, 0x8C, 0xA3, 0x00
|
||||
};
|
||||
|
||||
|
||||
// QR code (version 4, 33x33 modules, EC level L) encoding:
|
||||
// https://github.com/armel/uv-k1-k5v3-firmware-custom/wiki
|
||||
// Stored in framebuffer column-major format: 5 fb-lines x 33 columns.
|
||||
// Last fb-line uses only bit 0 (row 32); bits 1..7 are always 0.
|
||||
//
|
||||
// static const uint8_t BITMAP_QR_GitHub_Wiki[5][33] = {
|
||||
// { 0x7F, 0x41, 0x5D, 0x5D, 0x5D, 0x41, 0x7F, 0x00, 0x6A, 0x0F, 0x74, 0x0E, 0xD2, 0xB0, 0x74, 0xB1, 0x6D, 0x18, 0x59, 0x15, 0x79, 0x86, 0xCE, 0x15, 0x43, 0x00, 0x7F, 0x41, 0x5D, 0x5D, 0x5D, 0x41, 0x7F },
|
||||
// { 0xCD, 0x5D, 0x83, 0x65, 0xE7, 0xC6, 0x55, 0xBD, 0x6B, 0x3F, 0xA9, 0x1C, 0xA5, 0xE0, 0x69, 0xE3, 0x4A, 0x15, 0x06, 0x2A, 0xFE, 0xCE, 0xCA, 0x3A, 0x70, 0xD9, 0xEA, 0xF5, 0x5C, 0x15, 0x8A, 0x67, 0x22 },
|
||||
// { 0xFF, 0x25, 0xAE, 0xB6, 0x30, 0xF8, 0x55, 0xDD, 0x07, 0xB6, 0xC2, 0x1C, 0xA5, 0xE0, 0x69, 0xC4, 0x66, 0x15, 0x84, 0x2B, 0x72, 0xE8, 0xE9, 0x23, 0x11, 0xCD, 0xE6, 0xC1, 0x91, 0xE6, 0x88, 0x77, 0x22 },
|
||||
// { 0xFD, 0x04, 0x74, 0x74, 0x74, 0x05, 0xFD, 0x01, 0xF7, 0xAE, 0x81, 0x1C, 0xA5, 0xE0, 0x69, 0x54, 0xFE, 0x37, 0x22, 0x2B, 0xEA, 0xAA, 0xA7, 0x8D, 0x5F, 0x31, 0x55, 0xB1, 0x3F, 0xCE, 0xCA, 0x24, 0x33 },
|
||||
// { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00 }
|
||||
// };
|
||||
|
||||
static const uint8_t BITMAP_QR_GitHub_Wiki_Compressed[137] = {
|
||||
0x7F, 0x41, 0x5D, 0x5D, 0x5D, 0x41, 0x7F, 0x00, 0x6A, 0x0F, 0x74, 0x0E, 0xD2, 0xB0, 0x74, 0xB1, 0x6D, 0x18, 0x59, 0x15, 0x79, 0x86, 0xCE, 0x15, 0x43, 0x00, 0x7F, 0x41, 0x5D, 0x5D, 0x5D, 0x41, 0x7F,
|
||||
0xCD, 0x5D, 0x83, 0x65, 0xE7, 0xC6, 0x55, 0xBD, 0x6B, 0x3F, 0xA9, 0x1C, 0xA5, 0xE0, 0x69, 0xE3, 0x4A, 0x15, 0x06, 0x2A, 0xFE, 0xCE, 0xCA, 0x3A, 0x70, 0xD9, 0xEA, 0xF5, 0x5C, 0x15, 0x8A, 0x67, 0x22,
|
||||
0xFF, 0x25, 0xAE, 0xB6, 0x30, 0xF8, 0x55, 0xDD, 0x07, 0xB6, 0xC2, 0x1C, 0xA5, 0xE0, 0x69, 0xC4, 0x66, 0x15, 0x84, 0x2B, 0x72, 0xE8, 0xE9, 0x23, 0x11, 0xCD, 0xE6, 0xC1, 0x91, 0xE6, 0x88, 0x77, 0x22,
|
||||
0xFD, 0x04, 0x74, 0x74, 0x74, 0x05, 0xFD, 0x01, 0xF7, 0xAE, 0x81, 0x1C, 0xA5, 0xE0, 0x69, 0x54, 0xFE, 0x37, 0x22, 0x2B, 0xEA, 0xAA, 0xA7, 0x8D, 0x5F, 0x31, 0x55, 0xB1, 0x3F, 0xCE, 0xCA, 0x24, 0x33,
|
||||
0x7F, 0x53, 0x8E, 0xA3, 0x00
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_MEM
|
||||
// Linker symbols (provided by the linker script)
|
||||
extern uint8_t _sdata; // Start of .data in RAM
|
||||
@@ -88,11 +162,62 @@ static inline uint16_t pct_x100(uint32_t used, uint32_t total)
|
||||
{
|
||||
return (uint16_t)((used * 10000u) / total); // 7559 => 75.59%
|
||||
}
|
||||
|
||||
void UI_GetMemPercents(uint16_t *flash_pct_x100, uint16_t *ram_pct_x100)
|
||||
{
|
||||
uint32_t ram_used = 0;
|
||||
uint32_t flash_used = 0;
|
||||
build_usage(&ram_used, &flash_used);
|
||||
if (flash_pct_x100) *flash_pct_x100 = pct_x100(flash_used, FLASH_SIZE_BYTES);
|
||||
if (ram_pct_x100) *ram_pct_x100 = pct_x100(ram_used, RAM_SIZE_BYTES);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_QRCODE
|
||||
// Set a single pixel at LCD-physical (x, y). y=0..7 maps to gStatusLine,
|
||||
// y=8..63 maps to gFrameBuffer (line = (y-8)/8, bit = (y-8)%8).
|
||||
static void QR_SetPixel(uint8_t x, uint8_t y)
|
||||
{
|
||||
if (x >= 128 || y >= 64) return;
|
||||
if (y < 8) {
|
||||
gStatusLine[x] |= (uint8_t)(1u << y);
|
||||
} else {
|
||||
const uint8_t fb_y = (uint8_t)(y - 8u);
|
||||
gFrameBuffer[fb_y >> 3][x] |= (uint8_t)(1u << (fb_y & 7u));
|
||||
}
|
||||
}
|
||||
|
||||
// Render a square QR bitmap stored in framebuffer column-major format
|
||||
// (size cols × ceil(size/8) fb-lines, row-major in memory).
|
||||
static void QR_Draw(const uint8_t *bitmap, uint8_t size, uint8_t origin_x, uint8_t origin_y)
|
||||
{
|
||||
for (uint8_t qy = 0; qy < size; qy++) {
|
||||
for (uint8_t qx = 0; qx < size; qx++) {
|
||||
// const uint16_t idx = (uint16_t)(qy >> 3) * (uint16_t)size + (uint16_t)qx;
|
||||
// if ((bitmap[idx] >> (qy & 7u)) & 1u) {
|
||||
if (qy < 32 ?
|
||||
((bitmap[(uint16_t)(qy >> 3) * (uint16_t)size + (uint16_t)qx] >> (qy & 7u)) & 1u) :
|
||||
((bitmap[132 + (qx >> 3)] >> (qx & 7u)) & 1u)) {
|
||||
QR_SetPixel((uint8_t)(origin_x + qx),
|
||||
(uint8_t)(origin_y + qy));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UI_DrawQRCode(bool wiki, uint8_t origin_x, uint8_t origin_y)
|
||||
{
|
||||
// QR_Draw(wiki ? (const uint8_t *)BITMAP_QR_GitHub_Wiki
|
||||
// : (const uint8_t *)BITMAP_QR_GitHub,
|
||||
QR_Draw(wiki ? (const uint8_t *)BITMAP_QR_GitHub_Wiki_Compressed
|
||||
: (const uint8_t *)BITMAP_QR_GitHub_Compressed,
|
||||
33, origin_x, origin_y);
|
||||
}
|
||||
#endif
|
||||
|
||||
void UI_DisplayReleaseKeys(void)
|
||||
{
|
||||
memset(gStatusLine, 0, sizeof(gStatusLine));
|
||||
UI_StatusClear();
|
||||
#if defined(ENABLE_FEAT_F4HWN_CTR) || defined(ENABLE_FEAT_F4HWN_INV)
|
||||
ST7565_ContrastAndInv();
|
||||
#endif
|
||||
@@ -107,12 +232,7 @@ void UI_DisplayReleaseKeys(void)
|
||||
|
||||
void UI_DisplayWelcome(void)
|
||||
{
|
||||
char WelcomeString0[16];
|
||||
char WelcomeString1[16];
|
||||
char WelcomeString2[16];
|
||||
char WelcomeString3[32];
|
||||
|
||||
memset(gStatusLine, 0, sizeof(gStatusLine));
|
||||
UI_StatusClear();
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN_CTR) || defined(ENABLE_FEAT_F4HWN_INV)
|
||||
ST7565_ContrastAndInv();
|
||||
@@ -122,16 +242,27 @@ void UI_DisplayWelcome(void)
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
ST7565_BlitStatusLine();
|
||||
ST7565_BlitFullScreen();
|
||||
|
||||
|
||||
if (gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_NONE || gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_SOUND) {
|
||||
ST7565_FillScreen(0x00);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_NONE || gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_FULL_SCREEN) {
|
||||
ST7565_FillScreen(0xFF);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
memset(WelcomeString0, 0, sizeof(WelcomeString0));
|
||||
memset(WelcomeString1, 0, sizeof(WelcomeString1));
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO
|
||||
else if (gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_LOGO) {
|
||||
UI_LoadLogo();
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
char WelcomeString0[16];
|
||||
char WelcomeString1[16];
|
||||
char WelcomeString2[16];
|
||||
char WelcomeString3[32];
|
||||
|
||||
// 0x0EB0
|
||||
PY25Q16_ReadBuffer(0x00A0C8, WelcomeString0, 16);
|
||||
@@ -193,6 +324,7 @@ void UI_DisplayWelcome(void)
|
||||
gFrameBuffer[4][108] ^= 0x7F;
|
||||
UI_DrawLineBuffer(gFrameBuffer, 109, 35, 127, 35, 1);
|
||||
|
||||
/*
|
||||
#ifdef ENABLE_FEAT_F4HWN_MEM
|
||||
uint32_t ram_used = 0;
|
||||
uint32_t flash_used = 0;
|
||||
@@ -210,6 +342,7 @@ void UI_DisplayWelcome(void)
|
||||
GUI_DisplaySmallest(WelcomeString3, 5, 1, true, true);
|
||||
ST7565_BlitStatusLine();
|
||||
#endif
|
||||
*/
|
||||
|
||||
sprintf(WelcomeString3, "%s Edition", Edition);
|
||||
UI_PrintStringSmallNormal(WelcomeString3, 0, 127, 6);
|
||||
@@ -217,12 +350,12 @@ void UI_DisplayWelcome(void)
|
||||
#else
|
||||
UI_PrintStringSmallNormal(Version, 0, 127, 6);
|
||||
#endif
|
||||
|
||||
//ST7565_BlitStatusLine(); // blank status line : I think it's useless
|
||||
ST7565_BlitFullScreen();
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot(true);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
ST7565_BlitStatusLine();
|
||||
ST7565_BlitFullScreen();
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
SCREENSHOT_Update(true);
|
||||
#endif
|
||||
}
|
||||
@@ -17,8 +17,25 @@
|
||||
#ifndef UI_WELCOME_H
|
||||
#define UI_WELCOME_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void UI_DisplayReleaseKeys(void);
|
||||
void UI_DisplayWelcome(void);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO
|
||||
void UI_DisplayLogo(void);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_QRCODE
|
||||
// Draw the embedded GitHub QR code (V4, 33x33) at (origin_x, origin_y).
|
||||
// `wiki` selects the wiki URL bitmap, otherwise the repo URL bitmap.
|
||||
void UI_DrawQRCode(bool wiki, uint8_t origin_x, uint8_t origin_y);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_MEM
|
||||
// Compute current FLASH and SRAM usage as percentages × 100 (e.g. 7559 = 75.59%).
|
||||
void UI_GetMemPercents(uint16_t *flash_pct_x100, uint16_t *ram_pct_x100);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -190,8 +190,13 @@ void cdc_acm_data_send_with_dtr(const uint8_t *buf, uint32_t size)
|
||||
{
|
||||
ep_tx_busy_flag = true;
|
||||
usbd_ep_start_write(CDC_IN_EP, buf, size);
|
||||
while (ep_tx_busy_flag)
|
||||
uint32_t timeout = 100000;
|
||||
while (ep_tx_busy_flag && --timeout)
|
||||
;
|
||||
if (!timeout) {
|
||||
ep_tx_busy_flag = false;
|
||||
dtr_enable = 0; // Consider USB disconnected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
const char Version[] = AUTHOR_STRING_2 " " VERSION_STRING_2;
|
||||
const char Edition[] = EDITION_STRING;
|
||||
const char BuildDate[] = __DATE__;
|
||||
const char BuildTime[] = __TIME__;
|
||||
const char BuildCommit[] = BUILD_COMMIT;
|
||||
#else
|
||||
const char Version[] = AUTHOR_STRING VER;
|
||||
#endif
|
||||
|
||||
@@ -24,4 +24,7 @@ extern const char UART_Version[];
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
extern const char Edition[];
|
||||
extern const char BuildDate[];
|
||||
extern const char BuildTime[];
|
||||
extern const char BuildCommit[];
|
||||
#endif
|
||||
@@ -19,6 +19,21 @@ message("Build type: " ${CMAKE_BUILD_TYPE})
|
||||
|
||||
enable_language(C ASM)
|
||||
|
||||
find_package(Git QUIET)
|
||||
set(BUILD_COMMIT "unknown")
|
||||
if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" rev-parse --short HEAD
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE BUILD_COMMIT
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
)
|
||||
if(NOT BUILD_COMMIT)
|
||||
set(BUILD_COMMIT "unknown")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_FEAT_F4HWN)
|
||||
if(NOT AUTHOR_STRING_1)
|
||||
set(AUTHOR_STRING_1 "EGZUMER")
|
||||
|
||||
+22
-3
@@ -34,7 +34,7 @@
|
||||
"ENABLE_BOOT_BEEPS": false,
|
||||
"ENABLE_SHOW_CHARGE_LEVEL": false,
|
||||
"ENABLE_REVERSE_BAT_SYMBOL": false,
|
||||
"ENABLE_NO_CODE_SCAN_TIMEOUT": true,
|
||||
"ENABLE_NO_CODE_SCAN_TIMEOUT": false,
|
||||
"ENABLE_SQUELCH_MORE_SENSITIVE": true,
|
||||
"ENABLE_FASTER_CHANNEL_SCAN": true,
|
||||
"ENABLE_RSSI_BAR": true,
|
||||
@@ -60,17 +60,26 @@
|
||||
"ENABLE_FEAT_F4HWN_RESCUE_OPS": false,
|
||||
"ENABLE_FEAT_F4HWN_VOL": false,
|
||||
"ENABLE_FEAT_F4HWN_AUDIO": false,
|
||||
"ENABLE_FEAT_F4HWN_AUDIO_SCOPE": false,
|
||||
"ENABLE_FEAT_F4HWN_RESET_VFO": true,
|
||||
"ENABLE_FEAT_F4HWN_PMR": false,
|
||||
"ENABLE_FEAT_F4HWN_GMRS_FRS_MURS": false,
|
||||
"ENABLE_FEAT_F4HWN_CA": true,
|
||||
"ENABLE_FEAT_F4HWN_DEBUG": false,
|
||||
"ENABLE_FEAT_F4HWN_QRCODE": false,
|
||||
"ENABLE_FEAT_F4HWN_MEM": false,
|
||||
"ENABLE_FEAT_F4HWN_SCAN_PROGRESS": false,
|
||||
"ENABLE_FEAT_F4HWN_SCAN_FASTER": false,
|
||||
"ENABLE_FEAT_F4HWN_SCAN_RSSI": false,
|
||||
"ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE": false,
|
||||
"ENABLE_FEAT_F4HWN_BEAM": false,
|
||||
"ENABLE_FEAT_F4HWN_LOGO": false,
|
||||
"ENABLE_FEAT_F4HWN_LOGO_SAV": false,
|
||||
"ENABLE_AGC_SHOW_DATA": false,
|
||||
"ENABLE_UART_RW_BK_REGS": false,
|
||||
"ENABLE_SWD": false,
|
||||
"VERSION_STRING_1": "v0.22",
|
||||
"VERSION_STRING_2": "v5.2.0"
|
||||
"VERSION_STRING_2": "v5.6.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -189,6 +198,16 @@
|
||||
"ENABLE_FEAT_F4HWN_RESCUE_OPS": true,
|
||||
"ENABLE_FEAT_F4HWN_VOL": true,
|
||||
"ENABLE_FEAT_F4HWN_AUDIO": true,
|
||||
"ENABLE_FEAT_F4HWN_AUDIO_SCOPE": true,
|
||||
"ENABLE_FEAT_F4HWN_SCAN_PROGRESS": true,
|
||||
"ENABLE_FEAT_F4HWN_SCAN_FASTER": true,
|
||||
"ENABLE_FEAT_F4HWN_SCAN_RSSI": true,
|
||||
"ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE": true,
|
||||
"ENABLE_FEAT_F4HWN_BEAM": true,
|
||||
"ENABLE_FEAT_F4HWN_QRCODE": true,
|
||||
"ENABLE_FEAT_F4HWN_MEM": true,
|
||||
"ENABLE_FEAT_F4HWN_LOGO": true,
|
||||
"ENABLE_FEAT_F4HWN_LOGO_SAV": true,
|
||||
"ENABLE_SWD": true,
|
||||
"EDITION_STRING": "Fusion",
|
||||
"TARGET": "f4hwn.fusion"
|
||||
@@ -225,4 +244,4 @@
|
||||
"configurePreset": "Fusion"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -31,9 +31,9 @@ Anyway, have fun.
|
||||
> _FR - CE FIRMWARE N'A PAS DE VÉRITABLE CERVEAU. VEUILLEZ UTILISER LE VÔTRE. Utilisez ce firmware à vos risques et périls. Il n'y a absolument aucune garantie qu'il fonctionnera d'une manière ou d'une autre sur votre (vos) radio(s), il peut même bousiller votre (vos) radio(s), dans ce cas, vous devrez acheter une autre radio. Quoi qu'il en soit, amusez-vous bien._
|
||||
|
||||
> [!NOTE]
|
||||
> EN - About Chirp, as many others firmwares, you need to use a dedicated driver available on [this repository](https://github.com/armel/uv-k5-chirp-driver).
|
||||
> EN - About CHIRP, as with many other firmwares, you need to use a dedicated driver. The matching CHIRP driver is now bundled with each release of this repository, so you can download the firmware and its driver together from the [Releases page](https://github.com/armel/uv-k1-k5v3-firmware-custom/releases).
|
||||
>
|
||||
> _FR - A propos de Chirp, comme beaucoup d'autres firmwares, vous devez utiliser un pilote dédié disponible sur [ce dépôt](https://github.com/armel/uv-k5-chirp-driver)._
|
||||
> _FR - A propos de CHIRP, comme pour beaucoup d'autres firmwares, vous devez utiliser un pilote dédié. Le driver CHIRP correspondant est désormais fourni avec chaque release de ce dépôt, ce qui permet de récupérer ensemble le firmware et son pilote depuis la page des [Releases](https://github.com/armel/uv-k1-k5v3-firmware-custom/releases)._
|
||||
|
||||
> [!CAUTION]
|
||||
> EN - I recommend to backup your calibration data with [uvtools2](https://armel.github.io/uvtools2/) just after flashing this firmware. It's a good reflex to have.
|
||||
@@ -42,7 +42,7 @@ Anyway, have fun.
|
||||
|
||||
# Donations
|
||||
|
||||
Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Olivier 14RC206, Frédéric F4ESO, Stéphane F5LGW (2 times), Jorge Ornelas (4 times), Laurent F4AXK, Christophe Morel, Clayton W0LED, Pierre Antoine F6FWB, Jean-Claude 14FRS3306, Thierry F4GVO, Eric F1NOU, PricelessToolkit, Ady M6NYJ, Tom McGovern (4 times), Joseph Roth, Pierre-Yves Colin, Frank DJ7FG, Marcel Testaz, Brian Frobisher, Yannick F4JFO, Paolo Bussola, Dirk DL8DF, Levente Szőke (2 times), Bernard-Michel Herrera, Jérôme Saintespes, Paul Davies, RS (3 times), Johan F4WAT, Robert Wörle, Rafael Sundorf, Paul Harker, Peter Fintl, Pascal F4ICR (2 times), Mike DL2MF (3 times), Eric KI1C (2 times), Phil G0ELM, Jérôme Lambert, Eliot Vedel, Alfonso EA7KDF, Jean-François F1EVM, Robert DC1RDB (2 times), Ian KE2CHJ, Daryl VK3AWA, Roberto Brunelli, Robert Boardman, Stephen Oliver, Nicolas F4INE, William Bruno, Daniel OK2VLK, Tayler Chew, Peter DL7RFP, Philippe Kopp, Rune LA6YMA, Jeremy Luna, Steef Wagenaar (2 times), Zhuo BG7SGA, Jamie M0JLB, Antoine LIBERT, Vince K0DKR, Julia DF7JA, Ken 2E0UMK and Victor TI2SYS for their [donations](https://www.paypal.com/paypalme/F4HWN). That’s so kind of them. Thanks so much 🙏🏻
|
||||
Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Olivier 14RC206, Frédéric F4ESO, Stéphane F5LGW (2 times), Jorge Ornelas (4 times), Laurent F4AXK, Christophe Morel, Clayton W0LED, Pierre Antoine F6FWB, Jean-Claude 14FRS3306, Thierry F4GVO, Eric F1NOU, PricelessToolkit, Ady M6NYJ, Tom McGovern (4 times), Joseph Roth, Pierre-Yves Colin, Frank DJ7FG, Marcel Testaz, Brian Frobisher, Yannick F4JFO, Paolo Bussola, Dirk DL8DF, Levente Szőke (2 times), Bernard-Michel Herrera, Jérôme Saintespes, Paul Davies, RS (3 times), Johan F4WAT, Robert Wörle, Rafael Sundorf, Paul Harker, Peter Fintl, Pascal F4ICR (2 times), Mike DL2MF (3 times), Eric KI1C (2 times), Phil G0ELM, Jérôme Lambert, Eliot Vedel, Alfonso EA7KDF, Jean-François F1EVM, Robert DC1RDB (2 times), Ian KE2CHJ, Daryl VK3AWA, Roberto Brunelli, Robert Boardman, Stephen Oliver, Nicolas F4INE, William Bruno, Daniel OK2VLK, Tayler Chew, Peter DL7RFP, Philippe Kopp, Rune LA6YMA, Jeremy Luna, Steef Wagenaar (2 times), Zhuo BG7SGA, Jamie M0JLB, Antoine LIBERT, Vince K0DKR, Julia DF7JA, Ken 2E0UMK, Victor TI2SYS, Tobi DG9LAY, Deaglan K4DFQ, Catherine PALMER, Brian WA6JFK, Stéphane Hintzy, Roger F1HCN, Marcin Kusaj and Flavio Cottarelli for their [donations](https://www.paypal.com/paypalme/F4HWN). That’s so kind of them. Thanks so much 🙏🏻
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@@ -57,12 +57,15 @@ Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Ol
|
||||
|
||||
## Main features and improvements from F4HWN:
|
||||
|
||||
* several firmware versions:
|
||||
* Bandscope (with spectrum analyzer made by Fagci),
|
||||
* Broadcast (with commercial FM radio support),
|
||||
* Basic (with spectrum analyzer and commercial FM radios support, but without certain functions such as Vox, Aircopy, etc.),
|
||||
* RescueOps (specifically designed for first responders: firefighters, sea rescue, mountain rescue),
|
||||
* Game (with a small breakout game),
|
||||
* Fusion is now the reference edition of the project:
|
||||
* all-in-one firmware for UV-K1 and UV-K5 V3,
|
||||
* spectrum analyzer made by Fagci,
|
||||
* commercial FM radio support,
|
||||
* Vox and Aircopy support,
|
||||
* screenshots and K5Viewer support,
|
||||
* advanced RX audio profiles and Audio Scope,
|
||||
* first-responder oriented options,
|
||||
* small breakout game,
|
||||
* improve default power settings level:
|
||||
* Low1 to Low5 (<~20mW, ~125mW, ~250mW, ~500mW, ~1W),
|
||||
* Mid ~2W,
|
||||
@@ -92,6 +95,8 @@ Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Ol
|
||||
* improve keyboard frequency input,
|
||||
* add percent and gauge to Air Copy,
|
||||
* improve audio bar,
|
||||
* add backlight fading,
|
||||
* add Audio Scope on TX,
|
||||
* and more...
|
||||
* new menu entries and changes:
|
||||
* add SetPwr menu to set User power (<20mW, 125mW, 250mW, 500mW, 1W, 2W or 5W),
|
||||
@@ -103,10 +108,13 @@ Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Ol
|
||||
* add SetMet menu to set s-meter style (Classic or Tiny),
|
||||
* add SetLck menu to set what is locked (Keys or Keys + PTT),
|
||||
* add SetGUI menu to set font size on the VFO baseline (Classic or Tiny),
|
||||
* add SetRxA menu to select RX audio profiles,
|
||||
* add TXLock menu to open TX on channel,
|
||||
* add SetTmr menu to set RX and TX timers (Off or On),
|
||||
* add SetOff menu to set the delay before the transceiver goes into deep sleep (Off or 1 minute to 2 hours),
|
||||
* add SetNFM menu to set Narrow width (12.5kHz or 6.25kHz),
|
||||
* add SetVol menu to adjust RX audio volume,
|
||||
* add SetScn menu to set Scan mode
|
||||
* rename BatVol menu (52/63) to SysInf, which displays the firmware version in addition to the battery status,
|
||||
* improve PonMsg menu,
|
||||
* improve BackLt menu,
|
||||
@@ -116,6 +124,7 @@ Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Ol
|
||||
* add HAM CA F Lock band (for Canadian zone),
|
||||
* add PMR 446 F Lock band,
|
||||
* add FRS/GMRS/MURS F Lock band,
|
||||
* add SetNav hidden menu to select the navigation layout according to the radio model,
|
||||
* remove blink and SOS functionality,
|
||||
* remove AM Fix menu (AM Fix is ENABLED by default),
|
||||
* add support of 3500mAh battery,
|
||||
@@ -124,18 +133,18 @@ Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Ol
|
||||
* change font and bitmaps,
|
||||
* move USB icon to left of battery information,
|
||||
* add RX and TX timers,
|
||||
* improve lists and scan lists options:
|
||||
* add new list 3,
|
||||
* add new list 0 (channel without list...),
|
||||
* add new scan lists options,
|
||||
* scan list 0 (all channels without list),
|
||||
* scan list 1,
|
||||
* scan list 2,
|
||||
* scan list 3,
|
||||
* scan lists [1, 2, 3],
|
||||
* scan all (all channels with or without list),
|
||||
* add scan list shortcuts,
|
||||
* add resume mode on startup (scan, bandscope and broadcast FM),
|
||||
* improve channel scanning:
|
||||
* support up to 24 scan lists,
|
||||
* each memory channel can be assigned to `OFF`, to one list (`01` to `24`), or to `ALL`,
|
||||
* `ALL` scans every channel except those set to `OFF`,
|
||||
* named scan lists are shown in the UI and status bar when available,
|
||||
* if the selected scan list is empty or invalid, the firmware automatically jumps to the next valid one,
|
||||
* very fast scan mode (around 150 freq/s),
|
||||
* frequencies exclusions,
|
||||
* add resume mode on startup (scan, spectrum analyzer and FM radio),
|
||||
* improve VFO persistence and restore behavior:
|
||||
* save the Squelch level adjusted with F + UP or F + DOWN,
|
||||
* restore the full VFO state on long press of EXIT,
|
||||
* new actions:
|
||||
* RX MODE,
|
||||
* MAIN ONLY,
|
||||
@@ -143,23 +152,26 @@ Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Ol
|
||||
* WIDE NARROW,
|
||||
* 1750Hz,
|
||||
* MUTE,
|
||||
* POWER HIGH (RescueOps),
|
||||
* REMOVE OFFSET (RescueOps),
|
||||
* POWER HIGH,
|
||||
* REMOVE OFFSET,
|
||||
* BEAM,
|
||||
* new key combinations:
|
||||
* add the F + UP or F + DOWN key combination to dynamically change the Squelch level,
|
||||
* add the F + F1 or F + F2 key combination to dynamically change the Step,
|
||||
* add F + 8 to quickly switch backlight between BLMin and BLMax on demand (this bypass BackLt strategy),
|
||||
* add F + 9 to return to BackLt strategy,
|
||||
* add long press on MENU, in * SCAN mode, to temporarily exclude a memory channel,
|
||||
* add short press on [0, 1, 2, 3, 4 or 5], in * SCAN mode, to dynamically change scan list.
|
||||
* add long press on MENU, in * SCAN mode, to exclude the current memory channel,
|
||||
* add direct scan list selection while scanning with two digits (`00` = `ALL`, `01` to `24` = scan list).
|
||||
* many fix:
|
||||
* squelch,
|
||||
* s-meter,
|
||||
* DTMF overlaying,
|
||||
* scan list 2 ignored,
|
||||
* scan range limit,
|
||||
* clean display on startup,
|
||||
* no more PWM noise,
|
||||
* K5Viewer/serial key handling,
|
||||
* spectrum freeze on USB-C unplug,
|
||||
* Audio Scope behavior in OnePush mode and after DTMF/1750,
|
||||
* and more...
|
||||
* enabled AIR COPY
|
||||
* disabled ENABLE_DTMF_CALLING,
|
||||
@@ -167,7 +179,7 @@ Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Ol
|
||||
* remove 200Tx, 350Tx and 500Tx,
|
||||
* unlock TX on all bands needs only to be repeat 3 times,
|
||||
* code refactoring and many memory optimization,
|
||||
* displays the live screen of the Quansheng K5 on your computer via a USB-to-Serial cable,
|
||||
* stream the live screen of the Quansheng K5 to K5Viewer and capture screenshots over a USB-to-Serial cable,
|
||||
* and more...
|
||||
|
||||
## Main features from Egzumer:
|
||||
@@ -203,7 +215,7 @@ Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Ol
|
||||
|
||||
## Manual
|
||||
|
||||
Up to date manual is available in the [Wiki section](https://github.com/armel/uv-k5-firmware-custom/wiki)
|
||||
Up to date manual is available in the [Wiki section](https://github.com/armel/uv-k1-k5v3-firmware-custom/wiki)
|
||||
|
||||
## Radio performance
|
||||
|
||||
@@ -211,21 +223,21 @@ Please note that the Quansheng UV-Kx radios are not professional quality transce
|
||||
performance is strictly limited. The RX front end has no track-tuned band pass filtering
|
||||
at all, and so are wide band/wide open to any and all signals over a large frequency range.
|
||||
|
||||
Using the radio in high intensity RF environments will most likely make reception anything but
|
||||
easy (AM mode will suffer far more than FM ever will), the receiver simply doesn't have a
|
||||
great dynamic range, which results in distorted AM audio with stronger RX'ed signals.
|
||||
There is nothing more anyone can do in firmware/software to improve that, once the RX gain
|
||||
adjustment I do (AM fix) reaches the hardwares limit, your AM RX audio will be all but
|
||||
non-existent (just like Quansheng's firmware).
|
||||
On the other hand, FM RX audio will/should be fine.
|
||||
Using the radio in high intensity RF environments will most likely make reception difficult,
|
||||
especially in AM mode. The receiver simply does not have a great dynamic range, so stronger
|
||||
signals can easily cause distortion, desensitization and poor AM audio.
|
||||
This is fundamentally a hardware limitation: firmware can improve behavior at the margins, but
|
||||
it cannot overcome the front-end design of the radio.
|
||||
In practice, AM reception will degrade first and most severely, while FM reception is generally
|
||||
more tolerant and should remain more usable.
|
||||
|
||||
But, they are nice toys for the price, fun to play with.
|
||||
|
||||
## Compiling and Building from Docker
|
||||
|
||||
This project provides a Docker-based build system to compile all firmware editions for the UV-K1 and UV-K5 V3. Everything is handled through the `compile-with-docker.sh` helper script.
|
||||
This project provides a Docker-based build system to compile the Fusion firmware for the UV-K1 and UV-K5 V3. Everything is handled through the `compile-with-docker.sh` helper script.
|
||||
|
||||
All build outputs are generated inside the `build/<Preset>` directory, according to the CMake presets defined in `CMakePresets.json`.
|
||||
The documented build output is generated inside `build/Fusion`, using the CMake presets defined in `CMakePresets.json`.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
@@ -238,40 +250,25 @@ The script `compile-with-docker.sh` performs the following actions:
|
||||
|
||||
1. Builds the Docker image (`uvk1-uvk5v3`) if it does not already exist.
|
||||
2. Removes any previous `build` directory to ensure a clean configuration.
|
||||
3. Runs CMake using the selected preset inside the Docker container.
|
||||
4. Builds the firmware and outputs `.elf`, `.bin` and `.hex` files for the chosen edition.
|
||||
3. Runs CMake using the `Fusion` preset inside the Docker container.
|
||||
4. Builds the firmware and outputs `.elf`, `.bin` and `.hex` files.
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
./compile-with-docker.sh <Preset> [extra CMake options]
|
||||
./compile-with-docker.sh Fusion [extra CMake options]
|
||||
```
|
||||
|
||||
### Available Presets
|
||||
### Documented Preset
|
||||
|
||||
- **Custom**
|
||||
- **Bandscope**
|
||||
- **Broadcast**
|
||||
- **Basic**
|
||||
- **RescueOps**
|
||||
- **Game**
|
||||
- **Fusion**
|
||||
- **All** (builds all editions sequentially)
|
||||
|
||||
### Examples
|
||||
|
||||
Build a single edition:
|
||||
Build Fusion:
|
||||
|
||||
```bash
|
||||
./compile-with-docker.sh Fusion
|
||||
./compile-with-docker.sh Bandscope
|
||||
./compile-with-docker.sh Broadcast
|
||||
```
|
||||
|
||||
Build everything:
|
||||
|
||||
```bash
|
||||
./compile-with-docker.sh All
|
||||
```
|
||||
|
||||
### Passing Additional CMake Options
|
||||
@@ -282,15 +279,14 @@ These are forwarded directly to `cmake --preset` inside the container.
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
./compile-with-docker.sh Bandscope -DENABLE_SPECTRUM=ON
|
||||
./compile-with-docker.sh Broadcast -DENABLE_FEAT_F4HWN_GAME=ON -DENABLE_NOAA=ON
|
||||
./compile-with-docker.sh Bandscope -DSQL_TONE=600
|
||||
./compile-with-docker.sh Fusion -DENABLE_SPECTRUM=ON
|
||||
./compile-with-docker.sh Fusion -DENABLE_FEAT_F4HWN_GAME=ON -DENABLE_NOAA=ON
|
||||
./compile-with-docker.sh Fusion -DSQL_TONE=600
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
- The first run may take a few minutes while Docker builds the base image.
|
||||
- Running with `All` will build every firmware variant in sequence.
|
||||
- Each build runs inside Docker, so your host environment remains clean.
|
||||
|
||||
## Flashing the Firmware with UVTools2
|
||||
@@ -342,6 +338,7 @@ Click `Restore Calibration Data` and wait until the process fully completes.
|
||||
Many thanks to various people:
|
||||
|
||||
* [Muzkr](https://github.com/muzkr)
|
||||
* [Mrkusypl](https://github.com/mrkusypl)
|
||||
* [Andrej](https://github.com/Tunas1337)
|
||||
* [Egzumer](https://github.com/egzumer)
|
||||
* [OneOfEleven](https://github.com/OneOfEleven)
|
||||
|
||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (c) 2026
|
||||
#
|
||||
# Licensed under the MIT License (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at the root of this repository.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
from binascii import crc_hqx
|
||||
from itertools import cycle
|
||||
|
||||
|
||||
# Quansheng stock updater pack format:
|
||||
# - raw firmware split at 0x2000
|
||||
# - 16-byte ASCII version field inserted there
|
||||
# - XOR obfuscation with the table below
|
||||
# - CRC16/XMODEM appended at the end
|
||||
OBFUSCATION = bytes(
|
||||
[
|
||||
0x47, 0x22, 0xC0, 0x52, 0x5D, 0x57, 0x48, 0x94, 0xB1, 0x60, 0x60, 0xDB, 0x6F, 0xE3, 0x4C, 0x7C,
|
||||
0xD8, 0x4A, 0xD6, 0x8B, 0x30, 0xEC, 0x25, 0xE0, 0x4C, 0xD9, 0x00, 0x7F, 0xBF, 0xE3, 0x54, 0x05,
|
||||
0xE9, 0x3A, 0x97, 0x6B, 0xB0, 0x6E, 0x0C, 0xFB, 0xB1, 0x1A, 0xE2, 0xC9, 0xC1, 0x56, 0x47, 0xE9,
|
||||
0xBA, 0xF1, 0x42, 0xB6, 0x67, 0x5F, 0x0F, 0x96, 0xF7, 0xC9, 0x3C, 0x84, 0x1B, 0x26, 0xE1, 0x4E,
|
||||
0x3B, 0x6F, 0x66, 0xE6, 0xA0, 0x6A, 0xB0, 0xBF, 0xC6, 0xA5, 0x70, 0x3A, 0xBA, 0x18, 0x9E, 0x27,
|
||||
0x1A, 0x53, 0x5B, 0x71, 0xB1, 0x94, 0x1E, 0x18, 0xF2, 0xD6, 0x81, 0x02, 0x22, 0xFD, 0x5A, 0x28,
|
||||
0x91, 0xDB, 0xBA, 0x5D, 0x64, 0xC6, 0xFE, 0x86, 0x83, 0x9C, 0x50, 0x1C, 0x73, 0x03, 0x11, 0xD6,
|
||||
0xAF, 0x30, 0xF4, 0x2C, 0x77, 0xB2, 0x7D, 0xBB, 0x3F, 0x29, 0x28, 0x57, 0x22, 0xD6, 0x92, 0x8B,
|
||||
]
|
||||
)
|
||||
|
||||
VERSION_OFFSET = 0x2000
|
||||
VERSION_SIZE = 16
|
||||
CRC_SIZE = 2
|
||||
|
||||
|
||||
def _vector_table_looks_valid(image: bytes) -> bool:
|
||||
if len(image) < 8:
|
||||
return False
|
||||
|
||||
stack_pointer = int.from_bytes(image[0:4], "little")
|
||||
reset_vector = int.from_bytes(image[4:8], "little")
|
||||
|
||||
return (stack_pointer & 0xFFF00000) == 0x20000000 and (reset_vector & 0xFFF00000) == 0x08000000
|
||||
|
||||
|
||||
def is_raw_image(image: bytes) -> bool:
|
||||
return _vector_table_looks_valid(image)
|
||||
|
||||
|
||||
def has_valid_vendor_crc(image: bytes) -> bool:
|
||||
if len(image) < CRC_SIZE:
|
||||
return False
|
||||
|
||||
expected_crc = crc_hqx(image[:-CRC_SIZE], 0)
|
||||
actual_crc = int.from_bytes(image[-CRC_SIZE:], "little")
|
||||
|
||||
return expected_crc == actual_crc
|
||||
|
||||
|
||||
def is_packed_image(image: bytes) -> bool:
|
||||
if len(image) <= VERSION_OFFSET + VERSION_SIZE + CRC_SIZE:
|
||||
return False
|
||||
|
||||
if is_raw_image(image):
|
||||
return False
|
||||
|
||||
return has_valid_vendor_crc(image)
|
||||
|
||||
|
||||
def decode_packed_image(image: bytes) -> tuple[bytes, str]:
|
||||
if not is_packed_image(image):
|
||||
raise ValueError("not a packed Quansheng firmware image")
|
||||
|
||||
decoded = bytes(a ^ b for a, b in zip(image[:-CRC_SIZE], cycle(OBFUSCATION)))
|
||||
version_bytes = decoded[VERSION_OFFSET:VERSION_OFFSET + VERSION_SIZE]
|
||||
version = version_bytes.split(b"\x00", 1)[0].decode("ascii", errors="replace")
|
||||
|
||||
raw = decoded[:VERSION_OFFSET] + decoded[VERSION_OFFSET + VERSION_SIZE:]
|
||||
if not is_raw_image(raw):
|
||||
raise ValueError("decoded firmware does not look like a raw MCU image")
|
||||
|
||||
return raw, version
|
||||
+80
-8
@@ -17,14 +17,11 @@
|
||||
|
||||
|
||||
import argparse
|
||||
import serial
|
||||
import signal
|
||||
from time import sleep
|
||||
import os
|
||||
|
||||
import _prog as pp
|
||||
import _dump as dd
|
||||
import _restore as rr
|
||||
import _fwcodec as fc
|
||||
|
||||
|
||||
def load_image(file: str) -> bytes:
|
||||
@@ -42,7 +39,9 @@ def load_image(file: str) -> bytes:
|
||||
return a
|
||||
|
||||
|
||||
def main_dump(args, ser: serial.Serial):
|
||||
def main_dump(args, ser):
|
||||
|
||||
import _dump as dd
|
||||
|
||||
dump_file: str = args.file
|
||||
|
||||
@@ -73,7 +72,10 @@ def main_dump(args, ser: serial.Serial):
|
||||
sleep(0)
|
||||
|
||||
|
||||
def main_restore(args, ser: serial.Serial):
|
||||
def main_restore(args, ser):
|
||||
|
||||
import _dump as dd
|
||||
import _restore as rr
|
||||
|
||||
dump_file: str = args.file
|
||||
|
||||
@@ -105,7 +107,9 @@ def main_restore(args, ser: serial.Serial):
|
||||
sleep(0)
|
||||
|
||||
|
||||
def main_flash(args, ser: serial.Serial):
|
||||
def main_flash(args, ser):
|
||||
|
||||
import _prog as pp
|
||||
|
||||
bl_ver: str = args.bl_ver
|
||||
fw_file: str = args.file
|
||||
@@ -139,6 +143,56 @@ def main_flash(args, ser: serial.Serial):
|
||||
sleep(0)
|
||||
|
||||
|
||||
def get_raw_output_path(file: str) -> str:
|
||||
|
||||
root, ext = os.path.splitext(file)
|
||||
if ext:
|
||||
file_name = "{}.raw{}".format(os.path.basename(root), ext)
|
||||
candidate = "{}.raw{}".format(root, ext)
|
||||
else:
|
||||
file_name = os.path.basename(file) + ".raw.bin"
|
||||
candidate = file + ".raw.bin"
|
||||
|
||||
out_dir = os.path.dirname(candidate) or "."
|
||||
if os.access(out_dir, os.W_OK):
|
||||
return candidate
|
||||
|
||||
return os.path.join(os.getcwd(), file_name)
|
||||
|
||||
|
||||
def main_decode(args):
|
||||
|
||||
fw_file: str = args.file
|
||||
out_file: str = args.output or get_raw_output_path(fw_file)
|
||||
|
||||
try:
|
||||
fw_image = load_image(fw_file)
|
||||
if 0 == len(fw_image):
|
||||
print("Invalid firmware image: {}: empty file".format(fw_file))
|
||||
return
|
||||
except Exception as e:
|
||||
print("Cannot load firmware image '{}': {}".format(fw_file, e))
|
||||
return
|
||||
|
||||
if fc.is_raw_image(fw_image):
|
||||
print("Firmware image already looks raw")
|
||||
raw_image = fw_image
|
||||
version = None
|
||||
else:
|
||||
try:
|
||||
raw_image, version = fc.decode_packed_image(fw_image)
|
||||
except Exception as e:
|
||||
print("Cannot decode firmware image '{}': {}".format(fw_file, e))
|
||||
return
|
||||
|
||||
with open(out_file, "wb") as fd:
|
||||
fd.write(raw_image)
|
||||
|
||||
print("Raw firmware image written: {}, size = {}".format(out_file, len(raw_image)))
|
||||
if version:
|
||||
print("Packed version field: {}".format(version))
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
# Usage:
|
||||
@@ -146,6 +200,7 @@ def main():
|
||||
# serialtool.py .. flash [--bl-ver <ver>] <file>
|
||||
# serialtool.py .. dump {--config | --calib [| --all]} file
|
||||
# serialtool.py .. restore {--config | --calib [| --all]} file
|
||||
# serialtool.py decode <packed.bin> [raw.bin]
|
||||
ap = argparse.ArgumentParser(description="UV-K5 V2 serial tool")
|
||||
|
||||
# TODO: have to add option to each of subcommands ??
|
||||
@@ -198,14 +253,31 @@ def main():
|
||||
)
|
||||
ap_restore.add_argument("file", help="input dump file")
|
||||
|
||||
ap_decode = sp.add_parser(
|
||||
"decode", help="decode a packed Quansheng stock firmware into a raw image"
|
||||
)
|
||||
ap_decode.add_argument("file", help="input firmware image file")
|
||||
ap_decode.add_argument(
|
||||
"output",
|
||||
nargs="?",
|
||||
help="output raw firmware image file (default: <input>.raw.bin)",
|
||||
)
|
||||
|
||||
args = ap.parse_args()
|
||||
port: str = args.port
|
||||
sub_name: str = args.subcommand
|
||||
|
||||
print(ap.description)
|
||||
# print("Press Ctrl-C to quit")
|
||||
|
||||
if "decode" == sub_name:
|
||||
main_decode(args)
|
||||
return
|
||||
|
||||
port: str = args.port
|
||||
|
||||
try:
|
||||
import serial
|
||||
|
||||
ser = serial.Serial(port, baudrate=38400, timeout=0.0001, write_timeout=None)
|
||||
except Exception as e:
|
||||
print("Cannot open port '{}': {}".format(port, e))
|
||||
|
||||
Reference in new issue
Block a user