Open RF Prototyping

LTC5582 and LT5537 Power Detectors

Features: LTC5582

  • Broadband performance: ± 0.5 dB over the range 0.1 to 4 GHz.

  • Linear dynamic range of 50 dB over the range 0.1 to 4 GHz.

  • Usable to over 6 GHz with reduced dynamic range.

  • On board EEPROM for calibration parameters.

  • Optional access to detector output voltage.

Features: LT5537

  • Broadband performance: ± 0.5 dB over the range 5 to 600 MHz.

  • Linear dynamic range of 55 dB over the range 5 to 600 MHz.

  • Usable to 1 GHz with reduced dynamic range.

  • On board EEPROM for calibration parameters.

  • Optional access to detector output voltage.

LTC5582 Typical Performance Characteristics

Dynamic Range and Accuracy

The actual range of the linear response will vary from one detector to another. The calibration process described later in this document deals with determining limits for the linear response of a given detector.

Figure 1 shows the calibrated response of a typical LTC5582 detector. By inspection of the plotted response it can be seen that region of linear response for this detector is from approximately 0 down to -50 dBm. The region is not constant across all input signal frequencies. At lower frequencies (up to approximately 3 GHz) the linear response region is actually -5 down to -55 dBm. At frequencies above 4 GHz it is expected that the linear response region will be reduced. This is in line with the response data presented in the LTC5582 data sheet.

Measured vs. input pwr

Figure 1: Measured detector power vs input signal power

Detector level measurement uncertainty

After calibration, the maximum measurement uncertainty when the detector is operated in it's region of linear response will be ± 1 dB. Typically, the uncertainty should be ± 0.5 dB.

Error in the detector input power level measurements will be from the following sources:

  1. Uncertainty in the measurement of the detector output voltage. This uncertainty comes from the detector board attiny45 MCU ADC which is 10 bits with an absolute uncertainty of 2 LSB. Since the internal 2.56V reference is used, this equates to an uncertainty of 5 mV in the detector voltage reading.

  2. Uncertainty in the detector calibration process and the derived calibration parameters. This is made up of two parts:

    1. Uncertainty in the input signal power levels as produced by the signal generator used for the detector calibration.

    2. Uncertainty in the derivation of the calibration parameters.

    The determination of the uncertainties in the calibration is deal with in more detail in the Calibration Procedure section of this document.

Input Return Loss

The detector input return loss is dependent of the input matching network. The detector board design currently uses the matching network as given in LTC5582 data sheet: Figure 2, p10, Test Schematic Optimized for 40MHz to 5500MHz in Single-Ended Input Configuration.

The effects of the detector board input return loss on the power level measurements are corrected by the detector calibration process. Further details are provided in the Calibration Procedure section of this document.

Measured vs. input pwr

Figure 2: Detector input return loss

LT5537 Typical Performance Characteristics

As with the LTC5582, the actual range of the linear response will vary from one detector to another. The calibration process described later in this document deals with determining limits for the linear response of a given detector.

Figure 3 shows the calibrated response of a typical LT5537 detector. By inspection of the plotted response it can be seen that region of linear response for this detector is from approximately -5 down to -65 dBm. The region is not constant across all input signal frequencies. At lower frequencies (up to approximately 200 MHz) the linear response region is actually 0 down to -65 dBm. At frequencies above 200 MHz it is expected that the linear response region will be reduced. This is in line with the response data presented in the LT5537 data sheet.

Measured vs. input pwr

Figure 3: Measured detector power vs input signal power

Applications Information

In general the most convenient method for taking detector input power measurements is by using the detector board serial peripheral interface (SPI). Figure 4 illustrates the SPI setup of a detector board with the atmega USB controller which is running the ECApp embedded control firmware. In this configuration, the atmega board becomes the SPI master with the detector board being the slave. When connected to a host computer, the USB port of the atmega board also powers the detector board.

SPI setup

Figure 4: Connecting the detector for use with SPI

The rfblocks Python package provides the LogDetector class which can be used for software control of the detector board. The code snippet below shows a minimal example of how the detector board is initialized and a power measurement taken. In this example the detector is the LTC5582 and the input signal is set to 1500 MHz with a power of approximately -6 dBm.

>>> from rfblocks import LogDetector, create_serial, write_cmd

>>> ser_device = '/dev/tty.usbmodem14101'
>>> ser = create_serial(ser_device)
>>> det = LogDetector('D0')
>>> write_cmd(ser, det.pin_config())
b'.'
>>> det.initialize(ser)
>>> det.frequency_range
(100.0, 6000.0)
>>> det.detector_type
0
>>> LogDetector.LTC5582
0
>>> det.read_raw(ser)
948
>>> det.vout(ser)
2317.5
>>> det.power(ser, 1500.0)
-6.275880776959141

