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

Dependents:   t2d Thing2Do

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers SI570.cpp Source File

SI570.cpp

00001 /* mbed SI570 Library, for driving the SI570 programable VCXO
00002  * Copyright (c) 2010, Gerrit Polder, PA3BYA
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a copy
00005  * of this software and associated documentation files (the "Software"), to deal
00006  * in the Software without restriction, including without limitation the rights
00007  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00008  * copies of the Software, and to permit persons to whom the Software is
00009  * furnished to do so, subject to the following conditions:
00010  *
00011  * The above copyright notice and this permission notice shall be included in
00012  * all copies or substantial portions of the Software.
00013  *
00014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00017  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00018  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00019  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00020  * THE SOFTWARE.
00021  */
00022 
00023 #include "SI570.h"
00024 #include "mbed.h"
00025 
00026 SI570::SI570(PinName sda, PinName scl, int address)
00027         : _i2c(sda, scl) {
00028     _address = address;
00029     si570reset();
00030 }
00031 
00032 
00033 
00034 void SI570::si570reset(void) {
00035     _i2c.frequency(100000);
00036 
00037     cmd[0] = 135;            // reset
00038     cmd[1] = 0x01;           //
00039     _i2c.write(_address, cmd, 2); // Send command string
00040 
00041     get_registers();
00042     fxtal_device = (FOUT_START_UP * n1 * hsdiv) / rfreq; //MHz
00043 
00044     currentFreq = FOUT_START_UP;
00045 }
00046 
00047 void SI570::get_registers() {
00048 
00049     // Set pointer to location 7 (first echo)
00050     cmd[0] = 0x7;
00051     _i2c.write(_address, cmd, 1);
00052 
00053     _i2c.read(_address, buf, 6); // read the six-byte result
00054 
00055     // HS_DIV conversion
00056     hsdiv = ((buf[0] & 0xE0) >> 5) + 4; // get reg 7 bits 5, 6, 7
00057     // hsdiv's value could be verified here to ensure that it is one
00058     // of the valid HS_DIV values from the datasheet.
00059     // n1 conversion
00060     n1 = (( buf[0] & 0x1F ) << 2 ) + // get reg 7 bits 0 to 4
00061                  (( buf[1] & 0xC0 ) >> 6 );  // add with reg 8 bits 7 and 8
00062     if (n1 == 0) {
00063         n1 = 1;
00064     } else if (n1 & 1 != 0) {
00065         // add one to an odd number
00066         n1 = n1 + 1;
00067     }
00068 
00069     frac_bits = (( buf[2] & 0xF ) * POW_2_24 );
00070     frac_bits = frac_bits + (buf[3] * POW_2_16);
00071     frac_bits = frac_bits + (buf[4] * 256);
00072     frac_bits = frac_bits + buf[5];
00073 
00074     rfreq = frac_bits;
00075     rfreq = rfreq / POW_2_28;
00076     rfreq = rfreq + ( (( buf[1] & 0x3F ) << 4 ) + (( buf[2] & 0xF0 ) >> 4 ) );
00077 }
00078 
00079 
00080 double SI570::get_frequency(void) {
00081     get_registers();
00082     return (rfreq*fxtal_device)/(hsdiv*n1);
00083 }
00084 
00085 double SI570::get_rfreq(void) {
00086     get_registers();
00087     return rfreq;
00088 }
00089 
00090 int SI570::get_n1(void) {
00091     get_registers();
00092     return n1;
00093 }
00094 
00095 int SI570::get_hsdiv(void) {
00096     get_registers();
00097     return hsdiv;
00098 }
00099 
00100 int SI570::set_frequency(double frequency) {
00101     int err;
00102     float diff = 1000000 * (abs(frequency - currentFreq) / currentFreq);
00103     if (diff < PPM) {
00104         err = set_frequency_small_change(frequency);
00105       } else {
00106         err = set_frequency_large_change(frequency);
00107     }
00108     return err;
00109 }
00110 
00111 int SI570::set_frequency_small_change(double frequency) {
00112     unsigned char reg135;
00113     unsigned int whole;
00114     unsigned char counter;
00115     int i;
00116     char reg[6];
00117 
00118     rfreq = currentRfreq * frequency / currentFreq;
00119 
00120     cmd[0] = 0x8;
00121     _i2c.write(_address, cmd, 1);
00122     _i2c.read(_address, buf, 1); // read register 0x8
00123     reg[1] = buf[0];
00124     reg[2] = 0;
00125 
00126     // convert new RFREQ to the binary representation
00127     // separate the integer part
00128     whole = floor(rfreq);
00129     // get the binary representation of the fractional part
00130     frac_bits = floor((rfreq - whole) * POW_2_28);
00131     // set reg 12 to 10 making frac_bits smaller by
00132     // shifting off the last 8 bits everytime
00133     for (counter=5; counter >=3; counter--) {
00134         reg[counter] = frac_bits & 0xFF;
00135         frac_bits = frac_bits >> 8;
00136     }
00137     // set the last 4 bits of the fractional portion in reg 9
00138     reg[2] = SetBits(reg[2], 0xF0, (frac_bits & 0xF));
00139     // set the integer portion of RFREQ across reg 8 and 9
00140     reg[2] = SetBits(reg[2], 0x0F, (whole & 0xF) << 4);
00141     reg[1] = SetBits(reg[1], 0xC0, (whole >> 4) & 0x3F);
00142 
00143     // Load the new frequency
00144     // get the current state of register 137
00145     buf[0]=135;
00146     _i2c.write(_address, buf, 1);
00147     _i2c.read(_address, buf, 1);
00148     reg135 = buf[0];
00149 
00150     // set the Freeze M bit in that register
00151     buf[0]=135;
00152     buf[1]=reg135 | 0x20;
00153     _i2c.write(_address, buf, 2);
00154 
00155     // load the new values into the device at registers 8 to 12;
00156     buf[0]=8;
00157     for (i=1;i<6;i++) {
00158         buf[i]=reg[i];
00159     }
00160     _i2c.write(_address, buf, 6);
00161 
00162     // get the current state of register 135
00163     buf[0]=135;
00164     _i2c.write(_address, buf, 1);
00165     _i2c.read(_address, buf, 1);
00166     reg135 = buf[0];
00167     // clear the M bit in that register
00168     buf[0]=135;
00169     buf[1]= reg135 & 0xDF;
00170     _i2c.write(_address, buf, 2);
00171     
00172     return 0;
00173 }
00174 
00175 
00176 
00177 int SI570::set_frequency_large_change(double frequency) {
00178     const unsigned char HS_DIV[6] = {11, 9, 7, 6, 5, 4};
00179     int i;
00180 //    float ratio = 0;
00181     unsigned char counter;
00182     unsigned char reg137;
00183     char buf[7];
00184     char reg[6];
00185     unsigned int divider_max;
00186     unsigned int curr_div;
00187     unsigned int whole;
00188     unsigned char validCombo;
00189     float curr_n1;
00190     float n1_tmp;
00191 
00192     // find dividers (get the max and min divider range for the HS_DIV and N1 combo)
00193     divider_max = floor(FDCO_MAX / frequency); //floorf for SDCC
00194     curr_div = ceil(FDCO_MIN / frequency); //ceilf for SDCC
00195     validCombo = 0;
00196     while (curr_div <= divider_max) {
00197         //check all the HS_DIV values with the next curr_div
00198         for (counter=0; counter<6; counter++) {
00199             // get the next possible n1 value
00200             hsdiv = HS_DIV[counter];
00201             curr_n1 = (curr_div * 1.0) / (hsdiv * 1.0);
00202             // determine if curr_n1 is an integer and an even number or one
00203             // then it will be a valid divider option for the new frequency
00204             n1_tmp = floor(curr_n1);
00205             n1_tmp = curr_n1 - n1_tmp;
00206             if (n1_tmp == 0.0) {
00207                 //then curr_n1 is an integer
00208                 n1 = (unsigned char) curr_n1;
00209                 if ( (n1 == 1) || ((n1 & 1) == 0) ) {
00210                     // then the calculated N1 is either 1 or an even number
00211                     validCombo = 1;
00212                 }
00213             }
00214             if (validCombo == 1) break;
00215         }
00216         if (validCombo == 1) break;
00217         //increment curr_div to find the next divider
00218         //since the current one was not valid
00219         curr_div = curr_div + 1;
00220     }
00221 
00222     // if(validCombo == 0) at this point then there's an error
00223     // in the calculation. Check if the provided frequencies
00224     // are valid.
00225     if (validCombo == 0)
00226         return -1;
00227 
00228     rfreq = (frequency * n1 * hsdiv) / fxtal_device; //using float
00229     for (counter = 0; counter < 6; counter++) {
00230         reg[counter] = 0; //clear registers
00231     }
00232 
00233     // new HS_DIV conversion
00234     hsdiv = hsdiv - 4;
00235     //reset this memory
00236     reg[0] = 0;
00237     //set the top 3 bits of reg 13
00238     reg[0] = (hsdiv << 5);
00239     // convert new N1 to the binary representation
00240     if (n1 == 1) n1 = 0;
00241     else if ((n1 & 1) == 0) n1 = n1 - 1; //if n1 is even, subtract one
00242     // set reg 7 bits 0 to 4
00243     reg[0] = SetBits(reg[0], 0xE0, n1 >> 2);
00244     // set reg 8 bits 6 and 7
00245     reg[1] = (n1 & 3) << 6;
00246 
00247     // convert new RFREQ to the binary representation
00248     // separate the integer part
00249     whole = floor(rfreq);
00250     // get the binary representation of the fractional part
00251     frac_bits = floor((rfreq - whole) * POW_2_28);
00252     // set reg 12 to 10 making frac_bits smaller by
00253     // shifting off the last 8 bits everytime
00254     for (counter=5; counter >=3; counter--) {
00255         reg[counter] = frac_bits & 0xFF;
00256         frac_bits = frac_bits >> 8;
00257     }
00258     // set the last 4 bits of the fractional portion in reg 9
00259     reg[2] = SetBits(reg[2], 0xF0, (frac_bits & 0xF));
00260     // set the integer portion of RFREQ across reg 8 and 9
00261     reg[2] = SetBits(reg[2], 0x0F, (whole & 0xF) << 4);
00262     reg[1] = SetBits(reg[1], 0xC0, (whole >> 4) & 0x3F);
00263 
00264 
00265     // Load the new frequency
00266     // get the current state of register 137
00267     buf[0]=137;
00268     _i2c.write(_address, buf, 1);
00269     _i2c.read(_address, buf, 1);
00270     reg137 = buf[0];
00271 
00272     // set the Freeze DCO bit in that register
00273     buf[0]=137;
00274     buf[1]=reg137 | 0x10;
00275     _i2c.write(_address, buf, 2);
00276 
00277     // load the new values into the device at registers 7 to 12;
00278     buf[0]=7;
00279     for (i=1;i<7;i++) {
00280         buf[i]=reg[i-1];
00281     }
00282     _i2c.write(_address, buf, 7);
00283 
00284 
00285     // get the current state of register 137
00286     buf[0]=137;
00287     _i2c.write(_address, buf, 1);
00288     _i2c.read(_address, buf, 1);
00289     reg137 = buf[0];
00290     // clear the FZ_DCO bit in that register
00291     buf[0]=137;
00292     buf[1]= reg137 & 0xEF;
00293     _i2c.write(_address, buf, 2);
00294 
00295 
00296     // set the NewFreq bit, bit will clear itself once the device is ready
00297     buf[0]=135;
00298     buf[1]= 0x40;
00299     _i2c.write(_address, buf, 2);
00300 
00301     currentFreq = frequency;
00302     currentRfreq = rfreq;
00303     return 0;
00304 }
00305 
00306 
00307 unsigned char SI570::SetBits(unsigned char original, unsigned char reset_mask, unsigned char new_val) {
00308     return (( original & reset_mask ) | new_val );
00309 }
00310