mirror of
https://github.com/armel/uv-k1-k5v3-firmware-custom.git
synced 2026-10-04 12:07:24 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
786318453f | ||
|
|
61662f3965 | ||
|
|
b3cfda76e5 | ||
|
|
a20f1ce28e | ||
|
|
8a292755b0 | ||
|
|
915fc6aa90 | ||
|
|
cac63f978e | ||
|
|
d5edba1fc2 | ||
|
|
2f0852cd03 | ||
|
|
a65f84305e | ||
|
|
1c0d5ea628 | ||
|
|
a0a5b93408 | ||
|
|
e8cb32a4ca | ||
|
|
706dea756c | ||
|
|
1ea8aba88e | ||
|
|
c2d6a4b751 | ||
|
|
db61bce014 | ||
|
|
a3c03f18f0 | ||
|
|
8977a0a112 | ||
|
|
09a5e3c1e7 | ||
|
|
4fa821e15f | ||
|
|
c9a383919a | ||
|
|
e8ba069000 | ||
|
|
acd2c340ea | ||
|
|
24fd6fd9b1 | ||
|
|
86bb710ca4 | ||
|
|
7148c15f98 | ||
|
|
2d7093651d | ||
|
|
874e5aac4b | ||
|
|
8051605c16 | ||
|
|
134624825f | ||
|
|
7d0fcc8415 | ||
|
|
c27b72d6bc | ||
|
|
066c0d0e14 | ||
|
|
e77b87f1f2 | ||
|
|
2dd5e1a509 | ||
|
|
ab9e97805e | ||
|
|
65c584ff7d | ||
|
|
4c76ca1fb4 | ||
|
|
094896b5f1 | ||
|
|
669dad5b2e | ||
|
|
b9902e0c49 | ||
|
|
0a955c1f15 | ||
|
|
9f649b5f5b | ||
|
|
bb992ca85d | ||
|
|
679cf4ea1f | ||
|
|
0004074ffe | ||
|
|
35e5d8df90 | ||
|
|
fa0de5f576 | ||
|
|
4162618473 | ||
|
|
284af7ca1d | ||
|
|
c6f1f7b483 | ||
|
|
6cea90d507 | ||
|
|
46608af767 | ||
|
|
24167a7902 | ||
|
|
e300c4e7e3 | ||
|
|
1a815b315f | ||
|
|
a7fed2c2d8 | ||
|
|
9e92037674 | ||
|
|
e536725731 | ||
|
|
f59370cee6 | ||
|
|
bdaf320c08 | ||
|
|
fb60d6f7fd | ||
|
|
22295eedcf | ||
|
|
21c215e9fd | ||
|
|
0061613883 | ||
|
|
aecbcc6409 | ||
|
|
642dcc9a43 | ||
|
|
2b05990a2c | ||
|
|
975084baca | ||
|
|
775cd7751a | ||
|
|
c5da503cb3 | ||
|
|
5f8a492f4f | ||
|
|
8dd5da195e |
No files matched your search
+2
-2
@@ -12,6 +12,6 @@ compile_commands.json
|
||||
k5_eeprom.raw
|
||||
|
||||
/build
|
||||
/serialtool/__pycache__
|
||||
__pycache__/
|
||||
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
@@ -78,6 +78,7 @@ endif()
|
||||
target_compile_definitions(App INTERFACE
|
||||
"AUTHOR_STRING=\"${AUTHOR_STRING}\""
|
||||
"VERSION_STRING=\"${VERSION_STRING}\""
|
||||
"BUILD_COMMIT=\"${BUILD_COMMIT}\""
|
||||
)
|
||||
|
||||
if(NOT SQL_TONE)
|
||||
@@ -194,6 +195,7 @@ enable_feature(ENABLE_FEAT_F4HWN_RESUME_STATE)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_NARROWER)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_INV)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_CTR)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_SCAN_PROGRESS)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_RESCUE_OPS)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_VOL)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_AUDIO)
|
||||
@@ -203,6 +205,7 @@ enable_feature(ENABLE_FEAT_F4HWN_GMRS_FRS_MURS)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_CA)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_DEBUG)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_MEM)
|
||||
enable_feature(ENABLE_FEAT_F4HWN_QRCODE)
|
||||
|
||||
# ---- DEBUGGING ----
|
||||
|
||||
|
||||
+9
-10
@@ -597,7 +597,7 @@ void ACTION_Wn(void)
|
||||
|
||||
if (pVfo->Modulation == MODULATION_AM)
|
||||
{
|
||||
BK4819_SetFilterBandwidth(BK4819_FILTER_BW_AM, true);
|
||||
BK4819_SetFilterBandwidth(RADIO_GetAMFilterBandwidth(pVfo), true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -660,26 +660,25 @@ void ACTION_Mute(void)
|
||||
BK1080_WriteRegister(BK1080_REG_05_SYSTEM_CONFIGURATION2, gMute ? 0x0A10 : 0x0A1F);
|
||||
#endif
|
||||
gEeprom.VOLUME_GAIN = gMute ? 0 : gEeprom.VOLUME_GAIN_BACKUP;
|
||||
BK4819_WriteRegister(BK4819_REG_48,
|
||||
(11u << 12) | // ??? .. 0 ~ 15, doesn't seem to make any difference
|
||||
(0u << 10) | // AF Rx Gain-1
|
||||
(gEeprom.VOLUME_GAIN << 4) | // AF Rx Gain-2
|
||||
(gEeprom.DAC_GAIN << 0)); // AF DAC Gain (after Gain-1 and Gain-2)
|
||||
BK4819_SetRxAudioGain();
|
||||
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
void ACTION_ToggleVfoSetting(bool *setting) {
|
||||
*setting = !(*setting);
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
}
|
||||
|
||||
void ACTION_Power_High(void)
|
||||
{
|
||||
gPowerHigh = !gPowerHigh;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
ACTION_ToggleVfoSetting(&gPowerHigh);
|
||||
}
|
||||
|
||||
void ACTION_Remove_Offset(void)
|
||||
{
|
||||
gRemoveOffset = !gRemoveOffset;
|
||||
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
||||
ACTION_ToggleVfoSetting(&gRemoveOffset);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
+27
-28
@@ -557,11 +557,7 @@ void APP_StartListening(FUNCTION_Type_t function)
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
|
||||
BK4819_WriteRegister(BK4819_REG_48,
|
||||
(11u << 12) | // ??? .. 0 to 15, doesn't seem to make any difference
|
||||
( 0u << 10) | // AF Rx Gain-1
|
||||
(gEeprom.VOLUME_GAIN << 4) | // AF Rx Gain-2
|
||||
(gEeprom.DAC_GAIN << 0)); // AF DAC Gain (after Gain-1 and Gain-2)
|
||||
BK4819_SetRxAudioGain();
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
if (gVoiceWriteIndex == 0) // AM/FM RX mode will be set when the voice has finished
|
||||
@@ -816,6 +812,22 @@ void APP_EndTransmission(void)
|
||||
}
|
||||
}
|
||||
|
||||
void APP_HandleEndTransmission(void) {
|
||||
if (gFlagEndTransmission) {
|
||||
FUNCTION_Select(FUNCTION_FOREGROUND);
|
||||
}
|
||||
else {
|
||||
APP_EndTransmission();
|
||||
|
||||
if (gEeprom.REPEATER_TAIL_TONE_ELIMINATION == 0)
|
||||
FUNCTION_Select(FUNCTION_FOREGROUND);
|
||||
else
|
||||
gRTTECountdown_10ms = gEeprom.REPEATER_TAIL_TONE_ELIMINATION * 10;
|
||||
}
|
||||
|
||||
gFlagEndTransmission = false;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_VOX
|
||||
static void HandleVox(void)
|
||||
{
|
||||
@@ -851,24 +863,10 @@ static void HandleVox(void)
|
||||
gVOX_NoiseDetected = false;
|
||||
|
||||
if (gCurrentFunction == FUNCTION_TRANSMIT && !gPttIsPressed && !gVOX_NoiseDetected) {
|
||||
if (gFlagEndTransmission) {
|
||||
//if (gCurrentFunction != FUNCTION_FOREGROUND)
|
||||
FUNCTION_Select(FUNCTION_FOREGROUND);
|
||||
}
|
||||
else {
|
||||
APP_EndTransmission();
|
||||
|
||||
if (gEeprom.REPEATER_TAIL_TONE_ELIMINATION == 0) {
|
||||
//if (gCurrentFunction != FUNCTION_FOREGROUND)
|
||||
FUNCTION_Select(FUNCTION_FOREGROUND);
|
||||
}
|
||||
else
|
||||
gRTTECountdown_10ms = gEeprom.REPEATER_TAIL_TONE_ELIMINATION * 10;
|
||||
}
|
||||
APP_HandleEndTransmission();
|
||||
|
||||
gUpdateStatus = true;
|
||||
gUpdateDisplay = true;
|
||||
gFlagEndTransmission = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1437,8 +1435,7 @@ void APP_TimeSlice10ms(void)
|
||||
if (gAlarmState == ALARM_STATE_TXALARM) {
|
||||
gAlarmState = ALARM_STATE_SITE_ALARM;
|
||||
|
||||
if(gEeprom.TAIL_TONE_ELIMINATION)
|
||||
RADIO_SendCssTail();
|
||||
RADIO_SendCssTail();
|
||||
BK4819_SetupPowerAmplifier(0, 0);
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO1_PIN29_PA_ENABLE, false);
|
||||
BK4819_Enable_AfDac_DiscMode_TxDsp();
|
||||
@@ -1534,7 +1531,11 @@ void APP_TimeSlice500ms(void)
|
||||
if (--gKeyInputCountdown == 0)
|
||||
{
|
||||
|
||||
if (IS_MR_CHANNEL(gTxVfo->CHANNEL_SAVE) && (gInputBoxIndex > 0 && gInputBoxIndex < 4) && (!gFmRadioMode))
|
||||
if (IS_MR_CHANNEL(gTxVfo->CHANNEL_SAVE) && (gInputBoxIndex > 0 && gInputBoxIndex < 4)
|
||||
#ifdef ENABLE_FMRADIO
|
||||
&& (!gFmRadioMode)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
channelMoveSwitch();
|
||||
|
||||
@@ -1943,12 +1944,10 @@ static void ProcessKey(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
bool lowBatPopup = gLowBattery && !gLowBatteryConfirmed && gScreenToDisplay == DISPLAY_MAIN;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN // Disable PTT if KEY_LOCK
|
||||
bool lck_condition = false;
|
||||
bool lck_condition = (gEeprom.KEY_LOCK || lowBatPopup) && gCurrentFunction != FUNCTION_TRANSMIT;
|
||||
|
||||
if(gSetting_set_lck)
|
||||
lck_condition = (gEeprom.KEY_LOCK || lowBatPopup) && gCurrentFunction != FUNCTION_TRANSMIT;
|
||||
else
|
||||
lck_condition = (gEeprom.KEY_LOCK || lowBatPopup) && gCurrentFunction != FUNCTION_TRANSMIT && Key != KEY_PTT;
|
||||
if(!gSetting_set_lck)
|
||||
lck_condition = lck_condition && Key != KEY_PTT;
|
||||
|
||||
if (lck_condition)
|
||||
#else
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "radio.h"
|
||||
|
||||
void APP_EndTransmission(void);
|
||||
void APP_HandleEndTransmission(void);
|
||||
void APP_StartListening(FUNCTION_Type_t function);
|
||||
uint32_t APP_SetFreqByStepAndLimits(VFO_Info_t *pInfo, int8_t direction, uint32_t lower, uint32_t upper);
|
||||
uint32_t APP_SetFrequencyByStep(VFO_Info_t *pInfo, int8_t direction);
|
||||
|
||||
+17
-10
@@ -40,7 +40,6 @@ volatile int8_t gFM_ScanState;
|
||||
bool gFM_AutoScan;
|
||||
uint8_t gFM_ChannelPosition;
|
||||
bool gFM_FoundFrequency;
|
||||
bool gFM_AutoScan;
|
||||
uint16_t gFM_RestoreCountdown_10ms;
|
||||
|
||||
|
||||
@@ -167,6 +166,12 @@ void FM_Tune(uint16_t Frequency, int8_t Step, bool bFlag)
|
||||
BK1080_SetFrequency(gEeprom.FM_FrequencyPlaying, gEeprom.FM_Band/*, gEeprom.FM_Space*/);
|
||||
}
|
||||
|
||||
void FM_AudioPathOn(void) {
|
||||
BACKLIGHT_TurnOn();
|
||||
AUDIO_AudioPathOn();
|
||||
gEnableSpeaker = true;
|
||||
}
|
||||
|
||||
void FM_PlayAndUpdate(void)
|
||||
{
|
||||
gFM_ScanState = FM_SCAN_OFF;
|
||||
@@ -184,9 +189,7 @@ void FM_PlayAndUpdate(void)
|
||||
gScheduleFM = false;
|
||||
gAskToSave = false;
|
||||
|
||||
AUDIO_AudioPathOn();
|
||||
|
||||
gEnableSpeaker = true;
|
||||
FM_AudioPathOn();
|
||||
}
|
||||
|
||||
int FM_CheckFrequencyLock(uint16_t Frequency, uint16_t LowerLimit)
|
||||
@@ -391,8 +394,14 @@ static void Key_FUNC(KEY_Code_t Key, uint8_t state)
|
||||
|
||||
static void Key_EXIT(uint8_t state)
|
||||
{
|
||||
if (state != BUTTON_EVENT_SHORT)
|
||||
return;
|
||||
if (gInputBoxIndex) {
|
||||
if (state != BUTTON_EVENT_SHORT)
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (state != BUTTON_EVENT_PRESSED)
|
||||
return;
|
||||
}
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
@@ -603,8 +612,7 @@ void FM_Play(void)
|
||||
if (!gEeprom.FM_IsMrMode)
|
||||
gEeprom.FM_SelectedFrequency = gEeprom.FM_FrequencyPlaying;
|
||||
|
||||
AUDIO_AudioPathOn();
|
||||
gEnableSpeaker = true;
|
||||
FM_AudioPathOn();
|
||||
|
||||
GUI_SelectNextDisplay(DISPLAY_FM);
|
||||
return;
|
||||
@@ -639,9 +647,8 @@ void FM_Start(void)
|
||||
// Disable UHF LNA, enable VHF LNA
|
||||
BK4819_PickRXFilterPathBasedOnFrequency(10320000); // 103.2 MHz < 280 MHz
|
||||
|
||||
AUDIO_AudioPathOn();
|
||||
FM_AudioPathOn();
|
||||
|
||||
gEnableSpeaker = true;
|
||||
gUpdateStatus = true;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
|
||||
|
||||
+2
-14
@@ -91,7 +91,7 @@ void GENERIC_Key_F(bool bKeyPressed, bool bKeyHeld)
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
gBeepToPlay = BEEP_440HZ_500MS;
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
gPttWasReleased = true;
|
||||
}
|
||||
}
|
||||
@@ -103,20 +103,8 @@ void GENERIC_Key_PTT(bool bKeyPressed)
|
||||
if (!bKeyPressed || SerialConfigInProgress())
|
||||
{ // PTT released
|
||||
if (gCurrentFunction == FUNCTION_TRANSMIT) {
|
||||
// we are transmitting .. stop
|
||||
if (gFlagEndTransmission) {
|
||||
FUNCTION_Select(FUNCTION_FOREGROUND);
|
||||
}
|
||||
else {
|
||||
APP_EndTransmission();
|
||||
APP_HandleEndTransmission();
|
||||
|
||||
if (gEeprom.REPEATER_TAIL_TONE_ELIMINATION == 0)
|
||||
FUNCTION_Select(FUNCTION_FOREGROUND);
|
||||
else
|
||||
gRTTECountdown_10ms = gEeprom.REPEATER_TAIL_TONE_ELIMINATION * 10;
|
||||
}
|
||||
|
||||
gFlagEndTransmission = false;
|
||||
#ifdef ENABLE_VOX
|
||||
gVOX_NoiseDetected = false;
|
||||
#endif
|
||||
|
||||
+13
-14
@@ -44,6 +44,7 @@
|
||||
#include "radio.h"
|
||||
#include "settings.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/main.h"
|
||||
#include "ui/ui.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -305,9 +306,9 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep)
|
||||
gSquelchLevelOriginal = gEeprom.SQUELCH_LEVEL;
|
||||
|
||||
if (isKeyUp) {
|
||||
gEeprom.SQUELCH_LEVEL = (gEeprom.SQUELCH_LEVEL < 9) ? gEeprom.SQUELCH_LEVEL + 1 : 9;
|
||||
if (gEeprom.SQUELCH_LEVEL < 9) gEeprom.SQUELCH_LEVEL++;
|
||||
} else {
|
||||
gEeprom.SQUELCH_LEVEL = (gEeprom.SQUELCH_LEVEL > 0) ? gEeprom.SQUELCH_LEVEL - 1 : 0;
|
||||
if (gEeprom.SQUELCH_LEVEL > 0) gEeprom.SQUELCH_LEVEL--;
|
||||
}
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
}
|
||||
@@ -555,10 +556,10 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
return;
|
||||
}
|
||||
|
||||
gKeyInputCountdown = (gInputBoxIndex >= totalDigits) ? (key_input_timeout_500ms / 16) : (key_input_timeout_500ms / 3);
|
||||
gKeyInputCountdown = key_input_timeout_500ms / (gInputBoxIndex >= totalDigits ? 16 : 3);
|
||||
|
||||
if (gInputBoxIndex > totalDigits) {
|
||||
gInputBoxIndex = totalDigits;
|
||||
gInputBoxIndex = totalDigits;
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -569,11 +570,8 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
// convert to int
|
||||
uint32_t inputFreq = StrToUL(inputStr);
|
||||
|
||||
// how many zero to add
|
||||
uint8_t zerosToAdd = totalDigits - inputLength;
|
||||
|
||||
// add missing zero
|
||||
for (uint8_t i = 0; i < zerosToAdd; i++) {
|
||||
for (uint8_t i = 0; i < totalDigits - inputLength; i++) {
|
||||
inputFreq *= 10;
|
||||
}
|
||||
|
||||
@@ -767,6 +765,7 @@ static void MAIN_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
||||
att->exclude = true;
|
||||
|
||||
MR_SaveChannelAttributesToFlash(lastFoundFrqOrChan, att);
|
||||
UI_MAIN_NotifyScanProgressDataChanged();
|
||||
|
||||
gVfoConfigureMode = VFO_CONFIGURE;
|
||||
gFlagResetVfos = true;
|
||||
@@ -941,12 +940,12 @@ static void MAIN_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
|
||||
uint16_t Channel = gEeprom.ScreenChannel[gEeprom.TX_VFO];
|
||||
|
||||
if (bKeyHeld || !bKeyPressed) { // key held or released
|
||||
if (gInputBoxIndex > 0) {
|
||||
gInputBoxIndex = 0;
|
||||
gHasVfoBackup = false;
|
||||
}
|
||||
if (gInputBoxIndex > 0) {
|
||||
gInputBoxIndex = 0;
|
||||
gHasVfoBackup = false;
|
||||
}
|
||||
|
||||
if (bKeyHeld || !bKeyPressed) { // key held or released
|
||||
if (!bKeyPressed) {
|
||||
if (!bKeyHeld || IS_FREQ_CHANNEL(Channel))
|
||||
return;
|
||||
@@ -1079,4 +1078,4 @@ void MAIN_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
+193
-76
@@ -457,6 +457,28 @@ int MENU_GetLimits(uint8_t menu_id, int32_t *pMin, int32_t *pMax)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
case MENU_VOL: {
|
||||
// SysInf paginates:
|
||||
// page 0 = identity,
|
||||
// page 1 = build date/time,
|
||||
// page 2 = battery,
|
||||
// +1 if F4HWN_MEM (Flash/SRAM),
|
||||
// +2 if F4HWN_QRCODE (Code QR + Wiki QR).
|
||||
int32_t vol_max = 0;
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
vol_max += 2;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_MEM
|
||||
vol_max += 1;
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_QRCODE
|
||||
vol_max += 2;
|
||||
#endif
|
||||
if (vol_max == 0) return -1;
|
||||
*pMax = vol_max;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
@@ -594,7 +616,7 @@ void MENU_AcceptSetting(void)
|
||||
|
||||
case MENU_MEM_NAME:
|
||||
for (int i = 9; i >= 0; i--) {
|
||||
if (edit[i] != ' ' && edit[i] != '_' && edit[i] != 0x00 && edit[i] != 0xff)
|
||||
if (edit[i] != ' ' && edit[i] != 0x00 && edit[i] != 0xff)
|
||||
break;
|
||||
edit[i] = ' ';
|
||||
}
|
||||
@@ -1030,6 +1052,11 @@ void MENU_ShowCurrentSetting(void)
|
||||
gSubMenuSelection = gEeprom.SQUELCH_LEVEL;
|
||||
break;
|
||||
|
||||
case MENU_VOL:
|
||||
// SysInf is paginated; always start on page 0 (identity).
|
||||
gSubMenuSelection = 0;
|
||||
break;
|
||||
|
||||
case MENU_STEP:
|
||||
gSubMenuSelection = FREQUENCY_GetSortedIdxFromStepIdx(gTxVfo->STEP_SETTING);
|
||||
break;
|
||||
@@ -1464,6 +1491,22 @@ void MENU_ShowCurrentSetting(void)
|
||||
}
|
||||
}
|
||||
|
||||
static KEY_Code_t edit_last_key = 255;
|
||||
static uint8_t edit_char_index = 0;
|
||||
|
||||
static const char* const char_map[10] = {
|
||||
" 0", // KEY_0
|
||||
".,-()@/\\+=*#<>[]~1", // KEY_1
|
||||
"abc2", // KEY_2
|
||||
"def3", // KEY_3
|
||||
"ghi4", // KEY_4
|
||||
"jkl5", // KEY_5
|
||||
"mno6", // KEY_6
|
||||
"pqrs7", // KEY_7
|
||||
"tuv8", // KEY_8
|
||||
"wxyz9" // KEY_9
|
||||
};
|
||||
|
||||
static void MENU_Key_0_to_9(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
uint8_t Offset;
|
||||
@@ -1471,33 +1514,55 @@ static void MENU_Key_0_to_9(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
int32_t Max;
|
||||
uint16_t Value = 0;
|
||||
|
||||
if (bKeyHeld || !bKeyPressed)
|
||||
if (!bKeyPressed)
|
||||
return;
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME && edit_index >= 0)
|
||||
{ // currently editing the channel name
|
||||
if (edit_index >= 10)
|
||||
return;
|
||||
|
||||
if (edit_index < 10)
|
||||
uint8_t key_idx = Key - KEY_0;
|
||||
|
||||
if (bKeyHeld)
|
||||
{
|
||||
if (Key <= KEY_9)
|
||||
edit[edit_index] = '0' + key_idx;
|
||||
edit_last_key = 255;
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Key != edit_last_key)
|
||||
{
|
||||
edit_last_key = Key;
|
||||
edit_char_index = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
edit_char_index++;
|
||||
if (char_map[key_idx][edit_char_index] == '\0')
|
||||
{
|
||||
edit[edit_index] = '0' + Key - KEY_0;
|
||||
|
||||
if (++edit_index >= 10)
|
||||
{ // exit edit
|
||||
gFlagAcceptSetting = false;
|
||||
gAskForConfirmation = 1;
|
||||
}
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
edit_char_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
char c = char_map[key_idx][edit_char_index];
|
||||
if (edit_is_uppercase && c >= 'a' && c <= 'z')
|
||||
{
|
||||
c -= 32;
|
||||
}
|
||||
edit[edit_index] = c;
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bKeyHeld)
|
||||
return;
|
||||
|
||||
INPUTBOX_Append(Key);
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
@@ -1586,7 +1651,7 @@ static void MENU_Key_0_to_9(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
gInputBoxIndex = 0;
|
||||
|
||||
//Value = ((gInputBox[0] * 100) + (gInputBox[1] * 10) + gInputBox[2]) - 1;
|
||||
Value = ((gInputBox[0] * 1000) + (gInputBox[1] * 100) + (gInputBox[2] * 10) + gInputBox[3]) - 1;
|
||||
Value = (((gInputBox[0] * 10 + gInputBox[1]) * 10 + gInputBox[2]) * 10 + gInputBox[3]) - 1;
|
||||
|
||||
if (IS_MR_CHANNEL(Value))
|
||||
{
|
||||
@@ -1643,6 +1708,58 @@ static void MENU_Key_0_to_9(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
|
||||
static void MENU_Key_EXIT(bool bKeyPressed, bool bKeyHeld)
|
||||
{
|
||||
const bool editing_name = !gCssBackgroundScan
|
||||
&& UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME
|
||||
&& gIsInSubMenu
|
||||
&& edit_index >= 0;
|
||||
|
||||
if (editing_name)
|
||||
{
|
||||
if (!bKeyPressed)
|
||||
{
|
||||
if (bKeyHeld)
|
||||
return; // release after a long press, keep editing
|
||||
|
||||
if (edit_index == 0)
|
||||
goto Skip;
|
||||
|
||||
if (edit_index > 0)
|
||||
{ // step back one character while editing the channel name
|
||||
edit_index--;
|
||||
edit_last_key = 255;
|
||||
gAskForConfirmation = 0;
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bKeyHeld)
|
||||
{ // wait to see if the user wants a short exit or a long backspace
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
return;
|
||||
}
|
||||
|
||||
Skip:
|
||||
|
||||
/* Backlight related menus set full brightness. Set it back to the configured value,
|
||||
just in case we are editing from one of them. */
|
||||
BACKLIGHT_TurnOn();
|
||||
|
||||
gAskForConfirmation = 0;
|
||||
gIsInSubMenu = false;
|
||||
gInputBoxIndex = 0;
|
||||
gFlagRefreshSetting = true;
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_CANCEL;
|
||||
#endif
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (bKeyHeld || !bKeyPressed)
|
||||
return;
|
||||
|
||||
@@ -1650,29 +1767,22 @@ static void MENU_Key_EXIT(bool bKeyPressed, bool bKeyHeld)
|
||||
|
||||
if (!gCssBackgroundScan)
|
||||
{
|
||||
/* Backlight related menus set full brightness. Set it back to the configured value,
|
||||
just in case we are exiting from one of them. */
|
||||
BACKLIGHT_TurnOn();
|
||||
|
||||
if (gIsInSubMenu)
|
||||
{
|
||||
if (gInputBoxIndex == 0 || UI_MENU_GetCurrentMenuId() != MENU_OFFSET)
|
||||
{
|
||||
gAskForConfirmation = 0;
|
||||
gIsInSubMenu = false;
|
||||
gInputBoxIndex = 0;
|
||||
gFlagRefreshSetting = true;
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = VOICE_ID_CANCEL;
|
||||
#endif
|
||||
goto Skip;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Backlight related menus set full brightness. Set it back to the configured value,
|
||||
just in case we are exiting from one of them. */
|
||||
BACKLIGHT_TurnOn();
|
||||
|
||||
gInputBox[--gInputBoxIndex] = 10;
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
}
|
||||
|
||||
// ***********************
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1751,12 +1861,18 @@ static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld)
|
||||
|
||||
SETTINGS_FetchChannelName(edit, gSubMenuSelection);
|
||||
|
||||
// pad the channel name out with '_'
|
||||
edit_index = strlen(edit);
|
||||
while (edit_index < 10)
|
||||
edit[edit_index++] = '_';
|
||||
edit[edit_index] = 0;
|
||||
// pad the channel name out with ' '
|
||||
size_t len = strlen(edit);
|
||||
if (len < 10)
|
||||
{
|
||||
memset(edit + len, ' ', 10 - len);
|
||||
edit[10] = '\0';
|
||||
}
|
||||
|
||||
edit_index = 0; // 'edit_index' is going to be used as the cursor position
|
||||
edit_last_key = 255;
|
||||
edit_char_index = 0;
|
||||
edit_is_uppercase = false;
|
||||
|
||||
// make a copy so we can test for change when exiting the menu item
|
||||
memcpy(edit_original, edit, sizeof(edit_original));
|
||||
@@ -1766,6 +1882,7 @@ static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld)
|
||||
else
|
||||
if (edit_index >= 0 && edit_index < 10)
|
||||
{ // editing the channel name characters
|
||||
edit_last_key = 255;
|
||||
|
||||
if (++edit_index < 10)
|
||||
return; // next char
|
||||
@@ -1844,7 +1961,7 @@ static void MENU_Key_MENU(const bool bKeyPressed, const bool bKeyHeld)
|
||||
|
||||
static void MENU_Key_STAR(const bool bKeyPressed, const bool bKeyHeld)
|
||||
{
|
||||
if (bKeyHeld || !bKeyPressed)
|
||||
if (!bKeyPressed)
|
||||
return;
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
@@ -1854,13 +1971,8 @@ static void MENU_Key_STAR(const bool bKeyPressed, const bool bKeyHeld)
|
||||
|
||||
if (edit_index < 10)
|
||||
{
|
||||
edit[edit_index] = '-';
|
||||
|
||||
if (++edit_index >= 10)
|
||||
{ // exit edit
|
||||
gFlagAcceptSetting = false;
|
||||
gAskForConfirmation = 1;
|
||||
}
|
||||
edit[edit_index] = !bKeyHeld ? '-' : '*';
|
||||
edit_last_key = 255;
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
}
|
||||
@@ -1868,6 +1980,9 @@ static void MENU_Key_STAR(const bool bKeyPressed, const bool bKeyHeld)
|
||||
return;
|
||||
}
|
||||
|
||||
if (bKeyHeld)
|
||||
return;
|
||||
|
||||
RADIO_SelectVfos();
|
||||
|
||||
#ifdef ENABLE_NOAA
|
||||
@@ -1902,28 +2017,6 @@ static void MENU_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
Direction = -Direction;
|
||||
}
|
||||
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME && gIsInSubMenu && edit_index >= 0)
|
||||
{ // change the character
|
||||
if (bKeyPressed && edit_index < 10 && Direction != 0)
|
||||
{
|
||||
const char unwanted[] = "$%&!\"':;?^`|{}";
|
||||
char c = edit[edit_index] + Direction;
|
||||
unsigned int i = 0;
|
||||
while (i < sizeof(unwanted) && c >= 32 && c <= 126)
|
||||
{
|
||||
if (c == unwanted[i++])
|
||||
{ // choose next character
|
||||
c += Direction;
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
edit[edit_index] = (c < 32) ? 126 : (c > 126) ? 32 : c;
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bKeyHeld)
|
||||
{
|
||||
if (!bKeyPressed)
|
||||
@@ -1937,6 +2030,29 @@ static void MENU_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
|
||||
if (!bKeyPressed)
|
||||
return;
|
||||
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME && gIsInSubMenu && edit_index >= 0)
|
||||
{ // change the character
|
||||
if (edit_index < 10 && Direction != 0)
|
||||
{
|
||||
const char unwanted[] = "$%&!\"':;?^`|{}_";
|
||||
char c = edit[edit_index] + Direction;
|
||||
unsigned int i = 0;
|
||||
while (i < sizeof(unwanted) && c >= 32 && c <= 126)
|
||||
{
|
||||
if (c == unwanted[i++])
|
||||
{ // choose next character
|
||||
c += Direction;
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
edit[edit_index] = (c < 32) ? 126 : (c > 126) ? 32 : c;
|
||||
edit_last_key = 255;
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (SCANNER_IsScanning()) {
|
||||
return;
|
||||
}
|
||||
@@ -2067,19 +2183,20 @@ void MENU_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
case KEY_F:
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME && edit_index >= 0)
|
||||
{ // currently editing the channel name
|
||||
if (!bKeyHeld && bKeyPressed)
|
||||
if (!bKeyPressed)
|
||||
break;
|
||||
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
|
||||
if (edit_index < 10)
|
||||
{
|
||||
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
if (edit_index < 10)
|
||||
{
|
||||
edit[edit_index] = ' ';
|
||||
if (++edit_index >= 10)
|
||||
{ // exit edit
|
||||
gFlagAcceptSetting = false;
|
||||
gAskForConfirmation = 1;
|
||||
}
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
}
|
||||
if (bKeyHeld)
|
||||
edit[edit_index] = '#';
|
||||
|
||||
edit_is_uppercase = !edit_is_uppercase;
|
||||
edit_last_key = 255;
|
||||
|
||||
gRequestDisplayScreen = DISPLAY_MENU;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
+4
-4
@@ -62,7 +62,7 @@ static void SCANNER_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
||||
gInputBoxIndex = 0;
|
||||
|
||||
// uint16_t chan = ((gInputBox[0] * 100) + (gInputBox[1] * 10) + gInputBox[2]) - 1;
|
||||
uint16_t chan = ((gInputBox[0] * 1000) + (gInputBox[1] * 100) + (gInputBox[2] * 10) + gInputBox[3]) - 1;
|
||||
uint16_t chan = (((gInputBox[0] * 10 + gInputBox[1]) * 10 + gInputBox[2]) * 10 + gInputBox[3]) - 1;
|
||||
if (IS_MR_CHANNEL(chan)) {
|
||||
#ifdef ENABLE_VOICE
|
||||
gAnotherVoiceID = (VOICE_ID_t)Key;
|
||||
@@ -345,7 +345,7 @@ void SCANNER_Start(bool singleFreq)
|
||||
gScanFrequency = 0xFFFFFFFF;
|
||||
|
||||
BK4819_PickRXFilterPathBasedOnFrequency(gScanFrequency);
|
||||
BK4819_EnableFrequencyScan();
|
||||
BK4819_SetFrequencyScan(true);
|
||||
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
@@ -418,10 +418,10 @@ void SCANNER_TimeSlice10ms(void)
|
||||
else
|
||||
scanHitCount = 0;
|
||||
|
||||
BK4819_DisableFrequencyScan();
|
||||
BK4819_SetFrequencyScan(false);
|
||||
|
||||
if (scanHitCount < 3) {
|
||||
BK4819_EnableFrequencyScan();
|
||||
BK4819_SetFrequencyScan(true);
|
||||
}
|
||||
else {
|
||||
BK4819_SetScanFrequency(gScanFrequency);
|
||||
|
||||
+1059
-283
File diff suppressed because it is too large.
Load diff
+20
-13
@@ -42,7 +42,8 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
static const uint8_t DrawingEndY = 40;
|
||||
static const uint8_t DrawingEndY = 40;
|
||||
static const uint8_t DrawingTopY = 8; // Reserve top 8px for frequency display (gFrameBuffer[0])
|
||||
|
||||
static const uint8_t U8RssiMap[] = {
|
||||
121,
|
||||
@@ -78,29 +79,35 @@ static const uint16_t scanStepValues[] = {
|
||||
static const uint16_t scanStepBWRegValues[] = {
|
||||
// RX RXw TX BW
|
||||
// 0b0 000 000 001 01 1000
|
||||
// 1
|
||||
// 1 (S_STEP_0_01kHz, index 0)
|
||||
0b0000000001011000, // 6.25
|
||||
// 10
|
||||
// 10 (S_STEP_0_1kHz, index 1)
|
||||
0b0000000001011000, // 6.25
|
||||
// 50
|
||||
// 50 (S_STEP_0_5kHz, index 2)
|
||||
0b0000000001011000, // 6.25
|
||||
// 100
|
||||
// 100 (S_STEP_1_0kHz, index 3)
|
||||
0b0000000001011000, // 6.25
|
||||
// 250
|
||||
// 250 (S_STEP_2_5kHz, index 4)
|
||||
0b0000000001011000, // 6.25
|
||||
// 500
|
||||
// 500 (S_STEP_5_0kHz, index 5)
|
||||
0b0010010001011000, // 6.25
|
||||
// 625
|
||||
// 625 (S_STEP_6_25kHz, index 6)
|
||||
0b0100100001011000, // 6.25
|
||||
// 833
|
||||
// 833 (S_STEP_8_33kHz, index 7)
|
||||
0b0110110001001000, // 6.25
|
||||
// 1000
|
||||
// 1000 (S_STEP_10_0kHz, index 8)
|
||||
0b0110110001001000, // 6.25
|
||||
// 1250
|
||||
// 1250 (S_STEP_12_5kHz, index 9)
|
||||
0b0111111100001000, // 6.25
|
||||
// 2500
|
||||
// 1500 (S_STEP_15_0kHz, index 10)
|
||||
0b0011011000101000, // 25
|
||||
// 10000
|
||||
// 2000 (S_STEP_20_0kHz, index 11)
|
||||
0b0011011000101000, // 25
|
||||
// 2500 (S_STEP_25_0kHz, index 12)
|
||||
0b0011011000101000, // 25
|
||||
// 5000 (S_STEP_50_0kHz, index 13)
|
||||
0b0011011000101000, // 25
|
||||
// 10000 (S_STEP_100_0kHz, index 14)
|
||||
0b0011011000101000, // 25
|
||||
};
|
||||
|
||||
|
||||
+5
-5
@@ -347,9 +347,9 @@ static void CMD_0514(uint32_t Port, const uint8_t *pBuffer)
|
||||
#endif
|
||||
|
||||
gSerialConfigCountDown_500ms = 12; // 6 sec
|
||||
|
||||
// turn the LCD backlight off
|
||||
BACKLIGHT_TurnOff();
|
||||
|
||||
if (gEeprom.BACKLIGHT_TIME < 61) // backlight is set to be always on
|
||||
BACKLIGHT_TurnOff(); // turn the LCD backlight off
|
||||
|
||||
SendVersion(Port);
|
||||
}
|
||||
@@ -593,8 +593,8 @@ static void CMD_052F(uint32_t Port, const uint8_t *pBuffer)
|
||||
}
|
||||
#endif
|
||||
|
||||
// turn the LCD backlight off
|
||||
BACKLIGHT_TurnOff();
|
||||
if (gEeprom.BACKLIGHT_TIME < 61) // backlight is set to be always on
|
||||
BACKLIGHT_TurnOff(); // turn the LCD backlight off
|
||||
|
||||
SendVersion(Port);
|
||||
}
|
||||
|
||||
+2
-5
@@ -48,7 +48,6 @@ void AUDIO_PlayBeep(BEEP_Type_t Beep)
|
||||
|
||||
if (Beep != BEEP_880HZ_60MS_DOUBLE_BEEP &&
|
||||
Beep != BEEP_500HZ_60MS_DOUBLE_BEEP &&
|
||||
Beep != BEEP_440HZ_500MS &&
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
Beep != BEEP_880HZ_200MS &&
|
||||
Beep != BEEP_880HZ_500MS &&
|
||||
@@ -95,9 +94,6 @@ void AUDIO_PlayBeep(BEEP_Type_t Beep)
|
||||
case BEEP_500HZ_60MS_DOUBLE_BEEP:
|
||||
ToneFrequency = 500;
|
||||
break;
|
||||
case BEEP_440HZ_500MS:
|
||||
ToneFrequency = 440;
|
||||
break;
|
||||
case BEEP_880HZ_60MS_DOUBLE_BEEP:
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
case BEEP_880HZ_200MS:
|
||||
@@ -118,10 +114,12 @@ void AUDIO_PlayBeep(BEEP_Type_t Beep)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
if(Beep == BEEP_400HZ_30MS || Beep == BEEP_500HZ_30MS || Beep == BEEP_600HZ_30MS)
|
||||
{
|
||||
BK4819_WriteRegister(BK4819_REG_70, BK4819_REG_70_ENABLE_TONE1 | ((1 & 0x7f) << BK4819_REG_70_SHIFT_TONE1_TUNING_GAIN));
|
||||
}
|
||||
#endif
|
||||
|
||||
BK4819_PlayTone(ToneFrequency, true);
|
||||
|
||||
@@ -153,7 +151,6 @@ void AUDIO_PlayBeep(BEEP_Type_t Beep)
|
||||
Duration = 30;
|
||||
break;
|
||||
#endif
|
||||
case BEEP_440HZ_500MS:
|
||||
#ifndef ENABLE_FEAT_F4HWN
|
||||
case BEEP_880HZ_200MS:
|
||||
BK4819_ExitTxMute();
|
||||
|
||||
@@ -27,7 +27,6 @@ enum BEEP_Type_t
|
||||
BEEP_NONE = 0,
|
||||
BEEP_1KHZ_60MS_OPTIONAL,
|
||||
BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL,
|
||||
BEEP_440HZ_500MS,
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
BEEP_880HZ_200MS,
|
||||
BEEP_880HZ_500MS,
|
||||
|
||||
@@ -26,6 +26,12 @@ const uint16_t CTCSS_Options[50] = {
|
||||
2035, 2065, 2107, 2181, 2257, 2291, 2336, 2418, 2503, 2541
|
||||
};
|
||||
|
||||
// Indices in CTCSS_Options for the 12 non-homologated tones.
|
||||
static const uint8_t CTCSS_ExtraIdx[12] = {
|
||||
1, 26, 28, 30, 32, 34, 36, 38, 39, 41,
|
||||
45, 49
|
||||
};
|
||||
|
||||
const uint16_t DCS_Options[104] = {
|
||||
0x0013, 0x0015, 0x0016, 0x0019, 0x001A, 0x001E, 0x0023, 0x0027,
|
||||
0x0029, 0x002B, 0x002C, 0x0035, 0x0039, 0x003A, 0x003B, 0x003C,
|
||||
@@ -108,3 +114,30 @@ uint8_t DCS_GetCtcssCode(int Code)
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
uint8_t DCS_GetCtcssApprovedIndex(uint8_t Option)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int extra_pos = 0;
|
||||
uint8_t approved_index = 0;
|
||||
|
||||
if (Option >= ARRAY_SIZE(CTCSS_Options))
|
||||
return 0xFF;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(CTCSS_Options); i++) {
|
||||
const bool is_extra = extra_pos < ARRAY_SIZE(CTCSS_ExtraIdx) &&
|
||||
i == CTCSS_ExtraIdx[extra_pos];
|
||||
|
||||
if (is_extra) {
|
||||
extra_pos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == Option)
|
||||
return approved_index;
|
||||
|
||||
approved_index++;
|
||||
}
|
||||
|
||||
return 0xFF;
|
||||
}
|
||||
@@ -40,6 +40,6 @@ extern const uint16_t DCS_Options[104];
|
||||
uint32_t DCS_GetGolayCodeWord(DCS_CodeType_t CodeType, uint8_t Option);
|
||||
uint8_t DCS_GetCdcssCode(uint32_t Code);
|
||||
uint8_t DCS_GetCtcssCode(int Code);
|
||||
uint8_t DCS_GetCtcssApprovedIndex(uint8_t Option);
|
||||
|
||||
#endif
|
||||
|
||||
+2
-2
@@ -147,11 +147,11 @@ uint8_t BK4819_GetGlitchIndicator(void);
|
||||
uint8_t BK4819_GetExNoiceIndicator(void);
|
||||
uint16_t BK4819_GetVoiceAmplitudeOut(void);
|
||||
uint8_t BK4819_GetAfTxRx(void);
|
||||
void BK4819_SetRxAudioGain(void);
|
||||
|
||||
bool BK4819_GetFrequencyScanResult(uint32_t *pFrequency);
|
||||
BK4819_CssScanResult_t BK4819_GetCxCSSScanResult(uint32_t *pCdcssFreq, uint16_t *pCtcssFreq);
|
||||
void BK4819_DisableFrequencyScan(void);
|
||||
void BK4819_EnableFrequencyScan(void);
|
||||
void BK4819_SetFrequencyScan(bool enable);
|
||||
void BK4819_SetScanFrequency(uint32_t Frequency);
|
||||
|
||||
void BK4819_Disable(void);
|
||||
|
||||
+67
-65
@@ -39,6 +39,16 @@ static const uint16_t FSK_RogerTable[7] = {0xF1A2, 0x7446, 0x61A4, 0x6544, 0x4E8
|
||||
|
||||
static uint16_t gBK4819_GpioOutState;
|
||||
|
||||
#define SHORT_DELAY() \
|
||||
__asm volatile("nop\n nop\n nop\n nop\n nop\n" \
|
||||
"nop\n nop\n nop\n nop\n nop\n" \
|
||||
"nop\n nop\n nop\n nop\n nop\n" \
|
||||
"nop\n nop\n nop\n nop\n nop\n" \
|
||||
"nop\n nop\n nop\n nop\n nop\n" \
|
||||
"nop\n nop\n nop\n nop\n nop\n" \
|
||||
"nop\n nop\n nop\n nop\n nop\n" \
|
||||
"nop\n nop\n nop\n nop\n nop\n")
|
||||
|
||||
bool gRxIdleMode;
|
||||
|
||||
static inline void CS_Assert()
|
||||
@@ -197,63 +207,78 @@ static uint16_t BK4819_ReadU16(void)
|
||||
uint16_t Value;
|
||||
|
||||
SDA_SetDir(false);
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
Value = 0;
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
Value <<= 1;
|
||||
Value |= SDA_ReadInput();
|
||||
SCL_Set();
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
SCL_Reset();
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
}
|
||||
SDA_SetDir(true);
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
static uint16_t reg_30_cache = 0xFFFF;
|
||||
static uint16_t reg_47_cache = 0xFFFF;
|
||||
|
||||
uint16_t BK4819_ReadRegister(BK4819_REGISTER_t Register)
|
||||
{
|
||||
uint16_t Value;
|
||||
|
||||
CS_Release();
|
||||
SCL_Reset();
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
|
||||
CS_Assert();
|
||||
BK4819_WriteU8(Register | 0x80);
|
||||
Value = BK4819_ReadU16();
|
||||
CS_Release();
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
|
||||
SCL_Set();
|
||||
SDA_Set();
|
||||
|
||||
if (Register == BK4819_REG_30)
|
||||
reg_30_cache = Value;
|
||||
else if (Register == BK4819_REG_47)
|
||||
reg_47_cache = Value;
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
void BK4819_WriteRegister(BK4819_REGISTER_t Register, uint16_t Data)
|
||||
{
|
||||
if (Register == BK4819_REG_30)
|
||||
{
|
||||
if (Data == reg_30_cache)
|
||||
return;
|
||||
reg_30_cache = Data;
|
||||
}
|
||||
else if (Register == BK4819_REG_47)
|
||||
{
|
||||
if (Data == reg_47_cache)
|
||||
return;
|
||||
reg_47_cache = Data;
|
||||
}
|
||||
|
||||
CS_Release();
|
||||
SCL_Reset();
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
|
||||
CS_Assert();
|
||||
BK4819_WriteU8(Register);
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
|
||||
BK4819_WriteU16(Data);
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
|
||||
CS_Release();
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
|
||||
SCL_Set();
|
||||
SDA_Set();
|
||||
@@ -271,14 +296,14 @@ void BK4819_WriteU8(uint8_t Data)
|
||||
else
|
||||
SDA_Set();
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
SCL_Set();
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
|
||||
Data <<= 1;
|
||||
|
||||
SCL_Reset();
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,14 +319,14 @@ void BK4819_WriteU16(uint16_t Data)
|
||||
else
|
||||
SDA_Set();
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
SCL_Set();
|
||||
|
||||
Data <<= 1;
|
||||
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
SCL_Reset();
|
||||
SYSTICK_DelayUs(1);
|
||||
SHORT_DELAY();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1432,24 +1457,15 @@ void BK4819_GenTail(uint8_t Tail)
|
||||
// freq(Hz) * 20.64888 for XTAL 13M/26M or
|
||||
// freq(Hz)*20.97152 for XTAL 12.8M/19.2M/25.6M/38.4M
|
||||
|
||||
switch (Tail)
|
||||
{
|
||||
case 0: // 134.4Hz CTCSS Tail
|
||||
BK4819_WriteRegister(BK4819_REG_52, 0x828F); // 1 00 0 001010 001111
|
||||
break;
|
||||
case 1: // 120° phase shift
|
||||
BK4819_WriteRegister(BK4819_REG_52, 0xA28F); // 1 01 0 001010 001111
|
||||
break;
|
||||
case 2: // 180° phase shift
|
||||
BK4819_WriteRegister(BK4819_REG_52, 0xC28F); // 1 10 0 001010 001111
|
||||
break;
|
||||
case 3: // 240° phase shift
|
||||
BK4819_WriteRegister(BK4819_REG_52, 0xE28F); // 1 11 0 001010 001111
|
||||
break;
|
||||
case 4: // 55Hz tone freq
|
||||
BK4819_WriteRegister(BK4819_REG_07, 0x046f); // 0 00 0 010001 101111
|
||||
break;
|
||||
}
|
||||
if (Tail <= 3)
|
||||
// 0: 134.4Hz CTCSS Tail
|
||||
// 1: 120° phase shift
|
||||
// 2: 180° phase shift
|
||||
// 3: 240° phase shift
|
||||
BK4819_WriteRegister(BK4819_REG_52, 0x028F | (Tail << 13));
|
||||
else if (Tail == 4)
|
||||
// 4: 55Hz tone freq
|
||||
BK4819_WriteRegister(BK4819_REG_07, 0x046F);
|
||||
}
|
||||
|
||||
void BK4819_PlayCDCSSTail(void)
|
||||
@@ -1531,6 +1547,14 @@ uint8_t BK4819_GetAfTxRx(void)
|
||||
return BK4819_ReadRegister(BK4819_REG_6F) & 0x003F;
|
||||
}
|
||||
|
||||
void BK4819_SetRxAudioGain(void) {
|
||||
BK4819_WriteRegister(BK4819_REG_48,
|
||||
(11u << 12) | // ??? .. 0 ~ 15, doesn't seem to make any difference
|
||||
( 0u << 10) | // AF Rx Gain-1
|
||||
(gEeprom.VOLUME_GAIN << 4) | // AF Rx Gain-2
|
||||
(gEeprom.DAC_GAIN << 0)); // AF DAC Gain (after Gain-1 and Gain-2)
|
||||
}
|
||||
|
||||
bool BK4819_GetFrequencyScanResult(uint32_t *pFrequency)
|
||||
{
|
||||
const uint16_t High = BK4819_ReadRegister(BK4819_REG_0D);
|
||||
@@ -1566,7 +1590,7 @@ BK4819_CssScanResult_t BK4819_GetCxCSSScanResult(uint32_t *pCdcssFreq, uint16_t
|
||||
return BK4819_CSS_RESULT_NOT_FOUND;
|
||||
}
|
||||
|
||||
void BK4819_DisableFrequencyScan(void)
|
||||
void BK4819_SetFrequencyScan(bool enable)
|
||||
{
|
||||
// REG_32
|
||||
//
|
||||
@@ -1582,32 +1606,10 @@ void BK4819_DisableFrequencyScan(void)
|
||||
// 1 = enable
|
||||
// 0 = disable
|
||||
//
|
||||
BK4819_WriteRegister(BK4819_REG_32, // 0x0244); // 00 0000100100010 0
|
||||
BK4819_WriteRegister(BK4819_REG_32,
|
||||
( 0u << 14) | // 0 frequency scan Time
|
||||
(290u << 1) | // ???
|
||||
( 0u << 0)); // 0 frequency scan enable
|
||||
}
|
||||
|
||||
void BK4819_EnableFrequencyScan(void)
|
||||
{
|
||||
// REG_32
|
||||
//
|
||||
// <15:14> 0 frequency scan time
|
||||
// 0 = 0.2 sec
|
||||
// 1 = 0.4 sec
|
||||
// 2 = 0.8 sec
|
||||
// 3 = 1.6 sec
|
||||
//
|
||||
// <13:1> ???
|
||||
//
|
||||
// <0> 0 frequency scan enable
|
||||
// 1 = enable
|
||||
// 0 = disable
|
||||
//
|
||||
BK4819_WriteRegister(BK4819_REG_32, // 0x0245); // 00 0000100100010 1
|
||||
( 0u << 14) | // 0 frequency scan time
|
||||
(290u << 1) | // ???
|
||||
( 1u << 0)); // 1 frequency scan enable
|
||||
(enable ? 1u : 0u)); // frequency scan (1: enable | 0: disable)
|
||||
}
|
||||
|
||||
void BK4819_SetScanFrequency(uint32_t Frequency)
|
||||
@@ -1671,7 +1673,7 @@ void BK4819_Disable(void)
|
||||
|
||||
void BK4819_StopScan(void)
|
||||
{
|
||||
BK4819_DisableFrequencyScan();
|
||||
BK4819_SetFrequencyScan(false);
|
||||
BK4819_Disable();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -362,7 +362,7 @@ const uint8_t gFontSmall[95-1][6] =
|
||||
{0x20, 0x40, 0x40, 0x3D, 0x00, 0x00}, // 'j'
|
||||
{0x00, 0x7F, 0x10, 0x28, 0x44, 0x00}, // 'k'
|
||||
{0x00, 0x00, 0x3F, 0x40, 0x00, 0x00}, // 'l'
|
||||
{0x7C, 0x08, 0x10, 0x10, 0x08, 0x7C}, // 'm'
|
||||
{0x7C, 0x04, 0x7C, 0x04, 0x78, 0x00}, // 'm'
|
||||
{0x7C, 0x04, 0x04, 0x04, 0x78, 0x00}, // 'n'
|
||||
{0x38, 0x44, 0x44, 0x44, 0x38, 0x00}, // 'o'
|
||||
{0x7C, 0x14, 0x14, 0x14, 0x08, 0x00}, // 'p'
|
||||
|
||||
+1
-5
@@ -157,11 +157,7 @@ void Main(void)
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
gEeprom.KEY_LOCK = 0;
|
||||
SETTINGS_SaveSettings();
|
||||
gMenuCursor = MENU_ITEMS;
|
||||
|
||||
#ifdef ENABLE_NOAA
|
||||
gMenuCursor += 1; // move to hidden section, fix me if change... !!!
|
||||
#endif
|
||||
gMenuCursor = UI_MENU_GetMenuIdx(FIRST_HIDDEN_MENU_ITEM);
|
||||
gSubMenuSelection = gSetting_F_LOCK;
|
||||
#endif
|
||||
}
|
||||
|
||||
+1
-3
@@ -39,8 +39,6 @@
|
||||
#define FM_CHANNELS_MAX 48
|
||||
#define MR_CHANNELS_MAX 1024
|
||||
#define MR_CHANNELS_LIST 24
|
||||
#define MENU_ITEMS 69
|
||||
|
||||
// CACHE-BASED OPTIMIZATION: Only keep active channels in RAM
|
||||
// Full array stays in EEPROM, cache holds ~10 most-used channels
|
||||
#define MR_CHANNELS_CACHE_SIZE 10
|
||||
@@ -472,4 +470,4 @@ void FUNCTION_NOP();
|
||||
|
||||
static inline bool SerialConfigInProgress() { return gSerialConfigCountDown_500ms != 0; }
|
||||
|
||||
#endif
|
||||
#endif
|
||||
+102
-173
@@ -70,64 +70,45 @@ const char gModulationStr[MODULATION_UKNOWN][4] = {
|
||||
// Audio impression: fuller bass, more direct sound, while still keeping the 3 kHz top-end limit.
|
||||
|
||||
static void AUDIO_ApplyFMProfile(uint8_t profile)
|
||||
{
|
||||
switch (profile)
|
||||
{
|
||||
default:
|
||||
case 0: // FLAT
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x3200);
|
||||
break;
|
||||
{ // | 0x54 || 0x55 |
|
||||
static const uint16_t fm_profiles[][2] = {
|
||||
{0x9009, 0x3200}, // 0: FLAT
|
||||
{0x9009, 0x33A9}, // 1: CLEAN
|
||||
{0x9009, 0x3600}, // 2: MID
|
||||
{0x8546, 0x3AF0}, // 3: BOOST
|
||||
{0x8566, 0x3D00} // 4: MAX
|
||||
};
|
||||
|
||||
case 1: // CLEAN
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x33A9);
|
||||
break;
|
||||
if (profile >= ARRAY_SIZE(fm_profiles))
|
||||
profile = 0;
|
||||
|
||||
case 2: // MID
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x3600);
|
||||
break;
|
||||
|
||||
case 3: // BOOST
|
||||
BK4819_WriteRegister(0x54, 0x8546);
|
||||
BK4819_WriteRegister(0x55, 0x3AF0);
|
||||
break;
|
||||
|
||||
case 4: // MAX
|
||||
BK4819_WriteRegister(0x54, 0x8566);
|
||||
BK4819_WriteRegister(0x55, 0x3D00);
|
||||
break;
|
||||
}
|
||||
BK4819_WriteRegister(0x54, fm_profiles[profile][0]);
|
||||
BK4819_WriteRegister(0x55, fm_profiles[profile][1]);
|
||||
}
|
||||
|
||||
static void AUDIO_ApplyAMProfile(uint8_t profile)
|
||||
{
|
||||
switch (profile)
|
||||
{
|
||||
default:
|
||||
case 0: // SHARP (ALPHA test profile) - Narrow IF filter (REG54 bits[14:8]=0, bits[7:0]=9), low IF gain (REG55 bits[11:8]=1, ref=169)
|
||||
// Selective and crisp, best adjacent channel rejection, may sound harsh on strong signals
|
||||
BK4819_WriteRegister(0x2b, 0x0300);
|
||||
BK4819_WriteRegister(0x2f, 0x9990);
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x31A9);
|
||||
break;
|
||||
case 1: // STOCK - Narrow IF filter (REG54 bits[14:8]=0, bits[7:0]=9), moderate IF gain (REG55 bits[11:8]=4, ref=180)
|
||||
// Selective filter with balanced gain, punchy and detailed, good compromise between rejection and sensitivity
|
||||
BK4819_WriteRegister(0x2b, 0x0500);
|
||||
BK4819_WriteRegister(0x2f, 0x9990);
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x31A9);
|
||||
break;
|
||||
case 2: // OPEN (BRAVO test profile) - Medium-wide IF filter (REG54 bits[14:8]=8, bits[7:0]=70), high IF gain (REG55 bits[11:8]=8, ref=192)
|
||||
// Wide and pleasant, better sensitivity on weak signals, may struggle with adjacent channel interference
|
||||
BK4819_WriteRegister(0x2b, 0x0300);
|
||||
BK4819_WriteRegister(0x2f, 0x9990);
|
||||
BK4819_WriteRegister(0x54, 0x8846);
|
||||
BK4819_WriteRegister(0x55, 0x38C0);
|
||||
break;
|
||||
}
|
||||
{ // | 0x2b || 0x2f || 0x54 || 0x55 |
|
||||
static const uint16_t am_profiles[][4] = {
|
||||
// SHARP (ALPHA test profile) - Narrow IF filter (REG54 bits[14:8]=0, bits[7:0]=9), low IF gain (REG55 bits[11:8]=1, ref=169)
|
||||
// Selective and crisp, best adjacent channel rejection, may sound harsh on strong signals
|
||||
{0x0300, 0x9990, 0x9009, 0x31A9},
|
||||
|
||||
// STOCK - Narrow IF filter (REG54 bits[14:8]=0, bits[7:0]=9), moderate IF gain (REG55 bits[11:8]=4, ref=180)
|
||||
// Selective filter with balanced gain, punchy and detailed, good compromise between rejection and sensitivity
|
||||
{0x0500, 0x9990, 0x9009, 0x31A9},
|
||||
|
||||
// OPEN (BRAVO test profile) - Medium-wide IF filter (REG54 bits[14:8]=8, bits[7:0]=70), high IF gain (REG55 bits[11:8]=8, ref=192)
|
||||
// Wide and pleasant, better sensitivity on weak signals, may struggle with adjacent channel interference
|
||||
{0x0300, 0x9990, 0x8846, 0x38C0}
|
||||
};
|
||||
|
||||
if (profile >= ARRAY_SIZE(am_profiles))
|
||||
profile = 0;
|
||||
|
||||
BK4819_WriteRegister(0x2b, am_profiles[profile][0]);
|
||||
BK4819_WriteRegister(0x2f, am_profiles[profile][1]);
|
||||
BK4819_WriteRegister(0x54, am_profiles[profile][2]);
|
||||
BK4819_WriteRegister(0x55, am_profiles[profile][3]);
|
||||
}
|
||||
|
||||
static void AUDIO_ApplyUSBProfile(void)
|
||||
@@ -252,6 +233,24 @@ void RADIO_InitInfo(VFO_Info_t *pInfo, const uint16_t ChannelSave, const uint32_
|
||||
RADIO_ConfigureSquelchAndOutputPower(pInfo);
|
||||
}
|
||||
|
||||
void RADIO_ValidateAndSetCode(FREQ_Config_t *pFreq_Config, uint8_t tmp) {
|
||||
switch (pFreq_Config->CodeType) {
|
||||
default:
|
||||
case CODE_TYPE_OFF:
|
||||
pFreq_Config->CodeType = CODE_TYPE_OFF;
|
||||
tmp = 0;
|
||||
break;
|
||||
|
||||
case CODE_TYPE_CONTINUOUS_TONE:
|
||||
case CODE_TYPE_DIGITAL:
|
||||
case CODE_TYPE_REVERSE_DIGITAL:
|
||||
if (tmp > ((pFreq_Config->CodeType == CODE_TYPE_CONTINUOUS_TONE ? ARRAY_SIZE(CTCSS_Options) : ARRAY_SIZE(DCS_Options)) - 1))
|
||||
tmp = 0;
|
||||
break;
|
||||
}
|
||||
pFreq_Config->Code = tmp;
|
||||
}
|
||||
|
||||
void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure)
|
||||
{
|
||||
VFO_Info_t *pVfo = &gEeprom.VfoInfo[VFO];
|
||||
@@ -325,7 +324,7 @@ void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure
|
||||
}
|
||||
|
||||
pVfo->Band = band;
|
||||
pVfo->SCANLIST_PARTICIPATION = bParticipation;
|
||||
pVfo->SCANLIST_PARTICIPATION = bParticipation;
|
||||
pVfo->CHANNEL_SAVE = channel;
|
||||
|
||||
uint32_t base;
|
||||
@@ -370,49 +369,8 @@ void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure
|
||||
pVfo->freq_config_RX.CodeType = (data[2] >> 0) & 0x0F;
|
||||
pVfo->freq_config_TX.CodeType = (data[2] >> 4) & 0x0F;
|
||||
|
||||
tmp = data[0];
|
||||
switch (pVfo->freq_config_RX.CodeType)
|
||||
{
|
||||
default:
|
||||
case CODE_TYPE_OFF:
|
||||
pVfo->freq_config_RX.CodeType = CODE_TYPE_OFF;
|
||||
tmp = 0;
|
||||
break;
|
||||
|
||||
case CODE_TYPE_CONTINUOUS_TONE:
|
||||
if (tmp > (ARRAY_SIZE(CTCSS_Options) - 1))
|
||||
tmp = 0;
|
||||
break;
|
||||
|
||||
case CODE_TYPE_DIGITAL:
|
||||
case CODE_TYPE_REVERSE_DIGITAL:
|
||||
if (tmp > (ARRAY_SIZE(DCS_Options) - 1))
|
||||
tmp = 0;
|
||||
break;
|
||||
}
|
||||
pVfo->freq_config_RX.Code = tmp;
|
||||
|
||||
tmp = data[1];
|
||||
switch (pVfo->freq_config_TX.CodeType)
|
||||
{
|
||||
default:
|
||||
case CODE_TYPE_OFF:
|
||||
pVfo->freq_config_TX.CodeType = CODE_TYPE_OFF;
|
||||
tmp = 0;
|
||||
break;
|
||||
|
||||
case CODE_TYPE_CONTINUOUS_TONE:
|
||||
if (tmp > (ARRAY_SIZE(CTCSS_Options) - 1))
|
||||
tmp = 0;
|
||||
break;
|
||||
|
||||
case CODE_TYPE_DIGITAL:
|
||||
case CODE_TYPE_REVERSE_DIGITAL:
|
||||
if (tmp > (ARRAY_SIZE(DCS_Options) - 1))
|
||||
tmp = 0;
|
||||
break;
|
||||
}
|
||||
pVfo->freq_config_TX.Code = tmp;
|
||||
RADIO_ValidateAndSetCode(&pVfo->freq_config_RX, data[0]);
|
||||
RADIO_ValidateAndSetCode(&pVfo->freq_config_TX, data[1]);
|
||||
|
||||
if (data[4] == 0xFF)
|
||||
{
|
||||
@@ -624,30 +582,15 @@ void RADIO_ConfigureSquelchAndOutputPower(VFO_Info_t *pInfo)
|
||||
uint8_t Op = 0; // Low eeprom calibration data
|
||||
uint8_t currentPower = pInfo->OUTPUT_POWER;
|
||||
|
||||
if(currentPower == OUTPUT_POWER_USER)
|
||||
{
|
||||
if(gSetting_set_pwr == 5)
|
||||
{
|
||||
Op = 1; // Mid eeprom calibration data
|
||||
}
|
||||
else if(gSetting_set_pwr == 6)
|
||||
{
|
||||
Op = 2; // High eeprom calibration data
|
||||
}
|
||||
if (currentPower == OUTPUT_POWER_USER)
|
||||
currentPower = gSetting_set_pwr;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentPower == OUTPUT_POWER_MID)
|
||||
{
|
||||
Op = 1; // Mid eeprom calibration data
|
||||
}
|
||||
else if(currentPower == OUTPUT_POWER_HIGH)
|
||||
{
|
||||
Op = 2; // High eeprom calibration data
|
||||
}
|
||||
currentPower--;
|
||||
}
|
||||
|
||||
if (currentPower == 5)
|
||||
Op = 1; // Mid eeprom calibration data
|
||||
else if (currentPower == 6)
|
||||
Op = 2; // High eeprom calibration data
|
||||
|
||||
PY25Q16_ReadBuffer(0x100D0 + (Band * 16) + (Op * 3), Txp, 3);
|
||||
|
||||
@@ -790,6 +733,12 @@ void RADIO_SelectVfos(void)
|
||||
RADIO_SelectCurrentVfo();
|
||||
}
|
||||
|
||||
BK4819_FilterBandwidth_t RADIO_GetAMFilterBandwidth(const VFO_Info_t *pVfo)
|
||||
{
|
||||
// On BK4829, AM "wide" intentionally reuses the wider RF filter preset.
|
||||
return (pVfo->CHANNEL_BANDWIDTH == BANDWIDTH_WIDE) ? BK4819_FILTER_BW_WIDE : BK4819_FILTER_BW_AM;
|
||||
}
|
||||
|
||||
void RADIO_SetupRegisters(bool switchToForeground)
|
||||
{
|
||||
BK4819_FilterBandwidth_t Bandwidth = gRxVfo->CHANNEL_BANDWIDTH;
|
||||
@@ -808,7 +757,7 @@ void RADIO_SetupRegisters(bool switchToForeground)
|
||||
BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false);
|
||||
|
||||
if (gRxVfo->Modulation == MODULATION_AM)
|
||||
BK4819_SetFilterBandwidth(BK4819_FILTER_BW_AM, true);
|
||||
BK4819_SetFilterBandwidth(RADIO_GetAMFilterBandwidth(gRxVfo), true);
|
||||
else
|
||||
{
|
||||
switch (Bandwidth)
|
||||
@@ -872,12 +821,7 @@ void RADIO_SetupRegisters(bool switchToForeground)
|
||||
|
||||
// AF RX Gain and DAC
|
||||
//BK4819_WriteRegister(BK4819_REG_48, 0xB3A8); // 1011 00 111010 1000
|
||||
BK4819_WriteRegister(BK4819_REG_48,
|
||||
(11u << 12) | // ??? .. 0 ~ 15, doesn't seem to make any difference
|
||||
( 0u << 10) | // AF Rx Gain-1
|
||||
(gEeprom.VOLUME_GAIN << 4) | // AF Rx Gain-2
|
||||
(gEeprom.DAC_GAIN << 0)); // AF DAC Gain (after Gain-1 and Gain-2)
|
||||
|
||||
BK4819_SetRxAudioGain();
|
||||
|
||||
uint16_t InterruptMask = BK4819_REG_3F_SQUELCH_FOUND | BK4819_REG_3F_SQUELCH_LOST;
|
||||
|
||||
@@ -1107,26 +1051,25 @@ 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();
|
||||
if (modulation == MODULATION_BYP || modulation == MODULATION_RAW) {
|
||||
uint16_t reg_3d_val = 0x0000;
|
||||
|
||||
if (modulation == MODULATION_BYP) {
|
||||
// BYP on BK4829 uses full audio bypass profile.
|
||||
BK4819_EnterBypass();
|
||||
reg_3d_val = 0x2AAB;
|
||||
} else {
|
||||
// RAW on BK4829 uses RX-only filter bypass profile.
|
||||
BK4819_EnterRaw();
|
||||
// reg_3d_val = 0x0000;
|
||||
}
|
||||
|
||||
BK4819_SetRegValue(afDacGainRegSpec, 0xF);
|
||||
BK4819_WriteRegister(BK4819_REG_3D, 0x2AAB);
|
||||
BK4819_WriteRegister(BK4819_REG_3D, reg_3d_val);
|
||||
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
|
||||
@@ -1172,28 +1115,11 @@ void RADIO_SetModulation(ModulationMode_t modulation)
|
||||
BK4819_WriteRegister(0x55, 0x31a9);
|
||||
#endif
|
||||
|
||||
BK4819_SetFilterBandwidth(BK4819_FILTER_BW_AM, true);
|
||||
BK4819_SetFilterBandwidth(RADIO_GetAMFilterBandwidth(gCurrentVfo), true);
|
||||
break;
|
||||
}
|
||||
|
||||
case MODULATION_USB:
|
||||
{
|
||||
uint16_t uVar1 = BK4819_ReadRegister(0x31);
|
||||
BK4819_WriteRegister(0x31, uVar1 & 0xfffe); // AM Demodulation Disable
|
||||
BK4819_WriteRegister(0x42, 0x6b5a);
|
||||
BK4819_WriteRegister(0x2a, 0x7400);
|
||||
BK4819_WriteRegister(0x2b, 0x0000);
|
||||
BK4819_WriteRegister(0x2f, 0x9890);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
AUDIO_ApplyUSBProfile();
|
||||
#else
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x31a9);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
case MODULATION_FM:
|
||||
default:
|
||||
{
|
||||
@@ -1205,7 +1131,10 @@ void RADIO_SetModulation(ModulationMode_t modulation)
|
||||
BK4819_WriteRegister(0x2f, 0x9890);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO
|
||||
AUDIO_ApplyFMProfile(gSetting_set_audio_fm);
|
||||
if (modulation == MODULATION_USB)
|
||||
AUDIO_ApplyUSBProfile();
|
||||
else
|
||||
AUDIO_ApplyFMProfile(gSetting_set_audio_fm);
|
||||
#else
|
||||
BK4819_WriteRegister(0x54, 0x9009);
|
||||
BK4819_WriteRegister(0x55, 0x31a9);
|
||||
@@ -1397,17 +1326,19 @@ void RADIO_PrepareTX(void)
|
||||
|
||||
void RADIO_SendCssTail(void)
|
||||
{
|
||||
switch (gCurrentVfo->pTX->CodeType) {
|
||||
case CODE_TYPE_DIGITAL:
|
||||
case CODE_TYPE_REVERSE_DIGITAL:
|
||||
BK4819_PlayCDCSSTail();
|
||||
break;
|
||||
default:
|
||||
BK4819_PlayCTCSSTail();
|
||||
break;
|
||||
}
|
||||
if (gEeprom.TAIL_TONE_ELIMINATION) {
|
||||
switch (gCurrentVfo->pTX->CodeType) {
|
||||
case CODE_TYPE_DIGITAL:
|
||||
case CODE_TYPE_REVERSE_DIGITAL:
|
||||
BK4819_PlayCDCSSTail();
|
||||
break;
|
||||
default:
|
||||
BK4819_PlayCTCSSTail();
|
||||
break;
|
||||
}
|
||||
|
||||
SYSTEM_DelayMs(200);
|
||||
SYSTEM_DelayMs(200);
|
||||
}
|
||||
}
|
||||
|
||||
void RADIO_SendEndOfTransmission(void)
|
||||
@@ -1416,8 +1347,7 @@ void RADIO_SendEndOfTransmission(void)
|
||||
DTMF_SendEndOfTransmission();
|
||||
|
||||
// send the CTCSS/DCS tail tone - allows the receivers to mute the usual FM squelch tail/crash
|
||||
if(gEeprom.TAIL_TONE_ELIMINATION)
|
||||
RADIO_SendCssTail();
|
||||
RADIO_SendCssTail();
|
||||
RADIO_SetupRegisters(false);
|
||||
}
|
||||
|
||||
@@ -1427,7 +1357,6 @@ void RADIO_PrepareCssTX(void)
|
||||
|
||||
SYSTEM_DelayMs(200);
|
||||
|
||||
if(gEeprom.TAIL_TONE_ELIMINATION)
|
||||
RADIO_SendCssTail();
|
||||
RADIO_SendCssTail();
|
||||
RADIO_SetupRegisters(true);
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "dcs.h"
|
||||
#include "driver/bk4819.h"
|
||||
#include "frequencies.h"
|
||||
|
||||
enum {
|
||||
@@ -161,6 +162,7 @@ void RADIO_SetupRegisters(bool switchToForeground);
|
||||
#ifdef ENABLE_NOAA
|
||||
void RADIO_ConfigureNOAA(void);
|
||||
#endif
|
||||
BK4819_FilterBandwidth_t RADIO_GetAMFilterBandwidth(const VFO_Info_t *pVfo);
|
||||
void RADIO_SetTxParameters(void);
|
||||
void RADIO_SetupAGC(bool listeningAM, bool disable);
|
||||
void RADIO_SetModulation(ModulationMode_t modulation);
|
||||
|
||||
+28
-34
@@ -30,6 +30,23 @@
|
||||
|
||||
EEPROM_Config_t gEeprom = { 0 };
|
||||
|
||||
// Load a DTMF code from EEPROM, falling back to default_val if invalid.
|
||||
static void SETTINGS_LoadEepromDtmf(uint32_t addr, char *dest, size_t size, const char *default_val)
|
||||
{
|
||||
uint8_t buf[16];
|
||||
|
||||
if (size > sizeof(buf))
|
||||
size = sizeof(buf);
|
||||
|
||||
PY25Q16_ReadBuffer(addr, buf, size);
|
||||
|
||||
if (DTMF_ValidateCodes((char *)buf, size)) {
|
||||
memcpy(dest, buf, size);
|
||||
} else {
|
||||
strcpy(dest, default_val);
|
||||
}
|
||||
}
|
||||
|
||||
void SETTINGS_InitEEPROM(void)
|
||||
{
|
||||
uint8_t Data[16] = {0};
|
||||
@@ -218,9 +235,12 @@ gEeprom.FreqChannel[1] = IS_FREQ_CHANNEL(Data16[5]) ? Data16[5] : (FREQ_CHANNE
|
||||
|
||||
gEeprom.FM_Band = fmCfg.band;
|
||||
//gEeprom.FM_Space = fmCfg.space;
|
||||
|
||||
uint16_t freqLoLimit = BK1080_GetFreqLoLimit(gEeprom.FM_Band);
|
||||
|
||||
gEeprom.FM_SelectedFrequency =
|
||||
(fmCfg.selFreq >= BK1080_GetFreqLoLimit(gEeprom.FM_Band) && fmCfg.selFreq <= BK1080_GetFreqHiLimit(gEeprom.FM_Band)) ?
|
||||
fmCfg.selFreq : BK1080_GetFreqLoLimit(gEeprom.FM_Band);
|
||||
(fmCfg.selFreq >= freqLoLimit && fmCfg.selFreq <= BK1080_GetFreqHiLimit(gEeprom.FM_Band)) ?
|
||||
fmCfg.selFreq : freqLoLimit;
|
||||
|
||||
gEeprom.FM_SelectedChannel = fmCfg.selChn;
|
||||
gEeprom.FM_IsMrMode = fmCfg.isMrMode;
|
||||
@@ -294,51 +314,25 @@ gEeprom.FreqChannel[1] = IS_FREQ_CHANNEL(Data16[5]) ? Data16[5] : (FREQ_CHANNE
|
||||
PY25Q16_ReadBuffer(0x00A0A8 + 0x48, Data, 8);
|
||||
gEeprom.DTMF_CODE_PERSIST_TIME = (Data[0] < 101) ? Data[0] * 10 : 100;
|
||||
gEeprom.DTMF_CODE_INTERVAL_TIME = (Data[1] < 101) ? Data[1] * 10 : 100;
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
gEeprom.PERMIT_REMOTE_KILL = (Data[2] < 2) ? Data[2] : true;
|
||||
|
||||
// 0EE0..0EE7
|
||||
|
||||
PY25Q16_ReadBuffer(0x00A0F8, Data, sizeof(gEeprom.ANI_DTMF_ID));
|
||||
if (DTMF_ValidateCodes((char *)Data, sizeof(gEeprom.ANI_DTMF_ID))) {
|
||||
memcpy(gEeprom.ANI_DTMF_ID, Data, sizeof(gEeprom.ANI_DTMF_ID));
|
||||
} else {
|
||||
strcpy(gEeprom.ANI_DTMF_ID, "123");
|
||||
}
|
||||
|
||||
SETTINGS_LoadEepromDtmf(0x00A0F8, gEeprom.ANI_DTMF_ID, sizeof(gEeprom.ANI_DTMF_ID), "123");
|
||||
|
||||
// 0EE8..0EEF
|
||||
PY25Q16_ReadBuffer(0x00A0F8 + 0x8, Data, sizeof(gEeprom.KILL_CODE));
|
||||
if (DTMF_ValidateCodes((char *)Data, sizeof(gEeprom.KILL_CODE))) {
|
||||
memcpy(gEeprom.KILL_CODE, Data, sizeof(gEeprom.KILL_CODE));
|
||||
} else {
|
||||
strcpy(gEeprom.KILL_CODE, "ABCD9");
|
||||
}
|
||||
SETTINGS_LoadEepromDtmf(0x00A0F8 + 0x08, gEeprom.KILL_CODE, sizeof(gEeprom.KILL_CODE), "ABCD9");
|
||||
|
||||
// 0EF0..0EF7
|
||||
PY25Q16_ReadBuffer(0x00A0F8 + 0x10, Data, sizeof(gEeprom.REVIVE_CODE));
|
||||
if (DTMF_ValidateCodes((char *)Data, sizeof(gEeprom.REVIVE_CODE))) {
|
||||
memcpy(gEeprom.REVIVE_CODE, Data, sizeof(gEeprom.REVIVE_CODE));
|
||||
} else {
|
||||
strcpy(gEeprom.REVIVE_CODE, "9DCBA");
|
||||
}
|
||||
SETTINGS_LoadEepromDtmf(0x00A0F8 + 0x10, gEeprom.REVIVE_CODE, sizeof(gEeprom.REVIVE_CODE), "9DCBA");
|
||||
#endif
|
||||
|
||||
// 0EF8..0F07
|
||||
PY25Q16_ReadBuffer(0x00A0F8 + 0x18, Data, sizeof(gEeprom.DTMF_UP_CODE));
|
||||
if (DTMF_ValidateCodes((char *)Data, sizeof(gEeprom.DTMF_UP_CODE))) {
|
||||
memcpy(gEeprom.DTMF_UP_CODE, Data, sizeof(gEeprom.DTMF_UP_CODE));
|
||||
} else {
|
||||
strcpy(gEeprom.DTMF_UP_CODE, "12345");
|
||||
}
|
||||
SETTINGS_LoadEepromDtmf(0x00A0F8 + 0x18, gEeprom.DTMF_UP_CODE, sizeof(gEeprom.DTMF_UP_CODE), "12345");
|
||||
|
||||
// 0F08..0F17
|
||||
PY25Q16_ReadBuffer(0x00A0F8 + 0x28, Data, sizeof(gEeprom.DTMF_DOWN_CODE));
|
||||
if (DTMF_ValidateCodes((char *)Data, sizeof(gEeprom.DTMF_DOWN_CODE))) {
|
||||
memcpy(gEeprom.DTMF_DOWN_CODE, Data, sizeof(gEeprom.DTMF_DOWN_CODE));
|
||||
} else {
|
||||
strcpy(gEeprom.DTMF_DOWN_CODE, "54321");
|
||||
}
|
||||
SETTINGS_LoadEepromDtmf(0x00A0F8 + 0x28, gEeprom.DTMF_DOWN_CODE, sizeof(gEeprom.DTMF_DOWN_CODE), "54321");
|
||||
|
||||
// 0F18..0F1F
|
||||
PY25Q16_ReadBuffer(0x00A130, Data, 8);
|
||||
|
||||
@@ -41,4 +41,19 @@ const char* INPUTBOX_GetAscii()
|
||||
inputBoxAscii[i] = (c==10)? '-' : '0' + c;
|
||||
}
|
||||
return inputBoxAscii;
|
||||
}
|
||||
|
||||
const char* INPUTBOX_GetAsciiAlignRight() {
|
||||
int targetPos = 7;
|
||||
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
if (gInputBox[i] != 10) {
|
||||
inputBoxAscii[targetPos--] = '0' + gInputBox[i];
|
||||
}
|
||||
}
|
||||
|
||||
while(targetPos >= 0)
|
||||
inputBoxAscii[targetPos--] = '-';
|
||||
|
||||
return inputBoxAscii;
|
||||
}
|
||||
@@ -25,7 +25,9 @@ extern char gInputBox[8];
|
||||
extern uint8_t gInputBoxIndex;
|
||||
|
||||
void INPUTBOX_Append(const KEY_Code_t Digit);
|
||||
|
||||
const char* INPUTBOX_GetAscii();
|
||||
const char* INPUTBOX_GetAsciiAlignRight();
|
||||
|
||||
#endif
|
||||
|
||||
+474
-41
@@ -60,6 +60,30 @@ center_line_t center_line = CENTER_LINE_NONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_PROGRESS
|
||||
#define SCAN_PROGRESS_MR_CHANNEL_BYTES ((MR_CHANNELS_MAX + 7u) / 8u)
|
||||
|
||||
static bool gScanProgressSessionActive;
|
||||
static bool gScanProgressSessionIsMemory;
|
||||
static uint8_t gScanProgressSessionScanList;
|
||||
static uint32_t gScanProgressSessionRangeStart;
|
||||
static uint32_t gScanProgressSessionRangeStop;
|
||||
static uint32_t gScanProgressSessionStep;
|
||||
static uint16_t gScanProgressMemoryTotal;
|
||||
static uint8_t gScanProgressMemoryMap[SCAN_PROGRESS_MR_CHANNEL_BYTES];
|
||||
static uint8_t gScanProgressMemoryExcludeOrdinalMap[SCAN_PROGRESS_MR_CHANNEL_BYTES];
|
||||
static bool gScanProgressPrevResetVfosFlag;
|
||||
static bool gScanProgressForceRebuild;
|
||||
static uint16_t gScanProgressLastMemoryIndex;
|
||||
static uint8_t gScanProgressPriorityState;
|
||||
#define SCAN_PROGRESS_PRIORITY_LABEL_MASK 0x03u
|
||||
#define SCAN_PROGRESS_PRIORITY_SEEN_SHIFT 2
|
||||
#define SCAN_PROGRESS_PRIORITY_SEEN_MASK 0x1cu
|
||||
#define SCAN_PROGRESS_PRIORITY_HOLD_SHIFT 5
|
||||
#define SCAN_PROGRESS_PRIORITY_HOLD_MASK 0xe0u
|
||||
#define SCAN_PROGRESS_PRIORITY_HOLD_FRAMES 6
|
||||
#endif
|
||||
|
||||
const char *VfoStateStr[] = {
|
||||
[VFO_STATE_NORMAL]="",
|
||||
[VFO_STATE_BUSY]="BUSY",
|
||||
@@ -70,6 +94,420 @@ const char *VfoStateStr[] = {
|
||||
[VFO_STATE_VOLTAGE_HIGH]="VOLT HIGH"
|
||||
};
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_PROGRESS
|
||||
static void ScanProgress_ResetSession(void)
|
||||
{
|
||||
gScanProgressSessionActive = false;
|
||||
gScanProgressMemoryTotal = 0;
|
||||
gScanProgressPrevResetVfosFlag = false;
|
||||
gScanProgressForceRebuild = false;
|
||||
gScanProgressLastMemoryIndex = 0;
|
||||
gScanProgressPriorityState = 0;
|
||||
}
|
||||
|
||||
void UI_MAIN_NotifyScanProgressDataChanged(void)
|
||||
{
|
||||
gScanProgressForceRebuild = true;
|
||||
}
|
||||
|
||||
static inline void ScanProgress_SetBit(uint8_t *map, uint16_t ch)
|
||||
{
|
||||
map[ch >> 3] |= (uint8_t)(1u << (ch & 7));
|
||||
}
|
||||
|
||||
static inline bool ScanProgress_GetBit(const uint8_t *map, uint16_t ch)
|
||||
{
|
||||
return ((map[ch >> 3] >> (ch & 7)) & 1u) != 0;
|
||||
}
|
||||
|
||||
static uint8_t ScanProgress_GetActiveScanList(void)
|
||||
{
|
||||
const uint8_t max_scan_list = MR_CHANNELS_LIST + 1;
|
||||
uint8_t scan_list = gEeprom.SCAN_LIST_DEFAULT;
|
||||
|
||||
if (scan_list == 0 || scan_list > max_scan_list)
|
||||
scan_list = max_scan_list;
|
||||
|
||||
return scan_list;
|
||||
}
|
||||
|
||||
static bool ScanProgress_ChannelBelongsToList(uint16_t channel, const ChannelAttributes_t *att, uint8_t scan_list)
|
||||
{
|
||||
if (!IS_MR_CHANNEL(channel))
|
||||
return false;
|
||||
|
||||
if (att->band > BAND7_470MHz)
|
||||
return false;
|
||||
|
||||
if (scan_list > MR_CHANNELS_LIST && att->scanlist != 0)
|
||||
return true;
|
||||
|
||||
if (scan_list > 0 && att->scanlist == (MR_CHANNELS_LIST + 1))
|
||||
return true;
|
||||
|
||||
if (scan_list == 0 || scan_list != att->scanlist)
|
||||
return false;
|
||||
|
||||
if (gEeprom.SCAN_LIST_ENABLED) {
|
||||
const uint16_t priority1 = gEeprom.SCANLIST_PRIORITY_CH[0];
|
||||
const uint16_t priority2 = gEeprom.SCANLIST_PRIORITY_CH[1];
|
||||
if (priority1 == channel || priority2 == channel)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ScanProgress_RebuildMemoryMap(uint8_t scan_list)
|
||||
{
|
||||
uint16_t ordinal = 0;
|
||||
|
||||
memset(gScanProgressMemoryMap, 0, sizeof(gScanProgressMemoryMap));
|
||||
memset(gScanProgressMemoryExcludeOrdinalMap, 0, sizeof(gScanProgressMemoryExcludeOrdinalMap));
|
||||
gScanProgressMemoryTotal = 0;
|
||||
|
||||
for (uint16_t ch = MR_CHANNEL_FIRST; IS_MR_CHANNEL(ch); ch++) {
|
||||
const ChannelAttributes_t *att = MR_GetChannelAttributes(ch);
|
||||
|
||||
if (att == NULL || !ScanProgress_ChannelBelongsToList(ch, att, scan_list))
|
||||
continue;
|
||||
|
||||
ScanProgress_SetBit(gScanProgressMemoryMap, ch);
|
||||
ordinal++;
|
||||
|
||||
if (att->exclude) {
|
||||
ScanProgress_SetBit(gScanProgressMemoryExcludeOrdinalMap, (uint16_t)(ordinal - 1));
|
||||
}
|
||||
|
||||
gScanProgressMemoryTotal++;
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t ScanProgress_GetMemoryOrdinal(uint16_t channel)
|
||||
{
|
||||
uint16_t ordinal = 0;
|
||||
|
||||
for (uint16_t ch = MR_CHANNEL_FIRST; IS_MR_CHANNEL(ch); ch++) {
|
||||
if (!ScanProgress_GetBit(gScanProgressMemoryMap, ch))
|
||||
continue;
|
||||
|
||||
ordinal++;
|
||||
if (ch == channel)
|
||||
return ordinal;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool ScanProgress_IsForward(void)
|
||||
{
|
||||
return gScanStateDir != SCAN_REV;
|
||||
}
|
||||
|
||||
static bool ScanProgress_BucketHasExcludedOrdinal(uint32_t first_ordinal, uint32_t last_ordinal)
|
||||
{
|
||||
if (first_ordinal == 0)
|
||||
first_ordinal = 1;
|
||||
if (last_ordinal > gScanProgressMemoryTotal)
|
||||
last_ordinal = gScanProgressMemoryTotal;
|
||||
if (first_ordinal > last_ordinal)
|
||||
return false;
|
||||
|
||||
for (uint32_t ordinal = first_ordinal; ordinal <= last_ordinal; ordinal++) {
|
||||
if (ScanProgress_GetBit(gScanProgressMemoryExcludeOrdinalMap, (uint16_t)(ordinal - 1)))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ScanProgress_DrawGaugeLine(uint8_t line, uint32_t current_index, uint32_t total, uint8_t width, bool memory_mode, uint8_t extra_left_offset)
|
||||
{
|
||||
const bool forward = ScanProgress_IsForward();
|
||||
|
||||
const uint8_t gauge_left = (uint8_t)(width * 8 + 9 + extra_left_offset);
|
||||
const uint8_t gauge_right = 126;
|
||||
const uint8_t fill_start = gauge_left + 2;
|
||||
const uint8_t fill_end = gauge_right - 2;
|
||||
const uint8_t fill_cols = fill_end - fill_start + 1;
|
||||
uint32_t head_col;
|
||||
|
||||
if (total == 0)
|
||||
total = 1;
|
||||
if (current_index == 0)
|
||||
current_index = 1;
|
||||
else if (current_index > total)
|
||||
current_index = total;
|
||||
|
||||
head_col = (total <= 1) ? (fill_cols - 1) : ((current_index - 1) * (fill_cols - 1)) / (total - 1);
|
||||
if (head_col >= fill_cols)
|
||||
head_col = fill_cols - 1;
|
||||
|
||||
gFrameBuffer[line][gauge_left] = 0x0c;
|
||||
gFrameBuffer[line][gauge_left + 1] = 0x12;
|
||||
gFrameBuffer[line][gauge_right - 1] = 0x12;
|
||||
gFrameBuffer[line][gauge_right] = 0x0c;
|
||||
|
||||
for (uint8_t col = 0; col < fill_cols; col++) {
|
||||
const uint32_t first_ordinal = ((uint32_t)col * total) / fill_cols + 1;
|
||||
uint32_t last_ordinal = ((uint32_t)(col + 1) * total) / fill_cols;
|
||||
const bool processed = forward ? (col <= head_col) : (col >= head_col);
|
||||
bool excluded = false;
|
||||
uint8_t pixel = 0x21;
|
||||
|
||||
if (last_ordinal < first_ordinal)
|
||||
last_ordinal = first_ordinal;
|
||||
|
||||
if (memory_mode)
|
||||
excluded = ScanProgress_BucketHasExcludedOrdinal(first_ordinal, last_ordinal);
|
||||
|
||||
if (processed && !excluded) {
|
||||
pixel = 0x2d;
|
||||
} else if (excluded) {
|
||||
pixel = 0x21;
|
||||
}
|
||||
|
||||
gFrameBuffer[line][fill_start + col] = pixel;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t ScanProgress_DecimalDigits(uint32_t value)
|
||||
{
|
||||
uint8_t digits = 1;
|
||||
|
||||
while (value >= 10u) {
|
||||
value /= 10u;
|
||||
digits++;
|
||||
}
|
||||
|
||||
return digits;
|
||||
}
|
||||
|
||||
static void ScanProgress_FormatIndex(char *out, size_t out_size, uint32_t current_index, uint32_t total, uint8_t width)
|
||||
{
|
||||
snprintf(out, out_size, "%0*u/%u",
|
||||
width, (unsigned int)current_index,
|
||||
(unsigned int)total);
|
||||
}
|
||||
|
||||
static uint8_t ScanProgress_NextPriorityLabel(uint8_t current_label, uint8_t state_mask)
|
||||
{
|
||||
for (uint8_t i = 0; i < 3; i++) {
|
||||
current_label++;
|
||||
if (current_label > 2)
|
||||
current_label = 0;
|
||||
|
||||
if ((state_mask & (1u << current_label)) != 0)
|
||||
return current_label;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t ScanProgress_GetPriorityLabel(void)
|
||||
{
|
||||
return gScanProgressPriorityState & SCAN_PROGRESS_PRIORITY_LABEL_MASK;
|
||||
}
|
||||
|
||||
static uint8_t ScanProgress_GetPrioritySeenMask(void)
|
||||
{
|
||||
return (gScanProgressPriorityState & SCAN_PROGRESS_PRIORITY_SEEN_MASK) >> SCAN_PROGRESS_PRIORITY_SEEN_SHIFT;
|
||||
}
|
||||
|
||||
static uint8_t ScanProgress_GetPriorityHoldFrames(void)
|
||||
{
|
||||
return (gScanProgressPriorityState & SCAN_PROGRESS_PRIORITY_HOLD_MASK) >> SCAN_PROGRESS_PRIORITY_HOLD_SHIFT;
|
||||
}
|
||||
|
||||
static void ScanProgress_SetPriorityFields(uint8_t label, uint8_t seen_mask, uint8_t hold_frames)
|
||||
{
|
||||
gScanProgressPriorityState =
|
||||
(uint8_t)(label & SCAN_PROGRESS_PRIORITY_LABEL_MASK) |
|
||||
(uint8_t)((seen_mask << SCAN_PROGRESS_PRIORITY_SEEN_SHIFT) & SCAN_PROGRESS_PRIORITY_SEEN_MASK) |
|
||||
(uint8_t)((hold_frames << SCAN_PROGRESS_PRIORITY_HOLD_SHIFT) & SCAN_PROGRESS_PRIORITY_HOLD_MASK);
|
||||
}
|
||||
|
||||
static bool ScanProgress_BuildRangeIndex(uint32_t *current_index_out, uint32_t *total_out)
|
||||
{
|
||||
#ifdef ENABLE_SCAN_RANGES
|
||||
uint32_t step = gScanProgressSessionStep ? gScanProgressSessionStep : 1;
|
||||
uint32_t total = ((gScanProgressSessionRangeStop - gScanProgressSessionRangeStart) / step) + 1;
|
||||
uint32_t current_freq = gRxVfo->freq_config_RX.Frequency;
|
||||
uint32_t current_abs;
|
||||
|
||||
if (gScanProgressSessionRangeStart == 0 || gScanProgressSessionRangeStop < gScanProgressSessionRangeStart || current_index_out == NULL || total_out == NULL)
|
||||
return false;
|
||||
|
||||
if (total == 0)
|
||||
total = 1;
|
||||
|
||||
if (current_freq < gScanProgressSessionRangeStart)
|
||||
current_freq = gScanProgressSessionRangeStart;
|
||||
else if (current_freq > gScanProgressSessionRangeStop)
|
||||
current_freq = gScanProgressSessionRangeStop;
|
||||
|
||||
current_abs = ((current_freq - gScanProgressSessionRangeStart) / step) + 1;
|
||||
if (current_abs > total)
|
||||
current_abs = total;
|
||||
|
||||
*current_index_out = current_abs;
|
||||
*total_out = total;
|
||||
|
||||
return true;
|
||||
#else
|
||||
(void)current_index_out;
|
||||
(void)total_out;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool UI_DrawScanProgress(void)
|
||||
{
|
||||
bool show_memory = IS_MR_CHANNEL(gNextMrChannel);
|
||||
bool show_range = false;
|
||||
bool show_priority_label = false;
|
||||
uint8_t priority_now = 0;
|
||||
const char *priority_label = " ";
|
||||
char text[24];
|
||||
uint8_t line;
|
||||
uint32_t current_index = 1;
|
||||
uint32_t total = 1;
|
||||
|
||||
#ifdef ENABLE_SCAN_RANGES
|
||||
show_range = !show_memory && gScanRangeStart != 0;
|
||||
#endif
|
||||
|
||||
if (!show_memory && !show_range) {
|
||||
ScanProgress_ResetSession();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (show_memory) {
|
||||
const uint8_t scan_list = ScanProgress_GetActiveScanList();
|
||||
const bool reset_vfos_edge = gFlagResetVfos && !gScanProgressPrevResetVfosFlag;
|
||||
const bool force_rebuild = !gScanProgressSessionActive ||
|
||||
!gScanProgressSessionIsMemory ||
|
||||
gScanProgressSessionScanList != scan_list ||
|
||||
reset_vfos_edge ||
|
||||
gScanProgressForceRebuild;
|
||||
|
||||
gScanProgressPrevResetVfosFlag = gFlagResetVfos;
|
||||
|
||||
if (force_rebuild) {
|
||||
gScanProgressSessionActive = true;
|
||||
gScanProgressSessionIsMemory = true;
|
||||
gScanProgressSessionScanList = scan_list;
|
||||
ScanProgress_RebuildMemoryMap(scan_list);
|
||||
gScanProgressForceRebuild = false;
|
||||
gScanProgressLastMemoryIndex = 0;
|
||||
}
|
||||
|
||||
if (gScanProgressMemoryTotal == 0)
|
||||
return false;
|
||||
|
||||
show_priority_label = gEeprom.SCAN_LIST_ENABLED &&
|
||||
(gEeprom.SCANLIST_PRIORITY_CH[0] < MR_CHANNELS_MAX ||
|
||||
gEeprom.SCANLIST_PRIORITY_CH[1] < MR_CHANNELS_MAX);
|
||||
|
||||
if (show_priority_label) {
|
||||
if (gEeprom.SCANLIST_PRIORITY_CH[0] < MR_CHANNELS_MAX &&
|
||||
gRxVfo->CHANNEL_SAVE == gEeprom.SCANLIST_PRIORITY_CH[0])
|
||||
priority_now = 1;
|
||||
else if (gEeprom.SCANLIST_PRIORITY_CH[1] < MR_CHANNELS_MAX &&
|
||||
gRxVfo->CHANNEL_SAVE == gEeprom.SCANLIST_PRIORITY_CH[1])
|
||||
priority_now = 2;
|
||||
}
|
||||
|
||||
current_index = ScanProgress_GetMemoryOrdinal(gRxVfo->CHANNEL_SAVE);
|
||||
if (priority_now != 0 || current_index == 0) {
|
||||
// Keep the last known normal index during priority channel hops so
|
||||
// the gauge does not flicker between the list and priority slots.
|
||||
current_index = gScanProgressLastMemoryIndex ? gScanProgressLastMemoryIndex : 1;
|
||||
} else {
|
||||
gScanProgressLastMemoryIndex = current_index;
|
||||
}
|
||||
total = gScanProgressMemoryTotal;
|
||||
} else {
|
||||
#ifdef ENABLE_SCAN_RANGES
|
||||
const uint32_t range_start = gScanRangeStart;
|
||||
const uint32_t range_stop = gScanRangeStop;
|
||||
const uint32_t step = gRxVfo->StepFrequency;
|
||||
|
||||
if (!gScanProgressSessionActive ||
|
||||
gScanProgressSessionIsMemory ||
|
||||
gScanProgressSessionRangeStart != range_start ||
|
||||
gScanProgressSessionRangeStop != range_stop ||
|
||||
gScanProgressSessionStep != step)
|
||||
{
|
||||
gScanProgressSessionActive = true;
|
||||
gScanProgressSessionIsMemory = false;
|
||||
gScanProgressSessionRangeStart = range_start;
|
||||
gScanProgressSessionRangeStop = range_stop;
|
||||
gScanProgressSessionStep = step;
|
||||
}
|
||||
|
||||
if (!ScanProgress_BuildRangeIndex(¤t_index, &total))
|
||||
return false;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (show_priority_label) {
|
||||
uint8_t priority_state_label = ScanProgress_GetPriorityLabel();
|
||||
uint8_t priority_state_seen_mask = ScanProgress_GetPrioritySeenMask();
|
||||
uint8_t priority_state_hold_frames = ScanProgress_GetPriorityHoldFrames();
|
||||
|
||||
priority_state_seen_mask |= (uint8_t)(1u << priority_now);
|
||||
|
||||
if (priority_state_hold_frames > 0)
|
||||
priority_state_hold_frames--;
|
||||
|
||||
if (priority_state_hold_frames == 0) {
|
||||
priority_state_label = ScanProgress_NextPriorityLabel(priority_state_label,
|
||||
priority_state_seen_mask);
|
||||
priority_state_seen_mask = 0;
|
||||
priority_state_hold_frames = SCAN_PROGRESS_PRIORITY_HOLD_FRAMES;
|
||||
}
|
||||
|
||||
ScanProgress_SetPriorityFields(priority_state_label, priority_state_seen_mask, priority_state_hold_frames);
|
||||
|
||||
if (priority_state_label != 0)
|
||||
priority_label = (priority_state_label == 1) ? "P1" : "P2";
|
||||
} else {
|
||||
gScanProgressPriorityState = 0;
|
||||
}
|
||||
|
||||
const uint8_t width = ScanProgress_DecimalDigits(total);
|
||||
|
||||
ScanProgress_FormatIndex(text, sizeof(text), current_index, total, width);
|
||||
|
||||
uint8_t extra_offset = 0;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
line = isMainOnly() ? 5 : 3;
|
||||
const uint8_t text_y = isMainOnly() ? 41 : 25;
|
||||
GUI_DisplaySmallest(text, 2, text_y, false, true);
|
||||
|
||||
if (show_priority_label) {
|
||||
const uint8_t priority_x = (uint8_t)(width * 8 + 11);
|
||||
for (uint8_t x = 0; x < 7; x++)
|
||||
for (uint8_t y = 0; y < 6; y++)
|
||||
PutPixel(priority_x + x, text_y + y, false);
|
||||
GUI_DisplaySmallest(priority_label, priority_x, text_y, false, true);
|
||||
extra_offset = 11;
|
||||
}
|
||||
#else
|
||||
line = 3;
|
||||
UI_PrintStringSmallNormal(text, 2, 0, line);
|
||||
#endif
|
||||
|
||||
ScanProgress_DrawGaugeLine(line, current_index, total, width, show_memory, extra_offset);
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ----------------------------------------
|
||||
|
||||
static void DrawSmallPowerBars(uint8_t *p, unsigned int level)
|
||||
@@ -664,12 +1102,21 @@ void UI_MAIN_TimeSlice500ms(void)
|
||||
|
||||
// ----------------------------------------
|
||||
|
||||
static void UI_FormatFrequency(uint32_t freq, char *buffer) {
|
||||
sprintf(buffer, "%3u.%05u", freq / 100000, freq % 100000);
|
||||
}
|
||||
|
||||
void UI_DisplayMain(void)
|
||||
{
|
||||
char String[22];
|
||||
|
||||
center_line = CENTER_LINE_NONE;
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_PROGRESS
|
||||
if (gScanStateDir == SCAN_OFF)
|
||||
ScanProgress_ResetSession();
|
||||
#endif
|
||||
|
||||
// clear the screen
|
||||
UI_DisplayClear();
|
||||
|
||||
@@ -752,9 +1199,9 @@ void UI_DisplayMain(void)
|
||||
}
|
||||
|
||||
UI_PrintString("ScnRng", 5, 0, line + shift, 8);
|
||||
sprintf(String, "%3u.%05u", gScanRangeStart / 100000, gScanRangeStart % 100000);
|
||||
UI_FormatFrequency(gScanRangeStart, String);
|
||||
UI_PrintStringSmallNormal(String, 56, 0, line + shift);
|
||||
sprintf(String, "%3u.%05u", gScanRangeStop / 100000, gScanRangeStop % 100000);
|
||||
UI_FormatFrequency(gScanRangeStop, String);
|
||||
UI_PrintStringSmallNormal(String, 56, 0, line + shift + 1);
|
||||
|
||||
if (!isMainOnly())
|
||||
@@ -766,9 +1213,9 @@ void UI_DisplayMain(void)
|
||||
}
|
||||
#else
|
||||
UI_PrintString("ScnRng", 5, 0, line, 8);
|
||||
sprintf(String, "%3u.%05u", gScanRangeStart / 100000, gScanRangeStart % 100000);
|
||||
UI_FormatFrequency(gScanRangeStart, String);
|
||||
UI_PrintStringSmallNormal(String, 56, 0, line);
|
||||
sprintf(String, "%3u.%05u", gScanRangeStop / 100000, gScanRangeStop % 100000);
|
||||
UI_FormatFrequency(gScanRangeStop, String);
|
||||
UI_PrintStringSmallNormal(String, 56, 0, line + 1);
|
||||
continue;
|
||||
#endif
|
||||
@@ -948,7 +1395,7 @@ void UI_DisplayMain(void)
|
||||
if (!inputting || gScanStateDir != SCAN_OFF)
|
||||
sprintf(String, "%04u", gEeprom.ScreenChannel[vfo_num] + 1);
|
||||
else
|
||||
sprintf(String, "%.4s", INPUTBOX_GetAscii()); // show the input text
|
||||
sprintf(String, "%.4s", INPUTBOX_GetAsciiAlignRight() + 4); // show the input text
|
||||
|
||||
//if (gSetting_set_gui) {
|
||||
UI_PrintStringSmallNormalInverse(String, x, 0, line + 1);
|
||||
@@ -1067,29 +1514,22 @@ void UI_DisplayMain(void)
|
||||
|
||||
const ChannelAttributes_t* att = MR_GetChannelAttributes(gEeprom.ScreenChannel[vfo_num]);
|
||||
|
||||
const char *displayStr;
|
||||
uint8_t xStart = 113; // 3-char name aligned left
|
||||
|
||||
if(att->exclude == false)
|
||||
{
|
||||
// show the scan list assigment symbols
|
||||
const ChannelAttributes_t* att = MR_GetChannelAttributes(gEeprom.ScreenChannel[vfo_num]);
|
||||
|
||||
uint8_t countList = att->scanlist;
|
||||
if(countList > MR_CHANNELS_LIST + 1) {
|
||||
countList = 0;
|
||||
}
|
||||
|
||||
const char *displayStr;
|
||||
uint8_t xStart, xDisplay;
|
||||
|
||||
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
|
||||
@@ -1099,41 +1539,27 @@ void UI_DisplayMain(void)
|
||||
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 ? 1 : 33, false, true);
|
||||
|
||||
gFrameBuffer[line][xStart] ^= 0x3E;
|
||||
for (uint8_t x = xStart + 1; x < 127; x++) {
|
||||
gFrameBuffer[line][x] ^= 0x7F;
|
||||
}
|
||||
gFrameBuffer[line][127] ^= 0x3E;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *displayStr = "EX";
|
||||
|
||||
uint8_t xStart = 117;
|
||||
uint8_t xDisplay = 119;
|
||||
|
||||
GUI_DisplaySmallest(displayStr, xDisplay, line == 0 ? 1 : 33, false, true);
|
||||
|
||||
gFrameBuffer[line][xStart] ^= 0x3E;
|
||||
for (uint8_t x = xStart + 1; x < 127; x++) {
|
||||
gFrameBuffer[line][x] ^= 0x7F;
|
||||
}
|
||||
gFrameBuffer[line][127] ^= 0x3E;
|
||||
displayStr = "EX";
|
||||
xStart = 117;
|
||||
}
|
||||
|
||||
GUI_DisplaySmallest(displayStr, xStart + 2, line == 0 ? 1 : 33, false, true);
|
||||
|
||||
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
|
||||
{
|
||||
}
|
||||
@@ -1151,7 +1577,7 @@ void UI_DisplayMain(void)
|
||||
switch (gEeprom.CHANNEL_DISPLAY_MODE)
|
||||
{
|
||||
case MDF_FREQUENCY: // show the channel frequency
|
||||
sprintf(String, "%3u.%05u", frequency / 100000, frequency % 100000);
|
||||
UI_FormatFrequency(frequency, String);
|
||||
#ifdef ENABLE_BIG_FREQ
|
||||
if(frequency < _1GHz_in_KHz) {
|
||||
// show the remaining 2 small frequency digits
|
||||
@@ -1211,7 +1637,7 @@ void UI_DisplayMain(void)
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
if (isMainOnly())
|
||||
{
|
||||
sprintf(String, "%3u.%05u", frequency / 100000, frequency % 100000);
|
||||
UI_FormatFrequency(frequency, String);
|
||||
if(frequency < _1GHz_in_KHz) {
|
||||
// show the remaining 2 small frequency digits
|
||||
UI_PrintStringSmallNormal(String + 7, 113, 0, line + 4);
|
||||
@@ -1241,7 +1667,7 @@ void UI_DisplayMain(void)
|
||||
}
|
||||
else
|
||||
{ // frequency mode
|
||||
sprintf(String, "%3u.%05u", frequency / 100000, frequency % 100000);
|
||||
UI_FormatFrequency(frequency, String);
|
||||
|
||||
#ifdef ENABLE_BIG_FREQ
|
||||
if(frequency < _1GHz_in_KHz) {
|
||||
@@ -1610,6 +2036,13 @@ void UI_DisplayMain(void)
|
||||
|
||||
const bool rx = FUNCTION_IsRx();
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_PROGRESS
|
||||
if (gScanStateDir != SCAN_OFF && UI_DrawScanProgress())
|
||||
{
|
||||
center_line = CENTER_LINE_SCAN_PROGRESS;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_AUDIO_SCOPE
|
||||
if (gSetting_mic_bar && gCurrentFunction == FUNCTION_TRANSMIT) {
|
||||
// Reserve the line so no other element overwrites it.
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
enum center_line_t {
|
||||
CENTER_LINE_NONE = 0,
|
||||
CENTER_LINE_IN_USE,
|
||||
CENTER_LINE_SCAN_PROGRESS,
|
||||
CENTER_LINE_AUDIO_BAR,
|
||||
CENTER_LINE_AUDIO_SCOPE,
|
||||
CENTER_LINE_RSSI,
|
||||
@@ -49,6 +50,12 @@ void UI_DisplayAudioScope(void);
|
||||
void UI_MAIN_TimeSlice500ms(void);
|
||||
void UI_DisplayMain(void);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_SCAN_PROGRESS
|
||||
void UI_MAIN_NotifyScanProgressDataChanged(void);
|
||||
#else
|
||||
static inline void UI_MAIN_NotifyScanProgressDataChanged(void) {}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_AGC_SHOW_DATA
|
||||
void UI_MAIN_PrintAGC(bool force);
|
||||
#endif
|
||||
|
||||
+196
-40
@@ -27,6 +27,7 @@
|
||||
#include "../driver/eeprom.h"
|
||||
#include "../driver/st7565.h"
|
||||
#include "../external/printf/printf.h"
|
||||
#include "../font.h"
|
||||
#include "../frequencies.h"
|
||||
#include "../helper/battery.h"
|
||||
#include "../misc.h"
|
||||
@@ -40,6 +41,7 @@
|
||||
#include "inputbox.h"
|
||||
#include "menu.h"
|
||||
#include "ui.h"
|
||||
#include "welcome.h"
|
||||
|
||||
|
||||
const t_menu_item MenuList[] =
|
||||
@@ -523,6 +525,64 @@ int32_t gSubMenuSelection;
|
||||
char edit_original[17]; // a copy of the text before editing so that we can easily test for changes/difference
|
||||
char edit[17];
|
||||
int edit_index;
|
||||
bool edit_is_uppercase = false;
|
||||
|
||||
static void UI_MENU_DrawTopRightRoundedBadge(const char *text, const uint8_t line, const bool center_in_area, const uint8_t area_x1, const uint8_t area_x2)
|
||||
{
|
||||
const size_t length = strlen(text);
|
||||
const size_t char_pitch = ARRAY_SIZE(gFontSmall[0]) + 1u;
|
||||
const size_t text_width = length * char_pitch;
|
||||
const size_t capsule_span = text_width + 1u; // matches UI_PrintStringSmallNormalInverse x_end computation
|
||||
uint8_t text_x;
|
||||
|
||||
if (length == 0 || line == 0 || line >= FRAME_LINES) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (center_in_area && area_x2 > area_x1 + 2u) {
|
||||
const uint8_t min_x = area_x1 + 1u;
|
||||
uint8_t max_x;
|
||||
const uint8_t area_width = area_x2 - area_x1 + 1u;
|
||||
|
||||
if (capsule_span >= area_width) {
|
||||
text_x = min_x;
|
||||
} else {
|
||||
text_x = (uint8_t)(area_x1 + ((area_width - capsule_span) / 2u));
|
||||
}
|
||||
|
||||
if (area_x2 > capsule_span) {
|
||||
max_x = (uint8_t)(area_x2 - capsule_span);
|
||||
} else {
|
||||
max_x = min_x;
|
||||
}
|
||||
|
||||
if (max_x < min_x) {
|
||||
max_x = min_x;
|
||||
}
|
||||
if (text_x < min_x) {
|
||||
text_x = min_x;
|
||||
} else if (text_x > max_x) {
|
||||
text_x = max_x;
|
||||
}
|
||||
} else {
|
||||
if (capsule_span >= (LCD_WIDTH - 3u)) {
|
||||
text_x = 1u;
|
||||
} else {
|
||||
const uint8_t global_shift_right = 1u;
|
||||
const uint8_t base_text_x = (uint8_t)(LCD_WIDTH - capsule_span - 3u);
|
||||
const uint8_t max_text_x = (uint8_t)(LCD_WIDTH - capsule_span - 1u);
|
||||
const uint16_t shifted_x = (uint16_t)base_text_x + global_shift_right;
|
||||
|
||||
if (shifted_x > max_text_x) {
|
||||
text_x = max_text_x;
|
||||
} else {
|
||||
text_x = (uint8_t)shifted_x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UI_PrintStringSmallNormalInverse(text, text_x, 0, line);
|
||||
}
|
||||
|
||||
void UI_DisplayMenu(void)
|
||||
{
|
||||
@@ -531,6 +591,7 @@ void UI_DisplayMenu(void)
|
||||
const unsigned int menu_item_x2 = LCD_WIDTH - 1;
|
||||
unsigned int i;
|
||||
char String[64]; // bigger cuz we can now do multi-line in one string (use '\n' char)
|
||||
char top_right_badge[16];
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
char Contact[16];
|
||||
@@ -629,6 +690,7 @@ void UI_DisplayMenu(void)
|
||||
// **************
|
||||
|
||||
memset(String, 0, sizeof(String));
|
||||
memset(top_right_badge, 0, sizeof(top_right_badge));
|
||||
|
||||
bool already_printed = false;
|
||||
|
||||
@@ -865,7 +927,7 @@ void UI_DisplayMenu(void)
|
||||
{ // show the frequency so that the user knows the channels frequency
|
||||
const uint32_t frequency = SETTINGS_FetchChannelFrequency(gSubMenuSelection);
|
||||
sprintf(String, "%u.%05u", frequency / 100000, frequency % 100000);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 4, 8);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 5, 8);
|
||||
}
|
||||
|
||||
SETTINGS_FetchChannelName(String, gSubMenuSelection);
|
||||
@@ -899,15 +961,34 @@ void UI_DisplayMenu(void)
|
||||
{ // show the channel name being edited
|
||||
//UI_PrintString(edit, menu_item_x1, 0, 2, 8);
|
||||
UI_PrintString(edit, menu_item_x1, menu_item_x2, 2, 8);
|
||||
if (edit_index < 10)
|
||||
//UI_PrintString("^", menu_item_x1 + (8 * edit_index), 0, 4, 8); // show the cursor
|
||||
UI_PrintString("^", menu_item_x1 - 1 + (8 * edit_index),0, 4, 8); // show the cursor
|
||||
if (edit_index < 10) {
|
||||
// UI_PrintString("^", menu_item_x1 - 1 + (8 * edit_index),0, 4, 8); // show the cursor
|
||||
uint8_t x = menu_item_x1 - 1;
|
||||
for (uint8_t i = 0; i < 10; i++)
|
||||
{
|
||||
if (i != edit_index)
|
||||
{
|
||||
if (edit[i] != 'g' && edit[i] != 'j')
|
||||
{
|
||||
UI_DrawLineBuffer(gFrameBuffer, x, 29, x + 6, 29, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UI_DrawLineBuffer(gFrameBuffer, x + 2, 30, x + 4, 30, 1);
|
||||
UI_DrawPixelBuffer(gFrameBuffer, x + 3, 29, 1);
|
||||
}
|
||||
x += 8;
|
||||
}
|
||||
|
||||
UI_PrintStringSmallNormal(edit_is_uppercase ? "ABC" : "abc", 77, 0, 4);
|
||||
}
|
||||
}
|
||||
|
||||
if (!gAskForConfirmation)
|
||||
{ // show the frequency so that the user knows the channels frequency
|
||||
sprintf(String, "%u.%05u", frequency / 100000, frequency % 100000);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 4 + (gIsInSubMenu && edit_index >= 0), 8);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 5, 8);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1049,18 +1130,98 @@ void UI_DisplayMenu(void)
|
||||
strcpy(String, gSubMenu_ROGER[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
case MENU_VOL:
|
||||
case MENU_VOL: {
|
||||
// SysInf is paginated. Pages appear in this order, only when their
|
||||
// feature flag is enabled:
|
||||
// 0 -> identity
|
||||
// next -> Build date/time (ENABLE_FEAT_F4HWN)
|
||||
// next -> Battery (ENABLE_FEAT_F4HWN)
|
||||
// next -> Flash / SRAM usage (ENABLE_FEAT_F4HWN_MEM)
|
||||
// next, +1 -> CODE / WIKI QR codes (ENABLE_FEAT_F4HWN_QRCODE)
|
||||
// In non-F4HWN builds, page 0 keeps the old battery-voltage display.
|
||||
const uint8_t page = (uint8_t)gSubMenuSelection;
|
||||
uint8_t p = 0;
|
||||
|
||||
if (page == p++) {
|
||||
// Page 0: firmware identity.
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
sprintf(String, "%s\n%s",
|
||||
AUTHOR_STRING_2,
|
||||
VERSION_STRING_2
|
||||
);
|
||||
sprintf(String, "%s\n%s", AUTHOR_STRING_2, VERSION_STRING_2);
|
||||
UI_PrintStringSmallNormal(Edition, menu_item_x1 - 1, menu_item_x2, 6);
|
||||
#else
|
||||
sprintf(String, "%u.%02uV\n%u%%",
|
||||
gBatteryVoltageAverage / 100, gBatteryVoltageAverage % 100,
|
||||
BATTERY_VoltsToPercent(gBatteryVoltageAverage));
|
||||
sprintf(String, "%u.%02uV\n%u%%",
|
||||
gBatteryVoltageAverage / 100, gBatteryVoltageAverage % 100,
|
||||
BATTERY_VoltsToPercent(gBatteryVoltageAverage));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
if (page == p++) {
|
||||
strcpy(top_right_badge, "BUILD");
|
||||
UI_PrintStringSmallNormal(BuildDate, menu_item_x1 - 1, menu_item_x2, 3);
|
||||
UI_PrintStringSmallNormal(BuildTime, menu_item_x1 - 1, menu_item_x2, 4);
|
||||
UI_PrintStringSmallNormal(BuildCommit, menu_item_x1 - 1, menu_item_x2, 6);
|
||||
|
||||
already_printed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (page == p++) {
|
||||
char val[16];
|
||||
|
||||
strcpy(top_right_badge, "BATTERY");
|
||||
|
||||
sprintf(val, "%u.%02uV %u%%",
|
||||
gBatteryVoltageAverage / 100, gBatteryVoltageAverage % 100,
|
||||
BATTERY_VoltsToPercent(gBatteryVoltageAverage));
|
||||
UI_PrintStringSmallNormal(val, menu_item_x1 - 1, menu_item_x2, 3);
|
||||
|
||||
UI_PrintStringSmallNormal(gSubMenu_BATTYP[gEeprom.BATTERY_TYPE], menu_item_x1 - 1, menu_item_x2, 5);
|
||||
|
||||
already_printed = true;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_MEM
|
||||
if (page == p++) {
|
||||
uint16_t flash_pct = 0;
|
||||
uint16_t ram_pct = 0;
|
||||
UI_GetMemPercents(&flash_pct, &ram_pct);
|
||||
|
||||
char val[16];
|
||||
|
||||
// MEMORY title capsule centered in right zone, fb line 1.
|
||||
strcpy(top_right_badge, "MEMORY");
|
||||
|
||||
// Flash + SRAM values stacked below, normal small font, with a fb-line of breathing space.
|
||||
sprintf(val, "FLASH %u.%u%%",
|
||||
(unsigned)(flash_pct / 100), (unsigned)((flash_pct / 10) % 10));
|
||||
UI_PrintStringSmallNormal(val, menu_item_x1 - 1, menu_item_x2, 3);
|
||||
|
||||
sprintf(val, "SRAM %u.%u%%",
|
||||
(unsigned)(ram_pct / 100), (unsigned)((ram_pct / 10) % 10));
|
||||
UI_PrintStringSmallNormal(val, menu_item_x1 - 1, menu_item_x2, 5);
|
||||
|
||||
already_printed = true;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef ENABLE_FEAT_F4HWN_QRCODE
|
||||
// Right zone: x=49..127 (79 px). QR centered at x=72..104.
|
||||
// Capsule label above QR (small-font Inverse style at fb line 1).
|
||||
if (page == p || page == p + 1) {
|
||||
const bool is_wiki = (page == (p + 1));
|
||||
|
||||
strcpy(top_right_badge, is_wiki ? "WIKI" : "CODE");
|
||||
UI_DrawQRCode(is_wiki, 72, 28);
|
||||
|
||||
already_printed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
p += 2;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
case MENU_RESET:
|
||||
strcpy(String, gSubMenu_RESET[gSubMenuSelection]);
|
||||
@@ -1191,15 +1352,15 @@ void UI_DisplayMenu(void)
|
||||
case MENU_SET_AUD:
|
||||
if(gTxVfo->Modulation == MODULATION_AM) {
|
||||
strcpy(String, gSubMenu_SET_AUD_AM[gSubMenuSelection]);
|
||||
UI_PrintStringSmallNormal("AM", 114, 0, 0);
|
||||
strcpy(top_right_badge, "AM");
|
||||
}
|
||||
else if (gTxVfo->Modulation == MODULATION_USB) {
|
||||
strcpy(String, "USB");
|
||||
UI_PrintStringSmallNormal("USB", 108, 0, 0);
|
||||
strcpy(top_right_badge, "USB");
|
||||
}
|
||||
else {
|
||||
strcpy(String, gSubMenu_SET_AUD_FM[gSubMenuSelection]);
|
||||
UI_PrintStringSmallNormal("FM", 114, 0, 0);
|
||||
strcpy(top_right_badge, "FM");
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
@@ -1227,11 +1388,7 @@ void UI_DisplayMenu(void)
|
||||
//#endif
|
||||
}
|
||||
// gEeprom.VOLUME_GAIN = gSubMenuSelection;
|
||||
BK4819_WriteRegister(BK4819_REG_48,
|
||||
(11u << 12) | // ??? .. 0 ~ 15, doesn't seem to make any difference
|
||||
( 0u << 10) | // AF Rx Gain-1
|
||||
(gEeprom.VOLUME_GAIN << 4) | // AF Rx Gain-2
|
||||
(gEeprom.DAC_GAIN << 0)); // AF DAC Gain (after Gain-1 and Gain-2)
|
||||
BK4819_SetRxAudioGain();
|
||||
break;
|
||||
#endif
|
||||
|
||||
@@ -1288,23 +1445,6 @@ void UI_DisplayMenu(void)
|
||||
|
||||
y = (small ? 3 : 2) - (lines / 2);
|
||||
|
||||
// only for SysInf
|
||||
if(UI_MENU_GetCurrentMenuId() == MENU_VOL)
|
||||
{
|
||||
sprintf(edit, "%u.%02uV %u%%",
|
||||
gBatteryVoltageAverage / 100, gBatteryVoltageAverage % 100,
|
||||
BATTERY_VoltsToPercent(gBatteryVoltageAverage)
|
||||
);
|
||||
|
||||
UI_PrintStringSmallNormal(edit, 54, 127, 1);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
UI_PrintStringSmallNormal(Edition, 54, 127, 6);
|
||||
#endif
|
||||
|
||||
y = 2;
|
||||
}
|
||||
|
||||
// draw the text lines
|
||||
for (i = 0; i < len && lines > 0; lines--)
|
||||
{
|
||||
@@ -1351,8 +1491,24 @@ void UI_DisplayMenu(void)
|
||||
|| UI_MENU_GetCurrentMenuId() == MENU_D_LIST
|
||||
#endif
|
||||
) {
|
||||
sprintf(String, "%03d", gSubMenuSelection);
|
||||
UI_PrintStringSmallNormal(String, 107, 0, 0);
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_R_CTCS ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_T_CTCS) {
|
||||
const uint8_t approved_index =
|
||||
(gSubMenuSelection > 0) ? DCS_GetCtcssApprovedIndex(gSubMenuSelection - 1) : 0xFF;
|
||||
|
||||
if (gSubMenuSelection == 0)
|
||||
sprintf(top_right_badge, "00/00");
|
||||
else if (approved_index != 0xFF)
|
||||
sprintf(top_right_badge, "%02u/%02u", (unsigned)gSubMenuSelection, approved_index + 1);
|
||||
else
|
||||
sprintf(top_right_badge, "%02u/--", (unsigned)gSubMenuSelection);
|
||||
} else {
|
||||
sprintf(top_right_badge, "%03d", gSubMenuSelection);
|
||||
}
|
||||
}
|
||||
|
||||
if (top_right_badge[0] != '\0') {
|
||||
UI_MENU_DrawTopRightRoundedBadge(top_right_badge, 1, true, menu_item_x1, menu_item_x2);
|
||||
}
|
||||
|
||||
if ((UI_MENU_GetCurrentMenuId() == MENU_RESET ||
|
||||
|
||||
@@ -233,6 +233,7 @@ extern int32_t gSubMenuSelection;
|
||||
extern char edit_original[17];
|
||||
extern char edit[17];
|
||||
extern int edit_index;
|
||||
extern bool edit_is_uppercase;
|
||||
|
||||
void UI_DisplayMenu(void);
|
||||
int UI_MENU_GetCurrentMenuId();
|
||||
|
||||
+32
-27
@@ -155,42 +155,47 @@ void UI_DisplayStatus()
|
||||
|
||||
if(!SCANNER_IsScanning()) {
|
||||
#ifdef ENABLE_FEAT_F4HWN_RX_TX_TIMER
|
||||
if(gCurrentFunction == FUNCTION_TRANSMIT && gSetting_set_tmr == true)
|
||||
{
|
||||
convertTime(line, 0);
|
||||
}
|
||||
else if(FUNCTION_IsRx() && gSetting_set_tmr == true)
|
||||
{
|
||||
convertTime(line, 1);
|
||||
bool isTransmit = gCurrentFunction == FUNCTION_TRANSMIT;
|
||||
if (gSetting_set_tmr && (isTransmit || FUNCTION_IsRx())) {
|
||||
convertTime(line, !isTransmit);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if(!gAirCopyBootMode) {
|
||||
const void *src = NULL; // Pointer to the font/bitmap to copy
|
||||
size_t sSize = 0; // Size of the font/bitmap
|
||||
uint8_t sOff = 2; // Offset relative to the reference position
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
if(gEeprom.MENU_LOCK == true) {
|
||||
memcpy(line + x + 2, gFontRO, sizeof(gFontRO));
|
||||
}
|
||||
else
|
||||
if (gEeprom.MENU_LOCK) {
|
||||
src = gFontRO;
|
||||
sSize = sizeof(gFontRO);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#endif
|
||||
uint8_t dw = (gEeprom.DUAL_WATCH != DUAL_WATCH_OFF) + (gEeprom.CROSS_BAND_RX_TX != CROSS_BAND_OFF) * 2;
|
||||
if(dw == 1 || dw == 3) { // DWR - dual watch + respond
|
||||
if(gDualWatchActive)
|
||||
memcpy(line + x + (dw==1?0:2), gFontDWR, sizeof(gFontDWR) - (dw==1?0:5));
|
||||
else
|
||||
memcpy(line + x + 3, gFontHold, sizeof(gFontHold));
|
||||
uint8_t xb = (gEeprom.CROSS_BAND_RX_TX != CROSS_BAND_OFF);
|
||||
|
||||
if (gEeprom.DUAL_WATCH != DUAL_WATCH_OFF) {
|
||||
if (gDualWatchActive) { // DWR - dual watch + respond
|
||||
src = gFontDWR;
|
||||
sOff = xb ? 2 : 0;
|
||||
sSize = sizeof(gFontDWR) - (xb ? 5 : 0);
|
||||
} else {
|
||||
src = gFontHold;
|
||||
sOff = 3;
|
||||
sSize = sizeof(gFontHold);
|
||||
}
|
||||
} else {
|
||||
src = xb ? gFontXB : gFontMO; // XB - crossband
|
||||
sSize = xb ? sizeof(gFontXB) : sizeof(gFontMO); // MO - main only
|
||||
}
|
||||
else if(dw == 2) { // XB - crossband
|
||||
memcpy(line + x + 2, gFontXB, sizeof(gFontXB));
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(line + x + 2, gFontMO, sizeof(gFontMO));
|
||||
}
|
||||
#ifdef ENABLE_FEAT_F4HWN_RESCUE_OPS
|
||||
}
|
||||
#endif
|
||||
|
||||
// Perform the memcpy if a source was selected
|
||||
if (src) {
|
||||
memcpy(line + x + sOff, src, sSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+100
-1
@@ -33,6 +33,52 @@
|
||||
#include "screenshot.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_QRCODE
|
||||
// QR code (version 4, 33x33 modules, EC level L) encoding:
|
||||
// https://github.com/armel/uv-k1-k5v3-firmware-custom
|
||||
// Stored in framebuffer column-major format: 5 fb-lines x 33 columns.
|
||||
// Each byte packs 8 vertical pixels (bit 0 = top). Last fb-line uses
|
||||
// only bit 0 (row 32); bits 1..7 are always 0.
|
||||
//
|
||||
// static const uint8_t BITMAP_QR_GitHub[5][33] = {
|
||||
// { 0x7F, 0x41, 0x5D, 0x5D, 0x5D, 0x41, 0x7F, 0x00, 0x6A, 0xB8, 0xCB, 0xA0, 0x6D, 0x07, 0xCB, 0x1F, 0xD2, 0x18, 0x59, 0x15, 0x79, 0x86, 0xCE, 0x15, 0x43, 0x00, 0x7F, 0x41, 0x5D, 0x5D, 0x5D, 0x41, 0x7F },
|
||||
// { 0x87, 0xA3, 0x69, 0xC3, 0x19, 0x0E, 0x55, 0x1F, 0x43, 0x11, 0x16, 0xC1, 0x5A, 0x0E, 0x96, 0x3E, 0xA5, 0x15, 0x06, 0x2A, 0xFE, 0xCE, 0xCA, 0x3A, 0x70, 0xD9, 0xEA, 0xF5, 0x5C, 0x15, 0x8A, 0x67, 0x22 },
|
||||
// { 0xE0, 0x0B, 0x1D, 0x28, 0xF5, 0x87, 0x55, 0xEB, 0xA8, 0x11, 0xA3, 0xC1, 0x5A, 0x0E, 0x96, 0x3E, 0xA5, 0x15, 0x84, 0x2B, 0x72, 0xE8, 0xE9, 0x23, 0x11, 0xCD, 0xE6, 0xC1, 0x91, 0xE6, 0x88, 0x77, 0x22 },
|
||||
// { 0xFD, 0x04, 0x75, 0x75, 0x75, 0x04, 0xFD, 0x01, 0xF7, 0xE0, 0xD6, 0xC1, 0x5A, 0x0E, 0x96, 0x3E, 0xA5, 0x37, 0x22, 0x2B, 0xEA, 0xAA, 0xA7, 0x8D, 0x5F, 0x31, 0x55, 0xB1, 0x3F, 0xCE, 0xCA, 0x2C, 0x2B },
|
||||
// { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00 }
|
||||
// };
|
||||
|
||||
static const uint8_t BITMAP_QR_GitHub_Compressed[137] = {
|
||||
0x7F, 0x41, 0x5D, 0x5D, 0x5D, 0x41, 0x7F, 0x00, 0x6A, 0xB8, 0xCB, 0xA0, 0x6D, 0x07, 0xCB, 0x1F, 0xD2, 0x18, 0x59, 0x15, 0x79, 0x86, 0xCE, 0x15, 0x43, 0x00, 0x7F, 0x41, 0x5D, 0x5D, 0x5D, 0x41, 0x7F,
|
||||
0x87, 0xA3, 0x69, 0xC3, 0x19, 0x0E, 0x55, 0x1F, 0x43, 0x11, 0x16, 0xC1, 0x5A, 0x0E, 0x96, 0x3E, 0xA5, 0x15, 0x06, 0x2A, 0xFE, 0xCE, 0xCA, 0x3A, 0x70, 0xD9, 0xEA, 0xF5, 0x5C, 0x15, 0x8A, 0x67, 0x22,
|
||||
0xE0, 0x0B, 0x1D, 0x28, 0xF5, 0x87, 0x55, 0xEB, 0xA8, 0x11, 0xA3, 0xC1, 0x5A, 0x0E, 0x96, 0x3E, 0xA5, 0x15, 0x84, 0x2B, 0x72, 0xE8, 0xE9, 0x23, 0x11, 0xCD, 0xE6, 0xC1, 0x91, 0xE6, 0x88, 0x77, 0x22,
|
||||
0xFD, 0x04, 0x75, 0x75, 0x75, 0x04, 0xFD, 0x01, 0xF7, 0xE0, 0xD6, 0xC1, 0x5A, 0x0E, 0x96, 0x3E, 0xA5, 0x37, 0x22, 0x2B, 0xEA, 0xAA, 0xA7, 0x8D, 0x5F, 0x31, 0x55, 0xB1, 0x3F, 0xCE, 0xCA, 0x2C, 0x2B,
|
||||
0x7F, 0x09, 0x8C, 0xA3, 0x00
|
||||
};
|
||||
|
||||
|
||||
// QR code (version 4, 33x33 modules, EC level L) encoding:
|
||||
// https://github.com/armel/uv-k1-k5v3-firmware-custom/wiki
|
||||
// Stored in framebuffer column-major format: 5 fb-lines x 33 columns.
|
||||
// Last fb-line uses only bit 0 (row 32); bits 1..7 are always 0.
|
||||
//
|
||||
// static const uint8_t BITMAP_QR_GitHub_Wiki[5][33] = {
|
||||
// { 0x7F, 0x41, 0x5D, 0x5D, 0x5D, 0x41, 0x7F, 0x00, 0x6A, 0x0F, 0x74, 0x0E, 0xD2, 0xB0, 0x74, 0xB1, 0x6D, 0x18, 0x59, 0x15, 0x79, 0x86, 0xCE, 0x15, 0x43, 0x00, 0x7F, 0x41, 0x5D, 0x5D, 0x5D, 0x41, 0x7F },
|
||||
// { 0xCD, 0x5D, 0x83, 0x65, 0xE7, 0xC6, 0x55, 0xBD, 0x6B, 0x3F, 0xA9, 0x1C, 0xA5, 0xE0, 0x69, 0xE3, 0x4A, 0x15, 0x06, 0x2A, 0xFE, 0xCE, 0xCA, 0x3A, 0x70, 0xD9, 0xEA, 0xF5, 0x5C, 0x15, 0x8A, 0x67, 0x22 },
|
||||
// { 0xFF, 0x25, 0xAE, 0xB6, 0x30, 0xF8, 0x55, 0xDD, 0x07, 0xB6, 0xC2, 0x1C, 0xA5, 0xE0, 0x69, 0xC4, 0x66, 0x15, 0x84, 0x2B, 0x72, 0xE8, 0xE9, 0x23, 0x11, 0xCD, 0xE6, 0xC1, 0x91, 0xE6, 0x88, 0x77, 0x22 },
|
||||
// { 0xFD, 0x04, 0x74, 0x74, 0x74, 0x05, 0xFD, 0x01, 0xF7, 0xAE, 0x81, 0x1C, 0xA5, 0xE0, 0x69, 0x54, 0xFE, 0x37, 0x22, 0x2B, 0xEA, 0xAA, 0xA7, 0x8D, 0x5F, 0x31, 0x55, 0xB1, 0x3F, 0xCE, 0xCA, 0x24, 0x33 },
|
||||
// { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00 }
|
||||
// };
|
||||
|
||||
static const uint8_t BITMAP_QR_GitHub_Wiki_Compressed[137] = {
|
||||
0x7F, 0x41, 0x5D, 0x5D, 0x5D, 0x41, 0x7F, 0x00, 0x6A, 0x0F, 0x74, 0x0E, 0xD2, 0xB0, 0x74, 0xB1, 0x6D, 0x18, 0x59, 0x15, 0x79, 0x86, 0xCE, 0x15, 0x43, 0x00, 0x7F, 0x41, 0x5D, 0x5D, 0x5D, 0x41, 0x7F,
|
||||
0xCD, 0x5D, 0x83, 0x65, 0xE7, 0xC6, 0x55, 0xBD, 0x6B, 0x3F, 0xA9, 0x1C, 0xA5, 0xE0, 0x69, 0xE3, 0x4A, 0x15, 0x06, 0x2A, 0xFE, 0xCE, 0xCA, 0x3A, 0x70, 0xD9, 0xEA, 0xF5, 0x5C, 0x15, 0x8A, 0x67, 0x22,
|
||||
0xFF, 0x25, 0xAE, 0xB6, 0x30, 0xF8, 0x55, 0xDD, 0x07, 0xB6, 0xC2, 0x1C, 0xA5, 0xE0, 0x69, 0xC4, 0x66, 0x15, 0x84, 0x2B, 0x72, 0xE8, 0xE9, 0x23, 0x11, 0xCD, 0xE6, 0xC1, 0x91, 0xE6, 0x88, 0x77, 0x22,
|
||||
0xFD, 0x04, 0x74, 0x74, 0x74, 0x05, 0xFD, 0x01, 0xF7, 0xAE, 0x81, 0x1C, 0xA5, 0xE0, 0x69, 0x54, 0xFE, 0x37, 0x22, 0x2B, 0xEA, 0xAA, 0xA7, 0x8D, 0x5F, 0x31, 0x55, 0xB1, 0x3F, 0xCE, 0xCA, 0x24, 0x33,
|
||||
0x7F, 0x53, 0x8E, 0xA3, 0x00
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_MEM
|
||||
// Linker symbols (provided by the linker script)
|
||||
extern uint8_t _sdata; // Start of .data in RAM
|
||||
@@ -88,6 +134,57 @@ static inline uint16_t pct_x100(uint32_t used, uint32_t total)
|
||||
{
|
||||
return (uint16_t)((used * 10000u) / total); // 7559 => 75.59%
|
||||
}
|
||||
|
||||
void UI_GetMemPercents(uint16_t *flash_pct_x100, uint16_t *ram_pct_x100)
|
||||
{
|
||||
uint32_t ram_used = 0;
|
||||
uint32_t flash_used = 0;
|
||||
build_usage(&ram_used, &flash_used);
|
||||
if (flash_pct_x100) *flash_pct_x100 = pct_x100(flash_used, FLASH_SIZE_BYTES);
|
||||
if (ram_pct_x100) *ram_pct_x100 = pct_x100(ram_used, RAM_SIZE_BYTES);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_QRCODE
|
||||
// Set a single pixel at LCD-physical (x, y). y=0..7 maps to gStatusLine,
|
||||
// y=8..63 maps to gFrameBuffer (line = (y-8)/8, bit = (y-8)%8).
|
||||
static void QR_SetPixel(uint8_t x, uint8_t y)
|
||||
{
|
||||
if (x >= 128 || y >= 64) return;
|
||||
if (y < 8) {
|
||||
gStatusLine[x] |= (uint8_t)(1u << y);
|
||||
} else {
|
||||
const uint8_t fb_y = (uint8_t)(y - 8u);
|
||||
gFrameBuffer[fb_y >> 3][x] |= (uint8_t)(1u << (fb_y & 7u));
|
||||
}
|
||||
}
|
||||
|
||||
// Render a square QR bitmap stored in framebuffer column-major format
|
||||
// (size cols × ceil(size/8) fb-lines, row-major in memory).
|
||||
static void QR_Draw(const uint8_t *bitmap, uint8_t size, uint8_t origin_x, uint8_t origin_y)
|
||||
{
|
||||
for (uint8_t qy = 0; qy < size; qy++) {
|
||||
for (uint8_t qx = 0; qx < size; qx++) {
|
||||
// const uint16_t idx = (uint16_t)(qy >> 3) * (uint16_t)size + (uint16_t)qx;
|
||||
// if ((bitmap[idx] >> (qy & 7u)) & 1u) {
|
||||
if (qy < 32 ?
|
||||
((bitmap[(uint16_t)(qy >> 3) * (uint16_t)size + (uint16_t)qx] >> (qy & 7u)) & 1u) :
|
||||
((bitmap[132 + (qx >> 3)] >> (qx & 7u)) & 1u)) {
|
||||
QR_SetPixel((uint8_t)(origin_x + qx),
|
||||
(uint8_t)(origin_y + qy));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UI_DrawQRCode(bool wiki, uint8_t origin_x, uint8_t origin_y)
|
||||
{
|
||||
// QR_Draw(wiki ? (const uint8_t *)BITMAP_QR_GitHub_Wiki
|
||||
// : (const uint8_t *)BITMAP_QR_GitHub,
|
||||
QR_Draw(wiki ? (const uint8_t *)BITMAP_QR_GitHub_Wiki_Compressed
|
||||
: (const uint8_t *)BITMAP_QR_GitHub_Compressed,
|
||||
33, origin_x, origin_y);
|
||||
}
|
||||
#endif
|
||||
|
||||
void UI_DisplayReleaseKeys(void)
|
||||
@@ -193,6 +290,7 @@ void UI_DisplayWelcome(void)
|
||||
gFrameBuffer[4][108] ^= 0x7F;
|
||||
UI_DrawLineBuffer(gFrameBuffer, 109, 35, 127, 35, 1);
|
||||
|
||||
/*
|
||||
#ifdef ENABLE_FEAT_F4HWN_MEM
|
||||
uint32_t ram_used = 0;
|
||||
uint32_t flash_used = 0;
|
||||
@@ -210,6 +308,7 @@ void UI_DisplayWelcome(void)
|
||||
GUI_DisplaySmallest(WelcomeString3, 5, 1, true, true);
|
||||
ST7565_BlitStatusLine();
|
||||
#endif
|
||||
*/
|
||||
|
||||
sprintf(WelcomeString3, "%s Edition", Edition);
|
||||
UI_PrintStringSmallNormal(WelcomeString3, 0, 127, 6);
|
||||
@@ -225,4 +324,4 @@ void UI_DisplayWelcome(void)
|
||||
SCREENSHOT_Update(true);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,22 @@
|
||||
#ifndef UI_WELCOME_H
|
||||
#define UI_WELCOME_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void UI_DisplayReleaseKeys(void);
|
||||
void UI_DisplayWelcome(void);
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_QRCODE
|
||||
// Draw the embedded GitHub QR code (V4, 33x33) at (origin_x, origin_y).
|
||||
// `wiki` selects the wiki URL bitmap, otherwise the repo URL bitmap.
|
||||
void UI_DrawQRCode(bool wiki, uint8_t origin_x, uint8_t origin_y);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN_MEM
|
||||
// Compute current FLASH and SRAM usage as percentages × 100 (e.g. 7559 = 75.59%).
|
||||
void UI_GetMemPercents(uint16_t *flash_pct_x100, uint16_t *ram_pct_x100);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
const char Version[] = AUTHOR_STRING_2 " " VERSION_STRING_2;
|
||||
const char Edition[] = EDITION_STRING;
|
||||
const char BuildDate[] = __DATE__;
|
||||
const char BuildTime[] = __TIME__;
|
||||
const char BuildCommit[] = BUILD_COMMIT;
|
||||
#else
|
||||
const char Version[] = AUTHOR_STRING VER;
|
||||
#endif
|
||||
|
||||
@@ -24,4 +24,7 @@ extern const char UART_Version[];
|
||||
|
||||
#ifdef ENABLE_FEAT_F4HWN
|
||||
extern const char Edition[];
|
||||
extern const char BuildDate[];
|
||||
extern const char BuildTime[];
|
||||
extern const char BuildCommit[];
|
||||
#endif
|
||||
@@ -19,6 +19,21 @@ message("Build type: " ${CMAKE_BUILD_TYPE})
|
||||
|
||||
enable_language(C ASM)
|
||||
|
||||
find_package(Git QUIET)
|
||||
set(BUILD_COMMIT "unknown")
|
||||
if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" rev-parse --short HEAD
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE BUILD_COMMIT
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
)
|
||||
if(NOT BUILD_COMMIT)
|
||||
set(BUILD_COMMIT "unknown")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_FEAT_F4HWN)
|
||||
if(NOT AUTHOR_STRING_1)
|
||||
set(AUTHOR_STRING_1 "EGZUMER")
|
||||
|
||||
+6
-1
@@ -66,12 +66,14 @@
|
||||
"ENABLE_FEAT_F4HWN_GMRS_FRS_MURS": false,
|
||||
"ENABLE_FEAT_F4HWN_CA": true,
|
||||
"ENABLE_FEAT_F4HWN_DEBUG": false,
|
||||
"ENABLE_FEAT_F4HWN_QRCODE": false,
|
||||
"ENABLE_FEAT_F4HWN_MEM": false,
|
||||
"ENABLE_FEAT_F4HWN_SCAN_PROGRESS": false,
|
||||
"ENABLE_AGC_SHOW_DATA": false,
|
||||
"ENABLE_UART_RW_BK_REGS": false,
|
||||
"ENABLE_SWD": false,
|
||||
"VERSION_STRING_1": "v0.22",
|
||||
"VERSION_STRING_2": "v5.3.1"
|
||||
"VERSION_STRING_2": "v5.4.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -191,6 +193,9 @@
|
||||
"ENABLE_FEAT_F4HWN_VOL": true,
|
||||
"ENABLE_FEAT_F4HWN_AUDIO": true,
|
||||
"ENABLE_FEAT_F4HWN_AUDIO_SCOPE": true,
|
||||
"ENABLE_FEAT_F4HWN_SCAN_PROGRESS": true,
|
||||
"ENABLE_FEAT_F4HWN_QRCODE": true,
|
||||
"ENABLE_FEAT_F4HWN_MEM": true,
|
||||
"ENABLE_SWD": true,
|
||||
"EDITION_STRING": "Fusion",
|
||||
"TARGET": "f4hwn.fusion"
|
||||
|
||||
@@ -31,9 +31,9 @@ Anyway, have fun.
|
||||
> _FR - CE FIRMWARE N'A PAS DE VÉRITABLE CERVEAU. VEUILLEZ UTILISER LE VÔTRE. Utilisez ce firmware à vos risques et périls. Il n'y a absolument aucune garantie qu'il fonctionnera d'une manière ou d'une autre sur votre (vos) radio(s), il peut même bousiller votre (vos) radio(s), dans ce cas, vous devrez acheter une autre radio. Quoi qu'il en soit, amusez-vous bien._
|
||||
|
||||
> [!NOTE]
|
||||
> EN - About Chirp, as many others firmwares, you need to use a dedicated driver available on [this repository](https://github.com/armel/uv-k5-chirp-driver).
|
||||
> EN - About CHIRP, as with many other firmwares, you need to use a dedicated driver. The matching CHIRP driver is now bundled with each release of this repository, so you can download the firmware and its driver together from the [Releases page](https://github.com/armel/uv-k1-k5v3-firmware-custom/releases).
|
||||
>
|
||||
> _FR - A propos de Chirp, comme beaucoup d'autres firmwares, vous devez utiliser un pilote dédié disponible sur [ce dépôt](https://github.com/armel/uv-k5-chirp-driver)._
|
||||
> _FR - A propos de CHIRP, comme pour beaucoup d'autres firmwares, vous devez utiliser un pilote dédié. Le driver CHIRP correspondant est désormais fourni avec chaque release de ce dépôt, ce qui permet de récupérer ensemble le firmware et son pilote depuis la page des [Releases](https://github.com/armel/uv-k1-k5v3-firmware-custom/releases)._
|
||||
|
||||
> [!CAUTION]
|
||||
> EN - I recommend to backup your calibration data with [uvtools2](https://armel.github.io/uvtools2/) just after flashing this firmware. It's a good reflex to have.
|
||||
@@ -42,7 +42,7 @@ Anyway, have fun.
|
||||
|
||||
# Donations
|
||||
|
||||
Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Olivier 14RC206, Frédéric F4ESO, Stéphane F5LGW (2 times), Jorge Ornelas (4 times), Laurent F4AXK, Christophe Morel, Clayton W0LED, Pierre Antoine F6FWB, Jean-Claude 14FRS3306, Thierry F4GVO, Eric F1NOU, PricelessToolkit, Ady M6NYJ, Tom McGovern (4 times), Joseph Roth, Pierre-Yves Colin, Frank DJ7FG, Marcel Testaz, Brian Frobisher, Yannick F4JFO, Paolo Bussola, Dirk DL8DF, Levente Szőke (2 times), Bernard-Michel Herrera, Jérôme Saintespes, Paul Davies, RS (3 times), Johan F4WAT, Robert Wörle, Rafael Sundorf, Paul Harker, Peter Fintl, Pascal F4ICR (2 times), Mike DL2MF (3 times), Eric KI1C (2 times), Phil G0ELM, Jérôme Lambert, Eliot Vedel, Alfonso EA7KDF, Jean-François F1EVM, Robert DC1RDB (2 times), Ian KE2CHJ, Daryl VK3AWA, Roberto Brunelli, Robert Boardman, Stephen Oliver, Nicolas F4INE, William Bruno, Daniel OK2VLK, Tayler Chew, Peter DL7RFP, Philippe Kopp, Rune LA6YMA, Jeremy Luna, Steef Wagenaar (2 times), Zhuo BG7SGA, Jamie M0JLB, Antoine LIBERT, Vince K0DKR, Julia DF7JA, Ken 2E0UMK, 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 🙏🏻
|
||||
Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Olivier 14RC206, Frédéric F4ESO, Stéphane F5LGW (2 times), Jorge Ornelas (4 times), Laurent F4AXK, Christophe Morel, Clayton W0LED, Pierre Antoine F6FWB, Jean-Claude 14FRS3306, Thierry F4GVO, Eric F1NOU, PricelessToolkit, Ady M6NYJ, Tom McGovern (4 times), Joseph Roth, Pierre-Yves Colin, Frank DJ7FG, Marcel Testaz, Brian Frobisher, Yannick F4JFO, Paolo Bussola, Dirk DL8DF, Levente Szőke (2 times), Bernard-Michel Herrera, Jérôme Saintespes, Paul Davies, RS (3 times), Johan F4WAT, Robert Wörle, Rafael Sundorf, Paul Harker, Peter Fintl, Pascal F4ICR (2 times), Mike DL2MF (3 times), Eric KI1C (2 times), Phil G0ELM, Jérôme Lambert, Eliot Vedel, Alfonso EA7KDF, Jean-François F1EVM, Robert DC1RDB (2 times), Ian KE2CHJ, Daryl VK3AWA, Roberto Brunelli, Robert Boardman, Stephen Oliver, Nicolas F4INE, William Bruno, Daniel OK2VLK, Tayler Chew, Peter DL7RFP, Philippe Kopp, Rune LA6YMA, Jeremy Luna, Steef Wagenaar (2 times), Zhuo BG7SGA, Jamie M0JLB, Antoine LIBERT, Vince K0DKR, Julia DF7JA, Ken 2E0UMK, Victor TI2SYS, Tobi DG9LAY, Deaglan K4DFQ, Catherine PALMER, Brian WA6JFK, Stéphane Hintzy, Roger F1HCN and Marcin Kusaj for their [donations](https://www.paypal.com/paypalme/F4HWN). That’s so kind of them. Thanks so much 🙏🏻
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@@ -57,12 +57,15 @@ Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Ol
|
||||
|
||||
## Main features and improvements from F4HWN:
|
||||
|
||||
* several firmware versions:
|
||||
* Bandscope (with spectrum analyzer made by Fagci),
|
||||
* Broadcast (with commercial FM radio support),
|
||||
* Basic (with spectrum analyzer and commercial FM radios support, but without certain functions such as Vox, Aircopy, etc.),
|
||||
* RescueOps (specifically designed for first responders: firefighters, sea rescue, mountain rescue),
|
||||
* Game (with a small breakout game),
|
||||
* Fusion is now the reference edition of the project:
|
||||
* all-in-one firmware for UV-K1 and UV-K5 V3,
|
||||
* spectrum analyzer made by Fagci,
|
||||
* commercial FM radio support,
|
||||
* Vox and Aircopy support,
|
||||
* screenshots and K5Viewer support,
|
||||
* advanced RX audio profiles and Audio Scope,
|
||||
* first-responder oriented options,
|
||||
* small breakout game,
|
||||
* improve default power settings level:
|
||||
* Low1 to Low5 (<~20mW, ~125mW, ~250mW, ~500mW, ~1W),
|
||||
* Mid ~2W,
|
||||
@@ -92,6 +95,8 @@ Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Ol
|
||||
* improve keyboard frequency input,
|
||||
* add percent and gauge to Air Copy,
|
||||
* improve audio bar,
|
||||
* add backlight fading,
|
||||
* add Audio Scope on TX,
|
||||
* and more...
|
||||
* new menu entries and changes:
|
||||
* add SetPwr menu to set User power (<20mW, 125mW, 250mW, 500mW, 1W, 2W or 5W),
|
||||
@@ -103,10 +108,12 @@ Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Ol
|
||||
* add SetMet menu to set s-meter style (Classic or Tiny),
|
||||
* add SetLck menu to set what is locked (Keys or Keys + PTT),
|
||||
* add SetGUI menu to set font size on the VFO baseline (Classic or Tiny),
|
||||
* add SetRxA menu to select RX audio profiles,
|
||||
* add TXLock menu to open TX on channel,
|
||||
* add SetTmr menu to set RX and TX timers (Off or On),
|
||||
* add SetOff menu to set the delay before the transceiver goes into deep sleep (Off or 1 minute to 2 hours),
|
||||
* add SetNFM menu to set Narrow width (12.5kHz or 6.25kHz),
|
||||
* add SetVol menu to adjust RX audio volume,
|
||||
* rename BatVol menu (52/63) to SysInf, which displays the firmware version in addition to the battery status,
|
||||
* improve PonMsg menu,
|
||||
* improve BackLt menu,
|
||||
@@ -116,6 +123,7 @@ Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Ol
|
||||
* add HAM CA F Lock band (for Canadian zone),
|
||||
* add PMR 446 F Lock band,
|
||||
* add FRS/GMRS/MURS F Lock band,
|
||||
* add SetNav hidden menu to select the navigation layout according to the radio model,
|
||||
* remove blink and SOS functionality,
|
||||
* remove AM Fix menu (AM Fix is ENABLED by default),
|
||||
* add support of 3500mAh battery,
|
||||
@@ -124,18 +132,16 @@ Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Ol
|
||||
* change font and bitmaps,
|
||||
* move USB icon to left of battery information,
|
||||
* add RX and TX timers,
|
||||
* improve lists and scan lists options:
|
||||
* add new list 3,
|
||||
* add new list 0 (channel without list...),
|
||||
* add new scan lists options,
|
||||
* scan list 0 (all channels without list),
|
||||
* scan list 1,
|
||||
* scan list 2,
|
||||
* scan list 3,
|
||||
* scan lists [1, 2, 3],
|
||||
* scan all (all channels with or without list),
|
||||
* add scan list shortcuts,
|
||||
* add resume mode on startup (scan, bandscope and broadcast FM),
|
||||
* improve channel scanning:
|
||||
* support up to 24 scan lists,
|
||||
* each memory channel can be assigned to `OFF`, to one list (`01` to `24`), or to `ALL`,
|
||||
* `ALL` scans every channel except those set to `OFF`,
|
||||
* named scan lists are shown in the UI and status bar when available,
|
||||
* if the selected scan list is empty or invalid, the firmware automatically jumps to the next valid one,
|
||||
* add resume mode on startup (scan, spectrum analyzer and FM radio),
|
||||
* improve VFO persistence and restore behavior:
|
||||
* save the Squelch level adjusted with F + UP or F + DOWN,
|
||||
* restore the full VFO state on long press of EXIT,
|
||||
* new actions:
|
||||
* RX MODE,
|
||||
* MAIN ONLY,
|
||||
@@ -143,23 +149,25 @@ Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Ol
|
||||
* WIDE NARROW,
|
||||
* 1750Hz,
|
||||
* MUTE,
|
||||
* POWER HIGH (RescueOps),
|
||||
* REMOVE OFFSET (RescueOps),
|
||||
* POWER HIGH,
|
||||
* REMOVE OFFSET,
|
||||
* new key combinations:
|
||||
* add the F + UP or F + DOWN key combination to dynamically change the Squelch level,
|
||||
* add the F + F1 or F + F2 key combination to dynamically change the Step,
|
||||
* add F + 8 to quickly switch backlight between BLMin and BLMax on demand (this bypass BackLt strategy),
|
||||
* add F + 9 to return to BackLt strategy,
|
||||
* add long press on MENU, in * SCAN mode, to temporarily exclude a memory channel,
|
||||
* add short press on [0, 1, 2, 3, 4 or 5], in * SCAN mode, to dynamically change scan list.
|
||||
* add long press on MENU, in * SCAN mode, to exclude the current memory channel,
|
||||
* add direct scan list selection while scanning with two digits (`00` = `ALL`, `01` to `24` = scan list).
|
||||
* many fix:
|
||||
* squelch,
|
||||
* s-meter,
|
||||
* DTMF overlaying,
|
||||
* scan list 2 ignored,
|
||||
* scan range limit,
|
||||
* clean display on startup,
|
||||
* no more PWM noise,
|
||||
* K5Viewer/serial key handling,
|
||||
* spectrum freeze on USB-C unplug,
|
||||
* Audio Scope behavior in OnePush mode and after DTMF/1750,
|
||||
* and more...
|
||||
* enabled AIR COPY
|
||||
* disabled ENABLE_DTMF_CALLING,
|
||||
@@ -167,7 +175,7 @@ Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Ol
|
||||
* remove 200Tx, 350Tx and 500Tx,
|
||||
* unlock TX on all bands needs only to be repeat 3 times,
|
||||
* code refactoring and many memory optimization,
|
||||
* displays the live screen of the Quansheng K5 on your computer via a USB-to-Serial cable,
|
||||
* stream the live screen of the Quansheng K5 to K5Viewer and capture screenshots over a USB-to-Serial cable,
|
||||
* and more...
|
||||
|
||||
## Main features from Egzumer:
|
||||
@@ -203,7 +211,7 @@ Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Ol
|
||||
|
||||
## Manual
|
||||
|
||||
Up to date manual is available in the [Wiki section](https://github.com/armel/uv-k5-firmware-custom/wiki)
|
||||
Up to date manual is available in the [Wiki section](https://github.com/armel/uv-k1-k5v3-firmware-custom/wiki)
|
||||
|
||||
## Radio performance
|
||||
|
||||
@@ -211,21 +219,21 @@ Please note that the Quansheng UV-Kx radios are not professional quality transce
|
||||
performance is strictly limited. The RX front end has no track-tuned band pass filtering
|
||||
at all, and so are wide band/wide open to any and all signals over a large frequency range.
|
||||
|
||||
Using the radio in high intensity RF environments will most likely make reception anything but
|
||||
easy (AM mode will suffer far more than FM ever will), the receiver simply doesn't have a
|
||||
great dynamic range, which results in distorted AM audio with stronger RX'ed signals.
|
||||
There is nothing more anyone can do in firmware/software to improve that, once the RX gain
|
||||
adjustment I do (AM fix) reaches the hardwares limit, your AM RX audio will be all but
|
||||
non-existent (just like Quansheng's firmware).
|
||||
On the other hand, FM RX audio will/should be fine.
|
||||
Using the radio in high intensity RF environments will most likely make reception difficult,
|
||||
especially in AM mode. The receiver simply does not have a great dynamic range, so stronger
|
||||
signals can easily cause distortion, desensitization and poor AM audio.
|
||||
This is fundamentally a hardware limitation: firmware can improve behavior at the margins, but
|
||||
it cannot overcome the front-end design of the radio.
|
||||
In practice, AM reception will degrade first and most severely, while FM reception is generally
|
||||
more tolerant and should remain more usable.
|
||||
|
||||
But, they are nice toys for the price, fun to play with.
|
||||
|
||||
## Compiling and Building from Docker
|
||||
|
||||
This project provides a Docker-based build system to compile all firmware editions for the UV-K1 and UV-K5 V3. Everything is handled through the `compile-with-docker.sh` helper script.
|
||||
This project provides a Docker-based build system to compile the Fusion firmware for the UV-K1 and UV-K5 V3. Everything is handled through the `compile-with-docker.sh` helper script.
|
||||
|
||||
All build outputs are generated inside the `build/<Preset>` directory, according to the CMake presets defined in `CMakePresets.json`.
|
||||
The documented build output is generated inside `build/Fusion`, using the CMake presets defined in `CMakePresets.json`.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
@@ -238,40 +246,25 @@ The script `compile-with-docker.sh` performs the following actions:
|
||||
|
||||
1. Builds the Docker image (`uvk1-uvk5v3`) if it does not already exist.
|
||||
2. Removes any previous `build` directory to ensure a clean configuration.
|
||||
3. Runs CMake using the selected preset inside the Docker container.
|
||||
4. Builds the firmware and outputs `.elf`, `.bin` and `.hex` files for the chosen edition.
|
||||
3. Runs CMake using the `Fusion` preset inside the Docker container.
|
||||
4. Builds the firmware and outputs `.elf`, `.bin` and `.hex` files.
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
./compile-with-docker.sh <Preset> [extra CMake options]
|
||||
./compile-with-docker.sh Fusion [extra CMake options]
|
||||
```
|
||||
|
||||
### Available Presets
|
||||
### Documented Preset
|
||||
|
||||
- **Custom**
|
||||
- **Bandscope**
|
||||
- **Broadcast**
|
||||
- **Basic**
|
||||
- **RescueOps**
|
||||
- **Game**
|
||||
- **Fusion**
|
||||
- **All** (builds all editions sequentially)
|
||||
|
||||
### Examples
|
||||
|
||||
Build a single edition:
|
||||
Build Fusion:
|
||||
|
||||
```bash
|
||||
./compile-with-docker.sh Fusion
|
||||
./compile-with-docker.sh Bandscope
|
||||
./compile-with-docker.sh Broadcast
|
||||
```
|
||||
|
||||
Build everything:
|
||||
|
||||
```bash
|
||||
./compile-with-docker.sh All
|
||||
```
|
||||
|
||||
### Passing Additional CMake Options
|
||||
@@ -282,15 +275,14 @@ These are forwarded directly to `cmake --preset` inside the container.
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
./compile-with-docker.sh Bandscope -DENABLE_SPECTRUM=ON
|
||||
./compile-with-docker.sh Broadcast -DENABLE_FEAT_F4HWN_GAME=ON -DENABLE_NOAA=ON
|
||||
./compile-with-docker.sh Bandscope -DSQL_TONE=600
|
||||
./compile-with-docker.sh Fusion -DENABLE_SPECTRUM=ON
|
||||
./compile-with-docker.sh Fusion -DENABLE_FEAT_F4HWN_GAME=ON -DENABLE_NOAA=ON
|
||||
./compile-with-docker.sh Fusion -DSQL_TONE=600
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
- The first run may take a few minutes while Docker builds the base image.
|
||||
- Running with `All` will build every firmware variant in sequence.
|
||||
- Each build runs inside Docker, so your host environment remains clean.
|
||||
|
||||
## Flashing the Firmware with UVTools2
|
||||
@@ -342,6 +334,7 @@ Click `Restore Calibration Data` and wait until the process fully completes.
|
||||
Many thanks to various people:
|
||||
|
||||
* [Muzkr](https://github.com/muzkr)
|
||||
* [Mrkusypl](https://github.com/mrkusypl)
|
||||
* [Andrej](https://github.com/Tunas1337)
|
||||
* [Egzumer](https://github.com/egzumer)
|
||||
* [OneOfEleven](https://github.com/OneOfEleven)
|
||||
|
||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (c) 2026
|
||||
#
|
||||
# Licensed under the MIT License (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at the root of this repository.
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
from binascii import crc_hqx
|
||||
from itertools import cycle
|
||||
|
||||
|
||||
# Quansheng stock updater pack format:
|
||||
# - raw firmware split at 0x2000
|
||||
# - 16-byte ASCII version field inserted there
|
||||
# - XOR obfuscation with the table below
|
||||
# - CRC16/XMODEM appended at the end
|
||||
OBFUSCATION = bytes(
|
||||
[
|
||||
0x47, 0x22, 0xC0, 0x52, 0x5D, 0x57, 0x48, 0x94, 0xB1, 0x60, 0x60, 0xDB, 0x6F, 0xE3, 0x4C, 0x7C,
|
||||
0xD8, 0x4A, 0xD6, 0x8B, 0x30, 0xEC, 0x25, 0xE0, 0x4C, 0xD9, 0x00, 0x7F, 0xBF, 0xE3, 0x54, 0x05,
|
||||
0xE9, 0x3A, 0x97, 0x6B, 0xB0, 0x6E, 0x0C, 0xFB, 0xB1, 0x1A, 0xE2, 0xC9, 0xC1, 0x56, 0x47, 0xE9,
|
||||
0xBA, 0xF1, 0x42, 0xB6, 0x67, 0x5F, 0x0F, 0x96, 0xF7, 0xC9, 0x3C, 0x84, 0x1B, 0x26, 0xE1, 0x4E,
|
||||
0x3B, 0x6F, 0x66, 0xE6, 0xA0, 0x6A, 0xB0, 0xBF, 0xC6, 0xA5, 0x70, 0x3A, 0xBA, 0x18, 0x9E, 0x27,
|
||||
0x1A, 0x53, 0x5B, 0x71, 0xB1, 0x94, 0x1E, 0x18, 0xF2, 0xD6, 0x81, 0x02, 0x22, 0xFD, 0x5A, 0x28,
|
||||
0x91, 0xDB, 0xBA, 0x5D, 0x64, 0xC6, 0xFE, 0x86, 0x83, 0x9C, 0x50, 0x1C, 0x73, 0x03, 0x11, 0xD6,
|
||||
0xAF, 0x30, 0xF4, 0x2C, 0x77, 0xB2, 0x7D, 0xBB, 0x3F, 0x29, 0x28, 0x57, 0x22, 0xD6, 0x92, 0x8B,
|
||||
]
|
||||
)
|
||||
|
||||
VERSION_OFFSET = 0x2000
|
||||
VERSION_SIZE = 16
|
||||
CRC_SIZE = 2
|
||||
|
||||
|
||||
def _vector_table_looks_valid(image: bytes) -> bool:
|
||||
if len(image) < 8:
|
||||
return False
|
||||
|
||||
stack_pointer = int.from_bytes(image[0:4], "little")
|
||||
reset_vector = int.from_bytes(image[4:8], "little")
|
||||
|
||||
return (stack_pointer & 0xFFF00000) == 0x20000000 and (reset_vector & 0xFFF00000) == 0x08000000
|
||||
|
||||
|
||||
def is_raw_image(image: bytes) -> bool:
|
||||
return _vector_table_looks_valid(image)
|
||||
|
||||
|
||||
def has_valid_vendor_crc(image: bytes) -> bool:
|
||||
if len(image) < CRC_SIZE:
|
||||
return False
|
||||
|
||||
expected_crc = crc_hqx(image[:-CRC_SIZE], 0)
|
||||
actual_crc = int.from_bytes(image[-CRC_SIZE:], "little")
|
||||
|
||||
return expected_crc == actual_crc
|
||||
|
||||
|
||||
def is_packed_image(image: bytes) -> bool:
|
||||
if len(image) <= VERSION_OFFSET + VERSION_SIZE + CRC_SIZE:
|
||||
return False
|
||||
|
||||
if is_raw_image(image):
|
||||
return False
|
||||
|
||||
return has_valid_vendor_crc(image)
|
||||
|
||||
|
||||
def decode_packed_image(image: bytes) -> tuple[bytes, str]:
|
||||
if not is_packed_image(image):
|
||||
raise ValueError("not a packed Quansheng firmware image")
|
||||
|
||||
decoded = bytes(a ^ b for a, b in zip(image[:-CRC_SIZE], cycle(OBFUSCATION)))
|
||||
version_bytes = decoded[VERSION_OFFSET:VERSION_OFFSET + VERSION_SIZE]
|
||||
version = version_bytes.split(b"\x00", 1)[0].decode("ascii", errors="replace")
|
||||
|
||||
raw = decoded[:VERSION_OFFSET] + decoded[VERSION_OFFSET + VERSION_SIZE:]
|
||||
if not is_raw_image(raw):
|
||||
raise ValueError("decoded firmware does not look like a raw MCU image")
|
||||
|
||||
return raw, version
|
||||
+80
-8
@@ -17,14 +17,11 @@
|
||||
|
||||
|
||||
import argparse
|
||||
import serial
|
||||
import signal
|
||||
from time import sleep
|
||||
import os
|
||||
|
||||
import _prog as pp
|
||||
import _dump as dd
|
||||
import _restore as rr
|
||||
import _fwcodec as fc
|
||||
|
||||
|
||||
def load_image(file: str) -> bytes:
|
||||
@@ -42,7 +39,9 @@ def load_image(file: str) -> bytes:
|
||||
return a
|
||||
|
||||
|
||||
def main_dump(args, ser: serial.Serial):
|
||||
def main_dump(args, ser):
|
||||
|
||||
import _dump as dd
|
||||
|
||||
dump_file: str = args.file
|
||||
|
||||
@@ -73,7 +72,10 @@ def main_dump(args, ser: serial.Serial):
|
||||
sleep(0)
|
||||
|
||||
|
||||
def main_restore(args, ser: serial.Serial):
|
||||
def main_restore(args, ser):
|
||||
|
||||
import _dump as dd
|
||||
import _restore as rr
|
||||
|
||||
dump_file: str = args.file
|
||||
|
||||
@@ -105,7 +107,9 @@ def main_restore(args, ser: serial.Serial):
|
||||
sleep(0)
|
||||
|
||||
|
||||
def main_flash(args, ser: serial.Serial):
|
||||
def main_flash(args, ser):
|
||||
|
||||
import _prog as pp
|
||||
|
||||
bl_ver: str = args.bl_ver
|
||||
fw_file: str = args.file
|
||||
@@ -139,6 +143,56 @@ def main_flash(args, ser: serial.Serial):
|
||||
sleep(0)
|
||||
|
||||
|
||||
def get_raw_output_path(file: str) -> str:
|
||||
|
||||
root, ext = os.path.splitext(file)
|
||||
if ext:
|
||||
file_name = "{}.raw{}".format(os.path.basename(root), ext)
|
||||
candidate = "{}.raw{}".format(root, ext)
|
||||
else:
|
||||
file_name = os.path.basename(file) + ".raw.bin"
|
||||
candidate = file + ".raw.bin"
|
||||
|
||||
out_dir = os.path.dirname(candidate) or "."
|
||||
if os.access(out_dir, os.W_OK):
|
||||
return candidate
|
||||
|
||||
return os.path.join(os.getcwd(), file_name)
|
||||
|
||||
|
||||
def main_decode(args):
|
||||
|
||||
fw_file: str = args.file
|
||||
out_file: str = args.output or get_raw_output_path(fw_file)
|
||||
|
||||
try:
|
||||
fw_image = load_image(fw_file)
|
||||
if 0 == len(fw_image):
|
||||
print("Invalid firmware image: {}: empty file".format(fw_file))
|
||||
return
|
||||
except Exception as e:
|
||||
print("Cannot load firmware image '{}': {}".format(fw_file, e))
|
||||
return
|
||||
|
||||
if fc.is_raw_image(fw_image):
|
||||
print("Firmware image already looks raw")
|
||||
raw_image = fw_image
|
||||
version = None
|
||||
else:
|
||||
try:
|
||||
raw_image, version = fc.decode_packed_image(fw_image)
|
||||
except Exception as e:
|
||||
print("Cannot decode firmware image '{}': {}".format(fw_file, e))
|
||||
return
|
||||
|
||||
with open(out_file, "wb") as fd:
|
||||
fd.write(raw_image)
|
||||
|
||||
print("Raw firmware image written: {}, size = {}".format(out_file, len(raw_image)))
|
||||
if version:
|
||||
print("Packed version field: {}".format(version))
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
# Usage:
|
||||
@@ -146,6 +200,7 @@ def main():
|
||||
# serialtool.py .. flash [--bl-ver <ver>] <file>
|
||||
# serialtool.py .. dump {--config | --calib [| --all]} file
|
||||
# serialtool.py .. restore {--config | --calib [| --all]} file
|
||||
# serialtool.py decode <packed.bin> [raw.bin]
|
||||
ap = argparse.ArgumentParser(description="UV-K5 V2 serial tool")
|
||||
|
||||
# TODO: have to add option to each of subcommands ??
|
||||
@@ -198,14 +253,31 @@ def main():
|
||||
)
|
||||
ap_restore.add_argument("file", help="input dump file")
|
||||
|
||||
ap_decode = sp.add_parser(
|
||||
"decode", help="decode a packed Quansheng stock firmware into a raw image"
|
||||
)
|
||||
ap_decode.add_argument("file", help="input firmware image file")
|
||||
ap_decode.add_argument(
|
||||
"output",
|
||||
nargs="?",
|
||||
help="output raw firmware image file (default: <input>.raw.bin)",
|
||||
)
|
||||
|
||||
args = ap.parse_args()
|
||||
port: str = args.port
|
||||
sub_name: str = args.subcommand
|
||||
|
||||
print(ap.description)
|
||||
# print("Press Ctrl-C to quit")
|
||||
|
||||
if "decode" == sub_name:
|
||||
main_decode(args)
|
||||
return
|
||||
|
||||
port: str = args.port
|
||||
|
||||
try:
|
||||
import serial
|
||||
|
||||
ser = serial.Serial(port, baudrate=38400, timeout=0.0001, write_timeout=None)
|
||||
except Exception as e:
|
||||
print("Cannot open port '{}': {}".format(port, e))
|
||||
|
||||
Reference in new issue
Block a user