The code does the following:

  1. Import the rfblocks LogDetector class and some support functions.

  2. Open the USB/RS-232 serial device which connects the host computer to the ECApp control firmware running on the atmega microcontroller board.

  3. Create an instance, det, of the LogDetector class. The detector board's chip select (CS) pin is connected to the controller PD0 pin and this is specified when creating det.

  4. Set the required controller pin states. The LogDetector.pin_config() method generates the required ECApp commands for this and the write_cmd support function sends the commands to the controller board.

  5. Initialize the det instance. This loads detector board calibration parameters from the detector on-board EEPROM.

  6. Check the frequency range of the detector and type of the detector.

  7. Measure the detector input signal power.

More detail

The LogDetector.power() method will return the measured input signal power corrected using the calibration parameters stored in the detector's on-board EEPROM. The power() method defines the following parameters:

ser

the serial device to write commands to. In general this will be an instance of the pySerial Serial class but can be any instance of a Python file object.

freq

the frequency of the detector input signal in MHz.

avg

an optional argument specifying the number of measurements to average over. The default value is 1.

The power() method does the following:

  1. Calls the LogDetector.vout() method to read the detector output voltage. vout() calculates this voltage by first calling the LogDetector.read_raw() method which reads a raw 10-bit value from the detector board. The raw value is converted to a voltage (in millivolts) by multiplying by LogDetector.RAW_TO_MILLIVOLTS.

  2. Corrects the raw detector voltage using the detector calibration parameters and the input signal frequency as specified by freq. Further details of the correction procedure and detector calibration is provided in the Calibration Procedure section below.

Calibration and correction. Using PwrDetectorController.

In general, power detectors will be connected to the device under test via some intermediate set of cabling and other devices. For example, if the expected signal power to be measured is outside the linear range of the detector then either attenuation or amplification must be included. Similarly, cable losses must be corrected. What is required is for the calibration plane of the detector to be moved from the detector's connector to the connector which is mated with the device under test.

>>> from rfblocks import LogDetector, PwrDetectorController, create_serial, write_cmd

>>> ser_device = '/dev/tty.usbmodem14101'
>>> ser = create_serial(ser_device)
>>> det = LogDetector('D0')

>>> det_ctl = PwrDetectorController('det1', det)
>>> det_ctl.initialize(ser)

>>> cal_file = 'referencedesigns/PowerMeter/app/cal-files/12dB-atten-5640-128-100-6000MHz.json'
>>> det_ctl.load_caldata(cal_file)
>>> det_ctl.apply_correction = True
>>> det_ctl.avg = 1

>>> det_ctl.freq = 1000
>>> det_ctl.measure(ser)
>>> det_ctl.pwr
-10.184789915966384

>>> det_ctl.avg = 8
>>> det_ctl.measure(ser)
>>> det_ctl.pwr
-10.10819677871148

Detector linearity

When measuring the input signal power it is vital to know whether the detector is operating within it's region of linear response. The LogDetector.linear_range() method returns a tuple containing the minimum and maximum input signal powers (in dBm) for which the detector provides a linear response.

Continuing with the minimal code snippet above, suppose the input signal power is -40 dBm (at 1000 MHz):

>>> det.power(ser, 1000, avg=8)
-40.387142857142855
>>> det.linear_range(1000)
(-57.41793316505692, -1.2007444914343335)

It can be seen that the detector's region of linear response is between -57.4 and -1.2 dBm. With the input signal power set to -40 dBm the measured power is seen to be within the quoted typical range of uncertainty for the detector.

Now the input signal power is set to -60 dBm and then to -65 dBm (at 1000 MHz) with measurements taken at each setting:

>>> det.power(ser, 1000)
-58.94456582633053
>>> det.power(ser, 1000)
-62.00829131652661

In these cases the detector is operating outside of it's linear response region and the measurements show an increasing departure from the actual input signal power.

Similar behaviour occurs at the other end of the linear response region. Setting the input signal power to 2 dBm and then to 5 dBm (at 1000 MHz) and making measurements at each setting:

>>> det.power(ser, 1000)
0.7543137254901922
>>> det.power(ser, 1000)
2.329943977591043

Low level SPI commands

CS on the power detector board is active low (that is, pulling CS low will enable detector SPI).

Note that the SPI clock rate should be 500 kHz or less. The S3 ECApp embedded control command can be used to set this.

