Sunday, 16 April 2017

BASIC Tech Group - MyNews 40 - VFO using AD9851 with OLED display

This code has been updated - see later posting!!

Here's the code for a VFO using the AD9851, up to 70MHz. The AD9851 code is here

// VFO_ADS51_OLED
// V1.0 16-4-17 VFO for the AD9851, 30MHz external xtal, x6 180MHz internal clock
// freq steps 10Hz, 100Hz, 1kHz, 10kHz, 100kHz, 1MHz
// AD9851
// RESET 8
// DATA 9
// FQ_UD 10
// W_CLK 11
// OLED 128x64
// SDA = A4
// SCL = A5
// rotary encoder pins
// CLK = 2
// DT = 3
// SW = 4


// OLED, AD9851, Rotary Encoder & I2C libraries
#include "Oled_128X64_I2C.h"
#include "ADS9851.h"
#include "Rotary.h"

// AD9851 pins
#define RESET 8
#define DATA 9
#define FQ_UD 10
#define W_CLK 11

// xtal calibration (30MHz external x6 REFCLK = 180MHz internal
#define CALIBRATE 180002300 // cal against SDR (cal at 7070 against CORRA)

// encoder
#define CLK 2
#define DT 3
#define SW 4

// ads (analog-output digital synthesiser) object
ADS9851 ads;

// Encoder object
Rotary enc = Rotary(DT, CLK);

// phase coding, 0-180 [0-5]
uint8_t ph[] = {0x00, 0x01, 0x02, 0x04, 0x08, 0x10};

// initial settings
volatile double freqHz = 7100000; // (Hz) start frequency 7.1MHz
volatile double freqChz = 0;  // (cHz) additional 0cHz
volatile double freqStep = 10; // (Hz) init 10Hz freqStep
uint8_t phase = ph[0]; // init phase

// freq change flag
volatile bool freqChange;

void setup() {
  // encoder, button pins
  pinMode(DT, INPUT_PULLUP);
  pinMode(CLK, INPUT_PULLUP);
  pinMode(SW, INPUT_PULLUP);

  // setup interrupts from DT or CLK for tuning
  attachInterrupt(digitalPinToInterrupt(DT), freqTune, CHANGE);
  attachInterrupt(digitalPinToInterrupt(CLK), freqTune, CHANGE);
  interrupts(); // enable

  // oled init, sets I2C addr to 0x3C
  oled.begin();

  // init ads, executes down() to flush buffers
  ads.begin(W_CLK, FQ_UD, DATA, RESET);

  // calibrate to xtal actual frequency
  ads.calibrate(CALIBRATE);

  ads.setFreq(freqHz, freqChz, phase);

  freqChange = false;

  dispUpdate();
}

// check button & freq tune, update display
void loop() {
  if (button()) {
    dispUpdate();
  }

  if (freqChange) {
    // freq updated
    ads.setFreq(freqHz, freqChz, phase);
    freqChange = false;
    dispUpdate();
  }
}


// ISR - encoder interrupt service routine
void freqTune() {
  unsigned char result;

  result = enc.process();
  if (result == DIR_CW ) {
    freqHz += freqStep;
    freqChange = true;
  }
  else if (result == DIR_CCW) {
    freqHz -= freqStep;
    freqChange = true;
  }
}

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

// picture loop, display init data
void dispUpdate() {
  oled.firstPage();
  do {
    dispMsg(50, 0, "VFO");
    dispFreq(15, 25, freqHz, freqChz, 2);
    dispMsg(15, 50, "<--");
    dispStep(45, 50, freqStep);
    dispMsg(95, 50, "-->");

  } while ( oled.nextPage() );
}

No comments: