Sunday 1 January 2017

BASIC Tech Group - MyNews 13 - AD8703 module RF _METER & Si5351 controller

Happy new year!

I have hacked together on an Arduino breadboard the AD8703 Module and an output for an LCD display. Here it is:

IMG 5707

It is showing the output from my DDS (Si5351) into its input impedance of 50R.

The planned Return Loss Bridge

The plan is to build an Return Loss bridge on the free side of the board, with an antenna connection. Then use new software I have written to scan 5-10MHz and display the Return Loss on a small 128x64 OLED display.

There will be an SMA link between the detector output of the bridge and the input of the detector module, and a separate antenna SMA input. Here's a diagram of a bridge:

Take no notice of the values given here. The resistors will be 50R (two 100R in parallel) and the transformer will be a FT37-43/2x10t. The RF source will come from the DDS output G0.

Code for RF Meter

// RFDBM-Module (AD8307 direct, no attenuator, 3.3V AREF
// displays dBm, Watts, Volts. Autoscaling

#include "Wire.h"
#include "si5351.h"
#include "LiquidCrystal_I2C.h"
#include "Rotary.h"

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

// rotary Encoder pins 2 & 3 (DT & CLK), step change pin 4 (SW)
#define DT 2
#define CLK 3
#define SW 4

// Analog input pin
#define DCIN A0
#define AREF 3.3

// intercept (dBm), slope (mw/dB), input impedance, attenuator (dB).
#define INTERCEPT -84.0
#define SLOPE 25.0
#define IMP 50.0
#define ATTN 0.0

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

// rotary Encoder object
Rotary rot = Rotary(DT, CLK);

// use uint64_t to go above 40MHz
uint64_t freq = 700000000; // start frequency cHz
uint64_t step = 10000; // init 100Hz step
uint64_t fminimum = 10000000; // 100kHz min
uint64_t fmaximum = 20000000000; // 200MHz max

// dds object
Si5351 dds;

void setup() {
  lcd.begin();
  lcd.backlight();

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

  // init dds si5351 module, "0" = default 25MHz XTAL, no correction
  dds.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);

  // set 8mA output drive (max possible)
  dds.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);

  // can insert Si5351 calibration here if required

  // enable VFO output CLK0, disable CLK1 & 2
  dds.output_enable(SI5351_CLK0, 1);
  dds.output_enable(SI5351_CLK1, 0);
  dds.output_enable(SI5351_CLK2, 0);

  // encoder, button, RX, TX, band and KEY pins
  pinMode(DT, INPUT_PULLUP);
  pinMode(CLK, INPUT_PULLUP);
  pinMode(SW, INPUT_PULLUP);

  freqOut(freq); // cHz, output freq

  dispFreq(0, 0, freq, 1); // display FREQ xxxxxx.xx kHz col 0 row 0
  dispStep(step, 10, 0); // display STEP xxxxHz|kHz col 8,  row 0
  dispDbm(); // display initial dBm, mW & mV

}

void loop() {

  // display dBm etc
  dispDbm();
  
  // tune?
  if (tune()) {
    freqOut(freq); // output freq
    dispFreq(0, 0, freq, 1); // update freq display
  }

  // step?
  if (button()) {
    dispStep(step, 10, 0); // update step display
  }
}

// tune?
bool tune() {
  unsigned char dir; // tuning direction CW/CCW

  dir = rot.process(); // read encoder
  if (dir != DIR_NONE) { // turned?
    if ((dir == DIR_CW) && (freq <= fmaximum)) freq += step;
    if ((dir == DIR_CCW) && (freq >= fminimum + 1)) freq -= step;
    return true;
  }
  else {
    return false;
  }
}

// change step?
bool button() {
  if (digitalRead(SW) == LOW) { // button pressed?
    while (!digitalRead(SW)); // wait for release
    if (step == 10000000) step = 10000; // back to 10Hz
    else step = step * 10; // or increase by x10
    return true;
  }
  else {
    return false;
  }
}

// Output Freq for VFO, on CLK0, f cHz
void freqOut(uint64_t f) {
  dds.set_freq(f, SI5351_CLK0);
}


// display freq in kHz at col c, row r, f cHz, d decimal places
void dispFreq(uint8_t c, uint8_t r, uint64_t f, uint8_t d) {
  lcd.setCursor(c, r);
  lcd.print((float)f / 100000, d); // convert to float & kHz
  lcd.print("kHz ");
}

void dispDbm() {
  float mV, dBm, mW, V;

  // calculations for mV input, dBm, mW and Volts
  mV = 1000.0 * (float)analogRead(DCIN) * (AREF / 1024);
  dBm = INTERCEPT + (mV / SLOPE);
  dBm += ATTN; // if external attenuation, add it back in
  mW = pow(10, (dBm / 10));
  V = sqrt((mW / 1000) * IMP);

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

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

  // Volts
  if (V < 1.0) {
    lcd.print(V * 1000.0, 0);
    lcd.print("mV  ");
  }
  else {
    lcd.print(V, 1);
    lcd.print("V  ");
  }
}

// display step
void dispStep(uint64_t s, byte c, byte r)
{
  lcd.setCursor(c, r);
  switch (s) // display step
  {
    case 1000:
      lcd.print("10Hz     ");
      break;
    case 10000:
      lcd.print("100Hz    ");
      break;
    case 100000:
      lcd.print("1kHz     ");
      break;
    case 1000000:
      lcd.print("10kHz    ");
      break;
    case 10000000:
      lcd.print("100kHz   ");
      break;
  }
}

No comments: