Rotary encoder - dubious video - Page 1
Forum section: electronics / projects

author: Peabody
author_rank: Super Contributor
timestamp: March 24, 2018, 05:08:16 am
content: Can I be forgiven for becoming more cynical and suspicious as I get older? Here's a video supposedly illustrating the lookup table method of reading a rotary encoder:The first 3:30 shows that no matter how fast or slow he turns it, it always keeps perfect count, and returns to exactly the same origin. Always.Or, that thing in front that "isn't part of the circuit" is a Hall effect or optical sensor that simply detects when the knob returns to the same place, and he isn't decoding anything. In the text below he says, "Don't ask me for the source code." Right.Why would an intelligent guy, fluent in at least two languages, and knowledgeable about electronics, post a video like this? Well, maybe I'm wrong, and it's all exactly as he says it is. Right.

author: BrianHG
author_rank: Super Contributor
timestamp: March 24, 2018, 05:27:41 am
content: What he says is true about the signal bounce moving the encoder as the internal switch brush makes contact. However, that encoder is new an does not yet experience random brush contact noise in the middle of a solid contact on position. Give it a year of turning back and forth use, and, occasional errors/skips will begin to creep in, worsening over time unless contact spray is applied.

author: Ian.M
author_rank: Super Contributor
timestamp: March 24, 2018, 06:09:13 am
content: While it is remotely possible to hack that breadboard by removing contact strips, and wiring stuff up with magnet wire to make that (possible) optical sensor apparently sitting in an unconnected bus bar, control the LED on the other board, that would be an extreme amount of work.If I had to fake it myself, I'd simply hack the encoder so that one position produces a unique output. The easy option would be to insulate all except one segment with hard laqquer. However if you were to add a sliver of thin copper tape in a segment where both outputs are open, you could arrange it to short the outputs to each other at that position only. When your code sees that input state, it would briefly make one input an output and pulse it ans see if the other input follows. With care it could even still function as a normal encoder and would pass all but the closest and most sceptical inspection!Sorry, I haven't got the patience to sit through his explanation of rising edge sampling vs state machine as he's not as easy to listen to as Dave and his DaveCAD is rather on the rough side, but I wouldn't immediately reject it for absence of code. I've hacked out plenty of code myself that's too dirty to display in public.

author: Peabody
author_rank: Super Contributor
timestamp: March 24, 2018, 01:58:04 pm
content: It bothered me that he turned it multiple turns in the same direction, but didn't have to do the same number of turns in the reverse direction to get back to the origin. So that means his code is not only perfect, but also keeping track of the number of revolutions based on the pulses per revolution of the encoder. So now it has become, in effect, an absolute position encoder instead of just an incremental one. But of course if you're keeping perfect track of the movements, that would be easy to do.It also would have been more convincing if he had powered up with the knob in a different position, and all the subsequent movements returned to THAT position as the index.Well perhaps my suspicions are misplaced. If so, I'd sure like to see his code.

author: rstofer
author_rank: Super Contributor
timestamp: March 24, 2018, 03:22:36 pm
content: I see no reason to doubt his approach. After all, I use an identical approach and it has always worked. At somewhere around 10:00 he mentions only considering a count for a complete 4 step transition. I count every edge.The trick is that the state machine will follow the bouncing edges and the count will bobble up and down by 1 count until the signal settles down. But only by 1 count!I used an AVR (ATmega128,I believeconfirmed) and organized it to use Interrupt On Change for the A & B signals and the code decides which transition to look for based on the current state. If the pin is high, the next expected transition is a falling edge.The code is very tight because each interrupt knows the current state and it just does a little lookup to decide whether to increment or decrement the count.I didn't watch the entire video. Once he started explaining how he handles bounce, I was done. It is identical to my approach.Something like this:Code:#include <avr/io.h>#include <avr/interrupt.h>#include <stdio.h>#include <ctype.h>#include "defines.h"volatile int8_t RightWheelState = 0;volatile int32_t RightWheelCount = 0;volatile int8_t LeftWheelState = 0;volatile int32_t LeftWheelCount = 0;volatile uint8_t urx;volatile uint8_t urx_recv;/*Quadrature Encoder Scheme**There are two pairs of quadrature signals, one for each wheel.* The right wheel is on EXINT4 (PE4) and EXINT5 (PE5)* The left wheel is on EXINT6 (PE6) and EXINT7 (PE7)* Each encoder has two outputs A & B. For convenience, A is considered the MSB*while B is considered the LSB.*/const int32_t EncoderB[] = {+1,-1,-1,+1};const int32_t EncoderA[] = {-1,+1,+1,-1};SIGNAL(SIG_INTERRUPT4)// encoder B for right wheel{EICRB ^= 0x01;// toggle the edge bitRightWheelState ^= 0x01;RightWheelCount += EncoderB;}SIGNAL(SIG_INTERRUPT5)// encoder A for right wheel{EICRB ^= 0x04;RightWheelState ^= 0x02;RightWheelCount += EncoderA;}SIGNAL(SIG_INTERRUPT6)// encoder B for left wheel{EICRB ^= 0x10;LeftWheelState ^= 0x01;LeftWheelCount += EncoderB;}SIGNAL(SIG_INTERRUPT7)// encoder A for left wheel{EICRB ^= 0x40;LeftWheelState ^= 0x02;LeftWheelCount += EncoderA;}void IOInit(void){UBRR0H = 0;UBRR0L = BAUD;UCSR0B = _BV(RXEN) | _BV(TXEN) | _BV(RXCIE);}static int uart_putchar(char c, FILE * stream){if (c == '\n')uart_putchar('\r', stream);while (! (UCSR0A & _BV(UDRE)));UDR0 = c;return c;}SIGNAL(SIG_UART0_RECV){uint8_t s;s = UCSR0A;urx = UDR0;if (bit_is_clear(s, FE))urx_recv = 1;}void QuadInit(void){LeftWheelState = (PORTE & 0xC0) >> 6;RightWheelState = (PORTE & 0x30) >> 4;EICRB = 0xff;// enable rising edge triggering on INT4..7EICRB ^= (LeftWheelState & 0x02) << 5;// if a signal is already setEICRB ^= (LeftWheelState & 0x01) << 4;// set the interrupt to falling edgeEICRB ^= (RightWheelState & 0x02) << 1;EICRB ^= (RightWheelState & 0x01);EIMSK = 0xf0;// enable these 4 interrupts}int main(void){IOInit();QuadInit();sei();fdevopen(uart_putchar, NULL);printf("Hello World\n");while (1) {printf("LWS =%2d LWC =%6ld RWS =%2d RWC =%6ld\n",LeftWheelState, LeftWheelCount, RightWheelState, RightWheelCount);}}

