Encoder Code

ticks/time is the way to go. The velocity is a translation of ticks to wheel
circumference. It helps if you have a fair number of ticks/rev so you can keep the time interval short. Otherwise, it is hard to differentiate stopped (0 velocity) from very slow. A short interval also reduces issues raised by velocity change during the counting interval. The problem with large # of ticks though is you get flooded with interrupts if the robot is moving quickly.

You need to optimize based on what you want to accomplish. Understanding what you are using the velocity value for (and how accurate you need it to be) should be a driving factor in setting up your encoder code.

also, do i have to use kevin’s code with all of the initialization? or could i make my own based upon the phase a and b on the digital inputs?

is there also a way i could have the encoder counting in the background of my code? or does it do that already…if not, how (if possible).

from looking at the code, do i also have to reset the encoder count? do i also have to manually make the ENCODER_x_COUNT increment every time the encoder ticks?

You really want to use the interrupt capability (Digio 1, 2) for the Phase A.
I suppose you could do it without interrupts, but you would have a lot of overhead and I don’t think you would be able to get the accurate counting that you need.

If you are not familiar with interrupt service processing, I would say use Kevin’s code with as few modifications as possible. As you understand more about how it works, then experiment with changes.

Kevins code has the encoder tick counting handled at interrupt level, so it is done effectively “in the back ground”. Note that the encoder count is incremented (of decremented) in the ISR. (Interrupt Service Routine)

The question about resetting the encoder count depends on what you want to do, and how many ticks you are expecting. I generally find that using a “long” to store the count will let me count for way more than the amount of ticks I could get during a match time. This does mean that you need to store copies in routines that are measuring an elapsed distance.

Note also that I use “long” and not “unsigned long” since the code counts backwards when (negative) when the drive is reversed. If you have a system with encoders where you can not detect rotation direction, you may want to zero the count whenever you stop.

To compute velocity, one of the timers can be used to provide accurate interval info. Based on the timer, you can run a routine periodically that computes the velocity over the time interval.

I know it’s been a while, but I’m designing the ratios for our prototype base now…

how many interrupts per second can the processor handle?

Our highest gear puts the encoder shaft at about 1500 rpm. We’re using the grayhill 128’s that Kevin reccomends (because we already have them) and that puts out a lot of counts per second , 1486.6 [rpm] / 60 [seconds/min] * 128 [counts/rev] = 3172 counts per second!!!, and that’s only one side. That seems like way too much to me.

can anyone confirm this?

Just saw grayhill offers lower res in the same package. Both the 32/25 look good with 793 and 620 interrupts per second at full speed in high.

Still, I would like to know what the processor can handle.

As I recall, it can handle somewhere in the 5000-7000 range. Keep in mind though, that your encoders will probably not be your only interrupt events. Serial communication (i.e. to the camera) & ADC conversions both also count as interrupts. You may also just have a clock running for other timing.

Something else to consider: where are you putting your encoder, relative to the motor, gearbox & wheel? I would suggest putting it close to the wheel.

Let’s suppose you have 10" diameter wheels (fairly large by FIRST standards). That means you have a 31.4" circumference. If that’s in a 1:1 ratio with your encoder at 128 ticks per revolution, that means you get an encoder tick every 1/4" of linear movement. That should be sufficient. (I usually aim for 1/4"-1/2" per tick, which has always been more than enough.)

