mirror of
https://github.com/nqrduck/ATM.git
synced 2024-12-11 11:10:50 +00:00
334 lines
8.6 KiB
C++
334 lines
8.6 KiB
C++
/*!
|
|
The ADF4351 chip is a wideband freqency synthesizer integrated circuit that can generate frequencies
|
|
from 35 MHz to 4.4 GHz. It incorporates a PLL (Fraction-N and Integer-N modes) and VCO, along with
|
|
prescalers, dividers and multipiers. The users add a PLL loop filter and reference frequency to
|
|
create a frequency generator with a very wide range, that is tuneable in settable frequency steps.
|
|
The ADF4351 chip provides an I2C interface for setting the device registers that control the
|
|
frequency and output levels, along with several IO pins for gathering chip status and
|
|
enabling/disabling output and power modes.
|
|
The ADF4351 library provides an Arduino API for accessing the features of the ADF chip.
|
|
The basic PLL equations for the ADF4351 are:
|
|
\f$ RF_{out} = f_{PFD} \times (INT +(\frac{FRAC}{MOD})) \f$
|
|
where:
|
|
\f$ f_{PFD} = REF_{IN} \times \left[ \frac{(1 + D)}{( R \times (1 + T))} \right] \f$
|
|
\f$ D = \textrm{RD2refdouble, ref doubler flag}\f$
|
|
\f$ R = \textrm{RCounter, ref divider}\f$
|
|
\f$ T = \textrm{RD1Rdiv2, ref divide by 2 flag}\f$
|
|
|
|
This library uses the BigNumber library (included) from Nick Gammon
|
|
|
|
Modified by J. Pfitzer from David Fannin (https://github.com/dfannin/adf4351), KK6DF for usage with an ESP32 board
|
|
|
|
MIT License
|
|
*/
|
|
|
|
#include "ADF4351.h"
|
|
|
|
#define ADF_FREQ_MAX 4294967295UL ///< Maximum Generated Frequency = value of MAX Unsigned Long
|
|
#define ADF_FREQ_MIN 34385000UL ///< Minimum Generated Frequency
|
|
#define ADF_PFD_MAX 32000000.0 ///< Maximum Frequency for Phase Detector
|
|
#define ADF_PFD_MIN 125000.0 ///< Minimum Frequency for Phase Detector
|
|
#define ADF_REFIN_MAX 250000000UL ///< Maximum Reference Frequency
|
|
#define REF_FREQ_DEFAULT 250000000UL ///< Default Reference Frequency
|
|
|
|
uint32_t steps[] = { 1000, 5000, 10000, 50000, 100000 , 500000, 1000000 }; ///< Array of Allowed Step Values (Hz)
|
|
|
|
|
|
/*!
|
|
single register constructor
|
|
*/
|
|
Reg::Reg()
|
|
{
|
|
whole = 0 ;
|
|
}
|
|
|
|
uint32_t Reg::get()
|
|
{
|
|
return whole ;
|
|
}
|
|
|
|
void Reg::set(uint32_t value)
|
|
{
|
|
whole = value ;
|
|
}
|
|
|
|
void Reg::setbf(uint8_t start, uint8_t len, uint32_t value)
|
|
{
|
|
uint32_t bitmask = ((1UL << len) - 1UL) ;
|
|
value &= bitmask ;
|
|
bitmask <<= start ;
|
|
whole = ( whole & ( ~bitmask)) | ( value << start ) ;
|
|
}
|
|
|
|
uint32_t Reg::getbf(uint8_t start, uint8_t len)
|
|
{
|
|
uint32_t bitmask = ((1UL << len) - 1UL) << start ;
|
|
uint32_t result = ( whole & bitmask) >> start ;
|
|
return ( result ) ;
|
|
}
|
|
|
|
// Constructor function; initializes communication pinouts
|
|
ADF4351::ADF4351(int SCLK, int DATA, int LE, int CE){
|
|
_sclk = SCLK;
|
|
_data=DATA;
|
|
_le=LE;
|
|
_ce=CE;
|
|
|
|
SPIClass SPI1(HSPI);
|
|
spi_settings = SPISettings(1000000UL, MSBFIRST, SPI_MODE0) ;
|
|
|
|
reffreq = REF_FREQ_DEFAULT ;
|
|
cfreq = 0 ;
|
|
ChanStep = steps[0] ;
|
|
RD2refdouble = 0 ;
|
|
RCounter = 25 ;
|
|
RD1Rdiv2 = 0 ;
|
|
BandSelClock = 80 ;
|
|
ClkDiv = 150 ;
|
|
Prescaler = 0 ;
|
|
pwrlevel = 0 ;
|
|
}
|
|
|
|
void ADF4351::begin(void){
|
|
pinMode(_sclk, OUTPUT);
|
|
pinMode(_data, OUTPUT);
|
|
pinMode(_le, OUTPUT);
|
|
pinMode(_ce, OUTPUT);
|
|
|
|
SPI1.begin();
|
|
}
|
|
|
|
int ADF4351::setf(uint32_t freq)
|
|
{
|
|
// calculate settings from freq
|
|
if ( freq > ADF_FREQ_MAX ) return 1 ;
|
|
|
|
if ( freq < ADF_FREQ_MIN ) return 1 ;
|
|
|
|
int localosc_ratio = 2200000000UL / freq ;
|
|
outdiv = 1 ;
|
|
int RfDivSel = 0 ;
|
|
|
|
// select the output divider
|
|
while ( outdiv <= localosc_ratio && outdiv <= 64 ) {
|
|
outdiv *= 2 ;
|
|
RfDivSel++ ;
|
|
}
|
|
|
|
if ( freq > 3600000000UL/outdiv )
|
|
Prescaler = 1 ;
|
|
else
|
|
Prescaler = 0 ;
|
|
|
|
PFDFreq = (float) reffreq * ( (float) ( 1.0 + RD2refdouble) / (float) (RCounter * (1.0 + RD1Rdiv2))); // find the loop freq
|
|
BigNumber::begin(10) ;
|
|
char tmpstr[20] ;
|
|
// kludge - BigNumber doesn't like leading spaces
|
|
// so you need to make sure the string passed doesnt
|
|
// have leading spaces.
|
|
int cntdigits = 0 ;
|
|
uint32_t num = (uint32_t) ( PFDFreq / 10000 ) ;
|
|
|
|
while ( num != 0 ) {
|
|
cntdigits++ ;
|
|
num /= 10 ;
|
|
}
|
|
|
|
dtostrf(PFDFreq, cntdigits + 8 , 3, tmpstr) ;
|
|
// end of kludge
|
|
BigNumber BN_PFDFreq = BigNumber(tmpstr) ;
|
|
BigNumber BN_N = ( BigNumber(freq) * BigNumber(outdiv) ) / BN_PFDFreq ;
|
|
N_Int = (uint16_t) ( (uint32_t) BN_N ) ;
|
|
BigNumber BN_Mod = BN_PFDFreq / BigNumber(ChanStep) ;
|
|
Mod = BN_Mod ;
|
|
BN_Mod = BigNumber(Mod) ;
|
|
BigNumber BN_Frac = ((BN_N - BigNumber(N_Int)) * BN_Mod) + BigNumber("0.5") ;
|
|
Frac = (int) ( (uint32_t) BN_Frac);
|
|
BN_N = BigNumber(N_Int) ;
|
|
|
|
if ( Frac != 0 ) {
|
|
uint32_t gcd = gcd_iter(Frac, Mod) ;
|
|
|
|
if ( gcd > 1 ) {
|
|
Frac /= gcd ;
|
|
BN_Frac = BigNumber(Frac) ;
|
|
Mod /= gcd ;
|
|
BN_Mod = BigNumber(Mod) ;
|
|
}
|
|
}
|
|
|
|
BigNumber BN_cfreq ;
|
|
|
|
if ( Frac == 0 ) {
|
|
BN_cfreq = ( BN_PFDFreq * BN_N) / BigNumber(outdiv) ;
|
|
|
|
} else {
|
|
BN_cfreq = ( BN_PFDFreq * ( BN_N + ( BN_Frac / BN_Mod) ) ) / BigNumber(outdiv) ;
|
|
}
|
|
|
|
cfreq = BN_cfreq ;
|
|
|
|
if ( cfreq != freq ) Serial.println(F("output freq diff than requested")) ;
|
|
|
|
BigNumber::finish() ;
|
|
|
|
if ( Mod < 2 || Mod > 4095) {
|
|
Serial.println(F("Mod out of range")) ;
|
|
return 1 ;
|
|
}
|
|
|
|
if ( (uint32_t) Frac > (Mod - 1) ) {
|
|
Serial.println(F("Frac out of range")) ;
|
|
return 1 ;
|
|
}
|
|
|
|
if ( Prescaler == 0 && ( N_Int < 23 || N_Int > 65535)) {
|
|
Serial.println(F("N_Int out of range")) ;
|
|
return 1;
|
|
|
|
} else if ( Prescaler == 1 && ( N_Int < 75 || N_Int > 65535 )) {
|
|
Serial.println(F("N_Int out of range")) ;
|
|
return 1;
|
|
}
|
|
|
|
// setting the registers to default values
|
|
// R0
|
|
R[0].set(0UL) ;
|
|
// (0,3,0) control bits
|
|
R[0].setbf(3, 12, Frac) ; // fractonal
|
|
R[0].setbf(15, 16, N_Int) ; // N integer
|
|
// R1
|
|
R[1].set(0UL) ;
|
|
R[1].setbf(0, 3, 1) ; // control bits
|
|
R[1].setbf(3, 12, Mod) ; // Mod
|
|
R[1].setbf(15, 12, 1); // phase
|
|
R[1].setbf(27, 1, Prescaler); // prescaler
|
|
// (28,1,0) phase adjust
|
|
// R2
|
|
R[2].set(0UL) ;
|
|
R[2].setbf(0, 3, 2) ; // control bits
|
|
// (3,1,0) counter reset
|
|
// (4,1,0) cp3 state
|
|
// (5,1,0) power down
|
|
R[2].setbf(6, 1, 1) ; // pd polarity
|
|
|
|
if ( Frac == 0 ) {
|
|
R[2].setbf(7, 1, 1) ; // LDP, int-n mode
|
|
R[2].setbf(8, 1, 1) ; // ldf, int-n mode
|
|
|
|
} else {
|
|
R[2].setbf(7, 1, 0) ; // LDP, frac-n mode
|
|
R[2].setbf(8, 1, 0) ; // ldf ,frac-n mode
|
|
}
|
|
|
|
R[2].setbf(9, 4, 7) ; // charge pump
|
|
// (13,1,0) dbl buf
|
|
R[2].setbf(14, 10, RCounter) ; // r counter
|
|
R[2].setbf(24, 1, RD1Rdiv2) ; // RD1_RDiv2
|
|
R[2].setbf(25, 1, RD2refdouble) ; // RD2refdouble
|
|
// R[2].setbf(26,3,0) ; // muxout, not used
|
|
// (29,2,0) low noise and spurs mode
|
|
// R3
|
|
R[3].set(0UL) ;
|
|
R[3].setbf(0, 3, 3) ; // control bits
|
|
R[3].setbf(3, 12, ClkDiv) ; // clock divider
|
|
|
|
// (15,2,0) clk div mode
|
|
// (17,1,0) reserved
|
|
// (18,1,0) CSR
|
|
// (19,2,0) reserved
|
|
if ( Frac == 0 ) {
|
|
R[3].setbf(21, 1, 1); // charge cancel, reduces pfd spurs
|
|
R[3].setbf(22, 1, 1); // ABP, int-n
|
|
|
|
} else {
|
|
R[3].setbf(21, 1, 0) ; // charge cancel
|
|
R[3].setbf(22, 1, 0); // ABP, frac-n
|
|
}
|
|
|
|
R[3].setbf(23, 1, 1) ; // Band Select Clock Mode
|
|
// (24,8,0) reserved
|
|
// R4
|
|
R[4].set(0UL) ;
|
|
R[4].setbf(0, 3, 4) ; // control bits
|
|
R[4].setbf(3, 2, pwrlevel) ; // output power 0-3 (-4dbM to 5dbM, 3db steps)
|
|
R[4].setbf(5, 1, 1) ; // rf output enable
|
|
// (6,2,0) aux output power
|
|
// (8,1,0) aux output enable
|
|
// (9,1,0) aux output select
|
|
// (10,1,0) mtld
|
|
// (11,1,0) vco power down
|
|
R[4].setbf(12, 8, BandSelClock) ; // band select clock divider
|
|
R[4].setbf(20, 3, RfDivSel) ; // rf divider select
|
|
R[4].setbf(23, 1, 1) ; // feedback select
|
|
// (24,8,0) reserved
|
|
// R5
|
|
R[5].set(0UL) ;
|
|
R[5].setbf(0, 3, 5) ; // control bits
|
|
// (3,16,0) reserved
|
|
R[5].setbf(19, 2, 3) ; // Reserved field,set to 11
|
|
// (21,1,0) reserved
|
|
R[5].setbf(22, 2, 1) ; // LD Pin Mode
|
|
// (24,8,0) reserved
|
|
int i ;
|
|
|
|
SPI1.beginTransaction(spi_settings);
|
|
for (i = 5 ; i > -1 ; i--) {
|
|
WriteRegister(getReg(i)) ;
|
|
//delayMicroseconds(2500) ;
|
|
}
|
|
SPI1.endTransaction();
|
|
|
|
return 0 ; // ok
|
|
}
|
|
|
|
int ADF4351::setrf(uint32_t f)
|
|
{
|
|
if ( f > ADF_REFIN_MAX ) return 1 ;
|
|
|
|
if ( f < 100000UL ) return 1 ;
|
|
|
|
float newfreq = (float) f * ( (float) ( 1.0 + RD2refdouble) / (float) (RCounter * (1.0 + RD1Rdiv2))); // check the loop freq
|
|
|
|
if ( newfreq > ADF_PFD_MAX ) return 1 ;
|
|
|
|
if ( newfreq < ADF_PFD_MIN ) return 1 ;
|
|
|
|
reffreq = f ;
|
|
return 0 ;
|
|
}
|
|
|
|
|
|
// write data into register
|
|
void ADF4351::WriteRegister(uint32_t regData){
|
|
byte txbyte ;
|
|
digitalWrite(_le, LOW) ;
|
|
|
|
for (int i = 3 ; i > -1 ; i--) {
|
|
txbyte = (byte) (regData >> (i * 8)) ;
|
|
SPI1.transfer(txbyte) ;
|
|
}
|
|
|
|
digitalWrite(_le, HIGH) ;
|
|
delayMicroseconds(5) ;
|
|
digitalWrite(_le, LOW) ;
|
|
|
|
}
|
|
|
|
uint32_t ADF4351::getReg(int n)
|
|
{
|
|
return R[n].whole ;
|
|
}
|
|
|
|
uint32_t ADF4351::gcd_iter(uint32_t u, uint32_t v)
|
|
{
|
|
uint32_t t;
|
|
|
|
while (v) {
|
|
t = u ;
|
|
u = v ;
|
|
v = t % v ;
|
|
}
|
|
|
|
return u ;
|
|
}
|