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.

Pic Micro RPM Counter using both Interrupt TM1 and On Interrupt (RB0)

Status
Not open for further replies.

mesamune80

New Member
Hi All,

Recently i am doing a fast speed (1000~4000) RPM counter using a motor's shaft with a plate and 12 slot evenly distributed on the plate.The plate is placed between a OMRON photomicrosensor EE-SX 671 .so that i can get a ON OFF signal (rising edge) when a slot is slice through the sensor.
but this idea don know really can work or not because when test with low speed (500) this work fine .but when the speed is as fast as 3000 RPM then my system can't detect the speed anymore.Does Anyone has any idea?
Thanks.
 
when test with low speed (500) this work fine .but when the speed is as fast as 3000 RPM then my system can't detect the speed anymore.Does Anyone has any idea?
You need to do one (or more) of the following in order of probability:
1: Optimize/debug your code.
2: Use a timer/counter in the PIC for counting pulses/measuring period.
(OOppps Just looked at the subject line! :eek: )
3: Increase the PICs clock speed.
Your pulse rate at 3000 RPM with a 12 slot wheel should only be around 600Hz which the PIC should have no problem counting even in polled mode. Why are you using two interrupts? You should be able to do it with just one. Are you multiplexing a display at the same time or some other task? Maybe post a schematic of your circuit and the code.
 
Last edited:
For your information i am using timer one interrupt to act as a counter(measuring period) and On interrupt external interrupt ,rising edge detection for RB0 to count the slotted wheel while slicing through the sensor.
Microcontroller i am using:pIC16F877 4MHZ

This is the picbasic compiler i am using:
https://oshonsoft.com/pic.html

This is the sensor i am using :
http://oeiwcsnts1.omron.com/pdfcatal.nsf/0/3D4B80C095EC4F4886256C5600712077/$File/D21EESX4700902.pdf?OpenElement

this is the code:
Dim interrupt_count As Byte
Dim timeroffset As Word
Dim tmroffset As Word
Dim count1 As Word 'RB0 interrupt count

EEPROM 21, 0, 0, 0, 0,

TRISA = %0000000
TRISC = 0
OPTION_REG.6 = 1 'RB0/INT edge triggered (Rising)
OPTION_REG.7 = 0 ' PORTB pull up enabled by individual port latch value
INTCON.INTE = 0 'External interrupt for RB0 disable!
INTCON.7 = 1 'Global interrupt enabled!

Symbol t1ck0 = T1CON.4 ' Timer1 Input Clock Prescale Select bits
Symbol t1ck1 = T1CON.5 ' Timer1 Input Clock Prescale Select bits
Symbol sensor = PORTD.0 'sensor
'WaitMs 3000
Define LCD_LINES = 2
Define LCD_CHARS = 8
Define LCD_BITS = 4 'allowed values are 4 and 8 - the number of data interface lines
Define LCD_DREG = PORTB
Define LCD_DBIT = 4 '0 or 4 for 4-bit interface, ignored for 8-bit interface
Define LCD_RSREG = PORTB
Define LCD_RSBIT = 1
Define LCD_EREG = PORTB
Define LCD_EBIT = 3
Define LCD_RWREG = PORTB 'set to 0 if not used, 0 is default
Define LCD_RWBIT = 2 'set to 0 if not used, 0 is default

Lcdinit
WaitMs 2 'wait For lcd To start
Lcdcmdout 0x0e 'Underline cursor on
Lcdcmdout LcdClear
Lcdcmdout LcdLine1Home
Lcdout "RPM" 'Clear LCD display
Lcdcmdout LcdLine2Home
Lcdout "Ver1.0"

timeroffset = 15535 ' interrput occurs every 50 ms(0.05sec)
t1ck0 = 0 'prescaler 1:1 selected
t1ck1 = 0

PIE1.0 = 1 'enable TMR1 interupts
INTCON.6 = 1 'enable all unmasked interrupts
INTCON.7 = 1 'enable Global interrupts
PIR1.0 = 0 'reset interupt flag
T1CON.0 = 0 'stop the timer
TMR1H = timeroffset.HB 'timersethigh 'load a start up value into the timer
TMR1L = timeroffset.LB 'timersetlow
T1CON.0 = 1 'start the timer
INTCON.INTE = 1 'External interrupt for RB0 enable!

main:

end

On Interrupt

If INTCON.1 = 1 Then 'RB0 External interrupt count!
count1 = count1 + 1
Write 21, count1.LB
Write 22, count1.HB
INTCON.INTF = 0 'RB0 External Interrupt flag bit cleared!
Endif

