LTC6951 - Ultralow Jitter Multi-Output Clock Synthesizer with Integrated VCO

Features

  • Low Noise Integer-N PLL with Integrated VCO
  • Output Jitter:
  • 90fs RMS (12kHz to 20MHz)
  • 115fs RMS (ADC SNR Method)
  • Noise Floor = –165dBc/Hz at 250MHz
  • EZSync™, ParallelSync™ Multichip Synchronization
  • SYSREF Generation for JESD204B, Subclass 1
  • Output Frequency Range:
  • 1.95MHz to 2.5GHz (LTC6951)
  • 2.1MHz to 2.7GHz (LTC6951-1)
    • –229dBc/Hz Normalized In-Band Phase Noise Floor
    • –277dBc/Hz Normalized In-Band 1/f Noise
  • Five Independent, Low Noise Outputs
  • Reference Input Frequency up to 425MHz
  • LTC6951Wizard™ Software Design Tool Support
  • –40°C to 105°C Operating Junction Temperature Range
  • LTC6951Wizard Design Tool

Typical Application

LTC6951 Typical Application
LTC6951 Typical Application

Description

The LTC®6951 is a high performance, low noise, Phase Locked Loop (PLL) with a fully integrated VCO. The low noise VCO uses no external components and is internally calibrated to the correct output frequency with no external system support.

The clock generation section provides five outputs based on the VCO prescaler signal with individual dividers for each output. Four outputs feature very low noise, low skew CML logic. The fifth output is low noise LVDS. All outputs can be synchronized and set to precise phase alignment using the programmable delays.

Choose the LTC6951-1 if any desired output frequency falls in the ranges 2.5GHz to 2.7GHz, 1.66GHz to 1.8GHz, or 1.25GHz to 1.35GHz. Choose the LTC6951 for all other frequencies.

Packaging

CAD Symbols and Footprints: The downloadable Zip file below contains the schematic symbol and PCB footprints.

For complete and up to date package information and drawings, please refer to our packaging page

Part Number Package Code Temp Package
Drawing
RoHS
LTC6951IUHF-1#PBF 5x7 QFN-40 UHF I 05-08-1951 Yes
LTC6951IUHF-1#TRPBF 5x7 QFN-40 UHF I 05-08-1951 Yes
LTC6951IUHF#PBF 5x7 QFN-40 UHF I 05-08-1951 Yes
LTC6951IUHF#TRPBF 5x7 QFN-40 UHF I 05-08-1951 Yes


LTC6951 Package Drawing

Order Info

  • Part numbers ending in PBF are lead free. Solder plated terminal finish (SnPb) versions are non-standard and special terms and conditions and pricing applies if available. Please contact LTC marketing for information.
  • Part numbers containing TR or TRM are shipped in tape and reel or 500 unit mini tape and reel, respectively
  • Please refer to our general ordering information or the product datasheet for more details

Package Variations and Pricing

Part Number Package Temp Price
(1-99)
Price
(1k)*
RoHS
LTC6951IUHF-1#PBF 5x7 QFN-40 I $12.50 $8.75 Yes
LTC6951IUHF-1#TRPBF 5x7 QFN-40 I $12.56 $8.81 Yes
LTC6951IUHF#PBF 5x7 QFN-40 I $12.50 $8.75 Yes
LTC6951IUHF#TRPBF 5x7 QFN-40 I $12.56 $8.81 Yes
Buy NowRequest Samples
* The USA list pricing shown is for BUDGETARY USE ONLY, shown in United States dollars (FOB USA per unit for the stated volume), and is subject to change. International prices may differ due to local duties, taxes, fees and exchange rates. For volume-specific price or delivery quotes, please contact your local Linear Technology sales office or authorized distributor.

Demo Boards

Linear Technology offers many demo boards free of charge to qualified customers. Contact your local sales office or distributor to inquire about a demo board. Certain demo boards are also available for sale via credit card on this website. Demo boards are for evaluation purposes only. It remains the customer’s responsibility to verify proper and reliable operation in the actual end application.

Part Number Description Price Documentation
DC2226A-A 4-Channel 14-bit 250Msps JESD204B ADC with Clocking [Featuring LTC2123, LTC6951] $300.00
DC2248A-A LTC6951 Demo | 5-Output Integer-N PLL with Integrated VCO, Output Range: 1.95MHz to 2.5GHz (requires DC590 or DC2026) $300.00
DC2248A-B LTC6951-1 Demo | 5-Output Integer-N PLL with Integrated VCO, Output Range: 2.1MHz to 2.7GHz (requires DC590 or DC2026) $300.00
Buy Now

Companion Boards

Part Number Description Price Documentation
DC2159A FMC to USB Adapter to Interface with PScope $50.00
DC2430A DC2430A Linduino SPI 1:8 Expander Demonstration Board (DC2026 Included) Contact Factory
DC2026C Linduino One Isolated USB Demo Board: An Arduino- and QuikEval-Compatible Code Development Platform $75.00
DC590B Isolated USB Serial Controller for Linear Technology QuikEval-Compatible Demo Boards $50.00
Buy Now
Click here to view our complete list of demo boards

Applications

  • High Performance Data Converter Clocking
  • Wireless Infrastructure
  • Test and Measurement

Product Notifications

Please login to your MyLinear account for notifications of datasheet updates, new document releases and LTspice model announcements for your favorite products. If you do not have a MyLinear account you may Sign Up Now.

Forgot your password? Click here.
Need help? Email mylinear@linear.com with questions and comments.

Design Tools

LTC6951Wizard

The LTC6951Wizard software provides appropriate LTC6951 PLL settings and loop filter component values with a click of a button, and accurately predicts the individual output’s phase noise and jitter. Besides performance simulation, the LTC6951Wizard GUI features a scope plot that simulates time domain results of the LTC6951 outputs based on the clock divider, delay and synchronization settings, simplifying the design process and assisting in the circuit debugging phase.

The LTC6951Wizard GUI is also used to communicate with the LTC6951 situated on the DC2248, the demo board of the LTC6951. It uses the DC590 (or the DC2026 emulating the DC590) to communicate with the PC.

The following list summarizes its uses:

  • Read and write all device registers
  • Determine each register’s contents
  • Recommend part parameters based on the given frequency plan
  • Design noise-optimized loop filters
  • Simulate loop frequency response and stability
  • Simulate VCO and Reference source noise
  • Simulate output noise characteristics and statistics
  • Simulate output timing and delays

Click Here to Download LTC6951Wizard 

Linduino

Linduino is an Arduino compatible platform for developing and distributing firmware libraries and code for SPI and I²C-compatible integrated circuits. The Linduino One board interfaces to more than 300 QuikEval demonstration cards, supporting a variety of product types including analog-to-digital converters (ADCs)digital-to-analog converters (DACs)power monitors, and more. Firmware libraries for individual devices are written in C and designed to be portable to a wide variety of processors and microcontrollers. Each library has a demonstration program that can be uploaded to the Linduino One platform to allow the circuit and software to be quickly and easily verified.

Click here for more information on Linduino

Code

Linduino is Linear Technology's Arduino compatible system for developing and distributing firmware libraries and example code for Linear Technology’s integrated circuits. The code below can be downloaded or copied and pasted into your project. Please visit the Linduino Home Page for demo board, manual and setup information.

This part is Code Supported: There is example code available for this part. The code below may rely on other drivers available in the full library.

Download LTC6951 - DC2248A.ino

/*!
DC2248A
LTC6951: Ultra-Low Jitter 2MHz to 2.7GHz Multi-Output Clock Synthesizer with Integrated VCO

@verbatim

  Setup:
    Set the terminal baud rate to 115200 and select the newline terminator.
    Refer to Demo Manual DC2248A.
    Ensure all jumpers are installed in the factory default positions.
    One Power Supply is needed for this demo board: 6V supply.


Command Description:
    1-  Load Default Settings from a Look-up Table- Loads the SPI map from one of 10 options in a look-up table.
        The look-up table allows the user to select different frequencies and synchronization option.  All options
        assume a 100MHz reference input and a default DC2248A BOM.

        ** If you want to use a different loop filter, reference frequency or different
        register settings.  Please use LTC6951Wizard for the loop filter design and initial
        device setup.  The register settings from LTC6951Wizard can be entered into menu option 2
        or you can change the default registers in the LTC6951_lkup_tbl array.  After the
        LTC6951Wizard settings are created, export the register settings by selecting
        File --> Export Register Settings from the TLC6951Wizard Menu.

    2-  READ/WRITE to Registers Addresses- Selecting this option will cause all the registers to
        be read, stored to variables, and displayed.  The user will then have the option
        to write to one register address at a time.

    3-  READ/WRITE to Registers Fields- Selecting this option will allow the user
        to read or write to one register field name at a time.

    4-  This function stores the current SPI settings in the demo boards EEPROM

    5-  This function loads SPI settings from the demo boards EEPROM to the device

    6-  Frequency chnage Timing test (SPI write + Calibration + Settling Time):
        Provides two frequency settings and allows user to toggle between the two frequencies by
        changing the N-divider(ND) value.  The user can monitor the STATUS pin.  The user
        can trigger off the STATUS or CS pins. In most cases the user will want to set the SPI
        frequency to 8MHz (see menu option 7)

     7- SPI Frequency:  Allows user to control Linduino's SPI frequency.  By default this is set to 8MHz
        when connecting Linduino directly to the DC2248.  If Linduino is connected to the DC2430 the default
        SPI rate is 4MHz

     8- If using a DC2430 (Linduino Expander), this allows the user to select 1 of 8 sites to talk to.

USER INPUT DATA FORMAT:
 decimal : 1024
 hex     : 0x400
 octal   : 02000  (leading 0 "zero")
 binary  : B10000000000
 float   : 1024.0

@endverbatim

http://www.linear.com/product/LTC6951

http://www.linear.com/product/LTC6951#demoboards

REVISION HISTORY
$Revision: 4818 $
$Date: 2016-03-24 11:37:16 -0700 (Thu, 24 Mar 2016) $

Copyright (c) 2013, Linear Technology Corp.(LTC)
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of Linear Technology Corp.

The Linear Technology Linduino is not affiliated with the official Arduino team.
However, the Linduino is only possible because of the Arduino team's commitment
to the open-source community.  Please, visit http://www.arduino.cc and
http://store.arduino.cc , and consider a purchase that will help fund their
ongoing work.
*/

/*! @file
    @ingroup LTC6951
*/

#include <Arduino.h>
#include <stdint.h>
#include "Linduino.h"
#include "LT_SPI.h"
#include "UserInterface.h"
#include "LT_I2C.h"
#include "QuikEval_EEPROM.h"
#include "LTC6951.h"
#include <SPI.h>
#include <Wire.h>

// Function Declaration
void print_title();             // Print the title block
void print_prompt();            // Print the main menu
void menu_1_load_default_settings();  // Sub-menus
void menu_2_RW_to_reg_addresss();
void menu_3_RW_to_reg_field();
void menu_4_store_settings();
void menu_5_restore_settings();
void menu_6_multibytewrite();
void menu_7_SPI_speed();
void menu_8_DC2430_site_select();
//void DUTsync();

// Global Variables
static int8_t demo_board_connected = 0;    //!< Demo Board Name stored in QuikEval EEPROM
static int8_t dc2248_board_connected = 0;  //!< Demo Board Name stored in QuikEval EEPROM
uint8_t First_Run=0;                       //!< if first time through loop = 0, otherwise=1
boolean Using_DC2430= false;               //!< Indicator if DC2430 (Linduino Expander) is connected

// Analog Pins used on DC2430 (Linduino Expander)
int dc2430_site_A0_APin =0;                //!< DC2430 resistor tree, if it reads 3.3V then DC2430 connected
int dc2430_site_A1_APin =1;                //!< DC2430 resistor tree, if it reads 1.66V then DC2430 connected
int dc2430_site_A2_APin =2;                //!< DC2430 INFO ONLY, measures DC2226 (Linduino) VCCIO voltage

// Digital Pins used on DC2430 (Linduino Expander)
int dc2430_site_DA0_Pin =2;                //!< DC2430 digital pin allows Linduino to control which DC2430 site is selected
int dc2430_site_DA1_Pin =3;                //!< DC2430 digital pin allows Linduino to control which DC2430 site is selected
int dc2430_site_DA2_Pin =4;                //!< DC2430 digital pin allows Linduino to control which DC2430 site is selected
int dc2430_en_Pin =5;                      //!< DC2430 digital pin enables Linduino to control which DC2430 site is selected & turns on DC2430 LED

