L’ADF4351 est un circuit intégré de type PLL, pilotable par un bus SPI pouvant générer un signal RF de 35MHz à 4.4GHz.

Les 4 premières parties sont de la documentation sur l’ADF4351 (permet d’aller plus vite que de lire la datasheet quand il s’agit de produire un programme de zéro). La partie ‘Métrologie’ est une ébauche pour caractériser l’ADF4351. La dernière partie est une annexe contenant le code pour pouvoir contrôler l’ADF en fréquence et puissance.

Branchement de la plaquette avec l’arduino

ArduinoADF4351commentaire
D8MUXOUTpas besoin d’étage d’adaptation
D9LEadapation 5->3.3V requis (à l’état bas ce signal indique le début du communication, c’est en gros une pin qui active le registre à décalage de l’ADF)
D11DATAadapatation (donné qui sera écrite dans le registre à décalage pendant que CLK sera sur un front montant)
D13CLKadaptation
VCCla plaquette s’alimente en 5V (même si la tension d’entrée de l’ADF est de 3.3V)

Communication avec l’ADF

Un registre à décalage est remplie pendant 32 coups d’horloge (correspondant aux 32 bits des registres). Une fois ce registre remplie, son contenu est envoyé dans l’un des 6 registres de l’ADF (les 3 bits de poid faible indique le bon registre). L’ensemble des 6 registres peux donc être programmé en coups d’horloge.

Description grossière des registres

(voir la fin de cette section pour la sémantique des valeurs)

Registre 0

  • FRAC du bit 3 au bit 14
  • INT du bit 15 au bit 30

Registre 1

  • MOD du bit 3 au bit 14
  • Prescaler bit 27

Registre 2

  • MUXOUT du bit 26 au bit 28

Registre 4

  • RF Divider Select du bit 20 à 22
  • Output Power bit 3 et 4
  • RF Output Enable bit 5

Sémantique

  • RF Output Enable active la sortie RF si ce bit est à 1 et la désactive sinon.
  • Output Power règle la puissance du signal de sortie selon:
    • | 00 -> -4 dbm
    • | 01 -> -1 dbm
    • | 10 -> +2 dbm
    • | 11 -> +5 dbm

La fréquence de sortie de l’ADF va être donné par le calcul suivant:

Structure typique d’un programme

La valeur de chaque registre est stocké dans un tableau de taille 6:

uint32_t registers[6] = {0x8F8008, 0x80080A1, 0x18005E42, 0x8004B3, 0x814004, 0x580005};

Il s’agit ici des valeurs initiales à mettre dans l’ADF. Il est également nécessaire de renseigner la fréquence du quartz au début du programme double PFDRFout = 10;. Voici le contenu typique du setup d’un programme utilisant l’ADF :

#include <SPI.h>
 
// etat initial des registres
uint32_t registers[6] = {0x8F8008, 0x80080A1, 0x18005E42, 0x8004B3, 0x814004, 0x580005};
// Fréquence du quartz de la plaquette
double PFDRFout = 10;
 
void setup() {
	SPI.begin()
 
	// Block initialement le transfert de donnee
	pinMode(ADF4351_LE, OUTPUT);
	digitalWrite(ADF4351_LE, HIGH);
 
	// Programmation de l'etat initial dans l'ADF
	SetADF4351();
}

Le squelette d’une fonction qui communique avec l’ADF est le suivant:

void some_function(int some_parameter) {
	// manipulate registers table
 
	SetADF4351();
}

SetADF4351 est une fonction qui programme les registres de l’ADF conformément aux valeurs contenues dans registers (voir code en annexe).

Métrologie

Lien vers une association de radio-amateur utilisant l’ADF4351 avec l’Arduino : ADF4351 et Arduino. On peut voire différentes mesures effectués à l’analyseur de spectre.

Annexe

Le code suivant permet de générer n’importe quelle fréquence dans la plage de fréquence de l’ADF avec la fonction SetFreq(unfloat), d’activer la sortie RF avec la fonction PowerEnable(0 ou 1) et de régler la puissance avec SetPower(1 2 3 ou 4) (pour -4, -1, +2, +5dbm)

#include <SPI.h>
 
 
// ADF4351 setting ////////////////////////////////////////////////////////////////
//  Arduino       PIN D8     ADF4351 MUXOUT
//  Arduino       PIN D9     ADF4351 LE
//  Arduino MOSI  PIN D11    ADF4351 DATA
//  Arduino SCK   PIN D13    ADF4351 CLK
#define ADF4351_LE 9 // LE pin of ADF4351
//  #define LOCK 8      // Input to detect lock-in from AD4351 (optional), a check of the lock can be performed after setting the ADF4351
uint32_t registers[6] = {0x8F8008, 0x80080A1, 0x18005E42, 0x8004B3, 0x814004, 0x580005}; // Default values to initialize the ADF4351 registers (from Analog Devices software)
double PFDRFout = 10;                       // Default, reference frequency of the ADF4351 module on-board oscillator (10 MHz)
 
 
void setup() {
  // put your setup code here, to run once:
 
  SPI.begin();
 
    // Initialize ADF4351
  // pinMode(LOCK, INPUT);               // Not used because AD4351 has 3.3V output logic
  pinMode(ADF4351_LE, OUTPUT);
  digitalWrite(ADF4351_LE, HIGH); // Set LE high to block data trasfer
  SetADF4351();
  SetPower(4);      // Set ouput power, possible values are 1,2,3,4
  PowerEnable(1);   // Set power on (1), or off (0)
  SetFreq(2800.0); // Set up a starting frequency for test
 
}
 
