mirror of
https://github.com/armel/uv-k1-k5v3-firmware-custom.git
synced 2026-10-04 12:07:24 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
435192e3bf | ||
|
|
9c86a7c349 | ||
|
|
0a61a6de12 | ||
|
|
1021ee5e18 | ||
|
|
6692b5191b | ||
|
|
c65db46bad | ||
|
|
a38f611bd5 | ||
|
|
528678a31b | ||
|
|
abc146a1b6 | ||
|
|
c7ff3eeb66 | ||
|
|
a3c72bdd93 | ||
|
|
e4408fcb55 | ||
|
|
5715cd27ef | ||
|
|
253104bb87 | ||
|
|
debf67a027 | ||
|
|
535ee80d2b | ||
|
|
559460739e | ||
|
|
0d69388e6d | ||
|
|
9bf075ea22 | ||
|
|
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 | ||
|
|
f4acafb541 | ||
|
|
5ea2f91d73 | ||
|
|
05ea1efe3d | ||
|
|
095c6f9bca | ||
|
|
45612e3e82 | ||
|
|
c13fb3a948 | ||
|
|
9bbb5b4cde | ||
|
|
608a992bca | ||
|
|
74de508a28 | ||
|
|
6b94ae4ed3 | ||
|
|
b9ce4934d8 | ||
|
|
70f41f2e6f | ||
|
|
e2b56d78b2 | ||
|
|
5caff3af5a | ||
|
|
dccdd37464 | ||
|
|
179a0ea8b8 | ||
|
|
b774f9d2ec | ||
|
|
e110448c68 | ||
|
|
3775232757 | ||
|
|
a4b8a236b0 | ||
|
|
9bc4efd0b7 | ||
|
|
815e5b1a82 | ||
|
|
212ef1a90e | ||
|
|
2f01dd0118 | ||
|
|
fe132a380b | ||
|
|
2438de2864 | ||
|
|
c2ee08d486 | ||
|
|
732acbcea1 | ||
|
|
992be7f0d1 | ||
|
|
efafbd868c | ||
|
|
b95d04ac65 | ||
|
|
6eae8f3448 | ||
|
|
06bc8b5711 | ||
|
|
e5ca2265e4 | ||
|
|
1e33ff0f1b | ||
|
|
8bb2915446 | ||
|
|
4658e8348d | ||
|
|
7250e81fb6 | ||
|
|
f17d9ab52f | ||
|
|
da6e5062a9 | ||
|
|
d9a1e0ae30 | ||
|
|
b42ea9d2db | ||
|
|
eee9fc0c1b | ||
|
|
bcc593a61f | ||
|
|
efa9da1c87 | ||
|
|
c2a855f6e9 | ||
|
|
e47ea8d9aa | ||
|
|
cdb8f86a9f | ||
|
|
17ae4dfccf | ||
|
|
691c2f8b35 | ||
|
|
a66f9c52e6 | ||
|
|
edfe11b7bd | ||
|
|
fadf71a893 | ||
|
|
3dad553090 | ||
|
|
0a3a5cfacd | ||
|
|
a88e385d8b | ||
|
|
c8729313e7 | ||
|
|
dad442a4b0 | ||
|
|
d8ac0d44a2 | ||
|
|
d22972b053 | ||
|
|
c89121ff0e | ||
|
|
386b268c81 | ||
|
|
a72433135f | ||
|
|
345c566b54 | ||
|
|
e036b35308 | ||
|
|
b7abcad04c | ||
|
|
5021b25dd5 | ||
|
|
20389310af | ||
|
|
f29b880f78 | ||
|
|
e0ac4de1ef | ||
|
|
9ad95af90e | ||
|
|
64fefa92c6 | ||
|
|
54ba31c4cb | ||
|
|
c9ee29438b | ||
|
|
89a6ad1428 | ||
|
|
5a4a89ae31 | ||
|
|
3dbe098ad9 | ||
|
|
78aeedc251 | ||
|
|
62b625475b | ||
|
|
9a28e489a1 | ||
|
|
8b4a2a5954 | ||
|
|
8af5a13979 | ||
|
|
654f313e2e | ||
|
|
ef9285f344 | ||
|
|
82e0b5c537 | ||
|
|
5c259cfcad | ||
|
|
44cf3de90a | ||
|
|
7d9ece1e18 | ||
|
|
872372a8aa | ||
|
|
50e95b1531 | ||
|
|
200882d322 | ||
|
|
40093dd705 | ||
|
|
643b205490 | ||
|
|
5110d59a9f | ||
|
|
09f893b126 | ||
|
|
4d471dc33d | ||
|
|
4516486867 | ||
|
|
15d9731611 | ||
|
|
6cb9535ca2 | ||
|
|
b8eca8810f | ||
|
|
227242567d | ||
|
|
e534610d86 | ||
|
|
f3e1a37701 | ||
|
|
75a6e20026 |
No files matched your search
+6
-1
@@ -7,8 +7,13 @@ firmware
|
||||
.cache
|
||||
compile_commands.json
|
||||
.vscode
|
||||
.claude
|
||||
.agents
|
||||
/docs
|
||||
k5_eeprom.raw
|
||||
|
||||
/build
|
||||
/serialtool/__pycache__
|
||||
__pycache__/
|
||||
|
||||
.DS_Store
|
||||
AGENTS.md
|
||||
+50
-13
@@ -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)
|
||||
@@ -157,26 +159,19 @@ enable_feature(ENABLE_BOOT_BEEPS)
|
||||
enable_feature(ENABLE_SHOW_CHARGE_LEVEL)
|
||||
enable_feature(ENABLE_REVERSE_BAT_SYMBOL)
|
||||
enable_feature(ENABLE_NO_CODE_SCAN_TIMEOUT)
|
||||
enable_feature(ENABLE_AM_FIX
|
||||
am_fix.c
|
||||
)
|
||||
enable_feature(ENABLE_SQUELCH_MORE_SENSITIVE)
|
||||
enable_feature(ENABLE_FASTER_CHANNEL_SCAN)
|
||||
enable_feature(ENABLE_RSSI_BAR)
|
||||
enable_feature(ENABLE_AUDIO_BAR)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_AUDIO_SCOPE)
|
||||
enable_feature(ENABLE_COPY_CHAN_TO_VFO)
|
||||
enable_feature(ENABLE_REDUCE_LOW_MID_TX_POWER)
|
||||
enable_feature(ENABLE_BYP_RAW_DEMODULATORS)
|
||||
enable_feature(ENABLE_BLMIN_TMP_OFF)
|
||||
enable_feature(ENABLE_SCAN_RANGES)
|
||||
enable_feature(ENABLE_NAVIG_LEFT_RIGHT)
|
||||
|
||||
# ---- CONTRIB MODS ----
|
||||
|
||||
# Thank you @markusb
|
||||
enable_feature(ENABLE_REGA
|
||||
app/rega.c
|
||||
)
|
||||
# Thank you @reppad
|
||||
enable_feature(ENABLE_EXTRA_UART_CMD)
|
||||
|
||||
@@ -186,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)
|
||||
@@ -197,17 +192,59 @@ 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_RESET_CHANNEL)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_AUDIO)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_RESET_VFO)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_PMR)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_GMRS_FRS_MURS)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_CA)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_DEBUG)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_MEM)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_BEAM
|
||||
app/beam.c
|
||||
)
|
||||
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_ACTION_PICKER)
|
||||
|
||||
if(ENABLE_FEAT_F4HWN_ACTION_PICKER AND NOT ENABLE_FEAT_F4HWN)
|
||||
message(FATAL_ERROR "ENABLE_FEAT_F4HWN_ACTION_PICKER requires ENABLE_FEAT_F4HWN.")
|
||||
endif()
|
||||
|
||||
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)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_MENU_CAT)
|
||||
|
||||
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()
|
||||
|
||||
if(ENABLE_FEAT_F4HWN_MENU_CAT AND NOT ENABLE_FEAT_F4HWN)
|
||||
message(FATAL_ERROR "ENABLE_FEAT_F4HWN_MENU_CAT requires ENABLE_FEAT_F4HWN.")
|
||||
endif()
|
||||
|
||||
# ---- DEBUGGING ----
|
||||
|
||||
enable_feature(ENABLE_AM_FIX_SHOW_DATA)
|
||||
enable_feature(ENABLE_AGC_SHOW_DATA)
|
||||
enable_feature(ENABLE_UART_RW_BK_REGS)
|
||||
|
||||
|
||||
+258
-134
@@ -40,9 +40,19 @@
|
||||
#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
|
||||
#ifdef ENABLE_FEAT_F4HWN_ACTION_PICKER
|
||||
#include "ui/menu.h"
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_FMRADIO)
|
||||
@@ -57,7 +67,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,
|
||||
@@ -114,6 +124,12 @@ void (*action_opt_table[])(void) = {
|
||||
//#else
|
||||
// [ACTION_OPT_MUTE] = &FUNCTION_NOP,
|
||||
//#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
[ACTION_OPT_RXA] = &ACTION_RxA,
|
||||
#else
|
||||
[ACTION_OPT_RXA] = &FUNCTION_NOP,
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
[ACTION_OPT_POWER_HIGH] = &ACTION_Power_High,
|
||||
[ACTION_OPT_REMOVE_OFFSET] = &ACTION_Remove_Offset,
|
||||
@@ -121,9 +137,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
|
||||
};
|
||||
|
||||
@@ -206,7 +227,7 @@ void ACTION_Scan(bool bRestart)
|
||||
DTMF_clear_RX();
|
||||
#endif
|
||||
gDTMF_RX_live_timeout = 0;
|
||||
memset(gDTMF_RX_live, 0, sizeof(gDTMF_RX_live));
|
||||
DTMF_clear_input_box_memory();
|
||||
|
||||
RADIO_SelectVfos();
|
||||
|
||||
@@ -230,15 +251,15 @@ void ACTION_Scan(bool bRestart)
|
||||
}
|
||||
|
||||
// channel mode. Keep scanning but toggle between scan lists
|
||||
gEeprom.SCAN_LIST_DEFAULT = (gEeprom.SCAN_LIST_DEFAULT + 1) % 6;
|
||||
RADIO_NextValidList(1);
|
||||
UI_MAIN_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
|
||||
@@ -281,8 +302,124 @@ void ACTION_SwitchDemodul(void)
|
||||
}
|
||||
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
inline static bool ACTION_IsBlockedInFM(uint8_t action)
|
||||
{
|
||||
switch (action) {
|
||||
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
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_ACTION_PICKER
|
||||
static void ACTION_Execute(uint8_t action)
|
||||
{
|
||||
if (action >= ACTION_OPT_LEN || action_opt_table[action] == NULL) {
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
if (gFmRadioMode && ACTION_IsBlockedInFM(action)) {
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
action_opt_table[action]();
|
||||
}
|
||||
|
||||
uint8_t gActionPickerKey;
|
||||
uint8_t gActionPickerSelection[2] = {1, 1};
|
||||
uint8_t gActionPickerTimeout_500ms;
|
||||
|
||||
bool ACTION_PickerProcessKey(KEY_Code_t key, bool isPressed, bool isHeld)
|
||||
{
|
||||
if (gActionPickerKey == 0)
|
||||
return false;
|
||||
if (isPressed)
|
||||
gActionPickerTimeout_500ms = ACTION_PICKER_TIMEOUT_500MS;
|
||||
uint8_t *selection = &gActionPickerSelection[gActionPickerKey - 1];
|
||||
|
||||
switch (key) {
|
||||
case KEY_UP:
|
||||
case KEY_DOWN:
|
||||
if (isPressed && !isHeld) {
|
||||
if (key == KEY_UP) {
|
||||
if (--*selection == 0)
|
||||
*selection = gSubMenu_SIDEFUNCTIONS_size - 1;
|
||||
}
|
||||
else if (++*selection >= gSubMenu_SIDEFUNCTIONS_size) {
|
||||
*selection = 1;
|
||||
}
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
gUpdateDisplay = true;
|
||||
}
|
||||
return true;
|
||||
|
||||
case KEY_MENU:
|
||||
if (!isPressed && !isHeld) {
|
||||
const uint8_t action = gSubMenu_SIDEFUNCTIONS[*selection].id;
|
||||
gActionPickerKey = 0;
|
||||
gUpdateDisplay = true;
|
||||
ACTION_Execute(action);
|
||||
}
|
||||
return true;
|
||||
|
||||
case KEY_EXIT:
|
||||
case KEY_F:
|
||||
if (!isPressed) {
|
||||
gActionPickerKey = 0;
|
||||
gUpdateDisplay = true;
|
||||
}
|
||||
return true;
|
||||
|
||||
case KEY_PTT:
|
||||
gActionPickerKey = 0;
|
||||
gUpdateDisplay = true;
|
||||
return false;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void ACTION_Handle(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
HideFKeyIcon();
|
||||
|
||||
if (gScreenToDisplay == DISPLAY_MAIN && gDTMF_InputMode){
|
||||
// entering DTMF code
|
||||
|
||||
@@ -312,45 +449,48 @@ 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
|
||||
|
||||
if(!(bKeyHeld && !bKeyPressed)) // don't beep on released after hold
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
if (bKeyHeld || bKeyPressed) // held
|
||||
{
|
||||
funcShort = funcLong;
|
||||
|
||||
if (!bKeyPressed) //ignore release if held
|
||||
return;
|
||||
// held or released after short press
|
||||
#ifdef ENABLE_FEAT_F4HWN_ACTION_PICKER
|
||||
ACTION_Execute(func);
|
||||
#else
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
if (gFmRadioMode && ACTION_IsBlockedInFM(func)) {
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// held or released after short press beyond this point
|
||||
|
||||
action_opt_table[funcShort]();
|
||||
action_opt_table[func]();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -374,6 +514,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);
|
||||
|
||||
@@ -430,7 +576,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)
|
||||
@@ -496,17 +642,13 @@ void ACTION_RxMode(void)
|
||||
{
|
||||
static bool cycle = 0;
|
||||
|
||||
switch(cycle) {
|
||||
case 0:
|
||||
gEeprom.DUAL_WATCH = !gEeprom.DUAL_WATCH;
|
||||
cycle = 1;
|
||||
break;
|
||||
case 1:
|
||||
gEeprom.CROSS_BAND_RX_TX = !gEeprom.CROSS_BAND_RX_TX;
|
||||
cycle = 0;
|
||||
break;
|
||||
if (cycle) {
|
||||
gEeprom.CROSS_BAND_RX_TX = !gEeprom.CROSS_BAND_RX_TX;
|
||||
} else {
|
||||
gEeprom.DUAL_WATCH = !gEeprom.DUAL_WATCH;
|
||||
}
|
||||
|
||||
cycle = !cycle;
|
||||
ACTION_Update();
|
||||
}
|
||||
|
||||
@@ -516,81 +658,66 @@ void ACTION_MainOnly(void)
|
||||
static uint8_t dw = 0;
|
||||
static uint8_t cb = 0;
|
||||
|
||||
switch(cycle) {
|
||||
case 0:
|
||||
dw = gEeprom.DUAL_WATCH;
|
||||
cb = gEeprom.CROSS_BAND_RX_TX;
|
||||
if (cycle) {
|
||||
gEeprom.DUAL_WATCH = dw;
|
||||
gEeprom.CROSS_BAND_RX_TX = cb;
|
||||
} else {
|
||||
dw = gEeprom.DUAL_WATCH;
|
||||
cb = gEeprom.CROSS_BAND_RX_TX;
|
||||
|
||||
gEeprom.DUAL_WATCH = 0;
|
||||
gEeprom.CROSS_BAND_RX_TX = 0;
|
||||
cycle = 1;
|
||||
break;
|
||||
case 1:
|
||||
gEeprom.DUAL_WATCH = dw;
|
||||
gEeprom.CROSS_BAND_RX_TX = cb;
|
||||
cycle = 0;
|
||||
break;
|
||||
gEeprom.DUAL_WATCH = 0;
|
||||
gEeprom.CROSS_BAND_RX_TX = 0;
|
||||
}
|
||||
|
||||
cycle = !cycle;
|
||||
ACTION_Update();
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
void ACTION_RxA(void)
|
||||
{
|
||||
if(gRxVfo->Modulation == MODULATION_AM)
|
||||
gSetting_set_audio_am = (gSetting_set_audio_am + 1) % 3;
|
||||
else if (gRxVfo->Modulation == MODULATION_FM)
|
||||
gSetting_set_audio_fm = (gSetting_set_audio_fm + 1) % 5;
|
||||
|
||||
RADIO_SetModulation(gRxVfo->Modulation);
|
||||
}
|
||||
#endif
|
||||
|
||||
void ACTION_Ptt(void)
|
||||
{
|
||||
gSetting_set_ptt_session = !gSetting_set_ptt_session;
|
||||
|
||||
ACTION_Update();
|
||||
}
|
||||
|
||||
void ACTION_Wn(void)
|
||||
{
|
||||
const bool isRx = FUNCTION_IsRx();
|
||||
VFO_Info_t *pVfo = isRx ? gRxVfo : gTxVfo;
|
||||
|
||||
pVfo->CHANNEL_BANDWIDTH = !pVfo->CHANNEL_BANDWIDTH;
|
||||
|
||||
if (pVfo->Modulation == MODULATION_AM)
|
||||
{
|
||||
BK4819_SetFilterBandwidth(RADIO_GetAMFilterBandwidth(pVfo), true);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t bw = pVfo->CHANNEL_BANDWIDTH;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
bool narrower = 0;
|
||||
if (FUNCTION_IsRx())
|
||||
if (isRx && bw == BANDWIDTH_NARROW && gSetting_set_nfm == 1)
|
||||
{
|
||||
gRxVfo->CHANNEL_BANDWIDTH = (gRxVfo->CHANNEL_BANDWIDTH == 0) ? 1: 0;
|
||||
if(gRxVfo->CHANNEL_BANDWIDTH == BANDWIDTH_NARROW && gSetting_set_nfm == 1)
|
||||
{
|
||||
narrower = 1;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_AM_FIX
|
||||
BK4819_SetFilterBandwidth(gRxVfo->CHANNEL_BANDWIDTH + narrower, true);
|
||||
#else
|
||||
BK4819_SetFilterBandwidth(gRxVfo->CHANNEL_BANDWIDTH + narrower, false);
|
||||
#endif
|
||||
bw++;
|
||||
}
|
||||
else
|
||||
{
|
||||
gTxVfo->CHANNEL_BANDWIDTH = (gTxVfo->CHANNEL_BANDWIDTH == 0) ? 1: 0;
|
||||
if(gTxVfo->CHANNEL_BANDWIDTH == BANDWIDTH_NARROW && gSetting_set_nfm == 1)
|
||||
{
|
||||
narrower = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_AM_FIX
|
||||
BK4819_SetFilterBandwidth(gTxVfo->CHANNEL_BANDWIDTH, true);
|
||||
#else
|
||||
BK4819_SetFilterBandwidth(gTxVfo->CHANNEL_BANDWIDTH, false);
|
||||
#endif
|
||||
}
|
||||
#ifdef ENABLE_AM_FIX
|
||||
BK4819_SetFilterBandwidth(bw, true);
|
||||
#else
|
||||
if (FUNCTION_IsRx())
|
||||
{
|
||||
gRxVfo->CHANNEL_BANDWIDTH = (gRxVfo->CHANNEL_BANDWIDTH == 0) ? 1: 0;
|
||||
#ifdef ENABLE_AM_FIX
|
||||
BK4819_SetFilterBandwidth(gRxVfo->CHANNEL_BANDWIDTH, true);
|
||||
#else
|
||||
BK4819_SetFilterBandwidth(gRxVfo->CHANNEL_BANDWIDTH, false);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
gTxVfo->CHANNEL_BANDWIDTH = (gTxVfo->CHANNEL_BANDWIDTH == 0) ? 1: 0;
|
||||
#ifdef ENABLE_AM_FIX
|
||||
BK4819_SetFilterBandwidth(gTxVfo->CHANNEL_BANDWIDTH, true);
|
||||
#else
|
||||
BK4819_SetFilterBandwidth(gTxVfo->CHANNEL_BANDWIDTH, false);
|
||||
#endif
|
||||
}
|
||||
BK4819_SetFilterBandwidth(bw, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -627,38 +754,35 @@ void ACTION_BackLightOnDemand(void)
|
||||
BACKLIGHT_TurnOn();
|
||||
}
|
||||
|
||||
//#if !defined(ENABLE_SPECTRUM) || !defined(ENABLE_FMRADIO)
|
||||
void ACTION_Mute(void)
|
||||
{
|
||||
// Toggle mute state
|
||||
gMute = !gMute;
|
||||
void ACTION_Mute(void)
|
||||
{
|
||||
// Toggle mute state
|
||||
gMute = !gMute;
|
||||
|
||||
// Update the registers
|
||||
#ifdef ENABLE_FMRADIO
|
||||
BK1080_WriteRegister(BK1080_REG_05_SYSTEM_CONFIGURATION2, gMute ? 0x0A10 : 0x0A1F);
|
||||
#endif
|
||||
gEeprom.VOLUME_GAIN = gMute ? 0 : gEeprom.VOLUME_GAIN_BACKUP;
|
||||
BK4819_WriteRegister(BK4819_REG_48,
|
||||
(11u << 12) | // ??? .. 0 ~ 15, doesn't seem to make any difference
|
||||
(0u << 10) | // AF Rx Gain-1
|
||||
(gEeprom.VOLUME_GAIN << 4) | // AF Rx Gain-2
|
||||
(gEeprom.DAC_GAIN << 0)); // AF DAC Gain (after Gain-1 and Gain-2)
|
||||
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
//#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
void ACTION_Power_High(void)
|
||||
{
|
||||
gPowerHigh = !gPowerHigh;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
}
|
||||
|
||||
void ACTION_Remove_Offset(void)
|
||||
{
|
||||
gRemoveOffset = !gRemoveOffset;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
}
|
||||
// Update the registers
|
||||
#ifdef ENABLE_FMRADIO
|
||||
BK1080_WriteRegister(BK1080_REG_05_SYSTEM_CONFIGURATION2, gMute ? 0x0A10 : 0x0A1F);
|
||||
#endif
|
||||
#endif
|
||||
gEeprom.VOLUME_GAIN = gMute ? 0 : gEeprom.VOLUME_GAIN_BACKUP;
|
||||
BK4819_SetRxAudioGain();
|
||||
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
void ACTION_ToggleVfoSetting(bool *setting) {
|
||||
*setting = !(*setting);
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
}
|
||||
|
||||
void ACTION_Power_High(void)
|
||||
{
|
||||
ACTION_ToggleVfoSetting(&gPowerHigh);
|
||||
}
|
||||
|
||||
void ACTION_Remove_Offset(void)
|
||||
{
|
||||
ACTION_ToggleVfoSetting(&gRemoveOffset);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
+13
-2
@@ -42,15 +42,26 @@ void ACTION_SwitchDemodul(void);
|
||||
void ACTION_Wn(void);
|
||||
void ACTION_BackLightOnDemand(void);
|
||||
void ACTION_BackLight(void);
|
||||
//#if !defined(ENABLE_SPECTRUM) || !defined(ENABLE_FMRADIO)
|
||||
void ACTION_Mute(void);
|
||||
//#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
void ACTION_RxA(void);
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
void ACTION_Power_High(void);
|
||||
void ACTION_Remove_Offset(void);
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_RXTX_LOG
|
||||
void ACTION_RxTxLog(void);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_ACTION_PICKER
|
||||
#define ACTION_PICKER_TIMEOUT_500MS 10u
|
||||
extern uint8_t gActionPickerKey;
|
||||
extern uint8_t gActionPickerSelection[2];
|
||||
extern uint8_t gActionPickerTimeout_500ms;
|
||||
bool ACTION_PickerProcessKey(KEY_Code_t key, bool isPressed, bool isHeld);
|
||||
#endif
|
||||
void ACTION_Handle(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld);
|
||||
|
||||
#endif
|
||||
+251
-96
@@ -16,10 +16,6 @@
|
||||
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
|
||||
//#if !defined(ENABLE_OVERLAY)
|
||||
// #include "ARMCM0.h"
|
||||
//#endif
|
||||
|
||||
#include "app/aircopy.h"
|
||||
#include "audio.h"
|
||||
#include "driver/bk4819.h"
|
||||
@@ -31,9 +27,11 @@
|
||||
#include "ui/helper.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/ui.h"
|
||||
#include "settings.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
#include "screenshot.h"
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
#include "k5viewer.h"
|
||||
#endif
|
||||
|
||||
static const uint16_t Obfuscation[8] = { 0x6C16, 0xE614, 0x912E, 0x400D, 0x3521, 0x40D5, 0x0313, 0x80E9 };
|
||||
@@ -41,24 +39,160 @@ static const uint16_t Obfuscation[8] = { 0x6C16, 0xE614, 0x912E, 0x400D, 0x3521,
|
||||
AIRCOPY_State_t gAircopyState;
|
||||
uint16_t gAirCopyBlockNumber;
|
||||
uint16_t gErrorsDuringAirCopy;
|
||||
uint8_t gAirCopyIsSendMode;
|
||||
bool gAirCopyIsSendMode;
|
||||
|
||||
uint16_t g_FSK_Buffer[36];
|
||||
|
||||
// ============================================================================
|
||||
// Transfer Maps Definition
|
||||
// ============================================================================
|
||||
|
||||
#define AIRCOPY_BANK_SEGMENTS(bank) \
|
||||
{ \
|
||||
{ 0x0000 + (bank)*0x0800, 0x0000 + (bank)*0x0800 + 0x0800, AIRCOPY_WRITE_STRUCT }, \
|
||||
{ 0x4000 + (bank)*0x0800, 0x4000 + (bank)*0x0800 + 0x0800, AIRCOPY_WRITE_STRUCT }, \
|
||||
{ 0x8000 + (bank)*0x0100, 0x8000 + (bank)*0x0100 + 0x0100, AIRCOPY_WRITE_BYTES }, \
|
||||
}
|
||||
|
||||
#define AIRCOPY_STD_MAP(seg_array) \
|
||||
{ \
|
||||
.segments = seg_array, \
|
||||
.num_segments = 3, \
|
||||
.total_blocks = 68 \
|
||||
}
|
||||
|
||||
// total_blocks = 68 (blocs of 64 bytes) because (16 bytes + 16 bytes + 2 bytes) * 128 = 4352 / 64 = 68
|
||||
|
||||
#define DECLARE_AIRCOPY_BANK(n) \
|
||||
static const AIRCOPY_Segment_t AIRCOPY_Segments_Bank##n[] = \
|
||||
AIRCOPY_BANK_SEGMENTS(n); \
|
||||
\
|
||||
static const AIRCOPY_TransferMap_t AIRCOPY_Map_Bank##n = \
|
||||
AIRCOPY_STD_MAP(AIRCOPY_Segments_Bank##n);
|
||||
|
||||
DECLARE_AIRCOPY_BANK(0)
|
||||
DECLARE_AIRCOPY_BANK(1)
|
||||
#if AIRCOPY_NUM_BANKS >= 4 // if 512 MR CHANNEL
|
||||
DECLARE_AIRCOPY_BANK(2)
|
||||
DECLARE_AIRCOPY_BANK(3)
|
||||
#endif
|
||||
#if AIRCOPY_NUM_BANKS >= 6 // if 758 MR CHANNEL
|
||||
DECLARE_AIRCOPY_BANK(4)
|
||||
DECLARE_AIRCOPY_BANK(5)
|
||||
#endif
|
||||
#if AIRCOPY_NUM_BANKS >= 8 // if 1024 MR CHANNEL
|
||||
DECLARE_AIRCOPY_BANK(6)
|
||||
DECLARE_AIRCOPY_BANK(7)
|
||||
#endif
|
||||
|
||||
// For settings only
|
||||
|
||||
static const AIRCOPY_Segment_t AIRCOPY_Segments_Settings[] = {
|
||||
{ 0xA000, 0xA170, AIRCOPY_WRITE_BYTES },
|
||||
{ 0x880E, 0x886E, AIRCOPY_WRITE_BYTES },
|
||||
{ 0x9000, 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 = 3,
|
||||
.total_blocks = 12
|
||||
};
|
||||
|
||||
// Finally
|
||||
|
||||
static const AIRCOPY_TransferMap_t *AIRCOPY_AvailableMaps[] = {
|
||||
&AIRCOPY_Map_Bank0,
|
||||
&AIRCOPY_Map_Bank1,
|
||||
#if AIRCOPY_NUM_BANKS >= 4 // if 512 MR CHANNEL
|
||||
&AIRCOPY_Map_Bank2,
|
||||
&AIRCOPY_Map_Bank3,
|
||||
#endif
|
||||
#if AIRCOPY_NUM_BANKS >= 6 // if 758 MR CHANNEL
|
||||
&AIRCOPY_Map_Bank4,
|
||||
&AIRCOPY_Map_Bank5,
|
||||
#endif
|
||||
#if AIRCOPY_NUM_BANKS >= 8 // if 1024 MR CHANNEL
|
||||
&AIRCOPY_Map_Bank6,
|
||||
&AIRCOPY_Map_Bank7,
|
||||
#endif
|
||||
&AIRCOPY_Map_Settings,
|
||||
};
|
||||
|
||||
#define AIRCOPY_NUM_MAPS (sizeof(AIRCOPY_AvailableMaps) / sizeof(AIRCOPY_AvailableMaps[0]))
|
||||
|
||||
// ============================================================================
|
||||
// Helper Functions
|
||||
// ============================================================================
|
||||
|
||||
const AIRCOPY_TransferMap_t* AIRCOPY_GetCurrentMap(void)
|
||||
{
|
||||
if (gAircopyCurrentMapIndex >= AIRCOPY_NUM_MAPS) {
|
||||
gAircopyCurrentMapIndex = 0;
|
||||
}
|
||||
return AIRCOPY_AvailableMaps[gAircopyCurrentMapIndex];
|
||||
}
|
||||
|
||||
static void AIRCOPY_clear()
|
||||
{
|
||||
for (uint8_t i = 0; i < 15; i++)
|
||||
{
|
||||
crc[i] = 0;
|
||||
}
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot(true);
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
K5VIEWER_Update(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline const AIRCOPY_Segment_t *AIRCOPY_FindSegmentForOffset(uint16_t off)
|
||||
{
|
||||
const AIRCOPY_TransferMap_t *map = AIRCOPY_GetCurrentMap();
|
||||
|
||||
for (uint16_t i = 0; i < map->num_segments; i++)
|
||||
{
|
||||
const AIRCOPY_Segment_t *seg = &map->segments[i];
|
||||
|
||||
if (off >= seg->start_offset && off < seg->end_offset)
|
||||
return seg;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void AIRCOPY_CheckComplete(uint16_t *num)
|
||||
{
|
||||
*num = *num + 1;
|
||||
|
||||
const AIRCOPY_TransferMap_t *map = AIRCOPY_GetCurrentMap();
|
||||
uint16_t done = gAirCopyBlockNumber + gErrorsDuringAirCopy;
|
||||
|
||||
if (done >= map->total_blocks)
|
||||
{
|
||||
gAircopyState = AIRCOPY_COMPLETE;
|
||||
#ifdef ENABLE_FEAT_F4HWN_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
|
||||
// ============================================================================
|
||||
|
||||
bool AIRCOPY_SendMessage(void)
|
||||
{
|
||||
static uint8_t gAircopySendCountdown = 1;
|
||||
static uint16_t CurrentOffset = 0;
|
||||
static uint16_t CurrentSegmentIndex = 0;
|
||||
|
||||
if (gAircopyState != AIRCOPY_TRANSFER) {
|
||||
return 1;
|
||||
@@ -68,23 +202,40 @@ bool AIRCOPY_SendMessage(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
g_FSK_Buffer[1] = (gAirCopyBlockNumber & 0x3FF) << 6;
|
||||
const AIRCOPY_TransferMap_t *map = AIRCOPY_GetCurrentMap();
|
||||
|
||||
EEPROM_ReadBuffer(g_FSK_Buffer[1], &g_FSK_Buffer[2], 64);
|
||||
// Initialize on first call
|
||||
if (gAirCopyBlockNumber == 0) {
|
||||
CurrentSegmentIndex = 0;
|
||||
CurrentOffset = map->segments[0].start_offset;
|
||||
}
|
||||
|
||||
// Advance to next segment if current is done
|
||||
while (CurrentSegmentIndex < map->num_segments &&
|
||||
CurrentOffset >= map->segments[CurrentSegmentIndex].end_offset)
|
||||
{
|
||||
CurrentSegmentIndex++;
|
||||
if (CurrentSegmentIndex < map->num_segments) {
|
||||
CurrentOffset = map->segments[CurrentSegmentIndex].start_offset;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if transfer is complete
|
||||
if (CurrentSegmentIndex >= map->num_segments) {
|
||||
gAircopyState = AIRCOPY_COMPLETE;
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
K5VIEWER_Update(false);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Send data from current offset
|
||||
g_FSK_Buffer[1] = CurrentOffset;
|
||||
EEPROM_ReadBuffer(CurrentOffset, &g_FSK_Buffer[2], 64);
|
||||
|
||||
g_FSK_Buffer[34] = CRC_Calculate(&g_FSK_Buffer[1], 2 + 64);
|
||||
|
||||
for (unsigned int i = 0; i < 34; i++) {
|
||||
g_FSK_Buffer[i + 1] ^= Obfuscation[i % 8];
|
||||
}
|
||||
|
||||
if (++gAirCopyBlockNumber >= 0x78) {
|
||||
gAircopyState = AIRCOPY_COMPLETE;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot(false);
|
||||
#endif
|
||||
//NVIC_SystemReset();
|
||||
}
|
||||
AIRCOPY_Obfuscate(34);
|
||||
|
||||
RADIO_SetTxParameters();
|
||||
|
||||
@@ -92,6 +243,8 @@ bool AIRCOPY_SendMessage(void)
|
||||
BK4819_SetupPowerAmplifier(0, 0);
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO1_PIN29_PA_ENABLE, false);
|
||||
|
||||
CurrentOffset += 64;
|
||||
gAirCopyBlockNumber++;
|
||||
gAircopySendCountdown = 30;
|
||||
|
||||
return 0;
|
||||
@@ -108,57 +261,62 @@ void AIRCOPY_StorePacket(void)
|
||||
uint16_t Status = BK4819_ReadRegister(BK4819_REG_0B);
|
||||
BK4819_PrepareFSKReceive();
|
||||
|
||||
// Doc says bit 4 should be 1 = CRC OK, 0 = CRC FAIL, but original firmware checks for FAIL.
|
||||
|
||||
if ((Status & 0x0010U) != 0 || g_FSK_Buffer[0] != 0xABCD || g_FSK_Buffer[35] != 0xDCBA) {
|
||||
gErrorsDuringAirCopy++;
|
||||
BK4819_ResetFSK(); // <- important
|
||||
BK4819_PrepareFSKReceive(); // <- re-arm proprement
|
||||
|
||||
AIRCOPY_CheckComplete(&gErrorsDuringAirCopy);
|
||||
return;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < 34; i++) {
|
||||
g_FSK_Buffer[i + 1] ^= Obfuscation[i % 8];
|
||||
}
|
||||
AIRCOPY_Obfuscate(34);
|
||||
|
||||
uint16_t Crc = CRC_Calculate(&g_FSK_Buffer[1], 2 + 64);
|
||||
if (g_FSK_Buffer[34] != Crc) {
|
||||
gErrorsDuringAirCopy++;
|
||||
AIRCOPY_CheckComplete(&gErrorsDuringAirCopy);
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t Offset = g_FSK_Buffer[1];
|
||||
|
||||
if (Offset >= 0x1E00) {
|
||||
gErrorsDuringAirCopy++;
|
||||
const AIRCOPY_Segment_t *seg = AIRCOPY_FindSegmentForOffset(Offset);
|
||||
|
||||
if (seg == NULL) {
|
||||
AIRCOPY_CheckComplete(&gErrorsDuringAirCopy);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t *pData = &g_FSK_Buffer[2];
|
||||
for (unsigned int i = 0; i < 8; i++) {
|
||||
EEPROM_WriteBuffer(Offset, pData);
|
||||
pData += 4;
|
||||
Offset += 8;
|
||||
const uint8_t *pData = (const uint8_t *)&g_FSK_Buffer[2];
|
||||
|
||||
for (unsigned int i = 0; i < 8; i++)
|
||||
{
|
||||
EEPROM_WriteBuffer(Offset + (i * 8), pData + (i * 8));
|
||||
}
|
||||
|
||||
if (Offset == 0x1E00) {
|
||||
gAircopyState = AIRCOPY_COMPLETE;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
|
||||
getScreenShot(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
gAirCopyBlockNumber++;
|
||||
AIRCOPY_CheckComplete(&gAirCopyBlockNumber);
|
||||
}
|
||||
|
||||
static void AIRCOPY_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
static void AIRCOPY_InitTransfer(bool isSendMode)
|
||||
{
|
||||
if (bKeyHeld || !bKeyPressed) {
|
||||
return;
|
||||
}
|
||||
gAircopyStep = 1;
|
||||
gFSKWriteIndex = 0;
|
||||
gAirCopyBlockNumber = 0;
|
||||
gInputBoxIndex = 0;
|
||||
gAirCopyIsSendMode = isSendMode;
|
||||
|
||||
AIRCOPY_clear();
|
||||
|
||||
gAircopyState = AIRCOPY_TRANSFER;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Key Processing
|
||||
// ============================================================================
|
||||
|
||||
static void AIRCOPY_Key_DIGITS(KEY_Code_t Key)
|
||||
{
|
||||
INPUTBOX_Append(Key);
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_AIRCOPY;
|
||||
|
||||
if (gInputBoxIndex < 6) {
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
@@ -193,82 +351,79 @@ static void AIRCOPY_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
BK4819_ResetFSK();
|
||||
return;
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_AIRCOPY;
|
||||
}
|
||||
|
||||
static void AIRCOPY_Key_EXIT(bool bKeyPressed, bool bKeyHeld)
|
||||
static void AIRCOPY_Key_EXIT()
|
||||
{
|
||||
if (bKeyHeld || !bKeyPressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gInputBoxIndex == 0) {
|
||||
gAircopyStep = 1;
|
||||
gFSKWriteIndex = 0;
|
||||
gAirCopyBlockNumber = 0;
|
||||
gInputBoxIndex = 0;
|
||||
AIRCOPY_InitTransfer(0); // Mode: Receive
|
||||
gErrorsDuringAirCopy = lErrorsDuringAirCopy = 0;
|
||||
gAirCopyIsSendMode = 0;
|
||||
|
||||
AIRCOPY_clear();
|
||||
|
||||
BK4819_PrepareFSKReceive();
|
||||
|
||||
gAircopyState = AIRCOPY_TRANSFER;
|
||||
|
||||
} else {
|
||||
gInputBox[--gInputBoxIndex] = 10;
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_AIRCOPY;
|
||||
}
|
||||
|
||||
static void AIRCOPY_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
static void AIRCOPY_Key_MENU()
|
||||
{
|
||||
if (bKeyHeld || !bKeyPressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
gAircopyStep = 1;
|
||||
gFSKWriteIndex = 0;
|
||||
gAirCopyBlockNumber = 0;
|
||||
gInputBoxIndex = 0;
|
||||
gAirCopyIsSendMode = 1;
|
||||
AIRCOPY_InitTransfer(1); // Mode: Send
|
||||
|
||||
g_FSK_Buffer[0] = 0xABCD;
|
||||
g_FSK_Buffer[1] = 0;
|
||||
g_FSK_Buffer[35] = 0xDCBA;
|
||||
}
|
||||
|
||||
AIRCOPY_clear();
|
||||
static void AIRCOPY_Key_UP_DOWN(int8_t Direction)
|
||||
{
|
||||
if (!gEeprom.SET_NAV) {
|
||||
Direction = -Direction;
|
||||
}
|
||||
|
||||
GUI_DisplayScreen();
|
||||
|
||||
gAircopyState = AIRCOPY_TRANSFER;
|
||||
switch(Direction)
|
||||
{
|
||||
case 1:
|
||||
gAircopyCurrentMapIndex = (gAircopyCurrentMapIndex + 1) % AIRCOPY_NUM_MAPS;
|
||||
break;
|
||||
case -1:
|
||||
gAircopyCurrentMapIndex = (gAircopyCurrentMapIndex + AIRCOPY_NUM_MAPS - 1) % AIRCOPY_NUM_MAPS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AIRCOPY_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if (bKeyHeld || !bKeyPressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Key != KEY_PTT) {
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
}
|
||||
|
||||
switch (Key) {
|
||||
case KEY_0:
|
||||
case KEY_1:
|
||||
case KEY_2:
|
||||
case KEY_3:
|
||||
case KEY_4:
|
||||
case KEY_5:
|
||||
case KEY_6:
|
||||
case KEY_7:
|
||||
case KEY_8:
|
||||
case KEY_9:
|
||||
AIRCOPY_Key_DIGITS(Key, bKeyPressed, bKeyHeld);
|
||||
case KEY_0...KEY_9:
|
||||
AIRCOPY_Key_DIGITS(Key);
|
||||
break;
|
||||
case KEY_MENU:
|
||||
AIRCOPY_Key_MENU(bKeyPressed, bKeyHeld);
|
||||
AIRCOPY_Key_MENU();
|
||||
break;
|
||||
case KEY_EXIT:
|
||||
AIRCOPY_Key_EXIT(bKeyPressed, bKeyHeld);
|
||||
AIRCOPY_Key_EXIT();
|
||||
break;
|
||||
case KEY_UP:
|
||||
case KEY_DOWN:
|
||||
AIRCOPY_Key_UP_DOWN(Key == KEY_UP ? 1 : -1);
|
||||
break;
|
||||
case KEY_PTT:
|
||||
break;
|
||||
default:
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
break;
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_AIRCOPY;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
+75
-7
@@ -21,26 +21,94 @@
|
||||
|
||||
#include "driver/keyboard.h"
|
||||
|
||||
enum AIRCOPY_State_t
|
||||
{
|
||||
// ============================================================================
|
||||
// General definitions
|
||||
// ============================================================================
|
||||
|
||||
#define AIRCOPY_BLOCK_SIZE 0x0040u // 64 bytes per AirCopy block
|
||||
#define AIRCOPY_CHANNELS_PER_BANK 128
|
||||
#define AIRCOPY_NUM_BANKS MR_CHANNELS_MAX / AIRCOPY_CHANNELS_PER_BANK
|
||||
#define AIRCOPY_CHANNEL_SIZE 16 // bytes per channel (freq/name)
|
||||
#define AIRCOPY_BANK_SIZE_BYTES 0x1080u // 0x800 (Freq) + 0x800 (Name) + 0x80 (Attr)
|
||||
#define AIRCOPY_BAR_WIDTH 120 // Visible width of the progress gauge
|
||||
|
||||
// ============================================================================
|
||||
// Segment write mode
|
||||
// ============================================================================
|
||||
|
||||
/*
|
||||
* Defines how a segment must be written to EEPROM.
|
||||
*
|
||||
* - STRUCT: structured data (frequencies, names)
|
||||
* - BYTES : raw byte stream (attributes, settings, etc.)
|
||||
*/
|
||||
typedef enum {
|
||||
AIRCOPY_WRITE_STRUCT = 0,
|
||||
AIRCOPY_WRITE_BYTES = 1,
|
||||
} AIRCOPY_WriteMode_t;
|
||||
|
||||
// ============================================================================
|
||||
// Transfer segment structure
|
||||
// ============================================================================
|
||||
|
||||
/*
|
||||
* Describes a contiguous EEPROM region involved in AirCopy.
|
||||
* The write_mode defines how the RX side must write the data.
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t start_offset;
|
||||
uint16_t end_offset;
|
||||
AIRCOPY_WriteMode_t write_mode;
|
||||
} AIRCOPY_Segment_t;
|
||||
|
||||
// ============================================================================
|
||||
// Transfer map structure
|
||||
// ============================================================================
|
||||
|
||||
/*
|
||||
* A transfer map is a collection of segments describing
|
||||
* one complete AirCopy operation (bank, settings, etc.).
|
||||
*/
|
||||
typedef struct {
|
||||
const AIRCOPY_Segment_t *segments;
|
||||
uint16_t num_segments;
|
||||
uint16_t total_blocks;
|
||||
} AIRCOPY_TransferMap_t;
|
||||
|
||||
// ============================================================================
|
||||
// AirCopy state
|
||||
// ============================================================================
|
||||
|
||||
typedef enum {
|
||||
AIRCOPY_READY = 0,
|
||||
AIRCOPY_TRANSFER,
|
||||
AIRCOPY_COMPLETE
|
||||
};
|
||||
} AIRCOPY_State_t;
|
||||
|
||||
typedef enum AIRCOPY_State_t AIRCOPY_State_t;
|
||||
// ============================================================================
|
||||
// Globals
|
||||
// ============================================================================
|
||||
|
||||
extern AIRCOPY_State_t gAircopyState;
|
||||
extern uint16_t gAirCopyBlockNumber;
|
||||
extern uint16_t gErrorsDuringAirCopy;
|
||||
extern uint8_t gAirCopyIsSendMode;
|
||||
extern bool gAirCopyIsSendMode;
|
||||
|
||||
extern uint16_t g_FSK_Buffer[36];
|
||||
|
||||
// ============================================================================
|
||||
// API
|
||||
// ============================================================================
|
||||
|
||||
bool AIRCOPY_SendMessage(void);
|
||||
void AIRCOPY_StorePacket(void);
|
||||
void AIRCOPY_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld);
|
||||
|
||||
#endif
|
||||
const AIRCOPY_TransferMap_t* AIRCOPY_GetCurrentMap(void);
|
||||
|
||||
#endif
|
||||
// XOR-obfuscate `count` words of g_FSK_Buffer starting at index 1.
|
||||
// Self-inverse: applying twice restores the original buffer.
|
||||
void AIRCOPY_Obfuscate(unsigned int count);
|
||||
|
||||
#endif // ENABLE_AIRCOPY
|
||||
#endif // APP_AIRCOPY_H
|
||||
+591
-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;
|
||||
|
||||
|
||||
+855
-15
@@ -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,13 +758,31 @@ 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))
|
||||
{ // channel mode
|
||||
{
|
||||
bool scanListChanged = false;
|
||||
|
||||
if(!RADIO_CheckValidList(gEeprom.SCAN_LIST_DEFAULT)) {
|
||||
RADIO_NextValidList(1);
|
||||
scanListChanged = true;
|
||||
}
|
||||
|
||||
if (storeBackupSettings || scanListChanged)
|
||||
UI_MAIN_NotifyScanListChanged();
|
||||
|
||||
// channel mode
|
||||
if (storeBackupSettings) {
|
||||
initialFrqOrChan = gRxVfo->CHANNEL_SAVE;
|
||||
lastFoundFrqOrChan = initialFrqOrChan;
|
||||
@@ -78,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)
|
||||
{
|
||||
@@ -104,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))
|
||||
{
|
||||
@@ -121,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;
|
||||
@@ -193,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;
|
||||
@@ -228,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);
|
||||
@@ -249,12 +1048,12 @@ static void NextFreqChannel(void)
|
||||
|
||||
static void NextMemChannel(void)
|
||||
{
|
||||
static unsigned int prev_mr_chan = 0;
|
||||
const bool enabled = (gEeprom.SCAN_LIST_DEFAULT > 0 && gEeprom.SCAN_LIST_DEFAULT < 4) ? gEeprom.SCAN_LIST_ENABLED[gEeprom.SCAN_LIST_DEFAULT - 1] : true;
|
||||
const int chan1 = (gEeprom.SCAN_LIST_DEFAULT > 0 && gEeprom.SCAN_LIST_DEFAULT < 4) ? gEeprom.SCANLIST_PRIORITY_CH1[gEeprom.SCAN_LIST_DEFAULT - 1] : -1;
|
||||
const int chan2 = (gEeprom.SCAN_LIST_DEFAULT > 0 && gEeprom.SCAN_LIST_DEFAULT < 4) ? gEeprom.SCANLIST_PRIORITY_CH2[gEeprom.SCAN_LIST_DEFAULT - 1] : -1;
|
||||
const unsigned int prev_chan = gNextMrChannel;
|
||||
unsigned int chan = 0;
|
||||
static uint16_t prev_mr_chan = 0;
|
||||
const bool enabled = (gEeprom.SCAN_LIST_DEFAULT > 0 && gEeprom.SCAN_LIST_DEFAULT <= MR_CHANNELS_LIST + 1) ? gEeprom.SCAN_LIST_ENABLED : true;
|
||||
const int16_t chan1 = (gEeprom.SCAN_LIST_DEFAULT > 0 && gEeprom.SCAN_LIST_DEFAULT <= MR_CHANNELS_LIST + 1 && gEeprom.SCANLIST_PRIORITY_CH[0] != MR_CHANNELS_MAX) ? gEeprom.SCANLIST_PRIORITY_CH[0] : -1;
|
||||
const int16_t chan2 = (gEeprom.SCAN_LIST_DEFAULT > 0 && gEeprom.SCAN_LIST_DEFAULT <= MR_CHANNELS_LIST + 1 && gEeprom.SCANLIST_PRIORITY_CH[1] != MR_CHANNELS_MAX) ? gEeprom.SCANLIST_PRIORITY_CH[1] : -1;
|
||||
const uint16_t prev_chan = gNextMrChannel;
|
||||
uint16_t chan = 0;
|
||||
|
||||
//char str[64] = "";
|
||||
|
||||
@@ -327,29 +1126,66 @@ static void NextMemChannel(void)
|
||||
case SCAN_NEXT_CHAN_MR:
|
||||
currentScanList = SCAN_NEXT_CHAN_MR;
|
||||
gNextMrChannel = prev_mr_chan;
|
||||
chan = 0xff;
|
||||
chan = 0xFFFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!enabled || chan == 0xff)
|
||||
{
|
||||
if (!enabled || chan == 0xFFFF)
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
const uint16_t searchStart = gNextMrChannel;
|
||||
#endif
|
||||
chan = RADIO_FindNextChannel(gNextMrChannel + gScanStateDir, gScanStateDir, true, gEeprom.SCAN_LIST_DEFAULT);
|
||||
if (chan == 0xFF)
|
||||
{ // no valid channel found
|
||||
if (chan == 0xFFFF)
|
||||
{ // no valid channel found -> wrapping back to the first channel
|
||||
chan = MR_CHANNEL_FIRST;
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
// Wraparound: re-warm the precheck noise floor on the new pass
|
||||
// so it tracks current RF conditions instead of an EMA that
|
||||
// accumulated drift over the previous full sweep.
|
||||
ScanFastResetState();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
else if ((gScanStateDir > 0 && chan < searchStart) ||
|
||||
(gScanStateDir < 0 && chan > searchStart))
|
||||
{
|
||||
// RADIO_FindNextChannel() wraps internally, so 0xFFFF is not
|
||||
// returned on a normal full-sweep wrap. Detect that transition
|
||||
// here and restart the RSSI floor learning for the new pass.
|
||||
ScanFastResetState();
|
||||
}
|
||||
#endif
|
||||
|
||||
gNextMrChannel = chan;
|
||||
|
||||
//sprintf(str, "----> Chan %d\n", chan + 1);
|
||||
//LogUart(str);
|
||||
}
|
||||
|
||||
if (gNextMrChannel != prev_chan)
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
SetMemScanProgressChannel(gNextMrChannel);
|
||||
|
||||
if (ScanFastEnabled() && !MemChannelFastPrecheck(gNextMrChannel))
|
||||
{
|
||||
gScanPauseDelayIn_10ms = 1;
|
||||
gUpdateDisplay = true;
|
||||
AdvanceMemScanList(enabled);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gNextMrChannel != prev_chan
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
|| scanFastLastFullTuneCandidate
|
||||
#endif
|
||||
)
|
||||
{
|
||||
#ifndef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
gEeprom.MrChannel[ gEeprom.RX_VFO] = gNextMrChannel;
|
||||
gEeprom.ScreenChannel[gEeprom.RX_VFO] = gNextMrChannel;
|
||||
#endif
|
||||
|
||||
RADIO_ConfigureChannel(gEeprom.RX_VFO, VFO_CONFIGURE_RELOAD);
|
||||
RADIO_SetupRegisters(true);
|
||||
@@ -363,7 +1199,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
|
||||
+13
-2
@@ -3,6 +3,7 @@
|
||||
#include "functions.h"
|
||||
#include "misc.h"
|
||||
#include "settings.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
void COMMON_KeypadLockToggle()
|
||||
@@ -29,6 +30,11 @@ void COMMON_SwitchVFOs()
|
||||
#endif
|
||||
gEeprom.TX_VFO ^= 1;
|
||||
|
||||
if (gInputBoxIndex > 0) {
|
||||
gInputBoxIndex = 0;
|
||||
gHasVfoBackup = false;
|
||||
}
|
||||
|
||||
if (gEeprom.CROSS_BAND_RX_TX != CROSS_BAND_OFF)
|
||||
gEeprom.CROSS_BAND_RX_TX = gEeprom.TX_VFO + 1;
|
||||
if (gEeprom.DUAL_WATCH != DUAL_WATCH_OFF)
|
||||
@@ -49,6 +55,11 @@ void COMMON_SwitchVFOMode()
|
||||
if (gEeprom.VFO_OPEN)
|
||||
#endif
|
||||
{
|
||||
if (gInputBoxIndex > 0) {
|
||||
gInputBoxIndex = 0;
|
||||
gHasVfoBackup = false;
|
||||
}
|
||||
|
||||
if (IS_MR_CHANNEL(gTxVfo->CHANNEL_SAVE))
|
||||
{ // swap to frequency mode
|
||||
gEeprom.ScreenChannel[gEeprom.TX_VFO] = gEeprom.FreqChannel[gEeprom.TX_VFO];
|
||||
@@ -60,8 +71,8 @@ void COMMON_SwitchVFOMode()
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t Channel = RADIO_FindNextChannel(gEeprom.MrChannel[gEeprom.TX_VFO], 1, false, 0);
|
||||
if (Channel != 0xFF)
|
||||
uint16_t Channel = RADIO_FindNextChannel(gEeprom.MrChannel[gEeprom.TX_VFO], 1, false, 0);
|
||||
if (Channel != 0xFFFF)
|
||||
{ // swap to channel mode
|
||||
gEeprom.ScreenChannel[gEeprom.TX_VFO] = Channel;
|
||||
#ifdef ENABLE_VOICE
|
||||
|
||||
+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)
|
||||
|
||||
+133
-101
@@ -23,6 +23,7 @@
|
||||
#include "app/generic.h"
|
||||
#include "audio.h"
|
||||
#include "driver/bk1080.h"
|
||||
#include "driver/bk4819.h"
|
||||
#include "driver/py25q16.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "functions.h"
|
||||
@@ -31,11 +32,7 @@
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
|
||||
#endif
|
||||
|
||||
uint16_t gFM_Channels[20];
|
||||
uint16_t gFM_Channels[FM_CHANNELS_MAX];
|
||||
bool gFmRadioMode;
|
||||
uint8_t gFmRadioCountdown_500ms;
|
||||
volatile uint16_t gFmPlayCountdown_10ms;
|
||||
@@ -43,7 +40,6 @@ volatile int8_t gFM_ScanState;
|
||||
bool gFM_AutoScan;
|
||||
uint8_t gFM_ChannelPosition;
|
||||
bool gFM_FoundFrequency;
|
||||
bool gFM_AutoScan;
|
||||
uint16_t gFM_RestoreCountdown_10ms;
|
||||
|
||||
|
||||
@@ -98,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;
|
||||
@@ -109,6 +110,10 @@ void FM_TurnOff(void)
|
||||
|
||||
BK1080_Init0();
|
||||
|
||||
// Enable relevant LNA based on VFO frequency
|
||||
BK4819_PickRXFilterPathBasedOnFrequency(gRxVfo->freq_config_RX.Frequency);
|
||||
|
||||
|
||||
gUpdateStatus = true;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
@@ -119,10 +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();
|
||||
@@ -139,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)
|
||||
@@ -162,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)
|
||||
@@ -262,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;
|
||||
|
||||
@@ -277,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) {
|
||||
@@ -290,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;
|
||||
}
|
||||
@@ -299,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) {
|
||||
@@ -308,12 +316,12 @@ 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;
|
||||
}
|
||||
}
|
||||
else if (Channel < 20) {
|
||||
else if (Channel < FM_CHANNELS_MAX) {
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
#endif
|
||||
@@ -341,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) {
|
||||
@@ -364,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;
|
||||
@@ -384,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;
|
||||
|
||||
@@ -401,6 +422,7 @@ static void Key_EXIT(uint8_t state)
|
||||
}
|
||||
else {
|
||||
gInputBox[--gInputBoxIndex] = 10;
|
||||
gKeyInputCountdown = key_input_timeout_500ms;
|
||||
|
||||
if (gInputBoxIndex) {
|
||||
if (gInputBoxIndex != 1) {
|
||||
@@ -432,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;
|
||||
@@ -452,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;
|
||||
}
|
||||
@@ -476,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;
|
||||
@@ -487,9 +522,13 @@ static void Key_UP_DOWN(uint8_t state, int8_t Step)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gEeprom.SET_NAV) {
|
||||
Step = -Step;
|
||||
}
|
||||
|
||||
if (gAskToSave) {
|
||||
gRequestDisplayScreen = DISPLAY_FM;
|
||||
gFM_ChannelPosition = NUMBER_AddWithWraparound(gFM_ChannelPosition, Step, 0, 19);
|
||||
gFM_ChannelPosition = NUMBER_AddWithWraparound(gFM_ChannelPosition, Step, 0, FM_CHANNELS_MAX - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -515,10 +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;
|
||||
@@ -527,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;
|
||||
}
|
||||
@@ -547,18 +583,8 @@ void FM_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
Key_MENU(state);
|
||||
break;
|
||||
case KEY_UP:
|
||||
#ifdef ENABLE_NAVIG_LEFT_RIGHT
|
||||
Key_UP_DOWN(state, -1);
|
||||
#else
|
||||
Key_UP_DOWN(state, 1);
|
||||
#endif
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
#ifdef ENABLE_NAVIG_LEFT_RIGHT
|
||||
Key_UP_DOWN(state, 1);
|
||||
#else
|
||||
Key_UP_DOWN(state, -1);
|
||||
#endif
|
||||
Key_UP_DOWN(state, Key == KEY_UP ? 1 : -1);
|
||||
break;
|
||||
case KEY_EXIT:
|
||||
Key_EXIT(state);
|
||||
@@ -569,9 +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;
|
||||
}
|
||||
}
|
||||
@@ -586,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 < 20)
|
||||
if (gFM_ChannelPosition < FM_CHANNELS_MAX)
|
||||
gFM_Channels[gFM_ChannelPosition++] = gEeprom.FM_FrequencyPlaying;
|
||||
|
||||
if (gFM_ChannelPosition >= 20) {
|
||||
|
||||
if (gFM_ChannelPosition >= FM_CHANNELS_MAX) {
|
||||
FM_PlayAndUpdate();
|
||||
GUI_SelectNextDisplay(DISPLAY_FM);
|
||||
return;
|
||||
|
||||
goto Display;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -608,6 +638,7 @@ void FM_Play(void)
|
||||
else
|
||||
FM_Tune(gEeprom.FM_FrequencyPlaying, gFM_ScanState, false);
|
||||
|
||||
Display:
|
||||
GUI_SelectNextDisplay(DISPLAY_FM);
|
||||
}
|
||||
|
||||
@@ -619,10 +650,11 @@ void FM_Start(void)
|
||||
gFM_RestoreCountdown_10ms = 0;
|
||||
|
||||
BK1080_Init(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
|
||||
// Disable UHF LNA, enable VHF LNA
|
||||
BK4819_PickRXFilterPathBasedOnFrequency(10320000); // 103.2 MHz < 280 MHz
|
||||
|
||||
AUDIO_AudioPathOn();
|
||||
FM_AudioPathOn();
|
||||
|
||||
gEnableSpeaker = true;
|
||||
gUpdateStatus = true;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
|
||||
+2
-1
@@ -20,6 +20,7 @@
|
||||
#ifdef ENABLE_FMRADIO
|
||||
|
||||
#include "driver/keyboard.h"
|
||||
#include "misc.h"
|
||||
|
||||
#define FM_CHANNEL_UP 0x01
|
||||
#define FM_CHANNEL_DOWN 0xFF
|
||||
@@ -28,7 +29,7 @@ enum {
|
||||
FM_SCAN_OFF = 0U,
|
||||
};
|
||||
|
||||
extern uint16_t gFM_Channels[20];
|
||||
extern uint16_t gFM_Channels[FM_CHANNELS_MAX];
|
||||
extern bool gFmRadioMode;
|
||||
extern uint8_t gFmRadioCountdown_500ms;
|
||||
extern volatile uint16_t gFmPlayCountdown_10ms;
|
||||
|
||||
+1505
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
|
||||
|
||||
+347
-240
@@ -44,9 +44,36 @@
|
||||
#include "radio.h"
|
||||
#include "settings.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/main.h"
|
||||
#include "ui/menu.h"
|
||||
#include "ui/ui.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
// Full VFO backup for restore on EXIT
|
||||
static VFO_Info_t gVfoBackup;
|
||||
static uint16_t gScreenChannelBackup = 0;
|
||||
static uint16_t gFreqChannelBackup = 0;
|
||||
|
||||
static void VFO_RestoreBackup(void) {
|
||||
if (gHasVfoBackup) {
|
||||
const uint8_t Vfo = gEeprom.TX_VFO;
|
||||
|
||||
// Restore indices
|
||||
gEeprom.ScreenChannel[Vfo] = gScreenChannelBackup;
|
||||
gEeprom.FreqChannel[Vfo] = gFreqChannelBackup;
|
||||
|
||||
// Restore full VFO
|
||||
memcpy(gTxVfo, &gVfoBackup, sizeof(VFO_Info_t));
|
||||
|
||||
// Save and apply
|
||||
SETTINGS_SaveVfoIndices();
|
||||
RADIO_ConfigureSquelchAndOutputPower(gTxVfo);
|
||||
RADIO_SetupRegisters(true);
|
||||
|
||||
gHasVfoBackup = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void toggle_chan_scanlist(void)
|
||||
{ // toggle the selected channels scanlist setting
|
||||
|
||||
@@ -55,31 +82,33 @@ static void toggle_chan_scanlist(void)
|
||||
|
||||
if(!IS_MR_CHANNEL(gTxVfo->CHANNEL_SAVE)) {
|
||||
#ifdef ENABLE_SCAN_RANGES
|
||||
gScanRangeStart = gScanRangeStart ? 0 : gTxVfo->pRX->Frequency;
|
||||
gScanRangeStop = gEeprom.VfoInfo[!gEeprom.TX_VFO].freq_config_RX.Frequency;
|
||||
if(gScanRangeStart > gScanRangeStop)
|
||||
SWAP(gScanRangeStart, gScanRangeStop);
|
||||
CHFRSCANNER_ScanRange();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
ChannelAttributes_t *att = MR_GetChannelAttributes(gTxVfo->CHANNEL_SAVE);
|
||||
|
||||
// Remove exclude
|
||||
if(gMR_ChannelExclude[gTxVfo->CHANNEL_SAVE] == true)
|
||||
if(att->exclude == true)
|
||||
{
|
||||
gMR_ChannelExclude[gTxVfo->CHANNEL_SAVE] = false;
|
||||
return;
|
||||
att->exclude = false;
|
||||
MR_SaveChannelAttributesToFlash(gTxVfo->CHANNEL_SAVE, att);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t scanlist = gTxVfo->SCANLIST_PARTICIPATION;
|
||||
|
||||
scanlist++;
|
||||
|
||||
if (scanlist > MR_CHANNELS_LIST + 1)
|
||||
scanlist = 0;
|
||||
|
||||
gTxVfo->SCANLIST_PARTICIPATION = scanlist;
|
||||
|
||||
SETTINGS_UpdateChannel(gTxVfo->CHANNEL_SAVE, gTxVfo, true);
|
||||
}
|
||||
|
||||
uint8_t scanTmp = gTxVfo->SCANLIST1_PARTICIPATION | (gTxVfo->SCANLIST2_PARTICIPATION << 1) | (gTxVfo->SCANLIST3_PARTICIPATION << 2);
|
||||
|
||||
scanTmp = (scanTmp++ < 7) ? scanTmp: 0;
|
||||
|
||||
gTxVfo->SCANLIST1_PARTICIPATION = (scanTmp >> 0) & 0x01;
|
||||
gTxVfo->SCANLIST2_PARTICIPATION = (scanTmp >> 1) & 0x01;
|
||||
gTxVfo->SCANLIST3_PARTICIPATION = (scanTmp >> 2) & 0x01;
|
||||
|
||||
SETTINGS_UpdateChannel(gTxVfo->CHANNEL_SAVE, gTxVfo, true, true, true);
|
||||
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
gFlagResetVfos = true;
|
||||
}
|
||||
@@ -88,16 +117,14 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
|
||||
{
|
||||
uint8_t Vfo = gEeprom.TX_VFO;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
if(gEeprom.MENU_LOCK == true) {
|
||||
if(Key == 2) { // Enable A/B only
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
COMMON_SwitchVFOs();
|
||||
if (beep)
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
}
|
||||
if (beep)
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
return; // prevent F function if MENU LOCK is true
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
if(gEeprom.MENU_LOCK == true && Key != 2) {
|
||||
HideFKeyIcon();
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -105,8 +132,6 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
switch (Key) {
|
||||
case KEY_0:
|
||||
@@ -117,9 +142,7 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
|
||||
|
||||
case KEY_1:
|
||||
if (!IS_FREQ_CHANNEL(gTxVfo->CHANNEL_SAVE)) {
|
||||
gWasFKeyPressed = false;
|
||||
gUpdateStatus = true;
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
HideFKeyIcon();
|
||||
|
||||
#ifdef ENABLE_COPY_CHAN_TO_VFO
|
||||
if (!gEeprom.VFO_OPEN || gCssBackgroundScan) {
|
||||
@@ -183,9 +206,6 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
|
||||
if (beep)
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
break;
|
||||
|
||||
case KEY_2:
|
||||
@@ -193,8 +213,6 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
#endif
|
||||
COMMON_SwitchVFOs();
|
||||
if (beep)
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
break;
|
||||
|
||||
case KEY_3:
|
||||
@@ -202,26 +220,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];
|
||||
@@ -250,79 +269,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;
|
||||
@@ -348,8 +391,8 @@ void channelMove(uint16_t Channel)
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
#endif
|
||||
|
||||
gEeprom.MrChannel[Vfo] = (uint8_t)Channel;
|
||||
gEeprom.ScreenChannel[Vfo] = (uint8_t)Channel;
|
||||
gEeprom.MrChannel[Vfo] = (uint16_t)Channel;
|
||||
gEeprom.ScreenChannel[Vfo] = (uint16_t)Channel;
|
||||
//gRequestSaveVFO = true;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
|
||||
@@ -359,6 +402,8 @@ void channelMove(uint16_t Channel)
|
||||
#endif
|
||||
|
||||
RADIO_ConfigureChannel(gEeprom.TX_VFO, gVfoConfigureMode);
|
||||
|
||||
SETTINGS_SaveVfoIndices();
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -386,21 +431,17 @@ void channelMoveSwitch(void) {
|
||||
Channel = (Channel * 10) + gInputBox[i];
|
||||
}
|
||||
|
||||
if ((Channel == 0) && (gInputBoxIndex != 3)) {
|
||||
if ((Channel == 0) && (gInputBoxIndex != 4)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gInputBoxIndex == 3) {
|
||||
if (gInputBoxIndex == 4) {
|
||||
gInputBoxIndex = 0;
|
||||
gKeyInputCountdown = 1;
|
||||
|
||||
channelMove(Channel - 1);
|
||||
SETTINGS_SaveVfoIndices();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
channelMove(Channel - 1);
|
||||
SETTINGS_SaveVfoIndices();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,10 +455,19 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
}
|
||||
|
||||
gWasFKeyPressed = false;
|
||||
gUpdateStatus = true;
|
||||
#ifdef ENABLE_FEAT_F4HWN_ACTION_PICKER
|
||||
if (gWasFKeyPressed && (Key == KEY_SIDE1 || Key == KEY_SIDE2)) {
|
||||
gActionPickerKey = (Key == KEY_SIDE1) ? 1 : 2;
|
||||
gActionPickerTimeout_500ms = ACTION_PICKER_TIMEOUT_500MS;
|
||||
gUpdateDisplay = true;
|
||||
HideFKeyIcon();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
processFKeyFunction(Key, false);
|
||||
HideFKeyIcon();
|
||||
|
||||
processFKeyFunction(Key, true);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -432,8 +482,15 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
if (!gWasFKeyPressed) { // F-key wasn't pressed
|
||||
|
||||
if (gScanStateDir != SCAN_OFF){
|
||||
/*
|
||||
switch(Key) {
|
||||
case KEY_0...KEY_5:
|
||||
case KEY_0:
|
||||
gEeprom.SCAN_LIST_DEFAULT = MR_CHANNELS_LIST + 1;
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
SETTINGS_WriteCurrentState();
|
||||
#endif
|
||||
break;
|
||||
case KEY_1...KEY_9:
|
||||
gEeprom.SCAN_LIST_DEFAULT = Key;
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
SETTINGS_WriteCurrentState();
|
||||
@@ -443,9 +500,62 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
break;
|
||||
}
|
||||
return;
|
||||
*/
|
||||
|
||||
INPUTBOX_Append(Key);
|
||||
|
||||
/* Wait until exactly two digits are entered */
|
||||
if (gInputBoxIndex < 2)
|
||||
return;
|
||||
|
||||
/* Two digits entered */
|
||||
gInputBoxIndex = 0;
|
||||
|
||||
uint8_t value = (gInputBox[0] * 10) + gInputBox[1];
|
||||
|
||||
/* 00 = ALL scan lists */
|
||||
if (value == 0)
|
||||
{
|
||||
gEeprom.SCAN_LIST_DEFAULT = MR_CHANNELS_LIST + 1;
|
||||
UI_MAIN_NotifyScanListChanged();
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
SETTINGS_WriteCurrentState();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
/* 01 .. MR_CHANNELS_LIST */
|
||||
if (value <= MR_CHANNELS_LIST)
|
||||
{
|
||||
gEeprom.SCAN_LIST_DEFAULT = value;
|
||||
|
||||
if (!RADIO_CheckValidList(value))
|
||||
{
|
||||
/* Requested scan list is empty or invalid:
|
||||
jump to the next valid scan list */
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
RADIO_NextValidList(1);
|
||||
}
|
||||
UI_MAIN_NotifyScanListChanged();
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
SETTINGS_WriteCurrentState();
|
||||
#endif
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t Vfo = gEeprom.TX_VFO;
|
||||
|
||||
// Save full VFO state BEFORE first digit
|
||||
if (gInputBoxIndex == 0 && IS_FREQ_CHANNEL(gTxVfo->CHANNEL_SAVE)) {
|
||||
memcpy(&gVfoBackup, gTxVfo, sizeof(VFO_Info_t));
|
||||
gScreenChannelBackup = gEeprom.ScreenChannel[Vfo];
|
||||
gFreqChannelBackup = gEeprom.FreqChannel[Vfo];
|
||||
gHasVfoBackup = true;
|
||||
}
|
||||
|
||||
INPUTBOX_Append(Key);
|
||||
gKeyInputCountdown = key_input_timeout_500ms;
|
||||
|
||||
@@ -483,7 +593,13 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
return;
|
||||
}
|
||||
|
||||
gKeyInputCountdown = (gInputBoxIndex == totalDigits) ? (key_input_timeout_500ms / 16) : (key_input_timeout_500ms / 3);
|
||||
gKeyInputCountdown = key_input_timeout_500ms / (gInputBoxIndex >= totalDigits ? 16 : 3);
|
||||
|
||||
if (gInputBoxIndex > totalDigits) {
|
||||
gInputBoxIndex = totalDigits;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const char *inputStr = INPUTBOX_GetAscii();
|
||||
uint8_t inputLength = gInputBoxIndex;
|
||||
@@ -491,11 +607,8 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
// convert to int
|
||||
uint32_t inputFreq = StrToUL(inputStr);
|
||||
|
||||
// how many zero to add
|
||||
uint8_t zerosToAdd = totalDigits - inputLength;
|
||||
|
||||
// add missing zero
|
||||
for (uint8_t i = 0; i < zerosToAdd; i++) {
|
||||
for (uint8_t i = 0; i < totalDigits - inputLength; i++) {
|
||||
inputFreq *= 10;
|
||||
}
|
||||
|
||||
@@ -552,6 +665,7 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
}
|
||||
|
||||
gInputBoxIndex = 0;
|
||||
gHasVfoBackup = false;
|
||||
|
||||
uint8_t Channel = (gInputBox[0] * 10) + gInputBox[1];
|
||||
if (Channel >= 1 && Channel <= ARRAY_SIZE(NoaaFrequencyTable)) {
|
||||
@@ -573,95 +687,77 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
return;
|
||||
}
|
||||
|
||||
gWasFKeyPressed = false;
|
||||
gUpdateStatus = true;
|
||||
HideFKeyIcon();
|
||||
|
||||
if(Key == 8)
|
||||
{
|
||||
ACTION_BackLightOnDemand();
|
||||
return;
|
||||
}
|
||||
else if(Key == 9)
|
||||
{
|
||||
ACTION_BackLight();
|
||||
return;
|
||||
}
|
||||
#ifdef ENABLE_FEAT_F4HWN_GAME
|
||||
else if(Key == 7)
|
||||
{
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
if(gEeprom.MENU_LOCK == true) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
APP_RunBreakout();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
processFKeyFunction(Key, true);
|
||||
processFKeyFunction(Key, false);
|
||||
}
|
||||
|
||||
static void MAIN_Key_EXIT(bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
if (!bKeyHeld && bKeyPressed) { // exit key pressed
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL; // beep when key is pressed
|
||||
return; // don't use the key till it's released
|
||||
}
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
if (gDTMF_CallState != DTMF_CALL_STATE_NONE && gCurrentFunction != FUNCTION_TRANSMIT)
|
||||
{ // clear CALL mode being displayed
|
||||
gDTMF_CallState = DTMF_CALL_STATE_NONE;
|
||||
gUpdateDisplay = true;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
if (!gFmRadioMode)
|
||||
#endif
|
||||
{
|
||||
if (gScanStateDir == SCAN_OFF) {
|
||||
if (gInputBoxIndex == 0)
|
||||
return;
|
||||
gInputBox[--gInputBoxIndex] = 10;
|
||||
|
||||
gKeyInputCountdown = key_input_timeout_500ms;
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
if (gInputBoxIndex == 0)
|
||||
gAnotherVoiceID = VOICE_ID_CANCEL;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
gScanKeepResult = false;
|
||||
CHFRSCANNER_Stop();
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_SCANNING_STOP;
|
||||
#endif
|
||||
}
|
||||
if (bKeyHeld) { // exit key held down
|
||||
if (bKeyPressed) {
|
||||
// Restore full VFO state on long press EXIT
|
||||
VFO_RestoreBackup();
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
ACTION_FM();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (bKeyHeld && bKeyPressed) { // exit key held down
|
||||
if (gInputBoxIndex > 0 || gDTMF_InputBox_Index > 0 || gDTMF_InputMode)
|
||||
{ // cancel key input mode (channel/frequency entry)
|
||||
gDTMF_InputMode = false;
|
||||
gDTMF_InputBox_Index = 0;
|
||||
memset(gDTMF_String, 0, sizeof(gDTMF_String));
|
||||
gInputBoxIndex = 0;
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
}
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
if (gDTMF_CallState != DTMF_CALL_STATE_NONE && gCurrentFunction != FUNCTION_TRANSMIT)
|
||||
{ // clear CALL mode being displayed
|
||||
gDTMF_CallState = DTMF_CALL_STATE_NONE;
|
||||
gUpdateDisplay = true;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
if (!gFmRadioMode)
|
||||
#endif
|
||||
{
|
||||
if (gScanStateDir == SCAN_OFF) {
|
||||
if (gInputBoxIndex == 0)
|
||||
return;
|
||||
gInputBox[--gInputBoxIndex] = 10;
|
||||
|
||||
// Restore full VFO state when back to 0
|
||||
if (gInputBoxIndex == 0)
|
||||
VFO_RestoreBackup();
|
||||
|
||||
gKeyInputCountdown = key_input_timeout_500ms;
|
||||
channelMoveSwitch();
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
if (gInputBoxIndex == 0)
|
||||
gAnotherVoiceID = VOICE_ID_CANCEL;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
gScanKeepResult = false;
|
||||
gInputBoxIndex = 0;
|
||||
CHFRSCANNER_Stop();
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_SCANNING_STOP;
|
||||
#endif
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
ACTION_FM();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
static void MAIN_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
@@ -673,12 +769,30 @@ static void MAIN_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
if (bKeyPressed) { // long press MENU key
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
// Exclude work with list 1, 2, 3 or all list
|
||||
// Exclude current scan entry
|
||||
if(gScanStateDir != SCAN_OFF)
|
||||
{
|
||||
if(FUNCTION_IsRx() || gScanPauseDelayIn_10ms > 9)
|
||||
{
|
||||
gMR_ChannelExclude[gTxVfo->CHANNEL_SAVE] = true;
|
||||
#ifdef ENABLE_SCAN_RANGES
|
||||
if(gScanRangeStart && !IS_MR_CHANNEL(gNextMrChannel))
|
||||
{
|
||||
if(CHFRSCANNER_ExcludeCurrentScanRange())
|
||||
{
|
||||
UI_MAIN_NotifyScanProgressDataChanged();
|
||||
lastFoundFrqOrChan = lastFoundFrqOrChanOld;
|
||||
CHFRSCANNER_ContinueScanning();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
ChannelAttributes_t *att = MR_GetChannelAttributes(lastFoundFrqOrChan);
|
||||
att->exclude = true;
|
||||
|
||||
MR_SaveChannelAttributesToFlash(lastFoundFrqOrChan, att);
|
||||
UI_MAIN_NotifyScanProgressDataChanged();
|
||||
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
gFlagResetVfos = true;
|
||||
@@ -700,7 +814,6 @@ static void MAIN_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
}
|
||||
|
||||
gWasFKeyPressed = false;
|
||||
gUpdateStatus = true;
|
||||
|
||||
ACTION_Handle(KEY_MENU, bKeyPressed, bKeyHeld);
|
||||
@@ -711,6 +824,9 @@ static void MAIN_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
}
|
||||
|
||||
if (!bKeyPressed && !gDTMF_InputMode) { // menu key released
|
||||
gKeyInputCountdown = 1;
|
||||
channelMoveSwitch();
|
||||
|
||||
const bool bFlag = !gInputBoxIndex;
|
||||
gInputBoxIndex = 0;
|
||||
|
||||
@@ -721,18 +837,23 @@ 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;
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
#ifdef ENABLE_FEAT_F4HWN_MENU_CAT
|
||||
gMenuLevel = MENU_LEVEL_CAT;
|
||||
UI_MENU_BuildCategoryScreen();
|
||||
gMenuCursor = (gMenuCatCursor < gMenuListCount) ? gMenuCatCursor : 0;
|
||||
#endif
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_MENU;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
gRequestDisplayScreen = DISPLAY_MAIN;
|
||||
@@ -742,13 +863,6 @@ static void MAIN_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
|
||||
static void MAIN_Key_STAR(bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
if(gEeprom.MENU_LOCK == true) {
|
||||
return; // prevent F function if MENU LOCK is true
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gCurrentFunction == FUNCTION_TRANSMIT)
|
||||
return;
|
||||
|
||||
@@ -758,6 +872,19 @@ static void MAIN_Key_STAR(bool bKeyPressed, bool bKeyHeld)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bKeyHeld && bKeyPressed) { // star key pressed
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL; // beep when key is pressed
|
||||
return; // don't use the key till it's released
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
if(gEeprom.MENU_LOCK == true) {
|
||||
HideFKeyIcon();
|
||||
|
||||
return; // prevent F function if MENU LOCK is true
|
||||
}
|
||||
#endif
|
||||
|
||||
if (bKeyHeld && !gWasFKeyPressed){ // long press
|
||||
if (!bKeyPressed) // released
|
||||
return;
|
||||
@@ -780,12 +907,6 @@ static void MAIN_Key_STAR(bool bKeyPressed, bool bKeyHeld)
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bKeyPressed) { // just pressed
|
||||
return;
|
||||
}
|
||||
|
||||
// just released
|
||||
|
||||
if (!gWasFKeyPressed) // pressed without the F-key
|
||||
{
|
||||
@@ -798,7 +919,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;
|
||||
@@ -820,6 +940,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;
|
||||
@@ -834,18 +960,13 @@ static void MAIN_Key_STAR(bool bKeyPressed, bool bKeyHeld)
|
||||
|
||||
static void MAIN_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
{
|
||||
if (!gEeprom.SET_NAV) {
|
||||
Direction = -Direction;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN // Set Squelch F + UP or Down
|
||||
if(gWasFKeyPressed) {
|
||||
switch(Direction)
|
||||
{
|
||||
case 1:
|
||||
processFKeyFunction(KEY_UP, false);
|
||||
break;
|
||||
case -1:
|
||||
processFKeyFunction(KEY_DOWN, false);
|
||||
break;
|
||||
}
|
||||
processFKeyFunction(Direction == 1 ? KEY_UP : KEY_DOWN, true);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -855,12 +976,14 @@ static void MAIN_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
gPowerHigh = false;
|
||||
#endif
|
||||
|
||||
uint8_t Channel = gEeprom.ScreenChannel[gEeprom.TX_VFO];
|
||||
uint16_t Channel = gEeprom.ScreenChannel[gEeprom.TX_VFO];
|
||||
|
||||
if (gInputBoxIndex > 0) {
|
||||
gInputBoxIndex = 0;
|
||||
gHasVfoBackup = false;
|
||||
}
|
||||
|
||||
if (bKeyHeld || !bKeyPressed) { // key held or released
|
||||
if (gInputBoxIndex > 0)
|
||||
return; // leave if input box active
|
||||
|
||||
if (!bKeyPressed) {
|
||||
if (!bKeyHeld || IS_FREQ_CHANNEL(Channel))
|
||||
return;
|
||||
@@ -873,10 +996,6 @@ static void MAIN_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
}
|
||||
}
|
||||
else { // short pressed
|
||||
if (gInputBoxIndex > 0) {
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
}
|
||||
|
||||
@@ -885,7 +1004,7 @@ static void MAIN_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
if (!IS_NOAA_CHANNEL(Channel))
|
||||
#endif
|
||||
{
|
||||
uint8_t Next;
|
||||
uint16_t Next;
|
||||
if (IS_FREQ_CHANNEL(Channel)) { // step/down in frequency
|
||||
const uint32_t frequency = APP_SetFrequencyByStep(gTxVfo, Direction);
|
||||
|
||||
@@ -901,7 +1020,7 @@ static void MAIN_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
}
|
||||
|
||||
Next = RADIO_FindNextChannel(Channel + Direction, Direction, false, 0);
|
||||
if (Next == 0xFF)
|
||||
if (Next == 0xFFFF)
|
||||
return;
|
||||
if (Channel == Next)
|
||||
return;
|
||||
@@ -929,9 +1048,7 @@ static void MAIN_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
}
|
||||
|
||||
// jump to the next channel
|
||||
CHFRSCANNER_Start(false, Direction);
|
||||
gScanPauseDelayIn_10ms = 1;
|
||||
gScheduleScanListen = false;
|
||||
CHFRSCANNER_ManualResume(Direction);
|
||||
|
||||
gPttWasReleased = true;
|
||||
}
|
||||
@@ -977,18 +1094,8 @@ void MAIN_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
MAIN_Key_MENU(bKeyPressed, bKeyHeld);
|
||||
break;
|
||||
case KEY_UP:
|
||||
#ifdef ENABLE_NAVIG_LEFT_RIGHT
|
||||
MAIN_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
|
||||
#else
|
||||
MAIN_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
|
||||
#endif
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
#ifdef ENABLE_NAVIG_LEFT_RIGHT
|
||||
MAIN_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
|
||||
#else
|
||||
MAIN_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
|
||||
#endif
|
||||
MAIN_Key_UP_DOWN(bKeyPressed, bKeyHeld, Key == KEY_UP ? 1 : -1);
|
||||
break;
|
||||
case KEY_EXIT:
|
||||
MAIN_Key_EXIT(bKeyPressed, bKeyHeld);
|
||||
|
||||
+493
-205
File diff suppressed because it is too large.
Load diff
-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;
|
||||
|
||||
+1166
-388
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
|
||||
};
|
||||
|
||||
|
||||
+41
-9
@@ -347,9 +347,9 @@ static void CMD_0514(uint32_t Port, const uint8_t *pBuffer)
|
||||
#endif
|
||||
|
||||
gSerialConfigCountDown_500ms = 12; // 6 sec
|
||||
|
||||
// turn the LCD backlight off
|
||||
BACKLIGHT_TurnOff();
|
||||
|
||||
if (gEeprom.BACKLIGHT_TIME < 61) // backlight is set to be always on
|
||||
BACKLIGHT_TurnOff(); // turn the LCD backlight off
|
||||
|
||||
SendVersion(Port);
|
||||
}
|
||||
@@ -593,8 +593,8 @@ static void CMD_052F(uint32_t Port, const uint8_t *pBuffer)
|
||||
}
|
||||
#endif
|
||||
|
||||
// turn the LCD backlight off
|
||||
BACKLIGHT_TurnOff();
|
||||
if (gEeprom.BACKLIGHT_TIME < 61) // backlight is set to be always on
|
||||
BACKLIGHT_TurnOff(); // turn the LCD backlight off
|
||||
|
||||
SendVersion(Port);
|
||||
}
|
||||
@@ -677,14 +677,26 @@ bool UART_IsCommandAvailable(uint32_t Port)
|
||||
return false;
|
||||
}
|
||||
|
||||
while (1)
|
||||
// Limit iterations to prevent long loops when buffer is full of non-command data
|
||||
uint16_t maxIterations = ReadBufSize + 1;
|
||||
|
||||
while (maxIterations--)
|
||||
{
|
||||
if ((*pReadPointer) == DmaLength)
|
||||
return false;
|
||||
|
||||
while ((*pReadPointer) != DmaLength && ReadBuf[*pReadPointer] != 0xABU)
|
||||
// Find 0xAB with iteration limit
|
||||
uint16_t searchLimit = ReadBufSize;
|
||||
while ((*pReadPointer) != DmaLength && ReadBuf[*pReadPointer] != 0xABU && searchLimit--)
|
||||
*pReadPointer = DMA_INDEX((*pReadPointer), 1, ReadBufSize);
|
||||
|
||||
if (searchLimit == 0)
|
||||
{
|
||||
// Too many bytes without finding 0xAB - sync to current position and exit
|
||||
*pReadPointer = DmaLength;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((*pReadPointer) == DmaLength)
|
||||
return false;
|
||||
|
||||
@@ -702,6 +714,13 @@ bool UART_IsCommandAvailable(uint32_t Port)
|
||||
*pReadPointer = DMA_INDEX(*pReadPointer, 1, ReadBufSize);
|
||||
}
|
||||
|
||||
if (maxIterations == 0)
|
||||
{
|
||||
// Safety: too many outer loop iterations
|
||||
*pReadPointer = DmaLength;
|
||||
return false;
|
||||
}
|
||||
|
||||
Index = DMA_INDEX(*pReadPointer, 2, ReadBufSize);
|
||||
Size = (ReadBuf[DMA_INDEX(Index, 1, ReadBufSize)] << 8) | ReadBuf[Index];
|
||||
|
||||
@@ -844,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
|
||||
|
||||
+38
-102
@@ -33,38 +33,43 @@
|
||||
#include "settings.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
static const uint16_t BEEP_Classic_array[][3] = { /* Tone Duration Repeats */
|
||||
[BEEP_NONE] = {0, 0, 0 },
|
||||
[BEEP_1KHZ_60MS_OPTIONAL] = {1000, 60, 1 },
|
||||
[BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL] = {500, 60, 2 },
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
[BEEP_880HZ_200MS] = {880, 200, 1 },
|
||||
[BEEP_880HZ_500MS] = {880, 500, 1 },
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
[BEEP_400HZ_30MS] = {400, 30, 1 },
|
||||
[BEEP_500HZ_30MS] = {500, 30, 1 },
|
||||
[BEEP_600HZ_30MS] = {600, 30, 1 },
|
||||
#endif
|
||||
[BEEP_880HZ_60MS_TRIPLE_BEEP] = {880, 60, 3 }
|
||||
};
|
||||
|
||||
BEEP_Type_t gBeepToPlay = BEEP_NONE;
|
||||
|
||||
void AUDIO_PlayBeep(BEEP_Type_t Beep)
|
||||
{
|
||||
|
||||
if (Beep != BEEP_880HZ_60MS_DOUBLE_BEEP &&
|
||||
Beep != BEEP_500HZ_60MS_DOUBLE_BEEP &&
|
||||
Beep != BEEP_440HZ_500MS &&
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
Beep != BEEP_880HZ_200MS &&
|
||||
Beep != BEEP_880HZ_500MS &&
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
Beep != BEEP_400HZ_30MS &&
|
||||
Beep != BEEP_500HZ_30MS &&
|
||||
Beep != BEEP_600HZ_30MS &&
|
||||
#endif
|
||||
!gEeprom.BEEP_CONTROL)
|
||||
if (Beep == BEEP_NONE)
|
||||
return;
|
||||
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
if (gScreenToDisplay == DISPLAY_AIRCOPY)
|
||||
if ((Beep == BEEP_1KHZ_60MS_OPTIONAL ||
|
||||
Beep == BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL) &&
|
||||
!gEeprom.BEEP_CONTROL)
|
||||
return;
|
||||
#endif
|
||||
|
||||
|
||||
if (gCurrentFunction == FUNCTION_RECEIVE)
|
||||
return;
|
||||
|
||||
if (gCurrentFunction == FUNCTION_MONITOR)
|
||||
return;
|
||||
|
||||
if (Beep >= ARRAY_SIZE(BEEP_Classic_array))
|
||||
return;
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
if (gFmRadioMode)
|
||||
BK1080_Mute(true);
|
||||
@@ -79,49 +84,14 @@ void AUDIO_PlayBeep(BEEP_Type_t Beep)
|
||||
|
||||
uint16_t ToneConfig = BK4819_ReadRegister(BK4819_REG_71);
|
||||
|
||||
uint16_t ToneFrequency;
|
||||
switch (Beep)
|
||||
{
|
||||
default:
|
||||
case BEEP_NONE:
|
||||
ToneFrequency = 220;
|
||||
break;
|
||||
case BEEP_1KHZ_60MS_OPTIONAL:
|
||||
ToneFrequency = 1000;
|
||||
break;
|
||||
case BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL:
|
||||
case BEEP_500HZ_60MS_DOUBLE_BEEP:
|
||||
ToneFrequency = 500;
|
||||
break;
|
||||
case BEEP_440HZ_500MS:
|
||||
ToneFrequency = 440;
|
||||
break;
|
||||
case BEEP_880HZ_60MS_DOUBLE_BEEP:
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
case BEEP_880HZ_200MS:
|
||||
case BEEP_880HZ_500MS:
|
||||
#endif
|
||||
ToneFrequency = 880;
|
||||
break;
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
case BEEP_400HZ_30MS:
|
||||
ToneFrequency = 400;
|
||||
break;
|
||||
case BEEP_500HZ_30MS:
|
||||
ToneFrequency = 500;
|
||||
break;
|
||||
case BEEP_600HZ_30MS:
|
||||
ToneFrequency = 600;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
if(Beep == BEEP_400HZ_30MS || Beep == BEEP_500HZ_30MS || Beep == BEEP_600HZ_30MS)
|
||||
{
|
||||
BK4819_WriteRegister(BK4819_REG_70, BK4819_REG_70_ENABLE_TONE1 | ((1 & 0x7f) << BK4819_REG_70_SHIFT_TONE1_TUNING_GAIN));
|
||||
}
|
||||
#endif
|
||||
|
||||
BK4819_PlayTone(ToneFrequency, true);
|
||||
BK4819_PrepareToPlayTone(true);
|
||||
|
||||
SYSTEM_DelayMs(2);
|
||||
|
||||
@@ -129,52 +99,12 @@ void AUDIO_PlayBeep(BEEP_Type_t Beep)
|
||||
|
||||
SYSTEM_DelayMs(60);
|
||||
|
||||
uint16_t Duration;
|
||||
switch (Beep)
|
||||
{
|
||||
case BEEP_880HZ_60MS_DOUBLE_BEEP:
|
||||
BK4819_ExitTxMute();
|
||||
SYSTEM_DelayMs(60);
|
||||
BK4819_EnterTxMute();
|
||||
SYSTEM_DelayMs(20);
|
||||
[[fallthrough]];
|
||||
case BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL:
|
||||
case BEEP_500HZ_60MS_DOUBLE_BEEP:
|
||||
BK4819_ExitTxMute();
|
||||
SYSTEM_DelayMs(60);
|
||||
BK4819_EnterTxMute();
|
||||
SYSTEM_DelayMs(20);
|
||||
[[fallthrough]];
|
||||
case BEEP_1KHZ_60MS_OPTIONAL:
|
||||
BK4819_ExitTxMute();
|
||||
Duration = 60;
|
||||
break;
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
case BEEP_400HZ_30MS:
|
||||
case BEEP_500HZ_30MS:
|
||||
case BEEP_600HZ_30MS:
|
||||
BK4819_ExitTxMute();
|
||||
Duration = 30;
|
||||
break;
|
||||
#endif
|
||||
case BEEP_440HZ_500MS:
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
case BEEP_880HZ_200MS:
|
||||
BK4819_ExitTxMute();
|
||||
Duration = 200;
|
||||
break;
|
||||
case BEEP_880HZ_500MS:
|
||||
#endif
|
||||
default:
|
||||
BK4819_ExitTxMute();
|
||||
Duration = 500;
|
||||
break;
|
||||
for (uint8_t i = 0; i < BEEP_Classic_array[Beep][BEEP_REPEATS]; i++) {
|
||||
BK4819_PlayToneRaw( BEEP_Classic_array[Beep][BEEP_TONE],
|
||||
BEEP_Classic_array[Beep][BEEP_DURATION]);
|
||||
SYSTEM_DelayMs(20);
|
||||
}
|
||||
|
||||
SYSTEM_DelayMs(Duration);
|
||||
BK4819_EnterTxMute();
|
||||
SYSTEM_DelayMs(20);
|
||||
|
||||
AUDIO_AudioPathOff();
|
||||
|
||||
SYSTEM_DelayMs(5);
|
||||
@@ -182,11 +112,18 @@ void AUDIO_PlayBeep(BEEP_Type_t Beep)
|
||||
SYSTEM_DelayMs(5);
|
||||
BK4819_WriteRegister(BK4819_REG_71, ToneConfig);
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
const bool isFmRadio = gFmRadioMode;
|
||||
|
||||
if (isFmRadio)
|
||||
SYSTEM_DelayMs(10);
|
||||
#endif
|
||||
|
||||
if (gEnableSpeaker)
|
||||
AUDIO_AudioPathOn();
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
if (gFmRadioMode)
|
||||
if (isFmRadio)
|
||||
BK1080_Mute(false);
|
||||
#endif
|
||||
|
||||
@@ -196,7 +133,6 @@ void AUDIO_PlayBeep(BEEP_Type_t Beep)
|
||||
#ifdef ENABLE_VOX
|
||||
gVoxResumeCountdown = 80;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
@@ -306,7 +242,7 @@ static void LoadVoiceSamples()
|
||||
{
|
||||
gVoiceBuf[gVoiceBufWriteIndex][i] = VOICE_SAMPLES[Buf[i]];
|
||||
}
|
||||
VOCIE_BUF_ForwardWriteIndex();
|
||||
VOICE_BUF_ForwardWriteIndex();
|
||||
gVoiceBufLen++;
|
||||
}
|
||||
|
||||
|
||||
+8
-3
@@ -22,23 +22,28 @@
|
||||
|
||||
#include "driver/gpio.h"
|
||||
|
||||
enum {
|
||||
BEEP_TONE = 0,
|
||||
BEEP_DURATION,
|
||||
BEEP_REPEATS
|
||||
};
|
||||
|
||||
|
||||
enum BEEP_Type_t
|
||||
{
|
||||
BEEP_NONE = 0,
|
||||
BEEP_1KHZ_60MS_OPTIONAL,
|
||||
BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL,
|
||||
BEEP_440HZ_500MS,
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
BEEP_880HZ_200MS,
|
||||
BEEP_880HZ_500MS,
|
||||
#endif
|
||||
BEEP_500HZ_60MS_DOUBLE_BEEP,
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
BEEP_400HZ_30MS,
|
||||
BEEP_500HZ_30MS,
|
||||
BEEP_600HZ_30MS,
|
||||
#endif
|
||||
BEEP_880HZ_60MS_DOUBLE_BEEP
|
||||
BEEP_880HZ_60MS_TRIPLE_BEEP
|
||||
};
|
||||
|
||||
typedef enum BEEP_Type_t BEEP_Type_t;
|
||||
|
||||
+182
-113
@@ -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,108 +205,41 @@ const uint8_t BITMAP_VFO_NotDefault[7] =
|
||||
0b00010100,
|
||||
0b00001000
|
||||
};
|
||||
*/
|
||||
|
||||
const uint8_t BITMAP_ScanList0[7] =
|
||||
{ // '0' symbol
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01000011,
|
||||
0b01011101,
|
||||
0b01100001,
|
||||
0b01111111,
|
||||
0b01111111
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_ScanList1[7] =
|
||||
{ // '1' symbol
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01111011,
|
||||
0b01000001,
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01111111
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_ScanList2[7] =
|
||||
{ // '2' symbol
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01001101,
|
||||
0b01010101,
|
||||
0b01011011,
|
||||
0b01111111,
|
||||
0b01111111
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_ScanList3[7] =
|
||||
{ // '3' symbol
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01011101,
|
||||
0b01010101,
|
||||
0b01101011,
|
||||
0b01111111,
|
||||
0b01111111
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_ScanListE[7] =
|
||||
{ // 'E' symbol
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01000001,
|
||||
0b01010101,
|
||||
0b01010101,
|
||||
0b01111111,
|
||||
0b01111111
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_ScanList123[19] =
|
||||
// Compact arrow
|
||||
const uint8_t BITMAP_VFO_Default[7] =
|
||||
{
|
||||
// '123' symbol
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01111011,
|
||||
0b01000001,
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01001101,
|
||||
0b01010101,
|
||||
0b01011011,
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01011101,
|
||||
0b01010101,
|
||||
0b01101011,
|
||||
0b01111111,
|
||||
0b01111111
|
||||
0b00111110,
|
||||
0b00111110,
|
||||
0b00011100,
|
||||
0b00011100,
|
||||
0b00001000,
|
||||
0b00001000,
|
||||
0b00000000
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_ScanListAll[19] =
|
||||
// Compact empty arrow
|
||||
const uint8_t BITMAP_VFO_NotDefault[7] =
|
||||
{
|
||||
// 'All' symbol
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01000011,
|
||||
0b01110101,
|
||||
0b01000011,
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01000001,
|
||||
0b01011111,
|
||||
0b01011111,
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01111111,
|
||||
0b01000001,
|
||||
0b01011111,
|
||||
0b01011111,
|
||||
0b01111111,
|
||||
0b01111111
|
||||
0b00100010,
|
||||
0b00100010,
|
||||
0b00010100,
|
||||
0b00010100,
|
||||
0b00001000,
|
||||
0b00001000,
|
||||
0b00000000
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_VFO_Empty[7] =
|
||||
{
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000
|
||||
};
|
||||
|
||||
const uint8_t BITMAP_compand[6] =
|
||||
@@ -361,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
-8
@@ -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,22 +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_ScanList0[7];
|
||||
extern const uint8_t BITMAP_ScanList1[7];
|
||||
extern const uint8_t BITMAP_ScanList2[7];
|
||||
extern const uint8_t BITMAP_ScanList3[7];
|
||||
extern const uint8_t BITMAP_ScanList123[19];
|
||||
extern const uint8_t BITMAP_ScanListAll[19];
|
||||
extern const uint8_t BITMAP_ScanListE[7];
|
||||
extern const uint8_t BITMAP_PowerUser[3];
|
||||
extern const uint8_t BITMAP_compand[6];
|
||||
|
||||
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
|
||||
|
||||
+135
-38
@@ -31,8 +31,10 @@
|
||||
#include "misc.h"
|
||||
#endif
|
||||
|
||||
#define PWM_FREQ 240
|
||||
#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,10 +46,39 @@ 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
|
||||
const uint8_t value[] = {0, 3, 6, 9, 15, 24, 38, 62, 100, 159, 255};
|
||||
// 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
|
||||
16, // 2
|
||||
24, // 3
|
||||
32, // 4
|
||||
48, // 5
|
||||
72, // 6
|
||||
104, // 7
|
||||
150, // 8
|
||||
200, // 9
|
||||
255 // 10 max
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SLEEP
|
||||
@@ -79,7 +110,7 @@ void BACKLIGHT_InitHardware()
|
||||
| LL_DMA_MEMORY_INCREMENT //
|
||||
| LL_DMA_PDATAALIGN_WORD //
|
||||
| LL_DMA_MDATAALIGN_WORD //
|
||||
| LL_DMA_PRIORITY_LOW //
|
||||
| LL_DMA_PRIORITY_HIGH //
|
||||
);
|
||||
|
||||
LL_DMA_SetMemoryAddress(DMA1, DMA_CHANNEL, (uint32_t)dutyCycle);
|
||||
@@ -89,15 +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)
|
||||
{
|
||||
@@ -122,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:
|
||||
@@ -178,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);
|
||||
@@ -198,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);
|
||||
@@ -219,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();
|
||||
}
|
||||
|
||||
+12
-4
@@ -48,7 +48,8 @@ enum BK4819_FilterBandwidth_t
|
||||
{
|
||||
BK4819_FILTER_BW_WIDE = 0,
|
||||
BK4819_FILTER_BW_NARROW,
|
||||
BK4819_FILTER_BW_NARROWER
|
||||
BK4819_FILTER_BW_NARROWER,
|
||||
BK4819_FILTER_BW_AM
|
||||
};
|
||||
|
||||
typedef enum BK4819_FilterBandwidth_t BK4819_FilterBandwidth_t;
|
||||
@@ -76,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);
|
||||
@@ -104,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);
|
||||
@@ -115,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);
|
||||
@@ -142,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);
|
||||
@@ -162,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);
|
||||
|
||||
|
||||
+242
-242
@@ -19,6 +19,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "settings.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include "audio.h"
|
||||
|
||||
@@ -27,11 +28,6 @@
|
||||
#include "driver/system.h"
|
||||
#include "driver/systick.h"
|
||||
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
|
||||
#endif
|
||||
|
||||
#define PIN_CSN GPIO_MAKE_PIN(GPIOF, LL_GPIO_PIN_9)
|
||||
#define PIN_SCL GPIO_MAKE_PIN(GPIOB, LL_GPIO_PIN_8)
|
||||
#define PIN_SDA GPIO_MAKE_PIN(GPIOB, LL_GPIO_PIN_9)
|
||||
@@ -43,6 +39,16 @@ static const uint16_t FSK_RogerTable[7] = {0xF1A2, 0x7446, 0x61A4, 0x6544, 0x4E8
|
||||
|
||||
static uint16_t gBK4819_GpioOutState;
|
||||
|
||||
#define SHORT_DELAY() \
|
||||
__asm volatile("nop\n nop\n nop\n nop\n nop\n" \
|
||||
"nop\n nop\n nop\n nop\n nop\n" \
|
||||
"nop\n nop\n nop\n nop\n nop\n" \
|
||||
"nop\n nop\n nop\n nop\n nop\n" \
|
||||
"nop\n nop\n nop\n nop\n nop\n" \
|
||||
"nop\n nop\n nop\n nop\n nop\n" \
|
||||
"nop\n nop\n nop\n nop\n nop\n" \
|
||||
"nop\n nop\n nop\n nop\n nop\n")
|
||||
|
||||
bool gRxIdleMode;
|
||||
|
||||
static inline void CS_Assert()
|
||||
@@ -177,7 +183,7 @@ void BK4819_Init(void)
|
||||
|
||||
BK4819_WriteRegister(0x73, 0x4691);
|
||||
BK4819_WriteRegister(0x77, 0x88EF);
|
||||
BK4819_WriteRegister(BK4819_REG_19, 0x104E);
|
||||
BK4819_WriteRegister(BK4819_REG_19, 0x1041);
|
||||
BK4819_WriteRegister(BK4819_REG_28, 0x0B40);
|
||||
BK4819_WriteRegister(BK4819_REG_29, 0xAA00);
|
||||
BK4819_WriteRegister(0x2A, 0x6600);
|
||||
@@ -201,63 +207,78 @@ static uint16_t BK4819_ReadU16(void)
|
||||
uint16_t Value;
|
||||
|
||||
SDA_SetDir(false);
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
Value = 0;
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
Value <<= 1;
|
||||
Value |= SDA_ReadInput();
|
||||
SCL_Set();
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
SCL_Reset();
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
}
|
||||
SDA_SetDir(true);
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
static uint16_t reg_30_cache = 0xFFFF;
|
||||
static uint16_t reg_47_cache = 0xFFFF;
|
||||
|
||||
uint16_t BK4819_ReadRegister(BK4819_REGISTER_t Register)
|
||||
{
|
||||
uint16_t Value;
|
||||
|
||||
CS_Release();
|
||||
SCL_Reset();
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
|
||||
CS_Assert();
|
||||
BK4819_WriteU8(Register | 0x80);
|
||||
Value = BK4819_ReadU16();
|
||||
CS_Release();
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
|
||||
SCL_Set();
|
||||
SDA_Set();
|
||||
|
||||
if (Register == BK4819_REG_30)
|
||||
reg_30_cache = Value;
|
||||
else if (Register == BK4819_REG_47)
|
||||
reg_47_cache = Value;
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
void BK4819_WriteRegister(BK4819_REGISTER_t Register, uint16_t Data)
|
||||
{
|
||||
if (Register == BK4819_REG_30)
|
||||
{
|
||||
if (Data == reg_30_cache)
|
||||
return;
|
||||
reg_30_cache = Data;
|
||||
}
|
||||
else if (Register == BK4819_REG_47)
|
||||
{
|
||||
if (Data == reg_47_cache)
|
||||
return;
|
||||
reg_47_cache = Data;
|
||||
}
|
||||
|
||||
CS_Release();
|
||||
SCL_Reset();
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
|
||||
CS_Assert();
|
||||
BK4819_WriteU8(Register);
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
|
||||
BK4819_WriteU16(Data);
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
|
||||
CS_Release();
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
|
||||
SCL_Set();
|
||||
SDA_Set();
|
||||
@@ -275,14 +296,14 @@ void BK4819_WriteU8(uint8_t Data)
|
||||
else
|
||||
SDA_Set();
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
SCL_Set();
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
|
||||
Data <<= 1;
|
||||
|
||||
SCL_Reset();
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,14 +319,14 @@ void BK4819_WriteU16(uint16_t Data)
|
||||
else
|
||||
SDA_Set();
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
SCL_Set();
|
||||
|
||||
Data <<= 1;
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
SCL_Reset();
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,45 +359,9 @@ void BK4819_SetAGC(bool enable)
|
||||
|
||||
void BK4819_InitAGC(bool amModulation)
|
||||
{
|
||||
// REG_10, REG_11, REG_12 REG_13, REG_14
|
||||
//
|
||||
// Rx AGC Gain Table[]. (Index Max->Min is 3,2,1,0,-1)
|
||||
//
|
||||
// <15:10> ???
|
||||
//
|
||||
// <9:8> LNA Gain Short
|
||||
// 3 = 0dB <<< 1o11 read from spectrum reference manual
|
||||
// 2 = -24dB -19 -11
|
||||
// 1 = -30dB -24 -16
|
||||
// 0 = -33dB -28 -19
|
||||
//
|
||||
// <7:5> LNA Gain
|
||||
// 7 = 0dB
|
||||
// 6 = -2dB
|
||||
// 5 = -4dB
|
||||
// 4 = -6dB
|
||||
// 3 = -9dB
|
||||
// 2 = -14dB <<<
|
||||
// 1 = -19dB
|
||||
// 0 = -24dB
|
||||
//
|
||||
// <4:3> MIXER Gain
|
||||
// 3 = 0dB <<<
|
||||
// 2 = -3dB
|
||||
// 1 = -6dB
|
||||
// 0 = -8dB
|
||||
//
|
||||
// <2:0> PGA Gain
|
||||
// 7 = 0dB
|
||||
// 6 = -3dB <<<
|
||||
// 5 = -6dB
|
||||
// 4 = -9dB
|
||||
// 3 = -15dB
|
||||
// 2 = -21dB
|
||||
// 1 = -27dB
|
||||
// 0 = -33dB
|
||||
//
|
||||
|
||||
/*
|
||||
// Old strategy
|
||||
BK4819_WriteRegister(BK4819_REG_13, 0x03BE); // 0x03BE / 000000 11 101 11 110 / -7dB
|
||||
BK4819_WriteRegister(BK4819_REG_12, 0x037B); // 0x037B / 000000 11 011 11 011 / -24dB
|
||||
BK4819_WriteRegister(BK4819_REG_11, 0x027B); // 0x027B / 000000 10 011 11 011 / -43dB
|
||||
@@ -391,7 +376,21 @@ void BK4819_InitAGC(bool amModulation)
|
||||
}
|
||||
|
||||
BK4819_WriteRegister(BK4819_REG_7B, 0x8420);
|
||||
*/
|
||||
|
||||
// New strategy
|
||||
// Keep the runtime AGC profile aligned with the stock BK4829 firmware
|
||||
// until modulation-specific AGC changes are proven on hardware.
|
||||
|
||||
(void)amModulation;
|
||||
|
||||
BK4819_WriteRegister(BK4819_REG_10, 0x0318);
|
||||
BK4819_WriteRegister(BK4819_REG_11, 0x033A);
|
||||
BK4819_WriteRegister(BK4819_REG_12, 0x03DB);
|
||||
BK4819_WriteRegister(BK4819_REG_13, 0x03DF);
|
||||
BK4819_WriteRegister(BK4819_REG_14, 0x0210);
|
||||
BK4819_WriteRegister(BK4819_REG_49, 0x2AB2);
|
||||
BK4819_WriteRegister(BK4819_REG_7B, 0x73DC);
|
||||
}
|
||||
|
||||
int8_t BK4819_GetRxGain_dB(void)
|
||||
@@ -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,102 +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;
|
||||
@@ -803,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
|
||||
@@ -1044,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;
|
||||
|
||||
@@ -1059,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)
|
||||
{
|
||||
@@ -1082,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)
|
||||
{
|
||||
@@ -1160,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)
|
||||
@@ -1453,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)
|
||||
@@ -1552,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);
|
||||
@@ -1587,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
|
||||
//
|
||||
@@ -1603,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)
|
||||
@@ -1692,7 +1687,7 @@ void BK4819_Disable(void)
|
||||
|
||||
void BK4819_StopScan(void)
|
||||
{
|
||||
BK4819_DisableFrequencyScan();
|
||||
BK4819_SetFrequencyScan(false);
|
||||
BK4819_Disable();
|
||||
}
|
||||
|
||||
@@ -1764,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;
|
||||
@@ -1779,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
|
||||
}
|
||||
|
||||
@@ -1851,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();
|
||||
}
|
||||
|
||||
+47
-24
@@ -38,30 +38,53 @@ typedef struct
|
||||
#define _MK_MAPPING(PY25Q16_Addr, EEPROM_From, EEPROM_To) {PY25Q16_Addr, EEPROM_From, EEPROM_To - EEPROM_From}
|
||||
|
||||
static const AddrMapping_t ADDR_MAPPINGS[] = {
|
||||
// Sorted by EEPROM addr
|
||||
_MK_MAPPING(0x000000, 0x0000, 0x0c80), //
|
||||
_MK_MAPPING(0x001000, 0x0c80, 0x0d60), //
|
||||
_MK_MAPPING(0x002000, 0x0d60, 0x0e30), //
|
||||
_MK_MAPPING(HOLE_ADDR, 0x0e30, 0x0e40), //
|
||||
_MK_MAPPING(0x003000, 0x0e40, 0x0e68), //
|
||||
_MK_MAPPING(HOLE_ADDR, 0x0e68, 0x0e70), //
|
||||
_MK_MAPPING(0x004000, 0x0e70, 0x0e80), //
|
||||
_MK_MAPPING(0x005000, 0x0e80, 0x0e88), //
|
||||
_MK_MAPPING(0x006000, 0x0e88, 0x0e90), //
|
||||
_MK_MAPPING(0x007000, 0x0e90, 0x0ee0), //
|
||||
_MK_MAPPING(0x008000, 0x0ee0, 0x0f18), //
|
||||
_MK_MAPPING(0x009000, 0x0f18, 0x0f20), //
|
||||
_MK_MAPPING(HOLE_ADDR, 0x0f20, 0x0f30), //
|
||||
_MK_MAPPING(0x00a000, 0x0f30, 0x0f40), //
|
||||
_MK_MAPPING(0x00b000, 0x0f40, 0x0f48), //
|
||||
_MK_MAPPING(HOLE_ADDR, 0x0f48, 0x0f50), //
|
||||
_MK_MAPPING(0x00e000, 0x0f50, 0x1bd0), //
|
||||
_MK_MAPPING(HOLE_ADDR, 0x1bd0, 0x1c00), //
|
||||
_MK_MAPPING(0x00f000, 0x1c00, 0x1d00), //
|
||||
_MK_MAPPING(HOLE_ADDR, 0x1d00, 0x1e00), //
|
||||
_MK_MAPPING(0x010000, 0x1e00, 0x1f90), //
|
||||
_MK_MAPPING(HOLE_ADDR, 0x1f90, 0x1ff0), //
|
||||
_MK_MAPPING(0x00c000, 0x1ff0, 0x2000), //
|
||||
_MK_MAPPING(0x000000, 0x000000, 0x001000), // 256 MR Freq * 16 Bytes (ex 0x000000)
|
||||
_MK_MAPPING(0x001000, 0x001000, 0x002000), // 256 MR Freq * 16 Bytes
|
||||
_MK_MAPPING(0x002000, 0x002000, 0x003000), // 256 MR Freq * 16 Bytes
|
||||
_MK_MAPPING(0x003000, 0x003000, 0x004000), // 256 MR Freq * 16 Bytes
|
||||
|
||||
_MK_MAPPING(0x004000, 0x004000, 0x005000), // 256 MR Name * 16 Bytes (ex 0x00e000)
|
||||
_MK_MAPPING(0x005000, 0x005000, 0x006000), // 256 MR Name * 16 Bytes
|
||||
_MK_MAPPING(0x006000, 0x006000, 0x007000), // 256 MR Name * 16 Bytes
|
||||
_MK_MAPPING(0x007000, 0x007000, 0x008000), // 256 MR Name * 16 Bytes
|
||||
|
||||
_MK_MAPPING(0x008000, 0x008000, 0x00886E), // 1024 MR + 7 VFO Attributes * 2 Bytes (ex 0x002000) 0x008000 -> 0x00880E
|
||||
// List name * 4 Bytes 0x00880E -> 0x00886E
|
||||
|
||||
_MK_MAPPING(0x009000, 0x009000, 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, 0x00A170), // Settings * 16 Bytes (ex 0x004000) 0x00A000 -> 0x00A010
|
||||
// Settings * 16 Bytes (ex 0x005000) 0x00A010 -> 0x00A020
|
||||
// Settings FM * 8 Bytes (0x006000) 0x00A020 -> 0x00A028
|
||||
// MR FM * 128 Bytes (0x003000) 0x00A028 -> 0x00A0A8
|
||||
// Settings * 80 Bytes (0x007000) 0x00A0A8 -> 0x00A0F8
|
||||
// Settings * 56 Bytes (0x008000) 0x00A0F8 -> 0x00A130
|
||||
// Settings Scanlist * 8 Bytes (0x009000) 0x00A130 -> 0x00A138
|
||||
// Settings AES * 16 Bytes (0x00A000) 0x00A138 -> 0x00A148
|
||||
// Settings Spectrum * 8 Bytes 0x00A148 -> 0x00A150
|
||||
// Settings * 8 Bytes (0x00B000) 0x00A150 -> 0x00A158
|
||||
// Settings F4HWN * 8 Bytes (0x00C000) 0x00A158 -> 0x00A160
|
||||
// Settings Version * 16 Bytes 0x00A160 -> 0x00A170
|
||||
|
||||
_MK_MAPPING(0x010000, 0x00B000, 0x00B200), // Calibration 512 Bytes!!!
|
||||
|
||||
_MK_MAPPING(0x011000, 0x00C000, 0x00D000), // Boot Logo sector (4 KB):
|
||||
// [0x00..0x07] 8-byte header (reserved)
|
||||
// [0x08..0x407] 128x64 monochrome bitmap, 1024 Bytes
|
||||
// ST7565-native: 8 pages * 128 columns, column-major LSB-top
|
||||
|
||||
// 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);
|
||||
|
||||
+27
-3
@@ -22,6 +22,11 @@
|
||||
#define PIN_SCL GPIO_MAKE_PIN(GPIOF, LL_GPIO_PIN_5)
|
||||
#define PIN_SDA GPIO_MAKE_PIN(GPIOF, LL_GPIO_PIN_6)
|
||||
|
||||
// CRITICAL FIX: Define I2C timeout to prevent infinite hangs
|
||||
// If I2C slave doesn't respond, timeout after this many iterations
|
||||
// 255 iterations × 1µs = 255µs, plus we add safety margin
|
||||
#define I2C_ACK_TIMEOUT_ITERATIONS 255
|
||||
|
||||
static inline void SCL_Set()
|
||||
{
|
||||
GPIO_SetOutputPin(PIN_SCL);
|
||||
@@ -141,11 +146,24 @@ int I2C_Write(uint8_t Data)
|
||||
SCL_Set();
|
||||
SYSTICK_DelayUs(1);
|
||||
|
||||
for (i = 0; i < 255; i++) {
|
||||
// CRITICAL FIX #1: Add timeout to ACK check
|
||||
// Original: for (i = 0; i < 255; i++)
|
||||
// Problem: If I2C slave doesn't respond, waits 255 iterations = 255µs spin-wait
|
||||
// If slave broken/disconnected: loops forever with no break condition
|
||||
//
|
||||
// Solution: Keep iteration limit but add explicit timeout check
|
||||
// If slave doesn't pull SDA low within 255 iterations, return error
|
||||
// This prevents indefinite hangs while still being conservative
|
||||
|
||||
for (i = 0; i < I2C_ACK_TIMEOUT_ITERATIONS; i++) {
|
||||
if (!SDA_IsSet()) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
// CRITICAL FIX #2: Small delay between ACK checks
|
||||
// This prevents CPU from spinning 255 times at full speed
|
||||
// Also gives slave time to respond
|
||||
SYSTICK_DelayUs(1);
|
||||
}
|
||||
|
||||
SCL_Reset();
|
||||
@@ -178,11 +196,17 @@ int I2C_WriteBuffer(const void *pBuffer, uint8_t Size)
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < Size; i++) {
|
||||
// CRITICAL FIX #3: Check return value of I2C_Write
|
||||
// Original: if (I2C_Write(*pData++) < 0)
|
||||
// This detects if ACK timeout occurred
|
||||
// If slave not responding, return error immediately instead of continuing
|
||||
if (I2C_Write(*pData++) < 0) {
|
||||
// I2C slave not responding (ACK timeout)
|
||||
// Return error instead of continuing to write more bytes
|
||||
// This prevents cascading timeouts
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
+189
-20
@@ -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,38 +184,80 @@ 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;
|
||||
|
||||
// if (!GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT))
|
||||
// return KEY_PTT;
|
||||
|
||||
// *****************
|
||||
|
||||
// Scan all 5 columns - never break early to avoid GPIO state issues
|
||||
for (unsigned int j = 0; j < 5; j++)
|
||||
{
|
||||
uint32_t reg;
|
||||
unsigned int i;
|
||||
unsigned int k;
|
||||
uint32_t match_count = 0; // Count consecutive matching reads
|
||||
|
||||
// Set all high
|
||||
// Set all columns high first
|
||||
GPIO_SetOutputPin(PIN_COLS);
|
||||
|
||||
// Clear the pin we are selecting
|
||||
// Clear the specific column we are selecting
|
||||
if (j > 0)
|
||||
GPIO_ResetOutputPin(PIN_COL(j - 1));
|
||||
|
||||
// Read all 4 GPIO pins at once .. with de-noise, max of 8 sample loops
|
||||
for (i = 0, k = 0, reg = 0; i < 3 && k < 8; i++, k++)
|
||||
{
|
||||
SYSTICK_DelayUs(1);
|
||||
uint32_t reg2 = read_rows();
|
||||
i *= reg == reg2;
|
||||
reg = reg2;
|
||||
GPIO_ResetOutputPin(PIN_COL(j - 1));
|
||||
}
|
||||
|
||||
if (i < 3)
|
||||
break; // noise is too bad
|
||||
// Debounce: Read rows multiple times and look for stable reads
|
||||
// CRITICAL FIX #1: Proper debounce logic (replaces confusing i *= syntax)
|
||||
// CRITICAL FIX #2: Increased delay from 1µs to 10µs for real keyboard bounce capture
|
||||
// CRITICAL FIX #3: Clear match_count if reads differ (noise detection)
|
||||
reg = 0;
|
||||
for (unsigned int k = 0; k < 8; k++)
|
||||
{
|
||||
SYSTICK_DelayUs(10); // FIX #2: Increased from 1µs to 10µs
|
||||
uint32_t reg2 = read_rows();
|
||||
|
||||
// FIX #3: Clear match counter if values don't match
|
||||
if (reg2 != reg)
|
||||
{
|
||||
match_count = 0;
|
||||
reg = reg2;
|
||||
}
|
||||
else
|
||||
{
|
||||
match_count++;
|
||||
}
|
||||
|
||||
// Success: We have 3 consecutive matching reads = stable signal
|
||||
if (match_count >= 2)
|
||||
{
|
||||
break; // Debounce complete for this column
|
||||
}
|
||||
}
|
||||
|
||||
// FIX #1: Do NOT break on noise - continue scanning all columns
|
||||
// Only skip key detection if debounce failed, but GPIO state is cleaned
|
||||
if (match_count < 2)
|
||||
{
|
||||
// Debounce failed (too much noise), but continue to next column
|
||||
// This prevents GPIO from staying in invalid state and blocking other columns
|
||||
continue;
|
||||
}
|
||||
|
||||
// Debounce successful, check which row is pressed in this column
|
||||
for (unsigned int i = 0; i < 4; i++)
|
||||
{
|
||||
if (!(reg & PIN_MASK_ROW(i)))
|
||||
@@ -120,8 +268,29 @@ KEY_Code_t KEYBOARD_Poll(void)
|
||||
}
|
||||
|
||||
if (Key != KEY_INVALID)
|
||||
break;
|
||||
{
|
||||
break; // Found a valid key, stop scanning
|
||||
}
|
||||
}
|
||||
|
||||
// CRITICAL FIX #4: Always clean up GPIO state - set all columns high at end
|
||||
// This ensures GPIO pins are in a known state even if function exited early due to noise
|
||||
GPIO_SetOutputPin(PIN_COLS);
|
||||
|
||||
return Key;
|
||||
}
|
||||
|
||||
KEY_Code_t KEYBOARD_GetKey(void)
|
||||
{
|
||||
KEY_Code_t btn = KEYBOARD_Poll();
|
||||
if (btn == KEY_INVALID && GPIO_IsPttPressed())
|
||||
{
|
||||
btn = KEY_PTT;
|
||||
}
|
||||
return btn;
|
||||
}
|
||||
|
||||
void HideFKeyIcon(void) {
|
||||
gWasFKeyPressed = false;
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
+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
|
||||
+43
-14
@@ -25,6 +25,7 @@
|
||||
#include "driver/system.h"
|
||||
#include "driver/systick.h"
|
||||
#include "external/printf/printf.h"
|
||||
#include "misc.h"
|
||||
|
||||
// #define DEBUG
|
||||
|
||||
@@ -39,7 +40,7 @@
|
||||
|
||||
static uint32_t SectorCacheAddr = 0x1000000;
|
||||
static uint8_t SectorCache[SECTOR_SIZE];
|
||||
static uint8_t BlackHole[1];
|
||||
static uint8_t BlackHole[4] __attribute__((aligned(4)));
|
||||
static volatile bool TC_Flag;
|
||||
|
||||
static inline void CS_Assert()
|
||||
@@ -226,20 +227,20 @@ void PY25Q16_Init()
|
||||
|
||||
void PY25Q16_ReadBuffer(uint32_t Address, void *pBuffer, uint32_t Size)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("spi flash read: %06x %ld\n", Address, Size);
|
||||
#endif
|
||||
CS_Assert();
|
||||
|
||||
SPI_WriteByte(0x03); // Fast read
|
||||
WriteAddr(Address);
|
||||
SPI_WriteByte(0x03); // Send read command
|
||||
WriteAddr(Address); // Send address (3 bytes)
|
||||
|
||||
if (Size >= 16)
|
||||
// CRITICAL: Flush RX FIFO before DMA to remove residual data
|
||||
while (LL_SPI_RX_FIFO_EMPTY != LL_SPI_GetRxFIFOLevel(SPIx))
|
||||
{
|
||||
SPI_ReadBuf((uint8_t *)pBuffer, Size);
|
||||
LL_SPI_ReceiveData8(SPIx); // Read and discard
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (Size >= 16) {
|
||||
SPI_ReadBuf((uint8_t *)pBuffer, Size);
|
||||
} else {
|
||||
for (uint32_t i = 0; i < Size; i++)
|
||||
{
|
||||
((uint8_t *)(pBuffer))[i] = SPI_WriteByte(0xff);
|
||||
@@ -254,6 +255,11 @@ void PY25Q16_WriteBuffer(uint32_t Address, const void *pBuffer, uint32_t Size, b
|
||||
#ifdef DEBUG
|
||||
printf("spi flash write: %06x %ld %d\n", Address, Size, Append);
|
||||
#endif
|
||||
|
||||
//#ifdef ENABLE_FEAT_F4HWN_DEBUG
|
||||
// gDebug++;
|
||||
//#endif
|
||||
|
||||
uint32_t SecIndex = Address / SECTOR_SIZE;
|
||||
uint32_t SecAddr = SecIndex * SECTOR_SIZE;
|
||||
uint32_t SecOffset = Address % SECTOR_SIZE;
|
||||
@@ -261,6 +267,9 @@ void PY25Q16_WriteBuffer(uint32_t Address, const void *pBuffer, uint32_t Size, b
|
||||
|
||||
while (Size)
|
||||
{
|
||||
// CRITICAL FIX #1: Wait for flash ready before processing each sector
|
||||
WaitWIP();
|
||||
|
||||
if (Size < SecSize)
|
||||
{
|
||||
SecSize = Size;
|
||||
@@ -275,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;
|
||||
@@ -289,6 +302,10 @@ void PY25Q16_WriteBuffer(uint32_t Address, const void *pBuffer, uint32_t Size, b
|
||||
if (Erase)
|
||||
{
|
||||
SectorErase(SecAddr);
|
||||
|
||||
// CRITICAL FIX #2: Erase takes ~300ms, must complete before program starts
|
||||
WaitWIP();
|
||||
|
||||
if (Append)
|
||||
{
|
||||
SectorProgram(SecAddr, SectorCache, SecOffset + SecSize);
|
||||
@@ -313,6 +330,9 @@ void PY25Q16_WriteBuffer(uint32_t Address, const void *pBuffer, uint32_t Size, b
|
||||
SecOffset = 0;
|
||||
SecSize = SECTOR_SIZE;
|
||||
} // while
|
||||
|
||||
// CRITICAL FIX #3: Ensure all writes complete before function returns
|
||||
WaitWIP();
|
||||
}
|
||||
|
||||
void PY25Q16_SectorErase(uint32_t Address)
|
||||
@@ -454,11 +474,20 @@ void DMA1_Channel4_5_6_7_IRQHandler()
|
||||
LL_DMA_DisableIT_TC(DMA1, CHANNEL_RD);
|
||||
LL_DMA_ClearFlag_TC4(DMA1);
|
||||
|
||||
while (LL_SPI_TX_FIFO_EMPTY != LL_SPI_GetTxFIFOLevel(SPIx))
|
||||
// Wait a tiny bit for SPI to finish
|
||||
SYSTICK_DelayUs(10); // ← ADD THIS
|
||||
|
||||
uint32_t timeout = 10000;
|
||||
|
||||
while ((LL_SPI_TX_FIFO_EMPTY != LL_SPI_GetTxFIFOLevel(SPIx)) && timeout--)
|
||||
;
|
||||
while (LL_SPI_IsActiveFlag_BSY(SPIx))
|
||||
|
||||
timeout = 10000;
|
||||
while (LL_SPI_IsActiveFlag_BSY(SPIx) && timeout--)
|
||||
;
|
||||
while (LL_SPI_RX_FIFO_EMPTY != LL_SPI_GetRxFIFOLevel(SPIx))
|
||||
|
||||
timeout = 10000;
|
||||
while ((LL_SPI_RX_FIFO_EMPTY != LL_SPI_GetRxFIFOLevel(SPIx)) && timeout--)
|
||||
;
|
||||
|
||||
LL_SPI_DisableDMAReq_TX(SPIx);
|
||||
|
||||
+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
|
||||
|
||||
+24
-19
@@ -13,39 +13,44 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "py32f0xx.h"
|
||||
#include "systick.h"
|
||||
#include "misc.h"
|
||||
|
||||
// 0x20000324
|
||||
static uint32_t gTickMultiplier;
|
||||
|
||||
void SYSTICK_Init(void)
|
||||
{
|
||||
SysTick_Config(480000);
|
||||
gTickMultiplier = 48;
|
||||
SysTick_Config(SystemCoreClock / 100); // 10 ms (1/100 sec) systick interrupt
|
||||
gTickMultiplier = SystemCoreClock / 1000000;
|
||||
|
||||
NVIC_SetPriority(SysTick_IRQn, 0);
|
||||
}
|
||||
|
||||
void SYSTICK_DelayUs(uint32_t Delay)
|
||||
{
|
||||
const uint32_t ticks = Delay * gTickMultiplier;
|
||||
uint32_t elapsed_ticks = 0;
|
||||
uint32_t Start = SysTick->LOAD;
|
||||
uint32_t Previous = SysTick->VAL;
|
||||
do {
|
||||
uint32_t Current;
|
||||
|
||||
do {
|
||||
Current = SysTick->VAL;
|
||||
} while (Current == Previous);
|
||||
|
||||
uint32_t Delta = ((Current < Previous) ? - Current : Start - Current);
|
||||
|
||||
elapsed_ticks += Delta + Previous;
|
||||
|
||||
|
||||
// FIX: Simplified loop - removed inner "wait for change" loop
|
||||
while (elapsed_ticks < ticks)
|
||||
{
|
||||
uint32_t Current = SysTick->VAL;
|
||||
|
||||
// FIX: Corrected wraparound detection (was: "Current < Previous - 10")
|
||||
if (Current < Previous)
|
||||
{
|
||||
// Normal countdown case
|
||||
uint32_t Delta = Previous - Current;
|
||||
elapsed_ticks += Delta;
|
||||
}
|
||||
else if (Current > Previous)
|
||||
{
|
||||
// Wraparound case: SysTick went 0 → LOAD → Current
|
||||
uint32_t Delta = Previous + (Start - Current);
|
||||
elapsed_ticks += Delta;
|
||||
}
|
||||
|
||||
Previous = Current;
|
||||
} while (elapsed_ticks < ticks);
|
||||
}
|
||||
}
|
||||
}
|
||||
+52
-12
@@ -22,11 +22,19 @@
|
||||
#include "py32f071_ll_dma.h"
|
||||
#include "py32f071_ll_gpio.h"
|
||||
#include "py32f071_ll_usart.h"
|
||||
#include "driver/uart.h"
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
#include "driver/keyboard.h"
|
||||
#endif
|
||||
|
||||
#define USARTx USART1
|
||||
#define DMA_CHANNEL LL_DMA_CHANNEL_2
|
||||
|
||||
// CRITICAL FIX: Define UART TX timeout
|
||||
// If UART TX buffer doesn't clear within this many iterations, skip byte and continue
|
||||
// Prevents permanent freeze if UART is stuck
|
||||
#define UART_TX_TIMEOUT_ITERATIONS 10000
|
||||
|
||||
static bool UART_IsLogEnabled;
|
||||
uint8_t UART_DMA_Buffer[256];
|
||||
|
||||
@@ -111,9 +119,27 @@ void UART_Send(const void *pBuffer, uint32_t Size)
|
||||
|
||||
for (i = 0; i < Size; i++)
|
||||
{
|
||||
while (!LL_USART_IsActiveFlag_TXE(USARTx))
|
||||
;
|
||||
LL_USART_TransmitData8(USARTx, pData[i]);
|
||||
// CRITICAL FIX: Add timeout to UART TX busy-wait
|
||||
// Original: while (!LL_USART_IsActiveFlag_TXE(USARTx)) ;
|
||||
// Problem: If UART TX stuck or disconnected, this is infinite loop
|
||||
// Result: Radio freeze for 100ms+ while trying to send one character
|
||||
//
|
||||
// Solution: Add iteration counter timeout
|
||||
// If TXE flag doesn't set within timeout iterations, skip byte and continue
|
||||
// This caps maximum freeze at ~10ms per UART_Send() call
|
||||
|
||||
uint32_t timeout = UART_TX_TIMEOUT_ITERATIONS;
|
||||
while (!LL_USART_IsActiveFlag_TXE(USARTx) && timeout > 0)
|
||||
{
|
||||
timeout--;
|
||||
}
|
||||
|
||||
// Send byte only if TXE flag is set
|
||||
// If timeout occurred, skip this byte and continue
|
||||
if (timeout > 0)
|
||||
{
|
||||
LL_USART_TransmitData8(USARTx, pData[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,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
|
||||
#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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -37,3 +41,58 @@ void VCP_Init()
|
||||
NVIC_SetPriority(USBD_IRQn, 3);
|
||||
NVIC_EnableIRQ(USBD_IRQn);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
bool VCP_K5ViewerPing(void)
|
||||
{
|
||||
// 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
|
||||
|
||||
static uint8_t read_ptr = 0;
|
||||
static ParseState_t state = STATE_IDLE;
|
||||
|
||||
bool connected = false;
|
||||
uint8_t write_ptr = VCP_RxBufPointer; // snapshot once — ISR may update concurrently
|
||||
|
||||
// Cap bytes processed per call to VCP_RX_BUF_SIZE.
|
||||
// Prevents unbounded loop if the ISR write pointer laps read_ptr
|
||||
// (buffer overflow / corrupted state), which would freeze the firmware.
|
||||
uint32_t processed = 0;
|
||||
|
||||
while (read_ptr != write_ptr && processed < VCP_RX_BUF_SIZE)
|
||||
{
|
||||
uint8_t b = VCP_RxBuf[read_ptr];
|
||||
|
||||
read_ptr++;
|
||||
if (read_ptr >= VCP_RX_BUF_SIZE)
|
||||
read_ptr = 0;
|
||||
processed++;
|
||||
|
||||
if(KEYBOARD_ProcessProtocolByte(&state, b))
|
||||
connected = true;
|
||||
}
|
||||
|
||||
return connected;
|
||||
}
|
||||
#endif // ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include "usb_config.h"
|
||||
|
||||
#define VCP_RX_BUF_SIZE 256
|
||||
@@ -28,6 +29,10 @@ extern volatile uint32_t VCP_RxBufPointer;
|
||||
|
||||
void VCP_Init();
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
bool VCP_K5ViewerPing(void);
|
||||
#endif
|
||||
|
||||
static inline void VCP_Send(const uint8_t *Buf, uint32_t Size)
|
||||
{
|
||||
cdc_acm_data_send_with_dtr(Buf, Size);
|
||||
|
||||
+4
-4
@@ -97,7 +97,7 @@ void VOICE_Start()
|
||||
if (gVoiceBufLen > 0)
|
||||
{
|
||||
memcpy(DAC_Buf, gVoiceBuf[gVoiceBufReadIndex], VOICE_BUF_SIZE);
|
||||
VOCIE_BUF_ForwardReadIndex();
|
||||
VOICE_BUF_ForwardReadIndex();
|
||||
gVoiceBufLen--;
|
||||
}
|
||||
else
|
||||
@@ -110,7 +110,7 @@ void VOICE_Start()
|
||||
gVoiceBuf[gVoiceBufReadIndex], //
|
||||
VOICE_BUF_SIZE //
|
||||
);
|
||||
VOCIE_BUF_ForwardReadIndex();
|
||||
VOICE_BUF_ForwardReadIndex();
|
||||
gVoiceBufLen--;
|
||||
}
|
||||
else
|
||||
@@ -141,7 +141,7 @@ void DMA1_Channel2_3_IRQHandler()
|
||||
if (gVoiceBufLen > 0)
|
||||
{
|
||||
memcpy(DAC_Buf, gVoiceBuf[gVoiceBufReadIndex], VOICE_BUF_SIZE);
|
||||
VOCIE_BUF_ForwardReadIndex();
|
||||
VOICE_BUF_ForwardReadIndex();
|
||||
gVoiceBufLen--;
|
||||
}
|
||||
else
|
||||
@@ -158,7 +158,7 @@ void DMA1_Channel2_3_IRQHandler()
|
||||
gVoiceBuf[gVoiceBufReadIndex], //
|
||||
VOICE_BUF_SIZE //
|
||||
);
|
||||
VOCIE_BUF_ForwardReadIndex();
|
||||
VOICE_BUF_ForwardReadIndex();
|
||||
gVoiceBufLen--;
|
||||
}
|
||||
else
|
||||
|
||||
+2
-2
@@ -26,12 +26,12 @@ extern uint8_t gVoiceBufReadIndex;
|
||||
extern uint8_t gVoiceBufWriteIndex;
|
||||
extern uint8_t gVoiceBufLen;
|
||||
|
||||
static inline void VOCIE_BUF_ForwardReadIndex()
|
||||
static inline void VOICE_BUF_ForwardReadIndex()
|
||||
{
|
||||
gVoiceBufReadIndex = (1 + gVoiceBufReadIndex) % VOICE_BUF_CAP;
|
||||
}
|
||||
|
||||
static inline void VOCIE_BUF_ForwardWriteIndex()
|
||||
static inline void VOICE_BUF_ForwardWriteIndex()
|
||||
{
|
||||
gVoiceBufWriteIndex = (1 + gVoiceBufWriteIndex) % VOICE_BUF_CAP;
|
||||
}
|
||||
|
||||
+2
-2
@@ -362,7 +362,7 @@ const uint8_t gFontSmall[95-1][6] =
|
||||
{0x20, 0x40, 0x40, 0x3D, 0x00, 0x00}, // 'j'
|
||||
{0x00, 0x7F, 0x10, 0x28, 0x44, 0x00}, // 'k'
|
||||
{0x00, 0x00, 0x3F, 0x40, 0x00, 0x00}, // 'l'
|
||||
{0x7C, 0x08, 0x10, 0x10, 0x08, 0x7C}, // 'm'
|
||||
{0x7C, 0x04, 0x7C, 0x04, 0x78, 0x00}, // 'm'
|
||||
{0x7C, 0x04, 0x04, 0x04, 0x78, 0x00}, // 'n'
|
||||
{0x38, 0x44, 0x44, 0x44, 0x38, 0x00}, // 'o'
|
||||
{0x7C, 0x14, 0x14, 0x14, 0x08, 0x00}, // 'p'
|
||||
@@ -491,7 +491,7 @@ const uint8_t gFontSmall[95-1][6] =
|
||||
{0x03, 0x00, 0x03}, // 34 - quotedbl
|
||||
{0x1f, 0x0a, 0x1f}, // 35 - numbersign
|
||||
{0x0a, 0x1f, 0x05}, // 36 - dollar
|
||||
{0x09, 0x04, 0x12}, // 37 - percent
|
||||
{0x19, 0x04, 0x13}, // 37 - percent
|
||||
{0x0f, 0x17, 0x1c}, // 38 - ampersand
|
||||
{0x00, 0x03, 0x00}, // 39 - quotesingle
|
||||
{0x00, 0x0e, 0x11}, // 40 - parenleft
|
||||
|
||||
+5
-8
@@ -163,11 +163,8 @@ int32_t TX_freq_check(const uint32_t Frequency)
|
||||
{ // return '0' if TX frequency is allowed
|
||||
// otherwise return '-1'
|
||||
|
||||
if (Frequency < frequencyBandTable[0].lower || Frequency > frequencyBandTable[BAND_N_ELEM - 1].upper)
|
||||
return -1; // not allowed outside this range
|
||||
|
||||
if (Frequency >= BX4819_band1.upper && Frequency < BX4819_band2.lower)
|
||||
return -1; // BX chip does not work in this range
|
||||
if (RX_freq_check(Frequency))
|
||||
return -1;
|
||||
|
||||
switch (gSetting_F_LOCK)
|
||||
{
|
||||
@@ -280,10 +277,10 @@ int32_t RX_freq_check(const uint32_t Frequency)
|
||||
// otherwise return '-1'
|
||||
|
||||
if (Frequency < frequencyBandTable[0].lower || Frequency > frequencyBandTable[BAND_N_ELEM - 1].upper)
|
||||
return -1;
|
||||
return -1; // not allowed outside this range
|
||||
|
||||
if (Frequency >= BX4819_band1.upper && Frequency < BX4819_band2.lower)
|
||||
return -1;
|
||||
return -1; // BX chip does not work in this range
|
||||
|
||||
return 0; // OK frequency
|
||||
return 0; // OK frequency
|
||||
}
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#define _1GHz_in_KHz 100000000
|
||||
#define DEFAULT_FREQ 43450000 // Use for Reset and Aircopy
|
||||
|
||||
typedef struct {
|
||||
const uint32_t lower;
|
||||
|
||||
+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
|
||||
|
||||
+10
-8
@@ -61,8 +61,10 @@ BOOT_Mode_t BOOT_GetMode(void)
|
||||
return BOOT_MODE_F_LOCK;
|
||||
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
if (Keys[0] == KEY_SIDE2)
|
||||
if (Keys[0] == KEY_SIDE2) {
|
||||
gAirCopyBootMode = true;
|
||||
return BOOT_MODE_AIRCOPY;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -71,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
|
||||
@@ -95,7 +99,7 @@ void BOOT_ProcessMode(BOOT_Mode_t Mode)
|
||||
gEeprom.KEY_2_LONG_PRESS_ACTION = ACTION_OPT_NONE;
|
||||
gEeprom.KEY_M_LONG_PRESS_ACTION = ACTION_OPT_NONE;
|
||||
|
||||
RADIO_InitInfo(gRxVfo, FREQ_CHANNEL_LAST - 1, 43400000); // LPD
|
||||
RADIO_InitInfo(gRxVfo, FREQ_CHANNEL_LAST - 1, DEFAULT_FREQ); // LPD
|
||||
|
||||
gRxVfo->CHANNEL_BANDWIDTH = BANDWIDTH_NARROW;
|
||||
gRxVfo->OUTPUT_POWER = OUTPUT_POWER_LOW1;
|
||||
@@ -117,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
|
||||
+42
-84
@@ -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,30 +164,17 @@ void Main(void)
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
gEeprom.KEY_LOCK = 0;
|
||||
SETTINGS_SaveSettings();
|
||||
#ifndef ENABLE_VOX
|
||||
gMenuCursor = 67; // move to hidden section, fix me if change... !!! Remove VOX and Mic Bar
|
||||
#else
|
||||
gMenuCursor = 68; // move to hidden section, fix me if change... !!!
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NOAA
|
||||
gMenuCursor += 1; // move to hidden section, fix me if change... !!!
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
gMenuCursor += 1; // move to hidden section, fix me if change... !!!
|
||||
#ifdef ENABLE_FEAT_F4HWN_MENU_CAT
|
||||
gMenuLevel = MENU_LEVEL_ITEMS;
|
||||
gMenuCategory = CAT_ALL;
|
||||
#endif
|
||||
gMenuCursor = UI_MENU_GetMenuIdx(FIRST_HIDDEN_MENU_ITEM);
|
||||
gSubMenuSelection = gSetting_F_LOCK;
|
||||
#endif
|
||||
}
|
||||
|
||||
// count the number of menu items
|
||||
gMenuListCount = 0;
|
||||
while (MenuList[gMenuListCount].name[0] != '\0') {
|
||||
if(!gF_LOCK && MenuList[gMenuListCount].menu_id == FIRST_HIDDEN_MENU_ITEM)
|
||||
break;
|
||||
|
||||
gMenuListCount++;
|
||||
}
|
||||
// build the current menu view (Etape 1: vue = All, identite)
|
||||
UI_MENU_BuildView();
|
||||
|
||||
// wait for user to release all butts before moving on
|
||||
if (GPIO_IsPttPressed() ||
|
||||
@@ -256,13 +250,16 @@ 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;
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
{
|
||||
uint8_t Channel;
|
||||
uint16_t Channel;
|
||||
|
||||
AUDIO_SetVoiceID(0, VOICE_ID_WELCOME);
|
||||
|
||||
@@ -284,77 +281,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) {
|
||||
|
||||
+320
-10
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "misc.h"
|
||||
#include "settings.h"
|
||||
#include "driver/py25q16.h"
|
||||
|
||||
const uint8_t fm_radio_countdown_500ms = 2000 / 500; // 2 seconds
|
||||
const uint16_t fm_play_countdown_scan_10ms = 100 / 10; // 100ms
|
||||
@@ -86,7 +87,7 @@ const uint16_t NOAA_countdown_3_10ms = 200 / 10; // 200ms
|
||||
|
||||
const uint32_t gDefaultAesKey[4] = {0x4AA5CC60, 0x0312CC5F, 0xFFD2DABB, 0x6BBA7F92};
|
||||
|
||||
const uint8_t gMicGain_dB2[5] = {3, 8, 16, 24, 31};
|
||||
const uint8_t gMicGain_dB2[9] = {3, 8, 16, 24, 32, 40, 48, 56, 63}; // BK4819 {3, 8, 16, 24, 31};
|
||||
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
bool gSetting_350TX;
|
||||
@@ -115,6 +116,10 @@ enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
|
||||
bool gWakeUp = false;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
bool gSetting_set_scn = 1;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
uint8_t gSetting_set_pwr = 1;
|
||||
bool gSetting_set_ptt = 0;
|
||||
@@ -122,16 +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_fm = 0;
|
||||
uint8_t gSetting_set_audio_am = 0;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
bool gSetting_set_nfm = 0;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
uint8_t gSetting_set_sav = SET_SAV_OFF;
|
||||
#endif
|
||||
bool gSetting_set_tmr = 0;
|
||||
bool gSetting_set_ptt_session;
|
||||
#ifdef ENABLE_FEAT_F4HWN_DEBUG
|
||||
uint8_t gDebug;
|
||||
int16_t gDebug;
|
||||
#endif
|
||||
uint8_t gDW = 0;
|
||||
uint8_t gCB = 0;
|
||||
@@ -139,10 +151,13 @@ enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
|
||||
uint8_t crc[15] = { 0 };
|
||||
uint8_t lErrorsDuringAirCopy = 0;
|
||||
uint8_t gAircopyStep = 0;
|
||||
uint8_t gAircopyCurrentMapIndex = 0;
|
||||
bool gAirCopyBootMode = 0;
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
bool gPowerHigh = false;
|
||||
bool gRemoveOffset = false;
|
||||
#endif
|
||||
int8_t dBmCorrTable[7] = {-15, -16, -10, -4, -7, -6, -1};
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_AUDIO_BAR
|
||||
@@ -163,8 +178,13 @@ uint16_t gEEPROM_RSSI_CALIB[7][4];
|
||||
uint16_t gEEPROM_1F8A;
|
||||
uint16_t gEEPROM_1F8C;
|
||||
|
||||
ChannelAttributes_t gMR_ChannelAttributes[FREQ_CHANNEL_LAST + 1];
|
||||
bool gMR_ChannelExclude[FREQ_CHANNEL_LAST + 1];
|
||||
//
|
||||
// Cache-Based Architecture: channel attributes moved to Flash
|
||||
// Cache holds only active channels in RAM (instead of all!)
|
||||
//
|
||||
|
||||
MR_ChannelCache_t gMR_ChannelAttributes_Cache[MR_CHANNELS_CACHE_SIZE] = {0};
|
||||
ChannelAttributes_t gMR_ChannelAttributes_Current = {0};
|
||||
|
||||
volatile uint16_t gBatterySaveCountdown_10ms = battery_save_count_10ms;
|
||||
|
||||
@@ -190,8 +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
|
||||
#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
|
||||
|
||||
@@ -229,12 +250,13 @@ volatile uint16_t gScanPauseDelayIn_10ms;
|
||||
uint16_t gMenuCountdown;
|
||||
bool gPttWasReleased;
|
||||
bool gPttWasPressed;
|
||||
bool gHasVfoBackup;
|
||||
uint8_t gKeypadLocked;
|
||||
bool gFlagReconfigureVfos;
|
||||
uint8_t gVfoConfigureMode;
|
||||
bool gFlagResetVfos;
|
||||
bool gRequestSaveVFO;
|
||||
uint8_t gRequestSaveChannel;
|
||||
uint16_t gRequestSaveChannel;
|
||||
bool gRequestSaveSettings;
|
||||
#ifdef ENABLE_FMRADIO
|
||||
bool gRequestSaveFM;
|
||||
@@ -262,7 +284,7 @@ bool g_SquelchLost;
|
||||
volatile uint16_t gFlashLightBlinkCounter;
|
||||
|
||||
bool gFlagEndTransmission;
|
||||
uint8_t gNextMrChannel;
|
||||
uint16_t gNextMrChannel;
|
||||
ReceptionMode_t gRxReceptionMode;
|
||||
|
||||
bool gRxVfoIsActive;
|
||||
@@ -280,7 +302,7 @@ uint8_t gFSKWriteIndex;
|
||||
|
||||
#ifdef ENABLE_NOAA
|
||||
bool gIsNoaaMode;
|
||||
uint8_t gNoaaChannel;
|
||||
uint8_t gNoaaChannel;
|
||||
#endif
|
||||
|
||||
bool gUpdateDisplay;
|
||||
@@ -316,8 +338,14 @@ uint8_t gIsLocked = 0xFF;
|
||||
bool gMute = false;
|
||||
uint8_t gBacklightTimeOriginal;
|
||||
uint8_t gBacklightBrightnessOld;
|
||||
uint8_t gSquelchLevelOriginal = 10;
|
||||
uint8_t gPttOnePushCounter = 0;
|
||||
uint32_t gBlinkCounter = 0;
|
||||
|
||||
uint16_t gVfoSaveCountdown_10ms = 0;
|
||||
bool gScheduleVfoSave = false;
|
||||
bool gVfoStateChanged = false;
|
||||
char gListName[MR_CHANNELS_LIST][4];
|
||||
#endif
|
||||
|
||||
inline void FUNCTION_NOP() { ; }
|
||||
@@ -347,3 +375,285 @@ unsigned long StrToUL(const char * str)
|
||||
}
|
||||
return ul;
|
||||
}
|
||||
|
||||
//
|
||||
// Cache-Based Channel Attributes Implementation
|
||||
//
|
||||
//
|
||||
// This replaces the huge array (~ 2,000 bytes in RAM) with a smart cache system
|
||||
// that keeps only active channels in RAM and loads others from Flash on demand.
|
||||
//
|
||||
// SRAM Savings: ~ 2,000 bytes (84% reduction!)
|
||||
//
|
||||
|
||||
// Flash address where channel attributes start
|
||||
// NOTE: Verify this matches your Flash layout!
|
||||
|
||||
#define FLASH_CHANNEL_ATTR_BASE 0x8000
|
||||
|
||||
// Each channel takes 2 bytes (ChannelAttributes_t is uint16_t)
|
||||
#define FLASH_CHANNEL_ATTR_SIZE 2
|
||||
|
||||
// Cache hit/miss statistics (optional, for debugging)
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_DEBUG
|
||||
static uint32_t cache_hits = 0;
|
||||
static uint32_t cache_misses = 0;
|
||||
#endif
|
||||
|
||||
//
|
||||
// Internal Helper Functions
|
||||
//
|
||||
|
||||
// Find cache entry for given channel
|
||||
// Returns index if found, -1 if not found
|
||||
|
||||
static int MR_FindInCache(uint16_t channel_id)
|
||||
{
|
||||
for (int i = 0; i < MR_CHANNELS_CACHE_SIZE; i++) {
|
||||
if (gMR_ChannelAttributes_Cache[i].channel_id == channel_id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Find oldest entry in cache (for eviction - LRU)
|
||||
static int MR_FindOldestCacheEntry(void)
|
||||
{
|
||||
int oldest_index = 0;
|
||||
uint32_t oldest_time = gMR_ChannelAttributes_Cache[0].access_time;
|
||||
|
||||
for (int i = 1; i < MR_CHANNELS_CACHE_SIZE; i++) {
|
||||
if (gMR_ChannelAttributes_Cache[i].access_time < oldest_time) {
|
||||
oldest_time = gMR_ChannelAttributes_Cache[i].access_time;
|
||||
oldest_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
return oldest_index;
|
||||
}
|
||||
|
||||
// Find empty cache slot
|
||||
// Returns index if found, -1 if cache is full
|
||||
static int MR_FindEmptyCacheSlot(void)
|
||||
{
|
||||
for (int i = 0; i < MR_CHANNELS_CACHE_SIZE; i++) {
|
||||
if (gMR_ChannelAttributes_Cache[i].channel_id == 0xFFFF) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1; // Cache is full, need to evict
|
||||
}
|
||||
|
||||
// Get current time (for LRU eviction)
|
||||
static uint32_t GetCurrentTime(void)
|
||||
{
|
||||
// Using gBlinkCounter which increments continuously
|
||||
extern uint32_t gBlinkCounter;
|
||||
return gBlinkCounter;
|
||||
}
|
||||
|
||||
//
|
||||
// Public API Functions
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
// Load channel attributes from Flash
|
||||
void MR_LoadChannelAttributesFromFlash(uint16_t channel_id, ChannelAttributes_t* attributes)
|
||||
{
|
||||
// CRITICAL: Validate channel_id
|
||||
if (channel_id >= (MR_CHANNELS_MAX + 7)) {
|
||||
attributes->__val = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate Flash address
|
||||
uint32_t flash_addr = FLASH_CHANNEL_ATTR_BASE + (channel_id * sizeof(ChannelAttributes_t));
|
||||
|
||||
// Read 2 bytes from Flash
|
||||
PY25Q16_ReadBuffer(flash_addr, attributes, sizeof(ChannelAttributes_t));
|
||||
}
|
||||
|
||||
// Save channel attributes to Flash
|
||||
void MR_SaveChannelAttributesToFlash(uint16_t channel_id, const ChannelAttributes_t* attributes)
|
||||
{
|
||||
// CRITICAL: Validate channel_id
|
||||
if (channel_id >= (MR_CHANNELS_MAX + 7)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate Flash address
|
||||
uint16_t flash_addr = FLASH_CHANNEL_ATTR_BASE + (channel_id * FLASH_CHANNEL_ATTR_SIZE);
|
||||
|
||||
// Write 2 bytes to Flash
|
||||
PY25Q16_WriteBuffer(flash_addr, attributes, sizeof(ChannelAttributes_t), false);
|
||||
}
|
||||
|
||||
// Get channel attributes (from cache or Flash)
|
||||
// This is the main function used by the rest of the code
|
||||
ChannelAttributes_t* MR_GetChannelAttributes(uint16_t channel_id)
|
||||
{
|
||||
// Input validation
|
||||
if (channel_id >= (MR_CHANNELS_MAX + 7)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Check cache first (FAST PATH)
|
||||
int cache_index = MR_FindInCache(channel_id);
|
||||
|
||||
if (cache_index >= 0) {
|
||||
// CACHE HIT
|
||||
#ifdef ENABLE_FEAT_F4HWN_DEBUG
|
||||
cache_hits++;
|
||||
#endif
|
||||
|
||||
// Update access time for LRU
|
||||
gMR_ChannelAttributes_Cache[cache_index].access_time = GetCurrentTime();
|
||||
|
||||
return &gMR_ChannelAttributes_Cache[cache_index].attributes;
|
||||
}
|
||||
|
||||
// CACHE MISS - Load from Flash
|
||||
#ifdef ENABLE_FEAT_F4HWN_DEBUG
|
||||
cache_misses++;
|
||||
#endif
|
||||
|
||||
// Find slot for new entry
|
||||
int slot = MR_FindEmptyCacheSlot();
|
||||
|
||||
if (slot < 0) {
|
||||
// Cache is full, evict oldest entry
|
||||
slot = MR_FindOldestCacheEntry();
|
||||
}
|
||||
|
||||
// Load from Flash into cache slot
|
||||
MR_LoadChannelAttributesFromFlash(channel_id, &gMR_ChannelAttributes_Cache[slot].attributes);
|
||||
|
||||
// Store channel_id in cache
|
||||
gMR_ChannelAttributes_Cache[slot].channel_id = channel_id;
|
||||
|
||||
// Set access time
|
||||
gMR_ChannelAttributes_Cache[slot].access_time = GetCurrentTime();
|
||||
|
||||
return &gMR_ChannelAttributes_Cache[slot].attributes;
|
||||
}
|
||||
|
||||
// Set channel attributes (updates both cache and Flash)
|
||||
void MR_SetChannelAttributes(uint16_t channel_id, const ChannelAttributes_t* attributes)
|
||||
{
|
||||
// Input validation
|
||||
if (channel_id >= (MR_CHANNELS_MAX + 7) || !attributes) {
|
||||
return;
|
||||
}
|
||||
|
||||
// CRITICAL FIX: WRITE-PROTECT - Prevent Flash wear
|
||||
// Before writing, check if data already exists and is identical
|
||||
ChannelAttributes_t flash_version;
|
||||
MR_LoadChannelAttributesFromFlash(channel_id, &flash_version);
|
||||
|
||||
// Compare current Flash data with new data
|
||||
if (memcmp(&flash_version, attributes, sizeof(ChannelAttributes_t)) == 0) {
|
||||
// But still update cache for consistency
|
||||
int cache_index = MR_FindInCache(channel_id);
|
||||
if (cache_index >= 0) {
|
||||
gMR_ChannelAttributes_Cache[cache_index].attributes = *attributes;
|
||||
gMR_ChannelAttributes_Cache[cache_index].access_time = GetCurrentTime();
|
||||
}
|
||||
return; // Early exit - no Flash write needed
|
||||
}
|
||||
|
||||
// Data has changed - write to Flash
|
||||
MR_SaveChannelAttributesToFlash(channel_id, attributes);
|
||||
|
||||
// Update cache if entry exists
|
||||
int cache_index = MR_FindInCache(channel_id);
|
||||
|
||||
if (cache_index >= 0) {
|
||||
// Entry in cache, update it
|
||||
gMR_ChannelAttributes_Cache[cache_index].attributes = *attributes;
|
||||
gMR_ChannelAttributes_Cache[cache_index].access_time = GetCurrentTime();
|
||||
} else {
|
||||
// Not in cache, add it
|
||||
int slot = MR_FindEmptyCacheSlot();
|
||||
|
||||
if (slot < 0) {
|
||||
// Cache full, evict oldest
|
||||
slot = MR_FindOldestCacheEntry();
|
||||
}
|
||||
|
||||
gMR_ChannelAttributes_Cache[slot].channel_id = channel_id;
|
||||
gMR_ChannelAttributes_Cache[slot].attributes = *attributes;
|
||||
gMR_ChannelAttributes_Cache[slot].access_time = GetCurrentTime();
|
||||
}
|
||||
}
|
||||
|
||||
// Invalidate entire cache (call after loading Flash backup)
|
||||
void MR_InvalidateChannelAttributesCache(void)
|
||||
{
|
||||
for (int i = 0; i < MR_CHANNELS_CACHE_SIZE; i++) {
|
||||
gMR_ChannelAttributes_Cache[i].channel_id = 0xFFFF; // Mark as empty
|
||||
gMR_ChannelAttributes_Cache[i].access_time = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize cache (call from settings.c boot sequence)
|
||||
void MR_InitChannelAttributesCache(void)
|
||||
{
|
||||
// Clear cache
|
||||
MR_InvalidateChannelAttributesCache();
|
||||
|
||||
// Pre-load commonly used channels (VFO A, VFO B, channel 0)
|
||||
// This speeds up first access
|
||||
uint16_t channels_to_preload[] = {0, 1, 2};
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(channels_to_preload); i++) {
|
||||
if (channels_to_preload[i] < (MR_CHANNELS_MAX + 7)) {
|
||||
MR_GetChannelAttributes(channels_to_preload[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Debugging / Statistics (optional, for development)
|
||||
//
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_DEBUG
|
||||
|
||||
uint32_t MR_GetCacheHits(void)
|
||||
{
|
||||
return cache_hits;
|
||||
}
|
||||
|
||||
uint32_t MR_GetCacheMisses(void)
|
||||
{
|
||||
return cache_misses;
|
||||
}
|
||||
|
||||
float MR_GetCacheHitRate(void)
|
||||
{
|
||||
uint32_t total = cache_hits + cache_misses;
|
||||
if (total == 0) return 0.0f;
|
||||
return (float)cache_hits / (float)total * 100.0f;
|
||||
}
|
||||
|
||||
void MR_PrintCacheStats(void)
|
||||
{
|
||||
// This would print cache statistics (requires UART_Printf)
|
||||
// Uncomment if you want debug output:
|
||||
// UART_Printf("Cache Stats: Hits=%u Misses=%u Rate=%.1f%%\n",
|
||||
// cache_hits, cache_misses, MR_GetCacheHitRate());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_K5VIEWER
|
||||
bool K5VIEWER_IsLocked(void)
|
||||
{
|
||||
if (gUART_LockK5Viewer > 0) {
|
||||
gUART_LockK5Viewer--;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
+125
-21
@@ -36,18 +36,26 @@
|
||||
#define SWAP(a, b) ({ __typeof__ (a) _c = (a); a = b; b = _c; })
|
||||
#endif
|
||||
|
||||
#define IS_MR_CHANNEL(x) ((x) <= MR_CHANNEL_LAST)
|
||||
#define FM_CHANNELS_MAX 48
|
||||
#define MR_CHANNELS_MAX 1024
|
||||
#define MR_CHANNELS_LIST 24
|
||||
// CACHE-BASED OPTIMIZATION: Only keep active channels in RAM
|
||||
// Full array stays in EEPROM, cache holds ~10 most-used channels
|
||||
#define MR_CHANNELS_CACHE_SIZE 10
|
||||
|
||||
|
||||
#define IS_MR_CHANNEL(x) ((x) >= MR_CHANNEL_FIRST && (x) <= MR_CHANNEL_LAST)
|
||||
#define IS_FREQ_CHANNEL(x) ((x) >= FREQ_CHANNEL_FIRST && (x) <= FREQ_CHANNEL_LAST)
|
||||
#define IS_VALID_CHANNEL(x) ((x) < LAST_CHANNEL)
|
||||
#define IS_NOAA_CHANNEL(x) ((x) >= NOAA_CHANNEL_FIRST && (x) <= NOAA_CHANNEL_LAST)
|
||||
|
||||
enum {
|
||||
MR_CHANNEL_FIRST = 0,
|
||||
MR_CHANNEL_LAST = 199u,
|
||||
FREQ_CHANNEL_FIRST = 200u,
|
||||
FREQ_CHANNEL_LAST = 206u,
|
||||
NOAA_CHANNEL_FIRST = 207u,
|
||||
NOAA_CHANNEL_LAST = 216u,
|
||||
MR_CHANNEL_LAST = MR_CHANNELS_MAX - 1,
|
||||
FREQ_CHANNEL_FIRST = MR_CHANNELS_MAX,
|
||||
FREQ_CHANNEL_LAST = MR_CHANNELS_MAX + 6,
|
||||
NOAA_CHANNEL_FIRST = MR_CHANNELS_MAX + 7,
|
||||
NOAA_CHANNEL_LAST = MR_CHANNELS_MAX + 16,
|
||||
LAST_CHANNEL
|
||||
};
|
||||
|
||||
@@ -138,7 +146,7 @@ extern const uint16_t scan_pause_delay_in_7_10ms;
|
||||
//extern const uint16_t gMax_bat_v;
|
||||
//extern const uint16_t gMin_bat_v;
|
||||
|
||||
extern const uint8_t gMicGain_dB2[5];
|
||||
extern const uint8_t gMicGain_dB2[9];
|
||||
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
extern bool gSetting_350TX;
|
||||
@@ -168,23 +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_fm;
|
||||
extern uint8_t gSetting_set_audio_am;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
extern bool gSetting_set_nfm;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV
|
||||
extern uint8_t gSetting_set_sav;
|
||||
#endif
|
||||
extern bool gSetting_set_tmr;
|
||||
extern bool gSetting_set_ptt_session;
|
||||
#ifdef ENABLE_FEAT_F4HWN_DEBUG
|
||||
extern uint8_t gDebug;
|
||||
extern int16_t gDebug;
|
||||
#endif
|
||||
extern uint8_t gDW;
|
||||
extern uint8_t gCB;
|
||||
@@ -192,10 +234,13 @@ extern enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
|
||||
extern uint8_t crc[15];
|
||||
extern uint8_t lErrorsDuringAirCopy;
|
||||
extern uint8_t gAircopyStep;
|
||||
extern uint8_t gAircopyCurrentMapIndex;
|
||||
extern bool gAirCopyBootMode;
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
extern bool gPowerHigh;
|
||||
extern bool gRemoveOffset;
|
||||
#endif
|
||||
extern int8_t dBmCorrTable[7];
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_AUDIO_BAR
|
||||
@@ -219,18 +264,66 @@ extern uint16_t gEEPROM_1F8C;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t
|
||||
band : 3,
|
||||
uint16_t
|
||||
band : 3,
|
||||
compander : 2,
|
||||
scanlist1 : 1,
|
||||
scanlist2 : 1,
|
||||
scanlist3 : 1;
|
||||
unused_1 : 1,
|
||||
unused_2 : 1,
|
||||
exclude : 1,
|
||||
scanlist : 8;
|
||||
};
|
||||
uint8_t __val;
|
||||
uint16_t __val;
|
||||
} ChannelAttributes_t;
|
||||
|
||||
extern ChannelAttributes_t gMR_ChannelAttributes[207];
|
||||
extern bool gMR_ChannelExclude[207];
|
||||
//
|
||||
// Cache-Based Architecture
|
||||
//
|
||||
//
|
||||
// Instead of keeping all 1038 channel attributes in RAM (~ 2,000 bytes),
|
||||
// we now keep only the active ones in a small cache (40 bytes).
|
||||
//
|
||||
// The full array remains in Flash and is loaded on-demand.
|
||||
//
|
||||
// SRAM Savings: ~ 2,000 bytes (84% reduction!)
|
||||
//
|
||||
|
||||
// Cache entry structure
|
||||
typedef struct {
|
||||
uint16_t channel_id; // Which channel this is
|
||||
ChannelAttributes_t attributes; // The actual attributes
|
||||
uint32_t access_time; // For LRU eviction (optional)
|
||||
} MR_ChannelCache_t;
|
||||
|
||||
// The cache (small, stays in RAM)
|
||||
extern MR_ChannelCache_t gMR_ChannelAttributes_Cache[MR_CHANNELS_CACHE_SIZE];
|
||||
|
||||
// REMOVED: extern ChannelAttributes_t gMR_ChannelAttributes[MR_CHANNELS_MAX + 7];
|
||||
// This now stays in Flash, not in RAM
|
||||
|
||||
//
|
||||
// Cache Access Functions (See misc.c for implementation)
|
||||
//
|
||||
|
||||
// Get channel attributes (from cache or Flash)
|
||||
// Returns pointer to attributes, loads from Flash if not in cache
|
||||
|
||||
void MR_InitChannelAttributesCache(void);
|
||||
|
||||
ChannelAttributes_t* MR_GetChannelAttributes(uint16_t channel_id);
|
||||
|
||||
// Set channel attributes (updates both cache and Flasf)
|
||||
void MR_SetChannelAttributes(uint16_t channel_id, const ChannelAttributes_t* attributes);
|
||||
|
||||
// Invalidate cache (on Flash clear)
|
||||
void MR_InvalidateChannelAttributesCache(void);
|
||||
|
||||
// Load channel attributes from Flash directly (internal use)
|
||||
void MR_LoadChannelAttributesFromFlash(uint16_t channel_id, ChannelAttributes_t* attributes);
|
||||
|
||||
// Save channel attributes to Flash directly (internal use)
|
||||
void MR_SaveChannelAttributesToFlash(uint16_t channel_id, const ChannelAttributes_t* attributes);
|
||||
|
||||
extern ChannelAttributes_t gMR_ChannelAttributes_Current; // Current VFO attributes (for speed)
|
||||
|
||||
extern volatile uint16_t gBatterySaveCountdown_10ms;
|
||||
|
||||
@@ -256,8 +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
|
||||
#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
|
||||
|
||||
@@ -301,11 +397,12 @@ extern AlarmState_t gAlarmState;
|
||||
extern uint16_t gMenuCountdown;
|
||||
extern bool gPttWasReleased;
|
||||
extern bool gPttWasPressed;
|
||||
extern bool gHasVfoBackup;
|
||||
extern bool gFlagReconfigureVfos;
|
||||
extern uint8_t gVfoConfigureMode;
|
||||
extern bool gFlagResetVfos;
|
||||
extern bool gRequestSaveVFO;
|
||||
extern uint8_t gRequestSaveChannel;
|
||||
extern uint16_t gRequestSaveChannel;
|
||||
extern bool gRequestSaveSettings;
|
||||
#ifdef ENABLE_FMRADIO
|
||||
extern bool gRequestSaveFM;
|
||||
@@ -336,7 +433,7 @@ extern bool g_SquelchLost;
|
||||
extern volatile uint16_t gFlashLightBlinkCounter;
|
||||
|
||||
extern bool gFlagEndTransmission;
|
||||
extern uint8_t gNextMrChannel;
|
||||
extern uint16_t gNextMrChannel;
|
||||
extern ReceptionMode_t gRxReceptionMode;
|
||||
|
||||
//TRUE when dual watch is momentarly suspended and RX_VFO is locked to either last TX or RX
|
||||
@@ -385,8 +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);
|
||||
|
||||
+385
-260
@@ -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
|
||||
@@ -53,68 +56,147 @@ const char gModulationStr[MODULATION_UKNOWN][4] = {
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
|
||||
// About BK4819_WriteRegister(0x2b, val) experimentation...
|
||||
//
|
||||
// 0x000: 300 Hz high-pass filter enabled, 3 kHz low-pass filter enabled, de-emphasis enabled.
|
||||
// Audio impression: the most "classic radio" tuning, more filtered and smoother.
|
||||
//
|
||||
// 0x300: 300 Hz high-pass filter enabled, 3 kHz low-pass filter disabled, de-emphasis disabled.
|
||||
// Audio impression: clearer, brighter, and more open, while still cutting low frequencies.
|
||||
//
|
||||
// 0x400: 300 Hz high-pass filter disabled, 3 kHz low-pass filter enabled, de-emphasis enabled.
|
||||
// Audio impression: fuller low end, but still softened by de-emphasis and upper-frequency limiting.
|
||||
//
|
||||
// 0x500: 300 Hz high-pass filter disabled, 3 kHz low-pass filter enabled, de-emphasis disabled.
|
||||
// Audio impression: fuller bass, more direct sound, while still keeping the 3 kHz top-end limit.
|
||||
|
||||
static void AUDIO_ApplyFMProfile(uint8_t profile)
|
||||
{ // | 0x54 || 0x55 |
|
||||
static const uint16_t fm_profiles[][2] = {
|
||||
{0x9009, 0x3200}, // 0: FLAT
|
||||
{0x9009, 0x33A9}, // 1: CLEAN
|
||||
{0x9009, 0x3600}, // 2: MID
|
||||
{0x8546, 0x3AF0}, // 3: BOOST
|
||||
{0x8566, 0x3D00} // 4: MAX
|
||||
};
|
||||
|
||||
if (profile >= ARRAY_SIZE(fm_profiles))
|
||||
profile = 0;
|
||||
|
||||
BK4819_WriteRegister(0x54, fm_profiles[profile][0]);
|
||||
BK4819_WriteRegister(0x55, fm_profiles[profile][1]);
|
||||
}
|
||||
|
||||
static void AUDIO_ApplyAMProfile(uint8_t profile)
|
||||
{ // | 0x2b || 0x2f || 0x54 || 0x55 |
|
||||
static const uint16_t am_profiles[][4] = {
|
||||
// SHARP (ALPHA test profile) - Narrow IF filter (REG54 bits[14:8]=0, bits[7:0]=9), low IF gain (REG55 bits[11:8]=1, ref=169)
|
||||
// Selective and crisp, best adjacent channel rejection, may sound harsh on strong signals
|
||||
{0x0300, 0x9990, 0x9009, 0x31A9},
|
||||
|
||||
// STOCK - Narrow IF filter (REG54 bits[14:8]=0, bits[7:0]=9), moderate IF gain (REG55 bits[11:8]=4, ref=180)
|
||||
// Selective filter with balanced gain, punchy and detailed, good compromise between rejection and sensitivity
|
||||
{0x0500, 0x9990, 0x9009, 0x31A9},
|
||||
|
||||
// OPEN (BRAVO test profile) - Medium-wide IF filter (REG54 bits[14:8]=8, bits[7:0]=70), high IF gain (REG55 bits[11:8]=8, ref=192)
|
||||
// Wide and pleasant, better sensitivity on weak signals, may struggle with adjacent channel interference
|
||||
{0x0300, 0x9990, 0x8846, 0x38C0}
|
||||
};
|
||||
|
||||
if (profile >= ARRAY_SIZE(am_profiles))
|
||||
profile = 0;
|
||||
|
||||
BK4819_WriteRegister(0x2b, am_profiles[profile][0]);
|
||||
BK4819_WriteRegister(0x2f, am_profiles[profile][1]);
|
||||
BK4819_WriteRegister(0x54, am_profiles[profile][2]);
|
||||
BK4819_WriteRegister(0x55, am_profiles[profile][3]);
|
||||
}
|
||||
|
||||
static void AUDIO_ApplyUSBProfile(void)
|
||||
{
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x31A9);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool RADIO_CheckValidList(uint8_t scanList)
|
||||
{
|
||||
if(scanList == MR_CHANNELS_LIST + 1)
|
||||
return true;
|
||||
|
||||
for (uint16_t i = 0; IS_MR_CHANNEL(i); i++) {
|
||||
const ChannelAttributes_t* att = MR_GetChannelAttributes(i);
|
||||
if(att->scanlist == scanList && att->exclude == false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void RADIO_NextValidList(int8_t direction)
|
||||
{
|
||||
uint8_t startList = gEeprom.SCAN_LIST_DEFAULT;
|
||||
uint8_t attempts = 0;
|
||||
const uint8_t MAX_VALUE = MR_CHANNELS_LIST + 1; // 25 (1-24 lists + ALL)
|
||||
|
||||
do {
|
||||
if (direction > 0) {
|
||||
// Forward: 1 → 2 → ... → 25 → 1
|
||||
gEeprom.SCAN_LIST_DEFAULT = (gEeprom.SCAN_LIST_DEFAULT % MAX_VALUE) + 1;
|
||||
} else {
|
||||
// Backward: 25 → 24 → ... → 1 → 25
|
||||
gEeprom.SCAN_LIST_DEFAULT = ((gEeprom.SCAN_LIST_DEFAULT - 2 + MAX_VALUE) % MAX_VALUE) + 1;
|
||||
}
|
||||
attempts++;
|
||||
|
||||
if (RADIO_CheckValidList(gEeprom.SCAN_LIST_DEFAULT))
|
||||
return;
|
||||
|
||||
} while (gEeprom.SCAN_LIST_DEFAULT != startList && attempts < MAX_VALUE);
|
||||
|
||||
// Safety fallback: switch to ALL mode
|
||||
if (!RADIO_CheckValidList(gEeprom.SCAN_LIST_DEFAULT)) {
|
||||
gEeprom.SCAN_LIST_DEFAULT = MAX_VALUE; // ALL (25)
|
||||
}
|
||||
}
|
||||
|
||||
bool RADIO_CheckValidChannel(uint16_t channel, bool checkScanList, uint8_t scanList)
|
||||
{
|
||||
const ChannelAttributes_t* att = MR_GetChannelAttributes(channel);
|
||||
|
||||
// return true if the channel appears valid
|
||||
if (!IS_MR_CHANNEL(channel))
|
||||
return false;
|
||||
|
||||
const ChannelAttributes_t att = gMR_ChannelAttributes[channel];
|
||||
|
||||
if (checkScanList && gMR_ChannelExclude[channel] == true)
|
||||
if (checkScanList && att->exclude == true)
|
||||
return false;
|
||||
|
||||
if (att.band > BAND7_470MHz)
|
||||
if (att->band > BAND7_470MHz)
|
||||
return false;
|
||||
|
||||
if (!checkScanList || scanList > 4)
|
||||
if (!checkScanList || (scanList > MR_CHANNELS_LIST && att->scanlist != 0) || (scanList > 0 && att->scanlist == MR_CHANNELS_LIST + 1))
|
||||
return true;
|
||||
|
||||
/*
|
||||
if(scanList == 0 && (att.scanlist1 == 1 || att.scanlist2 == 1 || att.scanlist3 == 1))
|
||||
{
|
||||
if ((scanList == 0) || (scanList != att->scanlist)) {
|
||||
return false;
|
||||
}
|
||||
else if(scanList == 1 && att.scanlist1 != 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if(scanList == 2 && att.scanlist2 != 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if(scanList == 3 && att.scanlist3 != 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if(scanList == 4 && (att.scanlist1 == 0 && att.scanlist2 == 0 && att.scanlist3 == 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
if ((scanList == 0 && (att.scanlist1 == 1 || att.scanlist2 == 1 || att.scanlist3 == 1)) ||
|
||||
(scanList == 1 && att.scanlist1 != 1) ||
|
||||
(scanList == 2 && att.scanlist2 != 1) ||
|
||||
(scanList == 3 && att.scanlist3 != 1) ||
|
||||
(scanList == 4 && (att.scanlist1 == 0 && att.scanlist2 == 0 && att.scanlist3 == 0))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//return true;
|
||||
|
||||
// I don't understand what this code is for...
|
||||
|
||||
const uint8_t PriorityCh1 = gEeprom.SCANLIST_PRIORITY_CH1[scanList - 1];
|
||||
const uint8_t PriorityCh2 = gEeprom.SCANLIST_PRIORITY_CH2[scanList - 1];
|
||||
|
||||
return PriorityCh1 != channel && PriorityCh2 != channel;
|
||||
// Exclude priority channels ONLY if SCAN_LIST_ENABLED is active
|
||||
// Otherwise, treat them as normal channels in the list
|
||||
if (gEeprom.SCAN_LIST_ENABLED)
|
||||
{
|
||||
const uint16_t PriorityCh1 = gEeprom.SCANLIST_PRIORITY_CH[0];
|
||||
const uint16_t PriorityCh2 = gEeprom.SCANLIST_PRIORITY_CH[1];
|
||||
if (PriorityCh1 == channel || PriorityCh2 == channel)
|
||||
return false; // Excluded because it's a priority channel and they are enabled
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t RADIO_FindNextChannel(uint8_t Channel, int8_t Direction, bool bCheckScanList, uint8_t VFO)
|
||||
uint16_t RADIO_FindNextChannel(uint16_t Channel, int8_t Direction, bool bCheckScanList, uint8_t VFO)
|
||||
{
|
||||
for (unsigned int i = 0; IS_MR_CHANNEL(i); i++, Channel += Direction) {
|
||||
if (Channel == 0xFF) {
|
||||
for (uint16_t i = 0; IS_MR_CHANNEL(i); i++, Channel += Direction) {
|
||||
if (Channel == 0xFFFF) {
|
||||
Channel = MR_CHANNEL_LAST;
|
||||
} else if (!IS_MR_CHANNEL(Channel)) {
|
||||
Channel = MR_CHANNEL_FIRST;
|
||||
@@ -125,17 +207,15 @@ uint8_t RADIO_FindNextChannel(uint8_t Channel, int8_t Direction, bool bCheckScan
|
||||
}
|
||||
}
|
||||
|
||||
return 0xFF;
|
||||
return 0xFFFF;
|
||||
}
|
||||
|
||||
void RADIO_InitInfo(VFO_Info_t *pInfo, const uint8_t ChannelSave, const uint32_t Frequency)
|
||||
void RADIO_InitInfo(VFO_Info_t *pInfo, const uint16_t ChannelSave, const uint32_t Frequency)
|
||||
{
|
||||
memset(pInfo, 0, sizeof(*pInfo));
|
||||
|
||||
pInfo->Band = FREQUENCY_GetBand(Frequency);
|
||||
pInfo->SCANLIST1_PARTICIPATION = false;
|
||||
pInfo->SCANLIST2_PARTICIPATION = false;
|
||||
pInfo->SCANLIST3_PARTICIPATION = false;
|
||||
pInfo->SCANLIST_PARTICIPATION = 0;
|
||||
pInfo->STEP_SETTING = STEP_12_5kHz;
|
||||
pInfo->StepFrequency = gStepFrequencyTable[pInfo->STEP_SETTING];
|
||||
pInfo->CHANNEL_SAVE = ChannelSave;
|
||||
@@ -156,6 +236,24 @@ void RADIO_InitInfo(VFO_Info_t *pInfo, const uint8_t ChannelSave, const uint32_t
|
||||
RADIO_ConfigureSquelchAndOutputPower(pInfo);
|
||||
}
|
||||
|
||||
void RADIO_ValidateAndSetCode(FREQ_Config_t *pFreq_Config, uint8_t tmp) {
|
||||
switch (pFreq_Config->CodeType) {
|
||||
default:
|
||||
case CODE_TYPE_OFF:
|
||||
pFreq_Config->CodeType = CODE_TYPE_OFF;
|
||||
tmp = 0;
|
||||
break;
|
||||
|
||||
case CODE_TYPE_CONTINUOUS_TONE:
|
||||
case CODE_TYPE_DIGITAL:
|
||||
case CODE_TYPE_REVERSE_DIGITAL:
|
||||
if (tmp > ((pFreq_Config->CodeType == CODE_TYPE_CONTINUOUS_TONE ? ARRAY_SIZE(CTCSS_Options) : ARRAY_SIZE(DCS_Options)) - 1))
|
||||
tmp = 0;
|
||||
break;
|
||||
}
|
||||
pFreq_Config->Code = tmp;
|
||||
}
|
||||
|
||||
void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure)
|
||||
{
|
||||
VFO_Info_t *pVfo = &gEeprom.VfoInfo[VFO];
|
||||
@@ -168,7 +266,7 @@ void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure
|
||||
gEeprom.ScreenChannel[VFO] = FREQ_CHANNEL_FIRST + BAND6_400MHz;
|
||||
}
|
||||
|
||||
uint8_t channel = gEeprom.ScreenChannel[VFO];
|
||||
uint16_t channel = gEeprom.ScreenChannel[VFO];
|
||||
|
||||
if (IS_VALID_CHANNEL(channel)) {
|
||||
#ifdef ENABLE_NOAA
|
||||
@@ -188,7 +286,7 @@ void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure
|
||||
|
||||
if (IS_MR_CHANNEL(channel)) {
|
||||
channel = RADIO_FindNextChannel(channel, RADIO_CHANNEL_UP, false, VFO);
|
||||
if (channel == 0xFF) {
|
||||
if (channel == 0xFFFF) {
|
||||
channel = gEeprom.FreqChannel[VFO];
|
||||
gEeprom.ScreenChannel[VFO] = gEeprom.FreqChannel[VFO];
|
||||
}
|
||||
@@ -201,50 +299,42 @@ void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure
|
||||
else
|
||||
channel = FREQ_CHANNEL_LAST - 1;
|
||||
|
||||
ChannelAttributes_t att = gMR_ChannelAttributes[channel];
|
||||
if (att.__val == 0xFF) { // invalid/unused channel
|
||||
ChannelAttributes_t* att = MR_GetChannelAttributes(channel);
|
||||
if (att->__val == 0xFFFF) { // invalid/unused channel
|
||||
if (IS_MR_CHANNEL(channel)) {
|
||||
channel = gEeprom.FreqChannel[VFO];
|
||||
gEeprom.ScreenChannel[VFO] = channel;
|
||||
}
|
||||
|
||||
uint8_t bandIdx = channel - FREQ_CHANNEL_FIRST;
|
||||
uint16_t bandIdx = channel - FREQ_CHANNEL_FIRST;
|
||||
RADIO_InitInfo(pVfo, channel, frequencyBandTable[bandIdx].lower);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t band = att.band;
|
||||
uint8_t band = att->band;
|
||||
if (band > BAND7_470MHz) {
|
||||
band = BAND6_400MHz;
|
||||
}
|
||||
|
||||
bool bParticipation1;
|
||||
bool bParticipation2;
|
||||
bool bParticipation3;
|
||||
uint8_t bParticipation;
|
||||
|
||||
if (IS_MR_CHANNEL(channel)) {
|
||||
bParticipation1 = att.scanlist1;
|
||||
bParticipation2 = att.scanlist2;
|
||||
bParticipation3 = att.scanlist3;
|
||||
bParticipation = att->scanlist;
|
||||
}
|
||||
else {
|
||||
band = channel - FREQ_CHANNEL_FIRST;
|
||||
bParticipation1 = true;
|
||||
bParticipation2 = true;
|
||||
bParticipation3 = true;
|
||||
bParticipation = MR_CHANNELS_LIST + 1;
|
||||
}
|
||||
|
||||
pVfo->Band = band;
|
||||
pVfo->SCANLIST1_PARTICIPATION = bParticipation1;
|
||||
pVfo->SCANLIST2_PARTICIPATION = bParticipation2;
|
||||
pVfo->SCANLIST3_PARTICIPATION = bParticipation3;
|
||||
pVfo->SCANLIST_PARTICIPATION = bParticipation;
|
||||
pVfo->CHANNEL_SAVE = channel;
|
||||
|
||||
uint32_t base;
|
||||
if (IS_MR_CHANNEL(channel))
|
||||
base = channel * 16;
|
||||
else
|
||||
base = 0x001000 + ((channel - FREQ_CHANNEL_FIRST) * 32) + (VFO * 16);
|
||||
base = 0x009000 + ((channel - FREQ_CHANNEL_FIRST) * 32) + (VFO * 16);
|
||||
|
||||
if (configure == VFO_CONFIGURE_RELOAD || IS_FREQ_CHANNEL(channel))
|
||||
{
|
||||
@@ -282,49 +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)
|
||||
{
|
||||
@@ -424,7 +473,7 @@ void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure
|
||||
pConfig->Frequency = 43300000;
|
||||
}
|
||||
|
||||
pVfo->Compander = att.compander;
|
||||
pVfo->Compander = att->compander;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
if(gRemoveOffset)
|
||||
@@ -464,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;
|
||||
@@ -498,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);
|
||||
}
|
||||
|
||||
// *******************************
|
||||
@@ -513,7 +565,7 @@ void RADIO_ConfigureSquelchAndOutputPower(VFO_Info_t *pInfo)
|
||||
|
||||
Band = FREQUENCY_GetBand(pInfo->pTX->Frequency);
|
||||
|
||||
// my eeprom calibration data
|
||||
// my eeprom calibration data on UV-K5 (V1)
|
||||
//
|
||||
// 1ED0 32 32 32 64 64 64 8c 8c 8c ff ff ff ff ff ff ff 50 MHz
|
||||
// 1EE0 32 32 32 64 64 64 8c 8c 8c ff ff ff ff ff ff ff 108 MHz
|
||||
@@ -523,34 +575,28 @@ void RADIO_ConfigureSquelchAndOutputPower(VFO_Info_t *pInfo)
|
||||
// 1F20 5f 5f 5f 69 69 69 87 87 87 ff ff ff ff ff ff ff 400 MHz
|
||||
// 1F30 32 32 32 64 64 64 8c 8c 8c ff ff ff ff ff ff ff 470 MHz
|
||||
|
||||
// my eeprom calibration data on UV-K1
|
||||
// 32 32 32 64 64 64 8c 8c 8c ff ff ff ff ff ff ff 50 MHz
|
||||
// 32 32 32 64 64 64 8c 8c 8c ff ff ff ff ff ff ff 108 MHz
|
||||
// 4b 4b 4b 78 78 78 96 96 96 ff ff ff ff ff ff ff 137 MHz
|
||||
// 32 32 32 64 64 64 8c 8c 8c ff ff ff ff ff ff ff 174 MHz
|
||||
// 5a 5a 5a 64 64 64 a0 a0 a0 ff ff ff ff ff ff ff 350 MHz
|
||||
// 4b 4b 4b 78 78 78 96 96 96 ff ff ff ff ff ff ff 400 MHz
|
||||
// 32 32 32 64 64 64 94 8c 8c ff ff ff ff ff ff ff 470 MHz
|
||||
|
||||
uint8_t Txp[3];
|
||||
uint8_t Op = 0; // Low eeprom calibration data
|
||||
uint8_t currentPower = pInfo->OUTPUT_POWER;
|
||||
|
||||
if(currentPower == OUTPUT_POWER_USER)
|
||||
{
|
||||
if(gSetting_set_pwr == 5)
|
||||
{
|
||||
Op = 1; // Mid eeprom calibration data
|
||||
}
|
||||
else if(gSetting_set_pwr == 6)
|
||||
{
|
||||
Op = 2; // High eeprom calibration data
|
||||
}
|
||||
if (currentPower == OUTPUT_POWER_USER)
|
||||
currentPower = gSetting_set_pwr;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentPower == OUTPUT_POWER_MID)
|
||||
{
|
||||
Op = 1; // Mid eeprom calibration data
|
||||
}
|
||||
else if(currentPower == OUTPUT_POWER_HIGH)
|
||||
{
|
||||
Op = 2; // High eeprom calibration data
|
||||
}
|
||||
currentPower--;
|
||||
}
|
||||
|
||||
if (currentPower == 5)
|
||||
Op = 1; // Mid eeprom calibration data
|
||||
else if (currentPower == 6)
|
||||
Op = 2; // High eeprom calibration data
|
||||
|
||||
PY25Q16_ReadBuffer(0x100D0 + (Band * 16) + (Op * 3), Txp, 3);
|
||||
|
||||
@@ -602,7 +648,17 @@ void RADIO_ConfigureSquelchAndOutputPower(VFO_Info_t *pInfo)
|
||||
}
|
||||
*/
|
||||
|
||||
static const uint8_t dividers[6] = { 25, 19, 13, 10, 7, 4};
|
||||
//static const uint8_t dividers[6] = { 25, 19, 13, 10, 7, 4}; // For UV-K5 V1
|
||||
|
||||
static const uint8_t dividers_band2[6] = { 20, 15, 10, 8, 6, 4 };
|
||||
static const uint8_t dividers_band5[6] = { 25, 19, 13, 9, 6, 4 }; // Need to improve measure...
|
||||
|
||||
const uint8_t *dividers;
|
||||
|
||||
if (Band == 2) // VHF
|
||||
dividers = dividers_band2;
|
||||
else // UHF
|
||||
dividers = dividers_band5;
|
||||
|
||||
for (uint8_t p = 0; p < 3; p++)
|
||||
{
|
||||
@@ -612,7 +668,8 @@ void RADIO_ConfigureSquelchAndOutputPower(VFO_Info_t *pInfo)
|
||||
}
|
||||
else // case 6
|
||||
{
|
||||
Txp[p] += 30;
|
||||
// Txp[p] += 30; // For UV-K5 V1
|
||||
Txp[p] += 24;
|
||||
}
|
||||
}
|
||||
#else
|
||||
@@ -647,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;
|
||||
}
|
||||
|
||||
@@ -682,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;
|
||||
@@ -699,21 +755,26 @@ void RADIO_SetupRegisters(bool switchToForeground)
|
||||
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false);
|
||||
|
||||
switch (Bandwidth)
|
||||
if (gRxVfo->Modulation == MODULATION_AM)
|
||||
BK4819_SetFilterBandwidth(RADIO_GetAMFilterBandwidth(gRxVfo), true);
|
||||
else
|
||||
{
|
||||
default:
|
||||
Bandwidth = BK4819_FILTER_BW_WIDE;
|
||||
[[fallthrough]];
|
||||
case BK4819_FILTER_BW_WIDE:
|
||||
case BK4819_FILTER_BW_NARROW:
|
||||
case BK4819_FILTER_BW_NARROWER:
|
||||
#ifdef ENABLE_AM_FIX
|
||||
// BK4819_SetFilterBandwidth(Bandwidth, gRxVfo->Modulation == MODULATION_AM && gSetting_AM_fix);
|
||||
BK4819_SetFilterBandwidth(Bandwidth, true);
|
||||
#else
|
||||
BK4819_SetFilterBandwidth(Bandwidth, false);
|
||||
#endif
|
||||
break;
|
||||
switch (Bandwidth)
|
||||
{
|
||||
default:
|
||||
Bandwidth = BK4819_FILTER_BW_WIDE;
|
||||
[[fallthrough]];
|
||||
case BK4819_FILTER_BW_WIDE:
|
||||
case BK4819_FILTER_BW_NARROW:
|
||||
case BK4819_FILTER_BW_NARROWER:
|
||||
#ifdef ENABLE_AM_FIX
|
||||
// BK4819_SetFilterBandwidth(Bandwidth, gRxVfo->Modulation == MODULATION_AM && gSetting_AM_fix);
|
||||
BK4819_SetFilterBandwidth(Bandwidth, true);
|
||||
#else
|
||||
BK4819_SetFilterBandwidth(Bandwidth, false);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO5_PIN1_RED, false);
|
||||
@@ -733,8 +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
|
||||
@@ -747,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,
|
||||
@@ -759,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;
|
||||
|
||||
@@ -784,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:
|
||||
@@ -796,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;
|
||||
}
|
||||
|
||||
@@ -831,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
|
||||
|
||||
@@ -864,6 +913,7 @@ void RADIO_SetupRegisters(bool switchToForeground)
|
||||
InterruptMask |= BK4819_REG_3F_DTMF_5TONE_FOUND;
|
||||
|
||||
RADIO_SetupAGC(gRxVfo->Modulation == MODULATION_AM, false);
|
||||
//RADIO_SetupAGC(false, false);
|
||||
|
||||
// enable/disable BK4819 selected interrupts
|
||||
BK4819_WriteRegister(BK4819_REG_3F, InterruptMask);
|
||||
@@ -992,6 +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:
|
||||
@@ -999,24 +1073,68 @@ void RADIO_SetModulation(ModulationMode_t modulation)
|
||||
mod = BK4819_AF_FM;
|
||||
break;
|
||||
case MODULATION_AM:
|
||||
mod = BK4819_AF_AM;
|
||||
mod = BK4819_AF_FM; // AM no longer needs special AF setting
|
||||
break;
|
||||
case MODULATION_USB:
|
||||
mod = BK4819_AF_BASEBAND2;
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_BYP_RAW_DEMODULATORS
|
||||
case MODULATION_BYP:
|
||||
mod = BK4819_AF_UNKNOWN3;
|
||||
break;
|
||||
case MODULATION_RAW:
|
||||
mod = BK4819_AF_BASEBAND1;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
BK4819_SetAF(mod);
|
||||
|
||||
//
|
||||
// What follows is a direct copy of the AM enable/disable code from
|
||||
// the original UV-K1 firmware. It is not clear why these specific register
|
||||
// values are used for AM all of a sudden instead of the AF setting like on
|
||||
// the BK4819, nor what exactly they do.
|
||||
// So for now we just keep it as is to maintain compatibility.
|
||||
//
|
||||
|
||||
uint16_t uVar1 = BK4819_ReadRegister(0x31);
|
||||
|
||||
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);
|
||||
@@ -1026,30 +1144,22 @@ void RADIO_SetModulation(ModulationMode_t modulation)
|
||||
|
||||
void RADIO_SetupAGC(bool listeningAM, bool disable)
|
||||
{
|
||||
static uint8_t lastSettings;
|
||||
static uint8_t lastSettings = 0xFF;
|
||||
uint8_t newSettings = (listeningAM << 1) | disable;
|
||||
if(lastSettings == newSettings)
|
||||
if (lastSettings == newSettings)
|
||||
return;
|
||||
lastSettings = newSettings;
|
||||
|
||||
|
||||
if(!listeningAM) { // if not actively listening AM we don't need any AM specific regulation
|
||||
BK4819_SetAGC(!disable);
|
||||
BK4819_InitAGC(false);
|
||||
}
|
||||
else {
|
||||
#ifdef ENABLE_AM_FIX
|
||||
if(gSetting_AM_fix) { // if AM fix active lock AGC so AM-fix can do it's job
|
||||
BK4819_SetAGC(0);
|
||||
AM_fix_enable(!disable);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
BK4819_SetAGC(!disable);
|
||||
BK4819_InitAGC(true);
|
||||
}
|
||||
if (listeningAM && gSetting_AM_fix) {
|
||||
BK4819_SetAGC(0);
|
||||
AM_fix_enable(!disable);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
BK4819_SetAGC(!disable);
|
||||
BK4819_InitAGC(listeningAM);
|
||||
}
|
||||
|
||||
void RADIO_SetVfoState(VfoState_t State)
|
||||
@@ -1095,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
|
||||
@@ -1123,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
|
||||
@@ -1163,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)
|
||||
@@ -1202,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);
|
||||
}
|
||||
|
||||
@@ -1232,7 +1358,6 @@ void RADIO_PrepareCssTX(void)
|
||||
|
||||
SYSTEM_DelayMs(200);
|
||||
|
||||
if(gEeprom.TAIL_TONE_ELIMINATION)
|
||||
RADIO_SendCssTail();
|
||||
RADIO_SendCssTail();
|
||||
RADIO_SetupRegisters(true);
|
||||
}
|
||||
+13
-7
@@ -21,6 +21,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "dcs.h"
|
||||
#include "driver/bk4819.h"
|
||||
#include "frequencies.h"
|
||||
|
||||
enum {
|
||||
@@ -95,7 +96,7 @@ typedef struct VFO_Info_t
|
||||
uint32_t TX_OFFSET_FREQUENCY;
|
||||
uint16_t StepFrequency;
|
||||
|
||||
uint8_t CHANNEL_SAVE;
|
||||
uint16_t CHANNEL_SAVE;
|
||||
|
||||
uint8_t TX_OFFSET_FREQUENCY_DIRECTION;
|
||||
|
||||
@@ -115,11 +116,9 @@ typedef struct VFO_Info_t
|
||||
uint8_t SCRAMBLING_TYPE;
|
||||
uint8_t CHANNEL_BANDWIDTH;
|
||||
|
||||
uint8_t SCANLIST1_PARTICIPATION;
|
||||
uint8_t SCANLIST2_PARTICIPATION;
|
||||
uint8_t SCANLIST3_PARTICIPATION;
|
||||
uint8_t SCANLIST_PARTICIPATION;
|
||||
|
||||
uint8_t Band;
|
||||
uint16_t Band;
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
uint8_t DTMF_DECODING_ENABLE;
|
||||
#endif
|
||||
@@ -150,17 +149,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(int8_t direction);
|
||||
bool RADIO_CheckValidChannel(uint16_t channel, bool checkScanList, uint8_t scanList);
|
||||
uint8_t RADIO_FindNextChannel(uint8_t ChNum, int8_t Direction, bool bCheckScanList, uint8_t RadioNum);
|
||||
void RADIO_InitInfo(VFO_Info_t *pInfo, const uint8_t ChannelSave, const uint32_t Frequency);
|
||||
uint16_t RADIO_FindNextChannel(uint16_t ChNum, int8_t Direction, bool bCheckScanList, uint8_t RadioNum);
|
||||
void RADIO_InitInfo(VFO_Info_t *pInfo, const uint16_t ChannelSave, const uint32_t Frequency);
|
||||
void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure);
|
||||
void RADIO_ConfigureSquelchAndOutputPower(VFO_Info_t *pInfo);
|
||||
void RADIO_ValidateAndSetCode(FREQ_Config_t *pFreq_Config, uint8_t tmp);
|
||||
void RADIO_ApplyOffset(VFO_Info_t *pInfo);
|
||||
void RADIO_SelectVfos(void);
|
||||
void RADIO_SetupRegisters(bool switchToForeground);
|
||||
#ifdef ENABLE_NOAA
|
||||
void RADIO_ConfigureNOAA(void);
|
||||
#endif
|
||||
BK4819_FilterBandwidth_t RADIO_GetAMFilterBandwidth(const VFO_Info_t *pVfo);
|
||||
void RADIO_SetTxParameters(void);
|
||||
void RADIO_SetupAGC(bool listeningAM, bool disable);
|
||||
void RADIO_SetModulation(ModulationMode_t modulation);
|
||||
|
||||
@@ -55,6 +55,7 @@ void SysTick_Handler(void)
|
||||
gNextTimeslice_500ms = true;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
DECREMENT_AND_TRIGGER(gVfoSaveCountdown_10ms, gScheduleVfoSave);
|
||||
DECREMENT_AND_TRIGGER(gTxTimerCountdownAlert_500ms - ALERT_TOT * 2, gTxTimeoutReachedAlert);
|
||||
#ifdef ENABLE_FEAT_F4HWN_RX_TX_TIMER
|
||||
DECREMENT(gRxTimerCountdown_500ms);
|
||||
@@ -97,6 +98,38 @@ void SysTick_Handler(void)
|
||||
if (gCurrentFunction != FUNCTION_MONITOR && gCurrentFunction != FUNCTION_TRANSMIT)
|
||||
DECREMENT_AND_TRIGGER(gScanPauseDelayIn_10ms, gScheduleScanListen);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
// Scan stall watchdog: if the scan-resume countdown has expired
|
||||
// (gScanPauseDelayIn_10ms == 0) but no resume was actually scheduled
|
||||
// (gScheduleScanListen == false) and no real reception is happening
|
||||
// (squelch closed -> green LED off, not in RX/TX/MONITOR), the scan
|
||||
// loop is stuck. Force a resume after this many 10ms ticks. The
|
||||
// gScanPauseDelayIn_10ms == 0 guard ensures we never override the
|
||||
// user-configured ScnRev (SCAN_RESUME_MODE) pause: while that pause
|
||||
// is decrementing, the watchdog stays asleep.
|
||||
#define SCAN_FAST_STALL_WATCHDOG_10ms 25 // 250 ms
|
||||
static uint16_t scanFastStallCounter;
|
||||
|
||||
if (gSetting_set_scn
|
||||
&& gScanStateDir != SCAN_OFF
|
||||
&& gScanPauseDelayIn_10ms == 0
|
||||
&& !gScheduleScanListen
|
||||
&& !g_SquelchLost
|
||||
&& gCurrentFunction != FUNCTION_RECEIVE
|
||||
&& gCurrentFunction != FUNCTION_TRANSMIT
|
||||
&& gCurrentFunction != FUNCTION_MONITOR)
|
||||
{
|
||||
if (++scanFastStallCounter >= SCAN_FAST_STALL_WATCHDOG_10ms) {
|
||||
scanFastStallCounter = 0;
|
||||
gScheduleScanListen = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
scanFastStallCounter = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
DECREMENT_AND_TRIGGER(gTailNoteEliminationCountdown_10ms, gFlagTailNoteEliminationComplete);
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
|
||||
@@ -1,123 +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"
|
||||
|
||||
void getScreenShot(bool force)
|
||||
{
|
||||
static uint8_t previousFrame[1024] = {0}; // Last transmitted frame
|
||||
static uint8_t forcedBlock = 0; // Block forced for refresh on each frame
|
||||
static uint8_t keepAlive = 10; // Keepalive counter
|
||||
|
||||
// Use a single buffer to reduce stack usage
|
||||
static uint8_t currentFrame[1024]; // Current frame
|
||||
static uint8_t deltaFrame[128 * 9]; // Delta frame buffer
|
||||
uint16_t index = 0;
|
||||
uint8_t acc = 0;
|
||||
uint8_t bitCount = 0;
|
||||
|
||||
if (gUART_LockScreenshot > 0) // Lock screenshot if Chirp is in used
|
||||
{
|
||||
gUART_LockScreenshot--;
|
||||
return;
|
||||
}
|
||||
|
||||
if (UART_IsCableConnected()) {
|
||||
keepAlive = 10;
|
||||
}
|
||||
|
||||
if (keepAlive > 0) {
|
||||
if (--keepAlive == 0) return;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Build current frame from status line (first 8 lines)
|
||||
for (uint8_t b = 0; b < 8; b++) {
|
||||
for (uint8_t i = 0; i < 128; i++) {
|
||||
uint8_t bit = (gStatusLine[i] >> b) & 0x01;
|
||||
acc |= (bit << bitCount++);
|
||||
if (bitCount == 8) {
|
||||
currentFrame[index++] = acc;
|
||||
acc = 0;
|
||||
bitCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build remaining part of the frame (7 * 8 lines)
|
||||
for (uint8_t l = 0; l < 7; l++) {
|
||||
for (uint8_t b = 0; b < 8; b++) {
|
||||
for (uint8_t i = 0; i < 128; i++) {
|
||||
uint8_t bit = (gFrameBuffer[l][i] >> b) & 0x01;
|
||||
acc |= (bit << bitCount++);
|
||||
if (bitCount == 8) {
|
||||
currentFrame[index++] = acc;
|
||||
acc = 0;
|
||||
bitCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bitCount > 0)
|
||||
currentFrame[index++] = acc;
|
||||
|
||||
if (index != 1024)
|
||||
return; // Frame size mismatch, abort
|
||||
|
||||
// Generate delta frame
|
||||
uint16_t deltaLen = 0;
|
||||
|
||||
for (uint8_t block = 0; block < 128; block++) {
|
||||
uint8_t *cur = ¤tFrame[block * 8];
|
||||
uint8_t *prev = &previousFrame[block * 8];
|
||||
|
||||
bool changed = memcmp(cur, prev, 8) != 0;
|
||||
bool isForced = (block == forcedBlock);
|
||||
bool fullUpdate = force;
|
||||
|
||||
if (changed || isForced || fullUpdate) {
|
||||
deltaFrame[deltaLen++] = block;
|
||||
memcpy(&deltaFrame[deltaLen], cur, 8);
|
||||
deltaLen += 8;
|
||||
|
||||
memcpy(prev, cur, 8); // Update stored frame
|
||||
}
|
||||
}
|
||||
|
||||
forcedBlock = (forcedBlock + 1) % 128;
|
||||
|
||||
if (deltaLen == 0)
|
||||
return; // No update needed
|
||||
|
||||
// Send the delta frame over UART
|
||||
uint8_t header[5] = {
|
||||
0xAA, 0x55, 0x02,
|
||||
(uint8_t)(deltaLen >> 8),
|
||||
(uint8_t)(deltaLen & 0xFF)
|
||||
};
|
||||
|
||||
UART_Send(header, 5);
|
||||
UART_Send(deltaFrame, deltaLen);
|
||||
uint8_t end = 0x0A;
|
||||
UART_Send(&end, 1);
|
||||
}
|
||||
+572
-323
File diff suppressed because it is too large.
Load diff
+48
-18
@@ -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,14 +121,20 @@ enum ACTION_OPT_t {
|
||||
ACTION_OPT_WN,
|
||||
ACTION_OPT_BACKLIGHT,
|
||||
ACTION_OPT_MUTE,
|
||||
ACTION_OPT_RXA,
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
ACTION_OPT_POWER_HIGH,
|
||||
ACTION_OPT_REMOVE_OFFSET,
|
||||
#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
|
||||
};
|
||||
@@ -162,11 +171,11 @@ enum CHANNEL_DisplayMode_t {
|
||||
typedef enum CHANNEL_DisplayMode_t CHANNEL_DisplayMode_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t ScreenChannel[2]; // current channels set in the radio (memory or frequency channels)
|
||||
uint8_t FreqChannel[2]; // last frequency channels used
|
||||
uint8_t MrChannel[2]; // last memory channels used
|
||||
uint16_t ScreenChannel[2]; // current channels set in the radio (memory or frequency channels)
|
||||
uint16_t FreqChannel[2]; // last frequency channels used
|
||||
uint16_t MrChannel[2]; // last memory channels used
|
||||
#ifdef ENABLE_NOAA
|
||||
uint8_t NoaaChannel[2];
|
||||
uint16_t NoaaChannel[2];
|
||||
#endif
|
||||
|
||||
// The actual VFO index (0-upper/1-lower) that is now used for RX,
|
||||
@@ -193,7 +202,7 @@ 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
|
||||
bool MENU_LOCK;
|
||||
@@ -214,9 +223,8 @@ typedef struct {
|
||||
uint8_t BACKLIGHT_TIME;
|
||||
uint8_t SCAN_RESUME_MODE;
|
||||
uint8_t SCAN_LIST_DEFAULT;
|
||||
bool SCAN_LIST_ENABLED[3];
|
||||
uint8_t SCANLIST_PRIORITY_CH1[3];
|
||||
uint8_t SCANLIST_PRIORITY_CH2[3];
|
||||
bool SCAN_LIST_ENABLED;
|
||||
uint16_t SCANLIST_PRIORITY_CH[6];
|
||||
//#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE // Fix me !!! What the hell is this?
|
||||
uint8_t CURRENT_STATE;
|
||||
uint8_t CURRENT_LIST;
|
||||
@@ -241,7 +249,7 @@ typedef struct {
|
||||
uint8_t KEY_2_LONG_PRESS_ACTION;
|
||||
uint8_t MIC_SENSITIVITY;
|
||||
uint8_t MIC_SENSITIVITY_TUNING;
|
||||
uint8_t CHAN_1_CALL;
|
||||
uint16_t CHAN_1_CALL;
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
char ANI_DTMF_ID[8];
|
||||
char KILL_CODE[8];
|
||||
@@ -306,20 +314,42 @@ typedef struct {
|
||||
|
||||
extern EEPROM_Config_t gEeprom;
|
||||
|
||||
typedef struct {
|
||||
FREQ_Config_t rx;
|
||||
FREQ_Config_t tx;
|
||||
uint32_t offset;
|
||||
uint16_t stepFrequency;
|
||||
STEP_Setting_t stepSetting;
|
||||
ModulationMode_t modulation;
|
||||
uint8_t txOffsetFrequencyDirection;
|
||||
uint8_t outputPower;
|
||||
bool frequencyReverse;
|
||||
uint8_t channelBandwidth;
|
||||
uint8_t busyChannelLock;
|
||||
uint8_t txLock;
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
uint8_t dtmfDecodingEnable;
|
||||
#endif
|
||||
PTT_ID_t dtmfPttIdTxMode;
|
||||
} ChannelScanDisplayInfo_t;
|
||||
|
||||
void SETTINGS_InitEEPROM(void);
|
||||
void SETTINGS_LoadCalibration(void);
|
||||
uint32_t SETTINGS_FetchChannelFrequency(const int channel);
|
||||
void SETTINGS_FetchChannelName(char *s, const int channel);
|
||||
uint32_t SETTINGS_FetchChannelFrequency(const uint16_t channel);
|
||||
bool SETTINGS_FetchChannelScanInfo(const uint16_t channel, uint32_t *frequency, ModulationMode_t *modulation);
|
||||
bool SETTINGS_FetchChannelScanDisplayInfo(const uint16_t channel, ChannelScanDisplayInfo_t *info);
|
||||
void SETTINGS_FetchChannelName(char *s, const uint16_t channel);
|
||||
void SETTINGS_FactoryReset(bool bIsAll);
|
||||
#ifdef ENABLE_FMRADIO
|
||||
void SETTINGS_SaveFM(void);
|
||||
#endif
|
||||
void SETTINGS_SaveVfoIndices(void);
|
||||
void SETTINGS_SaveVfoIndicesFlush(void);
|
||||
void SETTINGS_SaveSettings(void);
|
||||
void SETTINGS_SaveChannelName(uint8_t channel, const char * name);
|
||||
void SETTINGS_SaveChannel(uint8_t Channel, uint8_t VFO, const VFO_Info_t *pVFO, uint8_t Mode);
|
||||
void SETTINGS_SaveChannelName(uint16_t channel, const char * name);
|
||||
void SETTINGS_SaveChannel(uint16_t Channel, uint8_t VFO, const VFO_Info_t *pVFO, uint8_t Mode);
|
||||
void SETTINGS_SaveBatteryCalibration(const uint16_t * batteryCalibration);
|
||||
void SETTINGS_UpdateChannel(uint8_t channel, const VFO_Info_t *pVFO, bool keep, bool check, bool save);
|
||||
void SETTINGS_UpdateChannel(uint16_t channel, const VFO_Info_t *pVFO, bool keep);
|
||||
void SETTINGS_WriteBuildOptions(void);
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
void SETTINGS_WriteCurrentState(void);
|
||||
|
||||
+68
-32
@@ -37,9 +37,8 @@ static int get_bit(uint8_t* array, int bit_index) {
|
||||
|
||||
void UI_DisplayAircopy(void)
|
||||
{
|
||||
char String[16] = { 0 };
|
||||
char *pPrintStr = { 0 };
|
||||
uint16_t percent;
|
||||
char String[16];
|
||||
char *pPrintStr;
|
||||
|
||||
UI_DisplayClear();
|
||||
|
||||
@@ -60,55 +59,92 @@ void UI_DisplayAircopy(void)
|
||||
// show the remaining 2 small frequency digits
|
||||
UI_PrintStringSmallNormal(String + 7, 97, 0, 3);
|
||||
String[7] = 0;
|
||||
// show the main large frequency digits
|
||||
UI_DisplayFrequency(String, 16, 2, false);
|
||||
} else {
|
||||
const char *ascii = INPUTBOX_GetAscii();
|
||||
sprintf(String, "%.3s.%.3s", ascii, ascii + 3);
|
||||
UI_DisplayFrequency(String, 16, 2, false);
|
||||
}
|
||||
|
||||
memset(String, 0, sizeof(String));
|
||||
// show the main large frequency digits
|
||||
UI_DisplayFrequency(String, 16, 2, false);
|
||||
|
||||
percent = (gAirCopyBlockNumber * 10000) / 120;
|
||||
// Get the current map and calculate percentage based on its total blocks
|
||||
const AIRCOPY_TransferMap_t *currentMap = AIRCOPY_GetCurrentMap();
|
||||
|
||||
if (gAirCopyIsSendMode == 0) {
|
||||
sprintf(String, "RCV:%02u.%02u%% E:%d", percent / 100, percent % 100, gErrorsDuringAirCopy);
|
||||
} else if (gAirCopyIsSendMode == 1) {
|
||||
sprintf(String, "SND:%02u.%02u%%", percent / 100, percent % 100);
|
||||
}
|
||||
uint16_t doneBlocks = gAirCopyBlockNumber + gErrorsDuringAirCopy;
|
||||
|
||||
// Draw gauge
|
||||
if(gAircopyStep != 0)
|
||||
if (doneBlocks > currentMap->total_blocks)
|
||||
doneBlocks = currentMap->total_blocks;
|
||||
|
||||
// Draw memory selection
|
||||
if (gAircopyState == AIRCOPY_READY)
|
||||
{
|
||||
doneBlocks = 0;
|
||||
|
||||
if(gAircopyCurrentMapIndex < AIRCOPY_NUM_BANKS) {
|
||||
sprintf(String, "MEM %03u - %03u", (gAircopyCurrentMapIndex * 128) + 1, (gAircopyCurrentMapIndex + 1) * 128);
|
||||
} else {
|
||||
strcpy(String, "Settings");
|
||||
}
|
||||
UI_PrintString(String, 2, 127, 5, 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t percent = (doneBlocks * 10000) / currentMap->total_blocks;
|
||||
|
||||
gFrameBuffer[4][1] = 0x3c;
|
||||
gFrameBuffer[4][2] = 0x42;
|
||||
|
||||
for(uint8_t i = 1; i <= 122; i++)
|
||||
{
|
||||
gFrameBuffer[4][2 + i] = 0x81;
|
||||
if (gAirCopyIsSendMode == 0) {
|
||||
sprintf(String, "RCV:%02u.%02u%% E:%d", percent / 100, percent % 100, gErrorsDuringAirCopy);
|
||||
} else {
|
||||
sprintf(String, "SND:%02u.%02u%%", percent / 100, percent % 100);
|
||||
}
|
||||
|
||||
gFrameBuffer[4][125] = 0x42;
|
||||
gFrameBuffer[4][126] = 0x3c;
|
||||
// Draw gauge
|
||||
if(gAircopyStep != 0)
|
||||
{
|
||||
UI_PrintString(String, 2, 127, 5, 8);
|
||||
|
||||
gFrameBuffer[4][1] = 0x3c;
|
||||
gFrameBuffer[4][2] = 0x42;
|
||||
|
||||
for(uint8_t i = 1; i <= AIRCOPY_BAR_WIDTH + 2; i++)
|
||||
{
|
||||
gFrameBuffer[4][2 + i] = 0x81;
|
||||
}
|
||||
|
||||
gFrameBuffer[4][125] = 0x42;
|
||||
gFrameBuffer[4][126] = 0x3c;
|
||||
}
|
||||
}
|
||||
|
||||
if(gAirCopyBlockNumber + gErrorsDuringAirCopy != 0)
|
||||
if (doneBlocks > 0)
|
||||
{
|
||||
// Check CRC
|
||||
if(gErrorsDuringAirCopy != lErrorsDuringAirCopy)
|
||||
// Track CRC errors per real block index
|
||||
if (gErrorsDuringAirCopy != lErrorsDuringAirCopy)
|
||||
{
|
||||
set_bit(crc, gAirCopyBlockNumber + gErrorsDuringAirCopy);
|
||||
// Mark the last processed block as faulty
|
||||
set_bit(crc, doneBlocks - 1);
|
||||
lErrorsDuringAirCopy = gErrorsDuringAirCopy;
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < (gAirCopyBlockNumber + gErrorsDuringAirCopy); i++)
|
||||
uint16_t b = 0;
|
||||
uint16_t fraction_accumulator = 0;
|
||||
|
||||
for (uint8_t col = 0; col < AIRCOPY_BAR_WIDTH; col++)
|
||||
{
|
||||
if(get_bit(crc, i) == 0)
|
||||
{
|
||||
gFrameBuffer[4][i + 4] = 0xbd;
|
||||
bool processed = (b < doneBlocks);
|
||||
bool error = processed && get_bit(crc, b);
|
||||
|
||||
if (!processed)
|
||||
gFrameBuffer[4][col + 4] = 0x81; // not yet processed
|
||||
else if (error)
|
||||
gFrameBuffer[4][col + 4] = 0x81; // error gap (intentional hole)
|
||||
else
|
||||
gFrameBuffer[4][col + 4] = 0xBD; // ok filled
|
||||
|
||||
// DDA/Bresenham algorythm
|
||||
fraction_accumulator += currentMap->total_blocks;
|
||||
while (fraction_accumulator >= AIRCOPY_BAR_WIDTH) {
|
||||
fraction_accumulator -= AIRCOPY_BAR_WIDTH;
|
||||
b++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,4 +152,4 @@ void UI_DisplayAircopy(void)
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
+7
-4
@@ -31,10 +31,14 @@
|
||||
|
||||
void UI_DisplayFM(void)
|
||||
{
|
||||
char String[16] = {0};
|
||||
char String[16];
|
||||
char *pPrintStr = String;
|
||||
UI_DisplayClear();
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
UI_DisplayUnlockKeyboard(5);
|
||||
#endif
|
||||
|
||||
UI_PrintString("FM", 2, 0, 0, 8);
|
||||
|
||||
sprintf(String, "%d%s-%dM",
|
||||
@@ -59,7 +63,7 @@ void UI_DisplayFM(void)
|
||||
pPrintStr = String;
|
||||
} else {
|
||||
pPrintStr = "VFO";
|
||||
for (unsigned int i = 0; i < 20; i++) {
|
||||
for (unsigned int i = 0; i < FM_CHANNELS_MAX; i++) {
|
||||
if (gEeprom.FM_FrequencyPlaying == gFM_Channels[i]) {
|
||||
sprintf(String, "VFO(CH%02u)", i + 1);
|
||||
pPrintStr = String;
|
||||
@@ -68,7 +72,7 @@ void UI_DisplayFM(void)
|
||||
}
|
||||
}
|
||||
} else if (gFM_AutoScan) {
|
||||
sprintf(String, "A-SCAN(%u)", gFM_ChannelPosition + 1);
|
||||
sprintf(String, "A-SCAN(%u)", gFM_ChannelPosition);
|
||||
pPrintStr = String;
|
||||
} else {
|
||||
pPrintStr = "M-SCAN";
|
||||
@@ -76,7 +80,6 @@ void UI_DisplayFM(void)
|
||||
|
||||
UI_PrintString(pPrintStr, 0, 127, 3, 10); // memory, vfo, scan
|
||||
|
||||
memset(String, 0, sizeof(String));
|
||||
if (gAskToSave || (gEeprom.FM_IsMrMode && gInputBoxIndex > 0)) {
|
||||
UI_GenerateChannelString(String, gFM_ChannelPosition);
|
||||
} else if (gAskToDelete) {
|
||||
|
||||
+90
-10
@@ -22,12 +22,10 @@
|
||||
#include "ui/helper.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "misc.h"
|
||||
#include "settings.h"
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0]))
|
||||
#endif
|
||||
|
||||
void UI_GenerateChannelString(char *pString, const uint8_t Channel)
|
||||
void UI_GenerateChannelString(char *pString, const uint16_t Channel)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
@@ -42,26 +40,30 @@ void UI_GenerateChannelString(char *pString, const uint8_t Channel)
|
||||
pString[2] = '-';
|
||||
for (i = 0; i < 2; i++)
|
||||
pString[i + 3] = (gInputBox[i] == 10) ? '-' : gInputBox[i] + '0';
|
||||
|
||||
pString[5] = 0;
|
||||
}
|
||||
|
||||
void UI_GenerateChannelStringEx(char *pString, const bool bShowPrefix, const uint8_t ChannelNumber)
|
||||
void UI_GenerateChannelStringEx(char *pString, const bool bShowPrefix, const uint16_t ChannelNumber)
|
||||
{
|
||||
if (gInputBoxIndex > 0) {
|
||||
for (unsigned int i = 0; i < 3; i++) {
|
||||
for (unsigned int i = 0; i < 4; i++) {
|
||||
pString[i] = (gInputBox[i] == 10) ? '-' : gInputBox[i] + '0';
|
||||
}
|
||||
|
||||
pString[3] = 0;
|
||||
pString[4] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bShowPrefix) {
|
||||
// BUG here? Prefixed NULLs are allowed
|
||||
sprintf(pString, "CH-%03u", ChannelNumber + 1);
|
||||
} else if (ChannelNumber == 0xFF) {
|
||||
sprintf(pString, "CH-%04u", ChannelNumber + 1);
|
||||
} else if (ChannelNumber == MR_CHANNEL_LAST + 1) {
|
||||
strcpy(pString, "None");
|
||||
} else if (ChannelNumber == 0xFFFF) {
|
||||
strcpy(pString, "NULL");
|
||||
} else {
|
||||
sprintf(pString, "%03u", ChannelNumber + 1);
|
||||
sprintf(pString, "%04u", ChannelNumber + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +118,35 @@ void UI_PrintStringSmallNormal(const char *pString, uint8_t Start, uint8_t End,
|
||||
UI_PrintStringSmall(pString, Start, End, Line, ARRAY_SIZE(gFontSmall[0]), (const uint8_t *)gFontSmall);
|
||||
}
|
||||
|
||||
void UI_PrintStringSmallNormalInverse(const char *pString, uint8_t Start, uint8_t End, uint8_t Line)
|
||||
{
|
||||
// First draw the string normally
|
||||
UI_PrintStringSmallNormal(pString, Start, End, Line);
|
||||
|
||||
// Now invert the framebuffer bits for the rendered area
|
||||
uint8_t len = strlen(pString);
|
||||
uint8_t char_width = 7; // small font is typically 6px wide
|
||||
|
||||
uint8_t x_start = Start;
|
||||
uint8_t x_end = Start + (len * char_width) + 1;
|
||||
|
||||
if (End != 0 && x_end > End)
|
||||
x_end = End;
|
||||
|
||||
//gFrameBuffer[Line][x_start - 2] ^= 0x3E;
|
||||
gFrameBuffer[Line][x_start - 1] ^= 0x7F;
|
||||
//gFrameBuffer[Line][x_start - 1] ^= 0xFF;
|
||||
for (uint8_t x = x_start; x < x_end; x++)
|
||||
{
|
||||
gFrameBuffer[Line][x] ^= 0xFF;
|
||||
gFrameBuffer[Line - 1][x] ^= 0x80;
|
||||
}
|
||||
//gFrameBuffer[Line][x_end + 0] ^= 0xFF;
|
||||
gFrameBuffer[Line][x_end + 0] ^= 0x7F;
|
||||
//gFrameBuffer[Line][x_end + 1] ^= 0x3E;
|
||||
}
|
||||
|
||||
|
||||
void UI_PrintStringSmallBold(const char *pString, uint8_t Start, uint8_t End, uint8_t Line)
|
||||
{
|
||||
#ifdef ENABLE_SMALL_BOLD
|
||||
@@ -293,6 +324,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)
|
||||
@@ -350,3 +425,8 @@ void UI_DisplayClear()
|
||||
{
|
||||
memset(gFrameBuffer, 0, sizeof(gFrameBuffer));
|
||||
}
|
||||
|
||||
void UI_StatusClear()
|
||||
{
|
||||
memset(gStatusLine, 0, sizeof(gStatusLine));
|
||||
}
|
||||
+7
-2
@@ -20,10 +20,11 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void UI_GenerateChannelString(char *pString, const uint8_t Channel);
|
||||
void UI_GenerateChannelStringEx(char *pString, const bool bShowPrefix, const uint8_t ChannelNumber);
|
||||
void UI_GenerateChannelString(char *pString, const uint16_t Channel);
|
||||
void UI_GenerateChannelStringEx(char *pString, const bool bShowPrefix, const uint16_t ChannelNumber);
|
||||
void UI_PrintString(const char *pString, uint8_t Start, uint8_t End, uint8_t Line, uint8_t Width);
|
||||
void UI_PrintStringSmallNormal(const char *pString, uint8_t Start, uint8_t End, uint8_t Line);
|
||||
void UI_PrintStringSmallNormalInverse(const char *pString, uint8_t Start, uint8_t End, uint8_t Line);
|
||||
void UI_PrintStringSmallBold(const char *pString, uint8_t Start, uint8_t End, uint8_t Line);
|
||||
void UI_PrintStringSmallBufferNormal(const char *pString, uint8_t *buffer);
|
||||
void UI_PrintStringSmallBufferBold(const char *pString, uint8_t * buffer);
|
||||
@@ -37,10 +38,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
|
||||
|
||||
+1145
-173
File diff suppressed because it is too large.
Load diff
+24
-2
@@ -17,14 +17,22 @@
|
||||
#ifndef UI_MAIN_H
|
||||
#define UI_MAIN_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.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 +44,26 @@ 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);
|
||||
bool UI_MAIN_ShouldHoldScanResume(void);
|
||||
#else
|
||||
static inline void UI_MAIN_NotifyScanProgressDataChanged(void) {}
|
||||
static inline void UI_MAIN_NotifyScanListChanged(void) {}
|
||||
static inline bool UI_MAIN_ShouldHoldScanResume(void) { return false; }
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_AGC_SHOW_DATA
|
||||
void UI_MAIN_PrintAGC(bool force);
|
||||
#endif
|
||||
|
||||
+676
-217
File diff suppressed because it is too large.
Load diff
+100
-37
@@ -23,7 +23,7 @@
|
||||
#include "audio.h" // VOICE_ID_t
|
||||
#include "settings.h"
|
||||
|
||||
typedef struct {
|
||||
typedef struct __attribute__((packed)) {
|
||||
const char name[7]; // menu display area only has room for 6 characters
|
||||
uint8_t menu_id;
|
||||
} t_menu_item;
|
||||
@@ -65,9 +65,7 @@ enum
|
||||
#endif
|
||||
MENU_SC_REV,
|
||||
MENU_AUTOLK,
|
||||
MENU_S_ADD1,
|
||||
MENU_S_ADD2,
|
||||
MENU_S_ADD3,
|
||||
MENU_LIST_CH,
|
||||
MENU_STE,
|
||||
MENU_RP_STE,
|
||||
MENU_MIC,
|
||||
@@ -75,9 +73,9 @@ enum
|
||||
MENU_COMPAND,
|
||||
MENU_1_CALL,
|
||||
MENU_S_LIST,
|
||||
MENU_SLIST1,
|
||||
MENU_SLIST2,
|
||||
MENU_SLIST3,
|
||||
MENU_S_PRI,
|
||||
MENU_S_PRI_CH_1,
|
||||
MENU_S_PRI_CH_2,
|
||||
#ifdef ENABLE_ALARM
|
||||
MENU_AL_MOD,
|
||||
#endif
|
||||
@@ -139,6 +137,9 @@ enum
|
||||
MENU_SET_MET,
|
||||
MENU_SET_GUI,
|
||||
MENU_SET_TMR,
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER
|
||||
MENU_SET_SCN,
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_NARROWER
|
||||
MENU_SET_NFM,
|
||||
#endif
|
||||
@@ -151,6 +152,10 @@ enum
|
||||
#ifdef ENABLE_NOAA
|
||||
MENU_NOAA_S,
|
||||
#endif
|
||||
MENU_SET_NAV,
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
MENU_SET_AUD,
|
||||
#endif
|
||||
#endif
|
||||
MENU_BATCAL, // battery voltage calibration
|
||||
MENU_F1SHRT,
|
||||
@@ -158,78 +163,136 @@ 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
|
||||
};
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_MENU_CAT
|
||||
// Categories (niveau 1) — l'ordre = ordre d'affichage de l'ecran des categories.
|
||||
enum {
|
||||
CAT_CHANNELS = 0,
|
||||
CAT_SCAN,
|
||||
CAT_KEYS,
|
||||
CAT_POWER,
|
||||
CAT_DISPLAY,
|
||||
CAT_TIMERS,
|
||||
CAT_AUDIO,
|
||||
CAT_RADIO,
|
||||
CAT_DTMF,
|
||||
CAT_SERVICE, // menu cache : n'apparait que si gF_LOCK
|
||||
CAT_ALL, // liste plate complete, ordre et numeros d'origine
|
||||
CAT_COUNT
|
||||
};
|
||||
|
||||
#define MENU_LEVEL_CAT 0
|
||||
#define MENU_LEVEL_ITEMS 1
|
||||
|
||||
extern const char *const CategoryNames[];
|
||||
extern uint8_t gMenuCategory;
|
||||
extern uint8_t gMenuLevel;
|
||||
extern uint8_t gCatOrder[];
|
||||
extern uint8_t gMenuCatCursor;
|
||||
extern uint8_t gCatLastPos[];
|
||||
|
||||
void UI_MENU_BuildCategoryScreen(void);
|
||||
uint8_t UI_MENU_CategoryItemCount(uint8_t cat);
|
||||
#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* 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;
|
||||
|
||||
extern uint8_t gMenuCursor;
|
||||
extern uint8_t gMenuIndices[];
|
||||
|
||||
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();
|
||||
uint8_t UI_MENU_GetMenuIdx(uint8_t id);
|
||||
uint8_t UI_MENU_GetViewPos(uint8_t id);
|
||||
void UI_MENU_BuildView(void);
|
||||
|
||||
#endif
|
||||
+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();
|
||||
}
|
||||
+176
-115
@@ -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,28 +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
|
||||
switch(gEeprom.SCAN_LIST_DEFAULT) {
|
||||
case 0:
|
||||
memcpy(line + 0, BITMAP_ScanList0, sizeof(BITMAP_ScanList0));
|
||||
break;
|
||||
case 1:
|
||||
memcpy(line + 0, BITMAP_ScanList1, sizeof(BITMAP_ScanList1));
|
||||
break;
|
||||
case 2:
|
||||
memcpy(line + 0, BITMAP_ScanList2, sizeof(BITMAP_ScanList2));
|
||||
break;
|
||||
case 3:
|
||||
memcpy(line + 0, BITMAP_ScanList3, sizeof(BITMAP_ScanList3));
|
||||
break;
|
||||
case 4:
|
||||
memcpy(line + 0, BITMAP_ScanList123, sizeof(BITMAP_ScanList123));
|
||||
break;
|
||||
case 5:
|
||||
memcpy(line + 0, BITMAP_ScanListAll, sizeof(BITMAP_ScanListAll));
|
||||
break;
|
||||
uint8_t end = 0;
|
||||
|
||||
if(gEeprom.SCAN_LIST_DEFAULT == MR_CHANNELS_LIST + 1)
|
||||
{
|
||||
strcpy(str, "ALL");
|
||||
end = 14;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *name = gListName[gEeprom.SCAN_LIST_DEFAULT - 1];
|
||||
|
||||
// Check if name is valid
|
||||
if (!IsEmptyName(name, sizeof(gListName[0]))) {
|
||||
sprintf(str, "%.3s", name);
|
||||
end = 14;
|
||||
} else {
|
||||
sprintf(str, "%02d", gEeprom.SCAN_LIST_DEFAULT);
|
||||
end = 10;
|
||||
}
|
||||
}
|
||||
|
||||
if (gEeprom.SCAN_LIST_ENABLED) {
|
||||
strcat(str, "+");
|
||||
end += 4;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
GUI_DisplaySmallestInverse(str, 2, 0, true, true, end);
|
||||
#else
|
||||
GUI_DisplaySmallest(str, 2, 1, true, true);
|
||||
|
||||
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));
|
||||
@@ -143,47 +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
|
||||
{
|
||||
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) {
|
||||
@@ -195,14 +253,16 @@ void UI_DisplayStatus()
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
// PTT indicator
|
||||
if (gSetting_set_ptt_session) {
|
||||
memcpy(line + x, gFontPttOnePush, sizeof(gFontPttOnePush));
|
||||
x1 = x + sizeof(gFontPttOnePush) + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(line + x, gFontPttClassic, sizeof(gFontPttClassic));
|
||||
x1 = x + sizeof(gFontPttClassic) + 1;
|
||||
if(!gAirCopyBootMode) {
|
||||
if (gSetting_set_ptt_session) {
|
||||
memcpy(line + x, gFontPttOnePush, sizeof(gFontPttOnePush));
|
||||
x1 = x + sizeof(gFontPttOnePush) + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(line + x, gFontPttClassic, sizeof(gFontPttClassic));
|
||||
x1 = x + sizeof(gFontPttClassic) + 1;
|
||||
}
|
||||
}
|
||||
x += sizeof(gFontPttClassic) + 3;
|
||||
#endif
|
||||
@@ -218,15 +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) {
|
||||
@@ -235,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) {
|
||||
@@ -255,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,
|
||||
|
||||
+255
-89
@@ -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,21 +243,32 @@ void UI_DisplayWelcome(void)
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
ST7565_BlitStatusLine();
|
||||
ST7565_BlitFullScreen();
|
||||
|
||||
|
||||
if (gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_NONE || gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_SOUND) {
|
||||
ST7565_FillScreen(0x00);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_NONE || gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_FULL_SCREEN) {
|
||||
ST7565_FillScreen(0xFF);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
memset(WelcomeString0, 0, sizeof(WelcomeString0));
|
||||
memset(WelcomeString1, 0, sizeof(WelcomeString1));
|
||||
#ifdef ENABLE_FEAT_F4HWN_LOGO
|
||||
else if (gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_LOGO) {
|
||||
UI_LoadLogo();
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
char WelcomeString0[16];
|
||||
char WelcomeString1[16];
|
||||
char WelcomeString2[16];
|
||||
char WelcomeString3[32];
|
||||
|
||||
// 0x0EB0
|
||||
PY25Q16_ReadBuffer(0x007020, WelcomeString0, 16);
|
||||
PY25Q16_ReadBuffer(0x00A0C8, WelcomeString0, 16);
|
||||
// 0x0EC0
|
||||
PY25Q16_ReadBuffer(0x007030, WelcomeString1, 16);
|
||||
PY25Q16_ReadBuffer(0x00A0D8, WelcomeString1, 16);
|
||||
|
||||
sprintf(WelcomeString2, "%u.%02uV %u%%",
|
||||
gBatteryVoltageAverage / 100,
|
||||
@@ -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}
|
||||
|
||||
Loaded 100 of 174 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user