ANOTHER RPM TACHOMETER USING CCP INPUT - 229 - 58000 RPM

General discussion on mikroBasic.
Author
Message
xor
Posts: 5465
Joined: 18 May 2005 00:59
Location: NYC
Contact:

ANOTHER RPM TACHOMETER USING CCP INPUT - 229 - 58000 RPM

#1 Post by xor » 19 Mar 2008 03:34

This is a similar program to the one found here which I had posted earlier: http://www.mikroe.com/forum/viewtopic.php?t=14035

Rackley mentioned some legitimate points about resolution and timing when it involves engines that have multiple sensors that create a quantity of pulse per engine RPM. http://www.mikroe.com/forum/viewtopic.php?p=70751#70751

This program can help address his points and actually provide a simple solution. Read my comments inside of the code and see how this new approach can provide an alternate solution, while changing very little the overall concept of using the CCP input pin to catch signals and calculate RPM.

Code: Select all

program RPM_TACH2_CCP


' ***********************************************
' ****     ANOTHER TACHOMETER DEMO USING THE
' ****          THE PIC's CCP MODULE
' ****      RANGE is 229 to 58,000 RPM
' ****     SHOWS RPM RESULT ON CHARACTER LCD
' ***********************************************

' ***********************************************
' **** Program by Warren Schroeder March 18,2008
' ****    Tach Demo Using 18F452 @ 8 MHz
' ****            mikroBASIC 6.0
'************************************************

'************************************************
' ****   Timer1 Prescaler=0 Ticks @ 0.5us
' ****   Timer1 Prescaler=2 Ticks @ 1.0us
' ****   Timer1 Prescaler=4 Ticks @ 2.0us
' ****   Timer1 Prescaler=8 Ticks @ 4.0us
' ************************************************

' *******************************************************************************
'*****            FURTHER NOTES ON THIS DEMONSTRATION CODE:

'*****   Using the CCP Capture Mode, this code does not care to read Timer1 at every
'*****   CCP Input Pin pulse and interrupt.
'*****   Instead, it will capture all pulses, accumulate
'*****   them, and then average them over a period of time.
'*****   This averaging can produce  some nice results for displaying finer RPM output
'*****   especially at low RPM in some special cases.
'*****
'*****   This program catches all input pulses over the full timing count of Timer1,
'*****   or until Timer1 rolls over to 0.
'*****   Timer1 sets PIR1.TMR1IF when rollover (overflow) occurs and is
'*****   very easy to test.  Timer1 below will rollover every 262ms.
'*****   262ms is equivalent to 229 RPM, if only 1 pulse was counted.
'*****   From this we can create some simple
'*****   RPM calculations for other pulse count totals.  The formula being:
'*****   pulsecount * 229, with 229 RPM being the best per pulse resolution
'*****
'*****   Let's take this a step further.  Assume that you have an engine with a rotor
'*****   that has 4 Hall Effect sensors.  This means that 1 RPM creates 4 pulses.
'*****   RPM calculation over 262ms would look like this:
'*****   pulsecount * 229 / 4, with 57 RPM being the best per pulse resolution
'*****   Adding these extra pulses can create a finer more accurate RPM output.

'********************************************************************************


CONST
       CAPTURE_FALLINGEDGE    as byte = 4
       CAPTURE_RISINGEDGE     as byte = 5
       T1_Prescale_0          as byte = 0
       T1_Prescale_2          as byte = 16
       T1_Prescale_4          as byte = 32
       T1_Prescale_8          as byte = 48
       RPMBase                as byte = 229
DIM
       REV     as Byte        ' total pulse counts
       RPMCalc as Byte
       RPMVal as Word
       RPMStr as String[5]


   Sub Procedure INTERRUPT()
       If PIR1.CCP1IF = 1 Then     ' input pulse counter, ignore CCPR1
          inc(REV)                 ' accumulate pulses
          PIR1.CCP1IF = 0          ' prepare for next CCP interrupt
       End If
       If PIR1.TMR1IF = 1 Then     ' test Timer1 for overflow
