Any good examples on rotary encoder library "WITH" velocity ? - Page 1
Forum section: electronics / general

author: BravoV
author_rank: Super Contributor
timestamp: September 13, 2013, 07:31:12 am
content: The title should be clear, appreciate if you can point out some links/references on the code library that you think is proven and reliable, or at least good one. Really love to learn how to do it properly and efficiently from the experts/pros.I don't need polling method, only ISR type, and don't worry about the cpu power, it will be used on relatively new arm m4f generation, so in term of raw power, cmiiw this should not be a problem right ?TIAEdit : Polling methods are also welcomed.

author: Psi
author_rank: Super Contributor
timestamp: September 13, 2013, 07:41:52 am
content: Would be pretty simple to write your own.Use one of the hardware timers to generate a slow clock, maybe 100Hz, and use it to get a timestamp on each interrupt then calc velocity and direction.Maybe add some filtering to the velocity.

author: dannyf
author_rank: Super Contributor
timestamp: September 13, 2013, 10:23:55 am
content: Not sure what "With velocity" means.But a typical approach to read an encoder is via a state machine. That can be easily implemented via polling or via two port-change / external interrupts. The code is almost identical in either case.

author: Psi
author_rank: Super Contributor
timestamp: September 13, 2013, 10:55:58 am
content: velocity means how fast the encoder is being rotated.You can then do different things depending on the rotation speed.ie,have slow movement changing a setting by 0.1a medium movement changing it by 1and a fast movement changing it by 10Or use a function to calculate the change amount based on the speed in real time.

author: Rerouter
author_rank: Super Contributor
timestamp: September 13, 2013, 12:40:04 pm
content: The best way to manage interrupts is to keep them short and sweet,my approach to this would be to first capture current time, but only for the first count (do your direction math in the interrupt and make count + or -)increment a volatile count variable, and capture the time after the reading,then in your main loop, it reads out the counts, and the 2 times to normal variables quickly then resets the interrupt count and time, freeing it up to continue countingthis then gives you an average if you receive many on a slow loop, and for a fast loop, you would run a rolling average, to determine velocity,

author: nessatse
author_rank: Regular Contributor
timestamp: September 13, 2013, 05:28:12 pm
content: Here is a snippet that I wrote a while ago. It is specifically written for the common (cheap) rotary encoders that give four counts per detent. The count handles two encoders and you can set independent velocity based increments for each one. There are three velocity thresholds and associated increment values, coarse, medium and fine.I do not use pin change interrupts, but do everything in a 1kHz timer interrupt, it makes it a lot easier in my opinion.PS. This is not a library, I just hacked bits out of my program and added a few comments, so it quite possible that I may have omitted a few pertinent bit, in which case I apologise.PPS. This code is for AVR & gcc, but most of it pretty generic, do it should easily translate to another architectureCode:// encoder 1 & 2 pins#define SW1APB0#define SW1BPB1#define SW2APC2#define SW2BPC3// Timer ticks per second#define TICKTIME1000// Encoder limits and increments// 4 transitions for 1 count#define ENDCODERSCALE 4// Max encoder count#define ENCODERMAX(3000)// These are the value increments seen by the main program for the// three velocity ranges#define ENC1_FINE1#define ENC1_MEDIUM10#define ENC1_COARSE50#define ENC2_FINE10#define ENC2_MEDIUM20#define ENC2_COARSE50// velocity thresholds for encoders// Speed is actually number of ticks between clicks#define SPEED_LOW (TICKTIME/2)#define SPEED_MED (TICKTIME/20)#define SPEED_HI (TICKTIME/50)volatile uint32_t ticks;// Encoder state transition table// The table is indexed by a four bit number representing// the previous and current state of the A&B pins of the encoder// The value is what we should add to the current encoder count, either +1// for forward motion, -1 for reverse or 0 for an invalid state transitionuint8_t enc_states PROGMEM = {0, //00001, //0001-1,//00100, //0011-1,//01000, //01010, //01101, //01111, //10000, //10010, //1010-1,//10110, //1100-1,//11011, //11100 //1111};// The state structure for each encoder. This allows for// independent velocity dependent increments. The only bit the// main routine needs to look at is 'value' which will vary between// zero and ENCODERMAXtypedef struct {uint8_t state;volatile uint16_t value;uint16_t inc;uint16_t inc_fine;uint16_t inc_med;uint16_t inc_coarse;int8_t count;uint32_t speed;uint32_t last_tick;} encoder_t;// Array for the two encoder state structuresencoder_t encoders;// bit 0 and 1 indicates to the main loop that the encoders have changed// needs to be reset by the main loop after processingvolatile uint8_t encoder_flags;// Checks if velocity thresholds crossed and adjusts the increment valuestatic void check_enc_speed(uint8_t enc_no){encoders.speed = ticks - encoders.last_tick;encoders.last_tick = ticks;if (encoders.speed < SPEED_MED) {if (encoders.speed < SPEED_HI) {encoders.inc = encoders.inc_coarse;} else {encoders.inc = encoders.inc_med;}} else {encoders.inc = encoders.inc_fine;}}// Update the encoder valuestatic void update_encoder(uint8_t enc_no,uint8_t buttons){encoders.count += (int8_t)pgm_read_byte(&(enc_states));if (encoders.count > 3) {check_enc_speed(enc_no);if (encoders.value < (ENCODERMAX-encoders.inc)) {encoders.value+=encoders.inc;} else {encoders.value = ENCODERMAX;}encoder_flags |= 0x01 << enc_no;encoders.count = 0;} else if (encoders.count < -3) {check_enc_speed(enc_no);if (encoders.value >= encoders.inc) {encoders.value-=encoders.inc;} else {encoders.value = 0;}encoder_flags |= 0x01 << enc_no;encoders.count = 0;}encoders.state = (buttons & 0b0011) << 2;}// 1 kHz timer interrupt. Reads encoders and adjusts velocity and values.ISR (TIMER0_COMPA_vect){uint8_t state;ticks++;// check encoder 0state = encoders.state | (PINB & (_BV(SW1A)|_BV(SW1B)));update_encoder(0,state);// check encoder 1// convoluted logic, but encoder 2 is on portc 2&3state = encoders.state | ((PINC & (_BV(SW2A)|_BV(SW2B))) >> 2);update_encoder(1,state);}

author: dannyf
author_rank: Super Contributor
timestamp: September 13, 2013, 08:01:21 pm
content: This is what I would do:Code:#define ENC_PORT PINB //encoder on portb#define ENC_A (1<<0) //ch a on pin0#define ENC_B (1<<2) //ch b on pin2//read an encoder//+/- indicates rotational speedsigned char enc_read(void) {static unsigned char enc_prev=0; //previous values for enc. lowest 4 bits effective. A(-1) B(-1) A(0) B(0)//static signed char enc_count=0; //enc count, if absolute value is requiredunsigned char tmp = ENC_PORT; //read the portenc_prev = enc_prev << 2; //shift enc_prev to make space for new readingsenc_prev |= ((tmp & ENC_A)?0x02:0) | ((tmp & ENC_B)?0x01:0); //update enc_prevreturn enc_stats[enc_prev & 0x0f]; //if relative count is need//return enc_count+=enc_stats[enc_prev & 0x0f]; //if absolute counts are needed}You can run it through isrs - with minor changes but the idea is the same.

author: BravoV
author_rank: Super Contributor
timestamp: September 14, 2013, 08:31:17 am
content: Appreciate the replies and the algorithms.Also to nessatse & dannyf, many thanks for the examples, now its time for this noob to digest it, this might take a while.

author: andersm
author_rank: Super Contributor
timestamp: September 14, 2013, 10:34:01 am
content: As a quick sidenote, as your hardware seems to be pretty modern (Cortex-M4F), have you checked to see if the hardware supports reading encoders directly? It's usually a special mode of the timer/counter hardware.

author: nctnico
author_rank: Super Contributor
timestamp: September 14, 2013, 11:45:29 am
content: Interrupts won't work very well because the encoders can bounce pretty bad. Its better to poll using a timer interrupt unless the controller has hardware support for an encoder.

author: Unknown
author_rank: Unknown
timestamp: September 14, 2013, 11:55:20 am
content: I wouldn't use pin change interrupts unless it was with an optical encoder which doesn't bounce.

author: mrflibble
author_rank: Super Contributor
timestamp: September 14, 2013, 12:07:01 pm
content: Quote from: nctnico on September 14, 2013, 11:45:29 amOn MSP430 I do exactly this, since the average encoder has a nice bit of bounce in it. Have a shift register that samples the input and then debounce it. On STM32 I use the hardware support for quadrature encoders on the timer modules.

author: BravoV
author_rank: Super Contributor
timestamp: September 16, 2013, 03:34:23 am
content: Quote from: andersm on September 14, 2013, 10:34:01 amYes, I'm aware of that, latest TI Tiva cortex m4f which I have has two dedicated hardware based quadrature encoder interface (QEI) module in it, also I have the older Stellaris which they stripped down version which doesn't have that.Its just I'm eager to learn the code/flow/algorithm from experienced fellows.Quote from: nctnico on September 14, 2013, 11:45:29 amQuote from: alm on September 14, 2013, 11:55:20 amThanks for the advice. Actually I learned my hard lesson well when naively I tried with cheap mechanical encoder that bounces like hell, that made me have to add dedicated external classic 555 ics to debounce it.Currently I'm planning to use optical version.Quote from: mrflibble on September 14, 2013, 12:07:01 pmAny chance I can take a peek at your code too ?

author: dannyf
author_rank: Super Contributor
timestamp: September 16, 2013, 10:27:24 am
content: QuoteNothing a little rc filter couldn't cure. Look up its datasheet.

author: mrflibble
author_rank: Super Contributor
timestamp: September 16, 2013, 11:38:34 am
content: Quote from: BravoV on September 16, 2013, 03:34:23 amFor STM32 I use something like this:quadrature_encoder.cppCode:#include "quadrature_encoder.hpp"#include "ch.h"#include "hal.h"QuadratureEncoder::QuadratureEncoder(void){}//// http://stackoverflow.com/questions/15203069/stm32-rotary-encoder-config-on-tim4// http://forum.chibios.org/phpbb/viewtopic.php?f=2&t=247&start=40// http://tronixstuff.wordpress.com/2010/06/26/getting-started-with-arduino-chapter-eleven/// https://www.sparkfun.com/products/9117//// PB.4 = TIM3 CH1// PB.5 = TIM3 CH2//void QuadratureEncoder::Init(void){// GPIO_PinAFConfig (GPIOB, GPIO_PinSource4, GPIO_AF_TIM3); // PB.4 = TIM3 CH1// GPIO_PinAFConfig (GPIOB, GPIO_PinSource5, GPIO_AF_TIM3); // PB.5 = TIM3 CH2palSetPadMode(GPIOB, 4, PAL_MODE_ALTERNATE(2) | PAL_STM32_PUDR_PULLUP | PAL_STM32_OSPEED_HIGHEST); /* AF2 = TIM3 */palSetPadMode(GPIOB, 5, PAL_MODE_ALTERNATE(2) | PAL_STM32_PUDR_PULLUP | PAL_STM32_OSPEED_HIGHEST); /* AF2 = TIM3 */// RCC_APB1PeriphClockCmd (RCC_APB1Periph_TIM3, ENABLE);RCC->APB1ENR |= (1 << 1); // TIM3//// RM0090, page 408// 011: Encoder mode 3 - Counter counts up/down on both TI1FP1 and TI2FP2 edges depending on the level of the other input.//// http://stackoverflow.com/questions/15203069/stm32-rotary-encoder-config-on-tim4// I have used an STM32F407 to read encoder counts from 3 optical encoders. I am using ChibiOS RTOS so the timer struct is// slightly different from the ST Peripheral library timer struct, but the information is basically the same. Here is how I// configure the registers of the actual timers://stm32_tim_t *timer = STM32_TIM3;timer->SMCR = 3; // Encoder mode 3timer->CCER = 0; // rising edge polaritytimer->ARR = 0xFFFF; // count from 0-ARR or ARR-0timer->CCMR1 = 0xC1C1; // f_DTS/16, N=8, IC1->TI1, IC2->TI2timer->CNT = 0; // Initialize countertimer->EGR = 1; // Generate an update eventcdtimer->CR1 = 1; // Enable the counterprev_encoder_count = GetCount();}int16_t QuadratureEncoder::GetCount(void){return (int16_t) TIM3->CNT;}int16_t QuadratureEncoder::GetIncrement(void){int16_t encoder_count = GetCount();int16_t increment = encoder_count - prev_encoder_count;prev_encoder_count = encoder_count;return increment;}quadrature_encoder.hppCode:#ifndef QUADRATURE_ENCODER_HPP_#define QUADRATURE_ENCODER_HPP_#include "ch.h"class QuadratureEncoder{private:int16_t prev_encoder_count;int16_t GetCount(void);public:QuadratureEncoder(void);void Init(void);int16_t GetIncrement(void);// void Run(void);// void SetCallback(void (*cb)(void));};Then in main you have:Code:static QuadratureEncoder myQuadratureEncoder;int main(void) {// ..myQuadratureEncoder.Init();And then in your favorite timer routine you callmyQuadratureEncoder.GetIncrement().

