ADF4351 (35MHz to 4.4GHz)

Synthesiser Interface

There was a discussion on the Positron8 BASIC compiler's forum about an interesting frequency synthesiser board that could span from 35MHz to 4.4GHz, and contains an ADF4351 chip. A few of the users on the forum asked if there was a set of routines for the compiler that performed the interface. This interested me, but I couldn't afford to buy one of the boards to create an interface. Then one of our good users sent me a board, so I could try my hand at it. So this is the result, and many thanks to Charlie for the board. It means a lot when one compiler user helps another.

The board looks like the image below, and is available from many places on the internet:

Doing a search on the internet, I came across a lot of sites that go way over the top about the interface mechanism to the ADF4351 chip, and talk far too much about things that simply don't matter. :-) The board uses a simple SPI interface and contains six 32-bit registers. These allow a whole host of changes to the frequency, but the vast majority of users simply want a frequency output, so I created a library to do just that!

The demo program to use the library is listed below:

'

' Interface with an ADF4351 based 35MHz to 4.4GHz PLL synthesiser module.

' Written by Les Johnson for the Positron8 BASIC compiler

'

Include "Amicus18.inc" ' We'll use the Amicus18 development board, which is a PIC18F25K20 running at 64MHz

'

' Setup the pins to use for the interface (These must be placed before the ADF4351 library is included in the program)

'

$define ADF_DAT_Pin PORTB.0 ' Connect to the ADF4351 DAT pin

$define ADF_CLK_Pin PORTB.1 ' Connect to the ADF4351 CLK pin

$define ADF_LD_Pin PORTB.2 ' Connect from the ADF4351 Lock Detect (LD) pin

$define ADF_LE_Pin PORTB.3 ' Connect to the ADF4351 LE pin


Include "ADF4351.inc" ' Load the ADF4351 interface routines into the program


Dim Frequency As Float ' Holds the frequency for the demo code


'---------------------------------------------------------------------------

' Sweep the frequency from 88.0 MHz to 108.0 MHz in 10KHz steps

'

Main:

Do ' Create an infinite loop

For Frequency = 88.0 To 108.0 Step 0.01 ' Create a loop for the frequency scan

Do ' Create a loop

ADF_SetFreq(Frequency) ' Alter the frequency of the ADF4351 board

If ADT_tTimedOut = True Then ' Did the PLL lock time out, while waiting?

PLL_TimeOut() ' Yes. So send a message to the serial terminal

EndIf

Loop Until ADF_tLock = True ' Do it again if the PLL frequency is not locked

DelayMS 10 ' A small delay between frequency changes

Next ' Close the frequency scan loop

Loop ' Do it forever


'---------------------------------------------------------------------------

' Come here if the PLL lock timed out

' Transmit a message to a serial terminal

'

Proc PLL_TimeOut()

HRSOutLn "PLL Lock Timed out"

EndProc

The demo program sweeps from frequency 88MHz to 108MHz. This is because I do not own a frequency meter, so I used a simple VHF FM radio tuned to a particular frequency, then set the ADF4351 to the same frequency, and heard the hiss drop from the radio, meaning the frequency was spot on. Simple, but effective. The main subroutine to use in the library is ADF_SetFreq. This alters the frequency output of the ADF4351 chip. It requires a floating point value for the MHz to transmit. It's as simple as that! The pin $defines tell the library which pins to use for the ADF4351 SPI interface.

Note. On the board used, the CE pin also had to be connected to the 3.3 Volt line, as it was floating. The CE line is the Chip Enable pin for the ADF4351 device, where a low (Gnd) disables the chip, and a high (3.3 Volts) enables the chip.

The image below is the ADF4351 board attached to an Amicus18 development board, which carries a PIC18F25K20 chip, operating at 3.3 Volts at a frequency of 64MHz, so it's perfect for the ADF4351 board, which also requires no more than 3.3 Volts. Unfortunately, I could not find a circuit diagram for the board anywhere. There are many similar boards available that have a circuit diagram, but not the one above. However, they will all follow the same circuitry, as this is most probably a reference circuit clone.

