/******************************************************************************
     XTAL_Calib.c    calibrates a PIC XTAL against GPS 1 second pulse
     
     The 1pps input is on PIC pin RC2, and period is captured using
     TMR1 CCP1 and a math process to derive MHz from the 2 16bit
     capture values while simply ignoring 16bit overflows.

     The display shows;
     (line1) the error in timer1 ticks (is expressed in PPM for a 4MHz xtal)
     (line2) actual xtal frequency in Hz
     
     MCU:             P16F887
     Dev.Board:       EasyPIC6
     Oscillator:      XT, 4 MHz xtal
     Ext. Modules:    LCD 2x16, GPS device with 1pps output pulse (SmartGPS)
     SW:              mikroC v7.0

******************************************************************************/
// global vars
#define XTAL_HZ 4000000    // user must enter this value
#define TIMER_HZ (XTAL_HZ / 4)
#define TIMER_MOD (TIMER_HZ % 65536)
unsigned int capture_last, capture_new, difference;
signed int xerror;
unsigned long real_timer_freq;
signed long real_xtal_freq;
unsigned char txt[14];  // for text string display

#include "RomanLCD.c"   // my 2x16 text LCD functions for EasyPIC6
//-----------------------------------------------------------------------------

//=============================================================================
//   MAIN 
//=============================================================================
void main()
{
  //----------------------------------------------------
  // setup the PIC 16F887
  ANSEL  = 0;             // Configure all PIC pins as digital
  ANSELH = 0;
  TRISB = 0b00000000; 	  // PORTB all outputs for LCD
  PORTC = 0;
  TRISC = 0b00000100; 	  // RC2 is connected to 1pps capture input
  TRISD = 0b11111111;
  
  // TMR1 (used to capture input period)
  T1CON =   0b00000001;  // TMR1 ON, 1:1 prescale
  CCP1CON = 0b00000101;  // CCP1, capture mode, every rising edge

  //----------------------------------------------------
  // setup LCD (using Romans LCD functions for EasyPIC6)
  RomanLCD_Init();
  // draw fixed text on LCD
  RomanLCD_Out(0,0,"Error         ti");
  RomanLCD_Out(1,0,"              Hz");

  // setup vars etc
  PIR1.CCP1IF = 0;   // clear capture flag
  
  //----------------------------------------------------
  // main run loop here. Every loop we measure the incoming 1pps signal
  // as a period and display the error and xtal Hz to the LCD.
  // NOTE! if there is no 1pps signal the PIC will appear to "hang"
  // until signal resumes.
  while(1)
  {
    //-----------------------------------------
    while(!PIR1.CCP1IF);                        // wait for 1pps input capture
    capture_new = CCPR1L + (CCPR1H * 256);      // get the 16bit capture 
    PIR1.CCP1IF = 0;                            // clear capture flag
    difference = (capture_new - capture_last);  // get diff between captures
    capture_last = capture_new;                 // store a capture copy for next time
    xerror = (difference - TIMER_MOD);          // get error compared to "perfect" xtal
    real_timer_freq = (TIMER_HZ + xerror);      // add error to timer
    real_xtal_freq = (real_timer_freq * 4);     // convert back to xtal Hz

    // format the Hz error value into a text string and display it
    IntToStr(xerror,txt);             // format into text
    RomanLCD_Out(0,7,txt);            // display it
    // format the xtal Hz value into a text string and display it
    LongToStr(real_xtal_freq,txt);    // format into text
    RomanLCD_Out(1,2,txt);            // display it
    RomanLCD_Out(1,0,"XTAL");
  }  
}
//-----------------------------------------------------------------------------

