From 9d8132978f0f84579acfa3b555856d5c00913bc6 Mon Sep 17 00:00:00 2001 From: Brumi-2021 <86470699+Brumi-2021@users.noreply.github.com> Date: Sat, 9 Mar 2024 23:46:38 +0100 Subject: [PATCH] Adding Rx IQ error phase CAL to SPEC Audio_App (#1963) --- .../application/apps/analog_audio_app.cpp | 34 ++++++++++++++++--- .../application/apps/analog_audio_app.hpp | 29 ++++++++++++++-- firmware/application/hw/max2837.cpp | 33 ++++++++++++++++-- firmware/application/hw/max2837.hpp | 2 +- firmware/application/hw/max2839.cpp | 29 ++++++++++++++-- firmware/application/hw/max2839.hpp | 8 ++--- firmware/application/hw/max283x.hpp | 2 +- firmware/application/radio.cpp | 4 +++ firmware/application/radio.hpp | 1 + 9 files changed, 125 insertions(+), 17 deletions(-) diff --git a/firmware/application/apps/analog_audio_app.cpp b/firmware/application/apps/analog_audio_app.cpp index 185b79e5..8cd816d1 100644 --- a/firmware/application/apps/analog_audio_app.cpp +++ b/firmware/application/apps/analog_audio_app.cpp @@ -30,6 +30,7 @@ #include "string_format.hpp" #include "ui_freqman.hpp" #include "utility.hpp" +#include "radio.hpp" using namespace portapack; using namespace tonekey; @@ -112,10 +113,14 @@ SPECOptionsView::SPECOptionsView( : View{parent_rect} { set_style(style); - add_children({&label_config, - &options_config, - &text_speed, - &field_speed}); + add_children({ + &label_config, + &options_config, + &text_speed, + &field_speed, + &text_rx_cal, + hackrf_r9 ? &field_rx_iq_phase_cal_2839 : &field_rx_iq_phase_cal_2837 // max2839 has 6 bits [0..63], max2837 has 5 bits [0..31] + }); options_config.set_selected_index(view->get_spec_bw_index()); options_config.on_change = [this, view](size_t n, OptionsField::value_t bw) { @@ -126,6 +131,18 @@ SPECOptionsView::SPECOptionsView( field_speed.on_change = [this, view](int32_t v) { view->set_spec_trigger(v); }; + + if (hackrf_r9) { // MAX2839 has 6 bits RX IQ CAL phasse adjustment. + field_rx_iq_phase_cal_2839.set_value(view->get_spec_iq_phase_calibration_value()); // using accessor function of AnalogAudioView to read iq_phase_calibration_value from rx_audio.ini + field_rx_iq_phase_cal_2839.on_change = [this, view](int32_t v) { + view->set_spec_iq_phase_calibration_value(v); // using accessor function of AnalogAudioView to write inside SPEC submenu, register value to max283x and save it to rx_audio.ini + }; + } else { // MAX2837 has 5 bits RX IQ CAL phase adjustment. + field_rx_iq_phase_cal_2837.set_value(view->get_spec_iq_phase_calibration_value()); // using accessor function of AnalogAudioView to read iq_phase_calibration_value from rx_audio.ini + field_rx_iq_phase_cal_2837.on_change = [this, view](int32_t v) { + view->set_spec_iq_phase_calibration_value(v); // using accessor function of AnalogAudioView to write inside SPEC submenu, register value to max283x and save it to rx_audio.ini + }; + } } /* AnalogAudioView *******************************************************/ @@ -213,6 +230,15 @@ void AnalogAudioView::set_spec_bw(size_t index, uint32_t bw) { receiver_model.set_baseband_bandwidth(bw / 2); } +uint8_t AnalogAudioView::get_spec_iq_phase_calibration_value() { // define accessor functions inside AnalogAudioView to read & write real iq_phase_calibration_value + return iq_phase_calibration_value; +} + +void AnalogAudioView::set_spec_iq_phase_calibration_value(uint8_t cal_value) { // define accessor functions + iq_phase_calibration_value = cal_value; + radio::set_rx_max283x_iq_phase_calibration(iq_phase_calibration_value); +} + uint16_t AnalogAudioView::get_spec_trigger() { return spec_trigger; } diff --git a/firmware/application/apps/analog_audio_app.hpp b/firmware/application/apps/analog_audio_app.hpp index 9d78618d..b4b86a80 100644 --- a/firmware/application/apps/analog_audio_app.hpp +++ b/firmware/application/apps/analog_audio_app.hpp @@ -133,6 +133,23 @@ class SPECOptionsView : public View { 1, ' ', }; + Text text_rx_cal{ + {19 * 8, 0 * 16, 11 * 8, 1 * 16}, // 18 (x col.) x char_size, 12 (length) x 8 blanking space to delete previous chars. + "Rx_IQ_CAL "}; + NumberField field_rx_iq_phase_cal_2837{ + {28 * 8, 0 * 16}, + 2, + {0, 31}, // 5 bits IQ CAL phase adjustment. + 1, + ' ', + }; + NumberField field_rx_iq_phase_cal_2839{ + {28 * 8, 0 * 16}, + 2, + {0, 63}, // 6 bits IQ CAL phase adjustment. + 1, + ' ', + }; }; class AnalogAudioView : public View { @@ -152,14 +169,22 @@ class AnalogAudioView : public View { uint16_t get_spec_trigger(); void set_spec_trigger(uint16_t trigger); + uint8_t get_spec_iq_phase_calibration_value(); + void set_spec_iq_phase_calibration_value(uint8_t cal_value); + private: static constexpr ui::Dim header_height = 3 * 16; NavigationView& nav_; RxRadioState radio_state_{}; + uint8_t iq_phase_calibration_value{15}; // initial default RX IQ phase calibration value , used for both max2837 & max2839 app_settings::SettingsManager settings_{ - "rx_audio", app_settings::Mode::RX, - app_settings::Options::UseGlobalTargetFrequency}; + "rx_audio", + app_settings::Mode::RX, + app_settings::Options::UseGlobalTargetFrequency, + { + {"iq_phase_calibration"sv, &iq_phase_calibration_value}, // we are saving and restoring that CAL from Settings. + }}; const Rect options_view_rect{0 * 8, 1 * 16, 30 * 8, 1 * 16}; const Rect nbfm_view_rect{0 * 8, 1 * 16, 18 * 8, 1 * 16}; diff --git a/firmware/application/hw/max2837.cpp b/firmware/application/hw/max2837.cpp index f262bec9..e550f32f 100644 --- a/firmware/application/hw/max2837.cpp +++ b/firmware/application/hw/max2837.cpp @@ -150,7 +150,7 @@ void MAX2837::init() { } void MAX2837::set_tx_LO_iq_phase_calibration(const size_t v) { - /* IQ phase deg CAL adj (+4 ...-4) in 32 steps (5 bits), 00000 = +4deg (Q lags I by 94degs, default), 01111 = +0deg, 11111 = -4deg (Q lags I by 86degs) */ + /* TX IQ phase deg CAL adj (+4 ...-4) in 32 steps (5 bits), 00000 = +4deg (Q lags I by 94degs, default), 01111 = +0deg, 11111 = -4deg (Q lags I by 86degs) */ // TX calibration , Logic pins , ENABLE, RXENABLE, TXENABLE = 1,0,1 (5dec), and Reg address 16, D1 (CAL mode 1):DO (CHIP ENABLE 1) set_mode(Mode::Tx_Calibration); // write to ram 3 LOGIC Pins . @@ -324,14 +324,41 @@ bool MAX2837::set_frequency(const rf::Frequency lo_frequency) { return true; } - -void MAX2837::set_rx_lo_iq_calibration(const size_t v) { +/* +void MAX2837::set_rx_lo_iq_calibration(const size_t v) { // Original code , rewritten below _map.r.rx_top_rx_bias.RX_IQERR_SPI_EN = 1; _dirty[Register::RX_TOP_RX_BIAS] = 1; _map.r.rxrf_2.iqerr_trim = v; _dirty[Register::RXRF_2] = 1; flush(); } +*/ + +void MAX2837::set_rx_LO_iq_phase_calibration(const size_t v) { + /* RX IQ phase deg CAL adj (+4 ...-4) in 32 steps (5 bits), 00000 = +4deg (Q lags I by 94degs, default), 01111 = +0deg, 11111 = -4deg (Q lags I by 86degs) */ + + // RX calibration , Logic pins , ENABLE, RXENABLE, TXENABLE = 1,1,0 (3dec), and Reg address 16, D1 (CAL mode 1):DO (CHIP ENABLE 1) + set_mode(Mode::Rx_Calibration); // write to ram 3 LOGIC Pins . + + gpio_max283x_enable.output(); + gpio_max2837_rxenable.output(); + gpio_max2837_txenable.output(); + + _map.r.spi_en.CAL_SPI = 1; // Register Settings reg address 16, D1 (CAL mode 1) + _map.r.spi_en.EN_SPI = 1; // Register Settings reg address 16, DO (CHIP ENABLE 1) + flush_one(Register::SPI_EN); + + _map.r.rx_top_rx_bias.RX_IQERR_SPI_EN = 1; // reg 8 D9, RX LO IQ Phase calibration SPI control. Active when Address 8 D<9> = 1. + flush_one(Register::RX_TOP_RX_BIAS); + + _map.r.rxrf_2.iqerr_trim = v; // reg 1 D9:D5, RX LO I/Q Phase SPI 5 bits Adjust + flush_one(Register::RXRF_2); + + // Exit Calibration mode, Go back to reg 16, D1:D0 , Out of CALIBRATION , back to default conditions, but keep CS activated. + _map.r.spi_en.CAL_SPI = 0; // Register Settings reg address 16, D1 (0 = Normal operation (default) + _map.r.spi_en.EN_SPI = 1; // Register Settings reg address 16, DO (1 = Chip select enable ) + flush_one(Register::SPI_EN); +} void MAX2837::set_rx_bias_trim(const size_t v) { _map.r.rx_top_rx_bias.EN_Bias_Trim = 1; diff --git a/firmware/application/hw/max2837.hpp b/firmware/application/hw/max2837.hpp index 96eb2709..fb8fcc51 100644 --- a/firmware/application/hw/max2837.hpp +++ b/firmware/application/hw/max2837.hpp @@ -828,7 +828,7 @@ class MAX2837 : public MAX283x { bool set_frequency(const rf::Frequency lo_frequency) override; - void set_rx_lo_iq_calibration(const size_t v) override; + void set_rx_LO_iq_phase_calibration(const size_t v) override; void set_tx_LO_iq_phase_calibration(const size_t v) override; void set_rx_bias_trim(const size_t v); void set_vco_bias(const size_t v); diff --git a/firmware/application/hw/max2839.cpp b/firmware/application/hw/max2839.cpp index bc50b118..02f89a3f 100644 --- a/firmware/application/hw/max2839.cpp +++ b/firmware/application/hw/max2839.cpp @@ -367,13 +367,38 @@ bool MAX2839::set_frequency(const rf::Frequency lo_frequency) { return true; } - -void MAX2839::set_rx_lo_iq_calibration(const size_t v) { +/* +void MAX2839::set_rx_LO_iq_phase_calibration(const size_t v) { // Original code , rewritten below _map.r.rxrf_2.RX_IQERR_SPI_EN = 1; _dirty[Register::RXRF_2] = 1; _map.r.rxrf_1.iqerr_trim = v; _dirty[Register::RXRF_1] = 1; flush(); +}*/ + +void MAX2839::set_rx_LO_iq_phase_calibration(const size_t v) { + /* RX IQ phase deg CAL adj (+4 ...-4) in 64 steps (6 bits), 000000 = +4deg (Q lags I by 94degs, default), 011111 = +0deg, 111111 = -4deg (Q lags I by 86degs) */ + + // RX calibration , Logic pins , ENABLE, RXENABLE, TXENABLE = 1,1,0 (3dec), and Reg address 16, D1 (CAL mode 1):DO (CHIP ENABLE 1) + set_mode(Mode::Rx_Calibration); // write to ram 3 LOGIC Pins . + + gpio_max283x_enable.output(); // max2839 has only 2 x pins + regs to decide mode. + gpio_max2839_rxtx.output(); // Here is combined rx & tx pin in one port. + + _map.r.spi_en.CAL_SPI = 1; // Register Settings reg address 16, D1 (CAL mode 1) + _map.r.spi_en.EN_SPI = 1; // Register Settings reg address 16, DO (CHIP ENABLE 1) + flush_one(Register::SPI_EN); + + _map.r.rxrf_2.RX_IQERR_SPI_EN = 1; // reg 2 D<2> = 1, RX LO IQ calibration SPI control. Active when Address 2 D<2> = 1. + _dirty[Register::RXRF_2] = 1; + + _map.r.rxrf_1.iqerr_trim = v; + _dirty[Register::RXRF_1] = 1; + flush(); + + _map.r.spi_en.CAL_SPI = 0; // Register Settings reg address 16, D1 (CAL mode 1) + _map.r.spi_en.EN_SPI = 1; // Register Settings reg address 16, DO (CHIP ENABLE 1) + flush_one(Register::SPI_EN); } void MAX2839::set_rx_buff_vcm(const size_t v) { diff --git a/firmware/application/hw/max2839.hpp b/firmware/application/hw/max2839.hpp index fba590fd..98adcc02 100644 --- a/firmware/application/hw/max2839.hpp +++ b/firmware/application/hw/max2839.hpp @@ -82,11 +82,11 @@ struct RXENABLE_Type { static_assert(sizeof(RXENABLE_Type) == sizeof(reg_t), "RXENABLE_Type wrong size"); struct RXRF_1_Type { - reg_t LNAband : 1; + reg_t LNAband : 2; // Datasheet says D1:D0 , 2 bits, maybe D1 Rx_B, D0 Rx_A , (original code said wrongly ,reg_t LNAband : 1; that was of for max2837, not max2839 ) reg_t RESERVED0 : 1; reg_t MIMOmode : 1; - reg_t iqerr_trim : 5; - reg_t RESERVED1 : 6; + reg_t iqerr_trim : 6; // Datasheet says D9_D4 , that means 6 bits, (original code said wrongly ,reg_t iqerr_trim : 5; ) + reg_t RESERVED1 : 6; // we are using 16 bits , even top part mapping is not used }; static_assert(sizeof(RXRF_1_Type) == sizeof(reg_t), "RXRF_1_Type wrong size"); @@ -689,7 +689,7 @@ class MAX2839 : public MAX283x { void set_lpf_rf_bandwidth_rx(const uint32_t bandwidth_minimum) override; void set_lpf_rf_bandwidth_tx(const uint32_t bandwidth_minimum) override; bool set_frequency(const rf::Frequency lo_frequency) override; - void set_rx_lo_iq_calibration(const size_t v) override; + void set_rx_LO_iq_phase_calibration(const size_t v) override; void set_tx_LO_iq_phase_calibration(const size_t v) override; void set_rx_buff_vcm(const size_t v) override; diff --git a/firmware/application/hw/max283x.hpp b/firmware/application/hw/max283x.hpp index b25de3f8..25656a76 100644 --- a/firmware/application/hw/max283x.hpp +++ b/firmware/application/hw/max283x.hpp @@ -125,7 +125,7 @@ class MAX283x { virtual bool set_frequency(const rf::Frequency lo_frequency); - virtual void set_rx_lo_iq_calibration(const size_t v); + virtual void set_rx_LO_iq_phase_calibration(const size_t v); virtual void set_tx_LO_iq_phase_calibration(const size_t v); virtual void set_rx_buff_vcm(const size_t v); diff --git a/firmware/application/radio.cpp b/firmware/application/radio.cpp index 23b5fa44..1e00c5cd 100644 --- a/firmware/application/radio.cpp +++ b/firmware/application/radio.cpp @@ -253,6 +253,10 @@ void set_tx_max283x_iq_phase_calibration(const size_t v) { second_if->set_tx_LO_iq_phase_calibration(v); } +void set_rx_max283x_iq_phase_calibration(const size_t v) { + second_if->set_rx_LO_iq_phase_calibration(v); +} + /*void enable(Configuration configuration) { configure(configuration); } diff --git a/firmware/application/radio.hpp b/firmware/application/radio.hpp index 293b9363..56065349 100644 --- a/firmware/application/radio.hpp +++ b/firmware/application/radio.hpp @@ -55,6 +55,7 @@ void set_baseband_filter_bandwidth_tx(const uint32_t bandwidth_minimum); void set_baseband_rate(const uint32_t rate); void set_antenna_bias(const bool on); void set_tx_max283x_iq_phase_calibration(const size_t v); +void set_rx_max283x_iq_phase_calibration(const size_t v); /* Use ReceiverModel or TransmitterModel instead. */ // void enable(Configuration configuration);