Library to control Silicon Labs SI570 10 MHZ TO 1.4 GHZ I2C PROGRAMMABLE XO/VCXO.

Dependents:   t2d Thing2Do

This is the page for the Silicon Laboratories Si570 frequency synthesizer, with I2C interface.

The Si570 XO/Si571 VCXO utilizes Silicon Laboratories’ advanced DSPLL® circuitry to provide a low-jitter clock at any frequency. The Si570/Si571 are user-programmable to any output frequency from 10 to 945 MHz and select frequencies to 1400 MHz with <1 ppb resolution. The device is programmed via an I2C serial interface. Unlike traditional XO/VCXOs where a different crystal is required for each output frequency, the Si57x uses one fixed- frequency crystal and a DSPLL clock synthesis IC to provide any-frequency operation. This IC-based approach allows the crystal resonator to provide exceptional frequency stability and reliability. In addition, DSPLL clock synthesis provides superior supply noise rejection, simplifying the task of generating low-jitter clocks in noisy environments typically found in communication systems.

The Si570 is very popular for amateur radio use. It can be used as the local oscillator in a superheterodyne receiver, or it can be the local oscillator in a direct conversion quadrature receiver for software defined radio (SDR) such as the Softrock. In addition to its use inside a receiver, the Si570 kit can be used as a stand-alone signal source for test and measurement and for other purposes, such as a VFO for your old Heathkit DX40 for that matter. Just keep in mind that the Si570's output is a square wave and may require additional filtering for some purposes.

Image of the SI570 in action

/media/uploads/soldeerridder/si570_sr.jpg

Hello World!

 #include "mbed.h"
 #include "TextLCD.h"
 #include "SI570.h"
 #include "QEI.h"
 
 TextLCD lcd(p11, p12, p15, p16, p29, p30); // rs, e, d0-d3
 SI570 si570(p9, p10, 0xAA);
 QEI wheel (p5, p6, NC, 360);
 
 int main() {
     int wp,swp=0;
     float startfreq=7.0;
     float freq;
 
     while (1) {
         wp =  wheel.getPulses();
         freq=startfreq+wp*0.00001;
         if (swp != wp) {
             si570.set_frequency(freq);
             swp = wp;
         }                
         lcd.locate(0,0);
         lcd.printf("%f MHz", si570.get_frequency());
     }
 }

Links

Reference

Files at this revision

API Documentation at this revision

Comitter:
soldeerridder
Date:
Tue Nov 09 20:56:52 2010 +0000
Commit message:
added documentation

Changed in this revision

