PDA

View Full Version : Low-Frequency Counter



Zeke
- 4th January 2006, 21:29
Hopefully someone has some suggestions for me on this one.
I need to write a program to be able to count pulses that run from around 0.25Hz to 4Hz. The PULSEIN command would likely time-out long before it reads any pulses. I think I will need to use tmr1 or similar and advance a counter at each roll-over of the timer, and then convert this value to a time between my external pulses. Then, with some simple math, I can figure out the frequency.
My biggest problem is being able to setup the timer to run properly. I'm not sure how to do this. And, should I set-up my external pulse as an interrupt? I will likely be using a PIC16F627 as I have a couple of these handy.
Any help would be much appreciated.
Nick

mister_e
- 5th January 2006, 00:23
Something to start
On this forum
http://www.picbasic.co.uk/forum/showthread.php?t=1044

Melabs Website... choose counter.zip
http://www.melabs.com/resources/samples.htm#submitted

Both are the same, the second include the schematic

Sorry, based on a old and dirty F84 but should gives you some pointer.

mister_e
- 5th January 2006, 01:19
Beurk... no forget that..i just see your frequency range...

Maybe the use of PAUSE could be just enough.

0.25HZ => period =4Sec
4HZ => period = 0.25 Sec

Something like


While FreqInput=1 : Wend
While FreqInput=0 : wend
While FreqInput = 1
Pause 10
HalfPeriod=HalfPeriod+1
wend

Frequency = 10000/(HalfPeriod * 2)


In the above 4HZ will return => 400, 0.25HZ will return 25 as value....
then just to play with the DEC, DIG to display your frequency.

Should be enough to start.

Zeke
- 5th January 2006, 20:30
Steve,

Thanks for the info. I'm going to start testing this in the next few days. I'll make sure I post what finally works.

Nick

Darrel Taylor
- 5th January 2006, 20:44
Steve,

Playing Devil's advocate here.

What happens if the Duty Cycle of the pulses isn't 50% ?
<br>

Zeke
- 6th January 2006, 04:54
Darrel, in fact, the pulses will be coming from a sensor measuring Heart Rate, like an ECG signal, except that it will be run through a comparator so that I only pick-up the largest spike. Therefore, the signal will likely never have 50% duty cycle.
My plan is this...
1. Use Timer1 to count pulses (5,000 at 20MHz gives 1ms) starting at a pre-defined value, I think it works out to 60,535. In a tight loop I will check for the timer overflow bit, everytime this happens, I increment a variable as 1 ms and re-start the timer. This way I am counting milliseconds.
2. I will use RB0 as my interrupt signal (which is my pulse signal) and perform an interrupt routine that will calculate the heart rate based on the time (milliseconds from my variable above) between pulses. This routine will then reset my millisecond variable to check for the next time between pulses.
3. Another variable will check for 3 or 4 seconds to pass and then send the data via the built-in USART to a PC.

This is roughly how it should work. There are other issues that I need to look at, but that's easy. The hard part is keeping it accurate since at a heart rate of about 240 beats per minute (BPM), that's about 4 Hz, or 250ms between pulses. Therefore, in this range, a difference of 1 ms, is approximately 1 BPM. At lower heart rates, the resolution increases.
What do think??

BTW: I really appreciate the input so far.

Thanks,
Nick

Darrel Taylor
- 6th January 2006, 18:45
Zeke,

I think you're on the right track. But, a couple changes might help the accuracy.

Let the timer "Free-Run" between each pulse, then at each overflow simply increment a variable and don't reload the timer. That variable then becomes the upper word of a 32-bit timer count, with a resolution of 1us or better (depending on FOSC and prescaler).

Since you are already planning to use interrupts to capture the pulses, you can use the Timer interrupt to increment the variable instead of sitting in a loop doing nothing.

On each pulse, stop the timer, copy the 32-bit result, reset the timer and upper word variable, then turn the timer back on.