If PIR1.0 = 1 Then 'TMR1F - Timer 1 overflow interrupt flag bit ;1=overflow
INTCON.INTE = 0 'External interrupt for RB0 disable!

T1CON.0 = 0 'stop the timer

TMR1H = timeroffset.HB 'timersethigh 'load a start up value into the timer
TMR1L = timeroffset.LB 'timersetlow
PIR1.0 = 0 'reset TMR1 interupt flag

interrupt_count = interrupt_count + 1 'timer 1 interrupt count
If interrupt_count = 20 Then '1 sec elapsed!

interrupt_count = 0
Gosub menu1
count1 = 0
Write 21, count1.LB
Write 22, count1.HB
T1CON.0 = 1 'start the timer
INTCON.INTE = 1 'External interrupt for RB0 enable!
Endif
Endif

T1CON.0 = 1 'start the timer
INTCON.INTE = 1 'External interrupt for RB0 enable!

Resume 'return back to where set timer was called
Enable 're enable interrupts

menu1:
Lcdcmdout LcdClear
Lcdout "RPM"
Lcdcmdout 0xc0
Lcdout "RPM:" 'count

Gosub update1

Return

update1:
Read 21, count1.LB
Read 22, count1.HB

Lcdcmdout LcdLine2Pos(5)
count1 = count1 * 5 '12 slot used (1 minute Total count)
Lcdout #count1 ' RPM
Return


what my program going to do is at 1st it will start the 0.05 sec timer,and RB0 will start generating pulse while the wheel is rotating ,so after 2 times timer overflow which is 1 sec the total number of slot passing through the sensor since the timer was started is now being stored and display to the LCD.the LCD will refresh almost every sec i can said.This process is repeated........
Anyone has any idea can share with me and the others ,after study my code above.So fell free to share anything ok! Enjoy ur Day!
 
You appear to be writing the values to EEPROM. This is very slow.

Set up a new variable such as CountCopy and replace
Write 21, count1.LB
Write 22, count1.HB

With
CountCopy = count1

and then just use CountCopy in your update routine.

Whoops, I just noticed you read from the EEPROM back into count1. Don't copy CountCopy into count1, just use CountCopy instead of count1 in the update routine.

HTH

Mike.
Edit, I just noticed that you do everything on interrupt. You need to change your code so that the interrupt just counts pulses and your main code updates the LCD etc.
 
Last edited:
i just tested the new modified code with my hardware ,but i found out that when i start the wheel in a differnt position then the result will be different,for instance when i start up the wheel with a slot is in between the sensor which will give high to my uC then when i start the wheel at about 3000 RPM it looks just like the sensor is keeping ON,then my LCD shown 500 for my RPM +_+.And another case is when i start up with no slot between the sensor when i start the wheel the sensor seem to be not ON at all!!!and of course my LCD shown RPM:0 while the whell is rotating in 3000+ RPM =_=
What is the reason behind this ?
And Pommie ,how do i manage to change my code so that only waiting for interrup,and i though my routine for updating the LCD is out of the interrupt routine?Please help me!!! >_< Thanks.
 
I don't know the compiler that you are using but your code should do something like this pseudo code,

Code:
On Interrupt
    if RB0int
        CountCopy=Count
        Count=0
    if Timer1int
        Count=Count+1
end interrupt

Main
    if CountCopy=0 then goto Main
    Display CountCopy
    CountCopy=0
    goto Main

HTH

Mike.
 
I use BASIC.. If I can not do the task in BASIC, I use assembler. There is no C for me, it is the same as basic when you are using microcontrollers.

There is easy code and real code. Basic/C or assmbler. Just my thought.
 
Last edited:
how about if i use a faster crystal oscillator? Can the speed be improve?
Let say i use 20MHZ instead of 4MHZ (which i am using now) a instruction cycle will take 200ns to accomplish instead of 1us.This definately will help right?
 
Using a faster crystal may help but your biggest problems are,

You are writing (and reading back) to EEPROM and writing to the LCD in your interrupt service routine. Both these things have a long wait before they finish. I'm assuming your interrupt start with the "on interrupt" instruction and ends at the resume instruction.

If I had to guess what your code would be, it would be something like,

Code:
Dim interrupt_count As Byte
Dim timeroffset As Word
Dim tmroffset As Word
Dim count1 As Word 'RB0 interrupt count
Dim countcopy As Word

TRISA = %0000000
TRISC = 0
OPTION_REG.6 = 1 'RB0/INT edge triggered (Rising)
OPTION_REG.7 = 0 ' PORTB pull up enabled by individual port latch value
INTCON.INTE = 0 'External interrupt for RB0 disable!
INTCON.7 = 1 'Global interrupt enabled!

