diff --git a/FPGA/VNA/DFT.vhd b/FPGA/VNA/DFT.vhd new file mode 100644 index 0000000..ec333d4 --- /dev/null +++ b/FPGA/VNA/DFT.vhd @@ -0,0 +1,261 @@ +---------------------------------------------------------------------------------- +-- Company: +-- Engineer: +-- +-- Create Date: 20:38:37 09/18/2020 +-- Design Name: +-- Module Name: DFT - Behavioral +-- Project Name: +-- Target Devices: +-- Tool versions: +-- Description: +-- +-- Dependencies: +-- +-- Revision: +-- Revision 0.01 - File Created +-- Additional Comments: +-- +---------------------------------------------------------------------------------- +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; + +-- Uncomment the following library declaration if using +-- arithmetic functions with Signed or Unsigned values +use IEEE.NUMERIC_STD.ALL; + +-- Uncomment the following library declaration if instantiating +-- any Xilinx primitives in this code. +--library UNISIM; +--use UNISIM.VComponents.all; + +entity DFT is + Generic (BINS : integer); + Port ( CLK : in STD_LOGIC; + RESET : in STD_LOGIC; + PORT1 : in STD_LOGIC_VECTOR (15 downto 0); + PORT2 : in STD_LOGIC_VECTOR (15 downto 0); + NEW_SAMPLE : in STD_LOGIC; + NSAMPLES : in STD_LOGIC_VECTOR (15 downto 0); + BIN1_PHASEINC : in STD_LOGIC_VECTOR (15 downto 0); + DIFFBIN_PHASEINC : in STD_LOGIC_VECTOR (15 downto 0); + WINDOW_INC : in STD_LOGIC_VECTOR (15 downto 0); + WINDOW_TYPE : in STD_LOGIC_VECTOR (1 downto 0); + RESULT_READY : out STD_LOGIC; + OUTPUT : out STD_LOGIC_VECTOR (191 downto 0); + NEXT_OUTPUT : in STD_LOGIC); +end DFT; + +architecture Behavioral of DFT is +COMPONENT SinCos + PORT ( + clk : IN STD_LOGIC; + phase_in : IN STD_LOGIC_VECTOR(11 DOWNTO 0); + cosine : OUT STD_LOGIC_VECTOR(15 DOWNTO 0); + sine : OUT STD_LOGIC_VECTOR(15 DOWNTO 0) + ); +END COMPONENT; +COMPONENT SinCosMult + PORT ( + clk : IN STD_LOGIC; + a : IN STD_LOGIC_VECTOR(15 DOWNTO 0); + b : IN STD_LOGIC_VECTOR(15 DOWNTO 0); + p : OUT STD_LOGIC_VECTOR(31 DOWNTO 0) + ); +END COMPONENT; +COMPONENT window +PORT( + CLK : IN std_logic; + INDEX : IN std_logic_vector(6 downto 0); + WINDOW_TYPE : IN std_logic_vector(1 downto 0); + VALUE : OUT std_logic_vector(15 downto 0) + ); +END COMPONENT; + + type result is array(BINS-1 downto 0) of std_logic_vector(47 downto 0); + signal port1_real : result; + signal port1_imag : result; + signal port2_real : result; + signal port2_imag : result; + signal sample_cnt : integer range 0 to 65535; + signal bin_cnt : integer range 0 to BINS+2; + signal output_cnt : integer range 0 to BINS-1; + type States is (WaitingForSample, WindowingStart, WaitMult, WaitMult2, PhaseReady, WindowingReady, WaitSinCos, Busy, Ready); + signal state : States; + signal port1_latch : std_logic_vector(15 downto 0); + signal port2_latch : std_logic_vector(15 downto 0); + + signal window_index : std_logic_vector(6 downto 0); + signal window_cnt : unsigned( + signal window_value : std_logic_vector(15 downto 0); + + signal phase : std_logic_vector(31 downto 0); + signal phase_inc : std_logic_vector(31 downto 0); + signal sine : std_logic_vector(15 downto 0); + signal cosine : std_logic_vector(15 downto 0); + + signal mult1_a : std_logic_vector(15 downto 0); + signal mult1_b : std_logic_vector(15 downto 0); + signal mult1_p : std_logic_vector(31 downto 0); + + signal mult2_a : std_logic_vector(15 downto 0); + signal mult2_b : std_logic_vector(15 downto 0); + signal mult2_p : std_logic_vector(31 downto 0); + + signal mult3_a : std_logic_vector(15 downto 0); + signal mult3_b : std_logic_vector(15 downto 0); + signal mult3_p : std_logic_vector(31 downto 0); + + signal mult4_a : std_logic_vector(15 downto 0); + signal mult4_b : std_logic_vector(15 downto 0); + signal mult4_p : std_logic_vector(31 downto 0); +begin + + LookupTable : SinCos + PORT MAP ( + clk => CLK, + phase_in => phase(31 downto 20), + cosine => cosine, + sine => sine + ); + Mult1 : SinCosMult + PORT MAP ( + clk => CLK, + a => mult1_a, + b => mult1_b, + p => mult1_p + ); + Mult2 : SinCosMult + PORT MAP ( + clk => CLK, + a => mult2_a, + b => mult2_b, + p => mult2_p + ); + Mult3 : SinCosMult + PORT MAP ( + clk => CLK, + a => mult3_a, + b => mult3_b, + p => mult3_p + ); + Mult4 : SinCosMult + PORT MAP ( + clk => CLK, + a => mult4_a, + b => mult4_b, + p => mult4_p + ); + WindowROM: window PORT MAP( + CLK => CLK, + INDEX => window_index(15 downto 9), + WINDOW_TYPE => WINDOW_TYPE, + VALUE => window_value + ); + + process(CLK, RESET) + begin + if rising_edge(CLK) then + if RESET = '1' then + window_index <= (others => '0'); + sample_cnt <= 0; + RESULT_READY <= '1'; + state <= WaitingForSample; + else + case state is + when WaitingForSample => + RESULT_READY <= '1'; + OUTPUT <= port1_real(output_cnt) & port1_imag(output_cnt) & port2_real(output_cnt) & port2_imag(output_cnt); + if NEXT_OUTPUT = '1' then + if output_cnt < BINS - 1 then + output_cnt <= output_cnt + 1; + else + output_cnt <= 0; + end if; + end if; + if NEW_SAMPLE = '1' then + -- calculate phase for initial bin + mult1_a <= std_logic_vector(to_unsigned(sample_cnt, 16)); + mult1_b <= BIN1_PHASEINC; + mult2_a <= std_logic_vector(to_unsigned(sample_cnt, 16)); + mult2_b <= DIFFBIN_PHASEINC; + -- window ADC data + mult3_a <= PORT1; + mult3_b <= window_value; + mult4_a <= PORT2; + mult4_b <= window_value; + state <= WaitMult; + end if; + when WaitMult => + RESULT_READY <= '0'; + state <= WaitMult2; + when WaitMult2 => + RESULT_READY <= '0'; + state <= PhaseReady; + when PhaseReady => + RESULT_READY <= '0'; + -- initial phase is ready + phase <= mult1_p; + phase_inc <= mult2_p; + state <= WindowingReady; + when WindowingReady => + RESULT_READY <= '0'; + phase <= std_logic_vector(unsigned(phase)+unsigned(phase_inc)); + port1_latch <= mult3_p(31 downto 16); + port2_latch <= mult4_p(31 downto 16); + bin_cnt <= 0; + state <= WaitSinCos; + when WaitSinCos => + phase <= std_logic_vector(unsigned(phase)+unsigned(phase_inc)); + RESULT_READY <= '0'; + if bin_cnt < 4 then + bin_cnt <= bin_cnt + 1; + else + bin_cnt <= 0; + state <= BUSY; + end if; + when BUSY => + phase <= std_logic_vector(unsigned(phase)+unsigned(phase_inc)); + mult1_a <= port1_latch; + mult1_b <= sine; + mult2_a <= port1_latch; + mult2_b <= cosine; + mult3_a <= port2_latch; + mult3_b <= sine; + mult4_a <= port2_latch; + mult4_b <= cosine; + if bin_cnt >= 3 then + -- multiplier result is available, accumulate + if sample_cnt = 0 then + port1_real(bin_cnt-3) <= mult1_p; + port1_imag(bin_cnt-3) <= mult2_p; + port2_real(bin_cnt-3) <= mult3_p; + port2_imag(bin_cnt-3) <= mult4_p; + else + port1_real(bin_cnt-3) <= std_logic_vector(unsigned(port1_real(bin_cnt-3))+unsigned(mult1_p)); + port1_imag(bin_cnt-3) <= std_logic_vector(unsigned(port1_imag(bin_cnt-3))+unsigned(mult2_p)); + port2_real(bin_cnt-3) <= std_logic_vector(unsigned(port2_real(bin_cnt-3))+unsigned(mult3_p)); + --port2_imag(bin_cnt-3) <= std_logic_vector(unsigned(port2_imag(bin_cnt-3))+unsigned(mult4_p)); + end if; + end if; + if bin_cnt >= BINS+2 then + state <= WaitingForSample; + RESULT_READY <= '1'; + sample_cnt <= sample_cnt + 1; + window_index <= std_logic_vector(unsigned(window_index)+unsigned(WINDOW_INC)); + output_cnt <= 0; + else + RESULT_READY <= '0'; + bin_cnt <= bin_cnt + 1; + end if; + when others => + RESULT_READY <= '0'; + state <= WaitingForSample; + end case; + end if; + end if; + end process; + + +end Behavioral; + diff --git a/FPGA/VNA/Test_DFT.vhd b/FPGA/VNA/Test_DFT.vhd new file mode 100644 index 0000000..225b4bb --- /dev/null +++ b/FPGA/VNA/Test_DFT.vhd @@ -0,0 +1,136 @@ +-------------------------------------------------------------------------------- +-- Company: +-- Engineer: +-- +-- Create Date: 21:25:23 09/18/2020 +-- Design Name: +-- Module Name: /home/jan/Projekte/VNA2/FPGA/VNA/Test_DFT.vhd +-- Project Name: VNA +-- Target Device: +-- Tool versions: +-- Description: +-- +-- VHDL Test Bench Created by ISE for module: DFT +-- +-- Dependencies: +-- +-- Revision: +-- Revision 0.01 - File Created +-- Additional Comments: +-- +-- Notes: +-- This testbench has been automatically generated using types std_logic and +-- std_logic_vector for the ports of the unit under test. Xilinx recommends +-- that these types always be used for the top-level I/O of a design in order +-- to guarantee that the testbench will bind correctly to the post-implementation +-- simulation model. +-------------------------------------------------------------------------------- +LIBRARY ieee; +USE ieee.std_logic_1164.ALL; + +-- Uncomment the following library declaration if using +-- arithmetic functions with Signed or Unsigned values +--USE ieee.numeric_std.ALL; + +ENTITY Test_DFT IS +END Test_DFT; + +ARCHITECTURE behavior OF Test_DFT IS + + -- Component Declaration for the Unit Under Test (UUT) + + COMPONENT DFT + GENERIC(BINS : integer); + PORT( + CLK : IN std_logic; + RESET : IN std_logic; + PORT1 : IN std_logic_vector(15 downto 0); + PORT2 : IN std_logic_vector(15 downto 0); + NEW_SAMPLE : IN std_logic; + BIN1_PHASEINC : IN std_logic_vector(15 downto 0); + DIFFBIN_PHASEINC : IN std_logic_vector(15 downto 0); + WINDOW_INC : IN std_logic_vector(15 downto 0); + WINDOW_TYPE : IN std_logic_vector(1 downto 0); + RESULT_READY : OUT std_logic; + OUTPUT : out STD_LOGIC_VECTOR (191 downto 0); + NEXT_OUTPUT : IN std_logic + ); + END COMPONENT; + + + --Inputs + signal CLK : std_logic := '0'; + signal RESET : std_logic := '0'; + signal PORT1 : std_logic_vector(15 downto 0) := (others => '0'); + signal PORT2 : std_logic_vector(15 downto 0) := (others => '0'); + signal NEW_SAMPLE : std_logic := '0'; + signal BIN1_PHASEINC : std_logic_vector(15 downto 0) := (others => '0'); + signal DIFFBIN_PHASEINC : std_logic_vector(15 downto 0) := (others => '0'); + signal WINDOW_INC : std_logic_vector(15 downto 0) := (others => '0'); + signal WINDOW_TYPE : std_logic_vector(1 downto 0) := (others => '0'); + signal NEXT_OUTPUT : std_logic := '0'; + + --Outputs + signal RESULT_READY : std_logic; + signal OUTPUT : STD_LOGIC_VECTOR (191 downto 0); + + -- Clock period definitions + constant CLK_period : time := 10 ns; + +BEGIN + + -- Instantiate the Unit Under Test (UUT) + uut: DFT + GENERIC MAP(BINS => 100) + PORT MAP ( + CLK => CLK, + RESET => RESET, + PORT1 => PORT1, + PORT2 => PORT2, + NEW_SAMPLE => NEW_SAMPLE, + BIN1_PHASEINC => BIN1_PHASEINC, + DIFFBIN_PHASEINC => DIFFBIN_PHASEINC, + WINDOW_INC => WINDOW_INC, + WINDOW_TYPE => WINDOW_TYPE, + RESULT_READY => RESULT_READY, + OUTPUT => OUTPUT, + NEXT_OUTPUT => NEXT_OUTPUT + ); + + -- Clock process definitions + CLK_process :process + begin + CLK <= '0'; + wait for CLK_period/2; + CLK <= '1'; + wait for CLK_period/2; + end process; + + + -- Stimulus process + stim_proc: process + begin + -- hold reset state for 100 ns. + RESET <= '1'; + PORT1 <= "1000000000000000"; + PORT2 <= "0100000000000000"; + BIN1_PHASEINC <= "0100000000000000"; + DIFFBIN_PHASEINC <= "0010000000000000"; + WINDOW_INC <= "0000100000000000"; + wait for 100 ns; + RESET <= '0'; + wait for CLK_period*10; + NEW_SAMPLE <= '1'; + wait for CLK_period; + NEW_SAMPLE <= '0'; + --wait until RESULT_READY = '1'; + wait for CLK_period*112; + NEW_SAMPLE <= '1'; + wait for CLK_period; + NEW_SAMPLE <= '0'; + -- insert stimulus here + + wait; + end process; + +END; diff --git a/FPGA/VNA/VNA.gise b/FPGA/VNA/VNA.gise index 743defc..5c340cb 100644 --- a/FPGA/VNA/VNA.gise +++ b/FPGA/VNA/VNA.gise @@ -26,6 +26,7 @@ + @@ -37,10 +38,11 @@ + + + - - @@ -60,7 +62,6 @@ - @@ -124,106 +125,70 @@ - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - + - + + + - - - - - - + - + + + - - - + @@ -236,13 +201,12 @@ - - - - - - - + + + + + + @@ -255,90 +219,77 @@ + + + + + + + - - - - - - - - - - - + + - - - - - + + + + + + + - + + + + - - - - - - - - - + - - - - - - - - - - - + + + + + + - - - - - - - - - - + + + + + + - + + @@ -347,8 +298,9 @@ - + + @@ -358,8 +310,9 @@ - + + @@ -369,10 +322,12 @@ - - - - + + + + + + diff --git a/FPGA/VNA/VNA.xise b/FPGA/VNA/VNA.xise index 97ea6ba..4141bd4 100644 --- a/FPGA/VNA/VNA.xise +++ b/FPGA/VNA/VNA.xise @@ -23,11 +23,11 @@ - + - + @@ -126,6 +126,16 @@ + + + + + + + + + + @@ -382,8 +392,8 @@ - - + + @@ -401,7 +411,7 @@ - + @@ -453,7 +463,7 @@ - + diff --git a/Software/PC_Application/Application b/Software/PC_Application/Application index 8f7913e..6a4470e 100755 Binary files a/Software/PC_Application/Application and b/Software/PC_Application/Application differ diff --git a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp index aeb57be..1d8e3c3 100644 --- a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp +++ b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp @@ -193,9 +193,9 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window) settings.f_stop = 1050000000; ConstrainAndUpdateFrequencies(); SetRBW(10000); + settings.pointNum = 1001; settings.WindowType = 1; settings.Detector = 0; - settings.pointNum = 1001; settings.SignalID = 0; // } @@ -230,6 +230,12 @@ void SpectrumAnalyzer::NewDatapoint(Protocol::SpectrumAnalyzerResult d) void SpectrumAnalyzer::SettingsChanged() { + if(settings.f_stop - settings.f_start >= 1000) { + settings.pointNum = 1001; + } else { + settings.pointNum = settings.f_stop - settings.f_start + 1; + } + if(window->getDevice()) { window->getDevice()->Configure(settings); } diff --git a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h index d813bf4..d64db7b 100644 --- a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h +++ b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h @@ -41,7 +41,7 @@ private: Preferences &pref; - Protocol::SpectrumAnalyzerSettings settings; + Protocol::SpectrumAnalyzerSettings settings; unsigned int averages; TraceModel traceModel; TraceMarkerModel *markerModel; diff --git a/Software/PC_Application/VNA/vna.cpp b/Software/PC_Application/VNA/vna.cpp index 5ea16cf..8eed8ab 100644 --- a/Software/PC_Application/VNA/vna.cpp +++ b/Software/PC_Application/VNA/vna.cpp @@ -20,6 +20,7 @@ #include #include #include "unit.h" +#include #include "CustomWidgets/toggleswitch.h" #include "Device/manualcontroldialog.h" #include "Traces/tracemodel.h" @@ -472,7 +473,7 @@ void VNA::initializeDevice() removeDefaultCal->setEnabled(false); } // Configure initial state of device - window->getDevice()->Configure(settings); + SettingsChanged(); } void VNA::deviceDisconnected() @@ -538,6 +539,7 @@ void VNA::UpdateStatusPanel() void VNA::SettingsChanged() { + settings.suppressPeaks = pref.Acquisition.suppressPeaks ? 1 : 0; if(window->getDevice()) { window->getDevice()->Configure(settings); } diff --git a/Software/PC_Application/preferences.cpp b/Software/PC_Application/preferences.cpp index 0f42d27..32ee833 100644 --- a/Software/PC_Application/preferences.cpp +++ b/Software/PC_Application/preferences.cpp @@ -67,6 +67,7 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) : p->Startup.DefaultSweep.points = ui->StartupSweepPoints->value(); p->Startup.DefaultSweep.excitation = ui->StartupSweepLevel->value(); p->Acquisition.alwaysExciteBothPorts = ui->AcquisitionAlwaysExciteBoth->isChecked(); + p->Acquisition.suppressPeaks = ui->AcquisitionSuppressPeaks->isChecked(); accept(); }); @@ -93,6 +94,7 @@ void PreferencesDialog::setInitialGUIState() ui->StartupSweepLevel->setValue(p->Startup.DefaultSweep.excitation); ui->AcquisitionAlwaysExciteBoth->setChecked(p->Acquisition.alwaysExciteBothPorts); + ui->AcquisitionSuppressPeaks->setChecked(p->Acquisition.suppressPeaks); } void Preferences::load() diff --git a/Software/PC_Application/preferences.h b/Software/PC_Application/preferences.h index 520eb72..aa26efe 100644 --- a/Software/PC_Application/preferences.h +++ b/Software/PC_Application/preferences.h @@ -48,6 +48,7 @@ public: } Startup; struct { bool alwaysExciteBothPorts; + bool suppressPeaks; } Acquisition; private: using SettingDescription = struct { @@ -55,7 +56,7 @@ private: QString name; QVariant def; }; - const std::array descr = {{ + const std::array descr = {{ {&Startup.ConnectToFirstDevice, "Startup.ConnectToFirstDevice", true}, {&Startup.RememberSweepSettings, "Startup.RememberSweepSettings", false}, {&Startup.DefaultSweep.start, "Startup.DefaultSweep.start", 1000000.0}, @@ -64,6 +65,7 @@ private: {&Startup.DefaultSweep.bandwidth, "Startup.DefaultSweep.bandwidth", 1000.0}, {&Startup.DefaultSweep.excitation, "Startup.DefaultSweep.excitation", -10.00}, {&Acquisition.alwaysExciteBothPorts, "Acquisition.alwaysExciteBothPorts", true}, + {&Acquisition.suppressPeaks, "Acquisition.suppressPeaks", true}, }}; }; diff --git a/Software/PC_Application/preferencesdialog.ui b/Software/PC_Application/preferencesdialog.ui index 27adf98..6588806 100644 --- a/Software/PC_Application/preferencesdialog.ui +++ b/Software/PC_Application/preferencesdialog.ui @@ -241,6 +241,16 @@ + + + + <html><head/><body><p>Due to limited fractional divider settings, the source and 1.LO PLLs are not able to reach every frequency exactly. At some specific frequencies this causes the final IF to shift. At these frequencies there will be a positive or negative peak in the trace measurement that is not actually there.<br/><br/>Checking this option shifts the 2.LO for points where this could be an issue. This will remove the peaks but slow down the sweep slightly.</p></body></html> + + + Suppress invalid peaks + + + diff --git a/Software/VNA_embedded/Application/Communication/Protocol.cpp b/Software/VNA_embedded/Application/Communication/Protocol.cpp index cde6a90..0bb812d 100644 --- a/Software/VNA_embedded/Application/Communication/Protocol.cpp +++ b/Software/VNA_embedded/Application/Communication/Protocol.cpp @@ -176,6 +176,7 @@ static Protocol::SweepSettings DecodeSweepSettings(uint8_t *buf) { e.get(d.cdbm_excitation); d.excitePort1 = e.getBits(1); d.excitePort2 = e.getBits(1); + d.suppressPeaks = e.getBits(1); return d; } static int16_t EncodeSweepSettings(Protocol::SweepSettings d, uint8_t *buf, @@ -188,6 +189,7 @@ static int16_t EncodeSweepSettings(Protocol::SweepSettings d, uint8_t *buf, e.add(d.cdbm_excitation); e.addBits(d.excitePort1, 1); e.addBits(d.excitePort2, 1); + e.addBits(d.suppressPeaks, 1); return e.getSize(); } diff --git a/Software/VNA_embedded/Application/Communication/Protocol.hpp b/Software/VNA_embedded/Application/Communication/Protocol.hpp index dff2420..1b4c5b9 100644 --- a/Software/VNA_embedded/Application/Communication/Protocol.hpp +++ b/Software/VNA_embedded/Application/Communication/Protocol.hpp @@ -23,6 +23,7 @@ using SweepSettings = struct _sweepSettings { int16_t cdbm_excitation; // in 1/100 dbm uint8_t excitePort1:1; uint8_t excitePort2:1; + uint8_t suppressPeaks:1; }; using ReferenceSettings = struct _referenceSettings { diff --git a/Software/VNA_embedded/Application/Drivers/USB/usb.c b/Software/VNA_embedded/Application/Drivers/USB/usb.c index 8eb2a7b..52f9270 100644 --- a/Software/VNA_embedded/Application/Drivers/USB/usb.c +++ b/Software/VNA_embedded/Application/Drivers/USB/usb.c @@ -3,6 +3,7 @@ #include "usbd_core.h" USBD_HandleTypeDef hUsbDeviceFS; +extern PCD_HandleTypeDef hpcd_USB_FS; #define EP_DATA_IN_ADDRESS 0x81 #define EP_DATA_OUT_ADDRESS 0x01 @@ -186,7 +187,9 @@ void usb_init(usbd_callback_t callback) { USBD_Init(&hUsbDeviceFS, &FS_Desc, 0); USBD_RegisterClass(&hUsbDeviceFS, &USBD_ClassDriver); USBD_Start(&hUsbDeviceFS); - HAL_NVIC_EnableIRQ(USB_HP_IRQn); + HAL_NVIC_SetPriority(USB_HP_IRQn, 6, 0); + HAL_NVIC_EnableIRQ(USB_HP_IRQn); + HAL_NVIC_SetPriority(USB_LP_IRQn, 6, 0); HAL_NVIC_EnableIRQ(USB_LP_IRQn); } @@ -217,3 +220,12 @@ void usb_log(const char *log, uint16_t length) { // still busy, unable to send log } } + +void USB_HP_IRQHandler(void) +{ + HAL_PCD_IRQHandler(&hpcd_USB_FS); +} +void USB_LP_IRQHandler(void) +{ + HAL_PCD_IRQHandler(&hpcd_USB_FS); +} diff --git a/Software/VNA_embedded/Application/Drivers/max2871.cpp b/Software/VNA_embedded/Application/Drivers/max2871.cpp index fe72405..5a78c95 100644 --- a/Software/VNA_embedded/Application/Drivers/max2871.cpp +++ b/Software/VNA_embedded/Application/Drivers/max2871.cpp @@ -39,6 +39,9 @@ bool MAX2871::Init(uint32_t f_ref, bool doubler, uint16_t r, bool div2) { // automatically switch to integer mode if F = 0 regs[5] |= (1UL << 24); + // recommended phase setting + regs[1] |= (1UL << 15); + SetMode(Mode::LowSpur2); // for all other CP modes the PLL reports unlock condition (output signal appears to be locked) SetCPMode(CPMode::CP20); diff --git a/Software/VNA_embedded/Application/Hardware.cpp b/Software/VNA_embedded/Application/Hardware.cpp index 6c65f85..1cb2b2a 100644 --- a/Software/VNA_embedded/Application/Hardware.cpp +++ b/Software/VNA_embedded/Application/Hardware.cpp @@ -92,9 +92,9 @@ bool HW::Init(WorkRequest wr) { Si5351.Disable(SiChannel::ReferenceOut); // Both MAX2871 get a 100MHz reference - Si5351.SetCLK(SiChannel::Source, 100000000, Si5351C::PLL::A, Si5351C::DriveStrength::mA2); + Si5351.SetCLK(SiChannel::Source, HW::PLLRef, Si5351C::PLL::A, Si5351C::DriveStrength::mA2); Si5351.Enable(SiChannel::Source); - Si5351.SetCLK(SiChannel::LO1, 100000000, Si5351C::PLL::A, Si5351C::DriveStrength::mA2); + Si5351.SetCLK(SiChannel::LO1, HW::PLLRef, Si5351C::PLL::A, Si5351C::DriveStrength::mA2); Si5351.Enable(SiChannel::LO1); // 16MHz FPGA clock Si5351.SetCLK(SiChannel::FPGA, 16000000, Si5351C::PLL::A, Si5351C::DriveStrength::mA2); @@ -133,7 +133,7 @@ bool HW::Init(WorkRequest wr) { // enable source synthesizer FPGA::Enable(FPGA::Periphery::SourceChip); FPGA::SetMode(FPGA::Mode::SourcePLL); - Source.Init(100000000, false, 1, false); + Source.Init(HW::PLLRef, false, 1, false); Source.SetPowerOutA(MAX2871::Power::n4dbm); // output B is not used Source.SetPowerOutB(MAX2871::Power::n4dbm, false); @@ -150,7 +150,7 @@ bool HW::Init(WorkRequest wr) { FPGA::Disable(FPGA::Periphery::SourceChip); FPGA::Enable(FPGA::Periphery::LO1Chip); FPGA::SetMode(FPGA::Mode::LOPLL); - LO1.Init(100000000, false, 1, false); + LO1.Init(HW::PLLRef, false, 1, false); LO1.SetPowerOutA(MAX2871::Power::n4dbm); LO1.SetPowerOutB(MAX2871::Power::n4dbm); if(!LO1.BuildVCOMap()) { diff --git a/Software/VNA_embedded/Application/Hardware.hpp b/Software/VNA_embedded/Application/Hardware.hpp index 0b6dde7..68bddac 100644 --- a/Software/VNA_embedded/Application/Hardware.hpp +++ b/Software/VNA_embedded/Application/Hardware.hpp @@ -6,8 +6,11 @@ namespace HW { static constexpr uint32_t ADCSamplerate = 914000; -static constexpr uint32_t IF1 = 60100000; +static constexpr uint32_t IF1 = 60000000; static constexpr uint32_t IF2 = 250000; +static constexpr uint32_t LO1_minFreq = 25000000; +static constexpr uint32_t MaxSamples = 130944; +static constexpr uint32_t PLLRef = 100000000; enum class Mode { Idle, diff --git a/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp b/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp index 004dc75..e79db7d 100644 --- a/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp +++ b/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp @@ -18,6 +18,7 @@ static uint32_t sampleNum; static Protocol::PacketInfo p; static bool active = false; static uint32_t lastLO2; +static uint32_t actualRBW; static float port1Measurement, port2Measurement; @@ -40,20 +41,42 @@ static void StartNextSample() { FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, 1120); break; case 1: - // Shift first LO to other side - LO1freq = freq - HW::IF1; LO2freq = HW::IF1 - HW::IF2; - break; + // Shift first LO to other side + // depending on the measurement frequency this is not possible or additive mixing has to be used + if(freq >= HW::IF1 + HW::LO1_minFreq) { + // frequency is high enough to shift 1.LO below measurement frequency + LO1freq = freq - HW::IF1; + break; + } else if(freq <= HW::IF1 - HW::LO1_minFreq) { + // frequency is low enough to add 1.LO to measurement frequency + LO1freq = HW::IF1 - freq; + break; + } + // unable to reach required frequency with 1.LO, skip this signal ID step + signalIDstep++; + /* no break */ case 2: // Shift both LOs to other side LO1freq = freq + HW::IF1; LO2freq = HW::IF1 + HW::IF2; break; case 3: - // Shift second LO to other side - LO1freq = freq - HW::IF1; LO2freq = HW::IF1 + HW::IF2; - break; + // Shift second LO to other side + // depending on the measurement frequency this is not possible or additive mixing has to be used + if(freq >= HW::IF1 + HW::LO1_minFreq) { + // frequency is high enough to shift 1.LO below measurement frequency + LO1freq = freq - HW::IF1; + break; + } else if(freq <= HW::IF1 - HW::LO1_minFreq) { + // frequency is low enough to add 1.LO to measurement frequency + LO1freq = HW::IF1 - freq; + break; + } + // unable to reach required frequency with 1.LO, skip this signal ID step + signalIDstep++; + /* no break */ case 4: // Use default frequencies with different ADC samplerate to remove images in final IF LO1freq = freq + HW::IF1; @@ -66,7 +89,7 @@ static void StartNextSample() { int32_t LO1deviation = (int64_t) LO1.GetActualFrequency() - LO1freq; LO2freq += LO1deviation; // only adjust LO2 PLL if necessary (if the deviation is significantly less than the RBW it does not matter) - if((uint32_t) abs(LO2freq - lastLO2) > s.RBW / 2) { + if((uint32_t) abs(LO2freq - lastLO2) > actualRBW / 2) { Si5351.SetCLK(SiChannel::Port1LO2, LO2freq, Si5351C::PLL::B, Si5351C::DriveStrength::mA2); Si5351.SetCLK(SiChannel::Port2LO2, LO2freq, Si5351C::PLL::B, Si5351C::DriveStrength::mA2); lastLO2 = LO2freq; @@ -88,19 +111,23 @@ void SA::Setup(Protocol::SpectrumAnalyzerSettings settings) { // in almost all cases a full sweep requires more points than the FPGA can handle at a time // individually start each point and do the sweep in the uC FPGA::SetNumberOfPoints(1); - // calculate amount of required points - points = 2 * (s.f_stop - s.f_start) / s.RBW; - // adjust to integer multiple of requested result points (in order to have the same amount of measurements in each bin) - points += s.pointNum - points % s.pointNum; - binSize = points / s.pointNum; - LOG_DEBUG("%u displayed points, resulting in %lu points and bins of size %u", s.pointNum, points, binSize); // calculate required samples per measurement for requested RBW // see https://www.tek.com/blog/window-functions-spectrum-analyzers for window factors constexpr float window_factors[4] = {0.89f, 2.23f, 1.44f, 3.77f}; sampleNum = HW::ADCSamplerate * window_factors[s.WindowType] / s.RBW; // round up to next multiple of 128 sampleNum += 128 - sampleNum%128; + if(sampleNum >= HW::MaxSamples) { + sampleNum = HW::MaxSamples; + } + actualRBW = HW::ADCSamplerate * window_factors[s.WindowType] / sampleNum; FPGA::SetSamplesPerPoint(sampleNum); + // calculate amount of required points + points = 2 * (s.f_stop - s.f_start) / actualRBW; + // adjust to integer multiple of requested result points (in order to have the same amount of measurements in each bin) + points += s.pointNum - points % s.pointNum; + binSize = points / s.pointNum; + LOG_DEBUG("%u displayed points, resulting in %lu points and bins of size %u", s.pointNum, points, binSize); // set initial state pointCnt = 0; // enable the required hardware resources diff --git a/Software/VNA_embedded/Application/VNA.cpp b/Software/VNA_embedded/Application/VNA.cpp index 9b5927a..5086a6c 100644 --- a/Software/VNA_embedded/Application/VNA.cpp +++ b/Software/VNA_embedded/Application/VNA.cpp @@ -14,10 +14,6 @@ #define LOG_MODULE "VNA" #include "Log.h" -static constexpr uint32_t IF1 = 60100000; -static constexpr uint32_t IF1_alternate = 57000000; -static constexpr uint32_t IF2 = 250000; - static VNA::SweepCallback sweepCallback; static Protocol::SweepSettings settings; static uint16_t pointCnt; @@ -27,11 +23,10 @@ static bool active = false; using IFTableEntry = struct { uint16_t pointCnt; - uint32_t IF1; uint8_t clkconfig[8]; }; -static constexpr uint16_t IFTableNumEntries = 100; +static constexpr uint16_t IFTableNumEntries = 500; static IFTableEntry IFTable[IFTableNumEntries]; static uint16_t IFTableIndexCnt = 0; @@ -58,6 +53,7 @@ bool VNA::Setup(Protocol::SweepSettings s, SweepCallback cb) { uint32_t samplesPerPoint = (HW::ADCSamplerate / s.if_bandwidth); // round up to next multiple of 128 (128 samples are spread across 35 IF2 periods) samplesPerPoint = ((uint32_t) ((samplesPerPoint + 127) / 128)) * 128; + uint32_t actualBandwidth = HW::ADCSamplerate / samplesPerPoint; // has to be one less than actual number of samples FPGA::SetSamplesPerPoint(samplesPerPoint); @@ -70,89 +66,82 @@ bool VNA::Setup(Protocol::SweepSettings s, SweepCallback cb) { attenuator = (-1000 - s.cdbm_excitation) / 25; } - uint32_t last_IF1 = IF1; + uint32_t last_LO2 = HW::IF1 - HW::IF2; + Si5351.SetCLK(SiChannel::Port1LO2, last_LO2, Si5351C::PLL::B, Si5351C::DriveStrength::mA2); + Si5351.SetCLK(SiChannel::Port2LO2, last_LO2, Si5351C::PLL::B, Si5351C::DriveStrength::mA2); + Si5351.SetCLK(SiChannel::RefLO2, last_LO2, Si5351C::PLL::B, Si5351C::DriveStrength::mA2); + Si5351.ResetPLL(Si5351C::PLL::B); IFTableIndexCnt = 0; bool last_lowband = false; + if(!s.suppressPeaks) { + // invalidate first entry of IFTable, preventing switing of 2.LO in halted callback + IFTable[0].pointCnt = 0xFFFF; + } + // Transfer PLL configuration to FPGA for (uint16_t i = 0; i < points; i++) { uint64_t freq = s.f_start + (s.f_stop - s.f_start) * i / (points - 1); // SetFrequency only manipulates the register content in RAM, no SPI communication is done. // No mode-switch of FPGA necessary here. - // Check which IF frequency is a better fit - uint32_t used_IF = IF1; -// if (freq < 290000000) { -// // for low frequencies the harmonics of the IF and source frequency should not be too close -// uint32_t dist_primary; -// if(freq < IF1) { -// dist_primary = IF1 - freq * (IF1 / freq); -// if (dist_primary > freq / 2) { -// dist_primary = freq - dist_primary; -// } -// } else { -// dist_primary = freq - IF1 * (freq / IF1); -// if (dist_primary > IF1 / 2) { -// dist_primary = IF1 - dist_primary; -// } -// } -// uint32_t dist_alternate; -// if(freq < IF1_alternate) { -// dist_alternate = IF1_alternate - freq * (IF1_alternate / freq); -// if (dist_alternate > freq / 2) { -// dist_alternate = freq - dist_primary; -// } -// } else { -// dist_alternate = freq - IF1_alternate * (freq / IF1_alternate); -// if (dist_alternate > IF1_alternate / 2) { -// dist_alternate = IF1_alternate - dist_primary; -// } -// } -// if(dist_alternate > dist_primary) { -// used_IF = IF1_alternate; -// } -// LOG_INFO("Distance: %lu/%lu", dist_primary, dist_alternate); -// } bool needs_halt = false; - if (used_IF != last_IF1) { - last_IF1 = used_IF; - LOG_INFO("Changing IF1 to %lu at point %u (f=%lu)", used_IF, i, (uint32_t) freq); - needs_halt = true; - if (IFTableIndexCnt >= IFTableNumEntries) { - LOG_ERR("IF table full, unable to add new entry"); - return false; - } - IFTable[IFTableIndexCnt].pointCnt = i; - IFTable[IFTableIndexCnt].IF1 = used_IF; - // Configure LO2 for the changed IF1. This is not necessary right now but it will generate - // the correct clock settings - Si5351.SetCLK(SiChannel::RefLO2, used_IF + IF2, Si5351C::PLL::A, Si5351C::DriveStrength::mA2); - // store calculated clock configuration for later change - Si5351.ReadRawCLKConfig(1, IFTable[IFTableIndexCnt].clkconfig); - IFTableIndexCnt++; - } + uint64_t actualSourceFreq; bool lowband = false; if (freq < BandSwitchFrequency) { needs_halt = true; lowband = true; + actualSourceFreq = freq; } else { Source.SetFrequency(freq); + actualSourceFreq = Source.GetActualFrequency(); } if (last_lowband && !lowband) { // additional halt before first highband point to enable highband source needs_halt = true; } - LO1.SetFrequency(freq + used_IF); + LO1.SetFrequency(freq + HW::IF1); + uint32_t actualFirstIF = LO1.GetActualFrequency() - actualSourceFreq; + uint32_t actualFinalIF = actualFirstIF - last_LO2; + uint32_t IFdeviation = abs(actualFinalIF - HW::IF2); + bool needs_LO2_shift = false; + if(IFdeviation > actualBandwidth / 2) { + needs_LO2_shift = true; + } + if (s.suppressPeaks && needs_LO2_shift) { + if (IFTableIndexCnt < IFTableNumEntries) { + // still room in table + LOG_INFO("Changing 2.LO at point %lu to reach correct 2.IF frequency"); + needs_halt = true; + IFTable[IFTableIndexCnt].pointCnt = i; + // Configure LO2 for the changed IF1. This is not necessary right now but it will generate + // the correct clock settings + last_LO2 = actualFirstIF - HW::IF2; + Si5351.SetCLK(SiChannel::RefLO2, last_LO2, + Si5351C::PLL::B, Si5351C::DriveStrength::mA2); + // store calculated clock configuration for later change + Si5351.ReadRawCLKConfig(1, IFTable[IFTableIndexCnt].clkconfig); + IFTableIndexCnt++; + needs_LO2_shift = false; + } + } + if(needs_LO2_shift) { + // if shift is still needed either peak suppression is disabled or no more room in IFTable was available + LOG_WARN( + "PLL deviation of %luHz for measurement at %lu%06luHz, will cause a peak", + IFdeviation, (uint32_t ) (freq / 1000000), (uint32_t ) (freq % 1000000)); + } + FPGA::WriteSweepConfig(i, lowband, Source.GetRegisters(), LO1.GetRegisters(), attenuator, freq, FPGA::SettlingTime::us20, FPGA::Samples::SPPRegister, needs_halt); last_lowband = lowband; } -// // revert clk configuration to previous value (might have been changed in sweep calculation) -// Si5351.SetCLK(1, IF1 + IF2, Si5351C::PLL::B, Si5351C::DriveStrength::mA2); -// Si5351.ResetPLL(Si5351C::PLL::B); + // revert clk configuration to previous value (might have been changed in sweep calculation) + Si5351.SetCLK(SiChannel::RefLO2, HW::IF1 - HW::IF2, Si5351C::PLL::B, Si5351C::DriveStrength::mA2); + Si5351.ResetPLL(Si5351C::PLL::B); // Enable mixers/amplifier/PLLs FPGA::SetWindow(FPGA::Window::None); FPGA::Enable(FPGA::Periphery::Port1Mixer); @@ -249,15 +238,15 @@ void VNA::SweepHalted() { } LOG_DEBUG("Halted before point %d", pointCnt); // Check if IF table has entry at this point -// if (IFTable[IFTableIndexCnt].pointCnt == pointCnt) { -// LOG_DEBUG("Shifting IF to %lu at point %u", -// IFTable[IFTableIndexCnt].IF1, pointCnt); -// Si5351.WriteRawCLKConfig(1, IFTable[IFTableIndexCnt].clkconfig); -// Si5351.WriteRawCLKConfig(4, IFTable[IFTableIndexCnt].clkconfig); -// Si5351.WriteRawCLKConfig(5, IFTable[IFTableIndexCnt].clkconfig); -// Si5351.ResetPLL(Si5351C::PLL::B); -// IFTableIndexCnt++; -// } + if (IFTable[IFTableIndexCnt].pointCnt == pointCnt) { + Si5351.WriteRawCLKConfig(SiChannel::Port1LO2, IFTable[IFTableIndexCnt].clkconfig); + Si5351.WriteRawCLKConfig(SiChannel::Port2LO2, IFTable[IFTableIndexCnt].clkconfig); + Si5351.WriteRawCLKConfig(SiChannel::RefLO2, IFTable[IFTableIndexCnt].clkconfig); + Si5351.ResetPLL(Si5351C::PLL::B); + IFTableIndexCnt++; + // PLL reset causes the 2.LO to turn off briefly and then ramp on back, needs delay before next point + Delay::us(1300); + } uint64_t frequency = settings.f_start + (settings.f_stop - settings.f_start) * pointCnt / (settings.points - 1); diff --git a/Software/VNA_embedded/Inc/stm32g4xx_it.h b/Software/VNA_embedded/Inc/stm32g4xx_it.h index 9286a70..d018514 100644 --- a/Software/VNA_embedded/Inc/stm32g4xx_it.h +++ b/Software/VNA_embedded/Inc/stm32g4xx_it.h @@ -57,8 +57,6 @@ void DMA1_Channel1_IRQHandler(void); void DMA1_Channel2_IRQHandler(void); void DMA1_Channel3_IRQHandler(void); void DMA1_Channel4_IRQHandler(void); -void USB_HP_IRQHandler(void); -void USB_LP_IRQHandler(void); void TIM1_TRG_COM_TIM17_IRQHandler(void); void UCPD1_IRQHandler(void); /* USER CODE BEGIN EFP */ diff --git a/Software/VNA_embedded/Src/stm32g4xx_hal_msp.c b/Software/VNA_embedded/Src/stm32g4xx_hal_msp.c index 69dadea..54baf42 100644 --- a/Software/VNA_embedded/Src/stm32g4xx_hal_msp.c +++ b/Software/VNA_embedded/Src/stm32g4xx_hal_msp.c @@ -507,11 +507,6 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef* hpcd) /* Peripheral clock enable */ __HAL_RCC_USB_CLK_ENABLE(); - /* USB interrupt Init */ - HAL_NVIC_SetPriority(USB_HP_IRQn, 6, 0); -// HAL_NVIC_EnableIRQ(USB_HP_IRQn); - HAL_NVIC_SetPriority(USB_LP_IRQn, 6, 0); -// HAL_NVIC_EnableIRQ(USB_LP_IRQn); /* USER CODE BEGIN USB_MspInit 1 */ /* USER CODE END USB_MspInit 1 */ @@ -541,9 +536,6 @@ void HAL_PCD_MspDeInit(PCD_HandleTypeDef* hpcd) */ HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11|GPIO_PIN_12); - /* USB interrupt DeInit */ - HAL_NVIC_DisableIRQ(USB_HP_IRQn); - HAL_NVIC_DisableIRQ(USB_LP_IRQn); /* USER CODE BEGIN USB_MspDeInit 1 */ /* USER CODE END USB_MspDeInit 1 */ diff --git a/Software/VNA_embedded/Src/stm32g4xx_it.c b/Software/VNA_embedded/Src/stm32g4xx_it.c index 7e4e40d..6780735 100644 --- a/Software/VNA_embedded/Src/stm32g4xx_it.c +++ b/Software/VNA_embedded/Src/stm32g4xx_it.c @@ -60,7 +60,6 @@ extern DMA_HandleTypeDef hdma_spi1_rx; extern DMA_HandleTypeDef hdma_spi1_tx; extern TIM_HandleTypeDef htim1; -extern PCD_HandleTypeDef hpcd_USB_FS; extern TIM_HandleTypeDef htim17; /* USER CODE BEGIN EV */ @@ -219,34 +218,6 @@ void DMA1_Channel4_IRQHandler(void) /* USER CODE END DMA1_Channel4_IRQn 1 */ } -/** - * @brief This function handles USB high priority interrupt remap. - */ -void USB_HP_IRQHandler(void) -{ - /* USER CODE BEGIN USB_HP_IRQn 0 */ - - /* USER CODE END USB_HP_IRQn 0 */ - HAL_PCD_IRQHandler(&hpcd_USB_FS); - /* USER CODE BEGIN USB_HP_IRQn 1 */ - - /* USER CODE END USB_HP_IRQn 1 */ -} - -/** - * @brief This function handles USB low priority interrupt remap. - */ -void USB_LP_IRQHandler(void) -{ - /* USER CODE BEGIN USB_LP_IRQn 0 */ - - /* USER CODE END USB_LP_IRQn 0 */ - HAL_PCD_IRQHandler(&hpcd_USB_FS); - /* USER CODE BEGIN USB_LP_IRQn 1 */ - - /* USER CODE END USB_LP_IRQn 1 */ -} - /** * @brief This function handles TIM1 trigger and commutation interrupts and TIM17 global interrupt. */ diff --git a/Software/VNA_embedded/VNA_embedded.ioc b/Software/VNA_embedded/VNA_embedded.ioc index 217582d..50997e1 100644 --- a/Software/VNA_embedded/VNA_embedded.ioc +++ b/Software/VNA_embedded/VNA_embedded.ioc @@ -165,8 +165,6 @@ NVIC.TIM1_TRG_COM_TIM17_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:true NVIC.TimeBase=TIM1_TRG_COM_TIM17_IRQn NVIC.TimeBaseIP=TIM17 NVIC.UCPD1_IRQn=true\:6\:0\:true\:false\:true\:true\:true\:false -NVIC.USB_HP_IRQn=true\:6\:0\:true\:false\:true\:true\:true\:true -NVIC.USB_LP_IRQn=true\:6\:0\:true\:false\:true\:true\:true\:true NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false PA1.GPIOParameters=GPIO_Label PA1.GPIO_Label=FPGA_AUX1