Saturday, April 19, 2014

Si570 Programming

Messing around with the Si570 chip that is used in the Minima transceiver designed by Farhan Akhtar.  This is my lash-up:


After a bit of tweaking, I have the oscillator working.  It is no picnic trying to interpret the Si570 datasheet however.

Interestingly enough most of the code implementations I have looked at for the Arduino make a couple of errors in interpretation of the datasheet.  The Si570 is not really fun to program, but it has a number of benefits going for it.

My particular chip is a 10-160 MHz CMOS model.  When I first power it up, it comes up at 56.320 MHz.  The datasheet is not explicit on this, but supposedly the individual parts are "tuned" at the factory to their default frequency by adjusting the various parameters that are used to set the frequency.  However, the datasheet does not provide very flattering stability numbers and nowhere does it provide a statement of how closely it is calibrated in the factory.

So, in the absence of highly accurate frequency measurement capabilities you are faced with having to calculate the internal crystal frequency based on the default output frequency (from the datasheet) after reading the frequency setting parameters from the chip itself.

The Si570 requires mathematical calculations with 38 bits of precision in order to accurately set it's frequency.  Most Arduino implementations rely on double precision floating point math functionality.  However, the Arduino implementation of double precision is in fact identical to its single precision floating point.  Namely double = float data types silently behind the scenes.  So, most of these implementations give up on the number of digits of precision required to control the Si570 even down to the 1 Hz level.  Theoretically you should be able to set the frequency to within .42 Hz, but the unfortunate floating point double precision implementation on Arduino prevents even 1 Hz resolution.

The Si570 stores a reference frequency used in the output frequency calculation as a 38 bit values where 10 bits are use as a decimal multiplier.  The remaining 28 bits are a fractional multiplier stored as an integer value scaled by 2^28.

The internal DCO is constrained to 4.85 to 5.67 GHz determined by the internal crystal frequency multiplied by a reference frequency.  According to the settings in my chip, I calculate my crystal frequency to be 114.252365 MHz.  Secondarily, frequency excursions of the DCO can be done in a phase continuous manner if the change in frequency is no more than +/- 3500 PPM.  Many implementations however are calculating this based on the output frequency rather than the DCO frequency in error.  Frequency excursions larger than this require the DCO to be stopped and restarted in a non-phase continuous manner that can take upwards of 10 ms to complete.  Circuitry sensitive to runt pulses may require resetting after this operation.

The output frequency is the DCO frequency divided by the product of two divisors.  There are additional restrictions on these two divisors.

More to follow...




6 comments:

  1. Jeff, How did you get the Si570 plugged into the protoboard? Small wires soldered to the back?

    ReplyDelete
    Replies
    1. Yes, I just soldered wires to it and pushed them into the proto board. I have a couple of the chips and I figured one of them could be done this way. I plan to build a PCB for the other one, but didn't have the time for building the board when focused right now on the programming details.

      Delete
  2. Hi Jeff, downloaded your code from github, but I see no example code? What would the the typical initialization? new Si750(xxx, yyy);

    ReplyDelete
    Replies
    1. Hello JEG.

      The library is quite simple actually. You need to include the header file in the sketch that wishes to use the Si570. The I2C address is defined here and you define an instance of a pointer to the Si570 object. Here I call it m_dds. These are just snippits of the code to give you an idea of how to get it oscillating as opposed to a debugged example. I am happy to help if you run into difficulties.

      #include "si570.h"

      #define SI570_I2C_ADDRESS (0x55)

      Si570 *m_dds;

      The Si570 library automatically reads the factory calibration settings of your Si570 but it needs to know for what frequency it was calibrated for. Looks like most HAM CMOS versions of the Si570 are calibrated for 56.320 Mhz. If yours was calibrated for another frequency, you need to change that here This creates an instance of the Si570 object, tells the library the I2C address and calibrated power-on frequency. The address of the new object is assigned to your m_dds variable above.

      m_dds = new Si570(SI570_I2C_ADDRESS, 56320000);

      Check for errors initializing the Si570

      if (m_dds == NULL || m_dds->status == SI570_ERROR)
      {
      // The Si570 is unreachable.
      Serial.println("Si570 comm error");
      }


      The Si570 will come up at 56.32 MHz. This function will set a new frequency expressed in Hz.

      void setFrequency(uint32_t freq)
      {
      static uint32_t lastFreq = 0;

      If this function is called in the main loop of your program, you really don't want to continually set the same frequency over and over again. So, keep track of the last set frequency and just return if it has not changed.

      if (freq == lastFreq) return;
      lastFreq = freq;

      If you are going to perform some calibration of your device's frequency, it is appropriate to store or calculate it as some offset from the given frequency. This offset should be applied before setting the Si570. Typically the calibrated frequency would be the displayed frequency plus some offset.

      int calOffset = 0; // None for this example
      status = (oscStatus_t) m_dds->setFrequency(freq + calOffset);
      }

      That should be enough to get you started. Again I am happy to help if you get stuck. Drop me a note at ko7m at arrl dot net by email and we can work out the details. Cheers.

      Delete
  3. You have couple of bugs in you Si570 library

    rfreq = rfreq + ((rfreq & 1<<(28-1))<<1);

    needs to be (1L), the compiler complains about the bit shift > size of the variable. (Arduino 1.8.13) (20 Aug 2021)

    rfreq = rfreq + ((rfreq & 1L<<(28-1))<<1);


    Secondly

    for (int i = 7; i < 15; i++)

    needs to be

    for (int i = 7; i < 13; i++)

    as the dco_reg array has elements only from 0 thru 12 i.e. dco_reg[13]

    ReplyDelete
    Replies
    1. Thank you very kindly for pointing out these errors as you are absolutely correct. I had not noticed as I had in my local copy commented out the rounding calculation and debug output turned off so I was not seeing either the compile warning or that it was trying to print too many dco_reg elements.

      I checked a project where I used this code and it had the 1L fix you propose. Sigh... Very sorry that I failed to update the github repository with either of these fixes. I have now made the changes and pushed them to github. Thanks again!

      Delete