Playing with Phasing - part 2a - VFO control code

Code for Quadrature VFO control.  Credits to ZL2CTM and NT7S (and probably others..)

Use at your own risk - If your radio starts to glow green and explodes from running this code, it's not my (or anyone else's) fault.  Target is Arduino Nano or Metro mini. 

_______________________________________________________________________________________________

 //
#include <Rotary.h>
#include <si5351.h>
#include <Wire.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"

#define I2C_ADDRESS 0x3C

// Define proper RST_PIN if required.
#define RST_PIN -1

SSD1306AsciiWire oled;

#define ENCODER_A 3  // Encoder pin A
#define ENCODER_B 2  // Encoder pin B
#define ENCODER_BTN 11
#define RESET_BTN 12
#define TX_ON 6
#define OFFSET_ON 8

Si5351 si5351;
Rotary r = Rotary(ENCODER_A, ENCODER_B);
volatile uint32_t cw_offset = 0ULL;
volatile uint32_t cwo_old = 0ULL;

volatile uint32_t radix = 1000;  //start step size - change to suit
boolean changed_f = 0;
String tbfo = "";
static const long long pll_min = 38000000000ULL;
static const long long pll_max = 90000000000ULL;
static const long long bandInit = 1410000000ULL;
volatile long long vfo = bandInit / SI5351_FREQ_MULT;
int multiple;
float pll_freq;
bool reset_pll;
bool tx_mode;
bool offset_mode;

ISR(PCINT2_vect) {
  unsigned char result = r.process();
  if (result == DIR_CW)
    set_frequency(1);
  else if (result == DIR_CCW)
    set_frequency(-1);
}

/* Change the frequency               */
/* dir = 1    Increment               */
/* dir = -1   Decrement               */
/**************************************/
void set_frequency(short dir) {
  if (dir == 1)
    vfo += radix;
  if (dir == -1)
    vfo -= radix;
  changed_f = 1;
}
/**************************************/
/* Read the button with debouncing    */
/**************************************/
boolean get_button() {
  if (!digitalRead(ENCODER_BTN)) {
    delay(20);
    if (!digitalRead(ENCODER_BTN)) {
      while (!digitalRead(ENCODER_BTN));
      return 1;
    }
  }
  return 0;
}

/**************************************/
/* Displays the frequency             */
/**************************************/
void display_frequency() {
  uint16_t f, g;
  //oled.clear(PAGE);
  oled.setCursor(0, 0);
  f = vfo / 1000000;  //variable is now vfo instead of 'frequency'
  if (f < 10)
    oled.print(' ');
  oled.print(f);
  oled.print('.');
  f = (vfo % 1000000) / 1000;
  if (f < 100)
    oled.print('0');
  if (f < 10)
    oled.print('0');
  oled.print(f);
  //oled.print('.');
  f = vfo % 1000;
  if (f < 100)
    oled.print('0');
  if (f < 10)
    oled.print('0');
  oled.print(f);
  oled.setCursor(60, 0);
  oled.print("MHz");
  oled.setCursor(0, 3);
  oled.print(tbfo);

  //Serial.println(vfo + bfo);
  //Serial.println(tbfo);
}

/**************************************/
/* Displays the frequency change step */
/**************************************/
void display_radix() {
  oled.setCursor(84, 0);
  switch (radix) {
    case 1:
      oled.print("    1");
      break;
    case 10:
      oled.print("   10");
      break;
    case 100:
      oled.print("  100");
      break;
    case 1000:
      oled.print("   1k");
      break;
    case 10000:
      oled.print("  10k");
      break;
    case 100000:
      //oled.setCursor(10, 40);
      oled.print(" 100k");
      break;
    case 1000000:
      //oled.setCursor(0, 40);
      oled.print("1000k");  //1MHz increments
      break;
  }
  oled.print("Hz");
}

void GetPLLFreq()

