Initial commit
This commit is contained in:
@@ -0,0 +1,783 @@
|
||||
/**
|
||||
* \file ad9528.c
|
||||
* \brief Contains source code for configuring and initializing AD9528 clock device
|
||||
*/
|
||||
|
||||
/**
|
||||
* \page Disclaimer Legal Disclaimer
|
||||
* Copyright 2015-2017 Analog Devices Inc.
|
||||
* Released under the AD9371 API license.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "ad9528.h"
|
||||
#include "../../../include/error.h"
|
||||
|
||||
static uint32_t _desired_time_to_elapse_ms = 0;
|
||||
|
||||
/**
|
||||
* \brief Write data to a register
|
||||
*/
|
||||
int32_t AD9528_spiWriteByte(ad9528Device_t *device, uint16_t addr,
|
||||
uint8_t data)
|
||||
{
|
||||
uint8_t buf[3];
|
||||
int32_t status;
|
||||
|
||||
buf[0] = (addr >> 8) & 0x7F;
|
||||
buf[1] = addr & 0xFF;
|
||||
buf[2] = data;
|
||||
status = spi_write_and_read_3wire(device->spi_desc,
|
||||
buf, NULL, 3, 0);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read data from a register
|
||||
*/
|
||||
int32_t AD9528_spiReadByte(ad9528Device_t *device, uint16_t addr,
|
||||
uint8_t *readdata)
|
||||
{
|
||||
uint8_t tx_buf[2];
|
||||
uint8_t rx_buf[1];
|
||||
int32_t status;
|
||||
|
||||
tx_buf[0] = 0x80 | addr >> 8;
|
||||
tx_buf[1] = addr & 0xFF;
|
||||
|
||||
status = spi_write_and_read_3wire(device->spi_desc,
|
||||
tx_buf, rx_buf, 2, 1);
|
||||
|
||||
*readdata = rx_buf[0];
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Modify a field of bits from a register
|
||||
*/
|
||||
int32_t AD9528_spiWriteField(ad9528Device_t *device,
|
||||
uint16_t addr, uint8_t fieldVal, uint8_t mask, uint8_t startBit)
|
||||
{
|
||||
int32_t errVal;
|
||||
uint8_t readVal;
|
||||
|
||||
errVal = AD9528_spiReadByte(device, addr, &readVal);
|
||||
if (errVal < 0)
|
||||
return errVal;
|
||||
|
||||
readVal = (readVal & ~mask) | ((fieldVal << startBit) & mask);
|
||||
|
||||
return AD9528_spiWriteByte(device, addr, readVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the desired timeout value
|
||||
*/
|
||||
int32_t AD9528_setTimeout_ms(uint32_t timeOut_ms)
|
||||
{
|
||||
_desired_time_to_elapse_ms = timeOut_ms;
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Verify if the desired time has elapsed
|
||||
*/
|
||||
int32_t AD9528_hasTimeoutExpired()
|
||||
{
|
||||
udelay(1000);
|
||||
|
||||
_desired_time_to_elapse_ms--;
|
||||
if (_desired_time_to_elapse_ms > 0)
|
||||
return SUCCESS;
|
||||
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Performs a hard reset on the AD9528 DUT
|
||||
*
|
||||
* <B>Dependencies</B>
|
||||
* - device->spiSettings->chipSelectIndex
|
||||
*
|
||||
* \param device is structure pointer to the AD9528 data structure containing settings
|
||||
* \return Returns enum ADI_ERR, ADIERR_OK=pass, !ADIERR_OK=fail
|
||||
*/
|
||||
int32_t AD9528_resetDevice(ad9528Device_t *device)
|
||||
{
|
||||
gpio_direction_output(device->gpio_resetb, 0);
|
||||
mdelay(100);
|
||||
gpio_direction_output(device->gpio_resetb, 1);
|
||||
mdelay(100);
|
||||
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_ADI_SPI_CONFIG_A, 0x81);
|
||||
mdelay(100);
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_ADI_SPI_CONFIG_A, 0x00);
|
||||
mdelay(100);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sets the AD9528 device SPI settings (3wire/4wire, MSBFirst, etc).
|
||||
*
|
||||
* This function will use the settings in the device->spiSettings structure
|
||||
* to set SPI stream mode, address auto increment direction, MSBFirst/LSBfirst,
|
||||
* and 3wire/4wire mode. The AD9528 device always uses SPI MODE 0 (CPHA=0, CPOL=0).
|
||||
* This function will update your device->spiSettings to set CHPA=0 and CPOL=0 and
|
||||
* longInstructionWord =1 to use a 16bit instruction word.
|
||||
*
|
||||
* <B>Dependencies</B>
|
||||
* - writes device->spiSettings->CPHA = 0
|
||||
* - writes device->spiSettings->CPOL = 0
|
||||
* - writes device->spiSettings->longInstructionWord = 1
|
||||
*
|
||||
* - device->spiSettings->MSBFirst
|
||||
* - device->spiSettings->enSpiStreaming
|
||||
* - device->spiSettings->autoIncAddrUp
|
||||
* - device->spiSettings->fourWireMode
|
||||
*
|
||||
* \param device is structure pointer to the AD9528 data structure containing settings
|
||||
*
|
||||
* \return Returns enum ADI_ERR, ADIERR_OK=pass, !ADIERR_OK=fail
|
||||
*/
|
||||
int32_t AD9528_setSpiSettings(ad9528Device_t *device)
|
||||
{
|
||||
uint8_t spiReg = 0;
|
||||
|
||||
device->spiSettings->CPHA = 0;
|
||||
device->spiSettings->CPOL = 0;
|
||||
device->spiSettings->longInstructionWord = 1;
|
||||
|
||||
if (device->spiSettings->MSBFirst == 0) {
|
||||
spiReg |= 0x42; /* SPI bit is 1=LSB first */
|
||||
}
|
||||
|
||||
if (device->spiSettings->autoIncAddrUp > 0) {
|
||||
spiReg |= 0x24;
|
||||
}
|
||||
|
||||
if (device->spiSettings->fourWireMode > 0) {
|
||||
spiReg |= 0x18;
|
||||
}
|
||||
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_ADI_SPI_CONFIG_A, spiReg);
|
||||
|
||||
if (device->spiSettings->enSpiStreaming > 0) {
|
||||
/* Allow SPI streaming mode: SPI message ends when chip select deasserts */
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_ADI_SPI_CONFIG_B, 0x00);
|
||||
} else {
|
||||
/* Force single instruction mode */
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_ADI_SPI_CONFIG_B, 0x80);
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Helper function for ADI transceiver eval boards to init the AD9528 data structure
|
||||
*
|
||||
* This function inits the AD9528 device data structure with defaults that
|
||||
* are known to work with the ADI AD9369 and AD9371 evaluation boards. NOTE: This function will
|
||||
* modify the contents of the AD9528 device data structure.
|
||||
*
|
||||
* <B>Dependencies</B>
|
||||
* - spi->chipSelectIndex
|
||||
* - spi
|
||||
*
|
||||
* \param device Pointer to the AD9528 device data structure to initialize
|
||||
* \param vcxoFrequency_Hz The VCXO frequency that is connected to the AD9528
|
||||
* \param refAFrequency_Hz The reference clock frequency being input into the AD9528 REFCLK A input
|
||||
* \param outputDeviceClock_Hz Desired output clock frequency to the FPGA and transceiver device
|
||||
*
|
||||
* \return Returns enum ADI_ERR, 0=pass, >0 = fail
|
||||
*/
|
||||
int32_t AD9528_initDeviceDataStruct(ad9528Device_t *device,
|
||||
uint32_t vcxoFrequency_Hz, uint32_t refAFrequency_Hz,
|
||||
uint32_t outputDeviceClock_Hz)
|
||||
{
|
||||
// uint16_t chDiv;
|
||||
// uint8_t i = 0;
|
||||
//
|
||||
// uint32_t pll1PfdFreq_Hz = 0;
|
||||
// uint8_t pll1Ndiv = 0;
|
||||
// uint8_t validPll1DivSetting = 0;
|
||||
// uint8_t refAdiv = 1;
|
||||
//
|
||||
//
|
||||
// uint8_t pll2RfDiv = 3; /* Valid values 3,4,5 */
|
||||
// uint16_t pll2Ndiv = 1; /* Valid values 1-256 */
|
||||
// uint32_t pll2InputFreq_Hz = 0;
|
||||
// uint64_t pll2VcoFreq_Hz = 0;
|
||||
// uint16_t pll2NdivStart = 0;
|
||||
// uint8_t validPll2DivSetting = 0;
|
||||
// uint64_t chDivU64 = 0;
|
||||
//
|
||||
// static const uint64_t PLL2_MIN_VCO_FREQ_HZ = 3450000000ULL;
|
||||
// static const uint64_t PLL2_MAX_VCO_FREQ_HZ = 4025000000ULL;
|
||||
|
||||
// struct spi_init_param spi_param;
|
||||
// int32_t status;
|
||||
//
|
||||
// status = gpio_get(&device->gpio_resetb, CLK_RESETB);
|
||||
// status |= gpio_get(&device->gpio_sysref_req, ADRV_SYSREF_REQ);
|
||||
//
|
||||
// spi_param.id = 0;
|
||||
// spi_param.mode = SPI_MODE_0;
|
||||
// spi_param.chip_select = CLK_CS;
|
||||
// status |= spi_init(&device->spi_desc, &spi_param);
|
||||
//
|
||||
// if (status != SUCCESS)
|
||||
// return FAILURE;
|
||||
|
||||
/*******************************************************************
|
||||
* PLL1 Settings
|
||||
*******************************************************************
|
||||
*/
|
||||
|
||||
/* find valid PLL1 Ndiv and REFA div */
|
||||
// for (pll1Ndiv = 1; pll1Ndiv < 20; pll1Ndiv++) {
|
||||
// pll1PfdFreq_Hz = vcxoFrequency_Hz / pll1Ndiv;
|
||||
//
|
||||
// if ((pll1PfdFreq_Hz <= 110000000) && (pll1PfdFreq_Hz >= 1000000) &&
|
||||
// (pll1PfdFreq_Hz <= refAFrequency_Hz) &&
|
||||
// (refAFrequency_Hz % pll1PfdFreq_Hz == 0)
|
||||
// ) {
|
||||
// validPll1DivSetting = 1;
|
||||
// refAdiv = refAFrequency_Hz / pll1PfdFreq_Hz ;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (validPll1DivSetting == 0) {
|
||||
// return FAILURE;
|
||||
// }
|
||||
|
||||
// device->pll1Settings->nDivider =
|
||||
// pll1Ndiv; //This init function always runs PLL1 at 30.72MHz Pfd rate
|
||||
// device->pll1Settings->refA_bufferCtrl =
|
||||
// DIFFERENTIAL; //DISABLED = 0, SINGLE_ENDED, NEG_SINGLE_ENDED, DIFFERENTIAL
|
||||
// device->pll1Settings->refA_Divider = refAdiv;
|
||||
// device->pll1Settings->refA_Frequency_Hz = refAFrequency_Hz;
|
||||
// device->pll1Settings->refB_bufferCtrl =
|
||||
// DISABLED; //DISABLED = 0, SINGLE_ENDED, NEG_SINGLE_ENDED, DIFFERENTIAL
|
||||
// device->pll1Settings->refB_Divider = 1;
|
||||
// device->pll1Settings->refB_Frequency_Hz = 0;
|
||||
// device->pll1Settings->vcxoBufferCtrl =
|
||||
// NEG_SINGLE_ENDED; //DISABLED = 0, SINGLE_ENDED, NEG_SINGLE_ENDED, DIFFERENTIAL
|
||||
// device->pll1Settings->vcxo_Frequency_Hz = vcxoFrequency_Hz;
|
||||
|
||||
/*******************************************************************
|
||||
* PLL2 Settings
|
||||
*******************************************************************
|
||||
*/
|
||||
|
||||
/*******************************************************************
|
||||
* Output Distribution Settings
|
||||
* 9369CE02A uses the following clock outputs
|
||||
* OUT 1: FPGA REFCLK
|
||||
* OUT 3: FPGA SYSREF
|
||||
* OUT 12: DUT SYSREF
|
||||
* OUT 13: DUT REFCLK
|
||||
*******************************************************************
|
||||
*/
|
||||
|
||||
/* PLL 2 input clock can be several different things. Currently hard coded by this init funciton to use
|
||||
* PLL 1 output (VCXO Frequency) with no scaling
|
||||
*/
|
||||
// pll2InputFreq_Hz = device->pll1Settings->vcxo_Frequency_Hz;
|
||||
// validPll2DivSetting = 0;
|
||||
|
||||
/* Sweep PLL2 Divider settings to find something that has a valid integer divide (3,4,5)*/
|
||||
// for (pll2RfDiv = 3; pll2RfDiv <= 5; pll2RfDiv++) {
|
||||
//
|
||||
// pll2NdivStart = (PLL2_MIN_VCO_FREQ_HZ / pll2RfDiv / pll2InputFreq_Hz);
|
||||
// /* find valid PLL2 Ndiv (1-256) */
|
||||
// for (pll2Ndiv = pll2NdivStart; pll2Ndiv <= 256; pll2Ndiv++) {
|
||||
// pll2VcoFreq_Hz = (pll2InputFreq_Hz * pll2RfDiv * pll2Ndiv);
|
||||
//
|
||||
// if (pll2VcoFreq_Hz < PLL2_MIN_VCO_FREQ_HZ) {
|
||||
// /* Continue to next PLL2 N div */
|
||||
// continue;
|
||||
// } else if (pll2VcoFreq_Hz > PLL2_MAX_VCO_FREQ_HZ) {
|
||||
// /* Break to next PLL2 RF div */
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// if (((pll2VcoFreq_Hz / pll2RfDiv) % outputDeviceClock_Hz) == 0) {
|
||||
//
|
||||
// device->pll2Settings->rfDivider = pll2RfDiv;
|
||||
// device->pll2Settings->n2Divider = pll2Ndiv;
|
||||
// device->pll2Settings->totalNdiv = pll2RfDiv * pll2Ndiv;
|
||||
// chDivU64 = ((pll2VcoFreq_Hz / pll2RfDiv) / outputDeviceClock_Hz);
|
||||
//
|
||||
// if ((chDivU64 < 1) || (chDivU64 > 256)) {
|
||||
// return FAILURE;
|
||||
// } else {
|
||||
// chDiv = (uint16_t)(chDivU64);
|
||||
// }
|
||||
//
|
||||
// validPll2DivSetting = 1;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (validPll2DivSetting == 1) {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (validPll2DivSetting == 0) {
|
||||
// return FAILURE;
|
||||
// }
|
||||
//
|
||||
// device->outputSettings->outPowerDown = (uint16_t)(
|
||||
// ~0x318A); //enable outputs 13,12,8,7,3 and 1
|
||||
//
|
||||
// for (i=0; i<14; i++) {
|
||||
// device->outputSettings->outSource[i] =
|
||||
// CHANNEL_DIV; //CHANNEL_DIV , PLL1_OUTPUT, SYSREF, INV_PLL1_OUTPUT
|
||||
// device->outputSettings->outBufferCtrl[i] = LVDS; //LVDS, LVDS_BOOST, HSTL
|
||||
// device->outputSettings->outChannelDiv[i] = chDiv;
|
||||
// device->outputSettings->outFrequency_Hz[i] = outputDeviceClock_Hz;
|
||||
// device->outputSettings->outAnalogDelay[i] = 0;
|
||||
// device->outputSettings->outDigitalDelay[i] = 0;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// device->outputSettings->outSource[3] = SYSREF;
|
||||
// device->outputSettings->outSource[7] = SYSREF;
|
||||
// device->outputSettings->outSource[12] = SYSREF;
|
||||
//
|
||||
//
|
||||
// device->sysrefSettings->sysrefDivide =
|
||||
// 512; //SYSREF frequency = VCXO / 2 / sysrefDivde
|
||||
// device->sysrefSettings->sysrefNshotMode =
|
||||
// ONE_PULSE; //ONE_PULSE=0, TWO_PULSES=2, FOUR_PULSES=3, SIX_PULSES=4, EIGHT_PULSES=5
|
||||
// device->sysrefSettings->sysrefPatternMode =
|
||||
// CONTINUOUS; //NSHOT=0, CONTINUOUS=1, PRBS=2, STOP=3
|
||||
// device->sysrefSettings->sysrefPinBufferMode =
|
||||
// DISABLED; //DISABLED = 0, SINGLE_ENDED, NEG_SINGLE_ENDED, DIFFERENTIAL
|
||||
// device->sysrefSettings->sysrefPinEdgeMode =
|
||||
// LEVEL_ACTIVE_HIGH; //LEVEL_ACTIVE_HIGH=0, LEVEL_ACTIVE_LOW=1, RISING_EDGE=2, FALLING_EDGE=3
|
||||
// device->sysrefSettings->sysrefRequestMethod = PIN; //SPI=0,PIN=1
|
||||
// device->sysrefSettings->sysrefSource =
|
||||
// INTERNAL; //EXTERNAL=0, EXT_RESAMPLED=1, INTERNAL=2
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int32_t AD9528_init(ad9528Device_t **device, const ad9528Init_t *init_device)
|
||||
{
|
||||
int ret = SUCCESS;
|
||||
ad9528Device_t *dev;
|
||||
|
||||
dev = (ad9528Device_t *)malloc(sizeof(*dev));
|
||||
if (!dev)
|
||||
return FAILURE;
|
||||
|
||||
/* SPI */
|
||||
ret = spi_init(&dev->spi_desc, init_device->spi_init);
|
||||
ret |= gpio_get(&dev->gpio_resetb, init_device->gpio_init_resetb);
|
||||
ret |= gpio_get(&dev->gpio_sysref_req, init_device->gpio_init_sysref_req);
|
||||
|
||||
gpio_direction_output(dev->gpio_resetb, 0);
|
||||
gpio_direction_output(dev->gpio_sysref_req, 0);
|
||||
|
||||
AD9528_resetDevice(dev);
|
||||
|
||||
dev->spiSettings = init_device->spiSettings;
|
||||
dev->pll1Settings = init_device->pll1Settings;
|
||||
dev->pll2Settings = init_device->pll2Settings;
|
||||
dev->outputSettings = init_device->outputSettings;
|
||||
dev->sysrefSettings = init_device->sysrefSettings;
|
||||
|
||||
*device = dev;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Initializes the AD9528 by writing all SPI registers
|
||||
*
|
||||
* Initializes the AD9528 for use with the AD9369 transceiver. Designed to work with
|
||||
* a reference clock of 122.88MHz. Reference clock is expected to be 30.72MHz.
|
||||
* The output clocks from the AD9528 can run at 122.88M, 153.6M, 184.32M, 245.76M, or 307.2M.
|
||||
* Typical setup: REFA frequency = 30.72, VCXO = 122.88MHz
|
||||
* SYNC(SYSREF) pulses output at rate of 120kHz
|
||||
*
|
||||
* <B>Dependencies</B>
|
||||
* - All parameters in device structure
|
||||
*
|
||||
* \param device is structure pointer to AD9528 clock device structure
|
||||
*
|
||||
* \return Returns enum ADI_ERR, 0=pass, >0 = fail
|
||||
*/
|
||||
int32_t AD9528_config(ad9528Device_t
|
||||
*device) //writes all registers to desired values, leaves clock outputs off, SYSREF output disabled.
|
||||
{
|
||||
uint8_t pll2_Adiv=0;
|
||||
uint8_t pll2_Bdiv=0;
|
||||
uint8_t reg108=0;
|
||||
uint8_t reg109=0x4;
|
||||
uint8_t i = 0;
|
||||
uint8_t j = 0;
|
||||
uint8_t reg508;
|
||||
int32_t retval = SUCCESS;
|
||||
|
||||
retval = AD9528_setSpiSettings(device);
|
||||
if (retval != SUCCESS) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* Configure PLL1
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_REF_A_DIVIDER_LSB,
|
||||
device->pll1Settings->refA_Divider & 0xFF); //REF A div[7:0]
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_REF_A_DIVIDER_MSB,
|
||||
(device->pll1Settings->refA_Divider >> 8) & 0x03); //REF A div[9:8]
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_REF_B_DIVIDER_LSB,
|
||||
device->pll1Settings->refB_Divider & 0xFF); //REF B div[7:0]
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_REF_B_DIVIDER_MSB,
|
||||
(device->pll1Settings->refB_Divider >> 8) & 0x03); //REF B div[9:8]
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_PLL1_N_DIV_LSB,
|
||||
device->pll1Settings->nDivider & 0xFF); //PLL1 N div[7:0]
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_PLL1_N_DIV_MSB,
|
||||
(device->pll1Settings->nDivider >> 8) & 0x03); //PLL1 N div[9:8]
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_PLL1_CHARGEPUMP,
|
||||
0x0A); //PLL1 Charge Pump control 1
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_PLL1_CP_CTRL2,
|
||||
0x03); //PLL1 Charge Pump control 2
|
||||
|
||||
switch (device->pll1Settings->refA_bufferCtrl) {
|
||||
case DISABLED:
|
||||
reg108 &= ~0x28;
|
||||
reg109 &= ~0x01;
|
||||
break; //clear all REFA enable bits
|
||||
case SINGLE_ENDED:
|
||||
reg108 |= 0x08;
|
||||
reg109 &= ~0x01;
|
||||
break; //Set REFA receiver enable, clear REFA SE negative input enable
|
||||
case NEG_SINGLE_ENDED:
|
||||
reg108 |= 0x08;
|
||||
reg108 &= ~0x10;
|
||||
reg109 |= 0x01;
|
||||
break; //Disable REFA diff receiver, enable REFA receiver and use negative REFA input
|
||||
case DIFFERENTIAL:
|
||||
reg108 |= 0x28;
|
||||
reg109 &= ~0x01;
|
||||
break;
|
||||
default:
|
||||
reg108 |= 0x08;
|
||||
reg109 &= ~0x01;
|
||||
break; //default to single ended case
|
||||
}
|
||||
switch (device->pll1Settings->refB_bufferCtrl) {
|
||||
case DISABLED:
|
||||
reg108 &= ~0x50;
|
||||
reg109 &= ~0x02;
|
||||
break; //clear all REFB enable bits
|
||||
case SINGLE_ENDED:
|
||||
reg108 |= 0x10;
|
||||
reg109 &= ~0x02;
|
||||
break; //Set REFB receiver enable, clear REFB SE negative input enable
|
||||
case NEG_SINGLE_ENDED:
|
||||
reg108 |= 0x10;
|
||||
reg108 &= ~0x40;
|
||||
reg109 |= 0x02;
|
||||
break; //Disable REFB diff receiver, enable REFB receiver and use negative REFB input
|
||||
case DIFFERENTIAL:
|
||||
reg108 |= 0x50;
|
||||
reg109 &= ~0x02;
|
||||
break;
|
||||
default:
|
||||
reg108 |= 0x10;
|
||||
reg109 &= ~0x02;
|
||||
break; //default to single ended case
|
||||
}
|
||||
switch (device->pll1Settings->vcxoBufferCtrl) {
|
||||
case DISABLED:
|
||||
reg108 &= ~0x03;
|
||||
reg108 |= 0x04;
|
||||
break; //power down VCXO input
|
||||
case SINGLE_ENDED:
|
||||
reg108 &= ~0x07;
|
||||
break; //Clear VCXO receiver power down, defaults to Single ended mode
|
||||
case NEG_SINGLE_ENDED:
|
||||
reg108 &= ~0x07;
|
||||
reg108 |= 0x02;
|
||||
break; //Clear VCXO power down, enable Negative single ended mode[1]
|
||||
case DIFFERENTIAL:
|
||||
reg108 &= ~0x07;
|
||||
reg108 |= 0x01;
|
||||
break; //Clear VCXO power down, enable differential input buffer[0]
|
||||
default:
|
||||
reg108 &= ~0x07;
|
||||
break; //default to positive single ended case
|
||||
}
|
||||
|
||||
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_INPUT_RECEIVERS1,
|
||||
reg108); //Input receiver settings (single ended or differential clock inputs)
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_INPUT_RECEIVERS2,
|
||||
reg109); //Input receiver settings
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_INPUT_RECEIVERS3,
|
||||
0x0A); //Reference selection mode
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_PLL1_FASTLOCK,
|
||||
0x00); //Disable Fast lock
|
||||
|
||||
/**********************************************************************
|
||||
* Configure PLL2
|
||||
**********************************************************************
|
||||
*/
|
||||
pll2_Bdiv = device->pll2Settings->totalNdiv / 4;
|
||||
pll2_Adiv = device->pll2Settings->totalNdiv % 4;
|
||||
|
||||
if (pll2_Adiv > 3) { //A div valid range (0 to 3)
|
||||
return FAILURE;
|
||||
}
|
||||
if (pll2_Bdiv < 3 || pll2_Bdiv > 63) {
|
||||
return FAILURE;
|
||||
}
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_CHARGEPUMP,
|
||||
0xE6); //PLL2 Charge Pump[7:0]
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_N_DIV,
|
||||
((pll2_Adiv & 0x3) << 6) | (pll2_Bdiv & 0x3F)); //PLL2 A and B divider
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_CTRL, 0x03); //PLL2 control
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_VCO_CTRL, 0x01); //Manual VCO CAL
|
||||
|
||||
if (device->pll2Settings->rfDivider < 3
|
||||
|| device->pll2Settings->rfDivider > 5) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_VCO_DIV,
|
||||
device->pll2Settings->rfDivider); //VCO divider
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_LF_CTRL1,
|
||||
0x3A); //PLL2 Loop filter [7:0]
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_LF_CTRL2,
|
||||
0x00); //PLL2 Loop filter [8]
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_RDIV, 0x01); //R divider
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_REPLICA_CHDIV,
|
||||
(device->pll2Settings->totalNdiv / device->pll2Settings->rfDivider)
|
||||
-1 ); //Replica channel divider: Ndivider = rfDivider * R divider
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_REPLICA_DIV_PHASE,
|
||||
0x00); //Replica Channel Divider phase
|
||||
|
||||
/**********************************************************************
|
||||
* Configure Output Distribution
|
||||
**********************************************************************
|
||||
*/
|
||||
for (i=0; i<14; i=i+1) {
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_CH_OUT0_CTRL1 + j,
|
||||
((device->outputSettings->outSource[i] & 0x7) << 5) |
|
||||
(device->outputSettings->outAnalogDelay[i] & 0x1F));
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_CH_OUT0_CTRL2 + j,
|
||||
((device->outputSettings->outBufferCtrl[i] & 0x3) << 6) |
|
||||
(device->outputSettings->outDigitalDelay[i] & 0x3F));
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_CH_OUT0_CHDIV + j,
|
||||
device->outputSettings->outChannelDiv[i]-1);
|
||||
j = j+3;
|
||||
}
|
||||
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_OUTPUT_SYNC, 0x00); //Sync outputs
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_MASK_SYNC1, 0x00); //Mask SYNC[7:0]
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_MASK_SYNC2, 0x00); //Mask SYNC[13:8]
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_EN_OUTPUT_PATH_SEL1,
|
||||
0x00); //Bypass SYSREF resample [6:0], bit0:Enable VCXO to feed output distrubution
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_EN_OUTPUT_PATH_SEL2,
|
||||
0x00); //Bypass SYSREF resample [13:7]
|
||||
|
||||
/**********************************************************************
|
||||
* Configure SYSREF operation
|
||||
**********************************************************************
|
||||
*/
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_SYSERF_DIV_LSB,
|
||||
device->sysrefSettings->sysrefDivide & 0xFF); //SYSREF divider [7:0]
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_SYSERF_DIV_MSB,
|
||||
(device->sysrefSettings->sysrefDivide >> 8) & 0xFF); //SYSREF divider [7:0]
|
||||
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL3,
|
||||
((device->sysrefSettings->sysrefRequestMethod & 0x1) << 7) |
|
||||
((device->sysrefSettings->sysrefPinEdgeMode & 0x3) << 5) );
|
||||
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL4,
|
||||
((device->sysrefSettings->sysrefSource & 0x03) << 6) |
|
||||
((device->sysrefSettings->sysrefPatternMode & 0x3) << 4 ) |
|
||||
((device->sysrefSettings->sysrefNshotMode & 0x7) << 1) );
|
||||
|
||||
if(device->sysrefSettings->sysrefPinBufferMode == DISABLED) {
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL5,
|
||||
0x04); //power down input buffer for external SYSREF
|
||||
} else if(device->sysrefSettings->sysrefPinBufferMode == SINGLE_ENDED) {
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL5,
|
||||
0x00); //enable positive singled ended input
|
||||
} else if(device->sysrefSettings->sysrefPinBufferMode == NEG_SINGLE_ENDED) {
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL5,
|
||||
0x02); //enable negative singled ended input
|
||||
} else if(device->sysrefSettings->sysrefPinBufferMode == DIFFERENTIAL) {
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL5,
|
||||
0x01); //enable differential input buffer
|
||||
} else {
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL5,
|
||||
0x04); //power down input buffer for external SYSREF
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* Power down control
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
if (device->pll1Settings->bypassPll1 > 0) {
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_POWERDOWN_CTRL,
|
||||
0x14);// power up PLL2 and output distribution, power down PLL1
|
||||
} else {
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_POWERDOWN_CTRL,
|
||||
0x10);// power up PLL1, PLL2 and output distribution
|
||||
}
|
||||
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_CH_POWERDOWN1,
|
||||
device->outputSettings->outPowerDown & 0xFF); //power down out[7:0]
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_CH_POWERDOWN2,
|
||||
((device->outputSettings->outPowerDown >> 8) & 0x3F)); //power down out[13:8]
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_LDO_ENABLES1,
|
||||
0xFF); //LDO enable per output channel [7:0]
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_LDO_ENABLES2,
|
||||
0xFF); //LDO enable per output channel [15:8]
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_STATUS0_CTRL,
|
||||
0x07); //STATUS 0 outputs REFA present
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_STATUS1_CTRL,
|
||||
0x01); //STATUS 1 outputs PLL1 and PLL2 locked
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_STATUS_OE,
|
||||
0x0C);// Make Status0 and Status1 outputs after reset.
|
||||
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_IO_UPDATE,
|
||||
0x01); //IO update to make all these settings valid in the 9528
|
||||
|
||||
AD9528_setTimeout_ms(1000);
|
||||
|
||||
do {
|
||||
AD9528_spiReadByte(device, AD9528_ADDR_STATUS_READBACK0, ®508);
|
||||
if(AD9528_hasTimeoutExpired() > 0) {
|
||||
return FAILURE;
|
||||
}
|
||||
} while((reg508 & 0x26) != 0x26);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/// Send a SPI message to request a SYSREF pulse or continuous SYSREF from the AD9528.
|
||||
/*******************************************************************************//**
|
||||
* Requests a SYSREF from the AD9528. It will use whatever settings for SYSREF
|
||||
* that are current configured in the AD9528. This could be a single pulse,
|
||||
* multiple pulses, or continuous pulses.
|
||||
* <B>Dependencies</B>
|
||||
* - device->spiSettings
|
||||
* - device->sysrefSettings->sysrefPatternMode
|
||||
*
|
||||
* \param device is structure pointer to AD9528 clock device structure
|
||||
* \param enableSYSREF If NSHOT SYSREF mode, this parameter is ignored. If PRBS/CONTINUOUS SYSREF mode, 1= enable SYSREF, 0= disable SYSREF.
|
||||
*
|
||||
* \return Returns enum ADI_ERR, 0=pass, >0 = fail
|
||||
***********************************************************************************/
|
||||
int32_t AD9528_requestSysref(ad9528Device_t *device, uint8_t enableSYSREF)
|
||||
{
|
||||
uint8_t _enableSYSREF=0;
|
||||
|
||||
if (device->sysrefSettings->sysrefPatternMode == NSHOT || enableSYSREF > 0) {
|
||||
_enableSYSREF = 1;
|
||||
} else {
|
||||
_enableSYSREF = 0;
|
||||
}
|
||||
|
||||
AD9528_spiWriteField(device, AD9528_ADDR_SYSREF_CTRL4, _enableSYSREF, 0x01,
|
||||
0x00); //Set SPI SYSREF request bit
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_IO_UPDATE,
|
||||
0x01); //IO update to make all these settings valid in the 9528
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/// Allow changing the 9528 SYSREF frequency and pattern mode(CONTINUOUS, PRBS vs NSHOT mode)
|
||||
/*******************************************************************************//**
|
||||
* NOTE: this function will update the device structure with the parameter values passed
|
||||
* into this function. The device->sysrefSettings->sysrefPatternMode, device->sysrefSettings->sysrefNshotMode,
|
||||
* and device->sysrefSettings->sysrefDivide values will be updated in the device structure.
|
||||
*
|
||||
* <B>Dependencies</B>
|
||||
* - device->spiSettings
|
||||
*
|
||||
* \param device is structure pointer to AD9528 clock device structure
|
||||
* \param divideFromPll1Out Sets the SYSREF pulse frequency. SYSREF frequency = PLL1_VCXO frequency/divideFromPll1Out
|
||||
* \param sysrefPatternMode Valid settings: NSHOT, CONTINUOUS, PRBS, STOP
|
||||
* \param nShotPulses Valid settings: ONE_PULSE=0, TWO_PULSES=2, FOUR_PULSES=3, SIX_PULSES=4, EIGHT_PULSES=5
|
||||
*
|
||||
* \return Returns enum ADI_ERR, 0=pass, >0 = fail
|
||||
***********************************************************************************/
|
||||
int32_t AD9528_setupSYSREF(ad9528Device_t *device,
|
||||
uint16_t divideFromPll1Out, sysrefPatternMode_t sysrefPatternMode,
|
||||
sysrefNshotMode_t nShotPulses)
|
||||
{
|
||||
uint8_t reg403=0;
|
||||
|
||||
device->sysrefSettings->sysrefPatternMode = sysrefPatternMode;
|
||||
device->sysrefSettings->sysrefNshotMode = nShotPulses;
|
||||
device->sysrefSettings->sysrefDivide = divideFromPll1Out >>
|
||||
1; //SYSREF frequency is (PLL1)VCXO frequency / 2 / device->sysrefSettings->sysrefDivide
|
||||
|
||||
AD9528_spiReadByte(device, AD9528_ADDR_SYSREF_CTRL4, ®403);
|
||||
|
||||
reg403 = reg403 & ~0x01; //clear bit 0 to stop a continuous sysref if it is on.
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL4, reg403);
|
||||
|
||||
reg403 = reg403 & ~0x3E; //clear bits for pattern mode and Nshot mode bitfields
|
||||
reg403 = reg403 | ((sysrefPatternMode & 0x3) << 4) | ((nShotPulses & 0x7) << 1);
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL4,
|
||||
reg403); //update internal sysref pattern mode and Nshot mode (number of pulses)
|
||||
|
||||
//SYSREF request bit x403[0] is left 0. The next call to AD9528_requestSysref will start the SYSREF
|
||||
AD9528_spiWriteByte(device, AD9528_ADDR_IO_UPDATE,
|
||||
0x01); //IO update to make all these settings valid in the 9528
|
||||
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int32_t AD9528_sysrefReq(ad9528Device_t *device,
|
||||
ad9528SysrefReqMode_t mode)
|
||||
{
|
||||
if (mode == SYSREF_CONT_ON)
|
||||
gpio_direction_output(device->gpio_sysref_req, 1);
|
||||
else if (mode == SYSREF_CONT_OFF)
|
||||
gpio_direction_output(device->gpio_sysref_req, 0);
|
||||
else if (mode == SYSREF_PULSE) {
|
||||
gpio_direction_output(device->gpio_sysref_req, 1);
|
||||
mdelay(1);
|
||||
gpio_direction_output(device->gpio_sysref_req, 0);
|
||||
} else
|
||||
return FAILURE;
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Free the allocated resources
|
||||
*/
|
||||
int32_t AD9528_remove(ad9528Device_t *device)
|
||||
{
|
||||
int32_t status;
|
||||
|
||||
status = gpio_remove(device->gpio_resetb);
|
||||
status |= gpio_remove(device->gpio_sysref_req);
|
||||
status |= spi_remove(device->spi_desc);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
/**
|
||||
* \file ad9528.h
|
||||
* \brief Contains function declarations and ad9528Device_t structure typedef for ad9528.c
|
||||
*/
|
||||
|
||||
/**
|
||||
* \page Disclaimer Legal Disclaimer
|
||||
* Copyright 2015-2017 Analog Devices Inc.
|
||||
* Released under the AD9371 API license.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _AD9528_H_
|
||||
#define _AD9528_H_
|
||||
|
||||
/******************************************************************************/
|
||||
/***************************** Include Files **********************************/
|
||||
/******************************************************************************/
|
||||
#include <stdint.h>
|
||||
#include "delay.h"
|
||||
#include "spi.h"
|
||||
#include "gpio.h"
|
||||
|
||||
/******************************************************************************/
|
||||
/********************** Macros and Constants Definitions **********************/
|
||||
/******************************************************************************/
|
||||
|
||||
/* AD9528 SPI Address defines */
|
||||
#define AD9528_ADDR_ADI_SPI_CONFIG_A 0x000
|
||||
#define AD9528_ADDR_ADI_SPI_CONFIG_B 0x001
|
||||
#define AD9528_ADDR_IO_UPDATE 0x00F
|
||||
|
||||
#define AD9528_ADDR_REF_A_DIVIDER_LSB 0x100
|
||||
#define AD9528_ADDR_REF_A_DIVIDER_MSB 0x101
|
||||
#define AD9528_ADDR_REF_B_DIVIDER_LSB 0x102
|
||||
#define AD9528_ADDR_REF_B_DIVIDER_MSB 0x103
|
||||
#define AD9528_ADDR_PLL1_N_DIV_LSB 0x104
|
||||
#define AD9528_ADDR_PLL1_N_DIV_MSB 0x105
|
||||
#define AD9528_ADDR_PLL1_CHARGEPUMP 0x106
|
||||
#define AD9528_ADDR_PLL1_CP_CTRL2 0x107
|
||||
#define AD9528_ADDR_INPUT_RECEIVERS1 0x108
|
||||
#define AD9528_ADDR_INPUT_RECEIVERS2 0x109
|
||||
#define AD9528_ADDR_INPUT_RECEIVERS3 0x10A
|
||||
#define AD9528_ADDR_PLL1_FASTLOCK 0x10B
|
||||
|
||||
#define AD9528_ADDR_PLL2_CHARGEPUMP 0x200
|
||||
#define AD9528_ADDR_PLL2_N_DIV 0x201
|
||||
#define AD9528_ADDR_PLL2_CTRL 0x202
|
||||
#define AD9528_ADDR_PLL2_VCO_CTRL 0x203
|
||||
#define AD9528_ADDR_PLL2_VCO_DIV 0x204
|
||||
#define AD9528_ADDR_PLL2_LF_CTRL1 0x205
|
||||
#define AD9528_ADDR_PLL2_LF_CTRL2 0x206
|
||||
#define AD9528_ADDR_PLL2_RDIV 0x207
|
||||
#define AD9528_ADDR_PLL2_REPLICA_CHDIV 0x208
|
||||
#define AD9528_ADDR_PLL2_REPLICA_DIV_PHASE 0x209
|
||||
|
||||
#define AD9528_ADDR_CH_OUT0_CTRL1 0x300
|
||||
#define AD9528_ADDR_CH_OUT0_CTRL2 0x301
|
||||
#define AD9528_ADDR_CH_OUT0_CHDIV 0x302
|
||||
#define AD9528_ADDR_OUTPUT_SYNC 0x32A
|
||||
#define AD9528_ADDR_MASK_SYNC1 0x32B
|
||||
#define AD9528_ADDR_MASK_SYNC2 0x32C
|
||||
#define AD9528_ADDR_EN_OUTPUT_PATH_SEL1 0x32D
|
||||
#define AD9528_ADDR_EN_OUTPUT_PATH_SEL2 0x32E
|
||||
|
||||
#define AD9528_ADDR_SYSERF_DIV_LSB 0x400
|
||||
#define AD9528_ADDR_SYSERF_DIV_MSB 0x401
|
||||
#define AD9528_ADDR_SYSREF_CTRL3 0x402
|
||||
#define AD9528_ADDR_SYSREF_CTRL4 0x403
|
||||
#define AD9528_ADDR_SYSREF_CTRL5 0x404
|
||||
|
||||
#define AD9528_ADDR_POWERDOWN_CTRL 0x500
|
||||
#define AD9528_ADDR_CH_POWERDOWN1 0x501
|
||||
#define AD9528_ADDR_CH_POWERDOWN2 0x502
|
||||
#define AD9528_ADDR_LDO_ENABLES1 0x503
|
||||
#define AD9528_ADDR_LDO_ENABLES2 0x504
|
||||
#define AD9528_ADDR_STATUS0_CTRL 0x505
|
||||
#define AD9528_ADDR_STATUS1_CTRL 0x506
|
||||
#define AD9528_ADDR_STATUS_OE 0x507
|
||||
#define AD9528_ADDR_STATUS_READBACK0 0x508
|
||||
#define AD9528_ADDR_STATUS_READBACK1 0x509
|
||||
|
||||
/******************************************************************************/
|
||||
/*************************** Types Declarations *******************************/
|
||||
/******************************************************************************/
|
||||
|
||||
///Enum to select what source outputs from each AD9528 output channel
|
||||
typedef enum {
|
||||
AD9528_CHANNEL_DIV = 0, PLL1_OUTPUT = 1, //VCXO frequency
|
||||
SYSREF = 2,
|
||||
INV_PLL1_OUTPUT = 5 //VCXO inverted
|
||||
} ad9528outSourceSel_t;
|
||||
|
||||
///Enum to select what output buffer standard is used for the clock outputs
|
||||
typedef enum {
|
||||
AD9528_LVDS = 0, AD9528_LVDS_BOOST = 1, AD9528_HSTL = 2
|
||||
} ad9528outBufferControl_t;
|
||||
|
||||
///Enum to select the clock input buffer mode.
|
||||
typedef enum {
|
||||
DISABLED = 0, SINGLE_ENDED, NEG_SINGLE_ENDED, DIFFERENTIAL
|
||||
} ad9528RefBuffer_t;
|
||||
|
||||
/// Structure to hold AD9528 PLL1 settings
|
||||
typedef struct {
|
||||
uint32_t refA_Frequency_Hz;
|
||||
uint16_t refA_Divider;
|
||||
ad9528RefBuffer_t refA_bufferCtrl;
|
||||
|
||||
uint32_t refB_Frequency_Hz;
|
||||
uint16_t refB_Divider;
|
||||
ad9528RefBuffer_t refB_bufferCtrl;
|
||||
|
||||
uint32_t vcxo_Frequency_Hz;
|
||||
ad9528RefBuffer_t vcxoBufferCtrl;
|
||||
|
||||
uint16_t nDivider;
|
||||
uint8_t bypassPll1;
|
||||
} ad9528pll1Settings_t;
|
||||
|
||||
/// Structure to hold AD9528 PLL2 settings
|
||||
typedef struct {
|
||||
uint8_t rfDivider; //VCO divider: Valid range (3,4,5)
|
||||
uint8_t n2Divider; //PLL2 N2 Divider
|
||||
uint8_t totalNdiv; //NDiv = 4*Bdiv + Adiv //Bdiv valid range (3 to 63), Adiv valid range (0-3)
|
||||
} ad9528pll2Settings_t;
|
||||
|
||||
/// Structure to hold AD9528 Output Clock settings
|
||||
typedef struct {
|
||||
uint16_t outPowerDown; //bit per output, if 1 power down that output.
|
||||
ad9528outSourceSel_t
|
||||
outSource[14]; //CHANNEL_DIV, PLL1_OUTPUT, SYSREF, INV_PLL1_OUTPUT
|
||||
ad9528outBufferControl_t outBufferCtrl[14]; //LVDS, LVDS_BOOST, HSTL
|
||||
uint8_t outAnalogDelay[14]; //5 bits
|
||||
uint8_t outDigitalDelay[14]; //6 bits, 1/2 clock resolution @ channel div input frequency
|
||||
uint8_t outChannelDiv[14]; //8 bit channel divider
|
||||
uint32_t outFrequency_Hz[14]; //output clock frequency per clock output
|
||||
} ad9528outputSettings_t;
|
||||
|
||||
///Enum to select the SYSREF output source
|
||||
typedef enum {
|
||||
EXTERNAL = 0, EXT_RESAMPLED = 1, INTERNAL = 2
|
||||
} sysrefSourceSelection_t;
|
||||
|
||||
///Enum to choose the SYSREF pattern mode.
|
||||
typedef enum {
|
||||
NSHOT = 0, CONTINUOUS = 1, PRBS = 2, STOP = 3
|
||||
} sysrefPatternMode_t;
|
||||
|
||||
///Enum to select the SYSREF input pin mode.
|
||||
typedef enum {
|
||||
LEVEL_ACTIVE_HIGH = 0, LEVEL_ACTIVE_LOW = 1, RISING_EDGE = 2, FALLING_EDGE = 3
|
||||
} sysrefPinEdgeMode_t;
|
||||
|
||||
///Enum to select the SYSREF output # of pulses for NSHOT mode.
|
||||
typedef enum {
|
||||
ONE_PULSE = 0, TWO_PULSES = 2, FOUR_PULSES = 3, SIX_PULSES = 4, EIGHT_PULSES = 5
|
||||
} sysrefNshotMode_t;
|
||||
|
||||
//Enum to select how a SYSREF is requested.
|
||||
typedef enum {
|
||||
SPI = 0, PIN = 1
|
||||
} sysrefRequestMethod_t;
|
||||
|
||||
/// Structure to hold AD9528 SYSREF output settings
|
||||
typedef struct {
|
||||
sysrefRequestMethod_t sysrefRequestMethod;
|
||||
sysrefSourceSelection_t sysrefSource;
|
||||
sysrefPinEdgeMode_t sysrefPinEdgeMode;
|
||||
ad9528RefBuffer_t sysrefPinBufferMode;
|
||||
sysrefPatternMode_t sysrefPatternMode;
|
||||
sysrefNshotMode_t sysrefNshotMode;
|
||||
uint16_t sysrefDivide;
|
||||
|
||||
} ad9528sysrefSettings_t;
|
||||
|
||||
/// Structure to hold AD9528 SPI settings
|
||||
typedef struct {
|
||||
uint8_t chipSelectIndex; ///< valid 1~8
|
||||
uint8_t writeBitPolarity; ///< the level of the write bit of a SPI write instruction word, value is inverted for SPI read operation
|
||||
uint8_t longInstructionWord; ///< 1 = 16bit instruction word, 0 = 8bit instruction word
|
||||
uint8_t MSBFirst; ///< 1 = MSBFirst, 0 = LSBFirst
|
||||
uint8_t CPHA; ///< clock phase, sets which clock edge the data updates (valid 0 or 1)
|
||||
uint8_t CPOL; ///< clock polarity 0 = clock starts low, 1 = clock starts high
|
||||
uint8_t enSpiStreaming; ///< Not implemented. SW feature to improve SPI throughput.
|
||||
uint8_t autoIncAddrUp; ///< Not implemented. For SPI Streaming, set address increment direction. 1= next addr = addr+1, 0:addr = addr-1
|
||||
uint8_t fourWireMode; ///< 1: Use 4-wire SPI, 0: 3-wire SPI (SDIO pin is bidirectional). NOTE: ADI's FPGA platform always uses 4-wire mode.
|
||||
uint32_t spiClkFreq_Hz; ///< SPI Clk frequency in Hz (default 25000000), platform will use next lowest frequency that it's baud rate generator can create */
|
||||
} ad9528spiSettings_t;
|
||||
|
||||
/// Structure to hold AD9528 settings
|
||||
typedef struct {
|
||||
struct gpio_desc *gpio_resetb;
|
||||
struct gpio_desc *gpio_sysref_req;
|
||||
struct spi_desc *spi_desc;
|
||||
ad9528spiSettings_t *spiSettings;
|
||||
ad9528pll1Settings_t *pll1Settings;
|
||||
ad9528pll2Settings_t *pll2Settings;
|
||||
ad9528outputSettings_t *outputSettings;
|
||||
ad9528sysrefSettings_t *sysrefSettings;
|
||||
} ad9528Device_t;
|
||||
|
||||
typedef struct {
|
||||
struct gpio_init_param *gpio_init_resetb;
|
||||
struct gpio_init_param *gpio_init_sysref_req;
|
||||
struct spi_init_param *spi_init;
|
||||
ad9528spiSettings_t *spiSettings;
|
||||
ad9528pll1Settings_t *pll1Settings;
|
||||
ad9528pll2Settings_t *pll2Settings;
|
||||
ad9528outputSettings_t *outputSettings;
|
||||
ad9528sysrefSettings_t *sysrefSettings;
|
||||
} ad9528Init_t;
|
||||
|
||||
typedef enum {
|
||||
SYSREF_CONT_ON,
|
||||
SYSREF_CONT_OFF,
|
||||
SYSREF_PULSE,
|
||||
} ad9528SysrefReqMode_t;
|
||||
|
||||
/******************************************************************************/
|
||||
/************************ Functions Declarations ******************************/
|
||||
/******************************************************************************/
|
||||
|
||||
//Helper function to setup the 9528 device data structure specifically for the ADI transceiver evaluation cards
|
||||
int32_t AD9528_initDeviceDataStruct(ad9528Device_t *device,
|
||||
uint32_t vcxoFrequency_Hz, uint32_t refAFrequency_Hz,
|
||||
uint32_t outputDeviceClock_Hz);
|
||||
int32_t AD9528_spiReadByte(ad9528Device_t *device, uint16_t addr,
|
||||
uint8_t *readdata);
|
||||
|
||||
//AD9528 API functions
|
||||
int32_t AD9528_hasTimeoutExpired();
|
||||
int32_t AD9528_resetDevice(ad9528Device_t *device);
|
||||
int32_t AD9528_init(ad9528Device_t **device, const ad9528Init_t *init_device);
|
||||
int32_t AD9528_config(ad9528Device_t
|
||||
*device); //writes all registers to desired values, leaves clock outputs off.
|
||||
int32_t AD9528_setupSYSREF(ad9528Device_t *device,
|
||||
uint16_t divideFromPll1Out, sysrefPatternMode_t sysrefPatternMode,
|
||||
sysrefNshotMode_t nShotPulses);
|
||||
int32_t AD9528_requestSysref(ad9528Device_t *device, uint8_t enableSYSREF);
|
||||
int32_t AD9528_sysrefReq(ad9528Device_t *device,
|
||||
ad9528SysrefReqMode_t mode);
|
||||
int32_t AD9528_remove(ad9528Device_t *device);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user