author: BravoV
author_rank: Super Contributor
timestamp: September 17, 2013, 04:42:57 am
content: mrflibble, thank you !

author: nctnico
author_rank: Super Contributor
timestamp: September 17, 2013, 10:01:04 am
content: That is pretty complicated. You just need to look for edges on one line 'A' and then check the state of line 'B'. Executing two 'if' statements is enough.

author: BravoV
author_rank: Super Contributor
timestamp: September 17, 2013, 11:48:57 am
content: TerminalJack505, wow, thank you very much for the examples, really love the state diagram too, really handy for a noob like me !Quote from: nctnico on September 17, 2013, 10:01:04 amQuote from: TerminalJack505 on September 17, 2013, 10:08:15 amOk, when it comes to these, just sitting quietly at the corner cause I'm too noob to comment/argue/debate which is better, just observe and learn should this turn into "quality" discussion on this routine.Nctnico, if you have any examples from your past experiences/projects related to this, really appreciate if you could share a snippet on that encoder part, no need to tidy it up, just copy & paste on the core section, I will learn to read & digest it from there.

author: dannyf
author_rank: Super Contributor
timestamp: September 17, 2013, 12:46:18 pm
content: QuoteYou don't really need that - the 4-bit array posted earlier is a nice implementation of that.

author: TerminalJack505
author_rank: Super Contributor
timestamp: September 17, 2013, 12:57:43 pm
content: Offending messages removed.

author: airiclenz
author_rank: Contributor
timestamp: September 17, 2013, 01:06:40 pm
content: Here is a pretty good example for the Arduino. It is well explained and can easily be ported to other systems:http://www.buxtronix.net/2011/10/rotary-encoders-done-properly.htmlGood luck,Airic

author: mrflibble
author_rank: Super Contributor
timestamp: September 17, 2013, 01:46:10 pm
content: Quote from: TerminalJack505 on September 17, 2013, 12:57:43 pmToo bad you removed the FSM as well, because now the responses to that make no sense. Besides, sometimes looking at it from a state machine angle can be helpful/instructive/fun.

author: nctnico
author_rank: Super Contributor
timestamp: September 17, 2013, 01:56:59 pm
content: Quote from: BravoV on September 17, 2013, 11:48:57 amThis is a cut down snippet which is called from a timer interrupt. In order to be get the data from the IRQ process space into the user process space without disabling the interrupts / missing steps I use two accumulators to count the cw and ccw steps. In the user process I simply calculate the difference between the current accumulator values and the previous values.Velocity can be added by adding a counter which counts the number of calls between two edges. The variable a can be increased when the counter reaches smaller values.static uint32_t rotation_accu_cw, rotation_accu_ccw;void rotary_handler(){static uint32_t prev_rotary_a;uint32_t a=1;if (prev_rotary_a != GET_ROTARY_A()){//edgeprev_rotary_a = GET_ROTARY_A();if (prev_rotary_a==0){//falling edgeif (GET_ROTARY_B()!=0){rotation_accu_cw+=a;}else{rotation_accu_ccw+=a;}}else{//rising edgeif (GET_ROTARY_B()==0){rotation_accu_cw+=a;}else{rotation_accu_ccw+=a;}}}}

author: TerminalJack505
author_rank: Super Contributor
timestamp: September 17, 2013, 02:15:28 pm
content: Quote from: mrflibble on September 17, 2013, 01:46:10 pmSorry. That's the only time I've ever deleted any of my posts. I know how it messes up the preceding discussion.I thought I could add something to the discussion but apparently anything more complicated than a "Hello, World!" solution is too complicated.

author: Tepe
author_rank: Frequent Contributor
timestamp: September 17, 2013, 02:38:39 pm
content: Quote from: TerminalJack505 on September 17, 2013, 02:15:28 pmAre you willing to insert them in the post again?

author: dannyf
author_rank: Super Contributor
timestamp: September 17, 2013, 02:52:59 pm
content: QuoteThat's effectively polling and consequentially not terribly efficient and may miss pulses if the encoder goes fast (aka on a motor).The state machine approach + interrupts is really the only way to go, short of hardware encoder support.

author: TerminalJack505
author_rank: Super Contributor
timestamp: September 17, 2013, 02:57:56 pm
content: Quote from: Tepe on September 17, 2013, 02:38:39 pmAlright. Here are the attachments from my original post.Note that they don't even address the question of "velocity." I was just pointing out a way to implement bounce tolerance.

author: nctnico
author_rank: Super Contributor
timestamp: September 17, 2013, 02:59:54 pm
content: Quote from: dannyf on September 17, 2013, 02:52:59 pmIts not inefficient because most of the code is only executed when an edge occurs. The problem is that a mechanical encoder will bounce. Before I came up with my piece of code I tried using interrupts and a hardware counter only to find out I needed proper debouncing in software. You may call it polling but in fact the real code (not the cut down version) also does filtering by skipping edges which are to close to eachother. Whether or not you miss pulses depends on how often its called (the sampling frequency).

author: TerminalJack505
author_rank: Super Contributor
timestamp: September 17, 2013, 03:32:03 pm
content: Quote from: nctnico on September 17, 2013, 02:59:54 pmWow. And you called my code too complicated.I do advise that if you use code similar to mine that you use an RC filter on the two encoder signals but there's no other "software filtering" required. You don't really want the interrupt running unnecessarily and the RC filter will help in this aspect. (Note that my code can be easily adapted to be used in a polling-type of scheme.)With the finite state machine, if the switches bounce then the state machine just ping-pongs back and forth between two adjacent states. It only emits a "clockwise" or "counterclockwise" action when it has reached the point where all 4 required states have been visited. Once it emits either the "clockwise" or "counterclockwise" action then the state machine returns to the idle state. Note how the state machine doesn't bounce between "idle" and either CW10 or CCW01 so there's no problem with false actions.

author: nessatse
author_rank: Regular Contributor
timestamp: September 17, 2013, 04:26:32 pm
content: Nice diagramsTerminalJack505.Using a FSM is the only reliable way to read these encoders, especially the cheap mechanical ones that will bounce in weird and wonderful ways.Quote from: nctnico on September 17, 2013, 10:01:04 amThis is a naive approach, it will not only count on invalid transitions, but your example also does not count all the valid transitions.I do however agree that polling during a timer interrupt is preferable to edge interrupts, especially when dealing with multiple encoders. Running your timer tick at a decent frequency, say 1000Hz, will minimise the chance of missing a step, and even if you do the FSM will recover elegantly.

author: nctnico
author_rank: Super Contributor
timestamp: September 17, 2013, 04:30:36 pm
content: So your statemachine approach requires a full period (two events) to determine the direction? The problem with that is that most encoders for manual input have audible/tactile feedback every half period so you need to respond to a half period otherwise the user has to advance the knob two 'clicks' to advance by 1 which is counter intuitive.Whether you do the filtering in software or hardware depends on where you need to save something. My method also works when called from a GPIO interrupt or a timer capture interrupt. Either way the golden rule is to never ever create an interrupt from an I/O pin which has 1) no proper filtering and 2) no schmitt trigger otherwise a simple fault like a loose wire or a worn contact can make your software come to a grinding halt because it keeps servicing interrupts. All in all using GPIO inferred interrupts just costs extra hardware.@nessatse: the code I posted is just to show how to use the edges on the 'A' line in order to determine the steps. Depending on the actual hardware filtering may be required. In the real software I keep track of the number of timer ticks between the edges for de-bouncing and determining velocity. If the number of ticks is too small the edge is ignored.

author: Rufus
author_rank: Super Contributor
timestamp: September 17, 2013, 04:49:43 pm
content: Quote from: TerminalJack505 on September 17, 2013, 03:32:03 pmHardware filters increase cost and board space, a few extra instructions usually costs nothing.

author: nessatse
author_rank: Regular Contributor
timestamp: September 17, 2013, 05:10:52 pm
content: Quote from: nctnico on September 17, 2013, 04:30:36 pmI admit I haven't worked through TerminalJack505's code, but for the code I posted earlier, (which used the same FSM logic, although in a slightly different way) this is definitely not the case. The fsm in fact acts just like a type of software filter, preventing counters from changing during illegal state transitions that you would typically get with noisy/bouncy switches. It will respond to every valid transition on either the A or B legs, i.e. "half" periods.The encoders I have used all count 4 counts, i.e. a full cycle 00-01-11-10, between detents, I have been running the code I posted in a real environment, without any external hardware or further software filtering and it has been performing flawlessly.

author: TerminalJack505
author_rank: Super Contributor
timestamp: September 17, 2013, 05:15:05 pm
content: Quote from: nctnico on September 17, 2013, 04:30:36 pmThat particular code was written for the cheap mechanical quadrature encoders that produce one pulse per detent. One cycle for these encoders (where each detent is located) is shown on the state diagram by a dotted line. The vast majority of the inexpensive mechanical encoders are of this variety.

author: dannyf
author_rank: Super Contributor
timestamp: September 17, 2013, 05:22:18 pm
content: "that produce one pulse per detent."That's fairly rare: most produce 4 pulses, cheap or not.As to the rc filter: it doesn't pay to solve a hardware problem with software. Though I have to agree with the other poster that the state machine approach is remarkably resistent to invalid pulses.

author: TerminalJack505
author_rank: Super Contributor
timestamp: September 17, 2013, 05:40:13 pm
content: Quote from: dannyf on September 17, 2013, 05:22:18 pmThat's not my experience. Last time I went shopping for mechanical quadrature rotary encoders, the one pulse per detent type was pretty much all I could find. Unfortunately, Digi-key doesn't have this as a parameter so you can't search by it but I remember going through page, after page of encoders looking for the half- and quarter-pulse per detent type and finding very few.Keep in mind that I'm talking about mechanical encoders. Optical encoders are another story.

author: David_AVD
author_rank: Super Contributor
timestamp: September 17, 2013, 09:20:38 pm
content: I'm implementing a rotary encoder in a current project. Both the original one and a replacement type are mechanical and 4 pulses per click. Both were bought from Digikey and were only a few dollars in one off quantity.

author: andersm
author_rank: Super Contributor
timestamp: September 17, 2013, 09:35:07 pm
content: It may be a question of terminology, whether you count all four edges or just the full cycles.

author: Rufus
author_rank: Super Contributor
timestamp: September 17, 2013, 11:15:59 pm
content: Still no response with any velocity sensitivity.I have a spare time project using a Bourns PEC11 series mechanical encoder. It is working with code similar to the examples already posted here. It might benefit from some velocity sensitivity as it is used to cycle through menu options and to adjust numbers.I will probably do some work on it eventually but I am pretty sure it will take some experimentation to get it to 'feel' right. Overshooting on number adjustment would be undesirable so the acceleration will have to 'feel' predictable.

author: mrflibble
author_rank: Super Contributor
timestamp: September 17, 2013, 11:51:12 pm
content: Heh, yeah, no velocity. If BravoV can write up a decent spec for what's needed, maybe someone will magically code it up.

