/********************************
 Project: A motor speed to voltage converter
 File: FV.C
 Date : 9.3.2002 Pekka Ritamaki
 Input 0..75 Hz 
 Output 0..5V 
 Hardware: PIC16F872-20/P
************************/
#include <16F872.h>
#use delay(clock=12000000)
#fuses HS,NOWDT,PUT,NOLVP,PROTECT,NOWRT,NOBROWNOUT
/*** byte and bit addreses ****/
#BYTE timer0 =1
#byte PORTA = 5
#byte PORTB = 6
#byte PORTC = 7
#bit LED = PORTC.6
#bit TEST_BUTTON = PORTB.1
/**variables *****/
byte rpm;
int1   update;  		// bit variable 
unsigned long  pwm;     // 16 bit variable for pwm value 
unsigned long pulse_counter, out_counter;  // 16 bit variable for counting TIMER0 pulses

/****************************************
 External interrupt RB.0
 Input pulses lo-to-high activate this routine  
********************************************/
#int_ext

void pulse_isr (void)
{
 out_counter = pulse_counter; 	// move pulse counter  for main routine 
 pulse_counter =0;   			// counter reset for next cycle
 update=1;            			// set a update bit 
 LED= !LED;                     // blink a led for interrupt indication 
}

/*******************************************
TIMER0 overflow interrupt routine counts 98.64 us pulses
prescaler = 1:8 => 2.66 us input pulses to TIMER0 
256-37= 219
*******************************/

#int_rtcc

void timer0_isr(void) 
{
 timer0 = 219;  		// reload 37 pulses to next interrupt
 pulse_counter++;   	// increase 98.64 us pulses
 if ( pulse_counter > 10315L)  {  // at low or missing input frequncy 
     pulse_counter =0 ;           // we must manually update the main routine
  	 update=1;            		  // set a update bit 
	 out_counter = 10315L;        // set 1 Hz or 60 rmp value => 66mV output
	}
}


/************************
Init the cpu hardware
*********************************/
void hardwar_init (void)
{
   setup_spi(FALSE);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   set_tris_b (0xff);  // interrupt  input
   set_tris_c (0x00);  // pwm & LED output
   setup_timer_0 ( RTCC_DIV_8 | RTCC_INTERNAL);
   enable_interrupts (INT_RTCC);  // enable TIMER0 interrupt
   enable_interrupts (INT_EXT);  // enable external interrupt
   enable_interrupts(GLOBAL);    // enable master interrupt
  
   setup_ccp1(CCP_PWM);           		//set timer2 to PWM mode 
   setup_timer_2(T2_DIV_BY_1, 0xff, 1); // PWM to fastest 10 bit mode 
   set_pwm1_duty (0);                  // reset pwm 
   out_counter = pulse_counter =0;
   

   port_b_pullups(TRUE);  // set RB.0 pullup resistor on 
   ext_int_edge (L_TO_H); // set Ext interrupt to low to high edge 
}

/************************************************************
a test button ramps voltage 0 to 5 in 1024 steps and back to zero 
************************************************************/
void test ( void) {
long i; 
 LED=0; 
 for( i=0; i< 1023L; i++)
 {
   set_pwm1_duty(i); 
   delay_ms(1); 

 }
 LED=1; 
 for( i= 1023L; i;  i--) 
 {
   set_pwm1_duty(i); 
   delay_ms(1); 
  }
 LED=0; 
}
/******************************
The main routine makes   a calucalation according timer value between input pulses
Note a 32-bit divider axuxilliary variable 
******************************/
void main(void ) 
{
int32 aux_32; 		// declare a 32 bit variable 

hardwar_init ();    // init the ardware 

while (1) {         // forever loop 
  if(!TEST_BUTTON)  // if test button pressed
     test();        // make a test 
 if( update) {      // if a new value is availble interrupt
     update=0;      // clear the flag 
     if ( out_counter < 135)  // limit a high frequency range to 5 volt 
         out_counter = 135L;
                                     	// set the range calibration 
     aux_32 = 138105L /out_counter;  	// 135 pulses should give a maximum pwm value 1023
     pwm = (unsigned long) (aux_32); 	// force 32 bit value to 16 bit value 
     set_pwm1_duty(pwm & 0x3ff); 	 	// set actual pwm value and limit maximum value 
  } 
}

}
