ARDUINO NANO + SI5351 = QUADRATURE VFO (modificado em julho 2018)
# new mod 03/19 now from 3.2 to 110MHz




VFO for use in SDR radios.
From 3.2 to ( NOT 200MHz) 110MHz (now tested).
Direct quadrature from SI5351 (dont need 74xx74 or 4x input frequency).

Video no YOUTUBE

A cópia da biblioteca (modificada para freq. minima 400MHz) esta aqui  library NT7S  por motivos de update.

O arquivo (arduino sketch file) .INO original .txt


VFO de 3.2 a 110MHz com saídas defasadas em 90 graus, para uso com radios SDR ou de fase.
Um VFO simples com LCD de duas linhas 14 caracteres, arduino nano e o SI5351.



Esquema :

As saidas defasadas estão nos clocks 0 e 1, pinos 9 e 10 do SI5351.
Devido ao PLL interno  do SI5351 trabalhar de 600 a 900MHz (o meu esta até 880MHz) e a necessidade de usar um divisor par para obter a quadratura e este divisor é limitado entre 0 e 127, o maior divisor é 126.
 Assim a menor frequencia obtida é 600/126=4,7619MHz.
Com a simples modificação de uma linha na biblioteca (library), alterei a frequencia minima para 400MHz, foi possivel atingir os 3,2MHz (400/126=3,174).
A maior frequencia possivel, gerando quadratura, foi com a divisão por 8 assim o meu VFO ficou limitado a 110MHz (880/8).
Ainda testei o VFO, com a quadratura, até 30Mhz, como não tenho equipamentos para testar uma frequencia maior, peço a algum colega que monte este VFO que escreva dizendo dos resultados (agora testei até 110MHz, acima disso não funciona).
Os meus sketches são simples funcionam, mas podem conter bugs, não sou programador mas um simples curioso. Os sketches são abertos a modificação, mas o uso comercial deve ser visto com outros idealizadores do programa e biblioteca.
#(O Hans Summers G0UPL explica na apresentação do kit do QCX que o SI5351 pode gerar quadratura no minimo a 3,2MHz, o que não consegui, vou escrever a ele e ao Jason Milldrum NT7S, autor da biblioteca para esclarecer como fazer.)#
Fiz  alteração de uma linha na biblioteca do
Jason Milldrum NT7S, alterando a frequencia minima do PLL de 600MHz para 400MHz e funcionou !
Em um proximo trabalho farei um VFO, como este, mas com possibilidade de ajuste independente do clock2, assim gerando um outro sinal independente.
Outra modificação possivel é a inversão de sinais de um dos cloks (0 ou 1) gerando um sinal de 0 a 270 graus, ao inves de 0 a 90 graus, assim permitindo via software a inversão de banda lateral em equipamentos SSB. Já testei um transceptor SSB por fase (não SDR / sem o uso do PC) usando este VFO.

Montagem
Montei em uma placa padrão, montando como sandwich.
Não ficou boa pois o uso dos pinos digitais 2 e 3 do arduino, no encoder é de uso exclusivo e foi necessario modificar.
Falta cortar o restante da placa sem uso.










Sketch


/*
This entire program is taken from Jason Mildrum, NT7S and Przemek Sadowski, SQ9NJE.
There is not enough original code written by AK2b to make it worth mentioning.
http://nt7s.com/
http://sq9nje.pl/
http://ak2b.blogspot.com/
I made some mods ...first updating the sketch to new library from NT7S
..in frequency coverage and the mode for frequency change..
pressing the encoder and turn it at same time ...it will move a underline showing
 the place where it is OK to change
 XXXXXXXXXXXXXXXXXXXXXX
 Now I made other mods ... is OK to use with SDR rigs ... the clock0 and
clock1 are in the same frequency, but in 90 degrees phase out.. it is ok
from 3.2MHz to 110MHz (teoric) I tested with 30MHz and it was OK
(to reach 3.2MHz I changed the NT7S library)
I made a new function according Hans Summers, for dont have clicks
when change the frequency ...reseting the SI5351 only when the evendivider is changed

 http://py2ohh.w2c.com.br/
*/