' **** If Timer1 Overflows = 65536 x 4us = 262ms = 229 RPM
          RPMCalc = REV            ' save pulsecounts for calculations below
          REV = 0                  ' clear pulsecounter
          PIR1.TMR1IF = 0          ' clear Timer1 Overflow Flag
       End If
   End Sub

   Sub Procedure CCP_SETUP()
       INTCON       = 192                   ' Enable Global & Peripheral Interrupts
       T1CON        = T1_Prescale_8         ' Ticks every 4us
       TRISC.2      = 1                     ' CCP1 pin is input
       CCP1CON      = CAPTURE_RISINGEDGE    ' catch every 4th rising edge
       PIE1.CCP1IE  = 1                     ' enable CCP1 Interrupt
       PIR1.CCP1IF  = 0                     ' Clear CCP1 INTERRUPT Flag
       TMR1H        = 0                     ' reset Timer1
       TMR1L        = 0
       PIR1.TMR1IF  = 0                     ' Timer1 Overflow Flag Cleared
       T1CON.TMR1ON = 1                     ' Timer1 Running
   end sub

   Sub Procedure GENERAL_INIT()
       ADCON1 = 6                           ' disable analogs
       LATA   = 0
       LATB   = 0
       LATC   = 0
       LATD   = 0
       LATE   = 0
       TRISA  = 0
       TRISB  = 0
       TRISC  = 0
       TRISD  = 0
       TRISE  = 0
   end sub


   main:
       General_Init()
       CCP_Setup()

       LCD_INIT(PORTB)
       LCD_CMD(LCD_CURSOR_OFF)
       LCD_OUT(1,6, " RPM")
       REV = 0
       RPMCalc = 0
       
       Do
          If RPMCalc <> 0 Then                     ' pulses occurred and make RPM Calculation
             RPMVal = RPMCalc * RPMBase
             RPMCalc = 0                           ' clear for next Timer1 period
          End If
          Delay_ms(50)                             ' update LCD every 50ms
          WordToStr(RPMVal, RPMStr)                ' convert for LCD
          LCD_OUT(1, 1, RPMStr)                    ' print to LCD
       Loop Until 0=1

   end.