The green, illuminated, LED indicates that the frequency required is locked and is transmitting. The red LED is simply a power indicator.

The interface library code listing is below, but can also be downloaded from here: ADF4351 Library and Demo.


$ifndef _ADF4351_INC_

$define _ADF4351_INC_

'

' Interface with an ADF4351 based 35MHz to 4.4GHz PLL synthesiser module.

' Written by Les Johnson for the Positron8 and Positron16 BASIC compilers

'

' My special thanks to Charlie for sending me the synthesiser board.

'

' Setup the default pins to use for the interface

'

$ifndef ADF_DAT_Pin ' Has the DAT pin been setup in the main program?

$define ADF_DAT_Pin PORTB.0 ' No. So connect to the ADF4351 Dat pin

$endif

$ifndef ADF_CLK_Pin ' Has the CLK pin been setup in the main program?

$define ADF_CLK_Pin PORTB.1 ' No. So connect to the ADF4351 Clock (CLK) pin

$endif

$ifndef ADF_LD_Pin ' Has the LD pin been setup in the main program?

$define ADF_LD_Pin PORTB.2 ' No. So connect from the ADF4351 Lock Detect (LD) pin

$endif

$ifndef ADF_LE_Pin ' Has the LE pin been setup in the main program?

$define ADF_LE_Pin PORTB.3 ' No. So connect to the ADF4351 LE pin

$endif


$ifndef True

$define True 1

$endif

$ifndef False

$define False 0

$endif

'

' Create variables

'

Dim ADF_bFlags As Byte Access ' Holds flag bits

Dim ADF_tLock As ADF_bFlags.0 ' True when the ADF4351 PLL locks

Dim ADT_tTimedOut As ADF_bFlags.1 ' True if the lock times out


Dim ADF_bBitCount As Byte Access ' Bit counter for the SPI interface

Dim ADF_bRegIndex As Byte ' ADF4351 Register Counter

Dim ADF_bRFODivider As Byte ' RF output divider value (1 - 64)

Dim ADF_dIntDiv As Dword ' Integer division factor

Dim ADF_dFracDiv As Dword ' Numerator of the fractional division


Dim ADF_dRegs[6] As Dword ' Array for ADF4351 control registers

$if _core = 24 Or _core = 33 ' Is the device a 16-bit core type?

Dim ADF_dReg0 As ADF_dRegs_0 ' Register 0

Dim ADF_dReg1 As ADF_dRegs_1 ' Register 1

Dim ADF_dReg2 As ADF_dRegs_2 ' Register 2

Dim ADF_dReg3 As ADF_dRegs_3 ' Register 3

Dim ADF_dReg4 As ADF_dRegs_4 ' Register 4

Dim ADF_dReg5 As ADF_dRegs_5 ' Register 5

$else ' Otherwise... The device is an 8-bit type

Dim ADF_dReg0 As ADF_dRegs#0 ' Register 0

Dim ADF_dReg1 As ADF_dRegs#1 ' Register 1

Dim ADF_dReg2 As ADF_dRegs#2 ' Register 2

Dim ADF_dReg3 As ADF_dRegs#3 ' Register 3

Dim ADF_dReg4 As ADF_dRegs#4 ' Register 4

Dim ADF_dReg5 As ADF_dRegs#5 ' Register 5

$endif


Symbol cADF_RefFrequency = 25.0

Symbol cADF_OutRes = 0.01


Symbol cADF_FracMod = (cADF_RefFrequency / cADF_OutRes) ' Fractional modulus (2 to 4095)

Symbol cADF_FracModSh3 = (cADF_FracMod << 3)


$define ADF_cSPIDelay() DelayCS 10 ' The very small delay used in the SPI interface


'-----------------------------------------------------------------

' Enable the ADF4351 device (if applicable)

' Input : None

' Output : None

' Notes : Is only applicable if the CE pin is defined in the main program

'

$define ADF_Enable() '

$ifdef ADF_CE_Pin '

PinSet ADF_CE_Pin '

$endif


'-----------------------------------------------------------------

' Disable the ADF4351 device (if applicable)

' Input : None

' Output : None

' Notes : Is only applicable if the CE pin is defined in the main program

