/**
	ADF4351-ESP32.cpp
	Firmware pour piloter un circuit DDS : ADF4351
	avec un ESP32

		Silicium628 - avril 2026
**/

/*
	Ce fichier source "ADF4351-ESP32.cpp" est libre, vous pouvez le redistribuer et/ou le modifier selon
	les termes de la Licence Publique Générale GNU.

	Un grand merci à Alain Fort F1CJN feb 2,2016
	pour son travail :
	
	"ADF4251 and Arduino
	update march 7, 2016 (ROBOT V1.1 and V1.0)"
	-http://f6kbf.free.fr/html/ADF4351%20and%20Arduino_Fr_Gb.htm
	-http://f6kbf.free.fr/html/ADF4351_LCD_07032016.zip
	
	dont je me suis inspiré pour créer cette objet (class) en C++
	J'ai aussi utilisé mon propre travail effectué pour les DDS AD9850 et AD9951

	Silicium628 - avril 2026
*/

#include <Arduino.h>
#include "ADF4351-ESP32.h"

#define bitSet(value, bit) ((value) |= (1UL << (bit)))  // 1UL signifie la valeur 1 au format Unsigned Long (32 bits)
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
#define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))

// Constructeur
ADF4351::ADF4351() { }


void ADF4351::init()
{
	//PORT_ADF_set_bit(ADF_LE);

Serial.println("B1"); 	
	digitalWrite(ADF_LE, HIGH);
Serial.println("B2"); 

	Write_All_Register();
Serial.println("B3"); 

	PFDRFout=25;
	RFint=7000;
	RFout = RFint/100 ; // fréquence de sortie
	OutputChannelSpacing = 0.01; // Pas de fréquence = 10kHz
}


void ADF4351::mute(uint8_t valeur)
{
	if (valeur == 1) {digitalWrite(ADF_CE, LOW);} 
	else {digitalWrite(ADF_CE, HIGH);}
}


void ADF4351::impulse_CLK()
{
	digitalWrite(ADF_CLK, HIGH); // HIGH
	delayMicroseconds(10); 	// le fait de supprimer ces tempos augmente grandement la réactivité du circuit. 
	digitalWrite(ADF_CLK, LOW); //LOW
	delayMicroseconds(10);
}


void ADF4351::impulse_LE()
{
	//PORT_ADF_set_bit(ADF_LE);
	digitalWrite(ADF_LE, HIGH);  // HIGH
	delayMicroseconds(10);
	digitalWrite(ADF_LE, LOW); //LOW
	delayMicroseconds(10);
}


void ADF4351::out1octet(uint8_t octet_i)
{
// MSB first (= mode par defaut)
	uint8_t i;
  uint8_t masque; 

	for (i=0;  i < 8; i++)  
	{
		masque = 0b10000000 >> i; // le '1' se déplace de gauche a droite
		if ( (octet_i & masque) != 0) {digitalWrite(ADF_DATA, HIGH);}	 else {digitalWrite(ADF_DATA, LOW); }
		impulse_CLK();
	}
}


void ADF4351::Write_Register32(uint8_t reg_num)   //Programme un registre 32bits
{
	uint8_t n; // numéro du registre (0..5) codé par les trois bits de poids faible
	uint8_t octet_i;
	uint32_t data;
	
	data = registers[reg_num];
	n = data && 0b00000111;
	if (data != memo_registers[n]) // on ne programme le registre que si son contenu doit changer. (cela accelère l'execution du programme)
	{
		memo_registers[n] = data;
		//PORT_ADF_clear_bit(pin_LE);
		digitalWrite(ADF_LE, LOW);

		for (int i = 3; i >= 0; i--) // boucle sur 4 x 8bits
		{        
			octet_i=(data >> 8 * i) & 0xFF; // décalage et masquage de l'octet
			out1octet(octet_i);
		}
		impulse_LE();
// LE = Load Enable. When LE goes high, the data stored in the 32-bit shift register is loaded
// into the register that is selected by the three control bits (ce sont les trois bits de poids faible)
	}
}


void ADF4351::Write_All_Register()  // Programme tous les registres de l'ADF4351
{
	for (int i = 5; i >= 0; i--)  // programmation ADF4351 en commencant par R5
	{	
		 Write_Register32(i);
	}
}


void ADF4351::setFreq(double frequence) // calcule la valeur des registres
{
  RFout = frequence;
  
	registers[4] = 0x8C803C; // cette valeur n'est pas arbitraire. En particulier les bits 20,21,22 doivent être =0.
// ce qui est le cas : 0x8C803C = 0b 1000 1100 1000 0000 0011 1100
	if (RFout >= 2200)    		{ DIV = 1; }
	else if (RFout >= 1100)  { DIV = 2;		registers[4] += (1UL<<20); } // 1UL signifie la valeur 1 au format Unsigned Long en memoire (uint32_t -> 4octets = 32 bits)
	else if (RFout >= 550)   { DIV = 4;		registers[4] += (2UL<<20); }
	else if (RFout >= 275)   { DIV = 8;		registers[4] += (3UL<<20); }
	else if (RFout >= 137.5) { DIV = 16;	registers[4] += (4UL<<20); }
	else if (RFout >= 68.75) { DIV = 32;	registers[4] += (5UL<<20); }
	else  { DIV = 64;		registers[4] += (6UL<<20); } 

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

	registers[0] = 0;
	registers[0] = INTA << 15; // décalage de 15 rangs pour ranger cette valeur (16 bits) à sa place dans R0 ; voir p:15 du pdf
	registers[0] = registers[0] + (FRAC << 3); // decalage de 3 rangs vers la gauche pour le rentrer dans les bits [DB3..DB14]

	registers[1] = 0;
	registers[1] = MOD << 3; // decalage de 3 pour le rentrer dans les bits [DB3..DB14]
	registers[1] = registers[1] + 1 ; // ajout de l'adresse "001"
	bitSet (registers[1], 27); // PR1-> Prescaler = 8/9 ; voir p:16 du pdf

	bitSet (registers[2], 28); // Digital lock == "110" sur b28 b27 b26
	bitSet (registers[2], 27); // digital lock 
	bitClear (registers[2], 26); // digital lock

// bitSet (registers[4], 10); //MTLD: Supply current to the RF output stage to be shut down until the part achieves lock

	Write_All_Register(); 
}



