Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

RPM Counter (Project Started PIC16F877A)

Status
Not open for further replies.

Ayne

New Member
Microcontroller = PIC16F877A
Compiler = MikroBasic
Output on PC through SerialPort (cuz i have not LCD)
Crystal Frequency = 20MHz
Instruction Cycle Time = 200 nano Seconds

For lower Speed, I will do: Measuring the Period of a
Square Wave

For Higher Speed, I will do: Measuring the Period of a
Square Wave with Averaging

First About Hardware:-I am using a comparator LM393.
**broken link removed**
As light strike on PhotoDiode, comparator's output goes LOW to HIGH. We will count time periode b/w two Egdes and measure RPM according to it.


Second: About Method of Measuring RPM.
PIC will be in Capture Mode.
It will capture Time b/w two edges and convert this time into RPM.

I am using TIMER1 with 1:8 Prescale value.
Instruction Cycle Time = 200 nano Seconds.
TIMER1 value = 1 means 1.6 microSeconds has been elapsed because of 1:8 PreScaler. Am i right!!!
Now some math:
x is the time periode "T". x is the time b/w the two Edges and it's value in 1.6 microSeconds
**broken link removed**

Now Program
Code:
program RPM_Counter

' variables declaration
Dim i, j, UART_Byte, Over_Flow, Averaging As Byte
Dim Digit as Byte[5]
Dim T As Longint[2]
Dim Capture As word absolute $15
Dim RPM, Period As Longint

'sub procedure procedure_name ' procedures declaration
sub procedure interrupt

 if TestBit(PIR1,CCP1IF) = 1 then     'Capture mode Interrupt Flag
  IF i > 1 Then
   i = 0
  End if
  
  T[i] = ( (Over_Flow *  65535) +  Capture )
   
  Inc(i)
  PIR1.CCP1IF = 0
 end if

 if TestBit(PIR1,TMR1IF) = 1 then     'TMR1 Interrupt Flag
  Inc(Over_Flow)
  PIR1.TMR1IF = 0
 end if
 
end sub

Sub procedure Delay_313
 Delay_ms(313)
End Sub
'end sub


'sub function function_name ' functions declaration

'end sub
main:                       ' main program body
 Delay_313                  'For Stability in Supply
 TRISC   = 255              ' PORTC all Inputs
 PORTC   = 0                ' PORTC = 0
 T1CON   = %00110000        'TIMER1 CONTROL REGISTER
 CCP1CON = %00000101
 INTCON.GIE  = 1
 INTCON.PEIE = 1
 Averaging   = 0
 USART_init(19200) ' initialize USART module
 
Reset_All:
 PIE1.CCP1IE  = 0           'CCP1 Interrupt Enable bit
 T1CON.TMR1ON = 0           'Timer1 On bit
 TMR1H        = 0
 TMR1L        = 0
 Over_Flow    = 0
 
ReStart:
 T1CON.TMR1ON = 1           'Timer1 On bit
 PIE1.CCP1IE  = 1           'CCP1 Interrupt Enable bit
 Delay_313                  'Aquire The values of T0 and T2

Convert_RPM:
 Period = T[1] - T[0]       'Subtract saved captured time (T0) from
                            'captured time (T1) and store
 
 IF Averaging = 1 Then      'if CCP1CON = every 16 edge the divide by 16
    Period = Period / 16
 End IF
 
 IF Period < 75000 Then    ' If RPM > 500 then next time
 CCP1CON = %00000111       ' Caculate every 16th Egde
 Averaging = 1             ' and Measure RPM with Averaging
 else                      ' IF RPM < 500 then next time
 CCP1CON = %00000101       ' calculate every rising edge
 Averaging = 0             ' Measure RPM without Averaging
 End If
 
 RPM = 37500000 / Period   'Convert Time Period in Round/Minute

Into_Digit:
 Digit[0] = RPM Div 10000 mod 10
 Digit[1] = RPM Div 1000  mod 10
 Digit[2] = RPM Div 100   mod 10
 Digit[3] = RPM Div 10    mod 10
 Digit[4] = RPM           mod 10
 
UsartWrite:
   Usart_Write(0x52)                   ' "R" For indication of RPM
   For j = 0 To 4 step 1
   UART_Byte = Digit[j] + 48           ' Add 48 for converting into ASCII
   Usart_Write(UART_Byte)
   Next j
   Usart_Write(0x0D)                   'CR
   Usart_Write(0x0A)                   'LF

Goto Reset_All
end. ' end of program
This Program has been compiled. Not Tested yet on PIC.

Am I Right!!! :confused:
Muhammad Ahmed. ;)
 
Ooo Yes.
It is my 350 Post.

Plz Find the Error in above project and i will try to remove them.
 
Perhaps analyzing your own code would be a great place to start.

Simulate it in real life, grind out a few errors, and then ask everyone to have a look.

Asking people to find errors in a large program that we have had no involvement in is like asking what a painting looks like with the paint in tins still :p

