Low-Frequency Counter


+ Reply to Thread
Results 1 to 8 of 8
  1. #1
    Zeke's Avatar
    Zeke Guest

    Default Low-Frequency Counter

    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

  2. #2
    Join Date
    Sep 2004
    Location
    montreal, canada
    Posts
    6,898

    Default

    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.
    Steve

    It's not a bug, it's a random feature.
    There's no problem, only learning opportunities.

  3. #3
    Join Date
    Sep 2004
    Location
    montreal, canada
    Posts
    6,898

    Default

    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
    Code:
    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.
    Last edited by mister_e; - 5th January 2006 at 01:22.
    Steve

    It's not a bug, it's a random feature.
    There's no problem, only learning opportunities.

  4. #4
    Zeke's Avatar
    Zeke Guest

    Default

    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

  5. #5
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959

    Default

    Steve,

    Playing Devil's advocate here.

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

  6. #6
    Zeke's Avatar
    Zeke Guest

    Default

    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

  7. #7
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959

    Default

    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>
    DT

  8. #8
    Zeke's Avatar
    Zeke Guest

    Default

    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

Similar Threads

  1. Sony LanC Program
    By l_gaminde in forum Code Examples
    Replies: 2
    Last Post: - 25th September 2009, 18:51
  2. Old and beyond help ?
    By DavidFMarks in forum mel PIC BASIC Pro
    Replies: 46
    Last Post: - 11th December 2008, 15:23
  3. Microcontroller with 2 way paging application problem
    By oneohthree in forum mel PIC BASIC Pro
    Replies: 30
    Last Post: - 20th April 2007, 17:27
  4. PIC16F84(A) counter
    By nmmr in forum mel PIC BASIC Pro
    Replies: 2
    Last Post: - 26th May 2005, 04:27
  5. 4-line LCD Help - using PortA instead of B
    By Tom Gonser in forum mel PIC BASIC Pro
    Replies: 28
    Last Post: - 31st March 2005, 03:14

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts