Shop OBEX P1 Docs P2 Docs Learn Events
Rotary Encoder Help — Parallax Forums

Rotary Encoder Help

ADZ_916ADZ_916 Posts: 30
edited 2013-01-28 10:16 in General Discussion
Hey all!

I'm having a tough time figuring out how to read my rotary encoder to the propeller as is is not the common 2-bit type.

Data Sheet : http://www.alps.com/WebObjects/catalog.woa/E/HTML/Encoder/Incremental/EC11/EC11EH224403.html

If any one could clue me in as to how to read this "ABC Switch"? type of encoder it would be of great help as I would like to use this as the interface for a Tesla coil driver I am building based around the propeller.

Thanks in advance for any help

-ADZ

Comments

  • kwinnkwinn Posts: 8,697
    edited 2013-01-25 21:25
    This switch/encoder produces 3 low going output signals. To decode direction you compare the next signal to the current one. If B comes after A the switch is turning in one direction (usually clockwise). If C comes after A it is turning in the other direction (usually counter clockwise. There are 30 detents per revolution so each channel will have 10 low going pulses per revolution.
  • ADZ_916ADZ_916 Posts: 30
    edited 2013-01-25 22:04
    Still not quite sure how to turn that into something useful on the prop, I'm looking for something similar to examples I've seen for standard rotary encoders,

    http://obex.parallax.com/objects/666/
    http://obex.parallax.com/objects/24/

    So far I have written some simple code that checks the input pins and displays the binary output through the serial terminal as expected i see:

    CW
    %010
    %001
    %100

    CCW
    %010
    %100
    %001

    with only a one bit change between detents.

    If you could include some simple code that dispays a value in the serial terminal that is manipulated by the knob I would be very gratefull

    Thanks Again!
    -ADZ
  • kwinnkwinn Posts: 8,697
    edited 2013-01-26 00:15
    Here is a bit of code that increments or decrements a variable (count) each time the encoder turns one position. Add this to what you already have and print the count as well as the binary output.
    '********************************************************************************************************
    ' input is the 3 bits read from the encoder            
    ' old is the previous value read from the encoder                  
    ' count is a variable that is incremented or decremented based on the direction the encoder is turned
    '********************************************************************************************************
      case input  
        %001:             
          case old
            %010: count := count - 1                        ' going ccw subtract 1 from count
            %100: count := count + 1                        ' going cw add 1 to count
        %010:             
          case old
            %100: count := count - 1                        ' going ccw subtract 1 from count
            %001: count := count + 1                        ' going cw add 1 to count
        %100:
          case old
            %001: count := count - 1                        ' going ccw subtract 1 from count
            %010: count := count + 1                        ' going cw add 1 to count
      old := input                                          ' 
    
  • ADZ_916ADZ_916 Posts: 30
    edited 2013-01-26 14:48
    I'm sure I'm missing something simple here but when i try the below code the count variable does not change.
    {3-bit encoder reader -ADZ}
    
    
    CON
            _clkmode = xtal1 + pll8x                                               'Standard clock mode * crystal frequency = 80 MHz
            _xinfreq = 10_000_000
    
    VAR
      long  old
      long  input
      long  count
       
    OBJ
      pst      : "Parallax Serial Terminal"
      
    PUB Main 
        init
        count := 50 
      repeat
         input := ina
         pst.str(string("Raw Input = "))
         pst.bin(input,3)
         pst.Chars(pst#NL, 1)
         read
         pst.str(string("Current Count = "))
         pst.dec(count)
         waitcnt(clkfreq / 20 + cnt)
         pst.clear
    
    PUB read
    
    '********************************************************************************************************
    ' input is the 3 bits read from the encoder            
    ' old is the previous value read from the encoder                  
    ' count is a variable that is incremented or decremented based on the direction the encoder is turned
    '********************************************************************************************************
           NOTE THAT THIS CODE IS NOT SHOWING UP CORRECTLY ON THE FORUM BUT IS EXACTLY AS POST #5 FROM kwinn
      case input  
        1:             
          case old
            0: count := count - 1                        ' going ccw subtract 1 from count
            0: count := count + 1                        ' going cw add 1 to count
        0:             
          case old
            0: count := count - 1                        ' going ccw subtract 1 from count
            1: count := count + 1                        ' going cw add 1 to count
        0:
          case old
            1: count := count - 1                        ' going ccw subtract 1 from count
            0: count := count + 1                        ' going cw add 1 to count
      old := input                                          '
    
    
    PRI init
    
    pst.start(921_600)
    'dira := 0
    
  • Mark_TMark_T Posts: 1,981
    edited 2013-01-26 15:05
    You need to ignore all situations where all inputs are HIGH - these give no useful information.
  • ADZ_916ADZ_916 Posts: 30
    edited 2013-01-26 15:10
    There are\should be no situations where all inputs are high as demonstrated by an earlier post. Perhaps you could explain how to "ignore all situations where all inputs are HIGH" as I'm not sure exactly what you mean by that.

    Thanks!
    -ADZ
  • evanhevanh Posts: 15,949
    edited 2013-01-26 18:32
    Are you sure you want only a 20000 operation device?
  • ADZ_916ADZ_916 Posts: 30
    edited 2013-01-26 18:43
    This was pulled from an old car stereo as I am trying to use only recycled parts and it was the only thing I could find that would be an all-encompassing control knob with the inbuilt push switch. It may also be helpful to note that I currently have it wired with a pullup on the com line and the three outputs going directly to the prop pins [0-2]. If need be I can change this to the seemingly more common pullup on A B & C with com to gnd. Thought maybe i could save a couple resistors that way.

    Again any help would be greatly appreciated as i am currently stumped.
  • kwinnkwinn Posts: 8,697
    edited 2013-01-26 20:59
    repeat
         input := ina
         pst.str(string("Raw Input = "))
         pst.bin(input,3)
    
    Well, for one thing you have not selected any pins to input in the ina. The statement should be ina [Px..Py] where x is the first pin and y is the last pin. As it is you are inputting all 32 pins. That means the case statement is looking at all 32 pins as well so the chance of a match is pretty slim.

    The other thing to do is to manually enter the code I posted. It looks like you may have cut and pasted it and it is possible some non printable characters got inserted.
  • ADZ_916ADZ_916 Posts: 30
    edited 2013-01-26 21:17
    I have changed the main routine to:
    PUB Main
        init
        count := 50 
      repeat
         input := ina[0..2]
         pst.str(string("Raw Input = "))
         pst.bin(input,3)
         pst.Chars(pst#NL, 1)
         read   
         pst.str(string("Current Count = "))
         pst.dec(count)
    
         waitcnt(clkfreq / 20 + cnt)
         pst.clear
    
    and have double checked my read routine and it is exactly as in your previous post. The count variable just sits there at 50 or 0 if I don't pre-initilize it.
    The input variable however changes as expected. Still cant figure out exactly what I'm doing wrong here.

    Thanks again for being so patient and helpful.
    -ADZ
  • kwinnkwinn Posts: 8,697
    edited 2013-01-26 23:18
    Try commenting out all the code in "PUB read" except the "old := input" and add "count := count + 1" just below it. If that does not work please post all of the code.
  • ADZ_916ADZ_916 Posts: 30
    edited 2013-01-26 23:23
    Commenting out the code and added line counts up on the count variable as expected.
  • kwinnkwinn Posts: 8,697
    edited 2013-01-27 00:21
    OK, so you are getting to read. Uncomment the original lines and add pst.str (string ("got to input 001" )) for each of the 3 input cases, changing 001 to 010 and 100 so we can see if any one was reached.
  • ADZ_916ADZ_916 Posts: 30
    edited 2013-01-27 00:35
    Is it at all possible that my issue has something to do with "switch bounce"? I notice from the datasheet "At R=5kΩ Chattering: 5ms max. Bounce: 5ms max." Not sure if this would cause me any problems but i do notice that my raw output seems to put random high on some pins as it moves from one detent to the next. I will try the code you suggested and report back.

    Thanks again!
    -ADZ
  • kwinnkwinn Posts: 8,697
    edited 2013-01-27 00:45
    I am not familiar with the part you are using but if it is a mechanical switch bounce is a possibility. To debounce in spin I generally use a repeat loop and read the input until it has stayed the same for 10 times through the loop.
  • ADZ_916ADZ_916 Posts: 30
    edited 2013-01-27 01:15
    OK, i think I'm getting some where by changing the spacing on the line:
     old := input
    
    so that it is part of the last case statement like so:
    Capture.jpg


    gets me the result of one click cw and the number continually counts up and if i move it once ccw it continually counts down. I think I'm close!

    EDIT : it seams that turning th knob has this result, one click cw and it continually counts up, next click in the same direction and it continually counts down, on the next click in the same direction the count stops. Not sure whats going on here. Ideally the count would increment by 1 for every forward click and decrement by 1 for reverse.
    1024 x 737 - 45K
  • ChrisGaddChrisGadd Posts: 310
    edited 2013-01-27 06:12
    I think you need a 'old := input' inside every case, not just the last one.
  • kwinnkwinn Posts: 8,697
    edited 2013-01-27 10:10
    Part of the problem is indentation. Try this. I have also attached the program in case the problem comes from copying from the post.
    CON
            _clkmode = xtal1 + pll8x                                               'Standard clock mode * crystal frequency = 80 MHz
            _xinfreq = 10_000_000
    
    VAR
      long  old
      long  input
      long  count
      long  count1
       
    OBJ
      pst      : "Parallax Serial Terminal"
      
    PUB Main 
        init
        count := 50 
      repeat
         input := ina[0..2]
         if input <> old                                    ' see if the switch input has changed
          pst.str(string("Raw Input = "))                   ' no point printing all this if the
          pst.bin(input,3)                                  ' switch has not moved
          pst.Chars(pst#NL, 1)
          read                                              ' increment or decrement the counter
          pst.str(string("Current Count = "))
          pst.dec(count)
    '     waitcnt(clkfreq / 20 + cnt)                        ' not really needed now
         pst.clear
    
    
    PRI read
    '********************************************************************************************************
    ' input is the 3 bits read from the encoder            
    ' old is the previous value read from the encoder                  
    ' count is a variable that is incremented or decremented based on the direction the encoder is turned
    '********************************************************************************************************
      case input  
        %001:             
          case old
            %010: count := count - 1                        ' going ccw subtract 1 from count
            %100: count := count + 1                        ' going cw add 1 to count
        %010:             
          case old
            %100: count := count - 1                        ' going ccw subtract 1 from count
            %001: count := count + 1                        ' going cw add 1 to count
        %100:
          case old
            %001: count := count - 1                        ' going ccw subtract 1 from count
            %010: count := count + 1                        ' going cw add 1 to count
      old := input                     
    
    PRI init
    pst.start(921_600)
    'dira := %000
    
    DAT
    name    byte  "string_data"        
            
    
  • kwinnkwinn Posts: 8,697
    edited 2013-01-27 10:28
    BTW, you will need to add a routine to debounce the encoder switches and ignore the %111 states in between the valid states. You should probably call that one "read" and rename the current read something like "decode".
  • ADZ_916ADZ_916 Posts: 30
    edited 2013-01-27 17:36
    Using the attached spin file results in no display at all on the serial terminal.
  • kwinnkwinn Posts: 8,697
    edited 2013-01-27 18:52
    Sorry about that. I don't have my protoboard to try things out with. Try this one and see what is printed last. All I can do is make sure it compiles.
  • ADZ_916ADZ_916 Posts: 30
    edited 2013-01-27 22:36
    With the code provided the serial terminal is flashing too fast for me to see whats going on, reintroducing the delay shows the program reaching the case statements:
    "Got to input case 001"
    However the counter is still not incrementing. I also posted this on the propeller 1 forum and got some useful code that with a few small changes does exactly what I was looking for, a counter that increments and decrements one per detent and resets with the pushbutton:
    CON
      _clkmode = xtal1 + pll8x
      _xinfreq = 10_000_000
    
      knob_basepin = 0                                    
      knob_button  = 3                                       '  Requires 3 pins to sense direction of rotation, offset is set to the lsb
                                                             '  Clockwise rotation produces the pattern 011, 110, 101 (with common on ground and pullups on pins)
    
    VAR
      byte  knob_pos
      long  counter
      
    OBJ
      pst : "Parallax Serial Terminal"
    
    PUB Main
    
    pst.start(921_600)                    ' Initialize serial terminal
    
      knob_pos := knob_position           ' Initialize knob position
      repeat                              ' Repeat loop
        if CW                             ' If CW sub-routine returns true
          counter++                       ' increment counter variable by 1
        if CCW                            ' If CCW sub-routine returns true
          counter--                       ' deincrement counter variable by 1
        if ina[3] == 0                    ' If button pressed 
          counter := 0                    ' Reset counter to zero
        pst.dec(counter)                  ' Display current counter
        waitcnt(clkfreq / 100 + cnt)      ' Delay to alow time to view terminal display
        pst.clear                         ' Clear screen to keep cursor in the same position
      
    PRI Knob_position : pos
      pos := (ina >> knob_basepin) & 1  
    
    PRI CW | c_pos
      c_pos := Knob_position
      if (c_pos == 1 and knob_pos == 0) or (c_pos == 0 and knob_pos == 1) or (c_pos == 1 and knob_pos == 1)
        knob_pos := c_pos
        result := 1
    
    PRI CCW | c_pos
      c_pos := Knob_position
      if (c_pos == 1 and knob_pos == 1) or (c_pos == 1 and knob_pos == 0) or (c_pos == 0 and knob_pos == 1)
        knob_pos := c_pos
        result := 1
    
    the thing with this is that I had to wire pullups for each switch pin as apposed to one pullup to 3.3v

    EDIT: Code will not paste correctly, file is attached.ABC_Encoder.spin
  • ADZ_916ADZ_916 Posts: 30
    edited 2013-01-27 23:26
    Working with your code a little Ive made an attempt at revising it to work with active low inputs and a very weak attempt at dealing with some formatting issues with the terminal display, result is attached. Unfortunately the damn counter is still not changing!! I think with your examples of how to debug with serial terminal statements I will be able to figure out a lot of my problems in the future.

    Capture4.PNG



    Thanks again for all your help!
  • jmgjmg Posts: 15,173
    edited 2013-01-28 01:07
    Your state variable can have 8 possible states, so you are still missing some coverage.

    If you have break before make sensor, you will go via 000, and your present state structure will fail.

    You can either cover all states, or pull in the old := input line, to be inside only the valid states.(as below)
    This means old can now only have one of three values - before it could have 8 possible values.

    It will take a couple of clicks for the state engine to 'grabs' the right phase, but from there it should ignore 000 and 111, and other states, and
    edge-act only on the valid three.
      {case input
        %001:
          pst.str(string("Got to input case 001"))             
          case old
            %010: count := count - 1  ' these are one-shot state branches, as old becomes %001
            %100: count := count + 1    
             ' %001 is no change, or no edge, so cycles many times, waiting for next change 
          old := input
        %010:             
          pst.str(string("Got to input case 010"))   
          case old
            %100: count := count - 1                 
            %001: count := count + 1                 
             ' %010 is no change, or no edge, so cycles many times, waiting for next change 
          old := input
        %100:
          pst.str(string("Got to input case 100"))   
          case old
            %001: count := count - 1                 
            %010: count := count + 1                 
             ' %100 is no change, or no edge, so cycles many times, waiting for next change 
          old := input
      }
    
    
  • kwinnkwinn Posts: 8,697
    edited 2013-01-28 10:16
    jmg spotted the problem. I overlooked the fact that there will always be %111 or %000 read from the switch before it gets to the next valid position. That gets stored in "old", and since the cases for old are %001, %010, and %100 the count++ or count-- will never be executed. I have uploaded a program with a fix for that. The "if" statement in Main will ignore invalid states (000 & 111) and the same state as that stored in "old". Wish I could test it but my stuff is all packed for moving.

    Regarding post 23:
    ...the thing with this is that I had to wire pullups for each switch pin as apposed to one pullup to 3.3v
    The most popular method of connecting this type of encoder is to have the switch common connected to ground and 3 pullup resistors to V+ on the switch outputs. The alternative is to have the common go to V+ and pulldown resistors to ground. The resistors are required to avoid floating inputs.
Sign In or Register to comment.