2026年2月4日水曜日

ADF4351 VFO

PLL ADF4351(35MHz-4.4GHz PLL)を使ってVFOを作った。このADF4351の用途は、SFH帯のLOとして固定周波数で使う事が多いと思われる。以前は1200MHz帯の自作でも、LOが出来れば半分以上完成したと言われていた。今回の製作は、10kHzステップのVFOとした。ADF4351ボードは数種類有るようだ。クロック25MHz搭載タイプを入手した。しかしオシレータの精度が悪いため、秋月電子製12.8MHz VCTCXOと交換。VFOの機能は、レピータシフト(最大±90MHz)、最小10kHzステップ。





PCBの様子

左側がADF4351をプラグイン仕様に改造し、メインボードに実装している。マイコンはPro mini 3.3V。oledの上側に有るのがレベル変換ボード。






回路図


 










スケッチ

 //////////////////////////////////////////////////////////////////////////////

//       Copyright©2018.JA2GQP.All rights reserved.
//            ADF4351 Pll Local oscillator        
//                                                    2026/2/4
//                                                    JA2GQP    
//
//----------------------------------------------------------------------------    
//  The original is here.
//      https://github.com/Giorgiofox/ADF4351-Arduino-LCDSHIELD
//
//////////////////////////////////////////////////////////////////////////////

#include "src/Rotary.h"             // http://www.buxtronix.net/2011/10/rotary-encoders-done-properly.html
#include <SPI.h>
#include <Tiny4kOLED.h>
#include "font/Tlcdnums14x24.h"
#include "font/Tfont5x7.h"
#include <EEPROM.h>

//------------------------ Define value ---------------------------------------

#define ENC_A         2                 // encorder A
#define ENC_B         3                 //          B
#define LE            5                 //         LE
#define SW_STEP       6                 // STEP SW
#define SW_REPT       7                 // REPT SW
#define SW_TX         8                 // TX SW
#define Xtal_Freq     12.8              // ADF4351 clock 12.8MHz
#define Scal_10kHz    0.01              //         STEP 10kHz

#define DEF_Freq      115000            // default frequency 1150.00MHz
#define DEF_Step      100               //         step 1MHz
#define DEF_Rsft      -2000             //         repeater shift -20.00MHz

///---------- Set Device -----------------------------

Rotary r = Rotary(ENC_A, ENC_B);              

//---------- EEPROM Memory Address ------------------

#define Eep_Init    0x00                // Eep Init(1byte)
#define Eep_Resw    0x02                // REPT sw(1byte)
#define Eep_Step    0x04                // STEP(4byte)
#define Eep_Rsft    0x08                // REPT shift(4byte)
#define Eep_Freq    0x0c                // Frequency(4byte)

//---- frequency limit offset
#define LOW_REPT   -9000
#define HI_REPT    9000

//------------------------ Register definition --------------------------------

/////////////////////////////////////////
// ADF4351 Register initialize value
/////////////////////////////////////////
unsigned long registers[6] = {          // 437.00MHz @12.8MHz
        0x888008,                       // Register 0( 0x4580A8   @25MHz)
        0x8008041,                      //          1(0x80080C9         )
        0x4E42,                         //          2(   0x4E42         )          
        0x4B3,                          //          3(    0x4B3         )
        0xB6703C,                       //          4( 0xBC803C         )
        0x580005} ;                     //          5( 0x580005         )    

/////////////////////////////////////////
// Register declaration
/////////////////////////////////////////
double  RFout,
        REFin,
        PFDRFout = Xtal_Freq,               // X'tal frequency set
        OutputChannelSpacing = Scal_10kHz,  // Scaling(10kHz)
        FRACF;
long    RFint,
        RFintold = 0,
        INTA,
        MOD,
        FRAC;
byte OutputDivider;

/////////////////////////////////////////
// etc
/////////////////////////////////////////
const byte Int_End = 73;                // Initial end code
byte Flg_eepWT = 0;                     // EEP Write Flag
byte Flg_Tx = 0;
byte Flg_Resw;
byte Flg_Re=0;
int Rsft = DEF_Rsft;
unsigned long Time_Passd;               // int to hold the arduino miilis since startup
long Time_Mode;                         // Mode change Time
int32_t Enc_Step;                       // STEP
int8_t Enc_Dir = 0;