/* ------------------------------------------------------------------------- */
//! Initialize Linduino
//! @return void
void setup()
{
  char demo_name[] = "DC2248";    // Demo Board Name stored in QuikEval EEPROM
  uint8_t data;
  int site_select=0;
  int dc2430_A0_val=0;
  int dc2430_A1_val=0;
  int dc2430_A2_val=0;
  float dc2430_A0_fval=0;
  float dc2430_A1_fval=0;
  float dc2430_A2_fval=0;

  Using_DC2430= false;

  quikeval_SPI_init();      //! Configure the spi port for 500kHz SCK
  quikeval_SPI_connect();   //! Connect SPI to main data port
  quikeval_I2C_init();      //! Configure the EEPROM I2C port for 100kHz
  Serial.begin(115200);     //! Initialize the serial port to the PC
  LTC6951_init();
  print_title();

//! **************** DETECT IF USING DC2430 SECTION ******************
//! this checks to see if a DC2026 (linduino) to DC2430 (linduino expander)
//! by measuring 3 analog voltages
//! if a DC2430 is connect then read A0, A1 , A2
  dc2430_A0_val = analogRead(dc2430_site_A0_APin);
  dc2430_A0_fval = (5.0*dc2430_A0_val)/1023;

  dc2430_A1_val = analogRead(dc2430_site_A1_APin);
  dc2430_A1_fval = (5.0*dc2430_A1_val)/1023;


//! If DC2430 located, auto search site 0 to 7 until a DC2248 is found
  if ((dc2430_A0_fval>3.0) && (dc2430_A0_fval<3.6) && (dc2430_A1_fval>1.5) && (dc2430_A1_fval<1.8))
  {
    Serial.println(F("--- DC2430 Connected ---"));
    Using_DC2430= true;
    pinMode(dc2430_site_DA0_Pin, OUTPUT);
    pinMode(dc2430_site_DA1_Pin, OUTPUT);
    pinMode(dc2430_site_DA2_Pin, OUTPUT);
    pinMode(dc2430_en_Pin, OUTPUT);      // sets the digital pin as output
    dc2248_board_connected =0;
    demo_board_connected = 0;

    digitalWrite(dc2430_en_Pin, HIGH);   // sets the LED on
    site_select=0;
    do
    {
      dc2430_site_select(site_select);
      delay(150);  //! probably could be shorter, but this allows the eyes to track which site is being tested
      demo_board_connected = discover_demo_board_local(demo_name);  //! Checks if any demo board is connected.
      if (demo_board_connected)
      {
        Serial.println(F("\n********************************************************"));
        dc2248_board_connected = discover_demo_board(demo_name);  //! Checks if correct demo board is connected.

        if (dc2248_board_connected)
        {
          dc2430_A2_val = analogRead(dc2430_site_A2_APin);
          dc2430_A2_fval = (5.0*dc2430_A2_val)/1023;
          Serial.print(F("Linduino's JP3 VCCIO voltage = "));
          Serial.println(dc2430_A2_fval);     //! if DC2430 connect, VCCIO voltage set by JP3 on DC2026
          spi_enable(SPI_CLOCK_DIV4);  //! 1) Configure the spi port for 4MHz SCK
          Serial.println(F("Linduino's SPI Frequency = 4MHz, max DC2430 SPI speed"));
          Serial.print(F("\nDC2248 FOUND ON DC2430 SITE "));
          Serial.println(site_select);
          site_select=15;
        }
      }

      if (!dc2248_board_connected)
      {
        Serial.print(F("\nDC2248 NOT FOUND ON DC2430 SITE "));
        Serial.print(site_select);
        site_select++;
      }
    }
    while (site_select<8);

    //! if no DC2248A was found.  Return dc2430_en_Pin to default state (LOW)
    if (!dc2248_board_connected)
    {
      digitalWrite(dc2430_en_Pin, LOW);   // sets the LED on
      Serial.println(F("\n\nConnect DC2248 demo board to DC2430, then press the reset button."));
    }
  }
  else
  {
    //! If DC2430 is not located, check to see if Linduino is connected directly to the DC2248
    //! most common use case
    Serial.println(F("DC2248A CONNECTED TO LINDUINO"));
    dc2248_board_connected = discover_demo_board(demo_name);  //! Checks if correct demo board is connected.
    if (dc2248_board_connected)
    {
      Serial.println(F("Linduino's SPI Frequency = 8MHz, max speed"));
      spi_enable(SPI_CLOCK_DIV2);  //! 1) Configure the spi port for 8MHz SCK
    }
  }

  if (dc2248_board_connected)
  {
    print_prompt();
  }
} // end of setup()


/* ------------------------------------------------------------------------- */
//! Repeats Linduino loop
//! @return void
void loop()
{
  uint16_t user_command;          // User input command

  if (Serial.available())          // Check for user input
  {
    if (First_Run==0)
    {
      First_Run=1;
    }

    user_command = read_int();  //! Reads the user command
    if (user_command != 'm')
      Serial.println(user_command);

    switch (user_command)       //! Prints the appropriate submenu
    {
      case 1:
        menu_1_load_default_settings();
        break;

      case 2:
        menu_2_RW_to_reg_addresss();
        break;

      case 3:
        menu_3_RW_to_reg_field();
        break;

      case 4:
        menu_4_store_settings();
        break;

      case 5:
        menu_5_restore_settings();
        break;

      case 6:
        menu_6_multibytewrite();
        break;

      case 7:
        menu_7_SPI_speed();
        break;

      case 8:
        menu_8_DC2430_site_select();
        break;


      default:
        Serial.println(F("Incorrect Option"));
        break;
    } // end of switch statement
    Serial.println(F("\n*****************************************************************"));
    print_prompt();
  } // end of if statement
} // end of loop()

// Function Definitions
/* ------------------------------------------------------------------------- */
//! Menu 1: Load Default SPI Register Settings
//!  This function loads the register settings from a look up table.
//!  The setting loaded with this function assume the LTC6951's
//!  reference is set to 100MHz and the DC2248A's BOM has not been modified.
//!  The settings in the look up table were created using the LTC6951Wizard.
//!  It is recommended to use the LTC6951Wizard to create the register settings for
//!  all desired frequency plans and enter these frequency plans into a look-up table.
//!  The LTC6951Wizard generates register values for optimal LTC6951 performance.
//! @return void
void menu_1_load_default_settings()
{

  uint8_t  field_num;
  long field_val;

  field_val=999L;
  field_num=1;
// Read/Write loop, can exit loop by choosing 'm'
  while  (field_num != 0)
  {
    Serial.print(F("\n*****************************************************************"));
    Serial.print(F("\n*************    Select LTC6951 Configuration   *****************"));
    Serial.println(F("\n*****************************************************************"));

    // Select Fields to read and write to
    Serial.print(F("1-  OUT[4:0] = 600MHZ, EZSync\n"));
    Serial.print(F("2-  OUT[4:0] = 600MHZ, EZSync Ref Aligned\n"));
    Serial.print(F("3-  OUT[4:0] = 600MHZ, EZParallelSync\n"));
    Serial.print(F("4-  OUT[4:0] = 600MHZ, ParallelSync\n"));
    Serial.print(F("5-  OUT[3:0] = 2500MHZ, OUT4:625MHz, EZSync\n"));
    Serial.print(F("6-  OUT[3:0] = 2500MHZ, OUT4:625MHz, EZSync Ref Aligned\n"));
    Serial.print(F("7-  OUT[3:1] = 2400MHZ, OUT0&OUT4 = 600MHz, EZParallelSync\n"));
    Serial.print(F("8-  OUT[3:0] = 2500MHZ, OUT4 = 625MHz, ParallelSync\n"));
    Serial.print(F("9-  OUT[4:0] = Same frequencies as DC2226 U10, JESD204B SC1, ParallelSync\n"));
    Serial.print(F("10- OUT[4:0] = Same frequencies as DC2226 U13, JESD204B SC1, ParallelSync\n"));
    Serial.println(F("0 - Return to Main Menu\n"));

    Serial.println(F("Load LTC6951 register settings from a look up table (Settings created from LTC6951Wizard)"));
    Serial.println(F("Enter a command (1-10), or '0' to return to Main Menu): "));
    field_num = read_int();  //! Reads the user command
    Serial.println(field_num);

    // User input: enter new setting for selected register
    if (field_num != 0)
    {
      field_num=field_num-1;
      set_LTC6951_REGS_lkup_tbl(field_num);
      field_num=field_num+1;
    } // end if user_command != 0 statement
  } // end while loop
} // end menu_1_load_default_settings function


/* ------------------------------------------------------------------------- */
//! Menu 2: Reads and/or Writes the SPI register address
//!  This function reads and displays all SPI register address settings in HEX format.
//!  It then provides an option to modify(write to) individual registers one at time
//!
//!  EXAMPLE:
//!  - 0- ADDR00 = 0x04 (read only)
//!  - 1- ADDR01 = 0x04
//!  - ....
//!  - 21- ADDR15 = 0x04
//!  - 22- ADDR16 = 0x65 (read only)
//!  - 0 - Return to Main Menu
//!  - Enter a command (1-21 to modify register, or '0' to return to Main Menu):
//! @return void
void menu_2_RW_to_reg_addresss()
{
  uint8_t i, regval, user_regval, num_reg;
  uint16_t user_address;          // User input command

  num_reg = get_LTC6951_REGSIZE();
  user_address=1;
// Read/Write loop, can exit loop by choosing '0'
  while  (user_address != 0)
  {
    Serial.println(F("\n*****************************************************************"));
    // Read All Registers and display results
    for (i=0; i<num_reg; i++)
    {
      regval = LTC6951_read(LTC6951_CS,i);
      Serial.print(i);
      if (i<16)
        Serial.print(F("- ADDR0"));
      else
        Serial.print(F("- ADDR"));
      Serial.print(i, HEX);
      Serial.print(F(" = 0x"));
      if (regval<16) Serial.print(F("0"));
      Serial.print(regval, HEX);
      if (i==3) Serial.print(F(" (warning: if D2=1 it resets all registers. RES6951 Bit)"));
      if ((i==0)||(i==(num_reg-1))) Serial.print(F(" (read only) "));
      Serial.println("");
    }  // end for loop
    Serial.print(F("0 - Return to Main Menu\n\n"));
    // User input: Select which register to modify, or return to main menu
    Serial.print(F("Enter a command (1-19 to modify register, or '0' to return to Main Menu): "));
    user_address = read_int();  //! Reads the user command
    Serial.println(user_address);

    // User input: enter new setting for selected register
    if (user_address >0 && user_address<(num_reg-1))
    {
      Serial.print(F("What value should ADDR"));
      Serial.print(user_address);
      Serial.print(F(" be set to (ex: HEX format 0xff): "));
      user_regval = read_int();  //! Reads the user command
      Serial.println(user_regval);

      // writes new setting to part
      LTC6951_write(LTC6951_CS, (uint8_t)user_address, user_regval);
    } // end if statement
  } // end while loop
}  // end menu_2_RW_to_reg_addresss


/* ------------------------------------------------------------------------- */
//! Support function for function menu_3_RW_to_reg_field
//!  displays current state of select field
//!  provides user the option to write to that field or return to menu
//!  @return field value (user input) that will be written to part
long field_menu_RW(long field_val,       //!< current state of the selected field
                   char field_name[],    //!< SPI Field name selected
                   uint8_t f            //!< SPI field identifier identifies selected fields information in SPI MAP arrays
                  )
{
  long usr_field_val;
  uint8_t field_size, i;
  long max_num=1, pow2=1;

  Serial.print("CURRENT STATE (HEX): ");
  Serial.print(field_name);
  Serial.print("= 0x");
  Serial.println(field_val, HEX);

  if (get_LTC6951_SPI_FIELD_RW(f)==0)
  {
    field_size=get_LTC6951_SPI_FIELD_NUMBITS(f);
    for (i=1; i<field_size; i++)
    {
      pow2=pow2*2;
      max_num=max_num + pow2;
    }

    Serial.print("What value should ");
    Serial.print(field_name);
    Serial.print(" be set to or type '-1' to exit: (ex: HEX format 0x00 to 0x");
    Serial.print(max_num, HEX);
    Serial.print(")");
    usr_field_val = read_int();  //! Reads the user command

    if (usr_field_val>=0 && usr_field_val<=max_num)
    {
      Serial.println(usr_field_val);
      return usr_field_val;
    }
    else
    {
      return field_val;
    } // end of if statement
  } // end of if statement
} // end of field_menu_RW


/* ------------------------------------------------------------------------- */
//! Menu 3: Reads and/or Writes individual SPI fields
//!  This function provides the user with a list of all SPI fields.
//!  The user can select a SPI field to read its current value.
//!  Then the user will be provided with an option to write to that field
//!  or return to the selection menu.
//!
//!  EXAMPLE:
//!  - 1-ALCCAL     25-LKWIN      49-PART *
//!  - 2-ALCEN      26-LOCK *     50-PD
//!  - ....
//!  - 21-DLY4      45-OINV1      69-SYNCEN4
//!  - 23-INVSTAT   47-OINV3      71-x
//!  - 24-LKCT      48-OINV4
//!  - 0 - Return to Main Menu
//!  - * = READ ONLY FIELD
//!  - Enter a command (1-71 to modify register, or '0' to return to Main Menu):
//! @return void
void menu_3_RW_to_reg_field()
{
  uint8_t  field_num;
  long field_val;

  field_val=999L;
  field_num=1;
// Read/Write loop, can exit loop by choosing 'm'
  while  (field_num != 0)
  {
    Serial.println(F("\n*****************************************************************"));
    // Select Fields to read and write to
    Serial.print(F("1-ALCCAL     25-LKWIN      49-PART *\n"));
    Serial.print(F("2-ALCEN      26-LOCK *     50-PD\n"));
    Serial.print(F("3-ALCHI *    27-!LOCK *    51-PDALL\n"));
    Serial.print(F("4-ALCLO *    28-MCO        52-PDOUT\n"));
    Serial.print(F("5-ALCMON     29-MC1        53-PDPLL\n"));
    Serial.print(F("6-ALCULOK    30-MC2        54-PDREFPK\n"));
    Serial.print(F("7-AUTOCAL    31-MC3        55-PDVCO\n"));
    Serial.print(F("8-BD         32-MC4        56-POR\n"));
    Serial.print(F("9-BST        33-MD0        57-RAO\n"));
    Serial.print(F("10-CAL       34-MD1        58-RD\n"));
    Serial.print(F("11-CP        35-MD2        59-REFOK *\n"));
    Serial.print(F("12-CPDN      36-MD3        60-!REFOK *\n"));
    Serial.print(F("13-CPMID     37-MD4        61-REV *\n"));
    Serial.print(F("14-CPRST     38-MUTE0      62-SN\n"));
    Serial.print(F("15-CPUP      39-MUTE1      63-SR\n"));
    Serial.print(F("16-CPWIDE    40-MUTE2      64-SSYNC\n"));
    Serial.print(F("17-DLY0      41-MUTE3      65-SYNCEN0\n"));
    Serial.print(F("18-DLY1      42-MUTE4      66-SYNCEN1\n"));
    Serial.print(F("19-DLY2      43-ND         67-SYNCEN2\n"));
    Serial.print(F("20-DLY3      44-OINV0      68-SYNCEN3\n"));
    Serial.print(F("21-DLY4      45-OINV1      69-SYNCEN4\n"));
    Serial.print(F("22-FILT      46-OINV2      70-UNLOCK *\n"));
    Serial.print(F("23-INVSTAT   47-OINV3      71-x\n"));
    Serial.print(F("24-LKCT      48-OINV4    \n"));

    Serial.print(F("0 - Return to Main Menu\n"));
    Serial.print(F("* = READ ONLY FIELD\n\n"));

    Serial.print(F("Enter a command (1-71 to modify register, or '0' to return to Main Menu): "));
    field_num = read_int();  //! Reads the user command
    Serial.println(field_num);

    // User input: enter new setting for selected register
    if (field_num != 0)
    {
      switch (field_num)        //! Prints the appropriate submenu
      {
        default:
          field_val=get_LTC6951_SPI_FIELD(LTC6951_CS,field_num);    // reads selected field
          field_val=field_menu_RW(field_val," ",field_num);      // user interface control and printout
          if (field_val>-1)
          {
            set_LTC6951_SPI_FIELD(LTC6951_CS, field_num, field_val); // updates selected field
          }
          break;
      }  // end of switch statement
    } // end if user_command != 0 statement
  } // end while loop
}  // end menu_3_RW_to_reg_field function


/* ------------------------------------------------------------------------- */
//! Store PLL settings to nonvolatile EEPROM on demo board
//! @return void
void menu_4_store_settings()
{
// Store the PLL Settings to the EEPROM
  uint8_t regval;

  uint8_t addr_offset;
  uint8_t num_reg;

  addr_offset=2;
  num_reg = get_LTC6951_REGSIZE();

  eeprom_write_int16(EEPROM_I2C_ADDRESS, EEPROM_CAL_KEY, EEPROM_CAL_STATUS_ADDRESS);         // Cal key

  for (uint8_t i = 0; i <= num_reg ; i++)
  {
    regval = LTC6951_read(LTC6951_CS,i);
    eeprom_write_byte(EEPROM_I2C_ADDRESS,(char) regval, EEPROM_CAL_STATUS_ADDRESS+ i+addr_offset);
  }
  Serial.println(F("PLL Settings Stored to EEPROM"));

}

/* ------------------------------------------------------------------------- */
//! Read stored PLL settings from nonvolatile EEPROM on demo board
//! @return void
void menu_5_restore_settings()
{
// Read the PLL settings from EEPROM
  int16_t cal_key;
  uint8_t regval;
  uint8_t user_address;

  uint8_t addr_offset;
  uint8_t num_reg;
  long field_val;
  uint8_t  field_num;

  addr_offset=2;
  num_reg = get_LTC6951_REGSIZE();

// read the cal key from the EEPROM
  eeprom_read_int16(EEPROM_I2C_ADDRESS, &cal_key, EEPROM_CAL_STATUS_ADDRESS);
  if (cal_key == EEPROM_CAL_KEY)
  {
    // PLL Settings has been stored, read PLL Settings
    user_address=2;
    for (uint8_t i = 0; i <= num_reg ; i++)
    {
      eeprom_read_byte(EEPROM_I2C_ADDRESS,(char *) &regval, EEPROM_CAL_STATUS_ADDRESS + i+addr_offset);
      LTC6951_write(LTC6951_CS, (uint8_t)i, regval);
      user_address++;
    }
    Serial.println(F("LTC6951 Settings Restored"));

    Serial.println(F("Calibrate LTC6951 VCO"));
    set_LTC6951_SPI_FIELD(LTC6951_CS, LTC6951_CAL, 1L);
    delay(1); // Calibrations typically ~50us long, CAL bit clears automatically

    Serial.println(F("Syncs LTC6951 OUTPUTS"));
    DUTsync(1);

  }
  else
  {
    Serial.println(F("PLL Settings not found"));
  }

}

/* ------------------------------------------------------------------------- */
//! Syncs LTC6951 Outputs using SSYNC bit
//! @return void
void DUTsync(uint16_t delay_num)
{
  set_LTC6951_SPI_FIELD(LTC6951_CS, LTC6951_SSYNC, 1L);
  delay(1);
  set_LTC6951_SPI_FIELD(LTC6951_CS, LTC6951_SSYNC, 0L);
  delay(delay_num);
}

/* ------------------------------------------------------------------------- */
//! Frequency change Timing test (SPI write + Calibration + Settling Time):
//! Provides two frequency settings and allows user to toggle between the two frequencies by
//! changing the N-divider(ND) value.  The user can monitor the STATUS pin.  The user
//! can trigger off the STATUS or CS pins. In most cases the user will want to set the SPI
//! frequency to 8MHz (see menu option 7)
//! @return void
void menu_6_multibytewrite()
{
  uint8_t init_setup;
  uint8_t lkup_tbl_row;          // User input command

  lkup_tbl_row=1;
  init_setup=1;

// Read/Write loop, can exit loop by choosing '0'
  while  (lkup_tbl_row != 0)
  {
    Serial.println(F("\n*****************************************************************"));
    Serial.println(F("This option is good for measuring calibration and settling time"));
    Serial.println(F("It allows one to hop back and forth between 2 frequencies"));
    Serial.println(F("For different frequencies - modify the lookup table in function"));
    Serial.println(F("Fset_LTC6951_REGS_freq_jump.  LTC6951Wizard exports register settings"));
    Serial.println(F("in the correct format for an easy modification"));
    Serial.println(F("For best timing results, set the SPI clock frequency to 8MHz (main menu: option 7)"));

    // Read All Registers and display results
    Serial.println(F("1 - Frequency 1: 600MHz"));
    Serial.println(F("2 - Frequency 2: 550MHz\n"));
    Serial.print(F("0 - Return to Main Menu\n\n"));
    Serial.print(F("Select an Option (0,1, or 2) : "));
    lkup_tbl_row = read_int();  //! Reads the user command
    Serial.println(lkup_tbl_row);

    if ((lkup_tbl_row >2)|| (lkup_tbl_row <0))
    {
      Serial.println(F("Not an option - try again"));
    }
    else if (lkup_tbl_row == 0)
    {
      // do nothing
    }
    else
    {
      set_LTC6951_REGS_freq_jump((lkup_tbl_row-1), init_setup);
      init_setup=0;
    }
  } // end while loop
}


/* ------------------------------------------------------------------------- */
//! Read stored PLL settings from nonvolatile EEPROM on demo board
//! @return void
void menu_7_SPI_speed()
{
  int temp_val;

  Serial.println(F("\nCommand Summary:"));
  if (Using_DC2430)
  {
    Serial.println(F("  1-8MHz NOT Available with DC2430"));
  }
  else
  {
    Serial.println(F("  1-SPI Clock = 8MHz"));
  }
  Serial.println(F("  2-SPI Clock = 4MHz"));
  Serial.println(F("  3-SPI Clock = 2MHz"));
  Serial.println(F("  4-SPI Clock = 1MHz"));
  Serial.println(F("  5-SPI Clock = 500kHz"));
  Serial.println("");
  Serial.print(F("Enter a command: "));
  temp_val = read_int();  //! Reads the user command

  switch (temp_val)        //! Prints the appropriate submenu
  {
    case 1:
      if (Using_DC2430)
      {
        Serial.println(F("incorrect option"));
      }
      else
      {
        spi_enable(SPI_CLOCK_DIV2);  //! 1) Configure the spi port for 8MHz SCK
      }
      break;

    case 2:
      spi_enable(SPI_CLOCK_DIV4);  //! 1) Configure the spi port for 4MHz SCK
      break;

    case 3:
      spi_enable(SPI_CLOCK_DIV8);  //! 1) Configure the spi port for 2MHz SCK
      break;

    case 4:
      spi_enable(SPI_CLOCK_DIV16);  //! 1) Configure the spi port for 1MHz SCK
      break;

    case 5:
      spi_enable(SPI_CLOCK_DIV32);  //! 1) Configure the spi port for 500kHz SCK
      break;

    default:
      Serial.println(F("Incorrect Option"));
      break;
  }
}


/* ------------------------------------------------------------------------- */
//! Read stored PLL settings from nonvolatile EEPROM on demo board
//! @return void
void menu_8_DC2430_site_select()
{
  int temp_val;
  char demo_name[] = "DC2248";    // Demo Board Name stored in QuikEval EEPROM


  Serial.println(F("\nCommand Summary:"));
  Serial.println(F("  0-Site 0"));
  Serial.println(F("  1-Site 1"));
  Serial.println(F("  2-Site 2"));
  Serial.println(F("  3-Site 3"));
  Serial.println(F("  4-Site 4"));
  Serial.println(F("  5-Site 5"));
  Serial.println(F("  6-Site 6"));
  Serial.println(F("  7-Site 7"));

  Serial.println("");
  Serial.print(F("Enter a command: "));
  temp_val = read_int();  //! Reads the user command

  dc2430_site_select(temp_val);

  demo_board_connected=0;
  dc2248_board_connected=0;
  demo_board_connected = discover_demo_board_local(demo_name);  //! Checks if any demo board is connected.

  if (demo_board_connected)
  {
    Serial.println(F("\n********************************************************"));
    dc2248_board_connected = discover_demo_board(demo_name);  //! Checks if correct demo board is connected.
  }
  if (!dc2248_board_connected)
  {
    Serial.print(F("\nDC2248 NOT FOUND ON DC2430 SITE "));
    Serial.print(temp_val);
  }

}

/* ------------------------------------------------------------------------- */
//! Called from function menu_8_DC2430_site_select(), programs DC2430 to site selected
//! @return void
void dc2430_site_select(int site_select)
{

  switch (site_select)        //! Prints the appropriate submenu
  {
    case 0:
      digitalWrite(dc2430_site_DA0_Pin, LOW);
      digitalWrite(dc2430_site_DA1_Pin, LOW);
      digitalWrite(dc2430_site_DA2_Pin, LOW);
      break;

    case 1:
      digitalWrite(dc2430_site_DA0_Pin, HIGH);
      digitalWrite(dc2430_site_DA1_Pin, LOW);
      digitalWrite(dc2430_site_DA2_Pin, LOW);
      break;

    case 2:
      digitalWrite(dc2430_site_DA0_Pin, LOW);
      digitalWrite(dc2430_site_DA1_Pin, HIGH);
      digitalWrite(dc2430_site_DA2_Pin, LOW);
      break;

    case 3:
      digitalWrite(dc2430_site_DA0_Pin, HIGH);
      digitalWrite(dc2430_site_DA1_Pin, HIGH);
      digitalWrite(dc2430_site_DA2_Pin, LOW);
      break;

    case 4:
      digitalWrite(dc2430_site_DA0_Pin, LOW);
      digitalWrite(dc2430_site_DA1_Pin, LOW);
      digitalWrite(dc2430_site_DA2_Pin, HIGH);
      break;

    case 5:
      digitalWrite(dc2430_site_DA0_Pin, HIGH);
      digitalWrite(dc2430_site_DA1_Pin, LOW);
      digitalWrite(dc2430_site_DA2_Pin, HIGH);
      break;

    case 6:
      digitalWrite(dc2430_site_DA0_Pin, LOW);
      digitalWrite(dc2430_site_DA1_Pin, HIGH);
      digitalWrite(dc2430_site_DA2_Pin, HIGH);
      break;

    case 7:
      digitalWrite(dc2430_site_DA0_Pin, HIGH);
      digitalWrite(dc2430_site_DA1_Pin, HIGH);
      digitalWrite(dc2430_site_DA2_Pin, HIGH);
      break;

    default:
      Serial.println(F("Incorrect Option"));
      break;
  }

}

/* ------------------------------------------------------------------------- */
//!    Prints the title block when program first starts.
void print_title()
{

  Serial.println(F("\n****************************************************************************************************"));
  Serial.println(F("* DC2248 Demonstration Program                                                                     *"));
  Serial.println(F("*   - Input Voltage 6V-20V (J15/J16)                                                               *"));
  Serial.println(F("*                                                                                                  *"));
  Serial.println(F("* This program demonstrates how to send data to the LTC6951 Ultralow Jitter                        *"));
  Serial.println(F("*  Multi-Output Clock Synthesizer with Integrated VCO.                                             *"));
  Serial.println(F("*                                                                                                  *"));
  Serial.println(F("* Set the baud rate to 115200 and select the newline terminator.                                   *"));
  Serial.println(F("*                                                                                                  *"));
  Serial.println(F("* Recommendations on how to best use LTC6951Wizard and DC2248 Linduino code together               *"));
  Serial.println(F("* Use LTC6951Wizard to                                                                             *"));
  Serial.println(F("*  - determine best SPI register values for specific frequenices and Sync Methods for all settings *"));
  Serial.println(F("*  - determine loop filter value                                                                   *"));
  Serial.println(F("*  - Simulate LTC6951 Phase noise and Time Domain response                                         *"));
  Serial.println(F("*  - initial engineering evaluation                                                                *"));
  Serial.println(F("* Use DC2248 Linduino code                                                                         *"));
  Serial.println(F("*  - Create a look up table of the SPI register values created from LTC6951Wizard (option 1)       *"));
  Serial.println(F("*  - Software development for reading and writing to specific register/address (option 2 & 3)      *"));
  Serial.println(F("*  - Storing demo board settings on the demo board EEPROM                                          *"));
  Serial.println(F("*  - test that require a fast SPI clocks up to 8MHz, (i.e. settling time)                          *"));
  Serial.println(F("*  - starting place to use the DC2248 with the DC2430                                              *"));
  Serial.println(F("****************************************************************************************************"));
  Serial.println();
} // end of print_title


/* ------------------------------------------------------------------------- */
//!    Prints main menu.
void print_prompt()
{

  Serial.println(F("\nCommand Summary:"));
  Serial.println(F("  1-Load Default Settings from a Look-up Table"));
  Serial.println(F("  2-READ/WRITE to Registers Addresses"));
  Serial.println(F("  3-READ/WRITE to Registers Fields"));
  Serial.println(F("  4-Store LTC6951 SPI settings to the DC2248's EEPROM"));
  Serial.println(F("  5-Restore LTC6951 SPI settings from the DC2248's EEPROM"));
  Serial.println(F("  6-Timing Measurement Aid: SPI WRITE + CALIBRAION + SETTLING TIME"));
  Serial.println(F("  7-Adjust SPI frequency"));
  if (Using_DC2430)
  {
    Serial.println(F("  8-DC2430 Site Selector"));
  }
  Serial.println("");
  Serial.print(F("Enter a command: "));
} // end of print_prompt


Download LTC6951 - Linduino Header File

/*!
 LTC6951: Ultra-Low Jitter 2MHz to 2.7GHz Multi-Output Clock Synthesizer with Integrated VCO

@verbatim
 SPI DATA FORMAT (MSB First):

 Write Sequence:
       Byte #1                    Byte #2
 MOSI: A6 A5 A4 A3 A2 A1 A0 W   D7 D6 D5 D4 D3 D2 D1 D0
 MISO: X  X  X  X  X  X  X  X   X  X  X  X  X  X  X  X

 Read Sequence:
       Byte #1                    Byte #2
 MOSI: A6 A5 A4 A3 A2 A1 A0 R   X  X  X  X  X  X  X  X
 MISO: X  X  X  X  X  X  X  X   D7 D6 D5 D4 D3 D2 D1 D0

 W    : SPI Write (0)
 R    : SPI Read  (1)
 Ax   : Address
 Dx   : Data Bits
 X    : Don't care

@endverbatim

http://www.linear.com/product/LTC6951

http://www.linear.com/product/LTC6951#demoboards

REVISION HISTORY
$Revision: 4818 $
$Date: 2016-03-24 11:37:16 -0700 (Thu, 24 Mar 2016) $

Copyright (c) 2013, Linear Technology Corp.(LTC)
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of Linear Technology Corp.

The Linear Technology Linduino is not affiliated with the official Arduino team.
However, the Linduino is only possible because of the Arduino team's commitment
to the open-source community.  Please, visit http://www.arduino.cc and
http://store.arduino.cc , and consider a purchase that will help fund their
ongoing work.
*/

/*! @file
    @ingroup LTC6951
    Header for LTC6951: Ultra-Low Jitter 2MHz to 2.5GHz Multi-Output Clock Synthesizer with Integrated VCO
*/

#ifndef LTC6951_H

#define LTC6951_H


//! Define the SPI CS pin
#ifndef LTC6951_CS
#define LTC6951_CS QUIKEVAL_CS  //! SPI Chip Select Pin
#endif


/*! @name LTC6951 Registers Fields in Alphabetical Order */
#define LTC6951_ALCCAL 1     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_ALCEN 2      //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_ALCHI 3      //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_ALCLO 4      //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_ALCMON 5     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_ALCULOK 6    //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_AUTOCAL 7    //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_BD 8         //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_BST 9        //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_CAL 10       //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_CP 11        //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_CPDN 12      //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_CPMID 13     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_CPRST 14     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_CPUP 15      //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_CPWIDE 16    //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_DLY0 17      //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_DLY1 18      //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_DLY2 19      //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_DLY3 20      //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_DLY4 21      //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_FILT 22      //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_INVSTAT 23   //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_LKCT 24      //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_LKWIN 25     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_LOCK 26      //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_NLOCK 27     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_MCO 28       //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_MC1 29       //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_MC2 30       //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_MC3 31       //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_MC4 32       //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_MD0 33       //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_MD1 34       //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_MD2 35       //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_MD3 36       //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_MD4 37       //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_MUTE0 38     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_MUTE1 39     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_MUTE2 40     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_MUTE3 41     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_MUTE4 42     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_ND 43        //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_OINV0 44     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_OINV1 45     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_OINV2 46     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_OINV3 47     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_OINV4 48     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_PART 49      //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_PD 50        //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_PDALL 51     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_PDOUT 52     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_PDPLL 53     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_PDREFPK 54   //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_PDVCO 55     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_POR 56       //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_RAO 57       //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_RD 58        //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_REFOK 59     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_NREFOK 60    //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_REV 61       //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_SN 62        //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_SR 63        //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_SSYNC 64     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_SYNCEN0 65   //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_SYNCEN1 66   //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_SYNCEN2 67   //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_SYNCEN3 68   //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_SYNCEN4 69   //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_UNLOCK 70     //!<  for spi_map array, defines location for field specific information used to create the spi map
#define LTC6951_x 71         //!<  for spi_map array, defines location for field specific information used to create the spi map



#define LTC6951_NUM_REGADDR 20  //!< Defines number of LTC6951 SPI registers, used in spi_map array
#define LTC6951_NUM_REGFIELD 71 //!< Defines number of LTC6951 SPI fields, used in spi_map array

#define ADDRx 0                 //!< used for 2nd dim of 2d spi_map array
#define DxMSB 1                 //!< used for 2nd dim of 2d spi_map array
#define NUMBITS 2               //!< used for 2nd dim of 2d spi_map array
#define R_ONLY 3                //!< used for 2nd dim of 2d spi_map array

//! @} */

/* ------------------------------------------------------------------------- */
//! LTC6951 Read Single Address
//!  reads 8 bit Data field to LTC6951.
//!  has to shift data by one bit to account for RW bit
//! @return data that was read from address
uint8_t LTC6951_read(uint8_t cs,    //!< Chip Select Pin
                     int8_t address //!< Register address for the LTC6951.
                    );


/* ------------------------------------------------------------------------- */
//! LTC6951 Read Single Field
//! For SPI FIELDS located in 1 or multiple address location
//!  reads specific address locations
//!  identifies and returns specific field in question
//!  can handle SPI fields in multiple addresses, if MSB bit is in the lower number address
//! @return data that was read from field
long LTC6951_read_field(uint8_t cs,       //!< Chip Select Pin
                        uint8_t address,  //!< Register address for the LTC6951.
                        uint8_t MSB_loc,  //!< MSB bit location of field
                        uint8_t numbits   //!< length of field (i.e. number of bits in field)
                       );


/* ------------------------------------------------------------------------- */
//! Gets the LTC6951 SPI field value
//! calls function LTC6951_read_field, which
//!  reads specific address locations
//!  identifies and returns specific field in question
//!  can handle SPI fields in multiple addresses, if MSB bit is in the lower number address
//! @return data that was read from field
long get_LTC6951_SPI_FIELD(uint8_t cs,          //!< Chip Select Pin
                           uint8_t f            //!< SPI field number
                          );


/* ------------------------------------------------------------------------- */
//! LTC6951 Write Single Address
//!  writes 8 bit Data field to LTC6951.
//!  has to shift data by one bit to account for RW bit
//! @return void
void LTC6951_write(uint8_t cs,                  //!< Chip Select Pin
                   uint8_t address,             //!< Register address for the LTC6951.
                   uint8_t Data                 //!< 8-bit data to be written to register
                  );
/* ------------------------------------------------------------------------- */
//! LTC6951 Write Single Field
//!  For SPI FIELDS in 1 or multiple address locations
//!  reads specific address/field location then writes to specific field
//!  can handle SPI fields in multiple addresses, if MSB bit is in the lower number address
//! @return void
uint8_t LTC6951_write_field(uint8_t cs,        //!< Chip Select Pin
                            long field_data,   //!< Value field value to be set to
                            uint8_t address,   //!< Register address for the LTC6951.
                            uint8_t MSB_loc,   //!< MSB bit location of field
                            uint8_t numbits   //!< length of field (i.e. number of bits in field)
                           );


/* ------------------------------------------------------------------------- */
//! Sets the LTC6951 SPI field value
//! calls function LTC6951_read_field, which
//!  reads specific address/field location then writes to specific field
//!  can handle SPI fields in multiple addresses, if MSB bit is in the lower number address
//! @return void
void set_LTC6951_SPI_FIELD(uint8_t cs,          //!< Chip Select Pin
                           uint8_t f,           //!< SPI field number
                           long field_data      //!< Value field value to be set to
                          );


/* ------------------------------------------------------------------------- */
//! Writes values to ALL LTC6951 RW addresses
//! @return void
void set_LTC6951_ALLREGS(uint8_t cs,            //!< Chip Select Pin
                         uint8_t reg01,         //!< LTC6951 register 1
                         uint8_t reg02,         //!< LTC6951 register 2
                         uint8_t reg03,         //!< LTC6951 register 3
                         uint8_t reg04,         //!< LTC6951 register 4
                         uint8_t reg05,         //!< LTC6951 register 5
                         uint8_t reg06,         //!< LTC6951 register 6
                         uint8_t reg07,         //!< LTC6951 register 7
                         uint8_t reg08,         //!< LTC6951 register 8
                         uint8_t reg09,         //!< LTC6951 register 9
                         uint8_t reg0A,         //!< LTC6951 register 10
                         uint8_t reg0B,         //!< LTC6951 register 11
                         uint8_t reg0C,         //!< LTC6951 register 12
                         uint8_t reg0D,         //!< LTC6951 register 13
                         uint8_t reg0E,         //!< LTC6951 register 14
                         uint8_t reg0F,         //!< LTC6951 register 15
                         uint8_t reg10,         //!< LTC6951 register 16
                         uint8_t reg11,         //!< LTC6951 register 17
                         uint8_t reg12          //!< LTC6951 register 18
                        );

/* ------------------------------------------------------------------------- */
//! Writes values to ALL LTC6951 RW addresses from a look-up table
//! - uses the Multi-byte write option for more efficient data transfer
//! - Calibrates VCO based on RAO and Autocal bit vales
//! - Either SPI Syncs or recommends the use to SYNC pin based on RAO, SN and SR values
//! @return void
void set_LTC6951_REGS_lkup_tbl(uint8_t lkup_tbl_row  //!< Any number 0-9, lookup table has 10 options
                              );

/* ------------------------------------------------------------------------- */
//! This function toggles between two frequencies
//! - if measuring calibration or settling times set SPI clock to max SPI rate (8MHz)
//! - writes LTC6951 registers from a local look up table
//! - uses the Multi-byte write option for more efficient data transfer
//! - Calibrates VCO based on RAO and Autocal bit vales
//! - DOES NOT SYNC OUTPUTS
//! - RECOMMENDATION: If measuring settling times with E5052, NOT MUTING OUTPUT DURING CAL can provide more meaningful results
//! @return void
void set_LTC6951_REGS_freq_jump(uint8_t lkup_tbl_row,  //!< Can be 0 or 1
                                uint8_t init_setup     //!<  0 = not first setup, 1= first setup
                               );


/* ------------------------------------------------------------------------- */
//! Initializes the SPI MAP arrays
//! The values set in initialization are used for all the LTC6951 SPI/WRITE and
//! read functions (set_LTC6951_SPI_FIELD, get_LTC6951_SPI_FIELD,
//! LTC6951_read, LTC6951_write, etc, etc)
//! @return void
void LTC6951_init();

/* ------------------------------------------------------------------------- */
//! returns # of addresses in parts register map (array size)

//! @return # of addresses in parts register map


uint8_t get_LTC6951_REGSIZE();








/* ------------------------------------------------------------------------- */


//! returns the number of bits for a given field name in the SPI map


//! @return the number of bits for a given field name in the SPI map


uint8_t get_LTC6951_SPI_FIELD_NUMBITS(uint8_t f //!< SPI field number


                                     );








/* ------------------------------------------------------------------------- */


//! returns if the given field name is (0)read/write or (1)read_only field


//! @return if the given field is a (0)read/write or (1)read_only field


uint8_t get_LTC6951_SPI_FIELD_RW(uint8_t f   //!< SPI field number


                                ) ;





/* ------------------------------------------------------------------------- */


//! Read the ID string from the EEPROM and determine if any demo board is connected.


//! Returns 1 if successful, 0 if not successful


int8_t discover_demo_board_local(char *demo_name


                                );





#endif  // LTC6951_H



Download LTC6951 - Linduino.CPP File

/*!
    LTC6951: Ultra-Low Jitter 2MHz to 2.7GHz Multi-Output Clock Synthesizer with Integrated VCO

@verbatim

The LTC®6951 is a high performance, low noise, Phase
Locked Loop (PLL) with a fully integrated VCO. The low
noise VCO uses no external components and is internally
calibrated to the correct output frequency with no external
system support.

The clock generation section provides five outputs based
on the VCO prescaler signal with individual dividers for
each output. Four outputs feature very low noise, low skew
CML logic. The fifth output is low noise LVDS. All outputs
can be synchronized and set to precise phase alignment
using the programmable delays.

Choose the LTC6951-1 if any desired output frequency
falls in the ranges 2.5GHz to 2.7GHz, 1.66GHz to 1.8GHz,
or 1.25GHz to 1.35GHz. Choose the LTC6951 for all other
frequencies.

@endverbatim


http://www.linear.com/product/LTC6951

http://www.linear.com/product/LTC6951#demoboards

REVISION HISTORY
$Revision: 4818 $
$Date: 2016-03-24 11:37:16 -0700 (Thu, 24 Mar 2016) $

Copyright (c) 2013, Linear Technology Corp.(LTC)
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of Linear Technology Corp.

The Linear Technology Linduino is not affiliated with the official Arduino team.
However, the Linduino is only possible because of the Arduino team's commitment
to the open-source community.  Please, visit http://www.arduino.cc and
http://store.arduino.cc , and consider a purchase that will help fund their
ongoing work.
*/

//! @defgroup LTC6951 LTC6951: Ultra-Low Jitter 2MHz to 2.7GHz Multi-Output Clock Synthesizer with Integrated VCO

/*! @file
    @ingroup LTC6951
    Library for LTC6951: Ultra-Low Jitter 2MHz to 2.7GHz Multi-Output Clock Synthesizer with Integrated VCO
*/

#include <stdint.h>
#include <Arduino.h>
#include "Linduino.h"
#include "UserInterface.h"
#include "LT_SPI.h"
#include "LTC6951.h"
#include "QuikEval_EEPROM.h"
#include <SPI.h>

uint8_t LTC6951_reg[LTC6951_NUM_REGADDR];              //!< number of LTC6951 spi addresses
uint8_t LTC6951_spi_map[(LTC6951_NUM_REGFIELD+1)][4];  //!< LTC6951 spi map, stores MSB address location, MSB bit location, field length in bits, and R or RW capability

uint8_t LTC6951_lkup_tbl[10][LTC6951_NUM_REGADDR] =   //!< the following settings assume a 100MHz reference input
{
  {0x05, 0xba, 0x00, 0x78, 0xb3, 0x04, 0x30, 0x07, 0x00, 0x92, 0x00, 0x92, 0x00, 0x92, 0x00, 0x92, 0x00, 0x92, 0x00, 0x11},  //!< ALL CHAN 600MHZ, EZSync
  {0x05, 0xba, 0x00, 0x74, 0xa3, 0x04, 0x06, 0x07, 0x00, 0x12, 0x00, 0x92, 0x06, 0x92, 0x06, 0x92, 0x06, 0x92, 0x06, 0x11},  //!< ALL CHAN 600MHZ, EZSync Ref Aligned
  {0x05, 0xba, 0x00, 0x74, 0xa3, 0x04, 0x06, 0x07, 0x00, 0x12, 0x00, 0x92, 0x06, 0x92, 0x06, 0x92, 0x06, 0x92, 0x06, 0x11},  //!< ALL CHAN 600MHZ, EZParallelSync
  {0x05, 0xba, 0x00, 0x74, 0xa3, 0x04, 0x06, 0x07, 0x00, 0x12, 0xc0, 0x92, 0x06, 0x92, 0x06, 0x92, 0x06, 0x92, 0x06, 0x11},  //!< ALL CHAN 600MHZ, ParallelSync
  {0x05, 0xba, 0x00, 0x78, 0xb3, 0x04, 0x32, 0x07, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x92, 0x00, 0x11},  //!< OUT[0:3] 2500MHZ, OUT4:625MHz, EZSync
  {0x05, 0xba, 0x00, 0x74, 0x83, 0x04, 0x19, 0x07, 0x00, 0x10, 0x00, 0x90, 0x07, 0x90, 0x07, 0x90, 0x07, 0x92, 0x07, 0x11},  //!< OUT[0:3] 2500MHZ, OUT4:625MHz, EZSync Ref Aligned
  {0x05, 0xba, 0x00, 0x74, 0xa3, 0x04, 0x06, 0x07, 0x00, 0x12, 0x00, 0x90, 0x06, 0x90, 0x06, 0x90, 0x06, 0x92, 0x06, 0x11},  //!< OUT[1:3] 2400MHZ, OUT0 & OUT4:600MHz, EZParallelSync
  {0x05, 0xba, 0x00, 0x74, 0x83, 0x04, 0x19, 0x07, 0x00, 0x10, 0xc0, 0x90, 0x07, 0x90, 0x07, 0x90, 0x07, 0x92, 0x07, 0x11},  //!< OUT[0:3] 2500MHZ, OUT4:625MHz, ParallelSync
  {0x00, 0xba, 0x00, 0x74, 0x93, 0x04, 0x14, 0x07, 0x00, 0x20, 0xc0, 0xd3, 0x05, 0x30, 0x00, 0xdb, 0x09, 0x95, 0x05, 0x00},  //!< Same frequency as DC2226 U10 device
  {0x00, 0xba, 0x00, 0x74, 0x93, 0x04, 0x14, 0x07, 0x00, 0x20, 0xc0, 0xd3, 0x05, 0x30, 0x00, 0xdb, 0x09, 0x9b, 0x09, 0x00}   //!< Same frequency as DC2226 U13 device
};  //!< LTC6951 Configuration look-up table


/* -------------------------------------------------------------------------
  FUNCTION: LTC6951_read
  - reads 8 bit Data field to LTC6951.
  - has to shift data by one bit to account for RW bit
 -------------------------------------------------------------------------- */
uint8_t LTC6951_read(uint8_t cs, int8_t address)
{
  int8_t address_shift;
  LT_union_int16_2bytes rx;

  address_shift =(address << 1) | 0x01; // shift to left to account for R/W bit, set bit high for read
  spi_transfer_word(cs, address_shift<<8 , &rx.LT_uint16);

  LTC6951_reg[address]=rx.LT_byte[0];
  return(rx.LT_byte[0]);
}


/* -------------------------------------------------------------------------
  FUNCTION: LTC6951_read_field
  For SPI FIELDS located in 1 or multiple address location
  - reads specific address locations
  - identifies and returns specific field in question
    - can handle SPI fields in multiple addresses, if MSB bit is in the lower number address
--------------------------------------------------------------------------- */
long LTC6951_read_field(uint8_t cs, uint8_t address, uint8_t MSB_loc, uint8_t numbits)
{
  int bit_shift, i, num_reg;
  long field_val, maskbits, pow2;

  num_reg=0;
  field_val=0;
// determines how many register are used
  do
  {
    bit_shift = (MSB_loc+1)- (numbits-num_reg*8);   // determines bit_shift for last register location
    field_val=LTC6951_read(cs, (address+num_reg))+(field_val<<8);  // reads current address locations, shifts previous address location 8 bits
    num_reg++;
  }
  while ((bit_shift<0) && (num_reg<4));

// creates a bit mask for complete word,
  maskbits = 1;
  pow2=1;
  for (i=1, maskbits=1; i<numbits; i++)
  {
    pow2=pow2*2;
    maskbits = maskbits+pow2;
  }

  field_val=(field_val >>bit_shift) &maskbits;
  return field_val;
}

/* -------------------------------------------------------------------------
  FUNCTION: get_LTC6951_SPI_FIELD
  For SPI FIELDS
  - reads specific address locations
  - identifies and returns specific field in question
    - can handle SPI fields in multiple addresses, if MSB bit is in the lower number address
--------------------------------------------------------------------------- */
long get_LTC6951_SPI_FIELD(uint8_t cs, uint8_t f)
{

  return LTC6951_read_field(cs, LTC6951_spi_map[f][ADDRx], LTC6951_spi_map[f][DxMSB], LTC6951_spi_map[f][NUMBITS]);
}

/* -------------------------------------------------------------------------
  FUNCTION: LTC6951_write
  - writes 8 bit Data field to LTC6951.
  - has to shift data by one bit to account for RW bit
--------------------------------------------------------------------------- */
void LTC6951_write(uint8_t cs, uint8_t address, uint8_t Data)
{
  LT_union_int16_2bytes rx;

  address=address << 1; // shift to left to account for R/W bit
  spi_transfer_word(cs, (address<<8) | Data, &rx.LT_uint16);
}


/* -------------------------------------------------------------------------
 FUNCTION: LTC6951_write_field
 For SPI FIELDS
 - reads specific address location
 - identifies and returns specific field in question
   - can handle SPI fields in multiple addresses, if MSB bit is in the lower number address
---------------------------------------------------------------------------- */
uint8_t LTC6951_write_field(uint8_t cs, long field_data, uint8_t address, uint8_t MSB_loc, uint8_t numbits)
{
  long current_content, desired_content, reg_val;
  int LSB_loc, i, j, num_reg, bit_shift;
  long temp_arr[32];

  for (i=0; i<32 ; i++) temp_arr[i]=0;  // init temp_arr

// read data in current address location and put in a bit array
  num_reg=0;
  current_content=0;
  do
  {
    bit_shift=(MSB_loc+1)-(numbits-num_reg*8);
    current_content=LTC6951_read(cs, (address+num_reg)) + (current_content<<8);

    num_reg++;
  }
  while ((bit_shift<0) && (num_reg<4));
  for (i=0; i<(8*num_reg); i++)
  {
    temp_arr[i]=(current_content>>i) & 1;
  }

// exchange current bits with desired bits
  LSB_loc = 8*(num_reg-1)+MSB_loc-numbits+1;
  for (i=LSB_loc, j=0; i<=(MSB_loc+(num_reg-1)*8); i++, j++)
  {
    temp_arr[i] = (field_data>>j) &1;
  } // end of for loop

// reconstruct bits into an integer
  desired_content = 0;
  for (i=0; i<(8*num_reg); i++)
  {
    desired_content = desired_content | (temp_arr[i]<<i);
  } // end of for loop

// write new field value to part
  for (i=0; i<num_reg; i++)
  {
    reg_val = (desired_content >> 8*(num_reg-1-i)) & 0xff;
    LTC6951_write(cs, (address+i), reg_val);
  } // end of for loop
} // end of LTC6951_write_field


/* -------------------------------------------------------------------------
   FUNCTION: get_LTC6951_REGSIZE
   - returns # of addresses in parts register map (array size)
---------------------------------------------------------------------------- */
uint8_t get_LTC6951_REGSIZE()
{
  return sizeof(LTC6951_reg);
}


/* -------------------------------------------------------------------------
   FUNCTION: get_LTC6951_SPI_FIELD_NUMBITS
   - returns the number of bits for a given field name in the SPI map
---------------------------------------------------------------------------- */
uint8_t get_LTC6951_SPI_FIELD_NUMBITS(uint8_t f)
{
  return LTC6951_spi_map[f][NUMBITS];
}


/* -------------------------------------------------------------------------
   FUNCTION: get_LTC6951_SPI_FIELD_RW
   - returns if the given field name is (0)read/write or (1)read_only field
---------------------------------------------------------------------------- */
uint8_t get_LTC6951_SPI_FIELD_RW(uint8_t f)
{
  return LTC6951_spi_map[f][R_ONLY];
}


/* -------------------------------------------------------------------------
   FUNCTION: set_LTC6951_SPI_FIELD
   For SPI FIELDS
   - reads specific address location
   - identifies and returns specific field in question
   - can handle SPI fields in multiple addresses, if MSB bit is in the lower number address
---------------------------------------------------------------------------- */
void set_LTC6951_SPI_FIELD(uint8_t cs, uint8_t f, long field_data)
{
  LTC6951_write_field(cs, field_data, LTC6951_spi_map[f][ADDRx], LTC6951_spi_map[f][DxMSB], LTC6951_spi_map[f][NUMBITS]);
}


/* -------------------------------------------------------------------------
   FUNCTION: set_LTC6951_ALLREGS
   - writes data to all registers at once
--------------------------------------------------------------------------- */
void set_LTC6951_ALLREGS(uint8_t cs, uint8_t reg01, uint8_t reg02, uint8_t reg03, uint8_t reg04, uint8_t reg05, uint8_t reg06, uint8_t reg07,
                         uint8_t reg08, uint8_t reg09, uint8_t reg0A, uint8_t reg0B, uint8_t reg0C, uint8_t reg0D, uint8_t reg0E, uint8_t reg0F,
                         uint8_t reg10, uint8_t reg11, uint8_t reg12)
{
  uint8_t i;

  LTC6951_reg[1] = reg01;
  LTC6951_reg[2] = reg02;
  LTC6951_reg[3] = reg03;
  LTC6951_reg[4] = reg04;
  LTC6951_reg[5] = reg05;
  LTC6951_reg[6] = reg06;
  LTC6951_reg[7] = reg07;
  LTC6951_reg[8] = reg08;
  LTC6951_reg[9] = reg09;
  LTC6951_reg[10] = reg0A;
  LTC6951_reg[11] = reg0B;
  LTC6951_reg[12] = reg0C;
  LTC6951_reg[13] = reg0D;
  LTC6951_reg[14] = reg0E;
  LTC6951_reg[15] = reg0F;
  LTC6951_reg[16] = reg10;
  LTC6951_reg[17] = reg11;
  LTC6951_reg[18] = reg12;


  for (i=1; i<19; i++)  LTC6951_write(cs, i, LTC6951_reg[i]);
} // end of set_LTC6951_ALLREGS


/* -------------------------------------------------------------------------
   FUNCTION: set_LTC6951_REGS_lkup_tbl
   - writes LTC6951 registers from a look up table
   - uses the Multi-byte write option for more efficient data transfer
   - Calibrates VCO based on RAO and Autocal bit vales
   - Either SPI Syncs or recommends the use to SYNC pin based on RAO, SN and SR values
--------------------------------------------------------------------------- */
void set_LTC6951_REGS_lkup_tbl(uint8_t lkup_tbl_row)
{
  uint8_t val_temp, i, RAO_bit, AUTOCAL_bit, SN_bit, SR_bit;
  uint8_t val_reg2, val_reg3, val_regA;
  uint8_t *rx;

//! ************** SPI REGISTER WRITE SECTION ************
  output_low(QUIKEVAL_CS);               //! 1) CS low, enable SPI
  val_temp = 1<<1;                       //! 2) addr 1, shift 1 bit to account for RW bit (W=0)
  *rx = SPI.transfer(val_temp);          //! 3) send ADDR1 byte
  for (i=1; i<19; i++)
  {
    val_temp=LTC6951_lkup_tbl[lkup_tbl_row][i];
    *rx = SPI.transfer(val_temp);       //! 4) send REG byte
  }
  output_high(QUIKEVAL_CS);              //! 5) CS high, disable SPI

//! **************** CALIBRATION  SECTION ******************
//! Calibration required after Register writes if RAO=1 or if RAO=AUTOCAL=0.
  val_reg2 = LTC6951_lkup_tbl[lkup_tbl_row][2];  //! CAL bit is located @ reg 2, bit 0, AUTOCAL bit reg 2, bit 3
  val_reg3 = LTC6951_lkup_tbl[lkup_tbl_row][3];  //! RAO bit is located @ reg 3, bit 2;  SSYNC bit @ reg 3, bit 4
  RAO_bit = (val_reg3 & 0x04)>>2;
  AUTOCAL_bit = (val_reg3 & 0x08)>>3;

  val_regA = LTC6951_lkup_tbl[lkup_tbl_row][10];  //! SN, SR bit are located @ reg A, bits 7.6
  SN_bit = (val_regA & 0x80)>>7;
  SR_bit = (val_regA & 0x40)>>6;

  if ((RAO_bit ==1) || (AUTOCAL_bit ==0))  //! checks if RAO=1 or AUTOCAL bit = 0
  {
    val_reg2 = val_reg2 | 0x01;           //! sets CAL bit = 1
    output_low(QUIKEVAL_CS);              //! CS low, enable SPI
    val_temp = 2<<1;                      //! addr 2, shift 1 bit to account for RW bit (W=0)
    *rx = SPI.transfer(val_temp);         //! send ADDR2 byte + RW byte
    *rx = SPI.transfer(val_reg2);         //! send REG2 byte w/ CAL=1
    output_high(QUIKEVAL_CS);             //! CS high, disable SPI
    val_reg2 = LTC6951_lkup_tbl[lkup_tbl_row][2];  //! CAL bit auto-clears after Calibration, set val_reg2 back to look-up table value
  }
  delay(1);                             //! wait 1ms for Calibration to complete, calibration usually takes <100us

//! **************** OUTPUT SYNC SECTION ******************
//! SSYNC can be used for EZSYNC StandAlone, EZSync Ref aligned standalone, EZParallelSYnc, EZ204Sync
//!  - EZSync Standalone, RAO=0
//!  - EZParallelSync, EZ204Sync, EZSync ref aligned: RA0=1, SN=SR=0
//! For ParallelSync or EZSync Controller to work the the SYNC pin should be used
//!  - EZSync Controller, RAO=0 (User will have to remember if its a Controller as there is not a bit to distinguish this)
//!  - ParallelSync: RA0=1, SN or SR= 1
//! See datasheet for timing requirements
  if ((RAO_bit==0) ||  ((RAO_bit == 1) && ((SN_bit+SR_bit)==0)))
  {
    val_reg2 = val_reg2 | 0x04;            //! Sets SSYNC bit =1
    output_low(QUIKEVAL_CS);               //! CS low, enable SPI
    val_temp = 2<<1;                       //! addr 2, shift 1 bit to account for RW bit (W=0)
    *rx = SPI.transfer(val_temp);          //! send ADDR2 byte + RW byte
    *rx = SPI.transfer(val_reg2);          //! send REG2 byte w/ CAL=1
    output_high(QUIKEVAL_CS);              //! CS high, disable SPI
    delay(1);                              //! SSYNC bit remains high for a minimum of 1ms

    val_reg2 = val_reg2 - 0x04;            //! Sets SSYNC bit  back to 0
    output_low(QUIKEVAL_CS);               //! CS low, enable SPI
    val_temp = 2<<1;                       //! addr 2, shift 1 bit to account for RW bit (W=0)
    *rx = SPI.transfer(val_temp);          //! send ADDR2 byte + RW byte
    *rx = SPI.transfer(val_reg2);          //! send REG2 byte w/ CAL=1
    output_high(QUIKEVAL_CS);              //! CS high, disable SPI
  }

//! **************** INFO SECTION ******************
//! INFO ONLY: Print Register Map written to part
  for (i=1; i<19; i++)
  {
    val_temp=LTC6951_lkup_tbl[lkup_tbl_row][i];
    Serial.print(F("ADDR"));
    if (i<16) Serial.print("0");
    Serial.print(i, HEX);
    Serial.print(F(" = 0x"));
    Serial.println(val_temp, HEX);
  }

  if (RAO_bit == 0)
  {
    Serial.println(F(" ***************************************************************************** "));
    Serial.println(F("If this is a EZSync Standalone Device ignore this message - LTC6951 has been SYNC'd via SPI "));
    Serial.println(F("If this is a EZSync Controller Device toggle all EZSync Device's Sync pins "));
    Serial.println(F("EZSync TIMING: Sync Pins must remain high for 1ms, and with <10us skew between all EZSync Sync pins "));
    Serial.println(F(" ***************************************************************************** "));
  }
  if ( (RAO_bit == 1) && ( (SN_bit+SR_bit)>0 ) )
  {
    Serial.println(F(" ***************************************************************************** "));
    Serial.println(F("ParallelSync Timing: See Datasheet for setup and hold times "));
    Serial.println(F("USER MUST TOGGLE SYNC PINS ACROSS ALL PARALLLESYNC DEVICES "));
    Serial.println(F("If a DC2430 is available:"));
    Serial.println(F(" - DC2430's J20-J22 circuitry can be used to create correct SYNC to REF setup and hold times"));
    Serial.println(F(" ***************************************************************************** "));
  }

} // end of set_LTC6951_REGS_lkup_tbl

/* -------------------------------------------------------------------------
   FUNCTION: set_LTC6951_REGS_freq_jump
   - used to toggle between two frequencies
   - if measuring calibration or settling times set SPI clock to max SPI rate (8MHz)
   = writes LTC6951 registers from a local look up table
   - uses the Multi-byte write option for more efficient data transfer
   - Calibrates VCO based on RAO and Autocal bit vales
   - DOES NOTE SYNC OUTPUTS
   - RECOMMENDATION: If measuring settling times with E5052, NOT MUTING OUTPUT DURING CAL can provide more meaningful results
--------------------------------------------------------------------------- */
void set_LTC6951_REGS_freq_jump(uint8_t lkup_tbl_row, uint8_t init_setup)
{
  boolean REGx3toREGx5_change=false;
  boolean REGx9toREGx12_change=false;
  boolean REGx7toREGx8_change=false;
  uint8_t start_addr, end_addr, val_temp, i, RAO_bit, AUTOCAL_bit, SN_bit, SR_bit;
  uint8_t val_reg2, val_reg3, val_regA;
  uint8_t *rx;
// Modifiy the 2 registers to try different settings.  LTC6951Wizard has an export register option that supplies the below format.
  uint8_t local6951_lkup_tbl[2][LTC6951_NUM_REGADDR] =   //!< the following settings assume a 100MHz reference input
  {
//{0x05, 0xba, 0x00, 0x74, 0xa3, 0x04, 0x06, 0x07, 0x00, 0x02, 0xc0, 0x82, 0x06, 0x82, 0x06, 0x82, 0x06, 0x82, 0x06, 0x11}, //! ALL CHAN 600MHz, ParallelSync
//{0x05, 0xba, 0x00, 0x78, 0xb3, 0x04, 0x31, 0x07, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0x11}, //! ALL CHAN 1225MHz, EZSync
    {0x05, 0xba, 0x00, 0x78, 0xb3, 0x04, 0x30, 0x07, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x11},  //!< ALL CHAN 600MHZ, EZSync
    {0x05, 0xba, 0x00, 0x78, 0xb3, 0x04, 0x2c, 0x07, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x11}  //!< ALL CHAN 550MHZ, EZSync
  };//

// inital setup program all registers
  if (init_setup)
  {
    //! ************** INIT: SPI REGISTERS (ALL) WRITE SECTION ************
    output_low(QUIKEVAL_CS);               //! 1) CS low, enable SPI
    start_addr = 1;
    val_temp = start_addr<<1;              //! 2) addr 1, shift 1 bit to account for RW bit (W=0)
    *rx = SPI.transfer(val_temp);          //! 3) send ADDR1 byte
    for (i=start_addr; i<19; i++)
    {
      val_temp=local6951_lkup_tbl[lkup_tbl_row][i];
      *rx = SPI.transfer(val_temp);       //! 4) send REG byte
    }
    output_high(QUIKEVAL_CS);              //! 5) CS high, disable SPI
  } // end if(init_setup)

// checks if b or r-divider change?
  REGx3toREGx5_change=false;
  for (i=3; i<=5; i++)
  {
    if (local6951_lkup_tbl[0][i] != local6951_lkup_tbl[1][i]) REGx3toREGx5_change=true;
  }

// checks if p-divider change?
  REGx7toREGx8_change=false;
  for (i=7; i<=8; i++)
  {
    if (local6951_lkup_tbl[0][i] != local6951_lkup_tbl[1][i]) REGx7toREGx8_change=true;
  }

// checks if output divider change?
  REGx9toREGx12_change=false;
  for (i=9; i<=0x12; i++)
  {
    if (local6951_lkup_tbl[0][i] != local6951_lkup_tbl[1][i]) REGx9toREGx12_change=true;
  }

//! **************** SELECTION SECTION ******************
//! Calibration required after Register writes if RAO=1 or if RAO=AUTOCAL=0.
  val_reg2 = local6951_lkup_tbl[lkup_tbl_row][2];  //! CAL bit is located @ reg 2, bit 0, AUTOCAL bit reg 2, bit 3
  val_reg3 = local6951_lkup_tbl[lkup_tbl_row][3];  //! RAO bit is located @ reg 3, bit 2;  SSYNC bit @ reg 3, bit 4
  RAO_bit = (val_reg3 & 0x04)>>2;
  AUTOCAL_bit = (val_reg3 & 0x08)>>3;

//! All Settings of RAO and AUTOCAL Run this section
//! ************** SPI REGISTERS 4-19 WRITE SECTION ************
//! it possible the that register 6 is the only register that needs to change (N-divider),
//! but this codes was written for any and all frequency changes.
  start_addr = 6;                             //! ADDR6 includes include ND
  if (REGx3toREGx5_change==true) start_addr=3; //! ADDR4 includes include BD (cal-divider), ADDR 5 includes RD
  end_addr=6;                                 //! ADDR6 includes include ND
  if (REGx7toREGx8_change==true) end_addr=8;  //! ADDR8 includes include PD
  if (REGx9toREGx12_change==true) end_addr=12; //! ADDR9-12 include output dividers

  output_low(QUIKEVAL_CS);               //! CS low, enable SPI
  val_temp = start_addr<<1;              //! addr 4, shift 1 bit to account for RW bit (W=0)
  *rx = SPI.transfer(val_temp);          //! send ADDR byte
  for (i=start_addr; i<=end_addr; i++)
  {
    val_temp=local6951_lkup_tbl[lkup_tbl_row][i];
    *rx = SPI.transfer(val_temp);       //! send REG bytes
  }
  output_high(QUIKEVAL_CS);              //! CS high, disable SPI


//! If Autocal=0 or RAO_bit=1 (ParallelSync, EZParallelSync, EZ204Sync, EZSync Ref Aligned
//! Then the cal bit needs to be toggle, with Linduino it maybe fastest to continue
//! The multi-byte write operation.  (reason: The CS L-H transistion has a delay)
  if (RAO_bit  || !AUTOCAL_bit)
  {
    start_addr = 2;
    val_temp = start_addr<<1;             //! ADDR, shift 1 bit to account for RW bit (W=0)
    val_reg2 = val_reg2 | 0x01;           //! sets CAL bit loc = 1

    output_low(QUIKEVAL_CS);              //! CS low, enable SPI
    *rx = SPI.transfer(val_temp);         //! send ADDR2 byte
    *rx = SPI.transfer(val_reg2);         //! send REG2 byte
    output_high(QUIKEVAL_CS);             //! CS high, disable SPI
  }

}  // end of set_LTC6951_REGS_freq_jump


/* -------------------------------------------------------------------------
   FUNCTION: LTC6951_init
   - initializes the SPI MAP
   - for ease of programming there is spreadsheet that automates this some.
----------------------------------------------------------------------------*/
void LTC6951_init()
{
// look up table

// spi map
  LTC6951_spi_map[LTC6951_ALCCAL][ADDRx]=0x03;
  LTC6951_spi_map[LTC6951_ALCCAL][DxMSB]= 5;
  LTC6951_spi_map[LTC6951_ALCCAL][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_ALCEN][ADDRx]=0x03;
  LTC6951_spi_map[LTC6951_ALCEN][DxMSB]= 7;
  LTC6951_spi_map[LTC6951_ALCEN][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_ALCHI][ADDRx]=0x00;
  LTC6951_spi_map[LTC6951_ALCHI][DxMSB]= 5;
  LTC6951_spi_map[LTC6951_ALCHI][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_ALCLO][ADDRx]=0x00;
  LTC6951_spi_map[LTC6951_ALCLO][DxMSB]= 4;
  LTC6951_spi_map[LTC6951_ALCLO][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_ALCMON][ADDRx]=0x03;
  LTC6951_spi_map[LTC6951_ALCMON][DxMSB]= 6;
  LTC6951_spi_map[LTC6951_ALCMON][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_ALCULOK][ADDRx]=0x03;
  LTC6951_spi_map[LTC6951_ALCULOK][DxMSB]= 4;
  LTC6951_spi_map[LTC6951_ALCULOK][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_AUTOCAL][ADDRx]=0x03;
  LTC6951_spi_map[LTC6951_AUTOCAL][DxMSB]= 3;
  LTC6951_spi_map[LTC6951_AUTOCAL][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_BD][ADDRx]=0x04;
  LTC6951_spi_map[LTC6951_BD][DxMSB]= 7;
  LTC6951_spi_map[LTC6951_BD][NUMBITS]= 4;
  LTC6951_spi_map[LTC6951_BST][ADDRx]=0x03;
  LTC6951_spi_map[LTC6951_BST][DxMSB]= 1;
  LTC6951_spi_map[LTC6951_BST][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_CAL][ADDRx]=0x02;
  LTC6951_spi_map[LTC6951_CAL][DxMSB]= 0;
  LTC6951_spi_map[LTC6951_CAL][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_CP][ADDRx]=0x07;
  LTC6951_spi_map[LTC6951_CP][DxMSB]= 2;
  LTC6951_spi_map[LTC6951_CP][NUMBITS]= 3;
  LTC6951_spi_map[LTC6951_CPDN][ADDRx]=0x07;
  LTC6951_spi_map[LTC6951_CPDN][DxMSB]= 3;
  LTC6951_spi_map[LTC6951_CPDN][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_CPMID][ADDRx]=0x07;
  LTC6951_spi_map[LTC6951_CPMID][DxMSB]= 7;
  LTC6951_spi_map[LTC6951_CPMID][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_CPRST][ADDRx]=0x07;
  LTC6951_spi_map[LTC6951_CPRST][DxMSB]= 5;
  LTC6951_spi_map[LTC6951_CPRST][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_CPUP][ADDRx]=0x07;
  LTC6951_spi_map[LTC6951_CPUP][DxMSB]= 4;
  LTC6951_spi_map[LTC6951_CPUP][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_CPWIDE][ADDRx]=0x07;
  LTC6951_spi_map[LTC6951_CPWIDE][DxMSB]= 6;
  LTC6951_spi_map[LTC6951_CPWIDE][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_DLY0][ADDRx]=0x0a;
  LTC6951_spi_map[LTC6951_DLY0][DxMSB]= 7;
  LTC6951_spi_map[LTC6951_DLY0][NUMBITS]= 8;
  LTC6951_spi_map[LTC6951_DLY1][ADDRx]=0x0c;
  LTC6951_spi_map[LTC6951_DLY1][DxMSB]= 7;
  LTC6951_spi_map[LTC6951_DLY1][NUMBITS]= 8;
  LTC6951_spi_map[LTC6951_DLY2][ADDRx]=0x0e;
  LTC6951_spi_map[LTC6951_DLY2][DxMSB]= 7;
  LTC6951_spi_map[LTC6951_DLY2][NUMBITS]= 8;
  LTC6951_spi_map[LTC6951_DLY3][ADDRx]=0x10;
  LTC6951_spi_map[LTC6951_DLY3][DxMSB]= 7;
  LTC6951_spi_map[LTC6951_DLY3][NUMBITS]= 8;
  LTC6951_spi_map[LTC6951_DLY4][ADDRx]=0x12;
  LTC6951_spi_map[LTC6951_DLY4][DxMSB]= 7;
  LTC6951_spi_map[LTC6951_DLY4][NUMBITS]= 8;
  LTC6951_spi_map[LTC6951_FILT][ADDRx]=0x03;
  LTC6951_spi_map[LTC6951_FILT][DxMSB]= 0;
  LTC6951_spi_map[LTC6951_FILT][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_INVSTAT][ADDRx]=0x01;
  LTC6951_spi_map[LTC6951_INVSTAT][DxMSB]= 7;
  LTC6951_spi_map[LTC6951_INVSTAT][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_LKCT][ADDRx]=0x04;
  LTC6951_spi_map[LTC6951_LKCT][DxMSB]= 1;
  LTC6951_spi_map[LTC6951_LKCT][NUMBITS]= 2;
  LTC6951_spi_map[LTC6951_LKWIN][ADDRx]=0x04;
  LTC6951_spi_map[LTC6951_LKWIN][DxMSB]= 2;
  LTC6951_spi_map[LTC6951_LKWIN][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_LOCK][ADDRx]=0x00;
  LTC6951_spi_map[LTC6951_LOCK][DxMSB]= 2;
  LTC6951_spi_map[LTC6951_LOCK][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_NLOCK][ADDRx]=0x00;
  LTC6951_spi_map[LTC6951_NLOCK][DxMSB]= 3;
  LTC6951_spi_map[LTC6951_NLOCK][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_MCO][ADDRx]=0x09;
  LTC6951_spi_map[LTC6951_MCO][DxMSB]= 5;
  LTC6951_spi_map[LTC6951_MCO][NUMBITS]= 2;
  LTC6951_spi_map[LTC6951_MC1][ADDRx]=0x0b;
  LTC6951_spi_map[LTC6951_MC1][DxMSB]= 5;
  LTC6951_spi_map[LTC6951_MC1][NUMBITS]= 2;
  LTC6951_spi_map[LTC6951_MC2][ADDRx]=0x0d;
  LTC6951_spi_map[LTC6951_MC2][DxMSB]= 5;
  LTC6951_spi_map[LTC6951_MC2][NUMBITS]= 2;
  LTC6951_spi_map[LTC6951_MC3][ADDRx]=0x0f;
  LTC6951_spi_map[LTC6951_MC3][DxMSB]= 5;
  LTC6951_spi_map[LTC6951_MC3][NUMBITS]= 2;
  LTC6951_spi_map[LTC6951_MC4][ADDRx]=0x11;
  LTC6951_spi_map[LTC6951_MC4][DxMSB]= 5;
  LTC6951_spi_map[LTC6951_MC4][NUMBITS]= 2;
  LTC6951_spi_map[LTC6951_MD0][ADDRx]=0x09;
  LTC6951_spi_map[LTC6951_MD0][DxMSB]= 3;
  LTC6951_spi_map[LTC6951_MD0][NUMBITS]= 4;
  LTC6951_spi_map[LTC6951_MD1][ADDRx]=0x0b;
  LTC6951_spi_map[LTC6951_MD1][DxMSB]= 3;
  LTC6951_spi_map[LTC6951_MD1][NUMBITS]= 4;
  LTC6951_spi_map[LTC6951_MD2][ADDRx]=0x0d;
  LTC6951_spi_map[LTC6951_MD2][DxMSB]= 3;
  LTC6951_spi_map[LTC6951_MD2][NUMBITS]= 4;
  LTC6951_spi_map[LTC6951_MD3][ADDRx]=0x0f;
  LTC6951_spi_map[LTC6951_MD3][DxMSB]= 3;
  LTC6951_spi_map[LTC6951_MD3][NUMBITS]= 4;
  LTC6951_spi_map[LTC6951_MD4][ADDRx]=0x11;
  LTC6951_spi_map[LTC6951_MD4][DxMSB]= 3;
  LTC6951_spi_map[LTC6951_MD4][NUMBITS]= 4;
  LTC6951_spi_map[LTC6951_MUTE0][ADDRx]=0x08;
  LTC6951_spi_map[LTC6951_MUTE0][DxMSB]= 0;
  LTC6951_spi_map[LTC6951_MUTE0][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_MUTE1][ADDRx]=0x08;
  LTC6951_spi_map[LTC6951_MUTE1][DxMSB]= 1;
  LTC6951_spi_map[LTC6951_MUTE1][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_MUTE2][ADDRx]=0x08;
  LTC6951_spi_map[LTC6951_MUTE2][DxMSB]= 2;
  LTC6951_spi_map[LTC6951_MUTE2][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_MUTE3][ADDRx]=0x08;
  LTC6951_spi_map[LTC6951_MUTE3][DxMSB]= 3;
  LTC6951_spi_map[LTC6951_MUTE3][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_MUTE4][ADDRx]=0x08;
  LTC6951_spi_map[LTC6951_MUTE4][DxMSB]= 4;
  LTC6951_spi_map[LTC6951_MUTE4][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_ND][ADDRx]=0x05;
  LTC6951_spi_map[LTC6951_ND][DxMSB]= 1;
  LTC6951_spi_map[LTC6951_ND][NUMBITS]= 10;
  LTC6951_spi_map[LTC6951_OINV0][ADDRx]=0x09;
  LTC6951_spi_map[LTC6951_OINV0][DxMSB]= 6;
  LTC6951_spi_map[LTC6951_OINV0][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_OINV1][ADDRx]=0x0b;
  LTC6951_spi_map[LTC6951_OINV1][DxMSB]= 6;
  LTC6951_spi_map[LTC6951_OINV1][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_OINV2][ADDRx]=0x0d;
  LTC6951_spi_map[LTC6951_OINV2][DxMSB]= 6;
  LTC6951_spi_map[LTC6951_OINV2][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_OINV3][ADDRx]=0x0f;
  LTC6951_spi_map[LTC6951_OINV3][DxMSB]= 6;
  LTC6951_spi_map[LTC6951_OINV3][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_OINV4][ADDRx]=0x11;
  LTC6951_spi_map[LTC6951_OINV4][DxMSB]= 6;
  LTC6951_spi_map[LTC6951_OINV4][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_PART][ADDRx]=0x13;
  LTC6951_spi_map[LTC6951_PART][DxMSB]= 3;
  LTC6951_spi_map[LTC6951_PART][NUMBITS]= 4;
  LTC6951_spi_map[LTC6951_PD][ADDRx]=0x08;
  LTC6951_spi_map[LTC6951_PD][DxMSB]= 7;
  LTC6951_spi_map[LTC6951_PD][NUMBITS]= 3;
  LTC6951_spi_map[LTC6951_PDALL][ADDRx]=0x02;
  LTC6951_spi_map[LTC6951_PDALL][DxMSB]= 7;
  LTC6951_spi_map[LTC6951_PDALL][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_PDOUT][ADDRx]=0x02;
  LTC6951_spi_map[LTC6951_PDOUT][DxMSB]= 4;
  LTC6951_spi_map[LTC6951_PDOUT][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_PDPLL][ADDRx]=0x02;
  LTC6951_spi_map[LTC6951_PDPLL][DxMSB]= 6;
  LTC6951_spi_map[LTC6951_PDPLL][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_PDREFPK][ADDRx]=0x02;
  LTC6951_spi_map[LTC6951_PDREFPK][DxMSB]= 3;
  LTC6951_spi_map[LTC6951_PDREFPK][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_PDVCO][ADDRx]=0x02;
  LTC6951_spi_map[LTC6951_PDVCO][DxMSB]= 5;
  LTC6951_spi_map[LTC6951_PDVCO][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_POR][ADDRx]=0x02;
  LTC6951_spi_map[LTC6951_POR][DxMSB]= 1;
  LTC6951_spi_map[LTC6951_POR][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_RAO][ADDRx]=0x03;
  LTC6951_spi_map[LTC6951_RAO][DxMSB]= 2;
  LTC6951_spi_map[LTC6951_RAO][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_RD][ADDRx]=0x05;
  LTC6951_spi_map[LTC6951_RD][DxMSB]= 7;
  LTC6951_spi_map[LTC6951_RD][NUMBITS]= 6;
  LTC6951_spi_map[LTC6951_REFOK][ADDRx]=0x00;
  LTC6951_spi_map[LTC6951_REFOK][DxMSB]= 0;
  LTC6951_spi_map[LTC6951_REFOK][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_NREFOK][ADDRx]=0x00;
  LTC6951_spi_map[LTC6951_NREFOK][DxMSB]= 1;
  LTC6951_spi_map[LTC6951_NREFOK][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_REV][ADDRx]=0x13;
  LTC6951_spi_map[LTC6951_REV][DxMSB]= 7;
  LTC6951_spi_map[LTC6951_REV][NUMBITS]= 4;
  LTC6951_spi_map[LTC6951_SN][ADDRx]=0x0a;
  LTC6951_spi_map[LTC6951_SN][DxMSB]= 7;
  LTC6951_spi_map[LTC6951_SN][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_SR][ADDRx]=0x0a;
  LTC6951_spi_map[LTC6951_SR][DxMSB]= 6;
  LTC6951_spi_map[LTC6951_SR][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_SSYNC][ADDRx]=0x02;
  LTC6951_spi_map[LTC6951_SSYNC][DxMSB]= 2;
  LTC6951_spi_map[LTC6951_SSYNC][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_SYNCEN0][ADDRx]=0x09;
  LTC6951_spi_map[LTC6951_SYNCEN0][DxMSB]= 7;
  LTC6951_spi_map[LTC6951_SYNCEN0][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_SYNCEN1][ADDRx]=0x0b;
  LTC6951_spi_map[LTC6951_SYNCEN1][DxMSB]= 7;
  LTC6951_spi_map[LTC6951_SYNCEN1][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_SYNCEN2][ADDRx]=0x0d;
  LTC6951_spi_map[LTC6951_SYNCEN2][DxMSB]= 7;
  LTC6951_spi_map[LTC6951_SYNCEN2][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_SYNCEN3][ADDRx]=0x0f;
  LTC6951_spi_map[LTC6951_SYNCEN3][DxMSB]= 7;
  LTC6951_spi_map[LTC6951_SYNCEN3][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_SYNCEN4][ADDRx]=0x11;
  LTC6951_spi_map[LTC6951_SYNCEN4][DxMSB]= 7;
  LTC6951_spi_map[LTC6951_SYNCEN4][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_UNLOCK][ADDRx]=0x00;
  LTC6951_spi_map[LTC6951_UNLOCK][DxMSB]= 6;
  LTC6951_spi_map[LTC6951_UNLOCK][NUMBITS]= 1;
  LTC6951_spi_map[LTC6951_x][ADDRx]=0x01;
  LTC6951_spi_map[LTC6951_x][DxMSB]= 6;
  LTC6951_spi_map[LTC6951_x][NUMBITS]= 7;

  LTC6951_spi_map[LTC6951_ALCCAL][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_ALCEN][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_ALCHI][R_ONLY]= 1;
  LTC6951_spi_map[LTC6951_ALCLO][R_ONLY]= 1;
  LTC6951_spi_map[LTC6951_ALCMON][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_ALCULOK][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_AUTOCAL][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_BD][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_BST][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_CAL][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_CP][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_CPDN][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_CPMID][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_CPRST][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_CPUP][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_CPWIDE][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_DLY0][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_DLY1][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_DLY2][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_DLY3][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_DLY4][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_FILT][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_INVSTAT][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_LKCT][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_LKWIN][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_LOCK][R_ONLY]= 1;
  LTC6951_spi_map[LTC6951_NLOCK][R_ONLY]= 1;
  LTC6951_spi_map[LTC6951_MCO][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_MC1][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_MC2][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_MC3][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_MC4][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_MD0][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_MD1][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_MD2][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_MD3][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_MD4][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_MUTE0][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_MUTE1][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_MUTE2][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_MUTE3][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_MUTE4][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_ND][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_OINV0][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_OINV1][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_OINV2][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_OINV3][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_OINV4][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_PART][R_ONLY]= 1;
  LTC6951_spi_map[LTC6951_PD][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_PDALL][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_PDOUT][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_PDPLL][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_PDREFPK][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_PDVCO][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_POR][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_RAO][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_RD][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_REFOK][R_ONLY]= 1;
  LTC6951_spi_map[LTC6951_NREFOK][R_ONLY]= 1;
  LTC6951_spi_map[LTC6951_REV][R_ONLY]= 1;
  LTC6951_spi_map[LTC6951_SN][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_SR][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_SSYNC][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_SYNCEN0][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_SYNCEN1][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_SYNCEN2][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_SYNCEN3][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_SYNCEN4][R_ONLY]= 0;
  LTC6951_spi_map[LTC6951_UNLOCK][R_ONLY]= 1;
  LTC6951_spi_map[LTC6951_x][R_ONLY]= 0;
} // end of LTC6951_init



// Read the ID string from the EEPROM and determine if the correct board is connected.
// Returns 1 if successful, 0 if not successful
int8_t discover_demo_board_local(char *demo_name)
{
  int8_t connected;
  connected = 1;
  // read the ID from the serial EEPROM on the board
  // reuse the buffer declared in UserInterface
  if (read_quikeval_id_string(&ui_buffer[0]) == 0) connected = 0;

  return(connected);
}

Technical Support