Questions about encoders, interrupts, and serial communication

Hello all. I have a project that will require the use of precision encoders with a very high PPR. Obviously a faster processor with be required so the teensy 4.0 looks like a good option compared to any audrino which may have trouble keeping up with pulse count at higher speeds. My question is about serial communication and interrupts. In order to ensure encoder count is stable without errors, it has to take #1 priority. Serial communication to another board will be required to relay variable values, but it does not have to take priority and short delays would be okay. If interrupts are used for the encoder inputs, will the interrupts "interrupt" serial communication and then pick up where it left off when it has a chance? Also, if there are any recommendations for how serial communication should be setup, I'm all ears. My initial thought was to use a master board to collect data with each encoder board going to a separate serial input on the master, and then poll for serial data. Any advice or insight would be greatly appreciated.
 
You'll probably get a variety of responses to this question. I'll give you my thoughts.

Yes, you can set interrupt priorities, and interrupts can be nested, so if you plan to do pulse counting (as opposed to period measurement) on the encoders, you can make your timer interrupt higher priority than serial comm interrupts. That means the timer ISR will execute very regularly, with minimum jitter, and you can read the running count of encoder pulses in that ISR for the best accuracy. Serial communication is interrupt-driven, at least by default, but if you have your timer ISR set to a higher priority, those interrupts will not get in the way of your encoder reads. The UART ISRs simply move data between FIFOs and the UART registers. Your code does serial read/write via the FIFOs, and should not be done at interrupt level. Serial comm will not affect the accuracy of your speed measurement as long as you do that. Another option is to do period measurement on the encoder inputs, and you can take a look at library FreqMeasureMulti for that. It's very cool.
 
