Get a couple of Honeywell 1GT101DC sensors from DigiKey ($25 each). These give you a pulse every time a tooth comes by the tip of the sensor. Sprocket teeth work well too.
The sensors in the KOP only give you rate information and therefore make distance measuring nearly impossible. Besides, they are hard to use.
The Honeywell sensors can operate up to 10,000 teeth per second which is plenty fast for our needs. We used our RC to clock the speed of a Dremel tool at 30,000 rpm (2 pulses per revolution = 1000 Hz).
In the RC we used interrupts 2 and 3 (on RC DIO pins 1 and 2). Every time the gear tooth stimulates a low-to-high transition on the signal pin, an interrupt is thrown. From this we can count teeth over time (be sure to use an interrupt driven timer, like the one in the IFI whitepapers) to calculate rates or just count teeth for distance measurement in a drivetrain. Works on sprockets too–that’s were we generally place it.
I verified that 1000 interrupts per second (the Dremel experiment) slowed the “fast” execution loop of my RC by about 30%; sounds bad but that was still running over 14 thousand loops per second!!! The “slow” loop was not affected in the least by this many interrupts. Of course you MUST keep your interrupt servicing routine as short as possible. As such we simply increment an unsigned int as a counter:
void InterruptHandlerLow ()
unsigned char int_byte;
if (INTCON3bits.INT2IF && INTCON3bits.INT2IE) /* The INT2 pin is RB2/DIG I/O 1. /
INTCON3bits.INT2IF = 0;
else if (INTCON3bits.INT3IF && INTCON3bits.INT3IE) / The INT3 pin is RB3/DIG I/O 2. */
INTCON3bits.INT3IF = 0;
BE SURE TO DISABLE INTERRUPTS when resetting the counters outside of the ISR or else you can random bad things happen. As such:
INTCONbits.GIEL = 0; /* Disable Low Priority Interrupts /
pulse2Count = 0;
INTCONbits.GIEL = 1; / Enable Low Priority Interrupts */
To connect the sensor, simply wire the sensor’s ground, power and signal pins accordingly to a DIO input. I found that a 5K pull-up resistor on the output pin of the sensor makes a very clean square wave when running at high tooth speeds. You could also wire power to 12VDC, but it works fine for us drawing power from the DIO bank.
Initialization code for INT2 and INT3:
/* Initialize INT2 for pulse counting. INT2 is rc_dig_in01 */
INTCON3bits.INT2IE = 1; //enable INT2
INTCON3bits.INT2IP = 0; //set INT2 to low priority
INTCON2bits.INTEDG2 = 1; //rising edge
/* Initialize INT3 for pulse counting INT3 is rc_dig_in02*/
INTCON3bits.INT3IE = 1; //enable INT3
INTCON2bits.INT3IP = 0; //set INT3 to low priority
INTCON2bits.INTEDG3 = 1; //rising edge
INTCONbits.GIEL = 1; /* Enable Global Low Priority Interrupts */
Again, I high recommend implementing the interrupt-driven timer as described in the IFIrobotics.com whitepapers page. We use a 1 second tick to printf out the status of our code for debugging. We increment counters every execution of the fast and slow loops in normal and auton modes, and then printf the counts every second (and resetting them too) as a sort of speedometer which is very useful for watching the impact a high number of interrupts would have on the code.