Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Rotary Pulse Encoder input

Status
Not open for further replies.
To read a rotary encoder, the best way is:

- Whenever a change is detected on one of the rotary encoder pins, you read both pins and add then put the 2 bits on the end of an 8-bit number.
- Then compare the 8-bit number to known sequences to work out whether clockwise, anticlockwise or no match.

Doing it this way you may miss pulses every now and again when changing direction or if you miss a switch closure, etc.
However there is almost 0% chance of getting a false positive and I found it to be very reliable.

Here is some assembly I wrote to read a rotary encoder which should be easily adapted to your purpose. Basically it will just set a flag for clockwise or anticlockwise and the flag is read and cleared in software.

The first part of the code compares the current rotary encoder reading with the previous reading and if nothing has changed then it returns.

Code:
ReadRotaryEncoder
	MOVLW	b'00000011'
	ANDWF	ROTARY_TEMP, w
	MOVWF	TEMP2
	MOVLW	b'00000011'
	ANDWF	BUTTONS2, w
	SUBWF	TEMP2
	BTFSS	STATUS, Z
	GoTo	$+2
	RETURN
; Stores the current setting every time it's called
	RLF		ROTARY_TEMP
	RLF		ROTARY_TEMP	;rotates left to accept the next 2 bits
	MOVLW	b'11111100'
	ANDWF	ROTARY_TEMP, f	; strips any unwanted bits from the start
	MOVLW	b'00000011'
	ANDWF	BUTTONS2, w
	ADDWF	ROTARY_TEMP, f
;************
;Compares ROTARY_TEMP with known sequences
	MOVLW 	b'00011110'
	SUBWF	ROTARY_TEMP, w
	BTFSC	STATUS, Z
	GoTo	FlagAnticlockwise
	MOVLW 	b'01111000'
	SUBWF	ROTARY_TEMP, w
	BTFSC	STATUS, Z
	GoTo	FlagAnticlockwise
	MOVLW 	b'11100001'
	SUBWF	ROTARY_TEMP, w
	BTFSC	STATUS, Z
	GoTo	FlagAnticlockwise
	MOVLW 	b'10000111'
	SUBWF	ROTARY_TEMP, w
	BTFSC	STATUS, Z
	GoTo	FlagAnticlockwise
	
	MOVLW	b'00101101'
	SUBWF	ROTARY_TEMP, w
	BTFSC	STATUS, Z
	GoTo	FlagClockwise
	MOVLW	b'10110100'
	SUBWF	ROTARY_TEMP, w
	BTFSC	STATUS, Z
	GoTo	FlagClockwise
	MOVLW	b'11010010'
	SUBWF	ROTARY_TEMP, w
	BTFSC	STATUS, Z
	GoTo	FlagClockwise
	MOVLW	b'01001011'
	SUBWF	ROTARY_TEMP, w
	BTFSC	STATUS, Z
	GoTo	FlagClockwise
	RETURN
FlagClockwise
	BSF		TMR0_TEMP, CLOCKWISE
	BCF		TMR0_TEMP, ANTICLOCKWISE
	RETURN
FlagAnticlockwise
	BCF		TMR0_TEMP, CLOCKWISE
	BSF		TMR0_TEMP, ANTICLOCKWISE
	RETURN

Hope this helps!
 
Last edited:
Thx for the quik reply Gobbledok.

I had a close look at the quad pulse waveform here :
https://www.fpga4fun.com/QuadratureDecoder.html

I came up with this..as yet untested....since I don't have the encoder yet and neither Proteus nor Pic Simulator has 'em either.


_____------_____------_____------ signal assigned to PORTA,0

---_____------_____------_____ signal assigned to PORTA,1

Assume that clockwise rotations move the pulses from left to right, anticlockwise from right to left. Easily verified upon getting the encoder.

We can see that upon PORTA,1 getting a state change (implies encoder rotation) if PORTA,1 has the same state as PORTA,0 a clockwise rotation is indicated and 1 pulse is received.
Conversely if PORTA,1 (upon statechange) is different from PORTA,0 then a single counterclockwise pulse is indicated.

This can be coded (i hope) as follows:
Code:
 ScanEncoderPort;
	movf PORTA,w
	xorwf LastSample,w 	; set changed bits
	xorwf LastSample,f 	; lastsample=currentsample.
	andlw d'2'			; filter bit 1
	skpnz 
	retlw 0			; else no encoder change
	rlf LastSample,w		; align bits
	xorwf LastSample,w	; compare bits
	andlw d'2'			; filter bit 1
	skpnz
	retlw 1			; 1=> 1 clockwise pulse
	retlw d'255'		        ; 255=> 1 anticlockwise pulse.