'

$define ADF_Disable() '

$ifdef ADF_CE_Pin '

PinClear ADF_CE_Pin '

$endif


'-----------------------------------------------------------------

' Wait until the ADF4351 PLL is in locked state

' Input : pTimeout holds the timeout value (in approx milliseconds)

' Output : ADF_tLock true if the chip is locked

' : ADT_tTimedOut true if a lock has not happened and the loop has timed out

' Notes : None

'

Proc ADF_WaitForPLL_Lock(pTimeout As Word)

ADF_tLock = False ' Default to no lock

ADT_tTimedOut = False ' Default to no timeout

Repeat ' Create a loop to wait for the PLL lock

If pTimeout = 0 Then ' Has the timeout counter reached 0?

ADT_tTimedOut = True ' Yes. So signal a timeout

Return ' And exit the subroutine

EndIf

DelayMS 1 ' Delay for 1 ms

Dec pTimeout ' Decrement the timeout counter

Until ADF_LD_Pin = 1 ' Wait until the LD pin is high

ADF_tLock = True ' If the program has gotten here, a lock has occured

EndProc


'------------------------------------------------------------------

' SPI mode 00 interface to the ADF4351

' Input : pData

' Output : None

' Notes : None

'

Proc ADF_WriteSPI(pData As Dword)

For ADF_bBitCount = 31 To 0 Step -1 ' Create a loop for the 32-bits of data to send

PinClear ADF_DAT_Pin ' Default to a clear bit

If pData.31 = 1 Then ' Is the bit of the value to send a 1?

PinSet ADF_DAT_Pin ' Yes. So place the outgoing bit onto the ADF_DAT_Pin

EndIf

ADF_cSPIDelay() ' A small delay

pData = pData << 1 ' Shift the next bit into MSB

PinSet ADF_CLK_Pin ' Set the SCK pin high

ADF_cSPIDelay() ' A small delay

PinClear ADF_CLK_Pin ' Pull the SCK pin low

Next

EndProc


'-----------------------------------------------------------------

' Send the values to the six ADF4351 registers

' Input : Array ADF_dRegs holds the 6 register values

' Output : ADF_tLock True if the chip is locked

' : ADT_tTimedOut true if a lock has not happened and the loop has timed out

' Notes : None

'

Proc ADF_WriteRegData()

For ADF_bRegIndex = 5 To 0 Step -1 ' Create a loop for all 6 registers

PinClear ADF_LE_Pin ' Pull the LE pin low to start a transaction

ADF_WriteSPI(ADF_dRegs[ADF_bRegIndex]) ' Send the 32-bit register data

PinSet ADF_LE_Pin ' Set the LE pin high to load it into a register

DelayUS 100 ' Delay for 100us to make sure data is latched

PinClear ADF_LE_Pin ' Pull the LE pin low

Next

ADF_WaitForPLL_Lock(10000) ' Wait until the ADF4351 locks

EndProc


'-----------------------------------------------------------------

' Calculate ADF4351 register settings for a new frequency

' Input : pFreq holds the frequency (in MHz)

' Output : ADF_dReg0 to ADF_dReg5 hold the register values

' : ADT_tTimedOut true if a lock has not happened and the loop has timed out

' Notes : None

'

Proc ADF_SetFreq(pFreq As Float)

'

' Setup ADF_dReg4 (register 4)

'

ADF_dReg4 = $0000803C ' Setup the default value for register 4

Select pFreq ' What frequency range is required?

Case 2200.0 To 4400.0 ' Is it 2200MHz to 4400MHz?

ADF_bRFODivider = 1 ' Yes. So set the divider value to 1

ADF_dReg4.Byte2 = $8C ' Alter byte 2 of register 4

Case 1100.0 To 2200.0 ' Is it 1100MHz to 2200MHz?

ADF_bRFODivider = 2 ' Yes. So set the divider value to 2

ADF_dReg4.Byte2 = $9C ' Alter byte 2 of register 4

Case 550.0 To 1100.0 ' Is it 550MHz to 110MHz?

ADF_bRFODivider = 4 ' Yes. So set the divider value to 4