An ADC conversion of the input RF power must be explicitly commanded using the 0x40 command byte. This will result in the raw ADC value being stored in register 1 (low byte) and register 2 (high byte).

Figure 10 shows the timing of an ADC read operation. The following Python code is taken from the LogDetector.read_raw method.

# SPI clock speed for the attiny45 should be <500 kHz
cmd = 'L{}:S3:'.format(self.cs)
write_cmd(ser, cmd)

# Start an ADC conversion
cmd = 'W40:'
write_cmd(ser, cmd)

# Max. ADC conversion time on the attiny is approx 0.2 millisecs
# (125kHz ADC clock)
time.sleep(0.00025)

# Read the ADC low byte from register 1
cmd = 'W01:'
write_cmd(ser, cmd)
cmd = 'R:'
resp = query_cmd(ser, cmd)
low_byte = int(b''.join(resp).decode('utf-8')[:-1],
               16) if len(resp) > 1 else 0

# Read the ADC high byte from register 2
cmd = 'W02:'
write_cmd(ser, cmd)
cmd = 'R:'
resp = query_cmd(ser, cmd)
high_byte = int(b''.join(resp).decode('utf-8')[:-1],
                16) if len(resp) > 1 else 0

# SPI clock speed is returned to 4 MHz
cmd = 'H{}:S1:'.format(self.cs)
write_cmd(ser, cmd)

return low_byte + (high_byte * 256)

The following C code is taken from Zephyr ECApp code. Since the C code is in general much faster than the equivalent Python code it is necessary to insert k_sleep calls in order to satisfy the timing requirements.

uint8_t  spi_rate[] = "3";
struct cmd_values  spi_cmd = { .data[0] = 0x40, .count = 1 };

// SPI clock speed for the attiny45 should be <500kHz
(void)set_spi_clock_rate(spi_rate);

// Enable attiny SPI
(void)set_pin_low(cs_pin);

k_sleep(K_TICKS(1));

// Start an ADC conversion
write_spi_bytes(&spi_cmd);

// Pause for 0.25 millisecs.
k_sleep(K_TICKS(10));

// Read ADC low byte
spi_cmd.data[0] = 0x01;
write_spi_bytes(&spi_cmd);
k_sleep(K_TICKS(1));
uint8_t  low_byte = read_spi_byte();

k_sleep(K_TICKS(1));

// Read ADC high byte
spi_cmd.data[0] = 0x02;
write_spi_bytes(&spi_cmd);
k_sleep(K_TICKS(1));
uint8_t  high_byte = read_spi_byte();

// Disable attiny SPI
(void)set_pin_high(cs_pin);

// SPI clock speed is returned to default
spi_rate[0] = '1';
(void)set_spi_clock_rate(spi_rate);

uint16_t  level = ((uint16_t)high_byte) << 8;
level += low_byte;
return level;

Board connector configuration

Table 1. Board pin configuration and function descriptions

Board Pin

Type

Description

MOSI

Input

Serial interface data input

MISO

Output

Serial interface data output

SCK

Input

Serial interface clock

CS

Input

Serial interface board select

Reset

Input

Module microcontroller reset

Vout

Output

Optional power detector voltage output

RF In

Input

Female SMA connector for RF input signal

5V

Power Input

5V DC module power

Gnd

Power Input

Module ground

Conn. layout

Figure 5: Clock generator module connector layout.

Reference Designs

Calibration Procedure

The steps taken to calibrate a given power detector board are as follows:

  1. Capture calibration measurements and produce linear fits to the data using measure-cal-data.py. Here's an example command line:

    python measure-cal-data.py -O ltc5582-unit2-cal-data.json

    See Figure 9 for an example of a calibration measurement setup. See Capturing calibration measurements for more details of the measure-cal-data.py script.

  2. Generate overall fits for the slopes and intercepts of the detector linear regions. Here's an example command line:

    python plot-cal-data-fits.py -I ltc5582-unit2-cal-data.json \
       -E -O ltc5582-unit2-extrapolated-cal-data.json -P \
       ltc5582-unit2-cal-fits.svg

    Figure 7 shows example plots of the overall fits for the detector slopes and intercepts as generated by measure-cal-data.py.

  3. Calculate detector linearity errors and linearity limits. An example command line:

    python plot-cal-data-linearity.py \
          -I ltc5582-unit2-cal-data.json \
          -L ltc5582-unit2-linlimits.json
  4. Format detector slope/intercept fits for EEPROM. An example command line:

    python cal-data-eeprom.py \
              -I ltc5582-unit2-extrapolated-cal-data.json
              -L ltc5582-unit2-linlimits.json
  5. Build EEPROM image and write it to the detector board.