//------------------------ Setup ----------------------------------------------

void setup(){
  pinMode(SW_STEP, INPUT_PULLUP);
  pinMode(SW_REPT, INPUT_PULLUP);
  pinMode(SW_TX, INPUT_PULLUP);          
  pinMode(LE, OUTPUT);                  // Setup pins

  attachInterrupt(0, rotary_encoder, CHANGE);
  attachInterrupt(1, rotary_encoder, CHANGE);

  oled.begin();
  oled.clear();
  oled.on();
 
  if(EEPROM.read(Eep_Init) != Int_End){ // Eep initialaz
    delay(10);
    eep_init();
  }
  else{
    if(digitalRead(SW_STEP) == LOW){
      delay(10);
      eep_init();
      fover_disp();
      while (digitalRead(SW_STEP) == LOW);
    }
  }

  digitalWrite(LE, HIGH);
  SPI.begin();                          // Init SPI bus
  SPI.setDataMode(SPI_MODE0);           // CPHA = 0 et Clock positive
  SPI.setBitOrder(MSBFIRST);            // highs in the lead

  eep_rdata();
  freq_disp(RFint);                     // frequency display
  step_disp();
}

//------------------------ Main -----------------------------------------------

void loop(){
/////////////////////////////////////////
// TX SW check
/////////////////////////////////////////
  if(digitalRead(SW_TX) == LOW)        
    Flg_Tx = 1;                         // <<< Tx >>>
  else
    Flg_Tx = 0;                         // <<< Rx >>>

/////////////////////////////////////////
// Repeater PROC
/////////////////////////////////////////
  if(Flg_Resw == 0){                    // repeater off?
    freq_disp(RFint);
    rpt_disp(Rsft);
    Pll_ADF4351(RFint);  
  }
  if(Flg_Resw == 1){                    // repeater on?
    freq_disp(RFint);
    rpt_disp(Rsft);
    if(Flg_Tx == 1)                     // <<< Tx >>>
      Pll_ADF4351(RFint + Rsft);        // frequency shift
    else                                // <<< Rx >>>
      Pll_ADF4351(RFint);               // normal frequency  
  }
  else if(Flg_Resw == 2){
    shift_disp(Rsft);
    rpt_disp(Rsft);
  }

/////////////////////////////////////////
// Step PROC
/////////////////////////////////////////
  if (digitalRead(SW_STEP) == LOW) {    // increment events
    enc_step();
    step_disp();
    delay(300);
    while (digitalRead(SW_STEP) == LOW);
  }

/////////////////////////////////////////
// Repeater SW check
/////////////////////////////////////////
  if(digitalRead(SW_REPT) == LOW){
    Time_Mode = millis();
 
    while (digitalRead(SW_REPT) == LOW){
      while (digitalRead(SW_REPT) == LOW);
      if(Time_Mode+1000 < millis()){
        Flg_Resw = 2;
        break;
      }      
      if(Flg_Resw == 0){
        Flg_Resw = 1;
        EEPROM.write(Eep_Resw,Flg_Resw);                  
        break;
      }
      else {
        Flg_Resw = 0;
        EEPROM.write(Eep_Resw,Flg_Resw);                
        break;
      }
    }
   
  }

/////////////////////////////////////////
// freqency EEP auto write
/////////////////////////////////////////
  if(Flg_eepWT == 1){                   // EEPROM auto Write
    if(Time_Passd+2000 < millis()){
      eep_wdata();
      Flg_eepWT = 0;
    }
  }
}

//----------  Write EEPROM 4byte  --------------------------------------

void eep_write4(long value,int address){
  address += 3;
  for(int i = 0;i < 4;i++){
    byte toSave = value & 0xFF;
    if(EEPROM.read(address) != toSave){
      EEPROM.write(address,toSave);
      }
    value = value >> 8;
    address--;
  }
}

//----------  Read EEPROM 4byte  ---------------------------------------

long eep_read4(int address){
  long value = 0;

  for(int i = 0;i < 4;i++){
    value = value | EEPROM.read(address);
    if( i < 3){
      value = value << 8;
      address++;
    }
  }
  return value;
}

