Driving a PLVCXO with an Arduino

 

Par Alain Fort, F1CJN, le 20 mars 2013       alain.fort.f1cjn@sfr.fr     

 

 

This program is used to program the PLVCXO decribed by DF4IC in order to be able to experiment any frequency. It has been tested with the CJ 2010 description by F6DRO.

The Arduino send to the PLVCXO the ADF4110 register values.

The values of the P, N, A and R registers are programmed by the SPI bus, using the serial interface at 9600 bauds of the Arduino to input the values.

Exemple : N1000 program the register N with 1000.

The memorization is done with "o" (Yes) or not done with "n" (No).

The programmed values of all registers are then printed on the serial interface.

It has been tested with an Arduino nano.

.

 

                                                Schematic

 

Arduino plvcxo

 

 

 

                                                 Source code

 

/* Le 20/03/2013
Par Alain Fort F1CJN alain.fort.f1cjn@sfr.fr

Ce programme pour Arduino permet de programmer le PLVCX0 décrit par DF9IC afin de pouvoir utiliser toutes les
combinaisons possibles.
Il a été validé avec la montage décrit par F6DRO dans le proceeding CJ 2010.

Version V1
Programmation de P en 64,32,16 ou 8
Surveillance et gestion du verrouillage de l'ADF4110

Ce programme lit et analyse les mots de commande pour programmer les registre d'un PLL ADF4110 via le bus SPI
Il permet de programmer les registres P,N,A et R du circuit ADF4110.
Fout = (NP + A)* (Fref/R)
Fout = Frequence de sortie
Fref = Frequence de référence
R : diviseur de Fref = Fcomp (comparateur de phase) 0 à 16383
P : valeur prescaler P,P+1 par P=8,16,32,ou 64
N : 0 à 8191
A : 0 à P-1

Pour programmer un registre, il suffit d'écrire la lettre correspondant au registre suivie de la valeur à

programmer.
Par exemple pour programmer le registre R à la valeur 16222 envoyer "R16222"
Pour Memoriser dans EEPROM, faire "w" puis répondre par "o" oui ou "n" non. (lettres en minuscules).
A la prochaine mise sous tension l'Arduino envoie le contenu des registres mémorisés à l'ADF4110.
Les registres P,N,A et R sont affichés en permanence via le port série à 9600.
On peut, bien sur, changer les valeurs des registres à la volée.

*/
#include <EEPROM.h>
#include <SPI.h>

String cmdstr = String(""); //String cmd
String inputstr = String(""); // String pour le calcul
char charBuf[10];

int p_value;
int p_addreLE;
int N;
int A = 0;
int P;
int R;
int flagW=0;// flag pour commande Write EEPROM
long LongVar;
byte loByte, hiByte;
int etatlock = 0;

char InChar;

byte temp ;
word tempw ;
byte i ;
byte stringComplete;
word n ;
word r ;
word b ;
byte a ;
byte L1;

#define LOCK 9 // D9 :Entrée de lock PLL
#define LE 10// D10 : Load Enable ADF4110
#define DATAOUT 11//D11 :DATA ADF4110
#define DATAIN 12// D12 : non utilisée
#define SPICLOCK 13//D13 : CLK ADF4100

void setup() {
pinMode(DATAOUT, OUTPUT);
pinMode(DATAIN, INPUT);
pinMode(SPICLOCK,OUTPUT);
pinMode(LE,OUTPUT); // Chip Select Very important to program LE (Arduino pin 10) as output when using SPI as

master
pinMode(LOCK, INPUT);

SPI.begin();
SPI.setDataMode(SPI_MODE0); //Mode Clock Polarity (CPOL)=0 Clock Phase (CPHA)=0SPI_MODE0
SPI.setBitOrder(MSBFIRST);
SPI.setClockDivider(SPI_CLOCK_DIV128);

digitalWrite(LE,LOW);

Serial.begin(9600); // communication serie à 9600):
Serial.println("La communication avec le port serie est ouverte.");

loByte = EEPROM.read(0); // lecture EEPROM
hiByte = EEPROM.read(1); // lecture EEPROM
P =word(hiByte,loByte); // registre P à l'adreLEe 0 et 1

loByte = EEPROM.read(2);
hiByte = EEPROM.read(3);
N =word(hiByte,loByte);// registre N à l'adreLEe 2 et 3

loByte = EEPROM.read(4);
hiByte = EEPROM.read(5);
A =word(hiByte,loByte); // registre A à l'adreLEe 4 et 5

loByte = EEPROM.read(6);
hiByte = EEPROM.read(7);
R =word(hiByte,loByte); // registre R à l'adreLEe 5 et 6

printReg(); // Affichage des registres P,N,A,R
EnvoieSPI();
}