Calibration Details

The following sections describe the calibration procedure in more detail.

  1. A set of detector measurements are taken using a known accurate RF signal source. The power range -75 to 10 dBm is covered at frequencies from 250 to 4250 MHz. Note that we use the Rohde & Schwarz SMHU58 signal generator as our reference. This has an upper frequency limit of approximately 4300 MHz. The power detector being calibrated is connected directly to the SMHU58 via an N-type to male SMA adapter. See Capturing calibration measurements for more details.

    Some example measurements are shown here:

    $ python scripts/measure-cal-data.py -O ltc5582-unit4-cal-data.json
    -75.0:  605.47 604.06 603.59 606.25 604.22 606.72 598.75 598.44 600.78 601.09 599.22 599.53 598.28 597.50 595.16 596.88 595.16
    -70.0:  629.22 626.56 624.84 623.12 620.94 617.03 612.66 614.38 608.75 610.31 609.38 606.56 606.72 608.75 613.91 607.19 607.19
    -65.0:  675.16 669.84 670.00 665.47 662.81 658.44 653.75 639.06 639.38 636.41 633.75 628.91 629.06 623.75 630.00 617.19 613.28
    ...
      0.0:  2433.91 2424.84 2416.88 2410.62 2406.72 2397.97 2383.59 2357.03 2362.19 2360.00 2350.78 2335.94 2327.19 2323.44 2315.94 2296.56 2278.75
      5.0:  2502.97 2504.69 2496.09 2489.69 2486.41 2479.06 2472.66 2452.19 2454.84 2447.97 2440.16 2430.78 2424.53 2412.19 2402.50 2389.53 2384.22
     10.0:  2553.59 2551.72 2549.84 2545.47 2506.56 2501.88 2495.00 2486.56 2477.81 2471.88 2460.62 2454.06 2443.91 2434.22 2423.44 2410.47 2397.50
  2. A set of lines are now fitted to the linear portion of the detector response curve at each of the measurement frequencies. An example of the line fits is shown in Figure 6.

    The measured detector response together with the slope and intercept of the line fits for each measured frequency are saved to a file for later use. See Capturing calibration measurements for more details.

    Linear fits

    Figure 6: Example linear fits for generating calibration data

  3. The LTC5582 datasheet shows plots of linear response slopes and intercepts across the frequency range 450 to 5800 MHz (p7 of the datasheet, 'Slope vs Frequency' and 'Logarithmic Intercept vs Frequency'). We produce similar plots using the slopes and intercepts produced in step (2). The slope and intercept data are extrapolated linearly up to 6000 MHz and down to 0 MHz. The detector slope and intercept data from the datasheet plots are used to guide the extrapolation (the plot data for a case temperature of \(25^\circ\) C is used).

    Table 2 shows an example set of slope/intercept values derived from the line fits of the measured detector response.

    Table 2: Example Calibration Data

    Frequency (MHz)

    Slope (mV/dB)

    Log Intercept (dBm)

    0

    30.66

    -82.38

    250

    30.66

    -82.38

    500

    30.67

    -82.12

    750

    30.62

    -81.95

    1000

    30.56

    -81.88

    1250

    30.60

    -81.72

    1500

    30.59

    -81.43

    1750

    30.57

    -80.73

    2000

    30.76

    -79.85

    2250

    30.90

    -79.52

    2500

    30.92

    -79.12

    2750

    30.57

    -79.43

    3000

    30.74

    -78.72

    3250

    30.88

    -78.09

    3500

    31.01

    -77.42

    3750

    31.22

    -76.55

    6000

    31.96

    -69.44

    Example plots are shown in Figure 7. The slope and intercept data from the LTC5582 datasheet are shown as red, green, and yellow dashed plots (for case temperatures of \(25^\circ\), \(-40^\circ\) and \(85^\circ\) C respectively). The measured slope and intercept values are shown as a solid blue line with the extrapolated values shown as blue dotted lines. Note that the last two measured values (at signal frequencies 4000 and 4250 MHz) are ignored for the purposes of the extrapolation. The accuracy of the signal generator power output levels for these cases is doubtful.

    ./static/ltc5582-unit1-cal-fits.svg

    Figure 7: Example slope/logarithmic intercept plots.

  4. In order to effectively use the detector the range of the linear response region as a function of input power and frequency must be known. This range is determined using the following procedure:

    1. Calculate the linearity error for each measured input signal power and frequency. This will be the difference between the measured input power and the input power as calculated from the linear fit at that input signal frequency.

    2. Perform polynomial fits for the calculated linearity errors. These fits are then used to determine the upper and lower input signal power linearity limits. The limits are taken to be the input signal powers which produce an (absolute) linearity error of \(\ge 0.5\) dB.

      The upper two panels of Figure 8 show example plots of the measured linearity errors and the associated polynomial fit. The horizontal dashed lines indicate the ± 0.5 dB linearity error limits.

      Linearity errors

      Figure 8: Example detector linearity errors, as measured and the associated polynomial fits.

