Compare commits

...
117 Commits
Author SHA1 Message Date
Armel FAUVEAU 064a203db0 Merge pull request #243 from armel/feature_update_v5
Feature update v5
2026-03-28 23:23:05 +01:00
Armel FAUVEAU 5987ec7b64 New version 5.3.0 2026-03-28 23:22:00 +01:00
Armel FAUVEAU d72524879b Merge pull request #242 from mrkusypl/feature_update_v5_2
Eliminating high bars in Audio Scope after releasing the DTMF button
2026-03-28 21:03:49 +01:00
mrkusypl db46cea0eb Eliminating high bars in Audio Scope after releasing the DTMF/1750 button 2026-03-28 18:15:20 +01:00
Armel FAUVEAU 8f75d6813b Merge pull request #241 from mrkusypl/feature_update_v5_2
Fix for the Audio Scope in OnePush mode
2026-03-28 01:55:15 +01:00
mrkusypl 1f577ec82e Fix for the Audio Scope in OnePush mode 2026-03-28 01:41:19 +01:00
Armel FAUVEAU 1f801757e9 Merge pull request #239 from mrkusypl/feature_update_v5_2
An even better improvement to Audio Scope
2026-03-27 19:25:52 +01:00
mrkusypl 4588e58af5 An even better improvement to Audio Scope 2026-03-27 17:18:16 +01:00
Armel FAUVEAU 6fe1bec80c Improve Audio Scope 2026-03-27 01:50:16 +01:00
Armel FAUVEAU a2735b1167 Merge pull request #238 from mrkusypl/feature_update_v5_2
Optimizing the Audio Scope code
2026-03-26 18:49:36 +01:00
mrkusypl 249b0b2bb7 Optimizing the Audio Scope code 2026-03-26 17:47:21 +01:00
Armel FAUVEAU 62320f5fbe Update donors 2026-03-26 05:00:43 +01:00
Armel FAUVEAU cebbda72ec Add audio scope feature just for fun 2026-03-26 04:58:49 +01:00
Armel FAUVEAU c51033db77 bk4829: align RX squelch REG_4E with stock firmware 2026-03-26 03:24:17 +01:00
Armel FAUVEAU f863c41d40 Merge pull request #234 from mrkusypl/feature_update_v5_2
Improvements to the FM radio and side buttons
2026-03-26 02:29:21 +01:00
mrkusypl ab7b4ca094 Adding the ‘gRequestSaveSquelch’ flag to the squelch level save 2026-03-25 22:44:20 +01:00
mrkusypl 179c271618 Save the squelch level adjusted using F+UP/DOWN 2026-03-25 21:36:38 +01:00
mrkusypl 2e6dfe3bc7 Revert changes related to RescueOps 2026-03-25 19:22:23 +01:00
Armel FAUVEAU 122cf48d3e Clarify BK4829 REG_43 docs and keep stock AM/W/N behavior coherent 2026-03-24 05:09:57 +01:00
Armel FAUVEAU dea747c628 Fix W/N (issue #233) 2026-03-24 03:49:52 +01:00
mrkusypl bbbc553064 Improvements to the FM radio and side buttons 2026-03-24 00:31:45 +01:00
Armel FAUVEAU 43c40ae9a0 Merge pull request #232 from mrkusypl/feature_update_v5_2
Displaying power bars during TX when using SetPwr; Restore full VFO state on long press of EXIT
2026-03-23 00:32:32 +01:00
Armel FAUVEAU 6fdb17d64a Align BK4829 AGC runtime with stock firmware 2026-03-23 00:31:16 +01:00
mrkusypl aa69d698a7 Code optimization (32 B); Restore full VFO state on long press of EXIT 2026-03-22 20:58:27 +01:00
mrkusypl f4a32b4350 Displaying power bars during TX when using SetPwr 2026-03-22 16:19:08 +01:00
Armel FAUVEAU 688b3a5c77 Merge pull request #229 from mrkusypl/feature_update_v5_2
Minor code refactoring (24 B)
2026-03-22 04:08:24 +01:00
Armel FAUVEAU ff336cd5d9 Retry this... 2026-03-22 03:00:40 +01:00
mrkusypl 85dae05ad3 Minor code refactoring (24 B) 2026-03-21 19:13:51 +01:00
Armel FAUVEAU 2939620066 Update donors 2026-03-21 15:45:18 +01:00
Armel FAUVEAU 0b3a390cb9 Update donors 2026-03-21 00:49:40 +01:00
Armel FAUVEAU 3d4ffd7900 Fix VFO frequency input final digit handling 2026-03-21 00:16:14 +01:00
Armel FAUVEAU 06907f9b67 Merge pull request #226 from mrkusypl/v5_refactor
Enable beeps for Aircopy, minor code refactoring
2026-03-20 23:52:00 +01:00
mrkusypl 7c343a2b3e Enable beeps for Aircopy, minor code refactoring 2026-03-20 18:44:57 +01:00
Armel FAUVEAU b4f66e9bf3 Merge pull request #225 from mrkusypl/v5_refactor
Major code refactoring
2026-03-20 13:52:08 +01:00
mrkusypl 138e078984 Fix for S-meter calculation, AirCopy - removed redundant code 2026-03-20 10:47:50 +01:00
mrkusypl e28177cb9b Major code refactoring 2026-03-19 23:09:39 +01:00
Armel FAUVEAU a1f2f0cdb9 Fix: replace map() with IARU-compliant S-meter thresholds 2026-03-19 03:41:54 +01:00
Armel FAUVEAU e77e558d64 Revert welcome.c and fix default keepAlive to 3 (PR #218) 2026-03-18 01:07:03 +01:00
Armel FAUVEAU 7d5e5b7d97 Merge pull request #218 from mrkusypl/v5_fix
Code refactoring; improvements to K5Viewer and the UI
2026-03-17 00:30:07 +01:00
mrkusypl 315d7a6b59 An even better way to fix a button that keeps getting stuck in K5Viewer 2026-03-16 23:57:17 +01:00
mrkusypl 67231fd326 Replacing 'write_ptr' with 'read_ptr' when deleting old bytes 2026-03-16 15:04:45 +01:00
mrkusypl f4d8abd7d9 Increase the keepAlive value, fix the CHIRP connection while maintaining the fix for the button getting stuck 2026-03-16 14:57:57 +01:00
mrkusypl 9574376d38 Code refactoring; improvements to K5Viewer and the UI 2026-03-16 00:52:28 +01:00
Armel FAUVEAU f2666a6248 Screenshot force full frame on reconnection 2026-03-15 17:35:56 +01:00
Armel FAUVEAU ee5878d641 Merge pull request #216 from mrkusypl/v5_k5viewer_input
K5Viewer: More reliable key handling
2026-03-15 02:02:35 +01:00
mrkusypl 470d1959ba K5Viewer: More reliable key handling 2026-03-15 01:31:53 +01:00
Armel FAUVEAU f5d6f5782b Fix breakout: reliable key handling from K5Viewer (issue #214) 2026-03-14 19:36:33 +01:00
Armel FAUVEAU 5cb6bbdf7a Overlap between Live DTMF and Unlock Keyboard (issue #215) 2026-03-14 17:47:23 +01:00
Armel FAUVEAU 9f0547f6af Fix spectrum: reliable serial key input + USB-C unplug freeze fix (issue #212) 2026-03-14 05:34:17 +01:00
Armel FAUVEAU fcf94e34ce Merge pull request #211 from mrkusypl/v5_backlight_fade
Backlight fading and improved backlight control in spectrum
2026-03-12 02:36:54 +01:00
mrkusypl 5d88fda158 Added backlight fading, improved backlight control in spectrum 2026-03-11 22:52:10 +01:00
Armel FAUVEAU 7b7186e293 Improve one and two-letter scan list names (issue #206) 2026-03-10 04:37:10 +01:00
Armel FAUVEAU d83c6451c8 Fix EX label 2026-03-10 04:20:38 +01:00
Armel FAUVEAU eaa05f107e Improve one and two-letter scan list names (issue #206) 2026-03-10 04:00:43 +01:00
Armel FAUVEAU d0fdca4dc1 BYP and RAW for BK4829 2026-03-10 02:54:25 +01:00
Armel FAUVEAU 37f18972e7 BYP and RAW for BK4829 2026-03-09 05:24:12 +01:00
Armel FAUVEAU 7471ac90c5 Update file 2026-03-09 01:15:38 +01:00
Armel FAUVEAU d826cbd138 Reduce the lag between consecutive RX on both frequencies under DW mode (issue #198) 2026-03-09 01:02:19 +01:00
Armel FAUVEAU 5293a8526d Update donors 2026-03-08 22:14:28 +01:00
Armel FAUVEAU 74aa7b354e SetVol(68/69) keeps the changes despite Exit key is pressed (issue #200) 2026-03-08 22:09:53 +01:00
Armel FAUVEAU ffddf9a9b3 Increase PWM frequency to suppress audio interference (issue #195) 2026-03-08 00:17:33 +01:00
Armel FAUVEAU 77785d14c2 Code refactoring 2026-03-06 05:17:22 +01:00
Armel FAUVEAU f715f776a4 Write dBmCorrTable only if flash zone is erased 2026-03-06 05:14:50 +01:00
Armel FAUVEAU 8a8752f80c Merge pull request #187 from armel/feature_update_v5
New version 5.2.0
2026-03-06 02:02:39 +01:00
Armel FAUVEAU b361b5c3e2 New version 5.2.0 2026-03-06 02:02:12 +01:00
Armel FAUVEAU 98c0552ee3 Merge pull request #186 from armel/feature_update_v5
Feature update v5
2026-03-06 02:00:58 +01:00
Armel FAUVEAU 9a46bdf6bf Add mic gauge 2026-03-05 16:36:01 +01:00
Armel FAUVEAU 3f7d2e56d7 Merge pull request #184 from mrkusypl/v5_refactor
Bug fixes: Receiving the same channel in Dual Watch mode, disabling channel exclusion, changing the scan list, compilation error
2026-03-05 16:22:07 +01:00
mrkusypl 5b638e298e Bug fixes: Receiving the same channel in Dual Watch mode, disabling channel exclusion, changing the scan list, compilation error 2026-03-05 15:19:20 +01:00
kusy 6e232131b9 merge! 2026-03-05 13:52:48 +01:00
Armel FAUVEAU d9eb55fccf FM_CheckFrequencyLock() refactoring 2026-03-05 00:19:11 +01:00
Armel FAUVEAU afb317d12a Merge pull request #182 from mrkusypl/v5_refactor
Major code optimization (456 bytes saved)
2026-03-05 00:11:51 +01:00
kusy 2f90b401cb Restore FM_CheckFrequencyLock() 2026-03-04 18:06:41 +00:00
kusy 28500e1667 Major code optimization (536 bytes saved) 2026-03-04 14:10:02 +00:00
Armel FAUVEAU 4b8f8cf22a Change gDebug type 2026-03-04 01:42:38 +01:00
Armel FAUVEAU 37fb4abd2e Update donors 2026-03-03 17:45:54 +01:00
Armel FAUVEAU ec9bcba781 Increase MIC gain values 2026-02-28 00:13:39 +01:00
Armel FAUVEAU ed0f15959b Fix bug: can't build with Custom #175 2026-02-26 05:41:49 +01:00
Armel FAUVEAU 0c90b7d95c Fix freeze when using K5Viewer and USB-C 2026-02-25 04:49:38 +01:00
Armel FAUVEAU 2248b0cbbe Update donors 2026-02-25 03:16:51 +01:00
Armel FAUVEAU a244a25513 Add long press 2026-02-25 03:08:16 +01:00
Armel FAUVEAU f072b3eb02 Add virtual keyboard 2026-02-25 02:24:39 +01:00
Armel FAUVEAU 6ed0bc3436 Merge pull request #170 from mrkusypl/feature_update_v5
UI improvements, backlight toggle in FM radio, code optimization
2026-02-24 18:08:23 +01:00
kusy 793a44855d modified: App/app/app.c
modified:   App/app/fm.c
	modified:   App/app/generic.c
	modified:   App/app/main.c
2026-02-24 15:50:05 +00:00
Armel FAUVEAU 4937d0baba Improve SMeter Calibration 2026-02-23 02:43:30 +01:00
Armel FAUVEAU 29c0338c61 Fix screenshot and keyboard race 2026-02-21 18:27:13 +01:00
Armel FAUVEAU ed640588c5 Welcome refactoring 2026-02-21 04:31:43 +01:00
Armel FAUVEAU 335e1b5501 Update donors 2026-02-21 00:37:46 +01:00
Armel FAUVEAU d4e29c56a3 Improve channel input 2026-02-21 00:36:28 +01:00
Armel FAUVEAU dc08631274 Update donors 2026-02-19 15:32:33 +01:00
Armel FAUVEAU 0234900baf Add ENABLE_FEAT_F4HWN_MEM 2026-02-19 05:15:20 +01:00
Armel FAUVEAU 30aa3f13a0 Clean code 2026-02-19 02:46:53 +01:00
Armel FAUVEAU 1b170b9ff9 Improve UI 2026-02-19 02:06:20 +01:00
Armel FAUVEAU ed6b98ce46 Add strong signal correction 2026-02-17 03:18:26 +01:00
Armel FAUVEAU f680b3af36 Fix -93dBm on AIR 2026-02-16 23:16:35 +01:00
Armel FAUVEAU 9ef3379ccf Fix -93dBm on VHF 2026-02-16 23:08:39 +01:00
Armel FAUVEAU 9081b01773 Add bidirectional list navigation with UP/DOWN keys (issue #145) 2026-02-15 04:19:57 +01:00
Armel FAUVEAU 706283c16b Fix blink arrow and screenshot 2026-02-15 03:38:01 +01:00
Armel FAUVEAU e51813e1bb Fix Reset All (issue #156) 2026-02-14 23:59:44 +01:00
Armel FAUVEAU 6b4e575d1f Remove stars... 2026-02-14 18:59:20 +01:00
Armel FAUVEAU f0cca71673 Rename function 2026-02-14 18:54:49 +01:00
Armel FAUVEAU aef113eacf Remove BITMAP_Antenna 2026-02-14 18:50:14 +01:00
Armel FAUVEAU 3f8ddb0502 Fix version 2026-02-13 18:21:34 +01:00
Armel FAUVEAU 5b4b8cf2f6 Blink arrow on RX (issue #146) 2026-02-13 02:04:55 +01:00
Armel FAUVEAU ee1d68e7ef Extend Tiny GUI ? 2026-02-12 04:32:12 +01:00
Armel FAUVEAU 261aeb4fb7 Fix issue #153 2026-02-12 00:28:03 +01:00
Armel FAUVEAU 5ccb6cd7ff Fix 4 digits 2026-02-11 19:54:58 +01:00
Armel FAUVEAU fa7537d539 Frequency Band refactoring 2026-02-11 02:52:51 +01:00
Armel FAUVEAU c1a6633302 Shift VFO 2026-02-11 02:14:41 +01:00
Armel FAUVEAU e515d53aaa Improve GUI 2026-02-11 01:20:16 +01:00
Armel FAUVEAU 0ddbb0ad82 Fix issue #40 2026-02-10 05:24:17 +01:00
Armel FAUVEAU c1e5f23d72 Remove N/A 2026-02-10 04:12:59 +01:00
Armel FAUVEAU 8825eb9ac6 Fix Aircopy because of named list 2026-02-10 01:46:31 +01:00
Armel FAUVEAU 7957377388 Add named lists 2026-02-10 01:35:08 +01:00
Armel FAUVEAU 712a984b96 Update donors 2026-02-08 04:29:09 +01:00
Armel FAUVEAU 1236c3863f Restore 10-character channel name limit with MAIN ONLY 2026-02-08 03:56:13 +01:00
Armel FAUVEAU 85f1bfdb7f Fix SCAN_SAVE_CHANNEL and UI_GenerateChannelStringEx 2026-02-08 03:24:25 +01:00
61 changed files with 1987 additions and 1491 deletions

No files matched your search

+1
View File
@@ -7,6 +7,7 @@ firmware
.cache
compile_commands.json
.vscode
.claude
/docs
k5_eeprom.raw
+2
View File
@@ -161,6 +161,7 @@ enable_feature(ENABLE_SQUELCH_MORE_SENSITIVE)
enable_feature(ENABLE_FASTER_CHANNEL_SCAN)
enable_feature(ENABLE_RSSI_BAR)
enable_feature(ENABLE_AUDIO_BAR)
enable_feature(ENABLE_FEAT_F4HWN_AUDIO_SCOPE)
enable_feature(ENABLE_COPY_CHAN_TO_VFO)
enable_feature(ENABLE_REDUCE_LOW_MID_TX_POWER)
enable_feature(ENABLE_BYP_RAW_DEMODULATORS)
@@ -201,6 +202,7 @@ 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)
# ---- DEBUGGING ----
+80 -81
View File
@@ -236,7 +236,7 @@ void ACTION_Scan(bool bRestart)
}
// channel mode. Keep scanning but toggle between scan lists
RADIO_NextValidList();
RADIO_NextValidList(1);
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
SETTINGS_WriteCurrentState();
@@ -290,6 +290,8 @@ void ACTION_SwitchDemodul(void)
void ACTION_Handle(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
{
HideFKeyIcon();
if (gScreenToDisplay == DISPLAY_MAIN && gDTMF_InputMode){
// entering DTMF code
@@ -319,19 +321,23 @@ 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;
@@ -347,17 +353,46 @@ void ACTION_Handle(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
if(!(bKeyHeld && !bKeyPressed)) // don't beep on released after hold
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
if (bKeyHeld || bKeyPressed) // held
if (bKeyHeld && !bKeyPressed) // button released after hold
{
funcShort = funcLong;
if (!bKeyPressed) //ignore release if held
return;
return;
}
// held or released after short press beyond this point
#ifdef ENABLE_FMRADIO
if (gFmRadioMode) { // do not run these actions in FM radio mode
switch (func) {
case ACTION_OPT_POWER:
case ACTION_OPT_MONITOR:
case ACTION_OPT_A_B:
case ACTION_OPT_VFO_MR:
case ACTION_OPT_SWITCH_DEMODUL:
#ifdef ENABLE_VOX
case ACTION_OPT_VOX:
#endif
#ifdef ENABLE_FEAT_F4HWN
case ACTION_OPT_RXMODE:
case ACTION_OPT_MAINONLY:
case ACTION_OPT_WN:
#ifdef ENABLE_FEAT_F4HWN_AUDIO
case ACTION_OPT_RXA:
#endif
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
case ACTION_OPT_POWER_HIGH:
case ACTION_OPT_REMOVE_OFFSET:
#endif
#endif
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
return;
action_opt_table[funcShort]();
default:
break;
}
}
#endif
action_opt_table[func]();
}
@@ -503,17 +538,13 @@ void ACTION_RxMode(void)
{
static bool cycle = 0;
switch(cycle) {
case 0:
gEeprom.DUAL_WATCH = !gEeprom.DUAL_WATCH;
cycle = 1;
break;
case 1:
gEeprom.CROSS_BAND_RX_TX = !gEeprom.CROSS_BAND_RX_TX;
cycle = 0;
break;
if (cycle) {
gEeprom.CROSS_BAND_RX_TX = !gEeprom.CROSS_BAND_RX_TX;
} else {
gEeprom.DUAL_WATCH = !gEeprom.DUAL_WATCH;
}
cycle = !cycle;
ACTION_Update();
}
@@ -523,22 +554,18 @@ 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();
}
@@ -553,64 +580,36 @@ void ACTION_RxA(void)
void ACTION_Ptt(void)
{
gSetting_set_ptt_session = !gSetting_set_ptt_session;
ACTION_Update();
}
void ACTION_Wn(void)
{
if (gRxVfo->Modulation == MODULATION_AM)
const bool isRx = FUNCTION_IsRx();
VFO_Info_t *pVfo = isRx ? gRxVfo : gTxVfo;
pVfo->CHANNEL_BANDWIDTH = !pVfo->CHANNEL_BANDWIDTH;
if (pVfo->Modulation == MODULATION_AM)
{
BK4819_SetFilterBandwidth(BK4819_FILTER_BW_AM, true);
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
}
+57 -96
View File
@@ -89,12 +89,13 @@ DECLARE_AIRCOPY_BANK(1)
static const AIRCOPY_Segment_t AIRCOPY_Segments_Settings[] = {
{ 0xA000, 0xA170, AIRCOPY_WRITE_BYTES },
{ 0x880E, 0x886E, AIRCOPY_WRITE_BYTES },
};
static const AIRCOPY_TransferMap_t AIRCOPY_Map_Settings = {
.segments = AIRCOPY_Segments_Settings,
.num_segments = 1,
.total_blocks = 6
.num_segments = 2,
.total_blocks = 8
};
// Finally
@@ -138,7 +139,7 @@ static void AIRCOPY_clear()
crc[i] = 0;
}
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
getScreenShot(true);
SCREENSHOT_Update(true);
#endif
}
@@ -166,11 +167,18 @@ static inline void AIRCOPY_CheckComplete(void)
{
gAircopyState = AIRCOPY_COMPLETE;
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
getScreenShot(false);
SCREENSHOT_Update(false);
#endif
}
}
static inline void AIRCOPY_Obfuscation(void)
{
for (unsigned int i = 0; i < 34; i++) {
g_FSK_Buffer[i + 1] ^= Obfuscation[i % 8];
}
}
// ============================================================================
// Send/Receive Functions
// ============================================================================
@@ -211,7 +219,7 @@ bool AIRCOPY_SendMessage(void)
if (CurrentSegmentIndex >= map->num_segments) {
gAircopyState = AIRCOPY_COMPLETE;
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
getScreenShot(false);
SCREENSHOT_Update(false);
#endif
return 0;
}
@@ -222,9 +230,7 @@ bool AIRCOPY_SendMessage(void)
g_FSK_Buffer[34] = CRC_Calculate(&g_FSK_Buffer[1], 2 + 64);
for (unsigned int i = 0; i < 34; i++) {
g_FSK_Buffer[i + 1] ^= Obfuscation[i % 8];
}
AIRCOPY_Obfuscation();
RADIO_SetTxParameters();
@@ -260,9 +266,7 @@ void AIRCOPY_StorePacket(void)
return;
}
for (unsigned int i = 0; i < 34; i++) {
g_FSK_Buffer[i + 1] ^= Obfuscation[i % 8];
}
AIRCOPY_Obfuscation();
uint16_t Crc = CRC_Calculate(&g_FSK_Buffer[1], 2 + 64);
if (g_FSK_Buffer[34] != Crc) {
@@ -272,25 +276,9 @@ void AIRCOPY_StorePacket(void)
}
uint16_t Offset = g_FSK_Buffer[1];
const AIRCOPY_TransferMap_t *map = AIRCOPY_GetCurrentMap();
// Validate offset is in the map
bool validOffset = false;
for (uint16_t i = 0; i < map->num_segments; i++) {
if (Offset >= map->segments[i].start_offset && Offset < map->segments[i].end_offset) {
validOffset = true;
break;
}
}
if (!validOffset) {
gErrorsDuringAirCopy++;
AIRCOPY_CheckComplete();
return;
}
const AIRCOPY_Segment_t *seg = AIRCOPY_FindSegmentForOffset(Offset);
if (seg == NULL) {
gErrorsDuringAirCopy++;
AIRCOPY_CheckComplete();
@@ -324,20 +312,27 @@ void AIRCOPY_StorePacket(void)
AIRCOPY_CheckComplete();
}
static void AIRCOPY_InitTransfer(bool isSendMode)
{
gAircopyStep = 1;
gFSKWriteIndex = 0;
gAirCopyBlockNumber = 0;
gInputBoxIndex = 0;
gAirCopyIsSendMode = isSendMode;
AIRCOPY_clear();
gAircopyState = AIRCOPY_TRANSFER;
}
// ============================================================================
// Key Processing
// ============================================================================
static void AIRCOPY_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
static void AIRCOPY_Key_DIGITS(KEY_Code_t Key)
{
if (bKeyHeld || !bKeyPressed) {
return;
}
INPUTBOX_Append(Key);
gRequestDisplayScreen = DISPLAY_AIRCOPY;
if (gInputBoxIndex < 6) {
#ifdef ENABLE_VOICE
gAnotherVoiceID = (VOICE_ID_t)Key;
@@ -372,62 +367,34 @@ static void AIRCOPY_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
BK4819_ResetFSK();
return;
}
gRequestDisplayScreen = DISPLAY_AIRCOPY;
}
static void AIRCOPY_Key_EXIT(bool bKeyPressed, bool bKeyHeld)
static void AIRCOPY_Key_EXIT()
{
if (bKeyHeld || !bKeyPressed) {
return;
}
if (gInputBoxIndex == 0) {
gAircopyStep = 1;
gFSKWriteIndex = 0;
gAirCopyBlockNumber = 0;
gInputBoxIndex = 0;
AIRCOPY_InitTransfer(0); // Mode: Receive
gErrorsDuringAirCopy = lErrorsDuringAirCopy = 0;
gAirCopyIsSendMode = 0;
AIRCOPY_clear();
BK4819_PrepareFSKReceive();
gAircopyState = AIRCOPY_TRANSFER;
} else {
gInputBox[--gInputBoxIndex] = 10;
}
gRequestDisplayScreen = DISPLAY_AIRCOPY;
}
static void AIRCOPY_Key_MENU(bool bKeyPressed, bool bKeyHeld)
static void AIRCOPY_Key_MENU()
{
if (bKeyHeld || !bKeyPressed) {
return;
}
gAircopyStep = 1;
gFSKWriteIndex = 0;
gAirCopyBlockNumber = 0;
gInputBoxIndex = 0;
gAirCopyIsSendMode = 1;
AIRCOPY_InitTransfer(1); // Mode: Send
g_FSK_Buffer[0] = 0xABCD;
g_FSK_Buffer[1] = 0;
g_FSK_Buffer[35] = 0xDCBA;
AIRCOPY_clear();
GUI_DisplayScreen();
gAircopyState = AIRCOPY_TRANSFER;
}
static void AIRCOPY_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
static void AIRCOPY_Key_UP_DOWN(int8_t Direction)
{
if (bKeyHeld || !bKeyPressed) {
return;
if (!gEeprom.SET_NAV) {
Direction = -Direction;
}
switch(Direction)
@@ -439,46 +406,40 @@ static void AIRCOPY_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Directio
gAircopyCurrentMapIndex = (gAircopyCurrentMapIndex + AIRCOPY_NUM_MAPS - 1) % AIRCOPY_NUM_MAPS;
break;
}
gRequestDisplayScreen = DISPLAY_AIRCOPY;
}
void AIRCOPY_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
{
if (bKeyHeld || !bKeyPressed) {
return;
}
if (Key != KEY_PTT) {
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
}
switch (Key) {
case KEY_0:
case KEY_1:
case KEY_2:
case KEY_3:
case KEY_4:
case KEY_5:
case KEY_6:
case KEY_7:
case KEY_8:
case KEY_9:
AIRCOPY_Key_DIGITS(Key, bKeyPressed, bKeyHeld);
case KEY_0...KEY_9:
AIRCOPY_Key_DIGITS(Key);
break;
case KEY_MENU:
AIRCOPY_Key_MENU(bKeyPressed, bKeyHeld);
AIRCOPY_Key_MENU();
break;
case KEY_EXIT:
AIRCOPY_Key_EXIT(bKeyPressed, bKeyHeld);
AIRCOPY_Key_EXIT();
break;
case KEY_UP:
if(gEeprom.SET_NAV == 0)
AIRCOPY_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
else
AIRCOPY_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
break;
case KEY_DOWN:
if(gEeprom.SET_NAV == 0)
AIRCOPY_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
else
AIRCOPY_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
AIRCOPY_Key_UP_DOWN(Key == KEY_UP ? 1 : -1);
break;
case KEY_PTT:
break;
default:
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
break;
}
gRequestDisplayScreen = DISPLAY_AIRCOPY;
}
#endif
+106 -131
View File
@@ -273,18 +273,9 @@ static void HandleReceive(void)
break;
case CODE_TYPE_CONTINUOUS_TONE:
if (gFoundCTCSS && gFoundCTCSSCountdown_10ms == 0)
{
gFoundCTCSS = false;
gFoundCDCSS = false;
Mode = END_OF_RX_MODE_END;
goto Skip;
}
break;
case CODE_TYPE_DIGITAL:
case CODE_TYPE_REVERSE_DIGITAL:
if (gFoundCDCSS && gFoundCDCSSCountdown_10ms == 0)
if ((gFoundCTCSS && gFoundCTCSSCountdown_10ms == 0) || (gFoundCDCSS && gFoundCDCSSCountdown_10ms == 0))
{
gFoundCTCSS = false;
gFoundCDCSS = false;
@@ -544,7 +535,17 @@ void APP_StartListening(FUNCTION_Type_t function)
gEeprom.DUAL_WATCH != DUAL_WATCH_OFF)
{ // not scanning, dual watch is enabled
gDualWatchCountdown_10ms = dual_watch_count_after_2_10ms;
//gDualWatchCountdown_10ms = dual_watch_count_after_2_10ms;
const bool isMainTxDualRx =
(gEeprom.DUAL_WATCH != DUAL_WATCH_OFF) &&
(gEeprom.CROSS_BAND_RX_TX != CROSS_BAND_OFF);
// Use a short hold only for MAIN TX DUAL RX, keep legacy hold otherwise
gDualWatchCountdown_10ms = isMainTxDualRx
? dual_watch_count_after_2_10ms / 4 // Short timer = 420 / 4 ...
: dual_watch_count_after_2_10ms;
gScheduleDualWatch = false;
// when crossband is active only the main VFO should be used for TX
@@ -891,6 +892,12 @@ static void HandleVox(void)
void APP_Update(void)
{
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
// Parse incoming packets on every tick so serial keys are never missed,
// regardless of whether the screen needs redrawing.
SCREENSHOT_ParseInput();
#endif
#ifdef ENABLE_VOICE
if (gFlagPlayQueuedVoice) {
AUDIO_PlayQueuedVoice();
@@ -1133,14 +1140,7 @@ void APP_Update(void)
// go back to sleep
#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
@@ -1165,8 +1165,21 @@ void APP_Update(void)
}
}
void StopTransmitting(void) {
ProcessKey(KEY_PTT, false, false);
gPttIsPressed = false;
if (gKeyReading1 != KEY_INVALID)
gPttWasReleased = true;
#ifdef ENABLE_FEAT_F4HWN
#if defined(ENABLE_FEAT_F4HWN_CTR) || defined(ENABLE_FEAT_F4HWN_INV)
ST7565_ContrastAndInv();
#endif
#endif
}
// called every 10ms
static void CheckKeys(void)
void CheckKeys(void)
{
#ifdef ENABLE_DTMF_CALLING
if(gSetting_KILLED){
@@ -1181,75 +1194,63 @@ static void CheckKeys(void)
#endif
// -------------------- PTT ------------------------
const bool serialConfigInProgress = SerialConfigInProgress();
#ifdef ENABLE_FEAT_F4HWN
const bool isPressed = GPIO_IsPttPressed() && !serialConfigInProgress;
#else
const bool isPressed = !GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT) && !serialConfigInProgress;
#endif
#ifdef ENABLE_FEAT_F4HWN
if (gSetting_set_ptt_session)
{
if (GPIO_IsPttPressed() && !SerialConfigInProgress() && gPttOnePushCounter == 0)
{ // PTT pressed
if (++gPttDebounceCounter >= 3) // 30ms
{ // start transmitting
boot_counter_10ms = 0;
if ((isPressed && (gPttOnePushCounter == 0 || gPttOnePushCounter == 2)) ||
(!isPressed && (gPttOnePushCounter == 1 || gPttOnePushCounter == 3)) ||
serialConfigInProgress)
{
if (++gPttDebounceCounter >= 3 || (serialConfigInProgress && gPttOnePushCounter > 0))
{
gPttDebounceCounter = 0;
gPttIsPressed = true;
gPttOnePushCounter = 1;
ProcessKey(KEY_PTT, true, false);
if (gPttOnePushCounter == 0)
{ // start transmitting
boot_counter_10ms = 0;
gPttIsPressed = true;
gPttOnePushCounter = 1;
ProcessKey(KEY_PTT, true, false);
}
else if (gPttOnePushCounter == 3 || serialConfigInProgress)
{ // stop transmitting
StopTransmitting();
gPttOnePushCounter = 0;
}
else
gPttOnePushCounter++;
}
}
else if ((!GPIO_IsPttPressed() || SerialConfigInProgress()) && gPttOnePushCounter == 1)
{
// PTT released or serial comms config in progress
if (++gPttDebounceCounter >= 3 || SerialConfigInProgress()) // 30ms
{ // stop transmitting
gPttOnePushCounter = 2;
}
}
else if (GPIO_IsPttPressed() && !SerialConfigInProgress() && gPttOnePushCounter == 2)
{ // PTT pressed again
if (++gPttDebounceCounter >= 3 || SerialConfigInProgress()) // 30ms
{ // stop transmitting
gPttOnePushCounter = 3;
}
}
else if ((!GPIO_IsPttPressed() || SerialConfigInProgress()) && gPttOnePushCounter == 3)
{ // PTT released or serial comms config in progress
if (++gPttDebounceCounter >= 3 || SerialConfigInProgress()) // 30ms
{ // stop transmitting
ProcessKey(KEY_PTT, false, false);
gPttIsPressed = false;
if (gKeyReading1 != KEY_INVALID)
gPttWasReleased = true;
gPttOnePushCounter = 0;
#if defined(ENABLE_FEAT_F4HWN_CTR) || defined(ENABLE_FEAT_F4HWN_INV)
ST7565_ContrastAndInv();
#endif
}
}
}
else
gPttDebounceCounter = 0;
//gDebug = gPttOnePushCounter;
}
else
}
else
#endif
{
if (gPttIsPressed)
{
if (!GPIO_IsPttPressed() || SerialConfigInProgress())
if (!isPressed)
{ // PTT released or serial comms config in progress
if (++gPttDebounceCounter >= 3 || SerialConfigInProgress()) // 30ms
if (++gPttDebounceCounter >= 3 || serialConfigInProgress) // 30ms
{ // stop transmitting
ProcessKey(KEY_PTT, false, false);
gPttIsPressed = false;
if (gKeyReading1 != KEY_INVALID)
gPttWasReleased = true;
#if defined(ENABLE_FEAT_F4HWN_CTR) || defined(ENABLE_FEAT_F4HWN_INV)
ST7565_ContrastAndInv();
#endif
gPttDebounceCounter = 0;
StopTransmitting();
}
}
else
}
else
gPttDebounceCounter = 0;
}
else if (GPIO_IsPttPressed() && !SerialConfigInProgress())
else if (isPressed)
{ // PTT pressed
if (++gPttDebounceCounter >= 3) // 30ms
{ // start transmitting
@@ -1259,38 +1260,9 @@ static void CheckKeys(void)
ProcessKey(KEY_PTT, true, false);
}
}
else
gPttDebounceCounter = 0;
}
#else
if (gPttIsPressed)
{
if (GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT) || SerialConfigInProgress())
{ // PTT released or serial comms config in progress
if (++gPttDebounceCounter >= 3 || SerialConfigInProgress()) // 30ms
{ // stop transmitting
ProcessKey(KEY_PTT, false, false);
gPttIsPressed = false;
if (gKeyReading1 != KEY_INVALID)
gPttWasReleased = true;
}
}
else
gPttDebounceCounter = 0;
}
else if (!GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT) && !SerialConfigInProgress())
{ // PTT pressed
if (++gPttDebounceCounter >= 3) // 30ms
{ // start transmitting
boot_counter_10ms = 0;
gPttDebounceCounter = 0;
gPttIsPressed = true;
ProcessKey(KEY_PTT, true, false);
}
}
else
gPttDebounceCounter = 0;
#endif
// --------------------- OTHER KEYS ----------------------------
@@ -1364,9 +1336,9 @@ void APP_TimeSlice10ms(void)
{
gNextTimeslice = false;
if (gScheduleVfoSave) {
SETTINGS_SaveVfoIndicesFlush();
}
SETTINGS_SaveVfoIndicesFlush();
BACKLIGHT_Update();
gFlashLightBlinkCounter++;
@@ -1392,12 +1364,18 @@ void APP_TimeSlice10ms(void)
if (gCurrentFunction == FUNCTION_TRANSMIT)
{ // transmitting
#ifdef ENABLE_AUDIO_BAR
#if defined(ENABLE_AUDIO_BAR) && !defined(ENABLE_FEAT_F4HWN_AUDIO_SCOPE)
if (gSetting_mic_bar && (gFlashLightBlinkCounter % (150 / 10)) == 0) // once every 150ms
UI_DisplayAudioBar();
#endif
}
#ifdef ENABLE_FEAT_F4HWN_AUDIO_SCOPE
if (gSetting_mic_bar && (gFlashLightBlinkCounter % (20 / 10)) == 0) // once every 20ms
// Sample audio amplitude and refresh display during TX only (FM RX has no usable audio register)
UI_DisplayAudioScope();
#endif
bool gUpdateDisplayCurrent = gUpdateDisplay;
bool gUpdateStatusCurrent = gUpdateStatus;
@@ -1412,7 +1390,7 @@ void APP_TimeSlice10ms(void)
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
if (gUpdateDisplayCurrent || gUpdateStatusCurrent) {
getScreenShot(false);
SCREENSHOT_Update(false);
}
#endif
@@ -1525,18 +1503,16 @@ void cancelUserInputModes(void)
if (gDTMF_InputMode || gDTMF_InputBox_Index > 0)
{
DTMF_clear_input_box();
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
gRequestDisplayScreen = DISPLAY_MAIN;
gUpdateDisplay = true;
}
if (gWasFKeyPressed || gKeyInputCountdown > 0 || gInputBoxIndex > 0)
{
gWasFKeyPressed = false;
HideFKeyIcon();
gInputBoxIndex = 0;
gKeyInputCountdown = 0;
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
gUpdateStatus = true;
gUpdateDisplay = true;
}
}
@@ -1558,18 +1534,17 @@ void APP_TimeSlice500ms(void)
if (--gKeyInputCountdown == 0)
{
if (IS_MR_CHANNEL(gTxVfo->CHANNEL_SAVE) && (gInputBoxIndex == 1 || gInputBoxIndex == 2))
if (IS_MR_CHANNEL(gTxVfo->CHANNEL_SAVE) && (gInputBoxIndex > 0 && gInputBoxIndex < 4) && (!gFmRadioMode))
{
channelMoveSwitch();
if (gBeepToPlay == BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL) {
AUDIO_PlayBeep(gBeepToPlay);
}
SETTINGS_SaveVfoIndices();
}
cancelUserInputModes();
gHasVfoBackup = false;
}
}
@@ -1612,9 +1587,11 @@ void APP_TimeSlice500ms(void)
}
#endif
const int m = UI_MENU_GetCurrentMenuId();
if (gBacklightCountdown_500ms > 0 && !gAskToSave && !gCssBackgroundScan
// don't turn off backlight if user is in backlight menu option
&& !(gScreenToDisplay == DISPLAY_MENU && (UI_MENU_GetCurrentMenuId() == MENU_ABR || UI_MENU_GetCurrentMenuId() == MENU_ABR_MAX))
&& !(gScreenToDisplay == DISPLAY_MENU && (m == MENU_ABR || m == MENU_ABR_MAX || m == MENU_ABR_MIN))
&& --gBacklightCountdown_500ms == 0
&& gEeprom.BACKLIGHT_TIME < 61
) {
@@ -1732,8 +1709,10 @@ void APP_TimeSlice500ms(void)
if (exit_menu) {
gMenuCountdown = 0;
if (gEeprom.BACKLIGHT_TIME == 0) {
BACKLIGHT_TurnOff();
const int m = UI_MENU_GetCurrentMenuId();
if (gScreenToDisplay == DISPLAY_MENU && (m == MENU_ABR || m == MENU_ABR_MAX || m == MENU_ABR_MIN)) {
BACKLIGHT_TurnOn();
}
if (gInputBoxIndex > 0 || gDTMF_InputMode) {
@@ -1751,13 +1730,12 @@ void APP_TimeSlice500ms(void)
*/
DTMF_clear_input_box();
gWasFKeyPressed = false;
HideFKeyIcon();
gInputBoxIndex = 0;
gAskToSave = false;
gAskToDelete = false;
gUpdateStatus = true;
gUpdateDisplay = true;
GUI_DisplayType_t disp = DISPLAY_INVALID;
@@ -1936,6 +1914,7 @@ static void ProcessKey(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
// cancel user input
cancelUserInputModes();
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
if (gMonitor)
ACTION_Monitor(); //turn off the monitor
@@ -2048,8 +2027,7 @@ static void ProcessKey(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
if (gWasFKeyPressed && (Key == KEY_PTT || Key == KEY_EXIT || Key == KEY_SIDE1 || Key == KEY_SIDE2)) {
#endif
// cancel the F-key
gWasFKeyPressed = false;
gUpdateStatus = true;
HideFKeyIcon();
}
if (bFlag) {
@@ -2126,18 +2104,14 @@ static void ProcessKey(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
}
#endif
}
else if (gScreenToDisplay != DISPLAY_INVALID && (
(Key != KEY_SIDE1 && Key != KEY_SIDE2)
#ifdef ENABLE_FEAT_F4HWN // For F + SIDE1 or F + SIDE2
else if (gWasFKeyPressed && (Key == KEY_SIDE1 || Key == KEY_SIDE2)) {
ProcessKeysFunctions[gScreenToDisplay](Key, bKeyPressed, bKeyHeld);
}
else if (Key != KEY_SIDE1 && Key != KEY_SIDE2 && gScreenToDisplay != DISPLAY_INVALID) {
ProcessKeysFunctions[gScreenToDisplay](Key, bKeyPressed, bKeyHeld);
}
#else
else if (Key != KEY_SIDE1 && Key != KEY_SIDE2 && gScreenToDisplay != DISPLAY_INVALID) {
ProcessKeysFunctions[gScreenToDisplay](Key, bKeyPressed, bKeyHeld);
}
|| (gWasFKeyPressed && (Key == KEY_SIDE1 || Key == KEY_SIDE2))
#endif
)) {
ProcessKeysFunctions[gScreenToDisplay](Key, bKeyPressed, bKeyHeld);
}
else if (!SCANNER_IsScanning()
#ifdef ENABLE_AIRCOPY
&& gScreenToDisplay != DISPLAY_AIRCOPY
@@ -2170,6 +2144,7 @@ Skip:
else
flagSaveSettings = 1;
gRequestSaveSettings = false;
gRequestSaveSquelch = false;
gUpdateStatus = true;
}
@@ -2192,7 +2167,7 @@ Skip:
}
if (gRequestSaveChannel > 0) { // TODO: remove the gRequestSaveChannel, why use global variable for that??
if ((!bKeyHeld && !bKeyPressed) || (UI_MENU_GetCurrentMenuId() != 0 && gScreenToDisplay == DISPLAY_MENU))
if ((!bKeyHeld && !bKeyPressed) || (UI_MENU_GetCurrentMenuId() != 0 && gScreenToDisplay == DISPLAY_MENU) || gScreenToDisplay == DISPLAY_SCANNER)
{
SETTINGS_SaveChannel(gTxVfo->CHANNEL_SAVE, gEeprom.TX_VFO, gTxVfo, gRequestSaveChannel);
+32 -43
View File
@@ -401,50 +401,35 @@ static void OnKeyDown(uint8_t key)
}
}
// Key
static KEY_Code_t GetKey()
{
KEY_Code_t btn = KEYBOARD_Poll();
if (btn == KEY_INVALID && GPIO_IsPttPressed())
{
btn = KEY_PTT;
}
return btn;
}
// HandleUserInput
static bool HandleUserInput()
{
// Store previous key state
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
// Parse incoming packets on every tick so serial keys are never missed,
// regardless of whether the screen needs redrawing.
SCREENSHOT_ParseInput();
#endif
kbd.prev = kbd.current;
// Get the current key
kbd.current = GetKey();
// Detect valid key press continuation (same key still pressed)
if (kbd.current != KEY_INVALID && kbd.current == kbd.prev)
{
kbd.counter = 1;
}
else
{
kbd.counter = 0;
}
// Process the key if counter indicates it should be handled
if (kbd.counter == 1)
kbd.current = KEYBOARD_GetKey();
if (kbd.current == KEY_INVALID)
return true;
// Movement keys: dispatch on every tick while held
if (kbd.current == KEY_UP || kbd.current == KEY_DOWN ||
kbd.current == KEY_4 || kbd.current == KEY_0)
{
OnKeyDown(kbd.current);
// Special handling for MENU key
if(kbd.current == KEY_MENU)
{
kbd.counter = 0;
SYSTEM_DelayMs(250);
}
return true;
}
// Action keys (MENU, EXIT): dispatch only on rising edge
if (kbd.current != kbd.prev)
{
OnKeyDown(kbd.current);
}
return true;
}
@@ -452,7 +437,6 @@ static bool HandleUserInput()
static void Tick()
{
HandleUserInput();
HandleUserInput();
}
// APP_RunBreakout
@@ -465,6 +449,9 @@ void APP_RunBreakout(void) {
// Init led
BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false);
// Finish the brightness fade if it is in progress
BACKLIGHT_UpdateTickless();
// Init game
UI_DisplayClear();
reset();
@@ -482,11 +469,6 @@ void APP_RunBreakout(void) {
if(swap == 0)
{
blockAnim = (blockAnim + 1) % 4;
// For screenshot
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
getScreenShot(false);
#endif
}
swap = (swap + 1) % 4;
@@ -509,5 +491,12 @@ void APP_RunBreakout(void) {
ST7565_BlitStatusLine(); // Blank status line
ST7565_BlitFullScreen();
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
if(isPaused || swap == 0)
{
SCREENSHOT_Update(false);
}
#endif
}
}
+10 -1
View File
@@ -37,6 +37,15 @@ uint8_t initialCROSS_BAND_RX_TX;
static void NextFreqChannel(void);
static void NextMemChannel(void);
#if defined(ENABLE_FEAT_F4HWN_RESUME_STATE) || defined(ENABLE_SCAN_RANGES)
void CHFRSCANNER_ScanRange(void) {
gScanRangeStart = gScanRangeStart ? 0 : gTxVfo->pRX->Frequency;
gScanRangeStop = gEeprom.VfoInfo[!gEeprom.TX_VFO].freq_config_RX.Frequency;
if(gScanRangeStart > gScanRangeStop)
SWAP(gScanRangeStart, gScanRangeStop);
}
#endif
void CHFRSCANNER_Start(const bool storeBackupSettings, const int8_t scan_direction)
{
if (storeBackupSettings) {
@@ -55,7 +64,7 @@ void CHFRSCANNER_Start(const bool storeBackupSettings, const int8_t scan_directi
{
if(!RADIO_CheckValidList(gEeprom.SCAN_LIST_DEFAULT)) {
RADIO_NextValidList();
RADIO_NextValidList(1);
}
// channel mode
+4
View File
@@ -20,6 +20,10 @@ void CHFRSCANNER_Stop(void);
void CHFRSCANNER_Start(const bool storeBackupSettings, const int8_t scan_direction);
void CHFRSCANNER_ContinueScanning(void);
#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;
+11
View File
@@ -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];
+19 -13
View File
@@ -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)
+80 -67
View File
@@ -129,6 +129,18 @@ void FM_EraseChannels(void)
memset(gFM_Channels, 0xFF, sizeof(gFM_Channels));
}
uint16_t FM_WrapFrequency(uint16_t Frequency) {
const uint16_t freqLoLimit = BK1080_GetFreqLoLimit(gEeprom.FM_Band);
const uint16_t freqHiLimit = BK1080_GetFreqHiLimit(gEeprom.FM_Band);
if (Frequency < freqLoLimit)
return freqHiLimit;
else if (Frequency > freqHiLimit)
return freqLoLimit;
return Frequency;
}
void FM_Tune(uint16_t Frequency, int8_t Step, bool bFlag)
{
AUDIO_AudioPathOff();
@@ -145,10 +157,7 @@ void FM_Tune(uint16_t Frequency, int8_t Step, bool bFlag)
if (!bFlag) {
Frequency += Step;
if (Frequency < BK1080_GetFreqLoLimit(gEeprom.FM_Band))
Frequency = BK1080_GetFreqHiLimit(gEeprom.FM_Band);
else if (Frequency > BK1080_GetFreqHiLimit(gEeprom.FM_Band))
Frequency = BK1080_GetFreqLoLimit(gEeprom.FM_Band);
Frequency = FM_WrapFrequency(Frequency);
gEeprom.FM_FrequencyPlaying = Frequency;
}
@@ -182,65 +191,45 @@ void FM_PlayAndUpdate(void)
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)
@@ -268,6 +257,7 @@ static void Key_DIGITS(KEY_Code_t Key, uint8_t state)
}
INPUTBOX_Append(Key);
gKeyInputCountdown = key_input_timeout_500ms;
gRequestDisplayScreen = DISPLAY_FM;
@@ -283,6 +273,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) {
@@ -305,6 +297,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) {
@@ -347,8 +341,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) {
@@ -377,6 +370,14 @@ static void Key_FUNC(KEY_Code_t Key, uint8_t state)
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
break;
case KEY_8:
ACTION_BackLightOnDemand();
break;
case KEY_9:
ACTION_BackLight();
break;
case KEY_STAR:
ACTION_Scan(autoScan);
break;
@@ -407,6 +408,7 @@ static void Key_EXIT(uint8_t state)
}
else {
gInputBox[--gInputBoxIndex] = 10;
gKeyInputCountdown = key_input_timeout_500ms;
if (gInputBoxIndex) {
if (gInputBoxIndex != 1) {
@@ -438,14 +440,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;
@@ -482,6 +495,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;
@@ -493,6 +508,10 @@ static void Key_UP_DOWN(uint8_t state, int8_t Step)
return;
}
if (!gEeprom.SET_NAV) {
Step = -Step;
}
if (gAskToSave) {
gRequestDisplayScreen = DISPLAY_FM;
gFM_ChannelPosition = NUMBER_AddWithWraparound(gFM_ChannelPosition, Step, 0, FM_CHANNELS_MAX - 1);
@@ -521,10 +540,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;
@@ -553,16 +569,8 @@ void FM_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
Key_MENU(state);
break;
case KEY_UP:
if(gEeprom.SET_NAV == 0)
Key_UP_DOWN(state, -1);
else
Key_UP_DOWN(state, 1);
break;
case KEY_DOWN:
if(gEeprom.SET_NAV == 0)
Key_UP_DOWN(state, 1);
else
Key_UP_DOWN(state, -1);
Key_UP_DOWN(state, Key == KEY_UP ? 1 : -1);
break;
case KEY_EXIT:
Key_EXIT(state);
@@ -573,9 +581,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;
}
}
+1 -1
View File
@@ -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;
+206 -197
View File
@@ -47,6 +47,31 @@
#include "ui/ui.h"
#include <stdlib.h>
// Full VFO backup for restore on EXIT
static VFO_Info_t gVfoBackup;
static uint16_t gScreenChannelBackup = 0;
static uint16_t gFreqChannelBackup = 0;
static void VFO_RestoreBackup(void) {
if (gHasVfoBackup) {
const uint8_t Vfo = gEeprom.TX_VFO;
// Restore indices
gEeprom.ScreenChannel[Vfo] = gScreenChannelBackup;
gEeprom.FreqChannel[Vfo] = gFreqChannelBackup;
// Restore full VFO
memcpy(gTxVfo, &gVfoBackup, sizeof(VFO_Info_t));
// Save and apply
SETTINGS_SaveVfoIndices();
RADIO_ConfigureSquelchAndOutputPower(gTxVfo);
RADIO_SetupRegisters(true);
gHasVfoBackup = false;
}
}
static void toggle_chan_scanlist(void)
{ // toggle the selected channels scanlist setting
@@ -55,10 +80,7 @@ static void toggle_chan_scanlist(void)
if(!IS_MR_CHANNEL(gTxVfo->CHANNEL_SAVE)) {
#ifdef ENABLE_SCAN_RANGES
gScanRangeStart = gScanRangeStart ? 0 : gTxVfo->pRX->Frequency;
gScanRangeStop = gEeprom.VfoInfo[!gEeprom.TX_VFO].freq_config_RX.Frequency;
if(gScanRangeStart > gScanRangeStop)
SWAP(gScanRangeStart, gScanRangeStop);
CHFRSCANNER_ScanRange();
#endif
return;
}
@@ -69,20 +91,22 @@ static void toggle_chan_scanlist(void)
if(att->exclude == true)
{
att->exclude = false;
return;
MR_SaveChannelAttributesToFlash(gTxVfo->CHANNEL_SAVE, att);
}
else
{
uint8_t scanlist = gTxVfo->SCANLIST_PARTICIPATION;
scanlist++;
if (scanlist > MR_CHANNELS_LIST + 1)
scanlist = 0;
gTxVfo->SCANLIST_PARTICIPATION = scanlist;
SETTINGS_UpdateChannel(gTxVfo->CHANNEL_SAVE, gTxVfo, true, true, true);
}
uint8_t scanlist = gTxVfo->SCANLIST_PARTICIPATION;
scanlist++;
if (scanlist > MR_CHANNELS_LIST + 1)
scanlist = 0;
gTxVfo->SCANLIST_PARTICIPATION = scanlist;
SETTINGS_UpdateChannel(gTxVfo->CHANNEL_SAVE, gTxVfo, true, true, true);
gVfoConfigureMode = VFO_CONFIGURE;
gFlagResetVfos = true;
}
@@ -91,16 +115,14 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
{
uint8_t Vfo = gEeprom.TX_VFO;
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
if(gEeprom.MENU_LOCK == true) {
if(Key == 2) { // Enable A/B only
gVfoConfigureMode = VFO_CONFIGURE;
COMMON_SwitchVFOs();
if (beep)
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
}
if (beep)
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
return; // prevent F function if MENU LOCK is true
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
if(gEeprom.MENU_LOCK == true && Key != 2) {
HideFKeyIcon();
return;
}
#endif
@@ -108,8 +130,6 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
return;
}
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
switch (Key) {
case KEY_0:
@@ -120,9 +140,7 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
case KEY_1:
if (!IS_FREQ_CHANNEL(gTxVfo->CHANNEL_SAVE)) {
gWasFKeyPressed = false;
gUpdateStatus = true;
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
HideFKeyIcon();
#ifdef ENABLE_COPY_CHAN_TO_VFO
if (!gEeprom.VFO_OPEN || gCssBackgroundScan) {
@@ -186,9 +204,6 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
gRequestDisplayScreen = DISPLAY_MAIN;
if (beep)
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
break;
case KEY_2:
@@ -196,8 +211,6 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
gVfoConfigureMode = VFO_CONFIGURE;
#endif
COMMON_SwitchVFOs();
if (beep)
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
break;
case KEY_3:
@@ -205,26 +218,21 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
gVfoConfigureMode = VFO_CONFIGURE;
#endif
COMMON_SwitchVFOMode();
if (beep)
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
break;
case KEY_4:
gWasFKeyPressed = false;
HideFKeyIcon();
gBackup_CROSS_BAND_RX_TX = gEeprom.CROSS_BAND_RX_TX;
gEeprom.CROSS_BAND_RX_TX = CROSS_BAND_OFF;
gUpdateStatus = true;
if (beep)
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
gEeprom.CROSS_BAND_RX_TX = CROSS_BAND_OFF;
SCANNER_Start(false);
gRequestDisplayScreen = DISPLAY_SCANNER;
break;
case KEY_5:
if(beep) {
if(!beep) {
#ifdef ENABLE_NOAA
if (!IS_NOAA_CHANNEL(gTxVfo->CHANNEL_SAVE)) {
gEeprom.ScreenChannel[Vfo] = gEeprom.NoaaChannel[gEeprom.TX_VFO];
@@ -285,47 +293,50 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
#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);
} else {
// Adjust squelch: UP increments, DOWN decrements
if (isKeyUp) {
gEeprom.SQUELCH_LEVEL = (gEeprom.SQUELCH_LEVEL < 9) ? gEeprom.SQUELCH_LEVEL + 1 : 9;
} else {
gEeprom.SQUELCH_LEVEL = (gEeprom.SQUELCH_LEVEL > 0) ? gEeprom.SQUELCH_LEVEL - 1 : 0;
}
gVfoConfigureMode = VFO_CONFIGURE;
}
gWasFKeyPressed = false;
break;
}
case KEY_SIDE1:
uint8_t a = FREQUENCY_GetSortedIdxFromStepIdx(gTxVfo->STEP_SETTING);
if (a < STEP_N_ELEM - 1)
{
gTxVfo->STEP_SETTING = FREQUENCY_GetStepIdxFromSortedIdx(a + 1);
}
if (IS_FREQ_CHANNEL(gTxVfo->CHANNEL_SAVE))
{
gRequestSaveChannel = 1;
}
gVfoConfigureMode = VFO_CONFIGURE;
gWasFKeyPressed = false;
break;
case KEY_SIDE2:
uint8_t b = FREQUENCY_GetSortedIdxFromStepIdx(gTxVfo->STEP_SETTING);
if (b > 0)
{
gTxVfo->STEP_SETTING = FREQUENCY_GetStepIdxFromSortedIdx(b - 1);
bool isKeySide1 = (Key == KEY_SIDE1);
uint8_t idx = FREQUENCY_GetSortedIdxFromStepIdx(gTxVfo->STEP_SETTING);
if ((isKeySide1 && idx < STEP_N_ELEM - 1) || (!isKeySide1 && idx > 0))
{
gTxVfo->STEP_SETTING = FREQUENCY_GetStepIdxFromSortedIdx(idx + (isKeySide1 ? 1 : -1));
if (IS_FREQ_CHANNEL(gTxVfo->CHANNEL_SAVE)) {
gRequestSaveChannel = 1;
}
gVfoConfigureMode = VFO_CONFIGURE;
}
gWasFKeyPressed = false;
break;
}
if (IS_FREQ_CHANNEL(gTxVfo->CHANNEL_SAVE))
{
gRequestSaveChannel = 1;
}
gVfoConfigureMode = VFO_CONFIGURE;
gWasFKeyPressed = false;
break;
#endif
default:
gUpdateStatus = true;
gWasFKeyPressed = false;
HideFKeyIcon();
if (beep)
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
@@ -362,6 +373,8 @@ void channelMove(uint16_t Channel)
#endif
RADIO_ConfigureChannel(gEeprom.TX_VFO, gVfoConfigureMode);
SETTINGS_SaveVfoIndices();
return;
}
@@ -396,11 +409,6 @@ void channelMoveSwitch(void) {
if (gInputBoxIndex == 4) {
gInputBoxIndex = 0;
gKeyInputCountdown = 1;
channelMove(Channel - 1);
SETTINGS_SaveVfoIndices();
return;
}
channelMove(Channel - 1);
@@ -418,10 +426,9 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
gRequestDisplayScreen = DISPLAY_MAIN;
}
gWasFKeyPressed = false;
gUpdateStatus = true;
HideFKeyIcon();
processFKeyFunction(Key, false);
processFKeyFunction(Key, true);
}
}
return;
@@ -480,17 +487,14 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
/* 01 .. MR_CHANNELS_LIST */
if (value <= MR_CHANNELS_LIST)
{
if (RADIO_CheckValidList(value))
{
/* Requested scan list is valid */
gEeprom.SCAN_LIST_DEFAULT = value;
}
else
gEeprom.SCAN_LIST_DEFAULT = value;
if (!RADIO_CheckValidList(value))
{
/* Requested scan list is empty or invalid:
jump to the next valid scan list */
gEeprom.SCAN_LIST_DEFAULT = value;
RADIO_NextValidList();
jump to the next valid scan list */
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
RADIO_NextValidList(1);
}
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
@@ -498,13 +502,19 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
#endif
}
gInputBoxIndex = 0;
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
return;
}
const uint8_t Vfo = gEeprom.TX_VFO;
// Save full VFO state BEFORE first digit
if (gInputBoxIndex == 0 && IS_FREQ_CHANNEL(gTxVfo->CHANNEL_SAVE)) {
memcpy(&gVfoBackup, gTxVfo, sizeof(VFO_Info_t));
gScreenChannelBackup = gEeprom.ScreenChannel[Vfo];
gFreqChannelBackup = gEeprom.FreqChannel[Vfo];
gHasVfoBackup = true;
}
INPUTBOX_Append(Key);
gKeyInputCountdown = key_input_timeout_500ms;
@@ -542,7 +552,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 = (gInputBoxIndex >= totalDigits) ? (key_input_timeout_500ms / 16) : (key_input_timeout_500ms / 3);
if (gInputBoxIndex > totalDigits) {
gInputBoxIndex = totalDigits;
return;
}
const char *inputStr = INPUTBOX_GetAscii();
uint8_t inputLength = gInputBoxIndex;
@@ -611,6 +627,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)) {
@@ -632,8 +649,13 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
return;
}
gWasFKeyPressed = false;
gUpdateStatus = true;
HideFKeyIcon();
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
if(gEeprom.MENU_LOCK == true && Key != 2) {
return;
}
#endif
if(Key == 8)
{
@@ -648,79 +670,80 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
#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)
@@ -762,7 +785,6 @@ static void MAIN_Key_MENU(bool bKeyPressed, bool bKeyHeld)
gRequestDisplayScreen = DISPLAY_MAIN;
}
gWasFKeyPressed = false;
gUpdateStatus = true;
ACTION_Handle(KEY_MENU, bKeyPressed, bKeyHeld);
@@ -773,6 +795,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;
@@ -783,7 +808,11 @@ static void MAIN_Key_MENU(bool bKeyPressed, bool bKeyHeld)
}
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
if(gEeprom.MENU_LOCK == false) {
if(gEeprom.MENU_LOCK == true) {
HideFKeyIcon();
return;
}
#endif
gFlagRefreshSetting = true;
@@ -791,10 +820,6 @@ static void MAIN_Key_MENU(bool bKeyPressed, bool bKeyHeld)
#ifdef ENABLE_VOICE
gAnotherVoiceID = VOICE_ID_MENU;
#endif
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
}
#endif
}
else {
gRequestDisplayScreen = DISPLAY_MAIN;
@@ -804,13 +829,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;
@@ -820,6 +838,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;
@@ -842,12 +873,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
{
@@ -860,7 +885,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;
@@ -896,18 +920,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
@@ -920,8 +939,10 @@ static void MAIN_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
uint16_t Channel = gEeprom.ScreenChannel[gEeprom.TX_VFO];
if (bKeyHeld || !bKeyPressed) { // key held or released
if (gInputBoxIndex > 0)
return; // leave if input box active
if (gInputBoxIndex > 0) {
gInputBoxIndex = 0;
gHasVfoBackup = false;
}
if (!bKeyPressed) {
if (!bKeyHeld || IS_FREQ_CHANNEL(Channel))
@@ -935,10 +956,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;
}
@@ -1039,16 +1056,8 @@ void MAIN_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
MAIN_Key_MENU(bKeyPressed, bKeyHeld);
break;
case KEY_UP:
if(gEeprom.SET_NAV == 0)
MAIN_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
else
MAIN_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
break;
case KEY_DOWN:
if(gEeprom.SET_NAV == 0)
MAIN_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
else
MAIN_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
MAIN_Key_UP_DOWN(bKeyPressed, bKeyHeld, Key == KEY_UP ? 1 : -1);
break;
case KEY_EXIT:
MAIN_Key_EXIT(bKeyPressed, bKeyHeld);
@@ -1067,4 +1076,4 @@ void MAIN_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
break;
}
}
}
+62 -68
View File
@@ -308,7 +308,7 @@ int MENU_GetLimits(uint8_t menu_id, int32_t *pMin, int32_t *pMax)
case MENU_MIC:
//*pMin = 0;
*pMax = 4;
*pMax = 8;
break;
case MENU_LIST_CH:
@@ -480,6 +480,7 @@ void MENU_AcceptSetting(void)
case MENU_SQL:
gEeprom.SQUELCH_LEVEL = gSubMenuSelection;
gVfoConfigureMode = VFO_CONFIGURE;
gRequestSaveSquelch = true;
break;
case MENU_STEP:
@@ -1547,15 +1548,17 @@ static void MENU_Key_0_to_9(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
return;
}
if (UI_MENU_GetCurrentMenuId() == MENU_MEM_CH ||
UI_MENU_GetCurrentMenuId() == MENU_DEL_CH ||
UI_MENU_GetCurrentMenuId() == MENU_1_CALL ||
UI_MENU_GetCurrentMenuId() == MENU_S_PRI_CH_1 ||
UI_MENU_GetCurrentMenuId() == MENU_S_PRI_CH_2 ||
UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME)
{ // enter 3-digit channel number
const int m = UI_MENU_GetCurrentMenuId();
if (gInputBoxIndex < 3)
if (m == MENU_MEM_CH ||
m == MENU_DEL_CH ||
m == MENU_1_CALL ||
m == MENU_S_PRI_CH_1 ||
m == MENU_S_PRI_CH_2 ||
m == MENU_MEM_NAME)
{ // enter 4-digit channel number
if (gInputBoxIndex < 4)
{
#ifdef ENABLE_VOICE
gAnotherVoiceID = (VOICE_ID_t)Key;
@@ -1566,7 +1569,8 @@ static void MENU_Key_0_to_9(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
gInputBoxIndex = 0;
Value = ((gInputBox[0] * 100) + (gInputBox[1] * 10) + gInputBox[2]) - 1;
//Value = ((gInputBox[0] * 100) + (gInputBox[1] * 10) + gInputBox[2]) - 1;
Value = ((gInputBox[0] * 1000) + (gInputBox[1] * 100) + (gInputBox[2] * 10) + gInputBox[3]) - 1;
if (IS_MR_CHANNEL(Value))
{
@@ -1691,19 +1695,21 @@ static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld)
if (!gIsInSubMenu)
{
const int m = UI_MENU_GetCurrentMenuId();
#ifdef ENABLE_VOICE
if (UI_MENU_GetCurrentMenuId() != MENU_SCR)
if (m != MENU_SCR)
gAnotherVoiceID = MenuList[gMenuCursor].voice_id;
#endif
if (UI_MENU_GetCurrentMenuId() == MENU_UPCODE
|| UI_MENU_GetCurrentMenuId() == MENU_DWCODE
if (m == MENU_UPCODE
|| m == MENU_DWCODE
#ifdef ENABLE_DTMF_CALLING
|| UI_MENU_GetCurrentMenuId() == MENU_ANI_ID
|| m == MENU_ANI_ID
#endif
)
return;
#if 1
if (UI_MENU_GetCurrentMenuId() == MENU_DEL_CH || UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME)
if (m == MENU_DEL_CH || m == MENU_MEM_NAME)
if (!RADIO_CheckValidChannel(gSubMenuSelection, false, 0))
return; // invalid channel
#endif
@@ -1711,7 +1717,7 @@ static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld)
gAskForConfirmation = 0;
gIsInSubMenu = true;
// if (UI_MENU_GetCurrentMenuId() != MENU_D_LIST)
// if (m != MENU_D_LIST)
{
gInputBoxIndex = 0;
edit_index = -1;
@@ -1762,10 +1768,12 @@ static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld)
if (gIsInSubMenu)
{
if (UI_MENU_GetCurrentMenuId() == MENU_RESET ||
UI_MENU_GetCurrentMenuId() == MENU_MEM_CH ||
UI_MENU_GetCurrentMenuId() == MENU_DEL_CH ||
UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME)
const int m = UI_MENU_GetCurrentMenuId();
if (m == MENU_RESET ||
m == MENU_MEM_CH ||
m == MENU_DEL_CH ||
m == MENU_MEM_NAME)
{
switch (gAskForConfirmation)
{
@@ -1778,7 +1786,7 @@ static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld)
UI_DisplayMenu();
if (UI_MENU_GetCurrentMenuId() == MENU_RESET)
if (m == MENU_RESET)
{
#ifdef ENABLE_VOICE
AUDIO_SetVoiceID(0, VOICE_ID_CONFIRM);
@@ -1852,7 +1860,8 @@ static void MENU_Key_STAR(const bool bKeyPressed, const bool bKeyHeld)
if (gRxVfo->Modulation == MODULATION_FM)
#endif
{
if ((UI_MENU_GetCurrentMenuId() == MENU_R_CTCS || UI_MENU_GetCurrentMenuId() == MENU_R_DCS) && gIsInSubMenu)
const int m = UI_MENU_GetCurrentMenuId();
if ((m == MENU_R_CTCS || m == MENU_R_DCS) && gIsInSubMenu)
{ // scan CTCSS or DCS to find the tone/code of the incoming signal
if (!SCANNER_IsScanning())
MENU_StartCssScan();
@@ -1873,6 +1882,10 @@ static void MENU_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
uint16_t Channel;
bool bCheckScanList;
if (!gEeprom.SET_NAV && gIsInSubMenu) {
Direction = -Direction;
}
if (UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME && gIsInSubMenu && edit_index >= 0)
{ // change the character
if (bKeyPressed && edit_index < 10 && Direction != 0)
@@ -1920,9 +1933,11 @@ static void MENU_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
gRequestDisplayScreen = DISPLAY_MENU;
if (UI_MENU_GetCurrentMenuId() != MENU_ABR
&& UI_MENU_GetCurrentMenuId() != MENU_ABR_MIN
&& UI_MENU_GetCurrentMenuId() != MENU_ABR_MAX
const int m = UI_MENU_GetCurrentMenuId();
if (m != MENU_ABR
&& m != MENU_ABR_MIN
&& m != MENU_ABR_MAX
&& gEeprom.BACKLIGHT_TIME == 0) // backlight always off and not in the backlight menu
{
BACKLIGHT_TurnOff();
@@ -1948,8 +1963,10 @@ static void MENU_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
}
VFO = 0;
const int m = UI_MENU_GetCurrentMenuId();
switch (UI_MENU_GetCurrentMenuId())
switch (m)
{
case MENU_DEL_CH:
case MENU_1_CALL:
@@ -1965,19 +1982,21 @@ static void MENU_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
return;
}
if(UI_MENU_GetCurrentMenuId() == MENU_S_PRI_CH_1 || UI_MENU_GetCurrentMenuId() == MENU_S_PRI_CH_2)
if(m == MENU_S_PRI_CH_1 || m == MENU_S_PRI_CH_2)
{
static int16_t last;
if(Direction > 0 && gSubMenuSelection == MR_CHANNELS_MAX)
{
gSubMenuSelection = -1;
last = -1;
}
else if(Direction < 0 && gSubMenuSelection == MR_CHANNELS_MAX)
{
gSubMenuSelection = MR_CHANNELS_MAX;
last = MR_CHANNELS_MAX;
if(gSubMenuSelection == MR_CHANNELS_MAX) {
if(Direction > 0)
{
gSubMenuSelection = -1;
last = -1;
}
else if(Direction < 0)
{
gSubMenuSelection = MR_CHANNELS_MAX;
last = MR_CHANNELS_MAX;
}
}
Channel = RADIO_FindNextChannel(gSubMenuSelection + Direction, Direction, bCheckScanList, VFO);
@@ -2013,42 +2032,15 @@ void MENU_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
{
switch (Key)
{
case KEY_0:
case KEY_1:
case KEY_2:
case KEY_3:
case KEY_4:
case KEY_5:
case KEY_6:
case KEY_7:
case KEY_8:
case KEY_9:
case KEY_0...KEY_9:
MENU_Key_0_to_9(Key, bKeyPressed, bKeyHeld);
break;
case KEY_MENU:
MENU_Key_MENU(bKeyPressed, bKeyHeld);
break;
case KEY_UP:
if(gEeprom.SET_NAV == 0) {
if(gIsInSubMenu)
MENU_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
else
MENU_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
}
else {
MENU_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
}
break;
case KEY_DOWN:
if(gEeprom.SET_NAV == 0) {
if(gIsInSubMenu)
MENU_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
else
MENU_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
}
else {
MENU_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
}
MENU_Key_UP_DOWN(bKeyPressed, bKeyHeld, Key == KEY_UP ? 1 : -1);
break;
case KEY_EXIT:
MENU_Key_EXIT(bKeyPressed, bKeyHeld);
@@ -2089,11 +2081,13 @@ void MENU_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
if (gScreenToDisplay == DISPLAY_MENU)
{
if (UI_MENU_GetCurrentMenuId() == MENU_VOL ||
const int m = UI_MENU_GetCurrentMenuId();
if (m == MENU_VOL ||
#ifdef ENABLE_F_CAL_MENU
UI_MENU_GetCurrentMenuId() == MENU_F_CALI ||
m == MENU_F_CALI ||
#endif
UI_MENU_GetCurrentMenuId() == MENU_BATCAL)
m == MENU_BATCAL)
{
gMenuCountdown = menu_timeout_long_500ms;
}
+11 -8
View File
@@ -32,7 +32,7 @@ DCS_CodeType_t gScanCssResultType;
uint8_t gScanCssResultCode;
bool gScanSingleFrequency; // scan CTCSS/DCS codes for current frequency
SCAN_SaveState_t gScannerSaveState;
uint8_t gScanChannel;
uint16_t gScanChannel;
uint32_t gScanFrequency;
SCAN_CssState_t gScanCssState;
uint8_t gScanProgressIndicator;
@@ -52,7 +52,7 @@ static void SCANNER_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
gRequestDisplayScreen = DISPLAY_SCANNER;
if (gInputBoxIndex < 3) {
if (gInputBoxIndex < 4) {
#ifdef ENABLE_VOICE
gAnotherVoiceID = (VOICE_ID_t)Key;
#endif
@@ -61,13 +61,14 @@ static void SCANNER_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
gInputBoxIndex = 0;
uint16_t chan = ((gInputBox[0] * 100) + (gInputBox[1] * 10) + gInputBox[2]) - 1;
// uint16_t chan = ((gInputBox[0] * 100) + (gInputBox[1] * 10) + gInputBox[2]) - 1;
uint16_t chan = ((gInputBox[0] * 1000) + (gInputBox[1] * 100) + (gInputBox[2] * 10) + gInputBox[3]) - 1;
if (IS_MR_CHANNEL(chan)) {
#ifdef ENABLE_VOICE
gAnotherVoiceID = (VOICE_ID_t)Key;
#endif
gShowChPrefix = RADIO_CheckValidChannel(chan, false, 0);
gScanChannel = (uint8_t)chan;
gScanChannel = chan;
return;
}
}
@@ -207,7 +208,7 @@ static void SCANNER_Key_MENU(bool bKeyPressed, bool bKeyHeld)
gTxVfo->freq_config_TX.Code = gScanCssResultCode;
}
uint8_t chan;
uint16_t chan;
if (IS_MR_CHANNEL(gTxVfo->CHANNEL_SAVE)) {
chan = gScanChannel;
gEeprom.MrChannel[gEeprom.TX_VFO] = chan;
@@ -256,6 +257,10 @@ static void SCANNER_Key_UP_DOWN(bool bKeyPressed, bool pKeyHeld, int8_t Directio
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);
@@ -275,10 +280,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);
+1 -1
View File
@@ -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;
+48 -71
View File
@@ -270,16 +270,6 @@ static void GUI_DisplaySmallest(const char *pString, uint8_t x, uint8_t y,
// Utility functions
static KEY_Code_t GetKey()
{
KEY_Code_t btn = KEYBOARD_Poll();
if (btn == KEY_INVALID && GPIO_IsPttPressed())
{
btn = KEY_PTT;
}
return btn;
}
static int clamp(int v, int min, int max)
{
return v <= min ? min : (v >= max ? max : v);
@@ -787,11 +777,13 @@ static void ToggleBacklight()
settings.backlightState = !settings.backlightState;
if (settings.backlightState)
{
BACKLIGHT_TurnOn();
// BACKLIGHT_TurnOn();
BACKLIGHT_SetBrightness(gEeprom.BACKLIGHT_MAX);
}
else
{
BACKLIGHT_TurnOff();
// BACKLIGHT_TurnOff();
BACKLIGHT_SetBrightness(gEeprom.BACKLIGHT_MIN);
}
}
@@ -1185,37 +1177,32 @@ static void DrawArrow(uint8_t x)
static void OnKeyDown(uint8_t key)
{
bool nav = (gEeprom.SET_NAV != 0);
bool nav = gEeprom.SET_NAV;
bool isTrue = false;
switch (key)
{
case KEY_3:
UpdateDBMax(true);
break;
isTrue = true;
[[fallthrough]];
case KEY_9:
UpdateDBMax(false);
UpdateDBMax(isTrue);
break;
case KEY_1:
UpdateScanStep(true);
break;
isTrue = true;
[[fallthrough]];
case KEY_7:
UpdateScanStep(false);
UpdateScanStep(isTrue);
break;
case KEY_2:
UpdateFreqChangeStep(true);
break;
isTrue = true;
[[fallthrough]];
case KEY_8:
UpdateFreqChangeStep(false);
UpdateFreqChangeStep(isTrue);
break;
case KEY_UP:
#ifdef ENABLE_SCAN_RANGES
if (!gScanRangeStart) {
#endif
UpdateCurrentFreq(nav);
#ifdef ENABLE_SCAN_RANGES
}
#endif
break;
nav = !nav;
[[fallthrough]];
case KEY_DOWN:
#ifdef ENABLE_SCAN_RANGES
if (!gScanRangeStart) {
@@ -1229,10 +1216,10 @@ static void OnKeyDown(uint8_t key)
Blacklist();
break;
case KEY_STAR:
UpdateRssiTriggerLevel(true);
break;
isTrue = true;
[[fallthrough]];
case KEY_F:
UpdateRssiTriggerLevel(false);
UpdateRssiTriggerLevel(isTrue);
break;
case KEY_5:
#ifdef ENABLE_SCAN_RANGES
@@ -1285,21 +1272,10 @@ static void OnKeyDownFreqInput(uint8_t key)
{
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:
case KEY_STAR:
UpdateFreqInput(key);
break;
case KEY_EXIT:
if (freqInputIndex == 0)
if (freqInputIndex == 0 && key == KEY_EXIT)
{
SetState(previousState);
break;
@@ -1330,23 +1306,20 @@ static void OnKeyDownFreqInput(uint8_t key)
void OnKeyDownStill(KEY_Code_t key)
{
bool nav = (gEeprom.SET_NAV != 0);
bool nav = gEeprom.SET_NAV;
bool isTrue = false;
switch (key)
{
case KEY_3:
UpdateDBMax(true);
break;
isTrue = true;
[[fallthrough]];
case KEY_9:
UpdateDBMax(false);
UpdateDBMax(isTrue);
break;
case KEY_UP:
if (menuState) {
SetRegMenuValue(menuState, nav);
break;
}
UpdateCurrentFreqStill(nav);
break;
nav = !nav;
[[fallthrough]];
case KEY_DOWN:
if (menuState) {
SetRegMenuValue(menuState, !nav);
@@ -1355,10 +1328,10 @@ void OnKeyDownStill(KEY_Code_t key)
UpdateCurrentFreqStill(!nav);
break;
case KEY_STAR:
UpdateRssiTriggerLevel(true);
break;
isTrue = true;
[[fallthrough]];
case KEY_F:
UpdateRssiTriggerLevel(false);
UpdateRssiTriggerLevel(isTrue);
break;
case KEY_5:
FreqInput();
@@ -1381,14 +1354,7 @@ void OnKeyDownStill(KEY_Code_t key)
BK4819_ToggleGpioOut(BK4819_GPIO5_PIN1_RED, true); */
break;
case KEY_MENU:
if (menuState == ARRAY_SIZE(registerSpecs) - 1)
{
menuState = 1;
}
else
{
menuState++;
}
menuState = (menuState == ARRAY_SIZE(registerSpecs) - 1) ? 1 : menuState + 1;
redrawScreen = true;
break;
case KEY_EXIT:
@@ -1541,7 +1507,7 @@ static void Render()
static bool HandleUserInput()
{
kbd.prev = kbd.current;
kbd.current = GetKey();
kbd.current = KEYBOARD_GetKey();
if (kbd.current != KEY_INVALID && kbd.current == kbd.prev)
{
@@ -1691,16 +1657,23 @@ static void UpdateListening()
static void Tick()
{
#ifdef ENABLE_AM_FIX
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
// Parse incoming packets on every tick so serial keys are never missed,
// regardless of whether the screen needs redrawing.
SCREENSHOT_ParseInput();
#endif
if (gNextTimeslice)
{
gNextTimeslice = false;
#ifdef ENABLE_AM_FIX
if (settings.modulationType == MODULATION_AM && !lockAGC)
{
AM_fix_10ms(vfo); // allow AM_Fix to apply its AGC action
}
}
#endif
BACKLIGHT_Update();
}
#ifdef ENABLE_SCAN_RANGES
if (gNextTimeslice_500ms)
@@ -1760,7 +1733,7 @@ static void Tick()
Render();
// For screenshot
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
getScreenShot(false);
SCREENSHOT_Update(false);
#endif
redrawScreen = false;
}
@@ -1768,6 +1741,8 @@ static void Tick()
void APP_RunSpectrum()
{
settings.backlightState = gEeprom.BACKLIGHT_TIME == 0 ? false : true;
// TX here coz it always? set to active VFO
vfo = gEeprom.TX_VFO;
#ifdef ENABLE_FEAT_F4HWN_SPECTRUM
@@ -1830,4 +1805,6 @@ void APP_RunSpectrum()
{
Tick();
}
BACKLIGHT_TurnOn();
}
+10 -7
View File
@@ -53,12 +53,7 @@ void AUDIO_PlayBeep(BEEP_Type_t Beep)
#endif
!gEeprom.BEEP_CONTROL)
return;
#ifdef ENABLE_AIRCOPY
if (gScreenToDisplay == DISPLAY_AIRCOPY)
return;
#endif
if (gCurrentFunction == FUNCTION_RECEIVE)
return;
@@ -182,11 +177,19 @@ 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
+37 -9
View File
@@ -157,15 +157,6 @@ 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] =
{
0b00111100,
@@ -177,6 +168,7 @@ const uint8_t BITMAP_VFO_Lock[7] =
0b00111100,
};
/*
const uint8_t BITMAP_VFO_Default[7] =
{
0b01111111,
@@ -198,6 +190,42 @@ const uint8_t BITMAP_VFO_NotDefault[7] =
0b00010100,
0b00001000
};
*/
// Compact arrow
const uint8_t BITMAP_VFO_Default[7] =
{
0b00111110,
0b00111110,
0b00011100,
0b00011100,
0b00001000,
0b00001000,
0b00000000
};
// Compact empty arrow
const uint8_t BITMAP_VFO_NotDefault[7] =
{
0b00100010,
0b00100010,
0b00010100,
0b00010100,
0b00001000,
0b00001000,
0b00000000
};
const uint8_t BITMAP_VFO_Empty[7] =
{
0b00000000,
0b00000000,
0b00000000,
0b00000000,
0b00000000,
0b00000000,
0b00000000
};
const uint8_t BITMAP_compand[6] =
{
+1 -1
View File
@@ -35,9 +35,9 @@ extern const uint8_t BITMAP_NotReady[7];
extern const uint8_t gFontVox[2][6];
#endif
extern const uint8_t BITMAP_Antenna[5];
extern const uint8_t BITMAP_VFO_Default[7];
extern const uint8_t BITMAP_VFO_NotDefault[7];
extern const uint8_t BITMAP_VFO_Empty[7];
extern const uint8_t BITMAP_VFO_Lock[7];
extern const uint8_t BITMAP_PowerUser[3];
extern const uint8_t BITMAP_compand[6];
+67 -32
View File
@@ -31,7 +31,7 @@
#include "misc.h"
#endif
#define PWM_FREQ 320
#define PWM_FREQ 4000
#define DUTY_CYCLE_LEVELS 64
#define DUTY_CYCLE_ON_VALUE GPIO_PIN_MASK(GPIO_PIN_BACKLIGHT)
@@ -44,8 +44,14 @@ static uint32_t dutyCycle[DUTY_CYCLE_LEVELS];
// this is decremented once every 500ms
uint16_t gBacklightCountdown_500ms = 0;
bool gUpdateBacklight = false;
bool backlightOn;
static uint8_t currentIndex = 0;
static int16_t currentBrightness = 0;
static int16_t targetBrightness = 0;
static int16_t fadeStep = 0;
#ifdef ENABLE_FEAT_F4HWN
const uint8_t value[] = {
0, // 0 off
@@ -110,6 +116,12 @@ static void BACKLIGHT_Sound(void)
gK5startup = false;
}
void BACKLIGHT_UpdateTickless(void) {
while(gUpdateBacklight) {
BACKLIGHT_Update();
SYSTEM_DelayMs(10);
}
}
void BACKLIGHT_TurnOn(void)
{
@@ -134,27 +146,23 @@ void BACKLIGHT_TurnOn(void)
backlightOn = true;
#ifdef ENABLE_FEAT_F4HWN
if(gK5startup == true) {
#if defined(ENABLE_FMRADIO) && defined(ENABLE_SPECTRUM)
BACKLIGHT_SetBrightness(gEeprom.BACKLIGHT_MAX);
#else
for(uint8_t i = 0; i <= gEeprom.BACKLIGHT_MAX; i++)
{
BACKLIGHT_SetBrightness(i);
SYSTEM_DelayMs(50);
}
#endif
BACKLIGHT_Sound();
}
else
{
BACKLIGHT_SetBrightness(gEeprom.BACKLIGHT_MAX);
}
#else
BACKLIGHT_SetBrightness(gEeprom.BACKLIGHT_MAX);
#ifdef ENABLE_FEAT_F4HWN
if(gK5startup == true)
#else
static bool startup = true;
if(startup)
#endif
{
BACKLIGHT_UpdateTickless();
#ifdef ENABLE_FEAT_F4HWN
BACKLIGHT_Sound();
#else
startup = false;
#endif
}
switch (gEeprom.BACKLIGHT_TIME) {
default:
@@ -190,18 +198,11 @@ bool BACKLIGHT_IsOn()
return backlightOn;
}
static uint8_t currentBrightness = 0;
void BACKLIGHT_SetBrightness(uint8_t brigtness)
static void BACKLIGHT_SetHardwareBrightness(uint8_t brightness)
{
// printf("BL: %d\n", brigtness);
if (currentBrightness == brigtness)
{
return;
}
if (0 == brigtness)
if (0 == brightness)
{
LL_TIM_DisableCounter(TIMx);
LL_DMA_DisableChannel(DMA1, DMA_CHANNEL);
@@ -210,7 +211,7 @@ void BACKLIGHT_SetBrightness(uint8_t brigtness)
}
else
{
const uint32_t level = (uint32_t)(value[brigtness]) * DUTY_CYCLE_LEVELS / 255;
const uint32_t level = (uint32_t)(brightness) * DUTY_CYCLE_LEVELS / 255;
if (level >= DUTY_CYCLE_LEVELS)
{
LL_TIM_DisableCounter(TIMx);
@@ -231,11 +232,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;
}
+2
View File
@@ -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
+4
View File
@@ -116,6 +116,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);
+135 -150
View File
@@ -334,45 +334,9 @@ void BK4819_SetAGC(bool enable)
void BK4819_InitAGC(bool amModulation)
{
// REG_10, REG_11, REG_12 REG_13, REG_14
//
// Rx AGC Gain Table[]. (Index Max->Min is 3,2,1,0,-1)
//
// <15:10> ???
//
// <9:8> LNA Gain Short
// 3 = 0dB <<< 1o11 read from spectrum reference manual
// 2 = -24dB -19 -11
// 1 = -30dB -24 -16
// 0 = -33dB -28 -19
//
// <7:5> LNA Gain
// 7 = 0dB
// 6 = -2dB
// 5 = -4dB
// 4 = -6dB
// 3 = -9dB
// 2 = -14dB <<<
// 1 = -19dB
// 0 = -24dB
//
// <4:3> MIXER Gain
// 3 = 0dB <<<
// 2 = -3dB
// 1 = -6dB
// 0 = -8dB
//
// <2:0> PGA Gain
// 7 = 0dB
// 6 = -3dB <<<
// 5 = -6dB
// 4 = -9dB
// 3 = -15dB
// 2 = -21dB
// 1 = -27dB
// 0 = -33dB
//
/*
// Old strategy
BK4819_WriteRegister(BK4819_REG_13, 0x03BE); // 0x03BE / 000000 11 101 11 110 / -7dB
BK4819_WriteRegister(BK4819_REG_12, 0x037B); // 0x037B / 000000 11 011 11 011 / -24dB
BK4819_WriteRegister(BK4819_REG_11, 0x027B); // 0x027B / 000000 10 011 11 011 / -43dB
@@ -387,7 +351,21 @@ void BK4819_InitAGC(bool amModulation)
}
BK4819_WriteRegister(BK4819_REG_7B, 0x8420);
*/
// New strategy
// Keep the runtime AGC profile aligned with the stock BK4829 firmware
// until modulation-specific AGC changes are proven on hardware.
(void)amModulation;
BK4819_WriteRegister(BK4819_REG_10, 0x0318);
BK4819_WriteRegister(BK4819_REG_11, 0x033A);
BK4819_WriteRegister(BK4819_REG_12, 0x03DB);
BK4819_WriteRegister(BK4819_REG_13, 0x03DF);
BK4819_WriteRegister(BK4819_REG_14, 0x0210);
BK4819_WriteRegister(BK4819_REG_49, 0x2AB2);
BK4819_WriteRegister(BK4819_REG_7B, 0x73DC);
}
int8_t BK4819_GetRxGain_dB(void)
@@ -625,100 +603,91 @@ void BK4819_EnableVox(uint16_t VoxEnableThreshold, uint16_t VoxDisableThreshold)
void BK4819_SetFilterBandwidth(const BK4819_FilterBandwidth_t Bandwidth, const bool weak_no_different)
{
// REG_43
// <15> 0 ???
(void)weak_no_different;
// REG_43 on BK4829, per BK4829 Registers Table.
//
// <14:12> 4 RF filter bandwidth
// 0 = 1.7 kHz
// 1 = 2.0 kHz
// 2 = 2.5 kHz
// 3 = 3.0 kHz
// 4 = 3.75 kHz
// 5 = 4.0 kHz
// 6 = 4.25 kHz
// 7 = 4.5 kHz
// if <5> == 1, RF filter bandwidth * 2
// <14:12> RF filter bandwidth (Apass = 0.1dB)
// 000 = 2.0 kHz
// 001 = 2.5 kHz
// 010 = 3.0 kHz
// 011 = 3.5 kHz
// 100 = 4.0 kHz
// 101 = 4.5 kHz
// 110 = 5.0 kHz
// 111 = 5.5 kHz
// if REG_43<5> == 1, RF filter bandwidth *= 2
//
// <11:9> 0 RF filter bandwidth when signal is weak
// 0 = 1.7 kHz
// 1 = 2.0 kHz
// 2 = 2.5 kHz
// 3 = 3.0 kHz
// 4 = 3.75 kHz
// 5 = 4.0 kHz
// 6 = 4.25 kHz
// 7 = 4.5 kHz
// if <5> == 1, RF filter bandwidth * 2
// <11:9> RF filter bandwidth when signal is weak (Apass = 0.1dB)
// 000 = 2.0 kHz
// 001 = 2.5 kHz
// 010 = 3.0 kHz
// 011 = 3.5 kHz
// 100 = 4.0 kHz
// 101 = 4.5 kHz
// 110 = 5.0 kHz
// 111 = 5.5 kHz
// if REG_43<5> == 1, RF filter bandwidth *= 2
//
// <8:6> 1 AFTxLPF2 filter Band Width
// 1 = 2.5 kHz (for 12.5k channel space)
// 2 = 2.75 kHz
// 0 = 3.0 kHz (for 25k channel space)
// 3 = 3.5 kHz
// 4 = 4.5 kHz
// 5 = 4.25 kHz
// 6 = 4.0 kHz
// 7 = 3.75 kHz
// <8:6> AF Tx LPF2 filter bandwidth (Apass = 1dB)
// 100 = 5.5 kHz
// 101 = 5.0 kHz
// 110 = 4.5 kHz
// 111 = 4.0 kHz
// 000 = 3.0 kHz
// 001 = 2.5 kHz
// 010 = 2.75 kHz
// 011 = 3.5 kHz
//
// <5:4> 0 BW Mode Selection
// 0 = 12.5k
// 1 = 6.25k
// 2 = 25k/20k
// <5:4> BW mode selection
// 00 = 12.5k
// 01 = 6.25k
// 10 = 25k/20k
//
// <3> 1 ???
// <2> gain after FM demodulation
// 0 = 0 dB
// 1 = 6 dB
//
// <2> 0 Gain after FM Demodulation
// 0 = 0dB
// 1 = 6dB
//
// <1:0> 0 ???
// <3> and <1:0> remain undocumented here.
uint16_t val = 0;
switch (Bandwidth)
{
case BK4819_FILTER_BW_WIDE: // 25kHz
// if (weak_no_different) {
// // make the RX bandwidth the same with weak signals
// val = 0x3628; // Old value 0x49a8 < v3.6
// } else {
// // with weak RX signals the RX bandwidth is reduced
// val = 0x3428; // Old value 0x45a8 < v3.6
// }
// 0x3028 = (0b011 << 12) | (0b000 << 9) | (0b000 << 6) |
// (0b10 << 4) | (1 << 3)
// = RF 3.5 kHz and weak-RF 2.0 kHz, both doubled because
// bit<5> is set in 25k/20k mode => RF 7.0 kHz, weak-RF
// 4.0 kHz, AF Tx LPF2 3.0 kHz, FM gain 0 dB.
val = 0x3028;
break;
case BK4819_FILTER_BW_NARROW: // 12.5kHz
// if (weak_no_different) {
// // make the RX bandwidth the same with weak signals
// val = 0x3648; // Old value 0x4808 < v3.6
// } else {
// // with weak RX signals the RX bandwidth is reduced
// val = 0x3448; // Old value 0x4408 < v3.6
// }
// 0x4048 = (0b100 << 12) | (0b000 << 9) | (0b001 << 6) |
// (0b00 << 4) | (1 << 3)
// = RF 4.0 kHz, weak-RF 2.0 kHz, AF Tx LPF2 2.5 kHz,
// 12.5k mode, FM gain 0 dB.
val = 0x4048;
break;
case BK4819_FILTER_BW_NARROWER: // 6.25kHz
// if (weak_no_different) {
// // make the RX bandwidth the same with weak signals
// val = 0x1348; // Old value 0x3658 < v3.6
// } else {
// // with weak RX signals the RX bandwidth is reduced
// val = 0x1148; // Old value 0x3658 < v3.6
// }
// 0x205C = (0b010 << 12) | (0b000 << 9) | (0b001 << 6) |
// (0b01 << 4) | (1 << 3) | (1 << 2)
// = RF 3.0 kHz, weak-RF 2.0 kHz, AF Tx LPF2 2.5 kHz,
// 6.25k mode, FM gain +6 dB.
val = 0x205C;
break;
case BK4819_FILTER_BW_AM: // 8.33kHz
// if (weak_no_different) {
// // make the RX bandwidth the same with weak signals
// val = 0x4858;
// } else {
// // with weak RX signals the RX bandwidth is reduced
// val = 0x4458;
// }
case BK4819_FILTER_BW_AM: // Stock AM preset
// 0x345C = (0b011 << 12) | (0b010 << 9) | (0b001 << 6) |
// (0b01 << 4) | (1 << 3) | (1 << 2)
// = RF 3.5 kHz, weak-RF 3.0 kHz, AF Tx LPF2 2.5 kHz,
// 6.25k mode, bit<2> set. The "8.33kHz AM" label used in
// the codebase is functional/empirical; it does not come
// directly from the BW mode bits of REG_43.
val = 0x345C;
break;
default:
val = 0x5C;
break;
@@ -797,21 +766,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
@@ -1154,45 +1122,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)
+2 -1
View File
@@ -48,7 +48,8 @@ static const AddrMapping_t ADDR_MAPPINGS[] = {
_MK_MAPPING(0x006000, 0x006000, 0x007000), // 256 MR Name * 16 Bytes
_MK_MAPPING(0x007000, 0x007000, 0x008000), // 256 MR Name * 16 Bytes
_MK_MAPPING(0x008000, 0x008000, 0x00880E), // 1024 MR + 7 VFO Attributes * 2 Bytes (ex 0x002000)
_MK_MAPPING(0x008000, 0x008000, 0x00886E), // 1024 MR + 7 VFO Attributes * 2 Bytes (ex 0x002000) 0x008000 -> 0x00880E
// List name * 4 Bytes 0x00880E -> 0x00886E
_MK_MAPPING(0x009000, 0x009000, 0x0090D6), // 14 VFO * 16 Bytes (ex 0x001000)
+112
View File
@@ -27,6 +27,84 @@ KEY_Code_t gKeyReading1 = KEY_INVALID;
uint16_t gDebounceCounter = 0;
bool gWasFKeyPressed = false;
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
// Short press: hold key for SERIAL_KEY_SHORT_POLLS calls.
// Must exceed key_debounce_10ms (2) to trigger ProcessKey(key, true, false).
#define SERIAL_KEY_SHORT_POLLS 5
// Long press: hold key for SERIAL_KEY_LONG_POLLS calls.
// Must exceed key_repeat_delay_10ms (40) to trigger ProcessKey(key, true, true).
#define SERIAL_KEY_LONG_POLLS 45
// Packet types for serial key injection (K5Viewer → radio)
#define SERIAL_KEY_TYPE 0x03
#define SERIAL_KEY_TYPE_LONG 0x04
volatile KEY_Code_t gKeyFromSerial = KEY_INVALID;
static uint8_t gSerialKeyHoldCount = 0;
static uint8_t gSerialKeyLong = 0; // 0 = short press, 1 = long press
// Inject a short or long press from serial (UART or VCP).
// KEY_PTT is explicitly blocked — PTT release cannot be guaranteed over serial.
static inline void KEYBOARD_InjectKey(uint8_t keyCode, bool keyLong)
{
if (keyCode < KEY_INVALID && keyCode != KEY_PTT) {
gKeyFromSerial = (KEY_Code_t)keyCode;
gSerialKeyHoldCount = 0;
gSerialKeyLong = keyLong;
}
}
bool KEYBOARD_ProcessProtocolByte(ParseState_t *state, uint8_t b)
{
bool connected = false;
switch (*state)
{
case STATE_IDLE:
if (b == 0x55) *state = STATE_KA_1;
else if (b == 0xAA) *state = STATE_KEY_1;
break;
case STATE_KA_1:
*state = (b == 0xAA) ? STATE_KA_2 : STATE_IDLE;
break;
case STATE_KA_2:
*state = (b == 0x00) ? STATE_KA_3 : STATE_IDLE;
break;
case STATE_KA_3:
if (b == 0x00) connected = true;
*state = STATE_IDLE;
break;
case STATE_KEY_1:
*state = (b == 0x55) ? STATE_KEY_2 : STATE_IDLE;
break;
case STATE_KEY_2:
if (b == SERIAL_KEY_TYPE) *state = STATE_KEY_3;
else if (b == SERIAL_KEY_TYPE_LONG) *state = STATE_KEY_3L;
else *state = STATE_IDLE;
break;
case STATE_KEY_3:
case STATE_KEY_3L:
KEYBOARD_InjectKey(b, *state == STATE_KEY_3L);
connected = true;
*state = STATE_IDLE;
break;
default:
*state = STATE_IDLE;
break;
}
return connected;
}
#endif
#define GPIOx GPIOB
#define PIN_MASK_COLS (LL_GPIO_PIN_6 | LL_GPIO_PIN_5 | LL_GPIO_PIN_4 | LL_GPIO_PIN_3)
#define PIN_COLS GPIO_MAKE_PIN(GPIOx, PIN_MASK_COLS)
@@ -78,6 +156,25 @@ static const KEY_Code_t keyboard[5][4] = {
KEY_Code_t KEYBOARD_Poll(void)
{
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
// Serial-injected key: hold it for SHORT or LONG polls depending on press type,
// so the debounce counter in app.c reaches the right threshold:
// - Short: key_debounce_10ms (2) → ProcessKey(key, true, false)
// - Long: key_repeat_delay_10ms (40) → ProcessKey(key, true, true)
// Once the hold count is exhausted we clear it — next call returns KEY_INVALID,
// which triggers the release path in app.c naturally.
if (gKeyFromSerial != KEY_INVALID) {
KEY_Code_t injected = gKeyFromSerial;
uint8_t threshold = gSerialKeyLong ? SERIAL_KEY_LONG_POLLS : SERIAL_KEY_SHORT_POLLS;
if (++gSerialKeyHoldCount >= threshold) {
gKeyFromSerial = KEY_INVALID;
gSerialKeyHoldCount = 0;
gSerialKeyLong = 0;
}
return injected;
}
#endif
KEY_Code_t Key = KEY_INVALID;
// Scan all 5 columns - never break early to avoid GPIO state issues
@@ -153,4 +250,19 @@ KEY_Code_t KEYBOARD_Poll(void)
GPIO_SetOutputPin(PIN_COLS);
return Key;
}
KEY_Code_t KEYBOARD_GetKey(void)
{
KEY_Code_t btn = KEYBOARD_Poll();
if (btn == KEY_INVALID && GPIO_IsPttPressed())
{
btn = KEY_PTT;
}
return btn;
}
void HideFKeyIcon(void) {
gWasFKeyPressed = false;
gUpdateStatus = true;
}
+21 -1
View File
@@ -45,12 +45,32 @@ enum KEY_Code_e {
};
typedef enum KEY_Code_e KEY_Code_t;
typedef enum {
STATE_IDLE = 0,
STATE_KA_1,
STATE_KA_2,
STATE_KA_3,
STATE_KEY_1,
STATE_KEY_2,
STATE_KEY_3,
STATE_KEY_3L,
} ParseState_t;
extern KEY_Code_t gKeyReading0;
extern KEY_Code_t gKeyReading1;
extern uint16_t gDebounceCounter;
extern bool gWasFKeyPressed;
KEY_Code_t KEYBOARD_Poll(void);
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
// Serial-injected key (written by UART/VCP parser, consumed by KEYBOARD_Poll).
extern volatile KEY_Code_t gKeyFromSerial;
bool KEYBOARD_ProcessProtocolByte(ParseState_t *state, uint8_t b);
#endif
KEY_Code_t KEYBOARD_Poll(void);
KEY_Code_t KEYBOARD_GetKey(void);
void HideFKeyIcon(void);
#endif
+3 -3
View File
@@ -256,9 +256,9 @@ void PY25Q16_WriteBuffer(uint32_t Address, const void *pBuffer, uint32_t Size, b
printf("spi flash write: %06x %ld %d\n", Address, Size, Append);
#endif
#ifdef ENABLE_FEAT_F4HWN_DEBUG
gDebug++;
#endif
//#ifdef ENABLE_FEAT_F4HWN_DEBUG
// gDebug++;
//#endif
uint32_t SecIndex = Address / SECTOR_SIZE;
uint32_t SecAddr = SecIndex * SECTOR_SIZE;
+4
View File
@@ -24,6 +24,7 @@
#include "driver/st7565.h"
#include "driver/system.h"
#include "misc.h"
#include "screenshot.h"
#define SPIx SPI1
@@ -163,6 +164,9 @@ void ST7565_DrawLine(const unsigned int Column, const unsigned int Line, const u
void ST7565_BlitLine(unsigned line)
{
ST7565_BlitScreen(line + 1);
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
SCREENSHOT_Update(true); // Force immediate capture
#endif
}
void ST7565_BlitStatusLine(void)
+24 -7
View File
@@ -22,7 +22,10 @@
#include "py32f071_ll_dma.h"
#include "py32f071_ll_gpio.h"
#include "py32f071_ll_usart.h"
#include "driver/uart.h"
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
#include "driver/keyboard.h"
#endif
#define USARTx USART1
#define DMA_CHANNEL LL_DMA_CHANNEL_2
@@ -149,12 +152,26 @@ void UART_LogSend(const void *pBuffer, uint32_t Size)
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
bool UART_IsCableConnected(void) {
for (size_t i = 0; i < sizeof(UART_DMA_Buffer); i++) {
if (UART_DMA_Buffer[i] == 0x55) {
UART_DMA_Buffer[i] = 0x00; // Clear only the matched byte
return true;
}
static uint8_t read_ptr = 0;
static ParseState_t state = STATE_IDLE;
bool connected = false;
// DMA write position: NbData counts DOWN from 256
uint8_t write_ptr = (uint8_t)(sizeof(UART_DMA_Buffer) -
LL_DMA_GetDataLength(DMA1, DMA_CHANNEL));
uint8_t processed = 0;
while (read_ptr != write_ptr && processed < sizeof(UART_DMA_Buffer))
{
uint8_t b = UART_DMA_Buffer[read_ptr++];
// read_ptr wraps naturally at 256 since it's uint8_t
processed++;
if(KEYBOARD_ProcessProtocolByte(&state, b))
connected = true;
}
return false;
return connected;
}
#endif
+39 -8
View File
@@ -18,6 +18,10 @@
#include "usb_config.h"
#include "py32f071_ll_bus.h"
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
#include "driver/keyboard.h"
#endif
uint8_t VCP_RxBuf[VCP_RX_BUF_SIZE];
volatile uint32_t VCP_RxBufPointer = 0;
@@ -38,24 +42,51 @@ void VCP_Init()
NVIC_EnableIRQ(USBD_IRQn);
}
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
bool VCP_ScreenshotPing(void)
{
static uint32_t read_ptr = 0;
// State machine for parsing incoming packets:
// Keepalive: 0x55 0xAA 0x00 0x00 → viewer alive
// Short key press: 0xAA 0x55 0x03 <key> → inject short press
// Long key press: 0xAA 0x55 0x04 <key> → inject long press
//
// State transitions:
// IDLE → 0x55 → KA_1
// KA_1 → 0xAA → KA_2 (else IDLE)
// KA_2 → 0x00 → KA_3 (else IDLE)
// KA_3 → 0x00 → keepalive OK, IDLE
//
// IDLE → 0xAA → KEY_1
// KEY_1 → 0x55 → KEY_2 (else IDLE)
// KEY_2 → 0x03 → KEY_3 (short press, else check long)
// KEY_2 → 0x04 → KEY_3L (long press, else IDLE)
// KEY_3 → <b> → KEYBOARD_InjectKey(b, false), IDLE
// KEY_3L → <b> → KEYBOARD_InjectKey(b, true), IDLE
uint32_t write_ptr = VCP_RxBufPointer;
static uint8_t read_ptr = 0;
static ParseState_t state = STATE_IDLE;
while (read_ptr != write_ptr)
bool connected = false;
uint8_t write_ptr = VCP_RxBufPointer; // snapshot once — ISR may update concurrently
// Cap bytes processed per call to VCP_RX_BUF_SIZE.
// Prevents unbounded loop if the ISR write pointer laps read_ptr
// (buffer overflow / corrupted state), which would freeze the firmware.
uint32_t processed = 0;
while (read_ptr != write_ptr && processed < VCP_RX_BUF_SIZE)
{
uint8_t b = VCP_RxBuf[read_ptr];
read_ptr++;
if (read_ptr >= VCP_RX_BUF_SIZE)
read_ptr = 0;
processed++;
if (b == 0x55)
{
return true;
}
if(KEYBOARD_ProcessProtocolByte(&state, b))
connected = true;
}
return false;
return connected;
}
#endif // ENABLE_FEAT_F4HWN_SCREENSHOT
+3
View File
@@ -28,7 +28,10 @@ extern uint8_t VCP_RxBuf[VCP_RX_BUF_SIZE];
extern volatile uint32_t VCP_RxBufPointer;
void VCP_Init();
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
bool VCP_ScreenshotPing(void);
#endif
static inline void VCP_Send(const uint8_t *Buf, uint32_t Size)
{
+1 -1
View File
@@ -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
View File
@@ -163,11 +163,8 @@ int32_t TX_freq_check(const uint32_t Frequency)
{ // return '0' if TX frequency is allowed
// otherwise return '-1'
if (Frequency < frequencyBandTable[0].lower || Frequency > frequencyBandTable[BAND_N_ELEM - 1].upper)
return -1; // not allowed outside this range
if (Frequency >= BX4819_band1.upper && Frequency < BX4819_band2.lower)
return -1; // BX chip does not work in this range
if (RX_freq_check(Frequency))
return -1;
switch (gSetting_F_LOCK)
{
@@ -280,10 +277,10 @@ int32_t RX_freq_check(const uint32_t Frequency)
// otherwise return '-1'
if (Frequency < frequencyBandTable[0].lower || Frequency > frequencyBandTable[BAND_N_ELEM - 1].upper)
return -1;
return -1; // not allowed outside this range
if (Frequency >= BX4819_band1.upper && Frequency < BX4819_band2.lower)
return -1;
return -1; // BX chip does not work in this range
return 0; // OK frequency
return 0; // OK frequency
}
+1 -8
View File
@@ -118,14 +118,7 @@ void FUNCTION_Foreground(const FUNCTION_Type_t PreviousFunction)
void FUNCTION_PowerSave() {
#ifdef ENABLE_FEAT_F4HWN_SLEEP
if(gWakeUp)
{
gPowerSave_10ms = gEeprom.BATTERY_SAVE * 200; // deep sleep now indexed on BatSav
}
else
{
gPowerSave_10ms = gEeprom.BATTERY_SAVE * 10;
}
gPowerSave_10ms = gEeprom.BATTERY_SAVE * (gWakeUp ? 200 : 10); // deep sleep now indexed on BatSav
#else
gPowerSave_10ms = gEeprom.BATTERY_SAVE * 10;
#endif
+25 -64
View File
@@ -277,77 +277,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) {
+19 -3
View File
@@ -87,7 +87,7 @@ const uint16_t NOAA_countdown_3_10ms = 200 / 10; // 200ms
const uint32_t gDefaultAesKey[4] = {0x4AA5CC60, 0x0312CC5F, 0xFFD2DABB, 0x6BBA7F92};
const uint8_t gMicGain_dB2[5] = {3, 8, 16, 24, 31};
const uint8_t gMicGain_dB2[9] = {3, 8, 16, 24, 32, 40, 48, 56, 63}; // BK4819 {3, 8, 16, 24, 31};
#ifndef ENABLE_FEAT_F4HWN
bool gSetting_350TX;
@@ -135,7 +135,7 @@ enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
bool gSetting_set_tmr = 0;
bool gSetting_set_ptt_session;
#ifdef ENABLE_FEAT_F4HWN_DEBUG
uint16_t gDebug;
int16_t gDebug;
#endif
uint8_t gDW = 0;
uint8_t gCB = 0;
@@ -149,6 +149,7 @@ enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
bool gPowerHigh = false;
bool gRemoveOffset = false;
#endif
int8_t dBmCorrTable[7] = {-15, -16, -10, -4, -7, -6, -1};
#endif
#ifdef ENABLE_AUDIO_BAR
@@ -241,6 +242,7 @@ volatile uint16_t gScanPauseDelayIn_10ms;
uint16_t gMenuCountdown;
bool gPttWasReleased;
bool gPttWasPressed;
bool gHasVfoBackup;
uint8_t gKeypadLocked;
bool gFlagReconfigureVfos;
uint8_t gVfoConfigureMode;
@@ -248,6 +250,7 @@ bool gFlagResetVfos;
bool gRequestSaveVFO;
uint16_t gRequestSaveChannel;
bool gRequestSaveSettings;
bool gRequestSaveSquelch;
#ifdef ENABLE_FMRADIO
bool gRequestSaveFM;
#endif
@@ -292,7 +295,7 @@ uint8_t gFSKWriteIndex;
#ifdef ENABLE_NOAA
bool gIsNoaaMode;
uint16_t gNoaaChannel;
uint8_t gNoaaChannel;
#endif
bool gUpdateDisplay;
@@ -334,6 +337,7 @@ uint8_t gIsLocked = 0xFF;
uint16_t gVfoSaveCountdown_10ms = 0;
bool gScheduleVfoSave = false;
bool gVfoStateChanged = false;
char gListName[MR_CHANNELS_LIST][4];
#endif
inline void FUNCTION_NOP() { ; }
@@ -632,4 +636,16 @@ void MR_PrintCacheStats(void)
// cache_hits, cache_misses, MR_GetCacheHitRate());
}
#endif
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
bool SCREENSHOT_IsLocked(void)
{
if (gUART_LockScreenshot > 0) {
gUART_LockScreenshot--;
return true;
}
return false;
}
#endif
+9 -2
View File
@@ -148,7 +148,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;
@@ -197,7 +197,7 @@ extern enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
extern bool gSetting_set_tmr;
extern bool gSetting_set_ptt_session;
#ifdef ENABLE_FEAT_F4HWN_DEBUG
extern uint16_t gDebug;
extern int16_t gDebug;
#endif
extern uint8_t gDW;
extern uint8_t gCB;
@@ -211,6 +211,7 @@ extern enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx;
extern bool gPowerHigh;
extern bool gRemoveOffset;
#endif
extern int8_t dBmCorrTable[7];
#endif
#ifdef ENABLE_AUDIO_BAR
@@ -322,6 +323,8 @@ extern volatile bool gTxTimeoutReached;
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
extern volatile uint8_t gUART_LockScreenshot; // lock screenshot if Chirp is used
extern bool gUSB_ScreenshotEnabled;
bool SCREENSHOT_IsLocked(void);
#endif
#endif
@@ -365,12 +368,14 @@ 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 uint16_t gRequestSaveChannel;
extern bool gRequestSaveSettings;
extern bool gRequestSaveSquelch;
#ifdef ENABLE_FMRADIO
extern bool gRequestSaveFM;
#endif
@@ -455,6 +460,8 @@ extern volatile uint8_t boot_counter_10ms;
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);
+65 -45
View File
@@ -102,28 +102,30 @@ bool RADIO_CheckValidList(uint8_t scanList)
return false;
}
void RADIO_NextValidList(void)
void RADIO_NextValidList(int8_t direction)
{
uint8_t startList = gEeprom.SCAN_LIST_DEFAULT;
uint8_t attempts = 0;
const uint8_t MAX_LISTS = MR_CHANNELS_LIST + 2; // 1-25, includes ALL
const uint8_t MAX_VALUE = MR_CHANNELS_LIST + 1; // 25 (1-24 lists + ALL)
do {
// Move to next scan list, wrapping around from 25 to 1
gEeprom.SCAN_LIST_DEFAULT = ((gEeprom.SCAN_LIST_DEFAULT + 1) % MAX_LISTS) ?: 1;
if (direction > 0) {
// Forward: 1 → 2 → ... → 25 → 1
gEeprom.SCAN_LIST_DEFAULT = (gEeprom.SCAN_LIST_DEFAULT % MAX_VALUE) + 1;
} else {
// Backward: 25 → 24 → ... → 1 → 25
gEeprom.SCAN_LIST_DEFAULT = ((gEeprom.SCAN_LIST_DEFAULT - 2 + MAX_VALUE) % MAX_VALUE) + 1;
}
attempts++;
// Check if current list has valid channels
if (RADIO_CheckValidList(gEeprom.SCAN_LIST_DEFAULT))
return;
// Stop if we've cycled through all lists or made too many attempts
} while (gEeprom.SCAN_LIST_DEFAULT != startList && attempts < MAX_LISTS);
} while (gEeprom.SCAN_LIST_DEFAULT != startList && attempts < MAX_VALUE);
// Safety fallback: if no valid list found, switch to ALL mode
// This prevents infinite loops and ensures scanning can continue
// Safety fallback: switch to ALL mode
if (!RADIO_CheckValidList(gEeprom.SCAN_LIST_DEFAULT)) {
gEeprom.SCAN_LIST_DEFAULT = MR_CHANNELS_LIST + 1; // ALL
gEeprom.SCAN_LIST_DEFAULT = MAX_VALUE; // ALL (25)
}
}
@@ -794,8 +796,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
@@ -924,8 +926,8 @@ void RADIO_SetupRegisters(bool switchToForeground)
BK4819_EnableDTMF();
InterruptMask |= BK4819_REG_3F_DTMF_5TONE_FOUND;
//RADIO_SetupAGC(gRxVfo->Modulation == MODULATION_AM, false);
RADIO_SetupAGC(false, false);
RADIO_SetupAGC(gRxVfo->Modulation == MODULATION_AM, false);
//RADIO_SetupAGC(false, false);
// enable/disable BK4819 selected interrupts
BK4819_WriteRegister(BK4819_REG_3F, InterruptMask);
@@ -1054,6 +1056,31 @@ void RADIO_SetTxParameters(void)
void RADIO_SetModulation(ModulationMode_t modulation)
{
#ifdef ENABLE_BYP_RAW_DEMODULATORS
// BYP on BK4829 uses full audio bypass profile.
if (modulation == MODULATION_BYP) {
BK4819_EnterBypass();
BK4819_SetRegValue(afDacGainRegSpec, 0xF);
BK4819_WriteRegister(BK4819_REG_3D, 0x2AAB);
RADIO_SetupAGC(false, false);
return;
}
// RAW on BK4829 uses RX-only filter bypass profile.
if (modulation == MODULATION_RAW) {
BK4819_EnterRaw();
BK4819_SetRegValue(afDacGainRegSpec, 0xF);
BK4819_WriteRegister(BK4819_REG_3D, 0x0000);
RADIO_SetupAGC(false, false);
return;
}
#endif
#ifdef ENABLE_BYP_RAW_DEMODULATORS
// Ensure we always leave bypass / raw mode before applying normal modulation settings.
BK4819_ExitBypass();
#endif
BK4819_AF_Type_t mod;
switch(modulation) {
default:
@@ -1067,14 +1094,6 @@ void RADIO_SetModulation(ModulationMode_t modulation)
mod = BK4819_AF_BASEBAND2;
break;
#ifdef ENABLE_BYP_RAW_DEMODULATORS
case MODULATION_BYP:
mod = BK4819_AF_UNKNOWN3;
break;
case MODULATION_RAW:
mod = BK4819_AF_BASEBAND1;
break;
#endif
}
BK4819_SetAF(mod);
@@ -1109,13 +1128,16 @@ void RADIO_SetModulation(ModulationMode_t modulation)
BK4819_WriteRegister(0x31,uVar1 | 1);
BK4819_WriteRegister(0x42,0x6f5c);
BK4819_WriteRegister(0x2a,0x7434);
BK4819_WriteRegister(0x2b,0x300);
BK4819_WriteRegister(0x2b,0x400);
BK4819_WriteRegister(0x2f,0x9990);
//BK4819_WriteRegister(0x54, 0x9775);
//BK4819_WriteRegister(0x55, 0x32c6);
BK4819_WriteRegister(0x54, 0x8846);
BK4819_WriteRegister(0x55, 0x38C0);
//BK4819_WriteRegister(0x54, 0x8846);
//BK4819_WriteRegister(0x55, 0x38C0);
BK4819_WriteRegister(0x54, 0x9009);
BK4819_WriteRegister(0x55, 0x31a9);
BK4819_SetFilterBandwidth(BK4819_FILTER_BW_AM, true);
}
@@ -1124,36 +1146,28 @@ void RADIO_SetModulation(ModulationMode_t modulation)
BK4819_WriteRegister(BK4819_REG_3D, modulation == MODULATION_USB ? 0 : 0x2AAB);
BK4819_SetRegValue(afcDisableRegSpec, modulation != MODULATION_FM);
//RADIO_SetupAGC(modulation == MODULATION_AM, false);
RADIO_SetupAGC(false, false);
RADIO_SetupAGC(modulation == MODULATION_AM, false);
//RADIO_SetupAGC(false, false);
}
void RADIO_SetupAGC(bool listeningAM, bool disable)
{
static uint8_t lastSettings;
static uint8_t lastSettings = 0xFF;
uint8_t newSettings = (listeningAM << 1) | disable;
if(lastSettings == newSettings)
if (lastSettings == newSettings)
return;
lastSettings = newSettings;
if(!listeningAM) { // if not actively listening AM we don't need any AM specific regulation
BK4819_SetAGC(!disable);
BK4819_InitAGC(false);
}
else {
#ifdef ENABLE_AM_FIX
if(gSetting_AM_fix) { // if AM fix active lock AGC so AM-fix can do it's job
BK4819_SetAGC(0);
AM_fix_enable(!disable);
}
else
#endif
{
BK4819_SetAGC(!disable);
BK4819_InitAGC(true);
}
if (listeningAM && gSetting_AM_fix) {
BK4819_SetAGC(0);
AM_fix_enable(!disable);
return;
}
#endif
BK4819_SetAGC(!disable);
BK4819_InitAGC(listeningAM);
}
void RADIO_SetVfoState(VfoState_t State)
@@ -1227,6 +1241,12 @@ void RADIO_PrepareTX(void)
// over voltage .. this is being a pain
State = VFO_STATE_VOLTAGE_HIGH;
}
#ifdef ENABLE_BYP_RAW_DEMODULATORS
else if (gCurrentVfo->Modulation == MODULATION_BYP || gCurrentVfo->Modulation == MODULATION_RAW) {
// BYP/RAW are receive-only modes.
State = VFO_STATE_TX_DISABLE;
}
#endif
#ifndef ENABLE_TX_WHEN_AM
else if (gCurrentVfo->Modulation != MODULATION_FM) {
// not allowed to TX if in AM mode
+1 -1
View File
@@ -149,7 +149,7 @@ extern DCS_CodeType_t gCurrentCodeType;
extern VfoState_t VfoState[2];
bool RADIO_CheckValidList(uint8_t scanList);
void RADIO_NextValidList(void);
void RADIO_NextValidList(int8_t direction);
bool RADIO_CheckValidChannel(uint16_t channel, bool checkScanList, uint8_t scanList);
uint16_t RADIO_FindNextChannel(uint16_t ChNum, int8_t Direction, bool bCheckScanList, uint8_t RadioNum);
void RADIO_InitInfo(VFO_Info_t *pInfo, const uint16_t ChannelSave, const uint32_t Frequency);
+60 -48
View File
@@ -19,8 +19,31 @@
#include "screenshot.h"
#include "misc.h"
#include "driver/vcp.h"
#include "driver/keyboard.h"
static void Screenshot_Send(const uint8_t *buf, uint16_t len)
// SRAM optimization: minimize static allocations
// - previousFrame: 1024 bytes (REQUIRED - need to compare for delta)
// - No currentFrame or deltaFrame static buffers
static uint8_t previousFrame[1024] = {0};
static uint8_t forcedBlock = 0;
static uint8_t keepAlive = 3;
void SCREENSHOT_ParseInput(void)
{
if (SCREENSHOT_IsLocked())
return;
if (UART_IsCableConnected()) {
keepAlive = 15;
gUSB_ScreenshotEnabled = false;
}
else if (VCP_ScreenshotPing()) {
keepAlive = 15;
gUSB_ScreenshotEnabled = true;
}
}
static void SCREENSHOT_Send(const uint8_t *buf, uint16_t len)
{
if (gUSB_ScreenshotEnabled) {
cdc_acm_data_send_with_dtr(buf, len);
@@ -29,14 +52,19 @@ static void Screenshot_Send(const uint8_t *buf, uint16_t len)
}
}
// SRAM optimization: minimize static allocations
// - previousFrame: 1024 bytes (REQUIRED - need to compare for delta)
// - No currentFrame or deltaFrame static buffers
static uint8_t previousFrame[1024] = {0};
static uint8_t forcedBlock = 0;
static uint8_t keepAlive = 10;
void SCREENSHOT_Line(uint8_t *src, uint8_t *dest, uint16_t *idx) {
for (uint8_t b = 0; b < 8; b++) {
for (uint8_t i = 0; i < 128; i += 8) {
uint8_t acc = 0;
for (uint8_t k = 0; k < 8; k++) {
if (src[i + k] & (1 << b)) acc |= (1 << k);
}
dest[(*idx)++] = gSetting_set_inv ? ~acc : acc;
}
}
}
void getScreenShot(bool force)
void SCREENSHOT_Update(bool force)
{
// Build frame in a temporary stack buffer
// This is 1024 bytes but it's temporary and gets freed after the function
@@ -44,55 +72,34 @@ void getScreenShot(bool force)
uint16_t index = 0;
uint8_t acc = 0;
uint8_t bitCount = 0;
static bool wasConnected = false;
if (gUART_LockScreenshot > 0) {
gUART_LockScreenshot--;
if (SCREENSHOT_IsLocked())
return;
}
if (UART_IsCableConnected()) {
keepAlive = 10;
gUSB_ScreenshotEnabled = false;
}
if (VCP_ScreenshotPing()) {
keepAlive = 10;
gUSB_ScreenshotEnabled = true;
}
if (keepAlive > 0) {
if (--keepAlive == 0) return;
if (--keepAlive == 0) {
// Connection just lost → reset state for next reconnection
wasConnected = false;
return;
}
} else {
return;
}
// Connection is alive — detect reconnection and force full frame
if (!wasConnected) {
force = true;
wasConnected = true;
}
// ==== BUILD FRAME ONCE ====
// Status line: 8 bit layers × 128 columns
for (uint8_t b = 0; b < 8; b++) {
for (uint8_t i = 0; i < 128; i++) {
uint8_t bit = (gStatusLine[i] >> b) & 0x01;
acc |= (bit << bitCount++);
if (bitCount == 8) {
frameBuffer[index++] = acc;
acc = 0;
bitCount = 0;
}
}
}
SCREENSHOT_Line(gStatusLine, frameBuffer, &index);
// Frame buffer: 7 lines × 8 bit layers × 128 columns
for (uint8_t l = 0; l < 7; l++) {
for (uint8_t b = 0; b < 8; b++) {
for (uint8_t i = 0; i < 128; i++) {
uint8_t bit = (gFrameBuffer[l][i] >> b) & 0x01;
acc |= (bit << bitCount++);
if (bitCount == 8) {
frameBuffer[index++] = acc;
acc = 0;
bitCount = 0;
}
}
}
SCREENSHOT_Line(gFrameBuffer[l], frameBuffer, &index);
}
if (bitCount > 0)
@@ -125,11 +132,16 @@ void getScreenShot(bool force)
if (deltaLen == 0)
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;
// ==== Send version marker (for backward compatibility detection) ====
// New format: sends 0xFF before header
// Old format: doesn't exist, so viewers can differentiate
uint8_t versionMarker = 0xFF;
Screenshot_Send(&versionMarker, 1);
SCREENSHOT_Send(&versionMarker, 1);
// ==== Send header ====
uint8_t header[5] = {
@@ -138,7 +150,7 @@ void getScreenShot(bool force)
(uint8_t)(deltaLen & 0xFF)
};
Screenshot_Send(header, 5);
SCREENSHOT_Send(header, 5);
// ==== SECOND PASS: Send only changed chunks ====
uint8_t chunk[9];
@@ -151,12 +163,12 @@ void getScreenShot(bool force)
chunk[0] = chunkIdx;
memcpy(&chunk[1], cur, 8);
Screenshot_Send(chunk, 9);
SCREENSHOT_Send(chunk, 9);
// Update previousFrame for next comparison
memcpy(prev, cur, 8);
}
uint8_t end = 0x0A;
Screenshot_Send(&end, 1);
SCREENSHOT_Send(&end, 1);
}
+2 -1
View File
@@ -17,6 +17,7 @@
#ifndef SCREENSHOT_H
#define SCREENSHOT_H
void getScreenShot(bool force);
void SCREENSHOT_Update(bool force);
void SCREENSHOT_ParseInput(void);
#endif
+46 -20
View File
@@ -59,7 +59,7 @@ void SETTINGS_InitEEPROM(void)
configByte[4] &= (uint8_t)~0x01; // KEY_LOCK = 0
configByte[4] &= (uint8_t)~0x02; // MENU_LOCK = 0
configByte[4] &= (uint8_t)~0x3C; // SET_KEY = 0
configByte[4] &= (uint8_t)~0x40; // SET_NAV = 0
//configByte[4] &= (uint8_t)~0x40; // SET_NAV = 0
PY25Q16_WriteBuffer(0x00A000, configByte, sizeof(configByte), false);
@@ -97,6 +97,24 @@ void SETTINGS_InitEEPROM(void)
if (needsWrite) {
PY25Q16_WriteBuffer(0x00A0C8, logoLines, sizeof(logoLines), false);
}
// 5. Reset dBmCorrTable
int8_t buf[7];
PY25Q16_ReadBuffer(0x00A0B9, (uint8_t *)buf, 7);
needsWrite = true;
for (uint8_t i = 0; i < 7; i++) {
if ((uint8_t)buf[i] != 0xFF) {
needsWrite = false;
break;
}
}
if (needsWrite) {
for (uint8_t i = 0; i < 7; i++)
buf[i] = dBmCorrTable[i];
PY25Q16_WriteBuffer(0x00A0B9, buf, 7, false);
}
}
}
@@ -122,7 +140,7 @@ void SETTINGS_InitEEPROM(void)
gEeprom.VOX_SWITCH = (Data[5] < 2) ? Data[5] : false;
gEeprom.VOX_LEVEL = (Data[6] < 10) ? Data[6] : 1;
#endif
gEeprom.MIC_SENSITIVITY = (Data[7] < 5) ? Data[7] : 4;
gEeprom.MIC_SENSITIVITY = (Data[7] < 9) ? Data[7] : 4;
// 0E78..0E7F
PY25Q16_ReadBuffer(0x00A008, Data, 8);
@@ -240,13 +258,10 @@ gEeprom.FreqChannel[1] = IS_FREQ_CHANNEL(Data16[5]) ? Data16[5] : (FREQ_CHANNE
gEeprom.VOICE_PROMPT = (Data[0] < 3) ? Data[0] : VOICE_PROMPT_ENGLISH;
#endif
#ifdef ENABLE_RSSI_BAR
if((Data[1] < 200 && Data[1] > 90) && (Data[2] < Data[1]-9 && Data[1] < 160 && Data[2] > 50)) {
gEeprom.S0_LEVEL = Data[1];
gEeprom.S9_LEVEL = Data[2];
}
else {
gEeprom.S0_LEVEL = 130;
gEeprom.S9_LEVEL = 76;
for (uint8_t i = 0; i < 7; i++) {
int8_t val = (int8_t)Data[i + 1];
if (val >= -64 && val <= 64)
dBmCorrTable[i] = val;
}
#endif
@@ -403,6 +418,9 @@ gEeprom.FreqChannel[1] = IS_FREQ_CHANNEL(Data16[5]) ? Data16[5] : (FREQ_CHANNE
}
*/
// Init list name
PY25Q16_ReadBuffer(0x00880E, gListName, sizeof(gListName));
// Init attr cache
MR_InitChannelAttributesCache();
@@ -599,15 +617,20 @@ void SETTINGS_FetchChannelName(char *s, const uint16_t channel)
void SETTINGS_FactoryReset(bool bIsAll)
{
PY25Q16_SectorErase(0x000000);
PY25Q16_SectorErase(0x001000);
PY25Q16_SectorErase(0x003000);
PY25Q16_SectorErase(0x004000);
PY25Q16_SectorErase(0x005000);
PY25Q16_SectorErase(0x006000);
PY25Q16_SectorErase(0x007000);
PY25Q16_SectorErase(0x008000);
PY25Q16_SectorErase(0x009000);
// PY25Q16_SectorErase(0x000000);
// PY25Q16_SectorErase(0x001000);
// PY25Q16_SectorErase(0x002000);
// PY25Q16_SectorErase(0x003000);
// PY25Q16_SectorErase(0x004000);
// PY25Q16_SectorErase(0x005000);
// PY25Q16_SectorErase(0x006000);
// PY25Q16_SectorErase(0x007000);
// PY25Q16_SectorErase(0x008000);
// PY25Q16_SectorErase(0x009000);
for (uint32_t addr = 0x000000; addr <= 0x009000; addr += 0x1000) {
PY25Q16_SectorErase(addr);
}
// 0d60 - 0e30
if (bIsAll)
@@ -743,7 +766,10 @@ void SETTINGS_SaveSettings(void)
#ifdef ENABLE_FEAT_F4HWN_AUDIO
State[0] = gSetting_set_audio;
#endif
State[1] = gEeprom.SQUELCH_LEVEL;
if (gRequestSaveSquelch)
{
State[1] = gEeprom.SQUELCH_LEVEL;
}
State[2] = gEeprom.TX_TIMEOUT_TIMER;
#ifdef ENABLE_NOAA
State[3] = gEeprom.NOAA_AUTO_SCAN;
@@ -1105,7 +1131,7 @@ void SETTINGS_UpdateChannel(uint16_t channel, const VFO_Info_t *pVFO, bool keep,
}; // default attributes
// 0x0D60
PY25Q16_ReadBuffer(0x008000 + channel, &state, 1);
PY25Q16_ReadBuffer(0x008000 + (channel * 2), &state, 2);
if (keep) {
att.band = pVFO->Band;
+4
View File
@@ -35,6 +35,10 @@ void UI_DisplayFM(void)
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",
+38 -34
View File
@@ -22,6 +22,7 @@
#include "ui/helper.h"
#include "ui/inputbox.h"
#include "misc.h"
#include "settings.h"
void UI_GenerateChannelString(char *pString, const uint16_t Channel)
@@ -44,23 +45,23 @@ void UI_GenerateChannelString(char *pString, const uint16_t Channel)
void UI_GenerateChannelStringEx(char *pString, const bool bShowPrefix, const uint16_t ChannelNumber)
{
if (gInputBoxIndex > 0) {
for (unsigned int i = 0; i < 3; i++) {
for (unsigned int i = 0; i < 4; i++) {
pString[i] = (gInputBox[i] == 10) ? '-' : gInputBox[i] + '0';
}
pString[3] = 0;
pString[4] = 0;
return;
}
if (bShowPrefix) {
// BUG here? Prefixed NULLs are allowed
sprintf(pString, "CH-%03u", ChannelNumber + 1);
sprintf(pString, "CH-%04u", ChannelNumber + 1);
} else if (ChannelNumber == MR_CHANNEL_LAST + 1) {
strcpy(pString, "None");
} else if (ChannelNumber == 0xFFFF) {
strcpy(pString, "NULL");
} else {
sprintf(pString, "%03u", ChannelNumber + 1);
sprintf(pString, "%04u", ChannelNumber + 1);
}
}
@@ -130,17 +131,17 @@ void UI_PrintStringSmallNormalInverse(const char *pString, uint8_t Start, uint8_
if (End != 0 && x_end > End)
x_end = End;
gFrameBuffer[Line][x_start - 3] ^= 0x3E;
gFrameBuffer[Line][x_start - 2] ^= 0x7F;
gFrameBuffer[Line][x_start - 1] ^= 0xFF;
//gFrameBuffer[Line][x_start - 2] ^= 0x3E;
gFrameBuffer[Line][x_start - 1] ^= 0x7F;
//gFrameBuffer[Line][x_start - 1] ^= 0xFF;
for (uint8_t x = x_start; x < x_end; x++)
{
gFrameBuffer[Line][x] ^= 0xFF;
gFrameBuffer[Line - 1][x] ^= 0x80;
}
gFrameBuffer[Line][x_end + 0] ^= 0xFF;
gFrameBuffer[Line][x_end + 1] ^= 0x7F;
gFrameBuffer[Line][x_end + 2] ^= 0x3E;
//gFrameBuffer[Line][x_end + 0] ^= 0xFF;
gFrameBuffer[Line][x_end + 0] ^= 0x7F;
//gFrameBuffer[Line][x_end + 1] ^= 0x3E;
}
@@ -157,30 +158,6 @@ void UI_PrintStringSmallBold(const char *pString, uint8_t Start, uint8_t End, ui
UI_PrintStringSmall(pString, Start, End, Line, char_width, font);
}
void UI_PrintStringSmallBoldInverse(const char *pString, uint8_t Start, uint8_t End, uint8_t Line)
{
// First draw the string normally
UI_PrintStringSmallBold(pString, Start, End, Line);
// Now invert the framebuffer bits for the rendered area
uint8_t len = strlen(pString);
uint8_t char_width = 7; // small font is typically 6px wide
uint8_t x_start = Start;
uint8_t x_end = Start + (len * char_width);
if (End != 0 && x_end > End)
x_end = End;
gFrameBuffer[Line][x_start] ^= 0x7F;
for (uint8_t x = x_start + 1; x < x_end; x++)
{
gFrameBuffer[Line][x] ^= 0x41;
}
gFrameBuffer[Line][x_end + 1] ^= 0x7F;
}
void UI_PrintStringSmallBufferNormal(const char *pString, uint8_t * buffer)
{
UI_PrintStringBuffer(pString, buffer, ARRAY_SIZE(gFontSmall[0]), (uint8_t *)gFontSmall);
@@ -345,6 +322,33 @@ static void sort(int16_t *a, int16_t *b)
x += 4;
}
}
void UI_DisplayUnlockKeyboard(uint8_t shift) {
if (gEeprom.KEY_LOCK && gKeypadLocked > 0)
{ // tell user how to unlock the keyboard
//memcpy(gFrameBuffer[shift] + 2, gFontKeyLock, sizeof(gFontKeyLock));
UI_PrintStringSmallBold("UNLOCK KEYBOARD", 12, 0, shift);
//memcpy(gFrameBuffer[shift] + 120, gFontKeyLock, sizeof(gFontKeyLock));
/*
for (uint8_t i = 12; i < 116; i++)
{
gFrameBuffer[shift][i] ^= 0xFF;
}
*/
}
}
bool IsEmptyName(const char *name, uint8_t len) {
if (name[0] == '\0' || name[0] == '\xff')
return true;
for (uint8_t i = 0; i < len; i++) {
if (name[i] != ' ' && name[i] != '\xff' && name[i] != '\0')
return false;
}
return true;
}
#endif
void UI_DrawLineBuffer(uint8_t (*buffer)[128], int16_t x1, int16_t y1, int16_t x2, int16_t y2, bool black)
+2 -1
View File
@@ -26,7 +26,6 @@ void UI_PrintString(const char *pString, uint8_t Start, uint8_t End, uint8_t Lin
void UI_PrintStringSmallNormal(const char *pString, uint8_t Start, uint8_t End, uint8_t Line);
void UI_PrintStringSmallNormalInverse(const char *pString, uint8_t Start, uint8_t End, uint8_t Line);
void UI_PrintStringSmallBold(const char *pString, uint8_t Start, uint8_t End, uint8_t Line);
void UI_PrintStringSmallBoldInverse(const char *pString, uint8_t Start, uint8_t End, uint8_t Line);
void UI_PrintStringSmallBufferNormal(const char *pString, uint8_t *buffer);
void UI_PrintStringSmallBufferBold(const char *pString, uint8_t * buffer);
void UI_DisplayFrequency(const char *string, uint8_t X, uint8_t Y, bool center);
@@ -39,6 +38,8 @@ void UI_DrawPixelBuffer(uint8_t (*buffer)[128], uint8_t x, uint8_t y, bool black
void PutPixel(uint8_t x, uint8_t y, bool fill);
void PutPixelStatus(uint8_t x, uint8_t y, bool fill);
void GUI_DisplaySmallest(const char *pString, uint8_t x, uint8_t y, bool statusbar, bool fill);
void UI_DisplayUnlockKeyboard(uint8_t shift);
bool IsEmptyName(const char *name, uint8_t len);
#endif
void UI_DrawLineBuffer(uint8_t (*buffer)[128], int16_t x1, int16_t y1, int16_t x2, int16_t y2, bool black);
void UI_DrawRectangleBuffer(uint8_t (*buffer)[128], int16_t x1, int16_t y1, int16_t x2, int16_t y2, bool black);
+338 -137
View File
@@ -46,7 +46,7 @@
center_line_t center_line = CENTER_LINE_NONE;
#ifdef ENABLE_FEAT_F4HWN
static int8_t RxBlink;
// static int8_t RxBlink;
static int8_t RxBlinkLed = 0;
static int8_t RxBlinkLedCounter;
static int8_t RxLine;
@@ -60,16 +60,6 @@ center_line_t center_line = CENTER_LINE_NONE;
}
#endif
const int8_t dBmCorrTable[7] = {
-15, // band 1
-25, // band 2
-20, // band 3
-4, // band 4
-7, // band 5
-6, // band 6
-1 // band 7
};
const char *VfoStateStr[] = {
[VFO_STATE_NORMAL]="",
[VFO_STATE_BUSY]="BUSY",
@@ -80,17 +70,19 @@ const char *VfoStateStr[] = {
[VFO_STATE_VOLTAGE_HIGH]="VOLT HIGH"
};
// ***************************************************************************
// ----------------------------------------
static void DrawSmallAntennaAndBars(uint8_t *p, unsigned int level)
static void DrawSmallPowerBars(uint8_t *p, unsigned int level)
{
if(level>6)
level = 6;
memcpy(p, BITMAP_Antenna, ARRAY_SIZE(BITMAP_Antenna));
char bar = 0b00111110;
for(uint8_t i = 1; i <= level; i++) {
char bar = (0xff << (6-i)) & 0x7F;
for(uint8_t i = 0; i <= level; i++) {
if(gSetting_set_gui) {
bar = (0xff << (6-i)) & 0x7F;
}
memset(p + 2 + i*3, bar, 2);
}
}
@@ -166,15 +158,17 @@ static void DrawLevelBar(uint8_t xpos, uint8_t line, uint8_t level, uint8_t bars
#endif
#ifdef ENABLE_AUDIO_BAR
// Approximation of a logarithmic scale using integer arithmetic
uint8_t log2_approx(unsigned int value) {
static uint8_t log2_approx(unsigned int value) {
uint8_t log = 0;
while (value >>= 1) {
log++;
}
return log;
}
#endif
#ifdef ENABLE_AUDIO_BAR
void UI_DisplayAudioBar(void)
{
@@ -239,6 +233,135 @@ void UI_DisplayAudioBar(void)
}
#endif
#ifdef ENABLE_FEAT_F4HWN_AUDIO_SCOPE
#define SCOPE_SAMPLES 43 // number of columns (43 × 3px = 128px wide)
#define SCOPE_NOISE_GATE 50u // minimum range below which the display shows baseline
#define SCOPE_FLOOR_RISE 2u // floor rise per frame (+100 units/s at 20ms/frame)
#define SCOPE_FLOOR_DROP_SHR 3u // floor drop IIR shift: drop by (floor-min) >> N per frame (~160ms to halve)
#define SCOPE_VOLUME_MIN 200u // let's assume that the sound level in silence is 200
void UI_DisplayAudioScope(void)
{
static uint16_t g_scope_buf[SCOPE_SAMPLES];
static uint8_t g_scope_write = 0;
static uint16_t g_scope_floor = SCOPE_VOLUME_MIN; // persistent floor: snaps down fast, rises slowly
static uint8_t g_scope_ready = 0; // number of valid samples since TX entry
// REG_64 (VoiceAmplitudeOut) is only meaningful in TX (mic input).
// FM RX audio is frequency-encoded — no register gives the instantaneous waveform.
// ------------------------------ Sample audio amplitude ------------------------------
static bool s_was_tx = false;
if (gCurrentFunction != FUNCTION_TRANSMIT) {
s_was_tx = false;
return;
}
// This prevents a sudden spike on the bar caused by release the PTT button
if (!GPIO_IsPttPressed()
#ifdef ENABLE_VOX
&& !gEeprom.VOX_SWITCH
#endif
#ifdef ENABLE_FEAT_F4HWN
&& !gSetting_set_ptt_session
#endif
)
return;
if (!s_was_tx) {
// TX entry: full reset so every new transmission starts from a clean state
for (uint8_t i = 0; i < SCOPE_SAMPLES; i++) g_scope_buf[i] = SCOPE_VOLUME_MIN;
g_scope_write = 0u;
g_scope_floor = SCOPE_VOLUME_MIN;
s_was_tx = true;
}
// The first 7 bars after turning on the radio
// will not display any values: they cause high bars.
if (g_scope_ready >= 7)
g_scope_buf[g_scope_write] = BK4819_GetVoiceAmplitudeOut();
else
g_scope_ready++;
// If the reading is 0, it is definitely an incorrect value
// caused by the microphone being muted - set it to 200.
if (g_scope_buf[g_scope_write] == 0)
g_scope_buf[g_scope_write] = SCOPE_VOLUME_MIN;
g_scope_write = (g_scope_write + 1u) % SCOPE_SAMPLES;
// --------------------------------- Refresh display ---------------------------------
if (gLowBattery && !gLowBatteryConfirmed)
return;
if (gScreenToDisplay != DISPLAY_MAIN
#ifdef ENABLE_DTMF_CALLING
|| gDTMF_CallState != DTMF_CALL_STATE_NONE
#endif
)
return;
#if defined(ENABLE_ALARM) || defined(ENABLE_TX1750)
if (gAlarmState != ALARM_STATE_OFF)
return;
#endif
#ifdef ENABLE_FEAT_F4HWN
RxBlinkLed = 0;
RxBlinkLedCounter = 0;
BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false);
const unsigned int line = isMainOnly() ? 5 : 3;
#else
const unsigned int line = 3;
#endif
uint8_t *p_line = gFrameBuffer[line];
memset(p_line, 0, LCD_WIDTH);
// Find min and max across current buffer
uint16_t min_val = g_scope_buf[0];
uint16_t max_val = g_scope_buf[0];
for (uint8_t i = 1u; i < SCOPE_SAMPLES; i++) {
if (g_scope_buf[i] < min_val) min_val = g_scope_buf[i];
if (g_scope_buf[i] > max_val) max_val = g_scope_buf[i];
}
// Floor tracks buffer minimum with asymmetric IIR:
// - drops toward min smoothly (SCOPE_FLOOR_DROP_SHR), avoiding instant-snap ghost
// - rises slowly (SCOPE_FLOOR_RISE/frame) to handle loud constant voice
if (g_scope_floor > min_val)
g_scope_floor -= ((g_scope_floor - min_val) >> SCOPE_FLOOR_DROP_SHR) + 1u;
else
g_scope_floor += SCOPE_FLOOR_RISE;
const uint16_t range = (max_val > g_scope_floor) ? (max_val - g_scope_floor) : 0u;
for (uint8_t i = 0u; i < SCOPE_SAMPLES; i++) {
const uint8_t idx = (g_scope_write + i) % SCOPE_SAMPLES;
uint8_t height = 0u;
if (range >= SCOPE_NOISE_GATE) {
const uint16_t v = (g_scope_buf[idx] > g_scope_floor) ? (g_scope_buf[idx] - g_scope_floor) : 0u;
height = (uint8_t)((uint32_t)v * 7u / range);
}
// Filled column using bits 6..0 only (bit 7 always off to avoid overlap with text below)
// At silence (height 0): single pixel at bit 6 (baseline)
const uint8_t mask = (height > 0u) ? (uint8_t)((0x7Fu << (7u - height)) & 0x7Fu) : 0x40u;
// 2px column + 1px gap per sample
uint8_t *p_col = &p_line[i * 3u];
p_col[0] = mask;
p_col[1] = mask;
}
ST7565_BlitLine(line);
}
#endif // ENABLE_FEAT_F4HWN_AUDIO_SCOPE
void DisplayRSSIBar(const bool now)
{
#if defined(ENABLE_RSSI_BAR)
@@ -273,24 +396,23 @@ void DisplayRSSIBar(const bool now)
//sprintf(String, "%d", RxBlink);
//UI_PrintStringSmallBold(String, 80, 0, RxLine);
/*
if(RxLine >= 0 && center_line != CENTER_LINE_IN_USE)
{
if (RxBlink == 0 || RxBlink == 1) {
UI_PrintStringSmallBold("RX", 8, 0, RxLine);
//GUI_DisplaySmallest("RX", 10, (RxLine * 8) + 1, false, true);
static bool clean = false;
uint8_t *p_line0 = gFrameBuffer[RxLine + 0];
if (RxBlink == 1) RxBlink = 2;
clean = !clean;
if(clean) {
for(uint8_t i = 0; i < sizeof(BITMAP_VFO_Default); i++)
p_line0[i] = (p_line0[i] & 0x80) | BITMAP_VFO_Default[i];
} else {
for (uint8_t i = 8; i < 24; i++)
{
gFrameBuffer[RxLine][i] = 0x00;
}
RxBlink = 1;
for(uint8_t i = 0; i < sizeof(BITMAP_VFO_Empty); i++)
p_line0[i] = (p_line0[i] & 0x80) | BITMAP_VFO_Empty[i];
}
ST7565_BlitLine(RxLine);
}
*/
#else
const unsigned int line = 3;
#endif
@@ -331,22 +453,34 @@ void DisplayRSSIBar(const bool now)
#endif
+ dBmCorrTable[gRxVfo->Band];
rssi_dBm = -rssi_dBm;
if(rssi_dBm > 141) rssi_dBm = 141;
if(rssi_dBm < 53) rssi_dBm = 53;
uint8_t s_level = 0;
uint8_t overS9dBm = 0;
// IARU VHF/UHF S-meter: S9 = -93 dBm, 1 S-unit = 6 dB
// S(n) threshold = -93 + (n - 9) * 6
uint8_t s_level = 0;
uint8_t overS9dBm = 0;
uint8_t overS9Bars = 0;
if(rssi_dBm >= 93) {
s_level = map(rssi_dBm, 141, 93, 1, 9);
}
else {
// if (rssi_dBm >= -93) s_level = 9; // S9 = -93 dBm
// else if (rssi_dBm >= -99) s_level = 8; // S8 = -99 dBm
// else if (rssi_dBm >= -105) s_level = 7; // S7 = -105 dBm
// else if (rssi_dBm >= -111) s_level = 6; // S6 = -111 dBm
// else if (rssi_dBm >= -117) s_level = 5; // S5 = -117 dBm
// else if (rssi_dBm >= -123) s_level = 4; // S4 = -123 dBm
// else if (rssi_dBm >= -129) s_level = 3; // S3 = -129 dBm
// else if (rssi_dBm >= -135) s_level = 2; // S2 = -135 dBm
// else if (rssi_dBm >= -141) s_level = 1; // S1 = -141 dBm
// else s_level = 0; // S0 (below -141 dBm)
if (rssi_dBm >= -93)
s_level = 9;
overS9dBm = map(rssi_dBm, 93, 53, 0, 40);
overS9Bars = map(overS9dBm, 0, 40, 0, 4);
else if (rssi_dBm < -141)
s_level = 0;
else
s_level = (rssi_dBm + 147) / 6;
if (s_level == 9) {
// Compute over-S9 dB directly
overS9dBm = (uint8_t)MIN(rssi_dBm - (-93), 40);
overS9Bars = overS9dBm / 10;
}
#else
const int16_t s0_dBm = -gEeprom.S0_LEVEL; // S0 .. base level
@@ -366,12 +500,12 @@ void DisplayRSSIBar(const bool now)
#ifdef ENABLE_FEAT_F4HWN
if (gSetting_set_gui)
{
sprintf(str, "%3d", -rssi_dBm);
sprintf(str, "%3d", rssi_dBm);
UI_PrintStringSmallNormal(str, LCD_WIDTH + 8, 0, line - 1);
}
else
{
sprintf(str, "% 4d %s", -rssi_dBm, "dBm");
sprintf(str, "% 4d %s", rssi_dBm, "dBm");
if(isMainOnly())
GUI_DisplaySmallest(str, 2, 41, false, true);
else
@@ -419,7 +553,7 @@ void DisplayRSSIBar(const bool now)
uint8_t *pLine = (gEeprom.RX_VFO == 0)? gFrameBuffer[2] : gFrameBuffer[6];
if (now)
memset(pLine, 0, 23);
DrawSmallAntennaAndBars(pLine, Level);
DrawSmallPowerBars(pLine, Level);
if (now)
ST7565_BlitFullScreen();
#endif
@@ -528,7 +662,7 @@ void UI_MAIN_TimeSlice500ms(void)
}
}
// ***************************************************************************
// ----------------------------------------
void UI_DisplayMain(void)
{
@@ -554,32 +688,7 @@ void UI_DisplayMain(void)
return;
}
#else
if (gEeprom.KEY_LOCK && gKeypadLocked > 0)
{ // tell user how to unlock the keyboard
uint8_t shift = 3;
/*
BK4819_ToggleGpioOut(BK4819_GPIO5_PIN1_RED, true);
SYSTEM_DelayMs(50);
BK4819_ToggleGpioOut(BK4819_GPIO5_PIN1_RED, false);
SYSTEM_DelayMs(50);
*/
if(isMainOnly())
{
shift = 5;
}
//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;
}
*/
}
UI_DisplayUnlockKeyboard(isMainOnly() ? 5 : 3);
#endif
unsigned int activeTxVFO = gRxVfoIsActive ? gEeprom.RX_VFO : gEeprom.TX_VFO;
@@ -771,46 +880,51 @@ void UI_DisplayMain(void)
{ // receiving .. show the RX symbol
mode = VFO_MODE_RX;
//if (FUNCTION_IsRx() && gEeprom.RX_VFO == vfo_num) {
if (FUNCTION_IsRx() && gEeprom.RX_VFO == vfo_num && VfoState[vfo_num] == VFO_STATE_NORMAL) {
if (FUNCTION_IsRx()) {
if (gEeprom.RX_VFO == vfo_num && VfoState[vfo_num] == VFO_STATE_NORMAL) {
#ifdef ENABLE_FEAT_F4HWN
RxBlinkLed = 1;
RxBlinkLedCounter = 0;
RxLine = line;
RxOnVfofrequency = frequency;
if(!isMainVFO)
{
RxBlink = 1;
}
else
{
RxBlink = 0;
}
RxBlinkLed = 1;
RxBlinkLedCounter = 0;
RxLine = line;
RxOnVfofrequency = frequency;
// if(!isMainVFO)
// {
// RxBlink = 1;
// }
// else
// {
// RxBlink = 0;
// }
if (RxBlink == 0 || RxBlink == 1) {
if(gRxVfo->Modulation == MODULATION_AM)
GUI_DisplaySmallest("AIR", 10, RxLine == 0 ? 1 : 33, false, true);
else {
#ifdef ENABLE_FEAT_F4HWN_AUDIO
strcpy(String, gSubMenu_SET_AUD[gSetting_set_audio]);
#else
strcpy(String, "RX");
#endif
GUI_DisplaySmallest(String, 10, RxLine == 0 ? 1 : 33, false, true);
}
// if (RxBlink == 0 || RxBlink == 1) {
if(gRxVfo->Modulation == MODULATION_AM)
GUI_DisplaySmallest("AIR", 10, RxLine == 0 ? 1 : 33, false, true);
else {
#ifdef ENABLE_FEAT_F4HWN_AUDIO
strcpy(String, gSubMenu_SET_AUD[gSetting_set_audio]);
#else
strcpy(String, "RX");
#endif
GUI_DisplaySmallest(String, 10, RxLine == 0 ? 1 : 33, false, true);
}
//UI_PrintStringSmallBold("RX", 8, 0, RxLine);
}
//UI_PrintStringSmallBold("RX", 8, 0, RxLine);
// }
#else
UI_PrintStringSmallBold("RX", 8, 0, line);
UI_PrintStringSmallBold("RX", 8, 0, line);
#endif
}
}
#ifdef ENABLE_FEAT_F4HWN
else
{
if(RxOnVfofrequency == frequency && !isMainOnly())
{
UI_PrintStringSmallNormal(">>", 8, 0, line);
else {
if(RxBlinkLed == 1)
RxBlinkLed = 2;
}
}
else {
if(RxOnVfofrequency == frequency && !isMainOnly()) {
//UI_PrintStringSmallNormal(">>", 8, 0, line);
//memcpy(p_line0 + 14, BITMAP_VFO_Default, sizeof(BITMAP_VFO_Default));
GUI_DisplaySmallest(">>", 8, RxLine == 0 ? 1 : 33, false, true);
}
if(RxBlinkLed == 1)
@@ -821,21 +935,60 @@ void UI_DisplayMain(void)
if (IS_MR_CHANNEL(gEeprom.ScreenChannel[vfo_num]))
{ // channel mode
const unsigned int x = 3;
const unsigned int x = 1;
const bool inputting = gInputBoxIndex != 0 && gEeprom.TX_VFO == vfo_num;
if (!inputting || gScanStateDir != SCAN_OFF)
sprintf(String, "%04u", gEeprom.ScreenChannel[vfo_num] + 1);
else
sprintf(String, "%.4s", INPUTBOX_GetAscii()); // show the input text
UI_PrintStringSmallNormalInverse(String, x, 0, line + 1);
//if (gSetting_set_gui) {
UI_PrintStringSmallNormalInverse(String, x, 0, line + 1);
/*
}
else
{
GUI_DisplaySmallest(String, x + 1, line == 0 ? 9 : 41, false, true);
gFrameBuffer[line + 1][0] ^= 0x1C;
gFrameBuffer[line + 1][1] ^= 0x3E;
for (uint8_t i = 2; i < 21; i++) {
gFrameBuffer[line + 1][i] ^= 0x7F;
}
gFrameBuffer[line + 1][21] ^= 0x3E;
gFrameBuffer[line + 1][22] ^= 0x1C;
}
*/
}
else if (IS_FREQ_CHANNEL(gEeprom.ScreenChannel[vfo_num]))
{ // frequency mode
// show the frequency band number
const unsigned int x = 2;
char * buf = gEeprom.VfoInfo[vfo_num].pRX->Frequency < _1GHz_in_KHz ? "" : "+";
sprintf(String, "F%u%s", 1 + gEeprom.ScreenChannel[vfo_num] - FREQ_CHANNEL_FIRST, buf);
UI_PrintStringSmallNormal(String, x, 0, line + 1);
const uint8_t f = 1 + gEeprom.ScreenChannel[vfo_num] - FREQ_CHANNEL_FIRST;
const bool over1GHz = gEeprom.VfoInfo[vfo_num].pRX->Frequency >= _1GHz_in_KHz;
sprintf(String, over1GHz ? "F%u+" : "F%u", f);
//if (gSetting_set_gui) {
UI_PrintStringSmallNormalInverse(String, x, 0, line + 1);
/*
}
else
{
GUI_DisplaySmallest(String, x + 2, line == 0 ? 9 : 41, false, true);
uint8_t g = 13;
if(over1GHz)
g = 17;
gFrameBuffer[line + 1][0] ^= 0x1C;
gFrameBuffer[line + 1][1] ^= 0x3E;
for (uint8_t i = 2; i < g; i++) {
gFrameBuffer[line + 1][i] ^= 0x7F;
}
gFrameBuffer[line + 1][g] ^= 0x3E;
gFrameBuffer[line + 1][g + 1] ^= 0x1C;
}
*/
}
#ifdef ENABLE_NOAA
else
@@ -852,7 +1005,7 @@ void UI_DisplayMain(void)
}
#endif
// ************
// ----------------------------------------
enum VfoState_t state = VfoState[vfo_num];
@@ -917,20 +1070,44 @@ void UI_DisplayMain(void)
countList = 0;
}
const char *displayStr = (countList == MR_CHANNELS_LIST + 1) ? "ALL" :
(countList == 0) ? "OFF" : String;
uint8_t xStart = (countList > 0 && countList != MR_CHANNELS_LIST + 1) ? 117 : 113;
uint8_t xDisplay = (countList > 0 && countList != MR_CHANNELS_LIST + 1) ? 119 : 115;
const char *displayStr;
uint8_t xStart, xDisplay;
if(countList > 0 && countList != MR_CHANNELS_LIST + 1) {
sprintf(String, "%02d", countList);
if (countList == MR_CHANNELS_LIST + 1) {
displayStr = "ALL";
xStart = 113;
xDisplay = 115;
}
else if (countList == 0) {
displayStr = "OFF";
xStart = 113;
xDisplay = 115;
}
else {
// List 1 to MR_CHANNELS_LIST
const char *name = gListName[countList - 1];
// If name is empty/invalid, display number
if (IsEmptyName(name, sizeof(gListName[0]))) {
sprintf(String, "%02d", countList);
xStart = 117; // 2-digit number aligned right
xDisplay = 119;
}
else {
sprintf(String, "%.3s", name);
xStart = 113; // 3-char name aligned left
xDisplay = 115;
}
displayStr = String;
}
GUI_DisplaySmallest(displayStr, xDisplay, line == 0 ? 2 : 34, false, true);
GUI_DisplaySmallest(displayStr, xDisplay, line == 0 ? 1 : 33, false, true);
for (uint8_t x = xStart; x < 128; x++) {
gFrameBuffer[line][x] ^= 0xFE;
gFrameBuffer[line][xStart] ^= 0x3E;
for (uint8_t x = xStart + 1; x < 127; x++) {
gFrameBuffer[line][x] ^= 0x7F;
}
gFrameBuffer[line][127] ^= 0x3E;
}
else
@@ -940,11 +1117,13 @@ void UI_DisplayMain(void)
uint8_t xStart = 117;
uint8_t xDisplay = 119;
GUI_DisplaySmallest(displayStr, xDisplay, line == 0 ? 2 : 34, false, true);
GUI_DisplaySmallest(displayStr, xDisplay, line == 0 ? 1 : 33, false, true);
for (uint8_t x = xStart; x < 128; x++) {
gFrameBuffer[line][x] ^= 0xFE;
gFrameBuffer[line][xStart] ^= 0x3E;
for (uint8_t x = xStart + 1; x < 127; x++) {
gFrameBuffer[line][x] ^= 0x7F;
}
gFrameBuffer[line][127] ^= 0x3E;
}
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
@@ -983,7 +1162,7 @@ void UI_DisplayMain(void)
break;
case MDF_CHANNEL: // show the channel number
sprintf(String, "CH-%03u", gEeprom.ScreenChannel[vfo_num] + 1);
sprintf(String, "CH-%04u", gEeprom.ScreenChannel[vfo_num] + 1);
UI_PrintString(String, 36, 0, line, 8);
break;
@@ -993,19 +1172,19 @@ void UI_DisplayMain(void)
SETTINGS_FetchChannelName(String, gEeprom.ScreenChannel[vfo_num]);
if (String[0] == 0)
{ // no channel name, show the channel number instead
sprintf(String, "CH-%03u", gEeprom.ScreenChannel[vfo_num] + 1);
sprintf(String, "CH-%04u", gEeprom.ScreenChannel[vfo_num] + 1);
}
if (gEeprom.CHANNEL_DISPLAY_MODE == MDF_NAME) {
String[9] = 0;
UI_PrintString(String, 36, 0, line, 8);
String[10] = 0;
UI_PrintString(String, 33, 0, line, 8);
}
else {
#ifdef ENABLE_FEAT_F4HWN
if (isMainOnly())
{
String[9] = 0;
UI_PrintString(String, 36, 0, line, 8);
String[10] = 0;
UI_PrintString(String, 33, 0, line, 8);
}
else
{
@@ -1082,7 +1261,7 @@ void UI_DisplayMain(void)
}
}
// ************
// ----------------------------------------
{ // show the TX/RX level
int8_t Level = -1;
@@ -1109,9 +1288,15 @@ void UI_DisplayMain(void)
Level = 2;
}
*/
Level = gRxVfo->OUTPUT_POWER - 1;
uint8_t currentPower = gRxVfo->OUTPUT_POWER;
if(currentPower == OUTPUT_POWER_USER)
Level = gSetting_set_pwr;
else
Level = currentPower - 1;
}
else
else
if (mode == VFO_MODE_RX)
{ // RX signal level
#ifndef ENABLE_RSSI_BAR
@@ -1121,10 +1306,10 @@ void UI_DisplayMain(void)
#endif
}
if(Level >= 0)
DrawSmallAntennaAndBars(p_line1 + LCD_WIDTH, Level);
DrawSmallPowerBars(p_line1 + LCD_WIDTH, Level);
}
// ************
// ----------------------------------------
String[0] = '\0';
const VFO_Info_t *vfoInfo = &gEeprom.VfoInfo[vfo_num];
@@ -1420,6 +1605,14 @@ void UI_DisplayMain(void)
const bool rx = FUNCTION_IsRx();
#ifdef ENABLE_FEAT_F4HWN_AUDIO_SCOPE
if (gSetting_mic_bar && gCurrentFunction == FUNCTION_TRANSMIT) {
// Reserve the line so no other element overwrites it.
// Actual drawing is handled exclusively by the app.c timeslice.
center_line = CENTER_LINE_AUDIO_SCOPE;
}
else
#endif
#ifdef ENABLE_AUDIO_BAR
if (gSetting_mic_bar && gCurrentFunction == FUNCTION_TRANSMIT) {
center_line = CENTER_LINE_AUDIO_BAR;
@@ -1455,7 +1648,7 @@ void UI_DisplayMain(void)
if (rx || gCurrentFunction == FUNCTION_FOREGROUND || gCurrentFunction == FUNCTION_POWER_SAVE)
{
#if 1
if (gSetting_live_DTMF_decoder && gDTMF_RX_live[0] != 0)
if (gSetting_live_DTMF_decoder && gDTMF_RX_live[0] != 0 && gKeypadLocked == 0)
{ // show live DTMF decode
const unsigned int len = strlen(gDTMF_RX_live);
const unsigned int idx = (len > (17 - 5)) ? len - (17 - 5) : 0; // limit to last 'n' chars
@@ -1530,11 +1723,21 @@ void UI_DisplayMain(void)
if (isMainOnly() && !gDTMF_InputMode)
{
sprintf(String, "VFO %s", activeTxVFO ? "B" : "A");
GUI_DisplaySmallest(String, 107, 50, false, true);
gFrameBuffer[6][105] ^= 0x7C;
for (uint8_t x = 106; x < 127; x++) {
gFrameBuffer[6][x] ^= 0xFE;
}
gFrameBuffer[6][127] ^= 0x7C;
/*
UI_PrintStringSmallBold(String, 92, 0, 6);
for (uint8_t i = 92; i < 128; i++)
{
gFrameBuffer[6][i] ^= 0x7F;
}
*/
}
//#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
//}
@@ -1543,5 +1746,3 @@ void UI_DisplayMain(void)
ST7565_BlitFullScreen();
}
// ***************************************************************************
+8 -1
View File
@@ -17,10 +17,13 @@
#ifndef UI_MAIN_H
#define UI_MAIN_H
#include <stdint.h>
enum center_line_t {
CENTER_LINE_NONE = 0,
CENTER_LINE_IN_USE,
CENTER_LINE_AUDIO_BAR,
CENTER_LINE_AUDIO_SCOPE,
CENTER_LINE_RSSI,
CENTER_LINE_AM_FIX_DATA,
CENTER_LINE_DTMF_DEC,
@@ -36,9 +39,13 @@ 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);
+24 -8
View File
@@ -646,7 +646,11 @@ void UI_DisplayMenu(void)
case MENU_MIC:
{ // display the mic gain in actual dB rather than just an index number
const uint8_t mic = gMicGain_dB2[gSubMenuSelection];
sprintf(String, "+%u.%01udB", mic / 2, mic % 2);
sprintf(String, "+%u.%udB", mic / 2, (mic % 2) * 5);
gaugeLine = 4;
gaugeMin = 0;
gaugeMax = 8;
}
break;
@@ -703,15 +707,14 @@ void UI_DisplayMenu(void)
if (!gIsInSubMenu || gInputBoxIndex == 0)
{
sprintf(String, "%3d.%05u", gSubMenuSelection / 100000, abs(gSubMenuSelection) % 100000);
UI_PrintString(String, menu_item_x1, menu_item_x2, 1, 8);
}
else
{
const char * ascii = INPUTBOX_GetAscii();
sprintf(String, "%.3s.%.3s ",ascii, ascii + 3);
UI_PrintString(String, menu_item_x1, menu_item_x2, 1, 8);
}
UI_PrintString(String, menu_item_x1, menu_item_x2, 1, 8);
UI_PrintString("MHz", menu_item_x1, menu_item_x2, 3, 8);
already_printed = true;
@@ -969,15 +972,28 @@ void UI_DisplayMenu(void)
strcpy(String, "ALL");
else if (gSubMenuSelection == 0)
strcpy(String, "OFF");
else
sprintf(String, "%u", gSubMenuSelection);
else {
const char *name = gListName[gSubMenuSelection - 1];
// If first character is empty/invalid, display "N/A"
if (IsEmptyName(name, sizeof(gListName[0])))
sprintf(String, "%02u", gSubMenuSelection);
else
sprintf(String, "%02u (%.3s)", gSubMenuSelection, name);
}
break;
case MENU_S_LIST:
if (gSubMenuSelection == MR_CHANNELS_LIST + 1)
strcpy(String, "ALL");
else
sprintf(String, "%u", gSubMenuSelection);
else {
const char *name = gListName[gSubMenuSelection - 1];
// If first character is empty/invalid, display "N/A"
if (IsEmptyName(name, sizeof(gListName[0])))
sprintf(String, "%02u", gSubMenuSelection);
else
sprintf(String, "%02u (%.3s)", gSubMenuSelection, name);
}
break;
#ifdef ENABLE_ALARM
@@ -1204,7 +1220,7 @@ void UI_DisplayMenu(void)
gaugeMax = 63;
//#endif
}
gEeprom.VOLUME_GAIN = gSubMenuSelection;
// gEeprom.VOLUME_GAIN = gSubMenuSelection;
BK4819_WriteRegister(BK4819_REG_48,
(11u << 12) | // ??? .. 0 ~ 15, doesn't seem to make any difference
( 0u << 10) | // AF Rx Gain-1
+35 -35
View File
@@ -101,31 +101,37 @@ void UI_DisplayStatus()
{
if(gEeprom.SCAN_LIST_ENABLED==1) {
sprintf(str, "%s+", "ALL");
end = 19;
end = 18;
}
else
{
sprintf(str, "%s", "ALL");
end = 15;
end = 14;
}
}
else
{
if(gEeprom.SCAN_LIST_ENABLED==1) {
sprintf(str, "%02d+", gEeprom.SCAN_LIST_DEFAULT);
end = 15;
}
const char *name = gListName[gEeprom.SCAN_LIST_DEFAULT - 1];
// Check if name is valid
if (!IsEmptyName(name, sizeof(gListName[0]))) {
sprintf(str, "%.3s%s", name, gEeprom.SCAN_LIST_ENABLED ? "+" : "");
end = gEeprom.SCAN_LIST_ENABLED ? 18 : 14;
}
else {
sprintf(str, "%02d", gEeprom.SCAN_LIST_DEFAULT);
end = 11;
sprintf(str, "%02d%s", gEeprom.SCAN_LIST_DEFAULT, gEeprom.SCAN_LIST_ENABLED ? "+" : "");
end = gEeprom.SCAN_LIST_ENABLED ? 14 : 10;
}
}
GUI_DisplaySmallest(str, 2, 1, true, true);
for (uint8_t x = 0; x < end; x++)
gStatusLine[0] ^= 0x3E;
for (uint8_t x = 1; x < end; x++)
{
gStatusLine[x] ^= 0x7F;
}
gStatusLine[end] ^= 0x3E;
}
else { // frequency mode
memcpy(line + x + 1, gFontS, sizeof(gFontS));
@@ -167,31 +173,32 @@ void UI_DisplayStatus()
else
#endif
{
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
if(gEeprom.MENU_LOCK == true) {
memcpy(line + x + 2, gFontRO, sizeof(gFontRO));
}
else
{
#endif
uint8_t dw = (gEeprom.DUAL_WATCH != DUAL_WATCH_OFF) + (gEeprom.CROSS_BAND_RX_TX != CROSS_BAND_OFF) * 2;
if(dw == 1 || dw == 3) { // DWR - dual watch + respond
if(gDualWatchActive)
memcpy(line + x + (dw==1?0:2), gFontDWR, sizeof(gFontDWR) - (dw==1?0:5));
else
memcpy(line + x + 3, gFontHold, sizeof(gFontHold));
}
else if(dw == 2) { // XB - crossband
memcpy(line + x + 2, gFontXB, sizeof(gFontXB));
if(!gAirCopyBootMode) {
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
if(gEeprom.MENU_LOCK == true) {
memcpy(line + x + 2, gFontRO, sizeof(gFontRO));
}
else
{
if(!gAirCopyBootMode)
#endif
uint8_t dw = (gEeprom.DUAL_WATCH != DUAL_WATCH_OFF) + (gEeprom.CROSS_BAND_RX_TX != CROSS_BAND_OFF) * 2;
if(dw == 1 || dw == 3) { // DWR - dual watch + respond
if(gDualWatchActive)
memcpy(line + x + (dw==1?0:2), gFontDWR, sizeof(gFontDWR) - (dw==1?0:5));
else
memcpy(line + x + 3, gFontHold, sizeof(gFontHold));
}
else if(dw == 2) { // XB - crossband
memcpy(line + x + 2, gFontXB, sizeof(gFontXB));
}
else
{
memcpy(line + x + 2, gFontMO, sizeof(gFontMO));
}
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
}
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
#endif
}
#endif
}
}
x += sizeof(gFontDWR) + 3;
@@ -233,15 +240,8 @@ void UI_DisplayStatus()
size = sizeof(gFontKeyLock);
}
else if (gWasFKeyPressed) {
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
if (!gEeprom.MENU_LOCK) {
src = gFontF;
size = sizeof(gFontF);
}
#else
src = gFontF;
size = sizeof(gFontF);
#endif
}
#ifdef ENABLE_FEAT_F4HWN
else if (gMute) {
+1 -2
View File
@@ -94,9 +94,8 @@ void GUI_SelectNextDisplay(GUI_DisplayType_t Display)
gAskForConfirmation = 0;
gAskToSave = false;
gAskToDelete = false;
gWasFKeyPressed = false;
gUpdateStatus = true;
HideFKeyIcon();
}
gScreenToDisplay = Display;
+86 -70
View File
@@ -15,6 +15,7 @@
*/
#include <string.h>
#include <stdint.h>
#include "driver/py25q16.h"
#include "driver/st7565.h"
@@ -32,6 +33,63 @@
#include "screenshot.h"
#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%
}
#endif
void UI_DisplayReleaseKeys(void)
{
memset(gStatusLine, 0, sizeof(gStatusLine));
@@ -52,7 +110,7 @@ void UI_DisplayWelcome(void)
char WelcomeString0[16];
char WelcomeString1[16];
char WelcomeString2[16];
char WelcomeString3[20];
char WelcomeString3[32];
memset(gStatusLine, 0, sizeof(gStatusLine));
@@ -125,79 +183,37 @@ void UI_DisplayWelcome(void)
#ifdef ENABLE_FEAT_F4HWN
UI_PrintStringSmallNormal(Version, 0, 128, 4);
UI_DrawLineBuffer(gFrameBuffer, 0, 31, 127, 31, 1); // Be ware, status zone = 8 lines, the rest = 56 ->total 64
for (uint8_t i = 18; i < 110; i++)
UI_DrawLineBuffer(gFrameBuffer, 0, 35, 18, 35, 1);
gFrameBuffer[4][19] ^= 0x7F;
for (uint8_t x = 20; x < 108; x++)
{
gFrameBuffer[4][i] ^= 0xFF;
gFrameBuffer[4][x] ^= 0xFF;
gFrameBuffer[3][x] ^= 0x80;
}
gFrameBuffer[4][108] ^= 0x7F;
UI_DrawLineBuffer(gFrameBuffer, 109, 35, 127, 35, 1);
#ifdef ENABLE_FEAT_F4HWN_MEM
uint32_t ram_used = 0;
uint32_t flash_used = 0;
build_usage(&ram_used, &flash_used);
const uint16_t ram_pct = pct_x100(ram_used, RAM_SIZE_BYTES);
const uint16_t flash_pct = pct_x100(flash_used, FLASH_SIZE_BYTES);
// No floats: 7559 => 75.59%
sprintf(WelcomeString3,
"FLASH %u.%02u %% - SRAM %u.%02u %%",
(unsigned)(flash_pct / 100), (unsigned)(flash_pct % 100),
(unsigned)(ram_pct / 100), (unsigned)(ram_pct % 100));
GUI_DisplaySmallest(WelcomeString3, 5, 1, true, true);
ST7565_BlitStatusLine();
#endif
sprintf(WelcomeString3, "%s Edition", Edition);
UI_PrintStringSmallNormal(WelcomeString3, 0, 127, 6);
/*
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
#if ENABLE_FEAT_F4HWN_RESCUE_OPS > 1
UI_PrintStringSmallNormal(Edition, 18, 0, 6);
if(gEeprom.MENU_LOCK == true) {
memcpy(gFrameBuffer[6] + 103, BITMAP_Ready, sizeof(BITMAP_Ready));
}
else
{
memcpy(gFrameBuffer[6] + 103, BITMAP_NotReady, sizeof(BITMAP_NotReady));
}
#else
UI_PrintStringSmallNormal(Edition, 18, 0, 5);
memcpy(gFrameBuffer[5] + 103, BITMAP_Ready, sizeof(BITMAP_Ready));
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
UI_PrintStringSmallNormal("RescueOps", 18, 0, 6);
if(gEeprom.MENU_LOCK == true) {
memcpy(gFrameBuffer[6] + 103, BITMAP_Ready, sizeof(BITMAP_Ready));
}
else
{
memcpy(gFrameBuffer[6] + 103, BITMAP_NotReady, sizeof(BITMAP_NotReady));
}
#endif
#endif
#else
UI_PrintStringSmallNormal(Edition, 18, 0, 6);
memcpy(gFrameBuffer[6] + 103, BITMAP_Ready, sizeof(BITMAP_Ready));
#endif
*/
/*
#ifdef ENABLE_SPECTRUM
#ifdef ENABLE_FMRADIO
UI_PrintStringSmallNormal(Based, 0, 127, 5);
UI_PrintStringSmallNormal(Credits, 0, 127, 6);
#else
UI_PrintStringSmallNormal("Bandscope ", 0, 127, 5);
memcpy(gFrameBuffer[5] + 95, BITMAP_Ready, sizeof(BITMAP_Ready));
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
UI_PrintStringSmallNormal("RescueOps ", 0, 127, 6);
if(gEeprom.MENU_LOCK == true) {
memcpy(gFrameBuffer[6] + 95, BITMAP_Ready, sizeof(BITMAP_Ready));
}
#else
UI_PrintStringSmallNormal("Broadcast ", 0, 127, 6);
#endif
#endif
#else
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
UI_PrintStringSmallNormal("RescueOps ", 0, 127, 5);
if(gEeprom.MENU_LOCK == true) {
memcpy(gFrameBuffer[5] + 95, BITMAP_Ready, sizeof(BITMAP_Ready));
}
#else
UI_PrintStringSmallNormal("Bandscope ", 0, 127, 5);
#endif
UI_PrintStringSmallNormal("Broadcast ", 0, 127, 6);
memcpy(gFrameBuffer[6] + 95, BITMAP_Ready, sizeof(BITMAP_Ready));
#endif
*/
#else
UI_PrintStringSmallNormal(Version, 0, 127, 6);
#endif
@@ -206,7 +222,7 @@ void UI_DisplayWelcome(void)
ST7565_BlitFullScreen();
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
getScreenShot(true);
SCREENSHOT_Update(true);
#endif
}
}
}
+6 -1
View File
@@ -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
}
}
}
+4 -1
View File
@@ -60,16 +60,18 @@
"ENABLE_FEAT_F4HWN_RESCUE_OPS": false,
"ENABLE_FEAT_F4HWN_VOL": false,
"ENABLE_FEAT_F4HWN_AUDIO": false,
"ENABLE_FEAT_F4HWN_AUDIO_SCOPE": false,
"ENABLE_FEAT_F4HWN_RESET_VFO": true,
"ENABLE_FEAT_F4HWN_PMR": false,
"ENABLE_FEAT_F4HWN_GMRS_FRS_MURS": false,
"ENABLE_FEAT_F4HWN_CA": true,
"ENABLE_FEAT_F4HWN_DEBUG": false,
"ENABLE_FEAT_F4HWN_MEM": false,
"ENABLE_AGC_SHOW_DATA": false,
"ENABLE_UART_RW_BK_REGS": false,
"ENABLE_SWD": false,
"VERSION_STRING_1": "v0.22",
"VERSION_STRING_2": "v5.1.0"
"VERSION_STRING_2": "v5.3.0"
}
},
{
@@ -188,6 +190,7 @@
"ENABLE_FEAT_F4HWN_RESCUE_OPS": true,
"ENABLE_FEAT_F4HWN_VOL": true,
"ENABLE_FEAT_F4HWN_AUDIO": true,
"ENABLE_FEAT_F4HWN_AUDIO_SCOPE": true,
"ENABLE_SWD": true,
"EDITION_STRING": "Fusion",
"TARGET": "f4hwn.fusion"
+9
View File
@@ -145,6 +145,15 @@ SECTIONS
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
/* Explicit placement of .noncacheable (CherryUSB) + end-of-FLASH symbol */
.noncacheable :
{
. = ALIGN(4);
*(.noncacheable)
*(.noncacheable*)
. = ALIGN(4);
} >RAM AT> FLASH
_eflash_used = LOADADDR(.noncacheable) + SIZEOF(.noncacheable);
/* Uninitialized data section */
. = ALIGN(4);
+1 -1
View File
@@ -42,7 +42,7 @@ Anyway, have fun.
# Donations
Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Olivier 14RC206, Frédéric F4ESO, Stéphane F5LGW (2 times), Jorge Ornelas (4 times), Laurent F4AXK, Christophe Morel, Clayton W0LED, Pierre Antoine F6FWB, Jean-Claude 14FRS3306, Thierry F4GVO, Eric F1NOU, PricelessToolkit, Ady M6NYJ, Tom McGovern (4 times), Joseph Roth, Pierre-Yves Colin, Frank DJ7FG, Marcel Testaz, Brian Frobisher, Yannick F4JFO, Paolo Bussola, Dirk DL8DF, Levente Szőke (2 times), Bernard-Michel Herrera, Jérôme Saintespes, Paul Davies, RS (3 times), Johan F4WAT, Robert Wörle, Rafael Sundorf, Paul Harker, Peter Fintl, Pascal F4ICR (2 times), Mike DL2MF (3 times), Eric KI1C (2 times), Phil G0ELM, Jérôme Lambert, Eliot Vedel, Alfonso EA7KDF, Jean-François F1EVM, Robert DC1RDB (2 times), Ian KE2CHJ, Daryl VK3AWA, Roberto Brunelli, Robert Boardman, Stephen Oliver, Nicolas F4INE, William Bruno, Daniel OK2VLK, Tayler Chew, Peter DL7RFP, Philippe Kopp, Rune LA6YMA, Jeremy Luna, Steef Wagenaar, Zhuo BG7SGA and Jamie M0JLB for their [donations](https://www.paypal.com/paypalme/F4HWN). That’s so kind of them. Thanks so much 🙏🏻
Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Olivier 14RC206, Frédéric F4ESO, Stéphane F5LGW (2 times), Jorge Ornelas (4 times), Laurent F4AXK, Christophe Morel, Clayton W0LED, Pierre Antoine F6FWB, Jean-Claude 14FRS3306, Thierry F4GVO, Eric F1NOU, PricelessToolkit, Ady M6NYJ, Tom McGovern (4 times), Joseph Roth, Pierre-Yves Colin, Frank DJ7FG, Marcel Testaz, Brian Frobisher, Yannick F4JFO, Paolo Bussola, Dirk DL8DF, Levente Szőke (2 times), Bernard-Michel Herrera, Jérôme Saintespes, Paul Davies, RS (3 times), Johan F4WAT, Robert Wörle, Rafael Sundorf, Paul Harker, Peter Fintl, Pascal F4ICR (2 times), Mike DL2MF (3 times), Eric KI1C (2 times), Phil G0ELM, Jérôme Lambert, Eliot Vedel, Alfonso EA7KDF, Jean-François F1EVM, Robert DC1RDB (2 times), Ian KE2CHJ, Daryl VK3AWA, Roberto Brunelli, Robert Boardman, Stephen Oliver, Nicolas F4INE, William Bruno, Daniel OK2VLK, Tayler Chew, Peter DL7RFP, Philippe Kopp, Rune LA6YMA, Jeremy Luna, Steef Wagenaar (2 times), Zhuo BG7SGA, Jamie M0JLB, Antoine LIBERT, Vince K0DKR, Julia DF7JA, Ken 2E0UMK, Victor TI2SYS, Tobi DG9LAY, Deaglan K4DFQ, Catherine PALMER and Brian WA6JFK for their [donations](https://www.paypal.com/paypalme/F4HWN). That’s so kind of them. Thanks so much 🙏🏻
## Table of Contents
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
+2 -2
View File
@@ -2,11 +2,11 @@
<img width="2016" height="1512" alt="pcbite" src="https://github.com/user-attachments/assets/b3086bf2-c14c-47da-b68b-a6867268ae78" />
Restoring a UV-K5 V1 accidentally flashed with the F4HWN 4.3 Fusion 🔥 Edition firmware, using OpenOCD and a ST-LINK programmer.
Restoring a UV-K5 V1 accidentally flashed with the F4HWN Fusion 🔥 Edition firmware, using OpenOCD and a ST-LINK programmer.
## Introduction
Some users have accidentally flashed a UV-K5 V1 with the F4HWN 4.3 Fusion 🔥 Edition firmware. This edition **is only** compatible with:
Some users have accidentally flashed a UV-K5 V1 with the F4HWN Fusion 🔥 Edition firmware. This edition **is only** compatible with:
- UV-K5 V3
- UV-K1