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