PIC C, CCS-PCM, Measuring Pulse Widths


// PULSIN.C (CCS Info - PIC16F84)
//
// Implements equivalent of BASIC Stamp PULSIN command.
//
// Illustrates use of TMR0 and external interrupt.
//
// In function pulse_0, the external interrupt on RB.0 is configured for 
// interrrupt on negative edge, TMR0 is set to zero and T0 interrupt is 
// enabled.  If 256 roll overs of TMR0 occur (65536 * 2 usec) with no 
// external interrupt, the overflow flag is set and 0x0ffff is returned.  
// The interpretation is that no negative edge going signal was present
// over the nominal 130 ms.
//
// If, however, an external interrupt occurs prior to "overflow", a "fall
// detected" flag is set, the external interrupt is reconfigured for 
// interrupt on postive edge, and the timing process begins again.  If 
// the timer overflows, the value 0xffff is returned.  The interpretation
// is that no postive edge occurred.
//
// If, however, an external interrupt occurs (int_ocurred & fall_detected),
// the number of number of roll overs of TMR0 and the current value of TMR0
// is returned.  This multiplied by 2 usecs is the width of the negative 
// pulse.
//
// Function pulse_1, is similar in concept except the external interrupt is
// first configured for rising and then falling.  I opted to use a separate 
// flag "rise_detected" for clarity.
//
// copyright, Peter H. Anderson, Scotland Co, NC, Mar, '99.
// NOT VERIFIED

#case
#include <16f84.h>
#include <defs_f84.h>
#include <string.h>

long pulse_0(void);
long pulse_1(void);

// delay routines
void delay_ms(long t);
void delay_10us(int t);

// LCD routines
void lcd_init(void);
void out_RAM_str(int *s);
void lcd_hex_byte(int val);
void lcd_dec_byte(int val, int digits);
int num_to_char(int val);
void lcd_char(int ch);
void lcd_new_line(void);

#define TxData 0	// RA.0 - interface to serial LCD

// global declarations
short int overflow, ext_int_occurred, fall_detected, rise_detected;
int count_hi, count_lo;

void main(void)
{
   long t;
   char s[5];
   lcd_init();

   t = pulse_0();	// measure width of a zero pulse on RB.0
   strcpy(s, "t_0=");	// and display via serial LCD on RA.0
   out_RAM_str(s);
   lcd_hex_byte((int)((t>>8)&0xff));
   lcd_hex_byte((int)t&0xff);
   lcd_new_line();

   t = pulse_1();	// measure width of a one pulse on RB.0
   strcpy(s, "t_1=");	// and display
   out_RAM_str(s);
   lcd_hex_byte((int)((t>>8)&0xff));
   lcd_hex_byte((int)t&0xff);
   lcd_new_line();

   while(1)		// endless loop
   {
#asm
      CLRWDT
#endasm
   }
}

long pulse_0(void)
{
   overflow=FALSE;
   ext_int_occurred=FALSE;
   fall_detected=FALSE;
   t0cs=0;	// clock
   psa=0;	// prescale assignment
   ps2=0;  ps1=0; ps0=0;	// prescale 1:2

   count_hi=0;

   trisb0=1;	// make RB0 an input
   intedg=0;  	// ext int on falling edge
   t0if = 0;
   intf=0;
   TMR0 = 0;	// fire up the timer

   inte = 1;
   t0ie = 1;

   while(1)
   {
#asm
      CLRWDT
#endasm
      if (overflow || ext_int_occurred) // for the moment turn off ints
      {
         while(gie)
         {
            gie=0;
         }
      }
      if (overflow)  // if overflow, return 0xffff
      {
         inte=0;
         t0ie = 0;	// housekeeping
         return(0xffff);
      }

      if (ext_int_occurred && !fall_detected) // fall was detected
      {
         TMR0=0;
         count_hi=0;
         intedg=1;      // now interrupt on rising edge
         intf=0;
         ext_int_occurred=FALSE;
         overflow=FALSE;    // perhaps not really necessary
         fall_detected = TRUE;
         gie=1;
      }

      if (ext_int_occurred && fall_detected) // rise was detected
      {
         inte=0;
         t0ie=0;
         return((count_hi << 8) | count_lo);
      }
   }
}