//----------  EEPROM Dat Read  -----------------------------------------

void eep_rdata(){
  RFint = eep_read4(Eep_Freq);
  Enc_Step = eep_read4(Eep_Step);
  Rsft = eep_read4(Eep_Rsft);
  Flg_Resw = EEPROM.read(Eep_Resw);
}

//----------  EEPROM Dat Write  ----------------------------------------

void eep_wdata(){
  eep_write4(RFint,Eep_Freq);
  eep_write4(Enc_Step,Eep_Step);
  eep_write4(Rsft,Eep_Rsft);
}  
   
//----------  EEPROM Initialization ------------------------------------

void eep_init(){
  int i;

  for (i=0;i<128;i++)                   // 0 clear(128byte)
    EEPROM.write(i, 0);

  EEPROM.write(Eep_Resw,Flg_Resw);      // Repeater off
  eep_write4(DEF_Freq,Eep_Freq);        // Frequency 1150MHz
  eep_write4(DEF_Step,Eep_Step);        // Step 1MHz
  eep_write4(DEF_Rsft,Eep_Rsft);        // Repeater shift -20MHz

  EEPROM.write(Eep_Init,Int_End);       // Init end set(73)  
}

//----------  Rotaly Encorder  -----------------------------------------

void rotary_encoder() {                 // rotary encoder events
long wk4;
int wk2;
  uint8_t result = r.process();

  if(Flg_Tx == 0){
    if (result) {
      if (result == DIR_CW){
        wk2 = Rsft + Enc_Step;
        wk4 = RFint + Enc_Step;
      }  
      else{
        wk2 = Rsft - Enc_Step;
        wk4 = RFint - Enc_Step;
      }

      wk2 = constrain(wk2,LOW_REPT,HI_REPT);

      if(Flg_Resw == 2)
        Rsft = wk2;
      else
        RFint = wk4;
     
      Flg_eepWT = 1;
      Time_Passd = millis();
    }
  }
}

//----------  Encorede STEP  -------------------------------------------

void enc_step() {
  if(Enc_Step == 1)
    Enc_Step = 10;
  else if(Enc_Step == 10)
    Enc_Step = 100;
  else if(Enc_Step == 100)
    Enc_Step = 1000;
  else if(Enc_Step == 1000)
    Enc_Step = 1;
  else
    Enc_Step = 100;
}

//----------  STEP Display  --------------------------------------------

void step_disp() {
  oled.setCursor(64, 3);
  oled.print("    ");                  
  oled.setCursor(64, 3);
  oled.setFont(&Tfont5x7);
  if (Enc_Step == 1)
    oled.print(" 10k");                 // 10k
  else if(Enc_Step == 10)
    oled.print("100k");                 // 100k
  else if(Enc_Step == 100)
    oled.print("  1M");                 // 1M
  else
    oled.print(" 10M");                 // 10M
}

//---------- Frequency renge over --------------------------------------

void fover_disp() {
  oled.setFont(&Tlcdnums14x24);
  oled.setCursor(1, 0);
  oled.print("--.---.--");
}

//---------- Frequency renge over --------------------------------------

void rpt_disp(int32_t rep_shift) {
  oled.setFont(FONT8X16);
  if(Flg_Resw == 1){
    oled.setFont(FONT8X16);
    oled.setCursor(102, 0);
    oled.print("REP");
    oled.setCursor(102, 2);
    if(rep_shift >= 0)
      oled.print('+');
      oled.print(rep_shift / 100);
      oled.print(' ');
  }
  else {
    oled.setCursor(102, 0);
    oled.print("   ");
    oled.setCursor(102, 2);
    oled.print("   ");
  }
}

//----------  Shift Display  ---------------------------------------

void shift_disp(int32_t sf_rx) {
  int32_t fr;

  oled.setFont(&Tlcdnums14x24);
  oled.setCursor(0, 0);
  fr = sf_rx / 100;
    oled.print(':');                    // ':' is changed to ' ' in lcdnums14x24.h
  if(fr >= 0)
    oled.print('+');                    
  oled.print(fr);
  oled.print('.');
  fr = (sf_rx % 100);
  if (fr < 10)
    oled.print('0');
  oled.print(fr);
    oled.print(':');                    // ':' is changed to ' ' in lcdnums14x24.h
}