Suppose instead, you have very small wheel - 12" circumference (about 4" dia), and a very fast robot - 20 ft/sec. That means you get 128 ticks per linear foot, and 2560 ticks per second (and 0.1" per tick) at full speed. Double that to count both sides, and you’re only at 5k interrupts per second, assuming you’re traveling at full speed with both wheels.

If your robot is at a more reasonable speed in the 10-14 ft/sec range, and your wheels are typical FIRST wheels in the 4"-8" dia range, and you have a 1:1 ratio between the encoder and the wheels, at 128 ticks/rev you’ll have a good balance among ticks/second, ticks/inch and total interrupts.

We’re using all dead axles so It’s far easier to mount it on the gearbox output shaft. There is a either a 12/28 or 16/28 (depending on how many motors were using in the drive) reduction. The stats above are for the 16/28. Also, the res for above is .33 and .43 inches. [also, the sprocket ratio only effects the resolution]

I think we’ll go with 64 counts. That gives us about 1586 ticks per second (per side) and a res of .17 inches. That leaves plenty of interrupts open for possible future addons.

Does that sound good?

We used the 64 Grayhill encoder back in '06 with the speed of our shooters (~1300 rpm) and they worked pretty well for us. We stayed away from the 128 model, way too much resolution at that speed, expecially for that application where all we needed was velocity, not position.

I read the encoder_readme.txt and encoder.h files but I didn’t find out how to disable unused counters. I tried commenting out the ENABLE_ENCODER_X entries in encoder.h but that didn’t work.

Any details on how to disable unused encoder channels?

Thanks again Kevin. I’m really looking forward to using this.:slight_smile:

You could cut the number of interrupts you are handling down by about a factor of 4 or 5 and see absolutely no change in performance. I would bet my shiny new iPhone that no FIRST robot ever has, or ever will, be accurate to 0.17". You may want to shoot for a resolution of .5 or 1 inch. It will drop the the cost of your hardware solution and save your programmers a whole bunch of headache. While the amount of interrupts you are looking at are in the theoretical limits of what the PIC can handle, your program will have to be very robust and optimized to not crash the system.

It seems the encoder_readme.txt file directs you to instructions in the encoder.h file that I forgot to include :rolleyes:. Until I get around to fixing this, you should be able to disable individual channels by commenting out the associated #define ENABLE_ENCODER_n line in the encoder.h file.


Thanks Kevin. That works great.

I’m also using your “No Jitter” PWM code to run my drive motors on channels 13 and 15.

My bot will only be using 3 or 4 PWM channels total. Should I use channels 13 through 16 if I am using interrupts for encoders? The reasons for using (or not using) PWMs 13 through 16 have never been clear.


The twelve “master-generated” PWM signals can only be changed when the regular communication happens between the two CPUs every 26.2 milliseconds. The original reason to use the four “user-generated” PWM signals was to be able to update them more often than that if necessary. The reason not to use them was that they were prone to jittery operation if the user program had interrupts enabled, including turning on Victor speed controllers when the code requested they be off. You can see why it might not be good to use them for drivetrain motors.

With Kevin’s new stable PWM code, there’s a new reason to use them: it’s possible to get better resolution than the standard PWM outputs. If you want extra-precise control of servo position, you can essentially concentrate the 255 discrete steps into a smaller range of travel. This works great for controlling the CMUCam tilt/pan assembly and getting much better information on exactly which direction the green target light is from the robot.

Kevin - during the season you worked a bit on new full-blown camera code with the improved resolution (which I believe you can still download off the link here in the forums, but not on your FRC page). Any chance you’ll post the the streamlined version with the same improvements? We had it working at one point but we got rid of it in favor of the old version when we had to reprogram some other portions of our code.

I try to use kevin’s encoder
I opend the project :‘encoder.mcw’
but I get en errors :
'encoder.c:130:Error [1205] unknown member ‘INT3IP’ in ‘___tag_223’
‘encoder.c:130:Error [1131] type mismatch in assignment’

what can I do ??? :ahh:


You can do two things. First, read the documentation that came with the code you’re using.

Second, learn about the wonderful search facility here on the Chief Delphi forums. Searching for the keyword INT3IP provides plenty of information that explains the problem and its solution.

Alan, I think you forgot the most important:

Welcome to Chief Delphi! (first post of Avi):smiley:

I’ve been programming for over 25 years, including some fairly heavy scientific programming and hobby programming on PIC microprocessors, and I have found this entire process of mentoring in FIRST to be quite confusing.

I think that those who make obnoxious remarks have probably forgotten what it was like to be completely new at this, on a rookie team, and working with new tools.

I agreed to mentor. Little did I know that I would be directed to read hundreds of pages of tedious documentation, when instead a simple example might seem to be in order.

Oh, I know: search the forum for “simple example” … sorry, no hits.

I don't think the obnoxious remarks are made on purpose. Sometimes people are kidding and the written English language is tricky when it comes to expressing emotions. ( I used to be a writer.)
One basic problem is that brilliant design engineers, software engineers, electrical engineers do not always make good online teachers. A teacher is someone TRAINED to break down ideas for the expressed intent of explaining to someone who needs a very basic explanation with simple, concrete examples. (Teachers deserve a LOT more respect!) Half the stuff I read in here would be impossible to understand if I hadn't studied PIC programming and read a thick book on the chips that solved all my insomnia problems! 

This stuff is darned confusing and C is a fairly hostile language when it comes to humans. It’s highly unintuitive and I can hardly wait for the next fad language, or a decent C translator to come along and make some sense out of it all. (Oh I CAN do it… but it takes more time and effort than it should and goes against my philosophy that computers should make things easy for people and not the other way around.)
In the end, I can hardly disagree that there are a profound lack of SIMPLE examples and explanations of concepts. That’s one reason mentors are important. If you understand it, then you can translate for the kids on the team in a way they can understand. Still I agree with you, it’s all too cryptic and tedious. Mentoring s tough (and sometimes thankless) and I doubt we’ll ever see someone get a Nobel prize out of their involvement with a FIRST team, but it’s important if not rewarding.
And by the way, if no one else has said it, Thanks for being a mentor.

Best wishes


The default code is a simple example. The documentation for the default code explains it well.

Maybe I just never found this “default code” … are you talking about the EasyC, Kevin Watson’s JPL code, or the regular MPLab code? Where is the documentation? The only documentation I have seen is for EasyC, which wasn’t really recommended at our kickoff meeting, so I only looked at it because the Juggernaut team helping us uses EasyC.

Everything I’ve seen in MPLab doesn’t look documented at all. Just a bunch of empty functions with no explanation.