diff --git a/firmware/application/apps/ui_debug.cpp b/firmware/application/apps/ui_debug.cpp index 6e02bd687..8382a9226 100644 --- a/firmware/application/apps/ui_debug.cpp +++ b/firmware/application/apps/ui_debug.cpp @@ -1499,6 +1499,10 @@ SignalPathStatusView::SignalPathStatusView(NavigationView& nav) &text_max_mode, &text_lbl_rf_path, &text_rf_path, + &text_lbl_filter, + &text_filter, + &text_lbl_mixer, + &text_mixer, &text_lbl_rf_amp, &text_rf_amp, &text_lbl_lna, @@ -1507,8 +1511,13 @@ SignalPathStatusView::SignalPathStatusView(NavigationView& nav) &text_vga, &text_lbl_fpga_decim, &text_fpga_decim, + &text_lbl_fpga_ctrl_dc_q, + &text_fpga_ctrl_dc_q, + &text_lbl_fpga_ctrl_qs, + &text_fpga_ctrl_qs, &text_status, &button_refresh, + &button_toggle_q, &button_done, }); @@ -1518,6 +1527,21 @@ SignalPathStatusView::SignalPathStatusView(NavigationView& nav) refresh_status(); }; + button_toggle_q.on_select = [this](Button&) { + static bool q_override = false; + q_override = !q_override; + + uint8_t ctrl_reg = 0x01; // DC_BLOCK always on + if (q_override) { + ctrl_reg |= 0x02; // Force Q_INVERT ON + } + + radio::debug::fpga::register_write(1, ctrl_reg); + radio::invalidate_spi_config(); + + refresh_status(); + }; + button_done.on_select = [&nav](Button&) { nav.pop(); }; @@ -1537,6 +1561,37 @@ void SignalPathStatusView::refresh_status() { int_fast8_t cached_lna = radio::debug::get_cached_lna_gain(); int_fast8_t cached_vga = radio::debug::get_cached_vga_gain(); + // Get current band. + auto current_band = radio::debug::rf_path_info::get_current_band(); + switch (current_band) { + case rf::path::Band::Low: + text_filter.set("LOW PASS"); + text_filter.set_style(Theme::getInstance()->fg_green); + text_mixer.set("ENABLED"); + text_mixer.set_style(Theme::getInstance()->fg_green); + break; + + case rf::path::Band::Mid: + text_filter.set("BYPASS"); + text_filter.set_style(Theme::getInstance()->fg_green); + text_mixer.set("DISABLED"); + text_mixer.set_style(Theme::getInstance()->fg_orange); + break; + + case rf::path::Band::High: + text_filter.set("HIGH PASS"); + text_filter.set_style(Theme::getInstance()->fg_green); + text_mixer.set("ENABLED"); + text_mixer.set_style(Theme::getInstance()->fg_green); + break; + + default: + text_filter.set("UNKNOWN"); + text_filter.set_style(Theme::getInstance()->fg_red); + text_mixer.set("UNKNOWN"); + text_mixer.set_style(Theme::getInstance()->fg_red); + } + // Read actual register values to verify uint32_t max_r11 = radio::debug::second_if::register_read(11); @@ -1622,6 +1677,25 @@ void SignalPathStatusView::refresh_status() { text_fpga_decim.set("n=" + to_string_dec_uint(fpga_decim) + " (/" + to_string_dec_uint(1 << fpga_decim) + ")"); + // FPGA Register Ctrl Info + uint32_t fpga_ctrl = radio::debug::fpga::register_read(1); + // text_fpga_ctrl_dc_q.set(to_string_hex(fpga_ctrl, 2)); + // text_fpga_ctrl_qs.set(to_string_hex(fpga_ctrl, 2)); + + // Decode bits + bool dc_block = fpga_ctrl & 0x01; + bool q_invert = fpga_ctrl & 0x02; + uint8_t quarter_shift = (fpga_ctrl >> 2) & 0x03; + + // Display human-readable + std::string fpga_status_dc_q = "DC:" + std::string(dc_block ? "ON" : "OFF") + + " Q:" + std::string(q_invert ? "INV" : "NOR"); + + std::string fpga_status_qs = "QS:" + to_string_dec_uint(quarter_shift); + + text_fpga_ctrl_dc_q.set(fpga_status_dc_q); + text_fpga_ctrl_qs.set(fpga_status_qs); + // Summary status // Summary status - update to account for rounding tolerance int lna_diff = (actual_lna_db > cached_lna) ? (actual_lna_db - cached_lna) : (cached_lna - actual_lna_db); @@ -1643,6 +1717,355 @@ void SignalPathStatusView::refresh_status() { } #endif +#ifdef PRALINE +/* SystemDiagnosticsView *************************************************/ +SystemDiagnosticsView::SystemDiagnosticsView(NavigationView& nav) { + add_children({ + &text_title, + &text_lbl_sample, + &text_sample_rate, + &text_lbl_band, + &text_band, + &button_sample_2m, + &button_sample_4m, + &button_sample_8m, + &button_sample_20m, + &text_lbl_bb_filter, + &text_bb_filter, + &text_lbl_reg8, + &text_reg8, + &text_lbl_gpio, + &text_lbl_lpf, + &text_gpio_lpf, + &text_lbl_mix, + &text_gpio_mix, + &text_lbl_amp, + &text_gpio_amp, + &text_lbl_fpga, + &text_lbl_fpga_ctrl, + &text_fpga_ctrl, + &text_lbl_fpga_decode, + &button_toggle_q, + &button_toggle_dc, + &button_refresh, + &button_done, + }); + + text_title.set_style(Theme::getInstance()->fg_yellow); + text_lbl_gpio.set_style(Theme::getInstance()->fg_yellow); + text_lbl_fpga.set_style(Theme::getInstance()->fg_yellow); + + // Sample rate buttons + button_sample_2m.on_select = [this](Button&) { + set_sample_rate(2000000); + }; + + button_sample_4m.on_select = [this](Button&) { + set_sample_rate(4000000); + }; + + button_sample_8m.on_select = [this](Button&) { + set_sample_rate(8000000); + }; + + button_sample_20m.on_select = [this](Button&) { + set_sample_rate(20000000); + }; + + // Q inversion toggle + button_toggle_q.on_select = [this](Button&) { + uint32_t current = radio::debug::fpga::register_read(1); + uint8_t new_val = current ^ 0x02; // Toggle Q_INVERT bit + radio::debug::fpga::register_write(1, new_val); + radio::invalidate_spi_config(); + refresh(); + }; + + // DC block toggle + button_toggle_dc.on_select = [this](Button&) { + uint32_t current = radio::debug::fpga::register_read(1); + uint8_t new_val = current ^ 0x01; // Toggle DC_BLOCK bit + radio::debug::fpga::register_write(1, new_val); + radio::invalidate_spi_config(); + refresh(); + }; + + button_refresh.on_select = [this](Button&) { + refresh(); + }; + + button_done.on_select = [&nav](Button&) { + nav.pop(); + }; + + refresh(); +} + +void SystemDiagnosticsView::focus() { + button_refresh.focus(); +} + +void SystemDiagnosticsView::set_sample_rate(uint32_t rate) { + portapack::clock_manager.set_sampling_frequency(rate); + refresh(); +} + +void SystemDiagnosticsView::read_gpio_states() { + // Read actual GPIO port states + uint32_t gpio3_state = LPC_GPIO->PIN[3]; // GPIO3 for mixer + uint32_t gpio4_state = LPC_GPIO->PIN[4]; // GPIO4 for LPF and amp + + // Extract specific pins based on hackrf_gpio.hpp definitions: + // gpio_mix_enable_n = GPIO3_2 (P6_3) - bit 2 of port 3, INVERTED + // gpio_lpf_enable = GPIO4_8 (PA_1) - bit 8 of port 4 + // gpio_rf_amp_enable = GPIO4_9 (PA_2) - bit 9 of port 4 + + bool mix_n_actual = (gpio3_state >> 2) & 1; // GPIO3[2] + bool lpf_actual = (gpio4_state >> 8) & 1; // GPIO4[8] + bool amp_actual = (gpio4_state >> 9) & 1; // GPIO4[9] + + // Mixer is active LOW, so invert for display + bool mixer_enabled = !mix_n_actual; + + // Display with GPIO pin numbers + text_gpio_lpf.set( + std::string(lpf_actual ? "ON" : "OFF") + + " (GPIO4[8]=" + to_string_dec_uint(lpf_actual ? 1 : 0) + ")"); + + text_gpio_mix.set( + std::string(mixer_enabled ? "ENABLED" : "BYPASSED") + + " (GPIO3[2]=" + to_string_dec_uint(mix_n_actual ? 1 : 0) + ")"); + + text_gpio_amp.set( + std::string(amp_actual ? "ON" : "OFF") + + " (GPIO4[9]=" + to_string_dec_uint(amp_actual ? 1 : 0) + ")"); + + // Color code + text_gpio_lpf.set_style(lpf_actual ? Theme::getInstance()->fg_green : Theme::getInstance()->fg_red); + text_gpio_mix.set_style(mixer_enabled ? Theme::getInstance()->fg_green : Theme::getInstance()->fg_red); + text_gpio_amp.set_style(amp_actual ? Theme::getInstance()->fg_green : Theme::getInstance()->fg_orange); +} + +void SystemDiagnosticsView::refresh() { + // Sample rate + uint32_t sample_rate = portapack::clock_manager.get_sampling_frequency(); + if (sample_rate >= 1000000) { + text_sample_rate.set(to_string_dec_uint(sample_rate / 1000000) + " MSS"); + } else { + text_sample_rate.set(to_string_dec_uint(sample_rate / 1000) + " kSS"); + } + + // Baseband filter bandwidth + uint32_t reg8 = radio::debug::second_if::register_read(8); + text_reg8.set("0x" + to_string_hex(reg8, 4)); + + uint8_t lpf_coarse = reg8 & 0x03; // Bits 1:0 + const char* bw_names[] = {"7.5 MHz", "8.5 MHz", "15 MHz", "18 MHz"}; + text_bb_filter.set(bw_names[lpf_coarse]); + + // Color code - green if >= 8 MHz, red otherwise + if (lpf_coarse >= 1) { + text_bb_filter.set_style(Theme::getInstance()->fg_green); + } else { + text_bb_filter.set_style(Theme::getInstance()->fg_red); + } + + // Read actual GPIO pin states + read_gpio_states(); + + uint32_t gpio6_pin = LPC_GPIO->PIN[6]; + bool rffc_locked = (gpio6_pin >> 25) & 1; + + // Display it by changing one of the existing fields temporarily + // For example, modify the band display to show lock status: + + auto current_band = radio::debug::rf_path_info::get_current_band(); + switch (current_band) { + case rf::path::Band::Low: + text_band.set("LOW(0-2320MHz)|" + std::string(rffc_locked ? "LCK)" : "ULCK)")); + text_band.set_style(rffc_locked ? Theme::getInstance()->fg_green : Theme::getInstance()->fg_red); + break; + case rf::path::Band::Mid: + text_band.set("MID(2320-2740MHz)"); + text_band.set_style(Theme::getInstance()->fg_green); + break; + case rf::path::Band::High: + text_band.set("HIGH(2740-7250MHz)"); + text_band.set_style(Theme::getInstance()->fg_green); + break; + } + // FPGA control register + uint32_t fpga_ctrl = radio::debug::fpga::register_read(1); + text_fpga_ctrl.set("0x" + to_string_hex(fpga_ctrl, 2)); + + // Decode FPGA register bits + bool dc_block = fpga_ctrl & 0x01; + bool q_invert = fpga_ctrl & 0x02; + uint8_t quarter_shift = (fpga_ctrl >> 2) & 0x03; + + text_lbl_fpga_decode.set( + "|DC:" + std::string(dc_block ? "ON" : "OFF") + + " Q:" + std::string(q_invert ? "INV" : "NOR") + + " QS:" + to_string_dec_uint(quarter_shift)); +} +#endif + +#ifdef PRALINE +/* GPIODebugView *************************************************/ +GPIODebugView::GPIODebugView(NavigationView& nav) { + add_children({ + &text_lbl_gpio4, + &text_lbl_dir4, + &text_dir4, + &text_lbl_pin4, + &text_pin4, + &text_lbl_set4, + &text_set4, + &text_lbl_lpf_bit, + &text_lpf_dir, + &text_lpf_pin, + &text_lpf_set, + &button_lpf_toggle, + &button_lpf_on, + &button_lpf_off, + &text_lbl_amp_bit, + &text_amp_dir, + &text_amp_pin, + &text_amp_set, + &button_amp_toggle, + &button_amp_on, + &button_amp_off, + &text_lbl_gpio3, + &text_lbl_mix_bit, + &text_mix_dir, + &text_mix_pin, + &text_mix_set, + &button_refresh, + &button_done, + }); + + text_lbl_gpio4.set_style(Theme::getInstance()->fg_yellow); + text_lbl_gpio3.set_style(Theme::getInstance()->fg_yellow); + + // LPF control buttons + button_lpf_toggle.on_select = [this](Button&) { + // Read current state + uint32_t current = LPC_GPIO->PIN[4]; + bool current_state = (current >> 8) & 1; + + // Toggle + if (current_state) { + LPC_GPIO->CLR[4] = (1 << 8); // Clear bit 8 + } else { + LPC_GPIO->SET[4] = (1 << 8); // Set bit 8 + } + + refresh(); + }; + + button_lpf_on.on_select = [this](Button&) { + LPC_GPIO->SET[4] = (1 << 8); // Force ON + refresh(); + }; + + button_lpf_off.on_select = [this](Button&) { + LPC_GPIO->CLR[4] = (1 << 8); // Force OFF + refresh(); + }; + + // RF Amp control buttons + button_amp_toggle.on_select = [this](Button&) { + uint32_t current = LPC_GPIO->PIN[4]; + bool current_state = (current >> 9) & 1; + + if (current_state) { + LPC_GPIO->CLR[4] = (1 << 9); + } else { + LPC_GPIO->SET[4] = (1 << 9); + } + + refresh(); + }; + + button_amp_on.on_select = [this](Button&) { + LPC_GPIO->SET[4] = (1 << 9); // Force ON + refresh(); + }; + + button_amp_off.on_select = [this](Button&) { + LPC_GPIO->CLR[4] = (1 << 9); // Force OFF + refresh(); + }; + + button_refresh.on_select = [this](Button&) { + refresh(); + }; + + button_done.on_select = [&nav](Button&) { + nav.pop(); + }; + + refresh(); +} + +void GPIODebugView::focus() { + button_refresh.focus(); +} + +void GPIODebugView::refresh() { + // Read GPIO4 registers + uint32_t gpio4_dir = LPC_GPIO->DIR[4]; // Direction: 1=output, 0=input + uint32_t gpio4_pin = LPC_GPIO->PIN[4]; // Actual pin state + uint32_t gpio4_set = LPC_GPIO->SET[4]; // What we're trying to output + + // Display full registers + text_dir4.set("0x" + to_string_hex(gpio4_dir, 8)); + text_pin4.set("0x" + to_string_hex(gpio4_pin, 8)); + text_set4.set("0x" + to_string_hex(gpio4_set, 8)); + + // Extract bit 8 (LPF) + bool lpf_dir = (gpio4_dir >> 8) & 1; + bool lpf_pin = (gpio4_pin >> 8) & 1; + bool lpf_set = (gpio4_set >> 8) & 1; + + text_lpf_dir.set("DIR: " + std::string(lpf_dir ? "OUT" : "IN")); + text_lpf_pin.set("PIN: " + std::string(lpf_pin ? "1" : "0")); + text_lpf_set.set("SET: " + std::string(lpf_set ? "1" : "0")); + + // Color code + text_lpf_dir.set_style(lpf_dir ? Theme::getInstance()->fg_green : Theme::getInstance()->fg_red); + text_lpf_pin.set_style(lpf_pin ? Theme::getInstance()->fg_green : Theme::getInstance()->fg_red); + + // Extract bit 9 (RF Amp) + bool amp_dir = (gpio4_dir >> 9) & 1; + bool amp_pin = (gpio4_pin >> 9) & 1; + bool amp_set = (gpio4_set >> 9) & 1; + + text_amp_dir.set("DIR: " + std::string(amp_dir ? "OUT" : "IN")); + text_amp_pin.set("PIN: " + std::string(amp_pin ? "1" : "0")); + text_amp_set.set("SET: " + std::string(amp_set ? "1" : "0")); + + text_amp_dir.set_style(amp_dir ? Theme::getInstance()->fg_green : Theme::getInstance()->fg_red); + text_amp_pin.set_style(amp_pin ? Theme::getInstance()->fg_green : Theme::getInstance()->fg_red); + + // Read GPIO3 (Mixer) - bit 2 + uint32_t gpio3_dir = LPC_GPIO->DIR[3]; + uint32_t gpio3_pin = LPC_GPIO->PIN[3]; + uint32_t gpio3_set = LPC_GPIO->SET[3]; + + bool mix_dir = (gpio3_dir >> 2) & 1; + bool mix_pin = (gpio3_pin >> 2) & 1; + bool mix_set = (gpio3_set >> 2) & 1; + + text_mix_dir.set("DIR: " + std::string(mix_dir ? "OUT" : "IN")); + text_mix_pin.set("PIN: " + std::string(mix_pin ? "1" : "0")); + text_mix_set.set("SET: " + std::string(mix_set ? "1" : "0")); + + text_mix_dir.set_style(mix_dir ? Theme::getInstance()->fg_green : Theme::getInstance()->fg_red); + text_mix_pin.set_style(mix_pin ? Theme::getInstance()->fg_green : Theme::getInstance()->fg_red); +} +#endif + #ifdef PRALINE /* RFFC5072StatusView *************************************************/ @@ -1650,6 +2073,10 @@ RFFC5072StatusView::RFFC5072StatusView(NavigationView& nav) : nav_(nav) { add_children({ &text_title, + &text_lbl_lock, + &text_lock, + &text_lbl_ctrl, + &text_ctrl, &text_lbl_enabled, &text_enabled, &text_lbl_freq, @@ -1664,7 +2091,6 @@ RFFC5072StatusView::RFFC5072StatusView(NavigationView& nav) &text_r1, &text_lbl_r2, &text_r2, - &text_lbl_decode, &text_lbl_n, &text_n, &text_lbl_lodiv, @@ -1672,12 +2098,13 @@ RFFC5072StatusView::RFFC5072StatusView(NavigationView& nav) &text_lbl_calc, &text_calc, &text_status, + &text_lbl_regs_status, + &text_regs_status, &button_refresh, &button_done, }); text_title.set_style(Theme::getInstance()->fg_yellow); - text_lbl_decode.set_style(Theme::getInstance()->fg_yellow); button_refresh.on_select = [this](Button&) { refresh_status(); @@ -1696,11 +2123,47 @@ void RFFC5072StatusView::focus() { } void RFFC5072StatusView::refresh_status() { + // === READ RAW GPIO STATES FOR DEBUGGING === + uint32_t gpio2_dir = LPC_GPIO->DIR[2]; + uint32_t gpio2_pin = LPC_GPIO->PIN[2]; + + // Check if the pins are even configured as outputs + bool enx_is_output = (gpio2_dir >> 13) & 1; + bool resetx_is_output = (gpio2_dir >> 14) & 1; + + // === LOCK DETECT === + uint32_t gpio6_pin = LPC_GPIO->PIN[6]; + bool rffc_locked = (gpio6_pin >> 25) & 1; + + text_lock.set(rffc_locked ? "LOCKED" : "UNLOCKED"); + text_lock.set_style(rffc_locked ? Theme::getInstance()->fg_green + : Theme::getInstance()->fg_red); + + uint8_t fpga_reg1 = radio::debug::fpga::register_read(1); // CTRL register + uint8_t fpga_reg2 = radio::debug::fpga::register_read(2); // RX_DECIM + + text_regs_status.set( + "FPGA R1:" + to_string_hex(fpga_reg1, 2) + + " R2:" + to_string_hex(fpga_reg2, 2)); + + // === CONTROL PINS === + // ENX = GPIO2[13] (P5_4) - active LOW (0 = enabled) + // RESETX = GPIO2[14] (P5_5) - active LOW (0 = reset) + bool enx = (gpio2_pin >> 13) & 1; + bool resetx = (gpio2_pin >> 14) & 1; + + text_ctrl.set(std::string(enx ? "DIS" : "EN") + " " + + std::string(resetx ? "RUN" : "RST") + " " + + "O:" + std::string(resetx_is_output ? "Y" : "N")); + + text_ctrl.set_style((enx == 0 && resetx == 1) ? Theme::getInstance()->fg_green + : Theme::getInstance()->fg_red); + + // === REGISTERS === // Read CORRECT registers for Path 2 (active path!) uint32_t r0 = radio::debug::first_if::register_read(0); // Control uint32_t r15 = radio::debug::first_if::register_read(15); // P2_FREQ1 uint32_t r16 = radio::debug::first_if::register_read(16); // P2_FREQ2 - uint32_t r17 = radio::debug::first_if::register_read(17); // P2_FREQ3 // Display text_r0.set(to_string_hex(r0, 4)); @@ -1713,35 +2176,22 @@ void RFFC5072StatusView::refresh_status() { text_enabled.set_style(enabled ? Theme::getInstance()->fg_green : Theme::getInstance()->fg_red); - // Decode from P2_FREQ1 (R15) using struct layout: - // bits [1:0] = p2vcosel - // bits [3:2] = p2presc (prescaler) - // bits [6:4] = p2lodiv (LO divider) - // bits [15:7] = p2n (N divider integer) - + // Decode from P2_FREQ1 (R15) uint16_t n_int = (r15 >> 7) & 0x1FF; // 9 bits uint8_t lodiv_sel = (r15 >> 4) & 0x07; // 3 bits uint8_t presc_sel = (r15 >> 2) & 0x03; // 2 bits - uint8_t vcosel = r15 & 0x03; // 2 bits text_n.set(to_string_dec_uint(n_int)); // LO divider: 0=÷2, 1=÷4, 2=÷8, 3=÷16, 4=÷32, 5=÷64 uint16_t lodiv_val = 1u << lodiv_sel; - - // Prescaler: 0=÷2, 1=÷4 (but code only uses 1 or 2 per rffc507x.cpp) uint16_t presc_val = 1u << presc_sel; text_lodiv.set("/" + to_string_dec_uint(lodiv_val) + - " (P: /" + to_string_dec_uint(presc_val) + ")"); + " (P:/" + to_string_dec_uint(presc_val) + ")"); // Calculate frequencies - // F_VCO = (F_ref × N) / Prescaler - // F_LO = F_VCO / LODIV const uint32_t f_ref_mhz = 40; - - // N divider is 24-bit fractional - // For quick calc, use integer part only uint32_t f_vco_mhz = (f_ref_mhz * n_int) / presc_val; uint32_t f_lo_mhz = f_vco_mhz / lodiv_val; @@ -1757,32 +2207,37 @@ void RFFC5072StatusView::refresh_status() { text_freq.set_style(vco_ok ? Theme::getInstance()->fg_green : Theme::getInstance()->fg_red); - // Check mixer mode (R0 bit 5 = MODE, 0=path1, 1=path2) + // Check mixer mode bool path2_active = (r0 & 0x0020) != 0; text_path.set(path2_active ? "PATH2" : "PATH1"); text_mixer.set(path2_active ? "ACTIVE" : "INACTIVE"); text_mixer.set_style(path2_active ? Theme::getInstance()->fg_green : Theme::getInstance()->fg_orange); - // Summary - if (!enabled) { - text_status.set("DISABLED!"); + // === SUMMARY STATUS === + if (!rffc_locked) { + text_status.set("PLL UNLOCKED!"); + text_status.set_style(Theme::getInstance()->fg_red); + } else if (enx == 1) { + text_status.set("DISABLED (ENX=1)!"); + text_status.set_style(Theme::getInstance()->fg_red); + } else if (resetx == 0) { + text_status.set("IN RESET (RESETX=0)!"); + text_status.set_style(Theme::getInstance()->fg_red); + } else if (!enabled) { + text_status.set("R0 bit 4 = 0 (disabled)"); text_status.set_style(Theme::getInstance()->fg_red); } else if (!path2_active) { text_status.set("Path 2 not selected!"); text_status.set_style(Theme::getInstance()->fg_red); } else if (!vco_ok) { - text_status.set("VCO " + to_string_dec_uint(f_vco_mhz) + "MHz OOR"); + text_status.set("VCO out of range!"); text_status.set_style(Theme::getInstance()->fg_red); } else if (!lo_ok) { - text_status.set("LO " + to_string_dec_uint(f_lo_mhz) + "MHz OOR"); + text_status.set("LO out of range!"); text_status.set_style(Theme::getInstance()->fg_red); } else { - text_status.set(to_string_dec_uint(f_ref_mhz) + "x" + - to_string_dec_uint(n_int) + "/" + - to_string_dec_uint(presc_val) + "/" + - to_string_dec_uint(lodiv_val) + "=" + - to_string_dec_uint(f_lo_mhz) + "MHz."); + text_status.set("All checks passed!"); text_status.set_style(Theme::getInstance()->fg_green); } } @@ -1893,6 +2348,125 @@ void RFFCTuningDebugView::refresh() { } } +/* MAX2831DebugView *************************************************/ +MAX2831DebugView::MAX2831DebugView(NavigationView& nav) { + add_children({ + &text_title, + &text_lbl_called, + &text_called, + &text_lbl_valid, + &text_valid, + &text_lbl_req, + &text_req, + &text_lbl_calc_n, + &text_calc_n, + &text_lbl_calc_frac, + &text_calc_frac, + &text_spacer, + &text_lbl_r3, + &text_r3, + &text_lbl_r4, + &text_r4, + &text_lbl_act_n, + &text_act_n, + &text_lbl_act_frac, + &text_act_frac, + &text_lbl_calc_freq, + &text_calc_freq, + &text_status, + &button_refresh, + &button_done, + }); + + text_spacer.set_style(Theme::getInstance()->fg_yellow); + + button_refresh.on_select = [this](Button&) { + refresh(); + }; + + button_done.on_select = [&nav](Button&) { + nav.pop(); + }; + + refresh(); +} + +void MAX2831DebugView::focus() { + button_refresh.focus(); +} + +void MAX2831DebugView::refresh() { + auto info = radio::debug::second_if::get_max2831_info(); + + // Show if set_frequency was called + if (info.set_frequency_called) { + text_called.set("YES"); + text_called.set_style(Theme::getInstance()->fg_green); + + if (info.frequency_valid) { + text_valid.set("YES (2.3-2.6G)"); + text_valid.set_style(Theme::getInstance()->fg_green); + } else { + text_valid.set("NO - OUT OF RANGE!"); + text_valid.set_style(Theme::getInstance()->fg_red); + } + + text_req.set(to_string_dec_uint(info.requested_freq_mhz) + " MHz"); + text_calc_n.set(to_string_dec_uint(info.calculated_n)); + text_calc_frac.set(to_string_hex(info.calculated_frac, 5)); + } else { + text_called.set("NO"); + text_called.set_style(Theme::getInstance()->fg_red); + text_valid.set("---"); + text_req.set("---"); + text_calc_n.set("---"); + text_calc_frac.set("---"); + } + + // Read actual hardware registers + uint32_t r3 = radio::debug::second_if::register_read(3); + uint32_t r4 = radio::debug::second_if::register_read(4); + + text_r3.set(to_string_hex(r3, 4)); + text_r4.set(to_string_hex(r4, 4)); + + // Decode actual values from registers + uint16_t act_n = r3 & 0xFF; + uint32_t act_frac_lo = (r3 >> 8) & 0x3F; + uint32_t act_frac_hi = r4 & 0x3FFF; + uint32_t act_frac = (act_frac_hi << 6) | act_frac_lo; + + text_act_n.set(to_string_dec_uint(act_n)); + text_act_frac.set(to_string_hex(act_frac, 5)); + + // Calculate actual frequency from registers + // F_LO = 20 MHz × (N + Frac/2^20) + // For display, show integer part only + uint32_t calc_freq_mhz = 20 * act_n; + // Add fractional contribution (approximate) + uint32_t frac_contribution = (act_frac * 20) >> 20; + calc_freq_mhz += frac_contribution; + + text_calc_freq.set(to_string_dec_uint(calc_freq_mhz) + " MHz"); + + // Status + if (!info.set_frequency_called) { + text_status.set("MAX2831 set_frequency\nNEVER called!"); + text_status.set_style(Theme::getInstance()->fg_red); + } else if (!info.frequency_valid) { + text_status.set("Freq " + to_string_dec_uint(info.requested_freq_mhz) + + " MHz OUT OF RANGE!\n(need 2300-2600)"); + text_status.set_style(Theme::getInstance()->fg_red); + } else if (act_n == info.calculated_n && act_frac == info.calculated_frac) { + text_status.set("MATCH!\nHardware = Expected"); + text_status.set_style(Theme::getInstance()->fg_green); + } else { + text_status.set("MISMATCH!\nN: exp=" + to_string_dec_uint(info.calculated_n) + + " act=" + to_string_dec_uint(act_n)); + text_status.set_style(Theme::getInstance()->fg_red); + } +} + #endif #endif @@ -1956,14 +2530,17 @@ void DebugMenuView::on_populate() { } add_items({ #ifdef PRALINE + {"System Diag", ui::Theme::getInstance()->fg_yellow->foreground, &bitmap_icon_peripherals, [this]() { nav_.push(); }}, {"Radio Diag", ui::Theme::getInstance()->fg_yellow->foreground, &bitmap_icon_peripherals, [this]() { nav_.push(); }}, - {"Baseband Status", ui::Theme::getInstance()->fg_yellow->foreground, &bitmap_icon_peripherals, [this]() { nav_.push(); }}, - {"SGPIO Live", ui::Theme::getInstance()->fg_yellow->foreground, &bitmap_icon_peripherals, [this]() { nav_.push(); }}, - {"SGPIO8 Clock", ui::Theme::getInstance()->fg_yellow->foreground, &bitmap_icon_peripherals, [this]() { nav_.push(); }}, - {"Si5351 Clocks", ui::Theme::getInstance()->fg_yellow->foreground, &bitmap_icon_peripherals, [this]() { nav_.push(); }}, {"Signal Path", ui::Theme::getInstance()->fg_yellow->foreground, &bitmap_icon_peripherals, [this]() { nav_.push(); }}, + {"GPIO Debug", ui::Theme::getInstance()->fg_yellow->foreground, &bitmap_icon_peripherals, [this]() { nav_.push(); }}, {"RFFC Status", ui::Theme::getInstance()->fg_yellow->foreground, &bitmap_icon_peripherals, [this]() { nav_.push(); }}, {"RFFC Tuning", ui::Theme::getInstance()->fg_yellow->foreground, &bitmap_icon_peripherals, [this]() { nav_.push(); }}, + {"MAX2831 Debug", ui::Theme::getInstance()->fg_yellow->foreground, &bitmap_icon_peripherals, [this]() { nav_.push(); }}, + {"Si5351 Clocks", ui::Theme::getInstance()->fg_yellow->foreground, &bitmap_icon_peripherals, [this]() { nav_.push(); }}, + {"SGPIO8 Clock", ui::Theme::getInstance()->fg_yellow->foreground, &bitmap_icon_peripherals, [this]() { nav_.push(); }}, + {"Baseband Status", ui::Theme::getInstance()->fg_yellow->foreground, &bitmap_icon_peripherals, [this]() { nav_.push(); }}, + {"SGPIO Live", ui::Theme::getInstance()->fg_yellow->foreground, &bitmap_icon_peripherals, [this]() { nav_.push(); }}, {"RX Test", ui::Theme::getInstance()->fg_yellow->foreground, &bitmap_icon_peripherals, [this]() { nav_.push(); }}, #endif {"Buttons Test", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_controls, [this]() { nav_.push(); }}, diff --git a/firmware/application/apps/ui_debug.hpp b/firmware/application/apps/ui_debug.hpp index ef974c079..73b03c6fa 100644 --- a/firmware/application/apps/ui_debug.hpp +++ b/firmware/application/apps/ui_debug.hpp @@ -703,20 +703,88 @@ class SignalPathStatusView : public View { Text text_lbl_rf_path{{0, 52, 114, 16}, "RF Path:"}; Text text_rf_path{{116, 52, 124, 16}, "---"}; - Text text_lbl_rf_amp{{0, 68, 114, 16}, "RF Amp:"}; - Text text_rf_amp{{116, 68, 124, 16}, "---"}; + Text text_lbl_filter{{0, 68, 114, 16}, "Filter Band:"}; + Text text_filter{{116, 68, 124, 16}, "---"}; - Text text_lbl_lna{{0, 84, 114, 16}, "LNA Gain:"}; - Text text_lna{{116, 84, 124, 16}, "---"}; + Text text_lbl_mixer{{0, 84, 114, 16}, "Mixer Enable:"}; + Text text_mixer{{116, 84, 124, 16}, "---"}; - Text text_lbl_vga{{0, 100, 114, 16}, "VGA Gain:"}; - Text text_vga{{116, 100, 124, 16}, "---"}; + Text text_lbl_rf_amp{{0, 100, 114, 16}, "RF Amp:"}; + Text text_rf_amp{{116, 100, 124, 16}, "---"}; - Text text_lbl_fpga_decim{{0, 116, 114, 16}, "FPGA Decim:"}; - Text text_fpga_decim{{116, 116, 124, 16}, "---"}; + Text text_lbl_lna{{0, 116, 114, 16}, "LNA Gain:"}; + Text text_lna{{116, 116, 124, 16}, "---"}; - Text text_status{{0, 140, 240, 32}, ""}; + Text text_lbl_vga{{0, 132, 114, 16}, "VGA Gain:"}; + Text text_vga{{116, 132, 124, 16}, "---"}; + Text text_lbl_fpga_decim{{0, 148, 114, 16}, "FPGA Decim:"}; + Text text_fpga_decim{{116, 148, 124, 16}, "---"}; + + Text text_lbl_fpga_ctrl_dc_q{{0, 164, 114, 16}, "FPGA Ctrl:"}; + Text text_fpga_ctrl_dc_q{{116, 164, 124, 16}, "---"}; + + Text text_lbl_fpga_ctrl_qs{{0, 180, 114, 16}, ""}; + Text text_fpga_ctrl_qs{{116, 180, 124, 16}, "---"}; + + Text text_status{{0, 200, 240, 32}, ""}; + + Button button_refresh{{0, 280, 72, 24}, "Refresh"}; + Button button_toggle_q{{88, 280, 72, 24}, "Q Inv"}; + Button button_done{{176, 280, 64, 24}, "Done"}; +}; +#endif + +#ifdef PRALINE +class GPIODebugView : public View { + public: + GPIODebugView(NavigationView& nav); + void focus() override; + std::string title() const override { return "GPIO Debug"; }; + + private: + void refresh(); + + // GPIO4 (LPF and RF Amp) + Text text_lbl_gpio4{{0, 0, 240, 16}, "Pin Diag: GPIO4 (LPF|Amp|Mix)"}; + + Text text_lbl_dir4{{0, 18, 114, 16}, "DIR[4]:"}; + Text text_dir4{{116, 18, 124, 16}, "---"}; + + Text text_lbl_pin4{{0, 36, 114, 16}, "PIN[4] (read):"}; + Text text_pin4{{116, 36, 124, 16}, "---"}; + + Text text_lbl_set4{{0, 54, 114, 16}, "SET[4] (write):"}; + Text text_set4{{116, 54, 124, 16}, "---"}; + + // Bit 8 (LPF) + Text text_lbl_lpf_bit{{0, 78, 240, 16}, "Bit 8 (LPF - GPIO4[8]):"}; + Text text_lpf_dir{{0, 96, 80, 16}, "DIR: ?"}; + Text text_lpf_pin{{82, 96, 78, 16}, "PIN: ?"}; + Text text_lpf_set{{162, 96, 78, 16}, "SET: ?"}; + + Button button_lpf_toggle{{8, 114, 110, 24}, "Toggle LPF"}; + Button button_lpf_on{{122, 114, 50, 24}, "ON"}; + Button button_lpf_off{{176, 114, 50, 24}, "OFF"}; + + // Bit 9 (RF Amp) + Text text_lbl_amp_bit{{0, 146, 240, 16}, "Bit 9 (Amp - GPIO4[9]):"}; + Text text_amp_dir{{0, 164, 80, 16}, "DIR: ?"}; + Text text_amp_pin{{82, 164, 78, 16}, "PIN: ?"}; + Text text_amp_set{{162, 164, 78, 16}, "SET: ?"}; + + Button button_amp_toggle{{8, 182, 110, 24}, "Toggle Amp"}; + Button button_amp_on{{122, 182, 50, 24}, "ON"}; + Button button_amp_off{{176, 182, 50, 24}, "OFF"}; + + // GPIO3 (Mixer) + Text text_lbl_gpio3{{0, 214, 240, 16}, "--- GPIO3 (Mixer) ---"}; + Text text_lbl_mix_bit{{0, 232, 240, 16}, "Bit 2 (MIX - GPIO3[2]):"}; + Text text_mix_dir{{0, 250, 80, 16}, "DIR: ?"}; + Text text_mix_pin{{82, 250, 78, 16}, "PIN: ?"}; + Text text_mix_set{{162, 250, 78, 16}, "SET: ?"}; + + // Controls Button button_refresh{{8, 280, 72, 24}, "Refresh"}; Button button_done{{168, 280, 64, 24}, "Done"}; }; @@ -736,42 +804,49 @@ class RFFC5072StatusView : public View { Text text_title{{0, 0, 240, 16}, "=== RFFC5072 (1st IF) ==="}; - Text text_lbl_enabled{{0, 20, 114, 16}, "Status:"}; - Text text_enabled{{116, 20, 124, 16}, "---"}; + Text text_lbl_lock{{0, 16, 114, 16}, "Lock Detect:"}; + Text text_lock{{116, 16, 124, 16}, "---"}; - Text text_lbl_freq{{0, 36, 114, 16}, "LO Freq:"}; - Text text_freq{{116, 36, 124, 16}, "---"}; + Text text_lbl_ctrl{{0, 32, 114, 16}, "Control:"}; + Text text_ctrl{{116, 32, 124, 16}, "---"}; - Text text_lbl_path{{0, 52, 114, 16}, "Path:"}; - Text text_path{{116, 52, 124, 16}, "---"}; + Text text_lbl_enabled{{0, 48, 114, 16}, "Status:"}; + Text text_enabled{{116, 48, 124, 16}, "---"}; - Text text_lbl_mixer{{0, 68, 114, 16}, "Mixer:"}; - Text text_mixer{{116, 68, 124, 16}, "---"}; + Text text_lbl_freq{{0, 64, 114, 16}, "LO Freq:"}; + Text text_freq{{116, 64, 124, 16}, "---"}; - Text text_lbl_r0{{0, 92, 114, 16}, "Reg 0:"}; - Text text_r0{{116, 92, 124, 16}, "---"}; + Text text_lbl_path{{0, 80, 114, 16}, "Path:"}; + Text text_path{{116, 80, 124, 16}, "---"}; - Text text_lbl_r1{{0, 108, 114, 16}, "Reg 1 (N):"}; - Text text_r1{{116, 108, 124, 16}, "---"}; + Text text_lbl_mixer{{0, 96, 114, 16}, "Mixer:"}; + Text text_mixer{{116, 96, 124, 16}, "---"}; - Text text_lbl_r2{{0, 124, 114, 16}, "Reg 2:"}; - Text text_r2{{116, 124, 124, 16}, "---"}; + Text text_lbl_r0{{0, 112, 114, 16}, "Reg 0:"}; + Text text_r0{{116, 112, 124, 16}, "---"}; - Text text_lbl_decode{{0, 148, 240, 16}, "--- Decoded Values ---"}; + Text text_lbl_r1{{0, 128, 114, 16}, "Reg 1 (N):"}; + Text text_r1{{116, 128, 124, 16}, "---"}; - Text text_lbl_n{{0, 168, 114, 16}, "N divider:"}; - Text text_n{{116, 168, 124, 16}, "---"}; + Text text_lbl_r2{{0, 144, 114, 16}, "Reg 2:"}; + Text text_r2{{116, 144, 124, 16}, "---"}; - Text text_lbl_lodiv{{0, 184, 114, 16}, "LO divider:"}; - Text text_lodiv{{116, 184, 124, 16}, "---"}; + Text text_lbl_n{{0, 160, 114, 16}, "N divider:"}; + Text text_n{{116, 160, 124, 16}, "---"}; - Text text_lbl_calc{{0, 200, 114, 16}, "Calc freq:"}; - Text text_calc{{116, 200, 124, 16}, "---"}; + Text text_lbl_lodiv{{0, 176, 114, 16}, "LO divider:"}; + Text text_lodiv{{116, 176, 124, 16}, "---"}; - Text text_status{{0, 224, 240, 32}, ""}; + Text text_lbl_calc{{0, 192, 114, 16}, "Calc freq:"}; + Text text_calc{{116, 192, 124, 16}, "---"}; - Button button_refresh{{8, 280, 72, 24}, "Refresh"}; - Button button_done{{168, 280, 64, 24}, "Done"}; + Text text_status{{0, 208, 240, 16}, ""}; + + Text text_lbl_regs_status{{0, 224, 48, 16}, "Regs:"}; + Text text_regs_status{{50, 224, 190, 16}, "---"}; + + Button button_refresh{{2, 280, 56, 24}, "Rfrsh"}; + Button button_done{{182, 280, 56, 24}, "Done"}; }; #ifdef PRALINE @@ -828,6 +903,120 @@ class RFFCTuningDebugView : public View { #endif +#ifdef PRALINE +/* MAX2831DebugView *************************************************/ + +class MAX2831DebugView : public View { + public: + MAX2831DebugView(NavigationView& nav); + void focus() override; + std::string title() const override { return "MAX2831 Debug"; }; + + private: + void refresh(); + + Text text_title{{0, 0, 240, 16}, "MAX2831 (2nd IF) Debug"}; + + Text text_lbl_called{{0, 24, 120, 16}, "Freq Set:"}; + Text text_called{{122, 24, 118, 16}, "NO"}; + + Text text_lbl_valid{{0, 42, 120, 16}, "In Range:"}; + Text text_valid{{122, 42, 118, 16}, "---"}; + + Text text_lbl_req{{0, 60, 120, 16}, "Requested:"}; + Text text_req{{122, 60, 118, 16}, "---"}; + + Text text_lbl_calc_n{{0, 78, 120, 16}, "Calc N:"}; + Text text_calc_n{{122, 78, 118, 16}, "---"}; + + Text text_lbl_calc_frac{{0, 96, 120, 16}, "Calc Frac:"}; + Text text_calc_frac{{122, 96, 118, 16}, "---"}; + + Text text_spacer{{0, 114, 240, 16}, "--- Hardware Regs ---"}; + + Text text_lbl_r3{{0, 132, 120, 16}, "Reg 3:"}; + Text text_r3{{122, 132, 118, 16}, "---"}; + + Text text_lbl_r4{{0, 150, 120, 16}, "Reg 4:"}; + Text text_r4{{122, 150, 118, 16}, "---"}; + + Text text_lbl_act_n{{0, 168, 120, 16}, "Actual N:"}; + Text text_act_n{{122, 168, 118, 16}, "---"}; + + Text text_lbl_act_frac{{0, 186, 120, 16}, "Actual Frac:"}; + Text text_act_frac{{122, 186, 118, 16}, "---"}; + + Text text_lbl_calc_freq{{0, 204, 120, 16}, "Calc Freq:"}; + Text text_calc_freq{{122, 204, 118, 16}, "---"}; + + Text text_status{{0, 228, 240, 44}, ""}; + + Button button_refresh{{8, 280, 72, 24}, "Refresh"}; + Button button_done{{168, 280, 64, 24}, "Done"}; +}; +#endif + +#ifdef PRALINE +class SystemDiagnosticsView : public View { + public: + SystemDiagnosticsView(NavigationView& nav); + void focus() override; + std::string title() const override { return "System Diagnostics"; }; + + private: + void refresh(); + void read_gpio_states(); + void set_sample_rate(uint32_t rate); + + Text text_title{{0, 0, 240, 16}, "System Diagnostics"}; + + // Sample Rate Section + Text text_lbl_sample{{0, 24, 42, 16}, "Rate:"}; + Text text_sample_rate{{44, 24, 50, 16}, "---"}; + Text text_lbl_fpga_decode{{96, 24, 144, 16}, "DC:? Q:? QS:?"}; + + Button button_sample_2m{{2, 44, 56, 24}, "2 MSPS"}; + Button button_sample_4m{{62, 44, 56, 24}, "4 MSPS"}; + Button button_sample_8m{{122, 44, 56, 24}, "8 MSPS"}; + Button button_sample_20m{{182, 44, 56, 24}, "20 MSPS"}; + + // Baseband Filter Section + Text text_lbl_bb_filter{{0, 76, 114, 16}, "BB Filter BW:"}; + Text text_bb_filter{{116, 76, 124, 16}, "---"}; + + Text text_lbl_reg8{{0, 94, 114, 16}, "MAX2831 R8:"}; + Text text_reg8{{116, 94, 124, 16}, "---"}; + + // GPIO States Section + Text text_lbl_gpio{{0, 118, 240, 16}, "--- GPIO Pin States ---"}; + + Text text_lbl_lpf{{0, 136, 56, 16}, "LPF:"}; + Text text_gpio_lpf{{58, 136, 181, 16}, "---"}; + + Text text_lbl_mix{{0, 154, 56, 16}, "Mixer:"}; + Text text_gpio_mix{{58, 154, 181, 16}, "---"}; + + Text text_lbl_amp{{0, 172, 56, 16}, "RF Amp:"}; + Text text_gpio_amp{{58, 172, 181, 16}, "---"}; + + // FPGA Control Section + Text text_lbl_fpga{{0, 196, 240, 16}, "--- FPGA Control ---"}; + + Text text_lbl_fpga_ctrl{{0, 214, 114, 16}, "FPGA Reg 1:"}; + Text text_fpga_ctrl{{116, 214, 124, 16}, "---"}; + + Text text_lbl_band{{0, 232, 48, 16}, "Band:"}; + Text text_band{{50, 232, 190, 16}, "---"}; + + Button button_toggle_q{{2, 252, 110, 24}, "Toggle Q Inv"}; + Button button_toggle_dc{{122, 252, 110, 24}, "Toggle DC Blk"}; + + // Control Buttons + Button button_refresh{{2, 280, 72, 24}, "Refresh"}; + Button button_done{{168, 280, 64, 24}, "Done"}; +}; +#endif + #endif class DebugPeripheralsMenuView : public BtnGridView { diff --git a/firmware/application/clock_manager.cpp b/firmware/application/clock_manager.cpp index 4e793e43d..0cac4c0fe 100644 --- a/firmware/application/clock_manager.cpp +++ b/firmware/application/clock_manager.cpp @@ -659,6 +659,8 @@ void ClockManager::set_sampling_frequency(const uint32_t frequency) { /* PRALINE: Match HackRF USB sample_rate_frac_set() * Reference: hackrf_usb radio.c lines 29-91, hackrf_core.c lines 501-685 */ + _base_band_frequency = frequency; + // Set FPGA decimation to 0 (no decimation) for direct passthrough fpga_debug_register_write(2, 0); radio::invalidate_spi_config(); diff --git a/firmware/application/clock_manager.hpp b/firmware/application/clock_manager.hpp index f3e17d8d2..999726bec 100644 --- a/firmware/application/clock_manager.hpp +++ b/firmware/application/clock_manager.hpp @@ -70,6 +70,10 @@ class ClockManager { void set_sampling_frequency(const uint32_t frequency); + uint32_t get_sampling_frequency() const { + return _base_band_frequency; + }; + void set_reference_ppb(const int32_t ppb); #ifdef PRALINE @@ -92,6 +96,8 @@ class ClockManager { si5351::Si5351& clock_generator; Reference reference; + uint32_t _base_band_frequency{20000000}; + void set_gp_clkin_to_clkin_direct(); void start_frequency_monitor_measurement(const cgu::CLK_SEL clk_sel); diff --git a/firmware/application/hw/max2831.cpp b/firmware/application/hw/max2831.cpp index 8a3a7a79f..8fb2e43d2 100644 --- a/firmware/application/hw/max2831.cpp +++ b/firmware/application/hw/max2831.cpp @@ -38,6 +38,16 @@ using namespace hackrf::one; #include #include +// Global debug tracking for MAX2831 +struct max2831_debug_t { + uint32_t requested_freq_mhz; + uint32_t calculated_n; + uint32_t calculated_frac; + bool set_frequency_called; + bool frequency_valid; +}; +extern "C" max2831_debug_t max2831_debug_info = {0, 0, 0, false, false}; + namespace max2831 { using namespace max283x; @@ -120,7 +130,8 @@ void MAX2831::init() { // set_reg_field(11, REG11_RXVGA_GAIN_MASK, 0x1F); // 62 dB VGA = MAX /* Configure baseband filter for 8 MHz TX - matches GSG reference */ - set_reg_field(8, REG8_LPF_COARSE_MASK, REG8_RX_LPF_7_5M); + // set_reg_field(8, REG8_LPF_COARSE_MASK, REG8_RX_LPF_7_5M); + set_reg_field(8, REG8_LPF_COARSE_MASK, REG8_RX_LPF_15M); set_reg_field(7, REG7_RX_LPF_FINE_MASK, REG7_RX_LPF_FINE_100); set_reg_field(7, REG7_TX_LPF_FINE_MASK, REG7_TX_LPF_FINE_100); @@ -329,9 +340,21 @@ uint32_t MAX2831::set_lpf_bandwidth_internal(const uint32_t bandwidth_hz) { void MAX2831::set_lpf_rf_bandwidth_rx(const uint32_t bandwidth_minimum) { _desired_lpf_bw = bandwidth_minimum; +#ifdef PRALINE + uint32_t actual_bw = bandwidth_minimum; + if (actual_bw < 22000000) { + actual_bw = 22000000; // Never go below 15 MHz, set by choosing 22.6 MHz bandwidth + } + + _desired_lpf_bw = actual_bw; + if (_mode == Mode::Receive || _mode == Mode::Rx_Calibration) { + set_lpf_bandwidth_internal(actual_bw); + } +#else if (_mode == Mode::Receive || _mode == Mode::Rx_Calibration) { set_lpf_bandwidth_internal(bandwidth_minimum); } +#endif } void MAX2831::set_lpf_rf_bandwidth_tx(const uint32_t bandwidth_minimum) { @@ -355,7 +378,20 @@ bool MAX2831::set_frequency(const rf::Frequency lo_frequency) { */ /* MAX2831 supports 2.3-2.6 GHz */ - if (lo_frequency < 2300000000ULL || lo_frequency > 2600000000ULL) { + // if (lo_frequency < 2300000000ULL || lo_frequency > 2600000000ULL) { + // return false; + // } + + bool valid = (lo_frequency >= 2300000000ULL && lo_frequency <= 2600000000ULL); + + // TRACK REQUEST IMMEDIATELY + max2831_debug_info.requested_freq_mhz = lo_frequency / 1000000; + max2831_debug_info.set_frequency_called = true; + max2831_debug_info.frequency_valid = valid; + + if (!valid) { + max2831_debug_info.calculated_n = 0; + max2831_debug_info.calculated_frac = 0; return false; } @@ -377,6 +413,10 @@ bool MAX2831::set_frequency(const rf::Frequency lo_frequency) { } } + // TRACK CALCULATED VALUES + max2831_debug_info.calculated_n = div_int; + max2831_debug_info.calculated_frac = div_frac; + /* Write order matters - matches GSG reference */ /* REG 3: SYN_INT (bits 7:0) and SYN_FRAC_LO (bits 13:8) */ uint16_t reg3_val = (div_int & 0xFF) | ((div_frac & 0x3F) << 8); diff --git a/firmware/application/hw/rffc507x.cpp b/firmware/application/hw/rffc507x.cpp index e2caa886d..87acc938f 100644 --- a/firmware/application/hw/rffc507x.cpp +++ b/firmware/application/hw/rffc507x.cpp @@ -197,9 +197,11 @@ struct SynthConfig { */ void RFFC507x::init() { +#ifndef PRALINE gpio_rffc5072_resetx.set(); gpio_rffc5072_resetx.output(); reset(); +#endif _bus.init(); @@ -211,10 +213,12 @@ void RFFC507x::reset() { /* TODO: Is RESETB pin ignored if sdi_ctrl.sipin=1? Programming guide * description of sdi_ctrl.sipin suggests the pin is not ignored. */ +#ifndef PRALINE gpio_rffc5072_resetx.clear(); halPolledDelay(ticks_during_reset); gpio_rffc5072_resetx.set(); halPolledDelay(ticks_after_reset); +#endif } void RFFC507x::flush() { diff --git a/firmware/application/radio.cpp b/firmware/application/radio.cpp index f57a02d36..cf844bf06 100644 --- a/firmware/application/radio.cpp +++ b/firmware/application/radio.cpp @@ -205,8 +205,10 @@ void set_direction(const rf::Direction new_direction) { #ifndef PRALINE baseband_cpld.set_invert(mixer_invert ^ baseband_invert); #else + // TEST: Force baseband invert for Praline (like r9) + // baseband_invert = (direction == rf::Direction::Receive); + // Praline: Control Q inversion via FPGA register - // Assuming register 1 bit 1 controls Q inversion uint8_t ctrl_reg = 0x01; // DC_BLOCK enabled if (mixer_invert ^ baseband_invert) { ctrl_reg |= 0x02; // Set Q_INVERT bit @@ -224,16 +226,6 @@ void set_direction(const rf::Direction new_direction) { led_rx.on(); else led_tx.on(); - - // #ifdef PRALINE - // Try with Q inversion OFF - // fpga_debug_register_write(1, 0x01); // DC_BLOCK=1, Q_INVERT=0 - // ssp1_arbiter.invalidate(); - - // If no signals, try with Q inversion ON - // fpga_debug_register_write(1, 0x03); // DC_BLOCK=1, Q_INVERT=1 - // ssp1_arbiter.invalidate(); - // #endif } bool set_tuning_frequency(const rf::Frequency frequency) { @@ -278,6 +270,17 @@ bool set_tuning_frequency(const rf::Frequency frequency) { mixer_invert = tuning_config.mixer_invert; #ifndef PRALINE baseband_cpld.set_invert(mixer_invert ^ baseband_invert); +#else + // TEST: Force baseband invert for Praline (like r9) + // baseband_invert = (direction == rf::Direction::Receive); + + // PRALINE: Update FPGA Q inversion when tuning changes + uint8_t ctrl_reg = 0x01; // DC_BLOCK enabled + if (mixer_invert ^ baseband_invert) { + ctrl_reg |= 0x02; // Set Q_INVERT bit + } + fpga_debug_register_write(1, ctrl_reg); + ssp1_arbiter.invalidate(); #endif return result_second_if; @@ -454,6 +457,27 @@ TuningInfo get_tuning_info() { namespace second_if { +#ifdef PRALINE +extern "C" { +extern struct max2831_debug_t { + uint32_t requested_freq_mhz; + uint32_t calculated_n; + uint32_t calculated_frac; + bool set_frequency_called; + bool frequency_valid; +} max2831_debug_info; +} + +MAX2831Info get_max2831_info() { + return { + max2831_debug_info.requested_freq_mhz, + max2831_debug_info.calculated_n, + max2831_debug_info.calculated_frac, + max2831_debug_info.set_frequency_called, + max2831_debug_info.frequency_valid}; +} +#endif + uint32_t register_read(const size_t register_number) { return radio::second_if->read(register_number); } @@ -468,6 +492,12 @@ int8_t temp_sense() { } /* namespace second_if */ +namespace rf_path_info { +rf::path::Band get_current_band() { + return radio::rf_path.get_band(); +} +} /* namespace rf_path_info */ + #ifdef PRALINE namespace fpga { diff --git a/firmware/application/radio.hpp b/firmware/application/radio.hpp index a24985f77..217b5a316 100644 --- a/firmware/application/radio.hpp +++ b/firmware/application/radio.hpp @@ -95,6 +95,17 @@ TuningInfo get_tuning_info(); namespace second_if { +#ifdef PRALINE +struct MAX2831Info { + uint32_t requested_freq_mhz; + uint32_t calculated_n; + uint32_t calculated_frac; + bool set_frequency_called; + bool frequency_valid; +}; +MAX2831Info get_max2831_info(); +#endif + uint32_t register_read(const size_t register_number); void register_write(const size_t register_number, uint32_t value); @@ -103,6 +114,12 @@ int8_t temp_sense(); } /* namespace second_if */ +namespace rf_path_info { + +rf::path::Band get_current_band(); + +} /* namespace rf_path_info */ + #ifdef PRALINE namespace fpga { diff --git a/firmware/application/rf_path.cpp b/firmware/application/rf_path.cpp index 66ae203d8..9a2aa90a5 100644 --- a/firmware/application/rf_path.cpp +++ b/firmware/application/rf_path.cpp @@ -258,6 +258,7 @@ void Path::set_direction(const Direction new_direction) { void Path::set_band(const Band new_band) { band = new_band; + _band = new_band; update(); } @@ -298,6 +299,7 @@ void Path::update() { /* Move to the final state by turning on required signals. */ /* LPF for low band */ + config.lpf_en = (band == Band::Low); /* RF amp when amplification requested */ @@ -307,6 +309,7 @@ void Path::update() { config.ant_bias_en_n = true; config.apply(); + #else /* HackRF One RF path control */ const auto config = get_config(direction, band, rf_amp); diff --git a/firmware/application/rf_path.hpp b/firmware/application/rf_path.hpp index e886624d8..5393df87b 100644 --- a/firmware/application/rf_path.hpp +++ b/firmware/application/rf_path.hpp @@ -58,12 +58,16 @@ class Path { void set_band(const Band band); void set_rf_amp(const bool rf_amp); + Band get_band() const { return _band; } //_band is used solely for debugging purposes. + private: Direction direction{Direction::Receive}; Band band{Band::Mid}; bool rf_amp{false}; void update(); + + Band _band{Band::Mid}; //_band is solely used of debugging purposes }; } // namespace path diff --git a/firmware/chibios-portapack/boards/PORTAPACK_APPLICATION/board.cpp b/firmware/chibios-portapack/boards/PORTAPACK_APPLICATION/board.cpp index 6d70d5a82..7aa060554 100755 --- a/firmware/chibios-portapack/boards/PORTAPACK_APPLICATION/board.cpp +++ b/firmware/chibios-portapack/boards/PORTAPACK_APPLICATION/board.cpp @@ -927,14 +927,29 @@ extern "C" void boardInit(void) { /* Configure Port A pins for RF path control */ /* PA_1 = GPIO4[8] LPF enable */ - LPC_SCU->SFSP[0xA][1] = 0xF4; /* SCU_GPIO_FAST | FUNCTION4 */ + LPC_SCU->SFSP[0xA][1] = 0xF0; /* SCU_GPIO_FAST | FUNCTION0 */ LPC_GPIO->SET[4] = (1 << 8); /* LPF enabled by default (low band) */ LPC_GPIO->DIR[4] |= (1 << 8); /* Output */ /* PA_2 = GPIO4[9] RF amp enable */ - LPC_SCU->SFSP[0xA][2] = 0xF4; /* SCU_GPIO_FAST | FUNCTION4 */ + LPC_SCU->SFSP[0xA][2] = 0xF0; /* SCU_GPIO_FAST | FUNCTION0 */ LPC_GPIO->CLR[4] = (1 << 9); /* RF amp off by default */ LPC_GPIO->DIR[4] |= (1 << 9); /* Output */ + /* Configure RFFC5072 control pins for PRALINE */ + /* P5_4 = GPIO2[13] RFFC5072 ENX (active low: 0=enabled) */ + LPC_SCU->SFSP[5][4] = 0xF0; /* FUNCTION0 (GPIO), no pulls */ + LPC_GPIO->DIR[2] &= ~(1 << 13); /* ENX: INPUT (let FPGA control) */ + + /* P5_5 = GPIO2[14] RFFC5072 RESETX - FPGA controlled, MCU should not touch */ + LPC_SCU->SFSP[5][5] = 0xF0; /* FUNCTION0 (GPIO), no pulls */ + LPC_GPIO->DIR[2] &= ~(1 << 14); /* RESETX: INPUT (let FPGA control) */ + + /* PD_11 = GPIO6[25] RFFC5072 Lock Detect (input) */ + LPC_SCU->SFSP[0xD][11] = 0xF0; /* FUNCTION0 (GPIO), no pulls */ + + /* Small delay for signals to stabilize */ + for (volatile int i = 0; i < 10000; i++) {} + /* Configure PRALINE-specific SGPIO pins for FPGA sample interface. * These override the HackRF One pin config from pins_setup. * PRALINE uses different pins than HackRF One for SGPIO4/8/9/10.