I recently found a Hittite (acquired by Analog Devices in 2014) HMC624LP4E evaluation board gathering dust in my miscellaneous PCB bin and thought it might be a great little project to add a microcontroller interface to it so I can use it as a convenient digital attenuator in my lab. The HMC624LP4E had been replaced by Analog Devices’ HMC624A-EP but these two chips are mostly identical so everything described in this post can be applied to the newer HMC624A as well.

The evaluation board utilizes a 4-layer design. From the gold-plated ground plane, you can clearly see via-stitching around the ground connectors of the SMA connectors and the via-fencing along the main signal path. These techniques are common in high frequency circuit designs to maintain low impedance and prevent crosstalk and interference.

HMC624LP4E is an MMIC (monolithic microwave integrated circuit) fabricated with Gallium Arsenide (GaAs) semiconductor. The picture below shows the chip itself in its LFCSP packaging (4mm x 4mm) mounted on the PCB.

On the evaluation board, a parallel interface with six switches is used to change the input attenuation. HMC624LP4E can also be controlled using a three-wire serial interface (SPI). The control is quite simple, all that is required is to send a single byte which represents the attenuation steps. With 6 bits, a total of 64 steps can be achieved while adjusting the attenuation from 0 dB to 31.5 dB, with each step representing a 0.5 dB change in attenuation. And because the attenuation is at the highest when all bits are 0, the attenuation for a given input can be calculated using the following formula:

\[ Attenuation (dB) = 31.5 – \frac{Input}{2} \]

To enable SPI control (as opposed to the default parallel control), the PS pin (parallel/serial, or serial enable) needs to be asserted high.

For simplicity, I used a single rotary encoder for controlling the attenuation. In order to prevent accidentally changing the set attenuation value, I used the built-in switch of the rotary encoder as the lock/adjust control. The idea is that the attenuation value can only be adjusted when the switch is in the “adjust” state and the attenuation value is set once the switch changes from adjust to the lock state. When the switch is in the “locked” state, adjusting the rotary encoder has no effect on the digital attenuator. The current attenuation value is displayed on a 1×16 LCD. For more details, you can find the Arduino code listing towards the end of this post along with a video demonstrating this control interface.

Here is a picture showing the digital attenuator connected to a 2 GHz RF source (HP 8642B) and the output from the attenuator is measured by my Wavetek 1045 power meter . The output power from the 8642B is adjusted to 6.5 dBm to compensate for the loss in the cable so that the output from the attenuator is roughly at 0 dBm when there is zero attenuation. From the picture you can see that the attenuation is set at 5 dB and the measured power is at roughly -5 dBm.

#include <SPI.h>
#include <Encoder.h>
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x3f,8,2); 
Encoder enc(2, 3);
const int BTN_PIN = 4;
const int SLOW_DOWN_FACTOR = 4;

const int CS_PIN = 10;
const int SER_EN = 8;

long encLastPos  = 0;
int adjVal = 0;
int buttonState = HIGH;
int lastButtonState = HIGH;
unsigned long lastDebounceTime = 0;

bool canAdjust = false;
float attenuation = 0;

void setup() {
  lcd.init();    
  pinMode(BTN_PIN, INPUT);
  digitalWrite(BTN_PIN, HIGH);

  pinMode(SER_EN, OUTPUT);
  digitalWrite(SER_EN, HIGH);
  
  pinMode(CS_PIN, OUTPUT);
  SPI.begin();

  printAttenuation(attenuation);
}

void printAttenuation(float val)
{
    float att = 31.5 - val;
    
    lcd.clear();
    lcd.setCursor(0,0);
    
    if (canAdjust) {
      lcd.blink();
      lcd.print ("Att Adj:");
    } else {
      lcd.noBlink();
      lcd.print("Att Lck:");
    }
    
    lcd.setCursor(0,1);
    if (att < 10.0) lcd.print(" ");
    lcd.print(att, 1);
    lcd.print(" dB");  
}

void loop() {
  //read encoder
  //adjVal is limited to between 0 and 63
  long encNewPos = enc.read() / SLOW_DOWN_FACTOR;
  
  if (encNewPos != encLastPos) {
    if (canAdjust) {
      if (encNewPos > encLastPos) { if (adjVal > 0) adjVal--; } 
      else { if (adjVal < 63) adjVal++; }
    }
    encLastPos = encNewPos;

    attenuation = (float) adjVal / 2.0;

    printAttenuation(attenuation);
  }  

  //read button
  //the button toggles whether attenuation can be adjusted
  int r = digitalRead(BTN_PIN);

  if (r != lastButtonState) lastDebounceTime = millis();

  if ((millis() - lastDebounceTime > 25) && (r != buttonState)) {
    buttonState = r;
    if (buttonState == 0) canAdjust = !canAdjust;

    //commit changes
    if (!canAdjust) {
      digitalWrite(CS_PIN, LOW);
      SPI.transfer(adjVal);
      digitalWrite(CS_PIN, HIGH);
    }

    printAttenuation(attenuation);
  }

  lastButtonState = r;
}

Here is a video showing the control of HMC624LP4E using either the parallel or the serial interface:

Be Sociable, Share!