16F877@4Mhz calculate Pulsin to RPM


Closed Thread
Results 1 to 25 of 25
  1. #1
    lwindridge's Avatar
    lwindridge Guest

    Angry 16F877@4Mhz calculate Pulsin to RPM

    OK, first things first I'm new to programming PIC's (although have been prgramming PC's etc for over 10 years now) and this is my first time out with PBP.
    For reference I am using a 16F877 MCU running with a 4Mhz crystal and porting my information out via serial as I don't have an LCD or similar at present.

    I've been trying for the past day or so to make a basic readout of
    RPM based on an incoming square wave to pin B0.
    I've got a 4500rpm signal coming in based on my cars crank angle sensor readout (4 pulses per rotation) and using the count function I can relate that back no problem (300 rps/4 * 60=4500rpm).
    What I need to do though is count the pulses WITHOUT holding the MCU in a state of flux while the reading is being taken.
    I've tried doing this with the pulsin command but I'm not sure I'm getting the right things back from it.
    The pulse (in theory) should run high for 1/8 of a revolution and low for 1/8 of a revolution which signifies a quarter turn on the crank.
    Currently if I count the pulse width using the pulsin command I get a result of 165 high and 168 low (using two pulsin commands, one for high one for low).

    The area where I am getting stuck is how to convert this into a useable RPS value and therefore convert to RPM.

    This has to be done fairly rapidly as the circuit I am designing will use this value to retard the timing on my car based on RPM and Airflow (Airflow is an easy 0-5v reference and can be read via an A/D input so no problems there). Obviously there will only be around 12-14 revs per second at idle raising to 125 at the engines redline of 7500RPM so it has to fit with a sample time of around 250ms I think (correct me if I'm wrong though!).

    Can anyone offer any suggestions or code snippets for how I should go about the calculation - or even if there is a much simpler/more efficient wat of doing it.

    One possibility I did think of is measuring the time between the pulses leading edges so I could get a time between the first pulse and the next and base the rps on that after multiplying by 4.

    Thanks in advance.

    Leigh Windridge

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


    Did you find this post helpful? Yes | No

    Default

    Hi Leigh,

    From your two values of 165 and 168, I calculate that the engine was running at 4504 RPM. So, the following formula may be what your looking for.
    Code:
    Pulse_High  Var  word
    Pulse_Low   Var  word
    Pulse_Total Var  Word
    RPM         Var  Word
    
    Pulse_High = 165
    Pulse_Low  = 168
    
    Pulse_Total = Pulse_High + Pulse_Low
    RPM = 1000
    RPM = RPM * RPM           ' Preload internal registers with 1,000,000
    RPM = DIV32 Pulse_Total   ' 1,000,000 / Pulse_Total
    RPM = RPM * 60            ' Per minute
    RPM = Div32 40            ' 4 pulses per rev
    
    lcdout $FE,1,dec RPM
    In normal math it might look like this:
    (1,000,000/(Pulse_High+Pulse_Low))*60/40

    A better way to take the measurements might be to use the Capture mode of the CCP module. Set the prescaler to 2:1 and have it count 4 rising edges of the input and then generate an interrupt. The Timer value would contain 1/2 the number of uS it takes for 1 revolution, which should be fairly easy to convert to RPM. This way, the processor can continue doing other things while it's taking the measurement. This should work from 500-10,000 RPM

    That's a bit more complicated, but may be worth looking into.

    HTH,
       Darrel
    Last edited by Darrel Taylor; - 18th April 2004 at 23:05.

  3. #3
    lwindridge's Avatar
    lwindridge Guest


    Did you find this post helpful? Yes | No

    Default

    Well the code you posted certainly confirms my math and exactly how I didn't know the code

    Do you have any demo code on how I could do the CCP method as it would be much more reliable I feel.

    Thanks again.

    Leigh

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


    Did you find this post helpful? Yes | No

    Default

    Nope, sorry, don't have any good code to show for the Capture mode.

    The RPM question comes up fairly often, and I've wanted to write a good routine for it, but so far haven't got around to it. Maybe it's about time I did.

    I'll play around with it and see what I can come up with, but No guarantees. In the mean time, I hope the above formula, helps get you back on track.

    L8R,
       Darrel

  5. #5
    Join Date
    Jul 2003
    Posts
    2,358


    Did you find this post helpful? Yes | No

    Default

    For Capture Compare usage, do a search at the MeLabs archives and look for "PICBASIC-L How to use ccpx2.bas example" (circa April 2003). The original datafile is posted on the MeLabs site also. Within that thread there are other references to frequency measurement including the thread "PICBASIC-L Frequency Measurement" also from April/May last year. From there that should give you a whole heap of ideas and code...

  6. #6
    lwindridge's Avatar
    lwindridge Guest


    Did you find this post helpful? Yes | No

    Smile

    Thanks again for all your help folks.

    Sure does help a newcomer keep going with experts around

    One very quick question though - can anyone recommend where the best place would be to get a serial LCD display from at a reasonable price in the UK? I'm probably looking for a 16x2 but any suggestions are welcome!
    I tried to order from the Crownhill website last night but the ordering section appears to not like my card number or something and keeps looping back to the payment options page and blanking out the previous values???

    I'm looking at a serial LCD as I want to use most of the other pins on the MCU for other functions, but as always I'm open to suggestions.

    Leigh

  7. #7
    Join Date
    Jul 2003
    Posts
    2,358


    Did you find this post helpful? Yes | No

    Default

    Go phone Crownhill... tell them about your ordering problems... if they don't know they've got a problem then they can't fix it. For 1-off items, Crownhill's pricing is reasonable and I always recommend them... for quantity you can always haggle...

    That aside, go build your own... you'll get change out of a tenner... standard 16x2 LCD, a 16F628 (buying one will help reduce Lester's F628 mountain *smiles*) and a resonator... and if you really want to be flash, a few jumpers or switches for baud-rate and polarity settings...

  8. #8
    lwindridge's Avatar
    lwindridge Guest


    Did you find this post helpful? Yes | No

    Default

    OK, so I'm getting somewhere now...

    I've setup the code to use the CCP1 register to scan the pulses and return the time back in uS.
    What I can't seem to figure out yet is whether I've got the prescaler etc. right.

    My code has the following lines....

    CCP1CON = %00000110
    T1CON = %00010001

    Does that look about right for the settings that Darrel suggested. 2:1 prescaler and measuring 4 rising edges?

    So does this look right to calculate the RPM? (Sorry for being so dumb, but this sort of math really bakes my noodle!!)

    dummy = period * 2
    RPM = (dummy*60)/100

    The only problem I can see is that this is going over the 65535 limit. Can I use the DIV32 command to make this work properly and read the value correctly?
    For example, at 6000rpm period is approx. 4989uS, which puts dummy to 9978 so rpm should work out (9978*60)/100=5986.8 very close to 6000rpm so not bad.
    BUT 9978*60=598680 - just a bit over the 65535 limit :o(

    Can anyone suggest a better way to to the math as this will become much worse to calculate at lower rpm due to the increased uS per RPM time.

    And yes, I do feel a bit of a fool asking these questions, but it is my first time programming a PIC :o)

    Cheers

    Leigh

  9. #9
    lwindridge's Avatar
    lwindridge Guest


    Did you find this post helpful? Yes | No

    Default

    Ignore my previous request - I've figured it out

    Amazing how a bit of deep thought and caffeine can help!!

    Leigh

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


    Did you find this post helpful? Yes | No

    Default

    Wow, if this is truly your first time programming PIC's then your doing pretty good. I'm impressed. And also bummed. Now I'll never get to write that RPM program.

    No worries, keep it up.

    DT

  11. #11
    lwindridge's Avatar
    lwindridge Guest


    Did you find this post helpful? Yes | No

    Default

    I think one of the thing that helps me is I've been programming in various languages on PC's for about 10 years now.
    Also I'm using the FlashLab system from Reynolds Electronics so the development is very easy and I don't need to worry about moving chip to programmer and back to circuit each time I make a small change.
    All helps anyway

    I might end up tapping your brain again soon anyway Darrel - got some interesting maths that's hurting my brain again....

    Cheers!

    Leigh

  12. #12
    Join Date
    Sep 2003
    Location
    INDIA
    Posts
    161


    Did you find this post helpful? Yes | No

    Default Final Code

    So is it possible to post the final working code in this foroum for others (like me) to take benifit.

    Thanks.

  13. #13
    lwindridge's Avatar
    lwindridge Guest


    Did you find this post helpful? Yes | No

    Default

    The code will probably end up being in seperate sections but I'll certainly post some here for reference

    Leigh

  14. #14
    lwindridge's Avatar
    lwindridge Guest


    Did you find this post helpful? Yes | No

    Default

    Yet another question.

    I've been using a variation on ccpx2.bas to find the pulse length in uS and working the math back from there.
    BUT, I don't seem to get a very reliable figure from this (eg. I have a 29hz input which is roughly 870rpm but it seems to equate to something wildly out eg. 255??).

    I've attached my current code and if someone can suggest a better way of taking the rpm reading, processing it, reading the 2 adc ports and telling a timer to delay the incoming signal on an output port (eg. 44us for a 2 degree delay at 7500rpm) and display some values on an LCD then I'd be more than welcome for the additional input.

    Fingers crossed it's just my maths playing up!!

    Leigh
    Attached Files Attached Files

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


    Did you find this post helpful? Yes | No

    Default

    Hi Leigh,

    Just took a quick look at your program, and I can't answer all those questions, but did notice one thing.

    The capture function of the CCP simply latches the Timer value into the CCPRH:L register when the specified event occurs. In this case thats after 4 RISING edges. To get a meaningfull result, the timer has to be started at the same time as the CCP starts counting it's 4 periods.

    You can just leave the timer turned off and let it capture 0 the first time, then as soon as the CCP1IF (PIR1.2) is raised, imediately start the timer, then reset the CCP1IF. On the next capture you should have a valid result.

    I don't know about all the rest, but one thing at a time usually works best.

    Maybe something like this
    Code:
    CCP1CON = %00000110			' Enable the CCP1 capture, every 4th rising edge
    loop:
    T1CON = %00010000 			' TMR1 prescale=1:2  Timer OFF
    TMR1H = 0                               ' Zero the Timer
    TMR1L = 0
    capture = 0
    StartLoop:
    	IF (capture = 0) Then StartLoop	' Wait here for the first capture
                    T1CON.0 = 1             ' Start the Timer
                    capture = 0             ' Reset  the capture flag
    CaptureLoop:
    	IF (capture = 0) Then CaptureLoop	' Wait here until captured
    	period.lowbyte = CCPR1L		' Store the captured value in
    	period.highbyte = CCPR1H	' period variable
    DT
    Last edited by Darrel Taylor; - 23rd April 2004 at 06:52.

  16. #16
    lwindridge's Avatar
    lwindridge Guest


    Did you find this post helpful? Yes | No

    Default

    Well I tried the suggestions but I still don't seem to be able to get a reliable value (always out by a good margin).

    I'm wondering if it would be better to include an assembly interrupt and do the pulse measurement that way?

    Still very new to this so any suggestions and code snippets welcome

    Also, I may already know the answer to this, but can a PIC reliably perform a phase shift on an incoming signal and output it in realtime?
    The reason for asking is say I have a 3750mS pulse (1925 high, 1825 low) coming in, I need to put a delay of 88uS on it for example and re-output it in its original form (or as close to as possible). The shortest delay I'll need to put on is about 44uS and the longest would be about 333uS.
    The theory I have is that I need to have a dedicated CMOS chip fed with a signal to modulate the pulses accordingly, but I'd rather do it with a PIC if I can because not only will I need to do it once (for ignition timing) but for two injector pulses too (current ecu provides trigger and I need to send a pulse EXACTLY on time of a length in mS to each channel when needed (2 injectors per channel on my car).

    Thanks again for all your help here.

    Leigh

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


    Did you find this post helpful? Yes | No

    Default

    Patience, patience,

    While what you've described is possible, you need to get to first base before you can get a home run.

    If you made the changes I suggested, then at 4500 RPM you should now be getting a constant capture result of around 6666.

    Other RPM's should give the following results.
    RPM - Capture
    --------------------
    500   - 60,000
    1000 - 30,000
    2000 - 15,000
    3000 - 10,000
    4500 - 6,666
    7000 - 4,285

    The formula in your program is still the same as what I gave you for the Pulsin readings. This will no longer work for the capture results and needs to be changed.

    But first, can you verify that you are getting the correct capture result?

    Best reagrds,
       Darrel

  18. #18
    lwindridge's Avatar
    lwindridge Guest


    Did you find this post helpful? Yes | No

    Thumbs up

    Right then, next update....

    I've started using an external clock source for the rpm after putting a scope on the car this morning and finding out a few more things.
    This has been clocked to give me 26 pulses per second (based on a reading from a 1 second count command) and seeing as I now know for sure that I get 2 pulses per crank revolution (2:1 scaling on the crankshaft to the crank angle sensor on the back of the camshaft which pulses every 90 degrees on the cam) I'm guessing that should work out to about 780rpm.
    I've chosen to go lower as it makes testing on the car easier as I can check at idle.

    The pulses I get from a pulsin low and high measurement are...
    1934 high and 1829 low (with a slight variation occasionally to 1935 high and 1838 low).

    Running up the modified code (I've attached another version of the program as I've re-structured it somewhat to make debugging easier) I get a period value of around 9975 (fluctuates a bit though between 1966 and 1982).

    Hope that make sense anyway

    Leigh

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


    Did you find this post helpful? Yes | No

    Default

    Leigh,

    Didn't get your latest program.

    However, now that you've changed the number of pulses per rev, the prescaler will need to be changed to 4 instead of 2.

    With a prescaler of 2. If the engine is running at 794 RPM the capture count would be 75,510. This is too high. The timer would wrap around after 65535 and you would be left with 9974 as the period result.

    By setting the prescaler to 4, this will keep it from overflowing untill the RPM gets below 460. Of course, this now changes the calculation again.

    I'm trying to come up with a formula can take all these changes into account and automatically correct for them. No doubt, you'll be changing other things later. Most likely is the OSC speed, for what you want to eventually do with the timing, and injectors, 4mhz isn't going to cut it.

    Let me know what you get with the new prescaler.

    Darrel

  20. #20
    lwindridge's Avatar
    lwindridge Guest


    Did you find this post helpful? Yes | No

    Default

    Must have clicked the wrong button for the attach but here's the code now..
    Attached Files Attached Files

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


    Did you find this post helpful? Yes | No

    Default

    Thanks Leigh,

    I think this should work for you, for now. Change the T1CON assignment to:

    T1CON = %00100000 ' TMR1 prescale=1:4 Timer OFF

    And, use this formula:

    period = period / 2
    RPM = 10000
    RPM = RPM * RPM ' 100,000,000
    RPM = DIV32 period ' 100,000,000 / RevCount
    RPM = RPM * 60 ' Per minute
    RPM = DIV32 400

    Also, for the lookup to the nearest 100 RPM, you might consider this:

    Nearest = RPM / 100
    if RPM DIG 1 > 5 then Nearest = Nearest + 1
    Nearest = Nearest * 100


    Hope this works for you.

    Darrel

  22. #22
    lwindridge's Avatar
    lwindridge Guest


    Did you find this post helpful? Yes | No

    Default

    Well, what can I say - it works!

    I just wish I had the maths ability to work these things out by myself - but then again I do get stuck after I run out of fingers

    The readout now shows a clocked RPM of 795 and using your little bit of maths I get a nearest rpm of 800.
    Works like a charm!

    Now for the fun part, putting the pulse back out on a pin in real time....

    Leigh

  23. #23
    Join Date
    Jun 2013
    Posts
    2


    Did you find this post helpful? Yes | No

    Default Re: 16F877@4Mhz calculate Pulsin to RPM

    Hi Darrel Taylor

    You said the ccp should work from 500 - 10,000 rpm. Please kindly advice how to measure rpm 0-10,000 rpm by use ccp module on [email protected] you have source code please show me. Thank you.

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


    Did you find this post helpful? Yes | No

    Default Re: 16F877@4Mhz calculate Pulsin to RPM

    You'll need to count the timer overflows and use them as the high word of the period after a capture.
    Then change all the math, because the values will be larger than 16-bits.

    Since you are using an 877A, you won't have the luxury of using LONG variables.
    DT

  25. #25
    Join Date
    Jun 2013
    Posts
    2


    Did you find this post helpful? Yes | No

    Wink Re: 16F877@4Mhz calculate Pulsin to RPM

    Thank you for your advice but i still not clear please kindly show example about your advice above. For my program show as below
    PIC16F877A

    DEFINE OSC 4
    Define LCD_DREG PORTD
    Define LCD_DBIT 4
    Define LCD_RSREG PORTE
    Define LCD_RSBIT 2
    Define LCD_EREG PORTD
    Define LCD_EBIT 1


    Low PORTE.2
    PIE1.2=1

    Symbol Capture = PIR1.2 ' CCP1 capture flag
    T1 VAR WORD ' 1st capture value
    PW VAR WORD ' 2nd capture value & ultimately final pulse width



    intermediate VAR WORD
    Dummyvar VAR WORD


    rpm VAR WORD

    TRISC.2 = 1 ' CCP1 input pin (Capture input)
    INTCON = 0 ' Interrupts off

    ReLoad:
    CCP1CON = %00000101 ' Capture mode, 4capture on rising edge
    T1CON = 0 ' TMR1 prescale=1, clock=Fosc/4, TMR1=off (200nS per count @20MHz)
    TMR1H = 0 ' Clear high byte of TMR1 counter
    TMR1L = 0 ' Clear low byte
    T1CON.0 = 1 ' Turn TMR1 on here


    Capture = 0 ' Clear capture int flag bit
    While (!Capture) ' Wait here until capture on rising edge
    Wend

    ' Rising edge detected / stuff Timer1 value in T1
    T1.HighByte = CCPR1H
    T1.LowByte = CCPR1L

    CCP1CON = %00000100 ' Configure capture for falling edge now
    Capture = 0 ' Clear capture interrupt flag bit

    While !Capture ' While here until capture on falling edge
    Wend

    ' Falling edge detected / stuff Timer1 value in PW
    PW.HighByte = CCPR1H
    PW.LowByte = CCPR1L

    PW = PW-T1 ' High pulse width = PW-T1


    intermediate = 60 * 1000 / 4 ' Compulsory !!!
    ' ( no "intermediate CON xxx" allowed )


    ' Disable interrupts ... if some used !!!


    Dummyvar = 1000 * intermediate


    rpm = DIV32 PW


    ' re-enable interrupts allowed
    lcdout $fe,1,"TCH",$fe,$84,DEC RPM

    GOTO ReLoad

    END

Similar Threads

  1. Interrupt RPM and Taylors Elapsed time on 18F4620
    By Tobias in forum mel PIC BASIC Pro
    Replies: 70
    Last Post: - 3rd February 2010, 17:12
  2. Slow code needs a tune up
    By Tobias in forum mel PIC BASIC Pro
    Replies: 7
    Last Post: - 5th January 2010, 16:05
  3. Pulsin vs. Interrupt RPM measurement
    By Tobias in forum General
    Replies: 1
    Last Post: - 31st December 2009, 02:29
  4. 16f877A and tmr1 external crystal question
    By comwarrior in forum General
    Replies: 3
    Last Post: - 13th July 2009, 01:40
  5. RPM - DIV32 Problem
    By davewanna in forum mel PIC BASIC Pro
    Replies: 10
    Last Post: - 11th April 2008, 05:33

Members who have read this thread : 3

You do not have permission to view the list of names.

Posting Permissions

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