#include <Rotary.h>
#include <si5351.h>
#include <Wire.h>
#include <LiquidCrystal.h>



#define F_MAX        22000000000UL

#define ENCODER_A    2                      // Encoder pin A  nano/uno pin D2
#define ENCODER_B    3                      // Encoder pin B  nano/uno pin D3
#define ENCODER_BTN  8
#define LCD_RS       12                     // LCD pin 4 nano/uno pin D12
#define LCD_E        10                  // LCD pin 6 nano/uno pin D10
#define LCD_D4        5                 // LCD pin11 nano/uno pin D5
#define LCD_D5        4                  // LCD pin12 nano/uno pin D4
#define LCD_D6        7                  // LCD pin13 nano/uno pin D7
#define LCD_D7        6                  // LCD pin14 nano/uno pin D6

LiquidCrystal lcd(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7);       // LCD - pin assignement in
Si5351 si5351;
Rotary r = Rotary(ENCODER_A, ENCODER_B);

volatile uint32_t vfo = 700000000ULL / SI5351_FREQ_MULT; //start freq now 7MHz- change to suit
volatile uint32_t vfo2 = 300000000ULL / SI5351_FREQ_MULT;
volatile uint32_t radix = 100;    //start step size - change to suit
double vfomhz ;
boolean changed_f = 0;
boolean changed_f2 = 0;
String tbfo = "";
short und = 3;   //controle do underline
short pot = 3;   // controle de multiplicador
int evendivisor = 100;
int oldevendivisor = 0;
//#define Direct_conversion //What you see on display is what you get