author: BrianHG
author_rank: Super Contributor
timestamp: March 25, 2018, 03:28:54 am
content: LOL, @rstofer, just to mess with the Youtube video maker, got to the Youtube video page and paste your source code in the comments saying it's "an improvement" over the youtube video author's code. Let's see what he says...Though, warning, if the youtube video author faked his results, he may be fishing exactly for this....

author: senso
author_rank: Frequent Contributor
timestamp: March 25, 2018, 03:47:10 am
content: Its an encoder, there is tons of code for those too lazy to code/understand it..Why even doubt such a basic thing to use with a micro?

author: ucanel
author_rank: Regular Contributor
timestamp: March 25, 2018, 05:23:37 am
content: I am sure video owner did not even heard about the encoders on motors with 200-600-1000 ppr (pulse per round).There is no need to fake a this kind of simple subject.And if you are so serious there is encoder modules on the some mcu's.

author: rstofer
author_rank: Super Contributor
timestamp: March 25, 2018, 06:54:00 am
content: Quote from: BrianHG on March 25, 2018, 03:28:54 amWhy does anyone think his results are fake? It's an obvious application, the AVR is quite capable of handling the rate and since he counts full transitions (4 states per count), it isn't very hard to do a little modulo arithmetic to keep the value constrained to one revolution. Of course he can come back to the index mark.I didn't see how he implemented the state machine in code but it is pretty obvious how it has to work. There's nothing magic about the application or the code.There are a lot of similar projects on the Internet.

author: Peabody
author_rank: Super Contributor
timestamp: March 25, 2018, 02:22:38 pm
content: Well I guess I was completely off base. My apologies to the Youtuber. I've just never seen a cheap encoder work quite that perfectly, never losing or gaining a single detent after being worked like that. But the clear consensus is I was wrong.

author: Dave
author_rank: Super Contributor
timestamp: March 25, 2018, 05:55:08 pm
content: The main problem is that arduino "engineers" code poor software to handle encoders, so they are prone to losing or gaining steps when the tiniest amount of contact bounce is present in the circuit.Then, in an attempt to botch together a semi-usable product, you see capacitors on the quadrature lines and other ridiculous "solutions".That's why you have the feeling that good encoder response is hard to achieve, when it really isn't.

author: rstofer
author_rank: Super Contributor
timestamp: March 25, 2018, 06:24:48 pm
content: In the Arduino world, it may not be common to use edge interrupts as I did in the code above. That's one of the problems when using a predesigned infrastructure. Sometimes, you just have to get your hands dirty down at the hardware level. The ATmega328p has only 3 pin interrupts plus 2 external interrupts (AFAICT) Whether this can accommodate my 4 edge interrupt code is left as an exercise. I have never tried it. The ATmega128 has 8 external interrupts that incorporate edge sensitivity.

author: mikerj
author_rank: Super Contributor
timestamp: March 26, 2018, 01:47:27 pm
content: Quote from: rstofer on March 25, 2018, 06:24:48 pmIt's not common to use external interrupts at all for these mechanical encoders. They are so noisy that you could end up servicing dozens of interrupts per transition of the encoder, and they only get worse with age. Polling the state via a timer interrupt gives a consistent overhead.