author: BravoV
author_rank: Super Contributor
timestamp: September 18, 2013, 04:24:04 am
content: Woah .. lots of updates and discussions, keep it going, ideas exchange and also different algorithms are good thing imo, especially to capture what other people are thinking on countering this matter.Please understand, I'm still learning, and while I did write my own code (even though it sucks big time), it works though not optimal, now when it comes to integrate with "velocity" feature, everything falling apart and turned into chaos, don't ask, I'm too embarrassed to post my code, just forget it. .. LOL ...Also, I repeat, I may not be active in discussing this, please trust me, I'm observing this thread intensively while learning how each of you tackle it from each perspective, thats matter and valuable to me in this learning process.Quote from: mrflibble on September 17, 2013, 11:51:12 pmThere are few previous posters already mentioned the idea like Psi did on 3 type of speeds, or nessatse's code implementing three velocity thresholds like coarse, medium and fine.To be honest, actually I have my own very crude idea "pirated"from TI tiva cortex m4f's API that handles it's hardware based QEI (Quadrature Encoder Interface). But I was hesitated to post this at the beginning since I don't want to have our hands tie up only to TI's idea/solution, and of course "ideally" the code should be easily adaptable to other platforms if possible.But since you mentioned it, here a snippet on TI's QEI's API brief description, captured screen for your viewing pleasure without needing to download the PDF with the highlighted regions at the "velocity" feature :Above quoted from chapter 19 at page 227, download link ->TivaWare™ Peripheral Driver Library for C Series User's Guide (PDF approx 1.8 MB)Again, this is just my noob idea, prolly I don't know heck what I'm talking about, but for me maybe that is a good start on the idea at handling/interfacing with the "velocity" feature. And its the way how you experts/experienced people tackle it that I love to learn from.Feel free to drop this TI's related thingy should you think this is useless, way too complicated or not feasible for easily adaptable to other different mcu architectures.

author: BravoV
author_rank: Super Contributor
timestamp: September 18, 2013, 04:44:54 am
content: On hardware level, this is how TI QEI module handles bounces, attached the print screen from the datasheet on the QEI hardware (highlighted).Again, just ignore this post if you think this is useless.

author: Rufus
author_rank: Super Contributor
timestamp: September 18, 2013, 05:04:38 am
content: Quote from: BravoV on September 18, 2013, 04:24:04 amWell if you wanted to do some experimentation your results might save me some effort, however, I am currently interested in cheap mechanical encoders with one pulse (or 4 edges) per dedent. So is that what you are working with?

author: BravoV
author_rank: Super Contributor
timestamp: September 18, 2013, 05:07:10 am
content: Quote from: Rufus on September 18, 2013, 05:04:38 amYep, dirt cheap mechanical with 4 edges, not even Bourns brand.

author: Rufus
author_rank: Super Contributor
timestamp: September 18, 2013, 05:45:21 am
content: Quote from: BravoV on September 18, 2013, 05:07:10 amMy suggestion then would be to implement a polled scheme from a timer interrupt at about 1kHz and use the previous + current state to index into a 16 element array of count deltas like the examples in this thread.Bounce on either input causes jitter on the LSB of the count, but, detent to detent the count changes by 4 so you should be ignoring the 2 LSBs of the count anyway.I think there is an issue that I have noticed (without actually investigating) where turning the encoder faster than it can track causes the count to get out of sync with the encoder dedents so I would suggest using a time out on encoder activity to set the count 2 LSBs to zero (on the assumption the encoder will be in a dedent).For velocity a similarly triggered timeout could be used and when you see a whole pulse if the timeout is still running you would adjust the count by more than 1. You could have more than one timeout to detect more than one velocity level. That said I suspect it could 'feel' better if you don't accelerate the second pulse but delay it till the 3rd or 4th which gets more complicated.I tend to use a lot of what I call software monostables.if(mono)mono--running at the 1kHz or whatever rate. You can trigger and re-trigger by setting mono to a value and mono being zero indicates timed out.