[color=darkred][b]xor[/b][/color]
[url=http://circuit-ed.com]CircuitED -[/url]

Charlie
Posts: 2744
Joined: 01 Dec 2004 22:29

#2 Post by Charlie » 19 Mar 2008 12:46

Thanks Xor,

I'm sure I can use this somewhere. :D
Regards Charlie M.

kd5byb
Posts: 13
Joined: 11 Dec 2006 08:36

Wow, fantastic!

#3 Post by kd5byb » 25 May 2008 15:30

What a neat program. I'm in the process of making a PIC-based computer fan controller that will be able to monitor RPM and this is almost EXACTLY what I need. I just have to add some PWM code to control fan voltage/speed through a MOSFET and I'm done. Eventually I'll add temperature monitoring and fan diagnostics, but that's down the road.

I originally was targeting an 16F877 as my chip of choice for this, but since your program is for the 18F452, I may just change the chip out in my EasyPIC4 for an 18F4520.

Originally when I tried to compile it, I didn't notice it was for the 18F452 and left the device window set to 16F877. It didn't compile, as it wasn't happy with the latch (LATA for instance) statements... I may try to rework it for the 16F877 as a learning experience...

Thanks for posting this!

-ben

xor
Posts: 5465
Joined: 18 May 2005 00:59
Location: NYC
Contact:

#4 Post by xor » 25 May 2008 16:35

The 16F877A and 18F452 are identical in most every way, except for those things that are specific to PIC18, such as RAM organization and a few SFR's.

The source code can port directly from one to the other, with exception of the LATx register, which is the PORT Output register of PIC18's. Just use PORTx to replace it for the 16F877A, and all should compile and work fine.
[color=darkred][b]xor[/b][/color]
[url=http://circuit-ed.com]CircuitED -[/url]

kd5byb
Posts: 13
Joined: 11 Dec 2006 08:36

Great!

#5 Post by kd5byb » 25 May 2008 19:08

Fantastic! That's almost easier than swapping the 16F877 for an 18F4520! I hope to try it later this afternoon. Have some chores to do around the house first, like weeding the flower beds and putting the new car stickers on our cars...

thanks much!
-ben

kd5byb
Posts: 13
Joined: 11 Dec 2006 08:36

Got it working!

#6 Post by kd5byb » 26 May 2008 00:12

Got it working this afternoon on a 16F877A - works great! I quickly added in a PWM function to the PWM2 and hooked PWM2's output into RC2 (CCP1) so that I'd have something to look at.

With 500 Hz coming out of PWM2 and into CCP1, the display shows 29,999 RPM which is close enough to 30,000 RPM (500 Hz) for my application.

I wouldn't be this far along without inspiration from this code...

thanks,
ben

Fritzables
Posts: 61
Joined: 07 Jun 2008 13:20
Location: Brisbane AUSTRALIA

#7 Post by Fritzables » 06 Jul 2008 02:51

program RPM_TACH2_CCP


' ***********************************************
' **** ANOTHER TACHOMETER DEMO USING THE
' **** THE PIC's CCP MODULE
' **** RANGE is 229 to 58,000 RPM
' **** SHOWS RPM RESULT ON CHARACTER LCD
' ***********************************************

' ***********************************************
' **** Program by Warren Schroeder March 18,2008
' **** Tach Demo Using 18F452 @ 8 MHz
' **** mikroBASIC 6.0
'************************************************
G'Day xor,

I shot you a 'PM' asking for assistance on this. This looks like it could do the trick for me.

I want to design a ECU to fit to a Honda VTR-1000F o convert it to fuel injection.

I am incredibly new to PIC programming but code in C++ and other languages for MS Win so now how to program for that environment.

I see your code supports the 18F452 processor. My new EasydsPIC4 board has the dsPIC30F4013 onboard and runs at 40Mhz I believe.

My question is, will your code run on the dsPIC30F4013 with some minor changes ?

Fritzables.

xor
Posts: 5465
Joined: 18 May 2005 00:59
Location: NYC
Contact:

#8 Post by xor » 06 Jul 2008 03:10

40MHz can be worked out. I can tweak up the code for more precision and better resolution with some information about your Rotation Detection.

How many pulses are you producing/receiving per engine cycle.... the more the better?

What is the max RPM value you expect?
[color=darkred][b]xor[/b][/color]
[url=http://circuit-ed.com]CircuitED -[/url]

Fritzables
Posts: 61
Joined: 07 Jun 2008 13:20
Location: Brisbane AUSTRALIA

#9 Post by Fritzables » 06 Jul 2008 03:25

You're a legend xor,

The magnetic sensor on the Honda counts 60 teeth from memory but I can adjust this later I assume.

Max RPM will be 11,000RPM but give some headroom so go for 14,000.

Pete (Fritzables)

xor
Posts: 5465
Joined: 18 May 2005 00:59
Location: NYC
Contact:

#10 Post by xor » 06 Jul 2008 03:32

Ok, I will assume 60 pulses per revolution and a max of 14000 RPM. 60 pulses/cycle is a very nice number.
[color=darkred][b]xor[/b][/color]
[url=http://circuit-ed.com]CircuitED -[/url]

Fritzables
Posts: 61
Joined: 07 Jun 2008 13:20
Location: Brisbane AUSTRALIA

#11 Post by Fritzables » 06 Jul 2008 04:42

Yep, 60 teeth so 60 pulses per revolution is the number.

Fritzables.

xor
Posts: 5465
Joined: 18 May 2005 00:59
Location: NYC
Contact:

#12 Post by xor » 07 Jul 2008 00:22

Fritzables wrote:Yep, 60 teeth so 60 pulses per revolution is the number.

Fritzables.
Based on 60 counts per revolution the following code is a super accurate Tachometer that can display any RPM value from 0 to 65500 RPM. I've tested it and it works a charm.

I chose 32MHz (8MHz x 4PLL) because the timing divisions work better. You should be able to get the same timings with the dsPIC.

I used a 18F4431 because it was handy. This code should port to other PIC18 with only a few adjustments.

Code will compile using the free demo version of the compiler.

Please Note:.... no ugly math in this program... :D

'

Code: Select all

**********************************************************************************
' **********************************************************************************
' *****
' *****         MOTORCYCLE TACHOMETER by Warren Schroeder July 5, 2008
' *****        ACCURATE OUTPUT FROM 0 TO 65500 RPM WITH 1RPM RESOLUTION
' *****                            MikroBASIC 7.2
' *****        Tested On EasyPIC5 with PIC18F4431 @ 32MHz (8MHz x 4PLL)
' *****
' ***********************************************************************************
' ***********************************************************************************
' *****
' *****   RPM Display on 16x2 LCD
' *****
' *****   This TACHOMETER Code is developed for 60 input pulses per engine revolution
' *****   Output Range is 0 to 62535 RPM in 1RPM Resolution with updates every second
' *****
' *****   Timer0 is set up as a 16-bit counter with T0CK1 RC3 catching rising edges
' *****   CCP1 is set up for 62500us interrupts using the special event feature
' *****   which resets Timer1 on Compare-Match
' *****   16 CCP1 Interrupts @ 62500us each = 1 Second
' *****
' ***********************************************************************************
' ***********************************************************************************


  Dim CCPR   as Word Absolute $FBE     ' CCPR1L:CCPR1H Pair as Word
  Dim Tally  as Byte
  Dim RPM    as Word
  Dim TRPML  as Byte Absolute $30
  Dim TRPMH  as Byte Absolute $31
  Dim TRPM   as Word Absolute $30
  Dim RPMStr as String[5]


  sub procedure interrupt()
         TRPML = TMR0L               ' capture Count; store in temp register
         TRPMH = TMR0H
         Tally = inc(Tally) And 15   ' Mod16 counter
      If Tally = 0 Then              ' if 1 second
         TMR0H = 0                   ' clear Timer0 Counter
         TMR0L = 0
         RPM = TRPM                  ' save RPM value for display
      End If
      PIR1.CCP1IF = 0                ' clear interrupt flag
  end sub

  sub procedure Timer0_As_Counter()
      T0CON   = 40      ' Off; 16Bit; Counter; rising edge; no prescaler
      TMR0H   = 0       ' Clear Timer0 Count
      TMR0L   = 0
      TRISC.3 = 1       ' RC3 is T0CK1 Counter Input
  end sub

  sub procedure CCP1_As_Timer()
      CCP1CON  = 11     ' CCP1 Compare Match Special Event
      T1CON    = 48     ' Timer1 Off; Prescaler=8 = 1us ticks (125ns x 8)
      TMR1H    = 0      ' Clear Timer1
      TMR1L    = 0
      CCPR     = 62500  ' interrupt every 62500us
      PIE1.CCP1IE = 1   ' enable CCP1 interrupt
      PIR1.CCP1IF = 0   ' clear CCP1 interrupt flag
      INTCON      = 192 ' enable GIE and PEIE
  end sub


main:
      ANSEL0 = 0        ' disable adc's
      ANSEL1 = 0
      TRISA  = 0
      TRISB  = 0
      TRISC  = 0
      TRISD  = 0
      LATA   = 0
      LATB   = 0
      LATC   = 0
      LATD   = 0

      Timer0_As_Counter()
      CCP1_As_Timer()
      LCD_CONFIG(PORTB,3,2,1,0,PORTB,4,0,5)
      LCD_CMD(LCD_CURSOR_OFF)

      Tally = 0          ' reset timing tally
      T0CON.TMR0ON = 1   ' start Timer0 counter
      T1CON.TMR1ON = 1   ' start Timer1

      While 1=1
          delay_ms(50)
          WordToStr(RPM, RPMStr)
          LCD_OUT(1,1,RPMStr)
          LCD_OUT_CP(" RPM")
      Wend

end.
[color=darkred][b]xor[/b][/color]
[url=http://circuit-ed.com]CircuitED -[/url]

Fritzables
Posts: 61
Joined: 07 Jun 2008 13:20
Location: Brisbane AUSTRALIA

#13 Post by Fritzables » 07 Jul 2008 00:48

Hey xor,

This is great.

Thanks a lot for taking a look at this for me. I will print this out and see if I can understand it, then when I get home tonight I will try it out.

I owe ya.


Fritzables
:D

Fritzables
Posts: 61
Joined: 07 Jun 2008 13:20
Location: Brisbane AUSTRALIA

#14 Post by Fritzables » 31 Jul 2008 09:45

G'Day XOR,

Well, I am slowly coming to terms with PIC programming - gone grey in the process but heading in the right direction.

I would like to confirm with you. Based on your code above: as the dsPIC30F4013 does not have an interrupt associated with Timer1, to modify sub procedure Timer0_As-Counter I would use Timer2 to perform the same tasks?

NOTE: dsPIC30F dose not have a Timer0

Cheers
Fritzables :D
[/b]

xor
Posts: 5465
Joined: 18 May 2005 00:59
Location: NYC
Contact:

#15 Post by xor » 01 Aug 2008 03:13

Since you use 60 pulses per revolution you have a perfect divisor for RTC apps, such as RPM calculations. Thus, no math or tricks are required for determining RPM.

The program above is as basic as this:
  • 1. Set up a 1 second timing period.... such as I did with the CCP module
  • 2. Catch pulses with a counter for the 1 second period.
  • 3. Read your counter value at the end of the 1 second period... this value is your RPM.
  • 4. Reset the counter and do it again for the next one second period.
You can set up any one of the 5 Timers on your dsPIC to work as a 16-bit counter. CCP has a capture input to count pulses. Also, all the timers and CCP have interrupt capability. You have a ton of usable options with the dsPIC.
[color=darkred][b]xor[/b][/color]
[url=http://circuit-ed.com]CircuitED -[/url]

Post Reply

Return to “mikroBasic General”