|
|
View previous topic :: View next topic |
Author |
Message |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Mar 02, 2010 6:16 pm |
|
|
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 |
Posted: Sun Mar 07, 2010 11:25 am |
|
|
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
|
|
Posted: Mon Sep 06, 2010 11:38 pm |
|
|
PCM programmer, thanks for another great example of good coding ! |
|
|
stronger_ht
Joined: 28 Sep 2010 Posts: 1
|
|
Posted: Wed Sep 29, 2010 5:17 am |
|
|
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
|
|
Posted: Wed Sep 29, 2010 11:15 am |
|
|
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
|
|
Posted: Sun Oct 03, 2010 1:10 pm |
|
|
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
|
|
Posted: Wed Oct 13, 2010 1:06 pm |
|
|
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
|
|
Posted: Wed Oct 13, 2010 1:09 pm |
|
|
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
|
|
Posted: Sat Jun 25, 2011 4:19 pm |
|
|
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
|
|
Posted: Sat Jun 25, 2011 4:30 pm |
|
|
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
|
|
Posted: Sat Jun 25, 2011 4:51 pm |
|
|
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
|
|
Posted: Sat Jun 25, 2011 7:12 pm |
|
|
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. |
|
|
jrgob
Joined: 21 Jan 2010 Posts: 10 Location: Brazil
|
|
Posted: Fri Sep 23, 2011 8:03 am |
|
|
I need help to make a frequency meter of to 200 kHz...
I'm using the code of matheuslps, but I could not change to read to 200KHz, someone could help me! _________________ Thanks.
jrgob |
|
|
|
|
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
|