Now you've got almost 1/4 second (at highest rate) to calulate and send the results. If you're sitting in a "tight loop" waiting for overflows, it doesn't leave any time to send the results, and you'll end up missing every other beat.

The math is a little harder this way, but it's not that bad if you use a DIV32 routine from the Code Examples forum. (PutMulResult?D)
http://www.picbasic.co.uk/forum/showthread.php?t=24
<br>

Zeke
- 19th January 2006, 18:06
For those interested...
I finally got this going after a lot of trouble trying to run with a 20MHz crystal. For some reason, my board layout didn't lend itself well to this higher speed. I ended up just using the internal oscillator at 4 Mhz.
In any case, below is my initial "rough" code that works for now. I'm able to record an input pulse train from around 0.25Hz to about 4 or 5Hz with good accuracy (for what I need). I was able to transmit the data to Hyperterminal on my PC via a MAX232 connected to the hardware serial port on the PIC. I still have more work to do, but please feel free to comment and offer suggestions.


------------------------------------------------------------
Include "modedefs.bas" ' Mode definitions for Serout

'Define OSC 20 ' Calibrate Oscilator for 20Mhz Crystal

@ DEVICE PIC16F627, INTRC_OSC_CLKOUT
'Use internal clock at 4Mhz
'DEFINE HSER_RCSTA 90h
'DEFINE HSER_TXSTA 24h
'DEFINE HSER_SPBRG 25 ' 9600 Bauds
'DEFINE HSER_CLOERR 1

DEFINE HSER_RCSTA 90h
DEFINE HSER_TXSTA 24h
DEFINE HSER_SPBRG 103 ' 2400 Bauds
DEFINE HSER_CLOERR 1


TRISB=%00001011 'Set RB0,RB1 and RB3 as inputs
T1CON=%00000000 'Timer1, 1:1 pre-scale, Timer1 Off
INTCON=%10010000 'Set RB0 as Interupt

Convert con $EA60 'Set convert constant at 60,000 for HR calculation

HRin var PORTB.0 'Heart rate signal
BPMCOMout var PORTB.2 'Serial data out
BPMCOMin var PORTB.1 'Serial data in
StartGo var PORTB.3 'Start calculating HR
Led var PORTB.4 'LED Pulse Indicator

Timer var Word 'Millisecond Timer for HR Calculation
SendTimer var Word 'Timer for when to transmit results
HeartRate var word 'Calculated Heart Rate result

Timer=0
SendTimer=0
HeartRate=0
TMR1H = $FC 'Pre-Set Timer1 to 64536 to get 1ms per
TMR1L = $18 'overflow flag.

On Interrupt goto HRCalc

While StartGo = 0 'Wait for Start Button before proceding
Wend

T1CON.0=1 'Start Timer1

MainProcedure:
If PIR1.0 then 'Check for timer1 overflow, ie. 1000us
T1CON.0=0 'Stop Timer1 while we're messing with it
PIR1.0=0 'Clear timer1 overflow flag.
TMR1H = $FC 'Reset timer1 high byte to start value
TMR1L = $18 'Reset timer1 low byte to start value
T1CON.0=1 'Restart timer1
Timer = Timer+1 'Increment ms timer by 1ms.
SendTimer = SendTimer+1 'Increment timer for sending data by 1ms
If (Led) and (Timer>=150) Then Low LED 'Turn off LED after 150ms
EndIf
Goto MainProcedure 'Repeat until Heart Rate Pulse received

Disable
HRCalc:
High Led 'Turn LED on
If (Timer<>0) Then
HeartRate = convert / Timer 'calculate HR
Endif
Timer=0 'reset Timer variable
INTCON.1=0
If SendTimer>=4000 Then 'check SendTimer variable for elapsed time
HSEROUT [Dec HeartRate,10] 'Send data via RS232
SendTimer=0 'and reset SendTimer variable
Endif
'Eventually - setup an array to calculate an average HR
Resume

Enable

End
--------------------------------------------------------------------------

Thanks,
Nick