Rotary encoders vs VHDL. - Page 1
Forum section: electronics / general

author: hamster_nz
author_rank: Super Contributor
timestamp: November 22, 2015, 08:14:47 pm
content: I was playing around with a rotary encoder on the weekend, and found that my usual way of decoding the quadrature outputs was not working. If you are driving a counter up and down then switch bounces don't really matter, but in this case I was zooming in by uneven amounts, so switch noise would cause uneven jumps.The solution I came up with was rather than debouncing the contacts (which just made things better) was to do away with the debounce all together, and to look at the last three changes in quadrature, and then make decisions based on that:Code:case enc_quad_safe & enc_quad_last & enc_quad_last_last iswhen "010010" => scale_internal <= scale_right;when "001011" => scale_internal <= scale_right;when "101101" => scale_internal <= scale_right;when "110100" => scale_internal <= scale_right;when "100001" => scale_internal <= scale_left;when "000111" => scale_internal <= scale_left;when "011110" => scale_internal <= scale_left;when "111000" => scale_internal <= scale_left;when others => NULL;end case;enc_quad_meta <= enc_b & enc_a;enc_quad_safe <= enc_quad_meta;if enc_quad_last /= enc_quad_safe thenenc_quad_last <= enc_quad_safeenc_quad_last_last <= enc_quad_last(3 downto 2);end if;It's a technique applicable to both micros and FPGAs.It is now super sweet -

author: andersm
author_rank: Super Contributor
timestamp: November 22, 2015, 11:21:30 pm
content: Atmel (of all people) used to have an app note about reading quadrature encoders in VHDL. Unfortunately I can't find it anymore, but IIRC they just used a simple filter that required the N last inputs to be stable before it changed state.

author: Someone
author_rank: Super Contributor
timestamp: November 22, 2015, 11:57:54 pm
content: A cascade of two 2-tap filters is slightly cheaper than the 3-tap filter in most archtectures, but they're both simple enough for most cases.

author: ehughes
author_rank: Frequent Contributor
timestamp: November 24, 2015, 06:24:41 pm
content: http://www.fpga4fun.com/QuadratureDecoder.htmlhttp://www.eng.utah.edu/~cs3710/xilinx-docs/examples/s3esk_rotary_encoder_interface.pdf

author: dannyf
author_rank: Super Contributor
timestamp: November 24, 2015, 07:41:05 pm
content: QuoteIt is based on a state machine. It has been done for decades, with four bits (16 combinations). Incredibly resistant to contact bounces.

author: hamster_nz
author_rank: Super Contributor
timestamp: November 25, 2015, 07:38:03 am
content: Quote from: dannyf on November 24, 2015, 07:41:05 pmHave to agree - super resistant to switch bounces (yay! no debounce with it's arbitrary timeout is needed).It could be implemented with an FSM, but coding it as an explicit FSM would be pretty ugly (16 states, three transitions out of each state). It also looks to be pretty compact (4 FFs for the synchronizer, 4 FFs for holding the encoder's prior inputs, a couple of LUTs to see if the encoder's state has changed, and two LUT6s to decide what action to do.Much smaller than anything with a debounce counter, and should work up to almost any speed until the switches no longer open and close in the correct sequence, and even then it should fail relatively gracefully with it just skipping counts.The only reason I bothered posting was that some of the other references I've bothered to read would suffer from the same problem as my original attempt had - noise input leads to noisy output. - e.g:http://www.fpga4fun.com/QuadratureDecoder.htmlhttp://jugglingpirate.net/using-rotary-encoder-with-fpga-vhdl-code/https://www.hackmeister.dk/2010/07/using-a-quadrature-encoder-as-input-to-fpga/

author: dannyf
author_rank: Super Contributor
timestamp: November 25, 2015, 11:52:56 am
content: QuoteHere would be one implementation:Code:static uint8_t enc_states=0; //encoder states: AB_prev..AB_now, lowest four bits effectiveenc_states <<=2; //left shift previous statesenc_states |= (IO_GET(ENCA_PORT, ENC_CHA)?0x02:0x00) | (IO_GET(ENCB_PORT, ENC_CHB)?0x01:0x00); //read current encoder pin statesreturn enc_outcome[enc_states & 0x0f]; //return encoder output from a look-up table, lowest four bits effectiveIf you want, you can actually write it out on a single return line - not terribly readable but 100% functional.

author: hamster_nz
author_rank: Super Contributor
timestamp: November 25, 2015, 06:49:20 pm
content: Quote from: dannyf on November 25, 2015, 11:52:56 amAh... you are missing the important bit. The output is based on the last twotransitions, not the last tworeadings.Say on a click to the left you add 1 to a counter, and on the click to the right you take 2 off the counter..Here is the data for two clicks to the left, with a bit of switch bounce on the first transition ("00" => "01")...."00", "01","00","01","00", 01","00","01","11","11"...If you base it on the last two reading, because of the bounce in the middle you get +1, -2, +1, -2,+1, -2, +1, +1, a total of -1.Code:Change input last output1: 01 00 +12: 00 01 -23: 01 00 +14: 00 01 -25: 01 00 +16: 00 01 -27: 01 00 +18: 11 01 +17: 11 11 0If you base your on the last two transitions you get a reliable output - when you get two "click to the right" or "click to the left" in a row, only then do you act on it.Code:Change input prior_values transitions output1: 01 00 01 L R 02: 00 01 00 R L 03: 01 00 01 L R 04: 00 01 00 R L 05: 01 00 01 L R 06: 00 01 00 R L 07: 01 00 01 L R 08: 11 01 00 R R +1In my case I was "zoom <= zoom * 3/4" one way, and "zoom <= zoom * 5/4", with my problem being that "(3*5)/(4*4) = 15/16, 15/16 != 1", so switch bounce in the encoder would show up as a visible jump in the zooming.

author: dannyf
author_rank: Super Contributor
timestamp: November 26, 2015, 04:38:30 pm
content: QuoteIt doesn't really matter: 1) you can run the code within an isr triggered by transitions; 2) in pulling, any (valid) transition will be read; and any invalid transition (bounces), other than the very first one, will be ignored, as the look-up table will return a value of 0.