long pulse_1(void)
{

   overflow=FALSE;
   ext_int_occurred=FALSE;
   rise_detected=FALSE;
   t0cs=0;	// clock
   psa=0;	// prescale assignment
   ps2=0;  ps1=0; ps0=0;	// prescale 1:2

   count_hi=0;

   trisb0=1;	// make RB0 an input
   intedg=1;  	// ext int on rising edge
   t0if = 0;
   intf=0;
   TMR0 = 0;	// fire up the timer

   inte = 1;
   t0ie = 1;

   while(1)
   {
#asm
      CLRWDT
#endasm
      if (overflow || ext_int_occurred) // for the moment turn off ints
      {
         while(gie)
         {
            gie=0;
         }
      }
      if (overflow)  // if overflow, return 0xffff
      {
         inte=0;
         t0ie = 0;	// housekeeping
         return(0xffff);
      }

      if (ext_int_occurred && !rise_detected) // rise was detected
      {
         TMR0=0;
         count_hi=0;
         intedg=0;      // now interrupt on falling edge
         intf=0;
         ext_int_occurred=FALSE;
         overflow=FALSE;    // perhaps not really necessary
         rise_detected = TRUE;
         gie=1;
      }

      if (ext_int_occurred && rise_detected) // fall was detected
      {
         inte=0;
         t0ie=0;
         return((count_hi << 8) | count_lo);
      }
   }
}

#int_rtcc rtcc_handler(void)
{
   ++count_hi;
   if (count_hi == 0)
   {
      overflow = TRUE;
   }
}

#int_ext ext_int_handler(void)
{
   count_lo = TMR0;	// get the low byte
   ext_int_occurred = TRUE;
}

#int_default default_handler(void)
{
}

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

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

// LCD routines.

int num_to_char(int val)	// converts val to hex character
{
   int ch;
   if (val < 10)
   {
     ch=val+'0';
   }
   else
   {
     val=val-10;
     ch=val + 'A';
   }
   return(ch);
}

void lcd_char(int ch)	// serial output to PIC-n-LCD, 9600 baud
{
   int n, dly;
   		// start bit + 8 data bits

  #asm
       BCF STATUS, RP0
       MOVLW 9
       MOVWF n
       BCF STATUS, C

  LCD_CHAR_1:

       BTFSS STATUS, C
       BSF PORTA, TxData
       BTFSC STATUS, C
       BCF PORTA, TxData
       MOVLW 32
       MOVWF dly

  LCD_CHAR_2:
       DECFSZ dly, F
       GOTO LCD_CHAR_2
       RRF ch, F
       DECFSZ n, F
       GOTO LCD_CHAR_1

       BCF PORTA, TxData
       CLRWDT
       MOVLW 96
       MOVWF dly

  LCD_CHAR_3:
       DECFSZ dly, F
       GOTO LCD_CHAR_3
       CLRWDT
   #endasm
}

void lcd_init(void)	// sets TxData in idle state and resets PIC-n-LCD
{
  #asm
        BCF STATUS, RP0
        BCF PORTA, TxData
        BSF STATUS, RP0
        BCF TRISA, TxData
        BCF STATUS, RP0
   #endasm
   lcd_char(0x0c);
   delay_ms(250);
}

void lcd_new_line(void)	// outputs 0x0d, 0x0a
{
   lcd_char(0x0d);
   delay_ms(10);	// give the PIC-n-LCD time to perform the
   lcd_char(0x0a);	// new line function
   delay_ms(10);
}


void out_RAM_str(int *s)
{
   while(*s)
   {
      lcd_char(*s);
      ++s;
   }
}

void lcd_hex_byte(int val) // displays val in hex format
{
   int ch;
   ch = num_to_char((val>>4) & 0x0f);
   lcd_char(ch);
   ch = num_to_char(val&0x0f);
   lcd_char(ch);
}

void lcd_dec_byte(int val, int digits)
// displays byte in decimal as either 1, 2 or 3 digits
{
   int d;
   int ch;
   if (digits == 3)
   {
      d=val/100;
      ch=num_to_char(d);
      lcd_char(ch);
   }
   if (digits >1)	// take the two lowest digits
   {
       val=val%100;
       d=val/10;
       ch=num_to_char(d);
       lcd_char(ch);
   }
   if (digits == 1)	// take the least significant digit
   {
       val = val%100;
   }

   d=val % 10;
   ch=num_to_char(d);
   lcd_char(ch);
}