{
  if (vfo < 6850000) {
    multiple = 126;
  }
  if ((vfo >= 6850000) && (vfo < 9500000)) {
    multiple = 88;
  }
  if ((vfo >= 9500000) && (vfo < 13600000)) {
    multiple = 64;
  }
  if ((vfo >= 13600000) && (vfo < 17500000)) {
    multiple = 44;
  }
  if ((vfo >= 17500000) && (vfo < 25000000)) {
    multiple = 34;
  }
  if ((vfo >= 25000000) && (vfo < 36000000)) {
    multiple = 24;
  }
  if ((vfo >= 36000000) && (vfo < 45000000)) {
    multiple = 18;
  }
  if ((vfo >= 45000000) && (vfo < 60000000)) {
    multiple = 14;
  }
  if ((vfo >= 60000000) && (vfo < 80000000)) {
    multiple = 10;
  }
  if ((vfo >= 80000000) && (vfo < 100000000)) {
    multiple = 8;
  }
  if ((vfo >= 100000000) && (vfo < 146600000)) {
    multiple = 6;
  }
  if ((vfo >= 150000000) && (vfo < 220000000)) {
    multiple = 4;
  }

  pll_freq = multiple * (vfo * SI5351_FREQ_MULT);


}

void send_freq() {

  int old_mult = multiple;
  GetPLLFreq();

  si5351.set_pll(pll_freq, SI5351_PLLA);

  si5351.set_freq_manual(((vfo+cw_offset) * SI5351_FREQ_MULT), pll_freq, SI5351_CLK0);
  si5351.set_freq_manual(((vfo+cw_offset) * SI5351_FREQ_MULT), pll_freq, SI5351_CLK2);

  si5351.set_phase(SI5351_CLK0, 0);
  si5351.set_phase(SI5351_CLK2, multiple);


  if (multiple != old_mult) {
    si5351.pll_reset(SI5351_PLLA);
  }


  tbfo = "QUAD";
}

void setup() {
  // Serial.begin(9600);
  Wire.begin();
  pinMode(RESET_BTN, INPUT_PULLUP);
  pinMode(TX_ON, INPUT_PULLUP);
  pinMode(OFFSET_ON, INPUT_PULLUP);

#if RST_PIN >= 0
  oled.begin(&Adafruit128x64, I2C_ADDRESS, RST_PIN);
#else   // RST_PIN >= 0
  oled.begin(&Adafruit128x64, I2C_ADDRESS);
#endif  // RST_PIN >= 0

  oled.setFont(Adafruit5x7);
  oled.clear();


  //initialize the Si5351
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 11000);
  //Correction = 11000 for Adafruit board; 118500 for Chinese board

  //si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
  si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_2MA);
  si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_2MA);
  // Set CLK0 to output the starting "vfo" frequency as set above by vfo = ?

  send_freq();

  pinMode(ENCODER_BTN, INPUT_PULLUP);
  PCICR |= (1 << PCIE2);  // Enable pin change interrupt for the encoder
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  sei();
  display_frequency();  // Update the display
  display_radix();
}


void loop() {
  // Update the display if the frequency has been changed
  if (changed_f) {
    display_frequency();
    send_freq();
    changed_f = 0;
  }

  // Button press changes the frequency change step for 1 Hz steps
  if (get_button()) {
    switch (radix) {
      case 1:
        radix = 10;
        break;
      case 10:
        radix = 100;
        break;
      case 100:
        radix = 1000;
        break;
      case 1000:
        radix = 10000;
        break;
      case 10000:
        radix = 100000;
        break;
      case 100000:
        radix = 1000000;
        break;
      case 1000000:
        radix = 1;
        break;
    }
    display_radix();
  }
    reset_pll = digitalRead(RESET_BTN);
    if (reset_pll == 0)
    {
    si5351.pll_reset(SI5351_PLLA);
    delay(1000);
    }
    cwo_old = cw_offset;
    offset_mode = digitalRead(OFFSET_ON);
    tx_mode = digitalRead(TX_ON);
    if (!offset_mode && !tx_mode)
    {
    cw_offset = 800ULL;
     if (cw_offset != cwo_old)
     {
      changed_f = 1;
      cwo_old = cw_offset;
     }
    }
    else
    {
    cw_offset = 0ULL;
     if (cw_offset != cwo_old)
     {
      changed_f = 1;
      cwo_old = cw_offset;
     }
    }
 
}

Comments

Popular posts from this blog

Even a Blind Pig finds an acorn once and a while (35 watt push-pull RD30HVF1 amp for HF)

Playing with Phasing - part 1

Playing with Phasing - part 2b - audio processing code