//----------  Frequency Display  ---------------------------------------

void freq_disp(int32_t sf_rx) {
  int32_t fr;

  oled.setFont(&Tlcdnums14x24);
  oled.setCursor(0, 0);
  fr = sf_rx / 100;
  if (fr < 1000)
    oled.print(':');                    // ':' is changed to ' ' in lcdnums14x24.h
  if (fr < 100)
    oled.print(':');                    // ':' is changed to ' ' in lcdnums14x24.h
  oled.print(fr);
  oled.print('.');
  fr = (sf_rx % 100);
  if (fr < 10)
    oled.print('0');
  oled.print(fr);
}

//------------------------ SPI 32BIT Register Write proc. ---------------------

void WriteRegister32(const uint32_t value){
  digitalWrite(LE, LOW);
  for (int i = 3; i >= 0; i-- )              
    SPI.transfer((value >> 8 * i) & 0xFF);
  digitalWrite(LE, HIGH);
  digitalWrite(LE, LOW);
}

//------------------------ ADF4351 Register set -------------------------------

void Set_Reg()                                
{
  for (int i = 5; i >= 0; i-- )         // Reg5 --> Reg0 Write
  WriteRegister32(registers[i]);
}

//------------------------ Pll ADF4351 proc. ----------------------------------

void Pll_ADF4351(int32_t frq){
  RFout=frq;
  RFout=RFout/100;

  if (frq != RFintold){
    if (RFout >= 2200){
      OutputDivider = 1;
      bitWrite (registers[4], 22, 0);
      bitWrite (registers[4], 21, 0);
      bitWrite (registers[4], 20, 0);
    }
    if (RFout < 2200){
      OutputDivider = 2;
      bitWrite (registers[4], 22, 0);
      bitWrite (registers[4], 21, 0);
      bitWrite (registers[4], 20, 1);
    }
    if (RFout < 1100){
      OutputDivider = 4;
      bitWrite (registers[4], 22, 0);
      bitWrite (registers[4], 21, 1);
      bitWrite (registers[4], 20, 0);
    }
    if (RFout < 550){
      OutputDivider = 8;
      bitWrite (registers[4], 22, 0);
      bitWrite (registers[4], 21, 1);
      bitWrite (registers[4], 20, 1);
    }
    if (RFout < 275){
      OutputDivider = 16;
      bitWrite (registers[4], 22, 1);
      bitWrite (registers[4], 21, 0);
      bitWrite (registers[4], 20, 0);
    }
    if (RFout < 137.5){
      OutputDivider = 32;
      bitWrite (registers[4], 22, 1);
      bitWrite (registers[4], 21, 0);
      bitWrite (registers[4], 20, 1);
    }
    if (RFout < 68.75){
      OutputDivider = 64;
      bitWrite (registers[4], 22, 1);
      bitWrite (registers[4], 21, 1);
      bitWrite (registers[4], 20, 0);
    }

    INTA = (RFout * OutputDivider) / PFDRFout;
    MOD = (PFDRFout / OutputChannelSpacing);
    FRACF = (((RFout * OutputDivider) / PFDRFout) - INTA) * MOD;
    FRAC =round(FRACF); // On arrondit le résultat

    registers[0] = 0;                   // Register 0
    registers[0] = INTA << 15;// OK
    FRAC = FRAC << 3;
    registers[0] = registers[0] + FRAC;

    registers[1] = 0;                   // Register 1
    registers[1] = MOD << 3;
    registers[1] = registers[1] + 1 ;   // adding the address "001"
    bitSet (registers[1], 27);          // Prescaler on 8/9

    bitSet (registers[2], 28);          // Register 2(Pll lock setup)
    bitSet (registers[2], 27);
    bitClear (registers[2], 26);

    Set_Reg();                          // ADF4351 Register write
    RFintold = frq;
  }
}

ダウンロード

回路図、スケッチなどJA2GQP's Download siteのadf4351フォルダからダウンロード出来る。