Calibration data is stored in the attiny45 EEPROM. Measured raw ADC values for eighteen input RF power levels are stored as 16-bit integers in the attiny45 EEPROM. These values can be retrieved via the SPI by reading registers 100 through to 135. The values are stored low byte first followed by the high byte.

Writing Calibration Data to EEPROM

The calibration data produced by the plot-cal-data-fits script is formatted for inclusion as EEPROM data using the code below. In essence, the calibration data is converted to a set of positive 16 bit integers which can then be stored as an array in the attiny45 EEPROM.

#
# Example command line:
#
#  python cal-data-eeprom.py \
#            -I measuredData/ltc5582/ltc5582-unit2-extrapolated-cal-data.json
#            -L measuredData/ltc5582/ltc5582-unit2-linlimits.json
#
import json
import struct
from argparse import ArgumentParser

if __name__ == '__main__':

    parser = ArgumentParser(description=
      '''Generate EEPROM formatted cal. data.''')

    parser.add_argument("-I", "--inputfile", required=True,
                        help="The JSON formatted input cal. data file.")
    parser.add_argument("-L", "--linearity", required=True,
                        help="JSON file containing linearity limit data.")
    args = parser.parse_args()

    with open(args.inputfile) as fd:
        cal_data = json.load(fd)

    with open(args.linearity) as fd:
        linlimits = json.load(fd)

    print('cal_data_type  cal_data __attribute__((section(".eeprom"))) = {')
    print('{', '{},'.format(len(cal_data)),
          ', '.join(['{}, {}, {}'.format(int(float(f)),
                                         int(float(v[0])*100),
                                         abs(int(float(v[1])*100)))
                     for f, v in cal_data.items()]), '}')
    print('};')

    ba = struct.pack('f', linlimits['min_limit_coeffs'][0])
    ba += struct.pack('f', linlimits['min_limit_coeffs'][1])
    ba += struct.pack('f', linlimits['min_limit_coeffs'][2])
    ba += struct.pack('f', linlimits['min_limit_coeffs'][3])
    ba += struct.pack('f', linlimits['max_limit_coeffs'][0])
    ba += struct.pack('f', linlimits['max_limit_coeffs'][1])
    ba += struct.pack('f', linlimits['max_limit_coeffs'][2])
    ba += struct.pack('f', linlimits['max_limit_coeffs'][3])
    print('lin_limits_type lin_limits_data __attribute__((section(".eeprom"))) = {')
    print('{', ', '.join([ "0x%02x" % b for b in ba ]), '}')
    print('};')

Output from this script can then be placed in the spiadc.cpp source file as shown in the following example code snippet:

namespace app
{
    namespace registers
    {
        const std::uint8_t  reg_file_size = 16U;
        const std::uint16_t cal_data_size = 52U;

        typedef std::array<std::uint8_t, reg_file_size>  reg_file_type;
        typedef std::array<std::uint16_t, cal_data_size> cal_data_type;

        reg_file_type  register_file;
        lin_limits_type lin_limits_data __attribute__((section(".eeprom"))) = {
            { 0x87, 0x76, 0xe9, 0xad, 0xcd, 0x47, 0x63, 0xb3, 0xab,
              0x10, 0xf5, 0x3a, 0x2a, 0xa9, 0x71, 0xc0, 0x2c, 0x48,
              0xd7, 0xae, 0x5d, 0x6b, 0xbb, 0x35, 0x38, 0xe9, 0x30,
              0xbb, 0x18, 0x26, 0x5e, 0xc2 }
        };
        cal_data_type  cal_data __attribute__((section(".eeprom"))) = {
            { 17, 0, 2977, 8657, 250, 2977, 8657, 500, 2972, 8640,
              750, 2953, 8651, 1000, 2952, 8634, 1250, 2953, 8609,
              1500, 2946, 8573, 1750, 2932, 8526, 2000, 2942, 8433,
              2250, 2958, 8443, 2500, 2956, 8429, 2750, 2942, 8409,
              3000, 2952, 8344, 3250, 2980, 8257, 3500, 2985, 8197,
              3750, 2993, 8106, 6000, 3067, 7394 }
        };
        void      write(const uint8_t reg, const uint8_t val);
        uint8_t   read(const uint8_t reg);
    }
}