Symbol t1ck0 = T1CON.4 ' Timer1 Input Clock Prescale Select bits
Symbol t1ck1 = T1CON.5 ' Timer1 Input Clock Prescale Select bits
Symbol sensor = PORTD.0 'sensor
'WaitMs 3000
Define LCD_LINES = 2
Define LCD_CHARS = 8
Define LCD_BITS = 4 'allowed values are 4 and 8 - the number of data interface lines
Define LCD_DREG = PORTB
Define LCD_DBIT = 4 '0 or 4 for 4-bit interface, ignored for 8-bit interface
Define LCD_RSREG = PORTB
Define LCD_RSBIT = 1
Define LCD_EREG = PORTB
Define LCD_EBIT = 3
Define LCD_RWREG = PORTB 'set to 0 if not used, 0 is default
Define LCD_RWBIT = 2 'set to 0 if not used, 0 is default

	Lcdinit
	WaitMs 2 'wait For lcd To start
	Lcdcmdout 0x0e 'Underline cursor on
	Lcdcmdout LcdClear
	Lcdcmdout LcdLine1Home
	Lcdout "RPM" 'Clear LCD display
	Lcdcmdout LcdLine2Home
	Lcdout "Ver1.0"

	timeroffset = 15535 ' interrput occurs every 50 ms(0.05sec)
	t1ck0 = 0 'prescaler 1:1 selected
	t1ck1 = 0

	PIE1.0 = 1 'enable TMR1 interupts
	INTCON.6 = 1 'enable all unmasked interrupts
	INTCON.7 = 1 'enable Global interrupts
	PIR1.0 = 0 'reset interupt flag
	T1CON.0 = 0 'stop the timer
	TMR1H = timeroffset.HB 'timersethigh 'load a start up value into the timer
	TMR1L = timeroffset.LB 'timersetlow
	T1CON.0 = 1 'start the timer
	INTCON.INTE = 1 'External interrupt for RB0 enable!

main:
	If countcopy=0 then goto main
	gosub menu1
	countcopy=0
	goto main

On Interrupt
	If INTCON.1 = 1 Then 'RB0 External interrupt count!
		count1 = count1 + 1
		INTCON.INTF = 0 'RB0 External Interrupt flag bit cleared!
	Endif
	
	If PIR1.0 = 1 Then 'TMR1F - Timer 1 overflow interrupt flag bit ;1=overflow
		T1CON.0 = 0 'stop the timer	
		TMR1H = timeroffset.HB 'timersethigh 'load a start up value into the timer
		TMR1L = timeroffset.LB 'timersetlow
		PIR1.0 = 0 'reset TMR1 interupt flag
	
		interrupt_count = interrupt_count + 1 'timer 1 interrupt count
		If interrupt_count = 20 Then '1 sec elapsed!	
			interrupt_count = 0
			Gosub menu1
			countcopy=count1
			count1 = 0
			T1CON.0 = 1 'start the timer 
		Endif
	Endif
	
Resume 'return back to where set timer was called
	

menu1:
	Lcdcmdout LcdClear
	Lcdout "RPM"
	Lcdcmdout 0xc0
	Lcdout "RPM:" 'count

	Gosub update1

	Return 

update1:
	Lcdcmdout LcdLine2Pos(5)
	countcopy = countcopy * 5 '12 slot used (1 minute Total count)
	Lcdout #countcopy ' RPM
	Return

Mike.
 
No i try to not use EEPROM and i use countcopy to count the interrupt

like this:
Dim interrupt_count As Byte
Dim countcopy As Word
Dim count_10_interrupts As Byte
Dim timeroffset As Word
Dim tmroffset As Word
Dim count1 As Word 'RB0 interrupt count

TRISA = %0000000
TRISC = 0
OPTION_REG.6 = 1 'RB0/INT edge triggered (Rising)
OPTION_REG.7 = 0 ' PORTB pull up enabled by individual port latch value
INTCON.INTE = 0 'External interrupt for RB0 disable!
INTCON.7 = 1 'Global interrupt enabled!

Symbol t1ck0 = T1CON.4 ' Timer1 Input Clock Prescale Select bits
Symbol t1ck1 = T1CON.5 ' Timer1 Input Clock Prescale Select bits
Symbol sensor = PORTD.0 'sensor

