mirror of
https://github.com/armel/uv-k1-k5v3-firmware-custom.git
synced 2026-10-04 03:57:29 +00:00
Compare commits
400
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dbc520cf8d | ||
|
|
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 | ||
|
|
8a8752f80c | ||
|
|
b361b5c3e2 | ||
|
|
98c0552ee3 | ||
|
|
9a46bdf6bf | ||
|
|
3f7d2e56d7 | ||
|
|
5b638e298e | ||
|
|
6e232131b9 | ||
|
|
d9eb55fccf | ||
|
|
afb317d12a | ||
|
|
2f90b401cb | ||
|
|
28500e1667 | ||
|
|
4b8f8cf22a | ||
|
|
37fb4abd2e | ||
|
|
ec9bcba781 | ||
|
|
ed0f15959b | ||
|
|
0c90b7d95c | ||
|
|
2248b0cbbe | ||
|
|
a244a25513 | ||
|
|
f072b3eb02 | ||
|
|
6ed0bc3436 | ||
|
|
793a44855d | ||
|
|
4937d0baba | ||
|
|
29c0338c61 | ||
|
|
ed640588c5 | ||
|
|
335e1b5501 | ||
|
|
d4e29c56a3 | ||
|
|
dc08631274 | ||
|
|
0234900baf | ||
|
|
30aa3f13a0 | ||
|
|
1b170b9ff9 | ||
|
|
ed6b98ce46 | ||
|
|
f680b3af36 | ||
|
|
9ef3379ccf | ||
|
|
9081b01773 | ||
|
|
706283c16b | ||
|
|
e51813e1bb | ||
|
|
6b4e575d1f | ||
|
|
f0cca71673 | ||
|
|
aef113eacf | ||
|
|
3f8ddb0502 | ||
|
|
5b4b8cf2f6 | ||
|
|
ee1d68e7ef | ||
|
|
261aeb4fb7 | ||
|
|
5ccb6cd7ff | ||
|
|
fa7537d539 | ||
|
|
c1a6633302 | ||
|
|
e515d53aaa | ||
|
|
0ddbb0ad82 | ||
|
|
c1e5f23d72 | ||
|
|
8825eb9ac6 | ||
|
|
7957377388 | ||
|
|
712a984b96 | ||
|
|
1236c3863f | ||
|
|
85f1bfdb7f | ||
|
|
086858945b | ||
|
|
df99e6dc1f | ||
|
|
5ee5ec8873 | ||
|
|
5170170bb1 | ||
|
|
76c48044c8 | ||
|
|
ead079dc5b | ||
|
|
add6fb17ed | ||
|
|
c4079b1c32 | ||
|
|
c4fb7de972 | ||
|
|
45ead3125f | ||
|
|
2b40d1216e | ||
|
|
e555b48811 | ||
|
|
d70cc8f59a | ||
|
|
cd79144a25 | ||
|
|
b7151de126 | ||
|
|
4eea0c7910 | ||
|
|
cd7fcdbf54 | ||
|
|
3df48febf0 | ||
|
|
7f6594ffb2 | ||
|
|
3f3dab7833 | ||
|
|
7dbabfe60f | ||
|
|
c10ed40a4a | ||
|
|
0f7b600516 | ||
|
|
7189d09d73 | ||
|
|
164a72738a | ||
|
|
9ab8040e4b | ||
|
|
d3b985c5ad | ||
|
|
6ba761db1a | ||
|
|
f4826ed46c | ||
|
|
4c41ff7929 | ||
|
|
91e2f61099 | ||
|
|
011009b294 | ||
|
|
1865651a5a | ||
|
|
889de45b18 | ||
|
|
f4acafb541 | ||
|
|
5ea2f91d73 | ||
|
|
05ea1efe3d | ||
|
|
095c6f9bca | ||
|
|
45612e3e82 | ||
|
|
c13fb3a948 | ||
|
|
9bbb5b4cde | ||
|
|
608a992bca | ||
|
|
74de508a28 | ||
|
|
6b94ae4ed3 | ||
|
|
b9ce4934d8 | ||
|
|
70f41f2e6f | ||
|
|
e2b56d78b2 | ||
|
|
5caff3af5a | ||
|
|
dccdd37464 | ||
|
|
179a0ea8b8 | ||
|
|
b774f9d2ec | ||
|
|
e110448c68 | ||
|
|
3775232757 | ||
|
|
a4b8a236b0 | ||
|
|
9bc4efd0b7 | ||
|
|
815e5b1a82 | ||
|
|
212ef1a90e | ||
|
|
2f01dd0118 | ||
|
|
fe132a380b | ||
|
|
2438de2864 | ||
|
|
c2ee08d486 | ||
|
|
732acbcea1 | ||
|
|
992be7f0d1 | ||
|
|
efafbd868c | ||
|
|
b95d04ac65 | ||
|
|
6eae8f3448 | ||
|
|
06bc8b5711 | ||
|
|
e5ca2265e4 | ||
|
|
1e33ff0f1b | ||
|
|
8bb2915446 | ||
|
|
4658e8348d | ||
|
|
7250e81fb6 | ||
|
|
f17d9ab52f | ||
|
|
da6e5062a9 | ||
|
|
d9a1e0ae30 | ||
|
|
b42ea9d2db | ||
|
|
eee9fc0c1b | ||
|
|
bcc593a61f | ||
|
|
efa9da1c87 | ||
|
|
c2a855f6e9 | ||
|
|
e47ea8d9aa | ||
|
|
cdb8f86a9f | ||
|
|
17ae4dfccf | ||
|
|
691c2f8b35 | ||
|
|
a66f9c52e6 | ||
|
|
edfe11b7bd | ||
|
|
fadf71a893 | ||
|
|
3dad553090 | ||
|
|
0a3a5cfacd | ||
|
|
a88e385d8b | ||
|
|
c8729313e7 | ||
|
|
dad442a4b0 | ||
|
|
d8ac0d44a2 | ||
|
|
d22972b053 | ||
|
|
c89121ff0e | ||
|
|
386b268c81 | ||
|
|
a72433135f | ||
|
|
345c566b54 | ||
|
|
e036b35308 | ||
|
|
b7abcad04c | ||
|
|
5021b25dd5 | ||
|
|
20389310af | ||
|
|
f29b880f78 | ||
|
|
e0ac4de1ef | ||
|
|
9ad95af90e | ||
|
|
64fefa92c6 | ||
|
|
54ba31c4cb | ||
|
|
c9ee29438b | ||
|
|
89a6ad1428 | ||
|
|
5a4a89ae31 | ||
|
|
3dbe098ad9 | ||
|
|
78aeedc251 | ||
|
|
62b625475b | ||
|
|
9a28e489a1 | ||
|
|
8b4a2a5954 | ||
|
|
8af5a13979 | ||
|
|
654f313e2e | ||
|
|
ef9285f344 | ||
|
|
82e0b5c537 | ||
|
|
5c259cfcad | ||
|
|
44cf3de90a | ||
|
|
7d9ece1e18 | ||
|
|
872372a8aa | ||
|
|
50e95b1531 | ||
|
|
200882d322 | ||
|
|
40093dd705 | ||
|
|
643b205490 | ||
|
|
5110d59a9f | ||
|
|
09f893b126 | ||
|
|
4d471dc33d | ||
|
|
4516486867 | ||
|
|
15d9731611 | ||
|
|
6cb9535ca2 | ||
|
|
b8eca8810f | ||
|
|
227242567d | ||
|
|
e534610d86 | ||
|
|
f3e1a37701 | ||
|
|
75a6e20026 |
No files matched your search
+4
-1
@@ -7,8 +7,11 @@ firmware
|
||||
.cache
|
||||
compile_commands.json
|
||||
.vscode
|
||||
.claude
|
||||
/docs
|
||||
k5_eeprom.raw
|
||||
|
||||
/build
|
||||
/serialtool/__pycache__
|
||||
__pycache__/
|
||||
|
||||
.DS_Store
|
||||
+22
-7
@@ -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)
|
||||
@@ -157,19 +158,16 @@ enable_feature(ENABLE_BOOT_BEEPS)
|
||||
enable_feature(ENABLE_SHOW_CHARGE_LEVEL)
|
||||
enable_feature(ENABLE_REVERSE_BAT_SYMBOL)
|
||||
enable_feature(ENABLE_NO_CODE_SCAN_TIMEOUT)
|
||||
enable_feature(ENABLE_AM_FIX
|
||||
am_fix.c
|
||||
)
|
||||
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)
|
||||
enable_feature(ENABLE_BLMIN_TMP_OFF)
|
||||
enable_feature(ENABLE_SCAN_RANGES)
|
||||
enable_feature(ENABLE_NAVIG_LEFT_RIGHT)
|
||||
|
||||
# ---- CONTRIB MODS ----
|
||||
|
||||
@@ -197,17 +195,34 @@ 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_RESCUE_OPS)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_VOL)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_RESET_CHANNEL)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_AUDIO)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_RESET_VFO)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_PMR)
|
||||
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 ----
|
||||
|
||||
enable_feature(ENABLE_AM_FIX_SHOW_DATA)
|
||||
enable_feature(ENABLE_AGC_SHOW_DATA)
|
||||
enable_feature(ENABLE_UART_RW_BK_REGS)
|
||||
|
||||
|
||||
+146
-125
@@ -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);
|
||||
@@ -114,6 +118,12 @@ void (*action_opt_table[])(void) = {
|
||||
//#else
|
||||
// [ACTION_OPT_MUTE] = &FUNCTION_NOP,
|
||||
//#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
[ACTION_OPT_RXA] = &ACTION_RxA,
|
||||
#else
|
||||
[ACTION_OPT_RXA] = &FUNCTION_NOP,
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
[ACTION_OPT_POWER_HIGH] = &ACTION_Power_High,
|
||||
[ACTION_OPT_REMOVE_OFFSET] = &ACTION_Remove_Offset,
|
||||
@@ -125,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);
|
||||
@@ -206,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();
|
||||
|
||||
@@ -230,15 +243,15 @@ void ACTION_Scan(bool bRestart)
|
||||
}
|
||||
|
||||
// channel mode. Keep scanning but toggle between scan lists
|
||||
gEeprom.SCAN_LIST_DEFAULT = (gEeprom.SCAN_LIST_DEFAULT + 1) % 6;
|
||||
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
|
||||
@@ -283,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
|
||||
|
||||
@@ -312,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]();
|
||||
}
|
||||
|
||||
|
||||
@@ -496,17 +539,13 @@ void ACTION_RxMode(void)
|
||||
{
|
||||
static bool cycle = 0;
|
||||
|
||||
switch(cycle) {
|
||||
case 0:
|
||||
gEeprom.DUAL_WATCH = !gEeprom.DUAL_WATCH;
|
||||
cycle = 1;
|
||||
break;
|
||||
case 1:
|
||||
gEeprom.CROSS_BAND_RX_TX = !gEeprom.CROSS_BAND_RX_TX;
|
||||
cycle = 0;
|
||||
break;
|
||||
if (cycle) {
|
||||
gEeprom.CROSS_BAND_RX_TX = !gEeprom.CROSS_BAND_RX_TX;
|
||||
} else {
|
||||
gEeprom.DUAL_WATCH = !gEeprom.DUAL_WATCH;
|
||||
}
|
||||
|
||||
cycle = !cycle;
|
||||
ACTION_Update();
|
||||
}
|
||||
|
||||
@@ -516,81 +555,66 @@ void ACTION_MainOnly(void)
|
||||
static uint8_t dw = 0;
|
||||
static uint8_t cb = 0;
|
||||
|
||||
switch(cycle) {
|
||||
case 0:
|
||||
dw = gEeprom.DUAL_WATCH;
|
||||
cb = gEeprom.CROSS_BAND_RX_TX;
|
||||
if (cycle) {
|
||||
gEeprom.DUAL_WATCH = dw;
|
||||
gEeprom.CROSS_BAND_RX_TX = cb;
|
||||
} else {
|
||||
dw = gEeprom.DUAL_WATCH;
|
||||
cb = gEeprom.CROSS_BAND_RX_TX;
|
||||
|
||||
gEeprom.DUAL_WATCH = 0;
|
||||
gEeprom.CROSS_BAND_RX_TX = 0;
|
||||
cycle = 1;
|
||||
break;
|
||||
case 1:
|
||||
gEeprom.DUAL_WATCH = dw;
|
||||
gEeprom.CROSS_BAND_RX_TX = cb;
|
||||
cycle = 0;
|
||||
break;
|
||||
gEeprom.DUAL_WATCH = 0;
|
||||
gEeprom.CROSS_BAND_RX_TX = 0;
|
||||
}
|
||||
|
||||
cycle = !cycle;
|
||||
ACTION_Update();
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
void ACTION_RxA(void)
|
||||
{
|
||||
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
|
||||
|
||||
void ACTION_Ptt(void)
|
||||
{
|
||||
gSetting_set_ptt_session = !gSetting_set_ptt_session;
|
||||
|
||||
ACTION_Update();
|
||||
}
|
||||
|
||||
void ACTION_Wn(void)
|
||||
{
|
||||
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(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
|
||||
}
|
||||
|
||||
@@ -627,38 +651,35 @@ void ACTION_BackLightOnDemand(void)
|
||||
BACKLIGHT_TurnOn();
|
||||
}
|
||||
|
||||
//#if !defined(ENABLE_SPECTRUM) || !defined(ENABLE_FMRADIO)
|
||||
void ACTION_Mute(void)
|
||||
{
|
||||
// Toggle mute state
|
||||
gMute = !gMute;
|
||||
void ACTION_Mute(void)
|
||||
{
|
||||
// Toggle mute state
|
||||
gMute = !gMute;
|
||||
|
||||
// Update the registers
|
||||
#ifdef ENABLE_FMRADIO
|
||||
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)
|
||||
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
//#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
void ACTION_Power_High(void)
|
||||
{
|
||||
gPowerHigh = !gPowerHigh;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
}
|
||||
|
||||
void ACTION_Remove_Offset(void)
|
||||
{
|
||||
gRemoveOffset = !gRemoveOffset;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
}
|
||||
// Update the registers
|
||||
#ifdef ENABLE_FMRADIO
|
||||
BK1080_WriteRegister(BK1080_REG_05_SYSTEM_CONFIGURATION2, gMute ? 0x0A10 : 0x0A1F);
|
||||
#endif
|
||||
#endif
|
||||
gEeprom.VOLUME_GAIN = gMute ? 0 : gEeprom.VOLUME_GAIN_BACKUP;
|
||||
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)
|
||||
{
|
||||
ACTION_ToggleVfoSetting(&gPowerHigh);
|
||||
}
|
||||
|
||||
void ACTION_Remove_Offset(void)
|
||||
{
|
||||
ACTION_ToggleVfoSetting(&gRemoveOffset);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
+3
-2
@@ -42,9 +42,10 @@ void ACTION_SwitchDemodul(void);
|
||||
void ACTION_Wn(void);
|
||||
void ACTION_BackLightOnDemand(void);
|
||||
void ACTION_BackLight(void);
|
||||
//#if !defined(ENABLE_SPECTRUM) || !defined(ENABLE_FMRADIO)
|
||||
void ACTION_Mute(void);
|
||||
//#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
void ACTION_RxA(void);
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
void ACTION_Power_High(void);
|
||||
void ACTION_Remove_Offset(void);
|
||||
|
||||
+247
-93
@@ -16,10 +16,6 @@
|
||||
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
|
||||
//#if !defined(ENABLE_OVERLAY)
|
||||
// #include "ARMCM0.h"
|
||||
//#endif
|
||||
|
||||
#include "app/aircopy.h"
|
||||
#include "audio.h"
|
||||
#include "driver/bk4819.h"
|
||||
@@ -31,6 +27,8 @@
|
||||
#include "ui/helper.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/ui.h"
|
||||
#include "settings.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
#include "screenshot.h"
|
||||
@@ -41,10 +39,101 @@ 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];
|
||||
|
||||
// ============================================================================
|
||||
// Transfer Maps Definition
|
||||
// ============================================================================
|
||||
|
||||
#define AIRCOPY_BANK_SEGMENTS(bank) \
|
||||
{ \
|
||||
{ 0x0000 + (bank)*0x0800, 0x0000 + (bank)*0x0800 + 0x0800, AIRCOPY_WRITE_STRUCT }, \
|
||||
{ 0x4000 + (bank)*0x0800, 0x4000 + (bank)*0x0800 + 0x0800, AIRCOPY_WRITE_STRUCT }, \
|
||||
{ 0x8000 + (bank)*0x0100, 0x8000 + (bank)*0x0100 + 0x0100, AIRCOPY_WRITE_BYTES }, \
|
||||
}
|
||||
|
||||
#define AIRCOPY_STD_MAP(seg_array) \
|
||||
{ \
|
||||
.segments = seg_array, \
|
||||
.num_segments = 3, \
|
||||
.total_blocks = 68 \
|
||||
}
|
||||
|
||||
// total_blocks = 68 (blocs of 64 bytes) because (16 bytes + 16 bytes + 2 bytes) * 128 = 4352 / 64 = 68
|
||||
|
||||
#define DECLARE_AIRCOPY_BANK(n) \
|
||||
static const AIRCOPY_Segment_t AIRCOPY_Segments_Bank##n[] = \
|
||||
AIRCOPY_BANK_SEGMENTS(n); \
|
||||
\
|
||||
static const AIRCOPY_TransferMap_t AIRCOPY_Map_Bank##n = \
|
||||
AIRCOPY_STD_MAP(AIRCOPY_Segments_Bank##n);
|
||||
|
||||
DECLARE_AIRCOPY_BANK(0)
|
||||
DECLARE_AIRCOPY_BANK(1)
|
||||
#if AIRCOPY_NUM_BANKS >= 4 // if 512 MR CHANNEL
|
||||
DECLARE_AIRCOPY_BANK(2)
|
||||
DECLARE_AIRCOPY_BANK(3)
|
||||
#endif
|
||||
#if AIRCOPY_NUM_BANKS >= 6 // if 758 MR CHANNEL
|
||||
DECLARE_AIRCOPY_BANK(4)
|
||||
DECLARE_AIRCOPY_BANK(5)
|
||||
#endif
|
||||
#if AIRCOPY_NUM_BANKS >= 8 // if 1024 MR CHANNEL
|
||||
DECLARE_AIRCOPY_BANK(6)
|
||||
DECLARE_AIRCOPY_BANK(7)
|
||||
#endif
|
||||
|
||||
// For settings only
|
||||
|
||||
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 = 3,
|
||||
.total_blocks = 12
|
||||
};
|
||||
|
||||
// Finally
|
||||
|
||||
static const AIRCOPY_TransferMap_t *AIRCOPY_AvailableMaps[] = {
|
||||
&AIRCOPY_Map_Bank0,
|
||||
&AIRCOPY_Map_Bank1,
|
||||
#if AIRCOPY_NUM_BANKS >= 4 // if 512 MR CHANNEL
|
||||
&AIRCOPY_Map_Bank2,
|
||||
&AIRCOPY_Map_Bank3,
|
||||
#endif
|
||||
#if AIRCOPY_NUM_BANKS >= 6 // if 758 MR CHANNEL
|
||||
&AIRCOPY_Map_Bank4,
|
||||
&AIRCOPY_Map_Bank5,
|
||||
#endif
|
||||
#if AIRCOPY_NUM_BANKS >= 8 // if 1024 MR CHANNEL
|
||||
&AIRCOPY_Map_Bank6,
|
||||
&AIRCOPY_Map_Bank7,
|
||||
#endif
|
||||
&AIRCOPY_Map_Settings,
|
||||
};
|
||||
|
||||
#define AIRCOPY_NUM_MAPS (sizeof(AIRCOPY_AvailableMaps) / sizeof(AIRCOPY_AvailableMaps[0]))
|
||||
|
||||
// ============================================================================
|
||||
// Helper Functions
|
||||
// ============================================================================
|
||||
|
||||
const AIRCOPY_TransferMap_t* AIRCOPY_GetCurrentMap(void)
|
||||
{
|
||||
if (gAircopyCurrentMapIndex >= AIRCOPY_NUM_MAPS) {
|
||||
gAircopyCurrentMapIndex = 0;
|
||||
}
|
||||
return AIRCOPY_AvailableMaps[gAircopyCurrentMapIndex];
|
||||
}
|
||||
|
||||
static void AIRCOPY_clear()
|
||||
{
|
||||
for (uint8_t i = 0; i < 15; i++)
|
||||
@@ -52,13 +141,57 @@ static void AIRCOPY_clear()
|
||||
crc[i] = 0;
|
||||
}
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot(true);
|
||||
SCREENSHOT_Update(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline const AIRCOPY_Segment_t *AIRCOPY_FindSegmentForOffset(uint16_t off)
|
||||
{
|
||||
const AIRCOPY_TransferMap_t *map = AIRCOPY_GetCurrentMap();
|
||||
|
||||
for (uint16_t i = 0; i < map->num_segments; i++)
|
||||
{
|
||||
const AIRCOPY_Segment_t *seg = &map->segments[i];
|
||||
|
||||
if (off >= seg->start_offset && off < seg->end_offset)
|
||||
return seg;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void AIRCOPY_CheckComplete(uint16_t *num)
|
||||
{
|
||||
*num = *num + 1;
|
||||
|
||||
const AIRCOPY_TransferMap_t *map = AIRCOPY_GetCurrentMap();
|
||||
uint16_t done = gAirCopyBlockNumber + gErrorsDuringAirCopy;
|
||||
|
||||
if (done >= map->total_blocks)
|
||||
{
|
||||
gAircopyState = AIRCOPY_COMPLETE;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
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
|
||||
// ============================================================================
|
||||
|
||||
bool AIRCOPY_SendMessage(void)
|
||||
{
|
||||
static uint8_t gAircopySendCountdown = 1;
|
||||
static uint16_t CurrentOffset = 0;
|
||||
static uint16_t CurrentSegmentIndex = 0;
|
||||
|
||||
if (gAircopyState != AIRCOPY_TRANSFER) {
|
||||
return 1;
|
||||
@@ -68,23 +201,40 @@ bool AIRCOPY_SendMessage(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
g_FSK_Buffer[1] = (gAirCopyBlockNumber & 0x3FF) << 6;
|
||||
const AIRCOPY_TransferMap_t *map = AIRCOPY_GetCurrentMap();
|
||||
|
||||
EEPROM_ReadBuffer(g_FSK_Buffer[1], &g_FSK_Buffer[2], 64);
|
||||
// Initialize on first call
|
||||
if (gAirCopyBlockNumber == 0) {
|
||||
CurrentSegmentIndex = 0;
|
||||
CurrentOffset = map->segments[0].start_offset;
|
||||
}
|
||||
|
||||
// Advance to next segment if current is done
|
||||
while (CurrentSegmentIndex < map->num_segments &&
|
||||
CurrentOffset >= map->segments[CurrentSegmentIndex].end_offset)
|
||||
{
|
||||
CurrentSegmentIndex++;
|
||||
if (CurrentSegmentIndex < map->num_segments) {
|
||||
CurrentOffset = map->segments[CurrentSegmentIndex].start_offset;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if transfer is complete
|
||||
if (CurrentSegmentIndex >= map->num_segments) {
|
||||
gAircopyState = AIRCOPY_COMPLETE;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
SCREENSHOT_Update(false);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Send data from current offset
|
||||
g_FSK_Buffer[1] = CurrentOffset;
|
||||
EEPROM_ReadBuffer(CurrentOffset, &g_FSK_Buffer[2], 64);
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
if (++gAirCopyBlockNumber >= 0x78) {
|
||||
gAircopyState = AIRCOPY_COMPLETE;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot(false);
|
||||
#endif
|
||||
//NVIC_SystemReset();
|
||||
}
|
||||
AIRCOPY_Obfuscate(34);
|
||||
|
||||
RADIO_SetTxParameters();
|
||||
|
||||
@@ -92,6 +242,8 @@ bool AIRCOPY_SendMessage(void)
|
||||
BK4819_SetupPowerAmplifier(0, 0);
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO1_PIN29_PA_ENABLE, false);
|
||||
|
||||
CurrentOffset += 64;
|
||||
gAirCopyBlockNumber++;
|
||||
gAircopySendCountdown = 30;
|
||||
|
||||
return 0;
|
||||
@@ -108,57 +260,62 @@ void AIRCOPY_StorePacket(void)
|
||||
uint16_t Status = BK4819_ReadRegister(BK4819_REG_0B);
|
||||
BK4819_PrepareFSKReceive();
|
||||
|
||||
// Doc says bit 4 should be 1 = CRC OK, 0 = CRC FAIL, but original firmware checks for FAIL.
|
||||
|
||||
if ((Status & 0x0010U) != 0 || g_FSK_Buffer[0] != 0xABCD || g_FSK_Buffer[35] != 0xDCBA) {
|
||||
gErrorsDuringAirCopy++;
|
||||
BK4819_ResetFSK(); // <- important
|
||||
BK4819_PrepareFSKReceive(); // <- re-arm proprement
|
||||
|
||||
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(&gErrorsDuringAirCopy);
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t Offset = g_FSK_Buffer[1];
|
||||
|
||||
if (Offset >= 0x1E00) {
|
||||
gErrorsDuringAirCopy++;
|
||||
const AIRCOPY_Segment_t *seg = AIRCOPY_FindSegmentForOffset(Offset);
|
||||
|
||||
if (seg == NULL) {
|
||||
AIRCOPY_CheckComplete(&gErrorsDuringAirCopy);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t *pData = &g_FSK_Buffer[2];
|
||||
for (unsigned int i = 0; i < 8; i++) {
|
||||
EEPROM_WriteBuffer(Offset, pData);
|
||||
pData += 4;
|
||||
Offset += 8;
|
||||
const uint8_t *pData = (const uint8_t *)&g_FSK_Buffer[2];
|
||||
|
||||
for (unsigned int i = 0; i < 8; i++)
|
||||
{
|
||||
EEPROM_WriteBuffer(Offset + (i * 8), pData + (i * 8));
|
||||
}
|
||||
|
||||
if (Offset == 0x1E00) {
|
||||
gAircopyState = AIRCOPY_COMPLETE;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
gAirCopyBlockNumber++;
|
||||
AIRCOPY_CheckComplete(&gAirCopyBlockNumber);
|
||||
}
|
||||
|
||||
static void AIRCOPY_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
static void AIRCOPY_InitTransfer(bool isSendMode)
|
||||
{
|
||||
if (bKeyHeld || !bKeyPressed) {
|
||||
return;
|
||||
}
|
||||
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)
|
||||
{
|
||||
INPUTBOX_Append(Key);
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_AIRCOPY;
|
||||
|
||||
if (gInputBoxIndex < 6) {
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
@@ -193,82 +350,79 @@ 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();
|
||||
static void AIRCOPY_Key_UP_DOWN(int8_t Direction)
|
||||
{
|
||||
if (!gEeprom.SET_NAV) {
|
||||
Direction = -Direction;
|
||||
}
|
||||
|
||||
GUI_DisplayScreen();
|
||||
|
||||
gAircopyState = AIRCOPY_TRANSFER;
|
||||
switch(Direction)
|
||||
{
|
||||
case 1:
|
||||
gAircopyCurrentMapIndex = (gAircopyCurrentMapIndex + 1) % AIRCOPY_NUM_MAPS;
|
||||
break;
|
||||
case -1:
|
||||
gAircopyCurrentMapIndex = (gAircopyCurrentMapIndex + AIRCOPY_NUM_MAPS - 1) % AIRCOPY_NUM_MAPS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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:
|
||||
case KEY_1:
|
||||
case KEY_2:
|
||||
case KEY_3:
|
||||
case KEY_4:
|
||||
case KEY_5:
|
||||
case KEY_6:
|
||||
case KEY_7:
|
||||
case KEY_8:
|
||||
case KEY_9:
|
||||
AIRCOPY_Key_DIGITS(Key, bKeyPressed, bKeyHeld);
|
||||
case KEY_0...KEY_9:
|
||||
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(Key == KEY_UP ? 1 : -1);
|
||||
break;
|
||||
case KEY_PTT:
|
||||
break;
|
||||
default:
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
break;
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_AIRCOPY;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
+75
-7
@@ -21,26 +21,94 @@
|
||||
|
||||
#include "driver/keyboard.h"
|
||||
|
||||
enum AIRCOPY_State_t
|
||||
{
|
||||
// ============================================================================
|
||||
// General definitions
|
||||
// ============================================================================
|
||||
|
||||
#define AIRCOPY_BLOCK_SIZE 0x0040u // 64 bytes per AirCopy block
|
||||
#define AIRCOPY_CHANNELS_PER_BANK 128
|
||||
#define AIRCOPY_NUM_BANKS MR_CHANNELS_MAX / AIRCOPY_CHANNELS_PER_BANK
|
||||
#define AIRCOPY_CHANNEL_SIZE 16 // bytes per channel (freq/name)
|
||||
#define AIRCOPY_BANK_SIZE_BYTES 0x1080u // 0x800 (Freq) + 0x800 (Name) + 0x80 (Attr)
|
||||
#define AIRCOPY_BAR_WIDTH 120 // Visible width of the progress gauge
|
||||
|
||||
// ============================================================================
|
||||
// Segment write mode
|
||||
// ============================================================================
|
||||
|
||||
/*
|
||||
* Defines how a segment must be written to EEPROM.
|
||||
*
|
||||
* - STRUCT: structured data (frequencies, names)
|
||||
* - BYTES : raw byte stream (attributes, settings, etc.)
|
||||
*/
|
||||
typedef enum {
|
||||
AIRCOPY_WRITE_STRUCT = 0,
|
||||
AIRCOPY_WRITE_BYTES = 1,
|
||||
} AIRCOPY_WriteMode_t;
|
||||
|
||||
// ============================================================================
|
||||
// Transfer segment structure
|
||||
// ============================================================================
|
||||
|
||||
/*
|
||||
* Describes a contiguous EEPROM region involved in AirCopy.
|
||||
* The write_mode defines how the RX side must write the data.
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t start_offset;
|
||||
uint16_t end_offset;
|
||||
AIRCOPY_WriteMode_t write_mode;
|
||||
} AIRCOPY_Segment_t;
|
||||
|
||||
// ============================================================================
|
||||
// Transfer map structure
|
||||
// ============================================================================
|
||||
|
||||
/*
|
||||
* A transfer map is a collection of segments describing
|
||||
* one complete AirCopy operation (bank, settings, etc.).
|
||||
*/
|
||||
typedef struct {
|
||||
const AIRCOPY_Segment_t *segments;
|
||||
uint16_t num_segments;
|
||||
uint16_t total_blocks;
|
||||
} AIRCOPY_TransferMap_t;
|
||||
|
||||
// ============================================================================
|
||||
// AirCopy state
|
||||
// ============================================================================
|
||||
|
||||
typedef enum {
|
||||
AIRCOPY_READY = 0,
|
||||
AIRCOPY_TRANSFER,
|
||||
AIRCOPY_COMPLETE
|
||||
};
|
||||
} AIRCOPY_State_t;
|
||||
|
||||
typedef enum AIRCOPY_State_t AIRCOPY_State_t;
|
||||
// ============================================================================
|
||||
// Globals
|
||||
// ============================================================================
|
||||
|
||||
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];
|
||||
|
||||
// ============================================================================
|
||||
// API
|
||||
// ============================================================================
|
||||
|
||||
bool AIRCOPY_SendMessage(void);
|
||||
void AIRCOPY_StorePacket(void);
|
||||
void AIRCOPY_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld);
|
||||
|
||||
#endif
|
||||
const AIRCOPY_TransferMap_t* AIRCOPY_GetCurrentMap(void);
|
||||
|
||||
#endif
|
||||
// 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
|
||||
+507
-172
File diff suppressed because it is too large.
Load diff
+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
-232
@@ -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,50 +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
|
||||
#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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
else
|
||||
{
|
||||
kbd.counter = 0;
|
||||
}
|
||||
|
||||
// Process the key if counter indicates it should be handled
|
||||
if (kbd.counter == 1)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -452,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;
|
||||
|
||||
|
||||
+770
-15
@@ -1,9 +1,16 @@
|
||||
|
||||
#include <stddef.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 +20,26 @@ bool gScanPauseMode;
|
||||
#ifdef ENABLE_SCAN_RANGES
|
||||
uint32_t gScanRangeStart;
|
||||
uint32_t gScanRangeStop;
|
||||
DCS_CodeType_t gScanRangeCssType = CODE_TYPE_OFF;
|
||||
uint8_t gScanRangeCssCode = 0xFF;
|
||||
static uint8_t scanRangeCssCandidate = 0xFF;
|
||||
static uint8_t scanRangeCssHitCount = 0;
|
||||
|
||||
#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 +63,618 @@ 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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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;
|
||||
|
||||
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();
|
||||
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);
|
||||
|
||||
if (ScanFastIsCandidate(ScanFastReadCandidateRssi()))
|
||||
{
|
||||
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,13 +685,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
|
||||
ScanFastResetState();
|
||||
#endif
|
||||
|
||||
if (IS_MR_CHANNEL(gNextMrChannel))
|
||||
{ // channel mode
|
||||
{
|
||||
|
||||
if(!RADIO_CheckValidList(gEeprom.SCAN_LIST_DEFAULT)) {
|
||||
RADIO_NextValidList(1);
|
||||
UI_MAIN_NotifyScanProgressDataChanged();
|
||||
}
|
||||
|
||||
// channel mode
|
||||
if (storeBackupSettings) {
|
||||
initialFrqOrChan = gRxVfo->CHANNEL_SAVE;
|
||||
lastFoundFrqOrChan = initialFrqOrChan;
|
||||
@@ -78,6 +728,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)
|
||||
{
|
||||
@@ -104,6 +764,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))
|
||||
{
|
||||
@@ -121,6 +793,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;
|
||||
@@ -228,11 +909,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);
|
||||
@@ -249,12 +965,12 @@ static void NextFreqChannel(void)
|
||||
|
||||
static void NextMemChannel(void)
|
||||
{
|
||||
static unsigned int prev_mr_chan = 0;
|
||||
const bool enabled = (gEeprom.SCAN_LIST_DEFAULT > 0 && gEeprom.SCAN_LIST_DEFAULT < 4) ? gEeprom.SCAN_LIST_ENABLED[gEeprom.SCAN_LIST_DEFAULT - 1] : true;
|
||||
const int chan1 = (gEeprom.SCAN_LIST_DEFAULT > 0 && gEeprom.SCAN_LIST_DEFAULT < 4) ? gEeprom.SCANLIST_PRIORITY_CH1[gEeprom.SCAN_LIST_DEFAULT - 1] : -1;
|
||||
const int chan2 = (gEeprom.SCAN_LIST_DEFAULT > 0 && gEeprom.SCAN_LIST_DEFAULT < 4) ? gEeprom.SCANLIST_PRIORITY_CH2[gEeprom.SCAN_LIST_DEFAULT - 1] : -1;
|
||||
const unsigned int prev_chan = gNextMrChannel;
|
||||
unsigned int chan = 0;
|
||||
static uint16_t prev_mr_chan = 0;
|
||||
const bool enabled = (gEeprom.SCAN_LIST_DEFAULT > 0 && gEeprom.SCAN_LIST_DEFAULT <= MR_CHANNELS_LIST + 1) ? gEeprom.SCAN_LIST_ENABLED : true;
|
||||
const int16_t chan1 = (gEeprom.SCAN_LIST_DEFAULT > 0 && gEeprom.SCAN_LIST_DEFAULT <= MR_CHANNELS_LIST + 1 && gEeprom.SCANLIST_PRIORITY_CH[0] != MR_CHANNELS_MAX) ? gEeprom.SCANLIST_PRIORITY_CH[0] : -1;
|
||||
const int16_t chan2 = (gEeprom.SCAN_LIST_DEFAULT > 0 && gEeprom.SCAN_LIST_DEFAULT <= MR_CHANNELS_LIST + 1 && gEeprom.SCANLIST_PRIORITY_CH[1] != MR_CHANNELS_MAX) ? gEeprom.SCANLIST_PRIORITY_CH[1] : -1;
|
||||
const uint16_t prev_chan = gNextMrChannel;
|
||||
uint16_t chan = 0;
|
||||
|
||||
//char str[64] = "";
|
||||
|
||||
@@ -327,29 +1043,64 @@ static void NextMemChannel(void)
|
||||
case SCAN_NEXT_CHAN_MR:
|
||||
currentScanList = SCAN_NEXT_CHAN_MR;
|
||||
gNextMrChannel = prev_mr_chan;
|
||||
chan = 0xff;
|
||||
chan = 0xFFFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!enabled || chan == 0xff)
|
||||
{
|
||||
if (!enabled || chan == 0xFFFF)
|
||||
{
|
||||
const uint16_t searchStart = gNextMrChannel;
|
||||
chan = RADIO_FindNextChannel(gNextMrChannel + gScanStateDir, gScanStateDir, true, gEeprom.SCAN_LIST_DEFAULT);
|
||||
if (chan == 0xFF)
|
||||
{ // no valid channel found
|
||||
if (chan == 0xFFFF)
|
||||
{ // 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);
|
||||
@@ -363,7 +1114,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
|
||||
}
|
||||
+16
-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,29 @@ extern bool gScanPauseMode;
|
||||
#ifdef ENABLE_SCAN_RANGES
|
||||
extern uint32_t gScanRangeStart;
|
||||
extern uint32_t gScanRangeStop;
|
||||
extern DCS_CodeType_t gScanRangeCssType;
|
||||
extern uint8_t gScanRangeCssCode;
|
||||
bool CHFRSCANNER_ExcludeCurrentScanRange(void);
|
||||
bool CHFRSCANNER_HasScanRangeExcludedOrdinal(uint32_t first_ordinal, uint32_t last_ordinal);
|
||||
void CHFRSCANNER_UpdateCssDetection(void);
|
||||
#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);
|
||||
#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
|
||||
+13
-2
@@ -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];
|
||||
@@ -60,8 +71,8 @@ void COMMON_SwitchVFOMode()
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t Channel = RADIO_FindNextChannel(gEeprom.MrChannel[gEeprom.TX_VFO], 1, false, 0);
|
||||
if (Channel != 0xFF)
|
||||
uint16_t Channel = RADIO_FindNextChannel(gEeprom.MrChannel[gEeprom.TX_VFO], 1, false, 0);
|
||||
if (Channel != 0xFFFF)
|
||||
{ // swap to channel mode
|
||||
gEeprom.ScreenChannel[gEeprom.TX_VFO] = Channel;
|
||||
#ifdef ENABLE_VOICE
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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)
|
||||
|
||||
+115
-89
@@ -23,6 +23,7 @@
|
||||
#include "app/generic.h"
|
||||
#include "audio.h"
|
||||
#include "driver/bk1080.h"
|
||||
#include "driver/bk4819.h"
|
||||
#include "driver/py25q16.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "functions.h"
|
||||
@@ -31,11 +32,7 @@
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
|
||||
#endif
|
||||
|
||||
uint16_t gFM_Channels[20];
|
||||
uint16_t gFM_Channels[FM_CHANNELS_MAX];
|
||||
bool gFmRadioMode;
|
||||
uint8_t gFmRadioCountdown_500ms;
|
||||
volatile uint16_t gFmPlayCountdown_10ms;
|
||||
@@ -43,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;
|
||||
|
||||
|
||||
@@ -109,6 +105,10 @@ void FM_TurnOff(void)
|
||||
|
||||
BK1080_Init0();
|
||||
|
||||
// Enable relevant LNA based on VFO frequency
|
||||
BK4819_PickRXFilterPathBasedOnFrequency(gRxVfo->freq_config_RX.Frequency);
|
||||
|
||||
|
||||
gUpdateStatus = true;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
@@ -119,10 +119,27 @@ void FM_TurnOff(void)
|
||||
|
||||
void FM_EraseChannels(void)
|
||||
{
|
||||
PY25Q16_SectorErase(0x003000);
|
||||
//PY25Q16_SectorErase(0x003000);
|
||||
|
||||
uint8_t clearBuf[128];
|
||||
memset(clearBuf, 0xFF, sizeof(clearBuf));
|
||||
PY25Q16_WriteBuffer(0x00A028, clearBuf, sizeof(clearBuf), false);
|
||||
|
||||
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();
|
||||
@@ -139,10 +156,7 @@ 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;
|
||||
}
|
||||
@@ -152,6 +166,12 @@ void FM_Tune(uint16_t Frequency, int8_t Step, bool bFlag)
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
|
||||
}
|
||||
|
||||
void FM_AudioPathOn(void) {
|
||||
BACKLIGHT_TurnOn();
|
||||
AUDIO_AudioPathOn();
|
||||
gEnableSpeaker = true;
|
||||
}
|
||||
|
||||
void FM_PlayAndUpdate(void)
|
||||
{
|
||||
gFM_ScanState = FM_SCAN_OFF;
|
||||
@@ -169,72 +189,50 @@ void FM_PlayAndUpdate(void)
|
||||
gScheduleFM = false;
|
||||
gAskToSave = false;
|
||||
|
||||
AUDIO_AudioPathOn();
|
||||
|
||||
gEnableSpeaker = true;
|
||||
FM_AudioPathOn();
|
||||
}
|
||||
|
||||
int FM_CheckFrequencyLock(uint16_t Frequency, uint16_t LowerLimit)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
const uint16_t Test2 = BK1080_ReadRegister(BK1080_REG_07);
|
||||
|
||||
// This is supposed to be a signed value, but above function is unsigned
|
||||
const uint16_t Test2 = BK1080_ReadRegister(BK1080_REG_07);
|
||||
const uint16_t Deviation = BK1080_REG_07_GET_FREQD(Test2);
|
||||
|
||||
if (BK1080_REG_07_GET_SNR(Test2) <= 2) {
|
||||
BK1080_FrequencyDeviation = Deviation;
|
||||
BK1080_BaseFrequency = Frequency;
|
||||
// Helper macro to update globals and return
|
||||
#define RETURN(val) \
|
||||
do { \
|
||||
BK1080_FrequencyDeviation = Deviation; \
|
||||
BK1080_BaseFrequency = Frequency; \
|
||||
return (val); \
|
||||
} while (0)
|
||||
|
||||
return ret;
|
||||
}
|
||||
if (BK1080_REG_07_GET_SNR(Test2) <= 2)
|
||||
RETURN(-1);
|
||||
|
||||
const uint16_t Status = BK1080_ReadRegister(BK1080_REG_10);
|
||||
if ((Status & BK1080_REG_10_MASK_AFCRL) != BK1080_REG_10_AFCRL_NOT_RAILED ||
|
||||
BK1080_REG_10_GET_RSSI(Status) < 10)
|
||||
RETURN(-1);
|
||||
|
||||
if ((Status & BK1080_REG_10_MASK_AFCRL) != BK1080_REG_10_AFCRL_NOT_RAILED || BK1080_REG_10_GET_RSSI(Status) < 10) {
|
||||
BK1080_FrequencyDeviation = Deviation;
|
||||
BK1080_BaseFrequency = Frequency;
|
||||
if (Deviation >= 280 && Deviation <= 3815)
|
||||
RETURN(-1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//if (Deviation > -281 && Deviation < 280)
|
||||
if (Deviation >= 280 && Deviation <= 3815) {
|
||||
BK1080_FrequencyDeviation = Deviation;
|
||||
BK1080_BaseFrequency = Frequency;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// not BLE(less than or equal)
|
||||
// Scanning upward: previous deviation was negative (bit 11 set) or near zero
|
||||
if (Frequency > LowerLimit && (Frequency - BK1080_BaseFrequency) == 1) {
|
||||
if (BK1080_FrequencyDeviation & 0x800 || (BK1080_FrequencyDeviation < 20))
|
||||
{
|
||||
BK1080_FrequencyDeviation = Deviation;
|
||||
BK1080_BaseFrequency = Frequency;
|
||||
|
||||
return ret;
|
||||
}
|
||||
if (BK1080_FrequencyDeviation & 0x800 || BK1080_FrequencyDeviation < 20)
|
||||
RETURN(-1);
|
||||
}
|
||||
|
||||
// not BLT(less than)
|
||||
|
||||
// Scanning downward: previous deviation was positive or saturated high
|
||||
if (Frequency >= LowerLimit && (BK1080_BaseFrequency - Frequency) == 1) {
|
||||
if ((BK1080_FrequencyDeviation & 0x800) == 0 || (BK1080_FrequencyDeviation > 4075))
|
||||
{
|
||||
BK1080_FrequencyDeviation = Deviation;
|
||||
BK1080_BaseFrequency = Frequency;
|
||||
|
||||
return ret;
|
||||
}
|
||||
if ((BK1080_FrequencyDeviation & 0x800) == 0 || BK1080_FrequencyDeviation > 4075)
|
||||
RETURN(-1);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
#undef RETURN
|
||||
|
||||
BK1080_FrequencyDeviation = Deviation;
|
||||
BK1080_BaseFrequency = Frequency;
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void Key_DIGITS(KEY_Code_t Key, uint8_t state)
|
||||
@@ -262,6 +260,7 @@ static void Key_DIGITS(KEY_Code_t Key, uint8_t state)
|
||||
}
|
||||
|
||||
INPUTBOX_Append(Key);
|
||||
gKeyInputCountdown = key_input_timeout_500ms;
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
|
||||
@@ -277,6 +276,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) {
|
||||
@@ -299,6 +300,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) {
|
||||
@@ -313,7 +316,7 @@ static void Key_DIGITS(KEY_Code_t Key, uint8_t state)
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (Channel < 20) {
|
||||
else if (Channel < FM_CHANNELS_MAX) {
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
#endif
|
||||
@@ -341,8 +344,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) {
|
||||
@@ -371,6 +373,14 @@ static void Key_FUNC(KEY_Code_t Key, uint8_t state)
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
break;
|
||||
|
||||
case KEY_8:
|
||||
ACTION_BackLightOnDemand();
|
||||
break;
|
||||
|
||||
case KEY_9:
|
||||
ACTION_BackLight();
|
||||
break;
|
||||
|
||||
case KEY_STAR:
|
||||
ACTION_Scan(autoScan);
|
||||
break;
|
||||
@@ -384,8 +394,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;
|
||||
|
||||
@@ -401,6 +417,7 @@ static void Key_EXIT(uint8_t state)
|
||||
}
|
||||
else {
|
||||
gInputBox[--gInputBoxIndex] = 10;
|
||||
gKeyInputCountdown = key_input_timeout_500ms;
|
||||
|
||||
if (gInputBoxIndex) {
|
||||
if (gInputBoxIndex != 1) {
|
||||
@@ -432,14 +449,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;
|
||||
@@ -476,6 +504,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;
|
||||
@@ -487,9 +517,13 @@ static void Key_UP_DOWN(uint8_t state, int8_t Step)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gEeprom.SET_NAV) {
|
||||
Step = -Step;
|
||||
}
|
||||
|
||||
if (gAskToSave) {
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
gFM_ChannelPosition = NUMBER_AddWithWraparound(gFM_ChannelPosition, Step, 0, 19);
|
||||
gFM_ChannelPosition = NUMBER_AddWithWraparound(gFM_ChannelPosition, Step, 0, FM_CHANNELS_MAX - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -515,10 +549,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;
|
||||
@@ -547,18 +578,8 @@ void FM_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
Key_MENU(state);
|
||||
break;
|
||||
case KEY_UP:
|
||||
#ifdef ENABLE_NAVIG_LEFT_RIGHT
|
||||
Key_UP_DOWN(state, -1);
|
||||
#else
|
||||
Key_UP_DOWN(state, 1);
|
||||
#endif
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
#ifdef ENABLE_NAVIG_LEFT_RIGHT
|
||||
Key_UP_DOWN(state, 1);
|
||||
#else
|
||||
Key_UP_DOWN(state, -1);
|
||||
#endif
|
||||
Key_UP_DOWN(state, Key == KEY_UP ? 1 : -1);
|
||||
break;
|
||||
case KEY_EXIT:
|
||||
Key_EXIT(state);
|
||||
@@ -569,9 +590,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;
|
||||
}
|
||||
}
|
||||
@@ -586,17 +612,16 @@ void FM_Play(void)
|
||||
if (!gEeprom.FM_IsMrMode)
|
||||
gEeprom.FM_SelectedFrequency = gEeprom.FM_FrequencyPlaying;
|
||||
|
||||
AUDIO_AudioPathOn();
|
||||
gEnableSpeaker = true;
|
||||
FM_AudioPathOn();
|
||||
|
||||
GUI_SelectNextDisplay(DISPLAY_FM);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gFM_ChannelPosition < 20)
|
||||
if (gFM_ChannelPosition < FM_CHANNELS_MAX)
|
||||
gFM_Channels[gFM_ChannelPosition++] = gEeprom.FM_FrequencyPlaying;
|
||||
|
||||
if (gFM_ChannelPosition >= 20) {
|
||||
if (gFM_ChannelPosition >= FM_CHANNELS_MAX) {
|
||||
FM_PlayAndUpdate();
|
||||
GUI_SelectNextDisplay(DISPLAY_FM);
|
||||
return;
|
||||
@@ -619,10 +644,11 @@ void FM_Start(void)
|
||||
gFM_RestoreCountdown_10ms = 0;
|
||||
|
||||
BK1080_Init(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
|
||||
// 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
-1
@@ -20,6 +20,7 @@
|
||||
#ifdef ENABLE_FMRADIO
|
||||
|
||||
#include "driver/keyboard.h"
|
||||
#include "misc.h"
|
||||
|
||||
#define FM_CHANNEL_UP 0x01
|
||||
#define FM_CHANNEL_DOWN 0xFF
|
||||
@@ -28,7 +29,7 @@ enum {
|
||||
FM_SCAN_OFF = 0U,
|
||||
};
|
||||
|
||||
extern uint16_t gFM_Channels[20];
|
||||
extern uint16_t gFM_Channels[FM_CHANNELS_MAX];
|
||||
extern bool gFmRadioMode;
|
||||
extern uint8_t gFmRadioCountdown_500ms;
|
||||
extern volatile uint16_t gFmPlayCountdown_10ms;
|
||||
|
||||
+3
-15
@@ -39,7 +39,7 @@
|
||||
|
||||
void GENERIC_Key_F(bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if (gInputBoxIndex > 0) {
|
||||
if (gInputBoxIndex > 0 || gScreenToDisplay == DISPLAY_MENU) {
|
||||
if (!bKeyHeld && bKeyPressed) // short pressed
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
@@ -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
|
||||
|
||||
+319
-240
@@ -44,9 +44,35 @@
|
||||
#include "radio.h"
|
||||
#include "settings.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/main.h"
|
||||
#include "ui/ui.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
// Full VFO backup for restore on EXIT
|
||||
static VFO_Info_t gVfoBackup;
|
||||
static uint16_t gScreenChannelBackup = 0;
|
||||
static uint16_t gFreqChannelBackup = 0;
|
||||
|
||||
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
|
||||
|
||||
@@ -55,31 +81,33 @@ 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;
|
||||
}
|
||||
|
||||
ChannelAttributes_t *att = MR_GetChannelAttributes(gTxVfo->CHANNEL_SAVE);
|
||||
|
||||
// Remove exclude
|
||||
if(gMR_ChannelExclude[gTxVfo->CHANNEL_SAVE] == true)
|
||||
if(att->exclude == true)
|
||||
{
|
||||
gMR_ChannelExclude[gTxVfo->CHANNEL_SAVE] = false;
|
||||
return;
|
||||
att->exclude = false;
|
||||
MR_SaveChannelAttributesToFlash(gTxVfo->CHANNEL_SAVE, att);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t scanlist = gTxVfo->SCANLIST_PARTICIPATION;
|
||||
|
||||
scanlist++;
|
||||
|
||||
if (scanlist > MR_CHANNELS_LIST + 1)
|
||||
scanlist = 0;
|
||||
|
||||
gTxVfo->SCANLIST_PARTICIPATION = scanlist;
|
||||
|
||||
SETTINGS_UpdateChannel(gTxVfo->CHANNEL_SAVE, gTxVfo, true, true, true);
|
||||
}
|
||||
|
||||
uint8_t scanTmp = gTxVfo->SCANLIST1_PARTICIPATION | (gTxVfo->SCANLIST2_PARTICIPATION << 1) | (gTxVfo->SCANLIST3_PARTICIPATION << 2);
|
||||
|
||||
scanTmp = (scanTmp++ < 7) ? scanTmp: 0;
|
||||
|
||||
gTxVfo->SCANLIST1_PARTICIPATION = (scanTmp >> 0) & 0x01;
|
||||
gTxVfo->SCANLIST2_PARTICIPATION = (scanTmp >> 1) & 0x01;
|
||||
gTxVfo->SCANLIST3_PARTICIPATION = (scanTmp >> 2) & 0x01;
|
||||
|
||||
SETTINGS_UpdateChannel(gTxVfo->CHANNEL_SAVE, gTxVfo, true, true, true);
|
||||
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
gFlagResetVfos = true;
|
||||
}
|
||||
@@ -88,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
|
||||
|
||||
@@ -105,8 +131,6 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
switch (Key) {
|
||||
case KEY_0:
|
||||
@@ -117,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) {
|
||||
@@ -183,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:
|
||||
@@ -193,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:
|
||||
@@ -202,26 +219,21 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
#endif
|
||||
COMMON_SwitchVFOMode();
|
||||
if (beep)
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_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;
|
||||
break;
|
||||
|
||||
case KEY_5:
|
||||
if(beep) {
|
||||
if(!beep) {
|
||||
#ifdef ENABLE_NOAA
|
||||
if (!IS_NOAA_CHANNEL(gTxVfo->CHANNEL_SAVE)) {
|
||||
gEeprom.ScreenChannel[Vfo] = gEeprom.NoaaChannel[gEeprom.TX_VFO];
|
||||
@@ -250,79 +262,103 @@ 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
|
||||
case KEY_UP:
|
||||
gEeprom.SQUELCH_LEVEL = (gEeprom.SQUELCH_LEVEL < 9) ? gEeprom.SQUELCH_LEVEL + 1: 9;
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
gWasFKeyPressed = false;
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
gEeprom.SQUELCH_LEVEL = (gEeprom.SQUELCH_LEVEL > 0) ? gEeprom.SQUELCH_LEVEL - 1: 0;
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
gWasFKeyPressed = false;
|
||||
break;
|
||||
{
|
||||
bool isKeyUp = (Key == KEY_UP);
|
||||
|
||||
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) {
|
||||
if (gEeprom.SQUELCH_LEVEL < 9) gEeprom.SQUELCH_LEVEL++;
|
||||
} else {
|
||||
if (gEeprom.SQUELCH_LEVEL > 0) gEeprom.SQUELCH_LEVEL--;
|
||||
}
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
}
|
||||
|
||||
gWasFKeyPressed = false;
|
||||
|
||||
break;
|
||||
}
|
||||
case KEY_SIDE1:
|
||||
uint8_t a = FREQUENCY_GetSortedIdxFromStepIdx(gTxVfo->STEP_SETTING);
|
||||
if (a < STEP_N_ELEM - 1)
|
||||
{
|
||||
gTxVfo->STEP_SETTING = FREQUENCY_GetStepIdxFromSortedIdx(a + 1);
|
||||
}
|
||||
if (IS_FREQ_CHANNEL(gTxVfo->CHANNEL_SAVE))
|
||||
{
|
||||
gRequestSaveChannel = 1;
|
||||
}
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
gWasFKeyPressed = false;
|
||||
break;
|
||||
case KEY_SIDE2:
|
||||
uint8_t b = FREQUENCY_GetSortedIdxFromStepIdx(gTxVfo->STEP_SETTING);
|
||||
if (b > 0)
|
||||
{
|
||||
gTxVfo->STEP_SETTING = FREQUENCY_GetStepIdxFromSortedIdx(b - 1);
|
||||
bool isKeySide1 = (Key == KEY_SIDE1);
|
||||
uint8_t idx = FREQUENCY_GetSortedIdxFromStepIdx(gTxVfo->STEP_SETTING);
|
||||
|
||||
if ((isKeySide1 && idx < STEP_N_ELEM - 1) || (!isKeySide1 && idx > 0))
|
||||
{
|
||||
gTxVfo->STEP_SETTING = FREQUENCY_GetStepIdxFromSortedIdx(idx + (isKeySide1 ? 1 : -1));
|
||||
|
||||
if (IS_FREQ_CHANNEL(gTxVfo->CHANNEL_SAVE)) {
|
||||
gRequestSaveChannel = 1;
|
||||
}
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
}
|
||||
|
||||
gWasFKeyPressed = false;
|
||||
|
||||
break;
|
||||
}
|
||||
if (IS_FREQ_CHANNEL(gTxVfo->CHANNEL_SAVE))
|
||||
{
|
||||
gRequestSaveChannel = 1;
|
||||
}
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
gWasFKeyPressed = false;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
gUpdateStatus = true;
|
||||
gWasFKeyPressed = false;
|
||||
HideFKeyIcon();
|
||||
|
||||
if (beep)
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
@@ -348,8 +384,8 @@ void channelMove(uint16_t Channel)
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
#endif
|
||||
|
||||
gEeprom.MrChannel[Vfo] = (uint8_t)Channel;
|
||||
gEeprom.ScreenChannel[Vfo] = (uint8_t)Channel;
|
||||
gEeprom.MrChannel[Vfo] = (uint16_t)Channel;
|
||||
gEeprom.ScreenChannel[Vfo] = (uint16_t)Channel;
|
||||
//gRequestSaveVFO = true;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
|
||||
@@ -359,6 +395,8 @@ void channelMove(uint16_t Channel)
|
||||
#endif
|
||||
|
||||
RADIO_ConfigureChannel(gEeprom.TX_VFO, gVfoConfigureMode);
|
||||
|
||||
SETTINGS_SaveVfoIndices();
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -386,21 +424,17 @@ void channelMoveSwitch(void) {
|
||||
Channel = (Channel * 10) + gInputBox[i];
|
||||
}
|
||||
|
||||
if ((Channel == 0) && (gInputBoxIndex != 3)) {
|
||||
if ((Channel == 0) && (gInputBoxIndex != 4)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gInputBoxIndex == 3) {
|
||||
if (gInputBoxIndex == 4) {
|
||||
gInputBoxIndex = 0;
|
||||
gKeyInputCountdown = 1;
|
||||
|
||||
channelMove(Channel - 1);
|
||||
SETTINGS_SaveVfoIndices();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
channelMove(Channel - 1);
|
||||
SETTINGS_SaveVfoIndices();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,10 +448,9 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
}
|
||||
|
||||
gWasFKeyPressed = false;
|
||||
gUpdateStatus = true;
|
||||
HideFKeyIcon();
|
||||
|
||||
processFKeyFunction(Key, false);
|
||||
processFKeyFunction(Key, true);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -432,8 +465,15 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
if (!gWasFKeyPressed) { // F-key wasn't pressed
|
||||
|
||||
if (gScanStateDir != SCAN_OFF){
|
||||
/*
|
||||
switch(Key) {
|
||||
case KEY_0...KEY_5:
|
||||
case KEY_0:
|
||||
gEeprom.SCAN_LIST_DEFAULT = MR_CHANNELS_LIST + 1;
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
SETTINGS_WriteCurrentState();
|
||||
#endif
|
||||
break;
|
||||
case KEY_1...KEY_9:
|
||||
gEeprom.SCAN_LIST_DEFAULT = Key;
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
SETTINGS_WriteCurrentState();
|
||||
@@ -443,9 +483,62 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
break;
|
||||
}
|
||||
return;
|
||||
*/
|
||||
|
||||
INPUTBOX_Append(Key);
|
||||
|
||||
/* Wait until exactly two digits are entered */
|
||||
if (gInputBoxIndex < 2)
|
||||
return;
|
||||
|
||||
/* Two digits entered */
|
||||
gInputBoxIndex = 0;
|
||||
|
||||
uint8_t value = (gInputBox[0] * 10) + gInputBox[1];
|
||||
|
||||
/* 00 = ALL scan lists */
|
||||
if (value == 0)
|
||||
{
|
||||
gEeprom.SCAN_LIST_DEFAULT = MR_CHANNELS_LIST + 1;
|
||||
UI_MAIN_NotifyScanProgressDataChanged();
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
SETTINGS_WriteCurrentState();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
/* 01 .. MR_CHANNELS_LIST */
|
||||
if (value <= MR_CHANNELS_LIST)
|
||||
{
|
||||
gEeprom.SCAN_LIST_DEFAULT = value;
|
||||
|
||||
if (!RADIO_CheckValidList(value))
|
||||
{
|
||||
/* Requested scan list is empty or invalid:
|
||||
jump to the next valid scan list */
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
RADIO_NextValidList(1);
|
||||
}
|
||||
UI_MAIN_NotifyScanProgressDataChanged();
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
SETTINGS_WriteCurrentState();
|
||||
#endif
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t Vfo = gEeprom.TX_VFO;
|
||||
|
||||
// Save full VFO state BEFORE first digit
|
||||
if (gInputBoxIndex == 0 && IS_FREQ_CHANNEL(gTxVfo->CHANNEL_SAVE)) {
|
||||
memcpy(&gVfoBackup, gTxVfo, sizeof(VFO_Info_t));
|
||||
gScreenChannelBackup = gEeprom.ScreenChannel[Vfo];
|
||||
gFreqChannelBackup = gEeprom.FreqChannel[Vfo];
|
||||
gHasVfoBackup = true;
|
||||
}
|
||||
|
||||
INPUTBOX_Append(Key);
|
||||
gKeyInputCountdown = key_input_timeout_500ms;
|
||||
|
||||
@@ -483,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;
|
||||
@@ -491,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;
|
||||
}
|
||||
|
||||
@@ -552,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)) {
|
||||
@@ -573,95 +670,77 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
return;
|
||||
}
|
||||
|
||||
gWasFKeyPressed = false;
|
||||
gUpdateStatus = true;
|
||||
HideFKeyIcon();
|
||||
|
||||
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
|
||||
|
||||
processFKeyFunction(Key, true);
|
||||
processFKeyFunction(Key, false);
|
||||
}
|
||||
|
||||
static void MAIN_Key_EXIT(bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if (!bKeyHeld && bKeyPressed) { // exit key pressed
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL; // beep when key is pressed
|
||||
return; // don't use the key till it's released
|
||||
}
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
if (gDTMF_CallState != DTMF_CALL_STATE_NONE && gCurrentFunction != FUNCTION_TRANSMIT)
|
||||
{ // clear CALL mode being displayed
|
||||
gDTMF_CallState = DTMF_CALL_STATE_NONE;
|
||||
gUpdateDisplay = true;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
if (!gFmRadioMode)
|
||||
#endif
|
||||
{
|
||||
if (gScanStateDir == SCAN_OFF) {
|
||||
if (gInputBoxIndex == 0)
|
||||
return;
|
||||
gInputBox[--gInputBoxIndex] = 10;
|
||||
|
||||
gKeyInputCountdown = key_input_timeout_500ms;
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
if (gInputBoxIndex == 0)
|
||||
gAnotherVoiceID = VOICE_ID_CANCEL;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
gScanKeepResult = false;
|
||||
CHFRSCANNER_Stop();
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_SCANNING_STOP;
|
||||
#endif
|
||||
}
|
||||
if (bKeyHeld) { // exit key held down
|
||||
if (bKeyPressed) {
|
||||
// Restore full VFO state on long press EXIT
|
||||
VFO_RestoreBackup();
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
ACTION_FM();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (bKeyHeld && bKeyPressed) { // exit key held down
|
||||
if (gInputBoxIndex > 0 || gDTMF_InputBox_Index > 0 || gDTMF_InputMode)
|
||||
{ // cancel key input mode (channel/frequency entry)
|
||||
gDTMF_InputMode = false;
|
||||
gDTMF_InputBox_Index = 0;
|
||||
memset(gDTMF_String, 0, sizeof(gDTMF_String));
|
||||
gInputBoxIndex = 0;
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
}
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
if (gDTMF_CallState != DTMF_CALL_STATE_NONE && gCurrentFunction != FUNCTION_TRANSMIT)
|
||||
{ // clear CALL mode being displayed
|
||||
gDTMF_CallState = DTMF_CALL_STATE_NONE;
|
||||
gUpdateDisplay = true;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
if (!gFmRadioMode)
|
||||
#endif
|
||||
{
|
||||
if (gScanStateDir == SCAN_OFF) {
|
||||
if (gInputBoxIndex == 0)
|
||||
return;
|
||||
gInputBox[--gInputBoxIndex] = 10;
|
||||
|
||||
// Restore full VFO state when back to 0
|
||||
if (gInputBoxIndex == 0)
|
||||
VFO_RestoreBackup();
|
||||
|
||||
gKeyInputCountdown = key_input_timeout_500ms;
|
||||
channelMoveSwitch();
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
if (gInputBoxIndex == 0)
|
||||
gAnotherVoiceID = VOICE_ID_CANCEL;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
gScanKeepResult = false;
|
||||
gInputBoxIndex = 0;
|
||||
CHFRSCANNER_Stop();
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_SCANNING_STOP;
|
||||
#endif
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
ACTION_FM();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
static void MAIN_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
@@ -673,12 +752,30 @@ static void MAIN_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
if (bKeyPressed) { // long press MENU key
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
// Exclude work with list 1, 2, 3 or all list
|
||||
// Exclude current scan entry
|
||||
if(gScanStateDir != SCAN_OFF)
|
||||
{
|
||||
if(FUNCTION_IsRx() || gScanPauseDelayIn_10ms > 9)
|
||||
{
|
||||
gMR_ChannelExclude[gTxVfo->CHANNEL_SAVE] = true;
|
||||
#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;
|
||||
@@ -700,7 +797,6 @@ static void MAIN_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
}
|
||||
|
||||
gWasFKeyPressed = false;
|
||||
gUpdateStatus = true;
|
||||
|
||||
ACTION_Handle(KEY_MENU, bKeyPressed, bKeyHeld);
|
||||
@@ -711,6 +807,9 @@ static void MAIN_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
}
|
||||
|
||||
if (!bKeyPressed && !gDTMF_InputMode) { // menu key released
|
||||
gKeyInputCountdown = 1;
|
||||
channelMoveSwitch();
|
||||
|
||||
const bool bFlag = !gInputBoxIndex;
|
||||
gInputBoxIndex = 0;
|
||||
|
||||
@@ -721,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;
|
||||
@@ -729,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;
|
||||
@@ -742,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;
|
||||
|
||||
@@ -758,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;
|
||||
@@ -780,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
|
||||
{
|
||||
@@ -798,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;
|
||||
@@ -834,18 +932,13 @@ static void MAIN_Key_STAR(bool bKeyPressed, bool bKeyHeld)
|
||||
|
||||
static void MAIN_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
{
|
||||
if (!gEeprom.SET_NAV) {
|
||||
Direction = -Direction;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN // Set Squelch F + UP or Down
|
||||
if(gWasFKeyPressed) {
|
||||
switch(Direction)
|
||||
{
|
||||
case 1:
|
||||
processFKeyFunction(KEY_UP, false);
|
||||
break;
|
||||
case -1:
|
||||
processFKeyFunction(KEY_DOWN, false);
|
||||
break;
|
||||
}
|
||||
processFKeyFunction(Direction == 1 ? KEY_UP : KEY_DOWN, true);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -855,12 +948,14 @@ static void MAIN_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
gPowerHigh = false;
|
||||
#endif
|
||||
|
||||
uint8_t Channel = gEeprom.ScreenChannel[gEeprom.TX_VFO];
|
||||
uint16_t Channel = gEeprom.ScreenChannel[gEeprom.TX_VFO];
|
||||
|
||||
if (gInputBoxIndex > 0) {
|
||||
gInputBoxIndex = 0;
|
||||
gHasVfoBackup = false;
|
||||
}
|
||||
|
||||
if (bKeyHeld || !bKeyPressed) { // key held or released
|
||||
if (gInputBoxIndex > 0)
|
||||
return; // leave if input box active
|
||||
|
||||
if (!bKeyPressed) {
|
||||
if (!bKeyHeld || IS_FREQ_CHANNEL(Channel))
|
||||
return;
|
||||
@@ -873,10 +968,6 @@ static void MAIN_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
}
|
||||
}
|
||||
else { // short pressed
|
||||
if (gInputBoxIndex > 0) {
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
}
|
||||
|
||||
@@ -885,7 +976,7 @@ static void MAIN_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
if (!IS_NOAA_CHANNEL(Channel))
|
||||
#endif
|
||||
{
|
||||
uint8_t Next;
|
||||
uint16_t Next;
|
||||
if (IS_FREQ_CHANNEL(Channel)) { // step/down in frequency
|
||||
const uint32_t frequency = APP_SetFrequencyByStep(gTxVfo, Direction);
|
||||
|
||||
@@ -901,7 +992,7 @@ static void MAIN_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
}
|
||||
|
||||
Next = RADIO_FindNextChannel(Channel + Direction, Direction, false, 0);
|
||||
if (Next == 0xFF)
|
||||
if (Next == 0xFFFF)
|
||||
return;
|
||||
if (Channel == Next)
|
||||
return;
|
||||
@@ -929,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;
|
||||
}
|
||||
@@ -977,18 +1066,8 @@ void MAIN_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
MAIN_Key_MENU(bKeyPressed, bKeyHeld);
|
||||
break;
|
||||
case KEY_UP:
|
||||
#ifdef ENABLE_NAVIG_LEFT_RIGHT
|
||||
MAIN_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
|
||||
#else
|
||||
MAIN_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
|
||||
#endif
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
#ifdef ENABLE_NAVIG_LEFT_RIGHT
|
||||
MAIN_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
|
||||
#else
|
||||
MAIN_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
|
||||
#endif
|
||||
MAIN_Key_UP_DOWN(bKeyPressed, bKeyHeld, Key == KEY_UP ? 1 : -1);
|
||||
break;
|
||||
case KEY_EXIT:
|
||||
MAIN_Key_EXIT(bKeyPressed, bKeyHeld);
|
||||
|
||||
+415
-198
File diff suppressed because it is too large.
Load diff
+22
-22
@@ -32,7 +32,7 @@ DCS_CodeType_t gScanCssResultType;
|
||||
uint8_t gScanCssResultCode;
|
||||
bool gScanSingleFrequency; // scan CTCSS/DCS codes for current frequency
|
||||
SCAN_SaveState_t gScannerSaveState;
|
||||
uint8_t gScanChannel;
|
||||
uint16_t gScanChannel;
|
||||
uint32_t gScanFrequency;
|
||||
SCAN_CssState_t gScanCssState;
|
||||
uint8_t gScanProgressIndicator;
|
||||
@@ -52,7 +52,7 @@ static void SCANNER_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_SCANNER;
|
||||
|
||||
if (gInputBoxIndex < 3) {
|
||||
if (gInputBoxIndex < 4) {
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
#endif
|
||||
@@ -61,13 +61,14 @@ 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] * 100) + (gInputBox[1] * 10) + gInputBox[2]) - 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;
|
||||
#endif
|
||||
gShowChPrefix = RADIO_CheckValidChannel(chan, false, 0);
|
||||
gScanChannel = (uint8_t)chan;
|
||||
gScanChannel = chan;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -207,7 +208,7 @@ static void SCANNER_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
gTxVfo->freq_config_TX.Code = gScanCssResultCode;
|
||||
}
|
||||
|
||||
uint8_t chan;
|
||||
uint16_t chan;
|
||||
if (IS_MR_CHANNEL(gTxVfo->CHANNEL_SAVE)) {
|
||||
chan = gScanChannel;
|
||||
gEeprom.MrChannel[gEeprom.TX_VFO] = chan;
|
||||
@@ -242,26 +243,27 @@ 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;
|
||||
}
|
||||
|
||||
if (!gEeprom.SET_NAV) {
|
||||
Direction = -Direction;
|
||||
}
|
||||
|
||||
if (gScannerSaveState == SCAN_SAVE_CHAN_SEL) {
|
||||
gScanChannel = NUMBER_AddWithWraparound(gScanChannel, Direction, 0, MR_CHANNEL_LAST);
|
||||
gShowChPrefix = RADIO_CheckValidChannel(gScanChannel, false, 0);
|
||||
gRequestDisplayScreen = DISPLAY_SCANNER;
|
||||
}
|
||||
else
|
||||
else if (!bKeyHeld)
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
}
|
||||
|
||||
@@ -275,10 +277,8 @@ void SCANNER_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
SCANNER_Key_MENU(bKeyPressed, bKeyHeld);
|
||||
break;
|
||||
case KEY_UP:
|
||||
SCANNER_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
SCANNER_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
|
||||
SCANNER_Key_UP_DOWN(bKeyPressed, bKeyHeld, Key == KEY_UP ? 1 : -1);
|
||||
break;
|
||||
case KEY_EXIT:
|
||||
SCANNER_Key_EXIT(bKeyPressed, bKeyHeld);
|
||||
@@ -342,7 +342,7 @@ void SCANNER_Start(bool singleFreq)
|
||||
gScanFrequency = 0xFFFFFFFF;
|
||||
|
||||
BK4819_PickRXFilterPathBasedOnFrequency(gScanFrequency);
|
||||
BK4819_EnableFrequencyScan();
|
||||
BK4819_SetFrequencyScan(true);
|
||||
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
@@ -415,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);
|
||||
@@ -506,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;
|
||||
@@ -525,4 +525,4 @@ void SCANNER_TimeSlice500ms(void)
|
||||
bool SCANNER_IsScanning(void)
|
||||
{
|
||||
return gCssBackgroundScan || (gScreenToDisplay == DISPLAY_SCANNER);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -40,7 +40,7 @@ extern DCS_CodeType_t gScanCssResultType;
|
||||
extern uint8_t gScanCssResultCode;
|
||||
extern bool gScanSingleFrequency;
|
||||
extern SCAN_SaveState_t gScannerSaveState;
|
||||
extern uint8_t gScanChannel;
|
||||
extern uint16_t gScanChannel;
|
||||
extern uint32_t gScanFrequency;
|
||||
extern SCAN_CssState_t gScanCssState;
|
||||
extern uint8_t gScanProgressIndicator;
|
||||
|
||||
+1141
-381
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
|
||||
};
|
||||
|
||||
|
||||
+27
-8
@@ -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);
|
||||
}
|
||||
@@ -677,14 +677,26 @@ bool UART_IsCommandAvailable(uint32_t Port)
|
||||
return false;
|
||||
}
|
||||
|
||||
while (1)
|
||||
// Limit iterations to prevent long loops when buffer is full of non-command data
|
||||
uint16_t maxIterations = ReadBufSize + 1;
|
||||
|
||||
while (maxIterations--)
|
||||
{
|
||||
if ((*pReadPointer) == DmaLength)
|
||||
return false;
|
||||
|
||||
while ((*pReadPointer) != DmaLength && ReadBuf[*pReadPointer] != 0xABU)
|
||||
// Find 0xAB with iteration limit
|
||||
uint16_t searchLimit = ReadBufSize;
|
||||
while ((*pReadPointer) != DmaLength && ReadBuf[*pReadPointer] != 0xABU && searchLimit--)
|
||||
*pReadPointer = DMA_INDEX((*pReadPointer), 1, ReadBufSize);
|
||||
|
||||
if (searchLimit == 0)
|
||||
{
|
||||
// Too many bytes without finding 0xAB - sync to current position and exit
|
||||
*pReadPointer = DmaLength;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((*pReadPointer) == DmaLength)
|
||||
return false;
|
||||
|
||||
@@ -702,6 +714,13 @@ bool UART_IsCommandAvailable(uint32_t Port)
|
||||
*pReadPointer = DMA_INDEX(*pReadPointer, 1, ReadBufSize);
|
||||
}
|
||||
|
||||
if (maxIterations == 0)
|
||||
{
|
||||
// Safety: too many outer loop iterations
|
||||
*pReadPointer = DmaLength;
|
||||
return false;
|
||||
}
|
||||
|
||||
Index = DMA_INDEX(*pReadPointer, 2, ReadBufSize);
|
||||
Size = (ReadBuf[DMA_INDEX(Index, 1, ReadBufSize)] << 8) | ReadBuf[Index];
|
||||
|
||||
@@ -847,4 +866,4 @@ void UART_HandleCommand(uint32_t Port)
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
gUART_LockScreenshot = 20; // lock screenshot
|
||||
#endif
|
||||
}
|
||||
}
|
||||
+38
-102
@@ -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
|
||||
@@ -306,7 +242,7 @@ static void LoadVoiceSamples()
|
||||
{
|
||||
gVoiceBuf[gVoiceBufWriteIndex][i] = VOICE_SAMPLES[Buf[i]];
|
||||
}
|
||||
VOCIE_BUF_ForwardWriteIndex();
|
||||
VOICE_BUF_ForwardWriteIndex();
|
||||
gVoiceBufLen++;
|
||||
}
|
||||
|
||||
|
||||
+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;
|
||||
|
||||
+38
-113
@@ -157,26 +157,18 @@ const uint8_t BITMAP_USB_C[9] =
|
||||
};
|
||||
#endif
|
||||
|
||||
const uint8_t BITMAP_Antenna[5] =
|
||||
{
|
||||
0b00000011,
|
||||
0b00000101,
|
||||
0b01111111,
|
||||
0b00000101,
|
||||
0b00000011
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_VFO_Lock[7] =
|
||||
{
|
||||
0b01111100,
|
||||
0b01000110,
|
||||
0b01000101,
|
||||
0b01000101,
|
||||
0b01000101,
|
||||
0b01000110,
|
||||
0b01111100,
|
||||
0b00111100,
|
||||
0b00100110,
|
||||
0b00100101,
|
||||
0b00100101,
|
||||
0b00100101,
|
||||
0b00100110,
|
||||
0b00111100,
|
||||
};
|
||||
|
||||
/*
|
||||
const uint8_t BITMAP_VFO_Default[7] =
|
||||
{
|
||||
0b01111111,
|
||||
@@ -198,108 +190,41 @@ const uint8_t BITMAP_VFO_NotDefault[7] =
|
||||
0b00010100,
|
||||
0b00001000
|
||||
};
|
||||
*/
|
||||
|
||||
const uint8_t BITMAP_ScanList0[7] =
|
||||
{ // '0' symbol
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01000011,
|
||||
0b01011101,
|
||||
0b01100001,
|
||||
0b01111111,
|
||||
0b01111111
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_ScanList1[7] =
|
||||
{ // '1' symbol
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01111011,
|
||||
0b01000001,
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01111111
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_ScanList2[7] =
|
||||
{ // '2' symbol
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01001101,
|
||||
0b01010101,
|
||||
0b01011011,
|
||||
0b01111111,
|
||||
0b01111111
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_ScanList3[7] =
|
||||
{ // '3' symbol
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01011101,
|
||||
0b01010101,
|
||||
0b01101011,
|
||||
0b01111111,
|
||||
0b01111111
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_ScanListE[7] =
|
||||
{ // 'E' symbol
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01000001,
|
||||
0b01010101,
|
||||
0b01010101,
|
||||
0b01111111,
|
||||
0b01111111
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_ScanList123[19] =
|
||||
// Compact arrow
|
||||
const uint8_t BITMAP_VFO_Default[7] =
|
||||
{
|
||||
// '123' symbol
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01111011,
|
||||
0b01000001,
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01001101,
|
||||
0b01010101,
|
||||
0b01011011,
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01011101,
|
||||
0b01010101,
|
||||
0b01101011,
|
||||
0b01111111,
|
||||
0b01111111
|
||||
0b00111110,
|
||||
0b00111110,
|
||||
0b00011100,
|
||||
0b00011100,
|
||||
0b00001000,
|
||||
0b00001000,
|
||||
0b00000000
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_ScanListAll[19] =
|
||||
// Compact empty arrow
|
||||
const uint8_t BITMAP_VFO_NotDefault[7] =
|
||||
{
|
||||
// 'All' symbol
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01000011,
|
||||
0b01110101,
|
||||
0b01000011,
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01000001,
|
||||
0b01011111,
|
||||
0b01011111,
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01000001,
|
||||
0b01011111,
|
||||
0b01011111,
|
||||
0b01111111,
|
||||
0b01111111
|
||||
0b00100010,
|
||||
0b00100010,
|
||||
0b00010100,
|
||||
0b00010100,
|
||||
0b00001000,
|
||||
0b00001000,
|
||||
0b00000000
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_VFO_Empty[7] =
|
||||
{
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_compand[6] =
|
||||
|
||||
+1
-8
@@ -35,17 +35,10 @@ extern const uint8_t BITMAP_NotReady[7];
|
||||
extern const uint8_t gFontVox[2][6];
|
||||
#endif
|
||||
|
||||
extern const uint8_t BITMAP_Antenna[5];
|
||||
extern const uint8_t BITMAP_VFO_Default[7];
|
||||
extern const uint8_t BITMAP_VFO_NotDefault[7];
|
||||
extern const uint8_t BITMAP_VFO_Empty[7];
|
||||
extern const uint8_t BITMAP_VFO_Lock[7];
|
||||
extern const uint8_t BITMAP_ScanList0[7];
|
||||
extern const uint8_t BITMAP_ScanList1[7];
|
||||
extern const uint8_t BITMAP_ScanList2[7];
|
||||
extern const uint8_t BITMAP_ScanList3[7];
|
||||
extern const uint8_t BITMAP_ScanList123[19];
|
||||
extern const uint8_t BITMAP_ScanListAll[19];
|
||||
extern const uint8_t BITMAP_ScanListE[7];
|
||||
extern const uint8_t BITMAP_PowerUser[3];
|
||||
extern const uint8_t BITMAP_compand[6];
|
||||
|
||||
|
||||
@@ -14,11 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include "dcs.h"
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
|
||||
#endif
|
||||
#include "misc.h"
|
||||
|
||||
// CTCSS Hz * 10
|
||||
const uint16_t CTCSS_Options[50] = {
|
||||
@@ -29,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,
|
||||
@@ -45,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;
|
||||
@@ -111,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
|
||||
|
||||
+89
-37
@@ -31,7 +31,7 @@
|
||||
#include "misc.h"
|
||||
#endif
|
||||
|
||||
#define PWM_FREQ 240
|
||||
#define PWM_FREQ 4000
|
||||
#define DUTY_CYCLE_LEVELS 64
|
||||
|
||||
#define DUTY_CYCLE_ON_VALUE GPIO_PIN_MASK(GPIO_PIN_BACKLIGHT)
|
||||
@@ -44,10 +44,28 @@ 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, 3, 6, 9, 15, 24, 38, 62, 100, 159, 255};
|
||||
const uint8_t value[] = {
|
||||
0, // 0 off
|
||||
8, // 1 visible in the dark
|
||||
14, // 2
|
||||
22, // 3
|
||||
32, // 4
|
||||
48, // 5
|
||||
72, // 6
|
||||
104, // 7
|
||||
150, // 8
|
||||
200, // 9
|
||||
255 // 10 max
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SLEEP
|
||||
@@ -79,7 +97,7 @@ void BACKLIGHT_InitHardware()
|
||||
| LL_DMA_MEMORY_INCREMENT //
|
||||
| LL_DMA_PDATAALIGN_WORD //
|
||||
| LL_DMA_MDATAALIGN_WORD //
|
||||
| LL_DMA_PRIORITY_LOW //
|
||||
| LL_DMA_PRIORITY_HIGH //
|
||||
);
|
||||
|
||||
LL_DMA_SetMemoryAddress(DMA1, DMA_CHANNEL, (uint32_t)dutyCycle);
|
||||
@@ -89,15 +107,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)
|
||||
{
|
||||
@@ -122,27 +151,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:
|
||||
@@ -178,18 +203,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);
|
||||
@@ -198,7 +216,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);
|
||||
@@ -219,11 +237,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
|
||||
@@ -20,10 +20,6 @@
|
||||
#include "driver/system.h"
|
||||
#include "misc.h"
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
||||
#endif
|
||||
|
||||
static const uint16_t BK1080_RegisterTable[] =
|
||||
{
|
||||
0x0008, 0x1080, 0x0201, 0x0000, 0x40C0, 0x0A1F, 0x002E, 0x02FF,
|
||||
|
||||
@@ -163,7 +163,7 @@ typedef enum BK4819_GPIO_PIN_t BK4819_GPIO_PIN_t;
|
||||
#define BK4819_REG_02_SHIFT_SQUELCH_LOST 2
|
||||
#define BK4819_REG_02_SHIFT_FSK_RX_SYNC 1
|
||||
|
||||
#define BK4819_REG_02_MASK_FSK_TX_FINISHED (1U << BK4819_REG_02_SHIFT_FSK_TX)
|
||||
#define BK4819_REG_02_MASK_FSK_TX_FINISHED (1U << BK4819_REG_02_SHIFT_FSK_TX_FINISHED)
|
||||
#define BK4819_REG_02_MASK_FSK_FIFO_ALMOST_EMPTY (1U << BK4819_REG_02_SHIFT_FSK_FIFO_ALMOST_EMPTY)
|
||||
#define BK4819_REG_02_MASK_FSK_RX_FINISHED (1U << BK4819_REG_02_SHIFT_FSK_RX_FINISHED)
|
||||
#define BK4819_REG_02_MASK_FSK_FIFO_ALMOST_FULL (1U << BK4819_REG_02_SHIFT_FSK_FIFO_ALMOST_FULL)
|
||||
|
||||
+1
-4
@@ -19,6 +19,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "settings.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include "audio.h"
|
||||
|
||||
@@ -28,10 +29,6 @@
|
||||
#include "driver/systick.h"
|
||||
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
|
||||
#endif
|
||||
|
||||
#define PIN_CSN GPIO_MAKE_PIN(GPIOF, LL_GPIO_PIN_9)
|
||||
#define PIN_SCL GPIO_MAKE_PIN(GPIOB, LL_GPIO_PIN_8)
|
||||
#define PIN_SDA GPIO_MAKE_PIN(GPIOB, LL_GPIO_PIN_9)
|
||||
|
||||
+10
-3
@@ -48,7 +48,8 @@ enum BK4819_FilterBandwidth_t
|
||||
{
|
||||
BK4819_FILTER_BW_WIDE = 0,
|
||||
BK4819_FILTER_BW_NARROW,
|
||||
BK4819_FILTER_BW_NARROWER
|
||||
BK4819_FILTER_BW_NARROWER,
|
||||
BK4819_FILTER_BW_AM
|
||||
};
|
||||
|
||||
typedef enum BK4819_FilterBandwidth_t BK4819_FilterBandwidth_t;
|
||||
@@ -104,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);
|
||||
@@ -115,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);
|
||||
@@ -142,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);
|
||||
|
||||
+221
-240
@@ -19,6 +19,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "settings.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include "audio.h"
|
||||
|
||||
@@ -27,11 +28,6 @@
|
||||
#include "driver/system.h"
|
||||
#include "driver/systick.h"
|
||||
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
|
||||
#endif
|
||||
|
||||
#define PIN_CSN GPIO_MAKE_PIN(GPIOF, LL_GPIO_PIN_9)
|
||||
#define PIN_SCL GPIO_MAKE_PIN(GPIOB, LL_GPIO_PIN_8)
|
||||
#define PIN_SDA GPIO_MAKE_PIN(GPIOB, LL_GPIO_PIN_9)
|
||||
@@ -43,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()
|
||||
@@ -177,7 +183,7 @@ void BK4819_Init(void)
|
||||
|
||||
BK4819_WriteRegister(0x73, 0x4691);
|
||||
BK4819_WriteRegister(0x77, 0x88EF);
|
||||
BK4819_WriteRegister(BK4819_REG_19, 0x104E);
|
||||
BK4819_WriteRegister(BK4819_REG_19, 0x1041);
|
||||
BK4819_WriteRegister(BK4819_REG_28, 0x0B40);
|
||||
BK4819_WriteRegister(BK4819_REG_29, 0xAA00);
|
||||
BK4819_WriteRegister(0x2A, 0x6600);
|
||||
@@ -201,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();
|
||||
@@ -275,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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,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
|
||||
@@ -391,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)
|
||||
@@ -629,102 +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;
|
||||
@@ -803,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
|
||||
@@ -1044,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;
|
||||
|
||||
@@ -1059,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)
|
||||
{
|
||||
@@ -1082,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)
|
||||
{
|
||||
@@ -1160,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)
|
||||
@@ -1453,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)
|
||||
@@ -1552,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);
|
||||
@@ -1587,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
|
||||
//
|
||||
@@ -1603,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)
|
||||
@@ -1692,7 +1682,7 @@ void BK4819_Disable(void)
|
||||
|
||||
void BK4819_StopScan(void)
|
||||
{
|
||||
BK4819_DisableFrequencyScan();
|
||||
BK4819_SetFrequencyScan(false);
|
||||
BK4819_Disable();
|
||||
}
|
||||
|
||||
@@ -1786,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
|
||||
|
||||
+34
-24
@@ -38,30 +38,40 @@ typedef struct
|
||||
#define _MK_MAPPING(PY25Q16_Addr, EEPROM_From, EEPROM_To) {PY25Q16_Addr, EEPROM_From, EEPROM_To - EEPROM_From}
|
||||
|
||||
static const AddrMapping_t ADDR_MAPPINGS[] = {
|
||||
// Sorted by EEPROM addr
|
||||
_MK_MAPPING(0x000000, 0x0000, 0x0c80), //
|
||||
_MK_MAPPING(0x001000, 0x0c80, 0x0d60), //
|
||||
_MK_MAPPING(0x002000, 0x0d60, 0x0e30), //
|
||||
_MK_MAPPING(HOLE_ADDR, 0x0e30, 0x0e40), //
|
||||
_MK_MAPPING(0x003000, 0x0e40, 0x0e68), //
|
||||
_MK_MAPPING(HOLE_ADDR, 0x0e68, 0x0e70), //
|
||||
_MK_MAPPING(0x004000, 0x0e70, 0x0e80), //
|
||||
_MK_MAPPING(0x005000, 0x0e80, 0x0e88), //
|
||||
_MK_MAPPING(0x006000, 0x0e88, 0x0e90), //
|
||||
_MK_MAPPING(0x007000, 0x0e90, 0x0ee0), //
|
||||
_MK_MAPPING(0x008000, 0x0ee0, 0x0f18), //
|
||||
_MK_MAPPING(0x009000, 0x0f18, 0x0f20), //
|
||||
_MK_MAPPING(HOLE_ADDR, 0x0f20, 0x0f30), //
|
||||
_MK_MAPPING(0x00a000, 0x0f30, 0x0f40), //
|
||||
_MK_MAPPING(0x00b000, 0x0f40, 0x0f48), //
|
||||
_MK_MAPPING(HOLE_ADDR, 0x0f48, 0x0f50), //
|
||||
_MK_MAPPING(0x00e000, 0x0f50, 0x1bd0), //
|
||||
_MK_MAPPING(HOLE_ADDR, 0x1bd0, 0x1c00), //
|
||||
_MK_MAPPING(0x00f000, 0x1c00, 0x1d00), //
|
||||
_MK_MAPPING(HOLE_ADDR, 0x1d00, 0x1e00), //
|
||||
_MK_MAPPING(0x010000, 0x1e00, 0x1f90), //
|
||||
_MK_MAPPING(HOLE_ADDR, 0x1f90, 0x1ff0), //
|
||||
_MK_MAPPING(0x00c000, 0x1ff0, 0x2000), //
|
||||
_MK_MAPPING(0x000000, 0x000000, 0x001000), // 256 MR Freq * 16 Bytes (ex 0x000000)
|
||||
_MK_MAPPING(0x001000, 0x001000, 0x002000), // 256 MR Freq * 16 Bytes
|
||||
_MK_MAPPING(0x002000, 0x002000, 0x003000), // 256 MR Freq * 16 Bytes
|
||||
_MK_MAPPING(0x003000, 0x003000, 0x004000), // 256 MR Freq * 16 Bytes
|
||||
|
||||
_MK_MAPPING(0x004000, 0x004000, 0x005000), // 256 MR Name * 16 Bytes (ex 0x00e000)
|
||||
_MK_MAPPING(0x005000, 0x005000, 0x006000), // 256 MR Name * 16 Bytes
|
||||
_MK_MAPPING(0x006000, 0x006000, 0x007000), // 256 MR Name * 16 Bytes
|
||||
_MK_MAPPING(0x007000, 0x007000, 0x008000), // 256 MR Name * 16 Bytes
|
||||
|
||||
_MK_MAPPING(0x008000, 0x008000, 0x00886E), // 1024 MR + 7 VFO Attributes * 2 Bytes (ex 0x002000) 0x008000 -> 0x00880E
|
||||
// List name * 4 Bytes 0x00880E -> 0x00886E
|
||||
|
||||
_MK_MAPPING(0x009000, 0x009000, 0x0090D6), // 14 VFO * 16 Bytes (ex 0x001000)
|
||||
|
||||
_MK_MAPPING(0x00A000, 0x00A000, 0x00A170), // Settings * 16 Bytes (ex 0x004000) 0x00A000 -> 0x00A010
|
||||
// Settings * 16 Bytes (ex 0x005000) 0x00A010 -> 0x00A020
|
||||
// Settings FM * 8 Bytes (0x006000) 0x00A020 -> 0x00A028
|
||||
// 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 -> 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);
|
||||
|
||||
+27
-3
@@ -22,6 +22,11 @@
|
||||
#define PIN_SCL GPIO_MAKE_PIN(GPIOF, LL_GPIO_PIN_5)
|
||||
#define PIN_SDA GPIO_MAKE_PIN(GPIOF, LL_GPIO_PIN_6)
|
||||
|
||||
// CRITICAL FIX: Define I2C timeout to prevent infinite hangs
|
||||
// If I2C slave doesn't respond, timeout after this many iterations
|
||||
// 255 iterations × 1µs = 255µs, plus we add safety margin
|
||||
#define I2C_ACK_TIMEOUT_ITERATIONS 255
|
||||
|
||||
static inline void SCL_Set()
|
||||
{
|
||||
GPIO_SetOutputPin(PIN_SCL);
|
||||
@@ -141,11 +146,24 @@ int I2C_Write(uint8_t Data)
|
||||
SCL_Set();
|
||||
SYSTICK_DelayUs(1);
|
||||
|
||||
for (i = 0; i < 255; i++) {
|
||||
// CRITICAL FIX #1: Add timeout to ACK check
|
||||
// Original: for (i = 0; i < 255; i++)
|
||||
// Problem: If I2C slave doesn't respond, waits 255 iterations = 255µs spin-wait
|
||||
// If slave broken/disconnected: loops forever with no break condition
|
||||
//
|
||||
// Solution: Keep iteration limit but add explicit timeout check
|
||||
// If slave doesn't pull SDA low within 255 iterations, return error
|
||||
// This prevents indefinite hangs while still being conservative
|
||||
|
||||
for (i = 0; i < I2C_ACK_TIMEOUT_ITERATIONS; i++) {
|
||||
if (!SDA_IsSet()) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
// CRITICAL FIX #2: Small delay between ACK checks
|
||||
// This prevents CPU from spinning 255 times at full speed
|
||||
// Also gives slave time to respond
|
||||
SYSTICK_DelayUs(1);
|
||||
}
|
||||
|
||||
SCL_Reset();
|
||||
@@ -178,11 +196,17 @@ int I2C_WriteBuffer(const void *pBuffer, uint8_t Size)
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < Size; i++) {
|
||||
// CRITICAL FIX #3: Check return value of I2C_Write
|
||||
// Original: if (I2C_Write(*pData++) < 0)
|
||||
// This detects if ACK timeout occurred
|
||||
// If slave not responding, return error immediately instead of continuing
|
||||
if (I2C_Write(*pData++) < 0) {
|
||||
// I2C slave not responding (ACK timeout)
|
||||
// Return error instead of continuing to write more bytes
|
||||
// This prevents cascading timeouts
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
+161
-20
@@ -27,6 +27,84 @@ KEY_Code_t gKeyReading1 = KEY_INVALID;
|
||||
uint16_t gDebounceCounter = 0;
|
||||
bool gWasFKeyPressed = false;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
// Short press: hold key for SERIAL_KEY_SHORT_POLLS calls.
|
||||
// Must exceed key_debounce_10ms (2) to trigger ProcessKey(key, true, false).
|
||||
#define SERIAL_KEY_SHORT_POLLS 5
|
||||
|
||||
// Long press: hold key for SERIAL_KEY_LONG_POLLS calls.
|
||||
// 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 or long press from serial (UART or VCP).
|
||||
// KEY_PTT is explicitly blocked — PTT release cannot be guaranteed over serial.
|
||||
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 = keyLong;
|
||||
}
|
||||
}
|
||||
|
||||
bool KEYBOARD_ProcessProtocolByte(ParseState_t *state, uint8_t b)
|
||||
{
|
||||
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
|
||||
|
||||
#define GPIOx GPIOB
|
||||
#define PIN_MASK_COLS (LL_GPIO_PIN_6 | LL_GPIO_PIN_5 | LL_GPIO_PIN_4 | LL_GPIO_PIN_3)
|
||||
#define PIN_COLS GPIO_MAKE_PIN(GPIOx, PIN_MASK_COLS)
|
||||
@@ -78,38 +156,80 @@ static const KEY_Code_t keyboard[5][4] = {
|
||||
|
||||
KEY_Code_t KEYBOARD_Poll(void)
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
// Serial-injected key: hold it for SHORT or LONG polls depending on press type,
|
||||
// so the debounce counter in app.c reaches the right threshold:
|
||||
// - Short: key_debounce_10ms (2) → ProcessKey(key, true, false)
|
||||
// - Long: key_repeat_delay_10ms (40) → ProcessKey(key, true, true)
|
||||
// Once the hold count is exhausted we clear it — next call returns KEY_INVALID,
|
||||
// which triggers the release path in app.c naturally.
|
||||
if (gKeyFromSerial != KEY_INVALID) {
|
||||
KEY_Code_t injected = gKeyFromSerial;
|
||||
uint8_t threshold = gSerialKeyLong ? SERIAL_KEY_LONG_POLLS : SERIAL_KEY_SHORT_POLLS;
|
||||
if (++gSerialKeyHoldCount >= threshold) {
|
||||
gKeyFromSerial = KEY_INVALID;
|
||||
gSerialKeyHoldCount = 0;
|
||||
gSerialKeyLong = 0;
|
||||
}
|
||||
return injected;
|
||||
}
|
||||
#endif
|
||||
|
||||
KEY_Code_t Key = KEY_INVALID;
|
||||
|
||||
// if (!GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT))
|
||||
// return KEY_PTT;
|
||||
|
||||
// *****************
|
||||
|
||||
// Scan all 5 columns - never break early to avoid GPIO state issues
|
||||
for (unsigned int j = 0; j < 5; j++)
|
||||
{
|
||||
uint32_t reg;
|
||||
unsigned int i;
|
||||
unsigned int k;
|
||||
uint32_t match_count = 0; // Count consecutive matching reads
|
||||
|
||||
// Set all high
|
||||
// Set all columns high first
|
||||
GPIO_SetOutputPin(PIN_COLS);
|
||||
|
||||
// Clear the pin we are selecting
|
||||
// Clear the specific column we are selecting
|
||||
if (j > 0)
|
||||
GPIO_ResetOutputPin(PIN_COL(j - 1));
|
||||
|
||||
// Read all 4 GPIO pins at once .. with de-noise, max of 8 sample loops
|
||||
for (i = 0, k = 0, reg = 0; i < 3 && k < 8; i++, k++)
|
||||
{
|
||||
SYSTICK_DelayUs(1);
|
||||
uint32_t reg2 = read_rows();
|
||||
i *= reg == reg2;
|
||||
reg = reg2;
|
||||
GPIO_ResetOutputPin(PIN_COL(j - 1));
|
||||
}
|
||||
|
||||
if (i < 3)
|
||||
break; // noise is too bad
|
||||
// Debounce: Read rows multiple times and look for stable reads
|
||||
// CRITICAL FIX #1: Proper debounce logic (replaces confusing i *= syntax)
|
||||
// CRITICAL FIX #2: Increased delay from 1µs to 10µs for real keyboard bounce capture
|
||||
// CRITICAL FIX #3: Clear match_count if reads differ (noise detection)
|
||||
reg = 0;
|
||||
for (unsigned int k = 0; k < 8; k++)
|
||||
{
|
||||
SYSTICK_DelayUs(10); // FIX #2: Increased from 1µs to 10µs
|
||||
uint32_t reg2 = read_rows();
|
||||
|
||||
// FIX #3: Clear match counter if values don't match
|
||||
if (reg2 != reg)
|
||||
{
|
||||
match_count = 0;
|
||||
reg = reg2;
|
||||
}
|
||||
else
|
||||
{
|
||||
match_count++;
|
||||
}
|
||||
|
||||
// Success: We have 3 consecutive matching reads = stable signal
|
||||
if (match_count >= 2)
|
||||
{
|
||||
break; // Debounce complete for this column
|
||||
}
|
||||
}
|
||||
|
||||
// FIX #1: Do NOT break on noise - continue scanning all columns
|
||||
// Only skip key detection if debounce failed, but GPIO state is cleaned
|
||||
if (match_count < 2)
|
||||
{
|
||||
// Debounce failed (too much noise), but continue to next column
|
||||
// This prevents GPIO from staying in invalid state and blocking other columns
|
||||
continue;
|
||||
}
|
||||
|
||||
// Debounce successful, check which row is pressed in this column
|
||||
for (unsigned int i = 0; i < 4; i++)
|
||||
{
|
||||
if (!(reg & PIN_MASK_ROW(i)))
|
||||
@@ -120,8 +240,29 @@ KEY_Code_t KEYBOARD_Poll(void)
|
||||
}
|
||||
|
||||
if (Key != KEY_INVALID)
|
||||
break;
|
||||
{
|
||||
break; // Found a valid key, stop scanning
|
||||
}
|
||||
}
|
||||
|
||||
// CRITICAL FIX #4: Always clean up GPIO state - set all columns high at end
|
||||
// This ensures GPIO pins are in a known state even if function exited early due to noise
|
||||
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;
|
||||
}
|
||||
+21
-1
@@ -45,12 +45,32 @@ 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;
|
||||
extern bool gWasFKeyPressed;
|
||||
|
||||
KEY_Code_t KEYBOARD_Poll(void);
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
// Serial-injected key (written by UART/VCP parser, consumed by KEYBOARD_Poll).
|
||||
extern volatile KEY_Code_t gKeyFromSerial;
|
||||
|
||||
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
|
||||
+39
-14
@@ -25,6 +25,7 @@
|
||||
#include "driver/system.h"
|
||||
#include "driver/systick.h"
|
||||
#include "external/printf/printf.h"
|
||||
#include "misc.h"
|
||||
|
||||
// #define DEBUG
|
||||
|
||||
@@ -39,7 +40,7 @@
|
||||
|
||||
static uint32_t SectorCacheAddr = 0x1000000;
|
||||
static uint8_t SectorCache[SECTOR_SIZE];
|
||||
static uint8_t BlackHole[1];
|
||||
static uint8_t BlackHole[4] __attribute__((aligned(4)));
|
||||
static volatile bool TC_Flag;
|
||||
|
||||
static inline void CS_Assert()
|
||||
@@ -226,20 +227,20 @@ void PY25Q16_Init()
|
||||
|
||||
void PY25Q16_ReadBuffer(uint32_t Address, void *pBuffer, uint32_t Size)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("spi flash read: %06x %ld\n", Address, Size);
|
||||
#endif
|
||||
CS_Assert();
|
||||
|
||||
SPI_WriteByte(0x03); // Fast read
|
||||
WriteAddr(Address);
|
||||
SPI_WriteByte(0x03); // Send read command
|
||||
WriteAddr(Address); // Send address (3 bytes)
|
||||
|
||||
if (Size >= 16)
|
||||
// CRITICAL: Flush RX FIFO before DMA to remove residual data
|
||||
while (LL_SPI_RX_FIFO_EMPTY != LL_SPI_GetRxFIFOLevel(SPIx))
|
||||
{
|
||||
SPI_ReadBuf((uint8_t *)pBuffer, Size);
|
||||
LL_SPI_ReceiveData8(SPIx); // Read and discard
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (Size >= 16) {
|
||||
SPI_ReadBuf((uint8_t *)pBuffer, Size);
|
||||
} else {
|
||||
for (uint32_t i = 0; i < Size; i++)
|
||||
{
|
||||
((uint8_t *)(pBuffer))[i] = SPI_WriteByte(0xff);
|
||||
@@ -254,6 +255,11 @@ void PY25Q16_WriteBuffer(uint32_t Address, const void *pBuffer, uint32_t Size, b
|
||||
#ifdef DEBUG
|
||||
printf("spi flash write: %06x %ld %d\n", Address, Size, Append);
|
||||
#endif
|
||||
|
||||
//#ifdef ENABLE_FEAT_F4HWN_DEBUG
|
||||
// gDebug++;
|
||||
//#endif
|
||||
|
||||
uint32_t SecIndex = Address / SECTOR_SIZE;
|
||||
uint32_t SecAddr = SecIndex * SECTOR_SIZE;
|
||||
uint32_t SecOffset = Address % SECTOR_SIZE;
|
||||
@@ -261,6 +267,9 @@ void PY25Q16_WriteBuffer(uint32_t Address, const void *pBuffer, uint32_t Size, b
|
||||
|
||||
while (Size)
|
||||
{
|
||||
// CRITICAL FIX #1: Wait for flash ready before processing each sector
|
||||
WaitWIP();
|
||||
|
||||
if (Size < SecSize)
|
||||
{
|
||||
SecSize = Size;
|
||||
@@ -289,6 +298,10 @@ void PY25Q16_WriteBuffer(uint32_t Address, const void *pBuffer, uint32_t Size, b
|
||||
if (Erase)
|
||||
{
|
||||
SectorErase(SecAddr);
|
||||
|
||||
// CRITICAL FIX #2: Erase takes ~300ms, must complete before program starts
|
||||
WaitWIP();
|
||||
|
||||
if (Append)
|
||||
{
|
||||
SectorProgram(SecAddr, SectorCache, SecOffset + SecSize);
|
||||
@@ -313,6 +326,9 @@ void PY25Q16_WriteBuffer(uint32_t Address, const void *pBuffer, uint32_t Size, b
|
||||
SecOffset = 0;
|
||||
SecSize = SECTOR_SIZE;
|
||||
} // while
|
||||
|
||||
// CRITICAL FIX #3: Ensure all writes complete before function returns
|
||||
WaitWIP();
|
||||
}
|
||||
|
||||
void PY25Q16_SectorErase(uint32_t Address)
|
||||
@@ -454,11 +470,20 @@ void DMA1_Channel4_5_6_7_IRQHandler()
|
||||
LL_DMA_DisableIT_TC(DMA1, CHANNEL_RD);
|
||||
LL_DMA_ClearFlag_TC4(DMA1);
|
||||
|
||||
while (LL_SPI_TX_FIFO_EMPTY != LL_SPI_GetTxFIFOLevel(SPIx))
|
||||
// Wait a tiny bit for SPI to finish
|
||||
SYSTICK_DelayUs(10); // ← ADD THIS
|
||||
|
||||
uint32_t timeout = 10000;
|
||||
|
||||
while ((LL_SPI_TX_FIFO_EMPTY != LL_SPI_GetTxFIFOLevel(SPIx)) && timeout--)
|
||||
;
|
||||
while (LL_SPI_IsActiveFlag_BSY(SPIx))
|
||||
|
||||
timeout = 10000;
|
||||
while (LL_SPI_IsActiveFlag_BSY(SPIx) && timeout--)
|
||||
;
|
||||
while (LL_SPI_RX_FIFO_EMPTY != LL_SPI_GetRxFIFOLevel(SPIx))
|
||||
|
||||
timeout = 10000;
|
||||
while ((LL_SPI_RX_FIFO_EMPTY != LL_SPI_GetRxFIFOLevel(SPIx)) && timeout--)
|
||||
;
|
||||
|
||||
LL_SPI_DisableDMAReq_TX(SPIx);
|
||||
@@ -466,4 +491,4 @@ void DMA1_Channel4_5_6_7_IRQHandler()
|
||||
|
||||
TC_Flag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "driver/st7565.h"
|
||||
#include "driver/system.h"
|
||||
#include "misc.h"
|
||||
#include "screenshot.h"
|
||||
|
||||
#define SPIx SPI1
|
||||
|
||||
@@ -163,6 +164,9 @@ void ST7565_DrawLine(const unsigned int Column, const unsigned int Line, const u
|
||||
void ST7565_BlitLine(unsigned line)
|
||||
{
|
||||
ST7565_BlitScreen(line + 1);
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
SCREENSHOT_Update(true); // Force immediate capture
|
||||
#endif
|
||||
}
|
||||
|
||||
void ST7565_BlitStatusLine(void)
|
||||
|
||||
+24
-19
@@ -13,39 +13,44 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "py32f0xx.h"
|
||||
#include "systick.h"
|
||||
#include "misc.h"
|
||||
|
||||
// 0x20000324
|
||||
static uint32_t gTickMultiplier;
|
||||
|
||||
void SYSTICK_Init(void)
|
||||
{
|
||||
SysTick_Config(480000);
|
||||
gTickMultiplier = 48;
|
||||
SysTick_Config(SystemCoreClock / 100); // 10 ms (1/100 sec) systick interrupt
|
||||
gTickMultiplier = SystemCoreClock / 1000000;
|
||||
|
||||
NVIC_SetPriority(SysTick_IRQn, 0);
|
||||
}
|
||||
|
||||
void SYSTICK_DelayUs(uint32_t Delay)
|
||||
{
|
||||
const uint32_t ticks = Delay * gTickMultiplier;
|
||||
uint32_t elapsed_ticks = 0;
|
||||
uint32_t Start = SysTick->LOAD;
|
||||
uint32_t Previous = SysTick->VAL;
|
||||
do {
|
||||
uint32_t Current;
|
||||
|
||||
do {
|
||||
Current = SysTick->VAL;
|
||||
} while (Current == Previous);
|
||||
|
||||
uint32_t Delta = ((Current < Previous) ? - Current : Start - Current);
|
||||
|
||||
elapsed_ticks += Delta + Previous;
|
||||
|
||||
|
||||
// FIX: Simplified loop - removed inner "wait for change" loop
|
||||
while (elapsed_ticks < ticks)
|
||||
{
|
||||
uint32_t Current = SysTick->VAL;
|
||||
|
||||
// FIX: Corrected wraparound detection (was: "Current < Previous - 10")
|
||||
if (Current < Previous)
|
||||
{
|
||||
// Normal countdown case
|
||||
uint32_t Delta = Previous - Current;
|
||||
elapsed_ticks += Delta;
|
||||
}
|
||||
else if (Current > Previous)
|
||||
{
|
||||
// Wraparound case: SysTick went 0 → LOAD → Current
|
||||
uint32_t Delta = Previous + (Start - Current);
|
||||
elapsed_ticks += Delta;
|
||||
}
|
||||
|
||||
Previous = Current;
|
||||
} while (elapsed_ticks < ticks);
|
||||
}
|
||||
}
|
||||
}
|
||||
+51
-11
@@ -22,11 +22,19 @@
|
||||
#include "py32f071_ll_dma.h"
|
||||
#include "py32f071_ll_gpio.h"
|
||||
#include "py32f071_ll_usart.h"
|
||||
#include "driver/uart.h"
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
#include "driver/keyboard.h"
|
||||
#endif
|
||||
|
||||
#define USARTx USART1
|
||||
#define DMA_CHANNEL LL_DMA_CHANNEL_2
|
||||
|
||||
// CRITICAL FIX: Define UART TX timeout
|
||||
// If UART TX buffer doesn't clear within this many iterations, skip byte and continue
|
||||
// Prevents permanent freeze if UART is stuck
|
||||
#define UART_TX_TIMEOUT_ITERATIONS 10000
|
||||
|
||||
static bool UART_IsLogEnabled;
|
||||
uint8_t UART_DMA_Buffer[256];
|
||||
|
||||
@@ -111,9 +119,27 @@ void UART_Send(const void *pBuffer, uint32_t Size)
|
||||
|
||||
for (i = 0; i < Size; i++)
|
||||
{
|
||||
while (!LL_USART_IsActiveFlag_TXE(USARTx))
|
||||
;
|
||||
LL_USART_TransmitData8(USARTx, pData[i]);
|
||||
// CRITICAL FIX: Add timeout to UART TX busy-wait
|
||||
// Original: while (!LL_USART_IsActiveFlag_TXE(USARTx)) ;
|
||||
// Problem: If UART TX stuck or disconnected, this is infinite loop
|
||||
// Result: Radio freeze for 100ms+ while trying to send one character
|
||||
//
|
||||
// Solution: Add iteration counter timeout
|
||||
// If TXE flag doesn't set within timeout iterations, skip byte and continue
|
||||
// This caps maximum freeze at ~10ms per UART_Send() call
|
||||
|
||||
uint32_t timeout = UART_TX_TIMEOUT_ITERATIONS;
|
||||
while (!LL_USART_IsActiveFlag_TXE(USARTx) && timeout > 0)
|
||||
{
|
||||
timeout--;
|
||||
}
|
||||
|
||||
// Send byte only if TXE flag is set
|
||||
// If timeout occurred, skip this byte and continue
|
||||
if (timeout > 0)
|
||||
{
|
||||
LL_USART_TransmitData8(USARTx, pData[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,12 +152,26 @@ void UART_LogSend(const void *pBuffer, uint32_t Size)
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
bool UART_IsCableConnected(void) {
|
||||
for (size_t i = 0; i < sizeof(UART_DMA_Buffer); i++) {
|
||||
if (UART_DMA_Buffer[i] == 0x55) {
|
||||
UART_DMA_Buffer[i] = 0x00; // Clear only the matched byte
|
||||
return true;
|
||||
}
|
||||
static uint8_t read_ptr = 0;
|
||||
static ParseState_t state = STATE_IDLE;
|
||||
|
||||
bool connected = false;
|
||||
|
||||
// DMA write position: NbData counts DOWN from 256
|
||||
uint8_t write_ptr = (uint8_t)(sizeof(UART_DMA_Buffer) -
|
||||
LL_DMA_GetDataLength(DMA1, DMA_CHANNEL));
|
||||
|
||||
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;
|
||||
}
|
||||
return false;
|
||||
|
||||
return connected;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -18,6 +18,10 @@
|
||||
#include "usb_config.h"
|
||||
#include "py32f071_ll_bus.h"
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
#include "driver/keyboard.h"
|
||||
#endif
|
||||
|
||||
uint8_t VCP_RxBuf[VCP_RX_BUF_SIZE];
|
||||
volatile uint32_t VCP_RxBufPointer = 0;
|
||||
|
||||
@@ -37,3 +41,52 @@ void VCP_Init()
|
||||
NVIC_SetPriority(USBD_IRQn, 3);
|
||||
NVIC_EnableIRQ(USBD_IRQn);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
bool VCP_ScreenshotPing(void)
|
||||
{
|
||||
// State machine for parsing incoming packets:
|
||||
// Keepalive: 0x55 0xAA 0x00 0x00 → viewer alive
|
||||
// Short key press: 0xAA 0x55 0x03 <key> → inject short press
|
||||
// Long key press: 0xAA 0x55 0x04 <key> → inject long press
|
||||
//
|
||||
// State transitions:
|
||||
// IDLE → 0x55 → KA_1
|
||||
// KA_1 → 0xAA → KA_2 (else IDLE)
|
||||
// KA_2 → 0x00 → KA_3 (else IDLE)
|
||||
// KA_3 → 0x00 → keepalive OK, IDLE
|
||||
//
|
||||
// IDLE → 0xAA → KEY_1
|
||||
// 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> → KEYBOARD_InjectKey(b, false), IDLE
|
||||
// KEY_3L → <b> → KEYBOARD_InjectKey(b, true), IDLE
|
||||
|
||||
static uint8_t read_ptr = 0;
|
||||
static ParseState_t state = STATE_IDLE;
|
||||
|
||||
bool connected = false;
|
||||
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
|
||||
// (buffer overflow / corrupted state), which would freeze the firmware.
|
||||
uint32_t processed = 0;
|
||||
|
||||
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++;
|
||||
|
||||
if(KEYBOARD_ProcessProtocolByte(&state, b))
|
||||
connected = true;
|
||||
}
|
||||
|
||||
return connected;
|
||||
}
|
||||
#endif // ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include "usb_config.h"
|
||||
|
||||
#define VCP_RX_BUF_SIZE 256
|
||||
@@ -28,6 +29,10 @@ extern volatile uint32_t VCP_RxBufPointer;
|
||||
|
||||
void VCP_Init();
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
bool VCP_ScreenshotPing(void);
|
||||
#endif
|
||||
|
||||
static inline void VCP_Send(const uint8_t *Buf, uint32_t Size)
|
||||
{
|
||||
cdc_acm_data_send_with_dtr(Buf, Size);
|
||||
|
||||
+4
-4
@@ -97,7 +97,7 @@ void VOICE_Start()
|
||||
if (gVoiceBufLen > 0)
|
||||
{
|
||||
memcpy(DAC_Buf, gVoiceBuf[gVoiceBufReadIndex], VOICE_BUF_SIZE);
|
||||
VOCIE_BUF_ForwardReadIndex();
|
||||
VOICE_BUF_ForwardReadIndex();
|
||||
gVoiceBufLen--;
|
||||
}
|
||||
else
|
||||
@@ -110,7 +110,7 @@ void VOICE_Start()
|
||||
gVoiceBuf[gVoiceBufReadIndex], //
|
||||
VOICE_BUF_SIZE //
|
||||
);
|
||||
VOCIE_BUF_ForwardReadIndex();
|
||||
VOICE_BUF_ForwardReadIndex();
|
||||
gVoiceBufLen--;
|
||||
}
|
||||
else
|
||||
@@ -141,7 +141,7 @@ void DMA1_Channel2_3_IRQHandler()
|
||||
if (gVoiceBufLen > 0)
|
||||
{
|
||||
memcpy(DAC_Buf, gVoiceBuf[gVoiceBufReadIndex], VOICE_BUF_SIZE);
|
||||
VOCIE_BUF_ForwardReadIndex();
|
||||
VOICE_BUF_ForwardReadIndex();
|
||||
gVoiceBufLen--;
|
||||
}
|
||||
else
|
||||
@@ -158,7 +158,7 @@ void DMA1_Channel2_3_IRQHandler()
|
||||
gVoiceBuf[gVoiceBufReadIndex], //
|
||||
VOICE_BUF_SIZE //
|
||||
);
|
||||
VOCIE_BUF_ForwardReadIndex();
|
||||
VOICE_BUF_ForwardReadIndex();
|
||||
gVoiceBufLen--;
|
||||
}
|
||||
else
|
||||
|
||||
+2
-2
@@ -26,12 +26,12 @@ extern uint8_t gVoiceBufReadIndex;
|
||||
extern uint8_t gVoiceBufWriteIndex;
|
||||
extern uint8_t gVoiceBufLen;
|
||||
|
||||
static inline void VOCIE_BUF_ForwardReadIndex()
|
||||
static inline void VOICE_BUF_ForwardReadIndex()
|
||||
{
|
||||
gVoiceBufReadIndex = (1 + gVoiceBufReadIndex) % VOICE_BUF_CAP;
|
||||
}
|
||||
|
||||
static inline void VOCIE_BUF_ForwardWriteIndex()
|
||||
static inline void VOICE_BUF_ForwardWriteIndex()
|
||||
{
|
||||
gVoiceBufWriteIndex = (1 + gVoiceBufWriteIndex) % VOICE_BUF_CAP;
|
||||
}
|
||||
|
||||
+2
-2
@@ -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'
|
||||
@@ -491,7 +491,7 @@ const uint8_t gFontSmall[95-1][6] =
|
||||
{0x03, 0x00, 0x03}, // 34 - quotedbl
|
||||
{0x1f, 0x0a, 0x1f}, // 35 - numbersign
|
||||
{0x0a, 0x1f, 0x05}, // 36 - dollar
|
||||
{0x09, 0x04, 0x12}, // 37 - percent
|
||||
{0x19, 0x04, 0x13}, // 37 - percent
|
||||
{0x0f, 0x17, 0x1c}, // 38 - ampersand
|
||||
{0x00, 0x03, 0x00}, // 39 - quotesingle
|
||||
{0x00, 0x0e, 0x11}, // 40 - parenleft
|
||||
|
||||
+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
|
||||
}
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#define _1GHz_in_KHz 100000000
|
||||
#define DEFAULT_FREQ 43450000 // Use for Reset and Aircopy
|
||||
|
||||
typedef struct {
|
||||
const uint32_t lower;
|
||||
|
||||
+2
-9
@@ -118,14 +118,7 @@ void FUNCTION_Foreground(const FUNCTION_Type_t PreviousFunction)
|
||||
|
||||
void FUNCTION_PowerSave() {
|
||||
#ifdef ENABLE_FEAT_F4HWN_SLEEP
|
||||
if(gWakeUp)
|
||||
{
|
||||
gPowerSave_10ms = gEeprom.BATTERY_SAVE * 200; // deep sleep now indexed on BatSav
|
||||
}
|
||||
else
|
||||
{
|
||||
gPowerSave_10ms = gEeprom.BATTERY_SAVE * 10;
|
||||
}
|
||||
gPowerSave_10ms = gEeprom.BATTERY_SAVE * (gWakeUp ? 200 : 10); // deep sleep now indexed on BatSav
|
||||
#else
|
||||
gPowerSave_10ms = gEeprom.BATTERY_SAVE * 10;
|
||||
#endif
|
||||
@@ -158,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
|
||||
|
||||
+4
-2
@@ -61,8 +61,10 @@ BOOT_Mode_t BOOT_GetMode(void)
|
||||
return BOOT_MODE_F_LOCK;
|
||||
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
if (Keys[0] == KEY_SIDE2)
|
||||
if (Keys[0] == KEY_SIDE2) {
|
||||
gAirCopyBootMode = true;
|
||||
return BOOT_MODE_AIRCOPY;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -95,7 +97,7 @@ void BOOT_ProcessMode(BOOT_Mode_t Mode)
|
||||
gEeprom.KEY_2_LONG_PRESS_ACTION = ACTION_OPT_NONE;
|
||||
gEeprom.KEY_M_LONG_PRESS_ACTION = ACTION_OPT_NONE;
|
||||
|
||||
RADIO_InitInfo(gRxVfo, FREQ_CHANNEL_LAST - 1, 43400000); // LPD
|
||||
RADIO_InitInfo(gRxVfo, FREQ_CHANNEL_LAST - 1, DEFAULT_FREQ); // LPD
|
||||
|
||||
gRxVfo->CHANNEL_BANDWIDTH = BANDWIDTH_NARROW;
|
||||
gRxVfo->OUTPUT_POWER = OUTPUT_POWER_LOW1;
|
||||
|
||||
+27
-77
@@ -157,18 +157,7 @@ void Main(void)
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
gEeprom.KEY_LOCK = 0;
|
||||
SETTINGS_SaveSettings();
|
||||
#ifndef ENABLE_VOX
|
||||
gMenuCursor = 67; // move to hidden section, fix me if change... !!! Remove VOX and Mic Bar
|
||||
#else
|
||||
gMenuCursor = 68; // move to hidden section, fix me if change... !!!
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NOAA
|
||||
gMenuCursor += 1; // move to hidden section, fix me if change... !!!
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
gMenuCursor += 1; // move to hidden section, fix me if change... !!!
|
||||
#endif
|
||||
gMenuCursor = UI_MENU_GetMenuIdx(FIRST_HIDDEN_MENU_ITEM);
|
||||
gSubMenuSelection = gSetting_F_LOCK;
|
||||
#endif
|
||||
}
|
||||
@@ -262,7 +251,7 @@ void Main(void)
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
{
|
||||
uint8_t Channel;
|
||||
uint16_t Channel;
|
||||
|
||||
AUDIO_SetVoiceID(0, VOICE_ID_WELCOME);
|
||||
|
||||
@@ -284,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) {
|
||||
|
||||
+318
-8
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "misc.h"
|
||||
#include "settings.h"
|
||||
#include "driver/py25q16.h"
|
||||
|
||||
const uint8_t fm_radio_countdown_500ms = 2000 / 500; // 2 seconds
|
||||
const uint16_t fm_play_countdown_scan_10ms = 100 / 10; // 100ms
|
||||
@@ -86,7 +87,7 @@ const uint16_t NOAA_countdown_3_10ms = 200 / 10; // 200ms
|
||||
|
||||
const uint32_t gDefaultAesKey[4] = {0x4AA5CC60, 0x0312CC5F, 0xFFD2DABB, 0x6BBA7F92};
|
||||
|
||||
const uint8_t gMicGain_dB2[5] = {3, 8, 16, 24, 31};
|
||||
const uint8_t gMicGain_dB2[9] = {3, 8, 16, 24, 32, 40, 48, 56, 63}; // BK4819 {3, 8, 16, 24, 31};
|
||||
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
bool gSetting_350TX;
|
||||
@@ -115,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;
|
||||
@@ -125,13 +130,20 @@ enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
|
||||
bool gSetting_set_lck = false;
|
||||
bool gSetting_set_met = 0;
|
||||
bool gSetting_set_gui = 0;
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
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
|
||||
uint8_t gDebug;
|
||||
int16_t gDebug;
|
||||
#endif
|
||||
uint8_t gDW = 0;
|
||||
uint8_t gCB = 0;
|
||||
@@ -139,10 +151,13 @@ enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
|
||||
uint8_t crc[15] = { 0 };
|
||||
uint8_t lErrorsDuringAirCopy = 0;
|
||||
uint8_t gAircopyStep = 0;
|
||||
uint8_t gAircopyCurrentMapIndex = 0;
|
||||
bool gAirCopyBootMode = 0;
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
bool gPowerHigh = false;
|
||||
bool gRemoveOffset = false;
|
||||
#endif
|
||||
int8_t dBmCorrTable[7] = {-15, -16, -10, -4, -7, -6, -1};
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_AUDIO_BAR
|
||||
@@ -163,8 +178,13 @@ uint16_t gEEPROM_RSSI_CALIB[7][4];
|
||||
uint16_t gEEPROM_1F8A;
|
||||
uint16_t gEEPROM_1F8C;
|
||||
|
||||
ChannelAttributes_t gMR_ChannelAttributes[FREQ_CHANNEL_LAST + 1];
|
||||
bool gMR_ChannelExclude[FREQ_CHANNEL_LAST + 1];
|
||||
//
|
||||
// Cache-Based Architecture: channel attributes moved to Flash
|
||||
// Cache holds only active channels in RAM (instead of all!)
|
||||
//
|
||||
|
||||
MR_ChannelCache_t gMR_ChannelAttributes_Cache[MR_CHANNELS_CACHE_SIZE] = {0};
|
||||
ChannelAttributes_t gMR_ChannelAttributes_Current = {0};
|
||||
|
||||
volatile uint16_t gBatterySaveCountdown_10ms = battery_save_count_10ms;
|
||||
|
||||
@@ -191,7 +211,8 @@ volatile bool gTxTimeoutReached;
|
||||
volatile uint16_t gRxTimerCountdown_500ms;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
volatile uint8_t gUART_LockScreenshot = 0; // lock screenshot if Chirp is used
|
||||
volatile uint8_t gUART_LockScreenshot = 0; // lock screenshot if Chirp is used
|
||||
bool gUSB_ScreenshotEnabled = false;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -229,12 +250,13 @@ volatile uint16_t gScanPauseDelayIn_10ms;
|
||||
uint16_t gMenuCountdown;
|
||||
bool gPttWasReleased;
|
||||
bool gPttWasPressed;
|
||||
bool gHasVfoBackup;
|
||||
uint8_t gKeypadLocked;
|
||||
bool gFlagReconfigureVfos;
|
||||
uint8_t gVfoConfigureMode;
|
||||
bool gFlagResetVfos;
|
||||
bool gRequestSaveVFO;
|
||||
uint8_t gRequestSaveChannel;
|
||||
uint16_t gRequestSaveChannel;
|
||||
bool gRequestSaveSettings;
|
||||
#ifdef ENABLE_FMRADIO
|
||||
bool gRequestSaveFM;
|
||||
@@ -262,7 +284,7 @@ bool g_SquelchLost;
|
||||
volatile uint16_t gFlashLightBlinkCounter;
|
||||
|
||||
bool gFlagEndTransmission;
|
||||
uint8_t gNextMrChannel;
|
||||
uint16_t gNextMrChannel;
|
||||
ReceptionMode_t gRxReceptionMode;
|
||||
|
||||
bool gRxVfoIsActive;
|
||||
@@ -280,7 +302,7 @@ uint8_t gFSKWriteIndex;
|
||||
|
||||
#ifdef ENABLE_NOAA
|
||||
bool gIsNoaaMode;
|
||||
uint8_t gNoaaChannel;
|
||||
uint8_t gNoaaChannel;
|
||||
#endif
|
||||
|
||||
bool gUpdateDisplay;
|
||||
@@ -316,8 +338,14 @@ 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;
|
||||
|
||||
uint16_t gVfoSaveCountdown_10ms = 0;
|
||||
bool gScheduleVfoSave = false;
|
||||
bool gVfoStateChanged = false;
|
||||
char gListName[MR_CHANNELS_LIST][4];
|
||||
#endif
|
||||
|
||||
inline void FUNCTION_NOP() { ; }
|
||||
@@ -347,3 +375,285 @@ unsigned long StrToUL(const char * str)
|
||||
}
|
||||
return ul;
|
||||
}
|
||||
|
||||
//
|
||||
// Cache-Based Channel Attributes Implementation
|
||||
//
|
||||
//
|
||||
// This replaces the huge array (~ 2,000 bytes in RAM) with a smart cache system
|
||||
// that keeps only active channels in RAM and loads others from Flash on demand.
|
||||
//
|
||||
// SRAM Savings: ~ 2,000 bytes (84% reduction!)
|
||||
//
|
||||
|
||||
// Flash address where channel attributes start
|
||||
// NOTE: Verify this matches your Flash layout!
|
||||
|
||||
#define FLASH_CHANNEL_ATTR_BASE 0x8000
|
||||
|
||||
// Each channel takes 2 bytes (ChannelAttributes_t is uint16_t)
|
||||
#define FLASH_CHANNEL_ATTR_SIZE 2
|
||||
|
||||
// Cache hit/miss statistics (optional, for debugging)
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_DEBUG
|
||||
static uint32_t cache_hits = 0;
|
||||
static uint32_t cache_misses = 0;
|
||||
#endif
|
||||
|
||||
//
|
||||
// Internal Helper Functions
|
||||
//
|
||||
|
||||
// Find cache entry for given channel
|
||||
// Returns index if found, -1 if not found
|
||||
|
||||
static int MR_FindInCache(uint16_t channel_id)
|
||||
{
|
||||
for (int i = 0; i < MR_CHANNELS_CACHE_SIZE; i++) {
|
||||
if (gMR_ChannelAttributes_Cache[i].channel_id == channel_id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Find oldest entry in cache (for eviction - LRU)
|
||||
static int MR_FindOldestCacheEntry(void)
|
||||
{
|
||||
int oldest_index = 0;
|
||||
uint32_t oldest_time = gMR_ChannelAttributes_Cache[0].access_time;
|
||||
|
||||
for (int i = 1; i < MR_CHANNELS_CACHE_SIZE; i++) {
|
||||
if (gMR_ChannelAttributes_Cache[i].access_time < oldest_time) {
|
||||
oldest_time = gMR_ChannelAttributes_Cache[i].access_time;
|
||||
oldest_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
return oldest_index;
|
||||
}
|
||||
|
||||
// Find empty cache slot
|
||||
// Returns index if found, -1 if cache is full
|
||||
static int MR_FindEmptyCacheSlot(void)
|
||||
{
|
||||
for (int i = 0; i < MR_CHANNELS_CACHE_SIZE; i++) {
|
||||
if (gMR_ChannelAttributes_Cache[i].channel_id == 0xFFFF) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1; // Cache is full, need to evict
|
||||
}
|
||||
|
||||
// Get current time (for LRU eviction)
|
||||
static uint32_t GetCurrentTime(void)
|
||||
{
|
||||
// Using gBlinkCounter which increments continuously
|
||||
extern uint32_t gBlinkCounter;
|
||||
return gBlinkCounter;
|
||||
}
|
||||
|
||||
//
|
||||
// Public API Functions
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
// Load channel attributes from Flash
|
||||
void MR_LoadChannelAttributesFromFlash(uint16_t channel_id, ChannelAttributes_t* attributes)
|
||||
{
|
||||
// CRITICAL: Validate channel_id
|
||||
if (channel_id >= (MR_CHANNELS_MAX + 7)) {
|
||||
attributes->__val = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate Flash address
|
||||
uint32_t flash_addr = FLASH_CHANNEL_ATTR_BASE + (channel_id * sizeof(ChannelAttributes_t));
|
||||
|
||||
// Read 2 bytes from Flash
|
||||
PY25Q16_ReadBuffer(flash_addr, attributes, sizeof(ChannelAttributes_t));
|
||||
}
|
||||
|
||||
// Save channel attributes to Flash
|
||||
void MR_SaveChannelAttributesToFlash(uint16_t channel_id, const ChannelAttributes_t* attributes)
|
||||
{
|
||||
// CRITICAL: Validate channel_id
|
||||
if (channel_id >= (MR_CHANNELS_MAX + 7)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate Flash address
|
||||
uint16_t flash_addr = FLASH_CHANNEL_ATTR_BASE + (channel_id * FLASH_CHANNEL_ATTR_SIZE);
|
||||
|
||||
// Write 2 bytes to Flash
|
||||
PY25Q16_WriteBuffer(flash_addr, attributes, sizeof(ChannelAttributes_t), false);
|
||||
}
|
||||
|
||||
// Get channel attributes (from cache or Flash)
|
||||
// This is the main function used by the rest of the code
|
||||
ChannelAttributes_t* MR_GetChannelAttributes(uint16_t channel_id)
|
||||
{
|
||||
// Input validation
|
||||
if (channel_id >= (MR_CHANNELS_MAX + 7)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Check cache first (FAST PATH)
|
||||
int cache_index = MR_FindInCache(channel_id);
|
||||
|
||||
if (cache_index >= 0) {
|
||||
// CACHE HIT
|
||||
#ifdef ENABLE_FEAT_F4HWN_DEBUG
|
||||
cache_hits++;
|
||||
#endif
|
||||
|
||||
// Update access time for LRU
|
||||
gMR_ChannelAttributes_Cache[cache_index].access_time = GetCurrentTime();
|
||||
|
||||
return &gMR_ChannelAttributes_Cache[cache_index].attributes;
|
||||
}
|
||||
|
||||
// CACHE MISS - Load from Flash
|
||||
#ifdef ENABLE_FEAT_F4HWN_DEBUG
|
||||
cache_misses++;
|
||||
#endif
|
||||
|
||||
// Find slot for new entry
|
||||
int slot = MR_FindEmptyCacheSlot();
|
||||
|
||||
if (slot < 0) {
|
||||
// Cache is full, evict oldest entry
|
||||
slot = MR_FindOldestCacheEntry();
|
||||
}
|
||||
|
||||
// Load from Flash into cache slot
|
||||
MR_LoadChannelAttributesFromFlash(channel_id, &gMR_ChannelAttributes_Cache[slot].attributes);
|
||||
|
||||
// Store channel_id in cache
|
||||
gMR_ChannelAttributes_Cache[slot].channel_id = channel_id;
|
||||
|
||||
// Set access time
|
||||
gMR_ChannelAttributes_Cache[slot].access_time = GetCurrentTime();
|
||||
|
||||
return &gMR_ChannelAttributes_Cache[slot].attributes;
|
||||
}
|
||||
|
||||
// Set channel attributes (updates both cache and Flash)
|
||||
void MR_SetChannelAttributes(uint16_t channel_id, const ChannelAttributes_t* attributes)
|
||||
{
|
||||
// Input validation
|
||||
if (channel_id >= (MR_CHANNELS_MAX + 7) || !attributes) {
|
||||
return;
|
||||
}
|
||||
|
||||
// CRITICAL FIX: WRITE-PROTECT - Prevent Flash wear
|
||||
// Before writing, check if data already exists and is identical
|
||||
ChannelAttributes_t flash_version;
|
||||
MR_LoadChannelAttributesFromFlash(channel_id, &flash_version);
|
||||
|
||||
// Compare current Flash data with new data
|
||||
if (memcmp(&flash_version, attributes, sizeof(ChannelAttributes_t)) == 0) {
|
||||
// But still update cache for consistency
|
||||
int cache_index = MR_FindInCache(channel_id);
|
||||
if (cache_index >= 0) {
|
||||
gMR_ChannelAttributes_Cache[cache_index].attributes = *attributes;
|
||||
gMR_ChannelAttributes_Cache[cache_index].access_time = GetCurrentTime();
|
||||
}
|
||||
return; // Early exit - no Flash write needed
|
||||
}
|
||||
|
||||
// Data has changed - write to Flash
|
||||
MR_SaveChannelAttributesToFlash(channel_id, attributes);
|
||||
|
||||
// Update cache if entry exists
|
||||
int cache_index = MR_FindInCache(channel_id);
|
||||
|
||||
if (cache_index >= 0) {
|
||||
// Entry in cache, update it
|
||||
gMR_ChannelAttributes_Cache[cache_index].attributes = *attributes;
|
||||
gMR_ChannelAttributes_Cache[cache_index].access_time = GetCurrentTime();
|
||||
} else {
|
||||
// Not in cache, add it
|
||||
int slot = MR_FindEmptyCacheSlot();
|
||||
|
||||
if (slot < 0) {
|
||||
// Cache full, evict oldest
|
||||
slot = MR_FindOldestCacheEntry();
|
||||
}
|
||||
|
||||
gMR_ChannelAttributes_Cache[slot].channel_id = channel_id;
|
||||
gMR_ChannelAttributes_Cache[slot].attributes = *attributes;
|
||||
gMR_ChannelAttributes_Cache[slot].access_time = GetCurrentTime();
|
||||
}
|
||||
}
|
||||
|
||||
// Invalidate entire cache (call after loading Flash backup)
|
||||
void MR_InvalidateChannelAttributesCache(void)
|
||||
{
|
||||
for (int i = 0; i < MR_CHANNELS_CACHE_SIZE; i++) {
|
||||
gMR_ChannelAttributes_Cache[i].channel_id = 0xFFFF; // Mark as empty
|
||||
gMR_ChannelAttributes_Cache[i].access_time = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize cache (call from settings.c boot sequence)
|
||||
void MR_InitChannelAttributesCache(void)
|
||||
{
|
||||
// Clear cache
|
||||
MR_InvalidateChannelAttributesCache();
|
||||
|
||||
// Pre-load commonly used channels (VFO A, VFO B, channel 0)
|
||||
// This speeds up first access
|
||||
uint16_t channels_to_preload[] = {0, 1, 2};
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(channels_to_preload); i++) {
|
||||
if (channels_to_preload[i] < (MR_CHANNELS_MAX + 7)) {
|
||||
MR_GetChannelAttributes(channels_to_preload[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Debugging / Statistics (optional, for development)
|
||||
//
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_DEBUG
|
||||
|
||||
uint32_t MR_GetCacheHits(void)
|
||||
{
|
||||
return cache_hits;
|
||||
}
|
||||
|
||||
uint32_t MR_GetCacheMisses(void)
|
||||
{
|
||||
return cache_misses;
|
||||
}
|
||||
|
||||
float MR_GetCacheHitRate(void)
|
||||
{
|
||||
uint32_t total = cache_hits + cache_misses;
|
||||
if (total == 0) return 0.0f;
|
||||
return (float)cache_hits / (float)total * 100.0f;
|
||||
}
|
||||
|
||||
void MR_PrintCacheStats(void)
|
||||
{
|
||||
// This would print cache statistics (requires UART_Printf)
|
||||
// Uncomment if you want debug output:
|
||||
// UART_Printf("Cache Stats: Hits=%u Misses=%u Rate=%.1f%%\n",
|
||||
// cache_hits, cache_misses, MR_GetCacheHitRate());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
bool SCREENSHOT_IsLocked(void)
|
||||
{
|
||||
if (gUART_LockScreenshot > 0) {
|
||||
gUART_LockScreenshot--;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
+109
-18
@@ -36,18 +36,26 @@
|
||||
#define SWAP(a, b) ({ __typeof__ (a) _c = (a); a = b; b = _c; })
|
||||
#endif
|
||||
|
||||
#define IS_MR_CHANNEL(x) ((x) <= MR_CHANNEL_LAST)
|
||||
#define FM_CHANNELS_MAX 48
|
||||
#define MR_CHANNELS_MAX 1024
|
||||
#define MR_CHANNELS_LIST 24
|
||||
// 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
|
||||
|
||||
|
||||
#define IS_MR_CHANNEL(x) ((x) >= MR_CHANNEL_FIRST && (x) <= MR_CHANNEL_LAST)
|
||||
#define IS_FREQ_CHANNEL(x) ((x) >= FREQ_CHANNEL_FIRST && (x) <= FREQ_CHANNEL_LAST)
|
||||
#define IS_VALID_CHANNEL(x) ((x) < LAST_CHANNEL)
|
||||
#define IS_NOAA_CHANNEL(x) ((x) >= NOAA_CHANNEL_FIRST && (x) <= NOAA_CHANNEL_LAST)
|
||||
|
||||
enum {
|
||||
MR_CHANNEL_FIRST = 0,
|
||||
MR_CHANNEL_LAST = 199u,
|
||||
FREQ_CHANNEL_FIRST = 200u,
|
||||
FREQ_CHANNEL_LAST = 206u,
|
||||
NOAA_CHANNEL_FIRST = 207u,
|
||||
NOAA_CHANNEL_LAST = 216u,
|
||||
MR_CHANNEL_LAST = MR_CHANNELS_MAX - 1,
|
||||
FREQ_CHANNEL_FIRST = MR_CHANNELS_MAX,
|
||||
FREQ_CHANNEL_LAST = MR_CHANNELS_MAX + 6,
|
||||
NOAA_CHANNEL_FIRST = MR_CHANNELS_MAX + 7,
|
||||
NOAA_CHANNEL_LAST = MR_CHANNELS_MAX + 16,
|
||||
LAST_CHANNEL
|
||||
};
|
||||
|
||||
@@ -138,7 +146,7 @@ extern const uint16_t scan_pause_delay_in_7_10ms;
|
||||
//extern const uint16_t gMax_bat_v;
|
||||
//extern const uint16_t gMin_bat_v;
|
||||
|
||||
extern const uint8_t gMicGain_dB2[5];
|
||||
extern const uint8_t gMicGain_dB2[9];
|
||||
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
extern bool gSetting_350TX;
|
||||
@@ -168,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;
|
||||
@@ -178,13 +200,20 @@ extern enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
|
||||
extern bool gSetting_set_lck;
|
||||
extern bool gSetting_set_met;
|
||||
extern bool gSetting_set_gui;
|
||||
#ifdef ENABLE_FEAT_F4HWN_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
|
||||
extern uint8_t gDebug;
|
||||
extern int16_t gDebug;
|
||||
#endif
|
||||
extern uint8_t gDW;
|
||||
extern uint8_t gCB;
|
||||
@@ -192,10 +221,13 @@ extern enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
|
||||
extern uint8_t crc[15];
|
||||
extern uint8_t lErrorsDuringAirCopy;
|
||||
extern uint8_t gAircopyStep;
|
||||
extern uint8_t gAircopyCurrentMapIndex;
|
||||
extern bool gAirCopyBootMode;
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
extern bool gPowerHigh;
|
||||
extern bool gRemoveOffset;
|
||||
#endif
|
||||
extern int8_t dBmCorrTable[7];
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_AUDIO_BAR
|
||||
@@ -219,18 +251,66 @@ extern uint16_t gEEPROM_1F8C;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t
|
||||
band : 3,
|
||||
uint16_t
|
||||
band : 3,
|
||||
compander : 2,
|
||||
scanlist1 : 1,
|
||||
scanlist2 : 1,
|
||||
scanlist3 : 1;
|
||||
unused_1 : 1,
|
||||
unused_2 : 1,
|
||||
exclude : 1,
|
||||
scanlist : 8;
|
||||
};
|
||||
uint8_t __val;
|
||||
uint16_t __val;
|
||||
} ChannelAttributes_t;
|
||||
|
||||
extern ChannelAttributes_t gMR_ChannelAttributes[207];
|
||||
extern bool gMR_ChannelExclude[207];
|
||||
//
|
||||
// Cache-Based Architecture
|
||||
//
|
||||
//
|
||||
// Instead of keeping all 1038 channel attributes in RAM (~ 2,000 bytes),
|
||||
// we now keep only the active ones in a small cache (40 bytes).
|
||||
//
|
||||
// The full array remains in Flash and is loaded on-demand.
|
||||
//
|
||||
// SRAM Savings: ~ 2,000 bytes (84% reduction!)
|
||||
//
|
||||
|
||||
// Cache entry structure
|
||||
typedef struct {
|
||||
uint16_t channel_id; // Which channel this is
|
||||
ChannelAttributes_t attributes; // The actual attributes
|
||||
uint32_t access_time; // For LRU eviction (optional)
|
||||
} MR_ChannelCache_t;
|
||||
|
||||
// The cache (small, stays in RAM)
|
||||
extern MR_ChannelCache_t gMR_ChannelAttributes_Cache[MR_CHANNELS_CACHE_SIZE];
|
||||
|
||||
// REMOVED: extern ChannelAttributes_t gMR_ChannelAttributes[MR_CHANNELS_MAX + 7];
|
||||
// This now stays in Flash, not in RAM
|
||||
|
||||
//
|
||||
// Cache Access Functions (See misc.c for implementation)
|
||||
//
|
||||
|
||||
// Get channel attributes (from cache or Flash)
|
||||
// Returns pointer to attributes, loads from Flash if not in cache
|
||||
|
||||
void MR_InitChannelAttributesCache(void);
|
||||
|
||||
ChannelAttributes_t* MR_GetChannelAttributes(uint16_t channel_id);
|
||||
|
||||
// Set channel attributes (updates both cache and Flasf)
|
||||
void MR_SetChannelAttributes(uint16_t channel_id, const ChannelAttributes_t* attributes);
|
||||
|
||||
// Invalidate cache (on Flash clear)
|
||||
void MR_InvalidateChannelAttributesCache(void);
|
||||
|
||||
// Load channel attributes from Flash directly (internal use)
|
||||
void MR_LoadChannelAttributesFromFlash(uint16_t channel_id, ChannelAttributes_t* attributes);
|
||||
|
||||
// Save channel attributes to Flash directly (internal use)
|
||||
void MR_SaveChannelAttributesToFlash(uint16_t channel_id, const ChannelAttributes_t* attributes);
|
||||
|
||||
extern ChannelAttributes_t gMR_ChannelAttributes_Current; // Current VFO attributes (for speed)
|
||||
|
||||
extern volatile uint16_t gBatterySaveCountdown_10ms;
|
||||
|
||||
@@ -258,6 +338,9 @@ extern volatile bool gTxTimeoutReached;
|
||||
#endif
|
||||
#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
|
||||
|
||||
@@ -301,11 +384,12 @@ 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;
|
||||
extern bool gRequestSaveVFO;
|
||||
extern uint8_t gRequestSaveChannel;
|
||||
extern uint16_t gRequestSaveChannel;
|
||||
extern bool gRequestSaveSettings;
|
||||
#ifdef ENABLE_FMRADIO
|
||||
extern bool gRequestSaveFM;
|
||||
@@ -336,7 +420,7 @@ extern bool g_SquelchLost;
|
||||
extern volatile uint16_t gFlashLightBlinkCounter;
|
||||
|
||||
extern bool gFlagEndTransmission;
|
||||
extern uint8_t gNextMrChannel;
|
||||
extern uint16_t gNextMrChannel;
|
||||
extern ReceptionMode_t gRxReceptionMode;
|
||||
|
||||
//TRUE when dual watch is momentarly suspended and RX_VFO is locked to either last TX or RX
|
||||
@@ -385,8 +469,15 @@ 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;
|
||||
|
||||
extern uint16_t gVfoSaveCountdown_10ms;
|
||||
extern bool gScheduleVfoSave;
|
||||
extern bool gVfoStateChanged;
|
||||
|
||||
extern char gListName[MR_CHANNELS_LIST][4];
|
||||
#endif
|
||||
|
||||
int32_t NUMBER_AddWithWraparound(int32_t Base, int32_t Add, int32_t LowerLimit, int32_t UpperLimit);
|
||||
|
||||
+327
-203
@@ -53,68 +53,147 @@ const char gModulationStr[MODULATION_UKNOWN][4] = {
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
|
||||
// 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)
|
||||
{
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x31A9);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool RADIO_CheckValidList(uint8_t scanList)
|
||||
{
|
||||
if(scanList == MR_CHANNELS_LIST + 1)
|
||||
return true;
|
||||
|
||||
for (uint16_t i = 0; IS_MR_CHANNEL(i); i++) {
|
||||
const ChannelAttributes_t* att = MR_GetChannelAttributes(i);
|
||||
if(att->scanlist == scanList && att->exclude == false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void RADIO_NextValidList(int8_t direction)
|
||||
{
|
||||
uint8_t startList = gEeprom.SCAN_LIST_DEFAULT;
|
||||
uint8_t attempts = 0;
|
||||
const uint8_t MAX_VALUE = MR_CHANNELS_LIST + 1; // 25 (1-24 lists + ALL)
|
||||
|
||||
do {
|
||||
if (direction > 0) {
|
||||
// Forward: 1 → 2 → ... → 25 → 1
|
||||
gEeprom.SCAN_LIST_DEFAULT = (gEeprom.SCAN_LIST_DEFAULT % MAX_VALUE) + 1;
|
||||
} else {
|
||||
// Backward: 25 → 24 → ... → 1 → 25
|
||||
gEeprom.SCAN_LIST_DEFAULT = ((gEeprom.SCAN_LIST_DEFAULT - 2 + MAX_VALUE) % MAX_VALUE) + 1;
|
||||
}
|
||||
attempts++;
|
||||
|
||||
if (RADIO_CheckValidList(gEeprom.SCAN_LIST_DEFAULT))
|
||||
return;
|
||||
|
||||
} while (gEeprom.SCAN_LIST_DEFAULT != startList && attempts < MAX_VALUE);
|
||||
|
||||
// Safety fallback: switch to ALL mode
|
||||
if (!RADIO_CheckValidList(gEeprom.SCAN_LIST_DEFAULT)) {
|
||||
gEeprom.SCAN_LIST_DEFAULT = MAX_VALUE; // ALL (25)
|
||||
}
|
||||
}
|
||||
|
||||
bool RADIO_CheckValidChannel(uint16_t channel, bool checkScanList, uint8_t scanList)
|
||||
{
|
||||
const ChannelAttributes_t* att = MR_GetChannelAttributes(channel);
|
||||
|
||||
// return true if the channel appears valid
|
||||
if (!IS_MR_CHANNEL(channel))
|
||||
return false;
|
||||
|
||||
const ChannelAttributes_t att = gMR_ChannelAttributes[channel];
|
||||
|
||||
if (checkScanList && gMR_ChannelExclude[channel] == true)
|
||||
if (checkScanList && att->exclude == true)
|
||||
return false;
|
||||
|
||||
if (att.band > BAND7_470MHz)
|
||||
if (att->band > BAND7_470MHz)
|
||||
return false;
|
||||
|
||||
if (!checkScanList || scanList > 4)
|
||||
if (!checkScanList || (scanList > MR_CHANNELS_LIST && att->scanlist != 0) || (scanList > 0 && att->scanlist == MR_CHANNELS_LIST + 1))
|
||||
return true;
|
||||
|
||||
/*
|
||||
if(scanList == 0 && (att.scanlist1 == 1 || att.scanlist2 == 1 || att.scanlist3 == 1))
|
||||
{
|
||||
if ((scanList == 0) || (scanList != att->scanlist)) {
|
||||
return false;
|
||||
}
|
||||
else if(scanList == 1 && att.scanlist1 != 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if(scanList == 2 && att.scanlist2 != 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if(scanList == 3 && att.scanlist3 != 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if(scanList == 4 && (att.scanlist1 == 0 && att.scanlist2 == 0 && att.scanlist3 == 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
if ((scanList == 0 && (att.scanlist1 == 1 || att.scanlist2 == 1 || att.scanlist3 == 1)) ||
|
||||
(scanList == 1 && att.scanlist1 != 1) ||
|
||||
(scanList == 2 && att.scanlist2 != 1) ||
|
||||
(scanList == 3 && att.scanlist3 != 1) ||
|
||||
(scanList == 4 && (att.scanlist1 == 0 && att.scanlist2 == 0 && att.scanlist3 == 0))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//return true;
|
||||
|
||||
// I don't understand what this code is for...
|
||||
|
||||
const uint8_t PriorityCh1 = gEeprom.SCANLIST_PRIORITY_CH1[scanList - 1];
|
||||
const uint8_t PriorityCh2 = gEeprom.SCANLIST_PRIORITY_CH2[scanList - 1];
|
||||
|
||||
return PriorityCh1 != channel && PriorityCh2 != channel;
|
||||
// Exclude priority channels ONLY if SCAN_LIST_ENABLED is active
|
||||
// Otherwise, treat them as normal channels in the list
|
||||
if (gEeprom.SCAN_LIST_ENABLED)
|
||||
{
|
||||
const uint16_t PriorityCh1 = gEeprom.SCANLIST_PRIORITY_CH[0];
|
||||
const uint16_t PriorityCh2 = gEeprom.SCANLIST_PRIORITY_CH[1];
|
||||
if (PriorityCh1 == channel || PriorityCh2 == channel)
|
||||
return false; // Excluded because it's a priority channel and they are enabled
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t RADIO_FindNextChannel(uint8_t Channel, int8_t Direction, bool bCheckScanList, uint8_t VFO)
|
||||
uint16_t RADIO_FindNextChannel(uint16_t Channel, int8_t Direction, bool bCheckScanList, uint8_t VFO)
|
||||
{
|
||||
for (unsigned int i = 0; IS_MR_CHANNEL(i); i++, Channel += Direction) {
|
||||
if (Channel == 0xFF) {
|
||||
for (uint16_t i = 0; IS_MR_CHANNEL(i); i++, Channel += Direction) {
|
||||
if (Channel == 0xFFFF) {
|
||||
Channel = MR_CHANNEL_LAST;
|
||||
} else if (!IS_MR_CHANNEL(Channel)) {
|
||||
Channel = MR_CHANNEL_FIRST;
|
||||
@@ -125,17 +204,15 @@ uint8_t RADIO_FindNextChannel(uint8_t Channel, int8_t Direction, bool bCheckScan
|
||||
}
|
||||
}
|
||||
|
||||
return 0xFF;
|
||||
return 0xFFFF;
|
||||
}
|
||||
|
||||
void RADIO_InitInfo(VFO_Info_t *pInfo, const uint8_t ChannelSave, const uint32_t Frequency)
|
||||
void RADIO_InitInfo(VFO_Info_t *pInfo, const uint16_t ChannelSave, const uint32_t Frequency)
|
||||
{
|
||||
memset(pInfo, 0, sizeof(*pInfo));
|
||||
|
||||
pInfo->Band = FREQUENCY_GetBand(Frequency);
|
||||
pInfo->SCANLIST1_PARTICIPATION = false;
|
||||
pInfo->SCANLIST2_PARTICIPATION = false;
|
||||
pInfo->SCANLIST3_PARTICIPATION = false;
|
||||
pInfo->SCANLIST_PARTICIPATION = 0;
|
||||
pInfo->STEP_SETTING = STEP_12_5kHz;
|
||||
pInfo->StepFrequency = gStepFrequencyTable[pInfo->STEP_SETTING];
|
||||
pInfo->CHANNEL_SAVE = ChannelSave;
|
||||
@@ -156,6 +233,24 @@ void RADIO_InitInfo(VFO_Info_t *pInfo, const uint8_t ChannelSave, const uint32_t
|
||||
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];
|
||||
@@ -168,7 +263,7 @@ void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure
|
||||
gEeprom.ScreenChannel[VFO] = FREQ_CHANNEL_FIRST + BAND6_400MHz;
|
||||
}
|
||||
|
||||
uint8_t channel = gEeprom.ScreenChannel[VFO];
|
||||
uint16_t channel = gEeprom.ScreenChannel[VFO];
|
||||
|
||||
if (IS_VALID_CHANNEL(channel)) {
|
||||
#ifdef ENABLE_NOAA
|
||||
@@ -188,7 +283,7 @@ void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure
|
||||
|
||||
if (IS_MR_CHANNEL(channel)) {
|
||||
channel = RADIO_FindNextChannel(channel, RADIO_CHANNEL_UP, false, VFO);
|
||||
if (channel == 0xFF) {
|
||||
if (channel == 0xFFFF) {
|
||||
channel = gEeprom.FreqChannel[VFO];
|
||||
gEeprom.ScreenChannel[VFO] = gEeprom.FreqChannel[VFO];
|
||||
}
|
||||
@@ -201,50 +296,42 @@ void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure
|
||||
else
|
||||
channel = FREQ_CHANNEL_LAST - 1;
|
||||
|
||||
ChannelAttributes_t att = gMR_ChannelAttributes[channel];
|
||||
if (att.__val == 0xFF) { // invalid/unused channel
|
||||
ChannelAttributes_t* att = MR_GetChannelAttributes(channel);
|
||||
if (att->__val == 0xFFFF) { // invalid/unused channel
|
||||
if (IS_MR_CHANNEL(channel)) {
|
||||
channel = gEeprom.FreqChannel[VFO];
|
||||
gEeprom.ScreenChannel[VFO] = channel;
|
||||
}
|
||||
|
||||
uint8_t bandIdx = channel - FREQ_CHANNEL_FIRST;
|
||||
uint16_t bandIdx = channel - FREQ_CHANNEL_FIRST;
|
||||
RADIO_InitInfo(pVfo, channel, frequencyBandTable[bandIdx].lower);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t band = att.band;
|
||||
uint8_t band = att->band;
|
||||
if (band > BAND7_470MHz) {
|
||||
band = BAND6_400MHz;
|
||||
}
|
||||
|
||||
bool bParticipation1;
|
||||
bool bParticipation2;
|
||||
bool bParticipation3;
|
||||
uint8_t bParticipation;
|
||||
|
||||
if (IS_MR_CHANNEL(channel)) {
|
||||
bParticipation1 = att.scanlist1;
|
||||
bParticipation2 = att.scanlist2;
|
||||
bParticipation3 = att.scanlist3;
|
||||
bParticipation = att->scanlist;
|
||||
}
|
||||
else {
|
||||
band = channel - FREQ_CHANNEL_FIRST;
|
||||
bParticipation1 = true;
|
||||
bParticipation2 = true;
|
||||
bParticipation3 = true;
|
||||
bParticipation = MR_CHANNELS_LIST + 1;
|
||||
}
|
||||
|
||||
pVfo->Band = band;
|
||||
pVfo->SCANLIST1_PARTICIPATION = bParticipation1;
|
||||
pVfo->SCANLIST2_PARTICIPATION = bParticipation2;
|
||||
pVfo->SCANLIST3_PARTICIPATION = bParticipation3;
|
||||
pVfo->SCANLIST_PARTICIPATION = bParticipation;
|
||||
pVfo->CHANNEL_SAVE = channel;
|
||||
|
||||
uint32_t base;
|
||||
if (IS_MR_CHANNEL(channel))
|
||||
base = channel * 16;
|
||||
else
|
||||
base = 0x001000 + ((channel - FREQ_CHANNEL_FIRST) * 32) + (VFO * 16);
|
||||
base = 0x009000 + ((channel - FREQ_CHANNEL_FIRST) * 32) + (VFO * 16);
|
||||
|
||||
if (configure == VFO_CONFIGURE_RELOAD || IS_FREQ_CHANNEL(channel))
|
||||
{
|
||||
@@ -282,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)
|
||||
{
|
||||
@@ -424,7 +470,7 @@ void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure
|
||||
pConfig->Frequency = 43300000;
|
||||
}
|
||||
|
||||
pVfo->Compander = att.compander;
|
||||
pVfo->Compander = att->compander;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
if(gRemoveOffset)
|
||||
@@ -513,7 +559,7 @@ void RADIO_ConfigureSquelchAndOutputPower(VFO_Info_t *pInfo)
|
||||
|
||||
Band = FREQUENCY_GetBand(pInfo->pTX->Frequency);
|
||||
|
||||
// my eeprom calibration data
|
||||
// my eeprom calibration data on UV-K5 (V1)
|
||||
//
|
||||
// 1ED0 32 32 32 64 64 64 8c 8c 8c ff ff ff ff ff ff ff 50 MHz
|
||||
// 1EE0 32 32 32 64 64 64 8c 8c 8c ff ff ff ff ff ff ff 108 MHz
|
||||
@@ -523,34 +569,28 @@ void RADIO_ConfigureSquelchAndOutputPower(VFO_Info_t *pInfo)
|
||||
// 1F20 5f 5f 5f 69 69 69 87 87 87 ff ff ff ff ff ff ff 400 MHz
|
||||
// 1F30 32 32 32 64 64 64 8c 8c 8c ff ff ff ff ff ff ff 470 MHz
|
||||
|
||||
// my eeprom calibration data on UV-K1
|
||||
// 32 32 32 64 64 64 8c 8c 8c ff ff ff ff ff ff ff 50 MHz
|
||||
// 32 32 32 64 64 64 8c 8c 8c ff ff ff ff ff ff ff 108 MHz
|
||||
// 4b 4b 4b 78 78 78 96 96 96 ff ff ff ff ff ff ff 137 MHz
|
||||
// 32 32 32 64 64 64 8c 8c 8c ff ff ff ff ff ff ff 174 MHz
|
||||
// 5a 5a 5a 64 64 64 a0 a0 a0 ff ff ff ff ff ff ff 350 MHz
|
||||
// 4b 4b 4b 78 78 78 96 96 96 ff ff ff ff ff ff ff 400 MHz
|
||||
// 32 32 32 64 64 64 94 8c 8c ff ff ff ff ff ff ff 470 MHz
|
||||
|
||||
uint8_t Txp[3];
|
||||
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);
|
||||
|
||||
@@ -602,7 +642,17 @@ void RADIO_ConfigureSquelchAndOutputPower(VFO_Info_t *pInfo)
|
||||
}
|
||||
*/
|
||||
|
||||
static const uint8_t dividers[6] = { 25, 19, 13, 10, 7, 4};
|
||||
//static const uint8_t dividers[6] = { 25, 19, 13, 10, 7, 4}; // For UV-K5 V1
|
||||
|
||||
static const uint8_t dividers_band2[6] = { 20, 15, 10, 8, 6, 4 };
|
||||
static const uint8_t dividers_band5[6] = { 25, 19, 13, 9, 6, 4 }; // Need to improve measure...
|
||||
|
||||
const uint8_t *dividers;
|
||||
|
||||
if (Band == 2) // VHF
|
||||
dividers = dividers_band2;
|
||||
else // UHF
|
||||
dividers = dividers_band5;
|
||||
|
||||
for (uint8_t p = 0; p < 3; p++)
|
||||
{
|
||||
@@ -612,7 +662,8 @@ void RADIO_ConfigureSquelchAndOutputPower(VFO_Info_t *pInfo)
|
||||
}
|
||||
else // case 6
|
||||
{
|
||||
Txp[p] += 30;
|
||||
// Txp[p] += 30; // For UV-K5 V1
|
||||
Txp[p] += 24;
|
||||
}
|
||||
}
|
||||
#else
|
||||
@@ -682,6 +733,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;
|
||||
@@ -699,21 +756,26 @@ void RADIO_SetupRegisters(bool switchToForeground)
|
||||
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false);
|
||||
|
||||
switch (Bandwidth)
|
||||
if (gRxVfo->Modulation == MODULATION_AM)
|
||||
BK4819_SetFilterBandwidth(RADIO_GetAMFilterBandwidth(gRxVfo), true);
|
||||
else
|
||||
{
|
||||
default:
|
||||
Bandwidth = BK4819_FILTER_BW_WIDE;
|
||||
[[fallthrough]];
|
||||
case BK4819_FILTER_BW_WIDE:
|
||||
case BK4819_FILTER_BW_NARROW:
|
||||
case BK4819_FILTER_BW_NARROWER:
|
||||
#ifdef ENABLE_AM_FIX
|
||||
// BK4819_SetFilterBandwidth(Bandwidth, gRxVfo->Modulation == MODULATION_AM && gSetting_AM_fix);
|
||||
BK4819_SetFilterBandwidth(Bandwidth, true);
|
||||
#else
|
||||
BK4819_SetFilterBandwidth(Bandwidth, false);
|
||||
#endif
|
||||
break;
|
||||
switch (Bandwidth)
|
||||
{
|
||||
default:
|
||||
Bandwidth = BK4819_FILTER_BW_WIDE;
|
||||
[[fallthrough]];
|
||||
case BK4819_FILTER_BW_WIDE:
|
||||
case BK4819_FILTER_BW_NARROW:
|
||||
case BK4819_FILTER_BW_NARROWER:
|
||||
#ifdef ENABLE_AM_FIX
|
||||
// BK4819_SetFilterBandwidth(Bandwidth, gRxVfo->Modulation == MODULATION_AM && gSetting_AM_fix);
|
||||
BK4819_SetFilterBandwidth(Bandwidth, true);
|
||||
#else
|
||||
BK4819_SetFilterBandwidth(Bandwidth, false);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO5_PIN1_RED, false);
|
||||
@@ -733,8 +795,8 @@ void RADIO_SetupRegisters(bool switchToForeground)
|
||||
}
|
||||
BK4819_WriteRegister(BK4819_REG_3F, 0);
|
||||
|
||||
// mic gain 0.5dB/step 0 to 31
|
||||
BK4819_WriteRegister(BK4819_REG_7D, 0xE940 | (gEeprom.MIC_SENSITIVITY_TUNING & 0x1f));
|
||||
// mic gain 0.5dB/step 0 to 63
|
||||
BK4819_WriteRegister(BK4819_REG_7D, 0xE940 | (gEeprom.MIC_SENSITIVITY_TUNING & 0x3f));
|
||||
|
||||
uint32_t Frequency;
|
||||
#ifdef ENABLE_NOAA
|
||||
@@ -759,12 +821,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;
|
||||
|
||||
@@ -864,6 +921,7 @@ void RADIO_SetupRegisters(bool switchToForeground)
|
||||
InterruptMask |= BK4819_REG_3F_DTMF_5TONE_FOUND;
|
||||
|
||||
RADIO_SetupAGC(gRxVfo->Modulation == MODULATION_AM, false);
|
||||
//RADIO_SetupAGC(false, false);
|
||||
|
||||
// enable/disable BK4819 selected interrupts
|
||||
BK4819_WriteRegister(BK4819_REG_3F, InterruptMask);
|
||||
@@ -992,6 +1050,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:
|
||||
@@ -999,24 +1081,68 @@ void RADIO_SetModulation(ModulationMode_t modulation)
|
||||
mod = BK4819_AF_FM;
|
||||
break;
|
||||
case MODULATION_AM:
|
||||
mod = BK4819_AF_AM;
|
||||
mod = BK4819_AF_FM; // AM no longer needs special AF setting
|
||||
break;
|
||||
case MODULATION_USB:
|
||||
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);
|
||||
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
switch (modulation)
|
||||
{
|
||||
case MODULATION_AM:
|
||||
{
|
||||
uint16_t uVar1 = BK4819_ReadRegister(0x31);
|
||||
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(gCurrentVfo), true);
|
||||
break;
|
||||
}
|
||||
|
||||
case MODULATION_USB:
|
||||
case MODULATION_FM:
|
||||
default:
|
||||
{
|
||||
uint16_t uVar1 = BK4819_ReadRegister(0x31);
|
||||
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);
|
||||
@@ -1026,30 +1152,22 @@ void RADIO_SetModulation(ModulationMode_t modulation)
|
||||
|
||||
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)
|
||||
@@ -1123,6 +1241,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
|
||||
@@ -1202,17 +1326,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)
|
||||
@@ -1221,8 +1347,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);
|
||||
}
|
||||
|
||||
@@ -1232,7 +1357,6 @@ void RADIO_PrepareCssTX(void)
|
||||
|
||||
SYSTEM_DelayMs(200);
|
||||
|
||||
if(gEeprom.TAIL_TONE_ELIMINATION)
|
||||
RADIO_SendCssTail();
|
||||
RADIO_SendCssTail();
|
||||
RADIO_SetupRegisters(true);
|
||||
}
|
||||
+10
-7
@@ -21,6 +21,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "dcs.h"
|
||||
#include "driver/bk4819.h"
|
||||
#include "frequencies.h"
|
||||
|
||||
enum {
|
||||
@@ -95,7 +96,7 @@ typedef struct VFO_Info_t
|
||||
uint32_t TX_OFFSET_FREQUENCY;
|
||||
uint16_t StepFrequency;
|
||||
|
||||
uint8_t CHANNEL_SAVE;
|
||||
uint16_t CHANNEL_SAVE;
|
||||
|
||||
uint8_t TX_OFFSET_FREQUENCY_DIRECTION;
|
||||
|
||||
@@ -115,11 +116,9 @@ typedef struct VFO_Info_t
|
||||
uint8_t SCRAMBLING_TYPE;
|
||||
uint8_t CHANNEL_BANDWIDTH;
|
||||
|
||||
uint8_t SCANLIST1_PARTICIPATION;
|
||||
uint8_t SCANLIST2_PARTICIPATION;
|
||||
uint8_t SCANLIST3_PARTICIPATION;
|
||||
uint8_t SCANLIST_PARTICIPATION;
|
||||
|
||||
uint8_t Band;
|
||||
uint16_t Band;
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
uint8_t DTMF_DECODING_ENABLE;
|
||||
#endif
|
||||
@@ -150,17 +149,21 @@ extern DCS_CodeType_t gCurrentCodeType;
|
||||
|
||||
extern VfoState_t VfoState[2];
|
||||
|
||||
bool RADIO_CheckValidList(uint8_t scanList);
|
||||
void RADIO_NextValidList(int8_t direction);
|
||||
bool RADIO_CheckValidChannel(uint16_t channel, bool checkScanList, uint8_t scanList);
|
||||
uint8_t RADIO_FindNextChannel(uint8_t ChNum, int8_t Direction, bool bCheckScanList, uint8_t RadioNum);
|
||||
void RADIO_InitInfo(VFO_Info_t *pInfo, const uint8_t ChannelSave, const uint32_t Frequency);
|
||||
uint16_t RADIO_FindNextChannel(uint16_t ChNum, int8_t Direction, bool bCheckScanList, uint8_t RadioNum);
|
||||
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);
|
||||
|
||||
@@ -55,6 +55,7 @@ void SysTick_Handler(void)
|
||||
gNextTimeslice_500ms = true;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
DECREMENT_AND_TRIGGER(gVfoSaveCountdown_10ms, gScheduleVfoSave);
|
||||
DECREMENT_AND_TRIGGER(gTxTimerCountdownAlert_500ms - ALERT_TOT * 2, gTxTimeoutReachedAlert);
|
||||
#ifdef ENABLE_FEAT_F4HWN_RX_TX_TIMER
|
||||
DECREMENT(gRxTimerCountdown_500ms);
|
||||
@@ -97,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
|
||||
|
||||
+111
-60
@@ -18,106 +18,157 @@
|
||||
#include "driver/st7565.h"
|
||||
#include "screenshot.h"
|
||||
#include "misc.h"
|
||||
#include "driver/vcp.h"
|
||||
#include "driver/keyboard.h"
|
||||
|
||||
void getScreenShot(bool force)
|
||||
// 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 = 3;
|
||||
|
||||
void SCREENSHOT_ParseInput(void)
|
||||
{
|
||||
static uint8_t previousFrame[1024] = {0}; // Last transmitted frame
|
||||
static uint8_t forcedBlock = 0; // Block forced for refresh on each frame
|
||||
static uint8_t keepAlive = 10; // Keepalive counter
|
||||
if (SCREENSHOT_IsLocked())
|
||||
return;
|
||||
|
||||
// Use a single buffer to reduce stack usage
|
||||
static uint8_t currentFrame[1024]; // Current frame
|
||||
static uint8_t deltaFrame[128 * 9]; // Delta frame buffer
|
||||
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);
|
||||
} else {
|
||||
UART_Send(buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
void SCREENSHOT_Line(uint8_t *src, uint8_t *dest, uint16_t *idx) {
|
||||
for (uint8_t b = 0; b < 8; b++) {
|
||||
for (uint8_t i = 0; i < 128; i += 8) {
|
||||
uint8_t acc = 0;
|
||||
for (uint8_t k = 0; k < 8; k++) {
|
||||
if (src[i + k] & (1 << b)) acc |= (1 << k);
|
||||
}
|
||||
dest[(*idx)++] = gSetting_set_inv ? ~acc : acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SCREENSHOT_Update(bool force)
|
||||
{
|
||||
// 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;
|
||||
static bool wasConnected = false;
|
||||
|
||||
if (gUART_LockScreenshot > 0) // Lock screenshot if Chirp is in used
|
||||
{
|
||||
gUART_LockScreenshot--;
|
||||
if (SCREENSHOT_IsLocked())
|
||||
return;
|
||||
}
|
||||
|
||||
if (UART_IsCableConnected()) {
|
||||
keepAlive = 10;
|
||||
}
|
||||
|
||||
if (keepAlive > 0) {
|
||||
if (--keepAlive == 0) return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (--keepAlive == 0) {
|
||||
// Connection just lost → reset state for next reconnection
|
||||
wasConnected = false;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build current frame from status line (first 8 lines)
|
||||
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) {
|
||||
currentFrame[index++] = acc;
|
||||
acc = 0;
|
||||
bitCount = 0;
|
||||
}
|
||||
}
|
||||
// Connection is alive — detect reconnection and force full frame
|
||||
if (!wasConnected) {
|
||||
force = true;
|
||||
wasConnected = true;
|
||||
}
|
||||
|
||||
// Build remaining part of the frame (7 * 8 lines)
|
||||
// ==== BUILD FRAME ONCE ====
|
||||
// Status line: 8 bit layers × 128 columns
|
||||
SCREENSHOT_Line(gStatusLine, frameBuffer, &index);
|
||||
|
||||
// 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) {
|
||||
currentFrame[index++] = acc;
|
||||
acc = 0;
|
||||
bitCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
SCREENSHOT_Line(gFrameBuffer[l], frameBuffer, &index);
|
||||
}
|
||||
|
||||
if (bitCount > 0)
|
||||
currentFrame[index++] = acc;
|
||||
frameBuffer[index++] = acc;
|
||||
|
||||
if (index != 1024)
|
||||
return; // Frame size mismatch, abort
|
||||
return;
|
||||
|
||||
// Generate delta frame
|
||||
// ==== FIRST PASS: Count changed chunks ====
|
||||
uint16_t deltaLen = 0;
|
||||
uint8_t changedChunks[128]; // List of changed chunk indices
|
||||
uint8_t changedCount = 0;
|
||||
|
||||
for (uint8_t block = 0; block < 128; block++) {
|
||||
uint8_t *cur = ¤tFrame[block * 8];
|
||||
uint8_t *prev = &previousFrame[block * 8];
|
||||
for (uint8_t chunk = 0; chunk < 128; chunk++) {
|
||||
uint8_t *cur = &frameBuffer[chunk * 8];
|
||||
uint8_t *prev = &previousFrame[chunk * 8];
|
||||
|
||||
bool changed = memcmp(cur, prev, 8) != 0;
|
||||
bool isForced = (block == forcedBlock);
|
||||
bool changed = memcmp(cur, prev, 8) != 0;
|
||||
bool isForced = (chunk == forcedBlock);
|
||||
bool fullUpdate = force;
|
||||
|
||||
if (changed || isForced || fullUpdate) {
|
||||
deltaFrame[deltaLen++] = block;
|
||||
memcpy(&deltaFrame[deltaLen], cur, 8);
|
||||
deltaLen += 8;
|
||||
|
||||
memcpy(prev, cur, 8); // Update stored frame
|
||||
changedChunks[changedCount++] = chunk;
|
||||
deltaLen += 9;
|
||||
}
|
||||
}
|
||||
|
||||
forcedBlock = (forcedBlock + 1) % 128;
|
||||
|
||||
if (deltaLen == 0)
|
||||
return; // No update needed
|
||||
return;
|
||||
|
||||
// Send the delta frame over UART
|
||||
// Skip transmission if a key is currently pressed
|
||||
// UART_Send is blocking - would freeze the main loop and lose keypresses
|
||||
if (gKeyReading0 != KEY_INVALID)
|
||||
return;
|
||||
|
||||
// ==== Send version marker (for backward compatibility detection) ====
|
||||
// New format: sends 0xFF before header
|
||||
// Old format: doesn't exist, so viewers can differentiate
|
||||
uint8_t versionMarker = 0xFF;
|
||||
SCREENSHOT_Send(&versionMarker, 1);
|
||||
|
||||
// ==== Send header ====
|
||||
uint8_t header[5] = {
|
||||
0xAA, 0x55, 0x02,
|
||||
(uint8_t)(deltaLen >> 8),
|
||||
(uint8_t)(deltaLen & 0xFF)
|
||||
};
|
||||
|
||||
UART_Send(header, 5);
|
||||
UART_Send(deltaFrame, deltaLen);
|
||||
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];
|
||||
|
||||
chunk[0] = chunkIdx;
|
||||
memcpy(&chunk[1], cur, 8);
|
||||
|
||||
SCREENSHOT_Send(chunk, 9);
|
||||
|
||||
// Update previousFrame for next comparison
|
||||
memcpy(prev, cur, 8);
|
||||
}
|
||||
|
||||
uint8_t end = 0x0A;
|
||||
UART_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
|
||||
+563
-283
File diff suppressed because it is too large.
Load diff
+43
-14
@@ -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;
|
||||
|
||||
@@ -118,6 +121,7 @@ enum ACTION_OPT_t {
|
||||
ACTION_OPT_WN,
|
||||
ACTION_OPT_BACKLIGHT,
|
||||
ACTION_OPT_MUTE,
|
||||
ACTION_OPT_RXA,
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
ACTION_OPT_POWER_HIGH,
|
||||
ACTION_OPT_REMOVE_OFFSET,
|
||||
@@ -126,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
|
||||
};
|
||||
@@ -162,11 +169,11 @@ enum CHANNEL_DisplayMode_t {
|
||||
typedef enum CHANNEL_DisplayMode_t CHANNEL_DisplayMode_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t ScreenChannel[2]; // current channels set in the radio (memory or frequency channels)
|
||||
uint8_t FreqChannel[2]; // last frequency channels used
|
||||
uint8_t MrChannel[2]; // last memory channels used
|
||||
uint16_t ScreenChannel[2]; // current channels set in the radio (memory or frequency channels)
|
||||
uint16_t FreqChannel[2]; // last frequency channels used
|
||||
uint16_t MrChannel[2]; // last memory channels used
|
||||
#ifdef ENABLE_NOAA
|
||||
uint8_t NoaaChannel[2];
|
||||
uint16_t NoaaChannel[2];
|
||||
#endif
|
||||
|
||||
// The actual VFO index (0-upper/1-lower) that is now used for RX,
|
||||
@@ -194,6 +201,7 @@ typedef struct {
|
||||
bool KEY_LOCK;
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
bool KEY_LOCK_PTT;
|
||||
bool SET_NAV;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
bool MENU_LOCK;
|
||||
@@ -214,9 +222,8 @@ typedef struct {
|
||||
uint8_t BACKLIGHT_TIME;
|
||||
uint8_t SCAN_RESUME_MODE;
|
||||
uint8_t SCAN_LIST_DEFAULT;
|
||||
bool SCAN_LIST_ENABLED[3];
|
||||
uint8_t SCANLIST_PRIORITY_CH1[3];
|
||||
uint8_t SCANLIST_PRIORITY_CH2[3];
|
||||
bool SCAN_LIST_ENABLED;
|
||||
uint16_t SCANLIST_PRIORITY_CH[6];
|
||||
//#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE // Fix me !!! What the hell is this?
|
||||
uint8_t CURRENT_STATE;
|
||||
uint8_t CURRENT_LIST;
|
||||
@@ -241,7 +248,7 @@ typedef struct {
|
||||
uint8_t KEY_2_LONG_PRESS_ACTION;
|
||||
uint8_t MIC_SENSITIVITY;
|
||||
uint8_t MIC_SENSITIVITY_TUNING;
|
||||
uint8_t CHAN_1_CALL;
|
||||
uint16_t CHAN_1_CALL;
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
char ANI_DTMF_ID[8];
|
||||
char KILL_CODE[8];
|
||||
@@ -306,20 +313,42 @@ 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 int channel);
|
||||
void SETTINGS_FetchChannelName(char *s, const int channel);
|
||||
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
|
||||
void SETTINGS_SaveFM(void);
|
||||
#endif
|
||||
void SETTINGS_SaveVfoIndices(void);
|
||||
void SETTINGS_SaveVfoIndicesFlush(void);
|
||||
void SETTINGS_SaveSettings(void);
|
||||
void SETTINGS_SaveChannelName(uint8_t channel, const char * name);
|
||||
void SETTINGS_SaveChannel(uint8_t Channel, uint8_t VFO, const VFO_Info_t *pVFO, uint8_t Mode);
|
||||
void SETTINGS_SaveChannelName(uint16_t channel, const char * name);
|
||||
void SETTINGS_SaveChannel(uint16_t Channel, uint8_t VFO, const VFO_Info_t *pVFO, uint8_t Mode);
|
||||
void SETTINGS_SaveBatteryCalibration(const uint16_t * batteryCalibration);
|
||||
void SETTINGS_UpdateChannel(uint8_t channel, const VFO_Info_t *pVFO, bool keep, bool check, bool save);
|
||||
void SETTINGS_UpdateChannel(uint16_t channel, const VFO_Info_t *pVFO, bool keep, bool check, bool save);
|
||||
void SETTINGS_WriteBuildOptions(void);
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
void SETTINGS_WriteCurrentState(void);
|
||||
|
||||
+68
-32
@@ -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,55 +59,92 @@ 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);
|
||||
|
||||
percent = (gAirCopyBlockNumber * 10000) / 120;
|
||||
// Get the current map and calculate percentage based on its total blocks
|
||||
const AIRCOPY_TransferMap_t *currentMap = AIRCOPY_GetCurrentMap();
|
||||
|
||||
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);
|
||||
}
|
||||
uint16_t doneBlocks = gAirCopyBlockNumber + gErrorsDuringAirCopy;
|
||||
|
||||
// Draw gauge
|
||||
if(gAircopyStep != 0)
|
||||
if (doneBlocks > currentMap->total_blocks)
|
||||
doneBlocks = currentMap->total_blocks;
|
||||
|
||||
// Draw memory selection
|
||||
if (gAircopyState == AIRCOPY_READY)
|
||||
{
|
||||
doneBlocks = 0;
|
||||
|
||||
if(gAircopyCurrentMapIndex < AIRCOPY_NUM_BANKS) {
|
||||
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;
|
||||
|
||||
gFrameBuffer[4][1] = 0x3c;
|
||||
gFrameBuffer[4][2] = 0x42;
|
||||
|
||||
for(uint8_t i = 1; i <= 122; i++)
|
||||
{
|
||||
gFrameBuffer[4][2 + i] = 0x81;
|
||||
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);
|
||||
}
|
||||
|
||||
gFrameBuffer[4][125] = 0x42;
|
||||
gFrameBuffer[4][126] = 0x3c;
|
||||
// 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(gAirCopyBlockNumber + gErrorsDuringAirCopy != 0)
|
||||
if (doneBlocks > 0)
|
||||
{
|
||||
// Check CRC
|
||||
if(gErrorsDuringAirCopy != lErrorsDuringAirCopy)
|
||||
// Track CRC errors per real block index
|
||||
if (gErrorsDuringAirCopy != lErrorsDuringAirCopy)
|
||||
{
|
||||
set_bit(crc, gAirCopyBlockNumber + gErrorsDuringAirCopy);
|
||||
// Mark the last processed block as faulty
|
||||
set_bit(crc, doneBlocks - 1);
|
||||
lErrorsDuringAirCopy = gErrorsDuringAirCopy;
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < (gAirCopyBlockNumber + gErrorsDuringAirCopy); i++)
|
||||
uint16_t b = 0;
|
||||
uint16_t fraction_accumulator = 0;
|
||||
|
||||
for (uint8_t col = 0; col < AIRCOPY_BAR_WIDTH; col++)
|
||||
{
|
||||
if(get_bit(crc, i) == 0)
|
||||
{
|
||||
gFrameBuffer[4][i + 4] = 0xbd;
|
||||
bool processed = (b < doneBlocks);
|
||||
bool error = processed && get_bit(crc, b);
|
||||
|
||||
if (!processed)
|
||||
gFrameBuffer[4][col + 4] = 0x81; // not yet processed
|
||||
else if (error)
|
||||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,4 +152,4 @@ void UI_DisplayAircopy(void)
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
+7
-4
@@ -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",
|
||||
@@ -59,7 +63,7 @@ void UI_DisplayFM(void)
|
||||
pPrintStr = String;
|
||||
} else {
|
||||
pPrintStr = "VFO";
|
||||
for (unsigned int i = 0; i < 20; i++) {
|
||||
for (unsigned int i = 0; i < FM_CHANNELS_MAX; i++) {
|
||||
if (gEeprom.FM_FrequencyPlaying == gFM_Channels[i]) {
|
||||
sprintf(String, "VFO(CH%02u)", i + 1);
|
||||
pPrintStr = String;
|
||||
@@ -68,7 +72,7 @@ void UI_DisplayFM(void)
|
||||
}
|
||||
}
|
||||
} else if (gFM_AutoScan) {
|
||||
sprintf(String, "A-SCAN(%u)", gFM_ChannelPosition + 1);
|
||||
sprintf(String, "A-SCAN(%u)", gFM_ChannelPosition);
|
||||
pPrintStr = String;
|
||||
} else {
|
||||
pPrintStr = "M-SCAN";
|
||||
@@ -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) {
|
||||
|
||||
+73
-10
@@ -22,12 +22,10 @@
|
||||
#include "ui/helper.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "misc.h"
|
||||
#include "settings.h"
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0]))
|
||||
#endif
|
||||
|
||||
void UI_GenerateChannelString(char *pString, const uint8_t Channel)
|
||||
void UI_GenerateChannelString(char *pString, const uint16_t Channel)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
@@ -42,26 +40,30 @@ void UI_GenerateChannelString(char *pString, const uint8_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 uint8_t ChannelNumber)
|
||||
void UI_GenerateChannelStringEx(char *pString, const bool bShowPrefix, const uint16_t ChannelNumber)
|
||||
{
|
||||
if (gInputBoxIndex > 0) {
|
||||
for (unsigned int i = 0; i < 3; i++) {
|
||||
for (unsigned int i = 0; i < 4; i++) {
|
||||
pString[i] = (gInputBox[i] == 10) ? '-' : gInputBox[i] + '0';
|
||||
}
|
||||
|
||||
pString[3] = 0;
|
||||
pString[4] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bShowPrefix) {
|
||||
// BUG here? Prefixed NULLs are allowed
|
||||
sprintf(pString, "CH-%03u", ChannelNumber + 1);
|
||||
} else if (ChannelNumber == 0xFF) {
|
||||
sprintf(pString, "CH-%04u", ChannelNumber + 1);
|
||||
} else if (ChannelNumber == MR_CHANNEL_LAST + 1) {
|
||||
strcpy(pString, "None");
|
||||
} else if (ChannelNumber == 0xFFFF) {
|
||||
strcpy(pString, "NULL");
|
||||
} else {
|
||||
sprintf(pString, "%03u", ChannelNumber + 1);
|
||||
sprintf(pString, "%04u", ChannelNumber + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +118,35 @@ void UI_PrintStringSmallNormal(const char *pString, uint8_t Start, uint8_t End,
|
||||
UI_PrintStringSmall(pString, Start, End, Line, ARRAY_SIZE(gFontSmall[0]), (const uint8_t *)gFontSmall);
|
||||
}
|
||||
|
||||
void UI_PrintStringSmallNormalInverse(const char *pString, uint8_t Start, uint8_t End, uint8_t Line)
|
||||
{
|
||||
// First draw the string normally
|
||||
UI_PrintStringSmallNormal(pString, Start, End, Line);
|
||||
|
||||
// Now invert the framebuffer bits for the rendered area
|
||||
uint8_t len = strlen(pString);
|
||||
uint8_t char_width = 7; // small font is typically 6px wide
|
||||
|
||||
uint8_t x_start = Start;
|
||||
uint8_t x_end = Start + (len * char_width) + 1;
|
||||
|
||||
if (End != 0 && x_end > End)
|
||||
x_end = End;
|
||||
|
||||
//gFrameBuffer[Line][x_start - 2] ^= 0x3E;
|
||||
gFrameBuffer[Line][x_start - 1] ^= 0x7F;
|
||||
//gFrameBuffer[Line][x_start - 1] ^= 0xFF;
|
||||
for (uint8_t x = x_start; x < x_end; x++)
|
||||
{
|
||||
gFrameBuffer[Line][x] ^= 0xFF;
|
||||
gFrameBuffer[Line - 1][x] ^= 0x80;
|
||||
}
|
||||
//gFrameBuffer[Line][x_end + 0] ^= 0xFF;
|
||||
gFrameBuffer[Line][x_end + 0] ^= 0x7F;
|
||||
//gFrameBuffer[Line][x_end + 1] ^= 0x3E;
|
||||
}
|
||||
|
||||
|
||||
void UI_PrintStringSmallBold(const char *pString, uint8_t Start, uint8_t End, uint8_t Line)
|
||||
{
|
||||
#ifdef ENABLE_SMALL_BOLD
|
||||
@@ -293,6 +324,33 @@ static void sort(int16_t *a, int16_t *b)
|
||||
x += 4;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -350,3 +408,8 @@ void UI_DisplayClear()
|
||||
{
|
||||
memset(gFrameBuffer, 0, sizeof(gFrameBuffer));
|
||||
}
|
||||
|
||||
void UI_StatusClear()
|
||||
{
|
||||
memset(gStatusLine, 0, sizeof(gStatusLine));
|
||||
}
|
||||
+6
-2
@@ -20,10 +20,11 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void UI_GenerateChannelString(char *pString, const uint8_t Channel);
|
||||
void UI_GenerateChannelStringEx(char *pString, const bool bShowPrefix, const uint8_t ChannelNumber);
|
||||
void UI_GenerateChannelString(char *pString, const uint16_t Channel);
|
||||
void UI_GenerateChannelStringEx(char *pString, const bool bShowPrefix, const uint16_t ChannelNumber);
|
||||
void UI_PrintString(const char *pString, uint8_t Start, uint8_t End, uint8_t Line, uint8_t Width);
|
||||
void UI_PrintStringSmallNormal(const char *pString, uint8_t Start, uint8_t End, uint8_t Line);
|
||||
void UI_PrintStringSmallNormalInverse(const char *pString, uint8_t Start, uint8_t End, uint8_t Line);
|
||||
void UI_PrintStringSmallBold(const char *pString, uint8_t Start, uint8_t End, uint8_t Line);
|
||||
void UI_PrintStringSmallBufferNormal(const char *pString, uint8_t *buffer);
|
||||
void UI_PrintStringSmallBufferBold(const char *pString, uint8_t * buffer);
|
||||
@@ -37,10 +38,13 @@ 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 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
|
||||
|
||||
+947
-169
File diff suppressed because it is too large.
Load diff
+19
-2
@@ -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{
|
||||
@@ -36,12 +43,22 @@ enum Vfo_txtr_mode{
|
||||
typedef enum center_line_t center_line_t;
|
||||
|
||||
extern center_line_t center_line;
|
||||
extern const int8_t dBmCorrTable[7];
|
||||
|
||||
#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
|
||||
|
||||
+432
-206
File diff suppressed because it is too large.
Load diff
+66
-37
@@ -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;
|
||||
@@ -65,9 +65,7 @@ enum
|
||||
#endif
|
||||
MENU_SC_REV,
|
||||
MENU_AUTOLK,
|
||||
MENU_S_ADD1,
|
||||
MENU_S_ADD2,
|
||||
MENU_S_ADD3,
|
||||
MENU_LIST_CH,
|
||||
MENU_STE,
|
||||
MENU_RP_STE,
|
||||
MENU_MIC,
|
||||
@@ -75,9 +73,9 @@ enum
|
||||
MENU_COMPAND,
|
||||
MENU_1_CALL,
|
||||
MENU_S_LIST,
|
||||
MENU_SLIST1,
|
||||
MENU_SLIST2,
|
||||
MENU_SLIST3,
|
||||
MENU_S_PRI,
|
||||
MENU_S_PRI_CH_1,
|
||||
MENU_S_PRI_CH_2,
|
||||
#ifdef ENABLE_ALARM
|
||||
MENU_AL_MOD,
|
||||
#endif
|
||||
@@ -139,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
|
||||
@@ -151,6 +152,10 @@ enum
|
||||
#ifdef ENABLE_NOAA
|
||||
MENU_NOAA_S,
|
||||
#endif
|
||||
MENU_SET_NAV,
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
MENU_SET_AUD,
|
||||
#endif
|
||||
#endif
|
||||
MENU_BATCAL, // battery voltage calibration
|
||||
MENU_F1SHRT,
|
||||
@@ -158,65 +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* 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;
|
||||
|
||||
@@ -227,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();
|
||||
}
|
||||
+87
-67
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/chFrScanner.h"
|
||||
#ifdef ENABLE_FMRADIO
|
||||
#include "app/fm.h"
|
||||
@@ -48,22 +49,24 @@ static void convertTime(uint8_t *line, uint8_t type)
|
||||
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;
|
||||
|
||||
char str[8] = "";
|
||||
|
||||
#ifdef ENABLE_NOAA
|
||||
// NOAA indicator
|
||||
if (!(gScanStateDir != SCAN_OFF || SCANNER_IsScanning()) && gIsNoaaMode) { // NOASS SCAN indicator
|
||||
@@ -94,26 +97,41 @@ void UI_DisplayStatus()
|
||||
{ // SCAN indicator
|
||||
if (gScanStateDir != SCAN_OFF || SCANNER_IsScanning()) {
|
||||
if (IS_MR_CHANNEL(gNextMrChannel) && !SCANNER_IsScanning()) { // channel mode
|
||||
switch(gEeprom.SCAN_LIST_DEFAULT) {
|
||||
case 0:
|
||||
memcpy(line + 0, BITMAP_ScanList0, sizeof(BITMAP_ScanList0));
|
||||
break;
|
||||
case 1:
|
||||
memcpy(line + 0, BITMAP_ScanList1, sizeof(BITMAP_ScanList1));
|
||||
break;
|
||||
case 2:
|
||||
memcpy(line + 0, BITMAP_ScanList2, sizeof(BITMAP_ScanList2));
|
||||
break;
|
||||
case 3:
|
||||
memcpy(line + 0, BITMAP_ScanList3, sizeof(BITMAP_ScanList3));
|
||||
break;
|
||||
case 4:
|
||||
memcpy(line + 0, BITMAP_ScanList123, sizeof(BITMAP_ScanList123));
|
||||
break;
|
||||
case 5:
|
||||
memcpy(line + 0, BITMAP_ScanListAll, sizeof(BITMAP_ScanListAll));
|
||||
break;
|
||||
|
||||
uint8_t end = 0;
|
||||
|
||||
if(gEeprom.SCAN_LIST_DEFAULT == MR_CHANNELS_LIST + 1)
|
||||
{
|
||||
strcpy(str, "ALL");
|
||||
end = 14;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *name = gListName[gEeprom.SCAN_LIST_DEFAULT - 1];
|
||||
|
||||
// Check if name is valid
|
||||
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;
|
||||
}
|
||||
|
||||
GUI_DisplaySmallest(str, 2, 1, true, true);
|
||||
|
||||
gStatusLine[0] ^= 0x3E;
|
||||
for (uint8_t x = 1; x < end; x++)
|
||||
{
|
||||
gStatusLine[x] ^= 0x7F;
|
||||
}
|
||||
gStatusLine[end] ^= 0x3E;
|
||||
}
|
||||
else { // frequency mode
|
||||
memcpy(line + x + 1, gFontS, sizeof(gFontS));
|
||||
@@ -144,41 +162,48 @@ void UI_DisplayStatus()
|
||||
|
||||
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);
|
||||
bool isTransmit = gCurrentFunction == FUNCTION_TRANSMIT;
|
||||
if (gSetting_set_tmr && (isTransmit || FUNCTION_IsRx())) {
|
||||
convertTime(line, !isTransmit);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
if(gEeprom.MENU_LOCK == true) {
|
||||
memcpy(line + x + 2, gFontRO, sizeof(gFontRO));
|
||||
}
|
||||
else
|
||||
{
|
||||
#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));
|
||||
}
|
||||
else if(dw == 2) { // XB - crossband
|
||||
memcpy(line + x + 2, gFontXB, sizeof(gFontXB));
|
||||
}
|
||||
else
|
||||
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) {
|
||||
src = gFontRO;
|
||||
sSize = sizeof(gFontRO);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
memcpy(line + x + 2, gFontMO, sizeof(gFontMO));
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// Perform the memcpy if a source was selected
|
||||
if (src) {
|
||||
memcpy(line + x + sOff, src, sSize);
|
||||
}
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
x += sizeof(gFontDWR) + 3;
|
||||
@@ -195,14 +220,16 @@ void UI_DisplayStatus()
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
// PTT indicator
|
||||
if (gSetting_set_ptt_session) {
|
||||
memcpy(line + x, gFontPttOnePush, sizeof(gFontPttOnePush));
|
||||
x1 = x + sizeof(gFontPttOnePush) + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(line + x, gFontPttClassic, sizeof(gFontPttClassic));
|
||||
x1 = x + sizeof(gFontPttClassic) + 1;
|
||||
if(!gAirCopyBootMode) {
|
||||
if (gSetting_set_ptt_session) {
|
||||
memcpy(line + x, gFontPttOnePush, sizeof(gFontPttOnePush));
|
||||
x1 = x + sizeof(gFontPttOnePush) + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(line + x, gFontPttClassic, sizeof(gFontPttClassic));
|
||||
x1 = x + sizeof(gFontPttClassic) + 1;
|
||||
}
|
||||
}
|
||||
x += sizeof(gFontPttClassic) + 3;
|
||||
#endif
|
||||
@@ -218,15 +245,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) {
|
||||
|
||||
+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;
|
||||
|
||||
+236
-87
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "driver/py25q16.h"
|
||||
#include "driver/st7565.h"
|
||||
@@ -32,9 +33,191 @@
|
||||
#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
|
||||
extern uint8_t _edata; // End of .data in RAM
|
||||
extern uint8_t _sbss; // Start of .bss in RAM
|
||||
extern uint8_t _ebss; // End of .bss in RAM
|
||||
|
||||
// _eflash_used must be defined in the linker script immediately after the last
|
||||
// section with a FLASH load address (after .noncacheable). Example:
|
||||
//
|
||||
// .noncacheable : {
|
||||
// ...
|
||||
// } > RAM AT> FLASH
|
||||
// _eflash_used = LOADADDR(.noncacheable) + SIZEOF(.noncacheable);
|
||||
//
|
||||
// This gives the exact byte count that the linker reports as FLASH used.
|
||||
extern uint8_t _eflash_used;
|
||||
|
||||
// Absolute symbols: their *address* IS the numeric size value (ARM/CMSIS convention).
|
||||
// RAM = .data + gap + .bss + heap_reserve + stack_reserve
|
||||
extern uint8_t _Min_Heap_Size;
|
||||
extern uint8_t _Min_Stack_Size;
|
||||
|
||||
// Region sizes (must match your linker MEMORY regions)
|
||||
#define RAM_SIZE_BYTES (16u * 1024u)
|
||||
#define FLASH_SIZE_BYTES (118u * 1024u)
|
||||
|
||||
// Base address of FLASH — must match ORIGIN(FLASH) in your linker script
|
||||
#define FLASH_BASE (0x08002800u)
|
||||
|
||||
static inline uint32_t span(const void* a, const void* b)
|
||||
{
|
||||
return (uint32_t)((uintptr_t)b - (uintptr_t)a);
|
||||
}
|
||||
|
||||
static void build_usage(uint32_t* ram_used, uint32_t* flash_used)
|
||||
{
|
||||
// RAM: span from start of .data to end of .bss covers .data + alignment gap + .bss.
|
||||
// Then add heap and stack reservations (absolute linker symbols: address = size).
|
||||
// Proof: (0x20002A60 - 0x20000000) + 0x200 + 0x400 = 10848 + 512 + 1024 = 12384 B ✓
|
||||
const uint32_t heap_size = (uint32_t)(uintptr_t)&_Min_Heap_Size;
|
||||
const uint32_t stack_size = (uint32_t)(uintptr_t)&_Min_Stack_Size;
|
||||
*ram_used = span(&_sdata, &_ebss) + heap_size + stack_size;
|
||||
|
||||
// FLASH: _eflash_used is placed by the linker script right after the last
|
||||
// section copied to FLASH (.data LMA + .noncacheable LMA).
|
||||
// Note: _etext is NOT usable here because this linker script places .rodata
|
||||
// sections AFTER _etext, making it an unreliable end-of-flash marker.
|
||||
*flash_used = span((void*)FLASH_BASE, &_eflash_used);
|
||||
}
|
||||
|
||||
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
|
||||
@@ -49,12 +232,7 @@ void UI_DisplayReleaseKeys(void)
|
||||
|
||||
void UI_DisplayWelcome(void)
|
||||
{
|
||||
char WelcomeString0[16];
|
||||
char WelcomeString1[16];
|
||||
char WelcomeString2[16];
|
||||
char WelcomeString3[20];
|
||||
|
||||
memset(gStatusLine, 0, sizeof(gStatusLine));
|
||||
UI_StatusClear();
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN_CTR) || defined(ENABLE_FEAT_F4HWN_INV)
|
||||
ST7565_ContrastAndInv();
|
||||
@@ -64,21 +242,32 @@ 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(0x007020, WelcomeString0, 16);
|
||||
PY25Q16_ReadBuffer(0x00A0C8, WelcomeString0, 16);
|
||||
// 0x0EC0
|
||||
PY25Q16_ReadBuffer(0x007030, WelcomeString1, 16);
|
||||
PY25Q16_ReadBuffer(0x00A0D8, WelcomeString1, 16);
|
||||
|
||||
sprintf(WelcomeString2, "%u.%02uV %u%%",
|
||||
gBatteryVoltageAverage / 100,
|
||||
@@ -125,88 +314,48 @@ void UI_DisplayWelcome(void)
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
UI_PrintStringSmallNormal(Version, 0, 128, 4);
|
||||
|
||||
UI_DrawLineBuffer(gFrameBuffer, 0, 31, 127, 31, 1); // Be ware, status zone = 8 lines, the rest = 56 ->total 64
|
||||
|
||||
for (uint8_t i = 18; i < 110; i++)
|
||||
UI_DrawLineBuffer(gFrameBuffer, 0, 35, 18, 35, 1);
|
||||
gFrameBuffer[4][19] ^= 0x7F;
|
||||
for (uint8_t x = 20; x < 108; x++)
|
||||
{
|
||||
gFrameBuffer[4][i] ^= 0xFF;
|
||||
gFrameBuffer[4][x] ^= 0xFF;
|
||||
gFrameBuffer[3][x] ^= 0x80;
|
||||
}
|
||||
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;
|
||||
build_usage(&ram_used, &flash_used);
|
||||
|
||||
const uint16_t ram_pct = pct_x100(ram_used, RAM_SIZE_BYTES);
|
||||
const uint16_t flash_pct = pct_x100(flash_used, FLASH_SIZE_BYTES);
|
||||
|
||||
// No floats: 7559 => 75.59%
|
||||
sprintf(WelcomeString3,
|
||||
"FLASH %u.%02u %% - SRAM %u.%02u %%",
|
||||
(unsigned)(flash_pct / 100), (unsigned)(flash_pct % 100),
|
||||
(unsigned)(ram_pct / 100), (unsigned)(ram_pct % 100));
|
||||
|
||||
GUI_DisplaySmallest(WelcomeString3, 5, 1, true, true);
|
||||
ST7565_BlitStatusLine();
|
||||
#endif
|
||||
*/
|
||||
|
||||
sprintf(WelcomeString3, "%s Edition", Edition);
|
||||
UI_PrintStringSmallNormal(WelcomeString3, 0, 127, 6);
|
||||
|
||||
/*
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
#if ENABLE_FEAT_F4HWN_RESCUE_OPS > 1
|
||||
UI_PrintStringSmallNormal(Edition, 18, 0, 6);
|
||||
if(gEeprom.MENU_LOCK == true) {
|
||||
memcpy(gFrameBuffer[6] + 103, BITMAP_Ready, sizeof(BITMAP_Ready));
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(gFrameBuffer[6] + 103, BITMAP_NotReady, sizeof(BITMAP_NotReady));
|
||||
}
|
||||
#else
|
||||
UI_PrintStringSmallNormal(Edition, 18, 0, 5);
|
||||
memcpy(gFrameBuffer[5] + 103, BITMAP_Ready, sizeof(BITMAP_Ready));
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
UI_PrintStringSmallNormal("RescueOps", 18, 0, 6);
|
||||
if(gEeprom.MENU_LOCK == true) {
|
||||
memcpy(gFrameBuffer[6] + 103, BITMAP_Ready, sizeof(BITMAP_Ready));
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(gFrameBuffer[6] + 103, BITMAP_NotReady, sizeof(BITMAP_NotReady));
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
UI_PrintStringSmallNormal(Edition, 18, 0, 6);
|
||||
memcpy(gFrameBuffer[6] + 103, BITMAP_Ready, sizeof(BITMAP_Ready));
|
||||
#endif
|
||||
*/
|
||||
|
||||
/*
|
||||
#ifdef ENABLE_SPECTRUM
|
||||
#ifdef ENABLE_FMRADIO
|
||||
UI_PrintStringSmallNormal(Based, 0, 127, 5);
|
||||
UI_PrintStringSmallNormal(Credits, 0, 127, 6);
|
||||
#else
|
||||
UI_PrintStringSmallNormal("Bandscope ", 0, 127, 5);
|
||||
memcpy(gFrameBuffer[5] + 95, BITMAP_Ready, sizeof(BITMAP_Ready));
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
UI_PrintStringSmallNormal("RescueOps ", 0, 127, 6);
|
||||
if(gEeprom.MENU_LOCK == true) {
|
||||
memcpy(gFrameBuffer[6] + 95, BITMAP_Ready, sizeof(BITMAP_Ready));
|
||||
}
|
||||
#else
|
||||
UI_PrintStringSmallNormal("Broadcast ", 0, 127, 6);
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
UI_PrintStringSmallNormal("RescueOps ", 0, 127, 5);
|
||||
if(gEeprom.MENU_LOCK == true) {
|
||||
memcpy(gFrameBuffer[5] + 95, BITMAP_Ready, sizeof(BITMAP_Ready));
|
||||
}
|
||||
#else
|
||||
UI_PrintStringSmallNormal("Bandscope ", 0, 127, 5);
|
||||
#endif
|
||||
UI_PrintStringSmallNormal("Broadcast ", 0, 127, 6);
|
||||
memcpy(gFrameBuffer[6] + 95, BITMAP_Ready, sizeof(BITMAP_Ready));
|
||||
#endif
|
||||
*/
|
||||
#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")
|
||||
@@ -70,6 +85,10 @@ target_link_libraries(${EXE_NAME} Core App)
|
||||
target_compile_definitions(${EXE_NAME} PRIVATE $<$<CONFIG:Debug>:DEBUG>)
|
||||
target_link_libraries(${EXE_NAME} ${TOOLCHAIN_LINK_LIBRARIES})
|
||||
|
||||
if(ENABLE_LTO)
|
||||
target_compile_options(${EXE_NAME} PRIVATE -flto=auto)
|
||||
endif()
|
||||
|
||||
# Generate map file
|
||||
target_link_options(${EXE_NAME} PRIVATE "-Wl,-Map=${EXE_NAME}.map")
|
||||
|
||||
|
||||
+24
-7
@@ -9,6 +9,7 @@
|
||||
"toolchainFile": "${sourceDir}/cmake/gcc-arm-none-eabi.cmake",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"ENABLE_LTO": false,
|
||||
"ENABLE_FMRADIO": false,
|
||||
"ENABLE_UART": true,
|
||||
"ENABLE_USB": true,
|
||||
@@ -33,8 +34,7 @@
|
||||
"ENABLE_BOOT_BEEPS": false,
|
||||
"ENABLE_SHOW_CHARGE_LEVEL": false,
|
||||
"ENABLE_REVERSE_BAT_SYMBOL": false,
|
||||
"ENABLE_NO_CODE_SCAN_TIMEOUT": true,
|
||||
"ENABLE_AM_FIX": true,
|
||||
"ENABLE_NO_CODE_SCAN_TIMEOUT": false,
|
||||
"ENABLE_SQUELCH_MORE_SENSITIVE": true,
|
||||
"ENABLE_FASTER_CHANNEL_SCAN": true,
|
||||
"ENABLE_RSSI_BAR": true,
|
||||
@@ -59,18 +59,25 @@
|
||||
"ENABLE_FEAT_F4HWN_CTR": true,
|
||||
"ENABLE_FEAT_F4HWN_RESCUE_OPS": false,
|
||||
"ENABLE_FEAT_F4HWN_VOL": false,
|
||||
"ENABLE_FEAT_F4HWN_RESET_CHANNEL": 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_AM_FIX_SHOW_DATA": 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_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_NAVIG_LEFT_RIGHT": true,
|
||||
"ENABLE_SWD": false,
|
||||
"VERSION_STRING_1": "v0.22",
|
||||
"VERSION_STRING_2": "v4.3"
|
||||
"VERSION_STRING_2": "v5.5.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -187,6 +194,16 @@
|
||||
"ENABLE_FEAT_F4HWN_PMR": true,
|
||||
"ENABLE_FEAT_F4HWN_GMRS_FRS_MURS": true,
|
||||
"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_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"
|
||||
@@ -223,4 +240,4 @@
|
||||
"configurePreset": "Fusion"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -145,6 +145,15 @@ SECTIONS
|
||||
_edata = .; /* define a global symbol at data end */
|
||||
} >RAM AT> FLASH
|
||||
|
||||
/* Explicit placement of .noncacheable (CherryUSB) + end-of-FLASH symbol */
|
||||
.noncacheable :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.noncacheable)
|
||||
*(.noncacheable*)
|
||||
. = ALIGN(4);
|
||||
} >RAM AT> FLASH
|
||||
_eflash_used = LOADADDR(.noncacheable) + SIZEOF(.noncacheable);
|
||||
|
||||
/* Uninitialized data section */
|
||||
. = ALIGN(4);
|
||||
|
||||
+2
-1
@@ -3,7 +3,8 @@ FROM mcr.microsoft.com/devcontainers/python:3.10-bookworm
|
||||
# ---------------------------------------------
|
||||
# Base build tools
|
||||
# ---------------------------------------------
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
RUN rm -f /etc/apt/sources.list.d/yarn.list && \
|
||||
apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential cmake ninja-build python3 curl xz-utils ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
+2
-1
@@ -3,7 +3,8 @@ FROM mcr.microsoft.com/devcontainers/python:3.10-bookworm
|
||||
# ---------------------------------------------
|
||||
# Base build tools
|
||||
# ---------------------------------------------
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
RUN rm -f /etc/apt/sources.list.d/yarn.list && \
|
||||
apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential cmake ninja-build python3 curl xz-utils ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# Stats
|
||||
|
||||

|
||||
|
||||
# F4HWN firmware port for the UV-K1 and UV-K5 V3 using the PY32F071 MCU
|
||||
|
||||
This repository is a fork of the [F4HWN custom firmware](https://github.com/armel/uv-k5-firmware-custom), who was a fork of [Egzumer custom firmware](https://github.com/egzumer/uv-k5-firmware-custom). It extends the work done for the UV-K5 V1, based on the DP32G030 MCU, and adapts it to the newer UV-K1 and UV-K5 V3 built around the PY32F071 MCU. It is the result of the joint work of [@muzkr](https://github.com/muzkr) and [@armel](https://github.com/armel).
|
||||
@@ -27,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.
|
||||
@@ -38,7 +42,7 @@ Anyway, have fun.
|
||||
|
||||
# Donations
|
||||
|
||||
Special thanks to Jean-Cyrille F6IWW (2 times), Fabrice 14RC123, David F4BPP, Olivier 14RC206, Frédéric F4ESO, Stéphane F5LGW, 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 (3 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, Eric KI1C (2 times), Phil G0ELM, Jérôme Lambert, Meinhard Frank Günther, Eliot Vedel, Alfonso EA7KDF, Jean-François F1EVM, Robert DC1RDB, Ian KE2CHJ, Daryl VK3AWA, Roberto Brunelli, Robert Boardman, Stephen Oliver, Nicolas F4INE and William Bruno 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 and Marcin Kusaj for their [donations](https://www.paypal.com/paypalme/F4HWN). That’s so kind of them. Thanks so much 🙏🏻
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@@ -53,12 +57,15 @@ Special thanks to Jean-Cyrille F6IWW (2 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,
|
||||
@@ -88,6 +95,8 @@ Special thanks to Jean-Cyrille F6IWW (2 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),
|
||||
@@ -99,10 +108,13 @@ Special thanks to Jean-Cyrille F6IWW (2 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,
|
||||
@@ -112,6 +124,7 @@ Special thanks to Jean-Cyrille F6IWW (2 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,
|
||||
@@ -120,18 +133,18 @@ Special thanks to Jean-Cyrille F6IWW (2 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,
|
||||
@@ -139,23 +152,26 @@ Special thanks to Jean-Cyrille F6IWW (2 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,
|
||||
@@ -163,7 +179,7 @@ Special thanks to Jean-Cyrille F6IWW (2 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:
|
||||
@@ -199,7 +215,7 @@ Special thanks to Jean-Cyrille F6IWW (2 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
|
||||
|
||||
@@ -207,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
|
||||
|
||||
@@ -234,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
|
||||
@@ -278,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
|
||||
@@ -338,11 +338,12 @@ 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)
|
||||
* [DualTachyon](https://github.com/DualTachyon)
|
||||
* [Mikhail](https://github.com/fagci)
|
||||
* [Andrej](https://github.com/Tunas1337)
|
||||
* [Manuel](https://github.com/manujedi)
|
||||
* @wagner
|
||||
* @Lohtse Shar
|
||||
|
||||
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.
Loaded 100 of 151 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user