mirror of
https://github.com/armel/uv-k1-k5v3-firmware-custom.git
synced 2026-10-05 12:37:29 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe9c4e9432 | ||
|
|
1e4698409b | ||
|
|
39f46a8f93 | ||
|
|
3518ab8294 | ||
|
|
c45b7d2deb | ||
|
|
f904b87172 | ||
|
|
926787ddf3 | ||
|
|
fb9491caa5 | ||
|
|
04e8d47130 | ||
|
|
3bdddce67b | ||
|
|
d5cde97e5b | ||
|
|
66ed14dead | ||
|
|
602dfad6f3 | ||
|
|
0203586cdc | ||
|
|
3e5795255d | ||
|
|
a8912d3208 | ||
|
|
fe602b075d | ||
|
|
e445cfca85 | ||
|
|
18e9de38e0 | ||
|
|
f371795ef8 | ||
|
|
e9181c2287 | ||
|
|
5437bf2d8e | ||
|
|
b972e026ca | ||
|
|
0cad22f369 | ||
|
|
2a13c1819d | ||
|
|
4799eb46ac | ||
|
|
2569e4649d | ||
|
|
a59f3edcff | ||
|
|
027240de54 | ||
|
|
73f44911ed | ||
|
|
c0e3909853 | ||
|
|
31ff3b4df0 | ||
|
|
bbee375623 | ||
|
|
12dfa8a87f | ||
|
|
c9da65117f | ||
|
|
222448e1ad | ||
|
|
e1610bcc6f | ||
|
|
7f906300e3 | ||
|
|
bf7eda8a48 | ||
|
|
2ee1d96e5c | ||
|
|
65ec12bb0d | ||
|
|
dd3fc786b5 | ||
|
|
10868858c5 | ||
|
|
3bd3ebba2c | ||
|
|
39cbbd7e90 | ||
|
|
0567f01786 | ||
|
|
fc12a35418 | ||
|
|
2580acfb07 | ||
|
|
56a6083689 | ||
|
|
29725ed704 | ||
|
|
b44df132d0 | ||
|
|
676f26be34 | ||
|
|
46034ec39f | ||
|
|
e2d21c61c2 | ||
|
|
f8e1b7a436 | ||
|
|
9447eaada9 | ||
|
|
43533c838b | ||
|
|
7306db5f8f | ||
|
|
fbd7b37c21 | ||
|
|
8d3754b42f | ||
|
|
6a556e0601 | ||
|
|
9c1b1cb063 | ||
|
|
ae7bf65383 | ||
|
|
fb1360cce2 | ||
|
|
374e52c4d1 | ||
|
|
42aeac3469 | ||
|
|
98ac57b032 | ||
|
|
3e6e7c87b5 | ||
|
|
e908eda1da | ||
|
|
4fccdb78b9 | ||
|
|
a14127867e | ||
|
|
c02aac265a | ||
|
|
c24592cbe8 | ||
|
|
ff096e12e5 | ||
|
|
5fcc1598dd | ||
|
|
9b8493b450 | ||
|
|
f1df96f1f6 | ||
|
|
f36c524fa7 | ||
|
|
9ab5542326 | ||
|
|
0187c05e65 | ||
|
|
914c354da0 | ||
|
|
a771387edd | ||
|
|
09bc14e64b | ||
|
|
58c0170fb1 | ||
|
|
5b4b3e2a71 | ||
|
|
65a5c9f4ef | ||
|
|
a2936402c2 | ||
|
|
f069ed935f | ||
|
|
dcbbd744ac | ||
|
|
bca06e3382 | ||
|
|
8723863fb0 | ||
|
|
c775fa8b2f | ||
|
|
80e7d4b3d1 | ||
|
|
e6a727eeda | ||
|
|
887e18efe6 | ||
|
|
20b87feb1d | ||
|
|
e2e9c9ba60 | ||
|
|
337eb0cb12 | ||
|
|
504a2b0fb0 | ||
|
|
16b07c6374 | ||
|
|
f5b20d4f80 | ||
|
|
ae14bf5df3 | ||
|
|
9dc85852ba | ||
|
|
f7d29f6a14 | ||
|
|
1184618731 | ||
|
|
3d15d59ce0 | ||
|
|
15fccc31d6 | ||
|
|
48f753caaf | ||
|
|
edc4673983 | ||
|
|
9e03f02296 | ||
|
|
25ea1f827b | ||
|
|
8f11a32c9d | ||
|
|
576b8c552f | ||
|
|
85db5a2394 | ||
|
|
98989d4f0a | ||
|
|
8c976cfffd | ||
|
|
0ce5422db3 | ||
|
|
d939009949 | ||
|
|
50efade5c4 | ||
|
|
682a59fc0e | ||
|
|
3d3f370b86 | ||
|
|
eebeba09db | ||
|
|
6cceb26814 | ||
|
|
a77b132fa5 | ||
|
|
86aa05f1ec | ||
|
|
953f579adb | ||
|
|
d429b3afe8 | ||
|
|
a65a4f601a | ||
|
|
546cd35681 | ||
|
|
8ecf976067 | ||
|
|
3e54dc1c00 | ||
|
|
9eb9a9da5c | ||
|
|
a74529b9ec | ||
|
|
7ccb53572a | ||
|
|
c515188a06 | ||
|
|
cdcb950ccd | ||
|
|
13ca3b8a39 | ||
|
|
34acff0fbf | ||
|
|
fa75ab4243 | ||
|
|
1589d8bdd7 | ||
|
|
a5597a1ab9 | ||
|
|
955b245e66 | ||
|
|
2c4bca4202 | ||
|
|
6c4c240e9a | ||
|
|
e21d472609 | ||
|
|
8f89312e99 | ||
|
|
ec9fdd17bf | ||
|
|
1ed550fb92 | ||
|
|
7d69b74b08 | ||
|
|
f50a29ae08 | ||
|
|
7a4226f483 | ||
|
|
4523108293 | ||
|
|
c92e63da0d | ||
|
|
33d949a4be | ||
|
|
a9ff58e4a7 | ||
|
|
b8936ace0e | ||
|
|
59ed9cd827 | ||
|
|
a0e4d2708b | ||
|
|
c0c1d9881c | ||
|
|
c1d5b7bd55 | ||
|
|
932d6e954a | ||
|
|
3e6b169b7b | ||
|
|
ec4ea28325 | ||
|
|
5157dc573b | ||
|
|
a0aefae01b | ||
|
|
46cf7d7d57 | ||
|
|
9a7b199bb9 | ||
|
|
1e03dc4af0 | ||
|
|
673d1b57ab | ||
|
|
6ae360d029 | ||
|
|
d5a29aa241 | ||
|
|
e0154de186 | ||
|
|
915083522d | ||
|
|
c6e7ac33e2 | ||
|
|
bc494a4526 | ||
|
|
1ee871cc79 | ||
|
|
b83e4c2491 | ||
|
|
c1f8e945c9 | ||
|
|
9013f83734 | ||
|
|
a46604d8f3 | ||
|
|
037de071eb | ||
|
|
2c116f9503 | ||
|
|
50681ef90b | ||
|
|
0501422c64 | ||
|
|
786318453f | ||
|
|
61662f3965 | ||
|
|
b3cfda76e5 | ||
|
|
a20f1ce28e | ||
|
|
8a292755b0 | ||
|
|
915fc6aa90 | ||
|
|
cac63f978e | ||
|
|
d5edba1fc2 | ||
|
|
2f0852cd03 | ||
|
|
a65f84305e | ||
|
|
1c0d5ea628 | ||
|
|
a0a5b93408 | ||
|
|
e8cb32a4ca | ||
|
|
706dea756c | ||
|
|
1ea8aba88e | ||
|
|
c2d6a4b751 | ||
|
|
db61bce014 | ||
|
|
a3c03f18f0 | ||
|
|
8977a0a112 | ||
|
|
09a5e3c1e7 | ||
|
|
4fa821e15f | ||
|
|
c9a383919a | ||
|
|
e8ba069000 | ||
|
|
acd2c340ea | ||
|
|
24fd6fd9b1 | ||
|
|
86bb710ca4 | ||
|
|
7148c15f98 | ||
|
|
2d7093651d | ||
|
|
874e5aac4b | ||
|
|
8051605c16 | ||
|
|
134624825f | ||
|
|
7d0fcc8415 | ||
|
|
c27b72d6bc | ||
|
|
066c0d0e14 | ||
|
|
e77b87f1f2 | ||
|
|
2dd5e1a509 | ||
|
|
ab9e97805e | ||
|
|
65c584ff7d | ||
|
|
4c76ca1fb4 | ||
|
|
094896b5f1 | ||
|
|
669dad5b2e | ||
|
|
b9902e0c49 | ||
|
|
0a955c1f15 | ||
|
|
9f649b5f5b | ||
|
|
bb992ca85d | ||
|
|
679cf4ea1f | ||
|
|
0004074ffe | ||
|
|
35e5d8df90 | ||
|
|
fa0de5f576 | ||
|
|
4162618473 | ||
|
|
284af7ca1d | ||
|
|
c6f1f7b483 | ||
|
|
6cea90d507 | ||
|
|
46608af767 | ||
|
|
24167a7902 | ||
|
|
e300c4e7e3 | ||
|
|
1a815b315f | ||
|
|
a7fed2c2d8 | ||
|
|
9e92037674 | ||
|
|
e536725731 | ||
|
|
f59370cee6 | ||
|
|
bdaf320c08 | ||
|
|
fb60d6f7fd | ||
|
|
22295eedcf | ||
|
|
21c215e9fd | ||
|
|
0061613883 | ||
|
|
aecbcc6409 | ||
|
|
642dcc9a43 | ||
|
|
2b05990a2c | ||
|
|
975084baca | ||
|
|
775cd7751a | ||
|
|
c5da503cb3 | ||
|
|
5f8a492f4f | ||
|
|
8dd5da195e | ||
|
|
61aa14cd86 | ||
|
|
86a6bac145 | ||
|
|
26616c506e | ||
|
|
f8c48ceb8a | ||
|
|
3d739c642e | ||
|
|
25b96be1a3 | ||
|
|
397ef318f8 | ||
|
|
fbb06fed05 | ||
|
|
064a203db0 | ||
|
|
5987ec7b64 | ||
|
|
d72524879b | ||
|
|
db46cea0eb | ||
|
|
8f75d6813b | ||
|
|
1f577ec82e | ||
|
|
1f801757e9 | ||
|
|
4588e58af5 | ||
|
|
6fe1bec80c | ||
|
|
a2735b1167 | ||
|
|
249b0b2bb7 | ||
|
|
62320f5fbe | ||
|
|
cebbda72ec | ||
|
|
c51033db77 | ||
|
|
f863c41d40 | ||
|
|
ab7b4ca094 | ||
|
|
179c271618 | ||
|
|
2e6dfe3bc7 | ||
|
|
122cf48d3e | ||
|
|
dea747c628 | ||
|
|
bbbc553064 | ||
|
|
43c40ae9a0 | ||
|
|
6fdb17d64a | ||
|
|
aa69d698a7 | ||
|
|
f4a32b4350 | ||
|
|
688b3a5c77 | ||
|
|
ff336cd5d9 | ||
|
|
85dae05ad3 | ||
|
|
2939620066 | ||
|
|
0b3a390cb9 | ||
|
|
3d4ffd7900 | ||
|
|
06907f9b67 | ||
|
|
7c343a2b3e | ||
|
|
b4f66e9bf3 | ||
|
|
138e078984 | ||
|
|
e28177cb9b | ||
|
|
a1f2f0cdb9 | ||
|
|
e77e558d64 | ||
|
|
7d5e5b7d97 | ||
|
|
315d7a6b59 | ||
|
|
67231fd326 | ||
|
|
f4d8abd7d9 | ||
|
|
9574376d38 | ||
|
|
f2666a6248 | ||
|
|
ee5878d641 | ||
|
|
470d1959ba | ||
|
|
f5d6f5782b | ||
|
|
5cb6bbdf7a | ||
|
|
9f0547f6af | ||
|
|
fcf94e34ce | ||
|
|
5d88fda158 | ||
|
|
7b7186e293 | ||
|
|
d83c6451c8 | ||
|
|
eaa05f107e | ||
|
|
d0fdca4dc1 | ||
|
|
37f18972e7 | ||
|
|
7471ac90c5 | ||
|
|
d826cbd138 | ||
|
|
5293a8526d | ||
|
|
74aa7b354e | ||
|
|
ffddf9a9b3 | ||
|
|
77785d14c2 | ||
|
|
f715f776a4 | ||
|
|
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 |
No files matched your search
+5
-2
@@ -7,10 +7,13 @@ firmware
|
||||
.cache
|
||||
compile_commands.json
|
||||
.vscode
|
||||
.claude
|
||||
.agents
|
||||
/docs
|
||||
k5_eeprom.raw
|
||||
|
||||
/build
|
||||
/serialtool/__pycache__
|
||||
__pycache__/
|
||||
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
AGENTS.md
|
||||
+37
-7
@@ -70,6 +70,7 @@ if(ENABLE_FEAT_F4HWN)
|
||||
"AUTHOR_STRING_2=\"${AUTHOR_STRING_2}\""
|
||||
"VERSION_STRING_1=\"${VERSION_STRING_1}\""
|
||||
"VERSION_STRING_2=\"${VERSION_STRING_2}\""
|
||||
"DISPLAY_VERSION_STRING_2=\"${DISPLAY_VERSION_STRING_2}\""
|
||||
"EDITION_STRING=\"${EDITION_STRING}\""
|
||||
ALERT_TOT=10
|
||||
)
|
||||
@@ -78,6 +79,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 +124,7 @@ enable_feature(ENABLE_FMRADIO
|
||||
ui/fmradio.c
|
||||
)
|
||||
enable_feature(ENABLE_AIRCOPY
|
||||
app/aircopy.c
|
||||
app/aircopy.c
|
||||
ui/aircopy.c
|
||||
)
|
||||
enable_feature(ENABLE_NOAA)
|
||||
@@ -161,6 +163,7 @@ enable_feature(ENABLE_SQUELCH_MORE_SENSITIVE)
|
||||
enable_feature(ENABLE_FASTER_CHANNEL_SCAN)
|
||||
enable_feature(ENABLE_RSSI_BAR)
|
||||
enable_feature(ENABLE_AUDIO_BAR)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_AUDIO_SCOPE)
|
||||
enable_feature(ENABLE_COPY_CHAN_TO_VFO)
|
||||
enable_feature(ENABLE_REDUCE_LOW_MID_TX_POWER)
|
||||
enable_feature(ENABLE_BYP_RAW_DEMODULATORS)
|
||||
@@ -169,10 +172,6 @@ enable_feature(ENABLE_SCAN_RANGES)
|
||||
|
||||
# ---- CONTRIB MODS ----
|
||||
|
||||
# Thank you @markusb
|
||||
enable_feature(ENABLE_REGA
|
||||
app/rega.c
|
||||
)
|
||||
# Thank you @reppad
|
||||
enable_feature(ENABLE_EXTRA_UART_CMD)
|
||||
|
||||
@@ -182,8 +181,8 @@ enable_feature(ENABLE_FEAT_F4HWN)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_GAME
|
||||
app/breakout.c
|
||||
)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
screenshot.c
|
||||
enable_feature(ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
k5viewer.c
|
||||
)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_SPECTRUM)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_RX_TX_TIMER)
|
||||
@@ -193,6 +192,10 @@ enable_feature(ENABLE_FEAT_F4HWN_RESUME_STATE)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_NARROWER)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_INV)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_CTR)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_SCAN_PROGRESS)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_SCAN_FASTER)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_SCAN_RSSI)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_RESCUE_OPS)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_VOL)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_AUDIO)
|
||||
@@ -201,6 +204,33 @@ 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
|
||||
)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
app/rxtx_log.c
|
||||
)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
app/foxhunt.c
|
||||
)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_RXTX_LOG_WRAP)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER)
|
||||
|
||||
if(ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER AND NOT (ENABLE_FEAT_F4HWN_RXTX_LOG AND ENABLE_FEAT_F4HWN_K5VIEWER))
|
||||
message(FATAL_ERROR "ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER requires ENABLE_FEAT_F4HWN_RXTX_LOG (log data) and ENABLE_FEAT_F4HWN_K5VIEWER (serial transport).")
|
||||
endif()
|
||||
|
||||
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 ----
|
||||
|
||||
|
||||
+129
-112
@@ -40,9 +40,16 @@
|
||||
#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"
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
#include "app/beam.h"
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
#include "app/rxtx_log.h"
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
#include "app/foxhunt.h"
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_FMRADIO)
|
||||
@@ -57,7 +64,7 @@ inline static void ACTION_1750() { ACTION_AlarmOr1750(true); };
|
||||
|
||||
inline static void ACTION_ScanRestart() { ACTION_Scan(true); };
|
||||
|
||||
void (*action_opt_table[])(void) = {
|
||||
void (*const action_opt_table[])(void) = {
|
||||
[ACTION_OPT_NONE] = &FUNCTION_NOP,
|
||||
[ACTION_OPT_POWER] = &ACTION_Power,
|
||||
[ACTION_OPT_MONITOR] = &ACTION_Monitor,
|
||||
@@ -127,9 +134,14 @@ void (*action_opt_table[])(void) = {
|
||||
#else
|
||||
[ACTION_OPT_RXMODE] = &FUNCTION_NOP,
|
||||
#endif
|
||||
#ifdef ENABLE_REGA
|
||||
[ACTION_OPT_REGA_ALARM] = &ACTION_RegaAlarm,
|
||||
[ACTION_OPT_REGA_TEST] = &ACTION_RegaTest,
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
[ACTION_OPT_BEAM] = &ACTION_Beam,
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
[ACTION_OPT_RXTX_LOG] = &ACTION_RxTxLog,
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
[ACTION_OPT_FOXHUNT] = &ACTION_FoxHunt,
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -212,7 +224,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();
|
||||
|
||||
@@ -236,16 +248,15 @@ void ACTION_Scan(bool bRestart)
|
||||
}
|
||||
|
||||
// channel mode. Keep scanning but toggle between scan lists
|
||||
RADIO_NextValidList();
|
||||
RADIO_NextValidList(1);
|
||||
UI_MAIN_NotifyScanListChanged();
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
SETTINGS_WriteCurrentState();
|
||||
#endif
|
||||
|
||||
// jump to the next channel
|
||||
CHFRSCANNER_Start(false, gScanStateDir);
|
||||
gScanPauseDelayIn_10ms = 1;
|
||||
gScheduleScanListen = false;
|
||||
CHFRSCANNER_ManualResume(gScanStateDir);
|
||||
} else {
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
if(gScanRangeStart == 0) // No ScanRange
|
||||
@@ -290,6 +301,8 @@ void ACTION_SwitchDemodul(void)
|
||||
|
||||
void ACTION_Handle(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
HideFKeyIcon();
|
||||
|
||||
if (gScreenToDisplay == DISPLAY_MAIN && gDTMF_InputMode){
|
||||
// entering DTMF code
|
||||
|
||||
@@ -319,45 +332,76 @@ 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
|
||||
#ifdef ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
case ACTION_OPT_FOXHUNT:
|
||||
#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]();
|
||||
}
|
||||
|
||||
|
||||
@@ -381,6 +425,12 @@ void ACTION_FM(void)
|
||||
|
||||
gMonitor = false;
|
||||
|
||||
if (gScanStateDir != SCAN_OFF) {
|
||||
// Stop the channel/frequency scan before switching to the FM radio.
|
||||
gScanKeepResult = false;
|
||||
CHFRSCANNER_Stop();
|
||||
}
|
||||
|
||||
RADIO_SelectVfos();
|
||||
RADIO_SetupRegisters(true);
|
||||
|
||||
@@ -437,7 +487,7 @@ static void ACTION_Scan_FM(bool bRestart)
|
||||
static void ACTION_AlarmOr1750(const bool b1750)
|
||||
{
|
||||
|
||||
if(gEeprom.KEY_LOCK && gEeprom.KEY_LOCK_PTT)
|
||||
if(gEeprom.KEY_LOCK && (gSetting_set_lck & SET_LCK_PTT))
|
||||
return;
|
||||
|
||||
#if defined(ENABLE_ALARM)
|
||||
@@ -503,17 +553,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();
|
||||
}
|
||||
|
||||
@@ -523,29 +569,29 @@ 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)
|
||||
{
|
||||
gSetting_set_audio = (gSetting_set_audio + 1) % 5;
|
||||
if(gRxVfo->Modulation == MODULATION_AM)
|
||||
gSetting_set_audio_am = (gSetting_set_audio_am + 1) % 3;
|
||||
else if (gRxVfo->Modulation == MODULATION_FM)
|
||||
gSetting_set_audio_fm = (gSetting_set_audio_fm + 1) % 5;
|
||||
|
||||
RADIO_SetModulation(gRxVfo->Modulation);
|
||||
}
|
||||
#endif
|
||||
@@ -553,64 +599,36 @@ void ACTION_RxA(void)
|
||||
void ACTION_Ptt(void)
|
||||
{
|
||||
gSetting_set_ptt_session = !gSetting_set_ptt_session;
|
||||
|
||||
ACTION_Update();
|
||||
}
|
||||
|
||||
void ACTION_Wn(void)
|
||||
{
|
||||
if (gRxVfo->Modulation == MODULATION_AM)
|
||||
const bool isRx = FUNCTION_IsRx();
|
||||
VFO_Info_t *pVfo = isRx ? gRxVfo : gTxVfo;
|
||||
|
||||
pVfo->CHANNEL_BANDWIDTH = !pVfo->CHANNEL_BANDWIDTH;
|
||||
|
||||
if (pVfo->Modulation == MODULATION_AM)
|
||||
{
|
||||
BK4819_SetFilterBandwidth(BK4819_FILTER_BW_AM, true);
|
||||
BK4819_SetFilterBandwidth(RADIO_GetAMFilterBandwidth(pVfo), true);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t bw = pVfo->CHANNEL_BANDWIDTH;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
bool narrower = 0;
|
||||
if (FUNCTION_IsRx())
|
||||
if (isRx && bw == BANDWIDTH_NARROW && gSetting_set_nfm == 1)
|
||||
{
|
||||
gRxVfo->CHANNEL_BANDWIDTH = (gRxVfo->CHANNEL_BANDWIDTH == 0) ? 1: 0;
|
||||
if(gRxVfo->CHANNEL_BANDWIDTH == BANDWIDTH_NARROW && gSetting_set_nfm == 1)
|
||||
{
|
||||
narrower = 1;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_AM_FIX
|
||||
BK4819_SetFilterBandwidth(gRxVfo->CHANNEL_BANDWIDTH + narrower, true);
|
||||
#else
|
||||
BK4819_SetFilterBandwidth(gRxVfo->CHANNEL_BANDWIDTH + narrower, false);
|
||||
#endif
|
||||
bw++;
|
||||
}
|
||||
else
|
||||
{
|
||||
gTxVfo->CHANNEL_BANDWIDTH = (gTxVfo->CHANNEL_BANDWIDTH == 0) ? 1: 0;
|
||||
if(gTxVfo->CHANNEL_BANDWIDTH == BANDWIDTH_NARROW && gSetting_set_nfm == 1)
|
||||
{
|
||||
narrower = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_AM_FIX
|
||||
BK4819_SetFilterBandwidth(gTxVfo->CHANNEL_BANDWIDTH, true);
|
||||
#else
|
||||
BK4819_SetFilterBandwidth(gTxVfo->CHANNEL_BANDWIDTH, false);
|
||||
#endif
|
||||
}
|
||||
#ifdef ENABLE_AM_FIX
|
||||
BK4819_SetFilterBandwidth(bw, true);
|
||||
#else
|
||||
if (FUNCTION_IsRx())
|
||||
{
|
||||
gRxVfo->CHANNEL_BANDWIDTH = (gRxVfo->CHANNEL_BANDWIDTH == 0) ? 1: 0;
|
||||
#ifdef ENABLE_AM_FIX
|
||||
BK4819_SetFilterBandwidth(gRxVfo->CHANNEL_BANDWIDTH, true);
|
||||
#else
|
||||
BK4819_SetFilterBandwidth(gRxVfo->CHANNEL_BANDWIDTH, false);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
gTxVfo->CHANNEL_BANDWIDTH = (gTxVfo->CHANNEL_BANDWIDTH == 0) ? 1: 0;
|
||||
#ifdef ENABLE_AM_FIX
|
||||
BK4819_SetFilterBandwidth(gTxVfo->CHANNEL_BANDWIDTH, true);
|
||||
#else
|
||||
BK4819_SetFilterBandwidth(gTxVfo->CHANNEL_BANDWIDTH, false);
|
||||
#endif
|
||||
}
|
||||
BK4819_SetFilterBandwidth(bw, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -657,26 +675,25 @@ void ACTION_Mute(void)
|
||||
BK1080_WriteRegister(BK1080_REG_05_SYSTEM_CONFIGURATION2, gMute ? 0x0A10 : 0x0A1F);
|
||||
#endif
|
||||
gEeprom.VOLUME_GAIN = gMute ? 0 : gEeprom.VOLUME_GAIN_BACKUP;
|
||||
BK4819_WriteRegister(BK4819_REG_48,
|
||||
(11u << 12) | // ??? .. 0 ~ 15, doesn't seem to make any difference
|
||||
(0u << 10) | // AF Rx Gain-1
|
||||
(gEeprom.VOLUME_GAIN << 4) | // AF Rx Gain-2
|
||||
(gEeprom.DAC_GAIN << 0)); // AF DAC Gain (after Gain-1 and Gain-2)
|
||||
BK4819_SetRxAudioGain();
|
||||
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
void ACTION_ToggleVfoSetting(bool *setting) {
|
||||
*setting = !(*setting);
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
}
|
||||
|
||||
void ACTION_Power_High(void)
|
||||
{
|
||||
gPowerHigh = !gPowerHigh;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
ACTION_ToggleVfoSetting(&gPowerHigh);
|
||||
}
|
||||
|
||||
void ACTION_Remove_Offset(void)
|
||||
{
|
||||
gRemoveOffset = !gRemoveOffset;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
ACTION_ToggleVfoSetting(&gRemoveOffset);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
@@ -50,6 +50,9 @@ void ACTION_SwitchDemodul(void);
|
||||
void ACTION_Power_High(void);
|
||||
void ACTION_Remove_Offset(void);
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
void ACTION_RxTxLog(void);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void ACTION_Handle(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld);
|
||||
|
||||
+77
-132
@@ -30,8 +30,8 @@
|
||||
#include "settings.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
#include "screenshot.h"
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
#include "k5viewer.h"
|
||||
#endif
|
||||
|
||||
static const uint16_t Obfuscation[8] = { 0x6C16, 0xE614, 0x912E, 0x400D, 0x3521, 0x40D5, 0x0313, 0x80E9 };
|
||||
@@ -39,7 +39,7 @@ static const uint16_t Obfuscation[8] = { 0x6C16, 0xE614, 0x912E, 0x400D, 0x3521,
|
||||
AIRCOPY_State_t gAircopyState;
|
||||
uint16_t gAirCopyBlockNumber;
|
||||
uint16_t gErrorsDuringAirCopy;
|
||||
uint8_t gAirCopyIsSendMode;
|
||||
bool gAirCopyIsSendMode;
|
||||
|
||||
uint16_t g_FSK_Buffer[36];
|
||||
|
||||
@@ -88,13 +88,17 @@ DECLARE_AIRCOPY_BANK(1)
|
||||
// For settings only
|
||||
|
||||
static const AIRCOPY_Segment_t AIRCOPY_Segments_Settings[] = {
|
||||
{ 0xA000, 0xA160, AIRCOPY_WRITE_BYTES },
|
||||
{ 0xA000, 0xA170, AIRCOPY_WRITE_BYTES },
|
||||
{ 0x880E, 0x886E, AIRCOPY_WRITE_BYTES },
|
||||
{ 0x9000, 0x90E5, AIRCOPY_WRITE_BYTES }, // VFO area (full 14 VFOs 0x9000..0x90E0) +
|
||||
// Fox Hunt settings tail 0x90E0..0x90E5
|
||||
};
|
||||
|
||||
// total_blocks = ceil(0x170/64) + ceil(0x60/64) + ceil(0xE5/64) = 6 + 2 + 4 = 12
|
||||
static const AIRCOPY_TransferMap_t AIRCOPY_Map_Settings = {
|
||||
.segments = AIRCOPY_Segments_Settings,
|
||||
.num_segments = 1,
|
||||
.total_blocks = 6
|
||||
.num_segments = 3,
|
||||
.total_blocks = 12
|
||||
};
|
||||
|
||||
// Finally
|
||||
@@ -137,8 +141,8 @@ static void AIRCOPY_clear()
|
||||
{
|
||||
crc[i] = 0;
|
||||
}
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot(true);
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
K5VIEWER_Update(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -157,20 +161,29 @@ static inline const AIRCOPY_Segment_t *AIRCOPY_FindSegmentForOffset(uint16_t off
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void AIRCOPY_CheckComplete(void)
|
||||
static inline void AIRCOPY_CheckComplete(uint16_t *num)
|
||||
{
|
||||
*num = *num + 1;
|
||||
|
||||
const AIRCOPY_TransferMap_t *map = AIRCOPY_GetCurrentMap();
|
||||
uint16_t done = gAirCopyBlockNumber + gErrorsDuringAirCopy;
|
||||
|
||||
if (done >= map->total_blocks)
|
||||
{
|
||||
gAircopyState = AIRCOPY_COMPLETE;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot(false);
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
K5VIEWER_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
|
||||
// ============================================================================
|
||||
@@ -210,8 +223,8 @@ bool AIRCOPY_SendMessage(void)
|
||||
// Check if transfer is complete
|
||||
if (CurrentSegmentIndex >= map->num_segments) {
|
||||
gAircopyState = AIRCOPY_COMPLETE;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot(false);
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
K5VIEWER_Update(false);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
@@ -222,9 +235,7 @@ bool AIRCOPY_SendMessage(void)
|
||||
|
||||
g_FSK_Buffer[34] = CRC_Calculate(&g_FSK_Buffer[1], 2 + 64);
|
||||
|
||||
for (unsigned int i = 0; i < 34; i++) {
|
||||
g_FSK_Buffer[i + 1] ^= Obfuscation[i % 8];
|
||||
}
|
||||
AIRCOPY_Obfuscate(34);
|
||||
|
||||
RADIO_SetTxParameters();
|
||||
|
||||
@@ -251,93 +262,61 @@ void AIRCOPY_StorePacket(void)
|
||||
BK4819_PrepareFSKReceive();
|
||||
|
||||
if ((Status & 0x0010U) != 0 || g_FSK_Buffer[0] != 0xABCD || g_FSK_Buffer[35] != 0xDCBA) {
|
||||
gErrorsDuringAirCopy++;
|
||||
|
||||
BK4819_ResetFSK(); // <- important
|
||||
BK4819_PrepareFSKReceive(); // <- re-arm proprement
|
||||
|
||||
AIRCOPY_CheckComplete();
|
||||
AIRCOPY_CheckComplete(&gErrorsDuringAirCopy);
|
||||
return;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < 34; i++) {
|
||||
g_FSK_Buffer[i + 1] ^= Obfuscation[i % 8];
|
||||
}
|
||||
AIRCOPY_Obfuscate(34);
|
||||
|
||||
uint16_t Crc = CRC_Calculate(&g_FSK_Buffer[1], 2 + 64);
|
||||
if (g_FSK_Buffer[34] != Crc) {
|
||||
gErrorsDuringAirCopy++;
|
||||
AIRCOPY_CheckComplete();
|
||||
AIRCOPY_CheckComplete(&gErrorsDuringAirCopy);
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t Offset = g_FSK_Buffer[1];
|
||||
const AIRCOPY_TransferMap_t *map = AIRCOPY_GetCurrentMap();
|
||||
|
||||
// Validate offset is in the map
|
||||
bool validOffset = false;
|
||||
for (uint16_t i = 0; i < map->num_segments; i++) {
|
||||
if (Offset >= map->segments[i].start_offset && Offset < map->segments[i].end_offset) {
|
||||
validOffset = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!validOffset) {
|
||||
gErrorsDuringAirCopy++;
|
||||
AIRCOPY_CheckComplete();
|
||||
return;
|
||||
}
|
||||
|
||||
const AIRCOPY_Segment_t *seg = AIRCOPY_FindSegmentForOffset(Offset);
|
||||
|
||||
|
||||
if (seg == NULL) {
|
||||
gErrorsDuringAirCopy++;
|
||||
AIRCOPY_CheckComplete();
|
||||
AIRCOPY_CheckComplete(&gErrorsDuringAirCopy);
|
||||
return;
|
||||
}
|
||||
|
||||
if (seg->write_mode == AIRCOPY_WRITE_BYTES)
|
||||
{
|
||||
/* Raw bytes stream, written in 8-byte EEPROM chunks */
|
||||
const uint8_t *p8 = (const uint8_t *)&g_FSK_Buffer[2];
|
||||
const uint8_t *pData = (const uint8_t *)&g_FSK_Buffer[2];
|
||||
|
||||
for (unsigned int i = 0; i < 8; i++)
|
||||
{
|
||||
EEPROM_WriteBuffer(Offset + (i * 8), p8 + (i * 8));
|
||||
}
|
||||
}
|
||||
else
|
||||
for (unsigned int i = 0; i < 8; i++)
|
||||
{
|
||||
/* Structured data path (kept as-is) */
|
||||
const uint16_t *pData = &g_FSK_Buffer[2];
|
||||
|
||||
for (unsigned int i = 0; i < 8; i++)
|
||||
{
|
||||
EEPROM_WriteBuffer(Offset, pData);
|
||||
pData += 4;
|
||||
Offset += 8;
|
||||
}
|
||||
EEPROM_WriteBuffer(Offset + (i * 8), pData + (i * 8));
|
||||
}
|
||||
|
||||
gAirCopyBlockNumber++;
|
||||
AIRCOPY_CheckComplete();
|
||||
AIRCOPY_CheckComplete(&gAirCopyBlockNumber);
|
||||
}
|
||||
|
||||
static void AIRCOPY_InitTransfer(bool isSendMode)
|
||||
{
|
||||
gAircopyStep = 1;
|
||||
gFSKWriteIndex = 0;
|
||||
gAirCopyBlockNumber = 0;
|
||||
gInputBoxIndex = 0;
|
||||
gAirCopyIsSendMode = isSendMode;
|
||||
|
||||
AIRCOPY_clear();
|
||||
|
||||
gAircopyState = AIRCOPY_TRANSFER;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Key Processing
|
||||
// ============================================================================
|
||||
|
||||
static void AIRCOPY_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
static void AIRCOPY_Key_DIGITS(KEY_Code_t Key)
|
||||
{
|
||||
if (bKeyHeld || !bKeyPressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
INPUTBOX_Append(Key);
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_AIRCOPY;
|
||||
|
||||
if (gInputBoxIndex < 6) {
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
@@ -372,62 +351,34 @@ static void AIRCOPY_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
BK4819_ResetFSK();
|
||||
return;
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_AIRCOPY;
|
||||
}
|
||||
|
||||
static void AIRCOPY_Key_EXIT(bool bKeyPressed, bool bKeyHeld)
|
||||
static void AIRCOPY_Key_EXIT()
|
||||
{
|
||||
if (bKeyHeld || !bKeyPressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gInputBoxIndex == 0) {
|
||||
gAircopyStep = 1;
|
||||
gFSKWriteIndex = 0;
|
||||
gAirCopyBlockNumber = 0;
|
||||
gInputBoxIndex = 0;
|
||||
AIRCOPY_InitTransfer(0); // Mode: Receive
|
||||
gErrorsDuringAirCopy = lErrorsDuringAirCopy = 0;
|
||||
gAirCopyIsSendMode = 0;
|
||||
|
||||
AIRCOPY_clear();
|
||||
|
||||
BK4819_PrepareFSKReceive();
|
||||
|
||||
gAircopyState = AIRCOPY_TRANSFER;
|
||||
|
||||
} else {
|
||||
gInputBox[--gInputBoxIndex] = 10;
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_AIRCOPY;
|
||||
}
|
||||
|
||||
static void AIRCOPY_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
static void AIRCOPY_Key_MENU()
|
||||
{
|
||||
if (bKeyHeld || !bKeyPressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
gAircopyStep = 1;
|
||||
gFSKWriteIndex = 0;
|
||||
gAirCopyBlockNumber = 0;
|
||||
gInputBoxIndex = 0;
|
||||
gAirCopyIsSendMode = 1;
|
||||
AIRCOPY_InitTransfer(1); // Mode: Send
|
||||
|
||||
g_FSK_Buffer[0] = 0xABCD;
|
||||
g_FSK_Buffer[1] = 0;
|
||||
g_FSK_Buffer[35] = 0xDCBA;
|
||||
|
||||
AIRCOPY_clear();
|
||||
|
||||
GUI_DisplayScreen();
|
||||
|
||||
gAircopyState = AIRCOPY_TRANSFER;
|
||||
}
|
||||
|
||||
static void AIRCOPY_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
static void AIRCOPY_Key_UP_DOWN(int8_t Direction)
|
||||
{
|
||||
if (bKeyHeld || !bKeyPressed) {
|
||||
return;
|
||||
if (!gEeprom.SET_NAV) {
|
||||
Direction = -Direction;
|
||||
}
|
||||
|
||||
switch(Direction)
|
||||
@@ -439,46 +390,40 @@ static void AIRCOPY_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Directio
|
||||
gAircopyCurrentMapIndex = (gAircopyCurrentMapIndex + AIRCOPY_NUM_MAPS - 1) % AIRCOPY_NUM_MAPS;
|
||||
break;
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_AIRCOPY;
|
||||
}
|
||||
|
||||
void AIRCOPY_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if (bKeyHeld || !bKeyPressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Key != KEY_PTT) {
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
}
|
||||
|
||||
switch (Key) {
|
||||
case KEY_0:
|
||||
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:
|
||||
if(gEeprom.SET_NAV == 0)
|
||||
AIRCOPY_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
|
||||
else
|
||||
AIRCOPY_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
if(gEeprom.SET_NAV == 0)
|
||||
AIRCOPY_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
|
||||
else
|
||||
AIRCOPY_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
|
||||
AIRCOPY_Key_UP_DOWN(Key == KEY_UP ? 1 : -1);
|
||||
break;
|
||||
case KEY_PTT:
|
||||
break;
|
||||
default:
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
break;
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_AIRCOPY;
|
||||
}
|
||||
|
||||
#endif
|
||||
+5
-1
@@ -92,7 +92,7 @@ typedef enum {
|
||||
extern AIRCOPY_State_t gAircopyState;
|
||||
extern uint16_t gAirCopyBlockNumber;
|
||||
extern uint16_t gErrorsDuringAirCopy;
|
||||
extern uint8_t gAirCopyIsSendMode;
|
||||
extern bool gAirCopyIsSendMode;
|
||||
|
||||
extern uint16_t g_FSK_Buffer[36];
|
||||
|
||||
@@ -106,5 +106,9 @@ void AIRCOPY_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld);
|
||||
|
||||
const AIRCOPY_TransferMap_t* AIRCOPY_GetCurrentMap(void);
|
||||
|
||||
// XOR-obfuscate `count` words of g_FSK_Buffer starting at index 1.
|
||||
// Self-inverse: applying twice restores the original buffer.
|
||||
void AIRCOPY_Obfuscate(unsigned int count);
|
||||
|
||||
#endif // ENABLE_AIRCOPY
|
||||
#endif // APP_AIRCOPY_H
|
||||
+536
-186
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
|
||||
+126
-234
@@ -16,8 +16,12 @@
|
||||
|
||||
#include "app/breakout.h"
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
#include "screenshot.h"
|
||||
#if defined(ENABLE_UART) || defined(ENABLE_USB)
|
||||
#include "app/uart.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
#include "k5viewer.h"
|
||||
#endif
|
||||
|
||||
static uint32_t randSeed = 1;
|
||||
@@ -25,9 +29,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 +45,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 +67,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 +84,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 +99,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 +114,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 +146,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 +154,6 @@ void drawBall() {
|
||||
ballCount--;
|
||||
UI_DisplayClear();
|
||||
drawScore();
|
||||
isBeep = true;
|
||||
tone = 800;
|
||||
if (ballCount < 0) {
|
||||
reset();
|
||||
@@ -261,15 +161,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 +187,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 +199,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 +211,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 +250,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 +286,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 +305,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_K5VIEWER
|
||||
// Parse incoming packets on every tick so serial keys are never missed,
|
||||
// regardless of whether the screen needs redrawing.
|
||||
K5VIEWER_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 +337,69 @@ 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)
|
||||
{
|
||||
#if defined(ENABLE_UART) || defined(ENABLE_USB)
|
||||
UART_ServiceCommands();
|
||||
#endif
|
||||
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_K5VIEWER
|
||||
if(isPaused || swap == 0)
|
||||
{
|
||||
K5VIEWER_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;
|
||||
|
||||
|
||||
+839
-7
@@ -1,9 +1,17 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/chFrScanner.h"
|
||||
#include "audio.h"
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
#include "driver/systick.h"
|
||||
#endif
|
||||
#include "functions.h"
|
||||
#include "misc.h"
|
||||
#include "settings.h"
|
||||
#include "ui/main.h"
|
||||
//#include "debugging.h"
|
||||
|
||||
int8_t gScanStateDir;
|
||||
@@ -13,6 +21,29 @@ bool gScanPauseMode;
|
||||
#ifdef ENABLE_SCAN_RANGES
|
||||
uint32_t gScanRangeStart;
|
||||
uint32_t gScanRangeStop;
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE) && ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE
|
||||
DCS_CodeType_t gScanRangeCssType = CODE_TYPE_OFF;
|
||||
uint8_t gScanRangeCssCode = 0xFF;
|
||||
static uint8_t scanRangeCssCandidate = 0xFF;
|
||||
static uint8_t scanRangeCssHitCount = 0;
|
||||
#endif
|
||||
|
||||
#define SCAN_RANGE_SKIP_MAX 64
|
||||
#if (SCAN_RANGE_SKIP_MAX & (SCAN_RANGE_SKIP_MAX - 1)) != 0
|
||||
#error SCAN_RANGE_SKIP_MAX must be a power of two
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint16_t sample[SCAN_RANGE_SKIP_MAX];
|
||||
uint32_t start;
|
||||
uint32_t stop;
|
||||
uint16_t step;
|
||||
uint8_t count;
|
||||
uint8_t next;
|
||||
} ScanRangeSkipList_t;
|
||||
|
||||
static ScanRangeSkipList_t scanRangeSkip;
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
@@ -36,6 +67,687 @@ uint8_t initialCROSS_BAND_RX_TX;
|
||||
|
||||
static void NextFreqChannel(void);
|
||||
static void NextMemChannel(void);
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
static void ScanFastResetState(void);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
static bool ScanFastEnabled(void)
|
||||
{
|
||||
return gSetting_set_scn;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SCAN_RANGES
|
||||
static void ScanRangeSkipClear(void)
|
||||
{
|
||||
scanRangeSkip.count = 0;
|
||||
scanRangeSkip.next = 0;
|
||||
}
|
||||
|
||||
static void ScanRangeSkipSync(void)
|
||||
{
|
||||
const uint16_t step = gRxVfo->StepFrequency;
|
||||
|
||||
if (scanRangeSkip.start == gScanRangeStart &&
|
||||
scanRangeSkip.stop == gScanRangeStop &&
|
||||
scanRangeSkip.step == step)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ScanRangeSkipClear();
|
||||
scanRangeSkip.start = gScanRangeStart;
|
||||
scanRangeSkip.stop = gScanRangeStop;
|
||||
scanRangeSkip.step = step;
|
||||
}
|
||||
|
||||
static uint16_t ScanRangeSkipSampleForFrequency(uint32_t frequency)
|
||||
{
|
||||
const uint16_t step = gRxVfo->StepFrequency;
|
||||
if (!gScanRangeStart || step == 0 || frequency < gScanRangeStart || frequency > gScanRangeStop)
|
||||
return 0;
|
||||
|
||||
const uint32_t sample = (frequency - gScanRangeStart) / step;
|
||||
if (sample >= 0xFFFFu)
|
||||
return 0;
|
||||
|
||||
return (uint16_t)(sample + 1);
|
||||
}
|
||||
|
||||
static bool ScanRangeSkipContainsFrequency(uint32_t frequency)
|
||||
{
|
||||
const uint16_t sample = ScanRangeSkipSampleForFrequency(frequency);
|
||||
if (sample == 0)
|
||||
return false;
|
||||
|
||||
for (uint8_t i = 0; i < scanRangeSkip.count; ++i)
|
||||
if (scanRangeSkip.sample[i] == sample)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint32_t ScanRangeNextFrequency(void)
|
||||
{
|
||||
uint32_t frequency;
|
||||
uint8_t guard = SCAN_RANGE_SKIP_MAX + 1;
|
||||
|
||||
ScanRangeSkipSync();
|
||||
|
||||
do
|
||||
{
|
||||
frequency = APP_SetFreqByStepAndLimits(gRxVfo, gScanStateDir, gScanRangeStart, gScanRangeStop);
|
||||
gRxVfo->freq_config_RX.Frequency = frequency;
|
||||
} while (--guard && ScanRangeSkipContainsFrequency(frequency));
|
||||
|
||||
return frequency;
|
||||
}
|
||||
|
||||
bool CHFRSCANNER_ExcludeCurrentScanRange(void)
|
||||
{
|
||||
ScanRangeSkipSync();
|
||||
|
||||
uint16_t sample = ScanRangeSkipSampleForFrequency(lastFoundFrqOrChan);
|
||||
if (sample == 0)
|
||||
sample = ScanRangeSkipSampleForFrequency(gRxVfo->freq_config_RX.Frequency);
|
||||
|
||||
if (sample == 0)
|
||||
return false;
|
||||
|
||||
for (uint8_t i = 0; i < scanRangeSkip.count; ++i)
|
||||
if (scanRangeSkip.sample[i] == sample)
|
||||
return true;
|
||||
|
||||
scanRangeSkip.sample[scanRangeSkip.next] = sample;
|
||||
scanRangeSkip.next = (scanRangeSkip.next + 1) & (SCAN_RANGE_SKIP_MAX - 1);
|
||||
if (scanRangeSkip.count < SCAN_RANGE_SKIP_MAX)
|
||||
scanRangeSkip.count++;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
ScanFastResetState();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CHFRSCANNER_HasScanRangeExcludedOrdinal(uint32_t first_ordinal, uint32_t last_ordinal)
|
||||
{
|
||||
if (first_ordinal == 0)
|
||||
first_ordinal = 1;
|
||||
if (last_ordinal < first_ordinal)
|
||||
return false;
|
||||
if (first_ordinal > 0xFFFFu)
|
||||
return false;
|
||||
if (last_ordinal > 0xFFFFu)
|
||||
last_ordinal = 0xFFFFu;
|
||||
|
||||
for (uint8_t i = 0; i < scanRangeSkip.count; ++i) {
|
||||
const uint16_t sample = scanRangeSkip.sample[i];
|
||||
if (sample >= first_ordinal && sample <= last_ordinal)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE) && ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE
|
||||
void CHFRSCANNER_UpdateCssDetection(void)
|
||||
{
|
||||
if (!gScanRangeStart || !FUNCTION_IsRx())
|
||||
{
|
||||
gScanRangeCssType = CODE_TYPE_OFF;
|
||||
gScanRangeCssCode = 0xFF;
|
||||
scanRangeCssCandidate = 0xFF;
|
||||
scanRangeCssHitCount = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t cdcssFreq;
|
||||
uint16_t ctcssFreq;
|
||||
const BK4819_CssScanResult_t result = BK4819_GetCxCSSScanResult(&cdcssFreq, &ctcssFreq);
|
||||
|
||||
if (result == BK4819_CSS_RESULT_CDCSS)
|
||||
{
|
||||
const uint8_t Code = DCS_GetCdcssCode(cdcssFreq);
|
||||
if (Code != 0xFF && Code != gScanRangeCssCode)
|
||||
{
|
||||
gScanRangeCssType = CODE_TYPE_DIGITAL;
|
||||
gScanRangeCssCode = Code;
|
||||
scanRangeCssCandidate = 0xFF;
|
||||
scanRangeCssHitCount = 0;
|
||||
gUpdateDisplay = true;
|
||||
}
|
||||
}
|
||||
else if (result == BK4819_CSS_RESULT_CTCSS)
|
||||
{
|
||||
const uint8_t Code = DCS_GetCtcssCode(ctcssFreq);
|
||||
if (Code != 0xFF)
|
||||
{
|
||||
if (Code == scanRangeCssCandidate)
|
||||
{
|
||||
if (++scanRangeCssHitCount >= 2 && Code != gScanRangeCssCode)
|
||||
{
|
||||
gScanRangeCssType = CODE_TYPE_CONTINUOUS_TONE;
|
||||
gScanRangeCssCode = Code;
|
||||
gUpdateDisplay = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
scanRangeCssCandidate = Code;
|
||||
scanRangeCssHitCount = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static void CHFRSCANNER_AbortActiveReception(void)
|
||||
{
|
||||
if (!FUNCTION_IsRx())
|
||||
return;
|
||||
|
||||
AUDIO_AudioPathOff();
|
||||
gEnableSpeaker = false;
|
||||
gMonitor = false;
|
||||
gRxReceptionMode = RX_MODE_NONE;
|
||||
gScanPauseMode = false;
|
||||
|
||||
FUNCTION_Init();
|
||||
FUNCTION_Select(FUNCTION_FOREGROUND);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
#define SCAN_FAST_PRECHECK_STEPS 6
|
||||
#define SCAN_FAST_RSSI_MARGIN 16
|
||||
#define SCAN_FAST_SQUELCH_MARGIN 8
|
||||
#define SCAN_FAST_WEAK_MARGIN 8
|
||||
#define SCAN_FAST_RECHECK_DELAY_US 350
|
||||
#define SCAN_FAST_FINE_STEP_LIMIT 250
|
||||
#define SCAN_FAST_FINE_REFINE_SPAN 1000
|
||||
#define SCAN_FAST_FINE_REFINE_MAX 80
|
||||
#define SCAN_FAST_FINE_RSSI_DROP 8
|
||||
#define SCAN_FAST_RSSI_MAX 65535u
|
||||
// Settle loop guard: at most this many 1us waits while the BK4819 glitch
|
||||
// indicator stays above SCAN_FAST_GLITCH_THRESHOLD. Caps the worst-case
|
||||
// settling time per step at ~50us before we read the RSSI anyway.
|
||||
#define SCAN_FAST_GLITCH_GUARD_MAX 50
|
||||
#define SCAN_FAST_GLITCH_THRESHOLD 200
|
||||
// HF/VHF boundary in Hz: BK4819_PickRXFilterPathBasedOnFrequency() switches
|
||||
// the front-end filter path here, so we only re-run that (relatively
|
||||
// expensive) call when we actually cross the boundary.
|
||||
#define SCAN_FAST_HF_VHF_BOUNDARY_HZ 28000000u
|
||||
|
||||
typedef enum {
|
||||
SCAN_FAST_DISABLED,
|
||||
SCAN_FAST_QUIET_BATCH,
|
||||
SCAN_FAST_CANDIDATE
|
||||
} scan_fast_result_t;
|
||||
|
||||
static uint16_t scanFastReg30;
|
||||
static uint16_t scanFastNoiseFloor = SCAN_FAST_RSSI_MAX;
|
||||
static uint32_t scanFastPrevFrequency;
|
||||
static bool scanFastLastFullTuneCandidate;
|
||||
static VFO_Info_t scanFastDisplayVfo;
|
||||
static bool scanFastDisplayVfoValid;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_RSSI
|
||||
static uint16_t scanRssiSparkline[CHFRSCANNER_RSSI_SPARKLINE_WIDTH];
|
||||
static uint8_t scanRssiSparklineWrite;
|
||||
static uint8_t scanRssiSparklineCount;
|
||||
|
||||
static void ScanRssiSparklineReset(void)
|
||||
{
|
||||
memset(scanRssiSparkline, 0, sizeof(scanRssiSparkline));
|
||||
scanRssiSparklineWrite = 0;
|
||||
scanRssiSparklineCount = 0;
|
||||
}
|
||||
|
||||
static void ScanRssiSparklinePush(uint16_t rssi)
|
||||
{
|
||||
scanRssiSparkline[scanRssiSparklineWrite] = rssi;
|
||||
scanRssiSparklineWrite++;
|
||||
if (scanRssiSparklineWrite >= CHFRSCANNER_RSSI_SPARKLINE_WIDTH)
|
||||
scanRssiSparklineWrite = 0;
|
||||
|
||||
if (scanRssiSparklineCount < CHFRSCANNER_RSSI_SPARKLINE_WIDTH)
|
||||
scanRssiSparklineCount++;
|
||||
}
|
||||
|
||||
bool CHFRSCANNER_HasScanRssiSparkline(void)
|
||||
{
|
||||
return gScanStateDir != SCAN_OFF && scanRssiSparklineCount > 1;
|
||||
}
|
||||
|
||||
uint8_t CHFRSCANNER_GetScanRssiSparklineLevel(uint8_t index)
|
||||
{
|
||||
uint16_t minRssi = SCAN_FAST_RSSI_MAX;
|
||||
uint16_t rssi = 0;
|
||||
uint8_t oldest;
|
||||
|
||||
if (index >= CHFRSCANNER_RSSI_SPARKLINE_WIDTH || scanRssiSparklineCount == 0)
|
||||
return 0;
|
||||
|
||||
if (index < CHFRSCANNER_RSSI_SPARKLINE_WIDTH - scanRssiSparklineCount)
|
||||
return 0;
|
||||
|
||||
oldest = (uint8_t)((scanRssiSparklineWrite + CHFRSCANNER_RSSI_SPARKLINE_WIDTH - scanRssiSparklineCount)
|
||||
% CHFRSCANNER_RSSI_SPARKLINE_WIDTH);
|
||||
index -= (uint8_t)(CHFRSCANNER_RSSI_SPARKLINE_WIDTH - scanRssiSparklineCount);
|
||||
|
||||
for (uint8_t i = 0; i < scanRssiSparklineCount; i++)
|
||||
{
|
||||
const uint16_t sample = scanRssiSparkline[(oldest + i) % CHFRSCANNER_RSSI_SPARKLINE_WIDTH];
|
||||
if (sample != 0 && sample < minRssi)
|
||||
minRssi = sample;
|
||||
}
|
||||
|
||||
rssi = scanRssiSparkline[(oldest + index) % CHFRSCANNER_RSSI_SPARKLINE_WIDTH];
|
||||
if (rssi == 0 || minRssi == SCAN_FAST_RSSI_MAX || rssi <= minRssi + 2)
|
||||
return 0;
|
||||
|
||||
// 48 RSSI units ~= 24 dB. This keeps quiet jitter low while strong hits pop.
|
||||
return (uint8_t)MIN(((uint32_t)(rssi - minRssi) * 5u + 24u) / 48u, 5u);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ScanFastResetState(void)
|
||||
{
|
||||
// Called on every scan (re)start, after a reception, and on each
|
||||
// wraparound to the start of the channel list / range. The noise
|
||||
// floor is re-warmed up from current conditions instead of carrying
|
||||
// stale calibration into a later sweep.
|
||||
scanFastNoiseFloor = SCAN_FAST_RSSI_MAX;
|
||||
scanFastPrevFrequency = 0;
|
||||
scanFastLastFullTuneCandidate = false;
|
||||
scanFastDisplayVfoValid = false;
|
||||
}
|
||||
|
||||
static void ScanFastResetNoiseFloor(void)
|
||||
{
|
||||
scanFastNoiseFloor = SCAN_FAST_RSSI_MAX;
|
||||
}
|
||||
|
||||
static uint16_t ScanFastReadRssi(void)
|
||||
{
|
||||
uint8_t guard = SCAN_FAST_GLITCH_GUARD_MAX;
|
||||
while (guard-- && BK4819_GetGlitchIndicator() >= SCAN_FAST_GLITCH_THRESHOLD)
|
||||
{
|
||||
SYSTICK_DelayUs(1);
|
||||
}
|
||||
|
||||
// Discard first read: after fast tuning the RSSI/AGC value may still be stale.
|
||||
BK4819_GetRSSI();
|
||||
return BK4819_GetRSSI();
|
||||
}
|
||||
|
||||
static void ScanFastTune(uint32_t frequency)
|
||||
{
|
||||
if (scanFastPrevFrequency == 0 ||
|
||||
((frequency < SCAN_FAST_HF_VHF_BOUNDARY_HZ) !=
|
||||
(scanFastPrevFrequency < SCAN_FAST_HF_VHF_BOUNDARY_HZ)))
|
||||
{
|
||||
BK4819_PickRXFilterPathBasedOnFrequency(frequency);
|
||||
}
|
||||
|
||||
scanFastPrevFrequency = frequency;
|
||||
BK4819_SetFrequency(frequency);
|
||||
BK4819_WriteRegister(BK4819_REG_30, 0);
|
||||
BK4819_WriteRegister(BK4819_REG_30, scanFastReg30);
|
||||
}
|
||||
|
||||
const VFO_Info_t *CHFRSCANNER_GetScanDisplayVfo(void)
|
||||
{
|
||||
if (!ScanFastEnabled() || !scanFastDisplayVfoValid || gScanStateDir == SCAN_OFF || FUNCTION_IsRx())
|
||||
return NULL;
|
||||
|
||||
return &scanFastDisplayVfo;
|
||||
}
|
||||
|
||||
static bool ScanFastUpdateDisplayVfo(uint16_t channel, uint32_t *frequency, ModulationMode_t *modulation)
|
||||
{
|
||||
ChannelScanDisplayInfo_t info;
|
||||
|
||||
if (!SETTINGS_FetchChannelScanDisplayInfo(channel, &info))
|
||||
{
|
||||
scanFastDisplayVfoValid = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
scanFastDisplayVfo = gEeprom.VfoInfo[gEeprom.RX_VFO];
|
||||
|
||||
scanFastDisplayVfo.CHANNEL_SAVE = channel;
|
||||
scanFastDisplayVfo.freq_config_RX = info.rx;
|
||||
scanFastDisplayVfo.freq_config_TX = info.tx;
|
||||
scanFastDisplayVfo.TX_OFFSET_FREQUENCY = info.offset;
|
||||
scanFastDisplayVfo.StepFrequency = info.stepFrequency;
|
||||
scanFastDisplayVfo.STEP_SETTING = info.stepSetting;
|
||||
scanFastDisplayVfo.Modulation = info.modulation;
|
||||
scanFastDisplayVfo.TX_OFFSET_FREQUENCY_DIRECTION = info.txOffsetFrequencyDirection;
|
||||
scanFastDisplayVfo.OUTPUT_POWER = info.outputPower;
|
||||
scanFastDisplayVfo.FrequencyReverse = info.frequencyReverse;
|
||||
scanFastDisplayVfo.CHANNEL_BANDWIDTH = info.channelBandwidth;
|
||||
scanFastDisplayVfo.BUSY_CHANNEL_LOCK = info.busyChannelLock;
|
||||
scanFastDisplayVfo.TX_LOCK = info.txLock;
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
scanFastDisplayVfo.DTMF_DECODING_ENABLE = info.dtmfDecodingEnable;
|
||||
#endif
|
||||
scanFastDisplayVfo.DTMF_PTT_ID_TX_MODE = info.dtmfPttIdTxMode;
|
||||
|
||||
if (!scanFastDisplayVfo.FrequencyReverse)
|
||||
{
|
||||
scanFastDisplayVfo.pRX = &scanFastDisplayVfo.freq_config_RX;
|
||||
scanFastDisplayVfo.pTX = &scanFastDisplayVfo.freq_config_TX;
|
||||
}
|
||||
else
|
||||
{
|
||||
scanFastDisplayVfo.pRX = &scanFastDisplayVfo.freq_config_TX;
|
||||
scanFastDisplayVfo.pTX = &scanFastDisplayVfo.freq_config_RX;
|
||||
}
|
||||
|
||||
scanFastDisplayVfoValid = true;
|
||||
|
||||
if (frequency)
|
||||
*frequency = info.rx.Frequency;
|
||||
if (modulation)
|
||||
*modulation = info.modulation;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint16_t ScanFastSaturatingAdd(uint16_t value, uint16_t add)
|
||||
{
|
||||
return (value > SCAN_FAST_RSSI_MAX - add) ? SCAN_FAST_RSSI_MAX : (uint16_t)(value + add);
|
||||
}
|
||||
|
||||
static uint16_t ScanFastSaturatingSub(uint16_t value, uint16_t sub)
|
||||
{
|
||||
return (value > sub) ? (uint16_t)(value - sub) : 0;
|
||||
}
|
||||
|
||||
static uint16_t ScanFastGetNoiseTrigger(void)
|
||||
{
|
||||
return ScanFastSaturatingAdd(scanFastNoiseFloor, SCAN_FAST_RSSI_MARGIN);
|
||||
}
|
||||
|
||||
static uint16_t ScanFastGetSquelchTrigger(void)
|
||||
{
|
||||
return ScanFastSaturatingSub(gRxVfo->SquelchOpenRSSIThresh, SCAN_FAST_SQUELCH_MARGIN);
|
||||
}
|
||||
|
||||
static bool ScanFastIsNearCandidate(uint16_t rssi)
|
||||
{
|
||||
const uint16_t rssiWithMargin = ScanFastSaturatingAdd(rssi, SCAN_FAST_WEAK_MARGIN);
|
||||
const uint16_t squelchTrigger = ScanFastGetSquelchTrigger();
|
||||
|
||||
if (scanFastNoiseFloor == SCAN_FAST_RSSI_MAX)
|
||||
return gRxVfo->SquelchOpenRSSIThresh > 0 &&
|
||||
rssiWithMargin >= gRxVfo->SquelchOpenRSSIThresh;
|
||||
|
||||
return rssiWithMargin >= ScanFastGetNoiseTrigger() &&
|
||||
rssiWithMargin >= squelchTrigger;
|
||||
}
|
||||
|
||||
static uint16_t ScanFastReadCandidateRssi(void)
|
||||
{
|
||||
uint16_t rssi = ScanFastReadRssi();
|
||||
|
||||
if (ScanFastIsNearCandidate(rssi))
|
||||
{
|
||||
SYSTICK_DelayUs(SCAN_FAST_RECHECK_DELAY_US);
|
||||
|
||||
const uint16_t retryRssi = ScanFastReadRssi();
|
||||
if (retryRssi > rssi)
|
||||
rssi = retryRssi;
|
||||
}
|
||||
|
||||
return rssi;
|
||||
}
|
||||
|
||||
static bool ScanFastIsCandidate(uint16_t rssi)
|
||||
{
|
||||
const uint16_t squelchTrigger = ScanFastGetSquelchTrigger();
|
||||
|
||||
if (scanFastNoiseFloor == SCAN_FAST_RSSI_MAX)
|
||||
{
|
||||
scanFastNoiseFloor = rssi;
|
||||
return gRxVfo->SquelchOpenRSSIThresh > 0 &&
|
||||
rssi >= gRxVfo->SquelchOpenRSSIThresh;
|
||||
}
|
||||
|
||||
const uint16_t noiseTrigger = ScanFastGetNoiseTrigger();
|
||||
const uint16_t rssiWithMargin = ScanFastSaturatingAdd(rssi, SCAN_FAST_WEAK_MARGIN);
|
||||
|
||||
if ((rssi >= noiseTrigger && rssi >= squelchTrigger) ||
|
||||
(rssiWithMargin >= noiseTrigger && rssiWithMargin >= squelchTrigger))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (rssi < scanFastNoiseFloor)
|
||||
scanFastNoiseFloor = rssi;
|
||||
else
|
||||
scanFastNoiseFloor = (uint16_t)((7u * scanFastNoiseFloor + rssi + 4u) >> 3);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ScanFastApplyChannelShape(ModulationMode_t modulation)
|
||||
{
|
||||
const bool modulationChanged = gRxVfo->Modulation != modulation;
|
||||
const bool bandwidthChanged = gRxVfo->CHANNEL_BANDWIDTH != BANDWIDTH_WIDE;
|
||||
|
||||
if (!modulationChanged && !bandwidthChanged)
|
||||
return;
|
||||
|
||||
gRxVfo->Modulation = modulation;
|
||||
gRxVfo->CHANNEL_BANDWIDTH = BANDWIDTH_WIDE;
|
||||
|
||||
if (modulationChanged)
|
||||
RADIO_SetModulation(modulation);
|
||||
|
||||
if (modulation == MODULATION_AM)
|
||||
{
|
||||
BK4819_SetFilterBandwidth(RADIO_GetAMFilterBandwidth(gRxVfo), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef ENABLE_AM_FIX
|
||||
BK4819_SetFilterBandwidth(BK4819_FILTER_BW_WIDE, true);
|
||||
#else
|
||||
BK4819_SetFilterBandwidth(BK4819_FILTER_BW_WIDE, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (modulationChanged)
|
||||
{
|
||||
// AM and FM use different demod/AGC profiles, so their RSSI
|
||||
// baselines are not directly comparable. Relearn the floor after
|
||||
// crossing that boundary instead of treating the next FM block as
|
||||
// a wall of candidates.
|
||||
ScanFastResetNoiseFloor();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN_SCAN_FASTER) && defined(ENABLE_SCAN_RANGES)
|
||||
static void ScanRangeFastRefineCandidate(uint16_t firstRssi)
|
||||
{
|
||||
const uint16_t step = gRxVfo->StepFrequency;
|
||||
if (step == 0 || step >= SCAN_FAST_FINE_STEP_LIMIT)
|
||||
return;
|
||||
|
||||
uint16_t maxSteps = SCAN_FAST_FINE_REFINE_SPAN / step;
|
||||
if (maxSteps == 0)
|
||||
maxSteps = 1;
|
||||
if (maxSteps > SCAN_FAST_FINE_REFINE_MAX)
|
||||
maxSteps = SCAN_FAST_FINE_REFINE_MAX;
|
||||
|
||||
uint16_t bestRssi = firstRssi;
|
||||
uint32_t bestFrequency = gRxVfo->freq_config_RX.Frequency;
|
||||
uint8_t fallingSteps = 0;
|
||||
|
||||
for (uint16_t i = 0; i < maxSteps; ++i)
|
||||
{
|
||||
const uint32_t prevRxFrequency = gRxVfo->pRX->Frequency;
|
||||
|
||||
gRxVfo->freq_config_RX.Frequency = ScanRangeNextFrequency();
|
||||
RADIO_ApplyOffset(gRxVfo);
|
||||
|
||||
const uint32_t freq = gRxVfo->pRX->Frequency;
|
||||
if ((gScanStateDir > 0 && freq < prevRxFrequency) ||
|
||||
(gScanStateDir < 0 && freq > prevRxFrequency))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ScanFastTune(freq);
|
||||
|
||||
const uint16_t rssi = ScanFastReadRssi();
|
||||
if (rssi > bestRssi)
|
||||
{
|
||||
bestRssi = rssi;
|
||||
bestFrequency = gRxVfo->freq_config_RX.Frequency;
|
||||
fallingSteps = 0;
|
||||
}
|
||||
else if (bestRssi > rssi && bestRssi - rssi >= SCAN_FAST_FINE_RSSI_DROP)
|
||||
{
|
||||
if (++fallingSteps >= 3)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gRxVfo->freq_config_RX.Frequency = bestFrequency;
|
||||
RADIO_ApplyOffset(gRxVfo);
|
||||
ScanFastTune(gRxVfo->pRX->Frequency);
|
||||
}
|
||||
|
||||
static scan_fast_result_t ScanRangeFastPrecheck(void)
|
||||
{
|
||||
if (!gScanRangeStart)
|
||||
return SCAN_FAST_DISABLED;
|
||||
|
||||
if (gRxVfo->SquelchOpenRSSIThresh == 0)
|
||||
return SCAN_FAST_DISABLED;
|
||||
|
||||
// Mute AF DAC during the precheck sweep: avoids audio glitches and
|
||||
// saves a few uA on each silent step. The bit is restored on the real
|
||||
// tune by RADIO_SetupRegisters() in NextFreqChannel().
|
||||
scanFastReg30 = BK4819_ReadRegister(BK4819_REG_30) & ~BK4819_REG_30_MASK_ENABLE_AF_DAC;
|
||||
|
||||
for (uint8_t i = 0; i < SCAN_FAST_PRECHECK_STEPS; ++i)
|
||||
{
|
||||
gRxVfo->freq_config_RX.Frequency = ScanRangeNextFrequency();
|
||||
RADIO_ApplyOffset(gRxVfo);
|
||||
|
||||
const uint32_t freq = gRxVfo->pRX->Frequency;
|
||||
|
||||
// Detect wraparound: scanning forward but the new freq is lower
|
||||
// than the previous one (or scanning backward but it's higher)
|
||||
// means the range has wrapped from stop back to start. Reset the
|
||||
// noise floor so the new pass re-warms up from current conditions.
|
||||
if (scanFastPrevFrequency != 0 &&
|
||||
((gScanStateDir > 0 && freq < scanFastPrevFrequency) ||
|
||||
(gScanStateDir < 0 && freq > scanFastPrevFrequency)))
|
||||
{
|
||||
ScanFastResetState();
|
||||
}
|
||||
|
||||
ScanFastTune(freq);
|
||||
|
||||
const uint16_t rssi = ScanFastReadCandidateRssi();
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_RSSI
|
||||
ScanRssiSparklinePush(rssi);
|
||||
#endif
|
||||
if (ScanFastIsCandidate(rssi))
|
||||
{
|
||||
ScanRangeFastRefineCandidate(rssi);
|
||||
return SCAN_FAST_CANDIDATE;
|
||||
}
|
||||
}
|
||||
|
||||
return SCAN_FAST_QUIET_BATCH;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
static bool MemChannelFastPrecheck(uint16_t channel)
|
||||
{
|
||||
uint32_t frequency;
|
||||
ModulationMode_t modulation;
|
||||
|
||||
if (gRxVfo->SquelchOpenRSSIThresh == 0)
|
||||
{
|
||||
scanFastLastFullTuneCandidate = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!ScanFastUpdateDisplayVfo(channel, &frequency, &modulation))
|
||||
{
|
||||
scanFastLastFullTuneCandidate = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
ScanFastApplyChannelShape(modulation);
|
||||
|
||||
// Mute AF DAC for the same reason as in ScanRangeFastPrecheck().
|
||||
scanFastReg30 = BK4819_ReadRegister(BK4819_REG_30) & ~BK4819_REG_30_MASK_ENABLE_AF_DAC;
|
||||
ScanFastTune(frequency);
|
||||
|
||||
const uint16_t rssi = ScanFastReadCandidateRssi();
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_RSSI
|
||||
ScanRssiSparklinePush(rssi);
|
||||
#endif
|
||||
|
||||
if (ScanFastIsCandidate(rssi))
|
||||
{
|
||||
scanFastLastFullTuneCandidate = true;
|
||||
return true; // signal detected: let the full tune path follow
|
||||
}
|
||||
|
||||
// No signal here: still mirror the probed frequency in the VFO so the
|
||||
// status line (channel name + frequency) keeps in sync as we skip.
|
||||
// RADIO_ConfigureChannel() will overwrite these values cleanly when a
|
||||
// candidate is eventually retained.
|
||||
scanFastLastFullTuneCandidate = false;
|
||||
gRxVfo->freq_config_RX.Frequency = frequency;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void AdvanceMemScanList(const bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
if (++currentScanList >= SCAN_NEXT_NUM)
|
||||
currentScanList = SCAN_NEXT_CHAN_SCANLIST1;
|
||||
}
|
||||
|
||||
static void SetMemScanProgressChannel(uint16_t channel)
|
||||
{
|
||||
gEeprom.MrChannel[ gEeprom.RX_VFO] = channel;
|
||||
gEeprom.ScreenChannel[gEeprom.RX_VFO] = channel;
|
||||
gRxVfo->CHANNEL_SAVE = channel;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN_RESUME_STATE) || defined(ENABLE_SCAN_RANGES)
|
||||
void CHFRSCANNER_ScanRange(void) {
|
||||
if (gScanRangeStart) {
|
||||
gScanRangeStart = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
gScanRangeStart = gTxVfo->pRX->Frequency;
|
||||
gScanRangeStop = gEeprom.VfoInfo[!gEeprom.TX_VFO].freq_config_RX.Frequency;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
ScanFastResetState();
|
||||
#endif
|
||||
if(gScanRangeStart > gScanRangeStop)
|
||||
SWAP(gScanRangeStart, gScanRangeStop);
|
||||
|
||||
ScanRangeSkipSync();
|
||||
}
|
||||
#endif
|
||||
|
||||
void CHFRSCANNER_Start(const bool storeBackupSettings, const int8_t scan_direction)
|
||||
{
|
||||
@@ -46,18 +758,30 @@ void CHFRSCANNER_Start(const bool storeBackupSettings, const int8_t scan_directi
|
||||
}
|
||||
|
||||
RADIO_SelectVfos();
|
||||
CHFRSCANNER_AbortActiveReception();
|
||||
|
||||
gNextMrChannel = gRxVfo->CHANNEL_SAVE;
|
||||
currentScanList = SCAN_NEXT_CHAN_SCANLIST1;
|
||||
gScanStateDir = scan_direction;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_RSSI
|
||||
ScanRssiSparklineReset();
|
||||
#endif
|
||||
ScanFastResetState();
|
||||
#endif
|
||||
|
||||
if (IS_MR_CHANNEL(gNextMrChannel))
|
||||
{
|
||||
{
|
||||
bool scanListChanged = false;
|
||||
|
||||
if(!RADIO_CheckValidList(gEeprom.SCAN_LIST_DEFAULT)) {
|
||||
RADIO_NextValidList();
|
||||
RADIO_NextValidList(1);
|
||||
scanListChanged = true;
|
||||
}
|
||||
|
||||
if (storeBackupSettings || scanListChanged)
|
||||
UI_MAIN_NotifyScanListChanged();
|
||||
|
||||
// channel mode
|
||||
if (storeBackupSettings) {
|
||||
initialFrqOrChan = gRxVfo->CHANNEL_SAVE;
|
||||
@@ -84,6 +808,16 @@ void CHFRSCANNER_Start(const bool storeBackupSettings, const int8_t scan_directi
|
||||
gScanPauseMode = false;
|
||||
}
|
||||
|
||||
void CHFRSCANNER_ManualResume(const int8_t scan_direction)
|
||||
{
|
||||
CHFRSCANNER_Start(false, scan_direction);
|
||||
|
||||
gScanPauseDelayIn_10ms = (gRxVfo->SquelchOpenRSSIThresh == 0)
|
||||
? scan_pause_delay_in_3_10ms
|
||||
: 1;
|
||||
gScheduleScanListen = false;
|
||||
}
|
||||
|
||||
/*
|
||||
void CHFRSCANNER_ContinueScanning(void)
|
||||
{
|
||||
@@ -110,6 +844,18 @@ void CHFRSCANNER_ContinueScanning(void)
|
||||
|
||||
void CHFRSCANNER_ContinueScanning(void)
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
if (scanFastLastFullTuneCandidate &&
|
||||
gCurrentFunction != FUNCTION_INCOMING &&
|
||||
!g_SquelchLost)
|
||||
{
|
||||
// A rejected full-tune candidate is just a false RSSI hit. Keep the
|
||||
// learned floor; resetting here can make the next channel blind when
|
||||
// it is the real signal, especially while scanning down.
|
||||
scanFastLastFullTuneCandidate = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gCurrentFunction == FUNCTION_INCOMING &&
|
||||
(IS_FREQ_CHANNEL(gNextMrChannel) || gCurrentCodeType == CODE_TYPE_OFF))
|
||||
{
|
||||
@@ -127,6 +873,15 @@ void CHFRSCANNER_ContinueScanning(void)
|
||||
|
||||
void CHFRSCANNER_Found(void)
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
// After a real reception the BK4819 AGC has shifted, biasing the next
|
||||
// few RSSI readings high. Reset the precheck state so it warms up from
|
||||
// the current noise floor instead of carrying stale calibration into
|
||||
// the post-reception scan, which otherwise turns nearly every channel
|
||||
// into a CANDIDATE and erases the speed gain.
|
||||
ScanFastResetState();
|
||||
#endif
|
||||
|
||||
if (gEeprom.SCAN_RESUME_MODE > 80) {
|
||||
if (!gScanPauseMode) {
|
||||
gScanPauseDelayIn_10ms = scan_pause_delay_in_5_10ms * (gEeprom.SCAN_RESUME_MODE - 80) * 5;
|
||||
@@ -199,6 +954,9 @@ void CHFRSCANNER_Stop(void)
|
||||
}
|
||||
|
||||
gScanStateDir = SCAN_OFF;
|
||||
#if defined(ENABLE_FEAT_F4HWN_SCAN_FASTER) && defined(ENABLE_FEAT_F4HWN_SCAN_RSSI)
|
||||
ScanRssiSparklineReset();
|
||||
#endif
|
||||
|
||||
const uint32_t chFr = gScanKeepResult ? lastFoundFrqOrChan : initialFrqOrChan;
|
||||
const bool channelChanged = chFr != initialFrqOrChan;
|
||||
@@ -234,11 +992,46 @@ static void NextFreqChannel(void)
|
||||
{
|
||||
#ifdef ENABLE_SCAN_RANGES
|
||||
if(gScanRangeStart) {
|
||||
gRxVfo->freq_config_RX.Frequency = APP_SetFreqByStepAndLimits(gRxVfo, gScanStateDir, gScanRangeStart, gScanRangeStop);
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
if (ScanFastEnabled())
|
||||
{
|
||||
const scan_fast_result_t fastResult = ScanRangeFastPrecheck();
|
||||
|
||||
if (fastResult == SCAN_FAST_QUIET_BATCH)
|
||||
{
|
||||
scanFastLastFullTuneCandidate = false;
|
||||
gScanPauseDelayIn_10ms = 1;
|
||||
gUpdateDisplay = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (fastResult == SCAN_FAST_DISABLED)
|
||||
{
|
||||
scanFastLastFullTuneCandidate = false;
|
||||
gRxVfo->freq_config_RX.Frequency = ScanRangeNextFrequency();
|
||||
}
|
||||
else
|
||||
{
|
||||
scanFastLastFullTuneCandidate = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
scanFastLastFullTuneCandidate = false;
|
||||
gRxVfo->freq_config_RX.Frequency = ScanRangeNextFrequency();
|
||||
}
|
||||
#else
|
||||
gRxVfo->freq_config_RX.Frequency = ScanRangeNextFrequency();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
scanFastLastFullTuneCandidate = false;
|
||||
#endif
|
||||
gRxVfo->freq_config_RX.Frequency = APP_SetFrequencyByStep(gRxVfo, gScanStateDir);
|
||||
}
|
||||
|
||||
RADIO_ApplyOffset(gRxVfo);
|
||||
RADIO_ConfigureSquelchAndOutputPower(gRxVfo);
|
||||
@@ -339,23 +1132,58 @@ static void NextMemChannel(void)
|
||||
}
|
||||
|
||||
if (!enabled || chan == 0xFFFF)
|
||||
{
|
||||
{
|
||||
const uint16_t searchStart = gNextMrChannel;
|
||||
chan = RADIO_FindNextChannel(gNextMrChannel + gScanStateDir, gScanStateDir, true, gEeprom.SCAN_LIST_DEFAULT);
|
||||
if (chan == 0xFFFF)
|
||||
{ // no valid channel found
|
||||
{ // no valid channel found -> wrapping back to the first channel
|
||||
chan = MR_CHANNEL_FIRST;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
// Wraparound: re-warm the precheck noise floor on the new pass
|
||||
// so it tracks current RF conditions instead of an EMA that
|
||||
// accumulated drift over the previous full sweep.
|
||||
ScanFastResetState();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
else if ((gScanStateDir > 0 && chan < searchStart) ||
|
||||
(gScanStateDir < 0 && chan > searchStart))
|
||||
{
|
||||
// RADIO_FindNextChannel() wraps internally, so 0xFFFF is not
|
||||
// returned on a normal full-sweep wrap. Detect that transition
|
||||
// here and restart the RSSI floor learning for the new pass.
|
||||
ScanFastResetState();
|
||||
}
|
||||
#endif
|
||||
|
||||
gNextMrChannel = chan;
|
||||
|
||||
//sprintf(str, "----> Chan %d\n", chan + 1);
|
||||
//LogUart(str);
|
||||
}
|
||||
|
||||
if (gNextMrChannel != prev_chan)
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
SetMemScanProgressChannel(gNextMrChannel);
|
||||
|
||||
if (ScanFastEnabled() && !MemChannelFastPrecheck(gNextMrChannel))
|
||||
{
|
||||
gScanPauseDelayIn_10ms = 1;
|
||||
gUpdateDisplay = true;
|
||||
AdvanceMemScanList(enabled);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gNextMrChannel != prev_chan
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
|| scanFastLastFullTuneCandidate
|
||||
#endif
|
||||
)
|
||||
{
|
||||
#ifndef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
gEeprom.MrChannel[ gEeprom.RX_VFO] = gNextMrChannel;
|
||||
gEeprom.ScreenChannel[gEeprom.RX_VFO] = gNextMrChannel;
|
||||
#endif
|
||||
|
||||
RADIO_ConfigureChannel(gEeprom.RX_VFO, VFO_CONFIGURE_RELOAD);
|
||||
RADIO_SetupRegisters(true);
|
||||
@@ -369,7 +1197,11 @@ static void NextMemChannel(void)
|
||||
gScanPauseDelayIn_10ms = scan_pause_delay_in_3_10ms;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
AdvanceMemScanList(enabled);
|
||||
#else
|
||||
if (enabled)
|
||||
if (++currentScanList >= SCAN_NEXT_NUM)
|
||||
currentScanList = SCAN_NEXT_CHAN_SCANLIST1; // back round we go
|
||||
#endif
|
||||
}
|
||||
+26
-1
@@ -4,6 +4,8 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "radio.h"
|
||||
|
||||
// scan direction, if not equal SCAN_OFF indicates
|
||||
// that we are in a process of scanning channels/frequencies
|
||||
extern int8_t gScanStateDir;
|
||||
@@ -13,16 +15,39 @@ extern bool gScanPauseMode;
|
||||
#ifdef ENABLE_SCAN_RANGES
|
||||
extern uint32_t gScanRangeStart;
|
||||
extern uint32_t gScanRangeStop;
|
||||
#if defined(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE) && ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE
|
||||
extern DCS_CodeType_t gScanRangeCssType;
|
||||
extern uint8_t gScanRangeCssCode;
|
||||
#endif
|
||||
|
||||
bool CHFRSCANNER_ExcludeCurrentScanRange(void);
|
||||
bool CHFRSCANNER_HasScanRangeExcludedOrdinal(uint32_t first_ordinal, uint32_t last_ordinal);
|
||||
#if defined(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE) && ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE
|
||||
void CHFRSCANNER_UpdateCssDetection(void);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void CHFRSCANNER_Found(void);
|
||||
void CHFRSCANNER_Stop(void);
|
||||
void CHFRSCANNER_Start(const bool storeBackupSettings, const int8_t scan_direction);
|
||||
void CHFRSCANNER_ManualResume(const int8_t scan_direction);
|
||||
void CHFRSCANNER_ContinueScanning(void);
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
const VFO_Info_t *CHFRSCANNER_GetScanDisplayVfo(void);
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_RSSI
|
||||
bool CHFRSCANNER_HasScanRssiSparkline(void);
|
||||
uint8_t CHFRSCANNER_GetScanRssiSparklineLevel(uint8_t index);
|
||||
#define CHFRSCANNER_RSSI_SPARKLINE_WIDTH 24u
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN_RESUME_STATE) || defined(ENABLE_SCAN_RANGES)
|
||||
void CHFRSCANNER_ScanRange(void);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
extern uint32_t lastFoundFrqOrChan;
|
||||
extern uint32_t lastFoundFrqOrChanOld;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "functions.h"
|
||||
#include "misc.h"
|
||||
#include "settings.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
void COMMON_KeypadLockToggle()
|
||||
@@ -29,6 +30,11 @@ void COMMON_SwitchVFOs()
|
||||
#endif
|
||||
gEeprom.TX_VFO ^= 1;
|
||||
|
||||
if (gInputBoxIndex > 0) {
|
||||
gInputBoxIndex = 0;
|
||||
gHasVfoBackup = false;
|
||||
}
|
||||
|
||||
if (gEeprom.CROSS_BAND_RX_TX != CROSS_BAND_OFF)
|
||||
gEeprom.CROSS_BAND_RX_TX = gEeprom.TX_VFO + 1;
|
||||
if (gEeprom.DUAL_WATCH != DUAL_WATCH_OFF)
|
||||
@@ -49,6 +55,11 @@ void COMMON_SwitchVFOMode()
|
||||
if (gEeprom.VFO_OPEN)
|
||||
#endif
|
||||
{
|
||||
if (gInputBoxIndex > 0) {
|
||||
gInputBoxIndex = 0;
|
||||
gHasVfoBackup = false;
|
||||
}
|
||||
|
||||
if (IS_MR_CHANNEL(gTxVfo->CHANNEL_SAVE))
|
||||
{ // swap to frequency mode
|
||||
gEeprom.ScreenChannel[gEeprom.TX_VFO] = gEeprom.FreqChannel[gEeprom.TX_VFO];
|
||||
|
||||
+5
-1
@@ -212,6 +212,10 @@ DTMF_CallMode_t DTMF_CheckGroupCall(const char *pMsg, const unsigned int size)
|
||||
}
|
||||
#endif
|
||||
|
||||
void DTMF_clear_input_box_memory() {
|
||||
memset(gDTMF_RX_live, 0, sizeof(gDTMF_RX_live));
|
||||
}
|
||||
|
||||
void DTMF_clear_input_box(void)
|
||||
{
|
||||
memset(gDTMF_InputBox, 0, sizeof(gDTMF_InputBox));
|
||||
@@ -470,7 +474,7 @@ void DTMF_Reply(void)
|
||||
if (pString == NULL)
|
||||
return;
|
||||
|
||||
Delay = (gEeprom.DTMF_PRELOAD_TIME < 200) ? 200 : gEeprom.DTMF_PRELOAD_TIME;
|
||||
Delay = MAX(gEeprom.DTMF_PRELOAD_TIME, 200);
|
||||
|
||||
if (gEeprom.DTMF_SIDE_TONE)
|
||||
{ // the user will also hear the transmitted tones
|
||||
|
||||
@@ -82,6 +82,7 @@ extern DTMF_ReplyState_t gDTMF_ReplyState;
|
||||
|
||||
bool DTMF_ValidateCodes(char *pCode, const unsigned int size);
|
||||
char DTMF_GetCharacter(const unsigned int code);
|
||||
void DTMF_clear_input_box_memory(void);
|
||||
void DTMF_clear_input_box(void);
|
||||
void DTMF_Append(const char code);
|
||||
void DTMF_Reply(void);
|
||||
|
||||
+19
-13
@@ -51,20 +51,26 @@ static inline void Flashlight_Toggle(){ GPIO_TogglePin(GPIO_PIN_FLASHLIGHT); }
|
||||
|
||||
void ACTION_FlashLight(void)
|
||||
{
|
||||
switch (gFlashLightState) {
|
||||
case FLASHLIGHT_OFF:
|
||||
gFlashLightState++;
|
||||
Flashlight_TurnOn();
|
||||
break;
|
||||
case FLASHLIGHT_ON:
|
||||
case FLASHLIGHT_BLINK:
|
||||
gFlashLightState++;
|
||||
break;
|
||||
case FLASHLIGHT_SOS:
|
||||
default:
|
||||
gFlashLightState = 0;
|
||||
Flashlight_TurnOff();
|
||||
// switch (gFlashLightState) {
|
||||
// case FLASHLIGHT_OFF:
|
||||
// Flashlight_TurnOn();
|
||||
// break;
|
||||
// case FLASHLIGHT_ON:
|
||||
// case FLASHLIGHT_BLINK:
|
||||
// break;
|
||||
// case FLASHLIGHT_SOS:
|
||||
// default:
|
||||
// Flashlight_TurnOff();
|
||||
// }
|
||||
|
||||
if(gFlashLightState == FLASHLIGHT_OFF) {
|
||||
Flashlight_TurnOn();
|
||||
}
|
||||
else if (gFlashLightState == FLASHLIGHT_SOS) {
|
||||
Flashlight_TurnOff();
|
||||
}
|
||||
|
||||
gFlashLightState = (gFlashLightState + 1) % 4;
|
||||
}
|
||||
#else
|
||||
void ACTION_FlashLight(void)
|
||||
|
||||
+121
-94
@@ -32,10 +32,6 @@
|
||||
#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[FM_CHANNELS_MAX];
|
||||
bool gFmRadioMode;
|
||||
uint8_t gFmRadioCountdown_500ms;
|
||||
@@ -44,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;
|
||||
|
||||
|
||||
@@ -99,6 +94,11 @@ int FM_ConfigureChannelState(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FM_SetFrequency(void)
|
||||
{
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
|
||||
}
|
||||
|
||||
void FM_TurnOff(void)
|
||||
{
|
||||
gFmRadioMode = false;
|
||||
@@ -124,10 +124,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();
|
||||
@@ -144,17 +161,19 @@ void FM_Tune(uint16_t Frequency, int8_t Step, bool bFlag)
|
||||
|
||||
if (!bFlag) {
|
||||
Frequency += Step;
|
||||
if (Frequency < BK1080_GetFreqLoLimit(gEeprom.FM_Band))
|
||||
Frequency = BK1080_GetFreqHiLimit(gEeprom.FM_Band);
|
||||
else if (Frequency > BK1080_GetFreqHiLimit(gEeprom.FM_Band))
|
||||
Frequency = BK1080_GetFreqLoLimit(gEeprom.FM_Band);
|
||||
Frequency = FM_WrapFrequency(Frequency);
|
||||
|
||||
gEeprom.FM_FrequencyPlaying = Frequency;
|
||||
}
|
||||
|
||||
gFM_ScanState = Step;
|
||||
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
|
||||
FM_SetFrequency();
|
||||
}
|
||||
|
||||
void FM_AudioPathOn(void) {
|
||||
AUDIO_AudioPathOn();
|
||||
gEnableSpeaker = true;
|
||||
}
|
||||
|
||||
void FM_PlayAndUpdate(void)
|
||||
@@ -167,79 +186,58 @@ void FM_PlayAndUpdate(void)
|
||||
}
|
||||
|
||||
FM_ConfigureChannelState();
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
|
||||
FM_SetFrequency();
|
||||
SETTINGS_SaveFM();
|
||||
|
||||
gFmPlayCountdown_10ms = 0;
|
||||
gScheduleFM = false;
|
||||
gAskToSave = false;
|
||||
|
||||
AUDIO_AudioPathOn();
|
||||
|
||||
gEnableSpeaker = true;
|
||||
BACKLIGHT_TurnOn();
|
||||
FM_AudioPathOn();
|
||||
}
|
||||
|
||||
int FM_CheckFrequencyLock(uint16_t Frequency, uint16_t LowerLimit)
|
||||
{
|
||||
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)
|
||||
@@ -267,6 +265,7 @@ static void Key_DIGITS(KEY_Code_t Key, uint8_t state)
|
||||
}
|
||||
|
||||
INPUTBOX_Append(Key);
|
||||
gKeyInputCountdown = key_input_timeout_500ms;
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
|
||||
@@ -282,6 +281,8 @@ static void Key_DIGITS(KEY_Code_t Key, uint8_t state)
|
||||
uint32_t Frequency;
|
||||
|
||||
gInputBoxIndex = 0;
|
||||
gKeyInputCountdown = 1;
|
||||
|
||||
Frequency = StrToUL(INPUTBOX_GetAscii());
|
||||
|
||||
if (Frequency < BK1080_GetFreqLoLimit(gEeprom.FM_Band) || BK1080_GetFreqHiLimit(gEeprom.FM_Band) < Frequency) {
|
||||
@@ -295,7 +296,7 @@ static void Key_DIGITS(KEY_Code_t Key, uint8_t state)
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
#endif
|
||||
gEeprom.FM_FrequencyPlaying = gEeprom.FM_SelectedFrequency;
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
|
||||
FM_SetFrequency();
|
||||
gRequestSaveFM = true;
|
||||
return;
|
||||
}
|
||||
@@ -304,6 +305,8 @@ static void Key_DIGITS(KEY_Code_t Key, uint8_t state)
|
||||
uint8_t Channel;
|
||||
|
||||
gInputBoxIndex = 0;
|
||||
gKeyInputCountdown = 1;
|
||||
|
||||
Channel = ((gInputBox[0] * 10) + gInputBox[1]) - 1;
|
||||
|
||||
if (State == STATE_MR_MODE) {
|
||||
@@ -313,7 +316,7 @@ static void Key_DIGITS(KEY_Code_t Key, uint8_t state)
|
||||
#endif
|
||||
gEeprom.FM_SelectedChannel = Channel;
|
||||
gEeprom.FM_FrequencyPlaying = gFM_Channels[Channel];
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
|
||||
FM_SetFrequency();
|
||||
gRequestSaveFM = true;
|
||||
return;
|
||||
}
|
||||
@@ -346,8 +349,7 @@ static void Key_FUNC(KEY_Code_t Key, uint8_t state)
|
||||
bool autoScan = gWasFKeyPressed || (state == BUTTON_EVENT_HELD);
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
gWasFKeyPressed = false;
|
||||
gUpdateStatus = true;
|
||||
HideFKeyIcon();
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
|
||||
switch (Key) {
|
||||
@@ -369,13 +371,21 @@ static void Key_FUNC(KEY_Code_t Key, uint8_t state)
|
||||
gEeprom.FM_IsMrMode = !gEeprom.FM_IsMrMode;
|
||||
|
||||
if (!FM_ConfigureChannelState()) {
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
|
||||
FM_SetFrequency();
|
||||
gRequestSaveFM = true;
|
||||
}
|
||||
else
|
||||
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;
|
||||
@@ -389,8 +399,14 @@ static void Key_FUNC(KEY_Code_t Key, uint8_t state)
|
||||
|
||||
static void Key_EXIT(uint8_t state)
|
||||
{
|
||||
if (state != BUTTON_EVENT_SHORT)
|
||||
return;
|
||||
if (gInputBoxIndex) {
|
||||
if (state != BUTTON_EVENT_SHORT)
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (state != BUTTON_EVENT_PRESSED)
|
||||
return;
|
||||
}
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
@@ -406,6 +422,7 @@ static void Key_EXIT(uint8_t state)
|
||||
}
|
||||
else {
|
||||
gInputBox[--gInputBoxIndex] = 10;
|
||||
gKeyInputCountdown = key_input_timeout_500ms;
|
||||
|
||||
if (gInputBoxIndex) {
|
||||
if (gInputBoxIndex != 1) {
|
||||
@@ -437,14 +454,25 @@ static void Key_EXIT(uint8_t state)
|
||||
|
||||
static void Key_MENU(uint8_t state)
|
||||
{
|
||||
if (state != BUTTON_EVENT_SHORT)
|
||||
if (state == BUTTON_EVENT_HELD) {
|
||||
ACTION_Handle(KEY_MENU, true, true);
|
||||
return;
|
||||
|
||||
}
|
||||
else if (state != BUTTON_EVENT_SHORT) {
|
||||
return;
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
HideFKeyIcon();
|
||||
|
||||
if (gFM_ScanState == FM_SCAN_OFF) {
|
||||
if (gInputBoxIndex) {
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gEeprom.FM_IsMrMode) {
|
||||
if (gAskToSave) {
|
||||
gFM_Channels[gFM_ChannelPosition] = gEeprom.FM_FrequencyPlaying;
|
||||
@@ -457,7 +485,7 @@ static void Key_MENU(uint8_t state)
|
||||
gFM_Channels[gEeprom.FM_SelectedChannel] = 0xFFFF;
|
||||
|
||||
FM_ConfigureChannelState();
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
|
||||
FM_SetFrequency();
|
||||
|
||||
gRequestSaveFM = true;
|
||||
}
|
||||
@@ -481,6 +509,8 @@ static void Key_MENU(uint8_t state)
|
||||
|
||||
static void Key_UP_DOWN(uint8_t state, int8_t Step)
|
||||
{
|
||||
HideFKeyIcon();
|
||||
|
||||
if (state == BUTTON_EVENT_PRESSED) {
|
||||
if (gInputBoxIndex) {
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
@@ -492,6 +522,10 @@ 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, FM_CHANNELS_MAX - 1);
|
||||
@@ -520,10 +554,7 @@ static void Key_UP_DOWN(uint8_t state, int8_t Step)
|
||||
else {
|
||||
uint16_t Frequency = gEeprom.FM_SelectedFrequency + Step;
|
||||
|
||||
if (Frequency < BK1080_GetFreqLoLimit(gEeprom.FM_Band))
|
||||
Frequency = BK1080_GetFreqHiLimit(gEeprom.FM_Band);
|
||||
else if (Frequency > BK1080_GetFreqHiLimit(gEeprom.FM_Band))
|
||||
Frequency = BK1080_GetFreqLoLimit(gEeprom.FM_Band);
|
||||
Frequency = FM_WrapFrequency(Frequency);
|
||||
|
||||
gEeprom.FM_FrequencyPlaying = Frequency;
|
||||
gEeprom.FM_SelectedFrequency = gEeprom.FM_FrequencyPlaying;
|
||||
@@ -532,7 +563,7 @@ static void Key_UP_DOWN(uint8_t state, int8_t Step)
|
||||
gRequestSaveFM = true;
|
||||
|
||||
Bail:
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
|
||||
FM_SetFrequency();
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
}
|
||||
@@ -552,16 +583,8 @@ void FM_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
Key_MENU(state);
|
||||
break;
|
||||
case KEY_UP:
|
||||
if(gEeprom.SET_NAV == 0)
|
||||
Key_UP_DOWN(state, -1);
|
||||
else
|
||||
Key_UP_DOWN(state, 1);
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
if(gEeprom.SET_NAV == 0)
|
||||
Key_UP_DOWN(state, 1);
|
||||
else
|
||||
Key_UP_DOWN(state, -1);
|
||||
Key_UP_DOWN(state, Key == KEY_UP ? 1 : -1);
|
||||
break;
|
||||
case KEY_EXIT:
|
||||
Key_EXIT(state);
|
||||
@@ -572,9 +595,14 @@ void FM_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
case KEY_PTT:
|
||||
GENERIC_Key_PTT(bKeyPressed);
|
||||
break;
|
||||
default:
|
||||
if (!bKeyHeld && bKeyPressed)
|
||||
case KEY_SIDE1:
|
||||
case KEY_SIDE2:
|
||||
if (state != BUTTON_EVENT_PRESSED) {
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
HideFKeyIcon();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -589,20 +617,19 @@ void FM_Play(void)
|
||||
if (!gEeprom.FM_IsMrMode)
|
||||
gEeprom.FM_SelectedFrequency = gEeprom.FM_FrequencyPlaying;
|
||||
|
||||
AUDIO_AudioPathOn();
|
||||
gEnableSpeaker = true;
|
||||
BACKLIGHT_TurnOn();
|
||||
FM_AudioPathOn();
|
||||
|
||||
GUI_SelectNextDisplay(DISPLAY_FM);
|
||||
return;
|
||||
goto Display;
|
||||
}
|
||||
|
||||
if (gFM_ChannelPosition < FM_CHANNELS_MAX)
|
||||
gFM_Channels[gFM_ChannelPosition++] = gEeprom.FM_FrequencyPlaying;
|
||||
|
||||
|
||||
if (gFM_ChannelPosition >= FM_CHANNELS_MAX) {
|
||||
FM_PlayAndUpdate();
|
||||
GUI_SelectNextDisplay(DISPLAY_FM);
|
||||
return;
|
||||
|
||||
goto Display;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -611,6 +638,7 @@ void FM_Play(void)
|
||||
else
|
||||
FM_Tune(gEeprom.FM_FrequencyPlaying, gFM_ScanState, false);
|
||||
|
||||
Display:
|
||||
GUI_SelectNextDisplay(DISPLAY_FM);
|
||||
}
|
||||
|
||||
@@ -625,9 +653,8 @@ void FM_Start(void)
|
||||
// Disable UHF LNA, enable VHF LNA
|
||||
BK4819_PickRXFilterPathBasedOnFrequency(10320000); // 103.2 MHz < 280 MHz
|
||||
|
||||
AUDIO_AudioPathOn();
|
||||
FM_AudioPathOn();
|
||||
|
||||
gEnableSpeaker = true;
|
||||
gUpdateStatus = true;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
|
||||
+1361
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,57 @@
|
||||
/* 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_FOXHUNT_H
|
||||
#define APP_FOXHUNT_H
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
|
||||
#include "keyboard_state.h"
|
||||
|
||||
#include "../bitmaps.h"
|
||||
#include "../board.h"
|
||||
#include "py32f0xx.h"
|
||||
#include "../driver/backlight.h"
|
||||
#include "../driver/bk4819-regs.h"
|
||||
#include "../driver/bk4819.h"
|
||||
#include "../driver/gpio.h"
|
||||
#include "../driver/keyboard.h"
|
||||
#include "../driver/py25q16.h"
|
||||
#include "../driver/st7565.h"
|
||||
#include "../driver/system.h"
|
||||
#include "../driver/systick.h"
|
||||
#include "../external/printf/printf.h"
|
||||
#include "../font.h"
|
||||
#include "../helper/battery.h"
|
||||
#include "../misc.h"
|
||||
#include "../radio.h"
|
||||
#include "../settings.h"
|
||||
#include "../ui/battery.h"
|
||||
#include "../ui/helper.h"
|
||||
#include "../ui/ui.h"
|
||||
#include "../audio.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
// Entry point wired as an assignable side-key action (see action.c).
|
||||
void ACTION_FoxHunt(void);
|
||||
|
||||
// Self-contained modal loop that owns the screen and keypad until EXIT.
|
||||
void APP_RunFoxHunt(void);
|
||||
|
||||
#endif // ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
#endif // APP_FOXHUNT_H
|
||||
+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
|
||||
|
||||
+272
-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,10 +81,7 @@ static void toggle_chan_scanlist(void)
|
||||
|
||||
if(!IS_MR_CHANNEL(gTxVfo->CHANNEL_SAVE)) {
|
||||
#ifdef ENABLE_SCAN_RANGES
|
||||
gScanRangeStart = gScanRangeStart ? 0 : gTxVfo->pRX->Frequency;
|
||||
gScanRangeStop = gEeprom.VfoInfo[!gEeprom.TX_VFO].freq_config_RX.Frequency;
|
||||
if(gScanRangeStart > gScanRangeStop)
|
||||
SWAP(gScanRangeStart, gScanRangeStop);
|
||||
CHFRSCANNER_ScanRange();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
@@ -69,20 +92,22 @@ static void toggle_chan_scanlist(void)
|
||||
if(att->exclude == true)
|
||||
{
|
||||
att->exclude = false;
|
||||
return;
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
gFlagResetVfos = true;
|
||||
}
|
||||
@@ -91,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
|
||||
|
||||
@@ -108,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:
|
||||
@@ -120,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) {
|
||||
@@ -186,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:
|
||||
@@ -196,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:
|
||||
@@ -205,26 +219,27 @@ 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();
|
||||
|
||||
if (gScanStateDir != SCAN_OFF) {
|
||||
// Stop the channel/frequency scan before saving the RX mode for Close Call.
|
||||
gScanKeepResult = false;
|
||||
CHFRSCANNER_Stop();
|
||||
}
|
||||
|
||||
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];
|
||||
@@ -253,79 +268,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_NotifyScanListChanged();
|
||||
} 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;
|
||||
@@ -362,6 +401,8 @@ void channelMove(uint16_t Channel)
|
||||
#endif
|
||||
|
||||
RADIO_ConfigureChannel(gEeprom.TX_VFO, gVfoConfigureMode);
|
||||
|
||||
SETTINGS_SaveVfoIndices();
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -396,14 +437,10 @@ void channelMoveSwitch(void) {
|
||||
if (gInputBoxIndex == 4) {
|
||||
gInputBoxIndex = 0;
|
||||
gKeyInputCountdown = 1;
|
||||
|
||||
channelMove(Channel - 1);
|
||||
SETTINGS_SaveVfoIndices();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
channelMove(Channel - 1);
|
||||
SETTINGS_SaveVfoIndices();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,10 +454,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;
|
||||
@@ -470,6 +506,7 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
if (value == 0)
|
||||
{
|
||||
gEeprom.SCAN_LIST_DEFAULT = MR_CHANNELS_LIST + 1;
|
||||
UI_MAIN_NotifyScanListChanged();
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
SETTINGS_WriteCurrentState();
|
||||
#endif
|
||||
@@ -479,31 +516,35 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
/* 01 .. MR_CHANNELS_LIST */
|
||||
if (value <= MR_CHANNELS_LIST)
|
||||
{
|
||||
if (RADIO_CheckValidList(value))
|
||||
{
|
||||
/* Requested scan list is valid */
|
||||
gEeprom.SCAN_LIST_DEFAULT = value;
|
||||
}
|
||||
else
|
||||
gEeprom.SCAN_LIST_DEFAULT = value;
|
||||
|
||||
if (!RADIO_CheckValidList(value))
|
||||
{
|
||||
/* Requested scan list is empty or invalid:
|
||||
jump to the next valid scan list */
|
||||
gEeprom.SCAN_LIST_DEFAULT = value;
|
||||
RADIO_NextValidList();
|
||||
jump to the next valid scan list */
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
RADIO_NextValidList(1);
|
||||
}
|
||||
UI_MAIN_NotifyScanListChanged();
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
SETTINGS_WriteCurrentState();
|
||||
#endif
|
||||
}
|
||||
|
||||
gInputBoxIndex = 0;
|
||||
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
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;
|
||||
|
||||
@@ -541,7 +582,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;
|
||||
@@ -549,11 +596,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;
|
||||
}
|
||||
|
||||
@@ -610,6 +654,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)) {
|
||||
@@ -631,95 +676,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)
|
||||
@@ -731,15 +758,30 @@ static void MAIN_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
if (bKeyPressed) { // long press MENU key
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
// Exclude channel
|
||||
// Exclude current scan entry
|
||||
if(gScanStateDir != SCAN_OFF)
|
||||
{
|
||||
if(FUNCTION_IsRx() || gScanPauseDelayIn_10ms > 9)
|
||||
{
|
||||
#ifdef ENABLE_SCAN_RANGES
|
||||
if(gScanRangeStart && !IS_MR_CHANNEL(gNextMrChannel))
|
||||
{
|
||||
if(CHFRSCANNER_ExcludeCurrentScanRange())
|
||||
{
|
||||
UI_MAIN_NotifyScanProgressDataChanged();
|
||||
lastFoundFrqOrChan = lastFoundFrqOrChanOld;
|
||||
CHFRSCANNER_ContinueScanning();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
ChannelAttributes_t *att = MR_GetChannelAttributes(lastFoundFrqOrChan);
|
||||
att->exclude = true;
|
||||
|
||||
MR_SaveChannelAttributesToFlash(lastFoundFrqOrChan, att);
|
||||
UI_MAIN_NotifyScanProgressDataChanged();
|
||||
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
gFlagResetVfos = true;
|
||||
@@ -761,7 +803,6 @@ static void MAIN_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
}
|
||||
|
||||
gWasFKeyPressed = false;
|
||||
gUpdateStatus = true;
|
||||
|
||||
ACTION_Handle(KEY_MENU, bKeyPressed, bKeyHeld);
|
||||
@@ -772,6 +813,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;
|
||||
|
||||
@@ -782,7 +826,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;
|
||||
@@ -790,10 +838,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;
|
||||
@@ -803,13 +847,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;
|
||||
|
||||
@@ -819,6 +856,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;
|
||||
@@ -841,12 +891,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
|
||||
{
|
||||
@@ -859,7 +903,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;
|
||||
@@ -881,6 +924,12 @@ static void MAIN_Key_STAR(bool bKeyPressed, bool bKeyHeld)
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (gScanStateDir != SCAN_OFF) {
|
||||
// Stop the channel/frequency scan before saving the RX mode for the CTCSS/DCS scan.
|
||||
gScanKeepResult = false;
|
||||
CHFRSCANNER_Stop();
|
||||
}
|
||||
|
||||
// scan the CTCSS/DCS code
|
||||
gBackup_CROSS_BAND_RX_TX = gEeprom.CROSS_BAND_RX_TX;
|
||||
gEeprom.CROSS_BAND_RX_TX = CROSS_BAND_OFF;
|
||||
@@ -895,18 +944,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
|
||||
@@ -918,10 +962,12 @@ static void MAIN_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
|
||||
uint16_t Channel = gEeprom.ScreenChannel[gEeprom.TX_VFO];
|
||||
|
||||
if (bKeyHeld || !bKeyPressed) { // key held or released
|
||||
if (gInputBoxIndex > 0)
|
||||
return; // leave if input box active
|
||||
if (gInputBoxIndex > 0) {
|
||||
gInputBoxIndex = 0;
|
||||
gHasVfoBackup = false;
|
||||
}
|
||||
|
||||
if (bKeyHeld || !bKeyPressed) { // key held or released
|
||||
if (!bKeyPressed) {
|
||||
if (!bKeyHeld || IS_FREQ_CHANNEL(Channel))
|
||||
return;
|
||||
@@ -934,10 +980,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;
|
||||
}
|
||||
|
||||
@@ -990,9 +1032,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;
|
||||
}
|
||||
@@ -1038,16 +1078,8 @@ void MAIN_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
MAIN_Key_MENU(bKeyPressed, bKeyHeld);
|
||||
break;
|
||||
case KEY_UP:
|
||||
if(gEeprom.SET_NAV == 0)
|
||||
MAIN_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
|
||||
else
|
||||
MAIN_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
if(gEeprom.SET_NAV == 0)
|
||||
MAIN_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
|
||||
else
|
||||
MAIN_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
|
||||
MAIN_Key_UP_DOWN(bKeyPressed, bKeyHeld, Key == KEY_UP ? 1 : -1);
|
||||
break;
|
||||
case KEY_EXIT:
|
||||
MAIN_Key_EXIT(bKeyPressed, bKeyHeld);
|
||||
|
||||
+310
-152
@@ -42,9 +42,6 @@
|
||||
#include "ui/menu.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
|
||||
#endif
|
||||
|
||||
uint8_t gUnlockAllTxConfCnt;
|
||||
|
||||
@@ -225,6 +222,13 @@ int MENU_GetLimits(uint8_t menu_id, int32_t *pMin, int32_t *pMax)
|
||||
*pMax = ARRAY_SIZE(gSubMenu_RX_TX) - 1;
|
||||
break;
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN) && defined(ENABLE_FEAT_F4HWN_LOGO_SAV)
|
||||
case MENU_SET_SAV:
|
||||
//*pMin = 0;
|
||||
*pMax = SET_SAV_LEN - 1;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
#ifdef ENABLE_AM_FIX
|
||||
case MENU_AM_FIX:
|
||||
@@ -311,7 +315,7 @@ int MENU_GetLimits(uint8_t menu_id, int32_t *pMin, int32_t *pMax)
|
||||
|
||||
case MENU_MIC:
|
||||
//*pMin = 0;
|
||||
*pMax = 4;
|
||||
*pMax = 8;
|
||||
break;
|
||||
|
||||
case MENU_LIST_CH:
|
||||
@@ -422,17 +426,28 @@ int MENU_GetLimits(uint8_t menu_id, int32_t *pMin, int32_t *pMax)
|
||||
#endif
|
||||
case MENU_SET_LCK:
|
||||
//*pMin = 0;
|
||||
*pMax = ARRAY_SIZE(gSubMenu_SET_LCK) - 1;
|
||||
*pMax = SET_LCK_LEN - 1;
|
||||
break;
|
||||
case MENU_SET_MET:
|
||||
case MENU_SET_GUI:
|
||||
//*pMin = 0;
|
||||
*pMax = ARRAY_SIZE(gSubMenu_SET_MET) - 1;
|
||||
break;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
case MENU_SET_SCN:
|
||||
//*pMin = 0;
|
||||
*pMax = ARRAY_SIZE(gSubMenu_SET_SCN) - 1;
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
case MENU_SET_AUD:
|
||||
//*pMin = 0;
|
||||
*pMax = ARRAY_SIZE(gSubMenu_SET_AUD) - 1;
|
||||
if(gTxVfo->Modulation == MODULATION_AM)
|
||||
*pMax = ARRAY_SIZE(gSubMenu_SET_AUD_AM) - 1;
|
||||
else if (gTxVfo->Modulation == MODULATION_USB)
|
||||
*pMax = 0;
|
||||
else
|
||||
*pMax = ARRAY_SIZE(gSubMenu_SET_AUD_FM) - 1;
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
@@ -455,6 +470,28 @@ int MENU_GetLimits(uint8_t menu_id, int32_t *pMin, int32_t *pMax)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
case MENU_VOL: {
|
||||
// SysInf paginates:
|
||||
// page 0 = identity,
|
||||
// page 1 = build date/time,
|
||||
// page 2 = battery,
|
||||
// +1 if F4HWN_MEM (Flash/SRAM),
|
||||
// +2 if F4HWN_QRCODE (Code QR + Wiki QR).
|
||||
int32_t vol_max = 0;
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
vol_max += 2;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_MEM
|
||||
vol_max += 1;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_QRCODE
|
||||
vol_max += 2;
|
||||
#endif
|
||||
if (vol_max == 0) return -1;
|
||||
*pMax = vol_max;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
@@ -483,6 +520,9 @@ void MENU_AcceptSetting(void)
|
||||
case MENU_SQL:
|
||||
gEeprom.SQUELCH_LEVEL = gSubMenuSelection;
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
gSquelchLevelOriginal = 10;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case MENU_STEP:
|
||||
@@ -589,7 +629,7 @@ void MENU_AcceptSetting(void)
|
||||
|
||||
case MENU_MEM_NAME:
|
||||
for (int i = 9; i >= 0; i--) {
|
||||
if (edit[i] != ' ' && edit[i] != '_' && edit[i] != 0x00 && edit[i] != 0xff)
|
||||
if (edit[i] != ' ' && edit[i] != 0x00 && edit[i] != 0xff)
|
||||
break;
|
||||
edit[i] = ' ';
|
||||
}
|
||||
@@ -685,7 +725,7 @@ void MENU_AcceptSetting(void)
|
||||
|
||||
case MENU_LIST_CH:
|
||||
gTxVfo->SCANLIST_PARTICIPATION = gSubMenuSelection;
|
||||
SETTINGS_UpdateChannel(gTxVfo->CHANNEL_SAVE, gTxVfo, true, false, true);
|
||||
SETTINGS_UpdateChannel(gTxVfo->CHANNEL_SAVE, gTxVfo, true);
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
gFlagResetVfos = true;
|
||||
return;
|
||||
@@ -712,7 +752,7 @@ void MENU_AcceptSetting(void)
|
||||
|
||||
case MENU_COMPAND:
|
||||
gTxVfo->Compander = gSubMenuSelection;
|
||||
SETTINGS_UpdateChannel(gTxVfo->CHANNEL_SAVE, gTxVfo, true, false, true);
|
||||
SETTINGS_UpdateChannel(gTxVfo->CHANNEL_SAVE, gTxVfo, true);
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
gFlagResetVfos = true;
|
||||
// gRequestSaveChannel = 1;
|
||||
@@ -773,7 +813,7 @@ void MENU_AcceptSetting(void)
|
||||
case MENU_D_LIVE_DEC:
|
||||
gSetting_live_DTMF_decoder = gSubMenuSelection;
|
||||
gDTMF_RX_live_timeout = 0;
|
||||
memset(gDTMF_RX_live, 0, sizeof(gDTMF_RX_live));
|
||||
DTMF_clear_input_box_memory();
|
||||
if (!gSetting_live_DTMF_decoder)
|
||||
BK4819_DisableDTMF();
|
||||
gFlagReconfigureVfos = true;
|
||||
@@ -824,7 +864,7 @@ void MENU_AcceptSetting(void)
|
||||
#endif
|
||||
|
||||
case MENU_DEL_CH:
|
||||
SETTINGS_UpdateChannel(gSubMenuSelection, NULL, false, false, true);
|
||||
SETTINGS_UpdateChannel(gSubMenuSelection, NULL, false);
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
gFlagResetVfos = true;
|
||||
return;
|
||||
@@ -962,10 +1002,19 @@ void MENU_AcceptSetting(void)
|
||||
case MENU_SET_GUI:
|
||||
gSetting_set_gui = gSubMenuSelection;
|
||||
break;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
case MENU_SET_SCN:
|
||||
gSetting_set_scn = gSubMenuSelection;
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
case MENU_SET_AUD:
|
||||
gSetting_set_audio = gSubMenuSelection;
|
||||
RADIO_SetModulation(gRxVfo->Modulation);
|
||||
if(gTxVfo->Modulation == MODULATION_AM)
|
||||
gSetting_set_audio_am = gSubMenuSelection;
|
||||
else if (gTxVfo->Modulation == MODULATION_FM)
|
||||
gSetting_set_audio_fm = gSubMenuSelection;
|
||||
|
||||
RADIO_SetModulation(gTxVfo->Modulation);
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
@@ -988,6 +1037,11 @@ void MENU_AcceptSetting(void)
|
||||
case MENU_SET_TMR:
|
||||
gSetting_set_tmr = gSubMenuSelection;
|
||||
break;
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
case MENU_SET_SAV:
|
||||
gSetting_set_sav = gSubMenuSelection;
|
||||
break;
|
||||
#endif
|
||||
case MENU_TX_LOCK:
|
||||
gTxVfo->TX_LOCK = gSubMenuSelection;
|
||||
gRequestSaveChannel = 1;
|
||||
@@ -1021,6 +1075,11 @@ void MENU_ShowCurrentSetting(void)
|
||||
gSubMenuSelection = gEeprom.SQUELCH_LEVEL;
|
||||
break;
|
||||
|
||||
case MENU_VOL:
|
||||
// SysInf is paginated; always start on page 0 (identity).
|
||||
gSubMenuSelection = 0;
|
||||
break;
|
||||
|
||||
case MENU_STEP:
|
||||
gSubMenuSelection = FREQUENCY_GetSortedIdxFromStepIdx(gTxVfo->STEP_SETTING);
|
||||
break;
|
||||
@@ -1417,9 +1476,19 @@ void MENU_ShowCurrentSetting(void)
|
||||
case MENU_SET_GUI:
|
||||
gSubMenuSelection = gSetting_set_gui;
|
||||
break;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
case MENU_SET_SCN:
|
||||
gSubMenuSelection = gSetting_set_scn;
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
case MENU_SET_AUD:
|
||||
gSubMenuSelection = gSetting_set_audio;
|
||||
if(gTxVfo->Modulation == MODULATION_AM)
|
||||
gSubMenuSelection = gSetting_set_audio_am;
|
||||
else if (gTxVfo->Modulation == MODULATION_USB)
|
||||
gSubMenuSelection = 0;
|
||||
else
|
||||
gSubMenuSelection = gSetting_set_audio_fm;
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
@@ -1440,6 +1509,11 @@ void MENU_ShowCurrentSetting(void)
|
||||
case MENU_SET_TMR:
|
||||
gSubMenuSelection = gSetting_set_tmr;
|
||||
break;
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
case MENU_SET_SAV:
|
||||
gSubMenuSelection = gSetting_set_sav;
|
||||
break;
|
||||
#endif
|
||||
case MENU_TX_LOCK:
|
||||
gSubMenuSelection = gTxVfo->TX_LOCK;
|
||||
break;
|
||||
@@ -1450,6 +1524,29 @@ void MENU_ShowCurrentSetting(void)
|
||||
}
|
||||
}
|
||||
|
||||
static KEY_Code_t edit_last_key = 255;
|
||||
static uint8_t edit_char_index = 0;
|
||||
|
||||
static const char* const char_map[10] = {
|
||||
" 0", // KEY_0
|
||||
".,-()@/\\+=*#<>[]~1", // KEY_1
|
||||
"abc2", // KEY_2
|
||||
"def3", // KEY_3
|
||||
"ghi4", // KEY_4
|
||||
"jkl5", // KEY_5
|
||||
"mno6", // KEY_6
|
||||
"pqrs7", // KEY_7
|
||||
"tuv8", // KEY_8
|
||||
"wxyz9" // KEY_9
|
||||
};
|
||||
|
||||
static bool MENU_IsEditingName() {
|
||||
return !gCssBackgroundScan
|
||||
&& UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME
|
||||
&& gIsInSubMenu
|
||||
&& edit_index >= 0;
|
||||
}
|
||||
|
||||
static void MENU_Key_0_to_9(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
uint8_t Offset;
|
||||
@@ -1457,33 +1554,55 @@ static void MENU_Key_0_to_9(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
int32_t Max;
|
||||
uint16_t Value = 0;
|
||||
|
||||
if (bKeyHeld || !bKeyPressed)
|
||||
if (!bKeyPressed)
|
||||
return;
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME && edit_index >= 0)
|
||||
{ // currently editing the channel name
|
||||
if (edit_index >= 10)
|
||||
return;
|
||||
|
||||
if (edit_index < 10)
|
||||
uint8_t key_idx = Key - KEY_0;
|
||||
|
||||
if (bKeyHeld)
|
||||
{
|
||||
if (Key <= KEY_9)
|
||||
edit[edit_index] = '0' + key_idx;
|
||||
edit_last_key = 255;
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Key != edit_last_key)
|
||||
{
|
||||
edit_last_key = Key;
|
||||
edit_char_index = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
edit_char_index++;
|
||||
if (char_map[key_idx][edit_char_index] == '\0')
|
||||
{
|
||||
edit[edit_index] = '0' + Key - KEY_0;
|
||||
|
||||
if (++edit_index >= 10)
|
||||
{ // exit edit
|
||||
gFlagAcceptSetting = false;
|
||||
gAskForConfirmation = 1;
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
edit_char_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
char c = char_map[key_idx][edit_char_index];
|
||||
if (edit_is_uppercase && c >= 'a' && c <= 'z')
|
||||
{
|
||||
c -= 32;
|
||||
}
|
||||
edit[edit_index] = c;
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bKeyHeld)
|
||||
return;
|
||||
|
||||
INPUTBOX_Append(Key);
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
@@ -1550,15 +1669,17 @@ static void MENU_Key_0_to_9(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
return;
|
||||
}
|
||||
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_MEM_CH ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_DEL_CH ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_1_CALL ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_S_PRI_CH_1 ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_S_PRI_CH_2 ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME)
|
||||
{ // enter 3-digit channel number
|
||||
const int m = UI_MENU_GetCurrentMenuId();
|
||||
|
||||
if (gInputBoxIndex < 3)
|
||||
if (m == MENU_MEM_CH ||
|
||||
m == MENU_DEL_CH ||
|
||||
m == MENU_1_CALL ||
|
||||
m == MENU_S_PRI_CH_1 ||
|
||||
m == MENU_S_PRI_CH_2 ||
|
||||
m == MENU_MEM_NAME)
|
||||
{ // enter 4-digit channel number
|
||||
|
||||
if (gInputBoxIndex < 4)
|
||||
{
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
@@ -1569,7 +1690,8 @@ static void MENU_Key_0_to_9(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
|
||||
gInputBoxIndex = 0;
|
||||
|
||||
Value = ((gInputBox[0] * 100) + (gInputBox[1] * 10) + gInputBox[2]) - 1;
|
||||
//Value = ((gInputBox[0] * 100) + (gInputBox[1] * 10) + gInputBox[2]) - 1;
|
||||
Value = (((gInputBox[0] * 10 + gInputBox[1]) * 10 + gInputBox[2]) * 10 + gInputBox[3]) - 1;
|
||||
|
||||
if (IS_MR_CHANNEL(Value))
|
||||
{
|
||||
@@ -1626,6 +1748,53 @@ static void MENU_Key_0_to_9(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
|
||||
static void MENU_Key_EXIT(bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if (MENU_IsEditingName())
|
||||
{
|
||||
if (!bKeyPressed)
|
||||
{
|
||||
if (bKeyHeld)
|
||||
return; // release after a long press, keep editing
|
||||
|
||||
if (edit_index == 0)
|
||||
goto Skip;
|
||||
|
||||
if (edit_index > 0)
|
||||
{ // step back one character while editing the channel name
|
||||
edit_index--;
|
||||
edit_last_key = 255;
|
||||
gAskForConfirmation = 0;
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bKeyHeld)
|
||||
{ // wait to see if the user wants a short exit or a long backspace
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
Skip:
|
||||
|
||||
/* Backlight related menus set full brightness. Set it back to the configured value,
|
||||
just in case we are editing from one of them. */
|
||||
BACKLIGHT_TurnOn();
|
||||
|
||||
gAskForConfirmation = 0;
|
||||
gIsInSubMenu = false;
|
||||
gInputBoxIndex = 0;
|
||||
gFlagRefreshSetting = true;
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_CANCEL;
|
||||
#endif
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (bKeyHeld || !bKeyPressed)
|
||||
return;
|
||||
|
||||
@@ -1633,29 +1802,22 @@ static void MENU_Key_EXIT(bool bKeyPressed, bool bKeyHeld)
|
||||
|
||||
if (!gCssBackgroundScan)
|
||||
{
|
||||
/* Backlight related menus set full brightness. Set it back to the configured value,
|
||||
just in case we are exiting from one of them. */
|
||||
BACKLIGHT_TurnOn();
|
||||
|
||||
if (gIsInSubMenu)
|
||||
{
|
||||
if (gInputBoxIndex == 0 || UI_MENU_GetCurrentMenuId() != MENU_OFFSET)
|
||||
{
|
||||
gAskForConfirmation = 0;
|
||||
gIsInSubMenu = false;
|
||||
gInputBoxIndex = 0;
|
||||
gFlagRefreshSetting = true;
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_CANCEL;
|
||||
#endif
|
||||
goto Skip;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Backlight related menus set full brightness. Set it back to the configured value,
|
||||
just in case we are exiting from one of them. */
|
||||
BACKLIGHT_TurnOn();
|
||||
|
||||
gInputBox[--gInputBoxIndex] = 10;
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
}
|
||||
|
||||
// ***********************
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1686,27 +1848,29 @@ static void MENU_Key_EXIT(bool bKeyPressed, bool bKeyHeld)
|
||||
|
||||
static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld)
|
||||
{
|
||||
if (bKeyHeld || !bKeyPressed)
|
||||
if (!bKeyPressed || (bKeyHeld && (!MENU_IsEditingName() || gAskForConfirmation)))
|
||||
return;
|
||||
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
|
||||
if (!gIsInSubMenu)
|
||||
{
|
||||
const int m = UI_MENU_GetCurrentMenuId();
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
if (UI_MENU_GetCurrentMenuId() != MENU_SCR)
|
||||
if (m != MENU_SCR)
|
||||
gAnotherVoiceID = MenuList[gMenuCursor].voice_id;
|
||||
#endif
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_UPCODE
|
||||
|| UI_MENU_GetCurrentMenuId() == MENU_DWCODE
|
||||
if (m == MENU_UPCODE
|
||||
|| m == MENU_DWCODE
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
|| UI_MENU_GetCurrentMenuId() == MENU_ANI_ID
|
||||
|| m == MENU_ANI_ID
|
||||
#endif
|
||||
)
|
||||
return;
|
||||
#if 1
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_DEL_CH || UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME)
|
||||
if (m == MENU_DEL_CH || m == MENU_MEM_NAME)
|
||||
if (!RADIO_CheckValidChannel(gSubMenuSelection, false, 0))
|
||||
return; // invalid channel
|
||||
#endif
|
||||
@@ -1714,7 +1878,7 @@ static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld)
|
||||
gAskForConfirmation = 0;
|
||||
gIsInSubMenu = true;
|
||||
|
||||
// if (UI_MENU_GetCurrentMenuId() != MENU_D_LIST)
|
||||
// if (m != MENU_D_LIST)
|
||||
{
|
||||
gInputBoxIndex = 0;
|
||||
edit_index = -1;
|
||||
@@ -1732,12 +1896,18 @@ static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld)
|
||||
|
||||
SETTINGS_FetchChannelName(edit, gSubMenuSelection);
|
||||
|
||||
// pad the channel name out with '_'
|
||||
edit_index = strlen(edit);
|
||||
while (edit_index < 10)
|
||||
edit[edit_index++] = '_';
|
||||
edit[edit_index] = 0;
|
||||
// pad the channel name out with ' '
|
||||
size_t len = strlen(edit);
|
||||
if (len < 10)
|
||||
{
|
||||
memset(edit + len, ' ', 10 - len);
|
||||
edit[10] = '\0';
|
||||
}
|
||||
|
||||
edit_index = 0; // 'edit_index' is going to be used as the cursor position
|
||||
edit_last_key = 255;
|
||||
edit_char_index = 0;
|
||||
edit_is_uppercase = false;
|
||||
|
||||
// make a copy so we can test for change when exiting the menu item
|
||||
memcpy(edit_original, edit, sizeof(edit_original));
|
||||
@@ -1747,9 +1917,14 @@ static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld)
|
||||
else
|
||||
if (edit_index >= 0 && edit_index < 10)
|
||||
{ // editing the channel name characters
|
||||
edit_last_key = 255;
|
||||
|
||||
if (++edit_index < 10)
|
||||
return; // next char
|
||||
if (bKeyHeld) {
|
||||
edit_index = 10;
|
||||
}
|
||||
else if (++edit_index < 10) {
|
||||
return;
|
||||
}
|
||||
|
||||
// exit
|
||||
gFlagAcceptSetting = false;
|
||||
@@ -1765,10 +1940,12 @@ static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld)
|
||||
|
||||
if (gIsInSubMenu)
|
||||
{
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_RESET ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_MEM_CH ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_DEL_CH ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME)
|
||||
const int m = UI_MENU_GetCurrentMenuId();
|
||||
|
||||
if (m == MENU_RESET ||
|
||||
m == MENU_MEM_CH ||
|
||||
m == MENU_DEL_CH ||
|
||||
m == MENU_MEM_NAME)
|
||||
{
|
||||
switch (gAskForConfirmation)
|
||||
{
|
||||
@@ -1781,7 +1958,7 @@ static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld)
|
||||
|
||||
UI_DisplayMenu();
|
||||
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_RESET)
|
||||
if (m == MENU_RESET)
|
||||
{
|
||||
#ifdef ENABLE_VOICE
|
||||
AUDIO_SetVoiceID(0, VOICE_ID_CONFIRM);
|
||||
@@ -1823,7 +2000,7 @@ static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld)
|
||||
|
||||
static void MENU_Key_STAR(const bool bKeyPressed, const bool bKeyHeld)
|
||||
{
|
||||
if (bKeyHeld || !bKeyPressed)
|
||||
if (!bKeyPressed)
|
||||
return;
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
@@ -1833,13 +2010,8 @@ static void MENU_Key_STAR(const bool bKeyPressed, const bool bKeyHeld)
|
||||
|
||||
if (edit_index < 10)
|
||||
{
|
||||
edit[edit_index] = '-';
|
||||
|
||||
if (++edit_index >= 10)
|
||||
{ // exit edit
|
||||
gFlagAcceptSetting = false;
|
||||
gAskForConfirmation = 1;
|
||||
}
|
||||
edit[edit_index] = !bKeyHeld ? '-' : '*';
|
||||
edit_last_key = 255;
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
}
|
||||
@@ -1847,6 +2019,9 @@ static void MENU_Key_STAR(const bool bKeyPressed, const bool bKeyHeld)
|
||||
return;
|
||||
}
|
||||
|
||||
if (bKeyHeld)
|
||||
return;
|
||||
|
||||
RADIO_SelectVfos();
|
||||
|
||||
#ifdef ENABLE_NOAA
|
||||
@@ -1855,7 +2030,8 @@ static void MENU_Key_STAR(const bool bKeyPressed, const bool bKeyHeld)
|
||||
if (gRxVfo->Modulation == MODULATION_FM)
|
||||
#endif
|
||||
{
|
||||
if ((UI_MENU_GetCurrentMenuId() == MENU_R_CTCS || UI_MENU_GetCurrentMenuId() == MENU_R_DCS) && gIsInSubMenu)
|
||||
const int m = UI_MENU_GetCurrentMenuId();
|
||||
if ((m == MENU_R_CTCS || m == MENU_R_DCS) && gIsInSubMenu)
|
||||
{ // scan CTCSS or DCS to find the tone/code of the incoming signal
|
||||
if (!SCANNER_IsScanning())
|
||||
MENU_StartCssScan();
|
||||
@@ -1876,11 +2052,23 @@ static void MENU_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
uint16_t Channel;
|
||||
bool bCheckScanList;
|
||||
|
||||
if (!bKeyPressed)
|
||||
return;
|
||||
|
||||
if (!bKeyHeld) {
|
||||
gInputBoxIndex = 0;
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
}
|
||||
|
||||
if (!gEeprom.SET_NAV && gIsInSubMenu) {
|
||||
Direction = -Direction;
|
||||
}
|
||||
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME && gIsInSubMenu && edit_index >= 0)
|
||||
{ // change the character
|
||||
if (bKeyPressed && edit_index < 10 && Direction != 0)
|
||||
if (edit_index < 10 && Direction != 0)
|
||||
{
|
||||
const char unwanted[] = "$%&!\"':;?^`|{}";
|
||||
const char unwanted[] = "$%&!\"':;?^`|{}_";
|
||||
char c = edit[edit_index] + Direction;
|
||||
unsigned int i = 0;
|
||||
while (i < sizeof(unwanted) && c >= 32 && c <= 126)
|
||||
@@ -1892,25 +2080,13 @@ static void MENU_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
}
|
||||
}
|
||||
edit[edit_index] = (c < 32) ? 126 : (c > 126) ? 32 : c;
|
||||
edit_last_key = 255;
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bKeyHeld)
|
||||
{
|
||||
if (!bKeyPressed)
|
||||
return;
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
gInputBoxIndex = 0;
|
||||
}
|
||||
else
|
||||
if (!bKeyPressed)
|
||||
return;
|
||||
|
||||
if (SCANNER_IsScanning()) {
|
||||
return;
|
||||
}
|
||||
@@ -1923,9 +2099,11 @@ static void MENU_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
|
||||
if (UI_MENU_GetCurrentMenuId() != MENU_ABR
|
||||
&& UI_MENU_GetCurrentMenuId() != MENU_ABR_MIN
|
||||
&& UI_MENU_GetCurrentMenuId() != MENU_ABR_MAX
|
||||
const int m = UI_MENU_GetCurrentMenuId();
|
||||
|
||||
if (m != MENU_ABR
|
||||
&& m != MENU_ABR_MIN
|
||||
&& m != MENU_ABR_MAX
|
||||
&& gEeprom.BACKLIGHT_TIME == 0) // backlight always off and not in the backlight menu
|
||||
{
|
||||
BACKLIGHT_TurnOff();
|
||||
@@ -1951,8 +2129,10 @@ static void MENU_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
}
|
||||
|
||||
VFO = 0;
|
||||
|
||||
const int m = UI_MENU_GetCurrentMenuId();
|
||||
|
||||
switch (UI_MENU_GetCurrentMenuId())
|
||||
switch (m)
|
||||
{
|
||||
case MENU_DEL_CH:
|
||||
case MENU_1_CALL:
|
||||
@@ -1968,19 +2148,21 @@ static void MENU_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
return;
|
||||
}
|
||||
|
||||
if(UI_MENU_GetCurrentMenuId() == MENU_S_PRI_CH_1 || UI_MENU_GetCurrentMenuId() == MENU_S_PRI_CH_2)
|
||||
if(m == MENU_S_PRI_CH_1 || m == MENU_S_PRI_CH_2)
|
||||
{
|
||||
static int16_t last;
|
||||
|
||||
if(Direction > 0 && gSubMenuSelection == MR_CHANNELS_MAX)
|
||||
{
|
||||
gSubMenuSelection = -1;
|
||||
last = -1;
|
||||
}
|
||||
else if(Direction < 0 && gSubMenuSelection == MR_CHANNELS_MAX)
|
||||
{
|
||||
gSubMenuSelection = MR_CHANNELS_MAX;
|
||||
last = MR_CHANNELS_MAX;
|
||||
if(gSubMenuSelection == MR_CHANNELS_MAX) {
|
||||
if(Direction > 0)
|
||||
{
|
||||
gSubMenuSelection = -1;
|
||||
last = -1;
|
||||
}
|
||||
else if(Direction < 0)
|
||||
{
|
||||
gSubMenuSelection = MR_CHANNELS_MAX;
|
||||
last = MR_CHANNELS_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
Channel = RADIO_FindNextChannel(gSubMenuSelection + Direction, Direction, bCheckScanList, VFO);
|
||||
@@ -2016,42 +2198,15 @@ void MENU_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
switch (Key)
|
||||
{
|
||||
case KEY_0:
|
||||
case KEY_1:
|
||||
case KEY_2:
|
||||
case KEY_3:
|
||||
case KEY_4:
|
||||
case KEY_5:
|
||||
case KEY_6:
|
||||
case KEY_7:
|
||||
case KEY_8:
|
||||
case KEY_9:
|
||||
case KEY_0...KEY_9:
|
||||
MENU_Key_0_to_9(Key, bKeyPressed, bKeyHeld);
|
||||
break;
|
||||
case KEY_MENU:
|
||||
MENU_Key_MENU(bKeyPressed, bKeyHeld);
|
||||
break;
|
||||
case KEY_UP:
|
||||
if(gEeprom.SET_NAV == 0) {
|
||||
if(gIsInSubMenu)
|
||||
MENU_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
|
||||
else
|
||||
MENU_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
|
||||
}
|
||||
else {
|
||||
MENU_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
|
||||
}
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
if(gEeprom.SET_NAV == 0) {
|
||||
if(gIsInSubMenu)
|
||||
MENU_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
|
||||
else
|
||||
MENU_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
|
||||
}
|
||||
else {
|
||||
MENU_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
|
||||
}
|
||||
MENU_Key_UP_DOWN(bKeyPressed, bKeyHeld, Key == KEY_UP ? 1 : -1);
|
||||
break;
|
||||
case KEY_EXIT:
|
||||
MENU_Key_EXIT(bKeyPressed, bKeyHeld);
|
||||
@@ -2062,19 +2217,20 @@ void MENU_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
case KEY_F:
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME && edit_index >= 0)
|
||||
{ // currently editing the channel name
|
||||
if (!bKeyHeld && bKeyPressed)
|
||||
if (!bKeyPressed)
|
||||
break;
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
if (edit_index < 10)
|
||||
{
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
if (edit_index < 10)
|
||||
{
|
||||
edit[edit_index] = ' ';
|
||||
if (++edit_index >= 10)
|
||||
{ // exit edit
|
||||
gFlagAcceptSetting = false;
|
||||
gAskForConfirmation = 1;
|
||||
}
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
}
|
||||
if (bKeyHeld)
|
||||
edit[edit_index] = '#';
|
||||
|
||||
edit_is_uppercase = !edit_is_uppercase;
|
||||
edit_last_key = 255;
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2092,11 +2248,13 @@ void MENU_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
|
||||
if (gScreenToDisplay == DISPLAY_MENU)
|
||||
{
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_VOL ||
|
||||
const int m = UI_MENU_GetCurrentMenuId();
|
||||
|
||||
if (m == MENU_VOL ||
|
||||
#ifdef ENABLE_F_CAL_MENU
|
||||
UI_MENU_GetCurrentMenuId() == MENU_F_CALI ||
|
||||
m == MENU_F_CALI ||
|
||||
#endif
|
||||
UI_MENU_GetCurrentMenuId() == MENU_BATCAL)
|
||||
m == MENU_BATCAL)
|
||||
{
|
||||
gMenuCountdown = menu_timeout_long_500ms;
|
||||
}
|
||||
|
||||
-187
@@ -1,187 +0,0 @@
|
||||
/*
|
||||
* REGA Alam and Test
|
||||
* ==================
|
||||
*
|
||||
* This bit of code is to implement the REGA test and alarm functions.
|
||||
* This function sends out a fixed ZVEI tone sequence for testing or alarm purposes.
|
||||
* Further information: https://www.rega.ch/en/our-missions/sites-and-infrastructure/emergency-radio
|
||||
*
|
||||
* There are two REGA ACTIONS: Test and Alarm.
|
||||
* The Test action sends out a fixed ZVEI tone sequence for testing purposes.
|
||||
* The Alarm action sends out a fixed ZVEI tone sequence for alarm purposes.
|
||||
*
|
||||
* These actions can be assigned to a key in the settings menu.
|
||||
*
|
||||
* The Test/Alarm function will perform the following actions:
|
||||
* - Set the radio to transmit mode
|
||||
* - Wait 100ms to allow the radio to switch to transmit mode and stabilize the tramsmitter
|
||||
* - Send out the ZVEI tone sequence
|
||||
* - Wait 100ms to allow the radio to finish transmitting
|
||||
* - Set the radio back to receive mode
|
||||
*
|
||||
* The ZVEI tone squence for alarm is: 21414
|
||||
* The ZVEI tone squence for test is: 21301
|
||||
*
|
||||
* To save space the two tone sequences are stored as precalculated register values.
|
||||
* This avoids the need to calculate the register values at runtime.
|
||||
* They are hardcoded as array of 32bit integers in the rega.h file.
|
||||
*
|
||||
* Copyright 2025 Markus Bärtschi
|
||||
* https://github.com/markusb
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "rega.h"
|
||||
#include "radio.h"
|
||||
#include "action.h"
|
||||
#include "misc.h"
|
||||
#include "settings.h"
|
||||
#include "functions.h"
|
||||
#include "driver/bk4819.h"
|
||||
#include "driver/st7565.h"
|
||||
#include "driver/system.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "bsp/dp32g030/gpio.h"
|
||||
#include "ui/helper.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
|
||||
const uint16_t rega_alarm_tones[5] = {
|
||||
1160, // 2 1160Hz
|
||||
1060, // 1 1060Hz
|
||||
1400, // 4 1400Hz
|
||||
1060, // 1 1060Hz
|
||||
1400, // 4 1400Hz
|
||||
};
|
||||
const uint16_t rega_test_tones[5] = {
|
||||
1160, // 2 1160Hz
|
||||
1060, // 1 1060Hz
|
||||
1260, // 3 1260Hz
|
||||
2400, // 0 2400Hz
|
||||
1060, // 1 1060Hz
|
||||
};
|
||||
|
||||
// Global variable to pass the message to the display
|
||||
char rega_message[16];
|
||||
|
||||
// Transmit the ZVEI tone sequence for alarm
|
||||
void ACTION_RegaAlarm()
|
||||
{
|
||||
const char message[16] = "REGA Alarm";
|
||||
REGA_TransmitZvei(rega_alarm_tones,message);
|
||||
}
|
||||
|
||||
// Tranmit the ZVEI tone sequence for test
|
||||
void ACTION_RegaTest()
|
||||
{
|
||||
const char message[16] = "REGA Test";
|
||||
REGA_TransmitZvei(rega_test_tones,message);
|
||||
}
|
||||
|
||||
// Display the REGA message on the screen
|
||||
void UI_DisplayREGA()
|
||||
{
|
||||
UI_DisplayClear();
|
||||
UI_DisplayPopup(rega_message);
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
|
||||
// Transmit a ZVEI tone sequence on the REGA frequency
|
||||
// Configures the radio on VFO A with the required parameters
|
||||
// tones: array of 5 ZVEI tones
|
||||
// message: message to display on the screen
|
||||
void REGA_TransmitZvei(const uint16_t tones[],const char message[])
|
||||
{
|
||||
// Copy the message text
|
||||
strncpy(rega_message,message,16);
|
||||
// Trigger the display
|
||||
gScreenToDisplay = DISPLAY_REGA;
|
||||
|
||||
// Flash the green LED twice
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, true);
|
||||
SYSTEM_DelayMs(70);
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false);
|
||||
SYSTEM_DelayMs(50);
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, true);
|
||||
SYSTEM_DelayMs(70);
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false);
|
||||
|
||||
// Select VFO A
|
||||
gEeprom.TX_VFO = 0;
|
||||
gEeprom.ScreenChannel[gEeprom.TX_VFO] = gEeprom.FreqChannel[gEeprom.TX_VFO];
|
||||
gRxVfo = gTxVfo;
|
||||
gRequestSaveVFO = true;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
|
||||
// Set the REGA frequency, no offset
|
||||
gTxVfo->freq_config_RX.Frequency = REGA_FREQUENCY;
|
||||
gTxVfo->freq_config_TX = gTxVfo->freq_config_RX;
|
||||
gTxVfo->TX_OFFSET_FREQUENCY = 0;
|
||||
|
||||
// Set the modulation to FM narrow
|
||||
gTxVfo->Modulation = MODULATION_FM;
|
||||
gTxVfo->CHANNEL_BANDWIDTH = BK4819_FILTER_BW_NARROW;
|
||||
|
||||
// Set Tx Squelch Tone
|
||||
gTxVfo->freq_config_TX.CodeType = CODE_TYPE_CONTINUOUS_TONE;
|
||||
gTxVfo->freq_config_TX.Code = REGA_CTCSS_FREQ_INDEX;
|
||||
BK4819_SetCTCSSFrequency(CTCSS_Options[gTxVfo->freq_config_TX.Code]);
|
||||
// Turn Rx squelch tone off
|
||||
gTxVfo->freq_config_RX.CodeType = CODE_TYPE_OFF;
|
||||
|
||||
// Set the transmit power to high
|
||||
gTxVfo->OUTPUT_POWER = OUTPUT_POWER_HIGH;
|
||||
|
||||
// Lock the keyboard
|
||||
gEeprom.KEY_LOCK = true;
|
||||
|
||||
gRequestSaveChannel = 1;
|
||||
gRequestSaveSettings = true;
|
||||
|
||||
// Configure the receiver
|
||||
BK4819_RX_TurnOn();
|
||||
|
||||
// Set the radio to transmit mode
|
||||
RADIO_PrepareCssTX();
|
||||
FUNCTION_Select(FUNCTION_TRANSMIT);
|
||||
|
||||
// Wait to allow the radio to switch to transmit mode and stabilize the transmitter
|
||||
SYSTEM_DelayMs(ZVEI_PRE_LENGTH_MS);
|
||||
|
||||
// Send out the ZVEI2 tone sequence
|
||||
for (int i = 0; i < ZVEI_NUM_TONES; i++)
|
||||
{
|
||||
BK4819_PlaySingleTone(tones[i], ZVEI_TONE_LENGTH_MS, 100, true);
|
||||
SYSTEM_DelayMs(ZVEI_PAUSE_LENGTH_MS);
|
||||
}
|
||||
|
||||
// Wait to allow the radio to finish transmitting
|
||||
SYSTEM_DelayMs(ZVEI_POST_LENGTH_MS);
|
||||
|
||||
// Flash the green LED twice
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, true);
|
||||
SYSTEM_DelayMs(70);
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false);
|
||||
SYSTEM_DelayMs(70);
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, true);
|
||||
SYSTEM_DelayMs(70);
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false);
|
||||
|
||||
// Display the normal screen
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* REGA Alam and Test
|
||||
* ==================
|
||||
*
|
||||
* This function sends out a fixed ZVEI tone sequence for testing or alarm purposes for the Alpine REGA alarm channel.
|
||||
* Further information: https://www.rega.ch/en/our-missions/sites-and-infrastructure/emergency-radio
|
||||
*
|
||||
* There are two REGA ACTIONS: Test and Alarm.
|
||||
* The Test action sends out a fixed ZVEI tone sequence for testing purposes.
|
||||
* The Alarm action sends out a fixed ZVEI tone sequence for alarm purposes.
|
||||
*
|
||||
* These actions can be assigned to a key in the settings menu.
|
||||
*
|
||||
* The Test/Alarm function will perform the following actions:
|
||||
* - Set the radio configuration to the REGA frequency, CTCSS, FM, full power etc.
|
||||
* - Start transmitting
|
||||
* - Wait 100ms to allow the radio to switch to transmit mode and stabilize the transmitter
|
||||
* - Send out the ZVEI tone sequence
|
||||
* - Wait 100ms to allow the radio to finish transmitting
|
||||
* - Set the radio back to receive mode
|
||||
*
|
||||
* The REGA frequency is 161.300 Mhz
|
||||
*
|
||||
* The ZVEI tone squence for alarm is: 21414
|
||||
* The ZVEI tone squence for test is: 21301
|
||||
*
|
||||
* The two tone sequences are hardcoded in two arrays
|
||||
*
|
||||
* Copyright 2025 Markus Bärtschi
|
||||
*
|
||||
* https://github.com/markusb
|
||||
*
|
||||
*
|
||||
* 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 REGA_H
|
||||
#define REGA_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define ZVEI_NUM_TONES 5
|
||||
#define ZVEI_TONE_LENGTH_MS 70 // 70
|
||||
#define ZVEI_PAUSE_LENGTH_MS 10 // 10
|
||||
#define ZVEI_PRE_LENGTH_MS 300
|
||||
#define ZVEI_POST_LENGTH_MS 100
|
||||
#define REGA_CTCSS_FREQ_INDEX 18 // dcs.c: CTCSS_Options[18] = 1230
|
||||
#define REGA_FREQUENCY 16130000 // 43370000 16130000
|
||||
|
||||
void ACTION_RegaAlarm(void);
|
||||
void ACTION_RegaTest(void);
|
||||
void UI_DisplayREGA(void);
|
||||
void REGA_TransmitZvei(const uint16_t[], const char[]);
|
||||
|
||||
#endif
|
||||
+1376
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,89 @@
|
||||
/* Copyright 2026
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
*/
|
||||
|
||||
#ifndef APP_RXTX_LOG_H
|
||||
#define APP_RXTX_LOG_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "driver/keyboard.h"
|
||||
#include "functions.h"
|
||||
#include "radio.h"
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
|
||||
#define RXTX_LOG_VISIBLE_COUNT 512
|
||||
|
||||
// Field order mirrors the leading fields of RXTX_LogFlashEntry_t
|
||||
// (rxtx_log.c) so both layouts match byte-for-byte up to and including
|
||||
// battVolt, copied in one pass. Scan-only fields (sequence) sit past the
|
||||
// copied prefix in the flash layout and are not cached in RAM.
|
||||
// The channel name is not stored in flash: it is resolved from `channel` when
|
||||
// exporting the K5Viewer packet.
|
||||
typedef struct {
|
||||
uint32_t frequency;
|
||||
uint32_t trafficSeq;
|
||||
uint16_t durationSeconds;
|
||||
uint16_t channel;
|
||||
uint8_t flags;
|
||||
uint8_t sMeter;
|
||||
uint8_t battVolt;
|
||||
} RXTX_LogEntry_t;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER
|
||||
#define RXTX_LOG_K5VIEWER_VERSION 2u
|
||||
#define RXTX_LOG_K5VIEWER_ROW_COUNT 64u
|
||||
#define RXTX_LOG_K5VIEWER_HISTORY_ROW_COUNT 64u
|
||||
#define RXTX_LOG_K5VIEWER_HISTORY_START 0xFFFFFFFFu
|
||||
#define RXTX_LOG_K5VIEWER_NAME_LENGTH 10u
|
||||
#define RXTX_LOG_K5VIEWER_STATUS_ACTIVE (1u << 0)
|
||||
#define RXTX_LOG_K5VIEWER_STATUS_HAS_TRAFFIC (1u << 1)
|
||||
#define RXTX_LOG_K5VIEWER_STATUS_CLEARING (1u << 2)
|
||||
#define RXTX_LOG_K5VIEWER_STATUS_DISABLED (1u << 3)
|
||||
#define RXTX_LOG_K5VIEWER_STATUS_PACKET_SIZE 4u
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint32_t frequency;
|
||||
uint32_t trafficSeq;
|
||||
uint16_t durationSeconds;
|
||||
uint16_t channel;
|
||||
uint8_t flags;
|
||||
uint8_t meter;
|
||||
uint8_t battVolt;
|
||||
char channelName[RXTX_LOG_K5VIEWER_NAME_LENGTH];
|
||||
} RXTX_LogK5ViewerRow_t;
|
||||
|
||||
// On-wire packet layout: a disabled log sends only the 4-byte status header.
|
||||
// An enabled log continues with one live row and
|
||||
// RXTX_LOG_K5VIEWER_ROW_COUNT row slots, zero-padded past the last valid row.
|
||||
// The packet is streamed row by row and never built whole in RAM.
|
||||
#define RXTX_LOG_K5VIEWER_PACKET_SIZE (RXTX_LOG_K5VIEWER_STATUS_PACKET_SIZE + ((RXTX_LOG_K5VIEWER_ROW_COUNT + 1u) * sizeof(RXTX_LogK5ViewerRow_t)))
|
||||
#define RXTX_LOG_K5VIEWER_HISTORY_PACKET_SIZE (RXTX_LOG_K5VIEWER_HISTORY_ROW_COUNT * sizeof(RXTX_LogK5ViewerRow_t))
|
||||
|
||||
uint32_t RXTX_LOG_K5ViewerSignature(void);
|
||||
void RXTX_LOG_SendK5ViewerPacket(void (*send)(const uint8_t *data, uint16_t size));
|
||||
// Sends the page of history rows below `beforeSeq` (HISTORY_START begins
|
||||
// a dump below the live packet's coverage) and returns the bound for the
|
||||
// next page, 0 once the visible history is exhausted.
|
||||
uint32_t RXTX_LOG_SendK5ViewerHistoryPage(uint32_t beforeSeq, void (*send)(const uint8_t *data, uint16_t size));
|
||||
#endif
|
||||
|
||||
void RXTX_LOG_Init(void);
|
||||
bool RXTX_LOG_IsEnabled(void);
|
||||
void RXTX_LOG_BeginRx(const VFO_Info_t *vfo, FUNCTION_Type_t function);
|
||||
void RXTX_LOG_BeginTx(const VFO_Info_t *vfo);
|
||||
void RXTX_LOG_EndActive(void);
|
||||
void RXTX_LOG_Task10ms(void);
|
||||
void RXTX_LOG_Tick500ms(void);
|
||||
const char *RXTX_LOG_GetFilterName(void);
|
||||
|
||||
void ACTION_RxTxLog(void);
|
||||
void RXTX_LOG_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld);
|
||||
void UI_DisplayRxTxLog(void);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+132
-36
@@ -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;
|
||||
@@ -41,6 +41,101 @@ bool gScanUseCssResult;
|
||||
STEP_Setting_t stepSetting;
|
||||
uint8_t scanHitCount;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SCAN_FREQ_VERIFY_OFF,
|
||||
SCAN_FREQ_VERIFY_FUNDAMENTAL,
|
||||
SCAN_FREQ_VERIFY_HARMONIC
|
||||
} SCAN_FrequencyVerifyState_t;
|
||||
|
||||
static SCAN_FrequencyVerifyState_t scanFreqVerifyState;
|
||||
static uint32_t scanVerifyFundamental;
|
||||
static uint32_t scanVerifyHarmonic;
|
||||
static uint16_t scanVerifyFundamentalRssi;
|
||||
|
||||
static bool SCANNER_ShouldVerifyVhfSecondHarmonic(const uint32_t frequency)
|
||||
{
|
||||
const uint32_t fundamental = frequency / 2;
|
||||
|
||||
return frequency >= (frequencyBandTable[BAND3_137MHz].lower * 2) &&
|
||||
frequency < (frequencyBandTable[BAND4_174MHz].lower * 2) &&
|
||||
fundamental >= frequencyBandTable[BAND3_137MHz].lower &&
|
||||
fundamental < frequencyBandTable[BAND4_174MHz].lower;
|
||||
}
|
||||
|
||||
static void SCANNER_StartCssScanAtFrequency(const uint32_t frequency)
|
||||
{
|
||||
gScanFrequency = frequency;
|
||||
gScanCssResultCode = 0xFF;
|
||||
gScanCssResultType = 0xFF;
|
||||
scanHitCount = 0;
|
||||
gScanUseCssResult = false;
|
||||
gScanProgressIndicator = 0;
|
||||
gScanCssState = SCAN_CSS_STATE_SCANNING;
|
||||
gScanDelay_10ms = scan_delay_10ms;
|
||||
|
||||
BK4819_SetScanFrequency(gScanFrequency);
|
||||
|
||||
if (!gCssBackgroundScan)
|
||||
GUI_SelectNextDisplay(DISPLAY_SCANNER);
|
||||
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
|
||||
static void SCANNER_TuneForFrequencyVerification(const uint32_t frequency)
|
||||
{
|
||||
BK4819_SetFrequency(frequency);
|
||||
BK4819_PickRXFilterPathBasedOnFrequency(frequency);
|
||||
BK4819_RX_TurnOn();
|
||||
}
|
||||
|
||||
static uint16_t SCANNER_ReadVerificationRssi(void)
|
||||
{
|
||||
(void)BK4819_GetRSSI();
|
||||
return BK4819_GetRSSI();
|
||||
}
|
||||
|
||||
static void SCANNER_StartFrequencyVerification(const uint32_t harmonic)
|
||||
{
|
||||
scanVerifyFundamental = harmonic / 2;
|
||||
scanVerifyHarmonic = harmonic;
|
||||
scanFreqVerifyState = SCAN_FREQ_VERIFY_FUNDAMENTAL;
|
||||
|
||||
SCANNER_TuneForFrequencyVerification(scanVerifyFundamental);
|
||||
gScanDelay_10ms = 2;
|
||||
}
|
||||
|
||||
static bool SCANNER_HandleFrequencyVerification(void)
|
||||
{
|
||||
switch (scanFreqVerifyState) {
|
||||
case SCAN_FREQ_VERIFY_FUNDAMENTAL:
|
||||
scanVerifyFundamentalRssi = SCANNER_ReadVerificationRssi();
|
||||
scanFreqVerifyState = SCAN_FREQ_VERIFY_HARMONIC;
|
||||
SCANNER_TuneForFrequencyVerification(scanVerifyHarmonic);
|
||||
gScanDelay_10ms = 2;
|
||||
return true;
|
||||
|
||||
case SCAN_FREQ_VERIFY_HARMONIC: {
|
||||
const uint16_t harmonicRssi = SCANNER_ReadVerificationRssi();
|
||||
const uint16_t margin = 4;
|
||||
uint32_t verifiedFrequency = scanVerifyHarmonic;
|
||||
|
||||
if (scanVerifyFundamentalRssi > harmonicRssi &&
|
||||
scanVerifyFundamentalRssi - harmonicRssi >= margin)
|
||||
{
|
||||
verifiedFrequency = scanVerifyFundamental;
|
||||
}
|
||||
|
||||
scanFreqVerifyState = SCAN_FREQ_VERIFY_OFF;
|
||||
SCANNER_StartCssScanAtFrequency(verifiedFrequency);
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void SCANNER_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if (!bKeyHeld && bKeyPressed)
|
||||
@@ -52,7 +147,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 +156,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 +303,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 +338,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 +372,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);
|
||||
@@ -341,8 +436,9 @@ void SCANNER_Start(bool singleFreq)
|
||||
gScanCssState = SCAN_CSS_STATE_OFF;
|
||||
gScanFrequency = 0xFFFFFFFF;
|
||||
|
||||
BK4819_PickRXFilterPathBasedOnFrequency(gScanFrequency);
|
||||
BK4819_EnableFrequencyScan();
|
||||
// Keep the RX input path selected by RADIO_SetupRegisters().
|
||||
// 0xFFFFFFFF means "unknown frequency" here and would turn both LNAs off.
|
||||
BK4819_SetFrequencyScan(true);
|
||||
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
@@ -366,6 +462,7 @@ void SCANNER_Start(bool singleFreq)
|
||||
g_SquelchLost = false;
|
||||
gScannerSaveState = SCAN_SAVE_NO_PROMPT;
|
||||
gScanProgressIndicator = 0;
|
||||
scanFreqVerifyState = SCAN_FREQ_VERIFY_OFF;
|
||||
}
|
||||
|
||||
void SCANNER_Stop(void)
|
||||
@@ -377,6 +474,7 @@ void SCANNER_Stop(void)
|
||||
gUpdateStatus = true;
|
||||
gCssBackgroundScan = false;
|
||||
gScanUseCssResult = false;
|
||||
scanFreqVerifyState = SCAN_FREQ_VERIFY_OFF;
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_CANCEL;
|
||||
#endif
|
||||
@@ -398,6 +496,10 @@ void SCANNER_TimeSlice10ms(void)
|
||||
return;
|
||||
}
|
||||
|
||||
if (SCANNER_HandleFrequencyVerification()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (gScanCssState) {
|
||||
case SCAN_CSS_STATE_OFF: {
|
||||
// must be RF frequency scanning if we're here ?
|
||||
@@ -415,27 +517,21 @@ void SCANNER_TimeSlice10ms(void)
|
||||
else
|
||||
scanHitCount = 0;
|
||||
|
||||
BK4819_DisableFrequencyScan();
|
||||
BK4819_SetFrequencyScan(false);
|
||||
|
||||
if (scanHitCount < 3) {
|
||||
BK4819_EnableFrequencyScan();
|
||||
BK4819_SetFrequencyScan(true);
|
||||
}
|
||||
else if (SCANNER_ShouldVerifyVhfSecondHarmonic(gScanFrequency)) {
|
||||
SCANNER_StartFrequencyVerification(gScanFrequency);
|
||||
}
|
||||
else {
|
||||
BK4819_SetScanFrequency(gScanFrequency);
|
||||
gScanCssResultCode = 0xFF;
|
||||
gScanCssResultType = 0xFF;
|
||||
scanHitCount = 0;
|
||||
gScanUseCssResult = false;
|
||||
gScanProgressIndicator = 0;
|
||||
gScanCssState = SCAN_CSS_STATE_SCANNING;
|
||||
|
||||
if(!gCssBackgroundScan)
|
||||
GUI_SelectNextDisplay(DISPLAY_SCANNER);
|
||||
|
||||
gUpdateStatus = true;
|
||||
SCANNER_StartCssScanAtFrequency(gScanFrequency);
|
||||
}
|
||||
|
||||
gScanDelay_10ms = scan_delay_10ms;
|
||||
if (scanFreqVerifyState == SCAN_FREQ_VERIFY_OFF) {
|
||||
gScanDelay_10ms = scan_delay_10ms;
|
||||
}
|
||||
//gScanDelay_10ms = 1; // 10ms
|
||||
break;
|
||||
}
|
||||
@@ -506,8 +602,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 +621,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;
|
||||
|
||||
+1156
-369
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
|
||||
};
|
||||
|
||||
|
||||
+21
-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);
|
||||
}
|
||||
@@ -863,7 +863,20 @@ void UART_HandleCommand(uint32_t Port)
|
||||
#endif
|
||||
} // switch
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
gUART_LockScreenshot = 20; // lock screenshot
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
gUART_LockK5Viewer = 20; // lock the K5Viewer stream
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void UART_ServiceCommands(void)
|
||||
{
|
||||
#ifdef ENABLE_USB
|
||||
if (UART_IsCommandAvailable(UART_PORT_VCP))
|
||||
UART_HandleCommand(UART_PORT_VCP);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_UART
|
||||
if (UART_IsCommandAvailable(UART_PORT_UART))
|
||||
UART_HandleCommand(UART_PORT_UART);
|
||||
#endif
|
||||
}
|
||||
+1
-1
@@ -32,6 +32,6 @@ enum
|
||||
|
||||
bool UART_IsCommandAvailable(uint32_t Port);
|
||||
void UART_HandleCommand(uint32_t Port);
|
||||
void UART_ServiceCommands(void);
|
||||
|
||||
#endif
|
||||
|
||||
+37
-101
@@ -33,38 +33,43 @@
|
||||
#include "settings.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
static const uint16_t BEEP_Classic_array[][3] = { /* Tone Duration Repeats */
|
||||
[BEEP_NONE] = {0, 0, 0 },
|
||||
[BEEP_1KHZ_60MS_OPTIONAL] = {1000, 60, 1 },
|
||||
[BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL] = {500, 60, 2 },
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
[BEEP_880HZ_200MS] = {880, 200, 1 },
|
||||
[BEEP_880HZ_500MS] = {880, 500, 1 },
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
[BEEP_400HZ_30MS] = {400, 30, 1 },
|
||||
[BEEP_500HZ_30MS] = {500, 30, 1 },
|
||||
[BEEP_600HZ_30MS] = {600, 30, 1 },
|
||||
#endif
|
||||
[BEEP_880HZ_60MS_TRIPLE_BEEP] = {880, 60, 3 }
|
||||
};
|
||||
|
||||
BEEP_Type_t gBeepToPlay = BEEP_NONE;
|
||||
|
||||
void AUDIO_PlayBeep(BEEP_Type_t Beep)
|
||||
{
|
||||
|
||||
if (Beep != BEEP_880HZ_60MS_DOUBLE_BEEP &&
|
||||
Beep != BEEP_500HZ_60MS_DOUBLE_BEEP &&
|
||||
Beep != BEEP_440HZ_500MS &&
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
Beep != BEEP_880HZ_200MS &&
|
||||
Beep != BEEP_880HZ_500MS &&
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
Beep != BEEP_400HZ_30MS &&
|
||||
Beep != BEEP_500HZ_30MS &&
|
||||
Beep != BEEP_600HZ_30MS &&
|
||||
#endif
|
||||
!gEeprom.BEEP_CONTROL)
|
||||
if (Beep == BEEP_NONE)
|
||||
return;
|
||||
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
if (gScreenToDisplay == DISPLAY_AIRCOPY)
|
||||
if ((Beep == BEEP_1KHZ_60MS_OPTIONAL ||
|
||||
Beep == BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL) &&
|
||||
!gEeprom.BEEP_CONTROL)
|
||||
return;
|
||||
#endif
|
||||
|
||||
|
||||
if (gCurrentFunction == FUNCTION_RECEIVE)
|
||||
return;
|
||||
|
||||
if (gCurrentFunction == FUNCTION_MONITOR)
|
||||
return;
|
||||
|
||||
if (Beep >= ARRAY_SIZE(BEEP_Classic_array))
|
||||
return;
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
if (gFmRadioMode)
|
||||
BK1080_Mute(true);
|
||||
@@ -79,49 +84,14 @@ void AUDIO_PlayBeep(BEEP_Type_t Beep)
|
||||
|
||||
uint16_t ToneConfig = BK4819_ReadRegister(BK4819_REG_71);
|
||||
|
||||
uint16_t ToneFrequency;
|
||||
switch (Beep)
|
||||
{
|
||||
default:
|
||||
case BEEP_NONE:
|
||||
ToneFrequency = 220;
|
||||
break;
|
||||
case BEEP_1KHZ_60MS_OPTIONAL:
|
||||
ToneFrequency = 1000;
|
||||
break;
|
||||
case BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL:
|
||||
case BEEP_500HZ_60MS_DOUBLE_BEEP:
|
||||
ToneFrequency = 500;
|
||||
break;
|
||||
case BEEP_440HZ_500MS:
|
||||
ToneFrequency = 440;
|
||||
break;
|
||||
case BEEP_880HZ_60MS_DOUBLE_BEEP:
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
case BEEP_880HZ_200MS:
|
||||
case BEEP_880HZ_500MS:
|
||||
#endif
|
||||
ToneFrequency = 880;
|
||||
break;
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
case BEEP_400HZ_30MS:
|
||||
ToneFrequency = 400;
|
||||
break;
|
||||
case BEEP_500HZ_30MS:
|
||||
ToneFrequency = 500;
|
||||
break;
|
||||
case BEEP_600HZ_30MS:
|
||||
ToneFrequency = 600;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
if(Beep == BEEP_400HZ_30MS || Beep == BEEP_500HZ_30MS || Beep == BEEP_600HZ_30MS)
|
||||
{
|
||||
BK4819_WriteRegister(BK4819_REG_70, BK4819_REG_70_ENABLE_TONE1 | ((1 & 0x7f) << BK4819_REG_70_SHIFT_TONE1_TUNING_GAIN));
|
||||
}
|
||||
#endif
|
||||
|
||||
BK4819_PlayTone(ToneFrequency, true);
|
||||
BK4819_PrepareToPlayTone(true);
|
||||
|
||||
SYSTEM_DelayMs(2);
|
||||
|
||||
@@ -129,52 +99,12 @@ void AUDIO_PlayBeep(BEEP_Type_t Beep)
|
||||
|
||||
SYSTEM_DelayMs(60);
|
||||
|
||||
uint16_t Duration;
|
||||
switch (Beep)
|
||||
{
|
||||
case BEEP_880HZ_60MS_DOUBLE_BEEP:
|
||||
BK4819_ExitTxMute();
|
||||
SYSTEM_DelayMs(60);
|
||||
BK4819_EnterTxMute();
|
||||
SYSTEM_DelayMs(20);
|
||||
[[fallthrough]];
|
||||
case BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL:
|
||||
case BEEP_500HZ_60MS_DOUBLE_BEEP:
|
||||
BK4819_ExitTxMute();
|
||||
SYSTEM_DelayMs(60);
|
||||
BK4819_EnterTxMute();
|
||||
SYSTEM_DelayMs(20);
|
||||
[[fallthrough]];
|
||||
case BEEP_1KHZ_60MS_OPTIONAL:
|
||||
BK4819_ExitTxMute();
|
||||
Duration = 60;
|
||||
break;
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
case BEEP_400HZ_30MS:
|
||||
case BEEP_500HZ_30MS:
|
||||
case BEEP_600HZ_30MS:
|
||||
BK4819_ExitTxMute();
|
||||
Duration = 30;
|
||||
break;
|
||||
#endif
|
||||
case BEEP_440HZ_500MS:
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
case BEEP_880HZ_200MS:
|
||||
BK4819_ExitTxMute();
|
||||
Duration = 200;
|
||||
break;
|
||||
case BEEP_880HZ_500MS:
|
||||
#endif
|
||||
default:
|
||||
BK4819_ExitTxMute();
|
||||
Duration = 500;
|
||||
break;
|
||||
for (uint8_t i = 0; i < BEEP_Classic_array[Beep][BEEP_REPEATS]; i++) {
|
||||
BK4819_PlayToneRaw( BEEP_Classic_array[Beep][BEEP_TONE],
|
||||
BEEP_Classic_array[Beep][BEEP_DURATION]);
|
||||
SYSTEM_DelayMs(20);
|
||||
}
|
||||
|
||||
SYSTEM_DelayMs(Duration);
|
||||
BK4819_EnterTxMute();
|
||||
SYSTEM_DelayMs(20);
|
||||
|
||||
AUDIO_AudioPathOff();
|
||||
|
||||
SYSTEM_DelayMs(5);
|
||||
@@ -182,11 +112,18 @@ void AUDIO_PlayBeep(BEEP_Type_t Beep)
|
||||
SYSTEM_DelayMs(5);
|
||||
BK4819_WriteRegister(BK4819_REG_71, ToneConfig);
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
const bool isFmRadio = gFmRadioMode;
|
||||
|
||||
if (isFmRadio)
|
||||
SYSTEM_DelayMs(10);
|
||||
#endif
|
||||
|
||||
if (gEnableSpeaker)
|
||||
AUDIO_AudioPathOn();
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
if (gFmRadioMode)
|
||||
if (isFmRadio)
|
||||
BK1080_Mute(false);
|
||||
#endif
|
||||
|
||||
@@ -196,7 +133,6 @@ void AUDIO_PlayBeep(BEEP_Type_t Beep)
|
||||
#ifdef ENABLE_VOX
|
||||
gVoxResumeCountdown = 80;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
|
||||
+8
-3
@@ -22,23 +22,28 @@
|
||||
|
||||
#include "driver/gpio.h"
|
||||
|
||||
enum {
|
||||
BEEP_TONE = 0,
|
||||
BEEP_DURATION,
|
||||
BEEP_REPEATS
|
||||
};
|
||||
|
||||
|
||||
enum BEEP_Type_t
|
||||
{
|
||||
BEEP_NONE = 0,
|
||||
BEEP_1KHZ_60MS_OPTIONAL,
|
||||
BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL,
|
||||
BEEP_440HZ_500MS,
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
BEEP_880HZ_200MS,
|
||||
BEEP_880HZ_500MS,
|
||||
#endif
|
||||
BEEP_500HZ_60MS_DOUBLE_BEEP,
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
BEEP_400HZ_30MS,
|
||||
BEEP_500HZ_30MS,
|
||||
BEEP_600HZ_30MS,
|
||||
#endif
|
||||
BEEP_880HZ_60MS_DOUBLE_BEEP
|
||||
BEEP_880HZ_60MS_TRIPLE_BEEP
|
||||
};
|
||||
|
||||
typedef enum BEEP_Type_t BEEP_Type_t;
|
||||
|
||||
+188
-16
@@ -57,6 +57,21 @@ const uint8_t gFontLight[9] =
|
||||
0b00001100,
|
||||
};
|
||||
|
||||
// Same bulb outline as gFontLight with the inner filament removed,
|
||||
// used when the manual backlight is currently off
|
||||
const uint8_t gFontLightOff[9] =
|
||||
{
|
||||
0b00001100,
|
||||
0b00010010,
|
||||
0b00100001,
|
||||
0b01100001,
|
||||
0b01100001,
|
||||
0b01100001,
|
||||
0b00100001,
|
||||
0b00010010,
|
||||
0b00001100,
|
||||
};
|
||||
|
||||
const uint8_t gFontMute[12] =
|
||||
{
|
||||
0b00011100,
|
||||
@@ -157,26 +172,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,6 +205,42 @@ const uint8_t BITMAP_VFO_NotDefault[7] =
|
||||
0b00010100,
|
||||
0b00001000
|
||||
};
|
||||
*/
|
||||
|
||||
// Compact arrow
|
||||
const uint8_t BITMAP_VFO_Default[7] =
|
||||
{
|
||||
0b00111110,
|
||||
0b00111110,
|
||||
0b00011100,
|
||||
0b00011100,
|
||||
0b00001000,
|
||||
0b00001000,
|
||||
0b00000000
|
||||
};
|
||||
|
||||
// Compact empty arrow
|
||||
const uint8_t BITMAP_VFO_NotDefault[7] =
|
||||
{
|
||||
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] =
|
||||
{
|
||||
@@ -258,6 +301,135 @@ const uint8_t BITMAP_NOAA[12] =
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
const uint8_t BITMAP_FoxHuntSignal[10] =
|
||||
{ // point source + the speaker bitmap's two sound waves, oriented horizontally
|
||||
0b00001000,
|
||||
0b00011100,
|
||||
0b00011100,
|
||||
0b00001000,
|
||||
0b00000000,
|
||||
0b00100010,
|
||||
0b00011100,
|
||||
0b01000001,
|
||||
0b00100010,
|
||||
0b00011100
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_FoxHuntSpeaker[10] =
|
||||
{ // speaker cone + two sound waves (bit0 = top row)
|
||||
0b00011100,
|
||||
0b00011100,
|
||||
0b00111110,
|
||||
0b01111111,
|
||||
0b00000000,
|
||||
0b00100010,
|
||||
0b00011100,
|
||||
0b01000001,
|
||||
0b00100010,
|
||||
0b00011100
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_FoxHuntUp[11] =
|
||||
{ // filled up triangle (getting nearer)
|
||||
0b00100000,
|
||||
0b00110000,
|
||||
0b00111000,
|
||||
0b00111100,
|
||||
0b00111110,
|
||||
0b00111111,
|
||||
0b00111110,
|
||||
0b00111100,
|
||||
0b00111000,
|
||||
0b00110000,
|
||||
0b00100000
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_FoxHuntDown[11] =
|
||||
{ // filled down triangle (getting farther)
|
||||
0b00000001,
|
||||
0b00000011,
|
||||
0b00000111,
|
||||
0b00001111,
|
||||
0b00011111,
|
||||
0b00111111,
|
||||
0b00011111,
|
||||
0b00001111,
|
||||
0b00000111,
|
||||
0b00000011,
|
||||
0b00000001
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_FoxHuntFlat[11] =
|
||||
{ // equals sign (stable)
|
||||
0b00010010,
|
||||
0b00010010,
|
||||
0b00010010,
|
||||
0b00010010,
|
||||
0b00010010,
|
||||
0b00010010,
|
||||
0b00010010,
|
||||
0b00010010,
|
||||
0b00010010,
|
||||
0b00010010,
|
||||
0b00010010
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_FoxHuntBars[11] =
|
||||
{ // ascending bars = S-meter (staircase) gauge mode (bit0 = top row)
|
||||
0b01000000,
|
||||
0b01000000,
|
||||
0b00000000,
|
||||
0b01110000,
|
||||
0b01110000,
|
||||
0b00000000,
|
||||
0b01111100,
|
||||
0b01111100,
|
||||
0b00000000,
|
||||
0b01111111,
|
||||
0b01111111
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_FoxHuntGraph[15] =
|
||||
{ // sine wave = signal-history gauge mode (bit0 = top row)
|
||||
0b00001000,
|
||||
0b00000100,
|
||||
0b00000010,
|
||||
0b00000100,
|
||||
0b00001000,
|
||||
0b00010000,
|
||||
0b00100000,
|
||||
0b00010000,
|
||||
0b00001000,
|
||||
0b00000100,
|
||||
0b00000010,
|
||||
0b00000100,
|
||||
0b00001000,
|
||||
0b00010000,
|
||||
0b00100000
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_FoxHuntTx[16] =
|
||||
{
|
||||
0b00011100,
|
||||
0b00100010,
|
||||
0b01000001,
|
||||
0b00011100,
|
||||
0b00100010,
|
||||
0b00000000,
|
||||
0b00001000,
|
||||
0b00011100,
|
||||
0b00011100,
|
||||
0b00001000,
|
||||
0b00000000,
|
||||
0b00100010,
|
||||
0b00011100,
|
||||
0b01000001,
|
||||
0b00100010,
|
||||
0b00011100
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef ENABLE_CUSTOM_MENU_LAYOUT
|
||||
const uint8_t BITMAP_CurrentIndicator[8] = {
|
||||
0xFF,
|
||||
|
||||
+13
-1
@@ -12,6 +12,7 @@ extern const uint8_t gFontS[6];
|
||||
|
||||
extern const uint8_t gFontKeyLock[9];
|
||||
extern const uint8_t gFontLight[9];
|
||||
extern const uint8_t gFontLightOff[9];
|
||||
extern const uint8_t gFontMute[12];
|
||||
|
||||
extern const uint8_t gFontXB[2][6];
|
||||
@@ -35,15 +36,26 @@ 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_PowerUser[3];
|
||||
extern const uint8_t BITMAP_compand[6];
|
||||
|
||||
extern const uint8_t BITMAP_NOAA[12];
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
extern const uint8_t BITMAP_FoxHuntSignal[10];
|
||||
extern const uint8_t BITMAP_FoxHuntSpeaker[10];
|
||||
extern const uint8_t BITMAP_FoxHuntUp[11];
|
||||
extern const uint8_t BITMAP_FoxHuntDown[11];
|
||||
extern const uint8_t BITMAP_FoxHuntFlat[11];
|
||||
extern const uint8_t BITMAP_FoxHuntBars[11];
|
||||
extern const uint8_t BITMAP_FoxHuntGraph[15];
|
||||
extern const uint8_t BITMAP_FoxHuntTx[16];
|
||||
#endif
|
||||
|
||||
#ifndef ENABLE_CUSTOM_MENU_LAYOUT
|
||||
extern const uint8_t BITMAP_CurrentIndicator[8];
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
|
||||
+123
-38
@@ -31,8 +31,10 @@
|
||||
#include "misc.h"
|
||||
#endif
|
||||
|
||||
#define PWM_FREQ 320
|
||||
#define DUTY_CYCLE_LEVELS 64
|
||||
#define PWM_FREQ 4000
|
||||
// 32 levels keep every step of the value[] table distinct after
|
||||
// quantization while halving the DMA duty cycle buffer (128 bytes saved)
|
||||
#define DUTY_CYCLE_LEVELS 32
|
||||
|
||||
#define DUTY_CYCLE_ON_VALUE GPIO_PIN_MASK(GPIO_PIN_BACKLIGHT)
|
||||
#define DUTY_CYCLE_OFF_VALUE (DUTY_CYCLE_ON_VALUE << 16)
|
||||
@@ -44,14 +46,31 @@ 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;
|
||||
|
||||
static void BACKLIGHT_SetHardwareBrightness(uint8_t brightness);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
// Power-on fade-in tuning.
|
||||
// STEPS = number of intermediate brightness stops (smoothness).
|
||||
// STEP_MS = pacing between stops.
|
||||
// STEPS * STEP_MS ~= total fade duration in ms.
|
||||
#define BL_STARTUP_FADE_STEPS 40
|
||||
#define BL_STARTUP_FADE_STEP_MS 12
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
const uint8_t value[] = {
|
||||
0, // 0 off
|
||||
8, // 1 visible in the dark
|
||||
14, // 2
|
||||
22, // 3
|
||||
16, // 2
|
||||
24, // 3
|
||||
32, // 4
|
||||
48, // 5
|
||||
72, // 6
|
||||
@@ -101,15 +120,57 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
// Soft, progressive power-on fade-in.
|
||||
// Ramps from the current brightness (0 at power-on) to targetBrightness using
|
||||
// a smoothstep (3x^2 - 2x^3) easing curve. Easing gently at both ends reads as
|
||||
// a smooth, progressive fade instead of the abrupt linear ramp, and stepping
|
||||
// through many fine stops keeps it fluid even with the 32-level PWM.
|
||||
static void BACKLIGHT_FadeInStartup(void)
|
||||
{
|
||||
const int16_t from = currentBrightness; // 0 at power-on
|
||||
const int16_t span = targetBrightness - from; // ramp amplitude
|
||||
|
||||
if (span <= 0) {
|
||||
gUpdateBacklight = false;
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint16_t s = 1; s < BL_STARTUP_FADE_STEPS; s++) {
|
||||
// x in Q8 (0..256), e = smoothstep(x) in Q16 (0..65536)
|
||||
uint32_t x = (uint32_t)s * 256 / BL_STARTUP_FADE_STEPS;
|
||||
uint32_t e = (x * x * (768 - 2 * x)) >> 8;
|
||||
currentBrightness = from + (int16_t)(((uint32_t)span * e) >> 16);
|
||||
BACKLIGHT_SetHardwareBrightness((uint8_t)currentBrightness);
|
||||
SYSTEM_DelayMs(BL_STARTUP_FADE_STEP_MS);
|
||||
}
|
||||
|
||||
currentBrightness = targetBrightness;
|
||||
BACKLIGHT_SetHardwareBrightness((uint8_t)currentBrightness);
|
||||
gUpdateBacklight = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
void BACKLIGHT_TurnOn(void)
|
||||
{
|
||||
@@ -134,27 +195,24 @@ 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
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
BACKLIGHT_FadeInStartup();
|
||||
BACKLIGHT_Sound();
|
||||
#else
|
||||
BACKLIGHT_UpdateTickless();
|
||||
startup = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
switch (gEeprom.BACKLIGHT_TIME) {
|
||||
default:
|
||||
@@ -190,18 +248,11 @@ bool BACKLIGHT_IsOn()
|
||||
return backlightOn;
|
||||
}
|
||||
|
||||
static uint8_t currentBrightness = 0;
|
||||
|
||||
void BACKLIGHT_SetBrightness(uint8_t brigtness)
|
||||
static void BACKLIGHT_SetHardwareBrightness(uint8_t brightness)
|
||||
{
|
||||
// printf("BL: %d\n", brigtness);
|
||||
|
||||
if (currentBrightness == brigtness)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (0 == brigtness)
|
||||
if (0 == brightness)
|
||||
{
|
||||
LL_TIM_DisableCounter(TIMx);
|
||||
LL_DMA_DisableChannel(DMA1, DMA_CHANNEL);
|
||||
@@ -210,7 +261,7 @@ void BACKLIGHT_SetBrightness(uint8_t brigtness)
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint32_t level = (uint32_t)(value[brigtness]) * DUTY_CYCLE_LEVELS / 255;
|
||||
const uint32_t level = (uint32_t)(brightness) * DUTY_CYCLE_LEVELS / 255;
|
||||
if (level >= DUTY_CYCLE_LEVELS)
|
||||
{
|
||||
LL_TIM_DisableCounter(TIMx);
|
||||
@@ -231,11 +282,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)
|
||||
|
||||
+22
-9
@@ -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)
|
||||
@@ -414,6 +411,11 @@ void BK4819_ToggleGpioOut(BK4819_GPIO_PIN_t Pin, bool bSet)
|
||||
BK4819_WriteRegister(BK4819_REG_33, gBK4819_GpioOutState);
|
||||
}
|
||||
|
||||
bool BK4819_IsGpioOutSet(BK4819_GPIO_PIN_t Pin)
|
||||
{
|
||||
return (gBK4819_GpioOutState & (0x40u >> Pin)) != 0;
|
||||
}
|
||||
|
||||
void BK4819_SetCDCSSCodeWord(uint32_t CodeWord)
|
||||
{
|
||||
// REG_51
|
||||
@@ -1722,7 +1724,7 @@ void BK4819_PrepareFSKReceive(void)
|
||||
BK4819_WriteRegister(BK4819_REG_59, 0x3068);
|
||||
}
|
||||
|
||||
static void BK4819_PlayRogerNormal(void)
|
||||
static void BK4819_PlayRogerNormal(BK4819_FilterBandwidth_t Bandwidth)
|
||||
{
|
||||
#if 0
|
||||
const uint32_t tone1_Hz = 500;
|
||||
@@ -1737,12 +1739,19 @@ static void BK4819_PlayRogerNormal(void)
|
||||
BK4819_EnterTxMute();
|
||||
BK4819_SetAF(BK4819_AF_MUTE);
|
||||
|
||||
BK4819_WriteRegister(BK4819_REG_70, BK4819_REG_70_ENABLE_TONE1 | (66u << BK4819_REG_70_SHIFT_TONE1_TUNING_GAIN));
|
||||
const uint8_t rogerToneGain = (Bandwidth == BK4819_FILTER_BW_WIDE) ? 32u : 66u;
|
||||
|
||||
if (Bandwidth == BK4819_FILTER_BW_WIDE)
|
||||
BK4819_SetFilterBandwidth(BK4819_FILTER_BW_NARROW, true);
|
||||
|
||||
BK4819_WriteRegister(BK4819_REG_71, scale_freq(tone1_Hz));
|
||||
|
||||
BK4819_EnableTXLink();
|
||||
SYSTEM_DelayMs(50);
|
||||
|
||||
BK4819_WriteRegister(BK4819_REG_71, scale_freq(tone1_Hz));
|
||||
BK4819_WriteRegister(BK4819_REG_70,
|
||||
BK4819_REG_70_ENABLE_TONE1 |
|
||||
(rogerToneGain << BK4819_REG_70_SHIFT_TONE1_TUNING_GAIN));
|
||||
|
||||
BK4819_ExitTxMute();
|
||||
SYSTEM_DelayMs(80);
|
||||
@@ -1755,6 +1764,10 @@ static void BK4819_PlayRogerNormal(void)
|
||||
BK4819_EnterTxMute();
|
||||
|
||||
BK4819_WriteRegister(BK4819_REG_70, 0x0000);
|
||||
|
||||
if (Bandwidth == BK4819_FILTER_BW_WIDE)
|
||||
BK4819_SetFilterBandwidth(Bandwidth, true);
|
||||
|
||||
BK4819_WriteRegister(BK4819_REG_30, 0xC1FE); // 1 1 0000 0 1 1111 1 1 1 0
|
||||
}
|
||||
|
||||
@@ -1807,10 +1820,10 @@ void BK4819_PlayRogerMDC(void)
|
||||
BK4819_WriteRegister(BK4819_REG_58, 0x0000);
|
||||
}
|
||||
|
||||
void BK4819_PlayRoger(void)
|
||||
void BK4819_PlayRoger(BK4819_FilterBandwidth_t Bandwidth)
|
||||
{
|
||||
if (gEeprom.ROGER == ROGER_MODE_ROGER) {
|
||||
BK4819_PlayRogerNormal();
|
||||
BK4819_PlayRogerNormal(Bandwidth);
|
||||
} else if (gEeprom.ROGER == ROGER_MODE_MDC) {
|
||||
BK4819_PlayRogerMDC();
|
||||
}
|
||||
|
||||
+10
-3
@@ -77,6 +77,7 @@ void BK4819_SetAGC(bool enable);
|
||||
void BK4819_InitAGC(bool amModulation);
|
||||
|
||||
void BK4819_ToggleGpioOut(BK4819_GPIO_PIN_t Pin, bool bSet);
|
||||
bool BK4819_IsGpioOutSet(BK4819_GPIO_PIN_t Pin);
|
||||
|
||||
void BK4819_SetCDCSSCodeWord(uint32_t CodeWord);
|
||||
void BK4819_SetCTCSSFrequency(uint32_t BaudRate);
|
||||
@@ -105,7 +106,9 @@ void BK4819_SetCompander(const unsigned int mode);
|
||||
void BK4819_DisableVox(void);
|
||||
void BK4819_DisableDTMF(void);
|
||||
void BK4819_EnableDTMF(void);
|
||||
void BK4819_PrepareToPlayTone(bool bTuningGainSwitch);
|
||||
void BK4819_PlayTone(uint16_t Frequency, bool bTuningGainSwitch);
|
||||
void BK4819_PlayToneRaw(const unsigned int tone_Hz, const unsigned int delay);
|
||||
void BK4819_PlaySingleTone(const unsigned int tone_Hz, const unsigned int delay, const unsigned int level, const bool play_speaker);
|
||||
void BK4819_EnterTxMute(void);
|
||||
void BK4819_ExitTxMute(void);
|
||||
@@ -116,6 +119,10 @@ void BK4819_TurnsOffTones_TurnsOnRX(void);
|
||||
#endif
|
||||
void BK4819_ResetFSK(void);
|
||||
void BK4819_Idle(void);
|
||||
#ifdef ENABLE_BYP_RAW_DEMODULATORS
|
||||
void BK4819_EnterBypass(void);
|
||||
void BK4819_EnterRaw(void);
|
||||
#endif
|
||||
void BK4819_ExitBypass(void);
|
||||
void BK4819_PrepareTransmit(void);
|
||||
void BK4819_TxOn_Beep(void);
|
||||
@@ -143,11 +150,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);
|
||||
@@ -163,7 +170,7 @@ uint8_t BK4819_GetCTCType(void);
|
||||
void BK4819_SendFSKData(uint16_t *pData);
|
||||
void BK4819_PrepareFSKReceive(void);
|
||||
|
||||
void BK4819_PlayRoger(void);
|
||||
void BK4819_PlayRoger(BK4819_FilterBandwidth_t Bandwidth);
|
||||
|
||||
void BK4819_Enable_AfDac_DiscMode_TxDsp(void);
|
||||
|
||||
|
||||
+241
-239
@@ -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()
|
||||
@@ -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)
|
||||
@@ -442,6 +441,11 @@ void BK4819_ToggleGpioOut(BK4819_GPIO_PIN_t Pin, bool bSet)
|
||||
BK4819_WriteRegister(BK4819_REG_33, gBK4819_GpioOutState);
|
||||
}
|
||||
|
||||
bool BK4819_IsGpioOutSet(BK4819_GPIO_PIN_t Pin)
|
||||
{
|
||||
return (gBK4819_GpioOutState & (0x40u >> Pin)) != 0;
|
||||
}
|
||||
|
||||
void BK4819_SetCDCSSCodeWord(uint32_t CodeWord)
|
||||
{
|
||||
// REG_51
|
||||
@@ -629,100 +633,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
|
||||
// }
|
||||
val = 0x205C;
|
||||
// 0x2058 = (0b010 << 12) | (0b000 << 9) | (0b001 << 6) |
|
||||
// (0b01 << 4) | (1 << 3)
|
||||
// = RF 3.0 kHz, weak-RF 2.0 kHz, AF Tx LPF2 2.5 kHz,
|
||||
// 6.25k mode, FM gain 0 dB.
|
||||
val = 0x2058;
|
||||
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;
|
||||
@@ -801,21 +796,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
|
||||
@@ -1042,7 +1036,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;
|
||||
|
||||
@@ -1057,10 +1051,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)
|
||||
{
|
||||
@@ -1080,11 +1087,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)
|
||||
{
|
||||
@@ -1158,45 +1161,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)
|
||||
@@ -1451,24 +1471,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)
|
||||
@@ -1550,6 +1561,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);
|
||||
@@ -1585,7 +1604,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
|
||||
//
|
||||
@@ -1601,32 +1620,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)
|
||||
@@ -1690,7 +1687,7 @@ void BK4819_Disable(void)
|
||||
|
||||
void BK4819_StopScan(void)
|
||||
{
|
||||
BK4819_DisableFrequencyScan();
|
||||
BK4819_SetFrequencyScan(false);
|
||||
BK4819_Disable();
|
||||
}
|
||||
|
||||
@@ -1762,7 +1759,7 @@ void BK4819_PrepareFSKReceive(void)
|
||||
BK4819_WriteRegister(BK4819_REG_59, 0x3068);
|
||||
}
|
||||
|
||||
static void BK4819_PlayRogerNormal(void)
|
||||
static void BK4819_PlayRogerNormal(BK4819_FilterBandwidth_t Bandwidth)
|
||||
{
|
||||
#if 0
|
||||
const uint32_t tone1_Hz = 500;
|
||||
@@ -1777,26 +1774,31 @@ static void BK4819_PlayRogerNormal(void)
|
||||
BK4819_EnterTxMute();
|
||||
BK4819_SetAF(BK4819_AF_MUTE);
|
||||
|
||||
BK4819_WriteRegister(BK4819_REG_70, // BK4819_REG_70_ENABLE_TONE1 | (66u << BK4819_REG_70_SHIFT_TONE1_TUNING_GAIN));
|
||||
0xC300
|
||||
);
|
||||
const uint8_t rogerToneGain = (Bandwidth == BK4819_FILTER_BW_WIDE) ? 32u : 67u;
|
||||
|
||||
if (Bandwidth == BK4819_FILTER_BW_WIDE)
|
||||
BK4819_SetFilterBandwidth(BK4819_FILTER_BW_NARROW, true);
|
||||
|
||||
BK4819_WriteRegister(BK4819_REG_71, scale_freq(tone1_Hz));
|
||||
|
||||
BK4819_EnableTXLink();
|
||||
SYSTEM_DelayMs(50);
|
||||
|
||||
BK4819_WriteRegister(BK4819_REG_71, scale_freq(tone1_Hz));
|
||||
BK4819_WriteRegister(BK4819_REG_70,
|
||||
BK4819_REG_70_ENABLE_TONE1 |
|
||||
(rogerToneGain << BK4819_REG_70_SHIFT_TONE1_TUNING_GAIN));
|
||||
|
||||
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(tone2_Hz, 80);
|
||||
|
||||
BK4819_WriteRegister(BK4819_REG_70, 0x0000);
|
||||
|
||||
if (Bandwidth == BK4819_FILTER_BW_WIDE)
|
||||
BK4819_SetFilterBandwidth(Bandwidth, true);
|
||||
|
||||
BK4819_WriteRegister(BK4819_REG_30, 0xC1FE); // 1 1 0000 0 1 1111 1 1 1 0
|
||||
}
|
||||
|
||||
@@ -1849,10 +1851,10 @@ void BK4819_PlayRogerMDC(void)
|
||||
BK4819_WriteRegister(BK4819_REG_58, 0x0000);
|
||||
}
|
||||
|
||||
void BK4819_PlayRoger(void)
|
||||
void BK4819_PlayRoger(BK4819_FilterBandwidth_t Bandwidth)
|
||||
{
|
||||
if (gEeprom.ROGER == ROGER_MODE_ROGER) {
|
||||
BK4819_PlayRogerNormal();
|
||||
BK4819_PlayRogerNormal(Bandwidth);
|
||||
} else if (gEeprom.ROGER == ROGER_MODE_MDC) {
|
||||
BK4819_PlayRogerMDC();
|
||||
}
|
||||
|
||||
@@ -48,22 +48,43 @@ static const AddrMapping_t ADDR_MAPPINGS[] = {
|
||||
_MK_MAPPING(0x006000, 0x006000, 0x007000), // 256 MR Name * 16 Bytes
|
||||
_MK_MAPPING(0x007000, 0x007000, 0x008000), // 256 MR Name * 16 Bytes
|
||||
|
||||
_MK_MAPPING(0x008000, 0x008000, 0x00880E), // 1024 MR + 7 VFO Attributes * 2 Bytes (ex 0x002000)
|
||||
_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(0x009000, 0x009000, 0x0090E7), // 14 VFO * 16 Bytes = 0x9000 -> 0x90E0 (the old
|
||||
// 0x90D6 bound was 10 B short: it truncated the
|
||||
// 470 MHz VFO1 record at 0x90D0 -> 0x90DF)
|
||||
// Fox Hunt settings * 7 Bytes 0x90E0 -> 0x90E7
|
||||
// (ENABLE_FEAT_F4HWN_FOXHUNT, written directly by
|
||||
// app/foxhunt.c; concatenated here so aircopy clones
|
||||
// them together with the VFOs)
|
||||
|
||||
_MK_MAPPING(0x00A000, 0x00A000, 0x00A160), // Settings * 16 Bytes (ex 0x004000) 0x00A000 -> 0x00A010
|
||||
_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 -> 0x00A140
|
||||
// Settings AES * 16 Bytes (0x00A000) 0x00A140 -> 0x00A150
|
||||
// Settings Scanlist * 8 Bytes (0x009000) 0x00A130 -> 0x00A138
|
||||
// Settings AES * 16 Bytes (0x00A000) 0x00A138 -> 0x00A148
|
||||
// Settings Spectrum * 8 Bytes 0x00A148 -> 0x00A150
|
||||
// Settings * 8 Bytes (0x00B000) 0x00A150 -> 0x00A158
|
||||
// Settings F4HWN * 8 Bytes (0x00C000) 0x00A158 -> 0x00A160
|
||||
// Settings Version * 16 Bytes 0x00A160 -> 0x00A170
|
||||
|
||||
_MK_MAPPING(0x010000, 0x00B000, 0x00B200), // Calibration 512 Bytes!!!
|
||||
|
||||
_MK_MAPPING(0x011000, 0x00C000, 0x00D000), // Boot Logo sector (4 KB):
|
||||
// [0x00..0x07] 8-byte header (reserved)
|
||||
// [0x08..0x407] 128x64 monochrome bitmap, 1024 Bytes
|
||||
// ST7565-native: 8 pages * 128 columns, column-major LSB-top
|
||||
|
||||
// Not mapped, for documentation only (the EEPROM API uses 16-bit
|
||||
// addresses and could not reach a 32 KB window anyway):
|
||||
//
|
||||
// 0x1E0000 -> 0x1E8000: RX/TX append-only log * 32 KB / 8 sectors
|
||||
// (ENABLE_FEAT_F4HWN_RXTX_LOG, accessed directly
|
||||
// by app/rxtx_log.c, not through this layer)
|
||||
};
|
||||
|
||||
static void AddrTranslate(uint16_t EEPROM_Addr, uint16_t Size, uint32_t *PY25Q16_Addr_out, uint16_t *Size_out, bool *End_out);
|
||||
|
||||
+141
-1
@@ -27,6 +27,112 @@ KEY_Code_t gKeyReading1 = KEY_INVALID;
|
||||
uint16_t gDebounceCounter = 0;
|
||||
bool gWasFKeyPressed = false;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
// 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
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER
|
||||
#define SERIAL_FEATURE_TYPE 0x05
|
||||
#endif
|
||||
|
||||
volatile KEY_Code_t gKeyFromSerial = KEY_INVALID;
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER
|
||||
volatile uint8_t gSerialViewerFeatures = 0;
|
||||
#endif
|
||||
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:
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER
|
||||
if (b == 0x00)
|
||||
*state = STATE_KA_3;
|
||||
else if (b == SERIAL_FEATURE_TYPE)
|
||||
*state = STATE_KA_FEATURE;
|
||||
else
|
||||
*state = STATE_IDLE;
|
||||
#else
|
||||
*state = (b == 0x00) ? STATE_KA_3 : STATE_IDLE;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case STATE_KA_3:
|
||||
if (b == 0x00) {
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER
|
||||
gSerialViewerFeatures = 0;
|
||||
#endif
|
||||
connected = true;
|
||||
}
|
||||
*state = STATE_IDLE;
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER
|
||||
case STATE_KA_FEATURE:
|
||||
gSerialViewerFeatures = b;
|
||||
connected = true;
|
||||
*state = STATE_IDLE;
|
||||
break;
|
||||
#endif
|
||||
|
||||
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,6 +184,25 @@ static const KEY_Code_t keyboard[5][4] = {
|
||||
|
||||
KEY_Code_t KEYBOARD_Poll(void)
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
// 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;
|
||||
|
||||
// Scan all 5 columns - never break early to avoid GPIO state issues
|
||||
@@ -153,4 +278,19 @@ KEY_Code_t KEYBOARD_Poll(void)
|
||||
GPIO_SetOutputPin(PIN_COLS);
|
||||
|
||||
return Key;
|
||||
}
|
||||
}
|
||||
|
||||
KEY_Code_t KEYBOARD_GetKey(void)
|
||||
{
|
||||
KEY_Code_t btn = KEYBOARD_Poll();
|
||||
if (btn == KEY_INVALID && GPIO_IsPttPressed())
|
||||
{
|
||||
btn = KEY_PTT;
|
||||
}
|
||||
return btn;
|
||||
}
|
||||
|
||||
void HideFKeyIcon(void) {
|
||||
gWasFKeyPressed = false;
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
+33
-1
@@ -45,12 +45,44 @@ 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,
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER
|
||||
STATE_KA_FEATURE,
|
||||
#endif
|
||||
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_K5VIEWER
|
||||
// Serial-injected key (written by UART/VCP parser, consumed by KEYBOARD_Poll).
|
||||
extern volatile KEY_Code_t gKeyFromSerial;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER
|
||||
#define SERIAL_VIEWER_FEATURE_RF_LOG 0x01u
|
||||
#define SERIAL_VIEWER_FEATURE_RF_LOG_HISTORY 0x02u
|
||||
#define SERIAL_VIEWER_FEATURE_RF_LOG_RESTART 0x80u
|
||||
|
||||
// Extension flags announced by the viewer's feature keepalive.
|
||||
extern volatile uint8_t gSerialViewerFeatures;
|
||||
#endif
|
||||
|
||||
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
|
||||
+10
-6
@@ -40,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()
|
||||
@@ -256,9 +256,9 @@ void PY25Q16_WriteBuffer(uint32_t Address, const void *pBuffer, uint32_t Size, b
|
||||
printf("spi flash write: %06x %ld %d\n", Address, Size, Append);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_DEBUG
|
||||
gDebug++;
|
||||
#endif
|
||||
//#ifdef ENABLE_FEAT_F4HWN_DEBUG
|
||||
// gDebug++;
|
||||
//#endif
|
||||
|
||||
uint32_t SecIndex = Address / SECTOR_SIZE;
|
||||
uint32_t SecAddr = SecIndex * SECTOR_SIZE;
|
||||
@@ -284,9 +284,13 @@ void PY25Q16_WriteBuffer(uint32_t Address, const void *pBuffer, uint32_t Size, b
|
||||
if (0 != memcmp(pBuffer, (char *)SectorCache + SecOffset, SecSize))
|
||||
{
|
||||
bool Erase = false;
|
||||
const uint8_t *oldData = SectorCache + SecOffset;
|
||||
const uint8_t *newData = (const uint8_t *)pBuffer;
|
||||
|
||||
for (uint32_t i = 0; i < SecSize; i++)
|
||||
{
|
||||
if (0xff != SectorCache[SecOffset + i])
|
||||
// NOR flash programming can only change bits from 1 to 0.
|
||||
if ((oldData[i] & newData[i]) != newData[i])
|
||||
{
|
||||
Erase = true;
|
||||
break;
|
||||
@@ -491,4 +495,4 @@ void DMA1_Channel4_5_6_7_IRQHandler()
|
||||
|
||||
TC_Flag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -24,6 +24,7 @@
|
||||
#include "driver/st7565.h"
|
||||
#include "driver/system.h"
|
||||
#include "misc.h"
|
||||
#include "k5viewer.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_K5VIEWER
|
||||
K5VIEWER_Update(true); // Force immediate capture
|
||||
#endif
|
||||
}
|
||||
|
||||
void ST7565_BlitStatusLine(void)
|
||||
@@ -256,7 +260,7 @@ void ST7565_FillScreen(uint8_t value)
|
||||
// D=0, display OFF
|
||||
#define ST7565_CMD_DISPLAY_ON_OFF 0xAE
|
||||
|
||||
uint8_t cmds[] = {
|
||||
const uint8_t cmds[] = {
|
||||
ST7565_CMD_BIAS_SELECT | 0, // Select bias setting: 1/9
|
||||
ST7565_CMD_COM_DIRECTION | (0 << 3), // Set output direction of COM: normal
|
||||
ST7565_CMD_SEG_DIRECTION | 1, // Set scan direction of SEG: reverse
|
||||
|
||||
@@ -20,8 +20,9 @@
|
||||
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)
|
||||
|
||||
+25
-8
@@ -22,7 +22,10 @@
|
||||
#include "py32f071_ll_dma.h"
|
||||
#include "py32f071_ll_gpio.h"
|
||||
#include "py32f071_ll_usart.h"
|
||||
#include "driver/uart.h"
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
#include "driver/keyboard.h"
|
||||
#endif
|
||||
|
||||
#define USARTx USART1
|
||||
#define DMA_CHANNEL LL_DMA_CHANNEL_2
|
||||
@@ -147,14 +150,28 @@ void UART_LogSend(const void *pBuffer, uint32_t Size)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
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
|
||||
+1
-1
@@ -27,7 +27,7 @@ void UART_Init(void);
|
||||
void UART_Send(const void *pBuffer, uint32_t Size);
|
||||
void UART_LogSend(const void *pBuffer, uint32_t Size);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
bool UART_IsCableConnected(void);
|
||||
#endif
|
||||
|
||||
|
||||
+46
-9
@@ -18,6 +18,10 @@
|
||||
#include "usb_config.h"
|
||||
#include "py32f071_ll_bus.h"
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
#include "driver/keyboard.h"
|
||||
#endif
|
||||
|
||||
uint8_t VCP_RxBuf[VCP_RX_BUF_SIZE];
|
||||
volatile uint32_t VCP_RxBufPointer = 0;
|
||||
|
||||
@@ -38,24 +42,57 @@ void VCP_Init()
|
||||
NVIC_EnableIRQ(USBD_IRQn);
|
||||
}
|
||||
|
||||
bool VCP_ScreenshotPing(void)
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
bool VCP_K5ViewerPing(void)
|
||||
{
|
||||
static uint32_t read_ptr = 0;
|
||||
// State machine for parsing incoming packets:
|
||||
// Keepalive: 0x55 0xAA 0x00 0x00 → viewer alive, no extensions
|
||||
// Feature keepalive (ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER builds only):
|
||||
// 0x55 0xAA 0x05 <flags> → viewer alive, extensions enabled
|
||||
// flags bit 0 = RF log stream
|
||||
// flags bit 1 = paged RF log history
|
||||
// flags bit 7 = restart RF log synchronization
|
||||
// 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 0x05 → KA_FEATURE, else IDLE)
|
||||
// KA_3 → 0x00 → keepalive OK, clear extensions, IDLE
|
||||
// KA_FEATURE → <flags> → keepalive OK, set extensions, 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
|
||||
|
||||
uint32_t write_ptr = VCP_RxBufPointer;
|
||||
static uint8_t read_ptr = 0;
|
||||
static ParseState_t state = STATE_IDLE;
|
||||
|
||||
while (read_ptr != write_ptr)
|
||||
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 (b == 0x55)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if(KEYBOARD_ProcessProtocolByte(&state, b))
|
||||
connected = true;
|
||||
}
|
||||
return false;
|
||||
|
||||
return connected;
|
||||
}
|
||||
#endif // ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
+4
-1
@@ -28,7 +28,10 @@ extern uint8_t VCP_RxBuf[VCP_RX_BUF_SIZE];
|
||||
extern volatile uint32_t VCP_RxBufPointer;
|
||||
|
||||
void VCP_Init();
|
||||
bool VCP_ScreenshotPing(void);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
bool VCP_K5ViewerPing(void);
|
||||
#endif
|
||||
|
||||
static inline void VCP_Send(const uint8_t *Buf, uint32_t Size)
|
||||
{
|
||||
|
||||
+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
|
||||
}
|
||||
+28
-9
@@ -33,6 +33,9 @@
|
||||
#include "frequencies.h"
|
||||
#include "functions.h"
|
||||
#include "helper/battery.h"
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
#include "app/rxtx_log.h"
|
||||
#endif
|
||||
#include "misc.h"
|
||||
#include "radio.h"
|
||||
#include "settings.h"
|
||||
@@ -118,14 +121,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 +154,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)
|
||||
@@ -246,8 +242,31 @@ void FUNCTION_Select(FUNCTION_Type_t Function)
|
||||
const FUNCTION_Type_t PreviousFunction = gCurrentFunction;
|
||||
const bool bWasPowerSave = PreviousFunction == FUNCTION_POWER_SAVE;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
const bool previousWasActive =
|
||||
PreviousFunction == FUNCTION_TRANSMIT ||
|
||||
PreviousFunction == FUNCTION_MONITOR ||
|
||||
PreviousFunction == FUNCTION_RECEIVE;
|
||||
const bool previousWasTx = PreviousFunction == FUNCTION_TRANSMIT;
|
||||
const bool nextIsActive =
|
||||
Function == FUNCTION_TRANSMIT ||
|
||||
Function == FUNCTION_MONITOR ||
|
||||
Function == FUNCTION_RECEIVE;
|
||||
const bool nextIsTx = Function == FUNCTION_TRANSMIT;
|
||||
|
||||
if (previousWasActive &&
|
||||
(!nextIsActive ||
|
||||
(previousWasTx != nextIsTx)))
|
||||
RXTX_LOG_EndActive();
|
||||
#endif
|
||||
|
||||
gCurrentFunction = Function;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
if (nextIsActive && !nextIsTx && (!previousWasActive || previousWasTx))
|
||||
RXTX_LOG_BeginRx(gRxVfo, Function);
|
||||
#endif
|
||||
|
||||
if (bWasPowerSave && Function != FUNCTION_POWER_SAVE) {
|
||||
BK4819_Conditional_RX_TurnOn_and_GPIO6_Enable();
|
||||
gRxIdleMode = false;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#ifndef FUNCTIONS_H
|
||||
#define FUNCTIONS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum FUNCTION_Type_t
|
||||
|
||||
@@ -228,7 +228,7 @@ void BATTERY_TimeSlice500ms(void)
|
||||
|
||||
if (lowBatteryCountdown < lowBatteryPeriod) {
|
||||
if (lowBatteryCountdown == lowBatteryPeriod-1 && !gChargingWithTypeC && !gLowBatteryConfirmed) {
|
||||
AUDIO_PlayBeep(BEEP_500HZ_60MS_DOUBLE_BEEP);
|
||||
AUDIO_PlayBeep(BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -241,7 +241,7 @@ void BATTERY_TimeSlice500ms(void)
|
||||
|
||||
// not on charge
|
||||
if (!gLowBatteryConfirmed) {
|
||||
AUDIO_PlayBeep(BEEP_500HZ_60MS_DOUBLE_BEEP);
|
||||
AUDIO_PlayBeep(BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL);
|
||||
#ifdef ENABLE_VOICE
|
||||
AUDIO_SetVoiceID(0, VOICE_ID_LOW_VOLTAGE);
|
||||
#endif
|
||||
|
||||
+6
-6
@@ -73,12 +73,14 @@ BOOT_Mode_t BOOT_GetMode(void)
|
||||
|
||||
void BOOT_ProcessMode(BOOT_Mode_t Mode)
|
||||
{
|
||||
GUI_DisplayType_t display = DISPLAY_MAIN;
|
||||
|
||||
if (Mode == BOOT_MODE_F_LOCK)
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
gEeprom.CURRENT_STATE = 0; // Don't resume is active...
|
||||
#endif
|
||||
GUI_SelectNextDisplay(DISPLAY_MENU);
|
||||
display = DISPLAY_MENU;
|
||||
}
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
else
|
||||
@@ -119,11 +121,9 @@ void BOOT_ProcessMode(BOOT_Mode_t Mode)
|
||||
gEeprom.CURRENT_STATE = 0; // Don't resume is active...
|
||||
#endif
|
||||
|
||||
GUI_SelectNextDisplay(DISPLAY_AIRCOPY);
|
||||
display = DISPLAY_AIRCOPY;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
GUI_SelectNextDisplay(DISPLAY_MAIN);
|
||||
}
|
||||
|
||||
GUI_SelectNextDisplay(display);
|
||||
}
|
||||
+338
@@ -0,0 +1,338 @@
|
||||
/* Copyright 2024 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.
|
||||
*/
|
||||
|
||||
#include "debugging.h"
|
||||
#include "driver/st7565.h"
|
||||
#include "k5viewer.h"
|
||||
#include "misc.h"
|
||||
#include "driver/vcp.h"
|
||||
#include "driver/keyboard.h"
|
||||
#include "driver/bk4819.h"
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER
|
||||
#include "app/rxtx_log.h"
|
||||
#endif
|
||||
|
||||
// SRAM optimization: minimize static allocations
|
||||
// - previousHash: one fingerprint per 8-byte chunk instead of a full
|
||||
// 1024-byte copy of the previous frame (chunks are only compared,
|
||||
// never retransmitted from history). A 16-bit fingerprint keeps the
|
||||
// collision odds at ~1/65536 per chunk, so a stale chunk slipping
|
||||
// through is rare; the forcedBlock rotation repairs that residual
|
||||
// within at most 128 frames.
|
||||
// Stack optimization: chunks are computed on demand straight from
|
||||
// gStatusLine/gFrameBuffer instead of building the whole 1024-byte
|
||||
// frame on the stack. Changed chunks are tracked in a 16-byte bitmap.
|
||||
// Peak stack drops from ~1.3 KB to ~40 bytes, at the cost of
|
||||
// transforming each transmitted chunk twice (negligible next to the
|
||||
// blocking UART transfer).
|
||||
static uint16_t previousHash[128];
|
||||
static uint8_t previousStateFlags = 0xFF;
|
||||
static uint8_t forcedBlock = 0;
|
||||
static uint8_t keepAlive = 3;
|
||||
static bool hasConnectionPing = false;
|
||||
static bool wasConnected = false;
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER
|
||||
static uint32_t previousRfLogSignature = 0;
|
||||
static bool rfLogSent = false;
|
||||
// Pagination bound of the history dump: rows with trafficSeq below this
|
||||
// value remain to be sent; HISTORY_START arms a new dump, 0 means done.
|
||||
static uint32_t rfLogHistoryBefore = RXTX_LOG_K5VIEWER_HISTORY_START;
|
||||
#endif
|
||||
|
||||
// FNV-1a over one 8-byte chunk, folded to 16 bits
|
||||
static uint16_t K5VIEWER_Hash(const uint8_t *data)
|
||||
{
|
||||
uint32_t h = 2166136261u;
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
h = (h ^ data[i]) * 16777619u;
|
||||
}
|
||||
return (uint16_t)(h ^ (h >> 16));
|
||||
}
|
||||
|
||||
void K5VIEWER_ParseInput(void)
|
||||
{
|
||||
if (K5VIEWER_IsLocked())
|
||||
return;
|
||||
|
||||
if (UART_IsCableConnected()) {
|
||||
keepAlive = 15;
|
||||
hasConnectionPing = true;
|
||||
gUSB_K5ViewerEnabled = false;
|
||||
}
|
||||
else if (VCP_K5ViewerPing()) {
|
||||
keepAlive = 15;
|
||||
hasConnectionPing = true;
|
||||
gUSB_K5ViewerEnabled = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void K5VIEWER_Send(const uint8_t *buf, uint16_t len)
|
||||
{
|
||||
if (gUSB_K5ViewerEnabled) {
|
||||
cdc_acm_data_send_with_dtr(buf, len);
|
||||
} else {
|
||||
UART_Send(buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
enum {
|
||||
K5VIEWER_CHUNK_SIZE = 8,
|
||||
K5VIEWER_CHUNKS_PER_LINE = 16,
|
||||
K5VIEWER_HALF_LINE_COLUMNS = LCD_WIDTH / 2,
|
||||
K5VIEWER_MARKER_BASE = 0xF0,
|
||||
K5VIEWER_TYPE_DIFF = 0x02,
|
||||
K5VIEWER_TYPE_RXTX_LOG = 0x05,
|
||||
K5VIEWER_TYPE_RXTX_LOG_HISTORY = 0x06,
|
||||
K5VIEWER_FLAG_DEEP_SLEEP = 1 << 0,
|
||||
K5VIEWER_FLAG_LED_RED = 1 << 1,
|
||||
K5VIEWER_FLAG_LED_GREEN = 1 << 2,
|
||||
};
|
||||
|
||||
static uint8_t K5VIEWER_StateFlags(void)
|
||||
{
|
||||
uint8_t flags = 0;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SLEEP
|
||||
if (gWakeUp)
|
||||
flags |= K5VIEWER_FLAG_DEEP_SLEEP;
|
||||
#endif
|
||||
|
||||
if (BK4819_IsGpioOutSet(BK4819_GPIO5_PIN1_RED))
|
||||
flags |= K5VIEWER_FLAG_LED_RED;
|
||||
|
||||
if (BK4819_IsGpioOutSet(BK4819_GPIO6_PIN2_GREEN))
|
||||
flags |= K5VIEWER_FLAG_LED_GREEN;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER
|
||||
static bool K5VIEWER_HasPendingRfLogUpdate(void)
|
||||
{
|
||||
if (!RXTX_LOG_IsEnabled())
|
||||
rfLogHistoryBefore = RXTX_LOG_K5VIEWER_HISTORY_START;
|
||||
|
||||
if ((gSerialViewerFeatures & SERIAL_VIEWER_FEATURE_RF_LOG_RESTART) != 0) {
|
||||
gSerialViewerFeatures &= ~SERIAL_VIEWER_FEATURE_RF_LOG_RESTART;
|
||||
rfLogSent = false;
|
||||
rfLogHistoryBefore = RXTX_LOG_K5VIEWER_HISTORY_START;
|
||||
}
|
||||
|
||||
if ((gSerialViewerFeatures & SERIAL_VIEWER_FEATURE_RF_LOG) == 0)
|
||||
return false;
|
||||
|
||||
return !rfLogSent || RXTX_LOG_K5ViewerSignature() != previousRfLogSignature;
|
||||
}
|
||||
|
||||
static void K5VIEWER_SendRfLogFrameHeader(uint8_t type, uint16_t len)
|
||||
{
|
||||
uint8_t header[5] = {0xAA, 0x55, type, (uint8_t)(len >> 8), (uint8_t)len};
|
||||
|
||||
K5VIEWER_Send(header, sizeof(header));
|
||||
}
|
||||
|
||||
static void K5VIEWER_SendRfLogFrameEnd(void)
|
||||
{
|
||||
const uint8_t end = 0x0A;
|
||||
|
||||
K5VIEWER_Send(&end, 1);
|
||||
}
|
||||
|
||||
static void K5VIEWER_SendRfLog(void)
|
||||
{
|
||||
// Capture the signature before streaming: a state change landing
|
||||
// during the blocking send still differs afterwards and triggers a
|
||||
// resend on the next cycle.
|
||||
previousRfLogSignature = RXTX_LOG_K5ViewerSignature();
|
||||
rfLogSent = true;
|
||||
|
||||
const uint16_t size = RXTX_LOG_IsEnabled()
|
||||
? RXTX_LOG_K5VIEWER_PACKET_SIZE
|
||||
: RXTX_LOG_K5VIEWER_STATUS_PACKET_SIZE;
|
||||
K5VIEWER_SendRfLogFrameHeader(K5VIEWER_TYPE_RXTX_LOG, size);
|
||||
RXTX_LOG_SendK5ViewerPacket(K5VIEWER_Send);
|
||||
K5VIEWER_SendRfLogFrameEnd();
|
||||
}
|
||||
|
||||
static bool K5VIEWER_HasPendingRfLogHistory(void)
|
||||
{
|
||||
return RXTX_LOG_IsEnabled() &&
|
||||
(gSerialViewerFeatures & SERIAL_VIEWER_FEATURE_RF_LOG_HISTORY) != 0 &&
|
||||
rfLogHistoryBefore != 0;
|
||||
}
|
||||
|
||||
static void K5VIEWER_SendRfLogHistory(void)
|
||||
{
|
||||
K5VIEWER_SendRfLogFrameHeader(K5VIEWER_TYPE_RXTX_LOG_HISTORY,
|
||||
RXTX_LOG_K5VIEWER_HISTORY_PACKET_SIZE);
|
||||
rfLogHistoryBefore = RXTX_LOG_SendK5ViewerHistoryPage(rfLogHistoryBefore, K5VIEWER_Send);
|
||||
K5VIEWER_SendRfLogFrameEnd();
|
||||
}
|
||||
#endif
|
||||
|
||||
bool K5VIEWER_HasPendingStateChange(void)
|
||||
{
|
||||
if (gUART_LockK5Viewer > 0 || keepAlive == 0 || !hasConnectionPing)
|
||||
return false;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER
|
||||
if (K5VIEWER_HasPendingRfLogUpdate())
|
||||
return true;
|
||||
if (K5VIEWER_HasPendingRfLogHistory())
|
||||
return true;
|
||||
#endif
|
||||
|
||||
return !wasConnected || K5VIEWER_StateFlags() != previousStateFlags;
|
||||
}
|
||||
|
||||
// Compute one 8-byte output chunk directly from the display buffers.
|
||||
// Frame layout: per line (status + 7 frame lines), 8 bit layers of
|
||||
// 16 bytes each. Each bit layer is split into two 64-column chunks.
|
||||
static void K5VIEWER_Chunk(uint8_t chunkIdx, uint8_t *dest)
|
||||
{
|
||||
const uint8_t chunkInLine = chunkIdx % K5VIEWER_CHUNKS_PER_LINE;
|
||||
const uint8_t line = chunkIdx / K5VIEWER_CHUNKS_PER_LINE;
|
||||
const uint8_t bit = chunkInLine / 2;
|
||||
const uint8_t columnBase = (chunkInLine % 2) * K5VIEWER_HALF_LINE_COLUMNS;
|
||||
const uint8_t *src = (line == 0 ? gStatusLine : gFrameBuffer[line - 1])
|
||||
+ columnBase;
|
||||
|
||||
for (uint8_t j = 0; j < K5VIEWER_CHUNK_SIZE; j++) {
|
||||
uint8_t acc = 0;
|
||||
for (uint8_t k = 0; k < K5VIEWER_CHUNK_SIZE; k++) {
|
||||
if (src[j * K5VIEWER_CHUNK_SIZE + k] & (1 << bit)) acc |= (1 << k);
|
||||
}
|
||||
dest[j] = gSetting_set_inv ? ~acc : acc;
|
||||
}
|
||||
}
|
||||
|
||||
void K5VIEWER_Update(bool force)
|
||||
{
|
||||
if (K5VIEWER_IsLocked())
|
||||
return;
|
||||
|
||||
if (keepAlive > 0) {
|
||||
if (--keepAlive == 0) {
|
||||
// Connection just lost → reset state for next reconnection
|
||||
wasConnected = false;
|
||||
hasConnectionPing = false;
|
||||
previousStateFlags = 0xFF;
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER
|
||||
gSerialViewerFeatures = 0;
|
||||
rfLogSent = false;
|
||||
rfLogHistoryBefore = RXTX_LOG_K5VIEWER_HISTORY_START;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Connection is alive — detect reconnection and force full frame
|
||||
if (!wasConnected) {
|
||||
force = true;
|
||||
}
|
||||
|
||||
const uint8_t stateFlags = K5VIEWER_StateFlags();
|
||||
const bool stateChanged = (stateFlags != previousStateFlags);
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER
|
||||
const bool rfLogPending = K5VIEWER_HasPendingRfLogUpdate();
|
||||
const bool rfLogHistoryPending = K5VIEWER_HasPendingRfLogHistory();
|
||||
#else
|
||||
const bool rfLogPending = false;
|
||||
const bool rfLogHistoryPending = false;
|
||||
#endif
|
||||
|
||||
// ==== FIRST PASS: Count changed chunks ====
|
||||
uint16_t deltaLen = 0;
|
||||
uint8_t changedBitmap[16] = {0}; // 1 bit per chunk
|
||||
uint8_t chunk[9]; // [0] = index, [1..8] = payload
|
||||
|
||||
for (uint8_t chunkIdx = 0; chunkIdx < 128; chunkIdx++) {
|
||||
K5VIEWER_Chunk(chunkIdx, &chunk[1]);
|
||||
|
||||
bool changed = K5VIEWER_Hash(&chunk[1]) != previousHash[chunkIdx];
|
||||
bool isForced = (chunkIdx == forcedBlock);
|
||||
|
||||
if (changed || isForced || force) {
|
||||
changedBitmap[chunkIdx >> 3] |= 1 << (chunkIdx & 7);
|
||||
deltaLen += 9;
|
||||
}
|
||||
}
|
||||
|
||||
forcedBlock = (forcedBlock + 1) % 128;
|
||||
|
||||
if (deltaLen == 0 && !stateChanged && !rfLogPending && !rfLogHistoryPending)
|
||||
return;
|
||||
|
||||
// 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;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER
|
||||
// A pending RF log packet may be the only thing to send: skip the
|
||||
// frame section when it carries nothing. Without the RF log stream
|
||||
// the early return above already guarantees this condition.
|
||||
if (deltaLen != 0 || stateChanged)
|
||||
#endif
|
||||
{
|
||||
// ==== Send version marker and state flags ====
|
||||
// 0xF0 keeps a resync-safe marker before the standard AA 55 header.
|
||||
uint8_t versionMarker = K5VIEWER_MARKER_BASE | stateFlags;
|
||||
K5VIEWER_Send(&versionMarker, 1);
|
||||
|
||||
// ==== Send header ====
|
||||
uint8_t header[5] = {
|
||||
0xAA, 0x55, K5VIEWER_TYPE_DIFF,
|
||||
(uint8_t)(deltaLen >> 8),
|
||||
(uint8_t)(deltaLen & 0xFF)
|
||||
};
|
||||
|
||||
K5VIEWER_Send(header, 5);
|
||||
|
||||
// ==== SECOND PASS: Send only changed chunks ====
|
||||
for (uint8_t chunkIdx = 0; chunkIdx < 128; chunkIdx++) {
|
||||
if (!(changedBitmap[chunkIdx >> 3] & (1 << (chunkIdx & 7))))
|
||||
continue;
|
||||
|
||||
chunk[0] = chunkIdx;
|
||||
K5VIEWER_Chunk(chunkIdx, &chunk[1]);
|
||||
|
||||
K5VIEWER_Send(chunk, 9);
|
||||
|
||||
// Update the fingerprint only once the chunk is actually sent,
|
||||
// so chunks skipped by an early return stay marked as changed.
|
||||
// Hashing the recomputed payload also keeps the fingerprint in
|
||||
// sync if the display buffer changed between the two passes.
|
||||
previousHash[chunkIdx] = K5VIEWER_Hash(&chunk[1]);
|
||||
}
|
||||
|
||||
uint8_t end = 0x0A;
|
||||
K5VIEWER_Send(&end, 1);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER
|
||||
if (rfLogPending)
|
||||
K5VIEWER_SendRfLog();
|
||||
if (rfLogHistoryPending)
|
||||
K5VIEWER_SendRfLogHistory();
|
||||
#endif
|
||||
|
||||
previousStateFlags = stateFlags;
|
||||
wasConnected = true;
|
||||
}
|
||||
@@ -14,9 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef SCREENSHOT_H
|
||||
#define SCREENSHOT_H
|
||||
#ifndef K5VIEWER_H
|
||||
#define K5VIEWER_H
|
||||
|
||||
void getScreenShot(bool force);
|
||||
void K5VIEWER_Update(bool force);
|
||||
void K5VIEWER_ParseInput(void);
|
||||
bool K5VIEWER_HasPendingStateChange(void);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
+36
-69
@@ -24,6 +24,9 @@
|
||||
|
||||
#include "audio.h"
|
||||
#include "board.h"
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
#include "app/rxtx_log.h"
|
||||
#endif
|
||||
#include "misc.h"
|
||||
#include "radio.h"
|
||||
#include "settings.h"
|
||||
@@ -99,6 +102,10 @@ void Main(void)
|
||||
|
||||
SETTINGS_InitEEPROM();
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
RXTX_LOG_Init();
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
gDW = gEeprom.DUAL_WATCH;
|
||||
gCB = gEeprom.CROSS_BAND_RX_TX;
|
||||
@@ -157,11 +164,7 @@ void Main(void)
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
gEeprom.KEY_LOCK = 0;
|
||||
SETTINGS_SaveSettings();
|
||||
gMenuCursor = MENU_ITEMS;
|
||||
|
||||
#ifdef ENABLE_NOAA
|
||||
gMenuCursor += 1; // move to hidden section, fix me if change... !!!
|
||||
#endif
|
||||
gMenuCursor = UI_MENU_GetMenuIdx(FIRST_HIDDEN_MENU_ITEM);
|
||||
gSubMenuSelection = gSetting_F_LOCK;
|
||||
#endif
|
||||
}
|
||||
@@ -249,6 +252,9 @@ void Main(void)
|
||||
|
||||
BOOT_ProcessMode(BootMode);
|
||||
|
||||
if (gEeprom.AUTO_KEYPAD_LOCK && !gEeprom.KEY_LOCK)
|
||||
gKeyLockCountdown = gEeprom.AUTO_KEYPAD_LOCK * 30; // 15 seconds step
|
||||
|
||||
// GPIO_ClearBit(&GPIOA->DATA, GPIOA_PIN_VOICE_0);
|
||||
|
||||
gUpdateStatus = true;
|
||||
@@ -277,77 +283,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) {
|
||||
|
||||
+33
-9
@@ -87,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;
|
||||
@@ -116,6 +116,10 @@ enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
|
||||
bool gWakeUp = false;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
bool gSetting_set_scn = 1;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
uint8_t gSetting_set_pwr = 1;
|
||||
bool gSetting_set_ptt = 0;
|
||||
@@ -123,19 +127,23 @@ enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
|
||||
uint8_t gSetting_set_ctr = 10;
|
||||
bool gSetting_set_inv = false;
|
||||
uint8_t gSetting_set_eot = 0;
|
||||
bool gSetting_set_lck = false;
|
||||
uint8_t gSetting_set_lck = SET_LCK_KEYS;
|
||||
bool gSetting_set_met = 0;
|
||||
bool gSetting_set_gui = 0;
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
uint8_t gSetting_set_audio = 0;
|
||||
uint8_t gSetting_set_audio_fm = 0;
|
||||
uint8_t gSetting_set_audio_am = 0;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
bool gSetting_set_nfm = 0;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
uint8_t gSetting_set_sav = SET_SAV_OFF;
|
||||
#endif
|
||||
bool gSetting_set_tmr = 0;
|
||||
bool gSetting_set_ptt_session;
|
||||
#ifdef ENABLE_FEAT_F4HWN_DEBUG
|
||||
uint16_t gDebug;
|
||||
int16_t gDebug;
|
||||
#endif
|
||||
uint8_t gDW = 0;
|
||||
uint8_t gCB = 0;
|
||||
@@ -149,6 +157,7 @@ enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
|
||||
bool gPowerHigh = false;
|
||||
bool gRemoveOffset = false;
|
||||
#endif
|
||||
int8_t dBmCorrTable[7] = {-15, -16, -10, -4, -7, -6, -1};
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_AUDIO_BAR
|
||||
@@ -201,9 +210,9 @@ volatile bool gTxTimeoutReached;
|
||||
#ifdef ENABLE_FEAT_F4HWN_RX_TX_TIMER
|
||||
volatile uint16_t gRxTimerCountdown_500ms;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
volatile uint8_t gUART_LockScreenshot = 0; // lock screenshot if Chirp is used
|
||||
bool gUSB_ScreenshotEnabled = false;
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
volatile uint8_t gUART_LockK5Viewer = 0; // lock the K5Viewer stream if Chirp is used
|
||||
bool gUSB_K5ViewerEnabled = false;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -241,6 +250,7 @@ volatile uint16_t gScanPauseDelayIn_10ms;
|
||||
uint16_t gMenuCountdown;
|
||||
bool gPttWasReleased;
|
||||
bool gPttWasPressed;
|
||||
bool gHasVfoBackup;
|
||||
uint8_t gKeypadLocked;
|
||||
bool gFlagReconfigureVfos;
|
||||
uint8_t gVfoConfigureMode;
|
||||
@@ -292,7 +302,7 @@ uint8_t gFSKWriteIndex;
|
||||
|
||||
#ifdef ENABLE_NOAA
|
||||
bool gIsNoaaMode;
|
||||
uint16_t gNoaaChannel;
|
||||
uint8_t gNoaaChannel;
|
||||
#endif
|
||||
|
||||
bool gUpdateDisplay;
|
||||
@@ -328,12 +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() { ; }
|
||||
@@ -632,4 +644,16 @@ void MR_PrintCacheStats(void)
|
||||
// cache_hits, cache_misses, MR_GetCacheHitRate());
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
bool K5VIEWER_IsLocked(void)
|
||||
{
|
||||
if (gUART_LockK5Viewer > 0) {
|
||||
gUART_LockK5Viewer--;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
+46
-10
@@ -39,8 +39,6 @@
|
||||
#define FM_CHANNELS_MAX 48
|
||||
#define MR_CHANNELS_MAX 1024
|
||||
#define MR_CHANNELS_LIST 24
|
||||
#define MENU_ITEMS 69
|
||||
|
||||
// CACHE-BASED OPTIMIZATION: Only keep active channels in RAM
|
||||
// Full array stays in EEPROM, cache holds ~10 most-used channels
|
||||
#define MR_CHANNELS_CACHE_SIZE 10
|
||||
@@ -148,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;
|
||||
@@ -178,26 +176,57 @@ 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
|
||||
// Keypad lock scope. Values are BOTH a bitmask (ACTIONS/PTT bits tested
|
||||
// individually) AND a contiguous 0..3 menu index: the order must stay
|
||||
// aligned with gSubMenu_SET_LCK[] and any new entry must keep the range
|
||||
// contiguous so that SET_LCK_LEN remains valid as menu bound and EEPROM
|
||||
// range check.
|
||||
enum SET_LCK_t {
|
||||
SET_LCK_KEYS = 0u,
|
||||
SET_LCK_ACTIONS = 1u,
|
||||
SET_LCK_PTT = 2u,
|
||||
SET_LCK_ACTIONS_PTT = SET_LCK_ACTIONS | SET_LCK_PTT,
|
||||
SET_LCK_LEN
|
||||
};
|
||||
|
||||
#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;
|
||||
extern uint8_t gSetting_set_ctr;
|
||||
extern bool gSetting_set_inv;
|
||||
extern uint8_t gSetting_set_eot;
|
||||
extern bool gSetting_set_lck;
|
||||
extern uint8_t gSetting_set_lck;
|
||||
extern bool gSetting_set_met;
|
||||
extern bool gSetting_set_gui;
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
extern uint8_t gSetting_set_audio;
|
||||
extern uint8_t gSetting_set_audio_fm;
|
||||
extern uint8_t gSetting_set_audio_am;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
extern bool gSetting_set_nfm;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
extern uint8_t gSetting_set_sav;
|
||||
#endif
|
||||
extern bool gSetting_set_tmr;
|
||||
extern bool gSetting_set_ptt_session;
|
||||
#ifdef ENABLE_FEAT_F4HWN_DEBUG
|
||||
extern uint16_t gDebug;
|
||||
extern int16_t gDebug;
|
||||
#endif
|
||||
extern uint8_t gDW;
|
||||
extern uint8_t gCB;
|
||||
@@ -211,6 +240,7 @@ extern enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
|
||||
extern bool gPowerHigh;
|
||||
extern bool gRemoveOffset;
|
||||
#endif
|
||||
extern int8_t dBmCorrTable[7];
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_AUDIO_BAR
|
||||
@@ -319,9 +349,11 @@ extern volatile bool gTxTimeoutReached;
|
||||
#ifdef ENABLE_FEAT_F4HWN_RX_TX_TIMER
|
||||
extern volatile uint16_t gRxTimerCountdown_500ms;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
extern volatile uint8_t gUART_LockScreenshot; // lock screenshot if Chirp is used
|
||||
extern bool gUSB_ScreenshotEnabled;
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
extern volatile uint8_t gUART_LockK5Viewer; // lock the K5Viewer stream if Chirp is used
|
||||
extern bool gUSB_K5ViewerEnabled;
|
||||
|
||||
bool K5VIEWER_IsLocked(void);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -365,6 +397,7 @@ extern AlarmState_t gAlarmState;
|
||||
extern uint16_t gMenuCountdown;
|
||||
extern bool gPttWasReleased;
|
||||
extern bool gPttWasPressed;
|
||||
extern bool gHasVfoBackup;
|
||||
extern bool gFlagReconfigureVfos;
|
||||
extern uint8_t gVfoConfigureMode;
|
||||
extern bool gFlagResetVfos;
|
||||
@@ -449,12 +482,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);
|
||||
@@ -464,4 +500,4 @@ void FUNCTION_NOP();
|
||||
|
||||
static inline bool SerialConfigInProgress() { return gSerialConfigCountDown_500ms != 0; }
|
||||
|
||||
#endif
|
||||
#endif
|
||||
+267
-246
@@ -19,6 +19,9 @@
|
||||
|
||||
#include "am_fix.h"
|
||||
#include "app/dtmf.h"
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
#include "app/rxtx_log.h"
|
||||
#endif
|
||||
#ifdef ENABLE_FMRADIO
|
||||
#include "app/fm.h"
|
||||
#endif
|
||||
@@ -54,36 +57,67 @@ const char gModulationStr[MODULATION_UKNOWN][4] = {
|
||||
};
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
static void AUDIO_ApplyProfile(uint8_t profile)
|
||||
|
||||
// About BK4819_WriteRegister(0x2b, val) experimentation...
|
||||
//
|
||||
// 0x000: 300 Hz high-pass filter enabled, 3 kHz low-pass filter enabled, de-emphasis enabled.
|
||||
// Audio impression: the most "classic radio" tuning, more filtered and smoother.
|
||||
//
|
||||
// 0x300: 300 Hz high-pass filter enabled, 3 kHz low-pass filter disabled, de-emphasis disabled.
|
||||
// Audio impression: clearer, brighter, and more open, while still cutting low frequencies.
|
||||
//
|
||||
// 0x400: 300 Hz high-pass filter disabled, 3 kHz low-pass filter enabled, de-emphasis enabled.
|
||||
// Audio impression: fuller low end, but still softened by de-emphasis and upper-frequency limiting.
|
||||
//
|
||||
// 0x500: 300 Hz high-pass filter disabled, 3 kHz low-pass filter enabled, de-emphasis disabled.
|
||||
// Audio impression: fuller bass, more direct sound, while still keeping the 3 kHz top-end limit.
|
||||
|
||||
static void AUDIO_ApplyFMProfile(uint8_t profile)
|
||||
{ // | 0x54 || 0x55 |
|
||||
static const uint16_t fm_profiles[][2] = {
|
||||
{0x9009, 0x3200}, // 0: FLAT
|
||||
{0x9009, 0x33A9}, // 1: CLEAN
|
||||
{0x9009, 0x3600}, // 2: MID
|
||||
{0x8546, 0x3AF0}, // 3: BOOST
|
||||
{0x8566, 0x3D00} // 4: MAX
|
||||
};
|
||||
|
||||
if (profile >= ARRAY_SIZE(fm_profiles))
|
||||
profile = 0;
|
||||
|
||||
BK4819_WriteRegister(0x54, fm_profiles[profile][0]);
|
||||
BK4819_WriteRegister(0x55, fm_profiles[profile][1]);
|
||||
}
|
||||
|
||||
static void AUDIO_ApplyAMProfile(uint8_t profile)
|
||||
{ // | 0x2b || 0x2f || 0x54 || 0x55 |
|
||||
static const uint16_t am_profiles[][4] = {
|
||||
// SHARP (ALPHA test profile) - Narrow IF filter (REG54 bits[14:8]=0, bits[7:0]=9), low IF gain (REG55 bits[11:8]=1, ref=169)
|
||||
// Selective and crisp, best adjacent channel rejection, may sound harsh on strong signals
|
||||
{0x0300, 0x9990, 0x9009, 0x31A9},
|
||||
|
||||
// STOCK - Narrow IF filter (REG54 bits[14:8]=0, bits[7:0]=9), moderate IF gain (REG55 bits[11:8]=4, ref=180)
|
||||
// Selective filter with balanced gain, punchy and detailed, good compromise between rejection and sensitivity
|
||||
{0x0500, 0x9990, 0x9009, 0x31A9},
|
||||
|
||||
// OPEN (BRAVO test profile) - Medium-wide IF filter (REG54 bits[14:8]=8, bits[7:0]=70), high IF gain (REG55 bits[11:8]=8, ref=192)
|
||||
// Wide and pleasant, better sensitivity on weak signals, may struggle with adjacent channel interference
|
||||
{0x0300, 0x9990, 0x8846, 0x38C0}
|
||||
};
|
||||
|
||||
if (profile >= ARRAY_SIZE(am_profiles))
|
||||
profile = 0;
|
||||
|
||||
BK4819_WriteRegister(0x2b, am_profiles[profile][0]);
|
||||
BK4819_WriteRegister(0x2f, am_profiles[profile][1]);
|
||||
BK4819_WriteRegister(0x54, am_profiles[profile][2]);
|
||||
BK4819_WriteRegister(0x55, am_profiles[profile][3]);
|
||||
}
|
||||
|
||||
static void AUDIO_ApplyUSBProfile(void)
|
||||
{
|
||||
switch (profile)
|
||||
{
|
||||
default:
|
||||
case 0: // FLAT
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x3200);
|
||||
break;
|
||||
|
||||
case 1: // CLEAN
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x33A9);
|
||||
break;
|
||||
|
||||
case 2: // MID
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x3600);
|
||||
break;
|
||||
|
||||
case 3: // BOOST
|
||||
BK4819_WriteRegister(0x54, 0x8546);
|
||||
BK4819_WriteRegister(0x55, 0x3AF0);
|
||||
break;
|
||||
|
||||
case 4: // MAX
|
||||
BK4819_WriteRegister(0x54, 0x8566);
|
||||
BK4819_WriteRegister(0x55, 0x3D00);
|
||||
break;
|
||||
}
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x31A9);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -102,28 +136,30 @@ bool RADIO_CheckValidList(uint8_t scanList)
|
||||
return false;
|
||||
}
|
||||
|
||||
void RADIO_NextValidList(void)
|
||||
void RADIO_NextValidList(int8_t direction)
|
||||
{
|
||||
uint8_t startList = gEeprom.SCAN_LIST_DEFAULT;
|
||||
uint8_t attempts = 0;
|
||||
const uint8_t MAX_LISTS = MR_CHANNELS_LIST + 2; // 1-25, includes ALL
|
||||
const uint8_t MAX_VALUE = MR_CHANNELS_LIST + 1; // 25 (1-24 lists + ALL)
|
||||
|
||||
do {
|
||||
// Move to next scan list, wrapping around from 25 to 1
|
||||
gEeprom.SCAN_LIST_DEFAULT = ((gEeprom.SCAN_LIST_DEFAULT + 1) % MAX_LISTS) ?: 1;
|
||||
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++;
|
||||
|
||||
// Check if current list has valid channels
|
||||
if (RADIO_CheckValidList(gEeprom.SCAN_LIST_DEFAULT))
|
||||
return;
|
||||
|
||||
// Stop if we've cycled through all lists or made too many attempts
|
||||
} while (gEeprom.SCAN_LIST_DEFAULT != startList && attempts < MAX_LISTS);
|
||||
} while (gEeprom.SCAN_LIST_DEFAULT != startList && attempts < MAX_VALUE);
|
||||
|
||||
// Safety fallback: if no valid list found, switch to ALL mode
|
||||
// This prevents infinite loops and ensures scanning can continue
|
||||
// Safety fallback: switch to ALL mode
|
||||
if (!RADIO_CheckValidList(gEeprom.SCAN_LIST_DEFAULT)) {
|
||||
gEeprom.SCAN_LIST_DEFAULT = MR_CHANNELS_LIST + 1; // ALL
|
||||
gEeprom.SCAN_LIST_DEFAULT = MAX_VALUE; // ALL (25)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +174,7 @@ bool RADIO_CheckValidChannel(uint16_t channel, bool checkScanList, uint8_t scanL
|
||||
return false;
|
||||
if (att->band > BAND7_470MHz)
|
||||
return false;
|
||||
if (!checkScanList || (scanList > MR_CHANNELS_LIST && att->scanlist != 0))
|
||||
if (!checkScanList || (scanList > MR_CHANNELS_LIST && att->scanlist != 0) || (scanList > 0 && att->scanlist == MR_CHANNELS_LIST + 1))
|
||||
return true;
|
||||
if ((scanList == 0) || (scanList != att->scanlist)) {
|
||||
return false;
|
||||
@@ -200,6 +236,24 @@ void RADIO_InitInfo(VFO_Info_t *pInfo, const uint16_t ChannelSave, const uint32_
|
||||
RADIO_ConfigureSquelchAndOutputPower(pInfo);
|
||||
}
|
||||
|
||||
void RADIO_ValidateAndSetCode(FREQ_Config_t *pFreq_Config, uint8_t tmp) {
|
||||
switch (pFreq_Config->CodeType) {
|
||||
default:
|
||||
case CODE_TYPE_OFF:
|
||||
pFreq_Config->CodeType = CODE_TYPE_OFF;
|
||||
tmp = 0;
|
||||
break;
|
||||
|
||||
case CODE_TYPE_CONTINUOUS_TONE:
|
||||
case CODE_TYPE_DIGITAL:
|
||||
case CODE_TYPE_REVERSE_DIGITAL:
|
||||
if (tmp > ((pFreq_Config->CodeType == CODE_TYPE_CONTINUOUS_TONE ? ARRAY_SIZE(CTCSS_Options) : ARRAY_SIZE(DCS_Options)) - 1))
|
||||
tmp = 0;
|
||||
break;
|
||||
}
|
||||
pFreq_Config->Code = tmp;
|
||||
}
|
||||
|
||||
void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure)
|
||||
{
|
||||
VFO_Info_t *pVfo = &gEeprom.VfoInfo[VFO];
|
||||
@@ -273,7 +327,7 @@ void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure
|
||||
}
|
||||
|
||||
pVfo->Band = band;
|
||||
pVfo->SCANLIST_PARTICIPATION = bParticipation;
|
||||
pVfo->SCANLIST_PARTICIPATION = bParticipation;
|
||||
pVfo->CHANNEL_SAVE = channel;
|
||||
|
||||
uint32_t base;
|
||||
@@ -318,49 +372,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)
|
||||
{
|
||||
@@ -500,17 +513,20 @@ void RADIO_ConfigureSquelchAndOutputPower(VFO_Info_t *pInfo)
|
||||
}
|
||||
else
|
||||
{ // squelch >= 1
|
||||
Base += gEeprom.SQUELCH_LEVEL; // my eeprom squelch-1
|
||||
// VHF UHF
|
||||
PY25Q16_ReadBuffer(Base + 0x00, &pInfo->SquelchOpenRSSIThresh, 1); // 50 10
|
||||
PY25Q16_ReadBuffer(Base + 0x10, &pInfo->SquelchCloseRSSIThresh, 1); // 40 5
|
||||
|
||||
PY25Q16_ReadBuffer(Base + 0x20, &pInfo->SquelchOpenNoiseThresh, 1); // 65 90
|
||||
PY25Q16_ReadBuffer(Base + 0x30, &pInfo->SquelchCloseNoiseThresh, 1); // 70 100
|
||||
|
||||
PY25Q16_ReadBuffer(Base + 0x40, &pInfo->SquelchCloseGlitchThresh, 1); // 90 90
|
||||
PY25Q16_ReadBuffer(Base + 0x50, &pInfo->SquelchOpenGlitchThresh, 1); // 100 100
|
||||
Base += gEeprom.SQUELCH_LEVEL; // my eeprom squelch-1
|
||||
// VHF UHF
|
||||
uint8_t *sq_ptrs[6] = {
|
||||
&pInfo->SquelchOpenRSSIThresh, // 50 10
|
||||
&pInfo->SquelchCloseRSSIThresh, // 40 5
|
||||
&pInfo->SquelchOpenNoiseThresh, // 65 90
|
||||
&pInfo->SquelchCloseNoiseThresh, // 70 100
|
||||
&pInfo->SquelchCloseGlitchThresh, // 90 90
|
||||
&pInfo->SquelchOpenGlitchThresh // 100 100
|
||||
};
|
||||
|
||||
for(uint8_t i = 0; i < 6; i++) {
|
||||
PY25Q16_ReadBuffer(Base + (i * 0x10), sq_ptrs[i], 1);
|
||||
}
|
||||
|
||||
uint16_t noise_open = pInfo->SquelchOpenNoiseThresh;
|
||||
uint16_t noise_close = pInfo->SquelchCloseNoiseThresh;
|
||||
@@ -534,14 +550,14 @@ void RADIO_ConfigureSquelchAndOutputPower(VFO_Info_t *pInfo)
|
||||
if (glitch_close == glitch_open && glitch_close <= 253)
|
||||
glitch_close += 2;
|
||||
|
||||
pInfo->SquelchOpenRSSIThresh = (rssi_open > 255) ? 255 : rssi_open;
|
||||
pInfo->SquelchCloseRSSIThresh = (rssi_close > 255) ? 255 : rssi_close;
|
||||
pInfo->SquelchOpenGlitchThresh = (glitch_open > 255) ? 255 : glitch_open;
|
||||
pInfo->SquelchCloseGlitchThresh = (glitch_close > 255) ? 255 : glitch_close;
|
||||
pInfo->SquelchOpenRSSIThresh = MIN(rssi_open, 255);
|
||||
pInfo->SquelchCloseRSSIThresh = MIN(rssi_close, 255);
|
||||
pInfo->SquelchOpenGlitchThresh = MIN(glitch_open, 255);
|
||||
pInfo->SquelchCloseGlitchThresh = MIN(glitch_close, 255);
|
||||
#endif
|
||||
|
||||
pInfo->SquelchOpenNoiseThresh = (noise_open > 127) ? 127 : noise_open;
|
||||
pInfo->SquelchCloseNoiseThresh = (noise_close > 127) ? 127 : noise_close;
|
||||
pInfo->SquelchOpenNoiseThresh = MIN(noise_open, 127);
|
||||
pInfo->SquelchCloseNoiseThresh = MIN(noise_close, 127);
|
||||
}
|
||||
|
||||
// *******************************
|
||||
@@ -572,30 +588,15 @@ void RADIO_ConfigureSquelchAndOutputPower(VFO_Info_t *pInfo)
|
||||
uint8_t Op = 0; // Low eeprom calibration data
|
||||
uint8_t currentPower = pInfo->OUTPUT_POWER;
|
||||
|
||||
if(currentPower == OUTPUT_POWER_USER)
|
||||
{
|
||||
if(gSetting_set_pwr == 5)
|
||||
{
|
||||
Op = 1; // Mid eeprom calibration data
|
||||
}
|
||||
else if(gSetting_set_pwr == 6)
|
||||
{
|
||||
Op = 2; // High eeprom calibration data
|
||||
}
|
||||
if (currentPower == OUTPUT_POWER_USER)
|
||||
currentPower = gSetting_set_pwr;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentPower == OUTPUT_POWER_MID)
|
||||
{
|
||||
Op = 1; // Mid eeprom calibration data
|
||||
}
|
||||
else if(currentPower == OUTPUT_POWER_HIGH)
|
||||
{
|
||||
Op = 2; // High eeprom calibration data
|
||||
}
|
||||
currentPower--;
|
||||
}
|
||||
|
||||
if (currentPower == 5)
|
||||
Op = 1; // Mid eeprom calibration data
|
||||
else if (currentPower == 6)
|
||||
Op = 2; // High eeprom calibration data
|
||||
|
||||
PY25Q16_ReadBuffer(0x100D0 + (Band * 16) + (Op * 3), Txp, 3);
|
||||
|
||||
@@ -703,18 +704,11 @@ void RADIO_ApplyOffset(VFO_Info_t *pInfo)
|
||||
{
|
||||
uint32_t Frequency = pInfo->freq_config_RX.Frequency;
|
||||
|
||||
switch (pInfo->TX_OFFSET_FREQUENCY_DIRECTION)
|
||||
{
|
||||
case TX_OFFSET_FREQUENCY_DIRECTION_OFF:
|
||||
break;
|
||||
case TX_OFFSET_FREQUENCY_DIRECTION_ADD:
|
||||
Frequency += pInfo->TX_OFFSET_FREQUENCY;
|
||||
break;
|
||||
case TX_OFFSET_FREQUENCY_DIRECTION_SUB:
|
||||
Frequency -= pInfo->TX_OFFSET_FREQUENCY;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pInfo->TX_OFFSET_FREQUENCY_DIRECTION == TX_OFFSET_FREQUENCY_DIRECTION_ADD)
|
||||
Frequency += pInfo->TX_OFFSET_FREQUENCY;
|
||||
else if (pInfo->TX_OFFSET_FREQUENCY_DIRECTION == TX_OFFSET_FREQUENCY_DIRECTION_SUB)
|
||||
Frequency -= pInfo->TX_OFFSET_FREQUENCY;
|
||||
|
||||
pInfo->freq_config_TX.Frequency = Frequency;
|
||||
}
|
||||
|
||||
@@ -738,6 +732,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;
|
||||
@@ -756,7 +756,7 @@ void RADIO_SetupRegisters(bool switchToForeground)
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false);
|
||||
|
||||
if (gRxVfo->Modulation == MODULATION_AM)
|
||||
BK4819_SetFilterBandwidth(BK4819_FILTER_BW_AM, true);
|
||||
BK4819_SetFilterBandwidth(RADIO_GetAMFilterBandwidth(gRxVfo), true);
|
||||
else
|
||||
{
|
||||
switch (Bandwidth)
|
||||
@@ -794,8 +794,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
|
||||
@@ -808,6 +808,9 @@ void RADIO_SetupRegisters(bool switchToForeground)
|
||||
#endif
|
||||
BK4819_SetFrequency(Frequency);
|
||||
|
||||
// Keep the demodulator in sync when retuning without entering RX audio.
|
||||
RADIO_SetModulation(gRxVfo->Modulation);
|
||||
|
||||
BK4819_SetupSquelch(
|
||||
gRxVfo->SquelchOpenRSSIThresh, gRxVfo->SquelchCloseRSSIThresh,
|
||||
gRxVfo->SquelchOpenNoiseThresh, gRxVfo->SquelchCloseNoiseThresh,
|
||||
@@ -820,12 +823,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;
|
||||
|
||||
@@ -845,7 +843,7 @@ void RADIO_SetupRegisters(bool switchToForeground)
|
||||
BK4819_SetCTCSSFrequency(SQL_TONE);
|
||||
BK4819_SetTailDetection(SQL_TONE); // Default 550 = QS's 55Hz tone method
|
||||
|
||||
InterruptMask = BK4819_REG_3F_CxCSS_TAIL | BK4819_REG_3F_SQUELCH_FOUND | BK4819_REG_3F_SQUELCH_LOST;
|
||||
InterruptMask |= BK4819_REG_3F_CxCSS_TAIL;
|
||||
break;
|
||||
|
||||
case CODE_TYPE_CONTINUOUS_TONE:
|
||||
@@ -857,24 +855,17 @@ void RADIO_SetupRegisters(bool switchToForeground)
|
||||
// BK4819_SetTailDetection(CTCSS_Options[Code]);
|
||||
//#endif
|
||||
|
||||
InterruptMask = 0
|
||||
| BK4819_REG_3F_CxCSS_TAIL
|
||||
| BK4819_REG_3F_CTCSS_FOUND
|
||||
| BK4819_REG_3F_CTCSS_LOST
|
||||
| BK4819_REG_3F_SQUELCH_FOUND
|
||||
| BK4819_REG_3F_SQUELCH_LOST;
|
||||
|
||||
InterruptMask |= BK4819_REG_3F_CxCSS_TAIL
|
||||
| BK4819_REG_3F_CTCSS_FOUND
|
||||
| BK4819_REG_3F_CTCSS_LOST;
|
||||
break;
|
||||
|
||||
case CODE_TYPE_DIGITAL:
|
||||
case CODE_TYPE_REVERSE_DIGITAL:
|
||||
BK4819_SetCDCSSCodeWord(DCS_GetGolayCodeWord(CodeType, Code));
|
||||
InterruptMask = 0
|
||||
| BK4819_REG_3F_CxCSS_TAIL
|
||||
| BK4819_REG_3F_CDCSS_FOUND
|
||||
| BK4819_REG_3F_CDCSS_LOST
|
||||
| BK4819_REG_3F_SQUELCH_FOUND
|
||||
| BK4819_REG_3F_SQUELCH_LOST;
|
||||
InterruptMask |= BK4819_REG_3F_CxCSS_TAIL
|
||||
| BK4819_REG_3F_CDCSS_FOUND
|
||||
| BK4819_REG_3F_CDCSS_LOST;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -892,11 +883,8 @@ void RADIO_SetupRegisters(bool switchToForeground)
|
||||
else
|
||||
{
|
||||
BK4819_SetCTCSSFrequency(2625);
|
||||
InterruptMask = 0
|
||||
| BK4819_REG_3F_CTCSS_FOUND
|
||||
| BK4819_REG_3F_CTCSS_LOST
|
||||
| BK4819_REG_3F_SQUELCH_FOUND
|
||||
| BK4819_REG_3F_SQUELCH_LOST;
|
||||
InterruptMask |= BK4819_REG_3F_CTCSS_FOUND
|
||||
| BK4819_REG_3F_CTCSS_LOST;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -924,8 +912,8 @@ void RADIO_SetupRegisters(bool switchToForeground)
|
||||
BK4819_EnableDTMF();
|
||||
InterruptMask |= BK4819_REG_3F_DTMF_5TONE_FOUND;
|
||||
|
||||
//RADIO_SetupAGC(gRxVfo->Modulation == MODULATION_AM, false);
|
||||
RADIO_SetupAGC(false, false);
|
||||
RADIO_SetupAGC(gRxVfo->Modulation == MODULATION_AM, false);
|
||||
//RADIO_SetupAGC(false, false);
|
||||
|
||||
// enable/disable BK4819 selected interrupts
|
||||
BK4819_WriteRegister(BK4819_REG_3F, InterruptMask);
|
||||
@@ -1054,6 +1042,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:
|
||||
@@ -1067,93 +1079,87 @@ void RADIO_SetModulation(ModulationMode_t modulation)
|
||||
mod = BK4819_AF_BASEBAND2;
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_BYP_RAW_DEMODULATORS
|
||||
case MODULATION_BYP:
|
||||
mod = BK4819_AF_UNKNOWN3;
|
||||
break;
|
||||
case MODULATION_RAW:
|
||||
mod = BK4819_AF_BASEBAND1;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
BK4819_SetAF(mod);
|
||||
|
||||
// HACK, FIXME:
|
||||
//
|
||||
// What follows is a direct copy of the AM enable/disable code from
|
||||
// the original UV-K1 firmware. It is not clear why these specific register
|
||||
// values are used for AM all of a sudden instead of the AF setting like on
|
||||
// the BK4819, nor what exactly they do.
|
||||
// So for now we just keep it as is to maintain compatibility.
|
||||
//
|
||||
if (modulation != MODULATION_AM)
|
||||
{
|
||||
uint16_t uVar1 = BK4819_ReadRegister(0x31);
|
||||
BK4819_WriteRegister(0x31,uVar1 & 0xfffffffe);
|
||||
BK4819_WriteRegister(0x42,0x6b5a);
|
||||
BK4819_WriteRegister(0x2a,0x7400);
|
||||
BK4819_WriteRegister(0x2b,0);
|
||||
BK4819_WriteRegister(0x2f,0x9890);
|
||||
//BK4819_WriteRegister(0x54, 0x9009);
|
||||
//BK4819_WriteRegister(0x55, 0x31a9);
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
AUDIO_ApplyProfile(gSetting_set_audio);
|
||||
#else
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x31a9);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t uVar1 = BK4819_ReadRegister(0x31);
|
||||
BK4819_WriteRegister(0x31,uVar1 | 1);
|
||||
BK4819_WriteRegister(0x42,0x6f5c);
|
||||
BK4819_WriteRegister(0x2a,0x7434);
|
||||
BK4819_WriteRegister(0x2b,0x300);
|
||||
BK4819_WriteRegister(0x2f,0x9990);
|
||||
//BK4819_WriteRegister(0x54, 0x9775);
|
||||
//BK4819_WriteRegister(0x55, 0x32c6);
|
||||
|
||||
BK4819_WriteRegister(0x54, 0x8846);
|
||||
BK4819_WriteRegister(0x55, 0x38C0);
|
||||
uint16_t uVar1 = BK4819_ReadRegister(0x31);
|
||||
|
||||
BK4819_SetFilterBandwidth(BK4819_FILTER_BW_AM, true);
|
||||
switch (modulation)
|
||||
{
|
||||
case MODULATION_AM:
|
||||
{
|
||||
BK4819_WriteRegister(0x31, uVar1 | 1); // AM Demodulation Enable
|
||||
BK4819_WriteRegister(0x42, 0x6f5c);
|
||||
BK4819_WriteRegister(0x2a, 0x7434);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
AUDIO_ApplyAMProfile(gSetting_set_audio_am);
|
||||
#else
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x31a9);
|
||||
#endif
|
||||
|
||||
BK4819_SetFilterBandwidth(RADIO_GetAMFilterBandwidth(gRxVfo), true);
|
||||
break;
|
||||
}
|
||||
|
||||
case MODULATION_USB:
|
||||
case MODULATION_FM:
|
||||
default:
|
||||
{
|
||||
BK4819_WriteRegister(0x31, uVar1 & 0xfffe); // AM Demodulation Disable
|
||||
BK4819_WriteRegister(0x42, 0x6b5a);
|
||||
BK4819_WriteRegister(0x2a, 0x7400);
|
||||
BK4819_WriteRegister(0x2b, 0x0000);
|
||||
BK4819_WriteRegister(0x2f, 0x9890);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
if (modulation == MODULATION_USB)
|
||||
AUDIO_ApplyUSBProfile();
|
||||
else
|
||||
AUDIO_ApplyFMProfile(gSetting_set_audio_fm);
|
||||
#else
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x31a9);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BK4819_SetRegValue(afDacGainRegSpec, 0xF);
|
||||
BK4819_WriteRegister(BK4819_REG_3D, modulation == MODULATION_USB ? 0 : 0x2AAB);
|
||||
BK4819_SetRegValue(afcDisableRegSpec, modulation != MODULATION_FM);
|
||||
|
||||
//RADIO_SetupAGC(modulation == MODULATION_AM, false);
|
||||
RADIO_SetupAGC(false, false);
|
||||
RADIO_SetupAGC(modulation == MODULATION_AM, false);
|
||||
}
|
||||
|
||||
void RADIO_SetupAGC(bool listeningAM, bool disable)
|
||||
{
|
||||
static uint8_t lastSettings;
|
||||
static uint8_t lastSettings = 0xFF;
|
||||
uint8_t newSettings = (listeningAM << 1) | disable;
|
||||
if(lastSettings == newSettings)
|
||||
if (lastSettings == newSettings)
|
||||
return;
|
||||
lastSettings = newSettings;
|
||||
|
||||
|
||||
if(!listeningAM) { // if not actively listening AM we don't need any AM specific regulation
|
||||
BK4819_SetAGC(!disable);
|
||||
BK4819_InitAGC(false);
|
||||
}
|
||||
else {
|
||||
#ifdef ENABLE_AM_FIX
|
||||
if(gSetting_AM_fix) { // if AM fix active lock AGC so AM-fix can do it's job
|
||||
BK4819_SetAGC(0);
|
||||
AM_fix_enable(!disable);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
BK4819_SetAGC(!disable);
|
||||
BK4819_InitAGC(true);
|
||||
}
|
||||
if (listeningAM && gSetting_AM_fix) {
|
||||
BK4819_SetAGC(0);
|
||||
AM_fix_enable(!disable);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
BK4819_SetAGC(!disable);
|
||||
BK4819_InitAGC(listeningAM);
|
||||
}
|
||||
|
||||
void RADIO_SetVfoState(VfoState_t State)
|
||||
@@ -1199,16 +1205,12 @@ void RADIO_PrepareTX(void)
|
||||
|
||||
RADIO_SelectCurrentVfo();
|
||||
|
||||
if(TX_freq_check(gCurrentVfo->pTX->Frequency) != 0
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
if(TX_freq_check(gCurrentVfo->pTX->Frequency) != 0 && gCurrentVfo->TX_LOCK == true
|
||||
#if defined(ENABLE_ALARM) || defined(ENABLE_TX1750)
|
||||
&& gAlarmState != ALARM_STATE_SITE_ALARM
|
||||
#endif
|
||||
#else
|
||||
if(TX_freq_check(gCurrentVfo->pTX->Frequency) != 0
|
||||
#if defined(ENABLE_ALARM) || defined(ENABLE_TX1750)
|
||||
&& gAlarmState != ALARM_STATE_SITE_ALARM
|
||||
#endif
|
||||
&& gCurrentVfo->TX_LOCK == true
|
||||
#endif
|
||||
#if defined(ENABLE_ALARM) || defined(ENABLE_TX1750)
|
||||
&& gAlarmState != ALARM_STATE_SITE_ALARM
|
||||
#endif
|
||||
){
|
||||
// TX frequency not allowed
|
||||
@@ -1227,6 +1229,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
|
||||
@@ -1267,6 +1275,10 @@ void RADIO_PrepareTX(void)
|
||||
|
||||
FUNCTION_Select(FUNCTION_TRANSMIT);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
RXTX_LOG_BeginTx(gCurrentVfo);
|
||||
#endif
|
||||
|
||||
gTxTimerCountdown_500ms = 0; // no timeout
|
||||
|
||||
#if defined(ENABLE_ALARM) || defined(ENABLE_TX1750)
|
||||
@@ -1306,27 +1318,37 @@ 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)
|
||||
{
|
||||
BK4819_PlayRoger();
|
||||
BK4819_FilterBandwidth_t Bandwidth = gCurrentVfo->CHANNEL_BANDWIDTH;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
if(Bandwidth == BK4819_FILTER_BW_NARROW && gSetting_set_nfm == 1)
|
||||
{
|
||||
Bandwidth = BK4819_FILTER_BW_NARROWER;
|
||||
}
|
||||
#endif
|
||||
|
||||
BK4819_PlayRoger(Bandwidth);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1336,7 +1358,6 @@ void RADIO_PrepareCssTX(void)
|
||||
|
||||
SYSTEM_DelayMs(200);
|
||||
|
||||
if(gEeprom.TAIL_TONE_ELIMINATION)
|
||||
RADIO_SendCssTail();
|
||||
RADIO_SendCssTail();
|
||||
RADIO_SetupRegisters(true);
|
||||
}
|
||||
+7
-1
@@ -21,6 +21,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "dcs.h"
|
||||
#include "driver/bk4819.h"
|
||||
#include "frequencies.h"
|
||||
|
||||
enum {
|
||||
@@ -148,19 +149,24 @@ extern DCS_CodeType_t gCurrentCodeType;
|
||||
|
||||
extern VfoState_t VfoState[2];
|
||||
|
||||
// Human-readable label per VfoState_t (defined in ui/main.c), e.g. "TX DISABLE".
|
||||
extern const char *const VfoStateStr[];
|
||||
|
||||
bool RADIO_CheckValidList(uint8_t scanList);
|
||||
void RADIO_NextValidList(void);
|
||||
void RADIO_NextValidList(int8_t direction);
|
||||
bool RADIO_CheckValidChannel(uint16_t channel, bool checkScanList, uint8_t scanList);
|
||||
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);
|
||||
|
||||
@@ -98,6 +98,38 @@ void SysTick_Handler(void)
|
||||
if (gCurrentFunction != FUNCTION_MONITOR && gCurrentFunction != FUNCTION_TRANSMIT)
|
||||
DECREMENT_AND_TRIGGER(gScanPauseDelayIn_10ms, gScheduleScanListen);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
// Scan stall watchdog: if the scan-resume countdown has expired
|
||||
// (gScanPauseDelayIn_10ms == 0) but no resume was actually scheduled
|
||||
// (gScheduleScanListen == false) and no real reception is happening
|
||||
// (squelch closed -> green LED off, not in RX/TX/MONITOR), the scan
|
||||
// loop is stuck. Force a resume after this many 10ms ticks. The
|
||||
// gScanPauseDelayIn_10ms == 0 guard ensures we never override the
|
||||
// user-configured ScnRev (SCAN_RESUME_MODE) pause: while that pause
|
||||
// is decrementing, the watchdog stays asleep.
|
||||
#define SCAN_FAST_STALL_WATCHDOG_10ms 25 // 250 ms
|
||||
static uint16_t scanFastStallCounter;
|
||||
|
||||
if (gSetting_set_scn
|
||||
&& gScanStateDir != SCAN_OFF
|
||||
&& gScanPauseDelayIn_10ms == 0
|
||||
&& !gScheduleScanListen
|
||||
&& !g_SquelchLost
|
||||
&& gCurrentFunction != FUNCTION_RECEIVE
|
||||
&& gCurrentFunction != FUNCTION_TRANSMIT
|
||||
&& gCurrentFunction != FUNCTION_MONITOR)
|
||||
{
|
||||
if (++scanFastStallCounter >= SCAN_FAST_STALL_WATCHDOG_10ms) {
|
||||
scanFastStallCounter = 0;
|
||||
gScheduleScanListen = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
scanFastStallCounter = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
DECREMENT_AND_TRIGGER(gTailNoteEliminationCountdown_10ms, gFlagTailNoteEliminationComplete);
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
/* Copyright 2024 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.
|
||||
*/
|
||||
|
||||
#include "debugging.h"
|
||||
#include "driver/st7565.h"
|
||||
#include "screenshot.h"
|
||||
#include "misc.h"
|
||||
#include "driver/vcp.h"
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// SRAM optimization: minimize static allocations
|
||||
// - previousFrame: 1024 bytes (REQUIRED - need to compare for delta)
|
||||
// - No currentFrame or deltaFrame static buffers
|
||||
static uint8_t previousFrame[1024] = {0};
|
||||
static uint8_t forcedBlock = 0;
|
||||
static uint8_t keepAlive = 10;
|
||||
|
||||
void getScreenShot(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;
|
||||
|
||||
if (gUART_LockScreenshot > 0) {
|
||||
gUART_LockScreenshot--;
|
||||
return;
|
||||
}
|
||||
|
||||
if (UART_IsCableConnected()) {
|
||||
keepAlive = 10;
|
||||
gUSB_ScreenshotEnabled = false;
|
||||
}
|
||||
|
||||
if (VCP_ScreenshotPing()) {
|
||||
keepAlive = 10;
|
||||
gUSB_ScreenshotEnabled = true;
|
||||
}
|
||||
|
||||
if (keepAlive > 0) {
|
||||
if (--keepAlive == 0) return;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// ==== BUILD FRAME ONCE ====
|
||||
// Status line: 8 bit layers × 128 columns
|
||||
for (uint8_t b = 0; b < 8; b++) {
|
||||
for (uint8_t i = 0; i < 128; i++) {
|
||||
uint8_t bit = (gStatusLine[i] >> b) & 0x01;
|
||||
acc |= (bit << bitCount++);
|
||||
if (bitCount == 8) {
|
||||
frameBuffer[index++] = acc;
|
||||
acc = 0;
|
||||
bitCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Frame buffer: 7 lines × 8 bit layers × 128 columns
|
||||
for (uint8_t l = 0; l < 7; l++) {
|
||||
for (uint8_t b = 0; b < 8; b++) {
|
||||
for (uint8_t i = 0; i < 128; i++) {
|
||||
uint8_t bit = (gFrameBuffer[l][i] >> b) & 0x01;
|
||||
acc |= (bit << bitCount++);
|
||||
if (bitCount == 8) {
|
||||
frameBuffer[index++] = acc;
|
||||
acc = 0;
|
||||
bitCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bitCount > 0)
|
||||
frameBuffer[index++] = acc;
|
||||
|
||||
if (index != 1024)
|
||||
return;
|
||||
|
||||
// ==== FIRST PASS: Count changed chunks ====
|
||||
uint16_t deltaLen = 0;
|
||||
uint8_t changedChunks[128]; // List of changed chunk indices
|
||||
uint8_t changedCount = 0;
|
||||
|
||||
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 = (chunk == forcedBlock);
|
||||
bool fullUpdate = force;
|
||||
|
||||
if (changed || isForced || fullUpdate) {
|
||||
changedChunks[changedCount++] = chunk;
|
||||
deltaLen += 9;
|
||||
}
|
||||
}
|
||||
|
||||
forcedBlock = (forcedBlock + 1) % 128;
|
||||
|
||||
if (deltaLen == 0)
|
||||
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)
|
||||
};
|
||||
|
||||
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;
|
||||
Screenshot_Send(&end, 1);
|
||||
}
|
||||
+326
-119
@@ -30,13 +30,116 @@
|
||||
|
||||
EEPROM_Config_t gEeprom = { 0 };
|
||||
|
||||
// Load a DTMF code from EEPROM, falling back to default_val if invalid.
|
||||
static void SETTINGS_LoadEepromDtmf(uint32_t addr, char *dest, size_t size, const char *default_val)
|
||||
{
|
||||
uint8_t buf[16];
|
||||
|
||||
if (size > sizeof(buf))
|
||||
size = sizeof(buf);
|
||||
|
||||
PY25Q16_ReadBuffer(addr, buf, size);
|
||||
|
||||
if (DTMF_ValidateCodes((char *)buf, size)) {
|
||||
memcpy(dest, buf, size);
|
||||
} else {
|
||||
strcpy(dest, default_val);
|
||||
}
|
||||
}
|
||||
|
||||
void SETTINGS_InitEEPROM(void)
|
||||
{
|
||||
uint8_t Data[16] = {0};
|
||||
|
||||
//
|
||||
// Version check
|
||||
// Read stored version from EEPROM and compare with VERSION_STRING_2
|
||||
//
|
||||
{
|
||||
char storedVersion[16] = {0};
|
||||
PY25Q16_ReadBuffer(0x00A160, storedVersion, sizeof(storedVersion));
|
||||
|
||||
// Compare with current version
|
||||
if (strncmp(storedVersion, VERSION_STRING_2, sizeof(storedVersion)) != 0)
|
||||
{
|
||||
// Different version: new install or firmware update
|
||||
|
||||
// 1. Write new version to EEPROM
|
||||
char newVersion[16] = {0};
|
||||
strncpy(newVersion, VERSION_STRING_2, sizeof(newVersion));
|
||||
PY25Q16_WriteBuffer(0x00A160, newVersion, sizeof(newVersion), false);
|
||||
|
||||
// 2. Reset sensitive parameters (MENU_LOCK, etc.)
|
||||
uint8_t configByte[8] = {0};
|
||||
PY25Q16_ReadBuffer(0x00A000, configByte, sizeof(configByte));
|
||||
|
||||
configByte[4] &= (uint8_t)~0x01; // KEY_LOCK = 0
|
||||
configByte[4] &= (uint8_t)~0x02; // MENU_LOCK = 0
|
||||
configByte[4] &= (uint8_t)~0x3C; // SET_KEY = 0
|
||||
//configByte[4] &= (uint8_t)~0x40; // SET_NAV = 0
|
||||
|
||||
PY25Q16_WriteBuffer(0x00A000, configByte, sizeof(configByte), false);
|
||||
|
||||
// 3. Reset display inversion (SET_INV = 0)
|
||||
uint8_t displayByte[8] = {0};
|
||||
PY25Q16_ReadBuffer(0x00A158, displayByte, sizeof(displayByte));
|
||||
|
||||
displayByte[5] &= (uint8_t)~0x10; // Clear bit 4 (SET_INV)
|
||||
|
||||
PY25Q16_WriteBuffer(0x00A158, displayByte, sizeof(displayByte), false);
|
||||
|
||||
// 4. Reset logo lines (clear to null for strlen() == 0)
|
||||
|
||||
char logoLines[32];
|
||||
PY25Q16_ReadBuffer(0x00A0C8, logoLines, sizeof(logoLines));
|
||||
|
||||
bool needsWrite = false;
|
||||
|
||||
for (int line = 0; line < 2; line++) {
|
||||
int offset = line * 16;
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
char c = logoLines[offset + i];
|
||||
if (c == 0) {
|
||||
break;
|
||||
}
|
||||
if (c < 0x20 || c > 0x7E) {
|
||||
memset(logoLines + offset, 0, 16);
|
||||
needsWrite = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (needsWrite) {
|
||||
PY25Q16_WriteBuffer(0x00A0C8, logoLines, sizeof(logoLines), false);
|
||||
}
|
||||
|
||||
// 5. Reset dBmCorrTable
|
||||
int8_t buf[7];
|
||||
PY25Q16_ReadBuffer(0x00A0B9, (uint8_t *)buf, 7);
|
||||
|
||||
needsWrite = true;
|
||||
for (uint8_t i = 0; i < 7; i++) {
|
||||
if ((uint8_t)buf[i] != 0xFF) {
|
||||
needsWrite = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (needsWrite) {
|
||||
for (uint8_t i = 0; i < 7; i++)
|
||||
buf[i] = dBmCorrTable[i];
|
||||
PY25Q16_WriteBuffer(0x00A0B9, buf, 7, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 0E70..0E77
|
||||
PY25Q16_ReadBuffer(0x00A000, Data, 8);
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
gSetting_set_audio = (Data[0] < 5) ? Data[0] : 0;
|
||||
gSetting_set_audio_fm = ((Data[0] & 0x0F) < 5) ? (Data[0] & 0x0F) : 0;
|
||||
gSetting_set_audio_am = (((Data[0] >> 4) & 0x0F) < 3) ? ((Data[0] >> 4) & 0x0F) : 0;
|
||||
#endif
|
||||
gEeprom.SQUELCH_LEVEL = (Data[1] < 10) ? Data[1] : 1;
|
||||
gEeprom.TX_TIMEOUT_TIMER = (Data[2] > 4 && Data[2] < 180) ? Data[2] : 11;
|
||||
@@ -55,7 +158,7 @@ void SETTINGS_InitEEPROM(void)
|
||||
gEeprom.VOX_SWITCH = (Data[5] < 2) ? Data[5] : false;
|
||||
gEeprom.VOX_LEVEL = (Data[6] < 10) ? Data[6] : 1;
|
||||
#endif
|
||||
gEeprom.MIC_SENSITIVITY = (Data[7] < 5) ? Data[7] : 4;
|
||||
gEeprom.MIC_SENSITIVITY = (Data[7] < 9) ? Data[7] : 4;
|
||||
|
||||
// 0E78..0E7F
|
||||
PY25Q16_ReadBuffer(0x00A008, Data, 8);
|
||||
@@ -132,9 +235,12 @@ gEeprom.FreqChannel[1] = IS_FREQ_CHANNEL(Data16[5]) ? Data16[5] : (FREQ_CHANNE
|
||||
|
||||
gEeprom.FM_Band = fmCfg.band;
|
||||
//gEeprom.FM_Space = fmCfg.space;
|
||||
|
||||
uint16_t freqLoLimit = BK1080_GetFreqLoLimit(gEeprom.FM_Band);
|
||||
|
||||
gEeprom.FM_SelectedFrequency =
|
||||
(fmCfg.selFreq >= BK1080_GetFreqLoLimit(gEeprom.FM_Band) && fmCfg.selFreq <= BK1080_GetFreqHiLimit(gEeprom.FM_Band)) ?
|
||||
fmCfg.selFreq : BK1080_GetFreqLoLimit(gEeprom.FM_Band);
|
||||
(fmCfg.selFreq >= freqLoLimit && fmCfg.selFreq <= BK1080_GetFreqHiLimit(gEeprom.FM_Band)) ?
|
||||
fmCfg.selFreq : freqLoLimit;
|
||||
|
||||
gEeprom.FM_SelectedChannel = fmCfg.selChn;
|
||||
gEeprom.FM_IsMrMode = fmCfg.isMrMode;
|
||||
@@ -173,13 +279,10 @@ gEeprom.FreqChannel[1] = IS_FREQ_CHANNEL(Data16[5]) ? Data16[5] : (FREQ_CHANNE
|
||||
gEeprom.VOICE_PROMPT = (Data[0] < 3) ? Data[0] : VOICE_PROMPT_ENGLISH;
|
||||
#endif
|
||||
#ifdef ENABLE_RSSI_BAR
|
||||
if((Data[1] < 200 && Data[1] > 90) && (Data[2] < Data[1]-9 && Data[1] < 160 && Data[2] > 50)) {
|
||||
gEeprom.S0_LEVEL = Data[1];
|
||||
gEeprom.S9_LEVEL = Data[2];
|
||||
}
|
||||
else {
|
||||
gEeprom.S0_LEVEL = 130;
|
||||
gEeprom.S9_LEVEL = 76;
|
||||
for (uint8_t i = 0; i < 7; i++) {
|
||||
int8_t val = (int8_t)Data[i + 1];
|
||||
if (val >= -64 && val <= 64)
|
||||
dBmCorrTable[i] = val;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -211,51 +314,25 @@ gEeprom.FreqChannel[1] = IS_FREQ_CHANNEL(Data16[5]) ? Data16[5] : (FREQ_CHANNE
|
||||
PY25Q16_ReadBuffer(0x00A0A8 + 0x48, Data, 8);
|
||||
gEeprom.DTMF_CODE_PERSIST_TIME = (Data[0] < 101) ? Data[0] * 10 : 100;
|
||||
gEeprom.DTMF_CODE_INTERVAL_TIME = (Data[1] < 101) ? Data[1] * 10 : 100;
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
gEeprom.PERMIT_REMOTE_KILL = (Data[2] < 2) ? Data[2] : true;
|
||||
|
||||
// 0EE0..0EE7
|
||||
|
||||
PY25Q16_ReadBuffer(0x00A0F8, Data, sizeof(gEeprom.ANI_DTMF_ID));
|
||||
if (DTMF_ValidateCodes((char *)Data, sizeof(gEeprom.ANI_DTMF_ID))) {
|
||||
memcpy(gEeprom.ANI_DTMF_ID, Data, sizeof(gEeprom.ANI_DTMF_ID));
|
||||
} else {
|
||||
strcpy(gEeprom.ANI_DTMF_ID, "123");
|
||||
}
|
||||
|
||||
SETTINGS_LoadEepromDtmf(0x00A0F8, gEeprom.ANI_DTMF_ID, sizeof(gEeprom.ANI_DTMF_ID), "123");
|
||||
|
||||
// 0EE8..0EEF
|
||||
PY25Q16_ReadBuffer(0x00A0F8 + 0x8, Data, sizeof(gEeprom.KILL_CODE));
|
||||
if (DTMF_ValidateCodes((char *)Data, sizeof(gEeprom.KILL_CODE))) {
|
||||
memcpy(gEeprom.KILL_CODE, Data, sizeof(gEeprom.KILL_CODE));
|
||||
} else {
|
||||
strcpy(gEeprom.KILL_CODE, "ABCD9");
|
||||
}
|
||||
SETTINGS_LoadEepromDtmf(0x00A0F8 + 0x08, gEeprom.KILL_CODE, sizeof(gEeprom.KILL_CODE), "ABCD9");
|
||||
|
||||
// 0EF0..0EF7
|
||||
PY25Q16_ReadBuffer(0x00A0F8 + 0x10, Data, sizeof(gEeprom.REVIVE_CODE));
|
||||
if (DTMF_ValidateCodes((char *)Data, sizeof(gEeprom.REVIVE_CODE))) {
|
||||
memcpy(gEeprom.REVIVE_CODE, Data, sizeof(gEeprom.REVIVE_CODE));
|
||||
} else {
|
||||
strcpy(gEeprom.REVIVE_CODE, "9DCBA");
|
||||
}
|
||||
SETTINGS_LoadEepromDtmf(0x00A0F8 + 0x10, gEeprom.REVIVE_CODE, sizeof(gEeprom.REVIVE_CODE), "9DCBA");
|
||||
#endif
|
||||
|
||||
// 0EF8..0F07
|
||||
PY25Q16_ReadBuffer(0x00A0F8 + 0x18, Data, sizeof(gEeprom.DTMF_UP_CODE));
|
||||
if (DTMF_ValidateCodes((char *)Data, sizeof(gEeprom.DTMF_UP_CODE))) {
|
||||
memcpy(gEeprom.DTMF_UP_CODE, Data, sizeof(gEeprom.DTMF_UP_CODE));
|
||||
} else {
|
||||
strcpy(gEeprom.DTMF_UP_CODE, "12345");
|
||||
}
|
||||
SETTINGS_LoadEepromDtmf(0x00A0F8 + 0x18, gEeprom.DTMF_UP_CODE, sizeof(gEeprom.DTMF_UP_CODE), "12345");
|
||||
|
||||
// 0F08..0F17
|
||||
PY25Q16_ReadBuffer(0x00A0F8 + 0x28, Data, sizeof(gEeprom.DTMF_DOWN_CODE));
|
||||
if (DTMF_ValidateCodes((char *)Data, sizeof(gEeprom.DTMF_DOWN_CODE))) {
|
||||
memcpy(gEeprom.DTMF_DOWN_CODE, Data, sizeof(gEeprom.DTMF_DOWN_CODE));
|
||||
} else {
|
||||
strcpy(gEeprom.DTMF_DOWN_CODE, "54321");
|
||||
}
|
||||
SETTINGS_LoadEepromDtmf(0x00A0F8 + 0x28, gEeprom.DTMF_DOWN_CODE, sizeof(gEeprom.DTMF_DOWN_CODE), "54321");
|
||||
|
||||
// 0F18..0F1F
|
||||
PY25Q16_ReadBuffer(0x00A130, Data, 8);
|
||||
@@ -336,6 +413,9 @@ gEeprom.FreqChannel[1] = IS_FREQ_CHANNEL(Data16[5]) ? Data16[5] : (FREQ_CHANNE
|
||||
}
|
||||
*/
|
||||
|
||||
// Init list name
|
||||
PY25Q16_ReadBuffer(0x00880E, gListName, sizeof(gListName));
|
||||
|
||||
// Init attr cache
|
||||
MR_InitChannelAttributesCache();
|
||||
|
||||
@@ -374,8 +454,21 @@ gEeprom.FreqChannel[1] = IS_FREQ_CHANNEL(Data16[5]) ? Data16[5] : (FREQ_CHANNE
|
||||
// 1FF0..0x1FF7
|
||||
// TODO: address TBD
|
||||
PY25Q16_ReadBuffer(0x00A158, Data, 8);
|
||||
const uint8_t set_ptt_scn_sav = Data[7] & 0x0F;
|
||||
const bool set_ptt_scn_sav_erased = Data[7] == 0xFF;
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
const bool set_ptt_scn_sav_valid = !set_ptt_scn_sav_erased && set_ptt_scn_sav < (SET_SAV_LEN << 2);
|
||||
#else
|
||||
const bool set_ptt_scn_sav_valid = !set_ptt_scn_sav_erased && set_ptt_scn_sav < 4;
|
||||
#endif
|
||||
gSetting_set_pwr = (((Data[7] & 0xF0) >> 4) < 7) ? ((Data[7] & 0xF0) >> 4) : 0;
|
||||
gSetting_set_ptt = (((Data[7] & 0x0F)) < 2) ? ((Data[7] & 0x0F)) : 0;
|
||||
gSetting_set_ptt = set_ptt_scn_sav_valid ? (set_ptt_scn_sav & 0x01) : 0;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
gSetting_set_scn = set_ptt_scn_sav_valid ? ((set_ptt_scn_sav & 0x02) == 0) : 0;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
gSetting_set_sav = set_ptt_scn_sav_valid ? ((set_ptt_scn_sav >> 2) & 0x03) : SET_SAV_OFF;
|
||||
#endif
|
||||
|
||||
gSetting_set_tot = (((Data[6] & 0xF0) >> 4) < 4) ? ((Data[6] & 0xF0) >> 4) : 0;
|
||||
gSetting_set_eot = (((Data[6] & 0x0F)) < 4) ? ((Data[6] & 0x0F)) : 0;
|
||||
@@ -384,7 +477,6 @@ gEeprom.FreqChannel[1] = IS_FREQ_CHANNEL(Data16[5]) ? Data16[5] : (FREQ_CHANNE
|
||||
int tmp = ((Data[5] & 0xF0) >> 4);
|
||||
|
||||
gSetting_set_inv = (((tmp >> 0) & 0x01) < 2) ? ((tmp >> 0) & 0x01): 0;
|
||||
gSetting_set_lck = (((tmp >> 1) & 0x01) < 2) ? ((tmp >> 1) & 0x01): 0;
|
||||
gSetting_set_met = (((tmp >> 2) & 0x01) < 2) ? ((tmp >> 2) & 0x01): 0;
|
||||
gSetting_set_gui = (((tmp >> 3) & 0x01) < 2) ? ((tmp >> 3) & 0x01): 0;
|
||||
gSetting_set_ctr = (((Data[5] & 0x0F)) > 00 && ((Data[5] & 0x0F)) < 16) ? ((Data[5] & 0x0F)) : 10;
|
||||
@@ -399,7 +491,7 @@ gEeprom.FreqChannel[1] = IS_FREQ_CHANNEL(Data16[5]) ? Data16[5] : (FREQ_CHANNE
|
||||
#else
|
||||
gSetting_set_inv = 0;
|
||||
#endif
|
||||
gSetting_set_lck = (tmp >> 1) & 0x01;
|
||||
gSetting_set_lck = (Data[2] < SET_LCK_LEN) ? Data[2] : SET_LCK_KEYS;
|
||||
gSetting_set_met = (tmp >> 2) & 0x01;
|
||||
gSetting_set_gui = (tmp >> 3) & 0x01;
|
||||
|
||||
@@ -415,13 +507,8 @@ gEeprom.FreqChannel[1] = IS_FREQ_CHANNEL(Data16[5]) ? Data16[5] : (FREQ_CHANNE
|
||||
gSetting_set_off = (Data[4] >> 1) > 120 ? 60 : (Data[4] >> 1);
|
||||
#endif
|
||||
|
||||
// Warning
|
||||
// Be aware, Data[3] is use by Spectrum
|
||||
// Warning
|
||||
|
||||
// And set special session settings for actions
|
||||
gSetting_set_ptt_session = gSetting_set_ptt;
|
||||
gEeprom.KEY_LOCK_PTT = gSetting_set_lck;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -503,6 +590,124 @@ uint32_t SETTINGS_FetchChannelFrequency(const uint16_t channel)
|
||||
return info.frequency;
|
||||
}
|
||||
|
||||
bool SETTINGS_FetchChannelScanInfo(const uint16_t channel, uint32_t *frequency, ModulationMode_t *modulation)
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint32_t frequency;
|
||||
uint32_t offset;
|
||||
uint8_t settings[4];
|
||||
} __attribute__((packed)) info;
|
||||
|
||||
PY25Q16_ReadBuffer(channel * 16, &info, sizeof(info));
|
||||
|
||||
if (frequency)
|
||||
*frequency = info.frequency;
|
||||
|
||||
if (modulation)
|
||||
{
|
||||
uint8_t mode = info.settings[3] >> 4;
|
||||
if (mode >= MODULATION_UKNOWN)
|
||||
mode = MODULATION_FM;
|
||||
*modulation = (ModulationMode_t)mode;
|
||||
}
|
||||
|
||||
return info.frequency != 0 && info.frequency != 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
bool SETTINGS_FetchChannelScanDisplayInfo(const uint16_t channel, ChannelScanDisplayInfo_t *info)
|
||||
{
|
||||
if (info == NULL)
|
||||
return false;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t frequency;
|
||||
uint32_t offset;
|
||||
uint8_t data[8];
|
||||
} __attribute__((packed)) raw;
|
||||
|
||||
PY25Q16_ReadBuffer(channel * 16, &raw, sizeof(raw));
|
||||
|
||||
if (raw.frequency == 0 || raw.frequency == 0xFFFFFFFF)
|
||||
return false;
|
||||
|
||||
memset(info, 0, sizeof(*info));
|
||||
|
||||
info->rx.Frequency = raw.frequency;
|
||||
info->tx.Frequency = raw.frequency;
|
||||
info->offset = (raw.offset >= _1GHz_in_KHz) ? (_1GHz_in_KHz / 100) : raw.offset;
|
||||
|
||||
info->rx.CodeType = (raw.data[2] >> 0) & 0x0F;
|
||||
info->tx.CodeType = (raw.data[2] >> 4) & 0x0F;
|
||||
RADIO_ValidateAndSetCode(&info->rx, raw.data[0]);
|
||||
RADIO_ValidateAndSetCode(&info->tx, raw.data[1]);
|
||||
|
||||
uint8_t tmp = raw.data[3] & 0x0F;
|
||||
if (tmp > TX_OFFSET_FREQUENCY_DIRECTION_SUB)
|
||||
tmp = TX_OFFSET_FREQUENCY_DIRECTION_OFF;
|
||||
info->txOffsetFrequencyDirection = tmp;
|
||||
|
||||
tmp = raw.data[3] >> 4;
|
||||
if (tmp >= MODULATION_UKNOWN)
|
||||
tmp = MODULATION_FM;
|
||||
info->modulation = (ModulationMode_t)tmp;
|
||||
|
||||
tmp = raw.data[6];
|
||||
if (tmp >= STEP_N_ELEM)
|
||||
tmp = STEP_12_5kHz;
|
||||
info->stepSetting = (STEP_Setting_t)tmp;
|
||||
info->stepFrequency = gStepFrequencyTable[tmp];
|
||||
|
||||
if (raw.data[4] == 0xFF)
|
||||
{
|
||||
info->frequencyReverse = false;
|
||||
info->channelBandwidth = BANDWIDTH_WIDE;
|
||||
info->outputPower = OUTPUT_POWER_LOW1;
|
||||
info->busyChannelLock = false;
|
||||
info->txLock = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint8_t d4 = raw.data[4];
|
||||
info->frequencyReverse = !!((d4 >> 0) & 1u);
|
||||
info->channelBandwidth = !!((d4 >> 1) & 1u);
|
||||
info->outputPower = ((d4 >> 2) & 7u);
|
||||
info->busyChannelLock = !!((d4 >> 5) & 1u);
|
||||
info->txLock = !!((d4 >> 6) & 1u);
|
||||
}
|
||||
|
||||
switch (info->txOffsetFrequencyDirection)
|
||||
{
|
||||
case TX_OFFSET_FREQUENCY_DIRECTION_ADD:
|
||||
info->tx.Frequency = raw.frequency + info->offset;
|
||||
break;
|
||||
case TX_OFFSET_FREQUENCY_DIRECTION_SUB:
|
||||
info->tx.Frequency = raw.frequency - info->offset;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (raw.data[5] == 0xFF)
|
||||
{
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
info->dtmfDecodingEnable = false;
|
||||
#endif
|
||||
info->dtmfPttIdTxMode = PTT_ID_OFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
info->dtmfDecodingEnable = (raw.data[5] >> 0) & 1u;
|
||||
#endif
|
||||
const uint8_t pttId = (raw.data[5] >> 1) & 7u;
|
||||
info->dtmfPttIdTxMode = pttId < ARRAY_SIZE(gSubMenu_PTT_ID) ? pttId : PTT_ID_OFF;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SETTINGS_FetchChannelName(char *s, const uint16_t channel)
|
||||
{
|
||||
if (s == NULL)
|
||||
@@ -532,15 +737,20 @@ void SETTINGS_FetchChannelName(char *s, const uint16_t channel)
|
||||
|
||||
void SETTINGS_FactoryReset(bool bIsAll)
|
||||
{
|
||||
PY25Q16_SectorErase(0x000000);
|
||||
PY25Q16_SectorErase(0x001000);
|
||||
PY25Q16_SectorErase(0x003000);
|
||||
PY25Q16_SectorErase(0x004000);
|
||||
PY25Q16_SectorErase(0x005000);
|
||||
PY25Q16_SectorErase(0x006000);
|
||||
PY25Q16_SectorErase(0x007000);
|
||||
PY25Q16_SectorErase(0x008000);
|
||||
PY25Q16_SectorErase(0x009000);
|
||||
// PY25Q16_SectorErase(0x000000);
|
||||
// PY25Q16_SectorErase(0x001000);
|
||||
// PY25Q16_SectorErase(0x002000);
|
||||
// PY25Q16_SectorErase(0x003000);
|
||||
// PY25Q16_SectorErase(0x004000);
|
||||
// PY25Q16_SectorErase(0x005000);
|
||||
// PY25Q16_SectorErase(0x006000);
|
||||
// PY25Q16_SectorErase(0x007000);
|
||||
// PY25Q16_SectorErase(0x008000);
|
||||
// PY25Q16_SectorErase(0x009000);
|
||||
|
||||
for (uint32_t addr = 0x000000; addr <= 0x009000; addr += 0x1000) {
|
||||
PY25Q16_SectorErase(addr);
|
||||
}
|
||||
|
||||
// 0d60 - 0e30
|
||||
if (bIsAll)
|
||||
@@ -558,6 +768,13 @@ void SETTINGS_FactoryReset(bool bIsAll)
|
||||
|
||||
Data8[4] &= (uint8_t)~0x01;
|
||||
Data8[4] &= (uint8_t)~0x02;
|
||||
|
||||
// SET_KEY to 0
|
||||
Data8[4] &= (uint8_t)~0x3C; // Clear bits 2-5 (SET_KEY)
|
||||
|
||||
// SET_NAV to false
|
||||
Data8[4] &= (uint8_t)~0x40; // Clear bit 6 (SET_NAV) for UV-K1 by default
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESET_VFO
|
||||
Data8[7] = (1 & 0x01);
|
||||
#endif
|
||||
@@ -580,9 +797,12 @@ void SETTINGS_FactoryReset(bool bIsAll)
|
||||
gEeprom.FreqChannel[0] = FREQ_CHANNEL_FIRST + BAND3_137MHz;
|
||||
gEeprom.FreqChannel[1] = FREQ_CHANNEL_FIRST + BAND6_400MHz;
|
||||
|
||||
SETTINGS_SaveVfoIndices();
|
||||
SETTINGS_SaveChannel(FREQ_CHANNEL_FIRST + BAND3_137MHz, 0, &gEeprom.VfoInfo[0], 2);
|
||||
SETTINGS_SaveChannel(FREQ_CHANNEL_FIRST + BAND6_400MHz, 1, &gEeprom.VfoInfo[1], 2);
|
||||
|
||||
gVfoStateChanged = true;
|
||||
gScheduleVfoSave = true;
|
||||
SETTINGS_SaveVfoIndicesFlush();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -664,9 +884,14 @@ void SETTINGS_SaveSettings(void)
|
||||
// 0x0E70
|
||||
State = SecBuf;
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
State[0] = gSetting_set_audio;
|
||||
State[0] = (gSetting_set_audio_fm & 0x0F) | ((gSetting_set_audio_am & 0x0F) << 4);
|
||||
#endif
|
||||
State[1] = gEeprom.SQUELCH_LEVEL;
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
if (gSquelchLevelOriginal < 10)
|
||||
State[1] = gSquelchLevelOriginal;
|
||||
else
|
||||
#endif
|
||||
State[1] = gEeprom.SQUELCH_LEVEL;
|
||||
State[2] = gEeprom.TX_TIMEOUT_TIMER;
|
||||
#ifdef ENABLE_NOAA
|
||||
State[3] = gEeprom.NOAA_AUTO_SCAN;
|
||||
@@ -892,8 +1117,6 @@ void SETTINGS_SaveSettings(void)
|
||||
|
||||
if(gSetting_set_inv == 1)
|
||||
tmp = tmp | (1 << 0);
|
||||
if (gSetting_set_lck == 1)
|
||||
tmp = tmp | (1 << 1);
|
||||
if (gSetting_set_met == 1)
|
||||
tmp = tmp | (1 << 2);
|
||||
if (gSetting_set_gui == 1)
|
||||
@@ -907,15 +1130,22 @@ void SETTINGS_SaveSettings(void)
|
||||
#endif
|
||||
|
||||
tmp = (gSetting_set_inv << 0) |
|
||||
(gSetting_set_lck << 1) |
|
||||
(gSetting_set_met << 2) |
|
||||
(gSetting_set_gui << 3);
|
||||
|
||||
State[2] = gSetting_set_lck;
|
||||
State[5] = ((tmp << 4) | (gSetting_set_ctr & 0x0F));
|
||||
State[6] = ((gSetting_set_tot << 4) | (gSetting_set_eot & 0x0F));
|
||||
State[7] = ((gSetting_set_pwr << 4) | (gSetting_set_ptt & 0x0F));
|
||||
uint8_t set_ptt_scn_sav = gSetting_set_ptt & 0x01;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
if (!gSetting_set_scn)
|
||||
set_ptt_scn_sav |= 0x02;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
set_ptt_scn_sav |= (gSetting_set_sav & 0x03) << 2;
|
||||
#endif
|
||||
|
||||
gEeprom.KEY_LOCK_PTT = gSetting_set_lck;
|
||||
State[7] = ((gSetting_set_pwr << 4) | set_ptt_scn_sav);
|
||||
|
||||
PY25Q16_WriteBuffer(0x00A158, SecBuf, 8, false);
|
||||
#endif
|
||||
@@ -980,7 +1210,7 @@ void SETTINGS_SaveChannel(uint16_t Channel, uint8_t VFO, const VFO_Info_t *pVFO,
|
||||
|
||||
PY25Q16_WriteBuffer(OffsetVFO, Buf, 0x10, false);
|
||||
|
||||
SETTINGS_UpdateChannel(Channel, pVFO, true, true, true);
|
||||
SETTINGS_UpdateChannel(Channel, pVFO, true);
|
||||
|
||||
if (IS_MR_CHANNEL(Channel)) {
|
||||
#ifndef ENABLE_KEEP_MEM_NAME
|
||||
@@ -1011,58 +1241,32 @@ void SETTINGS_SaveChannelName(uint16_t channel, const char * name)
|
||||
PY25Q16_WriteBuffer(0x004000 + offset, buf, 0x10, false);
|
||||
}
|
||||
|
||||
void SETTINGS_UpdateChannel(uint16_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)
|
||||
{
|
||||
#ifdef ENABLE_NOAA
|
||||
if (!IS_NOAA_CHANNEL(channel))
|
||||
if (IS_NOAA_CHANNEL(channel))
|
||||
return;
|
||||
#endif
|
||||
{
|
||||
ChannelAttributes_t state;
|
||||
ChannelAttributes_t att = {
|
||||
.band = 0x7,
|
||||
.compander = 0,
|
||||
.unused_1 = 0,
|
||||
.unused_2 = 0,
|
||||
.exclude = 0,
|
||||
.scanlist = 0,
|
||||
}; // default attributes
|
||||
|
||||
// 0x0D60
|
||||
PY25Q16_ReadBuffer(0x008000 + channel, &state, 1);
|
||||
ChannelAttributes_t att = {
|
||||
.band = 0x7,
|
||||
.compander = 0,
|
||||
.unused_1 = 0,
|
||||
.unused_2 = 0,
|
||||
.exclude = 0,
|
||||
.scanlist = 0,
|
||||
};
|
||||
|
||||
if (keep) {
|
||||
att.band = pVFO->Band;
|
||||
att.compander = pVFO->Compander;
|
||||
att.unused_1 = 0;
|
||||
att.unused_2 = 0;
|
||||
att.exclude = 0;
|
||||
att.scanlist = pVFO->SCANLIST_PARTICIPATION;
|
||||
if (check && state.__val == att.__val)
|
||||
return; // no change in the attributes
|
||||
}
|
||||
|
||||
state.__val = att.__val;
|
||||
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
save = true;
|
||||
#endif
|
||||
if(save)
|
||||
{
|
||||
uint16_t buf[MR_CHANNELS_MAX + 24];
|
||||
PY25Q16_ReadBuffer(0x008000, buf, sizeof(buf));
|
||||
buf[channel] = state.__val;
|
||||
PY25Q16_WriteBuffer(0x008000, buf, sizeof(buf), false);
|
||||
}
|
||||
|
||||
MR_SetChannelAttributes(channel, &att);
|
||||
|
||||
if (IS_MR_CHANNEL(channel)) { // it's a memory channel
|
||||
if (!keep) {
|
||||
// clear/reset the channel name
|
||||
SETTINGS_SaveChannelName(channel, "");
|
||||
}
|
||||
}
|
||||
if (keep) {
|
||||
att.band = pVFO->Band;
|
||||
att.compander = pVFO->Compander;
|
||||
att.scanlist = pVFO->SCANLIST_PARTICIPATION;
|
||||
}
|
||||
|
||||
MR_SetChannelAttributes(channel, &att);
|
||||
|
||||
if (IS_MR_CHANNEL(channel) && !keep)
|
||||
SETTINGS_SaveChannelName(channel, "");
|
||||
}
|
||||
|
||||
void SETTINGS_WriteBuildOptions(void)
|
||||
@@ -1176,12 +1380,12 @@ void SETTINGS_ResetTxLock(void)
|
||||
// This is an expensive operation: full scan of all MR channels
|
||||
|
||||
#define CHANNEL_SIZE 16
|
||||
#define TXLOCK_BYTE_OFFSET 4
|
||||
#define TXLOCK_BYTE_OFFSET 12
|
||||
#define TXLOCK_BIT 6
|
||||
#define SETTINGS_ResetTxLock_BATCH 16
|
||||
#define SETTINGS_ResetTxLock_BATCH 32
|
||||
|
||||
const uint32_t TotalBytes = MR_CHANNELS_MAX * CHANNEL_SIZE; // 512 * 16 = 8192
|
||||
const uint32_t BatchSize = TotalBytes / SETTINGS_ResetTxLock_BATCH; // 8192 / 16 = 512
|
||||
const uint32_t TotalBytes = MR_CHANNELS_MAX * CHANNEL_SIZE; // 1024 * 16 = 16 384
|
||||
const uint32_t BatchSize = TotalBytes / SETTINGS_ResetTxLock_BATCH; // 16 384 / 32 = 512
|
||||
const uint32_t BatchChCnt = BatchSize / CHANNEL_SIZE; // 32 channels per batch
|
||||
|
||||
uint8_t Buf[BatchSize];
|
||||
@@ -1201,6 +1405,9 @@ void SETTINGS_ResetTxLock(void)
|
||||
PY25Q16_WriteBuffer(Offset, Buf, BatchSize, false);
|
||||
}
|
||||
|
||||
RADIO_ConfigureChannel(0, VFO_CONFIGURE_RELOAD);
|
||||
RADIO_ConfigureChannel(1, VFO_CONFIGURE_RELOAD);
|
||||
|
||||
#undef SETTINGS_ResetTxLock_BATCH
|
||||
#undef CHANNEL_SIZE
|
||||
#undef TXLOCK_BYTE_OFFSET
|
||||
|
||||
+35
-9
@@ -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,17 +121,20 @@ enum ACTION_OPT_t {
|
||||
ACTION_OPT_WN,
|
||||
ACTION_OPT_BACKLIGHT,
|
||||
ACTION_OPT_MUTE,
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
ACTION_OPT_RXA,
|
||||
#endif
|
||||
ACTION_OPT_RXA,
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
ACTION_OPT_POWER_HIGH,
|
||||
ACTION_OPT_REMOVE_OFFSET,
|
||||
#endif
|
||||
#endif
|
||||
#ifdef ENABLE_REGA
|
||||
ACTION_OPT_REGA_ALARM,
|
||||
ACTION_OPT_REGA_TEST,
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
ACTION_OPT_BEAM,
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
ACTION_OPT_RXTX_LOG,
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
ACTION_OPT_FOXHUNT,
|
||||
#endif
|
||||
ACTION_OPT_LEN
|
||||
};
|
||||
@@ -196,7 +202,6 @@ typedef struct {
|
||||
uint8_t TX_TIMEOUT_TIMER;
|
||||
bool KEY_LOCK;
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
bool KEY_LOCK_PTT;
|
||||
bool SET_NAV;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
@@ -309,9 +314,30 @@ typedef struct {
|
||||
|
||||
extern EEPROM_Config_t gEeprom;
|
||||
|
||||
typedef struct {
|
||||
FREQ_Config_t rx;
|
||||
FREQ_Config_t tx;
|
||||
uint32_t offset;
|
||||
uint16_t stepFrequency;
|
||||
STEP_Setting_t stepSetting;
|
||||
ModulationMode_t modulation;
|
||||
uint8_t txOffsetFrequencyDirection;
|
||||
uint8_t outputPower;
|
||||
bool frequencyReverse;
|
||||
uint8_t channelBandwidth;
|
||||
uint8_t busyChannelLock;
|
||||
uint8_t txLock;
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
uint8_t dtmfDecodingEnable;
|
||||
#endif
|
||||
PTT_ID_t dtmfPttIdTxMode;
|
||||
} ChannelScanDisplayInfo_t;
|
||||
|
||||
void SETTINGS_InitEEPROM(void);
|
||||
void SETTINGS_LoadCalibration(void);
|
||||
uint32_t SETTINGS_FetchChannelFrequency(const uint16_t channel);
|
||||
bool SETTINGS_FetchChannelScanInfo(const uint16_t channel, uint32_t *frequency, ModulationMode_t *modulation);
|
||||
bool SETTINGS_FetchChannelScanDisplayInfo(const uint16_t channel, ChannelScanDisplayInfo_t *info);
|
||||
void SETTINGS_FetchChannelName(char *s, const uint16_t channel);
|
||||
void SETTINGS_FactoryReset(bool bIsAll);
|
||||
#ifdef ENABLE_FMRADIO
|
||||
@@ -323,7 +349,7 @@ void SETTINGS_SaveSettings(void);
|
||||
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(uint16_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);
|
||||
void SETTINGS_WriteBuildOptions(void);
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
void SETTINGS_WriteCurrentState(void);
|
||||
|
||||
+45
-52
@@ -37,9 +37,8 @@ static int get_bit(uint8_t* array, int bit_index) {
|
||||
|
||||
void UI_DisplayAircopy(void)
|
||||
{
|
||||
char String[16] = { 0 };
|
||||
char *pPrintStr = { 0 };
|
||||
uint16_t percent;
|
||||
char String[16];
|
||||
char *pPrintStr;
|
||||
|
||||
UI_DisplayClear();
|
||||
|
||||
@@ -60,15 +59,13 @@ void UI_DisplayAircopy(void)
|
||||
// show the remaining 2 small frequency digits
|
||||
UI_PrintStringSmallNormal(String + 7, 97, 0, 3);
|
||||
String[7] = 0;
|
||||
// show the main large frequency digits
|
||||
UI_DisplayFrequency(String, 16, 2, false);
|
||||
} else {
|
||||
const char *ascii = INPUTBOX_GetAscii();
|
||||
sprintf(String, "%.3s.%.3s", ascii, ascii + 3);
|
||||
UI_DisplayFrequency(String, 16, 2, false);
|
||||
}
|
||||
|
||||
memset(String, 0, sizeof(String));
|
||||
// show the main large frequency digits
|
||||
UI_DisplayFrequency(String, 16, 2, false);
|
||||
|
||||
// Get the current map and calculate percentage based on its total blocks
|
||||
const AIRCOPY_TransferMap_t *currentMap = AIRCOPY_GetCurrentMap();
|
||||
@@ -78,47 +75,44 @@ void UI_DisplayAircopy(void)
|
||||
if (doneBlocks > currentMap->total_blocks)
|
||||
doneBlocks = currentMap->total_blocks;
|
||||
|
||||
percent = (doneBlocks * 10000) / currentMap->total_blocks;
|
||||
|
||||
if (gAirCopyIsSendMode == 0) {
|
||||
sprintf(String, "RCV:%02u.%02u%% E:%d", percent / 100, percent % 100, gErrorsDuringAirCopy);
|
||||
} else if (gAirCopyIsSendMode == 1) {
|
||||
sprintf(String, "SND:%02u.%02u%%", percent / 100, percent % 100);
|
||||
}
|
||||
|
||||
// Draw gauge
|
||||
if(gAircopyStep != 0)
|
||||
{
|
||||
UI_PrintString(String, 2, 127, 5, 8);
|
||||
|
||||
gFrameBuffer[4][1] = 0x3c;
|
||||
gFrameBuffer[4][2] = 0x42;
|
||||
|
||||
for(uint8_t i = 1; i <= AIRCOPY_BAR_WIDTH + 2; i++)
|
||||
{
|
||||
gFrameBuffer[4][2 + i] = 0x81;
|
||||
}
|
||||
|
||||
gFrameBuffer[4][125] = 0x42;
|
||||
gFrameBuffer[4][126] = 0x3c;
|
||||
}
|
||||
|
||||
// Draw memory selection
|
||||
if(gAircopyState == AIRCOPY_READY)
|
||||
if (gAircopyState == AIRCOPY_READY)
|
||||
{
|
||||
doneBlocks = 0;
|
||||
|
||||
memset(gFrameBuffer[5], 0, 128);
|
||||
memset(gFrameBuffer[6], 0, 128);
|
||||
|
||||
if(gAircopyCurrentMapIndex < AIRCOPY_NUM_BANKS) {
|
||||
sprintf(String, "MEM %03u - %03u%", (gAircopyCurrentMapIndex * 128) + 1, (gAircopyCurrentMapIndex + 1) * 128);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(String, "Settings");
|
||||
sprintf(String, "MEM %03u - %03u", (gAircopyCurrentMapIndex * 128) + 1, (gAircopyCurrentMapIndex + 1) * 128);
|
||||
} else {
|
||||
strcpy(String, "Settings");
|
||||
}
|
||||
UI_PrintString(String, 2, 127, 5, 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t percent = (doneBlocks * 10000) / currentMap->total_blocks;
|
||||
|
||||
if (gAirCopyIsSendMode == 0) {
|
||||
sprintf(String, "RCV:%02u.%02u%% E:%d", percent / 100, percent % 100, gErrorsDuringAirCopy);
|
||||
} else {
|
||||
sprintf(String, "SND:%02u.%02u%%", percent / 100, percent % 100);
|
||||
}
|
||||
|
||||
// Draw gauge
|
||||
if(gAircopyStep != 0)
|
||||
{
|
||||
UI_PrintString(String, 2, 127, 5, 8);
|
||||
|
||||
gFrameBuffer[4][1] = 0x3c;
|
||||
gFrameBuffer[4][2] = 0x42;
|
||||
|
||||
for(uint8_t i = 1; i <= AIRCOPY_BAR_WIDTH + 2; i++)
|
||||
{
|
||||
gFrameBuffer[4][2 + i] = 0x81;
|
||||
}
|
||||
|
||||
gFrameBuffer[4][125] = 0x42;
|
||||
gFrameBuffer[4][126] = 0x3c;
|
||||
}
|
||||
}
|
||||
|
||||
if (doneBlocks > 0)
|
||||
@@ -131,19 +125,12 @@ void UI_DisplayAircopy(void)
|
||||
lErrorsDuringAirCopy = gErrorsDuringAirCopy;
|
||||
}
|
||||
|
||||
const AIRCOPY_TransferMap_t *currentMap = AIRCOPY_GetCurrentMap();
|
||||
|
||||
uint16_t total = currentMap->total_blocks;
|
||||
uint16_t done = gAirCopyBlockNumber + gErrorsDuringAirCopy;
|
||||
|
||||
if (done > total) done = total;
|
||||
uint16_t b = 0;
|
||||
uint16_t fraction_accumulator = 0;
|
||||
|
||||
for (uint8_t col = 0; col < AIRCOPY_BAR_WIDTH; col++)
|
||||
{
|
||||
/* Map column [0..BAR_WIDTH-1] to block [0..total-1] */
|
||||
uint16_t b = (uint16_t)((col * (uint32_t)total) / AIRCOPY_BAR_WIDTH);
|
||||
|
||||
bool processed = (b < done);
|
||||
bool processed = (b < doneBlocks);
|
||||
bool error = processed && get_bit(crc, b);
|
||||
|
||||
if (!processed)
|
||||
@@ -152,8 +139,14 @@ void UI_DisplayAircopy(void)
|
||||
gFrameBuffer[4][col + 4] = 0x81; // error gap (intentional hole)
|
||||
else
|
||||
gFrameBuffer[4][col + 4] = 0xBD; // ok filled
|
||||
}
|
||||
|
||||
// DDA/Bresenham algorythm
|
||||
fraction_accumulator += currentMap->total_blocks;
|
||||
while (fraction_accumulator >= AIRCOPY_BAR_WIDTH) {
|
||||
fraction_accumulator -= AIRCOPY_BAR_WIDTH;
|
||||
b++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ST7565_BlitFullScreen();
|
||||
|
||||
+5
-2
@@ -31,10 +31,14 @@
|
||||
|
||||
void UI_DisplayFM(void)
|
||||
{
|
||||
char String[16] = {0};
|
||||
char String[16];
|
||||
char *pPrintStr = String;
|
||||
UI_DisplayClear();
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
UI_DisplayUnlockKeyboard(5);
|
||||
#endif
|
||||
|
||||
UI_PrintString("FM", 2, 0, 0, 8);
|
||||
|
||||
sprintf(String, "%d%s-%dM",
|
||||
@@ -76,7 +80,6 @@ void UI_DisplayFM(void)
|
||||
|
||||
UI_PrintString(pPrintStr, 0, 127, 3, 10); // memory, vfo, scan
|
||||
|
||||
memset(String, 0, sizeof(String));
|
||||
if (gAskToSave || (gEeprom.FM_IsMrMode && gInputBoxIndex > 0)) {
|
||||
UI_GenerateChannelString(String, gFM_ChannelPosition);
|
||||
} else if (gAskToDelete) {
|
||||
|
||||
+62
-37
@@ -22,10 +22,8 @@
|
||||
#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 uint16_t Channel)
|
||||
{
|
||||
@@ -42,28 +40,30 @@ void UI_GenerateChannelString(char *pString, const uint16_t Channel)
|
||||
pString[2] = '-';
|
||||
for (i = 0; i < 2; i++)
|
||||
pString[i + 3] = (gInputBox[i] == 10) ? '-' : gInputBox[i] + '0';
|
||||
|
||||
pString[5] = 0;
|
||||
}
|
||||
|
||||
void UI_GenerateChannelStringEx(char *pString, const bool bShowPrefix, const uint16_t ChannelNumber)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,17 +133,17 @@ void UI_PrintStringSmallNormalInverse(const char *pString, uint8_t Start, uint8_
|
||||
if (End != 0 && x_end > End)
|
||||
x_end = End;
|
||||
|
||||
gFrameBuffer[Line][x_start - 3] ^= 0x3E;
|
||||
gFrameBuffer[Line][x_start - 2] ^= 0x7F;
|
||||
gFrameBuffer[Line][x_start - 1] ^= 0xFF;
|
||||
//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 + 1] ^= 0x7F;
|
||||
gFrameBuffer[Line][x_end + 2] ^= 0x3E;
|
||||
//gFrameBuffer[Line][x_end + 0] ^= 0xFF;
|
||||
gFrameBuffer[Line][x_end + 0] ^= 0x7F;
|
||||
//gFrameBuffer[Line][x_end + 1] ^= 0x3E;
|
||||
}
|
||||
|
||||
|
||||
@@ -160,30 +160,6 @@ void UI_PrintStringSmallBold(const char *pString, uint8_t Start, uint8_t End, ui
|
||||
UI_PrintStringSmall(pString, Start, End, Line, char_width, font);
|
||||
}
|
||||
|
||||
void UI_PrintStringSmallBoldInverse(const char *pString, uint8_t Start, uint8_t End, uint8_t Line)
|
||||
{
|
||||
// First draw the string normally
|
||||
UI_PrintStringSmallBold(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);
|
||||
|
||||
if (End != 0 && x_end > End)
|
||||
x_end = End;
|
||||
|
||||
gFrameBuffer[Line][x_start] ^= 0x7F;
|
||||
for (uint8_t x = x_start + 1; x < x_end; x++)
|
||||
{
|
||||
gFrameBuffer[Line][x] ^= 0x41;
|
||||
}
|
||||
gFrameBuffer[Line][x_end + 1] ^= 0x7F;
|
||||
}
|
||||
|
||||
|
||||
void UI_PrintStringSmallBufferNormal(const char *pString, uint8_t * buffer)
|
||||
{
|
||||
UI_PrintStringBuffer(pString, buffer, ARRAY_SIZE(gFontSmall[0]), (uint8_t *)gFontSmall);
|
||||
@@ -348,6 +324,50 @@ static void sort(int16_t *a, int16_t *b)
|
||||
x += 4;
|
||||
}
|
||||
}
|
||||
|
||||
void GUI_DisplaySmallestInverse(const char *pString, uint8_t x, uint8_t Line,
|
||||
bool statusbar, bool fill, uint8_t end)
|
||||
{
|
||||
// First draw the string normally
|
||||
GUI_DisplaySmallest(pString, x, (Line * 8) + 1, statusbar, fill);
|
||||
|
||||
// Now invert the framebuffer/statusline bits for the rendered area
|
||||
uint8_t start = (x - 2);
|
||||
uint8_t *buffer = statusbar ? gStatusLine : gFrameBuffer[Line];
|
||||
|
||||
buffer[start] ^= 0x3E;
|
||||
for (uint8_t i = start + 1; i < end; i++) {
|
||||
buffer[i] ^= 0x7F;
|
||||
}
|
||||
buffer[end] ^= 0x3E;
|
||||
}
|
||||
|
||||
void UI_DisplayUnlockKeyboard(uint8_t shift) {
|
||||
if (gEeprom.KEY_LOCK && gKeypadLocked > 0)
|
||||
{ // tell user how to unlock the keyboard
|
||||
|
||||
//memcpy(gFrameBuffer[shift] + 2, gFontKeyLock, sizeof(gFontKeyLock));
|
||||
UI_PrintStringSmallBold("UNLOCK KEYBOARD", 12, 0, shift);
|
||||
//memcpy(gFrameBuffer[shift] + 120, gFontKeyLock, sizeof(gFontKeyLock));
|
||||
|
||||
/*
|
||||
for (uint8_t i = 12; i < 116; i++)
|
||||
{
|
||||
gFrameBuffer[shift][i] ^= 0xFF;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
bool IsEmptyName(const char *name, uint8_t len) {
|
||||
if (name[0] == '\0' || name[0] == '\xff')
|
||||
return true;
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
if (name[i] != ' ' && name[i] != '\xff' && name[i] != '\0')
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void UI_DrawLineBuffer(uint8_t (*buffer)[128], int16_t x1, int16_t y1, int16_t x2, int16_t y2, bool black)
|
||||
@@ -405,3 +425,8 @@ void UI_DisplayClear()
|
||||
{
|
||||
memset(gFrameBuffer, 0, sizeof(gFrameBuffer));
|
||||
}
|
||||
|
||||
void UI_StatusClear()
|
||||
{
|
||||
memset(gStatusLine, 0, sizeof(gStatusLine));
|
||||
}
|
||||
+4
-1
@@ -26,7 +26,6 @@ void UI_PrintString(const char *pString, uint8_t Start, uint8_t End, uint8_t Lin
|
||||
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_PrintStringSmallBoldInverse(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);
|
||||
void UI_DisplayFrequency(const char *string, uint8_t X, uint8_t Y, bool center);
|
||||
@@ -39,10 +38,14 @@ void UI_DrawPixelBuffer(uint8_t (*buffer)[128], uint8_t x, uint8_t y, bool black
|
||||
void PutPixel(uint8_t x, uint8_t y, bool fill);
|
||||
void PutPixelStatus(uint8_t x, uint8_t y, bool fill);
|
||||
void GUI_DisplaySmallest(const char *pString, uint8_t x, uint8_t y, bool statusbar, bool fill);
|
||||
void GUI_DisplaySmallestInverse(const char *pString, uint8_t x, uint8_t Line, bool statusbar, bool fill, uint8_t endX);
|
||||
void UI_DisplayUnlockKeyboard(uint8_t shift);
|
||||
bool IsEmptyName(const char *name, uint8_t len);
|
||||
#endif
|
||||
void UI_DrawLineBuffer(uint8_t (*buffer)[128], int16_t x1, int16_t y1, int16_t x2, int16_t y2, bool black);
|
||||
void UI_DrawRectangleBuffer(uint8_t (*buffer)[128], int16_t x1, int16_t y1, int16_t x2, int16_t y2, bool black);
|
||||
|
||||
void UI_DisplayClear();
|
||||
void UI_StatusClear();
|
||||
|
||||
#endif
|
||||
@@ -41,4 +41,19 @@ const char* INPUTBOX_GetAscii()
|
||||
inputBoxAscii[i] = (c==10)? '-' : '0' + c;
|
||||
}
|
||||
return inputBoxAscii;
|
||||
}
|
||||
|
||||
const char* INPUTBOX_GetAsciiAlignRight() {
|
||||
int targetPos = 7;
|
||||
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
if (gInputBox[i] != 10) {
|
||||
inputBoxAscii[targetPos--] = '0' + gInputBox[i];
|
||||
}
|
||||
}
|
||||
|
||||
while(targetPos >= 0)
|
||||
inputBoxAscii[targetPos--] = '-';
|
||||
|
||||
return inputBoxAscii;
|
||||
}
|
||||
@@ -25,7 +25,9 @@ extern char gInputBox[8];
|
||||
extern uint8_t gInputBoxIndex;
|
||||
|
||||
void INPUTBOX_Append(const KEY_Code_t Digit);
|
||||
|
||||
const char* INPUTBOX_GetAscii();
|
||||
const char* INPUTBOX_GetAsciiAlignRight();
|
||||
|
||||
#endif
|
||||
|
||||
+2
-11
@@ -34,7 +34,7 @@ static void Render(void)
|
||||
unsigned int i;
|
||||
char String[7];
|
||||
|
||||
memset(gStatusLine, 0, sizeof(gStatusLine));
|
||||
UI_StatusClear();
|
||||
UI_DisplayClear();
|
||||
|
||||
UI_PrintString("PASSWORD", 0, 127, 1, 10);
|
||||
@@ -80,16 +80,7 @@ void UI_DisplayLock(void)
|
||||
|
||||
switch (Key)
|
||||
{
|
||||
case KEY_0:
|
||||
case KEY_1:
|
||||
case KEY_2:
|
||||
case KEY_3:
|
||||
case KEY_4:
|
||||
case KEY_5:
|
||||
case KEY_6:
|
||||
case KEY_7:
|
||||
case KEY_8:
|
||||
case KEY_9:
|
||||
case KEY_0...KEY_9:
|
||||
INPUTBOX_Append(Key - KEY_0);
|
||||
|
||||
if (gInputBoxIndex < 6) // 6 frequency digits
|
||||
|
||||
+1072
-178
File diff suppressed because it is too large.
Load diff
+21
-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,24 @@ 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);
|
||||
void UI_MAIN_NotifyScanListChanged(void);
|
||||
#else
|
||||
static inline void UI_MAIN_NotifyScanProgressDataChanged(void) {}
|
||||
static inline void UI_MAIN_NotifyScanListChanged(void) {}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_AGC_SHOW_DATA
|
||||
void UI_MAIN_PrintAGC(bool force);
|
||||
#endif
|
||||
|
||||
+378
-146
@@ -27,6 +27,7 @@
|
||||
#include "../driver/eeprom.h"
|
||||
#include "../driver/st7565.h"
|
||||
#include "../external/printf/printf.h"
|
||||
#include "../font.h"
|
||||
#include "../frequencies.h"
|
||||
#include "../helper/battery.h"
|
||||
#include "../misc.h"
|
||||
@@ -40,6 +41,7 @@
|
||||
#include "inputbox.h"
|
||||
#include "menu.h"
|
||||
#include "ui.h"
|
||||
#include "welcome.h"
|
||||
|
||||
|
||||
const t_menu_item MenuList[] =
|
||||
@@ -166,6 +168,12 @@ const t_menu_item MenuList[] =
|
||||
#ifdef ENABLE_NOAA
|
||||
{"SetNWR", MENU_NOAA_S },
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
{"SetScn", MENU_SET_SCN },
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
{"SetSav", MENU_SET_SAV },
|
||||
#endif
|
||||
#endif
|
||||
// hidden menu items from here on
|
||||
// enabled if pressing both the PTT and upper side button at power-on
|
||||
@@ -192,7 +200,7 @@ const t_menu_item MenuList[] =
|
||||
|
||||
const uint8_t FIRST_HIDDEN_MENU_ITEM = MENU_F_LOCK;
|
||||
|
||||
const char gSubMenu_TXP[][6] =
|
||||
const char* const gSubMenu_TXP[] =
|
||||
{
|
||||
"USER",
|
||||
"LOW 1",
|
||||
@@ -204,29 +212,26 @@ const char gSubMenu_TXP[][6] =
|
||||
"HIGH"
|
||||
};
|
||||
|
||||
const char gSubMenu_SFT_D[][4] =
|
||||
const char* const gSubMenu_SFT_D[] =
|
||||
{
|
||||
"OFF",
|
||||
"+",
|
||||
"-"
|
||||
};
|
||||
|
||||
const char gSubMenu_W_N[][7] =
|
||||
const char* const gSubMenu_W_N[] =
|
||||
{
|
||||
"WIDE",
|
||||
"NARROW"
|
||||
};
|
||||
|
||||
const char gSubMenu_OFF_ON[][4] =
|
||||
const char* const gSubMenu_OFF_ON[] =
|
||||
{
|
||||
"OFF",
|
||||
"ON"
|
||||
};
|
||||
|
||||
const char gSubMenu_NA[4] =
|
||||
{
|
||||
"N/A"
|
||||
};
|
||||
const char* gSubMenu_NA = "N/A";
|
||||
|
||||
const char* const gSubMenu_RXMode[] =
|
||||
{
|
||||
@@ -237,7 +242,7 @@ const char* const gSubMenu_RXMode[] =
|
||||
};
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
const char gSubMenu_VOICE[][4] =
|
||||
const char* const gSubMenu_VOICE[] =
|
||||
{
|
||||
"OFF",
|
||||
"CHI",
|
||||
@@ -254,7 +259,7 @@ const char* const gSubMenu_MDF[] =
|
||||
};
|
||||
|
||||
#ifdef ENABLE_ALARM
|
||||
const char gSubMenu_AL_MOD[][5] =
|
||||
const char* const gSubMenu_AL_MOD[] =
|
||||
{
|
||||
"SITE",
|
||||
"TONE"
|
||||
@@ -262,7 +267,7 @@ const char* const gSubMenu_MDF[] =
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
const char gSubMenu_D_RSP[][11] =
|
||||
const char* const gSubMenu_D_RSP[] =
|
||||
{
|
||||
"DO\nNOTHING",
|
||||
"RING",
|
||||
@@ -280,7 +285,7 @@ const char* const gSubMenu_PTT_ID[] =
|
||||
"APOLLO\nQUINDAR"
|
||||
};
|
||||
|
||||
const char gSubMenu_PONMSG[][8] =
|
||||
const char* const gSubMenu_PONMSG[] =
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
"ALL",
|
||||
@@ -290,23 +295,36 @@ const char gSubMenu_PONMSG[][8] =
|
||||
#endif
|
||||
"MESSAGE",
|
||||
"VOLTAGE",
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO
|
||||
"LOGO",
|
||||
#endif
|
||||
"NONE"
|
||||
};
|
||||
|
||||
const char gSubMenu_ROGER[][6] =
|
||||
#if defined(ENABLE_FEAT_F4HWN) && defined(ENABLE_FEAT_F4HWN_LOGO_SAV)
|
||||
const char* const gSubMenu_SET_SAV[] =
|
||||
{
|
||||
"OFF",
|
||||
"LOGO",
|
||||
"LOGO+",
|
||||
"MATRIX"
|
||||
};
|
||||
#endif
|
||||
|
||||
const char* const gSubMenu_ROGER[] =
|
||||
{
|
||||
"OFF",
|
||||
"ROGER",
|
||||
"MDC"
|
||||
};
|
||||
|
||||
const char gSubMenu_RESET[][4] =
|
||||
const char* const gSubMenu_RESET[] =
|
||||
{
|
||||
"VFO",
|
||||
"ALL"
|
||||
};
|
||||
|
||||
const char * const gSubMenu_F_LOCK[] =
|
||||
const char* const gSubMenu_F_LOCK[] =
|
||||
{
|
||||
"DEFAULT+\n137-174\n400-470",
|
||||
"FCC HAM\n144-148\n420-450",
|
||||
@@ -327,7 +345,7 @@ const char * const gSubMenu_F_LOCK[] =
|
||||
"UNLOCK\nALL",
|
||||
};
|
||||
|
||||
const char gSubMenu_RX_TX[][6] =
|
||||
const char* const gSubMenu_RX_TX[] =
|
||||
{
|
||||
"OFF",
|
||||
"TX",
|
||||
@@ -335,14 +353,14 @@ const char gSubMenu_RX_TX[][6] =
|
||||
"TX/RX"
|
||||
};
|
||||
|
||||
const char gSubMenu_BAT_TXT[][8] =
|
||||
const char* const gSubMenu_BAT_TXT[] =
|
||||
{
|
||||
"NONE",
|
||||
"VOLTAGE",
|
||||
"PERCENT"
|
||||
};
|
||||
|
||||
const char gSubMenu_BATTYP[][12] =
|
||||
const char* const gSubMenu_BATTYP[] =
|
||||
{
|
||||
"1600mAh K5",
|
||||
"2200mAh K5",
|
||||
@@ -351,14 +369,14 @@ const char gSubMenu_BATTYP[][12] =
|
||||
"2500mAh K1"
|
||||
};
|
||||
|
||||
const char gSubMenu_SET_NAV[][17] =
|
||||
const char* const gSubMenu_SET_NAV[] =
|
||||
{
|
||||
"LEFT\nRIGHT\nUV-K1",
|
||||
"UP\nDOWN\nUV-K5(8)",
|
||||
};
|
||||
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
const char gSubMenu_SCRAMBLER[][7] =
|
||||
const char* const gSubMenu_SCRAMBLER[] =
|
||||
{
|
||||
"OFF",
|
||||
"2600Hz",
|
||||
@@ -375,7 +393,7 @@ const char gSubMenu_SCRAMBLER[][7] =
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
const char gSubMenu_SET_PWR[][6] =
|
||||
const char* const gSubMenu_SET_PWR[] =
|
||||
{
|
||||
"< 20m",
|
||||
"125m",
|
||||
@@ -386,13 +404,13 @@ const char gSubMenu_SCRAMBLER[][7] =
|
||||
"5"
|
||||
};
|
||||
|
||||
const char gSubMenu_SET_PTT[][8] =
|
||||
const char* const gSubMenu_SET_PTT[] =
|
||||
{
|
||||
"CLASSIC",
|
||||
"ONEPUSH"
|
||||
};
|
||||
|
||||
const char gSubMenu_SET_TOT[][7] = // Use by SET_EOT too
|
||||
const char* const gSubMenu_SET_TOT[] =
|
||||
{
|
||||
"OFF",
|
||||
"SOUND",
|
||||
@@ -400,20 +418,30 @@ const char gSubMenu_SCRAMBLER[][7] =
|
||||
"ALL"
|
||||
};
|
||||
|
||||
const char gSubMenu_SET_LCK[][9] =
|
||||
const char* const gSubMenu_SET_LCK[] =
|
||||
{
|
||||
"KEYS",
|
||||
"KEYS+PTT"
|
||||
"KEYS\nACTIONS",
|
||||
"KEYS\nPTT",
|
||||
"KEYS\nACTIONS\nPTT"
|
||||
};
|
||||
|
||||
const char gSubMenu_SET_MET[][8] =
|
||||
const char* const gSubMenu_SET_MET[] =
|
||||
{
|
||||
"TINY",
|
||||
"CLASSIC"
|
||||
};
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
const char* const gSubMenu_SET_SCN[] =
|
||||
{
|
||||
"NORMAL",
|
||||
"FAST"
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
const char gSubMenu_SET_AUD[][6] =
|
||||
const char* const gSubMenu_SET_AUD_FM[] =
|
||||
{
|
||||
"FLAT",
|
||||
"CLEAN",
|
||||
@@ -421,10 +449,17 @@ const char gSubMenu_SCRAMBLER[][7] =
|
||||
"BOOST",
|
||||
"MAX"
|
||||
};
|
||||
|
||||
const char* const gSubMenu_SET_AUD_AM[] =
|
||||
{
|
||||
"SHARP",
|
||||
"STOCK",
|
||||
"OPEN"
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
const char gSubMenu_SET_NFM[][9] =
|
||||
const char* const gSubMenu_SET_NFM[] =
|
||||
{
|
||||
"NARROW",
|
||||
"NARROWER"
|
||||
@@ -432,7 +467,7 @@ const char gSubMenu_SCRAMBLER[][7] =
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
const char gSubMenu_SET_KEY[][9] =
|
||||
const char* const gSubMenu_SET_KEY[] =
|
||||
{
|
||||
"KEY_MENU",
|
||||
"KEY_UP",
|
||||
@@ -463,17 +498,13 @@ const t_sidefunction gSubMenu_SIDEFUNCTIONS[] =
|
||||
#endif
|
||||
#ifdef ENABLE_TX1750
|
||||
{"1750Hz", ACTION_OPT_1750},
|
||||
#endif
|
||||
#ifdef ENABLE_REGA
|
||||
{"REGA\nALARM", ACTION_OPT_REGA_ALARM},
|
||||
{"REGA\nTEST", ACTION_OPT_REGA_TEST},
|
||||
#endif
|
||||
{"LOCK\nKEYPAD", ACTION_OPT_KEYLOCK},
|
||||
{"VFO A\nVFO B", ACTION_OPT_A_B},
|
||||
{"VFO\nMEM", ACTION_OPT_VFO_MR},
|
||||
{"MODE", ACTION_OPT_SWITCH_DEMODUL},
|
||||
#ifdef ENABLE_BLMIN_TMP_OFF
|
||||
{"BLMIN\nTMP OFF", ACTION_OPT_BLMIN_TMP_OFF}, //BackLight Minimum Temporay OFF
|
||||
{"BLMIN\nTMP OFF", ACTION_OPT_BLMIN_TMP_OFF}, //BackLight Minimum Temporary OFF
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
{"RX MODE", ACTION_OPT_RXMODE},
|
||||
@@ -488,6 +519,15 @@ const t_sidefunction gSubMenu_SIDEFUNCTIONS[] =
|
||||
{"POWER\nHIGH", ACTION_OPT_POWER_HIGH},
|
||||
{"REMOVE\nOFFSET", ACTION_OPT_REMOVE_OFFSET},
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_BEAM
|
||||
{"BEAM", ACTION_OPT_BEAM},
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
{"RF LOG", ACTION_OPT_RXTX_LOG},
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_FOXHUNT
|
||||
{"FOX HUNT\nBEACON", ACTION_OPT_FOXHUNT},
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -516,6 +556,64 @@ int32_t gSubMenuSelection;
|
||||
char edit_original[17]; // a copy of the text before editing so that we can easily test for changes/difference
|
||||
char edit[17];
|
||||
int edit_index;
|
||||
bool edit_is_uppercase = false;
|
||||
|
||||
static void UI_MENU_DrawTopRightRoundedBadge(const char *text, const uint8_t line, const bool center_in_area, const uint8_t area_x1, const uint8_t area_x2)
|
||||
{
|
||||
const size_t length = strlen(text);
|
||||
const size_t char_pitch = ARRAY_SIZE(gFontSmall[0]) + 1u;
|
||||
const size_t text_width = length * char_pitch;
|
||||
const size_t capsule_span = text_width + 1u; // matches UI_PrintStringSmallNormalInverse x_end computation
|
||||
uint8_t text_x;
|
||||
|
||||
if (length == 0 || line == 0 || line >= FRAME_LINES) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (center_in_area && area_x2 > area_x1 + 2u) {
|
||||
const uint8_t min_x = area_x1 + 1u;
|
||||
uint8_t max_x;
|
||||
const uint8_t area_width = area_x2 - area_x1 + 1u;
|
||||
|
||||
if (capsule_span >= area_width) {
|
||||
text_x = min_x;
|
||||
} else {
|
||||
text_x = (uint8_t)(area_x1 + ((area_width - capsule_span) / 2u));
|
||||
}
|
||||
|
||||
if (area_x2 > capsule_span) {
|
||||
max_x = (uint8_t)(area_x2 - capsule_span);
|
||||
} else {
|
||||
max_x = min_x;
|
||||
}
|
||||
|
||||
if (max_x < min_x) {
|
||||
max_x = min_x;
|
||||
}
|
||||
if (text_x < min_x) {
|
||||
text_x = min_x;
|
||||
} else if (text_x > max_x) {
|
||||
text_x = max_x;
|
||||
}
|
||||
} else {
|
||||
if (capsule_span >= (LCD_WIDTH - 3u)) {
|
||||
text_x = 1u;
|
||||
} else {
|
||||
const uint8_t global_shift_right = 1u;
|
||||
const uint8_t base_text_x = (uint8_t)(LCD_WIDTH - capsule_span - 3u);
|
||||
const uint8_t max_text_x = (uint8_t)(LCD_WIDTH - capsule_span - 1u);
|
||||
const uint16_t shifted_x = (uint16_t)base_text_x + global_shift_right;
|
||||
|
||||
if (shifted_x > max_text_x) {
|
||||
text_x = max_text_x;
|
||||
} else {
|
||||
text_x = (uint8_t)shifted_x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UI_PrintStringSmallNormalInverse(text, text_x, 0, line);
|
||||
}
|
||||
|
||||
void UI_DisplayMenu(void)
|
||||
{
|
||||
@@ -524,6 +622,9 @@ void UI_DisplayMenu(void)
|
||||
const unsigned int menu_item_x2 = LCD_WIDTH - 1;
|
||||
unsigned int i;
|
||||
char String[64]; // bigger cuz we can now do multi-line in one string (use '\n' char)
|
||||
char top_right_badge[16];
|
||||
|
||||
const int m = UI_MENU_GetCurrentMenuId();
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
char Contact[16];
|
||||
@@ -571,57 +672,56 @@ void UI_DisplayMenu(void)
|
||||
#else
|
||||
{ // new menu layout .. experimental & unfinished
|
||||
const int menu_index = gMenuCursor; // current selected menu item
|
||||
i = 1;
|
||||
const int menu_count = (int)gMenuListCount;
|
||||
|
||||
if (!gIsInSubMenu) {
|
||||
while (i < 2)
|
||||
{ // leading menu items - small text
|
||||
const int k = menu_index + i - 2;
|
||||
if (k < 0)
|
||||
UI_PrintStringSmallNormal(MenuList[gMenuListCount + k].name, 0, 0, i); // wrap-a-round
|
||||
else if (k >= 0 && k < (int)gMenuListCount)
|
||||
UI_PrintStringSmallNormal(MenuList[k].name, 0, 0, i);
|
||||
i++;
|
||||
}
|
||||
if (menu_index >= 0 && menu_index < menu_count)
|
||||
{
|
||||
if (!gIsInSubMenu)
|
||||
{
|
||||
// leading menu items - small text
|
||||
int prev_index = menu_index - 1;
|
||||
if (prev_index < 0) {
|
||||
prev_index = menu_count - 1;
|
||||
}
|
||||
UI_PrintStringSmallNormal(MenuList[prev_index].name, 0, 0, 1);
|
||||
|
||||
// current menu item - keep big n fat
|
||||
if (menu_index >= 0 && menu_index < (int)gMenuListCount)
|
||||
// current menu item - keep big n fat
|
||||
UI_PrintString(MenuList[menu_index].name, 0, 0, 2, 8);
|
||||
i++;
|
||||
|
||||
while (i < 4)
|
||||
{ // trailing menu item - small text
|
||||
const int k = menu_index + i - 2;
|
||||
if (k >= 0 && k < (int)gMenuListCount)
|
||||
UI_PrintStringSmallNormal(MenuList[k].name, 0, 0, 1 + i);
|
||||
else if (k >= (int)gMenuListCount)
|
||||
UI_PrintStringSmallNormal(MenuList[gMenuListCount - k].name, 0, 0, 1 + i); // wrap-a-round
|
||||
i++;
|
||||
// trailing menu item - small text
|
||||
int next_index = menu_index + 1;
|
||||
if (next_index >= menu_count) {
|
||||
next_index = 0;
|
||||
}
|
||||
UI_PrintStringSmallNormal(MenuList[next_index].name, 0, 0, 4);
|
||||
|
||||
|
||||
// draw the menu index number/count
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
sprintf(String, "%2u.%u", 1 + menu_index, menu_count);
|
||||
UI_PrintStringSmallNormal(String, 2, 0, 6);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// current menu item
|
||||
// strcat(String, ":");
|
||||
UI_PrintString(MenuList[menu_index].name, 0, 0, 0, 8);
|
||||
// UI_PrintStringSmallNormal(String, 0, 0, 0);
|
||||
}
|
||||
|
||||
// draw the menu index number/count
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
sprintf(String, "%2u.%u", 1 + gMenuCursor, gMenuListCount);
|
||||
UI_PrintStringSmallNormal(String, 2, 0, 6);
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
sprintf(String, "%02u/%u", 1 + menu_index, menu_count);
|
||||
UI_PrintStringSmallNormal(String, 6, 0, 6);
|
||||
#endif
|
||||
}
|
||||
else if (menu_index >= 0 && menu_index < (int)gMenuListCount)
|
||||
{ // current menu item
|
||||
// strcat(String, ":");
|
||||
UI_PrintString(MenuList[menu_index].name, 0, 0, 0, 8);
|
||||
// UI_PrintStringSmallNormal(String, 0, 0, 0);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
sprintf(String, "%02u/%u", 1 + gMenuCursor, gMenuListCount);
|
||||
UI_PrintStringSmallNormal(String, 6, 0, 6);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
// **************
|
||||
|
||||
memset(String, 0, sizeof(String));
|
||||
String[0] = '\0';
|
||||
top_right_badge[0] = '\0';
|
||||
|
||||
bool already_printed = false;
|
||||
|
||||
@@ -637,7 +737,7 @@ void UI_DisplayMenu(void)
|
||||
uint8_t gaugeMax = 0;
|
||||
//#endif
|
||||
|
||||
switch (UI_MENU_GetCurrentMenuId())
|
||||
switch (m)
|
||||
{
|
||||
case MENU_SQL:
|
||||
sprintf(String, "%d", gSubMenuSelection);
|
||||
@@ -646,7 +746,11 @@ void UI_DisplayMenu(void)
|
||||
case MENU_MIC:
|
||||
{ // display the mic gain in actual dB rather than just an index number
|
||||
const uint8_t mic = gMicGain_dB2[gSubMenuSelection];
|
||||
sprintf(String, "+%u.%01udB", mic / 2, mic % 2);
|
||||
sprintf(String, "+%u.%udB", mic / 2, (mic % 2) * 5);
|
||||
|
||||
gaugeLine = 4;
|
||||
gaugeMin = 0;
|
||||
gaugeMax = 8;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -703,15 +807,14 @@ void UI_DisplayMenu(void)
|
||||
if (!gIsInSubMenu || gInputBoxIndex == 0)
|
||||
{
|
||||
sprintf(String, "%3d.%05u", gSubMenuSelection / 100000, abs(gSubMenuSelection) % 100000);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 1, 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
const char * ascii = INPUTBOX_GetAscii();
|
||||
sprintf(String, "%.3s.%.3s ",ascii, ascii + 3);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 1, 8);
|
||||
}
|
||||
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 1, 8);
|
||||
UI_PrintString("MHz", menu_item_x1, menu_item_x2, 3, 8);
|
||||
|
||||
already_printed = true;
|
||||
@@ -832,6 +935,12 @@ void UI_DisplayMenu(void)
|
||||
strcpy(String, gSubMenu_OFF_ON[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN) && defined(ENABLE_FEAT_F4HWN_LOGO_SAV)
|
||||
case MENU_SET_SAV:
|
||||
strcpy(String, gSubMenu_SET_SAV[gSubMenuSelection]);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case MENU_MEM_CH:
|
||||
case MENU_1_CALL:
|
||||
case MENU_DEL_CH:
|
||||
@@ -855,7 +964,7 @@ void UI_DisplayMenu(void)
|
||||
{ // show the frequency so that the user knows the channels frequency
|
||||
const uint32_t frequency = SETTINGS_FetchChannelFrequency(gSubMenuSelection);
|
||||
sprintf(String, "%u.%05u", frequency / 100000, frequency % 100000);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 4, 8);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 5, 8);
|
||||
}
|
||||
|
||||
SETTINGS_FetchChannelName(String, gSubMenuSelection);
|
||||
@@ -889,15 +998,34 @@ void UI_DisplayMenu(void)
|
||||
{ // show the channel name being edited
|
||||
//UI_PrintString(edit, menu_item_x1, 0, 2, 8);
|
||||
UI_PrintString(edit, menu_item_x1, menu_item_x2, 2, 8);
|
||||
if (edit_index < 10)
|
||||
//UI_PrintString("^", menu_item_x1 + (8 * edit_index), 0, 4, 8); // show the cursor
|
||||
UI_PrintString("^", menu_item_x1 - 1 + (8 * edit_index),0, 4, 8); // show the cursor
|
||||
if (edit_index < 10) {
|
||||
// UI_PrintString("^", menu_item_x1 - 1 + (8 * edit_index),0, 4, 8); // show the cursor
|
||||
uint8_t x = menu_item_x1 - 1;
|
||||
for (uint8_t i = 0; i < 10; i++)
|
||||
{
|
||||
if (i != edit_index)
|
||||
{
|
||||
if (edit[i] != 'g' && edit[i] != 'j')
|
||||
{
|
||||
UI_DrawLineBuffer(gFrameBuffer, x, 29, x + 6, 29, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UI_DrawLineBuffer(gFrameBuffer, x + 2, 30, x + 4, 30, 1);
|
||||
UI_DrawPixelBuffer(gFrameBuffer, x + 3, 29, 1);
|
||||
}
|
||||
x += 8;
|
||||
}
|
||||
|
||||
UI_PrintStringSmallNormal(edit_is_uppercase ? "ABC" : "abc", 77, 0, 4);
|
||||
}
|
||||
}
|
||||
|
||||
if (!gAskForConfirmation)
|
||||
{ // show the frequency so that the user knows the channels frequency
|
||||
sprintf(String, "%u.%05u", frequency / 100000, frequency % 100000);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 4 + (gIsInSubMenu && edit_index >= 0), 8);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 5, 8);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -965,21 +1093,22 @@ void UI_DisplayMenu(void)
|
||||
break;
|
||||
|
||||
case MENU_LIST_CH:
|
||||
if (gSubMenuSelection == MR_CHANNELS_LIST + 1)
|
||||
strcpy(String, "ALL");
|
||||
else if (gSubMenuSelection == 0)
|
||||
strcpy(String, "OFF");
|
||||
else
|
||||
sprintf(String, "%u", gSubMenuSelection);
|
||||
break;
|
||||
|
||||
case MENU_S_LIST:
|
||||
if (gSubMenuSelection == MR_CHANNELS_LIST + 1)
|
||||
strcpy(String, "ALL");
|
||||
else
|
||||
sprintf(String, "%u", gSubMenuSelection);
|
||||
else if (gSubMenuSelection == 0 && m == MENU_LIST_CH)
|
||||
strcpy(String, "OFF");
|
||||
else {
|
||||
const char *name = gListName[gSubMenuSelection - 1];
|
||||
|
||||
// If first character is empty/invalid, display "N/A"
|
||||
if (IsEmptyName(name, sizeof(gListName[0])))
|
||||
sprintf(String, "%02u", gSubMenuSelection);
|
||||
else
|
||||
sprintf(String, "%02u (%.3s)", gSubMenuSelection, name);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
#ifdef ENABLE_ALARM
|
||||
case MENU_AL_MOD:
|
||||
sprintf(String, gSubMenu_AL_MOD[gSubMenuSelection]);
|
||||
@@ -992,11 +1121,19 @@ void UI_DisplayMenu(void)
|
||||
break;
|
||||
#endif
|
||||
case MENU_UPCODE:
|
||||
sprintf(String, "%.8s\n%.8s", gEeprom.DTMF_UP_CODE, gEeprom.DTMF_UP_CODE + 8);
|
||||
if (gEeprom.DTMF_UP_CODE[8] != '\0' && gEeprom.DTMF_UP_CODE[8] != 0xFF) {
|
||||
sprintf(String, "%.8s\n%.8s", gEeprom.DTMF_UP_CODE, gEeprom.DTMF_UP_CODE + 8);
|
||||
} else {
|
||||
sprintf(String, "%.8s", gEeprom.DTMF_UP_CODE);
|
||||
}
|
||||
break;
|
||||
|
||||
case MENU_DWCODE:
|
||||
sprintf(String, "%.8s\n%.8s", gEeprom.DTMF_DOWN_CODE, gEeprom.DTMF_DOWN_CODE + 8);
|
||||
if (gEeprom.DTMF_DOWN_CODE[8] != '\0' && gEeprom.DTMF_DOWN_CODE[8] != 0xFF) {
|
||||
sprintf(String, "%.8s\n%.8s", gEeprom.DTMF_DOWN_CODE, gEeprom.DTMF_DOWN_CODE + 8);
|
||||
} else {
|
||||
sprintf(String, "%.8s", gEeprom.DTMF_DOWN_CODE);
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
@@ -1038,18 +1175,98 @@ void UI_DisplayMenu(void)
|
||||
strcpy(String, gSubMenu_ROGER[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
case MENU_VOL:
|
||||
case MENU_VOL: {
|
||||
// SysInf is paginated. Pages appear in this order, only when their
|
||||
// feature flag is enabled:
|
||||
// 0 -> identity
|
||||
// next -> Build date/time (ENABLE_FEAT_F4HWN)
|
||||
// next -> Battery (ENABLE_FEAT_F4HWN)
|
||||
// next -> Flash / SRAM usage (ENABLE_FEAT_F4HWN_MEM)
|
||||
// next, +1 -> CODE / WIKI QR codes (ENABLE_FEAT_F4HWN_QRCODE)
|
||||
// In non-F4HWN builds, page 0 keeps the old battery-voltage display.
|
||||
const uint8_t page = (uint8_t)gSubMenuSelection;
|
||||
uint8_t p = 0;
|
||||
|
||||
if (page == p++) {
|
||||
// Page 0: firmware identity.
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
sprintf(String, "%s\n%s",
|
||||
AUTHOR_STRING_2,
|
||||
VERSION_STRING_2
|
||||
);
|
||||
sprintf(String, "%s\n%s", AUTHOR_STRING_2, DISPLAY_VERSION_STRING_2);
|
||||
UI_PrintStringSmallNormal(Edition, menu_item_x1 - 1, menu_item_x2, 6);
|
||||
#else
|
||||
sprintf(String, "%u.%02uV\n%u%%",
|
||||
gBatteryVoltageAverage / 100, gBatteryVoltageAverage % 100,
|
||||
BATTERY_VoltsToPercent(gBatteryVoltageAverage));
|
||||
sprintf(String, "%u.%02uV\n%u%%",
|
||||
gBatteryVoltageAverage / 100, gBatteryVoltageAverage % 100,
|
||||
BATTERY_VoltsToPercent(gBatteryVoltageAverage));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
if (page == p++) {
|
||||
strcpy(top_right_badge, "BUILD");
|
||||
UI_PrintStringSmallNormal(BuildDate, menu_item_x1 - 1, menu_item_x2, 3);
|
||||
UI_PrintStringSmallNormal(BuildTime, menu_item_x1 - 1, menu_item_x2, 4);
|
||||
UI_PrintStringSmallNormal(BuildCommit, menu_item_x1 - 1, menu_item_x2, 6);
|
||||
|
||||
already_printed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (page == p++) {
|
||||
char val[16];
|
||||
|
||||
strcpy(top_right_badge, "BATTERY");
|
||||
|
||||
sprintf(val, "%u.%02uV %u%%",
|
||||
gBatteryVoltageAverage / 100, gBatteryVoltageAverage % 100,
|
||||
BATTERY_VoltsToPercent(gBatteryVoltageAverage));
|
||||
UI_PrintStringSmallNormal(val, menu_item_x1 - 1, menu_item_x2, 3);
|
||||
|
||||
UI_PrintStringSmallNormal(gSubMenu_BATTYP[gEeprom.BATTERY_TYPE], menu_item_x1 - 1, menu_item_x2, 5);
|
||||
|
||||
already_printed = true;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_MEM
|
||||
if (page == p++) {
|
||||
uint16_t flash_pct = 0;
|
||||
uint16_t ram_pct = 0;
|
||||
UI_GetMemPercents(&flash_pct, &ram_pct);
|
||||
|
||||
char val[16];
|
||||
|
||||
// MEMORY title capsule centered in right zone, fb line 1.
|
||||
strcpy(top_right_badge, "MEMORY");
|
||||
|
||||
// Flash + SRAM values stacked below, normal small font, with a fb-line of breathing space.
|
||||
sprintf(val, "FLASH %u.%u%%",
|
||||
(unsigned)(flash_pct / 100), (unsigned)((flash_pct / 10) % 10));
|
||||
UI_PrintStringSmallNormal(val, menu_item_x1 - 1, menu_item_x2, 3);
|
||||
|
||||
sprintf(val, "SRAM %u.%u%%",
|
||||
(unsigned)(ram_pct / 100), (unsigned)((ram_pct / 10) % 10));
|
||||
UI_PrintStringSmallNormal(val, menu_item_x1 - 1, menu_item_x2, 5);
|
||||
|
||||
already_printed = true;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_QRCODE
|
||||
// Right zone: x=49..127 (79 px). QR centered at x=72..104.
|
||||
// Capsule label above QR (small-font Inverse style at fb line 1).
|
||||
if (page == p || page == p + 1) {
|
||||
const bool is_wiki = (page == (p + 1));
|
||||
|
||||
strcpy(top_right_badge, is_wiki ? "WIKI" : "CODE");
|
||||
UI_DrawQRCode(is_wiki, 72, 28);
|
||||
|
||||
already_printed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
p += 2;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
case MENU_RESET:
|
||||
strcpy(String, gSubMenu_RESET[gSubMenuSelection]);
|
||||
@@ -1176,9 +1393,26 @@ void UI_DisplayMenu(void)
|
||||
strcpy(String, gSubMenu_SET_MET[gSubMenuSelection]); // Same as SET_MET
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
case MENU_SET_SCN:
|
||||
strcpy(String, gSubMenu_SET_SCN[gSubMenuSelection]);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
case MENU_SET_AUD:
|
||||
strcpy(String, gSubMenu_SET_AUD[gSubMenuSelection]);
|
||||
if(gTxVfo->Modulation == MODULATION_AM) {
|
||||
strcpy(String, gSubMenu_SET_AUD_AM[gSubMenuSelection]);
|
||||
strcpy(top_right_badge, "AM");
|
||||
}
|
||||
else if (gTxVfo->Modulation == MODULATION_USB) {
|
||||
strcpy(String, "USB");
|
||||
strcpy(top_right_badge, "USB");
|
||||
}
|
||||
else {
|
||||
strcpy(String, gSubMenu_SET_AUD_FM[gSubMenuSelection]);
|
||||
strcpy(top_right_badge, "FM");
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
@@ -1204,12 +1438,8 @@ void UI_DisplayMenu(void)
|
||||
gaugeMax = 63;
|
||||
//#endif
|
||||
}
|
||||
gEeprom.VOLUME_GAIN = gSubMenuSelection;
|
||||
BK4819_WriteRegister(BK4819_REG_48,
|
||||
(11u << 12) | // ??? .. 0 ~ 15, doesn't seem to make any difference
|
||||
( 0u << 10) | // AF Rx Gain-1
|
||||
(gEeprom.VOLUME_GAIN << 4) | // AF Rx Gain-2
|
||||
(gEeprom.DAC_GAIN << 0)); // AF DAC Gain (after Gain-1 and Gain-2)
|
||||
// gEeprom.VOLUME_GAIN = gSubMenuSelection;
|
||||
BK4819_SetRxAudioGain();
|
||||
break;
|
||||
#endif
|
||||
|
||||
@@ -1237,7 +1467,7 @@ void UI_DisplayMenu(void)
|
||||
unsigned int len = strlen(String);
|
||||
bool small = false;
|
||||
|
||||
if (len > 0)
|
||||
if (String[0] != '\0')
|
||||
{
|
||||
// count number of lines
|
||||
for (i = 0; i < len; i++)
|
||||
@@ -1266,23 +1496,6 @@ void UI_DisplayMenu(void)
|
||||
|
||||
y = (small ? 3 : 2) - (lines / 2);
|
||||
|
||||
// only for SysInf
|
||||
if(UI_MENU_GetCurrentMenuId() == MENU_VOL)
|
||||
{
|
||||
sprintf(edit, "%u.%02uV %u%%",
|
||||
gBatteryVoltageAverage / 100, gBatteryVoltageAverage % 100,
|
||||
BATTERY_VoltsToPercent(gBatteryVoltageAverage)
|
||||
);
|
||||
|
||||
UI_PrintStringSmallNormal(edit, 54, 127, 1);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
UI_PrintStringSmallNormal(Edition, 54, 127, 6);
|
||||
#endif
|
||||
|
||||
y = 2;
|
||||
}
|
||||
|
||||
// draw the text lines
|
||||
for (i = 0; i < len && lines > 0; lines--)
|
||||
{
|
||||
@@ -1304,16 +1517,16 @@ void UI_DisplayMenu(void)
|
||||
}
|
||||
}
|
||||
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_S_PRI_CH_1 || UI_MENU_GetCurrentMenuId() == MENU_S_PRI_CH_2)
|
||||
if (m == MENU_S_PRI_CH_1 || m == MENU_S_PRI_CH_2)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
if ((UI_MENU_GetCurrentMenuId() == MENU_R_CTCS || UI_MENU_GetCurrentMenuId() == MENU_R_DCS) && gCssBackgroundScan)
|
||||
if ((m == MENU_R_CTCS || m == MENU_R_DCS) && gCssBackgroundScan)
|
||||
UI_PrintString("SCAN", menu_item_x1, menu_item_x2, 4, 8);
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_D_LIST && gIsDtmfContactValid) {
|
||||
if (m == MENU_D_LIST && gIsDtmfContactValid) {
|
||||
Contact[11] = 0;
|
||||
memcpy(&gDTMF_ID, Contact + 8, 4);
|
||||
sprintf(String, "ID:%4s", gDTMF_ID);
|
||||
@@ -1321,26 +1534,45 @@ void UI_DisplayMenu(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_R_CTCS ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_T_CTCS ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_R_DCS ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_T_DCS
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
|| UI_MENU_GetCurrentMenuId() == MENU_D_LIST
|
||||
#endif
|
||||
) {
|
||||
sprintf(String, "%2d", gSubMenuSelection);
|
||||
UI_PrintStringSmallNormal(String, 105, 0, 0);
|
||||
const bool is_ctcs = (m == MENU_R_CTCS || m == MENU_T_CTCS);
|
||||
const bool is_dcs = (m == MENU_R_DCS || m == MENU_T_DCS);
|
||||
|
||||
if (is_ctcs || is_dcs) {
|
||||
if (gSubMenuSelection == 0) {
|
||||
strcpy(top_right_badge, is_ctcs ? "00/00" : "000/00");
|
||||
} else {
|
||||
const uint8_t approved_index = is_ctcs ?
|
||||
DCS_GetCtcssApprovedIndex(gSubMenuSelection - 1) :
|
||||
DCS_GetDcsApprovedIndex(gSubMenuSelection - 1);
|
||||
|
||||
const uint8_t width = is_ctcs ? 2 : 3;
|
||||
|
||||
if (approved_index != 0xFF) {
|
||||
sprintf(top_right_badge, "%0*u/%02u", width, (unsigned)gSubMenuSelection, (unsigned)approved_index + 1);
|
||||
} else {
|
||||
sprintf(top_right_badge, "%0*u/--", width, (unsigned)gSubMenuSelection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((UI_MENU_GetCurrentMenuId() == MENU_RESET ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_MEM_CH ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_DEL_CH) && gAskForConfirmation)
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
if (m == MENU_D_LIST) {
|
||||
sprintf(top_right_badge, "%03d", gSubMenuSelection);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (top_right_badge[0] != '\0') {
|
||||
UI_MENU_DrawTopRightRoundedBadge(top_right_badge, 1, true, menu_item_x1, menu_item_x2);
|
||||
}
|
||||
|
||||
if ((m == MENU_RESET ||
|
||||
m == MENU_MEM_CH ||
|
||||
m == MENU_MEM_NAME ||
|
||||
m == MENU_DEL_CH) && gAskForConfirmation)
|
||||
{ // display confirmation
|
||||
char *pPrintStr = (gAskForConfirmation == 1) ? "SURE?" : "WAIT!";
|
||||
UI_PrintString(pPrintStr, menu_item_x1, menu_item_x2, 5, 8);
|
||||
}
|
||||
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
}
|
||||
+56
-32
@@ -23,7 +23,7 @@
|
||||
#include "audio.h" // VOICE_ID_t
|
||||
#include "settings.h"
|
||||
|
||||
typedef struct {
|
||||
typedef struct __attribute__((packed)) {
|
||||
const char name[7]; // menu display area only has room for 6 characters
|
||||
uint8_t menu_id;
|
||||
} t_menu_item;
|
||||
@@ -137,6 +137,9 @@ enum
|
||||
MENU_SET_MET,
|
||||
MENU_SET_GUI,
|
||||
MENU_SET_TMR,
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
MENU_SET_SCN,
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
MENU_SET_NFM,
|
||||
#endif
|
||||
@@ -160,68 +163,88 @@ enum
|
||||
MENU_F2SHRT,
|
||||
MENU_F2LONG,
|
||||
MENU_MLONG,
|
||||
MENU_BATTYP
|
||||
MENU_BATTYP,
|
||||
#if defined(ENABLE_FEAT_F4HWN) && defined(ENABLE_FEAT_F4HWN_LOGO_SAV)
|
||||
MENU_SET_SAV
|
||||
#endif
|
||||
};
|
||||
|
||||
extern const uint8_t FIRST_HIDDEN_MENU_ITEM;
|
||||
extern const t_menu_item MenuList[];
|
||||
|
||||
extern const char gSubMenu_TXP[8][6];
|
||||
extern const char gSubMenu_SFT_D[3][4];
|
||||
extern const char gSubMenu_W_N[2][7];
|
||||
extern const char gSubMenu_OFF_ON[2][4];
|
||||
extern const char gSubMenu_NA[4];
|
||||
extern const char gSubMenu_TOT[11][7];
|
||||
extern const char* const gSubMenu_RXMode[4];
|
||||
extern const char* const gSubMenu_TXP[8];
|
||||
extern const char* const gSubMenu_SFT_D[3];
|
||||
extern const char* const gSubMenu_W_N[2];
|
||||
extern const char* const gSubMenu_OFF_ON[2];
|
||||
extern const char* gSubMenu_NA;
|
||||
extern const char* const gSubMenu_TOT[11];
|
||||
extern const char* const gSubMenu_RXMode[4];
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
extern const char gSubMenu_VOICE[3][4];
|
||||
extern const char* const gSubMenu_VOICE[3];
|
||||
#endif
|
||||
extern const char* const gSubMenu_MDF[4];
|
||||
extern const char* const gSubMenu_MDF[4];
|
||||
#ifdef ENABLE_ALARM
|
||||
extern const char gSubMenu_AL_MOD[2][5];
|
||||
extern const char* const gSubMenu_AL_MOD[2];
|
||||
#endif
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
extern const char gSubMenu_D_RSP[4][11];
|
||||
extern const char* const gSubMenu_D_RSP[4];
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
extern const char gSubMenu_SET_PWR[7][6];
|
||||
extern const char gSubMenu_SET_PTT[2][8];
|
||||
extern const char gSubMenu_SET_TOT[4][7];
|
||||
extern const char gSubMenu_SET_LCK[2][9];
|
||||
extern const char gSubMenu_SET_MET[2][8];
|
||||
extern const char* const gSubMenu_SET_PWR[7];
|
||||
extern const char* const gSubMenu_SET_PTT[2];
|
||||
extern const char* const gSubMenu_SET_TOT[4];
|
||||
extern const char* const gSubMenu_SET_LCK[];
|
||||
extern const char* const gSubMenu_SET_MET[2];
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
extern const char* const gSubMenu_SET_SCN[2];
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
extern const char gSubMenu_SET_NFM[2][9];
|
||||
extern const char* const gSubMenu_SET_NFM[2];
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
extern const char gSubMenu_SET_KEY[][9];
|
||||
extern const char* const gSubMenu_SET_KEY[5];
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
extern const char gSubMenu_SET_AUD[5][6];
|
||||
extern const char* const gSubMenu_SET_AUD_FM[5];
|
||||
extern const char* const gSubMenu_SET_AUD_AM[3];
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
extern const char* const gSubMenu_SET_SAV[];
|
||||
#endif
|
||||
#endif
|
||||
|
||||
extern const char* const gSubMenu_PTT_ID[5];
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
extern const char gSubMenu_PONMSG[5][8];
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO
|
||||
extern const char* const gSubMenu_PONMSG[6];
|
||||
#else
|
||||
extern const char* const gSubMenu_PONMSG[5];
|
||||
#endif
|
||||
#else
|
||||
extern const char gSubMenu_PONMSG[4][8];
|
||||
extern const char* const gSubMenu_PONMSG[4];
|
||||
#endif
|
||||
extern const char gSubMenu_ROGER[3][6];
|
||||
extern const char gSubMenu_RESET[2][4];
|
||||
extern const char* const gSubMenu_F_LOCK[F_LOCK_LEN];
|
||||
extern const char gSubMenu_RX_TX[4][6];
|
||||
extern const char gSubMenu_BAT_TXT[3][8];
|
||||
extern const char gSubMenu_BATTYP[5][12];
|
||||
|
||||
extern const char* const gSubMenu_ROGER[3];
|
||||
extern const char* const gSubMenu_RESET[2];
|
||||
extern const char* const gSubMenu_F_LOCK[F_LOCK_LEN];
|
||||
extern const char* const gSubMenu_RX_TX[4];
|
||||
extern const char* const gSubMenu_BAT_TXT[3];
|
||||
extern const char* const gSubMenu_BATTYP[5];
|
||||
extern const char* const gSubMenu_SET_NAV[2];
|
||||
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
extern const char gSubMenu_SCRAMBLER[11][7];
|
||||
extern const char* const gSubMenu_SCRAMBLER[11];
|
||||
#endif
|
||||
|
||||
typedef struct {char* name; uint8_t id;} t_sidefunction;
|
||||
typedef struct /* __attribute__((packed)) */ {
|
||||
const char* name;
|
||||
uint8_t id;
|
||||
} t_sidefunction;
|
||||
|
||||
extern const uint8_t gSubMenu_SIDEFUNCTIONS_size;
|
||||
extern const t_sidefunction gSubMenu_SIDEFUNCTIONS[];
|
||||
extern const t_sidefunction gSubMenu_SIDEFUNCTIONS[];
|
||||
|
||||
extern bool gIsInSubMenu;
|
||||
|
||||
@@ -232,6 +255,7 @@ extern int32_t gSubMenuSelection;
|
||||
extern char edit_original[17];
|
||||
extern char edit[17];
|
||||
extern int edit_index;
|
||||
extern bool edit_is_uppercase;
|
||||
|
||||
void UI_DisplayMenu(void);
|
||||
int UI_MENU_GetCurrentMenuId();
|
||||
|
||||
+34
-36
@@ -26,58 +26,56 @@
|
||||
|
||||
void UI_DisplayScanner(void)
|
||||
{
|
||||
char String[16] = {0};
|
||||
char String[16];
|
||||
char *pPrintStr = String;
|
||||
bool bCentered;
|
||||
uint8_t Start;
|
||||
|
||||
UI_DisplayClear();
|
||||
|
||||
if (gScanSingleFrequency || (gScanCssState != SCAN_CSS_STATE_OFF && gScanCssState != SCAN_CSS_STATE_FAILED)) {
|
||||
sprintf(String, "FREQ:%u.%05u", gScanFrequency / 100000, gScanFrequency % 100000);
|
||||
// 1st line
|
||||
if (gScannerSaveState == SCAN_SAVE_CHANNEL) {
|
||||
pPrintStr = "Save?";
|
||||
} else if (gScannerSaveState == SCAN_SAVE_CHAN_SEL) {
|
||||
strcpy(String, "Save:");
|
||||
UI_GenerateChannelStringEx(String + 5, gShowChPrefix, gScanChannel);
|
||||
pPrintStr = String;
|
||||
} else if ((gScanCssState < SCAN_CSS_STATE_FOUND) && ((gScanProgressIndicator & 1u) != 0)) {
|
||||
pPrintStr = "";
|
||||
} else if (gScanCssState == SCAN_CSS_STATE_OFF) {
|
||||
pPrintStr = "Search Freq";
|
||||
} else if (gScanCssState == SCAN_CSS_STATE_SCANNING) {
|
||||
pPrintStr = "Search Tone";
|
||||
} else if (gScanCssState == SCAN_CSS_STATE_FOUND) {
|
||||
pPrintStr = "Scan Complete";
|
||||
} else {
|
||||
pPrintStr = "FREQ:**.*****";
|
||||
pPrintStr = "Scan Failed";
|
||||
}
|
||||
|
||||
UI_PrintString(pPrintStr, 2, 0, 1, 8);
|
||||
|
||||
if (gScanCssState < SCAN_CSS_STATE_FOUND || !gScanUseCssResult) {
|
||||
pPrintStr = "CTC:******";
|
||||
// 2nd line
|
||||
if (gScanSingleFrequency || (gScanCssState != SCAN_CSS_STATE_OFF && gScanCssState != SCAN_CSS_STATE_FAILED)) {
|
||||
sprintf(String, "Freq:%u.%05u", gScanFrequency / 100000, gScanFrequency % 100000);
|
||||
pPrintStr = String;
|
||||
} else {
|
||||
pPrintStr = "Freq:---.-----";
|
||||
}
|
||||
|
||||
UI_PrintString(pPrintStr, 2, 0, 3, 8);
|
||||
|
||||
// 3rd line
|
||||
if (gScanCssState < SCAN_CSS_STATE_FOUND) {
|
||||
pPrintStr = "Tone:---";
|
||||
} else if (!gScanUseCssResult) {
|
||||
pPrintStr = "Tone:None";
|
||||
} else if (gScanCssResultType == CODE_TYPE_CONTINUOUS_TONE) {
|
||||
sprintf(String, "CTC:%u.%uHz", CTCSS_Options[gScanCssResultCode] / 10, CTCSS_Options[gScanCssResultCode] % 10);
|
||||
sprintf(String, "CTCSS:%u.%uHz", CTCSS_Options[gScanCssResultCode] / 10, CTCSS_Options[gScanCssResultCode] % 10);
|
||||
pPrintStr = String;
|
||||
} else {
|
||||
sprintf(String, "DCS:D%03oN", DCS_Options[gScanCssResultCode]);
|
||||
pPrintStr = String;
|
||||
}
|
||||
|
||||
UI_PrintString(pPrintStr, 2, 0, 3, 8);
|
||||
memset(String, 0, sizeof(String));
|
||||
if (gScannerSaveState == SCAN_SAVE_CHANNEL) {
|
||||
pPrintStr = "SAVE?";
|
||||
Start = 0;
|
||||
bCentered = 1;
|
||||
} else {
|
||||
Start = 2;
|
||||
bCentered = 0;
|
||||
|
||||
if (gScannerSaveState == SCAN_SAVE_CHAN_SEL) {
|
||||
strcpy(String, "SAVE:");
|
||||
UI_GenerateChannelStringEx(String + 5, gShowChPrefix, gScanChannel);
|
||||
pPrintStr = String;
|
||||
} else if (gScanCssState < SCAN_CSS_STATE_FOUND) {
|
||||
strcpy(String, "SCAN");
|
||||
memset(String + 4, '.', (gScanProgressIndicator & 7) + 1);
|
||||
pPrintStr = String;
|
||||
} else if (gScanCssState == SCAN_CSS_STATE_FOUND) {
|
||||
pPrintStr = "SCAN CMP.";
|
||||
} else {
|
||||
pPrintStr = "SCAN FAIL.";
|
||||
}
|
||||
}
|
||||
|
||||
UI_PrintString(pPrintStr, Start, bCentered ? 127 : 0, 5, 8);
|
||||
|
||||
UI_PrintString(pPrintStr, 2, 0, 5, 8);
|
||||
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
+152
-106
@@ -16,7 +16,11 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/chFrScanner.h"
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
#include "app/rxtx_log.h"
|
||||
#endif
|
||||
#ifdef ENABLE_FMRADIO
|
||||
#include "app/fm.h"
|
||||
#endif
|
||||
@@ -43,46 +47,76 @@ static void convertTime(uint8_t *line, uint8_t type)
|
||||
uint8_t m = t / 60;
|
||||
uint8_t s = t - (m * 60); // Replace modulo with subtraction for efficiency
|
||||
|
||||
gStatusLine[0] = gStatusLine[7] = gStatusLine[14] = 0x00; // Quick fix on display (on scanning I, II, etc.)
|
||||
|
||||
char str[6];
|
||||
sprintf(str, "%02u:%02u", m, s);
|
||||
UI_PrintStringSmallBufferNormal(str, line);
|
||||
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void UI_DisplayStatus()
|
||||
{
|
||||
char str[8] = "";
|
||||
|
||||
gUpdateStatus = false;
|
||||
memset(gStatusLine, 0, sizeof(gStatusLine));
|
||||
|
||||
if (APP_IsScreenSaverDisplayed())
|
||||
return;
|
||||
|
||||
UI_StatusClear();
|
||||
|
||||
uint8_t *line = gStatusLine;
|
||||
unsigned int x = 0;
|
||||
unsigned int x1 = 0;
|
||||
|
||||
#ifdef ENABLE_NOAA
|
||||
// NOAA indicator
|
||||
if (!(gScanStateDir != SCAN_OFF || SCANNER_IsScanning()) && gIsNoaaMode) { // NOASS SCAN indicator
|
||||
memcpy(line + x, BITMAP_NOAA, sizeof(BITMAP_NOAA));
|
||||
char str[8] = "";
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
// The filter label reuses the leftmost slot (pixels 2..16) normally
|
||||
// reserved by the power-save and scan indicators; those two are skipped
|
||||
// on the log screen so the rest of the bar keeps its usual layout.
|
||||
const bool isRxTxLogScreen = gScreenToDisplay == DISPLAY_RXTX_LOG;
|
||||
if (isRxTxLogScreen) {
|
||||
const char *filter = RXTX_LOG_GetFilterName();
|
||||
const uint8_t end = (filter[2] == 0) ? 10 : 14;
|
||||
|
||||
GUI_DisplaySmallestInverse(filter, 2, 0, true, true, end);
|
||||
}
|
||||
// Power Save indicator
|
||||
else if (gCurrentFunction == FUNCTION_POWER_SAVE) {
|
||||
memcpy(line + x, gFontPowerSave, sizeof(gFontPowerSave));
|
||||
}
|
||||
x += 8;
|
||||
#else
|
||||
// Power Save indicator
|
||||
if (gCurrentFunction == FUNCTION_POWER_SAVE) {
|
||||
memcpy(line + x, gFontPowerSave, sizeof(gFontPowerSave));
|
||||
}
|
||||
x += 8;
|
||||
#endif
|
||||
|
||||
unsigned int x1 = x;
|
||||
#if defined(ENABLE_FEAT_F4HWN_RX_TX_TIMER) && !defined(ENABLE_FEAT_F4HWN_DEBUG)
|
||||
bool isTransmit = gCurrentFunction == FUNCTION_TRANSMIT;
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
if (!isRxTxLogScreen && gSetting_set_tmr && (isTransmit || FUNCTION_IsRx())) {
|
||||
#else
|
||||
if (gSetting_set_tmr && (isTransmit || FUNCTION_IsRx())) {
|
||||
#endif
|
||||
convertTime(line, !isTransmit);
|
||||
x += 39;
|
||||
} else {
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
if (!isRxTxLogScreen)
|
||||
#endif
|
||||
{
|
||||
#ifdef ENABLE_NOAA
|
||||
// NOAA indicator
|
||||
if (!(gScanStateDir != SCAN_OFF || SCANNER_IsScanning()) && gIsNoaaMode) { // NOASS SCAN indicator
|
||||
memcpy(line + x, BITMAP_NOAA, sizeof(BITMAP_NOAA));
|
||||
}
|
||||
// Power Save indicator
|
||||
else if (gCurrentFunction == FUNCTION_POWER_SAVE) {
|
||||
memcpy(line + x, gFontPowerSave, sizeof(gFontPowerSave));
|
||||
}
|
||||
#else
|
||||
// Power Save indicator
|
||||
if (gCurrentFunction == FUNCTION_POWER_SAVE) {
|
||||
memcpy(line + x, gFontPowerSave, sizeof(gFontPowerSave));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
x += 8;
|
||||
|
||||
x1 = x;
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
if (gSetting_KILLED) {
|
||||
@@ -92,40 +126,50 @@ void UI_DisplayStatus()
|
||||
else
|
||||
#endif
|
||||
{ // SCAN indicator
|
||||
if (gScanStateDir != SCAN_OFF || SCANNER_IsScanning()) {
|
||||
if ((gScanStateDir != SCAN_OFF || SCANNER_IsScanning())
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
&& !isRxTxLogScreen
|
||||
#endif
|
||||
) {
|
||||
if (IS_MR_CHANNEL(gNextMrChannel) && !SCANNER_IsScanning()) { // channel mode
|
||||
|
||||
uint8_t end = 0;
|
||||
|
||||
if(gEeprom.SCAN_LIST_DEFAULT == MR_CHANNELS_LIST + 1)
|
||||
{
|
||||
if(gEeprom.SCAN_LIST_ENABLED==1) {
|
||||
sprintf(str, "%s+", "ALL");
|
||||
end = 19;
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(str, "%s", "ALL");
|
||||
end = 15;
|
||||
}
|
||||
strcpy(str, "ALL");
|
||||
end = 14;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(gEeprom.SCAN_LIST_ENABLED==1) {
|
||||
sprintf(str, "%02d+", gEeprom.SCAN_LIST_DEFAULT);
|
||||
end = 15;
|
||||
}
|
||||
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 = 11;
|
||||
end = 10;
|
||||
}
|
||||
}
|
||||
|
||||
if (gEeprom.SCAN_LIST_ENABLED) {
|
||||
strcat(str, "+");
|
||||
end += 4;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
GUI_DisplaySmallestInverse(str, 2, 0, true, true, end);
|
||||
#else
|
||||
GUI_DisplaySmallest(str, 2, 1, true, true);
|
||||
for (uint8_t x = 0; x < end; x++)
|
||||
|
||||
gStatusLine[0] ^= 0x3E;
|
||||
for (uint8_t x = 1; x < end; x++)
|
||||
{
|
||||
gStatusLine[x] ^= 0x7F;
|
||||
}
|
||||
gStatusLine[end] ^= 0x3E;
|
||||
#endif
|
||||
}
|
||||
else { // frequency mode
|
||||
memcpy(line + x + 1, gFontS, sizeof(gFontS));
|
||||
@@ -155,48 +199,49 @@ void UI_DisplayStatus()
|
||||
#endif
|
||||
|
||||
if(!SCANNER_IsScanning()) {
|
||||
#ifdef ENABLE_FEAT_F4HWN_RX_TX_TIMER
|
||||
if(gCurrentFunction == FUNCTION_TRANSMIT && gSetting_set_tmr == true)
|
||||
{
|
||||
convertTime(line, 0);
|
||||
}
|
||||
else if(FUNCTION_IsRx() && gSetting_set_tmr == true)
|
||||
{
|
||||
convertTime(line, 1);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if(!gAirCopyBootMode) {
|
||||
const void *src = NULL; // Pointer to the font/bitmap to copy
|
||||
size_t sSize = 0; // Size of the font/bitmap
|
||||
uint8_t sOff = 2; // Offset relative to the reference position
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
if(gEeprom.MENU_LOCK == true) {
|
||||
memcpy(line + x + 2, gFontRO, sizeof(gFontRO));
|
||||
}
|
||||
else
|
||||
if (gEeprom.MENU_LOCK) {
|
||||
src = gFontRO;
|
||||
sSize = sizeof(gFontRO);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#endif
|
||||
uint8_t dw = (gEeprom.DUAL_WATCH != DUAL_WATCH_OFF) + (gEeprom.CROSS_BAND_RX_TX != CROSS_BAND_OFF) * 2;
|
||||
if(dw == 1 || dw == 3) { // DWR - dual watch + respond
|
||||
if(gDualWatchActive)
|
||||
memcpy(line + x + (dw==1?0:2), gFontDWR, sizeof(gFontDWR) - (dw==1?0:5));
|
||||
else
|
||||
memcpy(line + x + 3, gFontHold, sizeof(gFontHold));
|
||||
uint8_t xb = (gEeprom.CROSS_BAND_RX_TX != CROSS_BAND_OFF);
|
||||
|
||||
if (gEeprom.DUAL_WATCH != DUAL_WATCH_OFF) {
|
||||
if (gDualWatchActive) { // DWR - dual watch + respond
|
||||
src = gFontDWR;
|
||||
sOff = xb ? 2 : 0;
|
||||
sSize = sizeof(gFontDWR) - (xb ? 5 : 0);
|
||||
} else {
|
||||
src = gFontHold;
|
||||
sOff = 3;
|
||||
sSize = sizeof(gFontHold);
|
||||
}
|
||||
} else {
|
||||
src = xb ? gFontXB : gFontMO; // XB - crossband
|
||||
sSize = xb ? sizeof(gFontXB) : sizeof(gFontMO); // MO - main only
|
||||
}
|
||||
else if(dw == 2) { // XB - crossband
|
||||
memcpy(line + x + 2, gFontXB, sizeof(gFontXB));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!gAirCopyBootMode)
|
||||
memcpy(line + x + 2, gFontMO, sizeof(gFontMO));
|
||||
}
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
}
|
||||
#endif
|
||||
|
||||
// Perform the memcpy if a source was selected
|
||||
if (src) {
|
||||
memcpy(line + x + sOff, src, sSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
x += sizeof(gFontDWR) + 3;
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_FEAT_F4HWN_RX_TX_TIMER) && !defined(ENABLE_FEAT_F4HWN_DEBUG)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_VOX
|
||||
// VOX indicator
|
||||
if (gEeprom.VOX_SWITCH) {
|
||||
@@ -233,15 +278,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) {
|
||||
@@ -250,8 +288,19 @@ void UI_DisplayStatus()
|
||||
}
|
||||
#endif
|
||||
else if (gBackLight) {
|
||||
src = gFontLight;
|
||||
size = sizeof(gFontLight);
|
||||
// Distinguish the manual backlight sub-state: BACKLIGHT_TIME == 0
|
||||
// means the light is currently off (hollow bulb), otherwise it is
|
||||
// forced on (bulb with filament).
|
||||
|
||||
if (gEeprom.BACKLIGHT_TIME == 0) {
|
||||
src = gFontLightOff;
|
||||
size = sizeof(gFontLightOff);
|
||||
}
|
||||
else
|
||||
{
|
||||
src = gFontLight;
|
||||
size = sizeof(gFontLight);
|
||||
}
|
||||
}
|
||||
#ifdef ENABLE_FEAT_F4HWN_CHARGING_C
|
||||
else if (gChargingWithTypeC) {
|
||||
@@ -270,38 +319,35 @@ void UI_DisplayStatus()
|
||||
|
||||
UI_DrawBattery(line + x2, gBatteryDisplayLevel, gLowBatteryBlink);
|
||||
|
||||
bool BatTxt = true;
|
||||
|
||||
switch (gSetting_battery_text) {
|
||||
default:
|
||||
case 0:
|
||||
BatTxt = false;
|
||||
break;
|
||||
|
||||
case 1: // voltage
|
||||
const uint16_t voltage = (gBatteryVoltageAverage <= 999) ? gBatteryVoltageAverage : 999; // limit to 9.99V
|
||||
sprintf(str, "%u.%02u", voltage / 100, voltage % 100);
|
||||
break;
|
||||
|
||||
case 1: // voltage
|
||||
case 2: // percentage
|
||||
//gBatteryVoltageAverage = 999;
|
||||
sprintf(str, "%02u%%", BATTERY_VoltsToPercent(gBatteryVoltageAverage));
|
||||
if (gSetting_battery_text == 1) {
|
||||
const uint16_t voltage = MIN(gBatteryVoltageAverage, 999); // limit to 9.99V
|
||||
sprintf(str, "%u.%02u", voltage / 100, voltage % 100);
|
||||
} else {
|
||||
//gBatteryVoltageAverage = 999;
|
||||
sprintf(str, "%02u%%", BATTERY_VoltsToPercent(gBatteryVoltageAverage));
|
||||
}
|
||||
|
||||
x2 -= (7 * strlen(str));
|
||||
UI_PrintStringSmallBufferNormal(str, line + x2);
|
||||
/*
|
||||
uint8_t shift = (strlen(str) < 5) ? 92 : 88;
|
||||
GUI_DisplaySmallest(str, shift, 1, true, true);
|
||||
|
||||
for (uint8_t i = shift - 2; i < 110; i++) {
|
||||
gStatusLine[i] ^= 0x7F; // invert
|
||||
}
|
||||
*/
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (BatTxt) {
|
||||
x2 -= (7 * strlen(str));
|
||||
UI_PrintStringSmallBufferNormal(str, line + x2);
|
||||
/*
|
||||
uint8_t shift = (strlen(str) < 5) ? 92 : 88;
|
||||
GUI_DisplaySmallest(str, shift, 1, true, true);
|
||||
|
||||
for (uint8_t i = shift - 2; i < 110; i++) {
|
||||
gStatusLine[i] ^= 0x7F; // invert
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// **************
|
||||
|
||||
ST7565_BlitStatusLine();
|
||||
|
||||
+6
-7
@@ -30,8 +30,8 @@
|
||||
#ifdef ENABLE_FMRADIO
|
||||
#include "ui/fmradio.h"
|
||||
#endif
|
||||
#ifdef ENABLE_REGA
|
||||
#include "app/rega.h"
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
#include "app/rxtx_log.h"
|
||||
#endif
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/main.h"
|
||||
@@ -48,7 +48,7 @@ bool gAskToSave;
|
||||
bool gAskToDelete;
|
||||
|
||||
|
||||
void (*UI_DisplayFunctions[])(void) = {
|
||||
void (*const UI_DisplayFunctions[])(void) = {
|
||||
[DISPLAY_MAIN] = &UI_DisplayMain,
|
||||
[DISPLAY_MENU] = &UI_DisplayMenu,
|
||||
[DISPLAY_SCANNER] = &UI_DisplayScanner,
|
||||
@@ -61,8 +61,8 @@ void (*UI_DisplayFunctions[])(void) = {
|
||||
[DISPLAY_AIRCOPY] = &UI_DisplayAircopy,
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_REGA
|
||||
[DISPLAY_REGA] = &UI_DisplayREGA,
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
[DISPLAY_RXTX_LOG] = &UI_DisplayRxTxLog,
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -94,9 +94,8 @@ void GUI_SelectNextDisplay(GUI_DisplayType_t Display)
|
||||
gAskForConfirmation = 0;
|
||||
gAskToSave = false;
|
||||
gAskToDelete = false;
|
||||
gWasFKeyPressed = false;
|
||||
|
||||
gUpdateStatus = true;
|
||||
HideFKeyIcon();
|
||||
}
|
||||
|
||||
gScreenToDisplay = Display;
|
||||
|
||||
+2
-2
@@ -34,8 +34,8 @@ enum GUI_DisplayType_t
|
||||
DISPLAY_AIRCOPY,
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_REGA
|
||||
DISPLAY_REGA,
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
DISPLAY_RXTX_LOG,
|
||||
#endif
|
||||
|
||||
DISPLAY_N_ELEM,
|
||||
|
||||
+253
-87
@@ -15,10 +15,12 @@
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "driver/py25q16.h"
|
||||
#include "driver/st7565.h"
|
||||
#include "external/printf/printf.h"
|
||||
#include "font.h"
|
||||
#include "helper/battery.h"
|
||||
#include "settings.h"
|
||||
#include "misc.h"
|
||||
@@ -28,13 +30,195 @@
|
||||
#include "version.h"
|
||||
#include "bitmaps.h"
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
#include "screenshot.h"
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
#include "k5viewer.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 +233,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,16 +243,27 @@ void UI_DisplayWelcome(void)
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
ST7565_BlitStatusLine();
|
||||
ST7565_BlitFullScreen();
|
||||
|
||||
|
||||
if (gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_NONE || gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_SOUND) {
|
||||
ST7565_FillScreen(0x00);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_NONE || gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_FULL_SCREEN) {
|
||||
ST7565_FillScreen(0xFF);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
memset(WelcomeString0, 0, sizeof(WelcomeString0));
|
||||
memset(WelcomeString1, 0, sizeof(WelcomeString1));
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO
|
||||
else if (gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_LOGO) {
|
||||
UI_LoadLogo();
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
char WelcomeString0[16];
|
||||
char WelcomeString1[16];
|
||||
char WelcomeString2[16];
|
||||
char WelcomeString3[32];
|
||||
|
||||
// 0x0EB0
|
||||
PY25Q16_ReadBuffer(0x00A0C8, WelcomeString0, 16);
|
||||
@@ -123,90 +313,66 @@ void UI_DisplayWelcome(void)
|
||||
UI_PrintString(WelcomeString1, 0, 127, 2, 10);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
UI_PrintStringSmallNormal(Version, 0, 128, 4);
|
||||
const size_t version_width = strlen(DisplayVersion) * (ARRAY_SIZE(gFontSmall[0]) + 1u);
|
||||
const uint8_t version_x = version_width < LCD_WIDTH
|
||||
? (uint8_t)((LCD_WIDTH - version_width + 1u) / 2u)
|
||||
: 0u;
|
||||
const uint8_t capsule_left = version_x > 2u ? (uint8_t)(version_x - 3u) : 0u;
|
||||
const size_t capsule_right_candidate = version_x + version_width + 2u;
|
||||
const uint8_t capsule_right = capsule_right_candidate < LCD_WIDTH
|
||||
? (uint8_t)capsule_right_candidate
|
||||
: (LCD_WIDTH - 1u);
|
||||
|
||||
UI_DrawLineBuffer(gFrameBuffer, 0, 31, 127, 31, 1); // Be ware, status zone = 8 lines, the rest = 56 ->total 64
|
||||
UI_PrintStringSmallNormal(DisplayVersion, version_x, 0, 4);
|
||||
|
||||
for (uint8_t i = 18; i < 110; i++)
|
||||
if (capsule_left > 0u)
|
||||
{
|
||||
gFrameBuffer[4][i] ^= 0xFF;
|
||||
UI_DrawLineBuffer(gFrameBuffer, 0, 35, capsule_left - 1u, 35, 1);
|
||||
}
|
||||
gFrameBuffer[4][capsule_left] ^= 0x7F;
|
||||
for (uint8_t x = capsule_left + 1u; x < capsule_right; x++)
|
||||
{
|
||||
gFrameBuffer[4][x] ^= 0xFF;
|
||||
gFrameBuffer[3][x] ^= 0x80;
|
||||
}
|
||||
gFrameBuffer[4][capsule_right] ^= 0x7F;
|
||||
if (capsule_right < LCD_WIDTH - 1u)
|
||||
{
|
||||
UI_DrawLineBuffer(gFrameBuffer, capsule_right + 1u, 35, LCD_WIDTH - 1u, 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_K5VIEWER
|
||||
K5VIEWER_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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -6,8 +6,12 @@
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
const char Version[] = AUTHOR_STRING_2 " " VERSION_STRING_2;
|
||||
const char Edition[] = EDITION_STRING;
|
||||
const char Version[] = AUTHOR_STRING_2 " " VERSION_STRING_2;
|
||||
const char DisplayVersion[] = AUTHOR_STRING_2 " " DISPLAY_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
|
||||
|
||||
@@ -23,5 +23,9 @@ extern const char UART_Version[];
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
extern const char DisplayVersion[];
|
||||
extern const char Edition[];
|
||||
extern const char BuildDate[];
|
||||
extern const char BuildTime[];
|
||||
extern const char BuildCommit[];
|
||||
#endif
|
||||
@@ -19,6 +19,23 @@ message("Build type: " ${CMAKE_BUILD_TYPE})
|
||||
|
||||
enable_language(C ASM)
|
||||
|
||||
option(DEV "Prepare the rolling Fusion development firmware" OFF)
|
||||
|
||||
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")
|
||||
@@ -56,7 +73,17 @@ else()
|
||||
set(EXE_NAME "firmware")
|
||||
endif()
|
||||
|
||||
if(DEV)
|
||||
if(NOT "${TARGET}" STREQUAL "f4hwn.fusion")
|
||||
message(FATAL_ERROR "DEV is only supported for the Fusion target")
|
||||
endif()
|
||||
set(DISPLAY_VERSION_STRING_2 "${VERSION_STRING_2} Dev")
|
||||
else()
|
||||
set(DISPLAY_VERSION_STRING_2 "${VERSION_STRING_2}")
|
||||
endif()
|
||||
|
||||
message("EXE_NAME = " ${EXE_NAME})
|
||||
message("Development build = " ${DEV})
|
||||
|
||||
add_executable(${EXE_NAME})
|
||||
|
||||
@@ -70,6 +97,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")
|
||||
|
||||
@@ -91,6 +122,17 @@ add_custom_command(
|
||||
COMMENT "Generating .bin file"
|
||||
)
|
||||
|
||||
if(DEV)
|
||||
add_custom_command(
|
||||
TARGET ${EXE_NAME}
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}.bin"
|
||||
"${CMAKE_SOURCE_DIR}/archive/f4hwn.fusion.development.bin"
|
||||
COMMENT "Preparing archive/f4hwn.fusion.development.bin"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Generate .hex file
|
||||
add_custom_command(
|
||||
TARGET ${EXE_NAME}
|
||||
|
||||
+40
-11
@@ -9,6 +9,8 @@
|
||||
"toolchainFile": "${sourceDir}/cmake/gcc-arm-none-eabi.cmake",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"DEV": false,
|
||||
"ENABLE_LTO": false,
|
||||
"ENABLE_FMRADIO": false,
|
||||
"ENABLE_UART": true,
|
||||
"ENABLE_USB": true,
|
||||
@@ -33,7 +35,7 @@
|
||||
"ENABLE_BOOT_BEEPS": false,
|
||||
"ENABLE_SHOW_CHARGE_LEVEL": false,
|
||||
"ENABLE_REVERSE_BAT_SYMBOL": false,
|
||||
"ENABLE_NO_CODE_SCAN_TIMEOUT": true,
|
||||
"ENABLE_NO_CODE_SCAN_TIMEOUT": false,
|
||||
"ENABLE_SQUELCH_MORE_SENSITIVE": true,
|
||||
"ENABLE_FASTER_CHANNEL_SCAN": true,
|
||||
"ENABLE_RSSI_BAR": true,
|
||||
@@ -43,11 +45,10 @@
|
||||
"ENABLE_BYP_RAW_DEMODULATORS": false,
|
||||
"ENABLE_BLMIN_TMP_OFF": false,
|
||||
"ENABLE_SCAN_RANGES": true,
|
||||
"ENABLE_REGA": false,
|
||||
"ENABLE_EXTRA_UART_CMD": false,
|
||||
"ENABLE_FEAT_F4HWN": true,
|
||||
"ENABLE_FEAT_F4HWN_GAME": false,
|
||||
"ENABLE_FEAT_F4HWN_SCREENSHOT": false,
|
||||
"ENABLE_FEAT_F4HWN_K5VIEWER": false,
|
||||
"ENABLE_FEAT_F4HWN_SPECTRUM": true,
|
||||
"ENABLE_FEAT_F4HWN_RX_TX_TIMER": true,
|
||||
"ENABLE_FEAT_F4HWN_CHARGING_C": false,
|
||||
@@ -59,16 +60,30 @@
|
||||
"ENABLE_FEAT_F4HWN_RESCUE_OPS": false,
|
||||
"ENABLE_FEAT_F4HWN_VOL": false,
|
||||
"ENABLE_FEAT_F4HWN_AUDIO": false,
|
||||
"ENABLE_FEAT_F4HWN_AUDIO_SCOPE": false,
|
||||
"ENABLE_FEAT_F4HWN_RESET_VFO": true,
|
||||
"ENABLE_FEAT_F4HWN_PMR": false,
|
||||
"ENABLE_FEAT_F4HWN_GMRS_FRS_MURS": false,
|
||||
"ENABLE_FEAT_F4HWN_CA": true,
|
||||
"ENABLE_FEAT_F4HWN_DEBUG": false,
|
||||
"ENABLE_FEAT_F4HWN_QRCODE": false,
|
||||
"ENABLE_FEAT_F4HWN_MEM": false,
|
||||
"ENABLE_FEAT_F4HWN_SCAN_PROGRESS": false,
|
||||
"ENABLE_FEAT_F4HWN_SCAN_FASTER": false,
|
||||
"ENABLE_FEAT_F4HWN_SCAN_RSSI": false,
|
||||
"ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE": false,
|
||||
"ENABLE_FEAT_F4HWN_BEAM": false,
|
||||
"ENABLE_FEAT_F4HWN_FOXHUNT": false,
|
||||
"ENABLE_FEAT_F4HWN_RXTX_LOG": false,
|
||||
"ENABLE_FEAT_F4HWN_RXTX_LOG_WRAP": false,
|
||||
"ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER": false,
|
||||
"ENABLE_FEAT_F4HWN_LOGO": false,
|
||||
"ENABLE_FEAT_F4HWN_LOGO_SAV": false,
|
||||
"ENABLE_AGC_SHOW_DATA": false,
|
||||
"ENABLE_UART_RW_BK_REGS": false,
|
||||
"ENABLE_SWD": false,
|
||||
"VERSION_STRING_1": "v0.22",
|
||||
"VERSION_STRING_2": "v5.0.1"
|
||||
"VERSION_STRING_2": "v5.8.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -87,7 +102,7 @@
|
||||
"ENABLE_FMRADIO": false,
|
||||
"ENABLE_VOX": false,
|
||||
"ENABLE_AIRCOPY": true,
|
||||
"ENABLE_FEAT_F4HWN_SCREENSHOT": true,
|
||||
"ENABLE_FEAT_F4HWN_K5VIEWER": true,
|
||||
"ENABLE_FEAT_F4HWN_GAME": false,
|
||||
"ENABLE_FEAT_F4HWN_PMR": true,
|
||||
"ENABLE_FEAT_F4HWN_GMRS_FRS_MURS": true,
|
||||
@@ -104,7 +119,7 @@
|
||||
"ENABLE_FMRADIO": true,
|
||||
"ENABLE_VOX": true,
|
||||
"ENABLE_AIRCOPY": true,
|
||||
"ENABLE_FEAT_F4HWN_SCREENSHOT": true,
|
||||
"ENABLE_FEAT_F4HWN_K5VIEWER": true,
|
||||
"ENABLE_FEAT_F4HWN_GAME": false,
|
||||
"ENABLE_FEAT_F4HWN_PMR": true,
|
||||
"ENABLE_FEAT_F4HWN_GMRS_FRS_MURS": true,
|
||||
@@ -121,7 +136,7 @@
|
||||
"ENABLE_FMRADIO": true,
|
||||
"ENABLE_VOX": false,
|
||||
"ENABLE_AIRCOPY": false,
|
||||
"ENABLE_FEAT_F4HWN_SCREENSHOT": false,
|
||||
"ENABLE_FEAT_F4HWN_K5VIEWER": false,
|
||||
"ENABLE_FEAT_F4HWN_GAME": false,
|
||||
"ENABLE_FEAT_F4HWN_SPECTRUM": false,
|
||||
"ENABLE_FEAT_F4HWN_PMR": true,
|
||||
@@ -145,7 +160,7 @@
|
||||
"ENABLE_FMRADIO": false,
|
||||
"ENABLE_VOX": true,
|
||||
"ENABLE_AIRCOPY": true,
|
||||
"ENABLE_FEAT_F4HWN_SCREENSHOT": true,
|
||||
"ENABLE_FEAT_F4HWN_K5VIEWER": true,
|
||||
"ENABLE_FEAT_F4HWN_GAME": false,
|
||||
"ENABLE_FEAT_F4HWN_PMR": true,
|
||||
"ENABLE_FEAT_F4HWN_GMRS_FRS_MURS": true,
|
||||
@@ -163,7 +178,7 @@
|
||||
"ENABLE_FMRADIO": true,
|
||||
"ENABLE_VOX": false,
|
||||
"ENABLE_AIRCOPY": true,
|
||||
"ENABLE_FEAT_F4HWN_SCREENSHOT": true,
|
||||
"ENABLE_FEAT_F4HWN_K5VIEWER": true,
|
||||
"ENABLE_FEAT_F4HWN_GAME": true,
|
||||
"ENABLE_FEAT_F4HWN_PMR": true,
|
||||
"ENABLE_FEAT_F4HWN_GMRS_FRS_MURS": true,
|
||||
@@ -180,13 +195,27 @@
|
||||
"ENABLE_FMRADIO": true,
|
||||
"ENABLE_VOX": true,
|
||||
"ENABLE_AIRCOPY": true,
|
||||
"ENABLE_FEAT_F4HWN_SCREENSHOT": true,
|
||||
"ENABLE_FEAT_F4HWN_K5VIEWER": true,
|
||||
"ENABLE_FEAT_F4HWN_GAME": true,
|
||||
"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_SCAN_RSSI": true,
|
||||
"ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE": true,
|
||||
"ENABLE_FEAT_F4HWN_BEAM": true,
|
||||
"ENABLE_FEAT_F4HWN_FOXHUNT": true,
|
||||
"ENABLE_FEAT_F4HWN_RXTX_LOG": true,
|
||||
"ENABLE_FEAT_F4HWN_RXTX_LOG_WRAP": false,
|
||||
"ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER": 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 +252,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/*
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
F4HWN Edition Fusion firmware for Quansheng UV-K1 and UV-K5 V3
|
||||
================================================================
|
||||
|
||||
This product includes software originally developed by Dual Tachyon
|
||||
for the Quansheng UV-K5 open-source firmware project:
|
||||
|
||||
https://github.com/DualTachyon/uv-k5-firmware
|
||||
|
||||
This product includes work derived from Egzumer's UV-K5 custom
|
||||
firmware:
|
||||
|
||||
https://github.com/egzumer/uv-k5-firmware-custom
|
||||
|
||||
This product includes work developed by Armel / F4HWN and contributors
|
||||
for the F4HWN firmware projects, including the F4HWN Edition Fusion
|
||||
port for UV-K1 and UV-K5 V3 radios:
|
||||
|
||||
https://github.com/armel/uv-k5-firmware-custom
|
||||
https://github.com/armel/uv-k1-k5v3-firmware-custom
|
||||
|
||||
The UV-K1 and UV-K5 V3 port is the result of joint work by muzkr and
|
||||
Armel / F4HWN.
|
||||
|
||||
Additional attribution notices
|
||||
------------------------------
|
||||
|
||||
This product includes contributions and/or derived work from, among
|
||||
others:
|
||||
|
||||
Dual Tachyon
|
||||
OneOfEleven
|
||||
fagci
|
||||
Egzumer
|
||||
muzkr
|
||||
Armel / F4HWN
|
||||
|
||||
Project-specific derived components
|
||||
-----------------------------------
|
||||
|
||||
The spectrum analyzer implementation includes work originally developed
|
||||
by fagci:
|
||||
|
||||
https://github.com/fagci
|
||||
|
||||
Third-party components
|
||||
----------------------
|
||||
|
||||
This product includes CherryUSB by sakumisu, licensed under the
|
||||
Apache License, Version 2.0. Its license is included at:
|
||||
|
||||
Middlewares/CherryUSB/LICENSE
|
||||
|
||||
This product includes CMSIS Version 5 by Arm, licensed under the
|
||||
Apache License, Version 2.0. Its license is included at:
|
||||
|
||||
App/external/CMSIS_5/LICENSE.txt
|
||||
|
||||
This product includes the embedded printf implementation by Marco
|
||||
Paland, licensed under the MIT License. Its license is included at:
|
||||
|
||||
App/external/printf/LICENSE
|
||||
|
||||
License and endorsement notice
|
||||
------------------------------
|
||||
|
||||
This NOTICE file is provided for attribution purposes only and does not
|
||||
modify the terms of any applicable license.
|
||||
|
||||
The main firmware is licensed under the Apache License, Version 2.0.
|
||||
Redistributions of this product or derivative works, in source or object
|
||||
form, must comply with the Apache License, Version 2.0, including its
|
||||
conditions on license copies, modified-file notices, retained copyright
|
||||
and attribution notices, and NOTICE files.
|
||||
|
||||
Attribution in this file does not imply endorsement by Dual Tachyon,
|
||||
OneOfEleven, fagci, Egzumer, muzkr, Armel / F4HWN or any other
|
||||
contributor.
|
||||
Loaded 100 of 133 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user