'WaitMs 3000
Define LCD_LINES = 2
Define LCD_CHARS = 8
Define LCD_BITS = 4 'allowed values are 4 and 8 - the number of data interface lines
Define LCD_DREG = PORTB
Define LCD_DBIT = 4 '0 or 4 for 4-bit interface, ignored for 8-bit interface
Define LCD_RSREG = PORTB
Define LCD_RSBIT = 1
Define LCD_EREG = PORTB
Define LCD_EBIT = 3
Define LCD_RWREG = PORTB 'set to 0 if not used, 0 is default
Define LCD_RWBIT = 2 'set to 0 if not used, 0 is default

Lcdinit
WaitMs 2 'wait For lcd To start
Lcdcmdout 0x0e 'Underline cursor on
Lcdcmdout LcdClear
Lcdcmdout LcdLine1Home
Lcdout "RPM" 'Clear LCD display
Lcdcmdout LcdLine2Home
Lcdout "Ver1.0"

PORTC.1 = 1 'dir of movement
PORTC.0 = 1 'output Enable for stepper driver
timeroffset = 15535 ' interrput occurs every 50 ms(0.05sec)
t1ck0 = 0 'prescaler 1:1 selected
t1ck1 = 0


PIE1.0 = 1 'enable TMR1 interupts
INTCON.6 = 1 'enable all unmasked interrupts
INTCON.7 = 1 'enable Global interrupts
PIR1.0 = 0 'reset interupt flag
T1CON.0 = 0 'stop the timer
TMR1H = timeroffset.HB 'timersethigh 'load a start up value into the timer
TMR1L = timeroffset.LB 'timersetlow
T1CON.0 = 1 'start the timer
INTCON.INTE = 1 'External interrupt for RB0 enable!

main:

End

On Interrupt

If INTCON.1 = 1 Then 'RB0 External interrupt count!
count1 = count1 + 1
countcopy = count1
'Write 21, count1.LB
'Write 22, count1.HB
INTCON.INTF = 0 'RB0 External Interrupt flag bit cleared!
Endif

If PIR1.0 = 1 Then 'TMR1F - Timer 1 overflow interrupt flag bit ;1=overflow
INTCON.INTE = 0 'External interrupt for RB0 disable!

T1CON.0 = 0 'stop the timer

TMR1H = timeroffset.HB 'timersethigh 'load a start up value into the timer
TMR1L = timeroffset.LB 'timersetlow
PIR1.0 = 0 'reset TMR1 interupt flag

interrupt_count = interrupt_count + 1 'timer 1 interrupt count
If interrupt_count = 20 Then

interrupt_count = 0
Gosub menu1
count1 = 0
countcopy = count1
T1CON.0 = 1 'start the timer
INTCON.INTE = 1 'External interrupt for RB0 enable!
Endif
Endif
T1CON.0 = 1 'start the timer
INTCON.INTE = 1 'External interrupt for RB0 enable!

Resume 'return back to where set timer was called
Enable 're enable interrupts


menu1:
Lcdcmdout LcdClear
Lcdout "RPM"
Lcdcmdout 0xc0
Lcdout "RPM:" 'count
Gosub update1

Return

update1:

Lcdcmdout LcdLine2Pos(5)
countcopy = countcopy * 5 '12 slot used (1 minute Total count)
Lcdout #countcopy ' RPM

Return
 
Yeah I can't follow BASIC exactly but if you use the EEPROM, not only is it far too slow but it's also got a limited rewrite endurance. If you write it a lot (like millions, check your spec sheet) the memory location will be permanently damaged and unusable. That won't happen in the life of the product if you only use it for something like configuration data or even backing up the timeclock every couple of minutes. Writing many times a sec can realistically exceed the write endurance specification long before the product's expected life is met.
 
I give up. You need to work your way through some tutorials.

Merry Xmas,

Mike.
 
i already try out the code but it seem high speed "cannot be detect" may be i should try to use different crystal.at low speed i connect a motor to +5VDC and i get the correct RPM displayed 571 ,but after i connect the motor to +12VDC the motor spin even faster than before,and i got a false displayed at my LCD.which is 505 .
 
BASIC is really slow, and it's Interrupt code in my experence is the pits. Try a shot at assembler, it's not that hard. Upping the crystal to 20MHz may help, also using timer0 and timer1 might help. Or use port B4,5 (int on change).
You might even try the CCP1 & 2 modules. PS a debugger will help alot, check on my site for the instructions on how to build one.
 
4000 rpm is only 67 pulses per second. Even with 12 slots per rotation you're only talking an 800hz square wave... You should NOT be having trouble reading that unless your code is wasting huge amounts of processing time.
 
ya ,it seem to be my mistake pump in 24VAC instead of 24 VDC 。now my RPM counter is running smoothly.thanks for everyone's helps and supports.
Have a nice day!!!
 
Status
Not open for further replies.

Latest threads

Back
Top