CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

code to measure frequency
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
prescott2006



Joined: 01 Mar 2010
Posts: 4

View user's profile Send private message

code to measure frequency
PostPosted: Mon Mar 01, 2010 12:05 pm     Reply with quote

I want to use measure the frequency of the square wave input at RA4/T0CKI of PIC16F84. So how should I start writing the code? Anyone can provide some draft of the C code? Thanks.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Mar 01, 2010 1:25 pm     Reply with quote

1. What's the frequency of the square wave signal ?

2. What's the crystal frequency for the 16F84 ?

3. Is it required to use a 16F84 ? It's much easier if you use a PIC that
has a built-in CCP module (such as 16F877, 16F886, 16F88, 16F684, etc).
prescott2006



Joined: 01 Mar 2010
Posts: 4

View user's profile Send private message

PostPosted: Mon Mar 01, 2010 1:41 pm     Reply with quote

1. It is an input signal from guitar which has been convert to square wave by analog comparator.
2. Oscillator is 4Mhz.
3. It is compulsory to use pic16f84.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue Mar 02, 2010 6:16 pm     Reply with quote

Try this code. It uses ASM code from the PicBasic Pro COUNT command
to measure the frequency. I took the code from the .LST file from a
PicBasic Pro program that uses the Count command, and I put a
CCS "wrapper" around it. This allows you to measure the frequency
without having a CCP. If you're using interrupts, you have to disable
them while you're calling the count() function, because they would disrupt
the counting.

I tested this on a PicDem2-Plus board with a 16F84A (I don't have a
plain 16F84), with compiler vs. 4.104. It works. I set my frequency
generator to 2 KHz, and connected it to pin B2. I got this output:
Quote:

2000
1999
2000
1999
1998
1999
1999
1999
1998

It works with other frequencies from about 1 Hz up to about 24 KHz.
The 16F84A has a 4 MHz crystal.

Here is the test program:
Code:

#include <16F84A.H>
#fuses XT, NOWDT, PUT
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_B1, rcv=PIN_B0)

#include "count.c"

//======================================
void main()
{
int16 result;

// Measure the number of counts in one second
// from the input signal on Pin B2.  This will
// be the frequency (in Hz) of that signal.
while(1)
  {
   result = count(PIN_B2, 1000);   // count period = 1000 ms
   printf("%lu \n\r", result);
  }

}


Here is the count.c program:
Code:

// Count.c

#define BYTE_PTR(x) &(int8 *)(x)

#define COUNT_DELAY ((int16)(getenv("CLOCK") / 80000))

// ASM definitions
#define W  0
#define F  1

// Status Register Bits
#define Z  2
#define C  0

// Register addresses (16F)
#byte INDF   = 0x00
#byte STATUS = 0x03
#byte FSR    = 0x04

