Control the ADF4351 with an Arduino – Source code

Hey all,

I've had a few requests for Arduino code to run the ADF4351 (also ADF4350) from an Arduino.

What I am posting is code that I received from Richard – OE6OCG a year or two back. He set it up to use with an LCD and 4 button controller. For my use I modified it to use a rotary encoder, TFT touch display, and some other additions, but that's for another post and not included here. This is exactly as I received it.

If you would like to see cetain features added to this code leave me a comment, send me an email, or get me on twitter @kd0cq.

I cobbled together the ADF4351 and an Analog Devices RF Detector Surf Board kit and made a microwave SNA of sorts. The eval board has 3 devices on it, ADL5902, ADL5513, and ADL5511 and only cost $5+ship! lol can't beat that for an eval board. I think they still have some left at that price. Check www.richardsonrfpd.com (a division of ARROW) Part # ADL5XDETECTRKIT

Whenever I find the files for that again I'll post them as well.

I'm also providing a download link as the Code display on this theme sucks.

If you use the code below please send Richard a few dollars for beer money or radio toys!

CLICK TO DOWNLOAD ~4KB

//
//*****************************************************************
// ADF4351 PLL-Synthesizer 33Mhz - 4,4Ghz
// Integer mode Demo OE6OCG 1/2015
// Hardware Arduino Uno mit Adafruit Keyb-LCD Shield 16x2 ~ 10USD
// ADF4351 PLL-VCO Board assembled from Ebay or China ~ 40 USD
// 
// Buttoms:
// up/down = Freq +- with stepsize
// left = frequency step's from 6.25khz to 1Mhz
// right = scan with stepsize
// select = predefined startfrequencies from 34Mhz to 4.4 Ghz
//
// IDE: Arduino 1.5.4 or up without spec. Library
// License: Free or a gift to Paypal oe6ocg@aon.at
//*****************************************************************
/*                                               Hardware connection Uno to PLL-Board (3.3V logic)
                                  +-----+
     +----[PWR]-------------------| USB |--+
     |                            +-----+  |                        output: 33Mhz - 4.4Ghz ~ 3 dbm
     |         GND/RST2  [ ][ ]            |             PLL-Board + Arduino with Display Bk.light draw 200mA on 5V USB 
     |       MOSI2/SCK2  [ ][ ]  A5/SCL[ ] |                                       #
     |          5V/MISO2 [ ][ ]  A4/SDA[ ] |    |---|_3.3k_|-----\                 |
     |                             AREF[ ] |    !---|_3.3k_|---\ |            _____|______
     |                              GND[ ] |----!---|_3.3k_|-| | |           |            |
     | [ ]N/C                    SCK/13[ ] |------|_1.5k_|---!-|-|----Clock--|ADF4351     |
     | [ ]v.ref                 MISO/12[ ] |                   | |           |PLL Board   |
     | [ ]RST                   MOSI/11[ ]~|------|_1.5k_|-----!-|----Data---|3.3V logic  |
     | [ ]3V3    +---+               10[ ]~|                     |           |            |
     | [ ]5v     | A |                9[ ]~|   LCD               !----LE-----|            |
     | [ ]GND   -| R |-               8[ ] |   LCD               |           |____________|
     | [ ]GND   -| D |-                    |                     |
     | [ ]Vin   -| U |-               7[ ] |   LCD               |
     |          -| I |-               6[ ]~|   LCD               |
Keyb | [ ]A0    -| N |-               5[ ]~|   LCD               |
RSSI | [ ]A1    -| O |-               4[ ] |   LCD               |
     | [ ]A2     +---+           INT1/3[ ]~|------|_1.5k_|---LE--/
     | [ ]A3                     INT0/2[ ] |
     | [ ]A4/SDA  RST SCK MISO     TX>1[ ] |
     | [ ]A5/SCL  [ ] [ ] [ ]      RX<0[ ] |
     |            [ ] [ ] [ ]              |
     |  UNO       GND MOSI 5V  ____________/
      \_______________________/

*/

#include <Wire.h>
#include <LiquidCrystal.h>
#include <SPI.h>

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);         //DFR LCD-keyb Shield