void loop() {
  // put your main code here, to run repeatedly:
 
  // Do whatever you want with SetFreq, SetPower and PowerEnable
}
 
 
 
// Send the 32 bits of the register to the AD4351
void WriteRegister32(const uint32_t value) // Register 32 bit
{
  SPI.setDataMode(SPI_MODE0); // See the AD4351 datasheet for SPI communication
  SPI.setBitOrder(MSBFIRST);
  digitalWrite(ADF4351_LE, LOW); // Latch low starts the data transfer
  // delayMicroseconds(1);
  for (int i = 3; i >= 0; i--)
  {                                        // Loop to send 32 bits as 8 bits four times
    SPI.transfer((value >> 8 * i) & 0xFF); // Set offset, byte masking and send via SPI
  }
  digitalWrite(ADF4351_LE, HIGH); // Latch high to complete the data transfer
  // delayMicroseconds(1);
}
 
 
// Program all the six registers of the AD4351
void SetADF4351()
{
  for (int i = 5; i >= 0; i--)
  { // Registers writing starts with the last register
    WriteRegister32(registers[i]);
  }
  delay(10);
}
 
 
// Enable or disable power of the ADF4351
void PowerEnable(byte val)
{
  if (val == 0)
  { // MW Disabled
    bitWrite(registers[4], 5, 0);
  }
  if (val == 1)
  { // MW Enabled
    bitWrite(registers[4], 5, 1);
  }
  SetADF4351();
}
 
 
 
// Set power of the ADF4351
void SetPower(byte val)
{
  if (val == 1)
  { // DB3, DB4 00 -4dbm
    bitWrite(registers[4], 4, 0);
    bitWrite(registers[4], 3, 0);
  }
  else if (val == 2)
  { // DB3, DB4 01 -1dbm
    bitWrite(registers[4], 4, 0);
    bitWrite(registers[4], 3, 1);
  }
  else if (val == 3)
  { // DB3, DB4 10 +2dbm
    bitWrite(registers[4], 4, 1);
    bitWrite(registers[4], 3, 0);
  }
  else if (val == 4)
  { // DB3, DB4 11 +5dbm
    bitWrite(registers[4], 4, 1);
    bitWrite(registers[4], 3, 1);
  }
  SetADF4351();
}
 
 
// Set the output frequency of the ADF4351
void SetFreq(double val)
{
  double RFout = val;
  double FRACF;                        
 
  byte OutputDivider;                         // Parameters to set the range of frequency
  if (RFout > 4400 && RFout < 35)
  {
  } // Frequency not available  // Maximum and Minimum frequency of the ADF
  else
  {
    if (RFout >= 2200)
    { // 2200-4400 MHz is the recommended range with a fundamental frequency
      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);
    }
 
    unsigned int long INTA, MOD, FRAC; // Parameters to set the frequency, use the unsigned int long 32 bit to save easily on the register by using the shifts command >> <<
 
    // Rfout = fpdf * (INTA +FRAC/MOD) Equation to use for the calculation
    INTA = (RFout * OutputDivider) / PFDRFout;
    MOD = (PFDRFout / 0.05);
    FRACF = (((RFout * OutputDivider) / PFDRFout) - INTA) * MOD;
    FRAC = round(FRACF); // Results is rounded in case of divisions with remainder
 
    // Register 0 is only to save FRAC and INT values
    registers[0] = 0;                   // Set all the bit zero and the control bits 000 to indicate register 0
    registers[0] = INTA << 15;          // Save the INTA in the register by using the shift
    FRAC = FRAC << 3;                   // Shift the FRAC to save
    registers[0] = registers[0] + FRAC; // Save the FRAC in the register
 
    // Register 1 is to save the MOD values, prescaler and phase value
    registers[1] = 0;                // Reset the register for calculation, phase adjust DB28 remains set to zero
    registers[1] = MOD << 3;         // Store the MOD values
    registers[1] = registers[1] + 1; // Add +1 to make the control bits 001 and indicate the register 1
    bitWrite(registers[1], 27, 1);   // Set prescaler of 8/9 on DB27
 
    // Set mux-out on digital-lock detect mode [110] for B28, B27, B26 bits (no need if it is already in default)
    bitWrite(registers[2], 28, 1);
    bitWrite(registers[2], 27, 1);
    bitWrite(registers[2], 26, 0);
 
    SetADF4351();
  }
  RFint = RFout;
}

Github avec un code pour contrôler un ADF4351: https://github.com/dfannin/adf4351