From 2638d803c0928ab0a0ef0c0f6db05a2b7ea2edf2 Mon Sep 17 00:00:00 2001 From: OneOfEleven Date: Thu, 2 Nov 2023 10:00:51 +0000 Subject: [PATCH] fix dual watch bug + other stuff --- Makefile | 19 +- am_fix.c | 8 +- app/action.c | 40 +-- app/aircopy.c | 2 + app/app.c | 323 ++++++++++--------- app/dtmf.c | 51 +-- app/fm.c | 120 ++++--- app/fm.h | 1 - app/generic.c | 10 +- app/main.c | 134 ++++---- app/menu.c | 265 ++++++++------- app/search.c | 12 +- app/uart.c | 39 +-- audio.c | 22 +- bitmaps.c | 25 +- bitmaps.h | 12 +- board.c | 505 ----------------------------- board.h | 9 - driver/backlight.c | 12 +- driver/backlight.h | 2 +- driver/bk1080.c | 8 +- driver/st7565.c | 4 +- firmware.bin | Bin 59532 -> 60340 bytes firmware.packed.bin | Bin 59550 -> 60358 bytes font.c | 100 ------ frequencies.c | 9 +- functions.c | 22 +- helper/battery.c | 16 +- helper/battery.h | 1 - helper/boot.c | 18 +- main.c | 192 ++++++++--- misc.c | 72 ++--- misc.h | 53 +-- radio.c | 381 +++++++++++----------- radio.h | 8 +- scheduler.c | 6 +- settings.c | 771 +++++++++++++++++++++++++++++--------------- settings.h | 484 +++++++++++---------------- ui/fmradio.c | 24 +- ui/lock.c | 2 +- ui/main.c | 143 ++++---- ui/menu.c | 97 +++--- ui/menu.h | 2 +- ui/search.c | 3 +- ui/status.c | 113 ++++--- ui/welcome.c | 101 ------ ui/welcome.h | 24 -- 47 files changed, 1880 insertions(+), 2385 deletions(-) delete mode 100644 ui/welcome.c delete mode 100644 ui/welcome.h diff --git a/Makefile b/Makefile index dc5c539..288c659 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ ENABLE_OVERLAY := 0 ENABLE_LTO := 1 # UART Programming 2.9 kB ENABLE_UART := 1 -ENABLE_UART_DEBUG := 1 +ENABLE_UART_DEBUG := 0 # AirCopy 2.5 kB ENABLE_AIRCOPY := 0 ENABLE_AIRCOPY_REMEMBER_FREQ := 1 @@ -29,7 +29,7 @@ ENABLE_NOAA := 0 ENABLE_VOICE := 0 ENABLE_MUTE_RADIO_FOR_VOICE := 0 # Tx on Voice 1.0 kB -ENABLE_VOX := 0 +ENABLE_VOX := 1 ENABLE_REDUCE_LOW_MID_TX_POWER := 1 # Tx Alarm 600 B ENABLE_ALARM := 0 @@ -38,14 +38,14 @@ ENABLE_TX1750 := 0 ENABLE_MDC1200 := 0 ENABLE_MDC1200_SHOW_OP_ARG := 1 ENABLE_PWRON_PASSWORD := 0 -ENABLE_RESET_AES_KEY := 1 +ENABLE_RESET_AES_KEY := 0 ENABLE_BIG_FREQ := 0 # smaa bolf 580 B ENABLE_SMALL_BOLD := 1 # smallest font 2 kB ENABLE_SMALLEST_FONT := 0 # trim trailing 44 B -ENABLE_TRIM_TRAILING_ZEROS := 1 +ENABLE_TRIM_TRAILING_ZEROS := 0 ENABLE_KEEP_MEM_NAME := 1 ENABLE_WIDE_RX := 1 ENABLE_TX_WHEN_AM := 0 @@ -56,11 +56,11 @@ ENABLE_CTCSS_TAIL_PHASE_SHIFT := 1 ENABLE_CONTRAST := 0 ENABLE_BOOT_BEEPS := 0 ENABLE_DTMF_CALL_FLASH_LIGHT := 1 -ENABLE_FLASH_LIGHT_SOS_TONE := 1 +ENABLE_FLASH_LIGHT_SOS_TONE := 0 ENABLE_SHOW_CHARGE_LEVEL := 0 ENABLE_REVERSE_BAT_SYMBOL := 1 -ENABLE_FREQ_SEARCH_TIMEOUT := 1 -ENABLE_CODE_SEARCH_TIMEOUT := 1 +ENABLE_FREQ_SEARCH_TIMEOUT := 0 +ENABLE_CODE_SEARCH_TIMEOUT := 0 # Kill and Revive 400 B ENABLE_KILL_REVIVE := 0 # AM Fix 800 B @@ -76,9 +76,9 @@ ENABLE_RX_SIGNAL_BAR := 1 # Tx Timeout Bar 200 B ENABLE_TX_TIMEOUT_BAR := 0 # Tx Audio Bar 300 B -ENABLE_TX_AUDIO_BAR := 1 +ENABLE_TX_AUDIO_BAR := 0 # Side Button Menu 300 B -ENABLE_SIDE_BUTT_MENU := 1 +ENABLE_SIDE_BUTT_MENU := 0 # Key Lock 400 B ENABLE_KEYLOCK := 0 #ENABLE_PANADAPTER := 0 @@ -224,7 +224,6 @@ OBJS += ui/menu.o OBJS += ui/search.o OBJS += ui/status.o OBJS += ui/ui.o -OBJS += ui/welcome.o OBJS += version.o OBJS += main.o diff --git a/am_fix.c b/am_fix.c index 706ecb7..3969bda 100644 --- a/am_fix.c +++ b/am_fix.c @@ -220,7 +220,7 @@ #ifdef ENABLE_AM_FIX_TEST1 // user manually sets the table index .. used to calibrate the desired dB gain table - unsigned int gain_table_index[2] = {1 + g_setting_am_fix_test1, 1 + g_setting_am_fix_test1}; + unsigned int gain_table_index[2] = {1 + g_eeprom.config.setting.am_fix_test1, 1 + g_eeprom.config.setting.am_fix_test1}; #else unsigned int gain_table_index[2] = {original_index, original_index}; #endif @@ -248,7 +248,7 @@ for (vfo = 0; vfo < 2; vfo++) { #ifdef ENABLE_AM_FIX_TEST1 - gain_table_index[vfo] = 1 + g_setting_am_fix_test1; + gain_table_index[vfo] = 1 + g_eeprom.config.setting.am_fix_test1; #else gain_table_index[vfo] = original_index; // re-start with original QS setting #endif @@ -271,7 +271,7 @@ rssi_gain_diff[vfo] = 0; gain_table_index_prev[vfo] = 0; #ifdef ENABLE_AM_FIX_TEST1 -// gain_table_index[vfo] = 1 + g_setting_am_fix_test1; +// gain_table_index[vfo] = 1 + g_eeprom.config.setting.am_fix_test1; #else // gain_table_index[vfo] = original_index; // re-start with original QS setting #endif @@ -347,7 +347,7 @@ // user is manually adjusting a gain register - don't do anything automatically { - int i = 1 + (int)g_setting_am_fix_test1; + int i = 1 + (int)g_eeprom.config.setting.am_fix_test1; i = (i < 1) ? 1 : (i > ((int)ARRAY_SIZE(gain_table) - 1) ? ARRAY_SIZE(gain_table) - 1 : i; if (gain_table_index[vfo] == i) diff --git a/app/action.c b/app/action.c index 0799d57..a939e7b 100644 --- a/app/action.c +++ b/app/action.c @@ -104,8 +104,8 @@ void ACTION_Monitor(void) BK4819_StopTones(g_current_function == FUNCTION_TRANSMIT); #ifdef ENABLE_NOAA -// if (g_rx_vfo->channel_save >= NOAA_CHANNEL_FIRST && g_is_noaa_mode) - if (IS_NOAA_CHANNEL(g_rx_vfo->channel_save) && g_is_noaa_mode) +// if (g_rx_vfo->channel_save >= NOAA_CHANNEL_FIRST && g_noaa_mode) + if (IS_NOAA_CHANNEL(g_rx_vfo->channel_save) && g_noaa_mode) g_noaa_channel = g_rx_vfo->channel_save - NOAA_CHANNEL_FIRST; #endif @@ -121,10 +121,10 @@ void ACTION_Monitor(void) GPIO_ClearBit(&GPIOC->DATA, GPIOC_PIN_SPEAKER); if (g_scan_state_dir != SCAN_STATE_DIR_OFF) - g_scan_pause_tick_10ms = g_eeprom.scan_hold_time_500ms * 50; + g_scan_pause_tick_10ms = g_eeprom.config.setting.scan_hold_time * 50; #ifdef g_power_save_expired - if (g_eeprom.dual_watch == DUAL_WATCH_OFF && g_is_noaa_mode) + if (g_eeprom.config.setting.dual_watch == DUAL_WATCH_OFF && g_noaa_mode) { g_noaa_tick_10ms = noaa_tick_10ms; g_schedule_noaa = false; @@ -177,7 +177,7 @@ void ACTION_Scan(bool bRestart) else { // scan without auto store g_fm_auto_scan = false; - Frequency = g_eeprom.fm_frequency_playing; + Frequency = g_eeprom.config.setting.fm_radio.selected_frequency; } g_fm_channel_position = 0; @@ -218,11 +218,11 @@ void ACTION_Scan(bool bRestart) if (g_scan_next_channel <= USER_CHANNEL_LAST) { // channel mode - if (g_eeprom.scan_list_default < 2) + if (g_eeprom.config.setting.scan_list_default < 2) { // keep scanning but toggle between scan lists - //g_eeprom.scan_list_default = (g_eeprom.scan_list_default + 1) % 3; - g_eeprom.scan_list_default++; + //g_eeprom.config.setting.scan_list_default = (g_eeprom.config.setting.scan_list_default + 1) % 3; + g_eeprom.config.setting.scan_list_default++; // jump to the next channel APP_channel_next(true, g_scan_state_dir); @@ -234,7 +234,7 @@ void ACTION_Scan(bool bRestart) return; } - g_eeprom.scan_list_default = 0; // back to scan list 1 - the next time we start scanning + g_eeprom.config.setting.scan_list_default = 0; // back to scan list 1 - the next time we start scanning } // stop scanning @@ -263,7 +263,7 @@ void ACTION_Scan(bool bRestart) #endif // clear the other vfo's rssi level (to hide the antenna symbol) - g_vfo_rssi_bar_level[(g_eeprom.rx_vfo + 1) & 1u] = 0; + g_vfo_rssi_bar_level[(g_rx_vfo_num + 1) & 1u] = 0; g_update_status = true; } @@ -280,7 +280,7 @@ void ACTION_Scan(bool bRestart) // if (!bRestart) if (!bRestart && g_scan_next_channel <= USER_CHANNEL_LAST) { // channel mode, keep scanning but toggle between scan lists - g_eeprom.scan_list_default = (g_eeprom.scan_list_default + 1) % 3; + g_eeprom.config.setting.scan_list_default = (g_eeprom.config.setting.scan_list_default + 1) % 3; // jump to the next channel APP_channel_next(true, g_scan_state_dir); @@ -300,13 +300,13 @@ void ACTION_Scan(bool bRestart) #ifdef ENABLE_VOX void ACTION_Vox(void) { - g_eeprom.vox_switch = !g_eeprom.vox_switch; - g_request_save_settings = true; - g_flag_reconfigure_vfos = true; + g_eeprom.config.setting.vox_switch = (g_eeprom.config.setting.vox_switch + 1) & 1u; + g_request_save_settings = true; + g_flag_reconfigure_vfos = true; #ifdef ENABLE_VOICE - g_another_voice_id = VOICE_ID_VOX; + g_another_voice_id = VOICE_ID_VOX; #endif - g_update_status = true; + g_update_status = true; } #endif @@ -378,14 +378,14 @@ void ACTION_process(const key_code_t Key, const bool key_pressed, const bool key if (Key == KEY_SIDE1) { - Short = g_eeprom.key1_short_press_action; - Long = g_eeprom.key1_long_press_action; + Short = g_eeprom.config.setting.key1_short; + Long = g_eeprom.config.setting.key1_long; } else if (Key == KEY_SIDE2) { - Short = g_eeprom.key2_short_press_action; - Long = g_eeprom.key2_long_press_action; + Short = g_eeprom.config.setting.key2_short; + Long = g_eeprom.config.setting.key2_long; } if (!key_held && key_pressed) diff --git a/app/aircopy.c b/app/aircopy.c index 2be708d..2f6eccf 100644 --- a/app/aircopy.c +++ b/app/aircopy.c @@ -114,6 +114,7 @@ void AIRCOPY_start_fsk_tx(const int request_block_num) if (request_block_num < 0) { EEPROM_ReadBuffer(eeprom_addr, &g_fsk_buffer[tx_size], 64); +// memcpy(&g_fsk_buffer[tx_size], ((uint8_t *)&g_eeprom) + eeprom_addr, 64); tx_size += 64 / 2; } @@ -590,6 +591,7 @@ void AIRCOPY_process_fsk_rx_10ms(void) } EEPROM_WriteBuffer8(eeprom_addr, data); // 8 bytes at a time +// memcpy(((uint8_t *)&g_eeprom) + eeprom_addr, data, 8); data += write_size / sizeof(data[0]); eeprom_addr += write_size; diff --git a/app/app.c b/app/app.c index 81f6c23..604da58 100644 --- a/app/app.c +++ b/app/app.c @@ -83,7 +83,7 @@ static void APP_update_rssi(const int vfo) #ifdef ENABLE_AM_FIX // add RF gain adjust compensation - if (g_eeprom.vfo_info[vfo].am_mode > 0 && g_setting_am_fix) + if (g_current_vfo->am_mode > 0 && g_eeprom.config.setting.am_fix) rssi -= rssi_gain_diff[vfo]; #endif @@ -113,11 +113,11 @@ static void APP_check_for_new_receive(void) g_rx_reception_mode = RX_MODE_DETECTED; } - if (g_eeprom.dual_watch == DUAL_WATCH_OFF) + if (g_eeprom.config.setting.dual_watch == DUAL_WATCH_OFF) { // dual watch is disabled #ifdef ENABLE_NOAA - if (g_is_noaa_mode) + if (g_noaa_mode) { g_noaa_tick_10ms = noaa_tick_3_10ms; g_schedule_noaa = false; @@ -132,7 +132,7 @@ static void APP_check_for_new_receive(void) if (g_rx_reception_mode != RX_MODE_NONE) goto done; - g_dual_watch_tick_10ms = g_eeprom.scan_hold_time_500ms * 50; + g_dual_watch_tick_10ms = g_eeprom.config.setting.scan_hold_time * 50; g_scan_pause_time_mode = false; g_update_status = true; @@ -162,7 +162,7 @@ done: } #endif } - APP_update_rssi(g_eeprom.rx_vfo); + APP_update_rssi(g_rx_vfo_num); g_update_rssi = true; // } } @@ -214,7 +214,7 @@ static void APP_process_new_receive(void) { // not code scanning #ifdef ENABLE_KILL_REVIVE - if (g_rx_vfo->dtmf_decoding_enable || g_setting_radio_disabled) + if (g_rx_vfo->dtmf_decoding_enable || g_eeprom.config.setting.radio_disabled) #else if (g_rx_vfo->dtmf_decoding_enable) #endif @@ -226,7 +226,7 @@ static void APP_process_new_receive(void) { if (g_rx_reception_mode == RX_MODE_DETECTED) { - g_dual_watch_tick_10ms = g_eeprom.scan_hold_time_500ms * 50; + g_dual_watch_tick_10ms = g_eeprom.config.setting.scan_hold_time * 50; g_rx_reception_mode = RX_MODE_LISTENING; g_update_status = true; @@ -262,12 +262,12 @@ static void APP_process_rx(void) { if (g_squelch_open || g_monitor_enabled) { - switch (g_eeprom.scan_resume_mode) + switch (g_eeprom.config.setting.carrier_search_mode) { case SCAN_RESUME_TIME: // stay only for a limited time break; case SCAN_RESUME_CARRIER: // stay untill the carrier goes away - g_scan_pause_tick_10ms = g_eeprom.scan_hold_time_500ms * 50; + g_scan_pause_tick_10ms = g_eeprom.config.setting.scan_hold_time * 50; g_scan_pause_time_mode = false; break; case SCAN_RESUME_STOP: // stop scan once we find any signal @@ -311,7 +311,7 @@ static void APP_process_rx(void) if (g_squelch_open || g_monitor_enabled) { - if (g_setting_backlight_on_tx_rx >= 2) + if (g_eeprom.config.setting.backlight_on_tx_rx >= 2) backlight_turn_on(backlight_tx_rx_time_500ms); // keep the backlight on while we're receiving if (!g_end_of_rx_detected_maybe && IS_NOT_NOAA_CHANNEL(g_rx_vfo->channel_save)) @@ -319,7 +319,7 @@ static void APP_process_rx(void) switch (g_current_code_type) { case CODE_TYPE_NONE: - if (g_eeprom.squelch_level > 0) + if (g_eeprom.config.setting.squelch_level > 0) { if (g_cxcss_tail_found) { @@ -381,7 +381,7 @@ static void APP_process_rx(void) if (!g_end_of_rx_detected_maybe && Mode == END_OF_RX_MODE_NONE && g_next_time_slice_40ms && - g_eeprom.tail_note_elimination && + g_eeprom.config.setting.tail_tone_elimination && (g_current_code_type == CODE_TYPE_DIGITAL || g_current_code_type == CODE_TYPE_REVERSE_DIGITAL) && BK4819_GetCTCType() == 1) { @@ -410,7 +410,7 @@ Skip: break; case END_OF_RX_MODE_TTE: - if (g_eeprom.tail_note_elimination) + if (g_eeprom.config.setting.tail_tone_elimination) { if (!g_monitor_enabled) GPIO_ClearBit(&GPIOC->DATA, GPIOC_PIN_SPEAKER); @@ -426,12 +426,12 @@ Skip: if (g_scan_state_dir != SCAN_STATE_DIR_OFF) { // we're RF scanning - switch (g_eeprom.scan_resume_mode) + switch (g_eeprom.config.setting.carrier_search_mode) { case SCAN_RESUME_TIME: // stay only for a limited time break; case SCAN_RESUME_CARRIER: // stay untill the carrier goes away - g_scan_pause_tick_10ms = g_eeprom.scan_hold_time_500ms * 50; + g_scan_pause_tick_10ms = g_eeprom.config.setting.scan_hold_time * 50; g_scan_pause_time_mode = false; break; case SCAN_RESUME_STOP: // stop scan once we find any signal @@ -443,18 +443,18 @@ Skip: bool APP_start_listening(void) { - const unsigned int chan = g_eeprom.rx_vfo; + const unsigned int chan = g_rx_vfo_num; // const unsigned int chan = g_rx_vfo->channel_save; #ifdef ENABLE_KILL_REVIVE - if (g_setting_radio_disabled) + if (g_eeprom.config.setting.radio_disabled) return false; #endif if (g_squelch_open) BK4819_set_GPIO_pin(BK4819_GPIO6_PIN2_GREEN, true); // LED on - if (g_setting_backlight_on_tx_rx >= 2) + if (g_eeprom.config.setting.backlight_on_tx_rx >= 2) backlight_turn_on(backlight_tx_rx_time_500ms); #ifdef ENABLE_MDC1200 @@ -470,20 +470,20 @@ bool APP_start_listening(void) g_rx_vfo->freq_in_channel = 0xff; if (IS_FREQ_CHANNEL(g_scan_next_channel)) - g_rx_vfo->freq_in_channel = BOARD_find_channel(g_rx_vfo->freq_config_rx.frequency); + g_rx_vfo->freq_in_channel = SETTINGS_find_channel(g_rx_vfo->freq_config_rx.frequency); - switch (g_eeprom.scan_resume_mode) + switch (g_eeprom.config.setting.carrier_search_mode) { case SCAN_RESUME_TIME: if (!g_scan_pause_time_mode) { - g_scan_pause_tick_10ms = g_eeprom.scan_hold_time_500ms * 50; + g_scan_pause_tick_10ms = g_eeprom.config.setting.scan_hold_time * 50; g_scan_pause_time_mode = true; } break; case SCAN_RESUME_CARRIER: - g_scan_pause_tick_10ms = g_eeprom.scan_hold_time_500ms * 50; + g_scan_pause_tick_10ms = g_eeprom.config.setting.scan_hold_time * 50; g_scan_pause_time_mode = false; break; @@ -495,12 +495,12 @@ bool APP_start_listening(void) } #ifdef ENABLE_NOAA - if (IS_NOAA_CHANNEL(g_rx_vfo->channel_save) && g_is_noaa_mode) + if (IS_NOAA_CHANNEL(g_rx_vfo->channel_save) && g_noaa_mode) { g_rx_vfo->channel_save = g_noaa_channel + NOAA_CHANNEL_FIRST; g_rx_vfo->p_rx->frequency = NOAA_FREQUENCY_TABLE[g_noaa_channel]; g_rx_vfo->p_tx->frequency = NOAA_FREQUENCY_TABLE[g_noaa_channel]; - g_eeprom.screen_channel[chan] = g_rx_vfo->channel_save; + g_eeprom.config.setting.indices.vfo[chan].screen = g_rx_vfo->channel_save; g_noaa_tick_10ms = 5000 / 10; // 5 sec g_schedule_noaa = false; } @@ -511,10 +511,10 @@ bool APP_start_listening(void) if (g_scan_state_dir == SCAN_STATE_DIR_OFF && g_css_scan_mode == CSS_SCAN_MODE_OFF && - g_eeprom.dual_watch != DUAL_WATCH_OFF) + g_eeprom.config.setting.dual_watch != DUAL_WATCH_OFF) { // dual watch is active - g_dual_watch_tick_10ms = g_eeprom.scan_hold_time_500ms * 50; + g_dual_watch_tick_10ms = g_eeprom.config.setting.scan_hold_time * 50; g_rx_vfo_is_active = true; g_update_status = true; } @@ -527,10 +527,10 @@ bool APP_start_listening(void) // else { BK4819_WriteRegister(0x48, - (11u << 12) | // ??? .. 0 ~ 15, doesn't seem to make any difference - ( 0u << 10) | // AF Rx Gain-1 - (g_eeprom.volume_gain << 4) | // AF Rx Gain-2 - (g_eeprom.dac_gain << 0)); // AF DAC Gain (after Gain-1 and Gain-2) + (11u << 12) | // ??? .. 0 ~ 15, doesn't seem to make any difference + ( 0u << 10) | // AF Rx Gain-1 + (g_eeprom.calib.volume_gain << 4) | // AF Rx Gain-2 + (g_eeprom.calib.dac_gain << 0)); // AF DAC Gain (after Gain-1 and Gain-2) } FUNCTION_Select(FUNCTION_RECEIVE); @@ -617,10 +617,10 @@ void APP_stop_scan(void) if (g_scan_restore_channel != 0xff) { - g_eeprom.user_channel[g_eeprom.rx_vfo] = g_scan_restore_channel; - g_eeprom.screen_channel[g_eeprom.rx_vfo] = g_scan_restore_channel; + g_eeprom.config.setting.indices.vfo[g_rx_vfo_num].user = g_scan_restore_channel; + g_eeprom.config.setting.indices.vfo[g_rx_vfo_num].screen = g_scan_restore_channel; - RADIO_configure_channel(g_eeprom.rx_vfo, VFO_CONFIGURE_RELOAD); + RADIO_configure_channel(g_rx_vfo_num, VFO_CONFIGURE_RELOAD); RADIO_setup_registers(true); } @@ -632,7 +632,7 @@ void APP_stop_scan(void) g_rx_vfo->freq_config_rx.frequency = g_scan_restore_frequency; // find the first channel that contains this frequency - g_rx_vfo->freq_in_channel = BOARD_find_channel(g_rx_vfo->freq_config_rx.frequency); + g_rx_vfo->freq_in_channel = SETTINGS_find_channel(g_rx_vfo->freq_config_rx.frequency); RADIO_ApplyOffset(g_rx_vfo, false); RADIO_ConfigureSquelchAndOutputPower(g_rx_vfo); @@ -648,7 +648,7 @@ void APP_stop_scan(void) { // frequency mode RADIO_ApplyOffset(g_rx_vfo, false); RADIO_ConfigureSquelchAndOutputPower(g_rx_vfo); - SETTINGS_save_channel(g_rx_vfo->channel_save, g_eeprom.rx_vfo, g_rx_vfo, 1); + SETTINGS_save_channel(g_rx_vfo->channel_save, g_rx_vfo_num, g_rx_vfo, 1); return; } @@ -716,9 +716,10 @@ static void APP_next_freq(void) static void APP_next_channel(void) { static unsigned int prevChannel = 0; - const bool enabled = (g_eeprom.scan_list_default < 2) ? g_eeprom.scan_list_enabled[g_eeprom.scan_list_default] : true; - const int chan1 = (g_eeprom.scan_list_default < 2) ? g_eeprom.scan_list_priority_ch1[g_eeprom.scan_list_default] : -1; - const int chan2 = (g_eeprom.scan_list_default < 2) ? g_eeprom.scan_list_priority_ch2[g_eeprom.scan_list_default] : -1; + const unsigned int index = g_eeprom.config.setting.scan_list_default; + const bool enabled = (index < 2) ? !!g_eeprom.config.setting.priority_scan_list[index].enabled : true; + const int chan1 = (index < 2) ? g_eeprom.config.setting.priority_scan_list[index].channel[0] : -1; + const int chan2 = (index < 2) ? g_eeprom.config.setting.priority_scan_list[index].channel[1] : -1; const unsigned int prev_chan = g_scan_next_channel; unsigned int chan = 0; @@ -757,10 +758,10 @@ static void APP_next_channel(void) // this bit doesn't yet work if the other VFO is a frequency case SCAN_NEXT_CHAN_DUAL_WATCH: // dual watch is enabled - include the other VFO in the scan -// if (g_eeprom.dual_watch != DUAL_WATCH_OFF) +// if (g_eeprom.config.setting.dual_watch != DUAL_WATCH_OFF) // { -// chan = (g_eeprom.rx_vfo + 1) & 1u; -// chan = g_eeprom.screen_channel[chan]; +// chan = (g_rx_vfo + 1) & 1u; +// chan = g_eeprom.config.setting.indices.vfo[chan].screen; // if (chan <= USER_CHANNEL_LAST) // { // g_scan_current_scan_list = SCAN_NEXT_CHAN_DUAL_WATCH; @@ -782,7 +783,7 @@ static void APP_next_channel(void) if (!enabled || chan == 0xff) { - chan = RADIO_FindNextChannel(g_scan_next_channel + g_scan_state_dir, g_scan_state_dir, (g_eeprom.scan_list_default < 2) ? true : false, g_eeprom.scan_list_default); + chan = RADIO_FindNextChannel(g_scan_next_channel + g_scan_state_dir, g_scan_state_dir, (index < 2) ? true : false, index); if (chan == 0xFF) { // no valid channel found @@ -799,10 +800,10 @@ static void APP_next_channel(void) // UART_printf("APP_next_channel %u\r\n", g_scan_next_channel); #endif - g_eeprom.user_channel[g_eeprom.rx_vfo] = g_scan_next_channel; - g_eeprom.screen_channel[g_eeprom.rx_vfo] = g_scan_next_channel; + g_eeprom.config.setting.indices.vfo[g_rx_vfo_num].user = g_scan_next_channel; + g_eeprom.config.setting.indices.vfo[g_rx_vfo_num].screen = g_scan_next_channel; - RADIO_configure_channel(g_eeprom.rx_vfo, VFO_CONFIGURE_RELOAD); + RADIO_configure_channel(g_rx_vfo_num, VFO_CONFIGURE_RELOAD); RADIO_setup_registers(true); @@ -830,40 +831,63 @@ static void APP_next_channel(void) } #endif -static void APP_toggle_dual_watch_vfo(void) +static bool APP_toggle_dual_watch_vfo(void) { + if (g_eeprom.config.setting.dual_watch == DUAL_WATCH_OFF) + return false; + if (g_current_function == FUNCTION_FOREGROUND && g_current_function == FUNCTION_POWER_SAVE) + return false; + if (g_current_display_screen == DISPLAY_SEARCH) + return false; + if (g_scan_state_dir != SCAN_STATE_DIR_OFF) + return false; + if (g_css_scan_mode != CSS_SCAN_MODE_OFF) + return false; + if (g_ptt_is_pressed) + return false; + if (g_dtmf_call_state != DTMF_CALL_STATE_NONE) + return false; + #ifdef ENABLE_FMRADIO +// if (g_fm_radio_mode) +// return false; + #endif + if (g_dual_watch_tick_10ms > 0) + return false; + #if defined(ENABLE_UART) && defined(ENABLE_UART_DEBUG) UART_SendText("dual wot\r\n"); #endif #ifdef ENABLE_NOAA - if (g_is_noaa_mode) + if (g_noaa_mode) { - if (IS_NOT_NOAA_CHANNEL(g_eeprom.screen_channel[0]) || IS_NOT_NOAA_CHANNEL(g_eeprom.screen_channel[1])) - g_eeprom.rx_vfo = (g_eeprom.rx_vfo + 1) & 1; + if (IS_NOT_NOAA_CHANNEL(g_eeprom.config.setting.indices.vfo[0].screen) || IS_NOT_NOAA_CHANNEL(g_eeprom.config.setting.indices.vfo[1].screen)) + g_rx_vfo_num = (g_rx_vfo_num + 1) & 1u; else - g_eeprom.rx_vfo = 0; + g_rx_vfo_num = 0; - g_rx_vfo = &g_eeprom.vfo_info[g_eeprom.rx_vfo]; + g_rx_vfo = &g_vfo_info[g_rx_vfo_num]; - if (g_eeprom.vfo_info[0].channel_save >= NOAA_CHANNEL_FIRST) + if (g_vfo_info[0].channel_save >= NOAA_CHANNEL_FIRST) APP_next_noaa(); } else #endif { // toggle between VFO's - g_eeprom.rx_vfo = (g_eeprom.rx_vfo + 1) & 1; - g_rx_vfo = &g_eeprom.vfo_info[g_eeprom.rx_vfo]; + g_rx_vfo_num = (g_rx_vfo_num + 1) & 1u; + g_rx_vfo = &g_vfo_info[g_rx_vfo_num]; g_update_status = true; } RADIO_setup_registers(false); #ifdef ENABLE_NOAA - g_dual_watch_tick_10ms = g_is_noaa_mode ? dual_watch_delay_noaa_10ms : dual_watch_delay_toggle_10ms; + g_dual_watch_tick_10ms = g_noaa_mode ? dual_watch_delay_noaa_10ms : dual_watch_delay_toggle_10ms; #else g_dual_watch_tick_10ms = dual_watch_delay_toggle_10ms; #endif + + return true; } void APP_process_radio_interrupts(void) @@ -884,7 +908,16 @@ void APP_process_radio_interrupts(void) int_bits = BK4819_ReadRegister(0x02); #if defined(ENABLE_UART) && defined(ENABLE_UART_DEBUG) - UART_printf("reg_c int_bits %04X\r\n", reg_c, int_bits); + { + int i; + UART_printf("int bits %04X %04X ", reg_c, int_bits); + for (i = 15; i >= 0; i--) + UART_printf("%c", (reg_c & (1u << i)) ? '#' : '.'); + UART_SendText(" "); + for (i = 15; i >= 0; i--) + UART_printf("%c", (int_bits & (1u << i)) ? '#' : '.'); + UART_SendText("\r\n"); + } #endif if (int_bits & BK4819_REG_02_DTMF_5TONE_FOUND) @@ -892,7 +925,7 @@ void APP_process_radio_interrupts(void) const char c = DTMF_GetCharacter(BK4819_GetDTMF_5TONE_Code()); if (c != 0xff && g_current_function != FUNCTION_TRANSMIT) { - if (g_setting_live_dtmf_decoder) + if (g_eeprom.config.setting.dtmf_live_decoder) { size_t len = strlen(g_dtmf_rx_live); if (len >= (sizeof(g_dtmf_rx_live) - 1)) @@ -905,9 +938,9 @@ void APP_process_radio_interrupts(void) g_dtmf_rx_live_timeout = dtmf_rx_live_timeout_500ms; // time till we delete it g_update_display = true; } - + #ifdef ENABLE_KILL_REVIVE - if (g_rx_vfo->dtmf_decoding_enable || g_setting_radio_disabled) + if (g_rx_vfo->dtmf_decoding_enable || g_eeprom.config.setting.radio_disabled) #else if (g_rx_vfo->dtmf_decoding_enable) #endif @@ -921,7 +954,7 @@ void APP_process_radio_interrupts(void) g_dtmf_rx[g_dtmf_rx_index] = 0; g_dtmf_rx_timeout = dtmf_rx_timeout_500ms; // time till we delete it g_dtmf_rx_pending = true; - + DTMF_HandleRequest(); } } @@ -980,7 +1013,7 @@ void APP_process_radio_interrupts(void) g_vox_lost = true; g_vox_pause_tick_10ms = 10; - if (g_eeprom.vox_switch) + if (g_eeprom.config.setting.vox_switch) { if (g_current_function == FUNCTION_POWER_SAVE && !g_rx_idle_mode) { @@ -988,7 +1021,7 @@ void APP_process_radio_interrupts(void) g_power_save_expired = false; } - if (g_eeprom.dual_watch != DUAL_WATCH_OFF && + if (g_eeprom.config.setting.dual_watch != DUAL_WATCH_OFF && (g_dual_watch_tick_10ms == 0 || g_dual_watch_tick_10ms < dual_watch_delay_after_vox_10ms)) { g_dual_watch_tick_10ms = dual_watch_delay_after_vox_10ms; @@ -1017,7 +1050,7 @@ void APP_process_radio_interrupts(void) UART_SendText("sq close\r\n"); #endif - //APP_update_rssi(g_eeprom.rx_vfo); + //APP_update_rssi(g_rx_vfo); g_update_rssi = true; g_update_display = true; @@ -1031,12 +1064,12 @@ void APP_process_radio_interrupts(void) UART_SendText("sq open\r\n"); #endif - //APP_update_rssi(g_eeprom.rx_vfo); + //APP_update_rssi(g_rx_vfo_num); g_update_rssi = true; if (g_monitor_enabled) BK4819_set_GPIO_pin(BK4819_GPIO6_PIN2_GREEN, true); // LED on - + g_update_display = true; } @@ -1054,8 +1087,8 @@ void APP_end_tx(void) if (g_current_vfo->p_tx->code_type != CODE_TYPE_NONE) { // CTCSS/CDCSS is enabled - //if (g_eeprom.tail_note_elimination && g_eeprom.repeater_tail_tone_elimination > 0) - if (g_eeprom.tail_note_elimination) + //if (g_eeprom.config.setting.tail_tone_elimination && g_eeprom.config.setting.repeater_tail_tone_elimination > 0) + if (g_eeprom.config.setting.tail_tone_elimination) { // send the CTCSS/DCS tail tone - allows the receivers to mute the usual FM squelch tail/crash RADIO_enable_CxCSS_tail(); } @@ -1079,7 +1112,7 @@ void APP_end_tx(void) static void APP_process_vox(void) { #ifdef ENABLE_KILL_REVIVE - if (g_setting_radio_disabled) + if (g_eeprom.config.setting.radio_disabled) return; #endif @@ -1126,10 +1159,10 @@ void APP_end_tx(void) { APP_end_tx(); - if (g_eeprom.repeater_tail_tone_elimination == 0) + if (g_eeprom.config.setting.repeater_tail_tone_elimination == 0) FUNCTION_Select(FUNCTION_FOREGROUND); else - g_rtte_count_down = g_eeprom.repeater_tail_tone_elimination * 10; + g_rtte_count_down = g_eeprom.config.setting.repeater_tail_tone_elimination * 10; } g_update_status = true; @@ -1165,7 +1198,7 @@ void APP_check_keys(void) key_code_t key; #ifdef ENABLE_KILL_REVIVE - if (g_setting_radio_disabled) + if (g_eeprom.config.setting.radio_disabled) return; #endif @@ -1176,23 +1209,23 @@ void APP_check_keys(void) { // PTT pressed #ifdef ENABLE_AIRCOPY - if (!g_ptt_is_pressed && g_serial_config_tick_500ms == 0 && g_setting_tx_enable && g_current_function != FUNCTION_TRANSMIT && g_current_display_screen != DISPLAY_AIRCOPY) + if (!g_ptt_is_pressed && g_serial_config_tick_500ms == 0 && g_eeprom.config.setting.tx_enable && g_current_function != FUNCTION_TRANSMIT && g_current_display_screen != DISPLAY_AIRCOPY) #else - if (!g_ptt_is_pressed && g_serial_config_tick_500ms == 0 && g_setting_tx_enable && g_current_function != FUNCTION_TRANSMIT) + if (!g_ptt_is_pressed && g_serial_config_tick_500ms == 0 && g_eeprom.config.setting.tx_enable && g_current_function != FUNCTION_TRANSMIT) #endif { #ifdef ENABLE_KILL_REVIVE - if (!g_setting_radio_disabled) + if (!g_eeprom.config.setting.radio_disabled) #endif { if (++g_ptt_debounce >= 3) // 30ms debounce { // start TX'ing - + g_boot_tick_10ms = 0; // cancel the boot-up screen g_ptt_is_pressed = ptt_pressed; g_ptt_was_released = false; g_ptt_debounce = 3; - + APP_process_key(KEY_PTT, true, false); } } @@ -1202,9 +1235,9 @@ void APP_check_keys(void) { // PTT released #ifdef ENABLE_KILL_REVIVE - if (g_ptt_is_pressed || g_serial_config_tick_500ms > 0 || !g_setting_tx_enable || g_current_function == FUNCTION_TRANSMIT || g_setting_radio_disabled) + if (g_ptt_is_pressed || g_serial_config_tick_500ms > 0 || !g_eeprom.config.setting.tx_enable || g_current_function == FUNCTION_TRANSMIT || g_eeprom.config.setting.radio_disabled) #else - if (g_ptt_is_pressed || g_serial_config_tick_500ms > 0 || !g_setting_tx_enable || g_current_function == FUNCTION_TRANSMIT) + if (g_ptt_is_pressed || g_serial_config_tick_500ms > 0 || !g_eeprom.config.setting.tx_enable || g_current_function == FUNCTION_TRANSMIT) #endif { if (--g_ptt_debounce <= 0) @@ -1319,7 +1352,8 @@ void APP_check_keys(void) { // only the up and down keys are made repeatable // key repeat max 10ms speed if user is moving up/down in freq/channel - const uint8_t repeat_10ms = (g_manual_scanning && g_monitor_enabled && g_current_display_screen == DISPLAY_MAIN) ? 1 : key_repeat_10ms; + const bool freq_chan = IS_FREQ_CHANNEL(g_eeprom.config.setting.indices.vfo[g_eeprom.config.setting.tx_vfo_num].screen); + const uint8_t repeat_10ms = (g_manual_scanning && g_monitor_enabled && freq_chan && g_current_display_screen == DISPLAY_MAIN) ? 1 : key_repeat_10ms; if (++g_key_debounce_repeat >= (key_long_press_10ms + repeat_10ms)) { // key repeat @@ -1384,7 +1418,7 @@ void APP_cancel_user_input_modes(void) if (!g_squelch_open && !g_monitor_enabled) GPIO_ClearBit(&GPIOC->DATA, GPIOC_PIN_SPEAKER); - if (g_eeprom.alarm_mode == ALARM_MODE_TONE) + if (g_eeprom.config.setting.alarm_mode == ALARM_MODE_TONE) { RADIO_tx_eot(); RADIO_enable_CxCSS_tail(); @@ -1570,7 +1604,7 @@ void APP_process_scan(void) MENU_SelectNextCode(); #ifdef ENABLE_NOAA - if (g_eeprom.dual_watch == DUAL_WATCH_OFF && g_is_noaa_mode && g_schedule_noaa) + if (g_eeprom.config.setting.dual_watch == DUAL_WATCH_OFF && g_noaa_mode && g_schedule_noaa) { APP_next_noaa(); @@ -1580,28 +1614,12 @@ void APP_process_scan(void) g_schedule_noaa = false; } #endif - switch (g_flash_light_state) - case FLASHLIGHT_SOS: - // toggle between the VFO's if dual watch is enabled - if (g_eeprom.dual_watch != DUAL_WATCH_OFF && - g_dual_watch_tick_10ms == 0 && - !g_ptt_is_pressed && - #ifdef ENABLE_FMRADIO - !g_fm_radio_mode && - #endif - g_dtmf_call_state == DTMF_CALL_STATE_NONE && - g_current_display_screen != DISPLAY_SEARCH && - g_scan_state_dir == SCAN_STATE_DIR_OFF && - g_css_scan_mode == CSS_SCAN_MODE_OFF && - g_current_function != FUNCTION_POWER_SAVE && - (g_current_function == FUNCTION_FOREGROUND || g_current_function == FUNCTION_POWER_SAVE)) + if (APP_toggle_dual_watch_vfo()) { - APP_toggle_dual_watch_vfo(); // toggle between the two VFO's - if (g_rx_vfo_is_active && g_current_display_screen == DISPLAY_MAIN) GUI_SelectNextDisplay(DISPLAY_MAIN); - + g_rx_vfo_is_active = false; g_rx_reception_mode = RX_MODE_NONE; } @@ -1658,7 +1676,7 @@ void APP_process_transmit(void) BK4819_SetScrambleFrequencyControlWord((Tone <= 1500) ? Tone : (1500 * 2) - Tone); - if (g_eeprom.alarm_mode == ALARM_MODE_TONE && g_alarm_running_counter_10ms == 512) + if (g_eeprom.config.setting.alarm_mode == ALARM_MODE_TONE && g_alarm_running_counter_10ms == 512) { g_alarm_running_counter_10ms = 0; @@ -1713,7 +1731,7 @@ void APP_process_functions(void) break; case FUNCTION_TRANSMIT: - if (g_setting_backlight_on_tx_rx == 1 || g_setting_backlight_on_tx_rx == 3) + if (g_eeprom.config.setting.backlight_on_tx_rx == 1 || g_eeprom.config.setting.backlight_on_tx_rx == 3) backlight_turn_on(backlight_tx_rx_time_500ms); break; @@ -1750,7 +1768,7 @@ void APP_process_power_save(void) g_fkey_pressed || g_key_pressed != KEY_INVALID || g_key_held || - g_eeprom.battery_save == 0 || + g_eeprom.config.setting.battery_save_ratio == 0 || g_scan_state_dir != SCAN_STATE_DIR_OFF || g_css_scan_mode != CSS_SCAN_MODE_OFF || g_current_display_screen != DISPLAY_MAIN || @@ -1761,9 +1779,9 @@ void APP_process_power_save(void) } #ifdef ENABLE_NOAA - if (IS_NOAA_CHANNEL(g_eeprom.screen_channel[0]) || - IS_NOAA_CHANNEL(g_eeprom.screen_channel[1]) || - g_is_noaa_mode) + if (IS_NOAA_CHANNEL(g_eeprom.config.setting.indices.vfo[0].screen) || + IS_NOAA_CHANNEL(g_eeprom.config.setting.indices.vfo[1].screen) || + g_noaa_mode) { power_save = false; } @@ -1786,6 +1804,8 @@ void APP_process_power_save(void) g_schedule_power_save = false; + // ************************** + #ifdef ENABLE_VOICE if (g_voice_write_index != 0) return; @@ -1805,17 +1825,12 @@ void APP_process_power_save(void) BK4819_Conditional_RX_TurnOn(); #ifdef ENABLE_VOX - if (g_eeprom.vox_switch) - BK4819_EnableVox(g_eeprom.vox1_threshold, g_eeprom.vox0_threshold); + if (g_eeprom.config.setting.vox_switch) + BK4819_EnableVox(g_vox_threshold[1], g_vox_threshold[0]); #endif - if (g_eeprom.dual_watch != DUAL_WATCH_OFF && - g_scan_state_dir == SCAN_STATE_DIR_OFF && - g_css_scan_mode == CSS_SCAN_MODE_OFF) - { // dual watch mode, toggle between the two VFO's - APP_toggle_dual_watch_vfo(); + if (APP_toggle_dual_watch_vfo()) g_update_rssi = false; - } FUNCTION_Init(); @@ -1823,17 +1838,17 @@ void APP_process_power_save(void) g_rx_idle_mode = false; // RX is awake } else - if (g_eeprom.dual_watch == DUAL_WATCH_OFF || + if (g_eeprom.config.setting.dual_watch == DUAL_WATCH_OFF || g_scan_state_dir != SCAN_STATE_DIR_OFF || g_css_scan_mode != CSS_SCAN_MODE_OFF || g_update_rssi) - { // dual watch mode, go back to sleep + { // go back to sleep - APP_update_rssi(g_eeprom.rx_vfo); + APP_update_rssi(g_rx_vfo_num); // go back to sleep - g_power_save_tick_10ms = g_eeprom.battery_save * 10; + g_power_save_tick_10ms = g_eeprom.config.setting.battery_save_ratio * 10; g_rx_idle_mode = true; BK4819_DisableVox(); @@ -1841,11 +1856,9 @@ void APP_process_power_save(void) BK4819_set_GPIO_pin(BK4819_GPIO0_PIN28_RX_ENABLE, false); } else + if (APP_toggle_dual_watch_vfo()) { - // toggle between the two VFO's - APP_toggle_dual_watch_vfo(); - - g_update_rssi = true; + g_update_rssi = true; g_power_save_tick_10ms = power_save1_10ms; } @@ -1945,14 +1958,14 @@ void APP_time_slice_500ms(void) // g_update_display = true; // can't do this if not FM scanning, it causes audio clicks #endif - if (g_backlight_count_down > 0 && + if (g_backlight_tick_500ms > 0 && !g_ask_to_save && g_css_scan_mode == CSS_SCAN_MODE_OFF && g_current_display_screen != DISPLAY_AIRCOPY) { if (g_current_display_screen != DISPLAY_MENU || g_menu_cursor != MENU_AUTO_BACKLITE) // don't turn off backlight if user is in backlight menu option - if (--g_backlight_count_down == 0) - if (g_eeprom.backlight < (ARRAY_SIZE(g_sub_menu_backlight) - 1)) + if (--g_backlight_tick_500ms == 0) + if (g_eeprom.config.setting.backlight_time < (ARRAY_SIZE(g_sub_menu_backlight) - 1)) GPIO_ClearBit(&GPIOB->DATA, GPIOB_PIN_BACKLIGHT); // turn backlight off } @@ -1960,7 +1973,7 @@ void APP_time_slice_500ms(void) { BOARD_ADC_GetBatteryInfo(&g_usb_current_voltage, &g_usb_current); - if (g_usb_current > 500 || g_battery_calibration[3] < g_usb_current_voltage) + if (g_usb_current > 500 || g_eeprom.calib.battery[3] < g_usb_current_voltage) { #ifdef ENABLE_OVERLAY overlay_FLASH_RebootToBootloader(); @@ -1984,7 +1997,7 @@ void APP_time_slice_500ms(void) // update every 2 sec if ((g_battery_check_counter & 3) == 0) { - if (g_charging_with_type_c || g_setting_battery_text > 0) + if (g_charging_with_type_c || g_eeprom.config.setting.battery_text > 0) g_update_status = true; #ifdef ENABLE_SHOW_CHARGE_LEVEL @@ -2010,7 +2023,7 @@ void APP_time_slice_500ms(void) { #ifdef ENABLE_KEYLOCK - if (g_eeprom.auto_keypad_lock && + if (g_eeprom.config.setting.auto_key_lock != 0 && g_key_lock_tick_500ms > 0 && !g_dtmf_input_mode && g_input_box_index == 0 && @@ -2018,8 +2031,8 @@ void APP_time_slice_500ms(void) { if (--g_key_lock_tick_500ms == 0) { // lock the keyboard - g_eeprom.key_lock = true; - g_update_status = true; + g_eeprom.config.setting.key_lock = true; + g_update_status = true; } } #endif @@ -2028,9 +2041,9 @@ void APP_time_slice_500ms(void) { g_menu_tick_10ms = 0; - if (g_eeprom.backlight == 0) + if (g_eeprom.config.setting.backlight_time == 0) { - g_backlight_count_down = 0; + g_backlight_tick_500ms = 0; GPIO_ClearBit(&GPIOB->DATA, GPIOB_PIN_BACKLIGHT); // turn the backlight OFF } @@ -2088,7 +2101,7 @@ void APP_time_slice_500ms(void) } if (g_current_function != FUNCTION_POWER_SAVE && g_current_function != FUNCTION_TRANSMIT) - APP_update_rssi(g_eeprom.rx_vfo); + APP_update_rssi(g_rx_vfo_num); if (g_low_battery) { @@ -2129,7 +2142,7 @@ void APP_time_slice_500ms(void) ST7565_HardwareReset(); - if (g_eeprom.backlight < (ARRAY_SIZE(g_sub_menu_backlight) - 1)) + if (g_eeprom.config.setting.backlight_time < (ARRAY_SIZE(g_sub_menu_backlight) - 1)) GPIO_ClearBit(&GPIOB->DATA, GPIOB_PIN_BACKLIGHT); // turn the backlight off } #ifdef ENABLE_VOICE @@ -2196,7 +2209,7 @@ void APP_time_slice_500ms(void) { if (--g_dtmf_auto_reset_time_500ms == 0) { - if (g_dtmf_call_state == DTMF_CALL_STATE_RECEIVED && g_eeprom.dtmf_auto_reset_time >= DTMF_HOLD_MAX) + if (g_dtmf_call_state == DTMF_CALL_STATE_RECEIVED && g_eeprom.config.setting.dtmf.auto_reset_time >= DTMF_HOLD_MAX) g_dtmf_call_state = DTMF_CALL_STATE_RECEIVED_STAY; // keep message on-screen till a key is pressed else g_dtmf_call_state = DTMF_CALL_STATE_NONE; @@ -2282,8 +2295,8 @@ void APP_time_slice_10ms(void) #endif #ifdef ENABLE_AM_FIX - if (g_rx_vfo->am_mode > 0 && g_setting_am_fix) - AM_fix_10ms(g_eeprom.rx_vfo); + if (g_rx_vfo->am_mode > 0 && g_eeprom.config.setting.am_fix) + AM_fix_10ms(g_rx_vfo_num); #endif #ifdef ENABLE_FMRADIO @@ -2308,10 +2321,10 @@ void APP_time_slice_10ms(void) if (g_flag_save_channel) { - SETTINGS_save_channel(g_tx_vfo->channel_save, g_eeprom.tx_vfo, g_tx_vfo, g_flag_save_channel ? 1 : 0); + SETTINGS_save_channel(g_tx_vfo->channel_save, g_eeprom.config.setting.tx_vfo_num, g_tx_vfo, g_flag_save_channel ? 1 : 0); g_flag_save_channel = false; - RADIO_configure_channel(g_eeprom.tx_vfo, VFO_CONFIGURE); + RADIO_configure_channel(g_eeprom.config.setting.tx_vfo_num, VFO_CONFIGURE); RADIO_setup_registers(true); @@ -2350,7 +2363,7 @@ void APP_time_slice_10ms(void) if (g_current_function == FUNCTION_TRANSMIT) { // transmitting #ifdef ENABLE_TX_AUDIO_BAR - if (g_setting_mic_bar && (g_flash_light_blink_tick_10ms % (150 / 10)) == 0 && !g_update_display) // once every 150ms + if (g_eeprom.config.setting.mic_bar && (g_flash_light_blink_tick_10ms % (150 / 10)) == 0 && !g_update_display) // once every 150ms UI_DisplayAudioBar(true); #endif } @@ -2383,7 +2396,7 @@ void APP_time_slice_10ms(void) if (g_vox_pause_tick_10ms > 0) g_vox_pause_tick_10ms--; - if (g_eeprom.vox_switch) + if (g_eeprom.config.setting.vox_switch) APP_process_vox(); #endif @@ -2435,7 +2448,7 @@ static void APP_process_key(const key_code_t Key, const bool key_pressed, const // remember the current backlight state (on / off) const bool backlight_was_on = GPIO_CheckBit(&GPIOB->DATA, GPIOB_PIN_BACKLIGHT); - if (Key == KEY_EXIT && !backlight_was_on && g_eeprom.backlight > 0) + if (Key == KEY_EXIT && !backlight_was_on && g_eeprom.config.setting.backlight_time > 0) { // just turn the back light on for now so the user can see what's what if (!key_pressed && !key_held) { // key has been released @@ -2459,7 +2472,7 @@ static void APP_process_key(const key_code_t Key, const bool key_pressed, const #ifdef ENABLE_KEYLOCK // keep the auto keylock at bay - if (g_eeprom.auto_keypad_lock) + if (g_eeprom.config.setting.auto_key_lock != 0) g_key_lock_tick_500ms = key_lock_timeout_500ms; #endif @@ -2471,8 +2484,8 @@ static void APP_process_key(const key_code_t Key, const bool key_pressed, const // ******************** - #ifdef ENABLE_KEYLOCK - if (g_eeprom.key_lock && g_current_function != FUNCTION_TRANSMIT && Key != KEY_PTT) +#ifdef ENABLE_KEYLOCK + if (g_eeprom.config.setting.key_lock && g_current_function != FUNCTION_TRANSMIT && Key != KEY_PTT) { // keyboard is locked if (Key == KEY_F) @@ -2483,7 +2496,7 @@ static void APP_process_key(const key_code_t Key, const bool key_pressed, const if (key_held) { // unlock the keypad - g_eeprom.key_lock = false; + g_eeprom.config.setting.g_eeprom.key_lock = false; g_request_save_settings = true; g_update_status = true; @@ -2514,7 +2527,7 @@ static void APP_process_key(const key_code_t Key, const bool key_pressed, const return; } } - #endif +#endif // key beep // if (Key != KEY_PTT && !key_held && key_pressed) @@ -2647,7 +2660,7 @@ static void APP_process_key(const key_code_t Key, const bool key_pressed, const BK4819_ExitDTMF_TX(false); - if (g_current_vfo->scrambling_type == 0 || !g_setting_scramble_enable) + if (g_current_vfo->scrambling_type == 0 || !g_eeprom.config.setting.enable_scrambler) BK4819_DisableScramble(); else BK4819_EnableScramble(g_current_vfo->scrambling_type - 1); @@ -2655,7 +2668,7 @@ static void APP_process_key(const key_code_t Key, const bool key_pressed, const } else { - if (g_eeprom.dtmf_side_tone) + if (g_eeprom.config.setting.dtmf.side_tone) { // user will here the DTMF tones in speaker GPIO_SetBit(&GPIOC->DATA, GPIOC_PIN_SPEAKER); } @@ -2663,9 +2676,9 @@ static void APP_process_key(const key_code_t Key, const bool key_pressed, const BK4819_DisableScramble(); if (Code == 0xFE) - BK4819_TransmitTone(g_eeprom.dtmf_side_tone, 1750); + BK4819_TransmitTone(g_eeprom.config.setting.dtmf.side_tone, 1750); else - BK4819_PlayDTMFEx(g_eeprom.dtmf_side_tone, Code); + BK4819_PlayDTMFEx(g_eeprom.config.setting.dtmf.side_tone, Code); } } #if defined(ENABLE_ALARM) || defined(ENABLE_TX1750) @@ -2674,10 +2687,10 @@ static void APP_process_key(const key_code_t Key, const bool key_pressed, const { APP_alarm_off(); - if (g_eeprom.repeater_tail_tone_elimination == 0) + if (g_eeprom.config.setting.repeater_tail_tone_elimination == 0) FUNCTION_Select(FUNCTION_FOREGROUND); else - g_rtte_count_down = g_eeprom.repeater_tail_tone_elimination * 10; + g_rtte_count_down = g_eeprom.config.setting.repeater_tail_tone_elimination * 10; if (Key == KEY_PTT) g_ptt_was_pressed = true; @@ -2800,7 +2813,7 @@ Skip: { if (!key_held) { - SETTINGS_save_channel(g_tx_vfo->channel_save, g_eeprom.tx_vfo, g_tx_vfo, g_request_save_channel); + SETTINGS_save_channel(g_tx_vfo->channel_save, g_eeprom.config.setting.tx_vfo_num, g_tx_vfo, g_request_save_channel); if (g_current_display_screen != DISPLAY_SEARCH) if (g_vfo_configure_mode == VFO_CONFIGURE_NONE) // don't wipe previous variable setting @@ -2826,7 +2839,7 @@ Skip: } else { - RADIO_configure_channel(g_eeprom.tx_vfo, g_vfo_configure_mode); + RADIO_configure_channel(g_eeprom.config.setting.tx_vfo_num, g_vfo_configure_mode); } if (g_request_display_screen == DISPLAY_INVALID) @@ -2847,7 +2860,7 @@ Skip: RADIO_setup_registers(true); -// g_tx_vfo->freq_in_channel = BOARD_find_channel(frequency); +// g_tx_vfo->freq_in_channel = SETTINGS_find_channel(frequency); g_dtmf_auto_reset_time_500ms = 0; g_dtmf_call_state = DTMF_CALL_STATE_NONE; diff --git a/app/dtmf.c b/app/dtmf.c index eec0bec..b3389b7 100644 --- a/app/dtmf.c +++ b/app/dtmf.c @@ -93,9 +93,10 @@ bool DTMF_ValidateCodes(char *pCode, const unsigned int size) bool DTMF_GetContact(const int Index, char *pContact) { int i = -1; - if (Index >= 0 && Index < MAX_DTMF_CONTACTS && pContact != NULL) + if (Index >= 0 && Index < (int)ARRAY_SIZE(g_eeprom.config.dtmf_contact)) { - EEPROM_ReadBuffer(0x1C00 + (Index * 16), pContact, 16); + memcpy(pContact, &g_eeprom.config.dtmf_contact[Index], 16); +// EEPROM_ReadBuffer(0x1C00 + (Index * 16), pContact, 16); i = (int)pContact[0] - ' '; } return (i < 0 || i >= 95) ? false : true; @@ -159,7 +160,7 @@ bool DTMF_CompareMessage(const char *pMsg, const char *pTemplate, const unsigned { if (pMsg[i] != pTemplate[i]) { - if (!bCheckGroup || pMsg[i] != g_eeprom.dtmf_group_call_code) + if (!bCheckGroup || pMsg[i] != g_eeprom.config.setting.dtmf.group_call_code) return false; g_dtmf_IsGroupCall = true; } @@ -172,7 +173,7 @@ dtmf_call_mode_t DTMF_CheckGroupCall(const char *pMsg, const unsigned int size) { unsigned int i; for (i = 0; i < size; i++) - if (pMsg[i] == g_eeprom.dtmf_group_call_code) + if (pMsg[i] == g_eeprom.config.setting.dtmf.group_call_code) break; return (i < size) ? DTMF_CALL_MODE_GROUP : DTMF_CALL_MODE_NOT_GROUP; @@ -213,7 +214,7 @@ void DTMF_HandleRequest(void) } #ifdef ENABLE_KILL_REVIVE - if (!g_rx_vfo->dtmf_decoding_enable && !g_setting_radio_disabled) + if (!g_rx_vfo->dtmf_decoding_enable && !g_eeprom.config.setting.radio_disabled) #else if (!g_rx_vfo->dtmf_decoding_enable) #endif @@ -228,16 +229,16 @@ void DTMF_HandleRequest(void) if (g_dtmf_rx_index >= 9) { // look for the RADIO DISABLE code - sprintf(String, "%s%c%s", g_eeprom.ani_dtmf_id, g_eeprom.dtmf_separate_code, g_eeprom.kill_code); + sprintf(String, "%s%c%s", g_eeprom.config.setting.dtmf.ani_id, g_eeprom.config.setting.dtmf.separate_code, g_eeprom.config.setting.dtmf.kill_code); Offset = g_dtmf_rx_index - strlen(String); if (DTMF_CompareMessage(g_dtmf_rx + Offset, String, strlen(String), true)) { // bugger - if (g_eeprom.permit_remote_kill) + if (g_eeprom.config.setting.dtmf.permit_remote_kill != 0) { - g_setting_radio_disabled = true; // :( + g_eeprom.config.setting.radio_disabled = true; // :( DTMF_clear_RX(); @@ -269,14 +270,14 @@ void DTMF_HandleRequest(void) if (g_dtmf_rx_index >= 9) { // look for the REVIVE code - sprintf(String, "%s%c%s", g_eeprom.ani_dtmf_id, g_eeprom.dtmf_separate_code, g_eeprom.revive_code); + sprintf(String, "%s%c%s", g_eeprom.config.setting.dtmf.ani_id, g_eeprom.config.setting.dtmf.separate_code, g_eeprom.config.setting.dtmf.revive_code); Offset = g_dtmf_rx_index - strlen(String); if (DTMF_CompareMessage(g_dtmf_rx + Offset, String, strlen(String), true)) { // shit, we're back ! - g_setting_radio_disabled = false; + g_eeprom.config.setting.radio_disabled = false; DTMF_clear_RX(); @@ -318,7 +319,7 @@ void DTMF_HandleRequest(void) g_dtmf_rx_index >= 9) { // waiting for a reply - sprintf(String, "%s%c%s", g_dtmf_string, g_eeprom.dtmf_separate_code, "AAAAA"); + sprintf(String, "%s%c%s", g_dtmf_string, g_eeprom.config.setting.dtmf.separate_code, "AAAAA"); Offset = g_dtmf_rx_index - strlen(String); @@ -331,7 +332,7 @@ void DTMF_HandleRequest(void) } #ifdef ENABLE_KILL_REVIVE - if (g_setting_radio_disabled) + if (g_eeprom.config.setting.radio_disabled) return; // we've been disabled #endif @@ -340,7 +341,7 @@ void DTMF_HandleRequest(void) g_dtmf_IsGroupCall = false; - sprintf(String, "%s%c", g_eeprom.ani_dtmf_id, g_eeprom.dtmf_separate_code); + sprintf(String, "%s%c", g_eeprom.config.setting.dtmf.ani_id, g_eeprom.config.setting.dtmf.separate_code); Offset = g_dtmf_rx_index - strlen(String) - 3; @@ -358,7 +359,7 @@ void DTMF_HandleRequest(void) g_update_display = true; - switch (g_eeprom.dtmf_decode_response) + switch (g_eeprom.config.setting.dtmf.decode_response) { case DTMF_DEC_RESPONSE_BOTH: g_dtmf_decode_ring_tick_500ms = dtmf_decode_ring_500ms; @@ -386,7 +387,7 @@ void DTMF_HandleRequest(void) bool DTMF_Reply(void) { - const uint16_t Delay = (g_eeprom.dtmf_preload_time < 150) ? 150 : g_eeprom.dtmf_preload_time; + const uint16_t delay_ms = ((g_eeprom.config.setting.dtmf.preload_time < 15) ? 15 : g_eeprom.config.setting.dtmf.preload_time) * 10; const char *pString = NULL; char String[23]; @@ -399,7 +400,7 @@ bool DTMF_Reply(void) } else { // append our ID code onto the end of the DTMF code to send - sprintf(String, "%s%c%s", g_dtmf_string, g_eeprom.dtmf_separate_code, g_eeprom.ani_dtmf_id); + sprintf(String, "%s%c%s", g_dtmf_string, g_eeprom.config.setting.dtmf.separate_code, g_eeprom.config.setting.dtmf.ani_id); pString = String; } break; @@ -409,7 +410,7 @@ bool DTMF_Reply(void) break; case DTMF_REPLY_AAAAA: - sprintf(String, "%s%c%s", g_eeprom.ani_dtmf_id, g_eeprom.dtmf_separate_code, "AAAAA"); + sprintf(String, "%s%c%s", g_eeprom.config.setting.dtmf.ani_id, g_eeprom.config.setting.dtmf.separate_code, "AAAAA"); pString = String; break; @@ -425,7 +426,7 @@ bool DTMF_Reply(void) } // send TX-UP DTMF - pString = g_eeprom.dtmf_key_up_code; + pString = g_eeprom.config.setting.dtmf.key_up_code; break; } @@ -434,22 +435,22 @@ bool DTMF_Reply(void) if (pString == NULL) return false; - if (g_eeprom.dtmf_side_tone) + if (g_eeprom.config.setting.dtmf.side_tone) { // the user will also hear the transmitted tones GPIO_SetBit(&GPIOC->DATA, GPIOC_PIN_SPEAKER); } - SYSTEM_DelayMs(Delay); + SYSTEM_DelayMs(delay_ms); - BK4819_EnterDTMF_TX(g_eeprom.dtmf_side_tone); + BK4819_EnterDTMF_TX(g_eeprom.config.setting.dtmf.side_tone); BK4819_PlayDTMFString( pString, 1, - g_eeprom.dtmf_first_code_persist_time, - g_eeprom.dtmf_hash_code_persist_time, - g_eeprom.dtmf_code_persist_time, - g_eeprom.dtmf_code_interval_time); + g_eeprom.config.setting.dtmf.first_code_persist_time * 10, + g_eeprom.config.setting.dtmf.hash_code_persist_time * 10, + g_eeprom.config.setting.dtmf.code_persist_time * 10, + g_eeprom.config.setting.dtmf.code_interval_time * 10); GPIO_ClearBit(&GPIOC->DATA, GPIOC_PIN_SPEAKER); diff --git a/app/fm.c b/app/fm.c index cbce0be..32ffe82 100644 --- a/app/fm.c +++ b/app/fm.c @@ -37,7 +37,6 @@ #define STATE_USER_MODE 1 #define STATE_SAVE 2 -uint16_t g_fm_channels[20]; bool g_fm_radio_mode; fm_scan_state_dir_t g_fm_scan_state_dir; bool g_fm_auto_scan; @@ -51,19 +50,19 @@ volatile bool g_fm_schedule; bool FM_check_valid_channel(const unsigned int Channel) { - return (Channel < ARRAY_SIZE(g_fm_channels) && (g_fm_channels[Channel] >= BK1080_freq_lower && g_fm_channels[Channel] < BK1080_freq_upper)) ? true : false; + return (Channel < ARRAY_SIZE(g_eeprom.config.setting.fm_channel) && (g_eeprom.config.setting.fm_channel[Channel] >= BK1080_freq_lower && g_eeprom.config.setting.fm_channel[Channel] < BK1080_freq_upper)) ? true : false; } unsigned int FM_find_next_channel(unsigned int Channel, const fm_scan_state_dir_t scan_state_dir) { unsigned int i; - for (i = 0; i < ARRAY_SIZE(g_fm_channels); i++) + for (i = 0; i < ARRAY_SIZE(g_eeprom.config.setting.fm_channel); i++) { - if (Channel > ARRAY_SIZE(g_fm_channels)) - Channel = ARRAY_SIZE(g_fm_channels) - 1; + if (Channel > ARRAY_SIZE(g_eeprom.config.setting.fm_channel)) + Channel = ARRAY_SIZE(g_eeprom.config.setting.fm_channel) - 1; else - if (Channel >= ARRAY_SIZE(g_fm_channels)) + if (Channel >= ARRAY_SIZE(g_eeprom.config.setting.fm_channel)) Channel = 0; if (FM_check_valid_channel(Channel)) @@ -77,19 +76,17 @@ unsigned int FM_find_next_channel(unsigned int Channel, const fm_scan_state_dir_ int FM_configure_channel_state(void) { - g_eeprom.fm_frequency_playing = g_eeprom.fm_selected_frequency; - - if (g_eeprom.fm_channel_mode) + if (g_eeprom.config.setting.fm_radio.channel_mode != 0) { - const uint8_t Channel = FM_find_next_channel(g_eeprom.fm_selected_channel, FM_CHANNEL_UP); + const uint8_t Channel = FM_find_next_channel(g_eeprom.config.setting.fm_radio.selected_channel, FM_CHANNEL_UP); if (Channel == 0xFF) { - g_eeprom.fm_channel_mode = false; + g_eeprom.config.setting.fm_radio.channel_mode = 0; return -1; } - g_eeprom.fm_selected_channel = Channel; - g_eeprom.fm_frequency_playing = g_fm_channels[Channel]; + g_eeprom.config.setting.fm_radio.selected_channel = Channel; + g_eeprom.config.setting.fm_radio.selected_frequency = g_eeprom.config.setting.fm_channel[Channel]; } return 0; @@ -99,12 +96,11 @@ void FM_erase_channels(void) { unsigned int i; uint8_t Template[8]; - memset(Template, 0xFF, sizeof(Template)); for (i = 0; i < 5; i++) EEPROM_WriteBuffer8(0x0E40 + (i * 8), Template); - memset(g_fm_channels, 0xFF, sizeof(g_fm_channels)); + memset(&g_eeprom.config.setting.fm_channel, 0xff, sizeof(g_eeprom.config.setting.fm_channel)); } void FM_tune(uint16_t frequency, const fm_scan_state_dir_t scan_state_dir, const bool flag) @@ -117,7 +113,7 @@ void FM_tune(uint16_t frequency, const fm_scan_state_dir_t scan_state_dir, const g_fm_found_frequency = false; g_ask_to_save = false; g_ask_to_delete = false; - g_eeprom.fm_frequency_playing = frequency; + g_eeprom.config.setting.fm_radio.selected_frequency = frequency; if (!flag) { // wrap-a-around @@ -129,12 +125,12 @@ void FM_tune(uint16_t frequency, const fm_scan_state_dir_t scan_state_dir, const if (frequency >= BK1080_freq_upper) frequency = BK1080_freq_lower; - g_eeprom.fm_frequency_playing = frequency; + g_eeprom.config.setting.fm_radio.selected_frequency = frequency; } g_fm_scan_state_dir = scan_state_dir; - BK1080_SetFrequency(g_eeprom.fm_frequency_playing); + BK1080_SetFrequency(g_eeprom.config.setting.fm_radio.selected_frequency); if (g_fm_resume_tick_500ms < 10) g_fm_resume_tick_500ms = 10; // update display for next 5 seconds @@ -147,18 +143,18 @@ void FM_stop_scan(void) g_fm_scan_state_dir = FM_SCAN_STATE_DIR_OFF; - if (g_fm_auto_scan || g_eeprom.fm_channel_mode) + if (g_fm_auto_scan || g_eeprom.config.setting.fm_radio.channel_mode != 0) { // switch to channel mode - g_eeprom.fm_channel_mode = true; - g_eeprom.fm_selected_channel = 0; + g_eeprom.config.setting.fm_radio.channel_mode = 1; + g_eeprom.config.setting.fm_radio.selected_channel = 0; FM_configure_channel_state(); } else - { - g_eeprom.fm_channel_mode = false; + { // frequency mode + g_eeprom.config.setting.fm_radio.channel_mode = 0; } - BK1080_SetFrequency(g_eeprom.fm_frequency_playing); + BK1080_SetFrequency(g_eeprom.config.setting.fm_radio.selected_frequency); SETTINGS_save_fm(); @@ -209,15 +205,15 @@ Bail: void FM_scan(void) { - if (!FM_check_frequency_lock(g_eeprom.fm_frequency_playing, BK1080_freq_lower)) + if (!FM_check_frequency_lock(g_eeprom.config.setting.fm_radio.selected_frequency, BK1080_freq_lower)) { if (!g_fm_auto_scan) { g_fm_play_tick_10ms = 0; g_fm_found_frequency = true; - if (!g_eeprom.fm_channel_mode) - g_eeprom.fm_selected_frequency = g_eeprom.fm_frequency_playing; +// if (g_eeprom.config.setting.fm_radio.channel_mode == 0) +// g_eeprom.config.setting.fm_radio.selected_frequency = g_eeprom.fm_frequency_playing; GPIO_SetBit(&GPIOC->DATA, GPIOC_PIN_SPEAKER); @@ -225,10 +221,10 @@ void FM_scan(void) return; } - if (g_fm_channel_position < ARRAY_SIZE(g_fm_channels)) - g_fm_channels[g_fm_channel_position++] = g_eeprom.fm_frequency_playing; + if (g_fm_channel_position < ARRAY_SIZE(g_eeprom.config.setting.fm_channel)) + g_eeprom.config.setting.fm_channel[g_fm_channel_position++] = g_eeprom.config.setting.fm_radio.selected_frequency; - if (g_fm_channel_position >= ARRAY_SIZE(g_fm_channels)) + if (g_fm_channel_position >= ARRAY_SIZE(g_eeprom.config.setting.fm_channel)) { FM_stop_scan(); GUI_SelectNextDisplay(DISPLAY_FM); @@ -236,10 +232,10 @@ void FM_scan(void) } } - if (g_fm_auto_scan && g_eeprom.fm_frequency_playing >= (BK1080_freq_upper - 1u)) + if (g_fm_auto_scan && g_eeprom.config.setting.fm_radio.selected_frequency >= (BK1080_freq_upper - 1u)) FM_stop_scan(); else - FM_tune(g_eeprom.fm_frequency_playing, g_fm_scan_state_dir, false); + FM_tune(g_eeprom.config.setting.fm_radio.selected_frequency, g_fm_scan_state_dir, false); GUI_SelectNextDisplay(DISPLAY_FM); } @@ -255,7 +251,7 @@ void FM_turn_on(void) g_fm_resume_tick_500ms = fm_resume_500ms; // update display again in 'n' seconds // enable the FM radio chip/audio - BK1080_Init(g_eeprom.fm_frequency_playing, true); + BK1080_Init(g_eeprom.config.setting.fm_radio.selected_frequency, true); GPIO_SetBit(&GPIOC->DATA, GPIOC_PIN_SPEAKER); @@ -285,13 +281,13 @@ void FM_turn_off(void) void FM_toggle_chan_freq_mode(void) { - g_eeprom.fm_channel_mode = !g_eeprom.fm_channel_mode; + g_eeprom.config.setting.fm_radio.channel_mode = (g_eeprom.config.setting.fm_radio.channel_mode + 1) & 1u; FM_stop_scan(); if (!FM_configure_channel_state()) { - BK1080_SetFrequency(g_eeprom.fm_frequency_playing); + BK1080_SetFrequency(g_eeprom.config.setting.fm_radio.selected_frequency); g_request_save_fm = true; } } @@ -344,7 +340,7 @@ static void FM_Key_DIGITS(const key_code_t Key, const bool key_pressed, const bo if (g_fm_scan_state_dir != FM_SCAN_STATE_DIR_OFF) return; - State = g_eeprom.fm_channel_mode ? STATE_USER_MODE : STATE_FREQ_MODE; + State = (g_eeprom.config.setting.fm_radio.channel_mode != 0) ? STATE_USER_MODE : STATE_FREQ_MODE; } INPUTBOX_append(Key); @@ -379,14 +375,14 @@ static void FM_Key_DIGITS(const key_code_t Key, const bool key_pressed, const bo if (Frequency >= BK1080_freq_upper) Frequency = BK1080_freq_upper - 1u; - g_eeprom.fm_selected_frequency = (uint16_t)Frequency; + g_eeprom.config.setting.fm_radio.selected_frequency = (uint16_t)Frequency; #ifdef ENABLE_VOICE g_another_voice_id = (voice_id_t)Key; #endif - g_eeprom.fm_frequency_playing = g_eeprom.fm_selected_frequency; - BK1080_SetFrequency(g_eeprom.fm_frequency_playing); +// g_eeprom.fm_frequency_playing = g_eeprom.config.setting.fm_radio.selected_frequency; + BK1080_SetFrequency(g_eeprom.config.setting.fm_radio.selected_frequency); g_request_save_fm = true; return; @@ -409,17 +405,17 @@ static void FM_Key_DIGITS(const key_code_t Key, const bool key_pressed, const bo g_another_voice_id = (voice_id_t)Key; #endif - g_eeprom.fm_selected_channel = Channel; - g_eeprom.fm_frequency_playing = g_fm_channels[Channel]; + g_eeprom.config.setting.fm_radio.selected_channel = Channel; + g_eeprom.config.setting.fm_radio.selected_frequency = g_eeprom.config.setting.fm_channel[Channel]; - BK1080_SetFrequency(g_eeprom.fm_frequency_playing); + BK1080_SetFrequency(g_eeprom.config.setting.fm_radio.selected_frequency); g_request_save_fm = true; return; } } else - if (Channel < ARRAY_SIZE(g_fm_channels)) + if (Channel < ARRAY_SIZE(g_eeprom.config.setting.fm_channel)) { #ifdef ENABLE_VOICE g_another_voice_id = (voice_id_t)Key; @@ -555,8 +551,8 @@ static void FM_Key_MENU(const bool key_pressed, const bool key_held) return; // key still pressed // see if the frequency is already stored in a channel - for (i = 0; i < ARRAY_SIZE(g_fm_channels) && channel < 0; i++) - if (g_fm_channels[i] == g_eeprom.fm_frequency_playing) + for (i = 0; i < ARRAY_SIZE(g_eeprom.config.setting.fm_channel) && channel < 0; i++) + if (g_eeprom.config.setting.fm_channel[i] == g_eeprom.config.setting.fm_radio.selected_frequency) channel = i; // found it in the channel list g_request_display_screen = DISPLAY_FM; @@ -568,16 +564,16 @@ static void FM_Key_MENU(const bool key_pressed, const bool key_held) if (g_fm_scan_state_dir == FM_SCAN_STATE_DIR_OFF) { // not scanning - if (!g_eeprom.fm_channel_mode) + if (g_eeprom.config.setting.fm_radio.channel_mode == 0) { // frequency mode if (g_ask_to_save) { if (channel < 0) { - g_fm_channels[g_fm_channel_position] = g_eeprom.fm_frequency_playing; - g_ask_to_save = false; - g_request_save_fm = true; + g_eeprom.config.setting.fm_channel[g_fm_channel_position] = g_eeprom.config.setting.fm_radio.selected_frequency; + g_ask_to_save = false; + g_request_save_fm = true; } } else @@ -588,10 +584,10 @@ static void FM_Key_MENU(const bool key_pressed, const bool key_held) { // channel mode if (g_ask_to_delete) { - g_fm_channels[g_eeprom.fm_selected_channel] = 0xFFFF; + g_eeprom.config.setting.fm_channel[g_eeprom.config.setting.fm_radio.selected_channel] = 0xFFFF; FM_configure_channel_state(); - BK1080_SetFrequency(g_eeprom.fm_frequency_playing); + BK1080_SetFrequency(g_eeprom.config.setting.fm_radio.selected_frequency); g_request_save_fm = true; g_ask_to_delete = false; @@ -621,7 +617,7 @@ static void FM_Key_MENU(const bool key_pressed, const bool key_held) if (g_ask_to_save) { - g_fm_channels[g_fm_channel_position] = g_eeprom.fm_frequency_playing; + g_eeprom.config.setting.fm_channel[g_fm_channel_position] = g_eeprom.config.setting.fm_radio.selected_frequency; g_ask_to_save = false; g_request_save_fm = true; return; @@ -664,32 +660,32 @@ static void FM_Key_UP_DOWN(const bool key_pressed, const bool key_held, const fm if (g_fm_auto_scan) return; - FM_tune(g_eeprom.fm_frequency_playing, scan_state_dir, false); + FM_tune(g_eeprom.config.setting.fm_radio.selected_frequency, scan_state_dir, false); g_request_display_screen = DISPLAY_FM; return; } - if (g_eeprom.fm_channel_mode) + if (g_eeprom.config.setting.fm_radio.channel_mode != 0) { // we're in channel mode - const uint8_t Channel = FM_find_next_channel(g_eeprom.fm_selected_channel + scan_state_dir, scan_state_dir); - if (Channel == 0xFF || g_eeprom.fm_selected_channel == Channel) + const uint8_t Channel = FM_find_next_channel(g_eeprom.config.setting.fm_radio.selected_channel + scan_state_dir, scan_state_dir); + if (Channel == 0xFF || g_eeprom.config.setting.fm_radio.selected_channel == Channel) goto Bail; - g_eeprom.fm_selected_channel = Channel; - g_eeprom.fm_frequency_playing = g_fm_channels[Channel]; + g_eeprom.config.setting.fm_radio.selected_channel = Channel; + g_eeprom.config.setting.fm_radio.selected_frequency = g_eeprom.config.setting.fm_channel[Channel]; } else { // no, frequency mode - uint16_t Frequency = g_eeprom.fm_selected_frequency + scan_state_dir; + uint16_t Frequency = g_eeprom.config.setting.fm_radio.selected_frequency + scan_state_dir; if (Frequency < BK1080_freq_lower) Frequency = BK1080_freq_upper - 1u; else if (Frequency >= BK1080_freq_upper) Frequency = BK1080_freq_lower; - g_eeprom.fm_frequency_playing = Frequency; - g_eeprom.fm_selected_frequency = g_eeprom.fm_frequency_playing; + g_eeprom.config.setting.fm_radio.selected_frequency = Frequency; +// g_eeprom.config.setting.fm_radio.selected_frequency = g_eeprom.fm_frequency_playing; } if (g_current_display_screen == DISPLAY_FM && g_fm_scan_state_dir == FM_SCAN_STATE_DIR_OFF) @@ -704,7 +700,7 @@ static void FM_Key_UP_DOWN(const bool key_pressed, const bool key_held, const fm g_request_save_fm = true; Bail: - BK1080_SetFrequency(g_eeprom.fm_frequency_playing); + BK1080_SetFrequency(g_eeprom.config.setting.fm_radio.selected_frequency); g_request_display_screen = DISPLAY_FM; } diff --git a/app/fm.h b/app/fm.h index 9d3f922..62e1771 100644 --- a/app/fm.h +++ b/app/fm.h @@ -29,7 +29,6 @@ enum fm_scan_state_dir_e { }; typedef enum fm_scan_state_dir_e fm_scan_state_dir_t; -extern uint16_t g_fm_channels[20]; extern bool g_fm_radio_mode; extern fm_scan_state_dir_t g_fm_scan_state_dir; extern bool g_fm_auto_scan; diff --git a/app/generic.c b/app/generic.c index aead342..1c9e81a 100644 --- a/app/generic.c +++ b/app/generic.c @@ -56,10 +56,10 @@ void GENERIC_Key_F(bool key_pressed, bool key_held) { // toggle the keyboad lock #ifdef ENABLE_VOICE - g_another_voice_id = g_eeprom.key_lock ? VOICE_ID_UNLOCK : VOICE_ID_LOCK; + g_another_voice_id = g_eeprom.config.setting.key_lock ? VOICE_ID_UNLOCK : VOICE_ID_LOCK; #endif - g_eeprom.key_lock = !g_eeprom.key_lock; + g_eeprom.config.setting.key_lock = (g_eeprom.key_lock + 1) & 1u; g_request_save_settings = true; g_update_status = true; @@ -113,10 +113,10 @@ void GENERIC_Key_PTT(bool key_pressed) { APP_end_tx(); - if (g_eeprom.repeater_tail_tone_elimination == 0) + if (g_eeprom.config.setting.repeater_tail_tone_elimination == 0) FUNCTION_Select(FUNCTION_FOREGROUND); else - g_rtte_count_down = g_eeprom.repeater_tail_tone_elimination * 10; + g_rtte_count_down = g_eeprom.config.setting.repeater_tail_tone_elimination * 10; } g_flag_end_tx = false; @@ -147,7 +147,7 @@ void GENERIC_Key_PTT(bool key_pressed) if (g_current_display_screen == DISPLAY_SEARCH) { // CTCSS/CDCSS scanning .. stop - g_eeprom.cross_vfo_rx_tx = g_backup_cross_vfo_rx_tx; + g_eeprom.config.setting.cross_vfo = g_backup_cross_vfo; g_search_flag_stop_scan = true; g_vfo_configure_mode = VFO_CONFIGURE_RELOAD; g_flag_reset_vfos = true; diff --git a/app/main.c b/app/main.c index 0041824..9444a00 100644 --- a/app/main.c +++ b/app/main.c @@ -47,7 +47,7 @@ bool g_manual_scanning; bool scanning_paused(void) { - if ((g_scan_state_dir != SCAN_STATE_DIR_OFF || g_eeprom.dual_watch != DUAL_WATCH_OFF) && + if ((g_scan_state_dir != SCAN_STATE_DIR_OFF || g_eeprom.config.setting.dual_watch != DUAL_WATCH_OFF) && g_scan_pause_tick_10ms > 0 && g_scan_pause_tick_10ms <= (200 / 10)) { // scanning isn't paused return false; @@ -105,9 +105,9 @@ void toggle_chan_scanlist(void) void MAIN_copy_mem_vfo_mem(void) { //const unsigned int vfo = get_RX_VFO(); - const unsigned int vfo = g_eeprom.tx_vfo; + const unsigned int vfo = g_eeprom.config.setting.tx_vfo_num; - if (g_css_scan_mode != CSS_SCAN_MODE_OFF || !g_eeprom.vfo_open) + if (g_css_scan_mode != CSS_SCAN_MODE_OFF || g_eeprom.config.setting.vfo_open == 0) { // scanning or VFO disabled g_beep_to_play = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL; return; @@ -119,14 +119,14 @@ void toggle_chan_scanlist(void) return; } - if (IS_USER_CHANNEL(g_eeprom.screen_channel[vfo])) + if (IS_USER_CHANNEL(g_eeprom.config.setting.indices.vfo[vfo].screen)) { // copy channel to VFO, then swap to the VFO - const unsigned int channel = FREQ_CHANNEL_FIRST + g_eeprom.vfo_info[vfo].band; + const unsigned int channel = FREQ_CHANNEL_FIRST + g_vfo_info[vfo].band; - g_eeprom.screen_channel[vfo] = channel; - g_eeprom.vfo_info[vfo].channel_save = channel; - g_eeprom.tx_vfo = vfo; + g_eeprom.config.setting.indices.vfo[vfo].screen = channel; + g_vfo_info[vfo].channel_save = channel; + g_eeprom.config.setting.tx_vfo_num = vfo; RADIO_select_vfos(); RADIO_ApplyOffset(g_tx_vfo, false); @@ -135,9 +135,9 @@ void toggle_chan_scanlist(void) RADIO_setup_registers(true); // find the first channel that contains this frequency - g_tx_vfo->freq_in_channel = BOARD_find_channel(g_tx_vfo->freq_config_tx.frequency); + g_tx_vfo->freq_in_channel = SETTINGS_find_channel(g_tx_vfo->freq_config_tx.frequency); - SETTINGS_save_channel(g_tx_vfo->channel_save, g_eeprom.tx_vfo, g_tx_vfo, 1); + SETTINGS_save_channel(g_tx_vfo->channel_save, g_eeprom.config.setting.tx_vfo_num, g_tx_vfo, 1); #if defined(ENABLE_UART) && defined(ENABLE_UART_DEBUG) UART_printf("chan-vfo %u\r\n", g_tx_vfo->channel_save); @@ -150,14 +150,14 @@ void toggle_chan_scanlist(void) g_update_display = true; } else - if (IS_NOT_NOAA_CHANNEL(g_eeprom.screen_channel[vfo])) + if (IS_NOT_NOAA_CHANNEL(g_eeprom.config.setting.indices.vfo[vfo].screen)) { // copy VFO to a channel // search the channels to see if the frequency is already present - unsigned int chan = BOARD_find_channel(g_eeprom.vfo_info[vfo].p_tx->frequency); + unsigned int chan = SETTINGS_find_channel(g_vfo_info[vfo].p_tx->frequency); if (chan > USER_CHANNEL_LAST) { // not found - find next free channel to save too - //for (chan = g_eeprom.screen_channel[vfo]; chan <= USER_CHANNEL_LAST; chan++) + //for (chan = g_eeprom.config.setting.indices.vfo[vfo].screen; chan <= USER_CHANNEL_LAST; chan++) for (chan = 0; chan <= USER_CHANNEL_LAST; chan++) if (!RADIO_CheckValidChannel(chan, false, vfo)) break; @@ -192,7 +192,7 @@ void toggle_chan_scanlist(void) void processFKeyFunction(const key_code_t Key) { uint8_t Band; - uint8_t Vfo = g_eeprom.tx_vfo; + uint8_t vfo = g_eeprom.config.setting.tx_vfo_num; if (g_current_function == FUNCTION_TRANSMIT || g_current_display_screen == DISPLAY_MENU) { @@ -243,7 +243,7 @@ void processFKeyFunction(const key_code_t Key) APP_stop_scan(); Band = g_tx_vfo->band + 1; - if (g_setting_350_enable || Band != BAND5_350MHz) + if (g_eeprom.config.setting.enable_350 || Band != BAND5_350MHz) { if (Band > BAND7_470MHz) Band = BAND1_50MHz; // wrap-a-round @@ -252,8 +252,8 @@ void processFKeyFunction(const key_code_t Key) Band = BAND6_400MHz; // jump to next band g_tx_vfo->band = Band; - g_eeprom.screen_channel[Vfo] = FREQ_CHANNEL_FIRST + Band; - g_eeprom.freq_channel[Vfo] = FREQ_CHANNEL_FIRST + Band; + g_eeprom.config.setting.indices.vfo[vfo].screen = FREQ_CHANNEL_FIRST + Band; + g_eeprom.config.setting.indices.vfo[vfo].frequency = FREQ_CHANNEL_FIRST + Band; g_request_save_vfo = true; g_vfo_configure_mode = VFO_CONFIGURE_RELOAD; @@ -265,19 +265,19 @@ void processFKeyFunction(const key_code_t Key) APP_stop_scan(); - if (g_eeprom.cross_vfo_rx_tx == CROSS_BAND_CHAN_A) - g_eeprom.cross_vfo_rx_tx = CROSS_BAND_CHAN_B; + if (g_eeprom.config.setting.cross_vfo == CROSS_BAND_CHAN_A) + g_eeprom.config.setting.cross_vfo = CROSS_BAND_CHAN_B; else - if (g_eeprom.cross_vfo_rx_tx == CROSS_BAND_CHAN_B) - g_eeprom.cross_vfo_rx_tx = CROSS_BAND_CHAN_A; + if (g_eeprom.config.setting.cross_vfo == CROSS_BAND_CHAN_B) + g_eeprom.config.setting.cross_vfo = CROSS_BAND_CHAN_A; else - if (g_eeprom.dual_watch == DUAL_WATCH_CHAN_A) - g_eeprom.dual_watch = DUAL_WATCH_CHAN_B; + if (g_eeprom.config.setting.dual_watch == DUAL_WATCH_CHAN_A) + g_eeprom.config.setting.dual_watch = DUAL_WATCH_CHAN_B; else - if (g_eeprom.dual_watch == DUAL_WATCH_CHAN_B) - g_eeprom.dual_watch = DUAL_WATCH_CHAN_A; + if (g_eeprom.config.setting.dual_watch == DUAL_WATCH_CHAN_B) + g_eeprom.config.setting.dual_watch = DUAL_WATCH_CHAN_A; else - g_eeprom.tx_vfo = (Vfo + 1) & 1u; + g_eeprom.config.setting.tx_vfo_num = (vfo + 1) & 1u; g_request_save_settings = 1; g_flag_reconfigure_vfos = true; @@ -289,13 +289,13 @@ void processFKeyFunction(const key_code_t Key) APP_stop_scan(); - if (g_eeprom.vfo_open && IS_NOT_NOAA_CHANNEL(g_tx_vfo->channel_save)) + if (g_eeprom.config.setting.vfo_open > 0 && IS_NOT_NOAA_CHANNEL(g_tx_vfo->channel_save)) { uint8_t Channel; if (IS_USER_CHANNEL(g_tx_vfo->channel_save)) { // swap to frequency mode - g_eeprom.screen_channel[Vfo] = g_eeprom.freq_channel[g_eeprom.tx_vfo]; + g_eeprom.config.setting.indices.vfo[vfo].screen = g_eeprom.config.setting.indices.vfo[g_eeprom.config.setting.tx_vfo_num].frequency; #ifdef ENABLE_VOICE g_another_voice_id = VOICE_ID_FREQUENCY_MODE; @@ -306,10 +306,10 @@ void processFKeyFunction(const key_code_t Key) break; } - Channel = RADIO_FindNextChannel(g_eeprom.user_channel[g_eeprom.tx_vfo], 1, false, 0); + Channel = RADIO_FindNextChannel(g_eeprom.config.setting.indices.vfo[g_eeprom.config.setting.tx_vfo_num].user, 1, false, 0); if (Channel != 0xFF) { // swap to channel mode - g_eeprom.screen_channel[Vfo] = Channel; + g_eeprom.config.setting.indices.vfo[vfo].screen= Channel; #ifdef ENABLE_VOICE AUDIO_SetVoiceID(0, VOICE_ID_CHANNEL_MODE); @@ -333,8 +333,8 @@ void processFKeyFunction(const key_code_t Key) g_search_flag_start_scan = true; g_search_single_frequency = false; - g_backup_cross_vfo_rx_tx = g_eeprom.cross_vfo_rx_tx; - g_eeprom.cross_vfo_rx_tx = CROSS_BAND_OFF; + g_backup_cross_vfo = g_eeprom.config.setting.cross_vfo; + g_eeprom.config.setting.cross_vfo = CROSS_BAND_OFF; break; case KEY_5: // NOAA @@ -345,11 +345,11 @@ void processFKeyFunction(const key_code_t Key) if (IS_NOT_NOAA_CHANNEL(g_tx_vfo->channel_save)) { - g_eeprom.screen_channel[Vfo] = g_eeprom.noaa_channel[g_eeprom.tx_vfo]; + g_eeprom.config.setting.indices.vfo[vfo].screen = g_eeprom.config.setting.indices.noaa_channel[g_eeprom.config.setting.tx_vfo_num]; } else { - g_eeprom.screen_channel[Vfo] = g_eeprom.freq_channel[g_eeprom.tx_vfo]; + g_eeprom.config.setting.indices.vfo[vfo].screen = g_eeprom.config.setting.indices.vfo[g_eeprom.config.setting.tx_vfo_num].frequency; #ifdef ENABLE_VOICE g_another_voice_id = VOICE_ID_FREQUENCY_MODE; @@ -404,7 +404,7 @@ void processFKeyFunction(const key_code_t Key) case KEY_9: // CALL - if (!RADIO_CheckValidChannel(g_eeprom2.config.call1, false, 0)) + if (!RADIO_CheckValidChannel(g_eeprom.config.setting.call1, false, 0)) { g_beep_to_play = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL; return; @@ -414,13 +414,13 @@ void processFKeyFunction(const key_code_t Key) APP_stop_scan(); - g_eeprom.user_channel[Vfo] = g_eeprom2.config.call1; - g_eeprom.screen_channel[Vfo] = g_eeprom2.config.call1; + g_eeprom.config.setting.indices.vfo[vfo].user = g_eeprom.config.setting.call1; + g_eeprom.config.setting.indices.vfo[vfo].screen = g_eeprom.config.setting.call1; #ifdef ENABLE_VOICE AUDIO_SetVoiceID(0, VOICE_ID_CHANNEL_MODE); - AUDIO_SetDigitVoice(1, 1 + g_eeprom2.config.call1); - g_another_voice_id = (voice_id_t)0xFE; + AUDIO_SetDigitVoice(1, 1 + g_eeprom.config.setting.call1); + g_another_voice_id = (voice_id_t)0xFE; #endif g_request_save_vfo = true; @@ -436,6 +436,8 @@ void processFKeyFunction(const key_code_t Key) void MAIN_Key_DIGITS(key_code_t Key, bool key_pressed, bool key_held) { + const uint8_t vfo = g_eeprom.config.setting.tx_vfo_num; + g_key_input_count_down = key_input_timeout_500ms; if (key_held) @@ -477,8 +479,6 @@ void MAIN_Key_DIGITS(key_code_t Key, bool key_pressed, bool key_held) return; } - const uint8_t Vfo = g_eeprom.tx_vfo; - if (g_scan_state_dir != SCAN_STATE_DIR_OFF || g_current_function == FUNCTION_TRANSMIT) { g_beep_to_play = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL; @@ -520,8 +520,8 @@ void MAIN_Key_DIGITS(key_code_t Key, bool key_pressed, bool key_held) g_another_voice_id = (voice_id_t)Key; #endif - g_eeprom.user_channel[Vfo] = (uint8_t)Channel; - g_eeprom.screen_channel[Vfo] = (uint8_t)Channel; + g_eeprom.config.setting.indices.vfo[vfo].user = (uint8_t)Channel; + g_eeprom.config.setting.indices.vfo[vfo].screen = (uint8_t)Channel; g_request_save_vfo = true; g_vfo_configure_mode = VFO_CONFIGURE_RELOAD; @@ -568,13 +568,13 @@ void MAIN_Key_DIGITS(key_code_t Key, bool key_pressed, bool key_held) if (g_tx_vfo->band != band) { - g_tx_vfo->band = band; - g_eeprom.screen_channel[Vfo] = band + FREQ_CHANNEL_FIRST; - g_eeprom.freq_channel[Vfo] = band + FREQ_CHANNEL_FIRST; + g_tx_vfo->band = band; + g_eeprom.config.setting.indices.vfo[vfo].screen = band + FREQ_CHANNEL_FIRST; + g_eeprom.config.setting.indices.vfo[vfo].frequency = band + FREQ_CHANNEL_FIRST; SETTINGS_save_vfo_indices(); - RADIO_configure_channel(Vfo, VFO_CONFIGURE_RELOAD); + RADIO_configure_channel(vfo, VFO_CONFIGURE_RELOAD); } Frequency += g_tx_vfo->step_freq / 2; // for rounding to nearest step size @@ -590,7 +590,7 @@ void MAIN_Key_DIGITS(key_code_t Key, bool key_pressed, bool key_held) g_tx_vfo->freq_config_tx.frequency = Frequency; // find the first channel that contains this frequency - g_tx_vfo->freq_in_channel = BOARD_find_channel(Frequency); + g_tx_vfo->freq_in_channel = SETTINGS_find_channel(Frequency); g_request_save_channel = 1; g_vfo_configure_mode = VFO_CONFIGURE; @@ -610,7 +610,7 @@ void MAIN_Key_DIGITS(key_code_t Key, bool key_pressed, bool key_held) if (g_input_box_index != 2) { #ifdef ENABLE_VOICE - g_another_voice_id = (voice_id_t)Key; + g_another_voice_id = (voice_id_t)Key; #endif // g_request_display_screen = DISPLAY_MAIN; return; @@ -621,15 +621,15 @@ void MAIN_Key_DIGITS(key_code_t Key, bool key_pressed, bool key_held) Channel = (g_input_box[0] * 10) + g_input_box[1]; if (Channel >= 1 && Channel <= ARRAY_SIZE(NOAA_FREQUENCY_TABLE)) { - Channel += NOAA_CHANNEL_FIRST; + Channel += NOAA_CHANNEL_FIRST; #ifdef ENABLE_VOICE - g_another_voice_id = (voice_id_t)Key; + g_another_voice_id = (voice_id_t)Key; #endif - g_eeprom.noaa_channel[Vfo] = Channel; - g_eeprom.screen_channel[Vfo] = Channel; - g_request_save_vfo = true; - g_vfo_configure_mode = VFO_CONFIGURE_RELOAD; - g_update_display = true; + g_eeprom.config.setting.indices.noaa_channel[vfo] = Channel; + g_eeprom.config.setting.indices.vfo[vfo].screen = Channel; + g_request_save_vfo = true; + g_vfo_configure_mode = VFO_CONFIGURE_RELOAD; + g_update_display = true; return; } } @@ -845,8 +845,8 @@ void MAIN_Key_STAR(bool key_pressed, bool key_held) // scan the CTCSS/DCS code g_search_flag_start_scan = true; g_search_single_frequency = true; - g_backup_cross_vfo_rx_tx = g_eeprom.cross_vfo_rx_tx; - g_eeprom.cross_vfo_rx_tx = CROSS_BAND_OFF; + g_backup_cross_vfo = g_eeprom.config.setting.cross_vfo; + g_eeprom.config.setting.cross_vfo = CROSS_BAND_OFF; } g_ptt_was_released = true; @@ -860,7 +860,7 @@ void MAIN_Key_UP_DOWN(bool key_pressed, bool key_held, scan_state_dir_t Directio static bool monitor_was_enabled = false; #endif - uint8_t Channel = g_eeprom.screen_channel[g_eeprom.tx_vfo]; + uint8_t Channel = g_eeprom.config.setting.indices.vfo[g_eeprom.config.setting.tx_vfo_num].screen; if (key_pressed && !key_held) { // key just pressed @@ -886,9 +886,9 @@ void MAIN_Key_UP_DOWN(bool key_pressed, bool key_held, scan_state_dir_t Directio g_tx_vfo->freq_config_tx.frequency = g_tx_vfo->freq_config_rx.frequency; // find the first channel that contains this frequency - g_tx_vfo->freq_in_channel = BOARD_find_channel(g_tx_vfo->freq_config_rx.frequency); + g_tx_vfo->freq_in_channel = SETTINGS_find_channel(g_tx_vfo->freq_config_rx.frequency); - SETTINGS_save_channel(g_tx_vfo->channel_save, g_eeprom.tx_vfo, g_tx_vfo, 1); + SETTINGS_save_channel(g_tx_vfo->channel_save, g_eeprom.config.setting.tx_vfo_num, g_tx_vfo, 1); RADIO_ApplyOffset(g_tx_vfo, true); @@ -969,7 +969,7 @@ void MAIN_Key_UP_DOWN(bool key_pressed, bool key_held, scan_state_dir_t Directio // TODO: include this once we have the entire eeprom loaded // //if (!key_held && key_pressed) - // g_tx_vfo->freq_in_channel = BOARD_find_channel(frequency); + // g_tx_vfo->freq_in_channel = SETTINGS_find_channel(frequency); //else //if (key_held && key_pressed) g_tx_vfo->freq_in_channel = 0xff; @@ -1027,8 +1027,8 @@ void MAIN_Key_UP_DOWN(bool key_pressed, bool key_held, scan_state_dir_t Directio } #endif - g_eeprom.user_channel[g_eeprom.tx_vfo] = Next; - g_eeprom.screen_channel[g_eeprom.tx_vfo] = Next; + g_eeprom.config.setting.indices.vfo[g_eeprom.config.setting.tx_vfo_num].user = Next; + g_eeprom.config.setting.indices.vfo[g_eeprom.config.setting.tx_vfo_num].screen = Next; if (!key_held) { @@ -1041,9 +1041,9 @@ void MAIN_Key_UP_DOWN(bool key_pressed, bool key_held, scan_state_dir_t Directio #ifdef ENABLE_NOAA else { - Channel = NOAA_CHANNEL_FIRST + NUMBER_AddWithWraparound(g_eeprom.screen_channel[g_eeprom.tx_vfo] - NOAA_CHANNEL_FIRST, Direction, 0, 9); - g_eeprom.noaa_channel[g_eeprom.tx_vfo] = Channel; - g_eeprom.screen_channel[g_eeprom.tx_vfo] = Channel; + Channel = NOAA_CHANNEL_FIRST + NUMBER_AddWithWraparound(g_eeprom.config.setting.indices.vfo[g_eeprom.config.setting.tx_vfo_num].screen - NOAA_CHANNEL_FIRST, Direction, 0, 9); + g_eeprom.config.setting.indices.noaa_channel[g_eeprom.config.setting.tx_vfo_num] = Channel; + g_eeprom.config.setting.indices.vfo[g_eeprom.config.setting.tx_vfo_num].screen = Channel; } #endif diff --git a/app/menu.c b/app/menu.c index 791961b..808edd2 100644 --- a/app/menu.c +++ b/app/menu.c @@ -61,7 +61,7 @@ uint8_t dac_gain; } __attribute__((packed)) misc; - g_eeprom.BK4819_xtal_freq_low = value; + g_eeprom.config.setting.BK4819_xtal_freq_low = value; // radio 1 .. 04 00 46 00 50 00 2C 0E // radio 2 .. 05 00 46 00 50 00 2C 0E @@ -406,7 +406,7 @@ void MENU_AcceptSetting(void) return; case MENU_SQL: - g_eeprom.squelch_level = g_sub_menu_selection; + g_eeprom.config.setting.squelch_level = g_sub_menu_selection; g_vfo_configure_mode = VFO_CONFIGURE; break; @@ -509,7 +509,7 @@ void MENU_AcceptSetting(void) case MENU_SCRAMBLER: g_tx_vfo->scrambling_type = g_sub_menu_selection; #if 0 - if (g_sub_menu_selection > 0 && g_setting_scramble_enable) + if (g_sub_menu_selection > 0 && g_eeprom.config.setting.enable_scrambler) BK4819_EnableScramble(g_sub_menu_selection - 1); else BK4819_DisableScramble(); @@ -525,13 +525,13 @@ void MENU_AcceptSetting(void) case MENU_MEM_SAVE: g_tx_vfo->channel_save = g_sub_menu_selection; #if 0 - g_eeprom.user_channel[0] = g_sub_menu_selection; + g_eeprom.config.setting.indices.vfo[0].user = g_sub_menu_selection; #else - g_eeprom.user_channel[g_eeprom.tx_vfo] = g_sub_menu_selection; + g_eeprom.config.setting.indices.vfo[g_eeprom.config.setting.tx_vfo_num].user = g_sub_menu_selection; #endif g_request_save_channel = 2; g_vfo_configure_mode = VFO_CONFIGURE_RELOAD; - g_flag_reset_vfos = true; + g_flag_reset_vfos = true; return; case MENU_MEM_NAME: @@ -547,30 +547,30 @@ void MENU_AcceptSetting(void) // save the channel name memset(g_tx_vfo->name, 0, sizeof(g_tx_vfo->name)); memcpy(g_tx_vfo->name, g_edit, 10); - SETTINGS_save_channel(g_sub_menu_selection, g_eeprom.tx_vfo, g_tx_vfo, 3); + SETTINGS_save_channel(g_sub_menu_selection, g_eeprom.config.setting.tx_vfo_num, g_tx_vfo, 3); g_flag_reconfigure_vfos = true; return; case MENU_BAT_SAVE: - g_eeprom.battery_save = g_sub_menu_selection; + g_eeprom.config.setting.battery_save_ratio = g_sub_menu_selection; break; #ifdef ENABLE_VOX case MENU_VOX: - g_eeprom.vox_switch = g_sub_menu_selection != 0; - if (g_eeprom.vox_switch) - g_eeprom.vox_level = g_sub_menu_selection - 1; + g_eeprom.config.setting.vox_switch = g_sub_menu_selection != 0; + if (g_eeprom.config.setting.vox_switch) + g_eeprom.config.setting.vox_level = g_sub_menu_selection - 1; g_flag_reconfigure_vfos = true; g_update_status = true; break; #endif case MENU_AUTO_BACKLITE: - g_eeprom.backlight = g_sub_menu_selection; + g_eeprom.config.setting.backlight_time = g_sub_menu_selection; break; case MENU_AUTO_BACKLITE_ON_TX_RX: - g_setting_backlight_on_tx_rx = g_sub_menu_selection; + g_eeprom.config.setting.backlight_on_tx_rx = g_sub_menu_selection; break; #ifdef ENABLE_CONTRAST @@ -581,54 +581,54 @@ void MENU_AcceptSetting(void) #endif case MENU_DUAL_WATCH: -// g_eeprom.dual_watch = g_sub_menu_selection; - g_eeprom.dual_watch = (g_sub_menu_selection > 0) ? 1 + g_eeprom.tx_vfo : DUAL_WATCH_OFF; +// g_eeprom.config.setting.dual_watch = g_sub_menu_selection; + g_eeprom.config.setting.dual_watch = (g_sub_menu_selection > 0) ? 1 + g_eeprom.config.setting.tx_vfo_num : DUAL_WATCH_OFF; g_flag_reconfigure_vfos = true; g_update_status = true; break; case MENU_SCAN_HOLD: - g_eeprom.scan_hold_time_500ms = g_sub_menu_selection; + g_eeprom.config.setting.scan_hold_time = g_sub_menu_selection; break; case MENU_CROSS_VFO: - if (IS_NOAA_CHANNEL(g_eeprom.screen_channel[0])) + if (IS_NOAA_CHANNEL(g_eeprom.config.setting.indices.vfo[0].screen)) return; - if (IS_NOAA_CHANNEL(g_eeprom.screen_channel[1])) + if (IS_NOAA_CHANNEL(g_eeprom.config.setting.indices.vfo[1].screen)) return; - g_eeprom.cross_vfo_rx_tx = g_sub_menu_selection; + g_eeprom.config.setting.cross_vfo = g_sub_menu_selection; g_flag_reconfigure_vfos = true; g_update_status = true; break; case MENU_BEEP: - g_eeprom.beep_control = g_sub_menu_selection; + g_eeprom.config.setting.beep_control = g_sub_menu_selection; break; case MENU_TX_TO: - g_eeprom.tx_timeout_timer = g_sub_menu_selection; + g_eeprom.config.setting.tx_timeout = g_sub_menu_selection; break; #ifdef ENABLE_VOICE case MENU_VOICE: - g_eeprom.voice_prompt = g_sub_menu_selection; + g_eeprom.config.setting.voice_prompt = g_sub_menu_selection; g_update_status = true; break; #endif case MENU_SCAN_CAR_RESUME: - g_eeprom.scan_resume_mode = g_sub_menu_selection; + g_eeprom.config.setting.carrier_search_mode = g_sub_menu_selection; break; case MENU_MEM_DISP: - g_eeprom.channel_display_mode = g_sub_menu_selection; + g_eeprom.config.setting.channel_display_mode = g_sub_menu_selection; break; #ifdef ENABLE_KEYLOCK case MENU_AUTO_KEY_LOCK: - g_eeprom.auto_keypad_lock = g_sub_menu_selection; + g_eeprom.config.setting.auto_key_lock = g_sub_menu_selection; g_key_lock_tick_500ms = key_lock_timeout_500ms; break; #endif @@ -648,29 +648,29 @@ void MENU_AcceptSetting(void) return; case MENU_STE: - g_eeprom.tail_note_elimination = g_sub_menu_selection; + g_eeprom.config.setting.tail_tone_elimination = g_sub_menu_selection; break; case MENU_RP_STE: - g_eeprom.repeater_tail_tone_elimination = g_sub_menu_selection; + g_eeprom.config.setting.repeater_tail_tone_elimination = g_sub_menu_selection; break; case MENU_MIC_GAIN: - g_eeprom.mic_sensitivity = g_sub_menu_selection; - g_eeprom.mic_sensitivity_tuning = g_mic_gain_dB_2[g_eeprom.mic_sensitivity]; - BK4819_set_mic_gain(g_eeprom.mic_sensitivity_tuning); + g_eeprom.config.setting.mic_sensitivity = g_sub_menu_selection; + g_mic_sensitivity_tuning = g_mic_gain_dB_2[g_eeprom.config.setting.mic_sensitivity]; + BK4819_set_mic_gain(g_mic_sensitivity_tuning); g_flag_reconfigure_vfos = true; break; #ifdef ENABLE_TX_AUDIO_BAR case MENU_TX_BAR: - g_setting_mic_bar = g_sub_menu_selection; + g_eeprom.config.setting.mic_bar = g_sub_menu_selection; break; #endif #ifdef ENABLE_RX_SIGNAL_BAR case MENU_RX_BAR: - g_setting_rssi_bar = g_sub_menu_selection; + g_eeprom.config.setting.enable_rssi_bar = g_sub_menu_selection; break; #endif @@ -679,39 +679,39 @@ void MENU_AcceptSetting(void) #if 1 g_request_save_channel = 1; #else - SETTINGS_save_channel(g_sub_menu_selection, g_eeprom.tx_vfo, g_tx_vfo, 3); + SETTINGS_save_channel(g_sub_menu_selection, g_eeprom.config.setting.tx_vfo_num, g_tx_vfo, 3); g_flag_reconfigure_vfos = true; #endif return; case MENU_1_CALL: - g_eeprom2.config.call1 = g_sub_menu_selection; + g_eeprom.config.setting.call1 = g_sub_menu_selection; break; case MENU_S_LIST: - g_eeprom.scan_list_default = g_sub_menu_selection; + g_eeprom.config.setting.scan_list_default = g_sub_menu_selection; break; #ifdef ENABLE_ALARM case MENU_ALARM_MODE: - g_eeprom.alarm_mode = g_sub_menu_selection; + g_eeprom.config.setting.alarm_mode = g_sub_menu_selection; break; #endif case MENU_DTMF_ST: - g_eeprom.dtmf_side_tone = g_sub_menu_selection; + g_eeprom.config.setting.dtmf.side_tone = g_sub_menu_selection; break; case MENU_DTMF_RSP: - g_eeprom.dtmf_decode_response = g_sub_menu_selection; + g_eeprom.config.setting.dtmf.decode_response = g_sub_menu_selection; break; case MENU_DTMF_HOLD: - g_eeprom.dtmf_auto_reset_time = g_sub_menu_selection; + g_eeprom.config.setting.dtmf.auto_reset_time = g_sub_menu_selection; break; case MENU_DTMF_PRE: - g_eeprom.dtmf_preload_time = g_sub_menu_selection * 10; + g_eeprom.config.setting.dtmf.preload_time = g_sub_menu_selection; break; #ifdef ENABLE_MDC1200 @@ -721,7 +721,7 @@ void MENU_AcceptSetting(void) break; case MENU_MDC1200_ID: - g_eeprom.mdc1200_id = g_sub_menu_selection; + g_eeprom.config.setting.mdc1200_id = g_sub_menu_selection; break; #endif @@ -731,14 +731,14 @@ void MENU_AcceptSetting(void) g_tx_vfo->dtmf_ptt_id_tx_mode == PTT_ID_BOTH || g_tx_vfo->dtmf_ptt_id_tx_mode == PTT_ID_APOLLO) { - g_eeprom.roger_mode = ROGER_MODE_OFF; + g_eeprom.config.setting.roger_mode = ROGER_MODE_OFF; break; } g_request_save_channel = 1; return; case MENU_BAT_TXT: - g_setting_battery_text = g_sub_menu_selection; + g_eeprom.config.setting.battery_text = g_sub_menu_selection; break; case MENU_DTMF_DCD: @@ -748,10 +748,10 @@ void MENU_AcceptSetting(void) return; case MENU_DTMF_LIVE_DEC: - g_setting_live_dtmf_decoder = g_sub_menu_selection; + g_eeprom.config.setting.dtmf_live_decoder = g_sub_menu_selection; g_dtmf_rx_live_timeout = 0; memset(g_dtmf_rx_live, 0, sizeof(g_dtmf_rx_live)); - if (!g_setting_live_dtmf_decoder) + if (!g_eeprom.config.setting.dtmf_live_decoder) BK4819_DisableDTMF(); g_flag_reconfigure_vfos = true; g_update_status = true; @@ -770,12 +770,12 @@ void MENU_AcceptSetting(void) return; case MENU_PON_MSG: - g_eeprom.pwr_on_display_mode = g_sub_menu_selection; + g_eeprom.config.setting.power_on_display_mode = g_sub_menu_selection; break; case MENU_ROGER_MODE: - g_eeprom.roger_mode = g_sub_menu_selection; - if (g_eeprom.roger_mode != ROGER_MODE_OFF) + g_eeprom.config.setting.roger_mode = g_sub_menu_selection; + if (g_eeprom.config.setting.roger_mode != ROGER_MODE_OFF) { if (g_tx_vfo->dtmf_ptt_id_tx_mode == PTT_ID_TX_DOWN || g_tx_vfo->dtmf_ptt_id_tx_mode == PTT_ID_BOTH || @@ -794,7 +794,7 @@ void MENU_AcceptSetting(void) /* #ifdef ENABLE_AM_FIX case MENU_AM_FIX: - g_setting_am_fix = g_sub_menu_selection; + g_eeprom.config.setting.am_fix = g_sub_menu_selection; g_vfo_configure_mode = VFO_CONFIGURE_RELOAD; g_flag_reset_vfos = true; break; @@ -802,7 +802,7 @@ void MENU_AcceptSetting(void) */ #ifdef ENABLE_AM_FIX_TEST1 case MENU_AM_FIX_TEST1: - g_setting_am_fix_test1 = g_sub_menu_selection; + g_eeprom.config.setting.am_fix_test1 = g_sub_menu_selection; g_vfo_configure_mode = VFO_CONFIGURE_RELOAD; g_flag_reset_vfos = true; break; @@ -810,7 +810,7 @@ void MENU_AcceptSetting(void) #ifdef ENABLE_NOAA case MENU_NOAA_SCAN: - g_eeprom.noaa_auto_scan = g_sub_menu_selection; + g_eeprom.config.setting.noaa_auto_scan = g_sub_menu_selection; g_flag_reconfigure_vfos = true; break; #endif @@ -823,55 +823,55 @@ void MENU_AcceptSetting(void) #ifdef ENABLE_SIDE_BUTT_MENU case MENU_SIDE1_SHORT: - g_eeprom.key1_short_press_action = g_sub_menu_selection; + g_eeprom.config.setting.key1_short = g_sub_menu_selection; break; case MENU_SIDE1_LONG: - g_eeprom.key1_long_press_action = g_sub_menu_selection; + g_eeprom.config.setting.key1_long = g_sub_menu_selection; break; case MENU_SIDE2_SHORT: - g_eeprom.key2_short_press_action = g_sub_menu_selection; + g_eeprom.config.setting.key2_short = g_sub_menu_selection; break; case MENU_SIDE2_LONG: - g_eeprom.key2_long_press_action = g_sub_menu_selection; + g_eeprom.config.setting.key2_long = g_sub_menu_selection; break; #endif case MENU_RESET: - BOARD_FactoryReset(g_sub_menu_selection); + SETTINGS_factory_reset(g_sub_menu_selection); return; case MENU_350_TX: - g_setting_350_tx_enable = g_sub_menu_selection; + g_eeprom.config.setting.enable_tx_350 = g_sub_menu_selection; break; case MENU_FREQ_LOCK: - g_setting_freq_lock = g_sub_menu_selection; + g_eeprom.config.setting.freq_lock = g_sub_menu_selection; break; case MENU_174_TX: - g_setting_174_tx_enable = g_sub_menu_selection; + g_eeprom.config.setting.enable_tx_200 = g_sub_menu_selection; break; case MENU_470_TX: - g_setting_470_tx_enable = g_sub_menu_selection; + g_eeprom.config.setting.enable_tx_470 = g_sub_menu_selection; break; case MENU_350_EN: - g_setting_350_enable = g_sub_menu_selection; + g_eeprom.config.setting.enable_350 = g_sub_menu_selection; g_vfo_configure_mode = VFO_CONFIGURE_RELOAD; g_flag_reset_vfos = true; break; case MENU_SCRAMBLER_EN: - g_setting_scramble_enable = g_sub_menu_selection; + g_eeprom.config.setting.enable_scrambler = g_sub_menu_selection; g_flag_reconfigure_vfos = true; break; case MENU_TX_EN: - g_setting_tx_enable = g_sub_menu_selection; + g_eeprom.config.setting.tx_enable = g_sub_menu_selection; break; #ifdef ENABLE_F_CAL_MENU @@ -882,20 +882,15 @@ void MENU_AcceptSetting(void) case MENU_BAT_CAL: { - uint16_t buf[4]; + g_eeprom.calib.battery[0] = (520ul * g_sub_menu_selection) / 760; // 5.20V empty, blinking above this value, reduced functionality below + g_eeprom.calib.battery[1] = (700ul * g_sub_menu_selection) / 760; // 7.00V, ~5%, 1 bars above this value + g_eeprom.calib.battery[2] = (745ul * g_sub_menu_selection) / 760; // 7.45V, ~17%, 2 bars above this value + g_eeprom.calib.battery[3] = g_sub_menu_selection; // 7.6V, ~29%, 3 bars above this value + g_eeprom.calib.battery[4] = (788ul * g_sub_menu_selection) / 760; // 7.88V, ~65%, 4 bars above this value + g_eeprom.calib.battery[5] = 2300; - g_battery_calibration[0] = (520ul * g_sub_menu_selection) / 760; // 5.20V empty, blinking above this value, reduced functionality below - g_battery_calibration[1] = (700ul * g_sub_menu_selection) / 760; // 7.00V, ~5%, 1 bars above this value - g_battery_calibration[2] = (745ul * g_sub_menu_selection) / 760; // 7.45V, ~17%, 2 bars above this value - g_battery_calibration[3] = g_sub_menu_selection; // 7.6V, ~29%, 3 bars above this value - g_battery_calibration[4] = (788ul * g_sub_menu_selection) / 760; // 7.88V, ~65%, 4 bars above this value - g_battery_calibration[5] = 2300; - EEPROM_WriteBuffer8(0x1F40, g_battery_calibration); - - EEPROM_ReadBuffer( 0x1F48, buf, sizeof(buf)); - buf[0] = g_battery_calibration[4]; - buf[1] = g_battery_calibration[5]; - EEPROM_WriteBuffer8(0x1F48, buf); + EEPROM_WriteBuffer8(0x1F40, &g_eeprom.calib.battery[0]); + EEPROM_WriteBuffer8(0x1F48, &g_eeprom.calib.battery[4]); break; } @@ -966,7 +961,7 @@ void MENU_ShowCurrentSetting(void) switch (g_menu_cursor) { case MENU_SQL: - g_sub_menu_selection = g_eeprom.squelch_level; + g_sub_menu_selection = g_eeprom.config.setting.squelch_level; break; case MENU_CHAN_SQL: @@ -1045,35 +1040,35 @@ void MENU_ShowCurrentSetting(void) case MENU_MEM_SAVE: #if 0 - g_sub_menu_selection = g_eeprom.user_channel[0]; + g_sub_menu_selection = g_eeprom.config.setting.indices.vfo[0].user; #else - g_sub_menu_selection = g_eeprom.user_channel[g_eeprom.tx_vfo]; + g_sub_menu_selection = g_eeprom.config.setting.indices.vfo[g_eeprom.config.setting.tx_vfo_num].user; #endif break; case MENU_MEM_NAME: - g_sub_menu_selection = g_eeprom.user_channel[g_eeprom.tx_vfo]; + g_sub_menu_selection = g_eeprom.config.setting.indices.vfo[g_eeprom.config.setting.tx_vfo_num].user; break; case MENU_BAT_SAVE: - g_sub_menu_selection = g_eeprom.battery_save; + g_sub_menu_selection = g_eeprom.config.setting.battery_save_ratio; break; #ifdef ENABLE_VOX case MENU_VOX: - g_sub_menu_selection = g_eeprom.vox_switch ? g_eeprom.vox_level + 1 : 0; + g_sub_menu_selection = g_eeprom.config.setting.vox_switch ? g_eeprom.config.setting.vox_level + 1 : 0; break; #endif case MENU_AUTO_BACKLITE: - g_sub_menu_selection = g_eeprom.backlight; + g_sub_menu_selection = g_eeprom.config.setting.backlight_time; - g_backlight_count_down = 0; + g_backlight_tick_500ms = 0; GPIO_SetBit(&GPIOB->DATA, GPIOB_PIN_BACKLIGHT); // turn the backlight ON while in backlight menu break; case MENU_AUTO_BACKLITE_ON_TX_RX: - g_sub_menu_selection = g_setting_backlight_on_tx_rx; + g_sub_menu_selection = g_eeprom.config.setting.backlight_on_tx_rx; break; #ifdef ENABLE_CONTRAST @@ -1083,43 +1078,43 @@ void MENU_ShowCurrentSetting(void) #endif case MENU_DUAL_WATCH: -// g_sub_menu_selection = g_eeprom.dual_watch; - g_sub_menu_selection = (g_eeprom.dual_watch == DUAL_WATCH_OFF) ? 0 : 1; +// g_sub_menu_selection = g_eeprom.config.setting.dual_watch; + g_sub_menu_selection = (g_eeprom.config.setting.dual_watch == DUAL_WATCH_OFF) ? 0 : 1; break; case MENU_SCAN_HOLD: - g_sub_menu_selection = g_eeprom.scan_hold_time_500ms; + g_sub_menu_selection = g_eeprom.config.setting.scan_hold_time; break; case MENU_CROSS_VFO: - g_sub_menu_selection = g_eeprom.cross_vfo_rx_tx; + g_sub_menu_selection = g_eeprom.config.setting.cross_vfo; break; case MENU_BEEP: - g_sub_menu_selection = g_eeprom.beep_control; + g_sub_menu_selection = g_eeprom.config.setting.beep_control; break; case MENU_TX_TO: - g_sub_menu_selection = g_eeprom.tx_timeout_timer; + g_sub_menu_selection = g_eeprom.config.setting.tx_timeout; break; #ifdef ENABLE_VOICE case MENU_VOICE: - g_sub_menu_selection = g_eeprom.voice_prompt; + g_sub_menu_selection = g_eeprom.config.setting.voice_prompt; break; #endif case MENU_SCAN_CAR_RESUME: - g_sub_menu_selection = g_eeprom.scan_resume_mode; + g_sub_menu_selection = g_eeprom.config.setting.carrier_search_mode; break; case MENU_MEM_DISP: - g_sub_menu_selection = g_eeprom.channel_display_mode; + g_sub_menu_selection = g_eeprom.config.setting.channel_display_mode; break; #ifdef ENABLE_KEYLOCK case MENU_AUTO_KEY_LOCK: - g_sub_menu_selection = g_eeprom.auto_keypad_lock; + g_sub_menu_selection = g_eeprom.config.setting.auto_key_lock; break; #endif @@ -1132,26 +1127,26 @@ void MENU_ShowCurrentSetting(void) break; case MENU_STE: - g_sub_menu_selection = g_eeprom.tail_note_elimination; + g_sub_menu_selection = g_eeprom.config.setting.tail_tone_elimination; break; case MENU_RP_STE: - g_sub_menu_selection = g_eeprom.repeater_tail_tone_elimination; + g_sub_menu_selection = g_eeprom.config.setting.repeater_tail_tone_elimination; break; case MENU_MIC_GAIN: - g_sub_menu_selection = g_eeprom.mic_sensitivity; + g_sub_menu_selection = g_eeprom.config.setting.mic_sensitivity; break; #ifdef ENABLE_TX_AUDIO_BAR case MENU_TX_BAR: - g_sub_menu_selection = g_setting_mic_bar; + g_sub_menu_selection = g_eeprom.config.setting.mic_bar; break; #endif #ifdef ENABLE_RX_SIGNAL_BAR case MENU_RX_BAR: - g_sub_menu_selection = g_setting_rssi_bar; + g_sub_menu_selection = g_eeprom.config.setting.enable_rssi_bar; break; #endif @@ -1160,11 +1155,11 @@ void MENU_ShowCurrentSetting(void) return; case MENU_1_CALL: - g_sub_menu_selection = g_eeprom2.config.call1; + g_sub_menu_selection = g_eeprom.config.setting.call1; break; case MENU_S_LIST: - g_sub_menu_selection = g_eeprom.scan_list_default; + g_sub_menu_selection = g_eeprom.config.setting.scan_list_default; break; case MENU_SLIST1: @@ -1177,20 +1172,20 @@ void MENU_ShowCurrentSetting(void) #ifdef ENABLE_ALARM case MENU_ALARM_MODE: - g_sub_menu_selection = g_eeprom.alarm_mode; + g_sub_menu_selection = g_eeprom.config.setting.alarm_mode; break; #endif case MENU_DTMF_ST: - g_sub_menu_selection = g_eeprom.dtmf_side_tone; + g_sub_menu_selection = g_eeprom.config.setting.dtmf.side_tone; break; case MENU_DTMF_RSP: - g_sub_menu_selection = g_eeprom.dtmf_decode_response; + g_sub_menu_selection = g_eeprom.config.setting.dtmf.decode_response; break; case MENU_DTMF_HOLD: - g_sub_menu_selection = g_eeprom.dtmf_auto_reset_time; + g_sub_menu_selection = g_eeprom.config.setting.dtmf.auto_reset_time; if (g_sub_menu_selection <= DTMF_HOLD_MIN) g_sub_menu_selection = DTMF_HOLD_MIN; @@ -1218,7 +1213,7 @@ void MENU_ShowCurrentSetting(void) break; case MENU_DTMF_PRE: - g_sub_menu_selection = g_eeprom.dtmf_preload_time / 10; + g_sub_menu_selection = g_eeprom.config.setting.dtmf.preload_time; break; #ifdef ENABLE_MDC1200 @@ -1227,7 +1222,7 @@ void MENU_ShowCurrentSetting(void) break; case MENU_MDC1200_ID: - g_sub_menu_selection = g_eeprom.mdc1200_id; + g_sub_menu_selection = g_eeprom.config.setting.mdc1200_id; break; #endif @@ -1236,7 +1231,7 @@ void MENU_ShowCurrentSetting(void) break; case MENU_BAT_TXT: - g_sub_menu_selection = g_setting_battery_text; + g_sub_menu_selection = g_eeprom.config.setting.battery_text; return; case MENU_DTMF_DCD: @@ -1248,15 +1243,15 @@ void MENU_ShowCurrentSetting(void) break; case MENU_DTMF_LIVE_DEC: - g_sub_menu_selection = g_setting_live_dtmf_decoder; + g_sub_menu_selection = g_eeprom.config.setting.dtmf_live_decoder; break; case MENU_PON_MSG: - g_sub_menu_selection = g_eeprom.pwr_on_display_mode; + g_sub_menu_selection = g_eeprom.config.setting.power_on_display_mode; break; case MENU_ROGER_MODE: - g_sub_menu_selection = g_eeprom.roger_mode; + g_sub_menu_selection = g_eeprom.config.setting.roger_mode; break; case MENU_MOD_MODE: @@ -1265,84 +1260,84 @@ void MENU_ShowCurrentSetting(void) /* #ifdef ENABLE_AM_FIX case MENU_AM_FIX: - g_sub_menu_selection = g_setting_am_fix; + g_sub_menu_selection = g_eeprom.config.setting.am_fix; break; #endif */ #ifdef ENABLE_AM_FIX_TEST1 case MENU_AM_FIX_TEST1: - g_sub_menu_selection = g_setting_am_fix_test1; + g_sub_menu_selection = g_eeprom.config.setting.am_fix_test1; break; #endif #ifdef ENABLE_NOAA case MENU_NOAA_SCAN: - g_sub_menu_selection = g_eeprom.noaa_auto_scan; + g_sub_menu_selection = g_eeprom.config.setting.noaa_auto_scan; break; #endif case MENU_MEM_DEL: #if 0 - g_sub_menu_selection = RADIO_FindNextChannel(g_eeprom.user_channel[0], 1, false, 1); + g_sub_menu_selection = RADIO_FindNextChannel(g_eeprom.config.setting.indices.vfo[0].user, 1, false, 1); #else - g_sub_menu_selection = RADIO_FindNextChannel(g_eeprom.user_channel[g_eeprom.tx_vfo], 1, false, 1); + g_sub_menu_selection = RADIO_FindNextChannel(g_eeprom.config.setting.indices.vfo[g_eeprom.config.setting.tx_vfo_num].user, 1, false, 1); #endif break; #ifdef ENABLE_SIDE_BUTT_MENU case MENU_SIDE1_SHORT: - g_sub_menu_selection = g_eeprom.key1_short_press_action; + g_sub_menu_selection = g_eeprom.config.setting.key1_short; break; case MENU_SIDE1_LONG: - g_sub_menu_selection = g_eeprom.key1_long_press_action; + g_sub_menu_selection = g_eeprom.config.setting.key1_long; break; case MENU_SIDE2_SHORT: - g_sub_menu_selection = g_eeprom.key2_short_press_action; + g_sub_menu_selection = g_eeprom.config.setting.key2_short; break; case MENU_SIDE2_LONG: - g_sub_menu_selection = g_eeprom.key2_long_press_action; + g_sub_menu_selection = g_eeprom.config.setting.key2_long; break; #endif case MENU_350_TX: - g_sub_menu_selection = g_setting_350_tx_enable; + g_sub_menu_selection = g_eeprom.config.setting.enable_tx_350; break; case MENU_FREQ_LOCK: - g_sub_menu_selection = g_setting_freq_lock; + g_sub_menu_selection = g_eeprom.config.setting.freq_lock; break; case MENU_174_TX: - g_sub_menu_selection = g_setting_174_tx_enable; + g_sub_menu_selection = g_eeprom.config.setting.enable_tx_200; break; case MENU_470_TX: - g_sub_menu_selection = g_setting_470_tx_enable; + g_sub_menu_selection = g_eeprom.config.setting.enable_tx_470; break; case MENU_350_EN: - g_sub_menu_selection = g_setting_350_enable; + g_sub_menu_selection = g_eeprom.config.setting.enable_350; break; case MENU_SCRAMBLER_EN: - g_sub_menu_selection = g_setting_scramble_enable; + g_sub_menu_selection = g_eeprom.config.setting.enable_scrambler; break; case MENU_TX_EN: - g_sub_menu_selection = g_setting_tx_enable; + g_sub_menu_selection = g_eeprom.config.setting.tx_enable; break; #ifdef ENABLE_F_CAL_MENU case MENU_F_CALI: - g_sub_menu_selection = g_eeprom.BK4819_xtal_freq_low; + g_sub_menu_selection = g_eeprom.config.setting.BK4819_xtal_freq_low; break; #endif case MENU_BAT_CAL: - g_sub_menu_selection = g_battery_calibration[3]; + g_sub_menu_selection = g_eeprom.calib.battery[3]; break; default: @@ -1599,9 +1594,9 @@ static void MENU_Key_EXIT(bool key_pressed, bool key_held) g_request_display_screen = DISPLAY_MAIN; - if (g_eeprom.backlight == 0) + if (g_eeprom.config.setting.backlight_time == 0) { - g_backlight_count_down = 0; + g_backlight_tick_500ms = 0; GPIO_ClearBit(&GPIOB->DATA, GPIOB_PIN_BACKLIGHT); // turn the backlight OFF } } @@ -1659,7 +1654,7 @@ static void MENU_Key_MENU(const bool key_pressed, const bool key_held) if (!RADIO_CheckValidChannel(g_sub_menu_selection, false, 0)) return; - BOARD_fetchChannelName(g_edit, g_sub_menu_selection); + SETTINGS_fetch_channel_name(g_edit, g_sub_menu_selection); // pad the channel name out with '_' g_edit_index = strlen(g_edit); @@ -1877,9 +1872,9 @@ static void MENU_Key_UP_DOWN(bool key_pressed, bool key_held, int8_t Direction) g_request_display_screen = DISPLAY_MENU; - if (g_menu_cursor != MENU_AUTO_BACKLITE && g_eeprom.backlight == 0) + if (g_menu_cursor != MENU_AUTO_BACKLITE && g_eeprom.config.setting.backlight_time == 0) { - g_backlight_count_down = 0; + g_backlight_tick_500ms = 0; GPIO_ClearBit(&GPIOB->DATA, GPIOB_PIN_BACKLIGHT); // turn the backlight OFF } diff --git a/app/search.c b/app/search.c index 365231e..9f8d1c1 100644 --- a/app/search.c +++ b/app/search.c @@ -112,7 +112,7 @@ static void SEARCH_Key_EXIT(bool key_pressed, bool key_held) switch (g_search_edit_state) { case SEARCH_EDIT_STATE_NONE: - g_eeprom.cross_vfo_rx_tx = g_backup_cross_vfo_rx_tx; + g_eeprom.config.setting.cross_vfo = g_backup_cross_vfo; g_update_status = true; g_vfo_configure_mode = VFO_CONFIGURE_RELOAD; g_flag_reset_vfos = true; @@ -184,7 +184,7 @@ static void SEARCH_Key_MENU(bool key_pressed, bool key_held) // determine what the current step size is for the detected frequency // use the 7 VFO channels/bands to determine it const unsigned int band = (unsigned int)FREQUENCY_GetBand(g_search_frequency); - g_search_step_setting = BOARD_fetchFrequencyStepSetting(band, g_eeprom.tx_vfo); + g_search_step_setting = SETTINGS_fetch_frequency_step_setting(band, g_eeprom.config.setting.tx_vfo_num); { // round to nearest step size const uint16_t step_size = STEP_FREQ_TABLE[g_search_step_setting]; g_search_frequency = ((g_search_frequency + (step_size / 2)) / step_size) * step_size; @@ -268,16 +268,16 @@ static void SEARCH_Key_MENU(bool key_pressed, bool key_held) if (g_tx_vfo->channel_save <= USER_CHANNEL_LAST) { Channel = g_search_channel; - g_eeprom.user_channel[g_eeprom.tx_vfo] = Channel; + g_eeprom.config.setting.indices.vfo[g_eeprom.config.setting.tx_vfo_num].user = Channel; } else { Channel = FREQ_CHANNEL_FIRST + g_tx_vfo->band; - g_eeprom.freq_channel[g_eeprom.tx_vfo] = Channel; + g_eeprom.config.setting.indices.vfo[g_eeprom.config.setting.tx_vfo_num].frequency = Channel; } g_tx_vfo->channel_save = Channel; - g_eeprom.screen_channel[g_eeprom.tx_vfo] = Channel; + g_eeprom.config.setting.indices.vfo[g_eeprom.config.setting.tx_vfo_num].screen = Channel; g_request_save_channel = 2; #ifdef ENABLE_VOICE @@ -619,7 +619,7 @@ void SEARCH_Start(void) RADIO_setup_registers(true); #ifdef ENABLE_NOAA - g_is_noaa_mode = false; + g_noaa_mode = false; #endif if (g_search_single_frequency) diff --git a/app/uart.c b/app/uart.c index 73ec176..bdce871 100644 --- a/app/uart.c +++ b/app/uart.c @@ -210,7 +210,7 @@ static void SendVersion(void) reply.Header.ID = 0x0515; reply.Header.Size = sizeof(reply.Data); memcpy(reply.Data.Version, Version_str, slen); - reply.Data.has_custom_aes_key = g_has_custom_aes_key; + reply.Data.has_custom_aes_key = g_has_aes_key; reply.Data.password_locked = g_password_locked; reply.Data.Challenge[0] = g_challenge[0]; reply.Data.Challenge[1] = g_challenge[1]; @@ -285,11 +285,12 @@ static void cmd_051B(const uint8_t *pBuffer) reply.Data.Offset = addr; reply.Data.Size = size; -// if (g_has_custom_aes_key) +// if (g_has_aes_key) // locked = is_locked; // if (!locked) - EEPROM_ReadBuffer(addr, reply.Data.Data, size); +// EEPROM_ReadBuffer(addr, reply.Data.Data, size); + memcpy(reply.Data.Data, ((uint8_t *)&g_eeprom) + addr, size); SendReply(&reply, size + 8); } @@ -303,7 +304,7 @@ static void cmd_051D(const uint8_t *pBuffer) unsigned int size = pCmd->Size; #ifdef INCLUDE_AES bool reload_eeprom = false; - bool locked = g_has_custom_aes_key ? is_locked : g_has_custom_aes_key; + bool locked = g_has_aes_key ? is_locked : g_has_aes_key; #endif reply_051D_t reply; @@ -369,7 +370,7 @@ static void cmd_051D(const uint8_t *pBuffer) #ifdef INCLUDE_AES if (reload_eeprom) - BOARD_eeprom_load(); + SETTINGS_read_eeprom(); #endif } @@ -412,7 +413,7 @@ static void cmd_0529(void) static void cmd_052D(const uint8_t *pBuffer) { cmd_052D_t *pCmd = (cmd_052D_t *)pBuffer; - bool locked = g_has_custom_aes_key; + bool locked = g_has_aes_key; uint32_t response[4]; reply_052D_t reply; @@ -420,8 +421,10 @@ static void cmd_052D(const uint8_t *pBuffer) if (!locked) { + uint32_t aes_key[4]; memcpy((void *)&response, &pCmd->Response, sizeof(response)); // overcome strict compiler warning settings - locked = IsBadChallenge(g_custom_aes_key, g_challenge, response); + memcpy(aes_key, g_eeprom.config.setting.aes_key, sizeof(aes_key)); + locked = IsBadChallenge(aes_key, g_challenge, response); } if (!locked) @@ -459,21 +462,21 @@ static void cmd_052F(const uint8_t *pBuffer) { const cmd_052F_t *pCmd = (const cmd_052F_t *)pBuffer; - g_eeprom.dual_watch = DUAL_WATCH_OFF; - g_eeprom.cross_vfo_rx_tx = CROSS_BAND_OFF; - g_eeprom.rx_vfo = 0; - g_eeprom.dtmf_side_tone = false; - g_eeprom.vfo_info[0].frequency_reverse = false; - g_eeprom.vfo_info[0].p_rx = &g_eeprom.vfo_info[0].freq_config_rx; - g_eeprom.vfo_info[0].p_tx = &g_eeprom.vfo_info[0].freq_config_tx; - g_eeprom.vfo_info[0].tx_offset_freq_dir = TX_OFFSET_FREQ_DIR_OFF; - g_eeprom.vfo_info[0].dtmf_ptt_id_tx_mode = PTT_ID_OFF; - g_eeprom.vfo_info[0].dtmf_decoding_enable = false; + g_rx_vfo = 0; + g_eeprom.config.setting.dual_watch = DUAL_WATCH_OFF; + g_eeprom.config.setting.cross_vfo = CROSS_BAND_OFF; + g_eeprom.config.setting.dtmf.side_tone = false; + g_vfo_info[0].frequency_reverse = false; + g_vfo_info[0].p_rx = &g_vfo_info[0].freq_config_rx; + g_vfo_info[0].p_tx = &g_vfo_info[0].freq_config_tx; + g_vfo_info[0].tx_offset_freq_dir = TX_OFFSET_FREQ_DIR_OFF; + g_vfo_info[0].dtmf_ptt_id_tx_mode = PTT_ID_OFF; + g_vfo_info[0].dtmf_decoding_enable = false; g_serial_config_tick_500ms = serial_config_tick_500ms; #ifdef ENABLE_NOAA - g_is_noaa_mode = false; + g_noaa_mode = false; #endif if (g_current_function == FUNCTION_POWER_SAVE) diff --git a/audio.c b/audio.c index 4f6cce2..d4279ba 100644 --- a/audio.c +++ b/audio.c @@ -97,7 +97,7 @@ void AUDIO_PlayBeep(beep_type_t Beep) uint16_t ToneFrequency; uint16_t Duration; - if (!g_eeprom.beep_control) + if (g_eeprom.config.setting.beep_control == 0) { // beep not enabled if (Beep != BEEP_880HZ_60MS_TRIPLE_BEEP && Beep != BEEP_500HZ_60MS_DOUBLE_BEEP && @@ -261,7 +261,7 @@ void AUDIO_PlayBeep(beep_type_t Beep) { unsigned int i; - if (g_eeprom.voice_prompt == VOICE_PROMPT_OFF) + if (g_eeprom.config.setting.voice_prompt == VOICE_PROMPT_OFF) return; GPIO_SetBit(&GPIOA->DATA, GPIOA_PIN_VOICE_0); @@ -288,10 +288,10 @@ void AUDIO_PlayBeep(beep_type_t Beep) uint8_t Delay; uint8_t VoiceID = g_voice_id[0]; - if (g_eeprom.voice_prompt == VOICE_PROMPT_OFF || g_voice_write_index == 0) + if (g_eeprom.config.setting.voice_prompt == VOICE_PROMPT_OFF || g_voice_write_index == 0) goto Bailout; - if (g_eeprom.voice_prompt == VOICE_PROMPT_CHINESE) + if (g_eeprom.config.setting.voice_prompt == VOICE_PROMPT_CHINESE) { // Chinese if (VoiceID >= ARRAY_SIZE(VoiceClipLengthChinese)) goto Bailout; @@ -371,13 +371,13 @@ void AUDIO_PlayBeep(beep_type_t Beep) void AUDIO_SetVoiceID(uint8_t Index, voice_id_t VoiceID) { - if (g_eeprom.voice_prompt == VOICE_PROMPT_OFF || Index == 0) + if (g_eeprom.config.setting.voice_prompt == VOICE_PROMPT_OFF || Index == 0) { g_voice_write_index = 0; g_voice_read_index = 0; } - if (g_eeprom.voice_prompt != VOICE_PROMPT_OFF && Index < ARRAY_SIZE(g_voice_id)) + if (g_eeprom.config.setting.voice_prompt != VOICE_PROMPT_OFF && Index < ARRAY_SIZE(g_voice_id)) { g_voice_id[Index] = VoiceID; g_voice_write_index++; @@ -390,13 +390,13 @@ void AUDIO_PlayBeep(beep_type_t Beep) uint8_t Result; uint8_t Count; - if (g_eeprom.voice_prompt == VOICE_PROMPT_OFF || Index == 0) + if (g_eeprom.config.setting.voice_prompt == VOICE_PROMPT_OFF || Index == 0) { g_voice_write_index = 0; g_voice_read_index = 0; } - if (g_eeprom.voice_prompt == VOICE_PROMPT_OFF) + if (g_eeprom.config.setting.voice_prompt == VOICE_PROMPT_OFF) return 0; Count = 0; @@ -431,17 +431,17 @@ void AUDIO_PlayBeep(beep_type_t Beep) uint8_t Delay; bool Skip = false; - if (g_eeprom.voice_prompt == VOICE_PROMPT_OFF) + if (g_eeprom.config.setting.voice_prompt == VOICE_PROMPT_OFF) { g_voice_write_index = 0; g_voice_read_index = 0; return; } - if (g_voice_read_index != g_voice_write_index && g_eeprom.voice_prompt != VOICE_PROMPT_OFF) + if (g_voice_read_index != g_voice_write_index && g_eeprom.config.setting.voice_prompt != VOICE_PROMPT_OFF) { VoiceID = g_voice_id[g_voice_read_index]; - if (g_eeprom.voice_prompt == VOICE_PROMPT_CHINESE) + if (g_eeprom.config.setting.voice_prompt == VOICE_PROMPT_CHINESE) { if (VoiceID < ARRAY_SIZE(VoiceClipLengthChinese)) { diff --git a/bitmaps.c b/bitmaps.c index 6525ec6..9e3b6ec 100644 --- a/bitmaps.c +++ b/bitmaps.c @@ -59,7 +59,7 @@ const uint8_t BITMAP_RX[8] = { __extension__ 0b00000000, __extension__ 0b00111110, - __extension__ 0b01100011, + __extension__ 0b01111111, __extension__ 0b01000001, __extension__ 0b01000001, __extension__ 0b01000001, @@ -94,7 +94,7 @@ const uint8_t BITMAP_RX[8] = __extension__ 0b01000001, __extension__ 0b01000001, __extension__ 0b01000001, - __extension__ 0b01100011, + __extension__ 0b01111111, __extension__ 0b00111110 }; #endif @@ -175,7 +175,7 @@ const uint8_t BITMAP_F_KEY[6] = }; #else // 'XB' (cross-band/cross-VFO) - const uint8_t BITMAP_XB[12] = + const uint8_t BITMAP_XB[11] = { // "XB" __extension__ 0b00000000, __extension__ 0b01100011, @@ -187,18 +187,16 @@ const uint8_t BITMAP_F_KEY[6] = __extension__ 0b01111111, __extension__ 0b01001001, __extension__ 0b01001001, - __extension__ 0b01001001, __extension__ 0b00110110 }; #endif -const uint8_t BITMAP_TDR_RUNNING[12] = +const uint8_t BITMAP_TDR_RUNNING[11] = { // "DW" __extension__ 0b00000000, __extension__ 0b01111111, __extension__ 0b01000001, __extension__ 0b01000001, - __extension__ 0b01000001, __extension__ 0b00111110, __extension__ 0b00000000, __extension__ 0b01111111, @@ -208,14 +206,13 @@ const uint8_t BITMAP_TDR_RUNNING[12] = __extension__ 0b01111111 }; -const uint8_t BITMAP_TDR_HOLDING[12] = +const uint8_t BITMAP_TDR_HOLDING[11] = { // "--" .. DW on hold __extension__ 0b00000000, __extension__ 0b00001000, __extension__ 0b00001000, __extension__ 0b00001000, __extension__ 0b00001000, - __extension__ 0b00001000, __extension__ 0b00000000, __extension__ 0b00001000, __extension__ 0b00001000, @@ -225,14 +222,12 @@ const uint8_t BITMAP_TDR_HOLDING[12] = }; #ifdef ENABLE_VOICE - const uint8_t BITMAP_VOICE_PROMPT[9] = + const uint8_t BITMAP_VOICE_PROMPT[7] = { __extension__ 0b00000000, __extension__ 0b00011000, __extension__ 0b00011000, __extension__ 0b00100100, - __extension__ 0b00100100, - __extension__ 0b01000010, __extension__ 0b01000010, __extension__ 0b11111111, __extension__ 0b00011000 @@ -250,13 +245,12 @@ const uint8_t BITMAP_MONITOR[6] = }; #ifdef ENABLE_FMRADIO - const uint8_t BITMAP_FM[12] = + const uint8_t BITMAP_FM[11] = { // "FM" __extension__ 0b00000000, __extension__ 0b01111111, __extension__ 0b00001001, __extension__ 0b00001001, - __extension__ 0b00001001, __extension__ 0b00000001, __extension__ 0b00000000, __extension__ 0b01111111, @@ -268,7 +262,7 @@ const uint8_t BITMAP_MONITOR[6] = #endif #ifdef ENABLE_NOAA - const uint8_t BITMAP_NOAA[12] = + const uint8_t BITMAP_NOAA[10] = { // "NS" __extension__ 0b00000000, __extension__ 0b01111111, @@ -276,12 +270,9 @@ const uint8_t BITMAP_MONITOR[6] = __extension__ 0b00001000, __extension__ 0b00010000, __extension__ 0b01111111, - __extension__ 0b00000000, __extension__ 0b01000110, __extension__ 0b01001001, - __extension__ 0b01001001, - __extension__ 0b01001001, __extension__ 0b00110001 }; #endif diff --git a/bitmaps.h b/bitmaps.h index 2fb03e2..03a14a0 100644 --- a/bitmaps.h +++ b/bitmaps.h @@ -25,24 +25,24 @@ extern const uint8_t BITMAP_F_KEY[6]; #if 0 extern const uint8_t BITMAP_WX[12]; #else - extern const uint8_t BITMAP_XB[12]; + extern const uint8_t BITMAP_XB[11]; #endif -extern const uint8_t BITMAP_TDR_RUNNING[12]; -extern const uint8_t BITMAP_TDR_HOLDING[12]; +extern const uint8_t BITMAP_TDR_RUNNING[11]; +extern const uint8_t BITMAP_TDR_HOLDING[11]; #ifdef ENABLE_VOICE - extern const uint8_t BITMAP_VOICE_PROMPT[9]; + extern const uint8_t BITMAP_VOICE_PROMPT[7]; #endif extern const uint8_t BITMAP_MONITOR[6]; #ifdef ENABLE_FMRADIO - extern const uint8_t BITMAP_FM[12]; + extern const uint8_t BITMAP_FM[11]; #endif #ifdef ENABLE_NOAA - extern const uint8_t BITMAP_NOAA[12]; + extern const uint8_t BITMAP_NOAA[10]; #endif extern const uint8_t BITMAP_ANTENNA[5]; diff --git a/board.c b/board.c index b86e7f7..5d82198 100644 --- a/board.c +++ b/board.c @@ -44,15 +44,6 @@ #endif #include "ui/menu.h" -static const uint32_t gDefaultFrequencyTable[] = -{ - 14500000, // - 14550000, // - 43300000, // - 43320000, // - 43350000 // -}; - #if defined(ENABLE_OVERLAY) void BOARD_FLASH_Init(void) { @@ -501,499 +492,3 @@ void BOARD_ADC_GetBatteryInfo(uint16_t *pVoltage, uint16_t *pCurrent) *pVoltage = ADC_GetValue(ADC_CH4); *pCurrent = ADC_GetValue(ADC_CH9); } - -void BOARD_Init(void) -{ - BOARD_PORTCON_Init(); - BOARD_GPIO_Init(); - CRC_Init(); - BOARD_ADC_Init(); - ST7565_Init(true); - - #ifdef ENABLE_FMRADIO - BK1080_Init(0, false); - #endif -} - -void BOARD_eeprom_load(void) -{ - unsigned int i; - uint8_t Data[16]; - -// memset(Data, 0, sizeof(Data)); - - // sanity checks - g_eeprom2.config.call1 = IS_USER_CHANNEL(g_eeprom2.config.call1) ? g_eeprom2.config.call1 : USER_CHANNEL_FIRST; - - // 0E70..0E77 - EEPROM_ReadBuffer(0x0E70, Data, 8); -// g_eeprom.chan_1_call = IS_USER_CHANNEL(Data[0]) ? Data[0] : USER_CHANNEL_FIRST; - g_eeprom.squelch_level = (Data[1] < 10) ? Data[1] : 1; - g_eeprom.tx_timeout_timer = (Data[2] < 11) ? Data[2] : 1; - #ifdef ENABLE_NOAA - g_eeprom.noaa_auto_scan = (Data[3] < 2) ? Data[3] : false; - #endif - #ifdef ENABLE_KEYLOCK - g_eeprom.key_lock = (Data[4] < 2) ? Data[4] : false; - #endif - #ifdef ENABLE_VOX - g_eeprom.vox_switch = (Data[5] < 2) ? Data[5] : false; - g_eeprom.vox_level = (Data[6] < 10) ? Data[6] : 1; - #endif - g_eeprom.mic_sensitivity = (Data[7] < 5) ? Data[7] : 4; - - // 0E78..0E7F - EEPROM_ReadBuffer(0x0E78, Data, 8); - #ifdef ENABLE_CONTRAST - g_setting_contrast = (Data[0] > 45) ? 31 : (Data[0] < 26) ? 31 : Data[0]; - #endif - g_eeprom.channel_display_mode = (Data[1] < 4) ? Data[1] : MDF_FREQUENCY; // 4 instead of 3 - extra display mode - g_eeprom.cross_vfo_rx_tx = (Data[2] < 3) ? Data[2] : CROSS_BAND_OFF; - g_eeprom.battery_save = (Data[3] < 5) ? Data[3] : 4; - g_eeprom.dual_watch = (Data[4] < 3) ? Data[4] : DUAL_WATCH_CHAN_A; - g_eeprom.backlight = (Data[5] < ARRAY_SIZE(g_sub_menu_backlight)) ? Data[5] : 3; - g_eeprom.tail_note_elimination = (Data[6] < 2) ? Data[6] : false; - g_eeprom.vfo_open = (Data[7] < 2) ? Data[7] : true; - - // 0E80..0E87 - EEPROM_ReadBuffer(0x0E80, Data, 8); - g_eeprom.screen_channel[0] = IS_VALID_CHANNEL(Data[0]) ? Data[0] : (FREQ_CHANNEL_FIRST + BAND6_400MHz); - g_eeprom.screen_channel[1] = IS_VALID_CHANNEL(Data[3]) ? Data[3] : (FREQ_CHANNEL_FIRST + BAND6_400MHz); - g_eeprom.user_channel[0] = IS_USER_CHANNEL(Data[1]) ? Data[1] : USER_CHANNEL_FIRST; - g_eeprom.user_channel[1] = IS_USER_CHANNEL(Data[4]) ? Data[4] : USER_CHANNEL_FIRST; - g_eeprom.freq_channel[0] = IS_FREQ_CHANNEL(Data[2]) ? Data[2] : (FREQ_CHANNEL_FIRST + BAND6_400MHz); - g_eeprom.freq_channel[1] = IS_FREQ_CHANNEL(Data[5]) ? Data[5] : (FREQ_CHANNEL_FIRST + BAND6_400MHz); - #ifdef ENABLE_NOAA - g_eeprom.noaa_channel[0] = IS_NOAA_CHANNEL(Data[6]) ? Data[6] : NOAA_CHANNEL_FIRST; - g_eeprom.noaa_channel[1] = IS_NOAA_CHANNEL(Data[7]) ? Data[7] : NOAA_CHANNEL_FIRST; - #endif - -#ifdef ENABLE_FMRADIO - { // 0E88..0E8F - struct - { - uint16_t freq; - uint8_t chan; - uint8_t chan_mode; - } __attribute__((packed)) fm; - - EEPROM_ReadBuffer(0x0E88, &fm, sizeof(fm)); - g_eeprom.fm_selected_frequency = (fm.freq >= BK1080_freq_lower && fm.freq < BK1080_freq_upper) ? fm.freq : BK1080_freq_lower; - g_eeprom.fm_selected_channel = fm.chan; - g_eeprom.fm_channel_mode = fm.chan_mode ? true : false; - } - - // 0E40..0E67 - EEPROM_ReadBuffer(0x0E40, g_fm_channels, sizeof(g_fm_channels)); - FM_configure_channel_state(); -#endif - - // 0E90..0E97 - EEPROM_ReadBuffer(0x0E90, Data, 8); - g_eeprom.beep_control = (Data[0] < 2) ? Data[0] : true; - g_eeprom.key1_short_press_action = (Data[1] < ACTION_OPT_LEN) ? Data[1] : ACTION_OPT_MONITOR; - g_eeprom.key1_long_press_action = (Data[2] < ACTION_OPT_LEN) ? Data[2] : ACTION_OPT_FLASHLIGHT; - g_eeprom.key2_short_press_action = (Data[3] < ACTION_OPT_LEN) ? Data[3] : ACTION_OPT_SCAN; - g_eeprom.key2_long_press_action = (Data[4] < ACTION_OPT_LEN) ? Data[4] : ACTION_OPT_NONE; - g_eeprom.scan_resume_mode = (Data[5] < 3) ? Data[5] : SCAN_RESUME_CARRIER; - g_eeprom.auto_keypad_lock = (Data[6] < 2) ? Data[6] : false; - g_eeprom.pwr_on_display_mode = (Data[7] < 4) ? Data[7] : PWR_ON_DISPLAY_MODE_VOLTAGE; - - // 0E98..0E9F - EEPROM_ReadBuffer(0x0E98, Data, 8); - memcpy(&g_eeprom.power_on_password, &Data[0], sizeof(g_eeprom.power_on_password)); - #ifdef ENABLE_MDC1200 - memcpy(&g_eeprom.mdc1200_id, &Data[4], sizeof(g_eeprom.mdc1200_id)); - #endif - - // 0EA0..0EA7 - #ifdef ENABLE_VOICE - EEPROM_ReadBuffer(0x0EA0, Data, 8); - g_eeprom.voice_prompt = (Data[0] < 3) ? Data[0] : VOICE_PROMPT_ENGLISH; - #endif - - { // 0EA8..0EAF - struct { - uint8_t alarm_mode; - uint8_t roger_mode; - uint8_t repeater_tail_tone_elimination; - uint8_t tx_vfo; - uint32_t air_copy_freq; - } __attribute__((packed)) array; - - EEPROM_ReadBuffer(0x0EA8, &array, sizeof(array)); - - #ifdef ENABLE_ALARM - g_eeprom.alarm_mode = (array.alarm_mode < 2) ? array.alarm_mode : true; - #endif - - g_eeprom.roger_mode = (array.roger_mode < 3) ? array.roger_mode : ROGER_MODE_OFF; - g_eeprom.repeater_tail_tone_elimination = (array.repeater_tail_tone_elimination < 11) ? array.repeater_tail_tone_elimination : 0; - g_eeprom.tx_vfo = (array.tx_vfo < 2) ? array.tx_vfo : 0; - - #ifdef ENABLE_AIRCOPY_REMEMBER_FREQ - { - unsigned int i; - for (i = 0; i < ARRAY_SIZE(FREQ_BAND_TABLE); i++) - { - if (array.air_copy_freq >= FREQ_BAND_TABLE[i].lower && array.air_copy_freq < FREQ_BAND_TABLE[i].upper) - { - g_aircopy_freq = array.air_copy_freq; - break; - } - } - } - #endif - } - - // 0ED0..0ED7 - EEPROM_ReadBuffer(0x0ED0, Data, 8); - g_eeprom.dtmf_side_tone = (Data[0] < 2) ? Data[0] : true; - g_eeprom.dtmf_separate_code = DTMF_ValidateCodes((char *)(Data + 1), 1) ? Data[1] : '*'; - g_eeprom.dtmf_group_call_code = DTMF_ValidateCodes((char *)(Data + 2), 1) ? Data[2] : '#'; - g_eeprom.dtmf_decode_response = (Data[3] < 4) ? Data[3] : DTMF_DEC_RESPONSE_RING; - g_eeprom.dtmf_auto_reset_time = (Data[4] <= DTMF_HOLD_MAX) ? Data[4] : (Data[4] >= DTMF_HOLD_MIN) ? Data[4] : DTMF_HOLD_MAX; - g_eeprom.dtmf_preload_time = (Data[5] < 101) ? Data[5] * 10 : 200; - g_eeprom.dtmf_first_code_persist_time = (Data[6] < 101) ? Data[6] * 10 : 70; - g_eeprom.dtmf_hash_code_persist_time = (Data[7] < 101) ? Data[7] * 10 : 70; - - // 0ED8..0EDF - EEPROM_ReadBuffer(0x0ED8, Data, 8); - g_eeprom.dtmf_code_persist_time = (Data[0] < 101) ? Data[0] * 10 : 70; - g_eeprom.dtmf_code_interval_time = (Data[1] < 101) ? Data[1] * 10 : 70; - g_eeprom.permit_remote_kill = (Data[2] < 2) ? Data[2] : false; - - // 0EE0..0EE7 - EEPROM_ReadBuffer(0x0EE0, Data, 8); - if (DTMF_ValidateCodes((char *)Data, 8)) - memcpy(g_eeprom.ani_dtmf_id, Data, sizeof(g_eeprom.power_on_password)); - else - { - memset(g_eeprom.ani_dtmf_id, 0, sizeof(g_eeprom.ani_dtmf_id)); - strcpy(g_eeprom.ani_dtmf_id, "123"); - } - - // 0EE8..0EEF - EEPROM_ReadBuffer(0x0EE8, Data, 8); - if (DTMF_ValidateCodes((char *)Data, 8)) - memcpy(g_eeprom.kill_code, Data, sizeof(g_eeprom.kill_code)); - else - { - memset(g_eeprom.kill_code, 0, sizeof(g_eeprom.kill_code)); - strcpy(g_eeprom.kill_code, "ABCD9"); - } - - // 0EF0..0EF7 - EEPROM_ReadBuffer(0x0EF0, Data, 8); - if (DTMF_ValidateCodes((char *)Data, 8)) - memcpy(g_eeprom.revive_code, Data, sizeof(g_eeprom.revive_code)); - else - { - memset(g_eeprom.revive_code, 0, sizeof(g_eeprom.revive_code)); - strcpy(g_eeprom.revive_code, "9DCBA"); - } - - // 0EF8..0F07 - EEPROM_ReadBuffer(0x0EF8, Data, 16); - if (DTMF_ValidateCodes((char *)Data, 16)) - memcpy(g_eeprom.dtmf_key_up_code, Data, sizeof(g_eeprom.dtmf_key_up_code)); - else - { - memset(g_eeprom.dtmf_key_up_code, 0, sizeof(g_eeprom.dtmf_key_up_code)); - strcpy(g_eeprom.dtmf_key_up_code, "12345"); - } - - // 0F08..0F17 - EEPROM_ReadBuffer(0x0F08, Data, 16); - if (DTMF_ValidateCodes((char *)Data, 16)) - memcpy(g_eeprom.dtmf_key_down_code, Data, sizeof(g_eeprom.dtmf_key_down_code)); - else - { - memset(g_eeprom.dtmf_key_down_code, 0, sizeof(g_eeprom.dtmf_key_down_code)); - strcpy(g_eeprom.dtmf_key_down_code, "54321"); - } - - // 0F18..0F1F - EEPROM_ReadBuffer(0x0F18, Data, 8); -// g_eeprom.scan_list_default = (Data[0] < 2) ? Data[0] : false; - g_eeprom.scan_list_default = (Data[0] < 3) ? Data[0] : false; // we now have 'all' channel scan option - for (i = 0; i < 2; i++) - { - const unsigned int j = 1 + (i * 3); - g_eeprom.scan_list_enabled[i] = (Data[j + 0] < 2) ? Data[j] : false; - g_eeprom.scan_list_priority_ch1[i] = Data[j + 1]; - g_eeprom.scan_list_priority_ch2[i] = Data[j + 2]; - } - - // 0F40..0F47 - EEPROM_ReadBuffer(0x0F40, Data, 8); - g_setting_freq_lock = (Data[0] < FREQ_LOCK_LAST) ? Data[0] : FREQ_LOCK_NORMAL; - g_setting_350_tx_enable = (Data[1] < 2) ? Data[1] : false; // was true - #ifdef ENABLE_KILL_REVIVE - g_setting_radio_disabled = (Data[2] < 2) ? Data[2] : false; - #endif - g_setting_174_tx_enable = (Data[3] < 2) ? Data[3] : false; - g_setting_470_tx_enable = (Data[4] < 2) ? Data[4] : false; - g_setting_350_enable = (Data[5] < 2) ? Data[5] : true; - g_setting_scramble_enable = (Data[6] & (1u << 0)) ? true : false; - #ifdef ENABLE_RX_SIGNAL_BAR - g_setting_rssi_bar = (Data[6] & (1u << 1)) ? true : false; - #endif - g_setting_tx_enable = (Data[7] & (1u << 0)) ? true : false; - g_setting_live_dtmf_decoder = (Data[7] & (1u << 1)) ? true : false; - g_setting_battery_text = (((Data[7] >> 2) & 3u) <= 2) ? (Data[7] >> 2) & 3 : 2; - #ifdef ENABLE_TX_AUDIO_BAR - g_setting_mic_bar = (Data[7] & (1u << 4)) ? true : false; - #endif - #ifdef ENABLE_AM_FIX -// g_setting_am_fix = (Data[7] & (1u << 5)) ? true : false; - #endif - g_setting_backlight_on_tx_rx = (Data[7] >> 6) & 3u; - - // 0F48..0F4F - EEPROM_ReadBuffer(0x0F48, Data, 8); - g_eeprom.scan_hold_time_500ms = (Data[0] > 40) ? 6 : (Data[0] < 2) ? 6 : Data[0]; - - if (!g_eeprom.vfo_open) - { - g_eeprom.screen_channel[0] = g_eeprom.user_channel[0]; - g_eeprom.screen_channel[1] = g_eeprom.user_channel[1]; - } - - // 0D60..0E27 - EEPROM_ReadBuffer(0x0D60, g_user_channel_attributes, sizeof(g_user_channel_attributes)); - - // ***************************** - - // 0F30..0F3F .. AES key - EEPROM_ReadBuffer(0x0F30, g_custom_aes_key, sizeof(g_custom_aes_key)); - g_has_custom_aes_key = false; - for (i = 0; i < ARRAY_SIZE(g_custom_aes_key); i++) - { - if (g_custom_aes_key[i] != 0xFFFFFFFFu) - { - g_has_custom_aes_key = true; - break; - } - } - -#if ENABLE_RESET_AES_KEY - // a fix to wipe the darned AES key - if (g_has_custom_aes_key) - { // ugh :( .. wipe it - uint8_t *p_aes = (uint8_t*)&g_custom_aes_key; - memset(p_aes, 0xff, sizeof(g_custom_aes_key)); - for (i = 0; i < sizeof(g_custom_aes_key); i += 8) - EEPROM_WriteBuffer8(0x0F30 + i, &p_aes[i]); - g_has_custom_aes_key = false; - } -#endif -} - -void BOARD_eeprom_loadCalibration(void) -{ -// uint8_t Mic; - - EEPROM_ReadBuffer(0x1EC0, g_eeprom_rssi_calib[3], 8); - memcpy(g_eeprom_rssi_calib[4], g_eeprom_rssi_calib[3], 8); - memcpy(g_eeprom_rssi_calib[5], g_eeprom_rssi_calib[3], 8); - memcpy(g_eeprom_rssi_calib[6], g_eeprom_rssi_calib[3], 8); - - EEPROM_ReadBuffer(0x1EC8, g_eeprom_rssi_calib[0], 8); - memcpy(g_eeprom_rssi_calib[1], g_eeprom_rssi_calib[0], 8); - memcpy(g_eeprom_rssi_calib[2], g_eeprom_rssi_calib[0], 8); - - EEPROM_ReadBuffer(0x1F40, g_battery_calibration, 12); - if (g_battery_calibration[0] >= 5000) - { - g_battery_calibration[0] = 1900; - g_battery_calibration[1] = 2000; - } - g_battery_calibration[5] = 2300; - - #ifdef ENABLE_VOX - EEPROM_ReadBuffer(0x1F50 + (g_eeprom.vox_level * 2), &g_eeprom.vox1_threshold, 2); - EEPROM_ReadBuffer(0x1F68 + (g_eeprom.vox_level * 2), &g_eeprom.vox0_threshold, 2); - #endif - - //EEPROM_ReadBuffer(0x1F80 + g_eeprom.mic_sensitivity, &Mic, 1); - //g_eeprom.mic_sensitivity_tuning = (Mic < 32) ? Mic : 15; - g_eeprom.mic_sensitivity_tuning = g_mic_gain_dB_2[g_eeprom.mic_sensitivity]; - - { - struct - { - int16_t BK4819_XtalFreqLow; - uint16_t EEPROM_1F8A; - uint16_t EEPROM_1F8C; - uint8_t volume_gain; - uint8_t dac_gain; - } __attribute__((packed)) Misc; - - // radio 1 .. 04 00 46 00 50 00 2C 0E - // radio 2 .. 05 00 46 00 50 00 2C 0E - EEPROM_ReadBuffer(0x1F88, &Misc, 8); - - g_eeprom.BK4819_xtal_freq_low = (Misc.BK4819_XtalFreqLow >= -1000 && Misc.BK4819_XtalFreqLow <= 1000) ? Misc.BK4819_XtalFreqLow : 0; -// g_eeprom_1F8A = Misc.EEPROM_1F8A & 0x01FF; -// g_eeprom_1F8C = Misc.EEPROM_1F8C & 0x01FF; - g_eeprom.volume_gain = (Misc.volume_gain < 64) ? Misc.volume_gain : 58; - g_eeprom.dac_gain = (Misc.dac_gain < 16) ? Misc.dac_gain : 8; - - BK4819_WriteRegister(0x3B, 22656 + g_eeprom.BK4819_xtal_freq_low); -// BK4819_WriteRegister(0x3C, g_eeprom.BK4819_XTAL_FREQ_HIGH); - } -} - -unsigned int BOARD_find_channel(const uint32_t frequency) -{ - unsigned int chan; - - if (frequency == 0 || frequency == 0xffffffff) - return 0xffffffff; - - for (chan = 0; chan <= USER_CHANNEL_LAST; chan++) - { - uint32_t chan_freq; - - if ((g_user_channel_attributes[chan] & USER_CH_BAND_MASK) > BAND7_470MHz) - continue; - - EEPROM_ReadBuffer(chan * 16, &chan_freq, 4); - - if (chan_freq == 0 || chan_freq == 0xffffffff) - continue; - - if (chan_freq == frequency) - return chan; // found it - } - - return 0xffffffff; -} - -uint32_t BOARD_fetchChannelFrequency(const int channel) -{ - uint32_t frequency; - - if (channel < 0 || channel > (int)FREQ_CHANNEL_LAST) - return 0; - - if ((g_user_channel_attributes[channel] & USER_CH_BAND_MASK) > BAND7_470MHz) - return 0; - - EEPROM_ReadBuffer(channel * 16, &frequency, 4); - - return frequency; -} - -unsigned int BOARD_fetchChannelStepSetting(const int channel) -{ - uint8_t data[8]; - unsigned int step_setting = 0; - - if (channel < 0) - return 0; - - if (channel <= USER_CHANNEL_LAST) - { - EEPROM_ReadBuffer(channel * 16, &data, sizeof(data)); - } - else - if (channel <= FREQ_CHANNEL_LAST) - { - EEPROM_ReadBuffer(channel * 16, &data, sizeof(data)); - } - - step_setting = (data[6] >= ARRAY_SIZE(STEP_FREQ_TABLE)) ? STEP_12_5kHz : data[6]; -// step_size = STEP_FREQ_TABLE[step_setting]; - - return step_setting; -} - -unsigned int BOARD_fetchFrequencyStepSetting(const int channel, const int vfo) -{ - uint8_t data[8]; - unsigned int step_setting = 0; - - if (channel < 0 || channel > (FREQ_CHANNEL_LAST - FREQ_CHANNEL_FIRST) || vfo < 0 || vfo >= 2) - return 0; - - EEPROM_ReadBuffer(FREQ_CHANNEL_FIRST + (channel * 16 * 2) + vfo, &data, sizeof(data)); - - step_setting = (data[6] >= ARRAY_SIZE(STEP_FREQ_TABLE)) ? STEP_12_5kHz : data[6]; -// step_size = STEP_FREQ_TABLE[step_setting]; - - return step_setting; -} - -void BOARD_fetchChannelName(char *s, const int channel) -{ - int i; - - if (s == NULL) - return; - - memset(s, 0, 11); // 's' had better be large enough ! - - if (channel < 0 || channel > (int)USER_CHANNEL_LAST) - return; - - if (!RADIO_CheckValidChannel(channel, false, 0)) - return; - - EEPROM_ReadBuffer(0x0F50 + (channel * 16), s, 10); - - for (i = 0; i < 10; i++) - if (s[i] < 32 || s[i] > 127) - break; // invalid char - - s[i--] = 0; // null term - - while (i >= 0 && s[i] == 32) // trim trailing spaces - s[i--] = 0; // null term -} - -void BOARD_FactoryReset(bool bIsAll) -{ - uint16_t i; - uint8_t Template[8]; - - memset(Template, 0xFF, sizeof(Template)); - - for (i = 0x0C80; i < 0x1E00; i += 8) - { - if ( - !(i >= 0x0EE0 && i < 0x0F18) && // ANI ID + DTMF codes - !(i >= 0x0F30 && i < 0x0F50) && // AES KEY + F LOCK + Scramble Enable - !(i >= 0x1C00 && i < 0x1E00) && // DTMF contacts - !(i >= 0x0EB0 && i < 0x0ED0) && // Welcome strings - !(i >= 0x0EA0 && i < 0x0EA8) && // Voice Prompt - (bIsAll || - ( - !(i >= 0x0D60 && i < 0x0E28) && // MR Channel Attributes - !(i >= 0x0F18 && i < 0x0F30) && // Scan List - !(i >= 0x0F50 && i < 0x1C00) && // MR Channel Names - !(i >= 0x0E40 && i < 0x0E70) && // FM Channels - !(i >= 0x0E88 && i < 0x0E90) // FM settings - )) - ) - { - EEPROM_WriteBuffer8(i, Template); - } - } - - if (bIsAll) - { - RADIO_InitInfo(g_rx_vfo, FREQ_CHANNEL_FIRST + BAND6_400MHz, 43350000); - - // set the first few memory channels - for (i = 0; i < ARRAY_SIZE(gDefaultFrequencyTable); i++) - { - const uint32_t Frequency = gDefaultFrequencyTable[i]; - g_rx_vfo->freq_config_rx.frequency = Frequency; - g_rx_vfo->freq_config_tx.frequency = Frequency; - g_rx_vfo->band = FREQUENCY_GetBand(Frequency); - SETTINGS_save_channel(USER_CHANNEL_FIRST + i, 0, g_rx_vfo, 2); - } - } -} diff --git a/board.h b/board.h index 158020c..07e6efe 100644 --- a/board.h +++ b/board.h @@ -24,15 +24,6 @@ void BOARD_GPIO_Init(void); void BOARD_PORTCON_Init(void); void BOARD_ADC_Init(void); void BOARD_ADC_GetBatteryInfo(uint16_t *pVoltage, uint16_t *pCurrent); -void BOARD_Init(void); -void BOARD_eeprom_load(void); -void BOARD_eeprom_loadCalibration(void); -unsigned int BOARD_find_channel(const uint32_t frequency); -uint32_t BOARD_fetchChannelFrequency(const int channel); -unsigned int BOARD_fetchChannelStepSetting(const int channel); -void BOARD_fetchChannelName(char *s, const int channel); -unsigned int BOARD_fetchFrequencyStepSetting(const int channel, const int vfo); -void BOARD_FactoryReset(bool bIsAll); #endif diff --git a/driver/backlight.c b/driver/backlight.c index d563c7d..d1acedd 100644 --- a/driver/backlight.c +++ b/driver/backlight.c @@ -20,11 +20,11 @@ #include "settings.h" // this is decremented once every 500ms -uint16_t g_backlight_count_down = 0; +uint16_t g_backlight_tick_500ms = 0; uint16_t backlight_ticks(void) { - switch (g_eeprom.backlight) + switch (g_eeprom.config.setting.backlight_time) { case 1: return 2 * 5; // 5 sec case 2: return 2 * 10; // 10 sec @@ -41,14 +41,14 @@ void backlight_turn_on(const uint16_t min_ticks) { if (min_ticks > 0) { - if (g_backlight_count_down < min_ticks) - g_backlight_count_down = min_ticks; + if (g_backlight_tick_500ms < min_ticks) + g_backlight_tick_500ms = min_ticks; GPIO_SetBit(&GPIOB->DATA, GPIOB_PIN_BACKLIGHT); } else - if (g_eeprom.backlight > 0) + if (g_eeprom.config.setting.backlight_time > 0) { GPIO_SetBit(&GPIOB->DATA, GPIOB_PIN_BACKLIGHT); - g_backlight_count_down = backlight_ticks(); + g_backlight_tick_500ms = backlight_ticks(); } } diff --git a/driver/backlight.h b/driver/backlight.h index 1cd24e7..fa35263 100644 --- a/driver/backlight.h +++ b/driver/backlight.h @@ -19,7 +19,7 @@ #include -extern uint16_t g_backlight_count_down; +extern uint16_t g_backlight_tick_500ms; uint16_t backlight_ticks(void); void backlight_turn_on(const uint16_t min_ticks); diff --git a/driver/bk1080.c b/driver/bk1080.c index e9bea4b..939227d 100644 --- a/driver/bk1080.c +++ b/driver/bk1080.c @@ -128,11 +128,7 @@ void BK1080_Init(const uint16_t frequency, const bool initialise) if (BK1080_freq_lower > lower) BK1080_freq_lower = lower; -// if (BK1080_freq_lower > upper) -// BK1080_freq_lower = upper; - -// if (BK1080_freq_upper < lower) -// BK1080_freq_upper = lower; + if (BK1080_freq_upper < upper) BK1080_freq_upper = upper; } @@ -239,7 +235,7 @@ void BK1080_SetFrequency(uint16_t Frequency) BK1080_WriteRegister(BK1080_REG_05_SYSTEM_CONFIGURATION2, (SEEK_THRESHOLD << 8) | (band << 6) | (CHAN_SPACING << 4) | (VOLUME << 0)); BK1080_WriteRegister(BK1080_REG_03_CHANNEL, (uint16_t)channel); - SYSTEM_DelayMs(1); +// SYSTEM_DelayMs(1); BK1080_WriteRegister(BK1080_REG_03_CHANNEL, (uint16_t)channel | (1u << 15)); } diff --git a/driver/st7565.c b/driver/st7565.c index 7d5a08f..c7e4070 100644 --- a/driver/st7565.c +++ b/driver/st7565.c @@ -215,11 +215,11 @@ void ST7565_Init(const bool full) void ST7565_HardwareReset(void) { - GPIO_SetBit(&GPIOB->DATA, GPIOB_PIN_ST7565_RES); + GPIO_SetBit( &GPIOB->DATA, GPIOB_PIN_ST7565_RES); SYSTEM_DelayMs(1); GPIO_ClearBit(&GPIOB->DATA, GPIOB_PIN_ST7565_RES); SYSTEM_DelayMs(20); - GPIO_SetBit(&GPIOB->DATA, GPIOB_PIN_ST7565_RES); + GPIO_SetBit( &GPIOB->DATA, GPIOB_PIN_ST7565_RES); SYSTEM_DelayMs(120); } diff --git a/firmware.bin b/firmware.bin index 884f3f41249c419694ace7c95dc7affdffb7f169..74acad64f3189eb1577750e5f50a0c37e074f785 100644 GIT binary patch literal 60340 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

uK45e2HT_3G0x;obeiyVi^JsEDK%9L<;n$I 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 literal 59532 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%0A7s@}kbZdSSf7>Xa~JxoP5cY6w-FeWo6t$^c^x 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 z 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 diff --git a/firmware.packed.bin b/firmware.packed.bin index e0a1afc364ff7045cb86590a6bde1919295973b9..8e1c23e018d92599196f85fcf44aa4d83cde3695 100644 GIT binary patch delta 58022 zcmZ5|?}_qe z#VGi<=Fl8Xb`E3F)H_sbATY5HmVDCiuBYq1CQVyU#A)WqRvqQ-of9lHh5w2JV8>8D zQNtRKv~Yy&wuDI*x{s;XEWn}1ocSJw$SP0q>PgfR*yo8Gx`!;bvY z*ic5sDXD1^cIUSeq!=Td5ET5%?n%z-4wvr5BTVej%P7HT@6cvKHJSlL5A6cc+{;50 z>y|zX$AiU8ty1krImJQcoc;dt;|v9v zM+M&@)SDWVTsli0X;^>bT5prWV;fX!w}G~yKV#c4%Q9(9`1IWsvR^qst2S1dtYqW_ z=+6G}y&Z_;UDuI;+Sb&g3fv8ZGU`j272GSXnVv3dql4Q(M5f`=N|_V zdsA%SErtQWfXvor@)&V_gM<3mI}s7@dfDaS)nM5vw1BMULUzV$%{0~i z&>%r>Fk)q8-)Qer!t_%31AbhQ)}w5xbPlK;?xn%A-OuoNwztLxTl%Xzm<(HttxvdH z#-*cvU#{!?b8_0rWoYbG>xOz(eI#`Bjkl3vMA8ENqq)TL?5r}Q70F?wJ3o9st*eyE zXlYQ+|8u(_^?v`-UT8tEn&fEu3q{~L^>b?CxApS<*)o!V$N@%TYH}^3_^Tv+X8$eK z>7vCCOyNw=Hi=BOiBD)VnmcFA&&?AVR?E}*9Yj1nquB+Flx8&MU*>gN`3RWR{}ZE( ze~M@YFig&lsy47&{cUjvJ3^15maHq0inpekqPcZM?!tG+Hg**DsPyfhv)~SJ;bv`G zj}`7fo15(-%d&oyv%%`~?&8TT+=AvxKd28=rj{fRHdC?QK)9JsGePH9wDW+CFVkWQ z--FF@RWzcQ%D1hN^&P6o+(Tt14eULJ=K5a&8~)v2CGBW-5j9A6qZSMHvHR%37wzj% zbxY)t+&Pb6cW9?vVslj<)m1#^`Dq<3PD0v+hs}~lnM-83-gG=-%8b@$79IE}Ep>$q z%z`H_x!e#ee;IrA@zs8nvTBPnFxs(7%=rZ+_*z5C#^!TR10v(QpC?hTU9e-K!c`0#I4RVAa(y*{8|VaIEp%+pES^5>96jK zP;glStlmoIs3WV>4p{q)kSIXuq2c3mkxY24E#6HrS=HHz!8`6EdY5k%@#U8bi}ws@Q5KebKFPC( z4+9vlZx?FZuV2cGuuc8WIj=h~yL)n8_r%|w=ghg&8Yh@^=G-flMk>W%MAz(4Nx?>g zq4II4BJ9&yY1Qli-S_v-TQ@M4TgAjDi|$l6r;v)cEHdkTj`fKTP$l2aKcs5q7-IDe`ikXg#M)Z#8K~I zyqRTy`ZY^OD>O|dDIDf2*PrIns98_Ct# zZQ?h^Osxtj5xg8C-3b#ZPbnfy_= zi>!;j&*-MU+$t?rN8@n-u7y<@7*gXA7d~tIDDUHvpjuRxxURJ&zX<&&#_*y9$+u-E zXa36tt<|2ART%jI`?$8bmrWcUCBtYq8uFQk@!HLmVID8C?8hYGD+?0o zTAw!%WsO}7xa88HFr%WT)s1-kE!eP+d{u_swMWCH@WFhi{7%EI<0%p5N8BR|t1S$A>eF$&=}yXFbnSNwnL%F+ z9wui*(YQgeD%do+*zS)(w#DMzblh%?eL<-0S5qjq__Pydtno=lq4uroOf2s7m%8@! z4u@R;aYK}G?l@jTV&C7A?6L9uRF-%h^yJHN;cLRMt=qz_aVy)Iprk4FI-_65p00)- zaeVDHSYx_pqn^UVfgl?$F?#fATO5eb!5J2^iqo-BtUC-!+q9`Nwd9v5Z|dc?j`}AS2jjxJ1PC@8x-o=7P>h!rZexX-8n3X%w+#D5gt`eq1a0`3E3ShnK&d;Yzw zZgRUV+5Fe@#vC2gv~29-Lp*+*Aqi>lKZ)#4cB1xdecp3#)zexyT#{<)wn%sb$H{L&cDGM6^HJQR9(;k^zC>O}1^O!#=>-W zBftOn8u~2Shw}+$3EGBxfL%xwA+EsZ$h1_00F=pw(BMYp_A#_X_xZLQ0Bah?*T*QX z>}ki{GjGUCf8g*UtP{tkUf>sHe~_j`iR=2gQ)1)nKT7NO5S;CN<_m{KgnS3!KBVF% zSsa;wM-#CZ&@S&J_3Q7uB}`gXjyLjc{_YfaPD{PI{GN;Wq6nI!bFySVVxACAdzrt{ z1_7v}S2}1Kg|$$3bk{(r@xuF?3Q;_xU2QyWi*9shw4c9Q)!RPN*CKfJKleqec~=lS zw>WR*}>a@P5GL?43n*#}p8Wqz9Z3PT~lAig`x~L)Pi<`mrr%u9c!0+SSiQl%c zdh>T`*p0?g-K$f|r+0}ncB?Skq_q~2Uf<=RH@DBkJeSCG&-vMfJ!C?T*=xjqih^vY z#x+cGt*72J>29->BLoep`1g5)^-yZfJe8TE^P8H=xS7?0=kghz?55q%8Jgw&>THb* zUpd0IJ1U+scR3|?fv4}qfE1GYH>E>Ef!br4BRF1vDcjQsOg7VkzcEips2LL-k^=<&Z~BJkE0}(yO&Dj+(CN~`Z%^cXa-flo@#T%kD&fgR3TlQh zi-yWCkFQ`cTAfenbLu7b!|df$s#O_ce>%CnxD42sc((O*wzU(g3?$sZOK!J^z*~@& z?!4VqnoyrhX<{@N&cw8=XTR=lEA}Gp!_fBmneSJ4M4G{xg6P(9sGTd^N9W)(WK8wu z8>|i?&RJ$-a|575a~EBBWkn934r|{(5z37pQ5`qCT|lT1GBk_#7s1F>uzSAQ{_v#{ z%b2Yk^qF1Qbfd%stachXQ&%NmZ9{F6-MUN$qk_w6GJ$7%uP*Y?THhyMIK0gbmhFpt zYh)4CLUi_#WZA~6!Ri|YCpsx=OZ}$Z3j^X;Vy*U+1#36Z_z*cC?2Cgh)YhOU!3=|MR9LMoc`cT0NQ?KMVb!)uKojn+GkWS z<$V3O4`*05^RRhm^0CERo2{Xvh^|3vb$jogHvU}?(iy97Os@E1D~z|kDkz(hFO1FS zQ&5x|4GTSJ*#3}D@!v8RDu2h7Q#X3Xd_dd8UVqP*8V>myw;}2f-gtWcA>T z$S$9GXsn8^vJ5V+90?M2l#6Lo(88tFKC58rxyNM6#|77mO`$rButr{nu8m0vWaoiiwh7iMzC={Q`rSio-Z}E+t-?u@A=fOg-(5 zrp=PBGc+XYIoX6*smL>Rwah3LryFds46 ze7V5)O^JY>BgKEN>`BClWqyLW1*oLyWG1l^>>QdTtDU|PJ$zJxbNw$ij7A29lm3U( ziU(`ks3fiIWu?qmPg}QwVIgkoa2$SV3Xn$EPM$<=E~$vFeaCnQj(DIQg3AKn^A*yPTvpl3K%>>R*9+42NKX~-&UZo z@S+aa;u@PXMCgf#C0;uUP-kS%dg$YltCfVe7!Cputq2%v0|BHJ(Q{uRrK5nK*`PaR z@i#~@tsWUp@cY>*k&n4u`Zz(y{&sf*uAnS>XNDE-9uEQt2@LNYwLYz900A(}I1|1g zkm95YSYh1qQ$Zah{rBH!W$y9D za_3WutY86;hDCz3dp5C*ieEfvOwL;Q*@0Y9x@;4~sjD6g9zx}Xe0JBJiT;x=`u?G#n2o0kFHzFbI&F21{#iGc z8q(Nm>YtDJBWObu#Z~wp3nr>3oy4yOex^5|VwmscTpMtBvJqx_RSs5wosPYLNNZ`c z$$H)A9|wN`CN9yAeQ+aq3v}^d7!;s61<zYe88?ljIRY%f#n~p(K?FyPK~rYqsJR&e(9>Up#H~*McAr&G zb8@v=0*ut{7IIbx`0;ouX~A27LT919?)8VGjeT;n<7b43{FeNf3dW@t=#GKuZcFJX z{g!o|5)H?l<4Q!Y6SJ3O)yI5{?NY`>uV!8GU?kWFP|8;ESgY19c8`SWyB27`Gr(O( z{0T2s<+mzd+VqAnGNMwENzxl;toPl^MyXM6eO2y;i!bpIxmt zPhV^3{CY3kA1E);G3}j^RRM&;NA?A#w!S7dc0yh2Zd?{_3A-gkC%_5-wWZOju3O@Q zAb>nnP29%DN;h;1hYo`RweR$CdPXCm)vh1{>0)THOT&rXx2vUyM?~GfEG& zVDH}a`$By{2fgyNmM3+++NUnp(#;mds2AO&`b4jW-P;BN04ikQSh80^xH*hiQh?0| zu=VtcK|CcjKRTe$8<;;6W}%6{3Vk1!cW2u9-m`Sh!8mLvf*?@AhZ`3|Zv-+EtFojb z*T50NfRXw{jbJ#!A4d}}N6QWuItr~>nyDTALaj#A$on=takKigX*k1L{|&a5Lb=_~ zubb*+MaMQj04IQE?tu;s7j0iqVRrwA4M$!lPTg&F>(Wx0=@-w+1mZLH-=n5m+a=vp z(*B1FY~+i4&?z$XMsUByYD6gW<%&}a=GJejqJ1NIV8g6jrAE6-ZHoz<2R6i{nf%K{ z;Q+GXxLF2R0dhueJa(tAf^b7mUfM*>(v&Wo1)YC+o_qMu#Ov7{6v? zkowVIJkM!#qS|3cobF~@t2{2}isqFu>?$e!xrW63$8Kcm$*0(%r=fsFctwmF{4 z6+if`y^T+n+NK8$d$*vcKmh(4#lq55k7xC%aDNkeHJpGK5k3~zH{=kW-3hq1_|o%S zV#yu?RNZTSYby?Zg$hpYs$p8NLRcdk_7fZ8h}%c7h4cnHg>-YQj;sY4wc209g|~GF zpRP!>>!|&f1lBd}hc5s0Rc3v}`Jq)!4K9-v+Q=%6HDxK7qtK_PLvX zkHc6sbj{FHP_jjg-u@a63z-J#7=6CWXOhQoCIYH6Q{ji&BOh;Qdj)&=1mAO%8r3Vc z6(?|x;w=v`!aBfKyhhpHx=#x2;Md}7%B5Z?+N5-=0>YEMe!L_jXxVy0xD}{#N?<~$ zk`VtNMI_!Y2`Zvd19ec4(1UE)f4%Y}P(Exal~yO}$|#~dzSf6h7qgZu+dTm0*-4|o zLd$#^KH;D!SLR>ibKAzHz7$dtKx6WJYm@tzT*<3;JBqSGQO5~b6h|t-xoSOR>y7Nk zHh^3Oqg%t->JjqKl-MV%)NRln&rvdfblhp?1^GeQD)<4+`?e=P&YhwjFJ5r2j`RuK5frSb?L6oK3bSMC zMo1o}6j5n(5S*ZFWjP$J6}4>M{rf{yyE5@QH<(7AAHTMa=Fc7I$z)C;gI{H`Q;JI*H0Hpfp}-F9mK`WH({54}*10dkFKzscFeuuS3t3E1SPc*+)b;u6eQ9eaYE z2zW~?!`&io_I{7$^I^vrsvicQf*Avo^hS8T0ZF8H6r9ZENS>4u!&AAk#Hu||58Sfq z?l;COG>J-lp>CveSirJsdh+U)d@3}fp6v{;Vr>ratyfUnZ|{Ji|Dz?)!IC`-e&~dQ zo=!W)t4+NVWj!SQec6v9h|En+lu0Sij%)cvUYnDIm2^7$_pA@sFY6_!XfR|*`R{rK zCLLWpa>aHONdMTKo1|f2IdwDN!EF7jdjpW3z(!KRszjUL|D}Fqaq}!=udtheTM&JL z$rM- zZ=r~P@sgj()`IqM6Q`(*x5=r?;hp)fQI!D+)!;?r@{{F!xerq?1KEk6~u|4kK*J3S9*67iv;)UHKXl>*LTdIRAE#44z-^p?EQW#;T#J5e!eY7-IZ|a|0yH6iflz6oNi!Fo%yg2eKU@qb119EhJ{pH_~RVXTNaDLzdkC+E8;QcQbgs~ucF}%Da z$3NysFwqAUwS&fFsDMNPH4i+5jkcD~uP=5A&j+pb>;-%Bf{xrCC%a$KXmI$Vw$^W` zVGl;E_&Kd^+68qw_qmfkwBj$+dGsFjTp~3k0gn3=DV;DK;1n-nZ75u}D4Ce@YnOs% z*_Re3zdid1)$kuM?(nHaal3N>$?do7R5s^W-xv<0#G~U)H`;-$C(8 za#A;ZdtS~PPK2`*r0#Oo_amzg|I#f){`)sbap8trrhGn+I~ASJ}E5m*4 zOUNwS1Z>i^1bE7qQ!lng)$gxHb%!m8>%961`EN}6(-Tv=dZ`2_4C~jr@(&gGxs=b2 z<7@7v!ALK&agwIPv{JUZ#gZ)&@V4he(y~v;yYqg&o>#Q_J}HsVtmz8>r@7SehME(^ zI%4XrPq59hQw4h%7G5?TAVy{cv=8rNB_Ojx>l9uc06*x>eo~)+=CIeW!%O2b3vvAX z+mv+a!zY+r-03USEzFwT_p0gu6%l()AXU+i3d;42Vmqdl#`hjPqovQ^{yIXeDuw(3 zXFKWMSa<#$Az_vp93gX*R^!XFOEVo7L1IuCBGXu0Nm)PCZSc#Dy58qPNg#c&{_YX3`{w4@;C&mNBLOOg$0jb@?}W$z!^(}v z;;dcK^x6OTF*e_=bX??*&>0l@sxnehrh~qK^)ZQ);SUYrw-Zy%*;~(Ou+WKb%hwQ& zLRWuwKk{)$On;7fgeLK8tyb5!`9O9&n@Z^G&4Fe{8+RJxgc_F5qpnel*+oDSX;Mv& zH{CG4xwmhmOJ#?nknS`|*nUYR-U?}ku}f=d%(an(pJS9AhR9mn+0uBU@kt|v29lFr zGqjbMlTr?P!Q_6FWn}SAS?Mnj{gm`RxFkIX-a7LGmHiKS2lyl+P}V6wJ>LIRB>~%e?NQnMExHXBm&Qw zH^Q)iv8uN#v&fm+Eb`B13{)IM59VlfT{yQek9lMC^{$fZyC*U3QGqmpXVScNG{?6_ z4bM9Je~ec%iMGbOG0l5g258{xW>&FgfuCSf^S?7ATySQzzi9}tPRV9!wXxwXHbC)$qLTc9}XcC=#FjOC5ZO@XQS_Gl^i^ii0|sw2`91cFg|P=4f4pQ zCz-d+>3ytg-MkAo2rls$?W{S|tOn13K$5y9s$nJ|39HWaDTjCDS3L@c>`CNSDtV7~ zn%9t#Vi&Qb58nv0<-b%6u=nHPN$s`T^*q41`e*x^Amynn5;FQnLtLJb$F!+&snb1; z@xCe?DroG-KT-50WS$2I|L{o`?NKx;vZKw^tS=-e8cNu%rHm$ANi6mn{qa1>A+xUI z0m=~&vq=c0m@>$Sb;rQ1-5wk3a&uXu)>?rDl^u*bj*53>8fMKxeW?^=a&HbuXi0 zu1q4s%E{lYGwtDxS#A&KIp{k?`X@g#uP$y?Jqd$guZ%Pr$*#x=@ePmtXyG~_fth-)`?+wvHFp#KE}U7=h;NgWW3N6&5Vu& zfNIn&sL67rnI8Yf3=*H|8aVreQ*H1Aqhgws8`;ipPP!;&(hcY7p1_fMh@r{kfhT$A z=u^o)78kGB^2au(cj4b$A?A2?!|m|4dQ>kuyHfb8#&vin|2f_!D9Px*a2Q8_O{q@j z>4UvBRZq)_wpoHchK4ADw*Uo)b!`;*f@1CV%kc3&b(B(3%O1Eua1e>A^Jt&xV^Xa! zJJ#~ygxpAc#Y;T#))p6L4(|Jc-9X84Yb)D_;qk+wM}&PM8rz)?{_8(&ubn9Fiz7pn z#0}7o7z4kYKk~5;iO%8Y2%5osQRmEr_+7T4USGuHmG-u0S9ak2@Q}dN>u#Wt_eWTW zel_pGfwyeE0Xj8_F2+yPt=#>feY6%wG`@tUdiH26$K6L+QlW>ByuW=pDvOPI5G2Xq z)J*C<9c9q>aI{5rO?*UVw#b*)T_uILo_e?pzTtJW=eLjFeE%?8i*L+yG27z?+lE2x z?6*im;%hYT%W=P;ejXOx2?DN3-@YR39aEXT^_=%%TYIZvpXLxhn5g=VOG(~no0d># zWK8h0l25Ral%r@ygzM?M`MD6EHI?CDn6tT0(fs=FC7$9SZlw8f&Qnd@q%a5JR2!&bR7x`=CEJ-D^J_)1YBTcmaTIM-YOouQ@?5Nxn13S0ER&p)X2HE* zyNF;y8ec8_&SG^qEH-YKw=?vxX3lSQJLfe%0j(L*xs@QNyAS??o4_)}P&t=7Hpblt z&w;)B-1iKyTAc91M%`g6vYY$tX}mx!`EUl9zJ?n;Zc5ol{TyORGe?t)%tPU6kmXGb z{-`NtzPVCv|8f)MX=Unq)2sX@e|v$6I0r zr&j&o;E?W8G2^#BVaBTw>8K=*b)9-!r=96lM1eRgd8EoP|B<)>l~VDRG-c0LN(Abi zHBQof%M+f9t;^Us3*%@n+T1^G?w$b%!%jZe`9f2^hzeboK_{r35}1%YtBgyN?uTk3 zfohgR*1n;66o7imkkwW=6OMR=)7f8l5;X&$Rt?|L)}rW9(ahW8G1iW$Ccp)s%GTO(P{{xuvYQTVptY|DXMV zmWPRn;4n$mhS6e^&9@T!O{nqgdjMG>Hz8ZewFd!SmvouhJI!&MN#c(_H6bOgCeknN znO34ZUtJ={lKlZlvOUah+i0JVAks$t0O~BtR5zk0GK$=z{#Fd>VWPa<3Vx~8wsZtx zw@x9wl4dqOiXVpjPn~^I$?n`L=L^?5&7GswpRmPXUY$nU^Eb1Cu9to@o^ zIEkF}$~p&0Q3q+-=nWh1)2^m`l@$=EPc`AB_;SC-d|I%rHg^OdA@Q+VZBKJYUsf>q zsb~`Qn}O1F{kJpc+Xg22_wG?YIxLAO?xP>rFskKQUNwK6v43``YwsA38}nU?fz&{` zcS+%?M{FSLUj!Q|cJWgj=#78;Ut*&-jK zpJ;rjHC~RBB$rzuqgxZ6O4hz zq#m3ZX<-7;ko#I>{D>*zn#}cmY!YQRTa#zTO|{2rdA-0cSqQzD19T z(dDL(F}>`KI`JVwd`x$#=IwK7wPf@f6}p+zjBf$`B#UX=IRn2Buht6sv;Oq-3$sv6 zv1DlKOy21JQWW~?7aIP$UH`&D?!?wzNVr1!zF$Y6;5&W0V|xB()eH7sN}K7WIyVVj z`?L)_Mv;4>=F(MCM&+t+l9Qi9;nO?s&O5mx0&$k`)FYuObEE;BqHZBaKlZ%MT3hT% zb1QBDH}r=1mn}Tio`6nV@^6LZtq`Xe03 z+|ke^>1(b4o&vX!pp|M)(-3Wy$Y)vrD2cRd*t&ZHM!>jy?+1!CH@HxECz+}B&i;+oD~dYB{pDD(>*!dR zgJE{oiExxJ-_wl58@@*pY&jtu^vXL+CM93C%J8r%UAovEoY}xuEaP&vUF8V_`@rj^ zj?m-b`wy8&0~-4lze{QFxpVEoT&C8suf{f*TGKBrh4gu=#?c3 zkwnNDNHQB(dUZOyT^mCVD9rBqnsh96+FV898%Or~xX1U{+II%8JKEhh@5;I9$CGMss`A8Kxb)m<=69c~=u`grg@7 zPcGakNE7d;<+k3d(X>BGCO2PnK=j%bdqRl8rBWwQko-rMgR|rR4y~HW;cxG2!?o4j zxP8z>D=Gin`KCHfIGHlHauj5bXaRk%tfB$aw9op_OWe_w`M**mtcp){C29Xf*LF)& zZo8Ap)M5eIkc@U?ztco*pZK3*OTkaw<<^MM@zJ1lN4{RR8Xu4g#(j0Y!t{-UStwZ) zE4GZJ+S9wVUT*JeBwam!I$wvs8_{d;o^XE6ZDRU`M>*VSP!0_@nj){OKmAp|mpWkl zY_^&y7+0~*l>e=S&}*ywVdpHF?7B~ zJO%~Hnj=fkbBq$*2J25@o{L>FE$rK+D+%>2=my7^Z35!&%2u)1Qo2Lm?tZDRm3Igh zBtHL0d3-lJ7@T!sHNnO#YI?hE(C-acmfN}2E5VbYc2;6IhqR>?w%`8w7cq^dQvuxM82TfSIm3p~YoxemzBFP~I=$T~)$!`GbVW-ZFK`igl^)%S~Buotr~JpxpXr{e44?Y+gH+|}(% zOs!tcA0PTf(7r_WUxg0>c2oim28Rt=F1Q~37M7~0S#4aoomJNL@zOPsF3!-58u;coEP*P%IN z8e>JMyr~4Wq1ggoUy{uKfMr<|-<7VJ6CO4YNVM9StxUufR^S1)W<(b6zOfB#A?*)8HG*gvSu?0L7|9PvaAn4p*+bF?dy{a@Do$9c@Ib{*Vs?iApa22))~Q zNRsTa8XdQ2G6|qc9S{9Pp4EEAB(>yU!Y)P(ORxS+%}3e58y-y)XUKXcS!!BLp3vSp zf&9D3t2)rhC=aqBMFzGkN~nwOx(7367V-0xLFA9AgejRdo9Ub?Z2g0k&EM3V;mjl} z<*U9daiKzq=w5dA`ZB@jzZtJaGoL+PdYL3}O>HdHlLAz_6EAU1l0lX4Ib8OSilAi! zfnVM*);&dmUOk}@F4CJT^eixA=D}mC=C=F!$KR?-4dY(J=@kwb(-DNY=>8RxR1Od= z)~`>S3T1-4*0-N-t>K{D`-NeP&zSme87J#&fs}I9irj_M*DjZ53FnENE2e|atP}9q z7o1o4%Zz}9x6gBK0u?RQ-ugC=8^^x4pTn?6`-DPhz8M#x8O{qC^X~r4kT35gLsS}$ zgB5UJZCj*q(j{YCp-Gn}=VaTirD2u-s3nScy8YY8rw3s_E%woR_15H228okej@xIUwOHW!q$M?Qce#2|Z69NS=-wevnm{p0j#?Cx!vD|DzExwyg zPk64gtv#^eA8@T!22{nR9(YURq+m#9s6W>8K~}>pD#@oOZ0L5xo*Q zOHy+*`Rqp#=3WkO4Tds&hzf_Ee;VJdVz;W49>xjxf8YakvY_uu|BFX}fX9e(!Y zzo%UK;;B86L`NPGI7@(LOtzq12VmT0oKmUveBV}$9lCmF5M=J&=Rxr8FPkV4Xpg@y zpjbPB?pPMAfRhd0&2z8C49_2f#_5|LW9FF7q{t#X9+G%rPRcn^= zyLP%L2|W24yn|DoeNJ?Z&J#FyV>&w1p) z)$SU=?)yehFPW4B)6jsI!?Qz1Zr(lL9H{Yf&Tv)F2hrG(_fP112VktEQ=IAbN_b55 za{XhcU_{fosN}Imj_IF2AMifHSpeR{!dE)wn*EQkTL_}|8?gZKhU-J^g?9!nQMFca zLXgRSd%;Fz7l_{NB%N?h$nlKg9N#}B3@TeQ&*_zi%&+d43#l_P4xJ5Wa>n%-(6M0c zUi8B{{7%8ja-5a12?qhBrl(@E-qd-|j7b-zQpBC~Dx4Rkwn(!8As0g^f3dR`;^{bd zz>97hE7O6-k-koZy;P(7lED*O;2EQKbG((UOj$ye2H}|e%Y_$#fL1>?cgPJf{#re6 zd$=NL%FP~iFI+pD|M9J1O++G1Q#0;I)7Dosz0HRczNKi6H0=o~mia}R^--S^yf^M( zTV&&eAEKYSc01n#XS$qpthRc|$B}O>-)%*of^_H4ub=CyyFlkO9-1U`g)1vrH*u

M$w9HweBR;YxL1T6y7%A>4g?lWj`JE(JUEipRxn&=a~mG&1+n@C1asF9G)1N zT#Vayb3zDm(SI8TWCyu2*_|v(ns|iBFCTl!lWO=vGk>sp3P+dsWIr<{Znm?^!_&qeV|*2lBK|Y zDIGxhH$NQho1z(q$Z|*M&xQMFyhuHom>FVZr@%R0aC2y95jTf1FSs}y&(;T{Hc?Oe z1TG9_8d(T53d|RBNl(02Oef^;owA%GpPEZugW`Q(gQDr*|>-{(e)>Pk*54Jjg70-AwuyNzkko=G4LMlfc4 z8p5Gz^XSGa=jCm1#YLt?G9~4D1@)yA(va83n%p6-q%7c$hI@E(pTYJXbt`xV(lC-C z^s*V%3gaE$yoBvZS;~I3i+_QTZiKE==9@RUf)9yGJs}SjaNSXqssO;1$)BTDGm=ea zSc}E&{)*)L6BW63eDGl6d&K&>GrWQQgBa~32e@1^8gIHmtQE~fSFh=V<31v!JD@pr zCY{gn%_I8>;h%m?)lE-4A1T5!yo{~ApX)|E@D5_djE6Ou|6lRIz%aU&Iv?}b4wV#e zwnWawP#FZbuQx+0!6~$Sc7Avt{y|^Vdg=C}Nr1vr>tAS+#I?9DR!`-iq3wZSVc}m7 z3#H)2#9l%>d0cXdDe75T)hiLPxC&p>WzSU(1HsyIL>w|aj?B6FxB>m*83iHcpS^w` z(uET@BFu}jc$`e@z28WuPSMwwmOdYVXgd#T%0A>eGgfo?>SNXjgfd2mwterZ?Ufko>Uf39hV#ehf>fr1Y^nEQ1qwjPHz1c=U%*w zxv^bQ4$@OuNo|pt2nbX8`J4;!)+kW@f{!xxX??HSSi_%sb%DU=b`gyNbO9ggoC=#r zCtof-zOUZfd8FYQkSca9+A5ArD0y|d2!=pzT~P>sDQKk?scwJMns#06)$`PJnNB%F zj-P9dY?gj_)HRUz3s**%$Bn2px(qZf7hgGLc_E3k?ZT-}pTj!q**<&@l57Ja$sMU; zO}#jgbVGYc9*A)37uQVOC-gQD`%e)H<}^N=lE_JeyU7;~G}8h6d)v53cd!-91zE;$ z{ApA&sCF^v>Yvm?RzC&>2NV<>4=)-+!2p8vIo{GFC^-796e?ASTN|Bw{-$Dy4&DGS zh;MAsoJWdGel9MTabN+}F2kBZR8)e&bFcI~(^3}2@6aJ()rGSThgU|~WcqCD^|;1$ zkn_8vKIZ{BbnY1Oy43pgq(x>-$;H%$`k*EE0dk!&$*6vYLz8LkA{3)8p=Y6c=vE8$ zbTl_pT$}a%fU-pA8>PhBUj#cnQT~(Omg~-T@~a51kfz}m_{$V zv{CNOOKXLigX1*o@blWpnoOTzzJAL1;Q@hvwCxvu{3n!DSv||wKwpR!_<*1Z;k(2* z@ABxo)07z_9^1RzyOk{~`qpl!S}v0nH`44R>52+_{vG||n=nHWpGC5BZ3h*n*`rrUa>pFL z;SnSzZ!pX#9hAWsTM=|pLU-#kZgKXzr&I1-pH z1Xd+n>A{};e42;lr7&0v~g%Ip4$ax z06a7+H+0BXV%}$7TedIQGDf2Xp@5P4t`z~kV7RR`EN2~sVs9|_42Wsty2EpaGWiy! z^*7f0qsy92%9w6Hq2qP`A(AO?^X`3=CFC&$6gHOTI-c-=05U-U79ap+5I`&lz!I#$ z1_` z)E4af)QimR`FZH=;v0K$bUIX`*w{A#uh=tiWywP!;yVT0+B(2a<$D3OqcTyS#yz}M zYdS5x?6i&F`+Q+EtUQ`H?Msax$z_qzgL9^b$2a;QfXn}_06A@VTxy}(O`q32y6>ui zboq(9WTTZN-C^Gh!U*NTeBQ1bvSxwSYXG!f17Ig?0#mR_(0mesGob`VY|MH2N9T1FWDZ{rd|VqN9wAFW+EY+XiPsWS|~U1OW^M;w{;L09xhM##}%EO&|a! z5Wo}&;1B?(Qh^`V452G5~ON57s3EpSYe?^9J}1KsT*Z_W7XhKvOSe;vq>fetlT zYlY<{{|X%cv`hDR+Z)?!Yngm6j`M_JWtqSxn|rKSEdH|eV=f7o61q}Bd0*!@>jr42 z7_OZ&7u%ArKhqw}$5_7Qz)0}G`3ZjHv`g*)%HdNYPc{yqwcqh^JWfGQbTZ3LyZE=U zYA0Q)I0Dxek5Ji+*Vn?n0t*1O+ang?T_sVX&8tJWc)K~7BT<|fcnhec2L3QZyx!T^ zeb1)G9{%z1GN#AHm{H*@0?Khk_jEw(7~6gVm$U#Ym5j-T;GZt!u*A+T%h3@KRf4^X zpFs>?Q4~>!))YGEGWC`-kO+E>a;bJ*jk7&3PU|fBnHN$wem@~IvJPPEX$l74?ZCac z@iEHI0z3NiF*-3rr6k$9Q0DXBV|l#`^sPPVh_c#RnaUpZF??=KcU!lAOYmZ6GHZ)t zU6<;TTB38#u;%H{*qM*u(`xTSXvnY%GM;bqzX8NO7{SznFPpExm(4>G8~cvlYGVmV zFn|EhK+bRJQUE|%cMk$L7TI4SdZRi#C66)l+1|jQ zd85j?^672N4D%`?B>B3AJRt?eE5Sj<$Vt6DP8pm|dz^$pKYW`n%;QX{*p_=T2N)B4 zC?*;h=}J=+9ep{nDOD#L=CPm-Ez7aw+uO*~sG`IF-oT66Qx9$~^MVn~pb241$Os@E zl?>j)2JjkkfY)%i**|FfV*xrbm9aErl%D%*2A9za&hfk(k882R(YbLH(!_Yl`fpsF z;GsvH3cG@&oxOwXhYit5un#=EW)lbdz%JMaz~ON9_eqgwI!P$66iMstvl17;^)9W;D4C<3a+TSH(t7tmhKLb5@`YHZjg=vgh3jl4<#s|bayw* z&>`I^pbRC_NU3zkJ^bH$*Sge&(?c*3b-!U=9dbZEh}p=JS`!O{Hpyc#2UA5Hel*ZH>LPM$DuZBj02f4mAY5## zvtppC)Lsr|k0<}s3r?BSFr;E)2A5B}Tt5wA#<|m(XJt!gg_us3XpIO$T$%hz@RWw0 z{Orx?`|$>s6T9^2j7Qgo=bWu_&zK0cKra3{X1&OZQvm>D71*Q3WG;S>-iIASQh)dS zCY$pLJ}>~;P3V0yX+6#Zqzr(|VA{rB4FV7zk0fd4*(#RC&bN%WC+_xf3kF##H}VC( zqPK6}R-}=!pL`e?;P@+O{1$~crMm3QDc~rnr^b<)bfhSSP!%;p$HG(RN8OEInxoHL zz;~R1VPS8^Y4drUS5)ZgQT^)UU>0z?1XL}Zjr@LcX+vi(-{!3 z!wjg(>f>EBErLRqJ6qu6_LTWMo2Vq4G ztK{=GQh_A=P|&9J}v}g{N{&6p^*KQ#Y%^hB&&YYBozB zP_~=hjPOT~(oU6b>Jkox@ja#RL$uSC699GEc7ac-CtCC-yOT0rJu{Qs7U5G<`e%}~ zyx`%bb<-e1MRnHw5Z_V#vX;h;;{+D(9Mq(L71H_kc^RA>#AME%%J&VvF_opVws?88 zN(iM|#im)`*p;xk=FKCA#Groaf}XyL)3_FVGK%sZeU+kF_0UW|uxARq^#&(EfTXsZ}0#4ly@Y95nLTviso7~Y^CCW06G$QU&nP-)y zmO;J^7o>H8nn}QdcPKO@i}0J;y~w^D0saab@;r z`|RL8YmI)_b0ll;+)xqfwNF&dile|%2K4rx7Ekq8=)d_#Qywz5P~;xuq_h3M$;&$8 zU*93?e0&yMI#6+Q7qaL`?_nbw8SUKV3HF3?MEAsRUuFkE&IrQ$G)6GTdOTlug!c&a z<%=rQI;^~pM1}>HJH4c;ZeC?55vx3w;sz3?^tXD&T8(DVS#K&GRhlI7X8|J6>f}}g z%Cik=XSMTF|Bd#HN3j zhj1(o; z!cD@|CBJRs%kMxk=qm!1%h=rbN6tgLnP0bTlX(AH-gYbNPhr+;Q%=;3+BPH$7s%21 z=qoGl)Vk5xbPC+L&{R0u#vz z0d@?~&MYHjk{zAL*I1`{8b}aI4N~ev)ooN8=B1MquHSS6dwfMCMOJZ;_EV%=D&JYS89p8FJ(n; z9Q7lg_10ewo3yK%o&|PesU^8M!E{h&f%Svfp|Q=jsittnBo;_Cm7(H06oX)%JH0%a zIG3!yiA)`Wq4!il>Up~>cStYpQkT4%TKOUl%ff3n%O>Kp>(O@Lp-Dv>dG<*aI&mV0 z{xm(4$$4@&t2%G)8*KJ+<1X#zb|%AARYoo(Str!6c5}Oi)|^eNa?fIM7?>`IErMPa)@8J05?A3L$InTdN5niBv0fnEZUQ?82CLFp*~ z-U0T6S9b5a_}@DzkeSDrBGfHXXH3K=qLPD zC54I^-w7yD^;sU=J}^$9|Jo=3Z1z5>kjYh)2TLbEM!{-m@%iV$RhJhmPF3B=)}_fm zef;=MvLVV$6-eTq^di^UACz9FeB?tZXs7-C;cS5L&-2?yOlBU8f-sxFcA%TX&<30f zEWjxE0!D!?7zJ%$6u5y=@cywU1f$@Qq_PX{h}gl&2#DXO74E5PdAf(xp^RB`=wK+` z=YN8rgGcA4Q(9(fN_XnT4yd@`vO0&DxCu7oiKccSqo{O#=Sb&nD$dzl9-YoUR62xr zo)Z!23OTjhwHrF>vR5LK{o9smB(wt2MtZOt*8R8pCN2ui#v)TY5-RWTm~4P`>H6@lHxat*ojI z*GSiAEhyHvqDJp{t_sx$&nE$>%ep&5L1|eqTKr>26bLq@V?Pq2Q6)jVKuIa-3b0&a zy;Tdy)SCl4BDU|^$Ve8jS~;bC@o-^cZ@@T=&HBgK3RzNoTyto%0ItY4p`3^?rS zxeDZ3_TuyuL`08CGmZ9}4(cF^;nq7|8&6)nMfFUyJ{3v}K18nRoRRlJra0gz09^iU zaG_kn@dFe@+WU+KApxGVUHkJ{C+L~@49;QqNQ^l{W*xBiE%9TqGYMN%PdgYBC~G-o>de?FISd;Y z(?fph3XWSBcE^7a0;fH3dI%b3B3K*RysCk~72kWQL=H#C-!9{~&SDF+@`CV-cFes(E&Iu)oQ!O*`^WUTAolurq z%#{46<+1>^T!LUZ_5e2|Ru9HlkKS*YY`9?;8d@~sjL_tHe-|OLQ}*F!p#nUPXS>5f z&>rs;P5q=iOO?MGg|>L7P~(ZJGPx4UY1UVDiB14(a6lBa)f+vmkYx0BQDJ#X7TB4SS3!f$}l}+LMl2 z5V>?lZn&nD!YR`mDQjt!p~=U`KUxw>1i5{f}^JO>pF&EKDmm0#p5Rkb3IA8(tp zj=BKYP_)iry;<|cQP$U_k5a1jU5Wg7vU;MgGF&AFKWjq&&Qs%&jR^}1`5f)R#zV*q|!(!=*Dtrz|?#GsZdnZI))FoEj|k$3cb z6Do%_7Dh#j|MQabyqIqahhqWyqjD`nG7ea-e48sx3b`(G`$YZy%d?vk?tS>jO~vjm z#;-C)c~2U~L)lK%23H}DT?EvEkYdn#0FFTgBlP1f=PbR$*@C0soX@uvbG=Lu0m;s&4pqv6ff` zXEQl%;aE&U61~{iR;D}CO6v>NDuoJi@rR<%b2w194Z_nxe`k~Tao&ep=C64f?ov!N z92U4tgOubd39T-j=Ioz{OY#b{9007{Mqo?y$lre)3|K&>5*!o|=z{`+Zg5N02<~c; z0xCz`%vK>HUHG~AS9MAKp6-dbG|Gw!j9*iz|&w_08XB&u7Sh;u?G0P0+Nm?$?nOO}(c zz{hxLtELmY#m(XTuk>%1%0DrKjzd3Kq$VY`=s~)iq#OkFONCkVMcAmciVZ`K-5%b4 zs>P$^gfLU_f99}$(box8NW%qh@xMa`Z03vsfyo(6^&1oeRY85h2R}C{xO!=nF?4;ufc64g9s{C;PI@qUvEHV~ zr{TwcCbubNFH>Yc5Sd1KVDf?Ot4g(l&WO6n)5#fShM`Wry24M-UVMINe6x>i=+3XP z#V|$g=9!Ff`Oaocvvo4+L=m~iL(#C##yvOp#6^HGlh2_H80P*Ha#hyDKrn$?d5kfX zqH@HV^4s#=`VO^9O3ror`raUJ>S`t@;X%ursle4`mx;W(L!%)nSE9Dzf^K?Wle~`} zoBz_eL%5iDoWIq9;I;DE#(i~-wxo?e(M|d4DVmpc03++0=gop8sZd3n-cZwVlTCQA zZ>7e@0V+!e(B#fPiCRgrM@5iTHu-*fF-^%t1aAsHdcZ55|Cua`s6(c=;LM8m1v!Ki z9fv|(&Q3W0Gd${C>s*gBJ`m&^)Gx2?*)>=z^w@EyN-B##?`*zMybd;|>6z+J5% zxGL2*R2-=}lue-pFJUwY7*K-*0y%I|$p|hgg$c#n4t1zgCkTwq^VDd?KPBr>**16c zg%wts@y@#p4I*?t?^2`H?EAS0fs{!O@exjU=Cg+pd3etdEj4tn?!;Q!W!Sz7*ha=W z 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 A8vpIHj`RLI$YEaSLm)WwV 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 diff --git a/font.c b/font.c index 7152ea1..40a6edf 100644 --- a/font.c +++ b/font.c @@ -477,106 +477,6 @@ const uint8_t g_font_small[95][6] = {0x0C, 0x06, 0x0C, 0x18, 0x0C, 0x00} }; #endif -/* -const uint8_t g_font_small_4x5[95][4] = -{ - {0x00, 0x00, 0x00, 0x00}, // ' ' - {0x00, 0x00, 0x17, 0x00}, // '!' - {0x20, 0x00, 0x03, 0x00}, // '"' - {0x00, 0x00, 0x00, 0x0A}, // '#' - {0x0A, 0x1F, 0x28, 0x00}, // '$' - {0x15, 0x1F, 0x09, 0x70}, // '%' - {0x11, 0x08, 0x04, 0x13}, // '&' - {0x00, 0x1E, 0x15, 0x15}, // ''' - {0x78, 0x00, 0x00, 0x00}, // '(' - {0x07, 0x00, 0x00, 0x0E}, // ')' - {0x00, 0x00, 0x20, 0x00}, // '*' - {0x11, 0x0E, 0x00, 0x20}, // '+' - {0x12, 0x0C, 0x0C, 0x12}, // ',' - {0x00, 0x04, 0x0E, 0x04}, // '-' - {0x00, 0x00, 0x00, 0x10}, // '.' - {0x00, 0x20, 0x00, 0x04}, // '/' - {0x04, 0x00, 0x00, 0x00}, // '0' - {0x10, 0x00, 0x00, 0x20}, // '1' - {0x10, 0x08, 0x04, 0x03}, // '2' - {0x00, 0x0E, 0x11, 0x11}, // '3' - {0x30, 0x00, 0x00, 0x12}, // '4' - {0x10, 0x38, 0x00, 0x19}, // '5' - {0x15, 0x12, 0x78, 0x00}, // '6' - {0x15, 0x15, 0x1F, 0x78}, // '7' - {0x0C, 0x0A, 0x09, 0x1F}, // '8' - {0x00, 0x17, 0x15, 0x15}, // '9' - {0x78, 0x00, 0x1E, 0x15}, // ':' - {0x1D, 0x78, 0x00, 0x01}, // ';' - {0x05, 0x03, 0x20, 0x00}, // '<' - {0x15, 0x15, 0x0A, 0x30}, // '=' - {0x07, 0x05, 0x05, 0x1F}, // '>' - {0x00, 0x00, 0x16, 0x00}, // '?' - {0x20, 0x00, 0x00, 0x10}, // '@' - {0x00, 0x20, 0x00, 0x08}, // 'A' - {0x22, 0x01, 0x20, 0x10}, // 'B' - {0x0A, 0x0A, 0x00, 0x00}, // 'C' - {0x00, 0x01, 0x22, 0x14}, // 'D' - {0x10, 0x02, 0x11, 0x05}, // 'E' - {0x20, 0x00, 0x0E, 0x15}, // 'F' - {0x0E, 0x30, 0x00, 0x1E}, // 'G' - {0x05, 0x1E, 0x48, 0x00}, // 'H' - {0x15, 0x15, 0x0E, 0x70}, // 'I' - {0x1F, 0x11, 0x11, 0x11}, // 'J' - {0x00, 0x1F, 0x11, 0x11}, // 'K' - {0x70, 0x00, 0x1F, 0x15}, // 'L' - {0x11, 0x78, 0x00, 0x1F}, // 'M' - {0x05, 0x01, 0x40, 0x00}, // 'N' - {0x11, 0x15, 0x1D, 0x78}, // 'O' - {0x1F, 0x04, 0x04, 0x1F}, // 'P' - {0x00, 0x11, 0x1F, 0x11}, // 'Q' - {0x70, 0x00, 0x08, 0x11}, // 'R' - {0x1F, 0x38, 0x00, 0x1F}, // 'S' - {0x0A, 0x11, 0x48, 0x00}, // 'T' - {0x10, 0x10, 0x10, 0x78}, // 'U' - {0x1F, 0x02, 0x02, 0x1F}, // 'V' - {0x00, 0x1F, 0x02, 0x04}, // 'W' - {0x48, 0x00, 0x0E, 0x11}, // 'X' - {0x0E, 0x30, 0x00, 0x1F}, // 'Y' - {0x05, 0x02, 0x40, 0x00}, // 'Z' - {0x11, 0x19, 0x1E, 0x38}, // '[' - {0x1F, 0x05, 0x05, 0x1A}, // '\' - {0x00, 0x12, 0x15, 0x15}, // ']' - {0x70, 0x00, 0x01, 0x1F}, // '^' - {0x00, 0x20, 0x00, 0x1F}, // '_' - {0x10, 0x1F, 0x78, 0x00}, // '`' - {0x10, 0x10, 0x0F, 0x30}, // 'a' - {0x1F, 0x08, 0x08, 0x1F}, // 'b' - {0x00, 0x1B, 0x04, 0x04}, // 'c' - {0x48, 0x00, 0x07, 0x1C}, // 'd' - {0x00, 0x20, 0x00, 0x19}, // 'e' - {0x15, 0x13, 0x78, 0x00}, // 'f' - {0x11, 0x00, 0x00, 0x60}, // 'g' - {0x01, 0x02, 0x04, 0x08}, // 'h' - {0x00, 0x00, 0x11, 0x1F}, // 'i' - {0x30, 0x00, 0x02, 0x01}, // 'j' - {0x00, 0x00, 0x00, 0x10}, // 'k' - {0x10, 0x10, 0x78, 0x00}, // 'l' - {0x03, 0x07, 0x06, 0x00}, // 'm' - {0x1D, 0x15, 0x15, 0x1E}, // 'n' - {0x00, 0x1F, 0x14, 0x14}, // 'o' - {0x70, 0x00, 0x0C, 0x12}, // 'p' - {0x12, 0x38, 0x00, 0x08}, // 'q' - {0x14, 0x1F, 0x38, 0x00}, // 'r' - {0x15, 0x15, 0x16, 0x38}, // 's' - {0x1E, 0x05, 0x05, 0x00}, // 't' - {0x00, 0x02, 0x15, 0x15}, // 'u' - {0x38, 0x00, 0x1F, 0x04}, // 'v' - {0x18, 0x48, 0x00, 0x00}, // 'w' - {0x00, 0x00, 0x20, 0x00}, // 'x' - {0x10, 0x1D, 0x00, 0x30}, // 'y' - {0x1F, 0x04, 0x0A, 0x10}, // 'z' - {0x00, 0x00, 0x1F, 0x00}, // '{' - {0x20, 0x00, 0x1E, 0x04}, // '|' - {0x1E, 0x48, 0x00, 0x1E}, // '}' - {0x02, 0x1C, 0x48, 0x00}, // '~' -}; -*/ #ifdef ENABLE_SMALLEST_FONT const uint8_t g_font3x5[160][3] = diff --git a/frequencies.c b/frequencies.c index 8442c8f..0f1be4e 100644 --- a/frequencies.c +++ b/frequencies.c @@ -215,21 +215,22 @@ int FREQUENCY_tx_freq_check(const uint32_t Frequency) if (Frequency < FREQ_BAND_TABLE[0].lower || Frequency > FREQ_BAND_TABLE[ARRAY_SIZE(FREQ_BAND_TABLE) - 1].upper) return -1; // TX not allowed outside this range - switch (g_setting_freq_lock) + switch (g_eeprom.config.setting.freq_lock) { + default: case FREQ_LOCK_NORMAL: if (Frequency >= AIR_BAND.upper && Frequency < 17400000) return 0; if (Frequency >= 17400000 && Frequency < 35000000) - if (g_setting_174_tx_enable) + if (g_eeprom.config.setting.enable_tx_200) return 0; if (Frequency >= 35000000 && Frequency < 40000000) - if (g_setting_350_tx_enable && g_setting_350_enable) + if (g_eeprom.config.setting.enable_tx_350 && g_eeprom.config.setting.enable_350) return 0; if (Frequency >= 40000000 && Frequency < 47000000) return 0; if (Frequency >= 47000000 && Frequency <= 60000000) - if (g_setting_470_tx_enable) + if (g_eeprom.config.setting.enable_tx_470) return 0; break; diff --git a/functions.c b/functions.c index 10e7ffd..850d370 100644 --- a/functions.c +++ b/functions.c @@ -119,14 +119,14 @@ void FUNCTION_Select(function_type_t Function) #ifdef ENABLE_FMRADIO if (g_fm_radio_mode) - g_fm_restore_tick_10ms = g_eeprom.scan_hold_time_500ms * 50; + g_fm_restore_tick_10ms = g_eeprom.config.setting.scan_hold_time * 50; #endif if (g_dtmf_call_state == DTMF_CALL_STATE_CALL_OUT || g_dtmf_call_state == DTMF_CALL_STATE_RECEIVED || g_dtmf_call_state == DTMF_CALL_STATE_RECEIVED_STAY) { - g_dtmf_auto_reset_time_500ms = g_eeprom.dtmf_auto_reset_time * 2; + g_dtmf_auto_reset_time_500ms = g_eeprom.config.setting.dtmf.auto_reset_time * 2; } return; @@ -154,7 +154,7 @@ void FUNCTION_Select(function_type_t Function) GPIO_ClearBit(&GPIOC->DATA, GPIOC_PIN_SPEAKER); } - g_power_save_tick_10ms = g_eeprom.battery_save * 10; + g_power_save_tick_10ms = g_eeprom.config.setting.battery_save_ratio * 10; g_power_save_expired = false; g_rx_idle_mode = true; @@ -174,14 +174,14 @@ void FUNCTION_Select(function_type_t Function) UART_SendText("func transmit\r\n"); #endif - if (g_setting_backlight_on_tx_rx == 1 || g_setting_backlight_on_tx_rx == 3) + if (g_eeprom.config.setting.backlight_on_tx_rx == 1 || g_eeprom.config.setting.backlight_on_tx_rx == 3) backlight_turn_on(backlight_tx_rx_time_500ms); - if (g_eeprom.dual_watch != DUAL_WATCH_OFF) + if (g_eeprom.config.setting.dual_watch != DUAL_WATCH_OFF) { // dual-RX is enabled g_dual_watch_tick_10ms = dual_watch_delay_after_tx_10ms; - if (g_dual_watch_tick_10ms < (g_eeprom.scan_hold_time_500ms * 50)) - g_dual_watch_tick_10ms = g_eeprom.scan_hold_time_500ms * 50; + if (g_dual_watch_tick_10ms < (g_eeprom.config.setting.scan_hold_time * 50)) + g_dual_watch_tick_10ms = g_eeprom.config.setting.scan_hold_time * 50; } #ifdef ENABLE_MDC1200 @@ -210,7 +210,7 @@ void FUNCTION_Select(function_type_t Function) GUI_DisplayScreen(); #ifdef ENABLE_ALARM - if (g_alarm_state == ALARM_STATE_TXALARM && g_eeprom.alarm_mode != ALARM_MODE_TONE) + if (g_alarm_state == ALARM_STATE_TXALARM && g_eeprom.config.setting.alarm_mode != ALARM_MODE_TONE) { // enable the alarm tone but not the TX g_alarm_state = ALARM_STATE_ALARM; @@ -233,7 +233,7 @@ void FUNCTION_Select(function_type_t Function) } #endif - if (g_current_vfo->scrambling_type == 0 || !g_setting_scramble_enable) + if (g_current_vfo->scrambling_type == 0 || !g_eeprom.config.setting.enable_scrambler) BK4819_DisableScramble(); RADIO_enableTX(false); @@ -279,7 +279,7 @@ void FUNCTION_Select(function_type_t Function) (1u << 1) | // enable TX DSP (0u << 0)); // disable RX DSP SYSTEM_DelayMs(120); - BK4819_send_MDC1200(MDC1200_OP_CODE_PTT_ID, 0x80, g_eeprom.mdc1200_id); + BK4819_send_MDC1200(MDC1200_OP_CODE_PTT_ID, 0x80, g_eeprom.config.setting.mdc1200_id); } else #endif @@ -301,7 +301,7 @@ void FUNCTION_Select(function_type_t Function) (1u << 1) | // enable TX DSP (0u << 0)); // disable RX DSP */ - if (g_current_vfo->scrambling_type > 0 && g_setting_scramble_enable) + if (g_current_vfo->scrambling_type > 0 && g_eeprom.config.setting.enable_scrambler) { BK4819_EnableScramble(g_current_vfo->scrambling_type - 1); } diff --git a/helper/battery.c b/helper/battery.c index 069ab5a..41e687a 100644 --- a/helper/battery.c +++ b/helper/battery.c @@ -17,11 +17,11 @@ #include "battery.h" #include "driver/backlight.h" #include "misc.h" +#include "settings.h" #include "ui/battery.h" #include "ui/menu.h" #include "ui/ui.h" -uint16_t g_battery_calibration[6]; uint16_t g_usb_current_voltage; uint16_t g_usb_current; uint16_t g_battery_voltages[4]; @@ -86,25 +86,25 @@ void BATTERY_GetReadings(const bool bDisplayBatteryLevel) g_battery_display_level = 0; - if (g_battery_calibration[5] < Voltage) + if (g_eeprom.calib.battery[5] < Voltage) g_battery_display_level = 6; else - if (g_battery_calibration[4] < Voltage) + if (g_eeprom.calib.battery[4] < Voltage) g_battery_display_level = 5; else - if (g_battery_calibration[3] < Voltage) + if (g_eeprom.calib.battery[3] < Voltage) g_battery_display_level = 4; else - if (g_battery_calibration[2] < Voltage) + if (g_eeprom.calib.battery[2] < Voltage) g_battery_display_level = 3; else - if (g_battery_calibration[1] < Voltage) + if (g_eeprom.calib.battery[1] < Voltage) g_battery_display_level = 2; else - if (g_battery_calibration[0] < Voltage) + if (g_eeprom.calib.battery[0] < Voltage) g_battery_display_level = 1; - g_battery_voltage_average = (Voltage * 760) / g_battery_calibration[3]; + g_battery_voltage_average = (Voltage * 760) / g_eeprom.calib.battery[3]; if ((g_current_display_screen == DISPLAY_MENU) && g_menu_cursor == MENU_VOLTAGE) g_update_display = true; diff --git a/helper/battery.h b/helper/battery.h index 7adf07a..14cf96b 100644 --- a/helper/battery.h +++ b/helper/battery.h @@ -20,7 +20,6 @@ #include #include -extern uint16_t g_battery_calibration[6]; extern uint16_t g_usb_current_voltage; extern uint16_t g_usb_current; extern uint16_t g_battery_voltages[4]; diff --git a/helper/boot.c b/helper/boot.c index 42696fc..112d87a 100644 --- a/helper/boot.c +++ b/helper/boot.c @@ -66,17 +66,17 @@ void BOOT_ProcessMode(boot_mode_t Mode) else if (Mode == BOOT_MODE_AIRCOPY) { - g_eeprom.dual_watch = DUAL_WATCH_OFF; - g_eeprom.battery_save = 0; + g_eeprom.config.setting.dual_watch = DUAL_WATCH_OFF; + g_eeprom.config.setting.battery_save_ratio = 0; #ifdef ENABLE_VOX - g_eeprom.vox_switch = false; + g_eeprom.config.setting.vox_switch = false; #endif - g_eeprom.cross_vfo_rx_tx = CROSS_BAND_OFF; - g_eeprom.auto_keypad_lock = false; - g_eeprom.key1_short_press_action = ACTION_OPT_NONE; - g_eeprom.key1_long_press_action = ACTION_OPT_NONE; - g_eeprom.key2_short_press_action = ACTION_OPT_NONE; - g_eeprom.key2_long_press_action = ACTION_OPT_NONE; + g_eeprom.config.setting.cross_vfo = CROSS_BAND_OFF; + g_eeprom.config.setting.auto_key_lock = 0; + g_eeprom.config.setting.key1_short = ACTION_OPT_NONE; + g_eeprom.config.setting.key1_long = ACTION_OPT_NONE; + g_eeprom.config.setting.key2_short = ACTION_OPT_NONE; + g_eeprom.config.setting.key2_long = ACTION_OPT_NONE; RADIO_InitInfo(g_rx_vfo, FREQ_CHANNEL_LAST - 1, g_aircopy_freq); diff --git a/main.c b/main.c index 4923baf..213f280 100644 --- a/main.c +++ b/main.c @@ -28,6 +28,8 @@ #include "driver/bk1080.h" #endif #include "driver/bk4819.h" +#include "driver/crc.h" +#include "driver/eeprom.h" #include "driver/gpio.h" #include "driver/st7565.h" #include "driver/system.h" @@ -35,6 +37,7 @@ #if defined(ENABLE_UART) #include "driver/uart.h" #endif +#include "external/printf/printf.h" #include "helper/battery.h" #include "helper/boot.h" #ifdef ENABLE_MDC1200 @@ -43,11 +46,107 @@ #include "misc.h" #include "radio.h" #include "settings.h" +#include "ui/helper.h" #include "ui/lock.h" -#include "ui/welcome.h" #include "ui/menu.h" +#include "ui/status.h" #include "version.h" +void MAIN_DisplayClear(void) +{ + memset(g_status_line, 0, sizeof(g_status_line)); + memset(g_frame_buffer, 0, sizeof(g_frame_buffer)); + + ST7565_BlitStatusLine(); + ST7565_BlitFullScreen(); +} + +void MAIN_DisplayReadingEEPROM(void) +{ + memset(g_status_line, 0, sizeof(g_status_line)); + memset(g_frame_buffer, 0, sizeof(g_frame_buffer)); + + UI_PrintString("READING", 0, LCD_WIDTH, 1, 10); + UI_PrintString("EEPROM", 0, LCD_WIDTH, 3, 10); + + ST7565_BlitStatusLine(); + ST7565_BlitFullScreen(); +} + +void MAIN_DisplayReleaseKeys(void) +{ + memset(g_status_line, 0, sizeof(g_status_line)); + memset(g_frame_buffer, 0, sizeof(g_frame_buffer)); + + UI_PrintString("RELEASE", 0, LCD_WIDTH, 1, 10); + UI_PrintString("ALL KEYS", 0, LCD_WIDTH, 3, 10); + + ST7565_BlitStatusLine(); // blank status line + ST7565_BlitFullScreen(); +} + +void MAIN_DisplayWelcome(void) +{ + char str0[17]; + char str1[17]; + char str2[17]; + + memset(g_status_line, 0, sizeof(g_status_line)); + memset(g_frame_buffer, 0, sizeof(g_frame_buffer)); + + if (g_eeprom.config.setting.power_on_display_mode == PWR_ON_DISPLAY_MODE_NONE) + { + ST7565_FillScreen(0xFF); + } + else + if (g_eeprom.config.setting.power_on_display_mode == PWR_ON_DISPLAY_MODE_FULL_SCREEN) + { + ST7565_FillScreen(0xFF); + } + else + { + unsigned int slen = strlen(Version_str); + if (slen > (sizeof(str2) - 1)) + slen = sizeof(str2) - 1; + + memset(str0, 0, sizeof(str0)); + memset(str1, 0, sizeof(str1)); + memset(str2, 0, sizeof(str2)); + + if (g_eeprom.config.setting.power_on_display_mode == PWR_ON_DISPLAY_MODE_VOLTAGE) + { + strcpy(str0, "VOLTAGE"); + sprintf(str1, "%u.%02uV %u%%", + g_battery_voltage_average / 100, + g_battery_voltage_average % 100, + BATTERY_VoltsToPercent(g_battery_voltage_average)); + } + else + { + //EEPROM_ReadBuffer(0x0EB0, str0, 16); + //sEEPROM_ReadBuffer(0x0EC0, str1, 16); + memcpy(str0, g_eeprom.config.setting.welcome_line[0], 16); + memcpy(str1, g_eeprom.config.setting.welcome_line[1], 16); + } + + memcpy(str2, Version_str, slen); + + UI_PrintString(str0, 0, LCD_WIDTH, 0, 10); + UI_PrintString(str1, 0, LCD_WIDTH, 2, 10); + UI_PrintStringSmall(str2, 0, LCD_WIDTH, 4); + UI_PrintStringSmall(__DATE__, 0, LCD_WIDTH, 5); + UI_PrintStringSmall(__TIME__, 0, LCD_WIDTH, 6); + + #if 1 + ST7565_BlitStatusLine(); // blank status line + #else + UI_DisplayStatus(true); // show all status line symbols (test) + #endif + + ST7565_BlitFullScreen(); + } +} + void Main(void) { unsigned int i; @@ -64,38 +163,48 @@ void Main(void) | SYSCON_DEV_CLK_GATE_CRC_BITS_ENABLE | SYSCON_DEV_CLK_GATE_AES_BITS_ENABLE; - g_monitor_enabled = false; - GPIO_ClearBit(&GPIOC->DATA, GPIOC_PIN_SPEAKER); - SYSTICK_Init(); -#ifdef ENABLE_UART - UART_Init(); -#endif + BOARD_PORTCON_Init(); + BOARD_GPIO_Init(); + CRC_Init(); + #ifdef ENABLE_UART + UART_Init(); + #endif + BOARD_ADC_Init(); + ST7565_Init(true); + #ifdef ENABLE_FMRADIO + BK1080_Init(0, false); + #endif + + // *************************** + + GPIO_ClearBit(&GPIOC->DATA, GPIOC_PIN_SPEAKER); #if defined(ENABLE_UART) UART_SendText(UART_Version_str); UART_SendText("\r\n"); #endif + BootMode = BOOT_GetMode(); + g_unhide_hidden = (BootMode == BOOT_MODE_UNHIDE_HIDDEN); // flag to say include the hidden menu items + // load the entire EEPROM contents into memory + GPIO_SetBit(&GPIOB->DATA, GPIOB_PIN_BACKLIGHT); // backlight on + MAIN_DisplayReadingEEPROM(); SETTINGS_read_eeprom(); - - BOARD_Init(); - - memset(&g_eeprom, 0, sizeof(g_eeprom)); - - memset(g_dtmf_string, '-', sizeof(g_dtmf_string)); - g_dtmf_string[sizeof(g_dtmf_string) - 1] = 0; + MAIN_DisplayClear(); FREQUENCY_init(); -#if 0 - SETTINGS_restore_calibration(); -#endif - BK4819_Init(); + BOARD_ADC_GetBatteryInfo(&g_usb_current_voltage, &g_usb_current); + + #ifdef ENABLE_CONTRAST + ST7565_SetContrast(g_setting_contrast); + #endif + #if defined(ENABLE_UART) UART_printf("BK4819 id %04X rev %04X\r\n", BK4819_ReadRegister(0x00), BK4819_ReadRegister(0x01)); #ifdef ENABLE_FMRADIO @@ -103,17 +212,18 @@ void Main(void) #endif #endif -#ifdef ENABLE_MDC1200 - MDC1200_init(); -#endif + memset(g_dtmf_string, '-', sizeof(g_dtmf_string)); + g_dtmf_string[sizeof(g_dtmf_string) - 1] = 0; - BOARD_ADC_GetBatteryInfo(&g_usb_current_voltage, &g_usb_current); + #ifdef ENABLE_MDC1200 + MDC1200_init(); + #endif - BOARD_eeprom_load(); + #ifdef ENABLE_AM_FIX + AM_fix_init(); + #endif - BOARD_eeprom_loadCalibration(); - - BK4819_set_mic_gain(g_eeprom.mic_sensitivity_tuning); + BK4819_set_mic_gain(g_mic_sensitivity_tuning); RADIO_configure_channel(0, VFO_CONFIGURE_RELOAD); RADIO_configure_channel(1, VFO_CONFIGURE_RELOAD); @@ -127,18 +237,6 @@ void Main(void) BATTERY_GetReadings(false); - #ifdef ENABLE_CONTRAST - ST7565_SetContrast(g_setting_contrast); - #endif - - #ifdef ENABLE_AM_FIX - AM_fix_init(); - #endif - - BootMode = BOOT_GetMode(); - - g_unhide_hidden = (BootMode == BOOT_MODE_UNHIDE_HIDDEN); // flag to say include the hidden menu items - #if defined(ENABLE_UART) && defined(ENABLE_UART_DEBUG) if (g_unhide_hidden) UART_SendText("boot_unhide_hidden\r\n"); @@ -147,13 +245,13 @@ void Main(void) // sort the menu list UI_SortMenu(!g_unhide_hidden); - // wait for user to release all butts before moving on + // wait for user to release all buttons before moving on if (!GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT) || KEYBOARD_Poll() != KEY_INVALID || BootMode != BOOT_MODE_NORMAL) { - backlight_turn_on(0); - UI_DisplayReleaseKeys(); + GPIO_SetBit(&GPIOB->DATA, GPIOB_PIN_BACKLIGHT); // backlight on + MAIN_DisplayReleaseKeys(); i = 0; while (i < (500 / 10)) // 500ms { @@ -166,7 +264,7 @@ void Main(void) { FUNCTION_Select(FUNCTION_POWER_SAVE); - if (g_eeprom.backlight < (ARRAY_SIZE(g_sub_menu_backlight) - 1)) + if (g_eeprom.config.setting.backlight_time < (ARRAY_SIZE(g_sub_menu_backlight) - 1)) GPIO_ClearBit(&GPIOB->DATA, GPIOB_PIN_BACKLIGHT); // turn the backlight OFF else GPIO_SetBit(&GPIOB->DATA, GPIOB_PIN_BACKLIGHT); // turn the backlight ON @@ -175,7 +273,7 @@ void Main(void) } else { - UI_DisplayWelcome(); + MAIN_DisplayWelcome(); backlight_turn_on(0); @@ -184,7 +282,7 @@ void Main(void) // AUDIO_PlaySingleVoice(0); #endif - if (g_eeprom.pwr_on_display_mode != PWR_ON_DISPLAY_MODE_NONE) + if (g_eeprom.config.setting.power_on_display_mode != PWR_ON_DISPLAY_MODE_NONE) { // 3 second boot-up screen while (g_boot_tick_10ms > 0) { @@ -201,7 +299,7 @@ void Main(void) } #ifdef ENABLE_PWRON_PASSWORD - if (g_eeprom.power_on_password < 1000000) + if (g_eeprom.config.setting.power_on_password < 1000000) { g_password_locked = true; UI_DisplayLock(); @@ -216,9 +314,9 @@ void Main(void) g_update_status = true; #ifdef ENABLE_VOICE - if (g_eeprom.voice_prompt != VOICE_PROMPT_OFF) + if (g_eeprom.config.setting.voice_prompt != VOICE_PROMPT_OFF) { - const uint8_t Channel = g_eeprom.screen_channel[g_eeprom.tx_vfo]; + const uint8_t Channel = g_eeprom.config.setting.indices.vfo[g_eeprom.config.setting.tx_vfo_num].screen; AUDIO_SetVoiceID(0, VOICE_ID_WELCOME); AUDIO_PlaySingleVoice(0); diff --git a/misc.c b/misc.c index 56c9071..1bfe890 100644 --- a/misc.c +++ b/misc.c @@ -81,38 +81,9 @@ const uint16_t noaa_tick_3_10ms = 200 / 10; // 200ms // *********************************************** -const uint32_t g_default_aes_key[4] = {0x4AA5CC60, 0x0312CC5F, 0xFFD2DABB, 0x6BBA7F92}; +const uint32_t g_default_aes_key[4] = {0x4AA5CC60, 0x0312CC5F, 0xFFD2DABB, 0x6BBA7F92}; -const uint8_t g_mic_gain_dB_2[5] = {3, 8, 16, 24, 31}; - -#ifdef ENABLE_KILL_REVIVE - bool g_setting_radio_disabled; -#endif - -bool g_setting_350_tx_enable; -bool g_setting_174_tx_enable; -bool g_setting_470_tx_enable; -bool g_setting_350_enable; -bool g_setting_tx_enable; -uint8_t g_setting_freq_lock; -bool g_setting_scramble_enable; - -uint8_t g_setting_backlight_on_tx_rx; - -#ifdef ENABLE_AM_FIX - bool g_setting_am_fix = true; -#endif -#ifdef ENABLE_AM_FIX_TEST1 - uint8_t g_setting_am_fix_test1 = 0; -#endif -#ifdef ENABLE_TX_AUDIO_BAR - bool g_setting_mic_bar; -#endif -#ifdef ENABLE_RX_SIGNAL_BAR - bool g_setting_rssi_bar; -#endif -bool g_setting_live_dtmf_decoder; -uint8_t g_setting_battery_text; +const uint8_t g_mic_gain_dB_2[5] = {3, 8, 16, 24, 31}; #ifdef ENABLE_CONTRAST uint8_t g_setting_contrast; @@ -125,17 +96,13 @@ uint8_t g_setting_side2_long; bool g_monitor_enabled; -uint32_t g_custom_aes_key[4]; -bool g_has_custom_aes_key; +bool g_has_aes_key; uint32_t g_challenge[4]; +uint16_t g_vox_threshold[2]; + uint16_t g_eeprom_rssi_calib[7][4]; -//uint16_t g_eeprom_1F8A; -//uint16_t g_eeprom_1F8C; - -uint8_t g_user_channel_attributes[FREQ_CHANNEL_LAST + 1]; - volatile uint16_t g_schedule_power_save_tick_10ms = battery_save_count_10ms; volatile bool g_schedule_power_save; @@ -234,16 +201,19 @@ bool g_scan_pause_time_mode; // set if we stopped in SCAN_ volatile uint16_t g_scan_pause_tick_10ms; scan_state_dir_t g_scan_state_dir; +uint8_t g_rx_vfo_num; bool g_rx_vfo_is_active; + #ifdef ENABLE_ALARM uint16_t g_alarm_tone_counter_10ms; uint16_t g_alarm_running_counter_10ms; #endif uint8_t g_menu_list_count; -uint8_t g_backup_cross_vfo_rx_tx; + +uint8_t g_backup_cross_vfo; #ifdef ENABLE_NOAA - bool g_is_noaa_mode; + bool g_noaa_mode; uint8_t g_noaa_channel; #endif @@ -268,36 +238,38 @@ volatile uint16_t g_boot_tick_10ms = 4000 / 10; // 4 seconds int16_t g_current_rssi[2] = {0, 0}; // now one per VFO +uint8_t g_mic_sensitivity_tuning; + unsigned int get_RX_VFO(void) { - unsigned int rx_vfo = g_eeprom.tx_vfo; - if (g_eeprom.cross_vfo_rx_tx == CROSS_BAND_CHAN_B) + unsigned int rx_vfo = g_eeprom.config.setting.tx_vfo_num; + if (g_eeprom.config.setting.cross_vfo == CROSS_BAND_CHAN_B) rx_vfo = 0; else - if (g_eeprom.cross_vfo_rx_tx == CROSS_BAND_CHAN_A) + if (g_eeprom.config.setting.cross_vfo == CROSS_BAND_CHAN_A) rx_vfo = 1; else - if (g_eeprom.dual_watch == DUAL_WATCH_CHAN_B) + if (g_eeprom.config.setting.dual_watch == DUAL_WATCH_CHAN_B) rx_vfo = 1; else - if (g_eeprom.dual_watch == DUAL_WATCH_CHAN_A) + if (g_eeprom.config.setting.dual_watch == DUAL_WATCH_CHAN_A) rx_vfo = 0; return rx_vfo; } unsigned int get_TX_VFO(void) { - unsigned int tx_vfo = g_eeprom.tx_vfo; - if (g_eeprom.cross_vfo_rx_tx == CROSS_BAND_CHAN_B) + unsigned int tx_vfo = g_eeprom.config.setting.tx_vfo_num; + if (g_eeprom.config.setting.cross_vfo == CROSS_BAND_CHAN_B) tx_vfo = 1; else - if (g_eeprom.cross_vfo_rx_tx == CROSS_BAND_CHAN_A) + if (g_eeprom.config.setting.cross_vfo == CROSS_BAND_CHAN_A) tx_vfo = 0; else - if (g_eeprom.dual_watch == DUAL_WATCH_CHAN_B) + if (g_eeprom.config.setting.dual_watch == DUAL_WATCH_CHAN_B) tx_vfo = 1; else - if (g_eeprom.dual_watch == DUAL_WATCH_CHAN_A) + if (g_eeprom.config.setting.dual_watch == DUAL_WATCH_CHAN_A) tx_vfo = 0; return tx_vfo; } diff --git a/misc.h b/misc.h index 4e918b9..c999985 100644 --- a/misc.h +++ b/misc.h @@ -22,6 +22,8 @@ #include #include +//#include "settings.h" + #ifndef ARRAY_SIZE #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) #endif @@ -43,6 +45,8 @@ //#define IS_NOT_NOAA_CHANNEL(x) ((x) >= USER_CHANNEL_FIRST && (x) <= FREQ_CHANNEL_LAST) #define IS_NOT_NOAA_CHANNEL(x) ((x) <= FREQ_CHANNEL_LAST) +#define CHANNEL_NUM(chaanel, vfo) IS_FREQ_CHANNEL(channel) ? (FREQ_CHANNEL_FIRST + ((channel) - FREQ_CHANNEL_FIRST) * 2) + (vfo) : (channel); + // PTT key-up/key-down audio tone freq's used in NASA's apollo rides to the moon #define APOLLO_TONE_MS 200 // slightly shorter tone length //#define APOLLO_TONE_MS 250 // NASA tone length @@ -173,35 +177,6 @@ extern const uint16_t scan_pause_chan_10ms; extern const uint8_t g_mic_gain_dB_2[5]; -#ifdef ENABLE_KILL_REVIVE - extern bool g_setting_radio_disabled; -#endif - -extern bool g_setting_350_tx_enable; -extern bool g_setting_174_tx_enable; -extern bool g_setting_470_tx_enable; -extern bool g_setting_350_enable; -extern bool g_setting_tx_enable; -extern uint8_t g_setting_freq_lock; -extern bool g_setting_scramble_enable; - -extern uint8_t g_setting_backlight_on_tx_rx; - -#ifdef ENABLE_AM_FIX - extern bool g_setting_am_fix; -#endif -#ifdef ENABLE_AM_FIX_TEST1 - extern uint8_t g_setting_am_fix_test1; -#endif -#ifdef ENABLE_TX_AUDIO_BAR - extern bool g_setting_mic_bar; -#endif -#ifdef ENABLE_RX_SIGNAL_BAR - extern bool g_setting_rssi_bar; -#endif -extern bool g_setting_live_dtmf_decoder; -extern uint8_t g_setting_battery_text; - #ifdef ENABLE_CONTRAST extern uint8_t g_setting_contrast; #endif @@ -214,17 +189,10 @@ extern uint8_t g_setting_side2_long; extern bool g_monitor_enabled; extern const uint32_t g_default_aes_key[4]; -extern uint32_t g_custom_aes_key[4]; -extern bool g_has_custom_aes_key; +extern bool g_has_aes_key; extern uint32_t g_challenge[4]; extern uint16_t g_eeprom_rssi_calib[7][4]; -//extern uint16_t g_eeprom_rssi_calib[2][4]; - -//extern uint16_t g_eeprom_1F8A; -//extern uint16_t g_eeprom_1F8C; - -extern uint8_t g_user_channel_attributes[207]; extern volatile uint16_t g_schedule_power_save_tick_10ms; extern volatile bool g_schedule_power_save; @@ -325,14 +293,17 @@ extern bool g_scan_pause_time_mode; // set if we stopped in S extern volatile uint16_t g_scan_pause_tick_10ms; // ticks till we move to next channel/frequency extern scan_state_dir_t g_scan_state_dir; // the direction we're scanning in - +extern uint8_t g_rx_vfo_num; extern bool g_rx_vfo_is_active; + +extern uint16_t g_vox_threshold[2]; + extern uint16_t g_alarm_tone_counter_10ms; extern uint16_t g_alarm_running_counter_10ms; extern uint8_t g_menu_list_count; -extern uint8_t g_backup_cross_vfo_rx_tx; +extern uint8_t g_backup_cross_vfo; #ifdef ENABLE_NOAA - extern bool g_is_noaa_mode; + extern bool g_noaa_mode; extern uint8_t g_noaa_channel; #endif extern volatile bool g_next_time_slice; @@ -355,6 +326,8 @@ extern volatile bool g_flag_tail_tone_elimination_complete; extern int16_t g_current_rssi[2]; // now one per VFO extern volatile uint16_t g_boot_tick_10ms; +extern uint8_t g_mic_sensitivity_tuning; + unsigned int get_TX_VFO(void); unsigned int get_RX_VFO(void); void NUMBER_Get(char *pDigits, uint32_t *pInteger); diff --git a/radio.c b/radio.c index 307f90d..0cf9d29 100644 --- a/radio.c +++ b/radio.c @@ -42,6 +42,7 @@ #include "ui/menu.h" #include "ui/ui.h" +vfo_info_t g_vfo_info[2]; vfo_info_t *g_tx_vfo; vfo_info_t *g_rx_vfo; vfo_info_t *g_current_vfo; @@ -53,16 +54,13 @@ vfo_state_t g_vfo_state[2]; bool RADIO_CheckValidChannel(uint16_t Channel, bool bCheckScanList, uint8_t VFO) { // return true if the channel appears valid - uint8_t Attributes; - uint8_t PriorityCh1; - uint8_t PriorityCh2; + unsigned int i; + uint8_t priority_channel[2]; if (Channel > USER_CHANNEL_LAST) return false; - Attributes = g_user_channel_attributes[Channel]; - - if ((Attributes & USER_CH_BAND_MASK) > BAND7_470MHz) + if (g_user_channel_attributes[Channel].band > BAND7_470MHz) return false; if (bCheckScanList) @@ -70,29 +68,29 @@ bool RADIO_CheckValidChannel(uint16_t Channel, bool bCheckScanList, uint8_t VFO) switch (VFO) { case 0: - if ((Attributes & USER_CH_SCANLIST1) == 0) + if (g_user_channel_attributes[Channel].scanlist1 == 0) return false; - PriorityCh1 = g_eeprom.scan_list_priority_ch1[0]; - PriorityCh2 = g_eeprom.scan_list_priority_ch2[0]; + for (i = 0; i < 2; i++) + priority_channel[i] = g_eeprom.config.setting.priority_scan_list[VFO].channel[i]; break; case 1: - if ((Attributes & USER_CH_SCANLIST2) == 0) + if (g_user_channel_attributes[Channel].scanlist2 == 0) return false; - PriorityCh1 = g_eeprom.scan_list_priority_ch1[1]; - PriorityCh2 = g_eeprom.scan_list_priority_ch2[1]; + for (i = 0; i < 2; i++) + priority_channel[i] = g_eeprom.config.setting.priority_scan_list[VFO].channel[i]; break; default: return true; } - if (PriorityCh1 == Channel) + if (priority_channel[0] == Channel) return false; - if (PriorityCh2 == Channel) + if (priority_channel[1] == Channel) return false; } @@ -151,113 +149,116 @@ void RADIO_InitInfo(vfo_info_t *p_vfo, const uint8_t ChannelSave, const uint32_t void RADIO_configure_channel(const unsigned int VFO, const unsigned int configure) { - uint8_t Channel; - uint8_t Attributes; - uint8_t Band; - uint16_t Base; - uint32_t Frequency; - vfo_info_t *p_vfo = &g_eeprom.vfo_info[VFO]; + unsigned int channel; + t_channel_attrib attributes; +// uint16_t base; + uint32_t frequency; + vfo_info_t *p_vfo = &g_vfo_info[VFO]; - if (!g_setting_350_enable) + if (!g_eeprom.config.setting.enable_350) { - if (g_eeprom.freq_channel[VFO] == (FREQ_CHANNEL_LAST - 2)) - g_eeprom.freq_channel[VFO] = FREQ_CHANNEL_LAST - 1; + if (g_eeprom.config.setting.indices.vfo[VFO].frequency == (FREQ_CHANNEL_LAST - 2)) + g_eeprom.config.setting.indices.vfo[VFO].frequency = FREQ_CHANNEL_LAST - 1; - if (g_eeprom.screen_channel[VFO] == (FREQ_CHANNEL_LAST - 2)) - g_eeprom.screen_channel[VFO] = FREQ_CHANNEL_LAST - 1; + if (g_eeprom.config.setting.indices.vfo[VFO].screen == (FREQ_CHANNEL_LAST - 2)) + g_eeprom.config.setting.indices.vfo[VFO].screen = FREQ_CHANNEL_LAST - 1; } - Channel = g_eeprom.screen_channel[VFO]; + channel = g_eeprom.config.setting.indices.vfo[VFO].screen; p_vfo->freq_in_channel = 0xff; - if (IS_VALID_CHANNEL(Channel)) + if (IS_VALID_CHANNEL(channel)) { #ifdef ENABLE_NOAA - if (Channel >= NOAA_CHANNEL_FIRST) + if (channel >= NOAA_CHANNEL_FIRST) { - RADIO_InitInfo(p_vfo, g_eeprom.screen_channel[VFO], NOAA_FREQUENCY_TABLE[Channel - NOAA_CHANNEL_FIRST]); - if (g_eeprom.cross_vfo_rx_tx == CROSS_BAND_OFF) + RADIO_InitInfo(p_vfo, g_eeprom.config.setting.indices.vfo[VFO].screen, NOAA_FREQUENCY_TABLE[channel - NOAA_CHANNEL_FIRST]); + if (g_eeprom.config.setting.cross_vfo == CROSS_BAND_OFF) return; - g_eeprom.cross_vfo_rx_tx = CROSS_BAND_OFF; + g_eeprom.config.setting.cross_vfo = CROSS_BAND_OFF; g_update_status = true; return; } #endif - if (Channel <= USER_CHANNEL_LAST) + if (channel <= USER_CHANNEL_LAST) { - Channel = RADIO_FindNextChannel(Channel, SCAN_STATE_DIR_FORWARD, false, VFO); - if (Channel == 0xFF) + channel = RADIO_FindNextChannel(channel, SCAN_STATE_DIR_FORWARD, false, VFO); + if (channel == 0xFF) { - Channel = g_eeprom.freq_channel[VFO]; - g_eeprom.screen_channel[VFO] = g_eeprom.freq_channel[VFO]; + channel = g_eeprom.config.setting.indices.vfo[VFO].frequency; + g_eeprom.config.setting.indices.vfo[VFO].screen = channel; } else { - g_eeprom.screen_channel[VFO] = Channel; - g_eeprom.user_channel[VFO] = Channel; + g_eeprom.config.setting.indices.vfo[VFO].screen = channel; + g_eeprom.config.setting.indices.vfo[VFO].user = channel; } } } else - Channel = FREQ_CHANNEL_LAST - 1; + { + channel = FREQ_CHANNEL_LAST - 1; + } - Attributes = g_user_channel_attributes[Channel]; - if (Attributes == 0xFF) + attributes = g_user_channel_attributes[channel]; + + if (attributes.band > BAND7_470MHz) { // invalid/unused channel - uint8_t Index; + unsigned int index; - if (Channel <= USER_CHANNEL_LAST) + if (channel <= USER_CHANNEL_LAST) { - Channel = g_eeprom.freq_channel[VFO]; - g_eeprom.screen_channel[VFO] = g_eeprom.freq_channel[VFO]; + channel = g_eeprom.config.setting.indices.vfo[VFO].frequency; + g_eeprom.config.setting.indices.vfo[VFO].screen = channel; } - Index = Channel - FREQ_CHANNEL_FIRST; + index = channel - FREQ_CHANNEL_FIRST; - RADIO_InitInfo(p_vfo, Channel, FREQ_BAND_TABLE[Index].lower); + RADIO_InitInfo(p_vfo, channel, FREQ_BAND_TABLE[index].lower); return; } - Band = Attributes & USER_CH_BAND_MASK; - if (Band > BAND7_470MHz) - Band = BAND6_400MHz; + if (attributes.band > BAND7_470MHz) + attributes.band = BAND6_400MHz; - if (Channel <= USER_CHANNEL_LAST) + if (channel <= USER_CHANNEL_LAST) { // USER channel - p_vfo->band = Band; - p_vfo->scanlist_2_participation = (Attributes & USER_CH_SCANLIST2) ? 1 : 0; - p_vfo->scanlist_1_participation = (Attributes & USER_CH_SCANLIST1) ? 1 : 0; + p_vfo->band = attributes.band; + p_vfo->scanlist_2_participation = attributes.scanlist2; + p_vfo->scanlist_1_participation = attributes.scanlist1; } else - if (IS_FREQ_CHANNEL(Channel)) + if (IS_FREQ_CHANNEL(channel)) { // VFO channel - Band = Channel - FREQ_CHANNEL_FIRST; - g_eeprom.vfo_info[VFO].band = Band; // shouldn't this be "Band / 2" ? .. two VFO's per band + attributes.band = channel - FREQ_CHANNEL_FIRST; // shouldn't this be "Band / 2" ? .. two VFO's per band TODO: + g_vfo_info[VFO].band = attributes.band; #if 0 p_vfo->scanlist_2_participation = 1; p_vfo->scanlist_1_participation = 1; #else // allowing the vfo's to be included in the scanning - p_vfo->scanlist_2_participation = (Attributes & USER_CH_SCANLIST2) ? 1 : 0; - p_vfo->scanlist_1_participation = (Attributes & USER_CH_SCANLIST1) ? 1 : 0; + p_vfo->scanlist_2_participation = attributes.scanlist2; + p_vfo->scanlist_1_participation = attributes.scanlist1; #endif } - p_vfo->channel_save = Channel; + p_vfo->channel_save = channel; - if (Channel <= USER_CHANNEL_LAST) - Base = Channel * 16; - else - Base = 0x0C80 + ((Channel - FREQ_CHANNEL_FIRST) * 16 * 2) + (VFO * 16); // VFO channel +// if (channel <= USER_CHANNEL_LAST) +// base = channel * 16; +// else +// base = 0x0C80 + ((channel - FREQ_CHANNEL_FIRST) * 16 * 2) + (VFO * 16); // VFO channel - if (configure == VFO_CONFIGURE_RELOAD || IS_FREQ_CHANNEL(Channel)) + if (configure == VFO_CONFIGURE_RELOAD || IS_FREQ_CHANNEL(channel)) { + const unsigned int chan = CHANNEL_NUM(channel, VFO); t_channel m_channel; - EEPROM_ReadBuffer(Base, &m_channel, sizeof(m_channel)); +// EEPROM_ReadBuffer(Base, &m_channel, sizeof(t_channel)); + memcpy(&m_channel, &g_eeprom.config.channel[chan], sizeof(t_channel)); p_vfo->freq_config_rx.frequency = m_channel.frequency; @@ -321,26 +322,26 @@ void RADIO_configure_channel(const unsigned int VFO, const unsigned int configur p_vfo->squelch_level = (m_channel.squelch_level < 10) ? m_channel.squelch_level : 0; } - Frequency = p_vfo->freq_config_rx.frequency; + frequency = p_vfo->freq_config_rx.frequency; - if (Frequency < FREQ_BAND_TABLE[Band].lower) - Frequency = FREQ_BAND_TABLE[Band].lower; + if (frequency < FREQ_BAND_TABLE[attributes.band].lower) + frequency = FREQ_BAND_TABLE[attributes.band].lower; else - if (Frequency >= FREQ_BAND_TABLE[Band].upper) - Frequency = FREQUENCY_floor_to_step(Frequency, p_vfo->step_freq, FREQ_BAND_TABLE[Band].lower, FREQ_BAND_TABLE[Band].upper); + if (frequency >= FREQ_BAND_TABLE[attributes.band].upper) + frequency = FREQUENCY_floor_to_step(frequency, p_vfo->step_freq, FREQ_BAND_TABLE[attributes.band].lower, FREQ_BAND_TABLE[attributes.band].upper); else - if (Channel >= FREQ_CHANNEL_FIRST) - Frequency = FREQUENCY_floor_to_step(Frequency, p_vfo->step_freq, FREQ_BAND_TABLE[Band].lower, FREQ_BAND_TABLE[Band].upper); + if (channel >= FREQ_CHANNEL_FIRST) + frequency = FREQUENCY_floor_to_step(frequency, p_vfo->step_freq, FREQ_BAND_TABLE[attributes.band].lower, FREQ_BAND_TABLE[attributes.band].upper); - if (!g_setting_350_enable && Frequency >= 35000000 && Frequency < 40000000) + if (!g_eeprom.config.setting.enable_350 && frequency >= 35000000 && frequency < 40000000) { // 350~400Mhz not allowed // hop onto the next band up - Frequency = 43350000; - p_vfo->freq_config_rx.frequency = Frequency; - p_vfo->freq_config_tx.frequency = Frequency; - Band = FREQUENCY_GetBand(Frequency); - p_vfo->band = Band; + frequency = 43350000; + p_vfo->freq_config_rx.frequency = frequency; + p_vfo->freq_config_tx.frequency = frequency; + attributes.band = FREQUENCY_GetBand(frequency); + p_vfo->band = attributes.band; p_vfo->frequency_reverse = 0; p_vfo->tx_offset_freq_dir = TX_OFFSET_FREQ_DIR_OFF; p_vfo->tx_offset_freq = 0; @@ -351,15 +352,15 @@ void RADIO_configure_channel(const unsigned int VFO, const unsigned int configur } - p_vfo->freq_config_rx.frequency = Frequency; + p_vfo->freq_config_rx.frequency = frequency; - if (Frequency >= AIR_BAND.lower && Frequency < AIR_BAND.upper) + if (frequency >= AIR_BAND.lower && frequency < AIR_BAND.upper) { // air band p_vfo->tx_offset_freq_dir = TX_OFFSET_FREQ_DIR_OFF; p_vfo->tx_offset_freq = 0; } else - if (Channel > USER_CHANNEL_LAST) + if (channel > USER_CHANNEL_LAST) { p_vfo->tx_offset_freq = FREQUENCY_floor_to_step(p_vfo->tx_offset_freq + (p_vfo->step_freq / 2), p_vfo->step_freq, 0, p_vfo->tx_offset_freq + p_vfo->step_freq); } @@ -368,8 +369,9 @@ void RADIO_configure_channel(const unsigned int VFO, const unsigned int configur // channel name memset(p_vfo->name, 0, sizeof(p_vfo->name)); - if (Channel <= USER_CHANNEL_LAST) - EEPROM_ReadBuffer(0x0F50 + (Channel * 16), p_vfo->name, 10); // only 10 bytes used + if (channel <= USER_CHANNEL_LAST) +// EEPROM_ReadBuffer(0x0F50 + (Channel * 16), p_vfo->name, 10); // only 10 bytes used + memcpy(p_vfo->name, &g_eeprom.config.channel_name[channel].name, sizeof(g_eeprom.config.channel_name[channel].name)); if (p_vfo->am_mode > 0) { // freq/chan is in AM mode @@ -383,7 +385,7 @@ void RADIO_configure_channel(const unsigned int VFO, const unsigned int configur RADIO_ConfigureSquelchAndOutputPower(p_vfo); #ifdef ENABLE_AM_FIX - if (p_vfo->am_mode > 0 && g_setting_am_fix) + if (p_vfo->am_mode > 0 && g_eeprom.config.setting.am_fix) { AM_fix_reset(VFO); AM_fix_10ms(VFO); @@ -405,25 +407,25 @@ void RADIO_configure_channel(const unsigned int VFO, const unsigned int configur } #endif -// if (configure == VFO_CONFIGURE_RELOAD || IS_FREQ_CHANNEL(Channel)) - if (IS_FREQ_CHANNEL(Channel)) - p_vfo->freq_in_channel = BOARD_find_channel(Frequency); // find channel that has this frequency +// if (configure == VFO_CONFIGURE_RELOAD || IS_FREQ_CHANNEL(channel)) + if (IS_FREQ_CHANNEL(channel)) + p_vfo->freq_in_channel = SETTINGS_find_channel(frequency); // find channel that has this frequency } void RADIO_ConfigureSquelchAndOutputPower(vfo_info_t *p_vfo) { - uint8_t TX_power[3]; - uint16_t Base; - frequency_band_t Band; +// uint8_t tx_power[3]; +// uint16_t base; +// frequency_band_t band; uint8_t squelch_level; // ******************************* // squelch - Band = FREQUENCY_GetBand(p_vfo->p_rx->frequency); - Base = (Band < BAND4_174MHz) ? 0x1E60 : 0x1E00; +// band = FREQUENCY_GetBand(p_vfo->p_rx->frequency); +// base = (Band < BAND4_174MHz) ? 0x1E60 : 0x1E00; - squelch_level = (p_vfo->squelch_level > 0) ? p_vfo->squelch_level : g_eeprom.squelch_level; + squelch_level = (p_vfo->squelch_level > 0) ? p_vfo->squelch_level : g_eeprom.config.setting.squelch_level; // note that 'noise' and 'glitch' values are inverted compared to 'rssi' values @@ -435,11 +437,12 @@ void RADIO_ConfigureSquelchAndOutputPower(vfo_info_t *p_vfo) p_vfo->squelch_open_noise_thresh = 127; // 127 ~ 0 p_vfo->squelch_close_noise_thresh = 127; // 127 ~ 0 - p_vfo->squelch_close_glitch_thresh = 255; // 255 ~ 0 p_vfo->squelch_open_glitch_thresh = 255; // 255 ~ 0 + p_vfo->squelch_close_glitch_thresh = 255; // 255 ~ 0 } else { // squelch >= 1 +#if 0 Base += squelch_level; // my eeprom squelch-1 // VHF UHF EEPROM_ReadBuffer(Base + 0x00, &p_vfo->squelch_open_rssi_thresh, 1); // 50 10 @@ -448,9 +451,21 @@ void RADIO_ConfigureSquelchAndOutputPower(vfo_info_t *p_vfo) EEPROM_ReadBuffer(Base + 0x20, &p_vfo->squelch_open_noise_thresh, 1); // 65 90 EEPROM_ReadBuffer(Base + 0x30, &p_vfo->squelch_close_noise_thresh, 1); // 70 100 - EEPROM_ReadBuffer(Base + 0x40, &p_vfo->squelch_close_glitch_thresh, 1); // 90 90 - EEPROM_ReadBuffer(Base + 0x50, &p_vfo->squelch_open_glitch_thresh, 1); // 100 100 + EEPROM_ReadBuffer(Base + 0x40, &p_vfo->squelch_close_glitch_thresh, 1); // 90 90 BUG ??? .. these 2 swapped ? + EEPROM_ReadBuffer(Base + 0x50, &p_vfo->squelch_open_glitch_thresh, 1); // 100 100 " " +#else + unsigned int band = (unsigned int)FREQUENCY_GetBand(p_vfo->p_rx->frequency); + band = (band < BAND4_174MHz) ? 1 : 0; + p_vfo->squelch_open_rssi_thresh = g_eeprom.calib.squelch_band[band].open_rssi_thresh[squelch_level]; + p_vfo->squelch_close_rssi_thresh = g_eeprom.calib.squelch_band[band].close_rssi_thresh[squelch_level]; + + p_vfo->squelch_open_noise_thresh = g_eeprom.calib.squelch_band[band].open_noise_thresh[squelch_level]; + p_vfo->squelch_close_noise_thresh = g_eeprom.calib.squelch_band[band].close_noise_thresh[squelch_level]; + + p_vfo->squelch_open_glitch_thresh = g_eeprom.calib.squelch_band[band].open_glitch_thresh[squelch_level]; + p_vfo->squelch_close_glitch_thresh = g_eeprom.calib.squelch_band[band].close_glitch_thresh[squelch_level]; +#endif // ********* // used in AM mode @@ -528,45 +543,49 @@ void RADIO_ConfigureSquelchAndOutputPower(vfo_info_t *p_vfo) // ******************************* // output power - // my calibration data - // - // 1ED0 32 32 32 64 64 64 8C 8C 8C FF FF FF FF FF FF FF .. 50 MHz - // 1EE0 32 32 32 64 64 64 8C 8C 8C FF FF FF FF FF FF FF .. 108 MHz - // 1EF0 5F 5F 5F 69 69 69 91 91 8F FF FF FF FF FF FF FF .. 137 MHz - // 1F00 32 32 32 64 64 64 8C 8C 8C FF FF FF FF FF FF FF .. 174 MHz - // 1F10 5A 5A 5A 64 64 64 82 82 82 FF FF FF FF FF FF FF .. 350 MHz - // 1F20 5A 5A 5A 64 64 64 8F 91 8A FF FF FF FF FF FF FF .. 400 MHz - // 1F30 32 32 32 64 64 64 8C 8C 8C FF FF FF FF FF FF FF .. 470 MHz + { + // my calibration data + // + // 1ED0 32 32 32 64 64 64 8C 8C 8C FF FF FF FF FF FF FF .. 50 MHz + // 1EE0 32 32 32 64 64 64 8C 8C 8C FF FF FF FF FF FF FF .. 108 MHz + // 1EF0 5F 5F 5F 69 69 69 91 91 8F FF FF FF FF FF FF FF .. 137 MHz + // 1F00 32 32 32 64 64 64 8C 8C 8C FF FF FF FF FF FF FF .. 174 MHz + // 1F10 5A 5A 5A 64 64 64 82 82 82 FF FF FF FF FF FF FF .. 350 MHz + // 1F20 5A 5A 5A 64 64 64 8F 91 8A FF FF FF FF FF FF FF .. 400 MHz + // 1F30 32 32 32 64 64 64 8C 8C 8C FF FF FF FF FF FF FF .. 470 MHz + + uint8_t tx_power[3]; + const unsigned int band = (unsigned int)FREQUENCY_GetBand(p_vfo->p_tx->frequency); + +// EEPROM_ReadBuffer(0x1ED0 + (band * 16) + (p_vfo->output_power * 3), tx_power, 3); + memcpy(&tx_power, &g_eeprom.calib.tx_band_power[band].level[p_vfo->output_power], 3); - Band = FREQUENCY_GetBand(p_vfo->p_tx->frequency); - - EEPROM_ReadBuffer(0x1ED0 + (Band * 16) + (p_vfo->output_power * 3), TX_power, 3); - - #ifdef ENABLE_REDUCE_LOW_MID_TX_POWER - // make low and mid even lower - if (p_vfo->output_power == OUTPUT_POWER_LOW) - { - TX_power[0] /= 5; //TX_power[0] /= 8; - TX_power[1] /= 5; //TX_power[1] /= 8; - TX_power[2] /= 5; //TX_power[2] /= 8; get more low power - } - else - if (p_vfo->output_power == OUTPUT_POWER_MID) - { - TX_power[0] /= 3; //TX_power[0] /= 5; - TX_power[1] /= 3; //TX_power[1] /= 5; - TX_power[2] /= 3; //TX_power[2] /= 5; get more low power - } - #endif - - p_vfo->txp_calculated_setting = FREQUENCY_CalculateOutputPower( - TX_power[0], - TX_power[1], - TX_power[2], - FREQ_BAND_TABLE[Band].lower, - (FREQ_BAND_TABLE[Band].lower + FREQ_BAND_TABLE[Band].upper) / 2, - FREQ_BAND_TABLE[Band].upper, - p_vfo->p_tx->frequency); + #ifdef ENABLE_REDUCE_LOW_MID_TX_POWER + // make low and mid even lower + if (p_vfo->output_power == OUTPUT_POWER_LOW) + { + tx_power[0] /= 5; //tx_power[0] /= 8; + tx_power[1] /= 5; //tx_power[1] /= 8; + tx_power[2] /= 5; //tx_power[2] /= 8; get more low power + } + else + if (p_vfo->output_power == OUTPUT_POWER_MID) + { + tx_power[0] /= 3; //tx_power[0] /= 5; + tx_power[1] /= 3; //tx_power[1] /= 5; + tx_power[2] /= 3; //tx_power[2] /= 5; get more low power + } + #endif + + p_vfo->txp_calculated_setting = FREQUENCY_CalculateOutputPower( + tx_power[0], + tx_power[1], + tx_power[2], + FREQ_BAND_TABLE[band].lower, + (FREQ_BAND_TABLE[band].lower + FREQ_BAND_TABLE[band].upper) / 2, + FREQ_BAND_TABLE[band].upper, + p_vfo->p_tx->frequency); + } // ******************************* } @@ -612,16 +631,16 @@ void RADIO_ApplyOffset(vfo_info_t *p_vfo, const bool set_pees) static void RADIO_SelectCurrentVfo(void) { - g_current_vfo = (g_eeprom.cross_vfo_rx_tx == CROSS_BAND_OFF) ? g_rx_vfo : &g_eeprom.vfo_info[g_eeprom.tx_vfo]; + g_current_vfo = (g_eeprom.config.setting.cross_vfo == CROSS_BAND_OFF) ? g_rx_vfo : &g_vfo_info[g_eeprom.config.setting.tx_vfo_num]; } void RADIO_select_vfos(void) { - g_eeprom.tx_vfo = get_TX_VFO(); - g_eeprom.rx_vfo = (g_eeprom.cross_vfo_rx_tx == CROSS_BAND_OFF) ? g_eeprom.tx_vfo : (g_eeprom.tx_vfo + 1) & 1u; + g_eeprom.config.setting.tx_vfo_num = get_TX_VFO(); + g_rx_vfo_num = (g_eeprom.config.setting.cross_vfo == CROSS_BAND_OFF) ? g_eeprom.config.setting.tx_vfo_num : (g_eeprom.config.setting.tx_vfo_num + 1) & 1u; - g_tx_vfo = &g_eeprom.vfo_info[g_eeprom.tx_vfo]; - g_rx_vfo = &g_eeprom.vfo_info[g_eeprom.rx_vfo]; + g_tx_vfo = &g_vfo_info[g_eeprom.config.setting.tx_vfo_num]; + g_rx_vfo = &g_vfo_info[g_rx_vfo_num]; RADIO_SelectCurrentVfo(); } @@ -649,7 +668,7 @@ void RADIO_setup_registers(bool switch_to_function_foreground) case BK4819_FILTER_BW_NARROW: #ifdef ENABLE_AM_FIX #if 0 -// BK4819_SetFilterBandwidth(Bandwidth, g_rx_vfo->am_mode > 0 && g_setting_am_fix); +// BK4819_SetFilterBandwidth(Bandwidth, g_rx_vfo->am_mode > 0 && g_eeprom.config.setting.am_fix); BK4819_SetFilterBandwidth(Bandwidth, true); #else if (g_rx_vfo->am_mode > 1) @@ -692,7 +711,7 @@ void RADIO_setup_registers(bool switch_to_function_foreground) BK4819_WriteRegister(0x3F, 0); // disable interrupts #ifdef ENABLE_NOAA - if (IS_NOAA_CHANNEL(g_rx_vfo->channel_save) && g_is_noaa_mode) + if (IS_NOAA_CHANNEL(g_rx_vfo->channel_save) && g_noaa_mode) Frequency = NOAA_FREQUENCY_TABLE[g_noaa_channel]; else #endif @@ -717,10 +736,10 @@ void RADIO_setup_registers(bool switch_to_function_foreground) // else { BK4819_WriteRegister(0x48, - (11u << 12) | // ??? .. 0 ~ 15, doesn't seem to make any difference - ( 0u << 10) | // AF Rx Gain-1 - (g_eeprom.volume_gain << 4) | // AF Rx Gain-2 - (g_eeprom.dac_gain << 0)); // AF DAC Gain (after Gain-1 and Gain-2) + (11u << 12) | // ??? .. 0 ~ 15, doesn't seem to make any difference + ( 0u << 10) | // AF Rx Gain-1 + (g_eeprom.calib.volume_gain << 4) | // AF Rx Gain-2 + (g_eeprom.calib.dac_gain << 0)); // AF DAC Gain (after Gain-1 and Gain-2) } #ifdef ENABLE_VOICE @@ -787,7 +806,7 @@ void RADIO_setup_registers(bool switch_to_function_foreground) break; } - if (g_rx_vfo->scrambling_type > 0 && g_setting_scramble_enable) + if (g_rx_vfo->scrambling_type > 0 && g_eeprom.config.setting.enable_scrambler) BK4819_EnableScramble(g_rx_vfo->scrambling_type - 1); else BK4819_DisableScramble(); @@ -807,11 +826,11 @@ void RADIO_setup_registers(bool switch_to_function_foreground) #ifdef ENABLE_FMRADIO !g_fm_radio_mode && #endif - g_eeprom.vox_switch && + g_eeprom.config.setting.vox_switch && IS_NOT_NOAA_CHANNEL(g_current_vfo->channel_save) && g_current_vfo->am_mode == 0) { - BK4819_EnableVox(g_eeprom.vox1_threshold, g_eeprom.vox0_threshold); + BK4819_EnableVox(g_vox_threshold[1], g_vox_threshold[0]); interrupt_mask |= BK4819_REG_3F_VOX_FOUND | BK4819_REG_3F_VOX_LOST; } else @@ -848,15 +867,15 @@ void RADIO_setup_registers(bool switch_to_function_foreground) g_update_status = true; - if (g_eeprom.noaa_auto_scan) + if (g_eeprom.config.setting.noaa_auto_scan) { - if (g_eeprom.dual_watch != DUAL_WATCH_OFF) + if (g_eeprom.config.setting.dual_watch != DUAL_WATCH_OFF) { - if (IS_NOT_NOAA_CHANNEL(g_eeprom.screen_channel[0])) + if (IS_NOT_NOAA_CHANNEL(g_eeprom.config.setting.indices.vfo[0].screen)) { - if (IS_NOT_NOAA_CHANNEL(g_eeprom.screen_channel[1])) + if (IS_NOT_NOAA_CHANNEL(g_eeprom.config.setting.indices.vfo[1].screen)) { - g_is_noaa_mode = false; + g_noaa_mode = false; return; } ChanAB = 1; @@ -864,25 +883,25 @@ void RADIO_setup_registers(bool switch_to_function_foreground) else ChanAB = 0; - if (!g_is_noaa_mode) - g_noaa_channel = g_eeprom.vfo_info[ChanAB].channel_save - NOAA_CHANNEL_FIRST; + if (!g_noaa_mode) + g_noaa_channel = g_vfo_info[ChanAB].channel_save - NOAA_CHANNEL_FIRST; - g_is_noaa_mode = true; + g_noaa_mode = true; return; } if (g_rx_vfo->channel_save >= NOAA_CHANNEL_FIRST) { - g_is_noaa_mode = true; + g_noaa_mode = true; g_noaa_channel = g_rx_vfo->channel_save - NOAA_CHANNEL_FIRST; g_noaa_tick_10ms = noaa_tick_2_10ms; g_schedule_noaa = false; } else - g_is_noaa_mode = false; + g_noaa_mode = false; } else - g_is_noaa_mode = false; + g_noaa_mode = false; } #endif @@ -905,7 +924,7 @@ void RADIO_enableTX(const bool fsk_tx) case BK4819_FILTER_BW_NARROW: #ifdef ENABLE_AM_FIX #if 0 -// BK4819_SetFilterBandwidth(Bandwidth, g_current_vfo->am_mode > 0 && g_setting_am_fix); +// BK4819_SetFilterBandwidth(Bandwidth, g_current_vfo->am_mode > 0 && g_eeprom.config.setting.am_fix); BK4819_SetFilterBandwidth(Bandwidth, true); #else if (g_current_vfo->am_mode > 1) @@ -980,7 +999,7 @@ void RADIO_set_vfo_state(vfo_state_t State) } else { // 1of11 - const unsigned int vfo = (g_eeprom.cross_vfo_rx_tx == CROSS_BAND_OFF) ? g_eeprom.rx_vfo : g_eeprom.tx_vfo; + const unsigned int vfo = (g_eeprom.config.setting.cross_vfo == CROSS_BAND_OFF) ? g_rx_vfo_num : g_eeprom.config.setting.tx_vfo_num; g_vfo_state[vfo] = State; } @@ -995,21 +1014,21 @@ void RADIO_PrepareTX(void) { vfo_state_t State = VFO_STATE_NORMAL; // default to OK to TX - if (g_eeprom.dual_watch != DUAL_WATCH_OFF) + if (g_eeprom.config.setting.dual_watch != DUAL_WATCH_OFF) { // dual-RX is enabled #if 0 if (g_rx_vfo_is_active) { // use the TX vfo - g_eeprom.rx_vfo = g_eeprom.tx_vfo; - g_rx_vfo = &g_eeprom.vfo_info[g_eeprom.tx_vfo]; + g_rx_vfo_num = g_eeprom.config.setting.tx_vfo_num; + g_rx_vfo = &g_vfo_info[g_eeprom.config.setting.tx_vfo_num]; g_rx_vfo_is_active = false; } g_current_vfo = g_rx_vfo; #else if (!g_rx_vfo_is_active) { // use the current RX vfo - g_eeprom.rx_vfo = g_eeprom.tx_vfo; - g_rx_vfo = &g_eeprom.vfo_info[g_eeprom.tx_vfo]; + g_rx_vfo_num = g_eeprom.config.setting.tx_vfo_num; + g_rx_vfo = &g_vfo_info[g_eeprom.config.setting.tx_vfo_num]; g_rx_vfo_is_active = true; } g_current_vfo = g_rx_vfo; @@ -1027,7 +1046,7 @@ void RADIO_PrepareTX(void) } else #endif - if (!g_setting_tx_enable || g_serial_config_tick_500ms > 0) + if (!g_eeprom.config.setting.tx_enable || g_serial_config_tick_500ms > 0) { // TX is disabled or config upload/download in progress State = VFO_STATE_TX_DISABLE; } @@ -1086,11 +1105,11 @@ void RADIO_PrepareTX(void) if (g_alarm_state == ALARM_STATE_OFF) #endif { - if (g_eeprom.tx_timeout_timer == 0) + if (g_eeprom.config.setting.tx_timeout == 0) g_tx_timer_tick_500ms = 60; // 30 sec else - if (g_eeprom.tx_timeout_timer < (ARRAY_SIZE(g_sub_menu_tx_timeout) - 1)) - g_tx_timer_tick_500ms = 120 * g_eeprom.tx_timeout_timer; // minutes + if (g_eeprom.config.setting.tx_timeout < (ARRAY_SIZE(g_sub_menu_tx_timeout) - 1)) + g_tx_timer_tick_500ms = 120 * g_eeprom.config.setting.tx_timeout; // minutes else g_tx_timer_tick_500ms = 120 * 15; // 15 minutes } @@ -1148,35 +1167,35 @@ void RADIO_tx_eot(void) if (g_dtmf_call_state == DTMF_CALL_STATE_NONE && (g_current_vfo->dtmf_ptt_id_tx_mode == PTT_ID_TX_DOWN || g_current_vfo->dtmf_ptt_id_tx_mode == PTT_ID_BOTH)) { // end-of-tx - if (g_eeprom.dtmf_side_tone) + if (g_eeprom.config.setting.dtmf.side_tone) { GPIO_SetBit(&GPIOC->DATA, GPIOC_PIN_SPEAKER); SYSTEM_DelayMs(60); } - BK4819_EnterDTMF_TX(g_eeprom.dtmf_side_tone); + BK4819_EnterDTMF_TX(g_eeprom.config.setting.dtmf.side_tone); BK4819_PlayDTMFString( - g_eeprom.dtmf_key_down_code, + g_eeprom.config.setting.dtmf.key_down_code, 0, - g_eeprom.dtmf_first_code_persist_time, - g_eeprom.dtmf_hash_code_persist_time, - g_eeprom.dtmf_code_persist_time, - g_eeprom.dtmf_code_interval_time); + g_eeprom.config.setting.dtmf.first_code_persist_time * 10, + g_eeprom.config.setting.dtmf.hash_code_persist_time * 10, + g_eeprom.config.setting.dtmf.code_persist_time * 10, + g_eeprom.config.setting.dtmf.code_interval_time * 10); GPIO_ClearBit(&GPIOC->DATA, GPIOC_PIN_SPEAKER); } else - if (g_eeprom.roger_mode == ROGER_MODE_ROGER) + if (g_eeprom.config.setting.roger_mode == ROGER_MODE_ROGER) { BK4819_PlayRoger(); } else #ifdef ENABLE_MDC1200 -// if (g_eeprom.roger_mode == ROGER_MODE_MDC) +// if (g_eeprom.config.setting.roger_mode == ROGER_MODE_MDC) if (g_current_vfo->mdc1200_mode == MDC1200_MODE_EOT || g_current_vfo->mdc1200_mode == MDC1200_MODE_BOTH) { - BK4819_send_MDC1200(MDC1200_OP_CODE_POST_ID, 0x00, g_eeprom.mdc1200_id); + BK4819_send_MDC1200(MDC1200_OP_CODE_POST_ID, 0x00, g_eeprom.config.setting.mdc1200_id); } else #endif diff --git a/radio.h b/radio.h index 9979861..aaeb7ac 100644 --- a/radio.h +++ b/radio.h @@ -24,12 +24,6 @@ #include "dcs.h" #include "frequencies.h" -enum { - USER_CH_BAND_MASK = 0x0F << 0, - USER_CH_SPARE = 3u << 4, - USER_CH_SCANLIST2 = 1u << 6, - USER_CH_SCANLIST1 = 1u << 7 -}; /* enum { RADIO_CHANNEL_UP = 0x01u, @@ -132,6 +126,8 @@ typedef struct vfo_info_t char name[16]; } vfo_info_t; +extern vfo_info_t g_vfo_info[2]; + extern vfo_info_t *g_tx_vfo; extern vfo_info_t *g_rx_vfo; extern vfo_info_t *g_current_vfo; diff --git a/scheduler.c b/scheduler.c index bb5c5f6..b119627 100644 --- a/scheduler.c +++ b/scheduler.c @@ -78,7 +78,7 @@ void SystickHandler(void) if (g_current_function == FUNCTION_POWER_SAVE) DECREMENT_AND_TRIGGER(g_power_save_tick_10ms, g_power_save_expired); - if (g_eeprom.dual_watch != DUAL_WATCH_OFF && + if (g_eeprom.config.setting.dual_watch != DUAL_WATCH_OFF && g_scan_state_dir == SCAN_STATE_DIR_OFF && g_css_scan_mode == CSS_SCAN_MODE_OFF) { @@ -89,8 +89,8 @@ void SystickHandler(void) #ifdef ENABLE_NOAA if (g_scan_state_dir == SCAN_STATE_DIR_OFF && g_css_scan_mode == CSS_SCAN_MODE_OFF && - g_eeprom.dual_watch == DUAL_WATCH_OFF && - g_is_noaa_mode && + g_eeprom.config.setting.dual_watch == DUAL_WATCH_OFF && + g_noaa_mode && !g_monitor_enabled && g_current_function != FUNCTION_TRANSMIT) { diff --git a/settings.c b/settings.c index e940c92..4c934ad 100644 --- a/settings.c +++ b/settings.c @@ -14,39 +14,304 @@ * limitations under the License. */ +#include "app/dtmf.h" #ifdef ENABLE_FMRADIO #include "app/fm.h" + #include "driver/bk1080.h" #endif +#include "driver/bk4819.h" #include "driver/eeprom.h" #if defined(ENABLE_UART) && defined(ENABLE_UART_DEBUG) #include "driver/uart.h" #endif #include "misc.h" #include "settings.h" +#include "ui/menu.h" -t_eeprom g_eeprom2; +// ****************************************** + + +static const uint32_t DEFAULT_FREQUENCY_TABLE[] = +{ + 14500000, // + 14550000, // + 43300000, // + 43320000, // + 43350000 // +}; + +t_eeprom g_eeprom; +t_channel_attrib g_user_channel_attributes[FREQ_CHANNEL_LAST + 1]; -eeprom_config_t g_eeprom; void SETTINGS_read_eeprom(void) { - EEPROM_ReadBuffer(0, &g_eeprom2, sizeof(g_eeprom2)); + unsigned int index; + + // read the entire EEPROM contents into memory as a whole + for (index = 0; index < sizeof(g_eeprom); index += 128) + EEPROM_ReadBuffer(index, (uint8_t *)(&g_eeprom) + index, 128); #if defined(ENABLE_UART) && defined(ENABLE_UART_DEBUG) UART_printf("config size %04X %u\r\n" + "unused size %04X %u\r\n" "calib size %04X %u\r\n" "eeprom size %04X %u\r\n", - sizeof(g_eeprom2.config), sizeof(g_eeprom2.config), - sizeof(g_eeprom2.calib), sizeof(g_eeprom2.calib), - sizeof(g_eeprom2), sizeof(g_eeprom2)); + sizeof(g_eeprom.config), sizeof(g_eeprom.config), + sizeof(g_eeprom.unused), sizeof(g_eeprom.unused), + sizeof(g_eeprom.calib), sizeof(g_eeprom.calib), + sizeof(g_eeprom), sizeof(g_eeprom)); #endif + + // sanity checks .. + + // 0D60..0E27 + memcpy(&g_user_channel_attributes, &g_eeprom.config.channel_attributes, sizeof(g_user_channel_attributes)); + + g_eeprom.config.setting.call1 = IS_USER_CHANNEL(g_eeprom.config.setting.call1) ? g_eeprom.config.setting.call1 : USER_CHANNEL_FIRST; + g_eeprom.config.setting.squelch_level = (g_eeprom.config.setting.squelch_level < 10) ? g_eeprom.config.setting.squelch_level : 1; + g_eeprom.config.setting.tx_timeout = (g_eeprom.config.setting.tx_timeout < 11) ? g_eeprom.config.setting.tx_timeout : 1; + g_eeprom.config.setting.noaa_auto_scan = (g_eeprom.config.setting.noaa_auto_scan < 2) ? g_eeprom.config.setting.noaa_auto_scan : 0; +#ifdef ENABLE_KEYLOCK + g_eeprom.config.setting.key_lock = (g_eeprom.config.setting.key_lock < 2) ? g_eeprom.config.setting.key_lock : 0; +#endif +#ifdef ENABLE_VOX + g_eeprom.config.setting.vox_switch = (g_eeprom.config.setting.vox_switch < 2) ? g_eeprom.config.setting.vox_switch : 0; + g_eeprom.config.setting.vox_level = (g_eeprom.config.setting.vox_level < 10) ? g_eeprom.config.setting.vox_level : 1; +#endif + g_eeprom.config.setting.mic_sensitivity = (g_eeprom.config.setting.mic_sensitivity < 5) ? g_eeprom.config.setting.mic_sensitivity : 4; + + #ifdef ENABLE_CONTRAST + g_eeprom.config.setting.lcd_contrast = (g_eeprom.config.setting.lcd_contrast > 45) ? 31 : (g_eeprom.config.setting.lcd_contrast < 26) ? 31 : g_eeprom.config.setting.lcd_contrast; + g_setting_contrast = g_eeprom.config.setting.lcd_contrast; + #endif + g_eeprom.config.setting.channel_display_mode = (g_eeprom.config.setting.channel_display_mode < 4) ? g_eeprom.config.setting.channel_display_mode : MDF_FREQUENCY; // 4 instead of 3 - extra display mode + g_eeprom.config.setting.cross_vfo = (g_eeprom.config.setting.cross_vfo < 3) ? g_eeprom.config.setting.cross_vfo : CROSS_BAND_OFF; + g_eeprom.config.setting.battery_save_ratio = (g_eeprom.config.setting.battery_save_ratio < 5) ? g_eeprom.config.setting.battery_save_ratio : 4; + g_eeprom.config.setting.dual_watch = (g_eeprom.config.setting.dual_watch < 3) ? g_eeprom.config.setting.dual_watch : DUAL_WATCH_CHAN_A; + g_eeprom.config.setting.backlight_time = (g_eeprom.config.setting.backlight_time < ARRAY_SIZE(g_sub_menu_backlight)) ? g_eeprom.config.setting.backlight_time : 3; + g_eeprom.config.setting.tail_tone_elimination = (g_eeprom.config.setting.tail_tone_elimination < 2) ? g_eeprom.config.setting.tail_tone_elimination : 0; + g_eeprom.config.setting.vfo_open = (g_eeprom.config.setting.vfo_open < 2) ? g_eeprom.config.setting.vfo_open : 1; + + if (g_eeprom.config.setting.vfo_open == 0) + { + for (index = 0; index < 2; index++) + g_eeprom.config.setting.indices.vfo[index].screen = g_eeprom.config.setting.indices.vfo[index].user; + } + + // 0E80 + for (index = 0; index < ARRAY_SIZE(g_eeprom.config.setting.indices.vfo); index++) + { + g_eeprom.config.setting.indices.vfo[index].screen = IS_VALID_CHANNEL(g_eeprom.config.setting.indices.vfo[index].screen) ? g_eeprom.config.setting.indices.vfo[index].screen : (FREQ_CHANNEL_FIRST + BAND6_400MHz); + g_eeprom.config.setting.indices.vfo[index].user = IS_USER_CHANNEL(g_eeprom.config.setting.indices.vfo[index].user) ? g_eeprom.config.setting.indices.vfo[index].user : USER_CHANNEL_FIRST; + g_eeprom.config.setting.indices.vfo[index].frequency = IS_FREQ_CHANNEL(g_eeprom.config.setting.indices.vfo[index].frequency) ? g_eeprom.config.setting.indices.vfo[index].frequency : (FREQ_CHANNEL_FIRST + BAND6_400MHz); + } +#ifdef ENABLE_NOAA + for (index = 0; index < ARRAY_SIZE(g_eeprom.config.setting.indices.noaa_channel); index++) + g_eeprom.config.setting.indices.noaa_channel[index] = IS_NOAA_CHANNEL(g_eeprom.config.setting.indices.noaa_channel[index]) ? g_eeprom.config.setting.indices.noaa_channel[index] : NOAA_CHANNEL_FIRST; +#endif + +#ifdef ENABLE_FMRADIO + // 0x0E88 + g_eeprom.config.setting.fm_radio.selected_frequency = (g_eeprom.config.setting.fm_radio.selected_frequency >= BK1080_freq_lower && g_eeprom.config.setting.fm_radio.selected_frequency < BK1080_freq_upper) ? g_eeprom.config.setting.fm_radio.selected_frequency : BK1080_freq_lower; + g_eeprom.config.setting.fm_radio.selected_channel = (g_eeprom.config.setting.fm_radio.selected_channel < ARRAY_SIZE(g_eeprom.config.setting.fm_channel)) ? g_eeprom.config.setting.fm_radio.selected_channel : 0; + g_eeprom.config.setting.fm_radio.channel_mode = (g_eeprom.config.setting.fm_radio.channel_mode < 2) ? !!g_eeprom.config.setting.fm_radio.channel_mode : 0; + + // 0E40..0E67 + FM_configure_channel_state(); +#endif + + // 0E90..0E97 + g_eeprom.config.setting.beep_control = (g_eeprom.config.setting.beep_control < 2) ? g_eeprom.config.setting.beep_control : 0; + g_eeprom.config.setting.key1_short = (g_eeprom.config.setting.key1_short < ACTION_OPT_LEN) ? g_eeprom.config.setting.key1_short : ACTION_OPT_MONITOR; + g_eeprom.config.setting.key1_long = (g_eeprom.config.setting.key1_long < ACTION_OPT_LEN) ? g_eeprom.config.setting.key1_long : ACTION_OPT_FLASHLIGHT; + g_eeprom.config.setting.key2_short = (g_eeprom.config.setting.key2_short < ACTION_OPT_LEN) ? g_eeprom.config.setting.key2_short : ACTION_OPT_SCAN; + g_eeprom.config.setting.key2_long = (g_eeprom.config.setting.key2_long < ACTION_OPT_LEN) ? g_eeprom.config.setting.key2_long : ACTION_OPT_NONE; + g_eeprom.config.setting.carrier_search_mode = (g_eeprom.config.setting.carrier_search_mode < 3) ? g_eeprom.config.setting.carrier_search_mode : SCAN_RESUME_CARRIER; + g_eeprom.config.setting.auto_key_lock = (g_eeprom.config.setting.auto_key_lock < 2) ? g_eeprom.config.setting.auto_key_lock : 0; + g_eeprom.config.setting.power_on_display_mode = (g_eeprom.config.setting.power_on_display_mode < 4) ? g_eeprom.config.setting.power_on_display_mode : PWR_ON_DISPLAY_MODE_VOLTAGE; + + // 0EA0..0EA7 + #ifdef ENABLE_VOICE + g_eeprom.config.setting.voice_prompt = (g_eeprom.config.setting.voice_prompt < 3) ? g_eeprom.config.setting.voice_prompt : VOICE_PROMPT_ENGLISH; + #endif + + // 0EA8..0EAF + #ifdef ENABLE_ALARM + g_eeprom.config.setting.alarm_mode = (g_eeprom.config.setting.alarm_mode < 2) ? g_eeprom.config.setting.alarm_mode : 1; + #endif + g_eeprom.config.setting.roger_mode = (g_eeprom.config.setting.roger_mode < 3) ? g_eeprom.config.setting.roger_mode : ROGER_MODE_OFF; + g_eeprom.config.setting.repeater_tail_tone_elimination = (g_eeprom.config.setting.repeater_tail_tone_elimination < 11) ? g_eeprom.config.setting.repeater_tail_tone_elimination : 0; + g_eeprom.config.setting.tx_vfo_num = (g_eeprom.config.setting.tx_vfo_num < 2) ? g_eeprom.config.setting.tx_vfo_num : 0; + #if defined(ENABLE_AIRCOPY) && defined(ENABLE_AIRCOPY_REMEMBER_FREQ) + if (g_eeprom.config.setting.air_copy_freq > 0 && g_eeprom.config.setting.air_copy_freq < 0xffffffff) + { + for (index = 0; index < ARRAY_SIZE(FREQ_BAND_TABLE); index++) + if (g_eeprom.config.setting.air_copy_freq >= FREQ_BAND_TABLE[index].lower && g_eeprom.config.setting.air_copy_freq < FREQ_BAND_TABLE[index].upper) + break; + g_aircopy_freq = (index < ARRAY_SIZE(FREQ_BAND_TABLE)) ? g_eeprom.config.setting.air_copy_freq : 0xffffffff; + } + #endif + + // 0ED0..0ED7 + g_eeprom.config.setting.dtmf.side_tone = (g_eeprom.config.setting.dtmf.side_tone < 2) ? g_eeprom.config.setting.dtmf.side_tone : 1; + g_eeprom.config.setting.dtmf.separate_code = DTMF_ValidateCodes((char *)(&g_eeprom.config.setting.dtmf.separate_code), 1) ? g_eeprom.config.setting.dtmf.separate_code : '*'; + g_eeprom.config.setting.dtmf.group_call_code = DTMF_ValidateCodes((char *)(&g_eeprom.config.setting.dtmf.group_call_code), 1) ? g_eeprom.config.setting.dtmf.group_call_code : '#'; + g_eeprom.config.setting.dtmf.decode_response = (g_eeprom.config.setting.dtmf.decode_response < 4) ? g_eeprom.config.setting.dtmf.decode_response : DTMF_DEC_RESPONSE_RING; + g_eeprom.config.setting.dtmf.auto_reset_time = (g_eeprom.config.setting.dtmf.auto_reset_time <= DTMF_HOLD_MAX) ? g_eeprom.config.setting.dtmf.auto_reset_time : (g_eeprom.config.setting.dtmf.auto_reset_time >= DTMF_HOLD_MIN) ? g_eeprom.config.setting.dtmf.auto_reset_time : DTMF_HOLD_MAX; + g_eeprom.config.setting.dtmf.preload_time = (g_eeprom.config.setting.dtmf.preload_time < 10) ? g_eeprom.config.setting.dtmf.preload_time : 20; + g_eeprom.config.setting.dtmf.first_code_persist_time = (g_eeprom.config.setting.dtmf.first_code_persist_time < 10) ? g_eeprom.config.setting.dtmf.first_code_persist_time : 7; + g_eeprom.config.setting.dtmf.hash_code_persist_time = (g_eeprom.config.setting.dtmf.hash_code_persist_time < 10) ? g_eeprom.config.setting.dtmf.hash_code_persist_time : 7; + g_eeprom.config.setting.dtmf.code_persist_time = (g_eeprom.config.setting.dtmf.code_persist_time < 10) ? g_eeprom.config.setting.dtmf.code_persist_time : 7; + g_eeprom.config.setting.dtmf.code_interval_time = (g_eeprom.config.setting.dtmf.code_interval_time < 10) ? g_eeprom.config.setting.dtmf.code_interval_time : 7; + #ifdef ENABLE_KILL_REVIVE + g_eeprom.config.setting.dtmf.permit_remote_kill = (g_eeprom.config.setting.dtmf.permit_remote_kill < 2) ? g_eeprom.config.setting.dtmf.permit_remote_kill : 0; + #else + g_eeprom.config.setting.dtmf.permit_remote_kill = 0; + #endif + + // 0EE0..0EE7 + if (!DTMF_ValidateCodes(g_eeprom.config.setting.dtmf.ani_id, sizeof(g_eeprom.config.setting.dtmf.ani_id))) + { + memset(g_eeprom.config.setting.dtmf.ani_id, 0, sizeof(g_eeprom.config.setting.dtmf.ani_id)); + strcpy(g_eeprom.config.setting.dtmf.ani_id, "123"); + } + + #ifdef ENABLE_KILL_REVIVE + // 0EE8..0EEF + if (!DTMF_ValidateCodes(g_eeprom.config.setting.dtmf.kill_code, sizeof(g_eeprom.config.setting.dtmf.kill_code))) + { + memset(g_eeprom.config.setting.dtmf.kill_code, 0, sizeof(g_eeprom.config.setting.dtmf.kill_code)); + strcpy(g_eeprom.config.setting.dtmf.kill_code, "ABCD9"); + } + + // 0EF0..0EF7 + if (!DTMF_ValidateCodes(g_eeprom.config.setting.dtmf.revive_code, sizeof(g_eeprom.config.setting.dtmf.revive_code))) + { + memset(g_eeprom.config.setting.dtmf.revive_code, 0, sizeof(g_eeprom.config.setting.dtmf.revive_code)); + strcpy(g_eeprom.config.setting.dtmf.revive_code, "9DCBA"); + } + #else + memset(g_eeprom.config.setting.dtmf.kill_code, 0, sizeof(g_eeprom.config.setting.dtmf.kill_code)); + memset(g_eeprom.config.setting.dtmf.revive_code, 0, sizeof(g_eeprom.config.setting.dtmf.revive_code)); + #endif + + // 0EF8..0F07 + if (!DTMF_ValidateCodes(g_eeprom.config.setting.dtmf.key_up_code, sizeof(g_eeprom.config.setting.dtmf.key_up_code))) + { + memset(g_eeprom.config.setting.dtmf.key_up_code, 0, sizeof(g_eeprom.config.setting.dtmf.key_up_code)); + strcpy(g_eeprom.config.setting.dtmf.key_up_code, "12345"); + } + + // 0F08..0F17 + if (!DTMF_ValidateCodes(g_eeprom.config.setting.dtmf.key_down_code, sizeof(g_eeprom.config.setting.dtmf.key_down_code))) + { + memset(g_eeprom.config.setting.dtmf.key_down_code, 0, sizeof(g_eeprom.config.setting.dtmf.key_down_code)); + strcpy(g_eeprom.config.setting.dtmf.key_down_code, "54321"); + } + + // 0F18..0F1F + g_eeprom.config.setting.scan_list_default = (g_eeprom.config.setting.scan_list_default < 3) ? g_eeprom.config.setting.scan_list_default : 0; // we now have 'all' channel scan option + for (index = 0; index < ARRAY_SIZE(g_eeprom.config.setting.priority_scan_list); index++) + { + unsigned int k; + g_eeprom.config.setting.priority_scan_list[index].enabled = (g_eeprom.config.setting.priority_scan_list[index].enabled < 2) ? g_eeprom.config.setting.priority_scan_list[index].enabled : 0; + for (k = 0; k < ARRAY_SIZE(g_eeprom.config.setting.priority_scan_list[index].channel); k++) + if (!IS_USER_CHANNEL(g_eeprom.config.setting.priority_scan_list[index].channel[k])) + g_eeprom.config.setting.priority_scan_list[index].channel[k] = 0xff; + } + g_eeprom.config.setting.unused10 = 0xff; + + // 0F30..0F3F .. AES key + g_has_aes_key = false; + #if ENABLE_RESET_AES_KEY + // wipe that darned AES key + memset(&g_eeprom.config.setting.aes_key, 0xff, sizeof(g_eeprom.config.setting.aes_key)); + #else + for (index = 0; index < ARRAY_SIZE(g_eeprom.config.setting.aes_key) && !g_has_aes_key; index++) + if (g_eeprom.config.setting.aes_key[index] != 0xffffffff) + g_has_aes_key = true; + #endif + + // 0F40..0F47 + g_eeprom.config.setting.freq_lock = (g_eeprom.config.setting.freq_lock < FREQ_LOCK_LAST) ? g_eeprom.config.setting.freq_lock : FREQ_LOCK_NORMAL; +// g_eeprom.config.setting.enable_tx_350 = (g_eeprom.config.setting.enable_tx_350 < 2) ? g_eeprom.config.setting.enable_tx_350 : false; + #ifdef ENABLE_KILL_REVIVE +// g_eeprom.config.setting.radio_disabled = (g_eeprom.config.setting.radio_disabled < 2) ? g_eeprom.config.setting.radio_disabled : 0; + #else + g_eeprom.config.setting.radio_disabled = 0; + #endif +// g_eeprom.config.setting.enable_tx_200 = (g_eeprom.config.setting.enable_tx_200 < 2) ? g_eeprom.config.setting.enable_tx_200 : 0; +// g_eeprom.config.setting.enable_tx_470 = (g_eeprom.config.setting.enable_tx_470 < 2) ? g_eeprom.config.setting.enable_tx_470 : 0; +// g_eeprom.config.setting.enable_350 = (g_eeprom.config.setting.enable_350 < 2) ? g_eeprom.config.setting.enable_350 : 1; +// g_eeprom.config.setting.enable_scrambler = (g_eeprom.config.setting.enable_scrambler & (1u << 0)) ? 1 : 0; + #ifdef ENABLE_RX_SIGNAL_BAR +// g_eeprom.config.setting.enable_rssi_bar = (Data[6] & (1u << 1)) ? true : false; + #else + g_eeprom.config.setting.enable_rssi_bar = 0; + #endif +// g_eeprom.config.setting.tx_enable = (Data[7] & (1u << 0)) ? true : false; +// g_eeprom.config.setting.dtmf_live_decoder = (Data[7] & (1u << 1)) ? true : false; + g_eeprom.config.setting.battery_text = (g_eeprom.config.setting.battery_text < 3) ? g_eeprom.config.setting.battery_text : 2; + #ifdef ENABLE_TX_AUDIO_BAR +// g_eeprom.config.setting.mic_bar = (Data[7] & (1u << 4)) ? true : false; + #endif + #ifdef ENABLE_AM_FIX +// g_eeprom.config.setting.am_fix = (Data[7] & (1u << 5)) ? true : false; + #endif +// g_eeprom.config.setting.backlight_on_tx_rx = (Data[7] >> 6) & 3u; + + // 0F48..0F4F + g_eeprom.config.setting.scan_hold_time = (g_eeprom.config.setting.scan_hold_time > 40) ? 6 : (g_eeprom.config.setting.scan_hold_time < 2) ? 6 : g_eeprom.config.setting.scan_hold_time; + + memset(&g_eeprom.config.unused13, 0xff, sizeof(g_eeprom.config.unused13)); + + memset(&g_eeprom.unused, 0xff, sizeof(g_eeprom.unused)); + + // **************************************** + + memset(&g_eeprom.calib.unused3, 0xff, sizeof(g_eeprom.calib.unused3)); + + memcpy(&g_eeprom_rssi_calib[0], &g_eeprom.calib.rssi_band_123, 8); + memcpy(&g_eeprom_rssi_calib[1], &g_eeprom_rssi_calib[0], 8); + memcpy(&g_eeprom_rssi_calib[2], &g_eeprom_rssi_calib[0], 8); + memcpy(&g_eeprom_rssi_calib[3], &g_eeprom.calib.rssi_band_4567, 8); + memcpy(&g_eeprom_rssi_calib[4], &g_eeprom_rssi_calib[3], 8); + memcpy(&g_eeprom_rssi_calib[5], &g_eeprom_rssi_calib[3], 8); + memcpy(&g_eeprom_rssi_calib[6], &g_eeprom_rssi_calib[3], 8); + + if (g_eeprom.calib.battery[0] >= 5000) + { + g_eeprom.calib.battery[0] = 1900; + g_eeprom.calib.battery[1] = 2000; + } + g_eeprom.calib.battery[5] = 2300; + + #ifdef ENABLE_VOX + g_vox_threshold[1] = g_eeprom.calib.vox[0].threshold[g_eeprom.config.setting.vox_level]; + g_vox_threshold[0] = g_eeprom.calib.vox[1].threshold[g_eeprom.config.setting.vox_level]; + #endif + + //EEPROM_ReadBuffer(0x1F80 + g_eeprom.config.setting.mic_sensitivity, &Mic, 1); + //g_mic_sensitivity_tuning = (Mic < 32) ? Mic : 15; + g_mic_sensitivity_tuning = g_mic_gain_dB_2[g_eeprom.config.setting.mic_sensitivity]; + + g_eeprom.calib.bk4819_xtal_freq_low = (g_eeprom.calib.bk4819_xtal_freq_low >= -1000 && g_eeprom.calib.bk4819_xtal_freq_low <= 1000) ? g_eeprom.calib.bk4819_xtal_freq_low : 0; + + g_eeprom.calib.volume_gain = (g_eeprom.calib.volume_gain < 64) ? g_eeprom.calib.volume_gain : 58; + g_eeprom.calib.dac_gain = (g_eeprom.calib.dac_gain < 16) ? g_eeprom.calib.dac_gain : 8; + + BK4819_WriteRegister(0x3B, 22656 + g_eeprom.calib.bk4819_xtal_freq_low); +// BK4819_WriteRegister(0x3C, g_eeprom.calib.BK4819_XTAL_FREQ_HIGH); + + // **************************************** } void SETTINGS_write_eeprom_config(void) -{ +{ // save the entire EEPROM contents uint32_t index; - for (index = 0; index < sizeof(g_eeprom2); index += 8) - EEPROM_WriteBuffer8(index, (uint8_t *)(&g_eeprom2) + index); + for (index = 0; index < sizeof(g_eeprom); index += 8) + EEPROM_WriteBuffer8(index, (uint8_t *)(&g_eeprom) + index); } #ifdef ENABLE_FMRADIO @@ -54,219 +319,71 @@ void SETTINGS_write_eeprom_config(void) { unsigned int i; - struct - { - uint16_t frequency; - uint8_t channel; - bool is_channel_selected; - uint8_t padding[4]; - } state; + uint16_t index = (uint16_t)((uint8_t *)&g_eeprom.config.setting.fm_radio - (uint8_t *)&g_eeprom.config); + EEPROM_WriteBuffer8(index, &g_eeprom.config.setting.fm_radio); - memset(&state, 0xFF, sizeof(state)); - state.channel = g_eeprom.fm_selected_channel; - state.frequency = g_eeprom.fm_selected_frequency; - state.is_channel_selected = g_eeprom.fm_channel_mode; - - EEPROM_WriteBuffer8(0x0E88, &state); - - for (i = 0; i < 5; i++) - EEPROM_WriteBuffer8(0x0E40 + (i * 8), &g_fm_channels[i * 4]); + index = (uint16_t)((uint8_t *)&g_eeprom.config.setting.fm_channel - (uint8_t *)&g_eeprom.config); + for (i = 0; i < sizeof(g_eeprom.config.setting.fm_channel); i += 8) + EEPROM_WriteBuffer8(index + i, ((uint8_t *)&g_eeprom.config.setting.fm_channel) + i); } #endif void SETTINGS_save_vfo_indices(void) { - uint8_t State[8]; - - #ifndef ENABLE_NOAA - EEPROM_ReadBuffer(0x0E80, State, sizeof(State)); - #endif - - State[0] = g_eeprom.screen_channel[0]; - State[1] = g_eeprom.user_channel[0]; - State[2] = g_eeprom.freq_channel[0]; - State[3] = g_eeprom.screen_channel[1]; - State[4] = g_eeprom.user_channel[1]; - State[5] = g_eeprom.freq_channel[1]; - #ifdef ENABLE_NOAA - State[6] = g_eeprom.noaa_channel[0]; - State[7] = g_eeprom.noaa_channel[1]; - #endif - - EEPROM_WriteBuffer8(0x0E80, State); + const uint16_t index = (uint16_t)((uint8_t *)&g_eeprom.config.setting.indices - (uint8_t *)&g_eeprom.config); + EEPROM_WriteBuffer8(index, &g_eeprom.config.setting.indices); } void SETTINGS_save(void) { - uint8_t State[8]; + uint32_t index; - State[0] = g_eeprom2.config.call1; - State[1] = g_eeprom.squelch_level; - State[2] = g_eeprom.tx_timeout_timer; - #ifdef ENABLE_NOAA - State[3] = g_eeprom.noaa_auto_scan; - #else - State[3] = false; + #ifndef ENABLE_KEYLOCK + g_eeprom.config.setting.key_lock = 0; #endif - - #ifdef ENABLE_KEYLOCK - State[4] = g_eeprom.key_lock; - #else - State[4] = false; - #endif - #ifdef ENABLE_VOX - State[5] = g_eeprom.vox_switch; - State[6] = g_eeprom.vox_level; - #else - State[5] = false; - State[6] = 0; - #endif - State[7] = g_eeprom.mic_sensitivity; - EEPROM_WriteBuffer8(0x0E70, State); - #ifdef ENABLE_CONTRAST - State[0] = g_setting_contrast; - #else - State[0] = 0xFF; + #ifndef ENABLE_VOX +// g_eeprom.config.setting.vox_switch = 0; +// g_eeprom.config.setting.vox_level = 0; #endif - State[1] = g_eeprom.channel_display_mode; - State[2] = g_eeprom.cross_vfo_rx_tx; - State[3] = g_eeprom.battery_save; - State[4] = g_eeprom.dual_watch; - State[5] = g_eeprom.backlight; - State[6] = g_eeprom.tail_note_elimination; - State[7] = g_eeprom.vfo_open; - EEPROM_WriteBuffer8(0x0E78, State); - State[0] = g_eeprom.beep_control; - State[1] = g_eeprom.key1_short_press_action; - State[2] = g_eeprom.key1_long_press_action; - State[3] = g_eeprom.key2_short_press_action; - State[4] = g_eeprom.key2_long_press_action; - State[5] = g_eeprom.scan_resume_mode; - State[6] = g_eeprom.auto_keypad_lock; - State[7] = g_eeprom.pwr_on_display_mode; - EEPROM_WriteBuffer8(0x0E90, State); + #ifndef ENABLE_CONTRAST +// g_eeprom.config.setting.unused4 = 0xff; + #endif +// memset(&g_eeprom.config.setting.unused6, 0xff, sizeof(g_eeprom.config.setting.unused6)); + + #ifndef ENABLE_PWRON_PASSWORD + memset(&g_eeprom.config.setting.power_on_password, 0xff, sizeof(g_eeprom.config.setting.power_on_password)); + #endif + + #if !defined(ENABLE_ALARM) && !defined(ENABLE_TX1750) + g_eeprom.config.setting.alarm_mode = 0; + #endif + + #if defined(ENABLE_AIRCOPY) && defined(ENABLE_AIRCOPY_REMEMBER_FREQ) + // remember the AIRCOPY frequency + g_eeprom.config.setting.air_copy_freq = g_aircopy_freq; + #else + memset(&g_eeprom.config.setting.unused8, 0xff, sizeof(g_eeprom.config.setting.unused8)); + #endif + + #ifndef ENABLE_KILL_REVIVE + g_eeprom.config.setting.radio_disabled = 0; + #endif + + for (index = 0; index < sizeof(g_eeprom.config.setting); index += 8) { - struct { - uint32_t password; - #ifdef ENABLE_MDC1200 - uint16_t mdc1200_id; // 1of11 - uint8_t spare[2]; - #else - uint8_t spare[4]; - #endif - } __attribute__((packed)) array; - - memset(&array, 0xff, sizeof(array)); - #ifdef ENABLE_PWRON_PASSWORD - array.password = g_eeprom.power_on_password; - #endif - #ifdef ENABLE_MDC1200 - array.mdc1200_id = g_eeprom.mdc1200_id; - #endif - - EEPROM_WriteBuffer8(0x0E98, &array); + const uint16_t offset = (uint16_t)((uint8_t *)&g_eeprom.config.setting - (uint8_t *)&g_eeprom.config); + EEPROM_WriteBuffer8(offset + index, (uint8_t *)(&g_eeprom.config.setting) + index); } - - #ifdef ENABLE_VOICE - memset(State, 0xFF, sizeof(State)); - State[0] = g_eeprom.voice_prompt; - EEPROM_WriteBuffer8(0x0EA0, State); - #endif - - // ***************************** - - { - struct { - uint8_t alarm_mode; - uint8_t roger_mode; - uint8_t repeater_tail_tone_elimination; - uint8_t tx_vfo; - uint32_t air_copy_freq; - } __attribute__((packed)) array; - - memset(&array, 0xff, sizeof(array)); - - #if defined(ENABLE_ALARM) || defined(ENABLE_TX1750) - array.alarm_mode = g_eeprom.alarm_mode; - #else - array.alarm_mode = false; - #endif - array.roger_mode = g_eeprom.roger_mode; - array.repeater_tail_tone_elimination = g_eeprom.repeater_tail_tone_elimination; - array.tx_vfo = g_eeprom.tx_vfo; - #ifdef ENABLE_AIRCOPY_REMEMBER_FREQ - // remember the AIRCOPY frequency - array.air_copy_freq = g_aircopy_freq; - #endif - - EEPROM_WriteBuffer8(0x0EA8, &array); - } - - State[0] = g_eeprom.dtmf_side_tone; - State[1] = g_eeprom.dtmf_separate_code; - State[2] = g_eeprom.dtmf_group_call_code; - State[3] = g_eeprom.dtmf_decode_response; - State[4] = g_eeprom.dtmf_auto_reset_time; - State[5] = g_eeprom.dtmf_preload_time / 10U; - State[6] = g_eeprom.dtmf_first_code_persist_time / 10U; - State[7] = g_eeprom.dtmf_hash_code_persist_time / 10U; - EEPROM_WriteBuffer8(0x0ED0, State); - - memset(State, 0xFF, sizeof(State)); - State[0] = g_eeprom.dtmf_code_persist_time / 10U; - State[1] = g_eeprom.dtmf_code_interval_time / 10U; - State[2] = g_eeprom.permit_remote_kill; - EEPROM_WriteBuffer8(0x0ED8, State); - - State[0] = g_eeprom.scan_list_default; - State[1] = g_eeprom.scan_list_enabled[0]; - State[2] = g_eeprom.scan_list_priority_ch1[0]; - State[3] = g_eeprom.scan_list_priority_ch2[0]; - State[4] = g_eeprom.scan_list_enabled[1]; - State[5] = g_eeprom.scan_list_priority_ch1[1]; - State[6] = g_eeprom.scan_list_priority_ch2[1]; - State[7] = 0xFF; - EEPROM_WriteBuffer8(0x0F18, State); - - memset(State, 0xFF, sizeof(State)); - State[0] = g_setting_freq_lock; - State[1] = g_setting_350_tx_enable; - #ifdef ENABLE_KILL_REVIVE - State[2] = g_setting_radio_disabled; - #else - State[2] = false; - #endif - State[3] = g_setting_174_tx_enable; - State[4] = g_setting_470_tx_enable; - State[5] = g_setting_350_enable; - if (!g_setting_scramble_enable) State[6] &= ~(1u << 0); - #ifdef ENABLE_RX_SIGNAL_BAR - if (!g_setting_rssi_bar) State[6] &= ~(1u << 1); - #endif - if (!g_setting_tx_enable) State[7] &= ~(1u << 0); - if (!g_setting_live_dtmf_decoder) State[7] &= ~(1u << 1); - State[7] = (State[7] & ~(3u << 2)) | ((g_setting_battery_text & 3u) << 2); - #ifdef ENABLE_TX_AUDIO_BAR - if (!g_setting_mic_bar) State[7] &= ~(1u << 4); - #endif - #ifdef ENABLE_AM_FIX -// if (!g_setting_am_fix) State[7] &= ~(1u << 5); - #endif - State[7] = (State[7] & ~(3u << 6)) | ((g_setting_backlight_on_tx_rx & 3u) << 6); - EEPROM_WriteBuffer8(0x0F40, State); - - memset(State, 0xFF, sizeof(State)); - State[0] = g_eeprom.scan_hold_time_500ms; - EEPROM_WriteBuffer8(0x0F48, State); } void SETTINGS_save_channel(const unsigned int channel, const unsigned int vfo, const vfo_info_t *p_vfo, const unsigned int mode) { - unsigned int eeprom_addr = channel * 16; - t_channel m_channel; + const unsigned int chan = CHANNEL_NUM(channel, vfo); + t_channel *p_channel = &g_eeprom.config.channel[chan]; + unsigned int eeprom_addr = chan * 16; if (IS_NOAA_CHANNEL(channel)) return; @@ -274,53 +391,50 @@ void SETTINGS_save_channel(const unsigned int channel, const unsigned int vfo, c if (mode < 2 && channel <= USER_CHANNEL_LAST) return; - if (IS_FREQ_CHANNEL(channel)) - eeprom_addr = 0x0C80 + (16 * vfo) + ((channel - FREQ_CHANNEL_FIRST) * 16 * 2); // a VFO - #if defined(ENABLE_UART) && defined(ENABLE_UART_DEBUG) -// UART_printf("sav_chan %04X %3u %u %u\r\n", eeprom_addr, channel, vfo, mode); + UART_printf("sav_chan %04X %3u %3u %u %u\r\n", eeprom_addr, chan, channel, vfo, mode); #endif // **************** if (p_vfo != NULL) { - memset(&m_channel, 0, sizeof(m_channel)); - m_channel.frequency = p_vfo->freq_config_rx.frequency; - m_channel.tx_offset = p_vfo->tx_offset_freq; - m_channel.rx_ctcss_cdcss_code = p_vfo->freq_config_rx.code; - m_channel.tx_ctcss_cdcss_code = p_vfo->freq_config_tx.code; - m_channel.rx_ctcss_cdcss_type = p_vfo->freq_config_rx.code_type; -// m_channel.unused1:2 - m_channel.tx_ctcss_cdcss_type = p_vfo->freq_config_tx.code_type; + memset(p_channel, 0, sizeof(t_channel)); + p_channel->frequency = p_vfo->freq_config_rx.frequency; + p_channel->tx_offset = p_vfo->tx_offset_freq; + p_channel->rx_ctcss_cdcss_code = p_vfo->freq_config_rx.code; + p_channel->tx_ctcss_cdcss_code = p_vfo->freq_config_tx.code; + p_channel->rx_ctcss_cdcss_type = p_vfo->freq_config_rx.code_type; +// p_channel->unused1:2 + p_channel->tx_ctcss_cdcss_type = p_vfo->freq_config_tx.code_type; #ifdef ENABLE_MDC1200 - m_channel.mdc1200_mode = p_vfo->mdc1200_mode; + p_channel->mdc1200_mode = p_vfo->mdc1200_mode; #endif - m_channel.tx_offset_dir = p_vfo->tx_offset_freq_dir; -// m_channel.unused3:2 - m_channel.am_mode = p_vfo->am_mode; -// m_channel.unused4:3 - m_channel.frequency_reverse = p_vfo->frequency_reverse; - m_channel.channel_bandwidth = p_vfo->channel_bandwidth; - m_channel.tx_power = p_vfo->output_power; - m_channel.busy_channel_lock = p_vfo->busy_channel_lock; -// m_channel.unused5:1 - m_channel.compand = p_vfo->compand; - m_channel.dtmf_decoding_enable = p_vfo->dtmf_decoding_enable; - m_channel.dtmf_ptt_id_tx_mode = p_vfo->dtmf_ptt_id_tx_mode; -// m_channel.unused6:4 - m_channel.step_setting = p_vfo->step_setting; - m_channel.scrambler = p_vfo->scrambling_type; - m_channel.squelch_level = p_vfo->squelch_level; + p_channel->tx_offset_dir = p_vfo->tx_offset_freq_dir; +// p_channel->unused3:2 + p_channel->am_mode = p_vfo->am_mode; +// p_channel->unused4:3 + p_channel->frequency_reverse = p_vfo->frequency_reverse; + p_channel->channel_bandwidth = p_vfo->channel_bandwidth; + p_channel->tx_power = p_vfo->output_power; + p_channel->busy_channel_lock = p_vfo->busy_channel_lock; +// p_channel->unused5:1 + p_channel->compand = p_vfo->compand; + p_channel->dtmf_decoding_enable = p_vfo->dtmf_decoding_enable; + p_channel->dtmf_ptt_id_tx_mode = p_vfo->dtmf_ptt_id_tx_mode; +// p_channel->unused6:4 + p_channel->step_setting = p_vfo->step_setting; + p_channel->scrambler = p_vfo->scrambling_type; + p_channel->squelch_level = p_vfo->squelch_level; } else if (channel <= USER_CHANNEL_LAST) { // user channel - memset(&m_channel, 0xff, sizeof(m_channel)); + memset(p_channel, 0xff, sizeof(t_channel)); } - EEPROM_WriteBuffer8(eeprom_addr + 0, (uint8_t *)(&m_channel) + 0); - EEPROM_WriteBuffer8(eeprom_addr + 8, (uint8_t *)(&m_channel) + 8); + EEPROM_WriteBuffer8(eeprom_addr + 0, (uint8_t *)(p_channel) + 0); + EEPROM_WriteBuffer8(eeprom_addr + 8, (uint8_t *)(p_channel) + 8); // **************** @@ -329,28 +443,35 @@ void SETTINGS_save_channel(const unsigned int channel, const unsigned int vfo, c if (channel <= USER_CHANNEL_LAST) { // user channel, it has a channel name const unsigned int eeprom_addr = 0x0F50 + (channel * 16); - uint8_t name[16]; - memset(name, (p_vfo != NULL) ? 0x00 : 0xff, sizeof(name)); + memset(&g_eeprom.config.channel_name[channel], (p_vfo != NULL) ? 0x00 : 0xff, sizeof(g_eeprom.config.channel_name[channel])); #ifndef ENABLE_KEEP_MEM_NAME + // clear/reset the channel name - EEPROM_WriteBuffer8(eeprom_addr + 0, name + 0); - EEPROM_WriteBuffer8(eeprom_addr + 8, name + 8); + EEPROM_WriteBuffer8(eeprom_addr + 0, g_eeprom.config.channel_name[channel] + 0); + EEPROM_WriteBuffer8(eeprom_addr + 8, g_eeprom.config.channel_name[channel] + 8); + #else + if (p_vfo != NULL) - memcpy(name, p_vfo->name, 10); + memcpy(&g_eeprom.config.channel_name[channel], p_vfo->name, 10); + if (mode >= 3 || p_vfo == NULL) { // save the channel name - EEPROM_WriteBuffer8(eeprom_addr + 0, name + 0); - EEPROM_WriteBuffer8(eeprom_addr + 8, name + 8); + + EEPROM_WriteBuffer8(eeprom_addr + 0, &g_eeprom.config.channel_name[channel] + 0); + EEPROM_WriteBuffer8(eeprom_addr + 8, &g_eeprom.config.channel_name[channel] + 8); } + #endif } } void SETTINGS_save_chan_attribs_name(const unsigned int channel, const vfo_info_t *p_vfo) { + const unsigned int index = channel & ~7ul; // eeprom writes are always 8 bytes in length + if (channel >= ARRAY_SIZE(g_user_channel_attributes)) return; @@ -360,40 +481,182 @@ void SETTINGS_save_chan_attribs_name(const unsigned int channel, const vfo_info_ if (p_vfo != NULL) { // channel attributes - const uint8_t attribs = - ((p_vfo->scanlist_1_participation & 1u) << 7) | - ((p_vfo->scanlist_2_participation & 1u) << 6) | - ((3u) << 4) | - ((p_vfo->band & 7u) << 0); + t_channel_attrib attribs; + + attribs.band = p_vfo->band & 7u; + attribs.unused = 3u; + attribs.scanlist2 = p_vfo->scanlist_2_participation; + attribs.scanlist1 = p_vfo->scanlist_1_participation; + + g_user_channel_attributes[channel] = attribs; // remember new attributes + g_eeprom.config.channel_attributes[channel] = attribs; - const unsigned int index = channel & ~7ul; // eeprom writes are always 8 bytes in length - g_user_channel_attributes[channel] = attribs; // remember new attributes EEPROM_WriteBuffer8(0x0D60 + index, g_user_channel_attributes + index); } else if (channel <= USER_CHANNEL_LAST) { // user channel - const unsigned int index = channel & ~7ul; // eeprom writes are always 8 bytes in length - g_user_channel_attributes[channel] = 0xff; + g_user_channel_attributes[channel].attributes = 0xff; + g_eeprom.config.channel_attributes[channel].attributes = 0xff; + EEPROM_WriteBuffer8(0x0D60 + index, g_user_channel_attributes + index); } if (channel <= USER_CHANNEL_LAST) { // user memory channel const unsigned int index = channel * 16; - uint8_t name[16]; if (p_vfo != NULL) { - memset(name, 0, sizeof(name)); - memcpy(name, p_vfo->name, 10); + memset(&g_eeprom.config.channel_name[channel], 0, sizeof(g_eeprom.config.channel_name[channel])); + memcpy(&g_eeprom.config.channel_name[channel], p_vfo->name, 10); } else { - memset(name, 0xff, sizeof(name)); + memset(&g_eeprom.config.channel_name[channel], 0xff, sizeof(g_eeprom.config.channel_name[channel])); } - EEPROM_WriteBuffer8(0x0F50 + 0 + index, name + 0); - EEPROM_WriteBuffer8(0x0F50 + 8 + index, name + 8); + EEPROM_WriteBuffer8(0x0F50 + 0 + index, &g_eeprom.config.channel_name[channel] + 0); + EEPROM_WriteBuffer8(0x0F50 + 8 + index, &g_eeprom.config.channel_name[channel] + 8); + } +} + +unsigned int SETTINGS_find_channel(const uint32_t frequency) +{ + unsigned int chan; + + if (frequency == 0 || frequency == 0xffffffff) + return 0xffffffff; + + for (chan = 0; chan <= USER_CHANNEL_LAST; chan++) + { + const uint32_t freq = g_eeprom.config.channel[chan].frequency; + + if (g_user_channel_attributes[chan].band > BAND7_470MHz || freq == 0 || freq == 0xffffffff) + continue; + + if (freq == frequency) + return chan; // found it + } + + return 0xffffffff; +} + +uint32_t SETTINGS_fetch_channel_frequency(const int channel) +{ + uint32_t freq; + + if (channel < 0 || channel > (int)USER_CHANNEL_LAST) + return 0; + + freq = g_eeprom.config.channel[channel].frequency; + + if (g_user_channel_attributes[channel].band > BAND7_470MHz || freq == 0 || freq == 0xffffffff) + return 0; + + return freq; +} + +unsigned int SETTINGS_fetch_channel_step_setting(const int channel) +{ + unsigned int step_setting; + + if (channel < 0) + return 0; + + if (channel <= USER_CHANNEL_LAST) + step_setting = g_eeprom.config.channel[channel].step_setting; + else + if (channel <= FREQ_CHANNEL_LAST) + step_setting = g_eeprom.config.vfo_channel[(channel - FREQ_CHANNEL_FIRST) * 2].step_setting; + +// step_size = STEP_FREQ_TABLE[step_setting]; + + return (step_setting >= ARRAY_SIZE(STEP_FREQ_TABLE)) ? STEP_12_5kHz : step_setting; +} + +unsigned int SETTINGS_fetch_frequency_step_setting(const int channel, const int vfo) +{ + unsigned int step_setting; + + if (channel < 0 || channel > (FREQ_CHANNEL_LAST - FREQ_CHANNEL_FIRST) || vfo < 0 || vfo >= 2) + return 0; + + step_setting = g_eeprom.config.vfo_channel[(channel * 2) + vfo].step_setting; + +// step_size = STEP_FREQ_TABLE[step_setting]; + + return (step_setting >= ARRAY_SIZE(STEP_FREQ_TABLE)) ? STEP_12_5kHz : step_setting; +} + +void SETTINGS_fetch_channel_name(char *s, const int channel) +{ + int i; + + if (s == NULL) + return; + + memset(s, 0, 11); // 's' had better be large enough ! + + if (channel < 0 || channel > (int)USER_CHANNEL_LAST) + return; + + if (g_user_channel_attributes[channel].band > BAND7_470MHz) + return; + + memcpy(s, &g_eeprom.config.channel_name[channel], 10); + + for (i = 0; i < 10; i++) + if (s[i] < 32 || s[i] > 127) + break; // invalid char + + s[i--] = 0; // null term + + while (i >= 0 && s[i] == 32) // trim trailing spaces + s[i--] = 0; // null term +} + +void SETTINGS_factory_reset(bool bIsAll) +{ + uint16_t i; + uint8_t Template[8]; + + memset(Template, 0xFF, sizeof(Template)); + + for (i = 0x0C80; i < 0x1E00; i += 8) + { + if ( + !(i >= 0x0EE0 && i < 0x0F18) && // ANI ID + DTMF codes + !(i >= 0x0F30 && i < 0x0F50) && // AES KEY + F LOCK + Scramble Enable + !(i >= 0x1C00 && i < 0x1E00) && // DTMF contacts + !(i >= 0x0EB0 && i < 0x0ED0) && // Welcome strings + !(i >= 0x0EA0 && i < 0x0EA8) && // Voice Prompt + (bIsAll || + ( + !(i >= 0x0D60 && i < 0x0E28) && // MR Channel Attributes + !(i >= 0x0F18 && i < 0x0F30) && // Scan List + !(i >= 0x0F50 && i < 0x1C00) && // MR Channel Names + !(i >= 0x0E40 && i < 0x0E70) && // FM Channels + !(i >= 0x0E88 && i < 0x0E90) // FM settings + )) + ) + { + EEPROM_WriteBuffer8(i, Template); + } + } + + if (bIsAll) + { + RADIO_InitInfo(g_rx_vfo, FREQ_CHANNEL_FIRST + BAND6_400MHz, 43350000); + + // set the first few memory channels + for (i = 0; i < ARRAY_SIZE(DEFAULT_FREQUENCY_TABLE); i++) + { + const uint32_t Frequency = DEFAULT_FREQUENCY_TABLE[i]; + g_rx_vfo->freq_config_rx.frequency = Frequency; + g_rx_vfo->freq_config_tx.frequency = Frequency; + g_rx_vfo->band = FREQUENCY_GetBand(Frequency); + SETTINGS_save_channel(USER_CHANNEL_FIRST + i, 0, g_rx_vfo, 2); + } } } diff --git a/settings.h b/settings.h index 421613d..29880c2 100644 --- a/settings.h +++ b/settings.h @@ -123,13 +123,6 @@ typedef enum mdf_display_mode_e mdf_display_mode_t; // ************************************************ // this is the full eeprom structure, both config and calibration areas -// -// am going to use this to replace ALL the currently scattered values -// -// this will also make AIRCOPY safe as we'll first save the incoming transfer -// into this ram area. -// Then, IF the transfer completes withput error, we'll copy it in one go to eeprom - // if channel is used, all unused bits are '0's // if channel not used, all bytes are 0xff @@ -193,184 +186,195 @@ typedef struct { #endif } __attribute__((packed)) t_channel; // -typedef struct { - uint8_t band:4; // why do QS have these bits ? band can/is computed from the frequency - uint8_t unused:2; // - uint8_t scanlist2:1; // set if in scan list 2 - uint8_t scanlist1:1; // set if in scan list 1 -} __attribute__((packed)) t_channel_attr; // +typedef union { + struct { + uint8_t band:4; // why do QS have these bits ? band can/is computed from the frequency + uint8_t unused:2; // + uint8_t scanlist2:1; // set if in scan list 2 + uint8_t scanlist1:1; // set if in scan list 1 + }; + uint8_t attributes; +} __attribute__((packed)) t_channel_attrib; // user configuration typedef struct { - // 0x0000 - t_channel channel[200]; // unused channels are set to all '0xff' + union { - // 0x0C80 - union { // 2 VFO's (upper/lower) per band, 7 frequency bands - t_channel vfo[14]; // - struct { // - t_channel a; // - t_channel b; // - } __attribute__((packed)) vfo_band[7]; // - } __attribute__((packed)); // + struct { + // 0x0000 + t_channel user_channel[200]; // unused channels are set to all '0xff' + // 0x0C80 + t_channel vfo_channel[14]; + }; + + // 0x0000 + t_channel channel[214]; + + } __attribute__((packed)); // 0x0D60 - t_channel_attr channel_attr[200]; // + t_channel_attrib channel_attributes[USER_CHANNEL_LAST - USER_CHANNEL_FIRST + 1]; - uint8_t unused1[8]; // 0xff's + struct { + // 0x0E28 + uint8_t unused1[8]; // 0xff's - // 0x0E30 - uint8_t unused2[16]; // 0xff's + // 0x0E30 + uint8_t unused2[16]; // 0xff's - // 0x0E40 - uint16_t fm_channel[20]; // - uint8_t unused3[8]; // 0xff's + // 0x0E40 + uint16_t fm_channel[20]; // + uint8_t unused3[8]; // 0xff's - // 0x0E70 - uint8_t call1; // - uint8_t squelch_level; // - uint8_t tx_timeout; // - uint8_t noaa_auto_scan; // - uint8_t key_lock; // - uint8_t vox_switch; // - uint8_t vox_level; // - uint8_t mic_sensitivity; // - #ifdef ENABLE_CONTRAST - uint8_t lcd_contrast; // 1of11 - #else - uint8_t unused4; // 0xff's - #endif - uint8_t channel_display_mode; // - uint8_t cross_vfo; // - uint8_t battery_save_ratio; // - uint8_t dual_watch; // - uint8_t backlight_time; // - uint8_t tail_tone_elimination; // - uint8_t vfo_open; // + // 0x0E70 + uint8_t call1; // + uint8_t squelch_level; // + uint8_t tx_timeout; // + uint8_t noaa_auto_scan; // + uint8_t key_lock; // + uint8_t vox_switch; // + uint8_t vox_level; // + uint8_t mic_sensitivity; // + #ifdef ENABLE_CONTRAST + uint8_t lcd_contrast; // 1of11 + #else + uint8_t unused4; // 0xff's + #endif + uint8_t channel_display_mode; // + uint8_t cross_vfo; // + uint8_t battery_save_ratio; // + uint8_t dual_watch; // + uint8_t backlight_time; // + uint8_t tail_tone_elimination; // + uint8_t vfo_open; // - // 0x0E80 - uint8_t screen_channel_a; // - uint8_t channel_a; // - uint8_t freq_channel_a; // - uint8_t screen_channel_b; // - uint8_t channel_b; // - uint8_t freq_channel_b; // - uint8_t noaa_channel_a; // - uint8_t noaa_channel_b; // - uint8_t fm_selected_frequency; // - uint8_t fm_selected_channel; // - uint8_t fm_channel_mode; // - uint8_t unused5[5]; // 0xff's + // 0x0E80 + struct { + struct { + uint8_t screen; // + uint8_t user; // + uint8_t frequency; // + } __attribute__((packed)) vfo[2]; + uint8_t noaa_channel[2]; // + } __attribute__((packed)) indices; - // 0x0E90 - uint8_t beep_control; // - uint8_t key1_short; // - uint8_t key1_long; // - uint8_t key2_short; // - uint8_t key2_long; // - uint8_t carrier_search_mode; // sc_rev; - uint8_t auto_key_lock; // - uint8_t display_mode; // - uint32_t power_on_password; // - #ifdef ENABLE_MDC1200 - uint16_t mdc1200_id; // 1of11 - uint8_t unused6[2]; // 0xff's - #else - uint8_t unused6[4]; // 0xff's - #endif + // 0x0E88 + struct { + uint16_t selected_frequency; // + uint8_t selected_channel; // + uint8_t channel_mode; // + uint8_t unused[4]; // 0xff's + } __attribute__((packed)) fm_radio; - // 0x0EA0 - uint8_t voice_prompt; // - uint8_t unused7[7]; // 0xff's - uint8_t alarm_mode; // - uint8_t roger_mode; // - uint8_t repeater_tail_tone_elimination; // rp_ste - uint8_t tx_channel; // - #ifdef ENABLE_AIRCOPY - uint32_t air_copy_freq; // 1of11 - #else - uint8_t unused8[4]; // 0xff's - #endif + // 0x0E90 + uint8_t beep_control; // + uint8_t key1_short; // + uint8_t key1_long; // + uint8_t key2_short; // + uint8_t key2_long; // + uint8_t carrier_search_mode; // sc_rev + uint8_t auto_key_lock; // + uint8_t power_on_display_mode; // + uint32_t power_on_password; // + #ifdef ENABLE_MDC1200 + uint16_t mdc1200_id; // 1of11 + uint8_t unused6[2]; // 0xff's + #else + uint8_t unused6[4]; // 0xff's + #endif - // 0x0EB0 - char welcome_line1[16]; // - char welcome_line2[16]; // + // 0x0EA0 + uint8_t voice_prompt; // + uint8_t unused7[7]; // 0xff's + uint8_t alarm_mode; // + uint8_t roger_mode; // + uint8_t repeater_tail_tone_elimination; // rp_ste + uint8_t tx_vfo_num; // + #ifdef ENABLE_AIRCOPY + uint32_t air_copy_freq; // 1of11 + #else + uint8_t unused8[4]; // 0xff's + #endif - // 0x0ED0 - uint8_t dtmf_side_tone; // - uint8_t dtmf_separate_code; // - uint8_t dtmf_group_call_code; // - uint8_t dtmf_rsp; // - uint8_t dtmf_auto_reset_time; // - uint8_t dtmf_preload_time; // - uint8_t dtmf_first_code_time; // - uint8_t dtmf_hash_code_time; // - uint8_t dtmf_code_time; // - uint8_t dtmf_code_interval; // - uint8_t dtmf_permit_kill; // - uint8_t unused9[5]; // 0xff's + // 0x0EB0 + char welcome_line[2][16]; // - // 0x0EE0 - uint8_t dtmf_ani_id[8]; // - uint8_t dtmf_kill_code[8]; // - uint8_t dtmf_revive_code[8]; // - uint8_t dtmf_key_up_code[16]; // - uint8_t dtmf_key_down_code[16]; // + struct { + // 0x0ED0 + uint8_t side_tone; // + uint8_t separate_code; // + uint8_t group_call_code; // + uint8_t decode_response; // + uint8_t auto_reset_time; // + uint8_t preload_time; // + uint8_t first_code_persist_time; // + uint8_t hash_code_persist_time; // + uint8_t code_persist_time; // + uint8_t code_interval_time; // + uint8_t permit_remote_kill; // + uint8_t unused[5]; // 0xff's - // 0x0F18 - uint8_t s_list_default; // - uint8_t priority1_enable; // - uint8_t priority1_channel1; // - uint8_t priority1_channel2; // - uint8_t priority2_enable; // - uint8_t priority2_channel1; // - uint8_t priority2_channel2; // - uint8_t unused10; // 0xff's + // 0x0EE0 + char ani_id[8]; // + char kill_code[8]; // + char revive_code[8]; // + char key_up_code[16]; // + char key_down_code[16]; // + } __attribute__((packed)) dtmf; - // 0x0F20 - uint8_t unused11[16]; // 0xff's + // 0x0F18 + uint8_t scan_list_default; // + struct { + uint8_t enabled; // + uint8_t channel[2]; // + } __attribute__((packed)) priority_scan_list[2]; + uint8_t unused10; // 0xff's - // 0x0F30 - uint8_t aes_key[16]; // disabled = all 0xff + // 0x0F20 + uint8_t unused11[16]; // 0xff's - // 0x0F40 - uint8_t freq_lock; // - uint8_t enable_tx_350:1; // 1 = 350MHz ~ 400MHz TX is enabled - uint8_t unused11a:7; // - uint8_t radio_disabled:1; // 1 = radio is disabled - uint8_t unused11b:7; // - uint8_t enable_tx_200:1; // 1 = 174MHz ~ 350MHz TX enabled - uint8_t unused11c:7; // - uint8_t enable_tx_470:1; // 1 = >= 470MHz TX enabled - uint8_t unused11d:7; // - uint8_t enable_350:1; // 1 = 350HMz ~ 400MHz enabled - uint8_t unused11e:7; // - uint8_t enable_scrambler:1; // - uint8_t enable_rssi_bar:1; // 1of11 - uint8_t unused11f:6; // - #if 0 - // QS - uint8_t unused12[9]; // 0xff's - #else - // 1of11 - uint8_t tx_enable:1; // 0 = completely disable TX, 1 = allow TX - uint8_t dtmf_live_decoder:1; // 1 = enable on-screen live DTMF decoder - uint8_t battery_text:2; // 0 = no battery text, 1 = voltage, 2 = percent .. on the status bar - uint8_t mic_bar:1; // 1 = on-screen TX audio level - uint8_t am_fix:1; // 1 = enable RX AM fix - uint8_t backlight_on_tx_rx:2; // 0 = no backlight when TX/RX, 1 = when TX, 2 = when RX, 3 = both RX/TX + // 0x0F30 + uint32_t aes_key[4]; // disabled = all 0xff - uint8_t scan_hold_time; // ticks we stay paused for on an RX'ed signal when scanning + // 0x0F40 + uint8_t freq_lock; // + uint8_t enable_tx_350:1; // 1 = 350MHz ~ 400MHz TX is enabled + uint8_t unused11a:7; // + uint8_t radio_disabled:1; // 1 = radio is disabled + uint8_t unused11b:7; // + uint8_t enable_tx_200:1; // 1 = 174MHz ~ 350MHz TX enabled + uint8_t unused11c:7; // + uint8_t enable_tx_470:1; // 1 = >= 470MHz TX enabled + uint8_t unused11d:7; // + uint8_t enable_350:1; // 1 = 350HMz ~ 400MHz enabled + uint8_t unused11e:7; // + uint8_t enable_scrambler:1; // + uint8_t enable_rssi_bar:1; // 1of11 + uint8_t unused11f:6; // + #if 0 + // QS + uint8_t unused12[9]; // 0xff's + #else + // 1of11 + uint8_t tx_enable:1; // 0 = completely disable TX, 1 = allow TX + uint8_t dtmf_live_decoder:1; // 1 = enable on-screen live DTMF decoder + uint8_t battery_text:2; // 0 = no battery text, 1 = voltage, 2 = percent .. on the status bar + uint8_t mic_bar:1; // 1 = on-screen TX audio level + uint8_t am_fix:1; // 1 = enable RX AM fix + uint8_t backlight_on_tx_rx:2; // 0 = no backlight when TX/RX, 1 = when TX, 2 = when RX, 3 = both RX/TX - uint8_t unused12[7]; // 0xff's - #endif + uint8_t scan_hold_time; // ticks we stay paused for on an RX'ed signal when scanning + + uint8_t unused12[7]; // 0xff's + #endif + } __attribute__((packed)) setting; // 0x0F50 struct { char name[10]; uint8_t unused[6]; // 0xff's - } __attribute__((packed)) channel_name[200]; + } __attribute__((packed)) channel_name[USER_CHANNEL_LAST - USER_CHANNEL_FIRST + 1]; // 0x1BD0 uint8_t unused13[16 * 3]; // 0xff's .. free to use @@ -400,23 +404,7 @@ typedef struct { uint8_t unused5[6]; // 0xff's uint8_t close_glitch_thresh[10]; // uint8_t unused6[6]; // 0xff's - } squelch_band_4567[6]; - - // 0x1E60 - struct { - uint8_t open_rssi_thresh[10]; // - uint8_t unused1[6]; // 0xff's - uint8_t close_rssi_thresh[10]; // - uint8_t unused2[6]; // 0xff's - uint8_t open_noise_thresh[10]; // - uint8_t unused3[6]; // 0xff's - uint8_t close_noise_thresh[10]; // - uint8_t unused4[6]; // 0xff's - uint8_t open_glitch_thresh[10]; // - uint8_t unused5[6]; // 0xff's - uint8_t close_glitch_thresh[10]; // - uint8_t unused6[6]; // 0xff's - } squelch_band_123[6]; + } __attribute__((packed)) squelch_band[2]; // 0 = bands 4567, 1 = bands 123 // 0x1EC0 uint16_t rssi_band_4567[4]; // RSSI bargraph thresholds .. (dBm + 160) * 2 @@ -425,11 +413,16 @@ typedef struct { // 0x1ED0 struct { - uint8_t low[3]; // - uint8_t mid[3]; // - uint8_t high[3]; // + union { + struct { + uint8_t low[3]; // + uint8_t mid[3]; // + uint8_t high[3]; // + }; + uint8_t level[3][3]; // + }; uint8_t unused[7]; // 0xff's - } tx_band_power[7]; // + } __attribute__((packed)) tx_band_power[7]; // 0x1F40 uint16_t battery[6]; // @@ -440,11 +433,13 @@ typedef struct { { uint16_t threshold[10]; // uint8_t unused[4]; // 0xff's - } vox[2]; + } __attribute__((packed)) vox[2]; // 0x1F80 uint8_t mic_gain_dB2[5]; // - uint8_t unused4[3]; // + uint8_t unused2[3]; // + + // 0x1F88 int16_t bk4819_xtal_freq_low; // uint16_t unknown2; // uint16_t unknown3; // @@ -452,7 +447,7 @@ typedef struct { uint8_t dac_gain; // // 0x1F90 - uint8_t unused5[16 * 7]; // 0xff's + uint8_t unused3[16 * 7]; // 0xff's // 0x2000 @@ -465,7 +460,7 @@ typedef struct { t_config config; // radios user config // 0x1D00 - uint8_t unused[256]; // does this belong to the config, or the calibration, or neither ? + uint8_t unused[16 * 16]; // does this belong to the config, or the calibration, or neither ? // 0x1E00 t_calibration calib; // calibration settings .. we DO NOT pass this through aircopy, it's radio specific @@ -473,122 +468,9 @@ typedef struct { } __attribute__((packed)) t_eeprom; // 8192 (0x2000) bytes of eeprom // ************************************************ -// this and all the other variables are going to be replaced with the above t_eeprom -typedef struct { - uint8_t screen_channel[2]; - uint8_t freq_channel[2]; - uint8_t user_channel[2]; - #ifdef ENABLE_NOAA - uint8_t noaa_channel[2]; - #endif - uint8_t rx_vfo; - uint8_t tx_vfo; - - uint8_t field7_0xa; - uint8_t field8_0xb; - - #ifdef ENABLE_FMRADIO - uint16_t fm_selected_frequency; - uint8_t fm_selected_channel; - bool fm_channel_mode; - uint16_t fm_frequency_playing; - #endif - - uint8_t squelch_level; - uint8_t tx_timeout_timer; - #ifdef ENABLE_KEYLOCK - bool key_lock; - #endif - bool vox_switch; - uint8_t vox_level; - #ifdef ENABLE_VOICE - voice_prompt_t voice_prompt; - #endif - bool beep_control; - uint8_t channel_display_mode; - bool tail_note_elimination; - bool vfo_open; - uint8_t dual_watch; - uint8_t cross_vfo_rx_tx; - uint8_t battery_save; - uint8_t backlight; - uint8_t scan_resume_mode; - uint8_t scan_list_default; - bool scan_list_enabled[2]; - uint8_t scan_list_priority_ch1[2]; - uint8_t scan_list_priority_ch2[2]; - - #ifdef ENABLE_MDC1200 - uint16_t mdc1200_id; - #endif - - bool auto_keypad_lock; - - #if defined(ENABLE_ALARM) || defined(ENABLE_TX1750) - alarm_mode_t alarm_mode; - #endif - pwr_on_display_mode_t pwr_on_display_mode; - roger_mode_t roger_mode; - uint8_t repeater_tail_tone_elimination; - uint8_t key1_short_press_action; - uint8_t key1_long_press_action; - uint8_t key2_short_press_action; - uint8_t key2_long_press_action; - uint8_t mic_sensitivity; - uint8_t mic_sensitivity_tuning; -// uint8_t chan_1_call; - char ani_dtmf_id[8]; - char kill_code[8]; - char revive_code[8]; - char dtmf_key_up_code[16]; - char dtmf_key_down_code[16]; - - char dtmf_separate_code; - char dtmf_group_call_code; - uint8_t dtmf_decode_response; - uint8_t dtmf_auto_reset_time; - uint16_t dtmf_preload_time; - uint16_t dtmf_first_code_persist_time; - uint16_t dtmf_hash_code_persist_time; - uint16_t dtmf_code_persist_time; - uint16_t dtmf_code_interval_time; - bool dtmf_side_tone; - bool permit_remote_kill; - int16_t BK4819_xtal_freq_low; - #ifdef ENABLE_NOAA - bool noaa_auto_scan; - #endif - uint8_t volume_gain; - uint8_t dac_gain; - vfo_info_t vfo_info[2]; - uint32_t power_on_password; - uint16_t vox1_threshold; - uint16_t vox0_threshold; - - uint8_t scan_hold_time_500ms; - -// uint8_t field29_0x26; -// uint8_t field30_0x27; - -// uint8_t field37_0x32; -// uint8_t field38_0x33; - -// uint8_t field57_0x6c; -// uint8_t field58_0x6d; - -// uint8_t field60_0x7e; -// uint8_t field61_0x7f; - -// uint8_t field77_0x95; -// uint8_t field78_0x96; -// uint8_t field79_0x97; - -} eeprom_config_t; - -extern t_eeprom g_eeprom2; - -extern eeprom_config_t g_eeprom; +extern t_eeprom g_eeprom; +extern t_channel_attrib g_user_channel_attributes[FREQ_CHANNEL_LAST + 1]; void SETTINGS_read_eeprom(void); void SETTINGS_write_eeprom_config(void); @@ -597,9 +479,15 @@ void SETTINGS_write_eeprom_config(void); void SETTINGS_save_fm(void); #endif void SETTINGS_save_vfo_indices(void); -//void SETTINGS_restore_calibration(void); void SETTINGS_save(void); void SETTINGS_save_channel(const unsigned int channel, const unsigned int vfo, const vfo_info_t *p_vfo, const unsigned int mode); void SETTINGS_save_chan_attribs_name(const unsigned int channel, const vfo_info_t *p_vfo); +unsigned int SETTINGS_find_channel(const uint32_t frequency); +uint32_t SETTINGS_fetch_channel_frequency(const int channel); +unsigned int SETTINGS_fetch_channel_step_setting(const int channel); +void SETTINGS_fetch_channel_name(char *s, const int channel); +unsigned int SETTINGS_fetch_frequency_step_setting(const int channel, const int vfo); +void SETTINGS_factory_reset(bool bIsAll); + #endif diff --git a/ui/fmradio.c b/ui/fmradio.c index 9cd014c..b428898 100644 --- a/ui/fmradio.c +++ b/ui/fmradio.c @@ -34,7 +34,7 @@ void UI_DisplayFM(void) memset(g_frame_buffer, 0, sizeof(g_frame_buffer)); #ifdef ENABLE_KEYLOCK - if (g_eeprom.key_lock && g_keypad_locked > 0) + if (g_eeprom.config.setting.key_lock && g_keypad_locked > 0) { // tell user how to unlock the keyboard backlight_turn_on(0); UI_PrintString("Long press #", 0, LCD_WIDTH - 1, 1, 8); @@ -54,7 +54,7 @@ void UI_DisplayFM(void) if (g_ask_to_save) { - const unsigned int freq = g_eeprom.fm_frequency_playing; + const unsigned int freq = g_eeprom.config.setting.fm_radio.selected_frequency; sprintf(str, "SAVE %u.%u ?", freq / 10, freq % 10); } else @@ -68,22 +68,22 @@ void UI_DisplayFM(void) if (g_fm_scan_state_dir == FM_SCAN_STATE_DIR_OFF) { - if (!g_eeprom.fm_channel_mode) + if (g_eeprom.config.setting.fm_radio.channel_mode == 0) { - for (i = 0; i < ARRAY_SIZE(g_fm_channels); i++) + for (i = 0; i < ARRAY_SIZE(g_eeprom.config.setting.fm_channel); i++) { - if (g_eeprom.fm_frequency_playing == g_fm_channels[i]) + if (g_eeprom.config.setting.fm_radio.selected_frequency == g_eeprom.config.setting.fm_channel[i]) { sprintf(str, "VFO (CH %u)", 1 + i); break; } } - if (i >= ARRAY_SIZE(g_fm_channels)) + if (i >= ARRAY_SIZE(g_eeprom.config.setting.fm_channel)) strcpy(str, "VFO"); } else - sprintf(str, "CH %u", 1 + g_eeprom.fm_selected_channel); + sprintf(str, "CH %u", 1 + g_eeprom.config.setting.fm_radio.selected_channel); } else if (!g_fm_auto_scan) @@ -102,13 +102,13 @@ void UI_DisplayFM(void) if (g_ask_to_save) { // channel mode const unsigned int chan = g_fm_channel_position; - const uint32_t freq = g_fm_channels[chan]; + const uint32_t freq = g_eeprom.config.setting.fm_channel[chan]; UI_GenerateChannelString(str, chan, ' '); if (FM_check_valid_channel(chan)) sprintf(str + strlen(str), " (%u.%u)", freq / 10, freq % 10); } else - if (g_eeprom.fm_channel_mode && g_input_box_index > 0) + if (g_eeprom.config.setting.fm_radio.channel_mode != 0 && g_input_box_index > 0) { // user is entering a channel number UI_GenerateChannelString(str, g_fm_channel_position, ' '); } @@ -117,7 +117,7 @@ void UI_DisplayFM(void) { if (g_input_box_index == 0) { // frequency mode - const uint32_t freq = g_eeprom.fm_frequency_playing; + const uint32_t freq = g_eeprom.config.setting.fm_radio.selected_frequency; NUMBER_ToDigits(freq * 10000, str); #ifdef ENABLE_TRIM_TRAILING_ZEROS UI_DisplayFrequency(str, 30, 4, false, true); @@ -132,8 +132,8 @@ void UI_DisplayFM(void) } else { // delete channel - const uint32_t chan = g_eeprom.fm_selected_channel; - const uint32_t freq = g_fm_channels[chan]; + const uint32_t chan = g_eeprom.config.setting.fm_radio.selected_channel; + const uint32_t freq = g_eeprom.config.setting.fm_channel[chan]; sprintf(str, "CH %u (%u.%u)", 1 + chan, freq / 10, freq % 10); } diff --git a/ui/lock.c b/ui/lock.c index 7bcb651..7c92ee1 100644 --- a/ui/lock.c +++ b/ui/lock.c @@ -107,7 +107,7 @@ void UI_DisplayLock(void) NUMBER_Get(g_input_box, &Password); - if ((g_eeprom.power_on_password * 100) == Password) + if ((g_eeprom.config.setting.power_on_password * 100) == Password) { AUDIO_PlayBeep(BEEP_1KHZ_60MS_OPTIONAL); return; diff --git a/ui/main.c b/ui/main.c index 91b685f..b06c658 100644 --- a/ui/main.c +++ b/ui/main.c @@ -85,11 +85,11 @@ void draw_bar(uint8_t *line, const int len, const int max_width) if (g_center_line != CENTER_LINE_NONE && g_center_line != CENTER_LINE_TX_TIMEOUT) return false; - if (g_eeprom.tx_timeout_timer == 0) + if (g_eeprom.config.setting.tx_timeout == 0) timeout_secs = 30; // 30 sec else - if (g_eeprom.tx_timeout_timer < (ARRAY_SIZE(g_sub_menu_tx_timeout) - 1)) - timeout_secs = 60 * g_eeprom.tx_timeout_timer; // minutes + if (g_eeprom.config.setting.tx_timeout < (ARRAY_SIZE(g_sub_menu_tx_timeout) - 1)) + timeout_secs = 60 * g_eeprom.config.setting.tx_timeout; // minutes else timeout_secs = 60 * 15; // 15 minutes @@ -182,7 +182,7 @@ void UI_drawBars(uint8_t *p, const unsigned int level) return false; #endif - if (g_setting_mic_bar) + if (g_eeprom.config.setting.mic_bar) { const unsigned int line = 3; const unsigned int txt_width = 7 * 3; // 3 text chars @@ -230,7 +230,7 @@ void UI_drawBars(uint8_t *p, const unsigned int level) #ifdef ENABLE_RX_SIGNAL_BAR bool UI_DisplayRSSIBar(const int16_t rssi, const bool now) { - if (g_setting_rssi_bar) + if (g_eeprom.config.setting.enable_rssi_bar) { // const int16_t s0_dBm = -127; // S0 .. base level const int16_t s0_dBm = -147; // S0 .. base level @@ -365,7 +365,7 @@ void UI_update_rssi(const int16_t rssi, const int vfo) // ********************************************************** #ifdef ENABLE_KEYLOCK - if (g_eeprom.key_lock && g_keypad_locked > 0) + if (g_eeprom.config.setting.key_lock && g_keypad_locked > 0) return; // display is in use #endif @@ -428,7 +428,7 @@ void UI_DisplayMain(void) g_center_line = CENTER_LINE_NONE; // #ifdef SINGLE_VFO_CHAN -// const bool single_vfo = (g_eeprom.dual_watch == DUAL_WATCH_OFF && g_eeprom.cross_vfo_rx_tx == CROSS_BAND_OFF) ? true : false; +// const bool single_vfo = (g_eeprom.config.setting.dual_watch == DUAL_WATCH_OFF && g_eeprom.config.setting.cross_vfo == CROSS_BAND_OFF) ? true : false; // #else const bool single_vfo = false; // #endif @@ -446,7 +446,7 @@ void UI_DisplayMain(void) } #ifdef ENABLE_KEYLOCK - if (g_eeprom.key_lock && g_keypad_locked > 0) + if (g_eeprom.config.setting.key_lock && g_keypad_locked > 0) { // tell user how to unlock the keyboard backlight_turn_on(10); // 5 seconds UI_PrintString("Long press #", 0, LCD_WIDTH, 1, 8); @@ -458,9 +458,10 @@ void UI_DisplayMain(void) for (vfo_num = 0; vfo_num < 2; vfo_num++) { + const unsigned int scrn_chan = g_eeprom.config.setting.indices.vfo[vfo_num].screen; const unsigned int line = (vfo_num == 0) ? line0 : line1; - unsigned int channel = g_eeprom.tx_vfo; -// unsigned int tx_channel = (g_eeprom.cross_vfo_rx_tx == CROSS_BAND_OFF) ? g_eeprom.rx_vfo : g_eeprom.tx_vfo; + unsigned int channel = g_eeprom.config.setting.tx_vfo_num; +// unsigned int tx_channel = (g_eeprom.config.setting.cross_vfo == CROSS_BAND_OFF) ? g_rx_vfo_num : g_eeprom.config.setting.tx_vfo_num; const bool same_vfo = (channel == vfo_num) ? true : false; uint8_t *p_line0 = g_frame_buffer[line + 0]; uint8_t *p_line1 = g_frame_buffer[line + 1]; @@ -476,8 +477,8 @@ void UI_DisplayMain(void) } - if (g_eeprom.dual_watch != DUAL_WATCH_OFF && g_rx_vfo_is_active) - channel = g_eeprom.rx_vfo; // we're currently monitoring the other VFO + if (g_eeprom.config.setting.dual_watch != DUAL_WATCH_OFF && g_rx_vfo_is_active) + channel = g_rx_vfo_num; // we're currently monitoring the other VFO if (channel != vfo_num) { @@ -547,7 +548,7 @@ void UI_DisplayMain(void) if (!single_vfo && same_vfo) memcpy(p_line0 + 0, BITMAP_VFO_DEFAULT, sizeof(BITMAP_VFO_DEFAULT)); else - if (g_eeprom.cross_vfo_rx_tx != CROSS_BAND_OFF) + if (g_eeprom.config.setting.cross_vfo != CROSS_BAND_OFF) memcpy(p_line0 + 0, BITMAP_VFO_NOT_DEFAULT, sizeof(BITMAP_VFO_NOT_DEFAULT)); } else @@ -556,7 +557,7 @@ void UI_DisplayMain(void) if (same_vfo) memcpy(p_line0 + 0, BITMAP_VFO_DEFAULT, sizeof(BITMAP_VFO_DEFAULT)); else - //if (g_eeprom.cross_vfo_rx_tx != CROSS_BAND_OFF) + //if (g_eeprom.config.setting.cross_vfo != CROSS_BAND_OFF) memcpy(p_line0 + 0, BITMAP_VFO_NOT_DEFAULT, sizeof(BITMAP_VFO_NOT_DEFAULT)); } @@ -569,7 +570,7 @@ void UI_DisplayMain(void) else #endif { - channel = (g_eeprom.cross_vfo_rx_tx == CROSS_BAND_OFF) ? g_eeprom.rx_vfo : g_eeprom.tx_vfo; + channel = (g_eeprom.config.setting.cross_vfo == CROSS_BAND_OFF) ? g_rx_vfo_num : g_eeprom.config.setting.tx_vfo_num; if (channel == vfo_num) { // show the TX symbol mode = 1; @@ -584,7 +585,7 @@ void UI_DisplayMain(void) else { // receiving .. show the RX symbol mode = 2; - if ((g_current_function == FUNCTION_RECEIVE && g_squelch_open) && g_eeprom.rx_vfo == vfo_num) + if ((g_current_function == FUNCTION_RECEIVE && g_squelch_open) && g_rx_vfo_num == vfo_num) { #ifdef ENABLE_SMALL_BOLD UI_PrintStringSmallBold("RX", 14, 0, line); @@ -594,32 +595,32 @@ void UI_DisplayMain(void) } } - if (g_eeprom.screen_channel[vfo_num] <= USER_CHANNEL_LAST) + if (scrn_chan <= USER_CHANNEL_LAST) { // channel mode const unsigned int x = 2; - const bool inputting = (g_input_box_index == 0 || g_eeprom.tx_vfo != vfo_num) ? false : true; + const bool inputting = (g_input_box_index == 0 || g_eeprom.config.setting.tx_vfo_num != vfo_num) ? false : true; if (!inputting) - NUMBER_ToDigits(g_eeprom.screen_channel[vfo_num] + 1, str); // show the memory channel number + NUMBER_ToDigits(scrn_chan + 1, str); // show the memory channel number else memcpy(str + 5, g_input_box, 3); // show the input text UI_PrintStringSmall("M", x, 0, line + 1); UI_Displaysmall_digits(3, str + 5, x + 7, line + 1, inputting); } else - if (IS_FREQ_CHANNEL(g_eeprom.screen_channel[vfo_num])) + if (IS_FREQ_CHANNEL(scrn_chan)) { // frequency mode // show the frequency band number const unsigned int x = 2; // was 14 -// sprintf(String, "FB%u", 1 + g_eeprom.screen_channel[vfo_num] - FREQ_CHANNEL_FIRST); - sprintf(str, "VFO%u", 1 + g_eeprom.screen_channel[vfo_num] - FREQ_CHANNEL_FIRST); +// sprintf(String, "FB%u", 1 + scrn_chan - FREQ_CHANNEL_FIRST); + sprintf(str, "VFO%u", 1 + scrn_chan - FREQ_CHANNEL_FIRST); UI_PrintStringSmall(str, x, 0, line + 1); } #ifdef ENABLE_NOAA else { - if (g_input_box_index == 0 || g_eeprom.tx_vfo != vfo_num) + if (g_input_box_index == 0 || g_eeprom.config.setting.tx_vfo_num != vfo_num) { // channel number - sprintf(str, "N%u", 1 + g_eeprom.screen_channel[vfo_num] - NOAA_CHANNEL_FIRST); + sprintf(str, "N%u", 1 + scrn_chan - NOAA_CHANNEL_FIRST); } else { // user entering channel number @@ -636,7 +637,7 @@ void UI_DisplayMain(void) #ifdef ENABLE_ALARM if (g_current_function == FUNCTION_TRANSMIT && g_alarm_state == ALARM_STATE_ALARM) { - channel = (g_eeprom.cross_vfo_rx_tx == CROSS_BAND_OFF) ? g_eeprom.rx_vfo : g_eeprom.tx_vfo; + channel = (g_eeprom.config.setting.cross_vfo == CROSS_BAND_OFF) ? g_rx_vfo_num : g_eeprom.config.setting.tx_vfo_num; if (channel == vfo_num) state = VFO_STATE_ALARM; } @@ -649,7 +650,7 @@ void UI_DisplayMain(void) UI_PrintString(state_list[state], 31, 0, line, 8); } else - if (g_input_box_index > 0 && IS_FREQ_CHANNEL(g_eeprom.screen_channel[vfo_num]) && g_eeprom.tx_vfo == vfo_num) + if (g_input_box_index > 0 && IS_FREQ_CHANNEL(scrn_chan) && g_eeprom.config.setting.tx_vfo_num == vfo_num) { // user entering a frequency UI_DisplayFrequency(g_input_box, 32, line, true, false); @@ -659,18 +660,18 @@ void UI_DisplayMain(void) { const unsigned int x = 32; - uint32_t frequency = g_eeprom.vfo_info[vfo_num].p_rx->frequency; + uint32_t frequency = g_vfo_info[vfo_num].p_rx->frequency; if (g_current_function == FUNCTION_TRANSMIT) { // transmitting - channel = (g_eeprom.cross_vfo_rx_tx == CROSS_BAND_OFF) ? g_eeprom.rx_vfo : g_eeprom.tx_vfo; + channel = (g_eeprom.config.setting.cross_vfo == CROSS_BAND_OFF) ? g_rx_vfo_num : g_eeprom.config.setting.tx_vfo_num; if (channel == vfo_num) - frequency = g_eeprom.vfo_info[vfo_num].p_tx->frequency; + frequency = g_vfo_info[vfo_num].p_tx->frequency; } - if (g_eeprom.screen_channel[vfo_num] <= USER_CHANNEL_LAST) + if (scrn_chan <= USER_CHANNEL_LAST) { // it's a channel - switch (g_eeprom.channel_display_mode) + switch (g_eeprom.config.setting.channel_display_mode) { case MDF_FREQUENCY: // just channel frequency @@ -689,7 +690,7 @@ void UI_DisplayMain(void) case MDF_CHANNEL: // just channel number - sprintf(str, "CH-%03u", g_eeprom.screen_channel[vfo_num] + 1); + sprintf(str, "CH-%03u", scrn_chan + 1); UI_PrintString(str, x, 0, line, 8); break; @@ -697,13 +698,13 @@ void UI_DisplayMain(void) case MDF_NAME: // channel name case MDF_NAME_FREQ: // channel name and frequency - BOARD_fetchChannelName(str, g_eeprom.screen_channel[vfo_num]); + SETTINGS_fetch_channel_name(str, scrn_chan); if (str[0] == 0) { // no channel name available, channel number instead - sprintf(str, "CH-%03u", 1 + g_eeprom.screen_channel[vfo_num]); + sprintf(str, "CH-%03u", 1 + scrn_chan); } - if (g_eeprom.channel_display_mode == MDF_NAME) + if (g_eeprom.config.setting.channel_display_mode == MDF_NAME) { // just the name UI_PrintString(str, x + 4, 0, line, 8); } @@ -729,7 +730,7 @@ void UI_DisplayMain(void) } } else -// if (IS_FREQ_CHANNEL(g_eeprom.screen_channel[vfo_num])) +// if (IS_FREQ_CHANNEL(scrn_chan)) { // frequency mode #ifdef ENABLE_BIG_FREQ big_freq(frequency, x, line); @@ -747,19 +748,19 @@ void UI_DisplayMain(void) // show channel symbols - if (g_eeprom.screen_channel[vfo_num] <= USER_CHANNEL_LAST) - //if (IS_NOT_NOAA_CHANNEL(g_eeprom.screen_channel[vfo_num])) + if (scrn_chan <= USER_CHANNEL_LAST) + //if (IS_NOT_NOAA_CHANNEL(scrn_chan)) { // it's a user channel or VFO unsigned int x = LCD_WIDTH - 1 - sizeof(BITMAP_SCANLIST2) - sizeof(BITMAP_SCANLIST1); - const uint8_t attributes = g_user_channel_attributes[g_eeprom.screen_channel[vfo_num]]; + const t_channel_attrib attributes = g_user_channel_attributes[scrn_chan]; - if (attributes & USER_CH_SCANLIST1) + if (attributes.scanlist1) memcpy(p_line0 + x, BITMAP_SCANLIST1, sizeof(BITMAP_SCANLIST1)); x += sizeof(BITMAP_SCANLIST1); - if (attributes & USER_CH_SCANLIST2) + if (attributes.scanlist2) memcpy(p_line0 + x, BITMAP_SCANLIST2, sizeof(BITMAP_SCANLIST2)); //x += sizeof(BITMAP_SCANLIST2); } @@ -772,35 +773,35 @@ void UI_DisplayMain(void) { unsigned int x = LCD_WIDTH + LCD_WIDTH - 1 - (smallest_char_spacing * 1) - (smallest_char_spacing * 4); - if (IS_FREQ_CHANNEL(g_eeprom.screen_channel[vfo_num])) + if (IS_FREQ_CHANNEL(scrn_chan)) { - //g_eeprom.vfo_info[vfo_num].freq_in_channel = BOARD_find_channel(frequency); - if (g_eeprom.vfo_info[vfo_num].freq_in_channel <= USER_CHANNEL_LAST) + //g_vfo_info[vfo_num].freq_in_channel = SETTINGS_find_channel(frequency); + if (g_vfo_info[vfo_num].freq_in_channel <= USER_CHANNEL_LAST) { // the channel number that contains this VFO frequency - sprintf(str, "%03u", 1 + g_eeprom.vfo_info[vfo_num].freq_in_channel); + sprintf(str, "%03u", 1 + g_vfo_info[vfo_num].freq_in_channel); UI_PrintStringSmallest(str, x, (line + 0) * 8, false, true); } } x += smallest_char_spacing * 4; - if (g_eeprom.vfo_info[vfo_num].compand) + if (g_vfo_info[vfo_num].compand) UI_PrintStringSmallest("C", x, (line + 0) * 8, false, true); //x += smallest_char_spacing * 1; } #else { - const bool is_freq_chan = IS_FREQ_CHANNEL(g_eeprom.screen_channel[vfo_num]); - const uint8_t freq_in_channel = g_eeprom.vfo_info[vfo_num].freq_in_channel; -// const uint8_t freq_in_channel = BOARD_find_channel(frequency); // currently way to slow + const bool is_freq_chan = IS_FREQ_CHANNEL(scrn_chan); + const uint8_t freq_in_channel = g_vfo_info[vfo_num].freq_in_channel; +// const uint8_t freq_in_channel = SETTINGS_find_channel(frequency); // currently way to slow - if (g_eeprom.vfo_info[vfo_num].compand) + if (g_vfo_info[vfo_num].compand) { strcpy(str, " "); if (is_freq_chan && freq_in_channel <= USER_CHANNEL_LAST) str[0] = 'F'; // channel number that contains this VFO frequency - if (g_eeprom.vfo_info[vfo_num].compand) + if (g_vfo_info[vfo_num].compand) str[1] = 'C'; // compander is enabled UI_PrintStringSmall(str, LCD_WIDTH - (7 * 2), 0, line + 1); @@ -844,10 +845,10 @@ void UI_DisplayMain(void) // ************ str[0] = '\0'; - if (g_eeprom.vfo_info[vfo_num].am_mode > 0) + if (g_vfo_info[vfo_num].am_mode > 0) { - //strcpy(str, g_sub_menu_mod_mode[g_eeprom.vfo_info[vfo_num].am_mode]); - switch (g_eeprom.vfo_info[vfo_num].am_mode) + //strcpy(str, g_sub_menu_mod_mode[g_vfo_info[vfo_num].am_mode]); + switch (g_vfo_info[vfo_num].am_mode) { default: case 0: strcpy(str, "FM"); break; @@ -857,7 +858,7 @@ void UI_DisplayMain(void) } else { // or show the CTCSS/DCS symbol - const freq_config_t *pConfig = (mode == 1) ? g_eeprom.vfo_info[vfo_num].p_tx : g_eeprom.vfo_info[vfo_num].p_rx; + const freq_config_t *pConfig = (mode == 1) ? g_vfo_info[vfo_num].p_tx : g_vfo_info[vfo_num].p_rx; const unsigned int code_type = pConfig->code_type; const char *code_list[] = {"", "CT", "DCS", "DCR"}; if (code_type < ARRAY_SIZE(code_list)) @@ -868,22 +869,22 @@ void UI_DisplayMain(void) #ifdef ENABLE_TX_WHEN_AM if (state == VFO_STATE_NORMAL || state == VFO_STATE_ALARM) #else - if ((state == VFO_STATE_NORMAL || state == VFO_STATE_ALARM) && g_eeprom.vfo_info[vfo_num].am_mode == 0) // not allowed to TX if not in FM mode + if ((state == VFO_STATE_NORMAL || state == VFO_STATE_ALARM) && g_vfo_info[vfo_num].am_mode == 0) // not allowed to TX if not in FM mode #endif { - if (FREQUENCY_tx_freq_check(g_eeprom.vfo_info[vfo_num].p_tx->frequency) == 0) + if (FREQUENCY_tx_freq_check(g_vfo_info[vfo_num].p_tx->frequency) == 0) { // show the TX power const char pwr_list[] = "LMH"; - const unsigned int i = g_eeprom.vfo_info[vfo_num].output_power; + const unsigned int i = g_vfo_info[vfo_num].output_power; str[0] = (i < ARRAY_SIZE(pwr_list)) ? pwr_list[i] : '\0'; str[1] = '\0'; UI_PrintStringSmall(str, LCD_WIDTH + 46, 0, line + 1); - if (g_eeprom.vfo_info[vfo_num].freq_config_rx.frequency != g_eeprom.vfo_info[vfo_num].freq_config_tx.frequency) + if (g_vfo_info[vfo_num].freq_config_rx.frequency != g_vfo_info[vfo_num].freq_config_tx.frequency) { // show the TX offset symbol const char dir_list[] = "\0+-"; - const unsigned int i = g_eeprom.vfo_info[vfo_num].tx_offset_freq_dir; + const unsigned int i = g_vfo_info[vfo_num].tx_offset_freq_dir; str[0] = (i < sizeof(dir_list)) ? dir_list[i] : '?'; str[1] = '\0'; UI_PrintStringSmall(str, LCD_WIDTH + 54, 0, line + 1); @@ -892,12 +893,12 @@ void UI_DisplayMain(void) } // show the TX/RX reverse symbol - if (g_eeprom.vfo_info[vfo_num].frequency_reverse) + if (g_vfo_info[vfo_num].frequency_reverse) UI_PrintStringSmall("R", LCD_WIDTH + 62, 0, line + 1); { // show the narrow band symbol str[0] = '\0'; - if (g_eeprom.vfo_info[vfo_num].channel_bandwidth == BANDWIDTH_NARROW) + if (g_vfo_info[vfo_num].channel_bandwidth == BANDWIDTH_NARROW) { str[0] = 'N'; str[1] = '\0'; @@ -907,17 +908,17 @@ void UI_DisplayMain(void) // show the DTMF decoding symbol #ifdef ENABLE_KILL_REVIVE - if (g_eeprom.vfo_info[vfo_num].dtmf_decoding_enable || g_setting_radio_disabled) + if (g_vfo_info[vfo_num].dtmf_decoding_enable || g_eeprom.config.setting.radio_disabled) UI_PrintStringSmall("DTMF", LCD_WIDTH + 78, 0, line + 1); #else - if (g_eeprom.vfo_info[vfo_num].dtmf_decoding_enable) + if (g_vfo_info[vfo_num].dtmf_decoding_enable) UI_PrintStringSmall("DTMF", LCD_WIDTH + 78, 0, line + 1); //UI_PrintStringSmall4x5("DTMF", LCD_WIDTH + 78, 0, line + 1); // font table is currently wrong //UI_PrintStringSmallest("DTMF", LCD_WIDTH + 78, (line + 1) * 8, false, true); #endif // show the audio scramble symbol - if (g_eeprom.vfo_info[vfo_num].scrambling_type > 0 && g_setting_scramble_enable) + if (g_vfo_info[vfo_num].scrambling_type > 0 && g_eeprom.config.setting.enable_scrambler) UI_PrintStringSmall("SCR", LCD_WIDTH + 106, 0, line + 1); } @@ -966,10 +967,10 @@ void UI_DisplayMain(void) #if defined(ENABLE_AM_FIX) && defined(ENABLE_AM_FIX_SHOW_DATA) // show the AM-FIX debug data - if (rx && g_eeprom.vfo_info[g_eeprom.rx_vfo].am_mode > 0 && g_setting_am_fix) + if (rx && g_vfo_info[g_rx_vfo_num].am_mode > 0 && g_eeprom.config.setting.am_fix) { g_center_line = CENTER_LINE_AM_FIX_DATA; - AM_fix_print_data(g_eeprom.rx_vfo, str); + AM_fix_print_data(g_rx_vfo_num, str); UI_PrintStringSmall(str, 2, 0, 3); } else @@ -977,10 +978,10 @@ void UI_DisplayMain(void) #ifdef ENABLE_RX_SIGNAL_BAR // show the RX RSSI dBm, S-point and signal strength bar graph - if (rx && g_setting_rssi_bar) + if (rx && g_eeprom.config.setting.enable_rssi_bar) { g_center_line = CENTER_LINE_RSSI; - UI_DisplayRSSIBar(g_current_rssi[g_eeprom.rx_vfo], false); + UI_DisplayRSSIBar(g_current_rssi[g_rx_vfo_num], false); } else #endif @@ -988,7 +989,7 @@ void UI_DisplayMain(void) if (rx || g_current_function == FUNCTION_FOREGROUND || g_current_function == FUNCTION_POWER_SAVE) { #if 1 - if (g_setting_live_dtmf_decoder && g_dtmf_rx_live[0] != 0) + if (g_eeprom.config.setting.dtmf_live_decoder && g_dtmf_rx_live[0] != 0) { // show live DTMF decode const unsigned int len = strlen(g_dtmf_rx_live); const unsigned int idx = (len > (17 - 5)) ? len - (17 - 5) : 0; // limit to last 'n' chars @@ -1003,7 +1004,7 @@ void UI_DisplayMain(void) UI_PrintStringSmall(str, 2, 0, 3); } #else - if (g_setting_live_dtmf_decoder && g_dtmf_rx_index > 0) + if (g_eeprom.config.setting.dtmf_live_decoder && g_dtmf_rx_index > 0) { // show live DTMF decode const unsigned int len = g_dtmf_rx_index; const unsigned int idx = (len > (17 - 5)) ? len - (17 - 5) : 0; // limit to last 'n' chars diff --git a/ui/menu.c b/ui/menu.c index 2834d00..ab1547f 100644 --- a/ui/menu.c +++ b/ui/menu.c @@ -21,7 +21,7 @@ #include "dcs.h" #include "driver/backlight.h" #include "driver/bk4819.h" -#include "driver/eeprom.h" // EEPROM_ReadBuffer() +#include "driver/eeprom.h" #include "driver/st7565.h" #if defined(ENABLE_UART) && defined(ENABLE_UART_DEBUG) #include "driver/uart.h" @@ -283,12 +283,12 @@ const char g_sub_menu_dtmf_rsp[4][9] = "RNG RPLY" }; -const char g_sub_menu_ptt_id[5][15] = +const char g_sub_menu_ptt_id[5][16] = { - "OFF", - "BOT", - "EOT", - "BOT+EOT", + "DTMF ID\nOFF", + "DTMF ID\nBOT", + "DTMF ID\nEOT", + "DTMF ID\nBOT+EOT", "APOLLO\nQUINDAR" }; @@ -469,9 +469,10 @@ void UI_SortMenu(const bool hide_hidden) void UI_DisplayMenu(void) { - const unsigned int menu_list_width = 6; // max no. of characters on the menu list (left side) + const unsigned int menu_list_width = 6; // max no. of characters on the menu list (left side) const unsigned int sub_menu_x1 = (8 * menu_list_width) + 2; // start x corrd const unsigned int sub_menu_x2 = LCD_WIDTH - 1; // end x coord + bool channel_setting = false; // set if the setting is a channel setting unsigned int i; char str[64]; // bigger cuz we can now do multi-line in one string (use '\n' char) @@ -573,6 +574,7 @@ void UI_DisplayMenu(void) sprintf(str, "%d", g_sub_menu_selection); // g_tx_vfo->squelch_level = g_sub_menu_selection; // RADIO_ConfigureSquelchAndOutputPower(g_tx_vfo); + channel_setting = true; break; case MENU_MIC_GAIN: @@ -596,11 +598,13 @@ void UI_DisplayMenu(void) NUMBER_trim_trailing_zeros(str); strcat(str, "kHz"); } + channel_setting = true; break; } case MENU_TX_POWER: strcpy(str, g_sub_menu_tx_power[g_sub_menu_selection]); + channel_setting = true; break; case MENU_RX_CDCSS: @@ -613,11 +617,13 @@ void UI_DisplayMenu(void) sprintf(str + strlen(str), "D%03oN", DCS_CODE_LIST[g_sub_menu_selection - 1]); else sprintf(str + strlen(str), "D%03oI", DCS_CODE_LIST[g_sub_menu_selection - 105]); + channel_setting = true; break; case MENU_RX_CTCSS: case MENU_TX_CTCSS: { + channel_setting = true; strcpy(str, "CTCSS\n"); #if 1 // set CTCSS as the user adjusts it @@ -658,9 +664,11 @@ void UI_DisplayMenu(void) case MENU_SHIFT_DIR: strcpy(str, g_sub_menu_shift_dir[g_sub_menu_selection]); + channel_setting = true; break; case MENU_OFFSET: + channel_setting = true; if (!g_in_sub_menu || g_input_box_index == 0) { sprintf(str, "%d.%05u", g_sub_menu_selection / 100000, abs(g_sub_menu_selection) % 100000); @@ -692,6 +700,7 @@ void UI_DisplayMenu(void) case MENU_BANDWIDTH: strcpy(str, g_sub_menu_bandwidth[g_sub_menu_selection]); + channel_setting = true; break; case MENU_SCRAMBLER: @@ -699,11 +708,12 @@ void UI_DisplayMenu(void) strcat(str, g_sub_menu_scrambler[g_sub_menu_selection]); #if 1 - if (g_sub_menu_selection > 0 && g_setting_scramble_enable) + if (g_sub_menu_selection > 0 && g_eeprom.config.setting.enable_scrambler) BK4819_EnableScramble(g_sub_menu_selection - 1); else BK4819_DisableScramble(); #endif + channel_setting = true; break; #ifdef ENABLE_VOX @@ -728,12 +738,13 @@ void UI_DisplayMenu(void) case MENU_MOD_MODE: // strcpy(str, (g_sub_menu_selection == 0) ? "FM" : "AM"); strcpy(str, g_sub_menu_mod_mode[g_sub_menu_selection]); + channel_setting = true; break; #ifdef ENABLE_AM_FIX_TEST1 case MENU_AM_FIX_TEST1: strcpy(str, g_sub_menu_AM_FIX_test1[g_sub_menu_selection]); -// g_setting_am_fix = g_sub_menu_selection; +// g_eeprom.config.setting.am_fix = g_sub_menu_selection; break; #endif @@ -748,6 +759,7 @@ void UI_DisplayMenu(void) case MENU_COMPAND: strcpy(str, g_sub_menu_rx_tx[g_sub_menu_selection]); + channel_setting = true; break; #ifdef ENABLE_CONTRAST @@ -772,6 +784,7 @@ void UI_DisplayMenu(void) case MENU_S_ADD1: case MENU_S_ADD2: strcpy(str, g_sub_menu_off_on[g_sub_menu_selection]); + channel_setting = true; break; case MENU_BUSY_CHAN_LOCK: @@ -830,6 +843,7 @@ void UI_DisplayMenu(void) case MENU_SCRAMBLER_EN: strcpy(str, "SCRAMBLER\n"); strcat(str, g_sub_menu_dis_en[g_sub_menu_selection]); + channel_setting = true; break; case MENU_TX_EN: @@ -847,13 +861,13 @@ void UI_DisplayMenu(void) UI_GenerateChannelStringEx(str, valid ? "CH-" : "", g_sub_menu_selection); // channel name - BOARD_fetchChannelName(s, g_sub_menu_selection); + SETTINGS_fetch_channel_name(s, g_sub_menu_selection); strcat(str, "\n"); strcat(str, (s[0] == 0) ? "--" : s); if (valid && !g_ask_for_confirmation) { // show the frequency so that the user knows the channels frequency - const uint32_t frequency = BOARD_fetchChannelFrequency(g_sub_menu_selection); + const uint32_t frequency = SETTINGS_fetch_channel_frequency(g_sub_menu_selection); sprintf(str + strlen(str), "\n%u.%05u", frequency / 100000, frequency % 100000); #ifdef ENABLE_TRIM_TRAILING_ZEROS @@ -861,6 +875,7 @@ void UI_DisplayMenu(void) #endif } + channel_setting = (g_menu_cursor != MENU_1_CALL) ? true : false; break; } @@ -874,11 +889,11 @@ void UI_DisplayMenu(void) if (valid) { - const uint32_t frequency = BOARD_fetchChannelFrequency(g_sub_menu_selection); + const uint32_t frequency = SETTINGS_fetch_channel_frequency(g_sub_menu_selection); if (!g_in_sub_menu || g_edit_index < 0) { // show the channel name - BOARD_fetchChannelName(str, g_sub_menu_selection); + SETTINGS_fetch_channel_name(str, g_sub_menu_selection); if (str[0] == 0) strcpy(str, "--"); UI_PrintString(str, sub_menu_x1, sub_menu_x2, y + 2, 8); @@ -901,6 +916,7 @@ void UI_DisplayMenu(void) } already_printed = true; + channel_setting = true; break; } @@ -931,7 +947,7 @@ void UI_DisplayMenu(void) strcpy(str, "SCAN\nRESUME\n"); strcat(str, g_sub_menu_scan_car_resume[g_sub_menu_selection]); if (g_sub_menu_selection == SCAN_RESUME_TIME) - sprintf(str + strlen(str), "%d.%ds", g_eeprom.scan_hold_time_500ms / 2, 5 * (g_eeprom.scan_hold_time_500ms % 2)); + sprintf(str + strlen(str), "%d.%ds", g_eeprom.config.setting.scan_hold_time / 2, 5 * (g_eeprom.config.setting.scan_hold_time % 2)); break; case MENU_SCAN_HOLD: @@ -951,10 +967,11 @@ void UI_DisplayMenu(void) break; case MENU_S_LIST: + strcpy(str, "SCAN LIST\n"); if (g_sub_menu_selection < 2) - sprintf(str, "LIST%u", 1 + g_sub_menu_selection); + sprintf(str + strlen(str), "LIST%u", 1 + g_sub_menu_selection); else - strcpy(str, "ALL"); + strcat(str, "ALL"); break; #ifdef ENABLE_ALARM @@ -966,17 +983,17 @@ void UI_DisplayMenu(void) case MENU_ANI_ID: strcpy(str, "YOUR ID\n"); - strcat(str, g_eeprom.ani_dtmf_id); + strcat(str, g_eeprom.config.setting.dtmf.ani_id); break; case MENU_UP_CODE: - strcpy(str, "PTT DTMF\nBEGIN\n"); - strcat(str, g_eeprom.dtmf_key_up_code); + strcpy(str, "DTMF BOT\n"); + strcat(str, g_eeprom.config.setting.dtmf.key_up_code); break; case MENU_DN_CODE: - strcpy(str, "PTT DTMF\nEND\n"); - strcat(str, g_eeprom.dtmf_key_down_code); + strcpy(str, "DTMF EOT\n"); + strcat(str, g_eeprom.config.setting.dtmf.key_down_code); break; case MENU_DTMF_RSP: @@ -1013,7 +1030,7 @@ void UI_DisplayMenu(void) break; case MENU_DTMF_PRE: - strcpy(str, "TX DTMF\nDELAY\n"); + strcpy(str, "DTMF BOT\nDELAY\n"); // sprintf(String + strlen(String), "%d*10ms", g_sub_menu_selection); sprintf(str + strlen(str), "%dms", 10 * g_sub_menu_selection); break; @@ -1022,6 +1039,7 @@ void UI_DisplayMenu(void) case MENU_MDC1200_MODE: strcpy(str, "MDC1200\nMODE\n"); strcat(str, g_sub_menu_mdc1200_mode[g_sub_menu_selection]); + channel_setting = true; break; case MENU_MDC1200_ID: @@ -1030,8 +1048,8 @@ void UI_DisplayMenu(void) #endif case MENU_PTT_ID: - strcpy(str, (g_sub_menu_selection > 0) ? "TX ID\n" : ""); - strcat(str, g_sub_menu_ptt_id[g_sub_menu_selection]); + strcpy(str, g_sub_menu_ptt_id[g_sub_menu_selection]); + channel_setting = true; break; case MENU_BAT_TXT: @@ -1178,11 +1196,11 @@ void UI_DisplayMenu(void) case MENU_BAT_CAL: { - const uint16_t vol = (uint32_t)g_battery_voltage_average * g_battery_calibration[3] / g_sub_menu_selection; + const uint16_t vol = (uint32_t)g_battery_voltage_average * g_eeprom.calib.battery[3] / g_sub_menu_selection; if (!g_in_sub_menu) sprintf(str, "%u.%02uV\n%d", vol / 100, vol % 100, g_sub_menu_selection); else - sprintf(str, "%u.%02uV\n(%#4d)\n%#4d", vol / 100, vol % 100, g_battery_calibration[3], g_sub_menu_selection); + sprintf(str, "%u.%02uV\n(%#4d)\n%#4d", vol / 100, vol % 100, g_eeprom.calib.battery[3], g_sub_menu_selection); break; } } @@ -1251,14 +1269,14 @@ void UI_DisplayMenu(void) else UI_GenerateChannelStringEx(str, "CH-", g_sub_menu_selection); -// if (g_sub_menu_selection == 0xFF || !g_eeprom.scan_list_enabled[i]) - if (g_sub_menu_selection < 0 || !g_eeprom.scan_list_enabled[i]) +// if (g_sub_menu_selection == 0xFF || g_eeprom.config.setting.priority_scan_list[i].enabled == 0) + if (g_sub_menu_selection < 0 || g_eeprom.config.setting.priority_scan_list[i].enabled == 0) { // channel number UI_PrintString(str, sub_menu_x1, sub_menu_x2, 0, 8); // channel name - BOARD_fetchChannelName(str, g_sub_menu_selection); + SETTINGS_fetch_channel_name(str, g_sub_menu_selection); if (str[0] == 0) strcpy(str, "--"); UI_PrintString(str, sub_menu_x1, sub_menu_x2, 2, 8); @@ -1270,20 +1288,20 @@ void UI_DisplayMenu(void) // channel name memset(str, 0, sizeof(str)); - BOARD_fetchChannelName(str, g_sub_menu_selection); + SETTINGS_fetch_channel_name(str, g_sub_menu_selection); if (str[0] == 0) strcpy(str, "--"); UI_PrintStringSmall(str, sub_menu_x1, sub_menu_x2, 2); - if (IS_USER_CHANNEL(g_eeprom.scan_list_priority_ch1[i])) + if (IS_USER_CHANNEL(g_eeprom.config.setting.priority_scan_list[i].channel[0])) { - sprintf(str, "PRI1:%u", g_eeprom.scan_list_priority_ch1[i] + 1); + sprintf(str, "PRI1:%u", 1 + g_eeprom.config.setting.priority_scan_list[i].channel[0]); UI_PrintString(str, sub_menu_x1, sub_menu_x2, 3, 8); } - if (IS_USER_CHANNEL(g_eeprom.scan_list_priority_ch2[i])) + if (IS_USER_CHANNEL(g_eeprom.config.setting.priority_scan_list[i].channel[1])) { - sprintf(str, "PRI2:%u", g_eeprom.scan_list_priority_ch2[i] + 1); + sprintf(str, "PRI2:%u", 1 + g_eeprom.config.setting.priority_scan_list[i].channel[1]); UI_PrintString(str, sub_menu_x1, sub_menu_x2, 5, 8); } } @@ -1293,12 +1311,12 @@ void UI_DisplayMenu(void) UI_PrintString("SCAN", sub_menu_x1, sub_menu_x2, 4, 8); if (g_menu_cursor == MENU_UP_CODE) - if (strlen(g_eeprom.dtmf_key_up_code) > 8) - UI_PrintString(g_eeprom.dtmf_key_up_code + 8, sub_menu_x1, sub_menu_x2, 4, 8); + if (strlen(g_eeprom.config.setting.dtmf.key_up_code) > 8) + UI_PrintString(g_eeprom.config.setting.dtmf.key_up_code + 8, sub_menu_x1, sub_menu_x2, 4, 8); if (g_menu_cursor == MENU_DN_CODE) - if (strlen(g_eeprom.dtmf_key_down_code) > 8) - UI_PrintString(g_eeprom.dtmf_key_down_code + 8, sub_menu_x1, sub_menu_x2, 4, 8); + if (strlen(g_eeprom.config.setting.dtmf.key_down_code) > 8) + UI_PrintString(g_eeprom.config.setting.dtmf.key_down_code + 8, sub_menu_x1, sub_menu_x2, 4, 8); if (g_menu_cursor == MENU_RX_CTCSS || g_menu_cursor == MENU_TX_CTCSS || @@ -1324,5 +1342,8 @@ void UI_DisplayMenu(void) UI_PrintString(str, sub_menu_x1, sub_menu_x2, 5, 8); } + if (channel_setting) + UI_PrintStringSmall("ch", sub_menu_x1, 0, 0); + ST7565_BlitFullScreen(); } diff --git a/ui/menu.h b/ui/menu.h index 7bd43fd..3debb9d 100644 --- a/ui/menu.h +++ b/ui/menu.h @@ -180,7 +180,7 @@ extern const char g_sub_menu_mem_disp[4][15]; extern const char g_sub_menu_alarm_mode[2][5]; #endif extern const char g_sub_menu_dtmf_rsp[4][9]; -extern const char g_sub_menu_ptt_id[5][15]; +extern const char g_sub_menu_ptt_id[5][16]; #ifdef ENABLE_MDC1200 extern const char g_sub_menu_mdc1200_mode[4][8]; #endif diff --git a/ui/search.c b/ui/search.c index 142a825..7954f15 100644 --- a/ui/search.c +++ b/ui/search.c @@ -22,6 +22,7 @@ #include "frequencies.h" #include "misc.h" #include "radio.h" +#include "settings.h" #include "ui/helper.h" #include "ui/search.h" #include "ui/ui.h" @@ -161,7 +162,7 @@ void UI_DisplaySearch(void) strcpy(String, "SAVE "); { char s[11]; - BOARD_fetchChannelName(s, g_search_channel); + SETTINGS_fetch_channel_name(s, g_search_channel); if (s[0] == 0) UI_GenerateChannelStringEx(s, g_search_show_chan_prefix ? "CH-" : "", g_search_channel); strcat(String, s); diff --git a/ui/status.c b/ui/status.c index c498f35..d3ba621 100644 --- a/ui/status.c +++ b/ui/status.c @@ -37,9 +37,9 @@ void UI_DisplayStatus(const bool test_display) uint8_t *line = g_status_line; unsigned int x = 0; unsigned int x1 = 0; - + g_update_status = false; - + memset(g_status_line, 0, sizeof(g_status_line)); // ************** @@ -65,78 +65,79 @@ void UI_DisplayStatus(const bool test_display) x1 = x + sizeof(BITMAP_POWERSAVE); } x += sizeof(BITMAP_POWERSAVE); + x++; #ifdef ENABLE_NOAA - // NOASS SCAN indicator - if (g_is_noaa_mode || test_display) + // NOAA scan indicator + if (g_noaa_mode || test_display) { memcpy(line + x, BITMAP_NOAA, sizeof(BITMAP_NOAA)); x1 = x + sizeof(BITMAP_NOAA); + x += sizeof(BITMAP_NOAA); } - x += sizeof(BITMAP_NOAA); - #else - // hmmm, what to put in it's place + x++; #endif - + #ifdef ENABLE_KILL_REVIVE - if (g_setting_radio_disabled) + if (g_eeprom.config.setting.radio_disabled) { memset(line + x, 0xFF, 10); x1 = x + 10; + x += 10; } - else #endif - { + #ifdef ENABLE_FMRADIO // FM indicator if (g_fm_radio_mode || test_display) { memcpy(line + x, BITMAP_FM, sizeof(BITMAP_FM)); x1 = x + sizeof(BITMAP_FM); + x += sizeof(BITMAP_FM); } - else + x++; #endif - // SCAN indicator - if (g_scan_state_dir != SCAN_STATE_DIR_OFF || test_display) + + // SCAN indicator + if (g_scan_state_dir != SCAN_STATE_DIR_OFF || test_display) + { + // don't display this if in search mode + if (g_current_display_screen != DISPLAY_SEARCH) { - // don't display this if in search mode - if (g_current_display_screen != DISPLAY_SEARCH) - { - if (g_scan_next_channel <= USER_CHANNEL_LAST) - { // channel mode - if (g_eeprom.scan_list_default == 0) - UI_PrintStringSmallBuffer("1", line + x); - else - if (g_eeprom.scan_list_default == 1) - UI_PrintStringSmallBuffer("2", line + x); - else - if (g_eeprom.scan_list_default == 2) - UI_PrintStringSmallBuffer("*", line + x); - } + if (g_scan_next_channel <= USER_CHANNEL_LAST) + { // channel mode + if (g_eeprom.config.setting.scan_list_default == 0) + UI_PrintStringSmallBuffer("1", line + x); else - { // frequency mode - UI_PrintStringSmallBuffer("S", line + x); - } - x1 = x + 7; + if (g_eeprom.config.setting.scan_list_default == 1) + UI_PrintStringSmallBuffer("2", line + x); + else + if (g_eeprom.config.setting.scan_list_default == 2) + UI_PrintStringSmallBuffer("*", line + x); } + else + { // frequency mode + UI_PrintStringSmallBuffer("S", line + x); + } + x1 = x + 7; } + x += 7; // font character width } - x += 7; // font character width + x++; #ifdef ENABLE_VOICE // VOICE indicator - if (g_eeprom.voice_prompt != VOICE_PROMPT_OFF || test_display) + if (g_eeprom.config.setting.voice_prompt != VOICE_PROMPT_OFF || test_display) { memcpy(line + x, BITMAP_VOICE_PROMPT, sizeof(BITMAP_VOICE_PROMPT)); x1 = x + sizeof(BITMAP_VOICE_PROMPT); + x += sizeof(BITMAP_VOICE_PROMPT); } - x += sizeof(BITMAP_VOICE_PROMPT); - #else - // hmmm, what to put in it's place + x++; #endif // DUAL-WATCH indicator - if (g_eeprom.dual_watch != DUAL_WATCH_OFF || test_display) + if (g_eeprom.config.setting.dual_watch != DUAL_WATCH_OFF || test_display) { if (g_dual_watch_tick_10ms > dual_watch_delay_toggle_10ms || g_dtmf_call_state != DTMF_CALL_STATE_NONE || @@ -152,42 +153,47 @@ void UI_DisplayStatus(const bool test_display) memcpy(line + x, BITMAP_TDR_RUNNING, sizeof(BITMAP_TDR_RUNNING)); } x1 = x + sizeof(BITMAP_TDR_RUNNING); + x += sizeof(BITMAP_TDR_RUNNING); } - x += sizeof(BITMAP_TDR_RUNNING); + x++; // monitor if (g_monitor_enabled) { memcpy(line + x, BITMAP_MONITOR, sizeof(BITMAP_MONITOR)); x1 = x + sizeof(BITMAP_MONITOR); + x += sizeof(BITMAP_MONITOR); } - x += sizeof(BITMAP_MONITOR); + x++; // CROSS-VFO indicator - if (g_eeprom.cross_vfo_rx_tx != CROSS_BAND_OFF || test_display) + if (g_eeprom.config.setting.cross_vfo != CROSS_BAND_OFF || test_display) { memcpy(line + x, BITMAP_XB, sizeof(BITMAP_XB)); x1 = x + sizeof(BITMAP_XB); + x += sizeof(BITMAP_XB); } - x += sizeof(BITMAP_XB); - + x++; + #ifdef ENABLE_VOX // VOX indicator - if (g_eeprom.vox_switch || test_display) + if (g_eeprom.config.setting.vox_switch || test_display) { memcpy(line + x, BITMAP_VOX, sizeof(BITMAP_VOX)); x1 = x + sizeof(BITMAP_VOX); + x += sizeof(BITMAP_VOX); } - x += sizeof(BITMAP_VOX); + x++; #endif #ifdef ENABLE_KEYLOCK // KEY-LOCK indicator - if (g_eeprom.key_lock || test_display) + if (g_eeprom.config.setting.key_lock || test_display) { memcpy(line + x, BITMAP_KEYLOCK, sizeof(BITMAP_KEYLOCK)); x += sizeof(BITMAP_KEYLOCK); x1 = x; + x++; } else #endif @@ -197,22 +203,23 @@ void UI_DisplayStatus(const bool test_display) x += sizeof(BITMAP_F_KEY); x1 = x; } + x++; { // battery voltage or percentage text char s[8]; unsigned int space_needed; - + unsigned int x2 = LCD_WIDTH - sizeof(BITMAP_BATTERY_LEVEL) - 3; if (g_charging_with_type_c) x2 -= sizeof(BITMAP_USB_C); // the radio is on USB charge - switch (g_setting_battery_text) + switch (g_eeprom.config.setting.battery_text) { default: case 0: break; - + case 1: // voltage { const uint16_t voltage = (g_battery_voltage_average <= 999) ? g_battery_voltage_average : 999; // limit to 9.99V @@ -222,7 +229,7 @@ void UI_DisplayStatus(const bool test_display) UI_PrintStringSmallBuffer(s, line + x2 - space_needed); break; } - + case 2: // percentage { sprintf(s, "%u%%", BATTERY_VoltsToPercent(g_battery_voltage_average)); @@ -233,10 +240,10 @@ void UI_DisplayStatus(const bool test_display) } } } - + // move to right side of the screen x = LCD_WIDTH - sizeof(BITMAP_BATTERY_LEVEL) - sizeof(BITMAP_USB_C); - + // USB-C charge indicator if (g_charging_with_type_c || test_display) memcpy(line + x, BITMAP_USB_C, sizeof(BITMAP_USB_C)); @@ -244,7 +251,7 @@ void UI_DisplayStatus(const bool test_display) // BATTERY LEVEL indicator UI_DrawBattery(line + x, g_battery_display_level, g_low_battery_blink); - + // ************** ST7565_BlitStatusLine(); diff --git a/ui/welcome.c b/ui/welcome.c deleted file mode 100644 index 8de1153..0000000 --- a/ui/welcome.c +++ /dev/null @@ -1,101 +0,0 @@ -/* Copyright 2023 Dual Tachyon - * https://github.com/DualTachyon - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "driver/eeprom.h" -#include "driver/st7565.h" -#include "external/printf/printf.h" -#include "helper/battery.h" -#include "settings.h" -#include "misc.h" -#include "ui/helper.h" -#include "ui/welcome.h" -#include "ui/status.h" -#include "version.h" - -void UI_DisplayReleaseKeys(void) -{ - memset(g_status_line, 0, sizeof(g_status_line)); - memset(g_frame_buffer, 0, sizeof(g_frame_buffer)); - - UI_PrintString("RELEASE", 0, LCD_WIDTH, 1, 10); - UI_PrintString("ALL KEYS", 0, LCD_WIDTH, 3, 10); - - ST7565_BlitStatusLine(); // blank status line - ST7565_BlitFullScreen(); -} - -void UI_DisplayWelcome(void) -{ - char str0[17]; - char str1[17]; - char str2[17]; - - memset(g_status_line, 0, sizeof(g_status_line)); - memset(g_frame_buffer, 0, sizeof(g_frame_buffer)); - - if (g_eeprom.pwr_on_display_mode == PWR_ON_DISPLAY_MODE_NONE) - { - ST7565_FillScreen(0xFF); - } - else - if (g_eeprom.pwr_on_display_mode == PWR_ON_DISPLAY_MODE_FULL_SCREEN) - { - ST7565_FillScreen(0xFF); - } - else - { - unsigned int slen = strlen(Version_str); - if (slen > (sizeof(str2) - 1)) - slen = sizeof(str2) - 1; - - memset(str0, 0, sizeof(str0)); - memset(str1, 0, sizeof(str1)); - memset(str2, 0, sizeof(str2)); - - if (g_eeprom.pwr_on_display_mode == PWR_ON_DISPLAY_MODE_VOLTAGE) - { - strcpy(str0, "VOLTAGE"); - sprintf(str1, "%u.%02uV %u%%", - g_battery_voltage_average / 100, - g_battery_voltage_average % 100, - BATTERY_VoltsToPercent(g_battery_voltage_average)); - } - else - { - EEPROM_ReadBuffer(0x0EB0, str0, 16); - EEPROM_ReadBuffer(0x0EC0, str1, 16); - } - - memcpy(str2, Version_str, slen); - - UI_PrintString(str0, 0, LCD_WIDTH, 0, 10); - UI_PrintString(str1, 0, LCD_WIDTH, 2, 10); - UI_PrintStringSmall(str2, 0, LCD_WIDTH, 4); - UI_PrintStringSmall(__DATE__, 0, LCD_WIDTH, 5); - UI_PrintStringSmall(__TIME__, 0, LCD_WIDTH, 6); - - #if 1 - ST7565_BlitStatusLine(); // blank status line - #else - UI_DisplayStatus(true); // show all status line symbols (test) - #endif - - ST7565_BlitFullScreen(); - } -} - diff --git a/ui/welcome.h b/ui/welcome.h deleted file mode 100644 index b21a85e..0000000 --- a/ui/welcome.h +++ /dev/null @@ -1,24 +0,0 @@ -/* Copyright 2023 Dual Tachyon - * https://github.com/DualTachyon - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef UI_WELCOME_H -#define UI_WELCOME_H - -void UI_DisplayReleaseKeys(void); -void UI_DisplayWelcome(void); - -#endif -