const int slaveSelectPin = 3;  //SPI-SS bzw. enable ADF4350

long Freq = 44220000;  //Startfrequenz generel 100Hz aulösung
long refin = 2500000; // Refrenquarz = 25Mhz
long ChanStep = 625; //Kanalraster = 6,25Khz kleinstes raster
long channel[13]; // Kanal-speicher
long Step[5]; // Kanalraster-speicher
int CHnum = 0; // eingestellter Kanal
int StepNum = 0; // eingestelltes Kanalraster
unsigned long Reg[6]; //ADF4351 Reg's

byte tenHz, hundredHz, ones, tens, hundreds, thousands, tenthousands, hundredthousands, millions; 

int lcd_key     = 0;
int adc_key_in  = 0;
#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5

void setup() {
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.print(" ADF4350  OE6OCG");

  //  Serial.begin(115200);// USB to PC for Debug only
  pinMode (slaveSelectPin, OUTPUT);
  digitalWrite(slaveSelectPin, LOW);
  SPI.setDataMode(SPI_MODE0);
  SPI.setBitOrder(MSBFIRST);
  SPI.setClockDivider(SPI_CLOCK_DIV128);
  SPI.begin();
  delay(500);

  Step[0] = 625; // 6,25 khz, 5khz geht nicht im Int-N Mode(MOD > 4095) bei 25Mhz Ref.
  Step[1] = 1000; // 10 khz
  Step[2] = 1250; // 12.5 khz
  Step[3] = 2500; // 25 khz
  Step[4] = 100000; // 1 Mhz Step

  channel[0] = 3400000; // Band select FIXFREQUENZEN
  channel[1] = 5150000; //51.5 Mhz
  channel[2] = 14455000;
  channel[3] = 14500000;
  channel[4] = 14560000;
  channel[5] = 43000000;
  channel[6] = 43127500;
  channel[7] = 43887500;
  channel[8] = 124500000;
  channel[9] = 129820000;
  channel[10] = 234500000;
  channel[11] = 300000000;
  channel[12] = 440000000; // 4.4Ghz

  lcd.setCursor(0, 0);
  lcd.print("         ");
  lcd.setCursor(0, 0);
  lcd.print("S=");
  lcd.print(ChanStep);
  lcd.print("0");

  lcd.setCursor(12, 1);
  lcd.print(" Mhz");// print Mhz

  SetFreq(Freq);
}

void loop() {
  lcd_key = read_LCD_buttons();
  switch (lcd_key)
  {
    case btnRIGHT:
      { //Scannen +-10Mhz mit eingestellten Kanalraster
        long FreqHi;
        Freq = channel[CHnum];
        if (ChanStep == 100000)
        {
          FreqHi = Freq + 10000000; // +100Mhz
        }
        else
        { FreqHi = Freq + 500000; // +5Mhz};
        };
        while (Freq < FreqHi) {
          Freq += ChanStep;
          SetFreq(Freq);
        }

        if (ChanStep == 100000)
        {
          FreqHi = FreqHi - 10000000; // -100Mhz
        }
        else
        { FreqHi = FreqHi - 500000; // -5Mhz};
        };

        while (FreqHi < Freq) {
          Freq -= ChanStep;
          SetFreq(Freq);
        }
        break;
      }
    case btnLEFT:
      {
        StepNum += 1;
        if (StepNum > 4) StepNum = 0;
        ChanStep = Step[StepNum];
        lcd.setCursor(2, 0);
        lcd.print("       ");
        lcd.setCursor(2, 0);
        lcd.print(ChanStep);
        lcd.print("0");
        // render Frequenz nach Raster
        SetFreq(Freq);
        delay(200);
        break;
      }
    case btnUP:
      {
        Freq += ChanStep;
        SetFreq(Freq);
        break;
      }
    case btnDOWN:
      {
        Freq -= ChanStep;
        SetFreq(Freq);
        break;
      }
    case btnSELECT:
      {
        //lcd.print("SELECT");
        CHnum += 1;
        if (CHnum > 12) CHnum = 0;
        Freq = channel[CHnum];
        SetFreq(Freq);
        delay(200);
        break;
      }
    case btnNONE:
      {
        //lcd.print("NONE  ");
        break;
      }
  }
  adc_key_in = analogRead(0); // verhindert scannen wenn taste zulange gedrückt wird
  delay(20);
  //Serial.print ("ADC=");    Serial.print (adc_key_in);    Serial.print ("\r\n");
  while (adc_key_in < 900) {
    adc_key_in = analogRead(0); 
    delay(20);
  }
}