void loop() { // Programme Principal
etatlock = digitalRead(LOCK);
if (etatlock==LOW){
EnvoieSPI(); // commande vers ADF4110 si la boucle n'est pas calée
delay(2000);
}

if (stringComplete) {
Serial.println(cmdstr);
Commandes();
cmdstr = ""; //Raz du String d'entrée
stringComplete = false;
}
}

//-------------------------------------------------------------------------------------------
void Commandes() { // analyse des commandes
if (cmdstr.charAt(0) == 'P') {
flagW=0;
if (cmdstr.length()<10){ //6 caracteres + fin de string
inputstr=cmdstr.substring(1,3); // recuperation des Digits placés de 1 à 4 (4 digits + Zéro)
inputstr.toCharArray(charBuf,3); // paLEage de mode String à Char
LongVar = atol(charBuf); // mise en variable Long
P=64; // on met 64 par défaut
if ((LongVar==8) || (LongVar==16) || (LongVar==32) || (LongVar==64)) { // si P n'est pas une bonne valeur
P=LongVar; // valeurs autorisées
}
}
Serial.print(" P = ");
Serial.println(P, DEC);
}
else if (cmdstr.charAt(0) == 'N') {
flagW=0;
if (cmdstr.length()<10){ //6 caracteres + fin de string
inputstr=cmdstr.substring(1,5); // recuperation des Digits placés de 1 à 4 (4 digits + Zéro)
inputstr.toCharArray(charBuf,5);
LongVar = atol(charBuf); // mise en variable Long
if (LongVar<=0){
LongVar=0; // pour interdire les nombre négatifs
}
if (LongVar>=8191){ // pour interdire les nombres > 8191
LongVar=8191; // pour ne pas dépaLEer 14 bits
}
N=LongVar; // long vers integer
}
}
else if (cmdstr.charAt(0) == 'A') {
flagW=0;
if (cmdstr.length()<10){ //6 caracteres + fin de string
inputstr=cmdstr.substring(1,3); // recuperation des Digits placés de 1 à 4 (4 digits + Zéro)
inputstr.toCharArray(charBuf,3);
LongVar = atol(charBuf); // mise en variable Long
if (LongVar<=0){
LongVar=0;
} // pour interdire les nombre négatifs
if (LongVar>=63){ // limitation à 63 pour ce registre
LongVar=63;
} // pour ne pas dépaLEer 14 bits
A=LongVar; // long vers int
}
}
else if (cmdstr.charAt(0) == 'R') { // COMMANDE R = XXXXX
flagW=0;
if (cmdstr.length()<10){ //6 caracteres + fin de string
inputstr=cmdstr.substring(1,6); // recuperation des Digits placés de 1 à 5 (5 digits + Zéro)
inputstr.toCharArray(charBuf,6);
LongVar = atol(charBuf); // mise en variable Long
if (LongVar<=0){
LongVar=0;
} // pour interdire les nombre négatifs
if (LongVar>=16383){ // limitation à 16383
LongVar=16383;
} // pour ne pas dépaLEer 14 bits
R=LongVar; // long vers integer
}
}
else if (cmdstr.charAt(0) == 'w') { // COMMANDE W Write EEPROM
Serial.println(" * Confirmer ecriture EEPROM o/n *");
flagW=1; //autorisation écriture néceLEaire pour la réponse oui/non o/n
}
else if ((cmdstr.charAt(0) == 'o') && (flagW==1) ){ // Ecriture si "o" et flagW=1
writeEEPROMint (0,P); //ecriture variable int P sur 2 octets à adreLEe 0
writeEEPROMint (2,N); //ecriture variable int N sur 2 octets à adreLEe 2
writeEEPROMint (4,A); //ecriture variable int A sur 2 octets à adreLEe 4
writeEEPROMint (6,R); //ecriture variable int R sur 2 octets à adreLEe 6
flagW=0;
Serial.println(" ****************************");
Serial.println(" * Ecriture EEPROM terminee *");
Serial.println(" ****************************");
}
else if ((cmdstr.charAt(0) == 'n') && (flagW==1) ){ // Pas d'écriture si "n" et flagW==1
Serial.println(" * Pas d'ecriture *");
flagW=0;
}
else{
// toutes les autres commandes sont ignorées.
Serial.println(" * Commande non reconnue *");
flagW=0;
}
printReg();
EnvoieSPI();
clearCmd();
}
//------------------------------------------------------------------------------
void writeEEPROMint (int p_addreLE,int p_value){ // écriture int en EEPROM
byte lowByte = ((p_value >> 0) & 0xFF);
byte highByte = ((p_value >> 8) & 0xFF);
EEPROM.write(p_addreLE, lowByte);
EEPROM.write(p_addreLE + 1, highByte);
}
//-----------------------------------------------------------------------------
void Toggle() { // validation données sur bus SPI
digitalWrite(LE,HIGH);
delayMicroseconds(1);
digitalWrite(LE,LOW);
}
//--------------------------------------------------------------------------------
void EnvoieSPI(){
switch (P) { // calcul des "prescaler values"
case 64:
L1=0xC3;
break ;
case 32:
L1=0x83;
break ;
case 16:
L1=0x43;
break ;
case 8:
L1=0x03;
break ;
}

Toggle();
// mettre L1 si cela fonctionne
SPI.transfer(L1); // Poids fort en tete FUNCTION LATCH (Initialisation du circuit ADF4110) avec prescaler
SPI.transfer(0X80);
SPI.transfer(0X96);
Toggle();

SPI.transfer(0X12); // REFERENCE COUNTER LATCH
// R=4000 for a 10 MHz signal Input and 2.5 kHz Fcomp
temp = R>>6 ; // on recupéère les 8 bits de poids forts de R
SPI.transfer(temp);
temp = R<<2 ; //On récupére les 6 bits de poids faibles de R
SPI.transfer(temp);
Toggle();

// N (and A) COUNTER LATCH
//nn=10800 ; //pour du 27 MHz
//nn =42600 ; // pour du 106.5 MHz
//N = nn / 64 ; // 168 dec 0xA8 pour 27 MHz
//A = nn-(N*64); // 48 dec 0x30 pour 27MHz
temp = N >>8 ;//on recupère les msbs de word b dans byte temp
//Serial.print(" N msb HEX = ");
//Serial.println(temp, HEX);
SPI.transfer(temp);
temp=N ; //on recupère les lsbs de word b dans byte temp
//Serial.print(" N HEX = ");
//Serial.println(N, HEX);
SPI.transfer(temp);
//Serial.print(" A HEX = ");
//Serial.println(A, HEX); // print avec cr lf
a=A;
a = a <<2 ; //programmation de A positionnement des bits
temp = a +1 ;// ajout de %01 pour les lsb
SPI.transfer(temp);
Toggle();

SPI.transfer(L1); // Last initialization Latch programming = start
SPI.transfer(0X80);
SPI.transfer(0X92);
Toggle();
delay(10);
//return 0;
}
//------------------------------------------------------------------------------------------
void clearCmd(){ // re init propre après analyse
cmdstr=""; // vidage du buffer d'entrée
inputstr="";// vidage du buffer de calcul
while(Serial.available()>0) Serial.read(); //vidage buffer physique en entrée
}
//-------------------------------------------------------------------------------------------
void printReg(){ // envoie des contenus des registres sur le port série
Serial.print( " P = " );
Serial.println(P, DEC);
Serial.print( " N = " );
Serial.println(N, DEC);
Serial.print( " A = " );
Serial.println(A, DEC);
Serial.print( " R = " );
Serial.println(R, DEC);
Serial.println(); // saut de ligne
}

void serialEvent() { // Lecture des caractère sur le bus série
while (Serial.available()) {
InChar = Serial.read(); // capture du byte
cmdstr += InChar;
if (InChar == 13) { // si "CR" alors fin de string
stringComplete = true;
}
}
}