/*********************************  dco.c  ***********************************
 * $Source: /home/cvs/ESP/gen2/software/msp430/lib/common/dco.c,v $
 *  Copyright (C) 2003 MBARI
 *
 *  MBARI Proprietary Information. All rights reserved.
 * $Id: dco.c,v 1.4 2003/12/09 00:56:24 brent Exp $
 *
 * Primitives for manipulating the MSP430's
 * Digitally Controlled clock Oscillator
 *
 *****************************************************************************/

#include "dco.h"


int adjustDCO (unsigned target, unsigned actual)
/*
  adjust the DCO paramters toward specified target frequency
  given the actual measured frequency
  
  returns -2 if target too low
          -1 if target too high
          0 if adjustment should be repeated
          1 if target frequency has been reached
*/

#define MOD   32    //# of modulation steps between real frequencies
#define fStep 12    //approx. % delta freqency between steps

#define DCO_MIN 5     //below this value, select lower Rsel
#define DCO_MAX 0xE2  //above this value, select higher Rsel

{
  unsigned denominator = actual/MOD, den4 = denominator/4;
  int newDCO, err = target - actual;     //frequency err
  err *= 100/fStep;              //rescale error
  if (err>=0) err+=den4; else err-=den4;  //round up/down if within 1/4 step
  //to avoid limit cycling about target, don't simply round to nearest
  err /= denominator;  //error in approximate number of DCO modulation steps
  if (!err)
            return 1;  //success
  newDCO = err + DCOCTL;   //new = err + current DCO
  if (newDCO > 0xff) {
    if ((BCSCTL1 & 0x7) == 0x7) {
      DCOCTL = 0xff;
      return -1;
    }
    BCSCTL1++;         //try next higher frequency range
    DCOCTL = DCO_MIN;
  }else if (newDCO < 0) {
    if (!(BCSCTL1 & 0x7)) { 
      DCOCTL = 0;
      return -2;
    }
    --BCSCTL1;         //try next lower frequency range
    DCOCTL = DCO_MAX;
  }else
    DCOCTL = newDCO;
  return 0;
}


unsigned setDCO(unsigned target)      // Set DCO to target frequency
/*
  Set the MCLK to target frequency multiple of ACLK
  It uses timer A and its associated capture register #2
  Interrupts should be disabled
  returns the actual frequency attained (in MCLKS/ACLK)
*/
{
  unsigned capture,smclks, try = 100;  //quit after at most 100 tries
  
  CCTL2 = CM_1 | CCIS_1 | CAP;        // CAP, ACLK
  TACTL = TASSEL_2 | MC_2 | TACLR;    // SMCLK, cont-mode, clear
  do {
    while (!(CCIFG & CCTL2));      // Wait until first rising edge of ACLK
    CCTL2 &= ~CCIFG;               // Capture occured, clear flag
    capture = CCR2;                // Save initial captured SMCLK
    while (!(CCIFG & CCTL2));      // Wait until next rising edge of ACLK
    CCTL2 &= ~CCIFG;               // Capture occured, clear flag
    smclks=CCR2 - capture;         // measured frequency in smclks
  } while (!adjustDCO (target, smclks) && --try);
  TACTL = CCTL2 = 0;       // Stop CCR2 & Timer_A      
  return smclks;
}

