Reading encoder using PIC18F452

Thread Starter

Smilodon

Joined Jul 9, 2013
5
I am trying to use PIC18F452 to read an optical encoder at a very low frequency (less than 100 Hz). I have tried three approaches:
-Using the CCP module to capture the rising edge and calculate the time between them to determine the period. This didn't work - The interrupt handler was never called
-Using polling: This worked in a way, but the time varied erratically and the result made no sense at all.
-Using the RBIF interrupt on port B -this works better than polling, but even then the result varies wildly.
The program also drives a seven segment display and reads an analogue input periodically.
I haven't been able to figure out what is wrong. Please help!
Thank you for reading.
 

Thread Starter

Smilodon

Joined Jul 9, 2013
5
With the CCP module, I set the CCP1M3:CCP1M0 to 0101 (to capture every rising edge), T3CCP2=0 (to use timer 1) and set the CCP1IE bit to enable the interrupt. However, the interrupt is never called and I cannot figure out how to make the CCP module work.
using the RBIF interrupt, I read the value of timer 1 when a rising edge occurred. I used
period = (65536-time1) + time to read the period when an overflow occurs, and period = time-time1 when it doesn't, where time1 is the previous reading, and time is the new reading.
Using this approach, the frequency reading varies through about 80 Hz.
Using polling with timer1, I get completely random readings.
 

joeyd999

Joined Jun 6, 2011
5,237
With the CCP module, I set the CCP1M3:CCP1M0 to 0101 (to capture every rising edge), T3CCP2=0 (to use timer 1) and set the CCP1IE bit to enable the interrupt. However, the interrupt is never called and I cannot figure out how to make the CCP module work.
using the RBIF interrupt, I read the value of timer 1 when a rising edge occurred. I used
period = (65536-time1) + time to read the period when an overflow occurs, and period = time-time1 when it doesn't, where time1 is the previous reading, and time is the new reading.
Using this approach, the frequency reading varies through about 80 Hz.
Using polling with timer1, I get completely random readings.
Why are you using a timer with an optical encoder (I assume you mean a quadrature encoder, no?)?

Perhaps if you provide us with data on the encoder itself, we may be of better help.
 

Thread Starter

Smilodon

Joined Jul 9, 2013
5
Actually, it's just an LED and a sensor, and I am using a sort of wheel with six equally spaced slots in it. I need to find its rotation speed.
 

joeyd999

Joined Jun 6, 2011
5,237
Actually, it's just an LED and a sensor, and I am using a sort of wheel with six equally spaced slots in it. I need to find its rotation speed.
And you didn't think this was pertinent?

The CCP module is the way to go. Post a schematic and some code.

There is *no* other way for us to help you.

Edit: BTW, this is a tachometer, not an encoder.
 

Thread Starter

Smilodon

Joined Jul 9, 2013
5
I used this code for initialising the CCP module (this is called from main())
Rich (BB code):
void CCP_Init(){
//Capture mode, every rising edge
CCP1M3=0; CCP1M2=1; CCP1M1=0; CCP1M0=1;
//Select timer as timer1
T3CCP2=0
//Enable interrupt
CCP1IE=1;
//Clear interrupt flag
CCP1IF=0;
}
The interrupt handler. The interrupt handler handles several interrupts - the ADC, timer 0 and timer 1, and the CCP module. the part relevant to the CCP module is given below:

Rich (BB code):
if (CCPIF&&CCPIE){
time = CCPR1;
count1= count //no of overflows of timer1
count=0; //clear overflow counter
if (count1>0){ //If overflow has occured
period = (65536-time1) + time;
}
else{
period = time-time1;
}
time1=time; //set current reading as previous value
CCPIF=0; //Clear flag
}
The value of period is then used by a different function to calculate the frequency when the display is updated. It is not included in the interrupt handler as it involves floating point calculations.

I don't have the code or the schematic with me now, but I wrote the code from memory. I think it is accurate.
 

tshuck

Joined Oct 18, 2012
3,534
Without knowing what the actual code/schematic/setup was that you used, we cannot tell you why what you used didn't work as you expected...
 

joeyd999

Joined Jun 6, 2011
5,237
First, in the initialization section, you are setting the CCP interrupt enable flag, but make sure you are also setting the global IE flag(s) (perhaps you are elsewhere in the program?).

Also, if you are trying to avoid a spurious interrupt by clearing CCP1IF, you should clear it before setting CCPIE:

Rich (BB code):
void CCP_Init()
{
	//Capture mode, every rising edge
	CCP1M3=0; CCP1M2=1; CCP1M1=0; CCP1M0=1;
	//Select timer as timer1
	T3CCP2=0
	//Clear interrupt flag before enabling
	CCP1IF=0;
	//Enable interrupt
	CCP1IE=1;
}
You should also consider initializing other variables in this section, like count and count1, but I can't figure out what they're for (more below).

In your interrupt routine:

Rich (BB code):
if (CCPIF&&CCPIE) 
{
	time = CCPR1;

	count1= count //no of overflows of timer1

	count=0; //clear overflow counter

	if (count1>0) //If overflow has occured
	{
		period = (65536-time1) + time;
	}
	else 
	{
		period = time-time1;
	}

	time1=time; //set current reading as previous value

	CCPIF=0; //Clear flag
}
Why do you think timer overflows need to be processed differently than if the timer did not overflow? You need to think of how things work in binary.

First off, CCPR1 is a 16 bit register. Computing (65536-time1) is the same as (0 - time1) or just -time1. So, you are telling the compiler that the time elapsed (period) is equal to the current timer value + the negative of the previous timer value. How is this different than period = time-time1?

Answer: It's not.

Secondly, you've got variables count and count1, neither of which seem to be doing anything except being set to 0. What is their purpose?
 

WBahn

Joined Mar 31, 2012
29,978
You almost certainly encountering bounce problems. As one of the slots begins to expose the detector to the LED you get a period in which it can't decide if it is seeing it or not. Similarly as you transition the other way as the slot leaves the LED. This is the whole idea behind a quadrature encoder. One way you can get the benefits of this is to mount a second LED/sensor half a slot away from the first (or aligned with any of the slot positions when the disk is positioned so that the first LED/sensor is midway between two slots).
 

MaxHeadRoom

Joined Jul 18, 2013
28,619
You almost certainly encountering bounce problems. As one of the slots begins to expose the detector to the LED you get a period in which it can't decide if it is seeing it or not.
I just built almost the very same idea, I just used a slot detector and used the first edge to reset T1 and read the T1 elapsed value at the second rising edge.
The results were very consistent between measurements using a servo controlled motor for the test.
Max.
 

ErnieM

Joined Apr 24, 2011
8,377
Bounce is the least of your problems. As long as your encoder is actually turning it should be minimal, and even if not a simple delay when processing a transition should lock out and nearby glitches.

The problem comes when the wheel is just about stationary, just on the edge, and is emitting a high frequency of glitches so a "STOP" looks kinda just like "GO LIKE HELL."

(Hint: that's why quadrature encoders were invented.)
 

Thread Starter

Smilodon

Joined Jul 9, 2013
5
I figured out what the problem was - I accidentally reset CCP1CON in the code - after initializing the CCP module. :D
I didn't notice that 65536-time1 + time is the same as time-time1. I corrected that.
count and count1 are for counting the overflows from timer 1 - because it overflows even with the maximum prescalar, as the minimum frequency to be measured is 1 Hz. count1 is used to capture the number of timer overflows as soon as the CCP interrupt occurs, and this is used for further calculations.
time1 is the previously measured value, and time is the current value.
 
Top