ADF_dReg4.Byte2 = $AC ' Alter byte 2 of register 4

Case 275.0 To 550.0 ' Is it 275MHz to 550MHz?

ADF_bRFODivider = 8 ' Yes. So set the divider value to 8

ADF_dReg4.Byte2 = $BC ' Alter byte 2 of register 4

Case 137.5 To 275.0 ' Is it 137.5MHz to 275MHz?

ADF_bRFODivider = 16 ' Yes. So set the divider value to 16

ADF_dReg4.Byte2 = $CC ' Alter byte 2 of register 4

Case 68.75 To 137.5 ' Is it 68.75MHz to 137.5MHz?

ADF_bRFODivider = 32 ' Yes. So set the divider value to 32

ADF_dReg4.Byte2 = $DC ' Alter byte 2 of register 4

Case 35.0 To 68.75 ' Is it 35MHz to 68.75MHz?

ADF_bRFODivider = 64 ' Yes. So set the divider value to 64

ADF_dReg4.Byte2 = $EC ' Alter byte 2 of register 4

Case Else ' Otherwise. The frequency is not recognised. So...

Return ' Exit the routine without making changes

EndSelect

'

' Calculate the values for ADF_dIntDiv and ADF_dFracDiv

'

ADF_dIntDiv = (pFreq * ADF_bRFODivider) / cADF_RefFrequency

ADF_dFracDiv = (((pFreq * ADF_bRFODivider) / cADF_RefFrequency) - ADF_dIntDiv) * cADF_FracMod


HRSOutLn "cADF_FracMod = ", Dec cADF_FracMod

HRSOutLn "pFreq = ", Dec1 pFreq

HRSOutLn "ADF_dIntDiv = ", Dec ADF_dIntDiv

HRSOutLn "ADF_dFracDiv = ", Dec ADF_dFracDiv

'

' Setup ADF_dReg0 (register 0)

'

ADF_dReg0 = ADF_dIntDiv << 15 ' Move ADF_dIntDiv into bits 15 - 30

ADF_dReg0 = ADF_dReg0 + (ADF_dFracDiv << 3) ' Insert ADF_dFracDiv to bits 3 - 14

'

' Setup ADF_dReg1 (register 1)

'

ADF_dReg1 = $08008000 ' Set the default value

ADF_dReg1 = ADF_dReg1 + cADF_FracModSh3 ' Move cADF_FracMod into bits 3 - 14

Inc ADF_dReg1 ' Add 1 for reg addr

'

' Setup ADF_dReg2, ADF_dReg3 and ADF_dReg5 (registers 2, 3, and 5)

'

ADF_dReg2 = $00004E42 ' MUX output disabled

ADF_dReg3 = $000004B3 ' PD polarity positive, clk divider off

ADF_dReg5 = $00580005 ' LD pin set for dig ADF_tLock detect


ADF_WriteRegData() ' Write the new register values to the ADF4351 chip

EndProc


'-----------------------------------------------------------------

' Setup the program and the ADF4351 SPI interface

' Input : None

' Output : None

' Notes : None

'

Proc ADF_Setup()

$ifdef ADF_CE_Pin ' Has the CE pin been setup in the main program?

High ADF_CE_Pin ' Yes. So set the CE pin to high

$endif

DelayMS 500 ' Wait for the ADF4351 board to settle after power up


Low ADF_DAT_Pin ' Pull the DAT pin to low

Low ADF_CLK_Pin ' Pull the CLK pin to low

High ADF_LE_Pin ' Set the LE pin to high

Input ADF_LD_Pin ' Make the LD pin an input


ADT_tTimedOut = False ' Locked time out flag (true if timed out)

ADF_tLock = False ' ADF4351 PLL Lock status (true if locked)

Clear ADF_dRegs ' Clear the registers array

EndProc


'---------------------------------------------------------------------------

' The Starting part of the library

'

ADF_Main:

ADF_Setup() ' Setup the program and the SPI interface


$endif ' _ADF4351_INC_



For the users that want to add to the library, or want to see more details concerning the registers within the ADF4251 chip, here is the datasheet for it: ADF4351_Datasheet