// 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()
{
}