// Capture1.C
//
// Illustrates the use of Timer1 and Input Capture to continually measure the duty
// of a PWM inout on CCP1 (RB3)
//
// In function meas_pwm_ratio, Timer 1 is configured for internal clock, 1:1 prescale.
// Thus, 1 usce per click.  The CCP module is configured for interrupt on the falling edge.
//
// This is repeated for the next rising input and then falling.  Thus three CCP1 interrupts.
//
// The t_low and t_period and ratio_100 (percent) is calculated and displayed.
//
// If Timer1 times out (65 ms) without the three CCP1 ints , it is assummed there is no
// valid signal on the CCP1 input.
//
// Copyright, Peter H. Anderson, Baltimore, MD, Nov, 11

#case

#device PIC16F1827 *=16 ICD=TRUE

#include <defs_1827.h>

#fuses INTRC_IO, WDT, PUT, MCLR, PROTECT, CPD, NOBROWNOUT, NOCLKOUT
#fuses NOIESO, NOFCMEN, WRT, PLL_SW, STVREN, DEBUG, NOLVP

#define TRUE !0
#define FALSE 0

#define MAKE_LONG(h, l)  (((long) h) << 8) | (l)

unsigned int meas_pwm_ratio(byte *p_success);
void asynch_setup(byte pol);
void ser_char(char ch);

void delay_10us(byte t);
void delay_ms(long t);
void delay_s(long t);

byte tmr1_int_occ, capture_int_occ;

void main(void)
{
   byte success;
   unsigned ratio_100;

   ANSELA = 0x00; // unassign A/Ds
   ANSELB = 0x00;
   OSCCON = 0x6a; // 4.0 MHz internal

   txcksel = 0;  // TX assigned to RB2
   ccp1sel = 0;  // CCP1 assigned to RB3

   wpuen_ = 0;
   wpub3 = 1; // pullup on RB3, CCP1

   asynch_setup(0);

   delay_ms(1000);
   printf(ser_char, "?bTesting ...\n\r");

   while(1)
   {
      ratio_100 = meas_pwm_ratio(&success);
      if (success)
      {
        printf(ser_char, "?n%02u", ratio_100);
      }
      else
      {
         printf(ser_char, "?nInvalid");
      }
      delay_ms(1000);
   }
}

unsigned int meas_pwm_ratio(byte *p_success)
{
   byte is_valid = TRUE;
   unsigned long t1, t2, t3, t_on, t_period;
   byte ratio_100;


   // fire up timer 1
   tmr1cs1 = 0;  tmr1cs0 = 0;      // 1 usec at 4.0 MHz
   t1ckps1 = 0;  t1ckps0 = 0;      // prescale 1:1

   TMR1H = 0x00;               // set Timer 1 to 0
   TMR1L = 0x00;

   tmr1if = 0;
   ccp1if = 0;                  // kill any old interrupts

   tmr1on = 1;      // get it going
   tmr1_int_occ = FALSE;

   tmr1ie = 1;
   ccp1ie = 1;
   peie = 1;
   gie = 1;

   // It would be more elegant to do this in a for loop.  But, this is prtty clear.
   CCP1CON = 0x04;               // interrupt on falling edge
   while(1)
   {
#asm
      CLRWDT
#endasm
      if (tmr1_int_occ)         // if a timer 1 interrupt
      {
         tmr1_int_occ = FALSE;
         is_valid = FALSE;
         break;
      }
      if (capture_int_occ)      // if an input capture interrupt
      {
         t1 = MAKE_LONG(CCPR1H, CCPR1L);
         capture_int_occ = FALSE;
         break;
       }
    }

    CCP1CON = 0x05;
    while(1)    // now for the rising edge
    {
#asm
      CLRWDT
#endasm
      if(!is_valid)
      {
         break;
      }

      if (tmr1_int_occ)
      {
         tmr1_int_occ = FALSE;
         is_valid = FALSE;
         break;

       }

       if (capture_int_occ)
       {
         t2 = MAKE_LONG(CCPR1H, CCPR1L);
         capture_int_occ = FALSE;
         break;
      }
    }

    CCP1CON = 0x04;

    while(1)    // now for the falling edge
    {
#asm
      CLRWDT
#endasm
      if(!is_valid)
      {
         break;
      }

      if(tmr1_int_occ)
      {
         tmr1_int_occ = FALSE;
         is_valid = FALSE;
         break;

       }

       if (capture_int_occ)
       {
         t3 = MAKE_LONG(CCPR1H, CCPR1L);
         capture_int_occ = FALSE;
         break;
      }
    }

    while(gie)
    {
      gie = 0;
    }
    tmr1ie = 0;
    ccp1ie = 0;

    if (is_valid)
    {
      t_on = t2 - t1;  t_period = t3 - t1;
      ratio_100 = t_on / (t_period / 100);
      *p_success = TRUE;
    }

    else
    {
      *p_success = FALSE;
      ratio_100 = 0; // dummy value
   }

   return(ratio_100);
}

void asynch_setup(byte pol)
{
    trisb2 = 1; // Is this right


    if (pol == 1)
    {
       sckp = 1; // non inverted
    }
    else
    {
      sckp = 0;
   }

    spen = 1;     // enable serial port
    sync = 0;     // asynchronous
    txen = 1;     // enable transmitter

    brgh = 1;
    brg16 = 1;
    SPBRGH = 0x00;
    SPBRGL = 103; // 9600 at 4.0 MHz clock
}

void ser_char(char ch) // no interrupts
{
   long n = 1000;

   delay_ms(5);
   while(!txif && --n)   /* txif goes to one when buffer is empty */
   {
      delay_10us(1);
   }
   TXREG = ch;       //  This clears txif
}
void delay_s(long t)
{
   while(t--)
   {
      delay_ms(1000);
   }
}

void delay_ms(long t)   // delays t millisecs
{
   do
   {
     delay_10us(100);
   } while(--t);
}

void delay_10us(byte t)
{
#asm
//      BCF STATUS, RP0
DELAY_10US_1:
      CLRWDT
      NOP
      NOP
      NOP
      NOP
      NOP
      NOP
      DECFSZ t, F
      GOTO DELAY_10US_1
#endasm
}

#int_timer1
void timer_int_handler(void)
{
   tmr1_int_occ = TRUE;
}


#int_ccp1
void ccp1_int_handler(void)
{
   capture_int_occ = TRUE;
}

#int_default
void default_interrupt_handler()
{
}