The ordering of the lin_limits_data and cal_data must be as indicated in the above code snippet. The compiler/linker then places cal_data first in EEPROM followed by the lin_limits_data:

.eeprom        0x0000000000810000       0x88 tmp/obj/spiadc.o
               0x0000000000810000                app::registers::cal_data
               0x0000000000810068                app::registers::lin_limits_data
               0x0000000000810088                __eeprom_end = .

After updating the cal_data in spiadc.cpp the firmware is rebuilt and the EEPROM data uploaded to the attiny45 using the procedures documented in Building the firmware.

Firmware

The firmware which runs on the attiny45 is a variant of the ECApp framework and acts as an SPI slave providing access to a collection of registers. Source for the firmware is available here: ECApp for the attiny45.

The contents of a register is read by performing an SPI write of the 8-bit address of the register and then an SPI read of the returned 8-bit value. For example, reading the contents of register 1 using ECApp commands:

write_cmd(ser, 'W01:')
resp = query_cmd(ser, 'R:')

After initialization, the firmware is entirely interrupt driven. Figure 9 shows a summary of the operation.

  • The pin change interrupt (vector_2) monitors pin B3 which is the CS line. When CS goes low the SPI system is enabled. When CS goes high the SPI system is disabled.

  • The USI overflow interrupt (vector_14) monitors serial traffic on the PB0 (MOSI) and PB2 (SCK) pins and receives SPI commands from the SPI master. Commands are interpreted and, if required, responses are loaded into the USIDR for transmission to the SPI master (via PB1, MISO).

  • The ADC conversion complete interrupt (vector_8) writes the low byte of the ADC conversion result to register 1 and the high byte to register 2.

Tiny ECApp

Figure 9: Tiny ECApp

Table 3: Firmware registers

Register

Description

0x0

Reserved

0x1

ADC low byte

0x2

ADC high byte

0x14

Detector type

0x40

Start ADC conversion

0x64

Calibration data

Writing the register value 0x40 starts an ADC conversion. Registers 1 and 2 will receive the 10 bit ADC output value after a conversion. Register 1 will contain the lower 8 bits and register 2 the upper 8 bits. The LogDetector.read_raw method illustrates doing an ADC conversion and reading the result.

Pwr det. read timing

Figure 10: Power detector read timing

t_1

Time required to enable the SPI system after the CS signal goes low. This is approximately 10uS.

t_2

Time required for the ADC to carry out one complete conversion. This is approximately 0.2mS.

t_3, t_5

Time required to setup a specific register for a read operation. This is approximately 35uS (with an SPI clock rate of 250kHz).

t_4, t_6

Time required to read the contents of a register. This is approximately 35uS (with an SPI clock rate of 250kHz).

Registers from (decimal) 100 upward are read only and access the contents of the attiny45 EEPROM. The detector calibration data is stored here and the LogDetector.read_cal_data method illustrates how the calibration data is retrieved fro use.

Building the firmware

Ensure that the correct branch of the source code is checked out:

$ git branch
  master
* spiadc/attiny45
  usbcmd/atmega32u2

If not, check out the correct branch before proceeding:

$ git checkout spiadc/attiny45
Switched to branch 'spiadc/attiny45'
Your branch is up to date with 'origin/spiadc/attiny45'.

The make file is target/app/make/app_make_linux.gmk. The essential build commands are:

$ cd $ECAPP
$ rm -rf ./tmp
$ make -f target/app/make/app_make_linux.gmk
$ make -f target/app/make/app_make_linux.gmk avrdude

Alternatively, the program firmware may be uploaded using the following avrdude command:

$ avrdude -p attiny45 -P /dev/tty.usbserial-A600e0V2 -c buspirate -U flash:w:bin/spiadc.hex

Building and uploading just the EEPROM data is done using the following commands:

