0
\$\begingroup\$

I wrote the following code to measure the frequency of a square wave using the capture module of the PIC16f1788. I wrote the code by following step by step what is required in the datasheet of this PIC. I would like to know if I have missed any important step in doing so and If my code makes sense. This is the first time that I use the capture module so any suggestions are welcome. Thanks

    #include <xc.h>
    // Config word
    #define frequ  2
    #define _XTAL_FREQ   32000000

    // CONFIG1
    #pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator:  I/O function on CLKIN pin)
    #pragma config WDTE = ON        // Watchdog Timer Enable (WDT enabled)
    #pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
    #pragma config MCLRE = ON       // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
    #pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
    #pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
    #pragma config BOREN = ON       // Brown-out Reset Enable (Brown-out Reset enabled)
    #pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
    #pragma config IESO = ON        // Internal/External Switchover (Internal/External Switchover mode is enabled)
    #pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)

    // CONFIG2
    #pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
    #pragma config VCAPEN = OFF     // Voltage Regulator Capacitor Enable bit (Vcap functionality is disabled on RA6.)
    #pragma config PLLEN = ON       // PLL Enable (4x PLL enabled)
    #pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
    #pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
    #pragma config LPBOR = OFF      // Low Power Brown-Out Reset Enable Bit (Low power brown-out is disabled)
    #pragma config LVP = ON         // Low-Voltage Programming Enable (Low-voltage programming enabled)





     void main(void)

     {

     int count=0;
     float rise1=0,rise2=0,frequency,period;

     OSCCON = 0b11111010;

     // clear the interrupts at beginning that way we are sure that our calculations will be exact no previous interrupt present 
     PIR1.CCP1IF=0;
     PIE1.CCP1IE=0;


    // output
    TRISBbits.TRISB0 = 1; // set it to input
    APFCON1bits.CCP1SEL=1;// toggle the CCP port output to RB0 from RC2


    // configure CCP1
    CCP1CONbits.CCP1M = 0x05;   // set it to Capture mode; on every rising edge



    While (count=! 2){

    count +=1; // increment the value of count

    // configure Timer 1
    T1CONbits.TMR1CS = 0; //set the  timer 1 clock to instruction clock Fosc/4 as mentioned in page300
    T1CONbits.TMR1ON = 1;       //  set Timer 1 on
    delay_us(10); //aquisition time
    T1GCON.T1GO/nDONE=1; // aquisition is ready wait for a rising edge
    while (T1GCON.T1GO/nDONE=1); //wait untill the aquisition is complete


     if (count==1) {

     rise1=CCPR1; }  //capture the first rising edge and store that value in rise1

     if count ==2) {

     rise2=CCPR1; } // capture the second rising edge and store the value in rise2

    }

    period = rise2-rise1;  // subtracte both values to get the period of the signal

    frequency=1/period;  

     // clear any interrupts that are set
     PIR1.CCP1IF=0;
     PIE1.CCP1IE=0;


}
\$\endgroup\$
3
  • \$\begingroup\$ You can use two timer to measure frequency. One for making a delay for example 1 second and another as a counter which counts the pulses come during one second. It has a very important feature i.e. both timers are running freely so you can put other tasks in your code rather than making the controller busy for frequency measurement. What you need is to update a variable when you want to know the frequency. If you interested i will suggest code. \$\endgroup\$
    – xcoder
    Apr 14, 2015 at 4:19
  • \$\begingroup\$ There are several ways to do it. Why don't you try yours and then, if it does not work according to your needs, we will get back to this question. The way you wrote it, you need to make sure the measured frequency is faster than the time to overflow the timer (as you might not have two consecutive ticks). The same issue is with the @xcoder's seggestion: if your frequency is below 1Hz then you will not be able to register any 'rizes' because the delay is 1 second. \$\endgroup\$
    – Nazar
    Apr 14, 2015 at 12:16
  • \$\begingroup\$ @xcoder a sample code would be very helpful to understand your logic thanks. \$\endgroup\$ Apr 14, 2015 at 19:18

1 Answer 1

1
\$\begingroup\$

From your request I am trying to explain frequency measurement using two timer. A timer can be used in various modes but here we need only two mode to use.

  1. Making a time duration
  2. Use as a counter

Since you are working with pulses then make Timer0 as a counter. Use Timer1 to measure a duration of 1 second . Suppose Timer0 starts counting the pulses, in the meantime Timer1 measures the time duration. When It measures 1 second, collect the value of counter. If the value of counter is 100 then frequency is 100Hz. Both Timer uses interrupt so your main code is free to assign any other tasks.

// LCD module connections
sbit LCD_RS at RB0_bit;
sbit LCD_EN at RB1_bit;
sbit LCD_D4 at RB2_bit;
sbit LCD_D5 at RB3_bit;
sbit LCD_D6 at RB4_bit;
sbit LCD_D7 at RB5_bit;

sbit LCD_RS_Direction at TRISB0_bit;
sbit LCD_EN_Direction at TRISB1_bit;
sbit LCD_D4_Direction at TRISB2_bit;
sbit LCD_D5_Direction at TRISB3_bit;
sbit LCD_D6_Direction at TRISB4_bit;
sbit LCD_D7_Direction at TRISB5_bit;
// End LCD module connections

int frequency,counter,cnt;
char txt[7];

void InitTimer1(){
  T1CON         = 0x31;
  TMR1IF_bit         = 0;
  TMR1H         = 0x0B;
  TMR1L         = 0xDC;
  TMR1IE_bit         = 1;
  INTCON         = 0xC0;
}

void Interrupt(){

  //Timer1 code to measure 1s duration
  if (TMR1IF_bit){
  cnt++;
    TMR1IF_bit = 0;
    TMR1H         = 0x0B;
    TMR1L         = 0xDC;
    //Enter your code here
    if (cnt >= 10) {      // 1s; after 10 interrupts LEDs will be toggled
          frequency=TMR0+(256*counter);
          TMR0=0;
          counter=0;
          cnt = 0;             // Reset cnt
        }
  }
  // Timer0 code to count incomming pulses
  if (TMR0IF_bit){
      TMR0IF_bit = 0;
     counter++;
  }
}

void main() {
frequency=0;
T0CS_bit=1;
T0SE_bit=0;
TMR0IE_bit=1;
InitTimer1();
Lcd_Init();
Lcd_Cmd(_LCD_CURSOR_OFF);
Lcd_Out(1,1,"Frequency In Hz:");
do {
         IntToStr(frequency,txt);
         Lcd_Out(2,4,txt);

  } while(1);

}

This code is written in MikroC. You can convert it as your compiler requirements. You can see this tutorial for further details.

I saw some tutorial where a delay of 1 second is made to measure frequency. But a 1s delay just set the processor idle for 1 second which is wastage of resources. The key idea is to make this delay by interrupt method then I can place another tasks for the controller such as voltage or current measurement. I think this will be helpful for all.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.