Server
All the functionality of the feedback loop module and data acquisition framework are controlled using a dedicated library written in C, which provides Application Programming Interface (API) for all the necessary operations: setting parameters for the feedback loop on FPGA, collecting the analog data from AD converters and driving DA converters or serial communication for microscope scanner. Using this library, a server is created, which purpose is to provide communication interface for microscope control software set up on user's side, which can be of different complexity, ranging from a small script to a complex graphical user interface with live view on all the scanning parameters and data.
The server operation is driven by timer interrupts that are periodically updating the values red from AD converters, from FPGA and passing the data for DA converters.
The maximum possible frequency of timer interrupts is the main bottleneck of using the server running on a processor for the SPM control. Depeding on use of SPI interfaces, which are the key limiting factor, the speed of this periodic update loop is 2–10 kHz.
It should be however noted that this limitation does not apply to the feedback loop which keeps the probe-sample position constant, which is performed independently on the FPGA with few orders of magnitude higher frequency, allowing even scans with speed of millimeters per second if the scanner is capable of doing this.
Interface
The communication interface is realised via sockets passed over Ethernet. Communication is done on a client-server basis, where the client (SPM control programme operated by user) sends some message to the server and gets some response.
Messages are passed as GwyFileObject structures serialised by Gwyfile library, with a variable number of items inside, depending on the message type and user’s wishes on the number of parameters that should be passed at the moment.
For deserialization, the Gwyfile library can be used again, or custom implementation based on the Gwyddion file format description can be done.
Messages can be simple, e.g. to set the system into the feedback, or complex, e.g. to set all the feedback parameters or collect multiple arrays of scanned data. See the details of the communication commands here.
Hardware configuration
Hwserver parameters are in a separate file, either the hwserver.ini file read from default location (working directory), or file provided as a parameter of the hwserver. Ini file includes all the information that is hardware dependent and should be set while physically setting up the system.
A sample hwserver.ini with a brief explanatory description of all the fields is provided below. It starts with a general section that contains most of the hardware-specific information, including aspects that are related to physical location of jumpers on boards. This should be set up when the microscope is put into operation and should not be changed without a reason. This section also contains calibration information for the RP fast inputs and outputs, for each potential configuration separately. After the general section various scanning modes can be defined. This includes parameters that should be switched when the particular measurement mode is set: input signals, source data for the feedback loop, use of PLL, scaling the error signal via bit-shifting on FPGA, timestep for running the main loop on CPU, FPGA outputs and many more.
; hwserver config file. Don't change this unless you know what you're are doing and match the settings to the hardware [general] debug = 0 ; report some extra information useful for debugging [hardware] ; System parameters scan_mode = 1 ; 0: nothing, 1: voltage scan, 2: PI RS232 scan, 3: RP two axis controller scan xrange = 100e-6 ; x scanner physical range in m yrange = 100e-6 ; y scanner physical range in m zrange = 10e-6 ; z scanner physical range in m hrdac_regime = 0 ; 0: 20bit DACs controlled by FPGA, 1: 20bit DACs controlled by CPU, 2: mixed, 2xCPU + 1xFPGA, hrdac1_range = 1 ; 20bit xy piezo range: 0: +-10 V, 1: 0-10 V, check jumpers hrdac2_range = 1 ; 20bit xy piezo range: 0: +-10 V, 1: 0-10 V, check jumpers hrdac3_range = 1 ; 20bit xy piezo range: 0: +-10 V, 1: 0-10 V, check jumpers rp1_input_hv = 0 ; use this if RP fast ADC1 jumper is on HV range, which normally should not rp2_input_hv = 0 ; use this if RP fast ADC1 jumper is on HV range, which normally should not rp_bare_input = 0 ; RP fast inputs are used directly, without the divider board rp_bare_output = 0 ; RP fast outputs are used directly without scaling to +-10 dds1_range = 0 ; dds1 output amplitude range: 0: +-1 V, 1: 0-1 V (scaled and shifted) dds2_range = 0 ; dds2 output amplitude range: 0: +-1 V, 1: 0-1 V (scaled and shifted) timestep = 500000 ; time step for most of the operations, in nanoseconds. was 3000000 for PI table, 1000000 for voltage scan, interrupted fine up to 200000 oversampling = 6 ; slow ADC oversampling factor, 0 (none oversampling) to 6 (highest oversampling). triggerout = 0 ; external data storage trigger output: 0: nothing, 1-4: the former slow analog output pins on the RP extension connector x_external = 0 ; use external sensor providing voltage and interpret it as position y_external = 0 ; use external sensor providing voltage and interpret it as position x_external_source = 0 ; slow ADC channel to be used as x position y_external_source = 1 ; slow ADC channel to be used as y position x_external_offset = 1 ; external x sensor voltage offset y_external_offset = 1 ; external y sensor voltage offset x_external_slope = 1e-6 ; external x sensor conversion factor m/V y_external_slope = 1e-6 ; external y sensor conversion factor m/V rpadc1_bare_lv_offset = 0.0 ; bare RP1 input, LV jumper, zero offset rpadc1_bare_lv_slope = 8191 ; bare RP1 input, LV jumper, slope (integer corresponding to 1V) rpadc1_bare_hv_offset = 0.0 ; bare RP1 input, HV jumper, zero offset rpadc1_bare_hv_slope = 410 ; bare RP1 input, HV jumper, slope (integer corresponding to 1V) rpadc2_bare_lv_offset = 0.0 ; bare RP2 input, LV jumper, zero offset rpadc2_bare_lv_slope = 8191 ; bare RP2 input, LV jumper, slope (integer corresponding to 1V) rpadc2_bare_hv_offset = 0.0 ; bare RP2 input, HV jumper, zero offset rpadc2_bare_hv_slope = 410 ; bare RP2 input, HV jumper, slope (integer corresponding to 1V) rpadc1_divhigh_lv_offset = 78 ; RP1 with input1_range=1 and LV jumper (silly choice), zero offset rpadc1_divhigh_lv_slope = 720 ; RP1 with input1_range=1 and LV jumper (silly choice), slope (integer corresponding to 1V) rpadc1_divhigh_hv_offset = 42 ; RP1 with input1_range=1 and HV jumper, zero offset rpadc1_divhigh_hv_slope = 29 ; RP1 with input1_range=1 and HV jumper, slope (integer corresponding to 1V) rpadc1_divlow_lv_offset = 120 ; RP1 with input1_range=0 and LV jumper, zero offset rpadc1_divlow_lv_slope = 7200 ; RP1 with input1_range=0 and LV jumper, slope (integer corresponding to 1V) rpadc1_divlow_hv_offset = 48 ; RP1 with input1_range=0 and HV jumper (silly choice), zero offset rpadc1_divlow_hv_slope = 290 ; RP1 with input1_range=0 and HV jumper (silly choice), slope (integer corresponding to 1V) rpadc2_divhigh_lv_offset = 78 ; RP2 with input1_range=1 and LV jumper (silly choice), zero offset rpadc2_divhigh_lv_slope = 720 ; RP2 with input1_range=1 and LV jumper (silly choice), slope (integer corresponding to 1V) rpadc2_divhigh_hv_offset = 48 ; RP2 with input1_range=1 and HV jumper, zero offset rpadc2_divhigh_hv_slope = 29 ; RP2 with input1_range=1 and HV jumper, slope (integer corresponding to 1V) rpadc2_divlow_lv_offset = 120 ; RP2 with input1_range=0 and LV jumper, zero offset rpadc2_divlow_lv_slope = 7200 ; RP2 with input1_range=0 and LV jumper, slope (integer corresponding to 1V) rpadc2_divlow_hv_offset = 48 ; RP2 with input1_range=0 and HV jumper (silly choice), zero offset rpadc2_divlow_hv_slope = 290 ; RP2 with input1_range=0 and HV jumper (silly choice), slope (integer corresponding to 1V) rpdac1_bare_offset = 0 ; bare RP1 output zero offset rpdac1_bare_slope = 8191 ; bare RP1 output zero slope rpdac2_bare_offset = 0 ; bare RP2 output zero offset rpdac2_bare_slope = 8191 ; bare RP2 output zero slope rpdac1_offset = 0 ; RP1 output zero offset rpdac1_slope = 819.1 ; RP1 output zero slope rpdac2_offset = 0 ; RP2 output zero offset rpdac2_slope = 819.1 ; RP2 output zero slope [mode0] name = off ; name of the measurement mode mux1 = 4 ; ADC channel routed to RP1 mux2 = 5 ; ADC channel routed to RP2 error_source = 2 ; error source signal (0: off, 1: RP1, 2: RP2, 3: A1, 4: P1, 5: A2, 6: P2, 7: freq) swap_in = 0 ; swap error signal direction (0: lower signal retracts piezo, 1: higher signal retract piezo) swap_out = 0 ; swap zpiezo signal direction error_bit_shift = 0 ; error signal bit shift (0-31) pll = 0 ; pll on/off, this also generates freq signal for feedback pll_input = 0 ; 0: phase1, 1: phase2 input1_range = 0 ; RP1 input range (0: +-1 V 1: +-10 V) input2_range = 0 ; RP2 input range (0: +-1 V 1: +-10 V) lockin1_hr = 1 ; high resolution low signal option for lockin 1 lockin2_hr = 1 ; high resolution low signal option for lockin 2 pidskip = 1 ; PID speed factor. 0: 125 MHz, 1: 1 MHz, 2: 120 kHz, 3: 15 kHz pllskip = 0 ; PLL speed factor. 0: 125 MHz, 1: 1 MHz, 2: 120 kHz, 3: 15 kHz lockin1_filter_amplitude = 0 ; filter amplitude in lockin1 output lockin1_filter_phase = 1 ; filter phase in lockin1 output lockin2_filter_amplitude = 0 ; filter amplitude in lockin2 output lockin2_filter_phase = 0 ; filter phase in lockin2 output out1 = 0 ; RP output routing, default 0: gen1 excitation (0: gen1, 1: gen2, 2: dac1, 3: dac2, 4: pid, 5: off, 6: excs) out2 = 4 ; RP output routing, default 4: pid result (0: gen1, 1: gen2, 2: dac1, 3: dac2, 4: pid, 5: off, 6: excs) outhr = 4 ; RP output routing, default 4: pid result (0: gen1, 1: gen2, 2: hrdac, 4: pid, 5: off, 6: excs) [mode1] name = proportional ; name of the measurement mode mux1 = 4 ; ADC channel routed to RP1 mux2 = 5 ; ADC channel routed to RP2 error_source = 1 ; error source signal (0: off, 1: RP1, 2: RP2, 3: A1, 4: P1, 5: A2, 6: P2, 7: freq) swap_in = 1 ; swap error signal direction (0: lower signal retracts piezo, 1: higher signal retract piezo) swap_out = 0 ; swap zpiezo signal direction error_bit_shift = 3 ; error signal bit shift (0-31), was 3 pll = 0 ; pll on/off, this also generates freq signal for feedback pll_input = 0 ; 0: phase1, 1: phase2 input1_range = 1 ; RP1 input range (0: +-1 V 1: +-10 V) input2_range = 1 ; RP2 input range (0: +-1 V 1: +-10 V) lockin1_hr = 0 ; high resolution low signal option for lockin 1 lockin2_hr = 0 ; high resolution low signal option for lockin 2 pidskip = 1 ; PID speed factor. 0: 125 MHz, 1: 1 MHz, 2: 120 kHz, 3: 15 kHz pllskip = 0 ; PLL speed factor. 0: 125 MHz, 1: 1 MHz, 2: 120 kHz, 3: 15 kHz lockin1_filter_amplitude = 0 ; filter amplitude in lockin1 output lockin1_filter_phase = 1 ; filter phase in lockin1 output lockin2_filter_amplitude = 0 ; filter amplitude in lockin2 output lockin2_filter_phase = 0 ; filter phase in lockin2 output out1 = 0 ; RP output routing, default 0: gen1 excitation (0: gen1, 1: gen2, 2: dac1, 3: dac2, 4: pid, 5: off, 6: excs) out2 = 4 ; RP output routing, default 4: pid result (0: gen1, 1: gen2, 2: dac1, 3: dac2, 4: pid, 5: off, 6: excs) outhr = 4 ; RP output routing, default 4: pid result (0: gen1, 1: gen2, 2: hrdac, 4: pid, 5: off, 6: excs) [mode2] name = ncamplitude ; name of the measurement mode mux1 = 4 ; ADC channel routed to RP1 mux2 = 5 ; ADC channel routed to RP2 error_source = 3 ; error source signal (0: off, 1: RP1, 2: RP2, 3: A1, 4: P1, 5: A2, 6: P2, 7: freq) swap_in = 0 ; swap error signal direction (0: lower signal retracts piezo, 1: higher signal retract piezo) swap_out = 0 ; swap zpiezo signal direction error_bit_shift = 3 ; error signal bit shift (0-31) pll = 0 ; pll on/off, this also generates freq signal for feedback pll_input = 0 ; 0: phase1, 1: phase2 input1_range = 0 ; RP1 input range (0: +-1 V 1: +-10 V) input2_range = 0 ; RP2 input range (0: +-1 V 1: +-10 V) lockin1_hr = 0 ; high resolution low signal option for lockin 1 lockin2_hr = 0 ; high resolution low signal option for lockin 2 pidskip = 2 ; PID speed factor. 0: 125 MHz, 1: 1 MHz, 2: 120 kHz, 3: 15 kHz pllskip = 0 ; PLL speed factor. 0: 125 MHz, 1: 1 MHz, 2: 120 kHz, 3: 15 kHz lockin1_filter_amplitude = 0 ; filter amplitude in lockin1 output lockin1_filter_phase = 1 ; filter phase in lockin1 output lockin2_filter_amplitude = 0 ; filter amplitude in lockin2 output lockin2_filter_phase = 0 ; filter phase in lockin2 output out1 = 0 ; RP output routing, default 0: gen1 excitation (0: gen1, 1: gen2, 2: dac1, 3: dac2, 4: pid, 5: off, 6: excs) out2 = 4 ; RP output routing, default 4: pid result (0: gen1, 1: gen2, 2: dac1, 3: dac2, 4: pid, 5: off, 6: excs) outhr = 4 ; RP output routing, default 4: pid result (0: gen1, 1: gen2, 2: hrdac, 4: pid, 5: off, 6: excs) [mode3] name = ncphase ; name of the measurement mode mux1 = 4 ; ADC channel routed to RP1 mux2 = 4 ; ADC channel routed to RP2 error_source = 4 ; error source signal (0: off, 1: RP1, 2: RP2, 3: A1, 4: P1, 5: A2, 6: P2, 7: freq) swap_in = 0 ; swap error signal direction (0: lower signal retracts piezo, 1: higher signal retract piezo) swap_out = 0 ; swap zpiezo signal direction error_bit_shift = 3 ; error signal bit shift (0-31) pll = 0 ; pll on/off, this also generates freq signal for feedback pll_input = 0 ; 0: phase1, 1: phase2 input1_range = 0 ; RP1 input range (0: +-1 V 1: +-10 V) input2_range = 0 ; RP2 input range (0: +-1 V 1: +-10 V) lockin1_hr = 0 ; high resolution low signal option for lockin 1 lockin2_hr = 0 ; high resolution low signal option for lockin 2 pidskip = 2 ; PID speed factor. 0: 125 MHz, 1: 1 MHz, 2: 120 kHz, 3: 15 kHz pllskip = 0 ; PLL speed factor. 0: 125 MHz, 1: 1 MHz, 2: 120 kHz, 3: 15 kHz lockin1_filter_amplitude = 0 ; filter amplitude in lockin1 output lockin1_filter_phase = 1 ; filter phase in lockin1 output lockin2_filter_amplitude = 0 ; filter amplitude in lockin2 output lockin2_filter_phase = 0 ; filter phase in lockin2 output out1 = 0 ; RP output routing, default 0: gen1 excitation (0: gen1, 1: gen2, 2: dac1, 3: dac2, 4: pid, 5: off, 6: excs) out2 = 4 ; RP output routing, default 4: pid result (0: gen1, 1: gen2, 2: dac1, 3: dac2, 4: pid, 5: off, 6: excs) outhr = 4 ; RP output routing, default 4: pid result (0: gen1, 1: gen2, 2: hrdac, 4: pid, 5: off, 6: excs) [mode4] name = akiyama ; name of the measurement mode mux1 = 10 ; ADC channel routed to RP1 mux2 = 11 ; ADC channel routed to RP2 error_source = 7 ; error source signal (0: off, 1: RP1, 2: RP2, 3: A1, 4: P1, 5: A2, 6: P2, 7: freq) swap_in = 1 ; swap error signal direction (0: lower signal retracts piezo, 1: higher signal retract piezo) swap_out = 0 ; swap zpiezo signal direction error_bit_shift = 9 ; error signal bit shift (0-31) pll = 1 ; pll on/off, this also generates freq signal for feedback pll_input = 0 ; 0: phase1, 1: phase2 input1_range = 1 ; RP1 input range (0: +-1 V 1: +-10 V) input2_range = 1 ; RP2 input range (0: +-1 V 1: +-10 V) lockin1_hr = 0 ; high resolution low signal option for lockin 1 lockin2_hr = 0 ; high resolution low signal option for lockin 2 pidskip = 0 ; PID speed factor. 0: 125 MHz, 1: 1 MHz, 2: 120 kHz, 3: 15 kHz pllskip = 0 ; PLL speed factor. 0: 125 MHz, 1: 1 MHz, 2: 120 kHz, 3: 15 kHz lockin1_filter_amplitude = 0 ; filter amplitude in lockin1 output lockin1_filter_phase = 1 ; filter phase in lockin1 output lockin2_filter_amplitude = 0 ; filter amplitude in lockin2 output lockin2_filter_phase = 0 ; filter phase in lockin2 output out1 = 0 ; RP output routing, default 0: gen1 excitation (0: gen1, 1: gen2, 2: dac1, 3: dac2, 4: pid, 5: off, 6: excs) out2 = 4 ; RP output routing, default 4: pid result (0: gen1, 1: gen2, 2: dac1, 3: dac2, 4: pid, 5: off, 6: excs) outhr = 4 ; RP output routing, default 4: pid result (0: gen1, 1: gen2, 2: hrdac, 4: pid, 5: off, 6: excs) [mode5] name = nenoprobe ; name of the measurement mode mux1 = 13 ; ADC channel routed to RP1 mux2 = 13 ; ADC channel routed to RP2 error_source = 3 ; error source signal (0: off, 1: RP1, 2: RP2, 3: A1, 4: P1, 5: A2, 6: P2, 7: freq) swap_in = 0 ; swap error signal direction (0: lower signal retracts piezo, 1: higher signal retract piezo) swap_out = 0 ; swap zpiezo signal direction error_bit_shift = 7 ; error signal bit shift (0-31) pll = 0 ; pll on/off, this also generates freq signal for feedback pll_input = 1 ; 0: phase1, 1: phase2 input1_range = 1 ; RP1 input range (0: +-1 V 1: +-10 V) input2_range = 1 ; RP2 input range (0: +-1 V 1: +-10 V) lockin1_hr = 0 ; high resolution low signal option for lockin 1 lockin2_hr = 0 ; high resolution low signal option for lockin 2 pidskip = 2 ; PID speed factor. 0: 125 MHz, 1: 1 MHz, 2: 120 kHz, 3: 15 kHz pllskip = 0 ; PLL speed factor. 0: 125 MHz, 1: 1 MHz, 2: 120 kHz, 3: 15 kHz lockin1_filter_amplitude = 0 ; filter amplitude in lockin1 output lockin1_filter_phase = 0 ; filter phase in lockin1 output lockin2_filter_amplitude = 0 ; filter amplitude in lockin2 output lockin2_filter_phase = 0 ; filter phase in lockin2 output out1 = 0 ; RP output routing, default 0: gen1 excitation (0: gen1, 1: gen2, 2: dac1, 3: dac2, 4: pid, 5: off, 6: excs) out2 = 4 ; RP output routing, default 4: pid result (0: gen1, 1: gen2, 2: dac1, 3: dac2, 4: pid, 5: off, 6: excs) outhr = 4 ; RP output routing, default 4: pid result (0: gen1, 1: gen2, 2: hrdac, 4: pid, 5: off, 6: excs) [mode6] name = stm ; name of the measurement mode mux1 = 12 ; ADC channel routed to RP1 mux2 = 12 ; ADC channel routed to RP2 error_source = 1 ; error source signal (0: off, 1: RP1, 2: RP2, 3: A1, 4: P1, 5: A2, 6: P2, 7: freq) swap_in = 0 ; swap error signal direction (0: lower signal retracts piezo, 1: higher signal retract piezo) swap_out = 0 ; swap zpiezo signal direction error_bit_shift = 3 ; error signal bit shift (0-31) pll = 0 ; pll on/off, this also generates freq signal for feedback pll_input = 0 ; 0: phase1, 1: phase2 input1_range = 0 ; RP1 input range (0: +-1 V 1: +-10 V) input2_range = 0 ; RP2 input range (0: +-1 V 1: +-10 V) lockin1_hr = 0 ; high resolution low signal option for lockin 1 lockin2_hr = 0 ; high resolution low signal option for lockin 2 pidskip = 2 ; PID speed factor. 0: 125 MHz, 1: 1 MHz, 2: 120 kHz, 3: 15 kHz pllskip = 0 ; PLL speed factor. 0: 125 MHz, 1: 1 MHz, 2: 120 kHz, 3: 15 kHz lockin1_filter_amplitude = 0 ; filter amplitude in lockin1 output lockin1_filter_phase = 0 ; filter phase in lockin1 output lockin2_filter_amplitude = 0 ; filter amplitude in lockin2 output lockin2_filter_phase = 0 ; filter phase in lockin2 output out1 = 2 ; RP output routing, default 0: gen1 excitation (0: gen1, 1: gen2, 2: dac1, 3: dac2, 4: pid, 5: off, 6: excs) out2 = 4 ; RP output routing, default 4: pid result (0: gen1, 1: gen2, 2: dac1, 3: dac2, 4: pid, 5: off, 6: excs) outhr = 4 ; RP output routing, default 4: pid result (0: gen1, 1: gen2, 2: hrdac, 4: pid, 5: off, 6: excs)