//---------------------------------------
int16 count(int8 ccs_pin, int16 period)
{
int8 const bitmask_table[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
int8  io_port;
int8  bitmask;
int16 R0;
int16 R1;
int16 R2;
int16 R3;
int32 loop_count;

// These variables are used in the ASM code
// and must be located in the same RAM bank.
#locate R0      = 0x30
#locate R1      = 0x32
#locate R2      = 0x34
#locate R3      = 0x36
#locate io_port = 0x38 
#locate bitmask = 0x39

// Extract the Port address and bitmask from
// the CCS pin number.
io_port = ccs_pin >> 3;
bitmask = bitmask_table[ccs_pin & 7];

// Set the pin to be an input pin.
output_float(ccs_pin);

// Calculate the number of loop iterations to do.
loop_count = _mul(period, COUNT_DELAY);

// Put loop_count into R0 (msb), R2+1 (mid), R2 (lsb)
R0 = make8(loop_count, 2);  // msb
(int8)R2 = make8(loop_count, 1);  // Get mid into  R2
R2 <<= 8;  // Move mid into R2+1
(int8)R2 = make8(loop_count, 0); // lsb

#asm   
    movf    io_port, W         ; Get port
    movwf   FSR
    incf    R0, F              ; Bump up high and mid for dec
    incf    BYTE_PTR(R2) +1, F
    clrf    R1                 ; Zero counter
    clrf    BYTE_PTR(R1) +1
    movf    INDF, W            ; Read pin
    andwf   bitmask, W         ; Isolate it
    movwf   R3                 ; Save starting state as last

countloop:                     ; 20 usec loop (at 4 MHz)
    nop                        ; 1
    movf    INDF, W            ; 1 Read pin
    andwf   bitmask, W         ; 1 Isolate it
    movwf   BYTE_PTR(R3) +1    ; 1 Save state
    xorwf   R3, W              ; 1 Compare with last time
    andwf   BYTE_PTR(R3) +1, W ; 1 Only count high states
    xorwf   bitmask, W         ; 1 Flip for next test
    btfsc   STATUS, Z          ; 1 / 2
    incf    R1, F              ; 1 / 0 Count pulse
    btfsc   STATUS, Z          ; 1 / 2
    incf    BYTE_PTR(R1) +1, F ; 1 / 0
    movf    BYTE_PTR(R3) +1, W ; 1 Set new last state
    movwf   R3                 ; 1
    decf    R2, F              ; 1 Count time
    btfsc   STATUS, Z          ; 1 / 2
    decf    BYTE_PTR(R2) +1, F ; 1 / 0
    btfsc   STATUS, Z          ; 1 / 2
    decfsz  R0, F              ; 1 / 2
    goto    countloop          ; 2 / 0
    movf    R1, W              ; Result to W
#endasm

return(R1);  // R1 holds the result
}
Guest








Re: code to measure frequency
PostPosted: Sun Mar 07, 2010 11:25 am     Reply with quote

You may use the interrupt-on-change feature of the chip to detect and count the signals from the guitar by connecting the incoming signal to one of the PORTB-pins (RP4:RB7. Timer0-module provides the clock for this counting: just count how many PORTB-interrupts you counted during a certain time-interval (defined by timer0).

But I think that the PIC16F84A is not the best choice for this task - much better to use a chip with 2 timers (16-bit).
dexxsterlab



Joined: 18 Aug 2008
Posts: 2

View user's profile Send private message

PostPosted: Mon Sep 06, 2010 11:38 pm     Reply with quote

PCM programmer, thanks for another great example of good coding !
stronger_ht



Joined: 28 Sep 2010
Posts: 1

View user's profile Send private message

PostPosted: Wed Sep 29, 2010 5:17 am     Reply with quote

18f452'de count fonksiyonu çalışmadı.
Derleme aşamasında sorun olmuyor ancak Isis programında denediğimde sistem kilitleniyor.

18f452'de çalışacak başka bir fonksiyon mevcut mu?



English Version by Google Translate

18f452, the count function does not work.
Not a problem but when I try to program the system hangs during compilation with Proteus Isis.

Is there another function for 18f452 that works?
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Sep 29, 2010 11:15 am     Reply with quote

Currently, there is only code for the 16F PICs. I will look at it later,
maybe during the weekend, and see if it's possible to convert it to 18F.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sun Oct 03, 2010 1:10 pm     Reply with quote

I successfully converted the Count program to run with an 18F-series
PIC. I tested it with an 18F452, with CCS compiler vs. 4.112. I ran it
on a PicDem2-Plus board (non-Rohs version). I used a B&K Model 4011
function generator to apply a 5v p/p squarewave signal to pin B0 on
the PIC. The program's output was displayed on TeraTerm terminal
window, running on a Windows XP system. The PIC runs at +5 volts.

With a 4 MHz crystal on the 18F452, it could correctly determine the
input signal frequency over a range of 1 Hz to 24.8 KHz. With a 20 MHz
crystal on the PIC (and with the HS fuse), it worked over a range from
1 Hz to 65.5 KHz. No adjustments are needed to the count_18f.c program
to make it work with a different crystal. Just re-compile it. (But
remember to change the oscillator #fuse and the #use delay() value
in your test program).

Here is the test program. Note that the test program has the possibility
of using the PIC's own PWM module to generate a test signal, by putting
a jumper from pin C2 to pin B0. I didn't use that feature for the formal
testing. I used a B&K function generator to make the test signal.
Code:

#include <18F452.H>
#fuses XT, NOWDT, BROWNOUT, PUT, NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)

#include "count_18f.c"

//======================================
void main()
{
int16 result;

// Create a test signal from the same PIC that is
// measuring the signal.  Then we don't need an
// external frequency generator.  The PIC supplies it.
// Use the hardware PWM module to generate the signal.
// Connect a jumper from pin C2 to pin B0 for this test.
// This will put a 244 Hz squarewave on pin B0.
//setup_ccp1(CCP_PWM);
//setup_timer_2(T2_DIV_BY_16, 255, 1);  // PWM freq = 244 Hz (@ 4 MHz)
//set_pwm1_duty(128);  // 50% duty cycle.

// Call the COUNT function to measure the frequency.
// Measure the number of counts in one second
// from the input signal on Pin B0.  This will
// be the frequency (in Hz) of that signal.
while(1)
  {
   result = count(PIN_B0, 1000); // Get count on Pin B0 for 1000 ms
   printf("%lu\n\r", result);
   delay_ms(500);
  }

}


Here is the count_18f.c program. It contains the count() function.
This file must be #include'd in your test program.
Code:

#define BYTE_PTR(x) &(int8 *)(x)

#define COUNT_DELAY ((int16)(getenv("CLOCK") / 80000))

// ASM definitions
#define W  0
#define F  1

// Status Register Bits
#define Z  2
#define C  0

// Register addresses (18F)
#byte INDF0  = 0xFEF
#byte STATUS = 0xFD8
#byte FSR0H  = 0xFEA
#byte FSR0L  = 0xFE9

//---------------------------------------

int16 count(int16 ccs_pin, int16 period) 
{
int8 const bitmask_table[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
int16 io_port;   
int8  bitmask;
int16 R0;
int16 R1;
int16 R2;
int16 R3;
int32 loop_count;

// These variables are used in the ASM code
// and must be located in the same RAM bank.
#locate R0      = 0x30
#locate R1      = 0x32
#locate R2      = 0x34
#locate R3      = 0x36
#locate io_port = 0x38 
#locate bitmask = 0x3A  // *** Changed to 0x3A for 18F version ***

// Extract the Port address and bitmask from
// the CCS pin number.
io_port = ccs_pin >> 3;
bitmask = bitmask_table[ccs_pin & 7];

// Set the pin to be an input pin.
output_float(ccs_pin);

// Calculate the number of loop iterations to do.
loop_count = _mul(period, COUNT_DELAY);

// Put loop_count into R0 (msb), R2+1 (mid), R2 (lsb)
R0 = make8(loop_count, 2);  // msb
(int8)R2 = make8(loop_count, 1);  // Get mid into  R2
R2 <<= 8;  // Move mid into R2+1
(int8)R2 = make8(loop_count, 0); // lsb

#asm   
    movf    io_port, W      ; Get port lsb
    movwf   FSR0L
    movf    BYTE_PTR(io_port) +1, W   ; Get port msb
    movwf   FSR0H
    incf    R0, F              ; Bump up high and mid for dec
    incf    BYTE_PTR(R2) +1, F
    clrf    R1                 ; Zero counter
    clrf    BYTE_PTR(R1) +1
    movf    INDF0, W 
    andwf   bitmask, W         ; Isolate it
    movwf   R3                 ; Save starting state as last

countloop:                     ; 20 usec loop (at 4 MHz)
    nop                        ; 1
    movf    INDF0, W           ; 1
    andwf   bitmask, W         ; 1 Isolate it
    movwf   BYTE_PTR(R3) +1    ; 1 Save state
    xorwf   R3, W              ; 1 Compare with last time
    andwf   BYTE_PTR(R3) +1, W ; 1 Only count high states
    xorwf   bitmask, W         ; 1 Flip for next test
    btfsc   STATUS, Z          ; 1 / 2
    incf    R1, F              ; 1 / 0 Count pulse
    btfsc   STATUS, Z          ; 1 / 2
    incf    BYTE_PTR(R1) +1, F ; 1 / 0
    movf    BYTE_PTR(R3) +1, W ; 1 Set new last state
    movwf   R3                 ; 1
    decf    R2, F              ; 1 Count time
    btfsc   STATUS, Z          ; 1 / 2
    decf    BYTE_PTR(R2) +1, F ; 1 / 0
    btfsc   STATUS, Z          ; 1 / 2
    decfsz  R0, F              ; 1 / 2
    goto    countloop          ; 2 / 0
    movf    R1, W              ; Result to W
#endasm

return(R1);  // R1 holds the result
}
flyingjoint



Joined: 22 Sep 2010
Posts: 3

View user's profile Send private message Send e-mail

PostPosted: Wed Oct 13, 2010 1:06 pm     Reply with quote

Hello, I also need a frequency counter and I'm using a PIC 16F874A. But my question about the frequency counter posted here is:

- Does the counter works for a 20MHz crystal?
- Would this increase the range of frequency counting?
_________________
Let's rock!
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Oct 13, 2010 1:09 pm     Reply with quote

Did you read my post immediately above your post ?
Every question you asked is answered, in detail.
matheuslps



Joined: 29 Sep 2010
Posts: 73
Location: Brazil

View user's profile Send private message MSN Messenger

PostPosted: Sat Jun 25, 2011 4:19 pm     Reply with quote

I need to measure 3 square wave signals.

I am using a 16F877A. For 2 signals I am using the CCP1 and CCP2 module. It is working very fine.

For the third signal, can I use this count.c file?

The signal go from 70hz to 7khz maximum.

bye
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sat Jun 25, 2011 4:30 pm     Reply with quote

If you run them all at the same time, the count.c code will be interrupted
by #int_ccp1 and #int_ccp2. The counting process will be stopped while
the interrupts are processed. This will reduce the accuracy of the count code.

Of course, you could stop using the CCP interrupts for a while by calling
the disable_interrupts() function for each one. Then you could safely
call the count() function and measure the 3rd signal. When that's done,
re-enable the CCP interrupts.
matheuslps



Joined: 29 Sep 2010
Posts: 73
Location: Brazil

View user's profile Send private message MSN Messenger

PostPosted: Sat Jun 25, 2011 4:51 pm     Reply with quote

Sure....

Thanks...

My readings will not be at real time, so it will work.

Let me make some test here.

bye
matheuslps



Joined: 29 Sep 2010
Posts: 73
Location: Brazil

View user's profile Send private message MSN Messenger

PostPosted: Sat Jun 25, 2011 7:12 pm     Reply with quote

Just a feedback:

I was having problems with my code to measure 3 frequencies using the CCP1, CCP2 and the code count.c posted.

The problem was change from each interrupt.

So i changed my approach and did all the thing with a different way:

I setup the TIMER1 to make a delay of 0.5 seconds (500mS) and count the number of pulses over the CCP1, CCP2 and RB0 using the INT_EXT interrupt.

Then i multiplied them by 2 and got everything fine. I using a 4Mhz crystal, so if I need to change, simple make change the factor 2. The code is very small, look:

Code:
#include <16f877a.h>             //O PIC utilizado, obigatório!
                                 //ser utilizado de 8 bits também.
#FUSES NOWDT                     //Sem Watch dog, evitando reset
#FUSES XT                        //Crystal de oscilação igual a 4mhz
#FUSES PUT                       //Tempo de início do PIC
#FUSES NOPROTECT                 //Codigo sem proteção de leitura, software livre!
#FUSES NOLVP                     //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD                     //No EE protection

#use delay(clock=4000000)        //Meu clock

#include <LCD.C>                 //Rotina de LCD modo 4 vias. Obrigatório!

int16 freq_1=0, freq_2=0,freq_3;
int1 flag;

int16 p, q,r;

int8 cont;

#int_timer1
void trata_timer1(void)
{   
   flag=1;
   
   p = freq_1*2;
   q = freq_2*2;
   r = freq_3*2;
   freq_1 = 0;
   freq_2 = 0;
   freq_3 = 0;
         
   set_timer1(3036);
}

#int_ccp1
void ccp1_int()
{
   freq_1++;
}

#int_ccp2
void ccp2_int()
{
   freq_2++;
}

#int_ext
void trata_ext()
{
   freq_3++;
}

void main()
{
   lcd_init();   
   setup_ccp1(CCP_CAPTURE_RE);
   setup_ccp2(CCP_CAPTURE_RE); 
   setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);
   
   enable_interrupts(int_ccp1);
   enable_interrupts(int_ccp2);
   enable_interrupts(int_timer1);
   enable_interrupts(int_ext);
   enable_interrupts(global);   
   
   set_timer1 (3036);
   
   while (TRUE)
   {
      if (flag == 1)
      { 
         printf (lcd_putc,"\fF_1:%lu\nF_2:%lu F_3:%lu",p,q,r);
         delay_ms (200);         
         flag=0;
      }
   }
}


And a simulation:



Thanks everyone.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group