/**************************************/
/* Interrupt service routine for      */
/* encoder frequency change           */
/**************************************/
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 and underline  */
/* dir = 1    Increment               */
/* dir = -1   Decrement
/**************************************/
void set_frequency(short dir)
{
  if (!digitalRead(ENCODER_BTN)) {
    lcd.setCursor( 12 - und, 0);
    if (dir == 1) {
      und += 1;
      switch (und) {
        case 4 :
          und = 5;
          break;
        case 8 :
          und = 9;
          break;
        case 12 :
          und = 11;
          break;
      }

    }

    if (dir == -1) {
      und += -1;
      switch (und) {
        case 4 :
          und = 3;
          break;
        case 8 :
          und = 7;
          break;
        case 0 :
          und = 1;
          break;
      }
    }
    pot = und;
    if (und > 3) (pot += -1);
    if (und > 7) (pot += -1);


    lcd.setCursor( 12 - und, 0);
    lcd.cursor();
  }

  else
  { lcd.noCursor();
    if (dir == 1)
      vfo += radix;
    if (dir == -1) {
      if  (vfo > radix ) {
        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;

  lcd.setCursor(1, 0);

  f = (vfo ) / 1000000;     //variable is now vfo instead of 'frequency'
  if (f < 100)  {
    lcd.print(' ');
  }
  if (f < 10) {
    lcd.print(' ');
  }
  lcd.print(f);
  lcd.print('.');
  f = (vfo % 1000000) / 1000;
  if (f < 100)
    lcd.print('0');
  if (f < 10)
    lcd.print('0');
  lcd.print(f);
  lcd.print('.');
  f = vfo % 1000;
  if (f < 100)
    lcd.print('0');
  if (f < 10)
    lcd.print('0');
  lcd.print(f);
  lcd.print("Hz");



}


void setup()
{
  Serial.begin(19200);
  lcd.begin(16, 2);                                                    // Initialize and clear the LCD
  lcd.clear();
  Wire.begin();

  si5351.set_correction(140); //**mine. There is a calibration sketch in File/Examples/si5351Arduino-Jason
  //where you can determine the correction by using the serial monitor.

  //initialize the Si5351
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 27000000, 0); //If you're using a 27Mhz crystal, put in 27000000 instead of 0
  // 0 is the default crystal frequency of 25Mhz.
 // si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
  //  si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
  // Set CLK0 to output the starting "vfo" frequency as set above by vfo = ?

 si5351.drive_strength(SI5351_CLK0,SI5351_DRIVE_8MA); //you can set this to 2MA, 4MA, 6MA or 8MA
  //be careful though - measure into 50ohms


  si5351.set_freq_manual(vfo * SI5351_FREQ_MULT, evendivisor * vfo * SI5351_FREQ_MULT, SI5351_CLK0);
  si5351.set_freq_manual(vfo * SI5351_FREQ_MULT, evendivisor * vfo * SI5351_FREQ_MULT, SI5351_CLK1);


  si5351.set_phase(SI5351_CLK0, 0);
  si5351.set_phase(SI5351_CLK1, evendivisor);
  si5351.pll_reset(SI5351_PLLA);
 

  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

}


void loop()
{
  // Update the display if the frequency has been changed
  if (changed_f)
  {
    vfomhz = vfo / 10000;
    if (vfomhz > 320) {//era476
      display_frequency();
    }
    else {
      lcd.setCursor(0, 1);
      // lcd.print(vfomhz);
      // delay(1000);
      lcd.print("NOT BELOW 3.2MHz");
      delay(1000);
      lcd.setCursor(0, 1);
      lcd.print("                ");
    }
    if ((vfomhz > 11000)) {
      lcd.print("NOT OVER 110MHz");
      delay(1000);
      lcd.setCursor(0, 1);
      lcd.print("                ");
    }
    //altera evendivisor
    alteraevendivisor();


   
    si5351.set_freq_manual(vfo * SI5351_FREQ_MULT, evendivisor * vfo * SI5351_FREQ_MULT, SI5351_CLK0);
    si5351.set_freq_manual(vfo * SI5351_FREQ_MULT, evendivisor * vfo * SI5351_FREQ_MULT, SI5351_CLK1);


    si5351.set_phase(SI5351_CLK0, 0);
    si5351.set_phase(SI5351_CLK1, evendivisor);
    if (evendivisor != oldevendivisor) { //reset if evendivisor is changed
      si5351.pll_reset(SI5351_PLLA);
      oldevendivisor = evendivisor;
    }
    tbfo = "";
   



    changed_f = 0;
  }


  if (get_button())
  {
    switch (pot)
    {
      case 1:
        radix = 1;
        break;
      case 2:
        radix = 10;
        break;
      case 3:
        radix = 100;
        break;
      case 4:
        radix = 1000;
        break;
      case 5:
        radix = 10000;
        break;
      case 6:
        radix = 100000;
        break;
      case 7:
        radix = 1000000;
        break;
      case 8:
        radix = 10000000;
        break;
      case 9:
        radix = 100000000;
        break;

    }

  }
}
void alteraevendivisor()
{
  if (vfomhz < 685) {
    evendivisor = 126;
  }
  if ((vfomhz >= 685) && (vfomhz < 950)) {
    evendivisor = 88;
  }
  if ((vfomhz >= 950) && (vfomhz < 1360)) {
    evendivisor = 64;
  }
  if ((vfomhz >= 1360) && (vfomhz < 1750)) {
    evendivisor = 44;
  }
  if ((vfomhz >= 1750) && (vfomhz < 2500)) {
    evendivisor = 34;
  }
  if ((vfomhz >= 2500) && (vfomhz < 3600)) {
    evendivisor = 24;
  }
  if ((vfomhz >= 3600) && (vfomhz < 4500)) {
    evendivisor = 18;
  }
  if ((vfomhz >= 4500) && (vfomhz < 6000)) {
    evendivisor = 14;
  }
  if ((vfomhz >= 6000) && (vfomhz < 8000)) {
    evendivisor = 10;
  }
  if ((vfomhz >= 8000) && (vfomhz < 11000)) {
    evendivisor = 8;
  }
  //if ((vfomhz >= 10000) && (vfomhz < 14660)) {
 //   evendivisor = 6;
//  }
 // if ((vfomhz >= 15000) && (vfomhz < 22000)) {
 //   evendivisor = 4;
 // }
}

73 de py2ohh miguel
jun 2018 # 03/2019