author: nessatse
author_rank: Regular Contributor
timestamp: September 18, 2013, 05:46:09 am
content: Quote from: Rufus on September 17, 2013, 11:15:59 pmUhm, did you see my post? (https://www.eevblog.com/forum/microcontrollers/any-good-examples-on-isr-based-rotary-encoder-library-'with'-velocity/msg291163/#msg291163) It has full velocity implementation

author: TerminalJack505
author_rank: Super Contributor
timestamp: September 18, 2013, 11:28:33 am
content: Quote from: andersm on September 17, 2013, 09:35:07 pmYes, the manufacturers seem to give different names to the same thing so I think we're probably talking about the same thing.1 Pulse per Detent == 4 Phase per Click == 4 edges per DetentAll of them seem to agree on the term "Pulses Per Revolution." So if the number of pulses per revolution is the same as the number of detents per revolution then that's also the same type.

author: TerminalJack505
author_rank: Super Contributor
timestamp: September 19, 2013, 06:25:49 am
content: Here's something I've been working on. It's a bounce and skip tolerant rotary encoder class with support for velocity. It's still half-baked. The port/pin stuff is still hard coded and I haven't even run it yet. I just bolted-on the skip handling and velocity stuff to my earlier code.The idea behind the velocity support is to sample the number of actions (full clockwise or counter-clockwise clicks) at a period of approximately 100ms to 300ms and report this as the velocity. This isn't the instantaneous velocity, obviously, and introduces a lag but hopefully that won't matter so long as the sampling periods are small enough. If it works then there won't be as much data to keep track of. I'm targeting the class toward 8-bit MCUs with limited resources--particularly SRAM.The velocity, as reported to the user (the class user) will be the number of actions during the last complete time slice. So, if the user (the end user) is turning a 24 pulse per rotation encoder counter-clockwise at 40 detents per second (pretty fast) and the sample period is 200ms, then velocity would be reported as -8. How this velocity is interpreted is up to class user.It will probably be a trick finding the best sample period size so that lag is minimized while resolution is maximized. This method will also benefit from using encoders with a higher number of pulses per rotation.

author: BravoV
author_rank: Super Contributor
timestamp: September 19, 2013, 07:09:22 am
content: TerminalJack505, much appreciated !Since I'm still novice, when it comes to reading other people's code at interested topic, I always expect and really love the excitement when .. "How come I didn't think of that !" moment.Going to print it on paper and bring it to bed later on.Again, thanks Jack.

author: TerminalJack505
author_rank: Super Contributor
timestamp: September 19, 2013, 07:22:50 am
content: No problem. I'll let you know how testing works out. I'm sure there will be some changes. Hopefully minor.Note that the skip tolerance code isn't the same as the skip tolerance state machine diagram I posted earlier. The skip logic takes the current velocity into account so that when a skip occurs it gives a bias to the current rotation direction.

author: dannyf
author_rank: Super Contributor
timestamp: September 19, 2013, 11:12:38 am
content: QuoteThat's futile for a generic routine, as you will never know for sure how it is to be used.QuoteHigh PPR can cause serious problems for complicated routines running from an isr.Polling is just inefficient and ineffective for those situations.

author: TerminalJack505
author_rank: Super Contributor
timestamp: September 19, 2013, 01:02:24 pm
content: Quote from: dannyf on September 19, 2013, 11:12:38 amYes, writing generic, reusable code for MCUs is usually a problem and not really worth the trouble. "Reuse" on MCUs typically means cut-copy-paste-modify, especially on 8-bit MCUs. I'm sure even if I were to reuse the code I'd likely wind-up making changes. I'm just doing research, really.I've attached the latest versions with a couple of fixes. I'll likely combine the two lookup tables into one. This will make the table bigger but the code smaller. Hopefully with a net gain of a couple dozen bytes.

author: TerminalJack505
author_rank: Super Contributor
timestamp: September 19, 2013, 08:15:26 pm
content: For anyone interested here's a state diagram of what I plan to implement for skip tolerance. I've included the latest code as well but the state machine in the code hasn't been updated yet.Say what you will about state machines but there's no way I'd ever want to try and implement that sort of behavior without one.I've yet to actually run it and I may not get a chance for the next day or so.

author: TerminalJack505
author_rank: Super Contributor
timestamp: September 21, 2013, 07:49:26 am
content: I finally got this thing programmed into a MCU (an ATtiny2313) and got it working. It works pretty much like I envisioned it. The bounce and skip tolerance works fine.The velocity works but I'm not sure if this particular implementation would be what a person would want for a real-world application. It's hard to say since all I really did--so far as testing--is print out the value of the velocity.I'll have to come up with some kind of useability test for the velocity before I know whether or not the implementation I chose is practical.The source code from my test project is attached for those that are interested.

author: BravoV
author_rank: Super Contributor
timestamp: September 21, 2013, 08:01:38 am
content: Thanks for keep updating this thread !

author: Teemo
author_rank: Regular Contributor
timestamp: September 22, 2013, 10:22:46 am
content: Thanks everybody for this exellent thread!I've also been thinking to use rotary encoders with velocity control in one of my projects.As a beeginning it seems good idea to go with those starting points:-Use edge detect interrupts to read encoder, (paying attention to debounce).-at every edge detect start countdown timer-at every next edge interrupt add countdown timer remaining value+1 to the count. And restart countdown timer.(this way turning faster results in more counting), timer running speed determines how powerful the velocity effect is.Seems simple

author: mrflibble
author_rank: Super Contributor
timestamp: September 22, 2013, 04:26:00 pm
content: Quote from: Teemo on September 22, 2013, 10:22:46 amOr better yet, sample the input pins from a timer routine. More predictable results with less chance of a runaway in case of hardware failures.As for velocity, just grab (current_count - previous_count) for the simplest case. And if you're like me and want a bit of averaging, you keep a circular buffer with the last N (for example 4) counter values, and then use that to calc average velocity.velocity = (count - count) / 4sort of thing. Preferably N in powers of 2, such that the divide is optimized to an arithmetic shift right.

author: dannyf
author_rank: Super Contributor
timestamp: September 22, 2013, 04:49:23 pm
content: QuoteThe 4-bit state machine approach posted earlier is incredibly reliable against bounces and it recovers quickly from bounces too.

author: mrflibble
author_rank: Super Contributor
timestamp: September 22, 2013, 05:13:31 pm
content: Agreed. The state machine approach is definitely a good one if you want something rugged.

author: jeroen74
author_rank: Frequent Contributor
timestamp: September 22, 2013, 05:41:00 pm
content: I wrote this long ago for a full quadrature cycle per detent encoder; state machine using interrupts on a mega128, with velocity sensing. Not the most efficient or beautiful code, I know...Code:#include <iom128v.h>#include "main.h"static byte encoder_a,encoder_b;static byte encoder_flag,state,timer;void handle_encoder(void);// First output from encoder tied to external interrupt 4#pragma interrupt_handler int4_isr:6void int4_isr(void){EICRB^=(1<<ISC40);encoder_a^=1;handle_encoder();}// Second output from encoder tied to external interrupt 5#pragma interrupt_handler int5_isr:7void int5_isr(void){EICRB^=(1<<ISC50); // Toggle edge senseencoder_b^=1;handle_encoder();}// Set external interrupt 4 and 5 to falling edge, make sure PE4 and PE5 are set to inputvoid init_encoder(void){DDRE|=(1<<4)|(1<<5);EICRB&=(1<<ISC40)|(1<<ISC50);EICRB|=(1<<ISC41)|(1<<ISC51);encoder_flag=0;state=0;}void handle_encoder(void){if (state==0){if (encoder_a) state=1;if (encoder_b) state=4;}else if (state==1){if (encoder_b) state=2;else if (!encoder_a) state=0;}else if (state==2){if (!encoder_a) state=3;}else if (state==3){if (!encoder_b){encoder_flag=2;state=0;}}else if (state==4){if (encoder_a) state=5;else if (!encoder_b) state=0;}else if (state==5){if (!encoder_b) state=6;}else if (state==6){if (!encoder_a){encoder_flag=1;state=0;}}else state=0;}byte get_encoder(void){byte f;if (encoder_flag){if (timer<7) encoder_flag|=32;timer=0;}f=encoder_flag;encoder_flag=0;return(f);}// Call this every 10ms//byte encoder_timer_handler(void){if (timer<200) timer+=1;}I guess it implements the same state machine as mentioned aboveThis particular encoder generated a lot of bounce if you held the encoder against the detent like when you carefully adjust a value. It does not continually switch between up/down in that case, even if on average that stream of up/down events is no movement at all.

author: Rufus
author_rank: Super Contributor
timestamp: September 22, 2013, 06:59:50 pm
content: Quote from: Teemo on September 22, 2013, 10:22:46 amDebouncing requires you to decide when an input has stopped changing state. Kinda awkward to do that in an interrupt handler which only runs when an input changes state.

author: TerminalJack505
author_rank: Super Contributor
timestamp: September 22, 2013, 07:40:00 pm
content: I think what I've learned from my research so far, regarding velocity, is that just having the capability to determine velocity is only half the problem. Theeasyhalf, even. Using the velocity in a way that actually makes for a positive user experience is the real trick.For useability testing I emulated an interface where the rotary encoder was used to set a voltage between the values 1.250V and 30.000V. The velocity was used to switch between adjusting the value in 1mV, 10mV and 100mV increments. I basically had three categories of velocities. The faster velocities controlling the larger increments.It turns out this isn't really sophisticated enough. If I were a user I'd despise this interface. Some fine-tuning might help but I'm pretty sure it is just a fundamentally flawed way to go about it.One of the biggest problems is that you have to spin the knob too fast to make large increments. Spinning the knob fast isn't a problem. Spinning it fast and keeping up with the changes is the problem. I think what the user would really want is to spin the knob fast enough to get it into the large increment mode but then be able to use a slower velocity. Some kind of hysteresis, in other words.I'm not saying this can't be done. I just saying it's a little more complicated todo right.

author: Teemo
author_rank: Regular Contributor
timestamp: September 22, 2013, 07:59:25 pm
content: Quote from: TerminalJack505 on September 22, 2013, 07:40:00 pmI'd suggest that velocity control should be more smooth, simple algorithm based, not in just three 10x steps. It should add counts directly after every detent.Isn't the gray code based encoder reading same accurate and bounce/skip proof, as full state machine as here above? (I read the example with the gray code here:http://www.circuitsathome.com/mcu/reading-rotary-encoder-on-arduino)

author: TerminalJack505
author_rank: Super Contributor
timestamp: September 22, 2013, 08:51:32 pm
content: My approach (with the large step sizes) was trying to deal with the large span of counts the user has to cover--nearly 30,000. I don't want it to take too long to go from one end of the range to the other.Something has to change, though, because it definitely doesn't 'feel' right. I haven't implemented the idea of hysteresis yet. (Entering and exiting the three velocity categories at different velocities.) That might help.

author: BravoV
author_rank: Super Contributor
timestamp: September 23, 2013, 01:12:44 am
content: Quote from: TerminalJack505 on September 22, 2013, 07:40:00 pmThats what how I feel too when I tried using categories on the velocities, not sure how to express it, it just feels "jagged".Quote from: TerminalJack505 on September 22, 2013, 07:40:00 pmThis, is one of the reason the code turned my world up side down, ended up I dumped it and simply give up.Not sure if my Google Fu is lame, its just I found this topic with the velocity feature is quite rare on the net, actually at 1st I was expecting and assuming this topic should be easy to find on the net, and some code experts already written it nicely (in term of performance + tight code + elegance solution), but that is not the case.Quote from: Teemo on September 22, 2013, 07:59:25 pm, yep, agree on the smooth thingy or so called "analog" feel if this is the right wording.Thanks for keeping this thread alive.

author: Crazy Ape
author_rank: Regular Contributor
timestamp: September 23, 2013, 02:50:44 am
content: Variable velocity user input in general is subject to the same problem, and therefore, the algorithm that provides a solution should work in all instances.Xorg recognized and even mentioned the problem, and seem to have come up with a good solution.My guess is that the algorithm you seek is just sitting there waiting for you find it in the Xorg source code.Quote from Xorg site (link below):Quotehttp://www.x.org/wiki/Development/Documentation/PointerAcceleration/The original ANNOUNCE is here, it'll show you where to look in the code.http://old.nabble.com/-ANNOUNCE--predictable-pointer-acceleration-td18980476.htmlCopies of above in case it goes away (Warning at top of page suggests it will soon die)http://lists.freedesktop.org/archives/xorg/2008-August/037734.htmlhttp://web.archiveorange.com/archive/v/Ky751HDOYzGLgQOYcYUB

author: Teemo
author_rank: Regular Contributor
timestamp: September 23, 2013, 08:07:21 am
content: Quote from: TerminalJack505 on September 22, 2013, 07:40:00 pmMaybe to use coeficents: 1;2;4;8;16;32;64;128;..... basically powers of two instead of ten. Maybe this does the trick?

author: dannyf
author_rank: Super Contributor
timestamp: September 23, 2013, 11:32:06 pm
content: Some thoughts on the 4-bit state machine approach: it will take about 15 us (assuming 1MIPS mcu) for interrupt latency and maybe 20 us for the actual processing. So each edge of channel A or B on the encoder will consume 35 us.That means the max number of edges to be processed in a second is 1000000us / 35us = 28,000 edges.At 4 edges per detent, that means 7,000 detents per second. at 16 detents or 32 detents per revolution (typically for a mechanism rotary encoder), that translates into 220 - 450 revolutions per second, far exceeding the capabilities of a mechanical encoder.For a 500 lines (=edges) per revolution optical encoder (typical for a precision motor control application), the maximum revolution is 28000/500=~60 revolutions per second, or 3500 revolutions per minute.That's not bad for a 1MIPS mcu,

author: dannyf
author_rank: Super Contributor
timestamp: September 23, 2013, 11:55:16 pm
content: Some rough numbers:ISR execution (from pulse coming in to counter being updated): 41us (including 1 statement to output the encoder counter on a port),Code size: 31 bytes,C-statement: 1-2 statements + 1 statement to update the counter.Using an old PICC compiler, no optimization (default optimization).Bounce resistance: 100%,

author: Christe4nM
author_rank: Unknown
timestamp: September 24, 2013, 09:44:27 am
content: This topic is a great read! Although I cannot submit any useful code, I did some thinking. Here are some thoughts.One thing that keeps coming back to me is that the velocity control will probably need an exponential curve (rotation speed vs. increments). This is more based on say "professional intuition" than fact. Though there are so many things in nature that are modeled as first or second order exponential curves that it seems highly probable. It is probably explained in that you want precision as you turn slowly, but want speed as you turn quickly. As mentioned before this should feel as a smooth transition.Another thought was this: does a user really want/like velocity control? If a user wants it, what do we need to do to make it feel right?I recall working with the BK precision electronic load and being not used to the velocity control I turned the rotary knob big time to increase the load current (knob was set to the least-significant digit, or so I thought). Due to the velocity control with that knob, the current was at max within what felt like half a turn, though probably in reality was 1,5 turn or something. It didn't feel right. It took me a long time to get used to the control having non-constant increment depending on rotation speed. I felt I needed to be extra careful while adjusting the load with the output on, not (yet) trusting the instrument would do what I wanted.So what think is that you need some clear feedback to the user that is more than just the numbers on the display increasing. The change in the displayed value is probably not really intuitive if you're used to look at a certain digit changing, or looking for a constant change, as you rotate the control knob. Maybe if velocity control kicks in you should indicate in real time which digit is increased so you aren't 'overwhelmed' by the way your value changes. (Is it clear what I mean here?)P.S. read for increasing also decreasing, but i.m.o. that's just a matter of rotation direction.

author: TerminalJack505
author_rank: Super Contributor
timestamp: September 24, 2013, 11:11:00 am
content: Quote from: dannyf on September 23, 2013, 11:55:16 pmBBM. It's bounce resistant up to the point that both channels are bouncing simultaneously. If that happens then all bets are off.

author: nessatse
author_rank: Regular Contributor
timestamp: September 24, 2013, 12:52:47 pm
content: Quote from: TerminalJack505 on September 24, 2013, 11:11:00 amIf both channels bounce simultaneously your encoder is probably broken. This is in effect Gray code, remember.

author: TerminalJack505
author_rank: Super Contributor
timestamp: September 24, 2013, 12:57:38 pm
content: Quote from: nessatse on September 24, 2013, 12:52:47 pmI'd be worried that this is going to be a common failure mode when these things reach their end-of-life.

author: Rufus
author_rank: Super Contributor
timestamp: September 24, 2013, 01:13:46 pm
content: Quote from: nessatse on September 24, 2013, 12:52:47 pmWhen you turn a mechanical switch encoder faster than it can track simultaneous bounce on both channels is about all you are going to see.

author: dannyf
author_rank: Super Contributor
timestamp: September 24, 2013, 01:51:14 pm
content: QuoteOr both channels are not pulsing at all,You have to stop somewhere and engineering against every possible fault leads to a useless product.

author: jeroen74
author_rank: Frequent Contributor
timestamp: September 24, 2013, 06:22:19 pm
content: One Philips/Fluke function generator (PM5139 I think) I once took a cursory peek at, at a previous job, had a nice big knob that was apparently quite heavy and undamped. Give it a good spin and it keeps on spinning for like a minuteI wonder how well that worked.

author: TerminalJack505
author_rank: Super Contributor
timestamp: September 24, 2013, 06:38:05 pm
content: I finally got things in my little research project to the point where I would consider it "not terrible."Just to remind everyone, for useability testing I have a mock interface to change a voltage between the values of 1.250V and 30.00V, using just a 20PPR rotary encoder. The challenge here is to give the user the ability to change the setting as conveniently as possible--avoiding overshoots and not requiring the user to spend a lot of time spinning the encoder just to make large changes.One of the important changes I made was to basically throw away half of the actions (=clockwise or counter-clockwise click) and turn the encoder into a 10PPR encoder instead of a 20PPR encoder. The problem was that it is actually too hard to control at 20PPR. It's too hard to keep up with the changes, that is.Note that, from the standpoint of calculating velocity, I still count the actions that are otherwise thrown away. This gives better resolution.I still have the three step sizes that I had earlier: 1mV, 10mV and 100mV. The problem with my earlier implementation was that I required the user to maintain the velocity to stay at particular step size. This meant that the user (=me) couldn't keep up with the changes for the larger step sizes and would, more often then not, overshoot.I changed it so that the user can slow down once they have stepped-up the speed. The step size can now onlyincreaseonce the user starts spinning the encoder. That is, they can step up but not down. The encoder has to come to a complete stop before the step size returns to the 1mV size. This actually works out pretty good for this particular mock environment. (Setting an output voltage.)Another change that helps a lot is that when the step size is 10mV then the last digit of the setting is always "0", and if the step size is 100mV then the last two digits are always "00". This helps the user focus on what is actually changing. (Like Christe4nM suggested.)There are two velocity thresholds to enter into the 10mV and 100mV step sizes. The user has to be fairly determined to pass these thresholds. Once they are passed, however, the user can slow down and catch back up and regain control. Control is one of the keys.Predictability is another key. So far as the user having a pretty good expectation as to when they are going to "step up." That doesn't seem too bad currently but could use some improvement.

author: BravoV
author_rank: Super Contributor
timestamp: September 25, 2013, 03:36:12 am
content: Uhmm ... another new one, geezz ... I haven't finished digesting your previous codes yet.Really appreciate that Jack.

author: Teemo
author_rank: Regular Contributor
timestamp: September 25, 2013, 07:49:48 am
content: Another idea: probably it is better to use undented rotary encoder with smooth turning. The detents kind of screw up the user experience, because they trick the brain to instinctively thinking that one step with detents should mean one step of adjustment.Our kitchen radio have volume knob with undented encoder(with velocity) and it feels so natural that most users even do not know it has this magnificent feature. It is possible to turn the volume from 0 to max (or form max to 0) in only half turn when turning really fast.

author: dannyf
author_rank: Super Contributor
timestamp: September 25, 2013, 10:31:04 am
content: another way to measure velocity is to measure frequency of the pulses and adjust increments based on that.

author: TerminalJack505
author_rank: Super Contributor
timestamp: September 25, 2013, 11:58:55 am
content: Quote from: BravoV on September 25, 2013, 03:36:12 amThe only file that changed since the last version I posted is main.cpp. All the other files are the same.I probably won't be playing around much more with the project. I don't have an immediate need for an encoder with velocity. I just thought it sounded like something interesting to research.Quote from: Teemo on September 25, 2013, 07:49:48 amThe encoder I'm using has no detents. I agree that you wouldn't want them for this sort of application.

author: mrflibble
author_rank: Super Contributor
timestamp: September 25, 2013, 10:28:02 pm
content: Quote from: TerminalJack505 on September 25, 2013, 11:58:55 amThat might also be a matter of both taste and the particular application.The affordable ones on ebay do have the detent. And while I would like to have the detent be a little less forceful they are good enough. Incidentally, does anyone know of a cheap source of decent knobs for these things?Regarding distinct 10/100/1000 mV steps vs continuous, I prefer the distinct steps. Precisely because that gives me some predictable behavior in step size.