int read_LCD_buttons()
{
  adc_key_in = analogRead(0);
  if (adc_key_in > 1000) return btnNONE;
  if (adc_key_in < 50)   return btnRIGHT;
  if (adc_key_in < 195)  return btnUP;
  if (adc_key_in < 380)  return btnDOWN;
  if (adc_key_in < 555)  return btnLEFT;
  if (adc_key_in < 790)  return btnSELECT;
  return btnNONE;
}

void SetFreq(long Freq)
{
  //  lcd.setCursor(0, 1);           // Cursor auf 2.Zeile
  //  lcd.print(Freq);
  //  lcd.print(" Mhz  ");

  showFreq(Freq);

  ConvertFreq(Freq, Reg);
  WriteADF2(5);
  delayMicroseconds(2500);
  WriteADF2(4);
  delayMicroseconds(2500);
  WriteADF2(3);
  delayMicroseconds(2500);
  WriteADF2(2);
  delayMicroseconds(2500);
  WriteADF2(1);
  delayMicroseconds(2500);
  WriteADF2(0);
  delayMicroseconds(2500);
}
void WriteADF2(int idx)
{ // make 4 byte from integer for SPI-Transfer
  byte buf[4];
  for (int i = 0; i < 4; i++)
    buf[i] = (byte)(Reg[idx] >> (i * 8));
  WriteADF(buf[3], buf[2], buf[1], buf[0]);
}
int WriteADF(byte a1, byte a2, byte a3, byte a4) {
  // write over SPI to ADF4350
  digitalWrite(slaveSelectPin, LOW);
  delayMicroseconds(10);
  SPI.transfer(a1);
  SPI.transfer(a2);
  SPI.transfer(a3);
  SPI.transfer(a4);
  Toggle();
}
int Toggle() {
  digitalWrite(slaveSelectPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(slaveSelectPin, LOW);
}

void ConvertFreq(long freq, unsigned long R[])
{
  // PLL-Reg-R0         =  32bit
  // Registerselect        3bit
  // int F_Frac = 4;       // 12bit
  // int N_Int = 92;       // 16bit
  // reserved           // 1bit

  // PLL-Reg-R1         =  32bit
  // Registerselect        3bit
  //int M_Mod = 5;        // 12bit
  int P_Phase = 1;     // 12bit bei 2x12bit hintereinander pow()-bug !!
  int Prescal = 0;     // 1bit geht nicht ???
  int PhaseAdj = 0;    // 1bit geht auch nicht ???
  // reserved           // 3bit

  // PLL-Reg-R2         =  32bit
  // Registerselect        3bit
  int U1_CountRes = 0; // 1bit
  int U2_Cp3state = 0; // 1bit
  int U3_PwrDown = 0;  // 1bit
  int U4_PDpola = 1;    // 1bit
  int U5_LPD = 0;       // 1bit
  int U6_LPF = 1;       // 1bit 1=Integer, 0=Frac not spported yet
  int CP_ChgPump = 7;     // 4bit
  int D1_DoublBuf = 0; // 1bit
  //  int R_Counter = 1;   // 10bit
  //  int RD1_Rdiv2 = 0;    // 1bit
  //  int RD2refdoubl = 0; // 1bit
  int M_Muxout = 0;     // 3bit
  int LoNoisSpur = 0;      // 2bit
  // reserved           // 1bit

  // PLL-Reg-R3         =  32bit
  // Registerselect        3bit
  int D_Clk_div = 150; // 12bit
  int C_Clk_mode = 0;   // 2bit
  //  reserved          // 1bit
  int F1_Csr = 0;       // 1bit
  //  reserved          // 2bit
  int F2_ChgChan = 0;   // 1bit
  int F3_ADB = 0;       // 1bit
  int F4_BandSel = 0;  // 1bit
  //  reserved          // 8bit

  // PLL-Reg-R4         =  32bit
  // Registerselect        3bit
  int D_out_PWR = 0 ;    // 2bit
  int D_RF_ena = 1;     // 1bit
  int D_auxOutPwr = 0;  // 2bit
  int D_auxOutEna = 0;  // 1bit
  int D_auxOutSel = 0;  // 1bit
  int D_MTLD = 0;       // 1bit
  int D_VcoPwrDown = 0; // 1bit 1=VCO off

  //  int B_BandSelClk = 200; // 8bit

  int D_RfDivSel = 3;    // 3bit 3=70cm 4=2m
  int D_FeedBck = 1;     // 1bit
  // reserved           // 8bit

  // PLL-Reg-R5         =  32bit
  // Registerselect     // 3bit
  // reserved           // 16bit
  // reserved     11    // 2bit
  // reserved           // 1bit
  int D_LdPinMod = 1;    // 2bit muss 1 sein
  // reserved           // 8bit

  // Referenz Freg Calc
  //  long refin = 250000; // Refrenquarz = 25000000hz
  int R_Counter = 1;   // 10bit
  int RD1_Rdiv2 = 0;    // 1bit
  int RD2refdoubl = 0; // 1bit
  int B_BandSelClk = 200; // 8bit
  //  int F4_BandSel = 0;  // 1bit

  // int F4_BandSel = 10.0 * B_BandSelClk / PFDFreq;

  long RFout = Freq;   // VCO-Frequenz
  // calc bandselect und RF-div
  int outdiv = 1;

  if (RFout >= 220000000) {
    outdiv = 1;
    D_RfDivSel = 0;
  }
  if (RFout < 220000000) {
    outdiv = 2;
    D_RfDivSel = 1;
  }
  if (RFout < 110000000) {
    outdiv = 4;
    D_RfDivSel = 2;
  }
  if (RFout < 55000000) {
    outdiv = 8;
    D_RfDivSel = 3;
  }
  if (RFout < 27500000) {
    outdiv = 16;
    D_RfDivSel = 4;
  }
  if (RFout < 13800000) {
    outdiv = 32;
    D_RfDivSel = 5;
  }
  if (RFout < 6900000) {
    outdiv = 64;
    D_RfDivSel = 6;
  }

  float PFDFreq = refin * ((1.0 + RD2refdoubl) / (R_Counter * (1.0 + RD1_Rdiv2))); //Referenzfrequenz
  float N = ((RFout) * outdiv) / PFDFreq;
  int N_Int = N;
  long M_Mod = PFDFreq * (100000 / ChanStep) / 100000;
  int F_Frac = round((N - N_Int) * M_Mod);

  R[0] = (unsigned long)(0 + F_Frac * pow(2, 3) + N_Int * pow(2, 15));
  R[1] = (unsigned long)(1 + M_Mod * pow(2, 3) + P_Phase * pow(2, 15) + Prescal * pow(2, 27) + PhaseAdj * pow(2, 28));
  //  R[1] = (R[1])+1; // Registerselect adjust ?? because unpossible 2x12bit in pow() funktion
  R[2] = (unsigned long)(2 + U1_CountRes * pow(2, 3) + U2_Cp3state * pow(2, 4) + U3_PwrDown * pow(2, 5) + U4_PDpola * pow(2, 6) + U5_LPD * pow(2, 7) + U6_LPF * pow(2, 8) + CP_ChgPump * pow(2, 9) + D1_DoublBuf * pow(2, 13) + R_Counter * pow(2, 14) + RD1_Rdiv2 * pow(2, 24) + RD2refdoubl * pow(2, 25) + M_Muxout * pow(2, 26) + LoNoisSpur * pow(2, 29));
  R[3] = (unsigned long)(3 + D_Clk_div * pow(2, 3) + C_Clk_mode * pow(2, 15) + 0 * pow(2, 17) + F1_Csr * pow(2, 18) + 0 * pow(2, 19) + F2_ChgChan * pow(2, 21) +  F3_ADB * pow(2, 22) + F4_BandSel * pow(2, 23) + 0 * pow(2, 24));
  R[4] = (unsigned long)(4 + D_out_PWR * pow(2, 3) + D_RF_ena * pow(2, 5) + D_auxOutPwr * pow(2, 6) + D_auxOutEna * pow(2, 8) + D_auxOutSel * pow(2, 9) + D_MTLD * pow(2, 10) + D_VcoPwrDown * pow(2, 11) + B_BandSelClk * pow(2, 12) + D_RfDivSel * pow(2, 20) + D_FeedBck * pow(2, 23));
  R[5] = (unsigned long)(5 + 0 * pow(2, 3) + 3 * pow(2, 19) + 0 * pow(2, 21) + D_LdPinMod * pow(2, 22));
}
//to do instead of writing 0x08000000 you can use other two possibilities: (1ul << 27) or (uint32_t) (1 << 27).

void showFreq(long FREQ) {
  millions = int(FREQ / 100000000);
  hundredthousands = ((FREQ / 10000000) % 10);
  tenthousands = ((FREQ / 1000000) % 10);
  thousands = ((FREQ / 100000) % 10);
  hundreds = ((FREQ / 10000) % 10);
  tens = ((FREQ / 1000) % 10);
  ones = ((FREQ / 100) % 10);
  hundredHz = ((FREQ / 10) % 10);
  tenHz = ((FREQ) % 10);
  lcd.setCursor(0, 1);
  lcd.print("            ");
  if (millions > 0) {
    lcd.setCursor(0, 1);
    lcd.print(millions);
    lcd.print(".");
  }
  else {
    lcd.setCursor(2, 1);
  }
  lcd.print(hundredthousands);
  lcd.print(tenthousands);
  lcd.print(thousands);
  lcd.print(",");
  lcd.print(hundreds);
  lcd.print(tens);
  lcd.print(ones);
  lcd.print(".");
  lcd.print(hundredHz);
  lcd.print(tenHz);
};

// as PLL-Register Referenz
// R[0] = (0x002E0020); // 145.0 Mhz, 12.5khz raster
// R[1] = (0x08008029);
// R[2] = (0x00004E42);
// R[3] = (0x000004B3);
// R[4] = (0x00BC8024);
// R[5] = (0x00580005);

 

About the Author

kd0cq
Chris - KD0CQ (formerly KB0TLW) has held an amateur (ham) radio license since 1994 and is stubbornly grasping onto his Advanced class operator status. His professional experience includes Network and Systems Administrator, Network Security, and Electronics Engineer. Sometimes even a little bit of web development. mail: chris at kd0cq dot com

5 Comments on "Control the ADF4351 with an Arduino – Source code"

  1. Hi
    I have done some testing with the arduino soft of OE60CG which you also used and modified and I came on a problem of transfer of bits, the clock sends well the 32bits but the DATA send only 24bits, to test you can Send R [5] = (unsigned long) (5 + 268435455 * pow (2, 3)); On the R5 this corresponds to 28bits + the 3 bits of register selection, I have not yet found the problem but I think you also have this worry, everything works if you do not use the last 8 bits
    73 f4fdw

  2. thanks, ported to PIC and improved 🙂

  3. Hello! Your project on ADF 4351 was very interested. It would be great if arduino supported the “sweep” function. Could you organize this function? In order for the frequency limits, width, delay time to be selected on the screen … I would like to change the frequency with the minimum delay and maximum speed. Is it possible to do this?

  4. Robin Szemeti | August 1, 2020 at 7:13 pm | Reply

    So, I found some issue with the code … I found that some frequencies from the ADF4351 worked just fine (2GHz for example) but other, such as 2.175GHz did not, even though it is an integer relationship to 25MHz reference … in the end, I pasted in the register values direct from the AD software, and it worked perfectly, but using the ConvertFreq() routine in the software produced some strange results.

    What is also strange/undocumented is why the reference frequencies in the code above are the actual frequencies/10 eg “2500000” for 25MHz, not “25000000” ..

  5. CODE IS WRONG – IT CONTINUES CHANGES FREQ OVER 4400 MHZ.

Leave a comment

Your email address will not be published.


*


This site uses Akismet to reduce spam. Learn how your comment data is processed.