$ cd $ECAPP
$ rm tmp/obj/*.d
$ make -f target/app/make/app_make_linux.gmk avrdude_eeprom

Updating the firmware

Prior to updating the firmware a copy of the EEPROM contents must be made. This is because a chip erase will be done before uploading the new firmware.

$ avrdude -p attiny45 -P /dev/tty.usbserial-A600e0V2 -c buspirate -U eeprom:r:spiadc-eeprom.eep:r

The new firmware is uploaded either using the procedure described in the previous section or using the following command to upload the hex file directly:

cd $ECAPP
$ avrdude -p attiny45 -P /dev/tty.usbserial-A600e0V2 -c buspirate -U flash:w:bin/spiadc.hex

Now the previously read copy of the EEPROM contents is uploaded:

$ avrdude -p attiny45 -P /dev/tty.usbserial-A600e0V2 -c buspirate -U eeprom:w:spiadc-eeprom.eep

Design Notes: LTC5582

The detector uses the LTC5582 coupled with an attiny45 MCU. IC3, the attiny45, is used essentially as an SPI enabled ADC. The voltage output from the LTC5582 is connected via R1 to channel 2 of the attiny45 ADC. Optionally, output from the LTC5582 can be accessed directly via J3 by populating R3 (and removing R1).

The attiny45 ADC is configured to make use of the internal 2.56V voltage reference. This matches quite well with the possible voltage swing of the LTC5582.

The input network for the LTC5582 is as specified for the test circuit shown in Figure 2 of the LTC5582 data sheet. Accordingly, the network is optimized for the frequency range 40 to 5500 MHz.

LTC5582 schematic design

Figure 9: LTC5582 power detector schematic design.

Design Notes: LT5537

To achieve the best sensitivity, the input termination impedance should be increased and the input pins should be differentially driven. The input network therefore uses a transformer to step up the impedance and perform the balun function. The 240 ohm resistor (R2) sets the impedance at the input of the chip to 200 ohm. A 1:4 transformer is used to match the 50 ohm signal source impedance to the circuit input impedance. C8 and C9 are DC blocking capacitors. According to the LT5537 data sheet this arrangement has a (3dB error) sensitivity of -82.4 dBm at 200MHz.

LT5537 schematic design

Figure 10: LT5537 power detector schematic design.

Scripts

This section presents the source code of the scripts referenced in this document. In order to more effectively maintain these scripts we use org-mode as a literate programming environment.

measure-cal-data.py

Capturing calibration measurements

plot-cal-data-fits.py

Plotting calibration data fits

plot-cal-data-linearity.py

Plotting calibration data linearity

measure-accuracy.py

Measure calibration accuracy

Capturing calibration measurements

This script measures the raw response of the detector under test. The output voltage of the detector board is recorded for a set of known input signal power levels at each of a set of input signal frequencies. The test setup is shown in Figure 11. Linear fits are then made for each set of measured detector voltages at each of the input signal frequencies. Finally, the measured voltages and linear fits are saved in a file.

Detector calibration setup

Figure 11: Detector calibration setup

usage: measure-cal-data.py [-h] [-d DEVICE] [-C CSPIN] [-G {DSG815,SMHU58}] [-O OUTFILE]

Calibrate LTC5582 power detector module.

optional arguments:
  -h, --help            show this help message and exit
  -d DEVICE, --device DEVICE
                        The serial device (default: /dev/tty.usbmodem14101)
  -C CSPIN, --cspin CSPIN
                        Chip select controller pin (default: D0)
  -G {DSG815,SMHU58}, --gen {DSG815,SMHU58}
                        The signal generator in use for the calibration. Default: SMHU58
  -O OUTFILE, --outfile OUTFILE
                        Output file for measured calibration data. Default: ltc5582-cal-data.json

An example command line:

python scripts/measure-cal-data.py -O ltc5582-unit2-cal-data_2.json

A full description and code for the script is given here: measure-cal-data.py.

Figure 6 shows a set of example plots of measured detector voltage together with linear fits for each. (These plots were produced by running the plot-cal-data.py script - further details of this script are given in the next section.)

Plotting calibration data fits

This script reads the calibration measurements and associated linear fit parameters acquired using the measure-cal-data.py script. The script produces plots of the linear fit slope and intercept parameters together with similar plots taken from the LTC5582 data sheet.

Optionally, the script may be used to extrapolate the measured fits up to 6 GHz using the data sheet curves as a guide.

usage: plot-cal-data-fits.py [-h] [-E] [-I INFILES [INFILES ...]] [-O OUTFILE] [-P PLOTFILE]

Plot LTC5582 detector calibration data fits.

optional arguments:
  -h, --help            show this help message and exit
  -E, --extrapolate     Extrapolate cal. data fits linearly to 6 GHz.
  -I INFILES [INFILES ...], --infiles INFILES [INFILES ...]
                        Input file(s) containing calibration data. Default: ['ltc5582-unit1-cal-data.json']
  -O OUTFILE, --outfile OUTFILE
                        Output file for extrapolated cal. data. Default: ltc5582-unit1-extrapolated-cal-data.json
  -P PLOTFILE, --plotfile PLOTFILE
                        Output file for slope/intercept plots. Default: static/tmp-ltc5582-unit1-cal-fits.svg

An example command line:

python scripts/plot-cal-data-fits.py -I ltc5582-unit4-cal-data.json \
   -E -O ltc5582-unit4-extrapolated-cal-data.json -P \
   ltc5582-unit4-cal-fits.svg

More than one JSON input file can be specified as follows:

python scripts/plot-cal-data-fits.py \
    -I measuredData/ltc5582/ltc5582-unit2-cal-data.json \
    measuredData/ltc5582/ltc5582-unit2-cal-data_2.json \
    measuredData/ltc5582/ltc5582-unit2-cal-data_3.json \
    measuredData/ltc5582/ltc5582-unit2-cal-data_4.json \
    measuredData/ltc5582/ltc5582-unit2-cal-data_5.json \
   -P static/tmp-ltc5582-unit2-cal-fits.svg -E \
   -O measuredData/ltc5582/ltc5582-unit2-extrapolated-cal-data.json

Note that if multiple data sets are specified the slopes and intercepts will be averaged.

A full description and code for the script is given here: plot-cal-data-fits.py.

Plotting calibration data linearity

This script calculates detector linearity errors and linearity limits from data captured using the measure-cal-data script. Additionally it produces plots of the detector linearity and the linearity limits as a function of input signal frequency.

usage: plot-cal-data-linearity.py [-h] [-I INFILE] [-O OUTFILE] [-P PLOTFILE] [-L LINLIMITSFILE] [-E LINERRFILE]

Plot LTC5582 detector calibration data linearity.

optional arguments:
  -h, --help            show this help message and exit
  -I INFILE, --infile INFILE
                        Input file containing calibration data. Default: ltc5582-cal-data.json
  -O OUTFILE, --outfile OUTFILE
                        Output file for plot. Default: ltc5582-cal-data.svg
  -P PLOTFILE, --plotfile PLOTFILE
                        Output file for slope/intercept plots. Default: ltc5582-lin-fits.svg
  -L LINLIMITSFILE, --linlimitsfile LINLIMITSFILE
                        Output file for linearity limits. Default: ltc5582-linlimits.json
  -E LINERRFILE, --linerrfile LINERRFILE
                        Output file for linearity error plots. Default: ltc5582-linerr.svg

An example command line:

python scripts/plot-cal-data-linearity.py \
      -I measuredData/ltc5582/ltc5582-unit4-cal-data.json \
      -L measuredData/ltc5582/ltc5582-unit4-linlimits.json

A full description and code for the script is given here: plot-cal-data-linearity.py.

Format calibration data for EEPROM

The calibration data produced by the plot-cal-data-fits script is formatted for inclusion as EEPROM data using the code below. The calibration data is converted to a set of positive 16 bit integers which can then be stored as an array in the attiny45 EEPROM.

usage: cal-data-eeprom.py [-h] -I INPUTFILE -L LINEARITY

Generate EEPROM formatted cal. data.

optional arguments:
  -h, --help            show this help message and exit
  -I INPUTFILE, --inputfile INPUTFILE
                        The JSON formatted input cal. data file.
  -L LINEARITY, --linearity LINEARITY
                        JSON file containing linearity limit data.

An example command line:

python cal-data-eeprom.py \
          -I measuredData/ltc5582/ltc5582-unit2-extrapolated-cal-data.json
          -L measuredData/ltc5582/ltc5582-unit2-linlimits.json

Measure calibration accuracy

This script will measure the calibration accuracy of a given detector. The test setup is shown in Figure 11. Currently, the script recognises either the R&S SMHU58 or the Rigol DSG815 signal generators. In order to eliminate cable losses, the detector input should be connected directly to the signal generator output via an N-type to SMA adapter. The script assumes the use of the rfblocks Python package together with the ECApp embedded control software.

usage: measure-accuracy.py [-h] [-d DEVICE] [-C CSPIN] [-G {DSG815,SMHU58}] [-O OUTFILE]

Measure LTC5582 power detector module accuracy.

optional arguments:
  -h, --help            show this help message and exit
  -d DEVICE, --device DEVICE
                        The serial device (default: /dev/tty.usbmodem14101)
  -C CSPIN, --cspin CSPIN
                        Chip select controller pin (default: D0)
  -G {DSG815,SMHU58}, --gen {DSG815,SMHU58}
                        The signal generator in use for the calibration. Default: SMHU58
  -O OUTFILE, --outfile OUTFILE
                        Output file for plot. Default: ltc5582-accuracy.svg

A full description and code for the script is given here: measure-accuracy.py.