SI570.cpp Show annotated file Show diff for this revision Revisions of this file
SI570.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SI570.cpp	Tue Nov 09 20:56:52 2010 +0000
@@ -0,0 +1,310 @@
+/* mbed SI570 Library, for driving the SI570 programable VCXO
+ * Copyright (c) 2010, Gerrit Polder, PA3BYA
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "SI570.h"
+#include "mbed.h"
+
+SI570::SI570(PinName sda, PinName scl, int address)
+        : _i2c(sda, scl) {
+    _address = address;
+    si570reset();
+}
+
+
+
+void SI570::si570reset(void) {
+    _i2c.frequency(100000);
+
+    cmd[0] = 135;            // reset
+    cmd[1] = 0x01;           //
+    _i2c.write(_address, cmd, 2); // Send command string
+
+    get_registers();
+    fxtal_device = (FOUT_START_UP * n1 * hsdiv) / rfreq; //MHz
+
+    currentFreq = FOUT_START_UP;
+}
+
+void SI570::get_registers() {
+
+    // Set pointer to location 7 (first echo)
+    cmd[0] = 0x7;
+    _i2c.write(_address, cmd, 1);
+
+    _i2c.read(_address, buf, 6); // read the six-byte result
+
+    // HS_DIV conversion
+    hsdiv = ((buf[0] & 0xE0) >> 5) + 4; // get reg 7 bits 5, 6, 7
+    // hsdiv's value could be verified here to ensure that it is one
+    // of the valid HS_DIV values from the datasheet.
+    // n1 conversion
+    n1 = (( buf[0] & 0x1F ) << 2 ) + // get reg 7 bits 0 to 4
+                 (( buf[1] & 0xC0 ) >> 6 );  // add with reg 8 bits 7 and 8
+    if (n1 == 0) {
+        n1 = 1;
+    } else if (n1 & 1 != 0) {
+        // add one to an odd number
+        n1 = n1 + 1;
+    }
+
+    frac_bits = (( buf[2] & 0xF ) * POW_2_24 );
+    frac_bits = frac_bits + (buf[3] * POW_2_16);
+    frac_bits = frac_bits + (buf[4] * 256);
+    frac_bits = frac_bits + buf[5];
+
+    rfreq = frac_bits;
+    rfreq = rfreq / POW_2_28;
+    rfreq = rfreq + ( (( buf[1] & 0x3F ) << 4 ) + (( buf[2] & 0xF0 ) >> 4 ) );
+}
+
+
+double SI570::get_frequency(void) {
+    get_registers();
+    return (rfreq*fxtal_device)/(hsdiv*n1);
+}
+
+double SI570::get_rfreq(void) {
+    get_registers();
+    return rfreq;
+}
+
+int SI570::get_n1(void) {
+    get_registers();
+    return n1;
+}
+
+int SI570::get_hsdiv(void) {
+    get_registers();
+    return hsdiv;
+}
+
+int SI570::set_frequency(double frequency) {
+    int err;
+    float diff = 1000000 * (abs(frequency - currentFreq) / currentFreq);
+    if (diff < PPM) {
+        err = set_frequency_small_change(frequency);
+      } else {
+        err = set_frequency_large_change(frequency);
+    }
+    return err;
+}
+
+int SI570::set_frequency_small_change(double frequency) {
+    unsigned char reg135;
+    unsigned int whole;
+    unsigned char counter;
+    int i;
+    char reg[6];
+
+    rfreq = currentRfreq * frequency / currentFreq;
+
+    cmd[0] = 0x8;
+    _i2c.write(_address, cmd, 1);
+    _i2c.read(_address, buf, 1); // read register 0x8
+    reg[1] = buf[0];
+    reg[2] = 0;
+
+    // convert new RFREQ to the binary representation
+    // separate the integer part
+    whole = floor(rfreq);
+    // get the binary representation of the fractional part
+    frac_bits = floor((rfreq - whole) * POW_2_28);
+    // set reg 12 to 10 making frac_bits smaller by
+    // shifting off the last 8 bits everytime
+    for (counter=5; counter >=3; counter--) {
+        reg[counter] = frac_bits & 0xFF;
+        frac_bits = frac_bits >> 8;
+    }
+    // set the last 4 bits of the fractional portion in reg 9
+    reg[2] = SetBits(reg[2], 0xF0, (frac_bits & 0xF));
+    // set the integer portion of RFREQ across reg 8 and 9
+    reg[2] = SetBits(reg[2], 0x0F, (whole & 0xF) << 4);
+    reg[1] = SetBits(reg[1], 0xC0, (whole >> 4) & 0x3F);
+
+    // Load the new frequency
+    // get the current state of register 137
+    buf[0]=135;
+    _i2c.write(_address, buf, 1);
+    _i2c.read(_address, buf, 1);
+    reg135 = buf[0];
+
+    // set the Freeze M bit in that register
+    buf[0]=135;
+    buf[1]=reg135 | 0x20;
+    _i2c.write(_address, buf, 2);
+
+    // load the new values into the device at registers 8 to 12;
+    buf[0]=8;
+    for (i=1;i<6;i++) {
+        buf[i]=reg[i];
+    }
+    _i2c.write(_address, buf, 6);
+
+    // get the current state of register 135
+    buf[0]=135;
+    _i2c.write(_address, buf, 1);
+    _i2c.read(_address, buf, 1);
+    reg135 = buf[0];
+    // clear the M bit in that register
+    buf[0]=135;
+    buf[1]= reg135 & 0xDF;
+    _i2c.write(_address, buf, 2);
+    
+    return 0;
+}
+
+
+
+int SI570::set_frequency_large_change(double frequency) {
+    const unsigned char HS_DIV[6] = {11, 9, 7, 6, 5, 4};
+    int i;
+//    float ratio = 0;
+    unsigned char counter;
+    unsigned char reg137;
+    char buf[7];
+    char reg[6];
+    unsigned int divider_max;
+    unsigned int curr_div;
+    unsigned int whole;
+    unsigned char validCombo;
+    float curr_n1;
+    float n1_tmp;
+
+    // find dividers (get the max and min divider range for the HS_DIV and N1 combo)
+    divider_max = floor(FDCO_MAX / frequency); //floorf for SDCC
+    curr_div = ceil(FDCO_MIN / frequency); //ceilf for SDCC
+    validCombo = 0;
+    while (curr_div <= divider_max) {
+        //check all the HS_DIV values with the next curr_div
+        for (counter=0; counter<6; counter++) {
+            // get the next possible n1 value
+            hsdiv = HS_DIV[counter];
+            curr_n1 = (curr_div * 1.0) / (hsdiv * 1.0);
+            // determine if curr_n1 is an integer and an even number or one
+            // then it will be a valid divider option for the new frequency
+            n1_tmp = floor(curr_n1);
+            n1_tmp = curr_n1 - n1_tmp;
+            if (n1_tmp == 0.0) {
+                //then curr_n1 is an integer
+                n1 = (unsigned char) curr_n1;
+                if ( (n1 == 1) || ((n1 & 1) == 0) ) {
+                    // then the calculated N1 is either 1 or an even number
+                    validCombo = 1;
+                }
+            }
+            if (validCombo == 1) break;
+        }
+        if (validCombo == 1) break;
+        //increment curr_div to find the next divider
+        //since the current one was not valid
+        curr_div = curr_div + 1;
+    }
+
+    // if(validCombo == 0) at this point then there's an error
+    // in the calculation. Check if the provided frequencies
+    // are valid.
+    if (validCombo == 0)
+        return -1;
+
+    rfreq = (frequency * n1 * hsdiv) / fxtal_device; //using float
+    for (counter = 0; counter < 6; counter++) {
+        reg[counter] = 0; //clear registers
+    }
+
+    // new HS_DIV conversion
+    hsdiv = hsdiv - 4;
+    //reset this memory
+    reg[0] = 0;
+    //set the top 3 bits of reg 13
+    reg[0] = (hsdiv << 5);
+    // convert new N1 to the binary representation
+    if (n1 == 1) n1 = 0;
+    else if ((n1 & 1) == 0) n1 = n1 - 1; //if n1 is even, subtract one
+    // set reg 7 bits 0 to 4
+    reg[0] = SetBits(reg[0], 0xE0, n1 >> 2);
+    // set reg 8 bits 6 and 7
+    reg[1] = (n1 & 3) << 6;
+
+    // convert new RFREQ to the binary representation
+    // separate the integer part
+    whole = floor(rfreq);
+    // get the binary representation of the fractional part
+    frac_bits = floor((rfreq - whole) * POW_2_28);
+    // set reg 12 to 10 making frac_bits smaller by
+    // shifting off the last 8 bits everytime
+    for (counter=5; counter >=3; counter--) {
+        reg[counter] = frac_bits & 0xFF;
+        frac_bits = frac_bits >> 8;
+    }
+    // set the last 4 bits of the fractional portion in reg 9
+    reg[2] = SetBits(reg[2], 0xF0, (frac_bits & 0xF));
+    // set the integer portion of RFREQ across reg 8 and 9
+    reg[2] = SetBits(reg[2], 0x0F, (whole & 0xF) << 4);
+    reg[1] = SetBits(reg[1], 0xC0, (whole >> 4) & 0x3F);
+
+
+    // Load the new frequency
+    // get the current state of register 137
+    buf[0]=137;
+    _i2c.write(_address, buf, 1);
+    _i2c.read(_address, buf, 1);
+    reg137 = buf[0];
+
+    // set the Freeze DCO bit in that register
+    buf[0]=137;
+    buf[1]=reg137 | 0x10;
+    _i2c.write(_address, buf, 2);
+
+    // load the new values into the device at registers 7 to 12;
+    buf[0]=7;
+    for (i=1;i<7;i++) {
+        buf[i]=reg[i-1];
+    }
+    _i2c.write(_address, buf, 7);
+
+
+    // get the current state of register 137
+    buf[0]=137;
+    _i2c.write(_address, buf, 1);
+    _i2c.read(_address, buf, 1);
+    reg137 = buf[0];
+    // clear the FZ_DCO bit in that register
+    buf[0]=137;
+    buf[1]= reg137 & 0xEF;
+    _i2c.write(_address, buf, 2);
+
+
+    // set the NewFreq bit, bit will clear itself once the device is ready
+    buf[0]=135;
+    buf[1]= 0x40;
+    _i2c.write(_address, buf, 2);
+
+    currentFreq = frequency;
+    currentRfreq = rfreq;
+    return 0;
+}
+
+
+unsigned char SI570::SetBits(unsigned char original, unsigned char reset_mask, unsigned char new_val) {
+    return (( original & reset_mask ) | new_val );
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SI570.h	Tue Nov 09 20:56:52 2010 +0000
@@ -0,0 +1,150 @@
+/* mbed SI570 Library, for driving the SI570 programable VCXO
+ * Copyright (c) 2010, Gerrit Polder, PA3BYA
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef MBED_SI570_H
+#define MBED_SI570_H
+
+#include "mbed.h"
+
+//these must be floating point number especially 2^28 so that
+//there is enough memory to use them in the calculation
+#define POW_2_16              65536.0
+#define POW_2_24           16777216.0
+#define POW_2_28          268435456.0
+#define FOUT_START_UP            56.320     //MHz 
+#define PPM                    3500         // +/- max ppm from center frequency
+#define FDCO_MAX                5670.0         //MHz
+#define FDCO_MIN                4850.0         //MHz
+
+
+/** Interface to the popular SI570 VCXO  */
+class SI570 {
+public:
+    /** Create an instance of the SI570 connected to specified I2C pins, with the specified address.
+     *
+     * Example:
+     * @code
+     * #include "mbed.h"
+     * #include "TextLCD.h"
+     * #include "SI570.h"
+     * #include "QEI.h"
+     * 
+     * TextLCD lcd(p11, p12, p15, p16, p29, p30); // rs, e, d0-d3
+     * SI570 si570(p9, p10, 0xAA);
+     * QEI wheel (p5, p6, NC, 360);
+     *
+     * int main() {
+     *     int wp,swp=0;
+     *     float startfreq=7.0;
+     *     float freq;
+     *
+     *     while (1) {
+     *         wp =  wheel.getPulses();
+     *         freq=startfreq+wp*0.00001;
+     *         if (swp != wp) {
+     *             si570.set_frequency(freq);
+     *             swp = wp;
+     *         }                
+     *         lcd.locate(0,0);
+     *         lcd.printf("%f MHz", si570.get_frequency());
+     *     }
+     * }
+     * @endcode
+     *
+     * @param sda The I2C data pin
+     * @param scl The I2C clock pin
+     * @param address The I2C address for this SI570
+     */
+    SI570(PinName sda, PinName scl, int address);
+
+    /** Resets the SI570.
+     *
+     * Resets and reads the startup configuration from the Si570
+     */
+    void si570reset(void);
+    
+   /** Read the current frequency.
+     *
+     * get the SI570 registers an calculate the current frequency
+     *
+     * @returns the current frequency   
+     */
+    double get_frequency(void);
+
+   /** Read the current rfreq value.
+     *
+     * get the SI570 registers an calculate the current rfreq
+     *
+     * @returns the current rfreq   
+     */    
+    double get_rfreq(void);
+    
+    /** Read the current n1.
+     *
+     * get the SI570 registers an calculate the current n1
+     *
+     * @returns the current n1   
+     */
+    int get_n1(void);
+    
+   /** Read the current hsdiv.
+     *
+     * get the SI570 registers an calculate the current hsdiv
+     *
+     * @returns the current hsdiv   
+     */
+    int get_hsdiv(void); 
+    
+   /** Set a new frequency.
+     *
+     * Set the SI570 registers to output frequency
+     * When the new frequency is within the 3500 PPM range of the center frequency, hsdiv and n1 needs not to be reprogrammed, resulting in a glitch free transition.
+     * For changes larger than 3500 PPM, the process of freezing and unfreezing the DCO will cause the output clock to momentarily stop and start at any arbitrary point during a clock cycle. This process can take up to 10 ms.
+     *
+     * @param frequency the frequency to set.
+     * @returns the current frequency   
+     */   
+    int set_frequency(double frequency);
+
+
+private:
+    I2C _i2c;
+    int _address;
+    char cmd[2];
+    char buf[6];
+    unsigned char n1;
+    unsigned char hsdiv;
+    unsigned long frac_bits;
+    double rfreq;
+    double fxtal_device;
+    double currentFreq;
+    double currentRfreq;
+
+    void get_registers(void);
+    int set_frequency_small_change(double currentFrequency);
+    int set_frequency_large_change(double currentFrequency);
+
+    unsigned char SetBits(unsigned char original, unsigned char reset_mask, unsigned char new_val);
+};
+
+
+#endif
\ No newline at end of file