What do u think....?
 
Hi Mosaic,

That will work in theory however in my experience it is not very reliable. It may work fine for optical encoders, however.

For example you may be turning clockwise and every now and again you may get a false anti-clockwise turn due to contact bounce etc (it happened more often than I would like with that code).

When I was trying different codes (admittedly a year or more ago) the only way which didn't give any false positives was my example.

The only problem is that from startup or a wrong reading, you have to wait through 4 more encoder states to get a correct reading which isn't usually a problem.
 
Based on the switch spec 5ms is adequate for debouncing, so either I apply some s'ware debounce or an RC debounce good for 5ms and things should be ok?
 
Last edited:
Based on the switch spec 5ms is adequate for debouncing, so either I apply some s'ware debounce or an RC debounce good for 5ms and things should be ok?

Certainly worth a try! Let us know how you go.
 
I use a piece of software that I'll be glad to give you... I never miss a pulse I can follow 5khz switching with 2x,4x and 8x encoding.

debouncing is not an issue with this style of software.

It runs in a pic12f675 and outputs a clean direction and count from rotary encoders or proximity switching.
 
Mosaic, your system in port #3 will work fine but as it only detects change of pin1 it gives a resolution of half the quadrature output.

Generally systems use 2 methods;
1. on / edge of pin1, pin2 tells the direction (simple, very reliable, only gives 1/4 quadrature output)
2. on any pin change, analyse both pins to know direction (needs debouncing, but gives full quadrature output)

For the second type of system you can use a pin lookup table like Gobbledok did or there is a simple bit-test way of decoding;
Code:
  // read encoder
  encoder = PORTB;

  // check for any change of encoder pins,
  // 2 pins; A and B on PORTB.F7 and PORTB.F6
  new = (encoder & 0b11000000);  // only keep the 2 pins
  if(new != last)   // if any pins changed
  {
    // now check direction, algorithm; if(newA != oldB) dir = +
    if(new.F7 != last.F6) value++;
    else value--;

    last = new;
  }
 
I have three versions... Basic, C and asm... I only have the basic one here (and the asm file that's created ) but the original asm file is at work. heres the basic file.

Its quite a simple idea and works extremely well
 

Attachments

  • quadrature.bas
    1.3 KB · Views: 295
Last edited:
I don't think that optical rotary encoders need debouncing.
 
Just curious if you're using it for a motor or as a user input control for incrementing or decrementing values?
 
User input for now....For motor speed I figure an optical approach would be better, no debounce latencies.
 
User input for now....For motor speed I figure an optical approach would be better, no debounce latencies.

Is your example code trying to decode all four distinct A/B transitions between detent positions? If so, why would you want to do that when using an encoder with detents as a user data input control?

I'm not trying to cut down your code. I'm just curious...

Cheerful regards, Mike
 
Last edited:
That's a good point, less transitions would be better for adj. purposes here.

EDIT:
Updated the code (now 35 instructions, 28 if you lop off all Testbit0 code for only detent based results) to permit both no-detent 'full' quad OR single detent, single pulse results.

thx mike!
 
Last edited:
You're welcome.

I've used PEC11 encoders with detents for user input in a couple projects now with good results. I use my standard 4 or 5 instruction parallel switch state logic and I zero in on a single A or B transition of the four transitions between detents to decode and filter as a "new press". I use a few more instructions to check the opposite bit in the switch state latch variable ('swold') to determine direction. In assembly language it would probably take about 10 instructions in all. Anyway, if anyone is interested, I can post a simple 18F14K22 3-Digit Ammo Counter Demo that demonstrates the method (excerpt below).

Cheerful regards, Mike

Code:
      /*                                                            *
       *  K8LH parallel switch state logic (new press filter)       *
       *                                                            *
       *  swnew  ___---___---___   sample active hi switches        *
       *  swold  ____---___---__   switch state latch               *
       *  swnew  ___-__-__-__-__   changes, press or release        *
       *  swnew  ___-_____-_____   filter out 'release' bits        *
       *                                                            */
         swnew = porta;         // sample active hi switches
         swnew ^= swold;        // changes, press or release
         swold ^= swnew;        // update switch state latch
         swnew &= swold;        // filter out 'release' bits

      /*                                                            *
       *  check the encoder (A on RA4, B on RA5)                    *
       *                                                            */
         if(swnew.5)            // if encoder B "new press"
         { if(swold.4)          // if encoder A state = 1
           { if(ammo < 150)     // if less than upper limit
               ammo++;          // inc "ammo"
           }                    //
           else                 //
           { if(ammo > 0)       // if more than lower limit
               ammo--;          // dec "ammo"
           }                    //
         }                      //
 
Last edited:
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top