Unless of course people have a spare hour or two too delve into your train of thought and programming techniques
 
The problem
Code:
Connected to COM2
Received: R00867

Received: R0/*(-

Received: R00(*)

Received: R01682

Received: R0/*)'

Received: R00(*)

Received: R01679

Received: R00867

Received: R00(*(

Received: R01680

Received: R00867

Received: R0/*)'

Received: R01681

Received: R00867

Received: R0/*(.

Received: R00(*)

Received: R00867

Received: R0/*)'

Received: R00(*)

Received: R01679

Received: R00867

Received: R00(*(

Received: R01681

Received: R00867

Received: R0/*(0

Received: R01681

Received: R00867

Received: R0/*(.

Received: R00(*)

Received: R00867

Received: R0/*)'

Received: R00(**

Received: R01679

Received: R00867

Received: R00(*(

Received: R01681

Received: R00867

Received: R0/*(0

I am receiving this.
I make astable circuit with 555 timer and feed it's output to PIC
The frequency is 15.15Hz and it means RPM 909..
I am receiving different valuse and aproxx vale is R00867.

Now the main problem is when i use every 16 edge and divide by 16 the result no be the same..
 
Infact my Problem is, I am learning.
Yes u r right, we can do this with ease.. (one thing more ur Proton+ is Jungly).
But i want to di this with Measuring the Period of a Square Wave
 
I found in my opto RPM counter on the Mongoose; I could hook the opto directly to an I/O pin. I use a 4.7K pulldown on the emitters (not a pullup) and tie the collectors to +5. It goes from GND to 4.2V nicely. I tried using the built in comparators but didn't need them. I use RB4&5 as they will cause IRQ on change.
PS the gears are IR transparent, black paint on both sides works perfectly.
**broken link removed**
 
Last edited:
blueroomelectronics
What is ur Method of measuring RPM??
Time periode b/w pulses ???
OR
Pulsed received in Known Time???
 
How u guys Measure the periode of a square wave?

Am i Right?
On first Intrupt i will start TIMER1 module and on seconed intrupt i will get the value of TIMER1H and TIMER1L. and store it into T. Now T is the time periode of our Input wave.
Intrupt means Intrupt on every rising edge.

Thanks
 
The capture function of the CCP module is designed to do exactly this.

Mike.
 
Yes i am using capture function of the CCP module.

But i want to know that how u guys use Capture function ???

In above program i am also using Capture function but result are not right.

Any one have example related to Capture function plz share.
 
Last edited:
Sorry, I should have looked at your code first. Having now looked at your code, I notice that you don't clear Timer1 when a capture occurs. Timer1 is not automatically reset to zero. A better way is to subtract the timer value from the previous value.

Mike
 
I wouldnt use the external clock for TMR1 in this application, you have no means by calculating the time period unless you use another TMR for a time reference. Just use the internal clock for TMR1 and an input from a pin to act as the signal

Code:
Device = 16F877
Xtal = 4
   		   
Dim uS as DWord
Dim Took_To_Long as Bit

Symbol GIE = INTCON.7				' Global Interrupt Enable Bit

Symbol Timer1 = TMR1L.WORD			' A special way of addressing both TMR1L and TMR1H with one register
Symbol TMR1_Enable = PIE1.0			' TMR1 interrupt enable
Symbol TMR1_Overflow = PIR1.0			' TMR1 overflow flag
Symbol TMR1_On = T1CON.0			' Enables TMR1 to start incrementing

Declare SERIAL_BAUD 19200
Declare RSOUT_PIN PORTA.1
Declare RSOUT_MODE TRUE
Declare RSOUT_PACE 5
			
ON_INTERRUPT Int_Sub

Goto Initialization			
			
Int_Sub:
	
	GIE = 0			
			
	If TMR1_Overflow = 1 And TMR1_Enable = 1 Then
		Took_To_Long = 1
		TMR1_Overflow = 0
	EndIf	
	
	GIE = 1
	
	Context Restore
	
Initialization:	

	ALL_DIGITAL = True
	
	High PORTA.1
	TRISA.0 = 1
	
	TMR1_Enable = 0
	GIE = 0
		
	INTCON.6 = 1   	  	' Peripheral Interrupts

	T1CON.1 = 0		' 1 = External clock from pin RC0/T1OSO/T1CKI (on the rising edge)
			   	' 0 = Internal clock (FOSC/4)

	T1CON.2 = 1		' 1 = Do not synchronize external clock input
			   	' 0 = Synchronize external clock input
				' When T1CON.1 = 0;
				'   this bit is ignored. Timer1 uses the internal clock when TMR1CS = 0.
					  	
	T1CON.4 = 1		' 11 = 1:8 prescale value
	T1CON.5 = 1		' 10 = 1:4 prescale value
				' 01 = 1:2 prescale value
				' 00 = 1:1 prescale value
	TMR1_Overflow = 0
	
	RSOUT "On",13
	
	TMR1_On = 0	  	  	' Disable TMR1 counting		
	TMR1_Enable = 1			' Enable TMR1 interrupts
	GIE = 1		  		' Enable Global Interrputs			
	
Main:

	Repeat

	Until PORTA.0 = 0		' Wait for PORTA.0 to go Low
	
	Repeat

	Until PORTA.0 = 1		' Wait for PORTA.0 to go high
	
	' The above two loops ensure we are not entering a pulse mid way, but on
	'   an actual rising edge
	
	Timer1 = 0			' Reset TMR1						  
	TMR1_On = 1	  	  	' Enable TMR1 counting			  	

	Repeat 

	Until PORTA.0 = 0 Or Took_To_Long = 1

	Repeat 

	Until PORTA.0 = 1 Or Took_To_Long = 1

	' The above 2 Repeat loops wait for a full cycle to occur
	
	TMR1_On = 0	  	  	' Disable TMR1 counting	

	If Took_To_Long = 1 Then	' Check if a time out occured
	    Took_To_Long = 0
		Goto Main
	Else
		Timer1 = Timer1 - 2	' Number of instructions that are executed while timed
			   	 	'  outside of pulse period
		uS = Timer1 * 8	  	' 1:8 prescaler & 4Mhz clock there for uS = Timer1 * 8
		RSOut "Period = ", Dec uS, " uS", 13
		RSOut "Frequency = ", Dec(1000000 / uS), " Hz", 13
		RSOut "RPM = ", Dec(1000000 / uS * 60), 13, 13
		Goto Main
	EndIf

Watch the program in action here

With a 4Mhz OSC and a TMR1 prescale of 1:8, every increment of TMR1 = 1/8th uS. This means that the longest period allowed is 8 * 65535 = 524280uS, or around 1/2 a second. If this occurs, the Took_To_Long flag will be set.

The faster the pulses comming in, the less accurate the program will become. Its like any sampling program that doesnt use interrupts too catch changes.

Notice that I first I wait for the input to go from low to high. This is so that I am deffinetly starting from a rising edge, and not jumping in half way.

Then I wait for that signal to go from high to low and stop the clock. Easy.

A little math and an output to see the result, presto.
 
Last edited:
Ayne said:
blueroomelectronics
What is ur Method of measuring RPM??
Time periode b/w pulses ???
OR
Pulsed received in Known Time???
Simple edge triggered pulse counting over time.

You can count low to high and high to low transistions, so 6 per rotation.

The motor is 12500RPM at 3V no load.
Gear reduction is 4.25:1 to the crown gear, then 3:1 for the sensor gear, and two more 3:1 till it hits the wheels.

Therefore the sensor gear rotates once every 12.75 rotations of the main motor and 9 times faster than the wheel axle. With three marking on the sensor gear we can see 54 edge transitions per whell rotation.

From the motors point of view...

12500 rpm (more with higher voltage but this is the rated speed at 3V)
So 12500/4.25/3 = 980.4 RPM on the sensor gear.
980.4 / 60 (for rotations per second) = 16.34
now multiply that by the 3 black markings each with 2 edges
16.34*3*2= 98 Hz, not too fast for a PIC to count.
Using RB4,5 change on interrupt should be a breeze to keep up.

PS I'm using both PWM channels to control the dual motor(s) speed, so I don't have the luxury of the CCP capture mode.

If 98Hz is too fast or slow add/remove black bands from the gear. I used 3 positions as there are three little circular indents on those gears make it easy to draw the hand painted (with black sharpie paint marker) stripes.
**broken link removed**
 
Last edited:
TIMER1 with 1:8 Prescale value.
Crystal Frequency = 20MHz
TIMER1 using internal clock cycle(Instruction Cycle).
Then
increment of TMR1 = ???

My bad math says it is about 1.6 microSeconds... Am I Right!!!
 
Yes, it will be 1.6uS. Have you added code to zero timer1 in your interrupt as I suggested earlier.

Mike.
 
Yes, it will be 1.6uS. Have you added code to zero timer1 in your interrupt as I suggested earlier.

Mike.

Now see on math.
x is the value of TIMER1, elapsed b/w two Low to High Edges.
**broken link removed**

I add zero zero value into TIMER1L and TIMER1H every time at startup of the loop..

Now on ur suggestion i will add zero zero value into TIMER1L and TIMER1H on interrupt.
 
What is the minimum range of traditional Tacho Meters???
I am asking this because i never see any tachometer. and here is no posibility to see the ranges of a traditional Tacho Meter.

Now i made some changes in my program and it is runing Smoothly. But code i will paste here with improved efficiency later.
 
Received: R03314

Received: R03314

Received: R03313

Received: R03312

Received: R03313

Received: R03314

Received: R03314

Received: R03312

My readings are right.
but they are roling near and near.... It is right!!

Ur sugestions
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top