Tuesday, 21 February 2017

BASIC Tech Group - MyNews 25 - AD8307 Power meter

There's a lot of hot wind about (as usual) on the internet about using the AD8307 as a power meter. The AD8307 is a very clever device implementing a logarithmic amplifier, with RF direct input up to above +10dBm (0.71Vrms, 10mW/50R) and with a 70-80dB range, and so able to measure down to better than -60dBm (0.225mVrms, 0.001uW/50R)!

Used with an Arduino to make the calculations and preceded by a 40dB attenuator it can make a good power meter for up to 100W/50R (70.1V) power measurements. It is connected across the 50R feed cable using a "T" connector. It can also serve as a general purpose RF voltage meter, when not reading across a 50R impedance. The input circuit is:

Screen Shot 2017 02 21 at 10 35 23

The 40dB attenuator is made up from standard value resistors 4k7 and 51R which give better than 1dB accuracy. For general RF voltage measurements its input impedance is around 5K.

FullSizeRender 2

Software

Here's where there are a lot of 'errors' on the web, mainly because when written in C++ for the Arduino not much care has been taken of the type of the variables and lots of hidden type conversions are made by the complier. A clear code is shown below.

// RF_METER, voltage, dBm & Pwr if 50R load
// V2 1-3-17
// 4k7 input impedance. 40dB attn
// LCD I2C bus (16x2)
// SDA = A4
// SCL = A5

//LCD library
#include "LiquidCrystal_I2C.h"
#include "math.h"

// chose address of 3F or 27 for LCD
//#define LCDADDR 0x27
#define LCDADDR 0x3F
#define LCDCOLS 16
#define LCDROWS 2

// Analog input pin, ref (measured) mV
// AREF can vary between Arduino, measure the 3.3V line and put AREF value here.
// scale for 80dB range to fill bar 0-1023
#define DCIN A3
#define AREF 3242.0
#define BARSCALE (1023 / 80)


// intercept (dBm), slope (mw/dB), input impedance, attenuator (dB).
// input attenuator is 4k7/51R
#define INTERCEPT -84.0
#define SLOPE 25.0
#define IMP 50.0
#define ATTN 40.0

// bar glyphs, 8 rows (top to bottom) for each glyph, 5 bits per row
uint8_t bar0[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B00000, B00000};
uint8_t bar1[8] = {B00000, B10000, B10000, B10000, B10000, B10000, B00000, B00000};
uint8_t bar2[8] = {B00000, B11000, B11000, B11000, B11000, B11000, B00000, B00000};
uint8_t bar3[8] = {B00000, B11100, B11100, B11100, B11100, B11100, B00000, B00000};
uint8_t bar4[8] = {B00000, B11110, B11110, B11110, B11110, B11110, B00000, B00000};
uint8_t bar5[8] = {B00000, B11111, B11111, B11111, B11111, B11111, B00000, B00000};

// create lcd object
LiquidCrystal_I2C lcd(LCDADDR, LCDCOLS, LCDROWS);

void setup() {
  Serial.begin(9600);
  
  // init LCD
  lcd.begin();

  lcd.createChar(0, bar0);
  lcd.createChar(1, bar1);
  lcd.createChar(2, bar2);
  lcd.createChar(3, bar3);
  lcd.createChar(4, bar4);
  lcd.createChar(5, bar5);

  lcd.clear();

  // 3.3V connected to AREF
  analogReference(EXTERNAL);

  dispMsg(0, 0, "RF"); // 0-10V bar graph goes to right...
}

void loop() {
  // display dBm etc
  dispDbm();
  delay(100);
}

void dispDbm() {
  int Ain;                  // int = signed 16 bit
  double mW, mV, dBm, Vrms; // double = double 4 x bytes = 32bit


  // calculations for mV input, dBm, mW and Volts, AREF in mV
  Ain = analogRead(DCIN); // returns an int 0-1023
  mV = (double)(Ain * AREF / 1023); // AREF in mV, calculate & convert to double
  dBm = (mV / SLOPE) + INTERCEPT + ATTN; // in doubles
  mW = pow(10.0, (dBm / 10.0)); // in double, out double
  Vrms = sqrt((mW / 1000.0) * IMP); // in double, out double,

  dispBar(3, 0, (dBm + 40) * BARSCALE, 13); // offset dBm and scale
  
  lcd.setCursor(0, 1);
  if (Vrms < 1.0) {
    lcd.print(Vrms * 1000.0, 0);
    lcd.print("mV ");
  }
  else {
    lcd.print(Vrms, 1);
    lcd.print("V ");
  }

  // dBm
  lcd.print(dBm, 0);
  lcd.print("dBm ");

  // Watts
  if (mW < 1000.0) {
    lcd.print(mW, 0);
    lcd.print("mW  ");
  }
  else {
    lcd.print(mW / 1000.0, 1);
    lcd.print("W   ");
  }
}

// display char msg at col c, row r
void dispMsg(uint8_t c, uint8_t r, char *m)
{
  lcd.setCursor(c, r);
  lcd.print(m);
}

// display bar at col, row, 0-1023, length
void dispBar(uint8_t c, uint8_t r, int d, int l) {
  uint8_t i, ncol;

  lcd.setCursor(c, r);

  ncol = map(d, 0, 1023, 0, l * 5);

  for (i = 0; i < l; ++i) {
    if(ncol == 0) {
      lcd.write(0);
    }
    else if (ncol >= 5) {
      lcd.write(5);
      ncol -= 5;
    }
    else {
      lcd.write(ncol);
      ncol = 0;
    }
  }
}

No comments: