/*! LTC6953: Ultra-Low Jitter, JESD204B Clock Distributor with Eleven Programmable Outputs @verbatim The LTC®6953 is a high performance, ultralow jitter, JESD204B clock distribution IC. The LTC6953’s eleven outputs can be configured as up to five JESD204B subclass 1 device clock/SYSREF pairs plus one general purpose output or simply eleven general purpose clock outputs for non-JESD204B applications. Each output has its own individually programmable frequency divider and output driver. All outputs can also be synchronized and set to precise phase alignment using individual coarse half cycle digital delays and fine analog time delays. For applications requiring more than eleven total outputs, multiple LTC6953s can be connected together with LTC6952s and LTC6955s using the EZSync or ParallelSync synchronization protocols. @endverbatim http://www.linear.com/product/LTC6953 http://www.linear.com/product/LTC6953#demoboards REVISION HISTORY $Revision: 1844 $ $Date: 2013-08-08 15:11:32 -0700 (Thu, 08 Aug 2013) $ 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. */ //! @ingroup WIP //! @{ //! @defgroup LTC6953 LTC6953: Ultra-Low Jitter, JESD204B Clock Distributor with Eleven Programmable Outputs //! @} /*! @file @ingroup LTC6953 Library for LTC6953: Ultra-Low Jitter, JESD204B Clock Distributor with Eleven Programmable Outputs */ #include #include #include "Linduino.h" #include "UserInterface.h" #include "LT_SPI.h" #include "LTC6953.h" #include "QuikEval_EEPROM.h" #include uint8_t LTC6953_reg[LTC6953_NUM_REGADDR]; //!< number of LTC6953 spi addresses uint16_t LTC6953_spi_map[(LTC6953_NUM_REGFIELD+1)]; //!< LTC6953 spi map, AAAA AAAA RMMM NNNN; A= ADDR LOC, R=R or RW, M = MSB bit location, N = field length uint8_t LTC6953_lkup_tbl[2][LTC6953_NUM_REGADDR] = { //!< created with the LTC6952Wizard tool {0x25, 0x04, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x8c, 0xe0, 0x1b, 0x00, 0x40, 0x80, 0x24, 0x00, 0x8c, 0xe0, 0x1b, 0x00, 0x40, 0x80, 0x24, 0x00, 0x8c, 0xe0, 0x00, 0x00, 0x89, 0x80, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0xe0, 0x23, 0x00, 0x00, 0x80, 0x24, 0x00, 0x8c, 0xe0, 0x23, 0x00, 0x00, 0x80, 0x24, 0x00, 0x13}, //!< xxxx {0x25, 0x04, 0x00, 0xf0, 0xf0, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13}, //!< xxxx }; //!< LTC6953 Configuration look-up table /* ------------------------------------------------------------------------- FUNCTION: LTC6953_read - reads 8 bit Data field to LTC6953. - has to shift data by one bit to account for RW bit -------------------------------------------------------------------------- */ uint8_t LTC6953_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); LTC6953_reg[address]=rx.LT_byte[0]; return(rx.LT_byte[0]); } /* ------------------------------------------------------------------------- FUNCTION: LTC6953_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 LTC6953_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=LTC6953_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>bit_shift) &maskbits; return field_val; } /* ------------------------------------------------------------------------- FUNCTION: get_LTC6953_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_LTC6953_SPI_FIELD(uint8_t cs, uint8_t f) { uint8_t addrx; uint8_t dxmsb; uint8_t numbits; addrx = (LTC6953_spi_map[f] & 0xff00) >> 8; dxmsb = (LTC6953_spi_map[f] & 0x0070) >> 4; numbits = (LTC6953_spi_map[f] & 0x000f) + 1; return LTC6953_read_field(cs, addrx, dxmsb, numbits); #if INCLUDE_REGMAP return LTC6953_read_field(cs, LTC6953_spi_map[f][ADDRx], LTC6953_spi_map[f][DxMSB], LTC6953_spi_map[f][NUMBITS]); #endif } /* ------------------------------------------------------------------------- FUNCTION: LTC6953_write - writes 8 bit Data field to LTC6953. - has to shift data by one bit to account for RW bit --------------------------------------------------------------------------- */ void LTC6953_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: LTC6953_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 LTC6953_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=LTC6953_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]<> 8*(num_reg-1-i)) & 0xff; LTC6953_write(cs, (address+i), reg_val); } // end of for loop } // end of LTC6953_write_field /* ------------------------------------------------------------------------- FUNCTION: get_LTC6953_REGSIZE - returns # of addresses in parts register map (array size) ---------------------------------------------------------------------------- */ uint8_t get_LTC6953_REGSIZE(){ return sizeof(LTC6953_reg); } /* ------------------------------------------------------------------------- FUNCTION: get_LTC6953_SPI_FIELD_NUMBITS - returns the number of bits for a given field name in the SPI map ---------------------------------------------------------------------------- */ uint8_t get_LTC6953_SPI_FIELD_NUMBITS(uint8_t f) { uint8_t numbits; numbits = (LTC6953_spi_map[f] & 0x000f) + 1; return numbits; } /* ------------------------------------------------------------------------- FUNCTION: get_LTC6953_SPI_FIELD_RW - returns if the given field name is (0)read/write or (1)read_only field ---------------------------------------------------------------------------- */ uint8_t get_LTC6953_SPI_FIELD_RW(uint8_t f) { uint8_t rw_stat; rw_stat = (LTC6953_spi_map[f] & 0x0080) >> 7; return rw_stat; } /* ------------------------------------------------------------------------- FUNCTION: set_LTC6953_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_LTC6953_SPI_FIELD(uint8_t cs, uint8_t f, long field_data) { uint8_t addrx; uint8_t dxmsb; uint8_t numbits; addrx = (LTC6953_spi_map[f] & 0xff00) >> 8; dxmsb = (LTC6953_spi_map[f] & 0x0070) >> 4; numbits = (LTC6953_spi_map[f] & 0x000f) + 1; LTC6953_write_field(cs, field_data, addrx, dxmsb, numbits); #if INCLUDE_REGMAP LTC6953_write_field(cs, field_data, LTC6953_spi_map[f][ADDRx], LTC6953_spi_map[f][DxMSB], LTC6953_spi_map[f][NUMBITS]); #endif } /* ------------------------------------------------------------------------- FUNCTION: set_LTC6953_REGS_lkup_tbl - writes LTC6953 registers from a look up table - uses the Multi-byte write option for more efficient data transfer - Either SPI Syncs or recommends the use to SYNC pin based on RAO value --------------------------------------------------------------------------- */ void set_LTC6953_REGS_lkup_tbl(uint8_t lkup_tbl_row) { uint8_t val_temp, i; 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