/** * \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, for more information see the "LICENSE.txt" file in this zip file. * */ #include "ad9528.h" /** * \brief Performs a hard reset on the AD9528 DUT * * Dependencies * - 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 */ ADI_ERR AD9528_resetDevice(ad9528Device_t *device) { /* toggle RESETB on device with matching spi chip select index */ CMB_hardReset(device->spiSettings->chipSelectIndex); return ADIERR_OK; } /** * \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. * * Dependencies * - 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 */ ADI_ERR AD9528_setSpiSettings(ad9528Device_t *device) { //device->spiSettings->enSpiStreaming 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; } CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_ADI_SPI_CONFIG_A, spiReg); if (device->spiSettings->enSpiStreaming > 0) { /* Allow SPI streaming mode: SPI message ends when chip select deasserts */ CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_ADI_SPI_CONFIG_B, 0x00); } else { /* Force single instruction mode */ CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_ADI_SPI_CONFIG_B, 0x80); } return ADIERR_OK; } /** * \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. * * Dependencies * - 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 */ ADI_ERR 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; // /******************************************************************* // * 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 ADIERR_INV_PARM; // } // // 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 ADIERR_INV_PARM; // } // else // { // chDiv = (uint16_t)(chDivU64); // } // // validPll2DivSetting = 1; // break; // } // } // // if (validPll2DivSetting == 1) // { // break; // } // } // // if (validPll2DivSetting == 0) // { // return ADIERR_INV_PARM; // } // // device->outputSettings->outPowerDown = (uint16_t)(~0x300A); //enable outputs 13,12,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[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 = NSHOT; //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 = SPI; //SPI=0,PIN=1 // device->sysrefSettings->sysrefSource = INTERNAL; //EXTERNAL=0, EXT_RESAMPLED=1, INTERNAL=2 return ADIERR_OK; } /** * \brief Debug function to print out summary of AD9528 setup * * \param device AD9528 Device data structure describing setup of device. * * \return Returns enum ADI_ERR, 0=pass, >0 = fail ***********************************************************************************/ ADI_ERR AD9528_reportClockRates(ad9528Device_t *device) { return ADIERR_OK; } //debug function to help verify data structure is setup correctly. /** * \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 * * Dependencies * - All parameters in device structure * * \param device is structure pointer to AD9528 clock device structure * * \return Returns enum ADI_ERR, 0=pass, >0 = fail */ ADI_ERR AD9528_initialize(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=0; uint8_t i = 0; uint8_t j = 0; uint8_t reg508; ADI_ERR retval = ADIERR_OK; CMB_writeToLog(ADIHAL_LOG_MESSAGE, device->spiSettings->chipSelectIndex, ADIERR_OK, "\n// AD9528_initialize\n"); //CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_ADI_SPI_CONFIG_A, 0x18); //[4] and [3] =1 enable 4 wire SPI //CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_ADI_SPI_CONFIG_B, 0x00); retval = AD9528_setSpiSettings(device); if (retval != ADIERR_OK) { return retval; } /********************************************************************** * Configure PLL1 ********************************************************************** */ CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_REF_A_DIVIDER_LSB, device->pll1Settings->refA_Divider & 0xFF); //REF A div[7:0] CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_REF_A_DIVIDER_MSB, (device->pll1Settings->refA_Divider >> 8) & 0x03); //REF A div[9:8] CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_REF_B_DIVIDER_LSB, device->pll1Settings->refB_Divider & 0xFF); //REF B div[7:0] CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_REF_B_DIVIDER_MSB, (device->pll1Settings->refB_Divider >> 8) & 0x03); //REF B div[9:8] CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_PLL1_N_DIV_LSB, device->pll1Settings->nDivider & 0xFF); //PLL1 N div[7:0] CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_PLL1_N_DIV_MSB, (device->pll1Settings->nDivider >> 8) & 0x03); //PLL1 N div[9:8] CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_PLL1_CHARGEPUMP, 0x0A); //PLL1 Charge Pump control 1 CMB_SPIWriteByte(device->spiSettings, 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 } CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_INPUT_RECEIVERS1, reg108); //Input receiver settings (single ended or differential clock inputs) CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_INPUT_RECEIVERS2, reg109); //Input receiver settings CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_INPUT_RECEIVERS3, 0x0A); //Reference selection mode CMB_SPIWriteByte(device->spiSettings, 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) CMB_writeToLog(ADIHAL_LOG_ERROR, device->spiSettings->chipSelectIndex, ADIERR_INV_PARM, "\n// AD9528 PLL2 A Divider out of range (0 to 3)\n"); return ADIERR_INV_PARM; } if (pll2_Bdiv < 3 || pll2_Bdiv > 63) { CMB_writeToLog(ADIHAL_LOG_ERROR, device->spiSettings->chipSelectIndex, ADIERR_INV_PARM, "\n// AD9528 PLL2 B Divider out of range (3 to 63)\n"); return ADIERR_INV_PARM; } CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_PLL2_CHARGEPUMP, 0xE6); //PLL2 Charge Pump[7:0] CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_PLL2_N_DIV, ((pll2_Adiv & 0x3) << 6) | (pll2_Bdiv & 0x3F)); //PLL2 A and B divider CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_PLL2_CTRL, 0x03); //PLL2 control CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_PLL2_VCO_CTRL, 0x01); //Manual VCO CAL if (device->pll2Settings->rfDivider < 3 || device->pll2Settings->rfDivider > 5) { CMB_writeToLog(ADIHAL_LOG_ERROR, device->spiSettings->chipSelectIndex, ADIERR_INV_PARM, "\n// AD9528 PLL2 RF Divider out of range (3,4, or 5)\n"); return ADIERR_INV_PARM; } CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_PLL2_VCO_DIV, device->pll2Settings->rfDivider); //VCO divider CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_PLL2_LF_CTRL1, 0x3A); //PLL2 Loop filter [7:0] CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_PLL2_LF_CTRL2, 0x00); //PLL2 Loop filter [8] CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_PLL2_RDIV, 0x00); //R divider CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_PLL2_REPLICA_CHDIV, (device->pll2Settings->totalNdiv / device->pll2Settings->rfDivider) -1 ); //Replica channel divider: Ndivider = rfDivider * R divider CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_PLL2_REPLICA_DIV_PHASE, 0x00); //Replica Channel Divider phase /********************************************************************** * Configure Output Distribution ********************************************************************** */ for (i=0; i<14; i=i+1){ CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_CH_OUT0_CTRL1 + j, ((device->outputSettings->outSource[i] & 0x7) << 5) | (device->outputSettings->outAnalogDelay[i] & 0x1F)); CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_CH_OUT0_CTRL2 + j, ((device->outputSettings->outBufferCtrl[i] & 0x3) << 6) | (device->outputSettings->outDigitalDelay[i] & 0x3F)); CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_CH_OUT0_CHDIV + j, device->outputSettings->outChannelDiv[i]-1); j = j+3; } CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_OUTPUT_SYNC, 0x00); //Sync outputs CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_MASK_SYNC1, 0x00); //Mask SYNC[7:0] CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_MASK_SYNC2, 0x00); //Mask SYNC[13:8] CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_EN_OUTPUT_PATH_SEL1, 0x00); //Bypass SYSREF resample [6:0], bit0:Enable VCXO to feed output distrubution CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_EN_OUTPUT_PATH_SEL2, 0x00); //Bypass SYSREF resample [13:7] /********************************************************************** * Configure SYSREF operation ********************************************************************** */ CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_SYSERF_DIV_LSB, device->sysrefSettings->sysrefDivide & 0xFF); //SYSREF divider [7:0] CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_SYSERF_DIV_MSB, (device->sysrefSettings->sysrefDivide >> 8) & 0xFF); //SYSREF divider [7:0] CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_SYSREF_CTRL3, ((device->sysrefSettings->sysrefRequestMethod & 0x1) << 7) | ((device->sysrefSettings->sysrefPinEdgeMode & 0x3) << 5) ); CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_SYSREF_CTRL4, ((device->sysrefSettings->sysrefSource & 0x03) << 6) | ((device->sysrefSettings->sysrefPatternMode & 0x3) << 4 ) | ((device->sysrefSettings->sysrefNshotMode & 0x7) << 1) ); if(device->sysrefSettings->sysrefPinBufferMode == DISABLED) { CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_SYSREF_CTRL5, 0x04); //power down input buffer for external SYSREF } else if(device->sysrefSettings->sysrefPinBufferMode == SINGLE_ENDED) { CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_SYSREF_CTRL5, 0x00); //enable positive singled ended input } else if(device->sysrefSettings->sysrefPinBufferMode == NEG_SINGLE_ENDED) { CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_SYSREF_CTRL5, 0x02); //enable negative singled ended input } else if(device->sysrefSettings->sysrefPinBufferMode == DIFFERENTIAL) { CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_SYSREF_CTRL5, 0x01); //enable differential input buffer } else { CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_SYSREF_CTRL5, 0x04); //power down input buffer for external SYSREF } /********************************************************************** * Power down control ********************************************************************** */ if (device->pll1Settings->bypassPll1 > 0) { CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_POWERDOWN_CTRL, 0x14);// power up PLL2 and output distribution, power down PLL1 } else { CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_POWERDOWN_CTRL, 0x10);// power up PLL1, PLL2 and output distribution } CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_CH_POWERDOWN1, device->outputSettings->outPowerDown & 0xFF); //power down out[7:0] CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_CH_POWERDOWN2, ((device->outputSettings->outPowerDown >> 8) & 0x3F)); //power down out[13:8] CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_LDO_ENABLES1, 0xFF); //LDO enable per output channel [7:0] CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_LDO_ENABLES2, 0xFF); //LDO enable per output channel [15:8] CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_STATUS0_CTRL, 0x07); //STATUS 0 outputs REFA present CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_STATUS1_CTRL, 0x01); //STATUS 1 outputs PLL1 and PLL2 locked CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_STATUS_OE, 0x0C);// Make Status0 and Status1 outputs after reset. CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_IO_UPDATE, 0x01); //IO update to make all these settings valid in the 9528 CMB_setTimeout_ms(1000); do{ CMB_SPIReadByte(device->spiSettings, AD9528_ADDR_STATUS_READBACK0, ®508); if(CMB_hasTimeoutExpired() > 0) { return ADIERR_FAILED; } }while((reg508 & 0xA2) != 0xA2); return ADIERR_OK; } /// 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. * Dependencies * - 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 ***********************************************************************************/ ADI_ERR AD9528_requestSysref(ad9528Device_t *device, uint8_t enableSYSREF){ uint8_t _enableSYSREF=0; if (device->sysrefSettings->sysrefPatternMode == NSHOT || enableSYSREF > 0) { _enableSYSREF = 1; } else { _enableSYSREF = 0; } CMB_SPIWriteField(device->spiSettings, AD9528_ADDR_SYSREF_CTRL4, _enableSYSREF, 0x01, 0x00); //Set SPI SYSREF request bit CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_IO_UPDATE, 0x01); //IO update to make all these settings valid in the 9528 return ADIERR_OK; } /// 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. * * Dependencies * - 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 ***********************************************************************************/ ADI_ERR 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 CMB_SPIReadByte(device->spiSettings, AD9528_ADDR_SYSREF_CTRL4, ®403); reg403 = reg403 & ~0x01; //clear bit 0 to stop a continuous sysref if it is on. CMB_SPIWriteByte(device->spiSettings, 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); CMB_SPIWriteByte(device->spiSettings, 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 CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_IO_UPDATE, 0x01); //IO update to make all these settings valid in the 9528 return ADIERR_OK; } /// Update the AD9528 clock outputs that are enabled. /*******************************************************************************//** * 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. * * Dependencies * - device->spiSettings * * \param device is structure pointer to AD9528 clock device structure * \param clkEnable Sets which clock outputs are enabled. Bit per clock output. bit0 = out0, etc. * * \return Returns enum ADI_ERR, 0=pass, >0 = fail ***********************************************************************************/ ADI_ERR AD9528_enableClockOutputs(ad9528Device_t *device, uint16_t clkEnable) { //clkEnable: bit per clock output device->outputSettings->outPowerDown = ~clkEnable; CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_CH_POWERDOWN1, device->outputSettings->outPowerDown & 0xFF); //power down out[7:0] CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_CH_POWERDOWN2, ((device->outputSettings->outPowerDown >> 8) & 0x3F)); //power down out[13:8] CMB_SPIWriteByte(device->spiSettings, AD9528_ADDR_IO_UPDATE, 0x01); //IO update to make all these settings valid in the 9528 return ADIERR_OK; } /// Waits for PLL1 and PLL2 to lock and the REFA and VCXO clocks to be present /*******************************************************************************//** * Waits until Reference A clock and VCXO clock input to be detected (present), * and PLL1 and PLL2 to lock. Will timeout after 1 second and return ADIERR_FAILED. * If clock are detected and PLLs locked, returns ADIERR_OK. * * Dependencies * - device->spiSettings * * \param device is structure pointer to AD9528 clock device structure * * \return Returns enum ADI_ERR, 0=pass, >0 = fail ***********************************************************************************/ ADI_ERR AD9528_waitForPllLock(ad9528Device_t *device) { uint8_t reg508; CMB_setTimeout_ms(1000); do{ CMB_SPIReadByte(device->spiSettings, AD9528_ADDR_STATUS_READBACK0, ®508); if(CMB_hasTimeoutExpired() > 0) { return ADIERR_FAILED; } }while((reg508 & 0x27) != 0x27); return ADIERR_OK; } /// Reads the Lock Status of the PLLs and present detect of the reference clocks /*******************************************************************************//** * NOTE: This is a non blocking function that reads a register to see if PLL1 * and PLL2 are locked and Reference clock A and the VCXO input clock are * detected (present). If the *status parameter is not NULL, the status will * be returned. If *status is NULL, the error return value will denote if * the PllStatus is locked or not with ADIERR_OK or ADIERR_FAILED. * * Dependencies * - device->spiSettings * * \param device is structure pointer to AD9528 clock device structure * * \return Returns enum ADI_ERR, 0=pass, >0 = fail ***********************************************************************************/ ADI_ERR AD9528_readPllStatus(ad9528Device_t *device, uint8_t *status) { uint8_t reg508; //bit5 is VCXO clock detected //bit2 is REFCLK A detected //bit1 is PLL2 lock detect //bit0 is PLL1 lock detect /* read twice to refresh status bits */ CMB_SPIReadByte(device->spiSettings, AD9528_ADDR_STATUS_READBACK0, ®508); CMB_SPIReadByte(device->spiSettings, AD9528_ADDR_STATUS_READBACK0, ®508); /* if status is a valid pointer, return the PLL status */ if (status > 0) { *status = reg508; } /* PLLs 1 and 2 are locked and VCXO and REFA detected if reg508 has bits 5,2,1 and 0 set */ if((reg508 & 0x27) == 0x27) return ADIERR_OK; else return ADIERR_FAILED; }