Hello all. I have a project that will require the use of precision encoders with a very high PPR.
The most important information would be the generated pulses per second which of course depends on the max rotation speed you need to support.
Generally, reading high frequency encoders with interrupt based algorithms can be problematic. Mainly because at higher frequencies the probability rises that one or more of the encoders generate pin interrupts while the processor is busy handling the pin interrupt for another signal transition. This might generate count errors. Up to medium count rates (<~500kHz) polling algorithms might be a better fit. (Here a T3.2 polling a 800kHz encoder, while it controls the driving stepper and communicates the readouts to the DRO via a web socket connection https://www.youtube.com/watch?v=ZLVXQfjfS6Q. Edit: Sorry, wrong link, this one shows the polling: https://www.youtube.com/watch?v=6hmIzB7Qtk0, the first one shows the hardware counters)

Anyway, the T4.x processors have 4 hardware based counters which do the counting in the background with no need for interrupts and without loading the processor much. They should be good for several MHz count rate. Mjs513 maintains a library for easy access of those counters: https://github.com/mjs513/Teensy-4.x-Quad-Encoder-Library.

If you want to test your code you might be interested in this simulator firmware: https://github.com/luni64/EncSim

Addendum: If you don't have other requirements (or insanely high frequencies) one T4.x will probably be more than fast enough to handle your four encoders.
 
Last edited:
The most important information would be the generated pulses per second which of course depends on the max rotation speed you need to support.
Generally, reading high frequency encoders with interrupt based algorithms can be problematic. Mainly because at higher frequencies the probability rises that one or more of the encoders generate pin interrupts while the processor is busy handling the pin interrupt for another signal transition. This might generate count errors. Up to medium count rates (<~500kHz) polling algorithms might be a better fit. (Here a T3.2 polling a 800kHz encoder, while it controls the driving stepper and communicates the readouts to the DRO via a web socket connection https://www.youtube.com/watch?v=ZLVXQfjfS6Q. Edit: Sorry, wrong link, this one shows the polling: https://www.youtube.com/watch?v=6hmIzB7Qtk0, the first one shows the hardware counters)

Anyway, the T4.x processors have 4 hardware based counters which do the counting in the background with no need for interrupts and without loading the processor much. They should be good for several MHz count rate. Mjs513 maintains a library for easy access of those counters: https://github.com/mjs513/Teensy-4.x-Quad-Encoder-Library.

If you want to test your code you might be interested in this simulator firmware: https://github.com/luni64/EncSim

Addendum: If you don't have other requirements (or insanely high frequencies) one T4.x will probably be more than fast enough to handle your four encoders.

Nice build with the stepper and DRO. I'm curious what type serial communication you used to send data to the DRO. You mentioned that interrupts could be problematic for encoders because one interrupt could interrupt another, but I would not have an issue having a separate board for each encoder if need be. Accuracy is the main concern here, and a $20 board is worth it to not have any missed pulses. That said, if one board can easily handle 4 encoders without errors, then that's even better. The main concern was the serial communication as I do not quite understand how it works when it comes to interrupts. You mention that the 4.x processors have hardware based counters which do counting in the background which may be the answer. Thanks for your reply.
 
You'll probably get a variety of responses to this question. I'll give you my thoughts.

Yes, you can set interrupt priorities, and interrupts can be nested, so if you plan to do pulse counting (as opposed to period measurement) on the encoders, you can make your timer interrupt higher priority than serial comm interrupts. That means the timer ISR will execute very regularly, with minimum jitter, and you can read the running count of encoder pulses in that ISR for the best accuracy. Serial communication is interrupt-driven, at least by default, but if you have your timer ISR set to a higher priority, those interrupts will not get in the way of your encoder reads. The UART ISRs simply move data between FIFOs and the UART registers. Your code does serial read/write via the FIFOs, and should not be done at interrupt level. Serial comm will not affect the accuracy of your speed measurement as long as you do that. Another option is to do period measurement on the encoder inputs, and you can take a look at library FreqMeasureMulti for that. It's very cool.

Thanks. I need to look into how to set priority then. I wasn't aware that I could override the serial com priority set by default and didn't know if all serial com types work the same. Really appreciate it.
 
You mentioned that interrupts could be problematic for encoders because one interrupt could interrupt another, but I would not have an issue having a separate board for each encoder if need be.
What max pulse frequency are you expecting?

Nice build with the stepper and DRO. I'm curious what type serial communication you used to send data to the DRO.
That was sent via Ethernet / WebSocket to the displaying browser
 
Last edited:
What max pulse frequency are you expecting?


That was sent via Ethernet / WebSocket to the displaying browser

Rough calculations give me about 40-60 khz. I noticed you were using a 40k cts/rev in one of your vids. That is about the resolution I'll need for my encoder if I can find one that's reasonably priced. This isn't for a CNC, but a CMM, so I wouldn't need near the speeds you were testing at, though it's quite impressive what you achieved there.

When you write your serial data, do you write it only once or do you have it sending in a loop, even at the same value?
 
Oh, 40-60kHz for 4 encoders is nothing. You can simply use the standard Encoder library, or if you want to make absolutely sure to not loose a count use the hardware counters. Also, at these low frequencies I doubt that you need to fiddle around with interrupt priorities. If you want to anyway, you can use NVIC_SET_PRIORITY(IRQ, nr), lower numbers give higher priority. Here the list of IRQ numbers https://github.com/PaulStoffregen/cores/blob/master/teensy4/imxrt.h.

When you write your serial data, do you write it only once or do you have it sending in a loop, even at the same value?
I usually avoid writing repetitive data to an interface if possible. Just Serial.print what you need to print. Teensyduino is making a good job in collecting data and send in blocks if necessary. Please also consider that the virtual Serial connection PC<->Teensy is capable of sending some 15-25MB per seconds without any problem. If you need to send over UART you can achieve up to 3MB /sec. I assume that your CMM will not generate data at those rates...


Regarding encoders. If you are making a CMM, make sure to not only look at the resolution of the encoder but, more important, at the system accuracy. This not only depends on the number and precision of lines on the encoder disk but also on the mechanical precision of the bearings and the alignment of the system. I'm a bit biased (started my prof. career at the company in the eighties) but I still recommend to have a look here: https://www.heidenhain.com/products/rotary-encoders. They usually guarantee a system accuracy of 1/20th of the line period. Pricewise a bit on the high side but they are worth it.
 
Last edited:
Oh, 40-60kHz for 4 encoders is nothing. You can simply use the standard Encoder library, or if you want to make absolutely sure to not loose a count use the hardware counters. Also, at these low frequencies I doubt that you need to fiddle around with interrupt priorities. If you want to anyway, you can use NVIC_SET_PRIORITY(IRQ, nr), lower numbers give higher priority. Here the list of IRQ numbers https://github.com/PaulStoffregen/cores/blob/master/teensy4/imxrt.h.


I usually avoid writing repetitive data to an interface if possible. Just Serial.print what you need to print. Teensyduino is making a good job in collecting data and send in blocks if necessary. Please also consider that the virtual Serial connection PC<->Teensy is capable of sending some 15-25MB per seconds without any problem. If you need to send over UART you can achieve up to 3MB /sec. I assume that your CMM will not generate data at those rates...


Regarding encoders. If you are making a CMM, make sure to not only look at the resolution of the encoder but, more important, at the system accuracy. This not only depends on the number and precision of lines on the encoder disk but also on the mechanical precision of the bearings and the alignment of the system. I'm a bit biased (started my prof. career at the company in the eighties) but I still recommend to have a look here: https://www.heidenhain.com/products/rotary-encoders. They usually guarantee a system accuracy of 1/20th of the line period. Pricewise a bit on the high side but they are worth it.

Great advice. I really appreciate it. Yes, I come from a tool/die making and optics background, so I hear ya about mechanical accuracy. We used air bearing spindles with the optics for similar reasons, and routinely calibrated our measuring equipment. It's possible that an encoder could have error correction programmed into it during a calibration routine, but I'd rather not have the program doing unnecessary tasks when the problem can be corrected mechanically. Thanks again!
 
Thanks. I need to look into how to set priority then. I wasn't aware that I could override the serial com priority set by default and didn't know if all serial com types work the same. Really appreciate it.

One more comment on pulse counting versus period measurement. The T4.x quad timers have a feature that is not exposed in the FreqMeasureMulti library, and that is the ability to define the number of edges over which to measure period. This lets you effectively divide the encoder pulse frequency (and interrupt rate) by up to 255. If you set the value to 200, you will measure the period of 100 encoder cycles, so you'll get position/speed updates at 400-600 Hz, which is probably a higher interrupt rate than you would use for pulse counting, but is not very high for T4.x, and you get very high resolution. You do have to deal with low speeds, which is a lot simpler with pulse counting, but sometimes it's worth the trouble. I have modified the FreqMeasureMulti library to support the multi-period feature, but haven't posted it back to github. I'll try to do that soon.
 
One more comment on pulse counting versus period measurement. The T4.x quad timers have a feature that is not exposed in the FreqMeasureMulti library, and that is the ability to define the number of edges over which to measure period. This lets you effectively divide the encoder pulse frequency (and interrupt rate) by up to 255. If you set the value to 200, you will measure the period of 100 encoder cycles, so you'll get position/speed updates at 400-600 Hz, which is probably a higher interrupt rate than you would use for pulse counting, but is not very high for T4.x, and you get very high resolution. You do have to deal with low speeds, which is a lot simpler with pulse counting, but sometimes it's worth the trouble. I have modified the FreqMeasureMulti library to support the multi-period feature, but haven't posted it back to github. I'll try to do that soon.

Very interesting. Thanks!
 
Back
Top