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 01:08 in Propeller 1
After posting in the general forum it seams to me this may have been the correct area to ask for assistance for my issue, original post is here:

http://forums.parallax.com/showthread.php/145613-Rotary-Encoder-Help

Comments

  • jmgjmg Posts: 15,173
    edited 2013-01-27 00:56
    ADZ_916 wrote: »
    Is it at all possible that my issue has something to do with "switch bounce"?]

    Close, with an encoder, you need to consider not just valid states, but also how the states get from one to another.
    eg Imagine your encoder is break-before-make, or you get one or more polls of 000, (or indeed, any 'illegal' combination ) - try that with a pencil thru your CASE statements, and see what the outcome is.
  • whickerwhicker Posts: 749
    edited 2013-01-27 01:39
    you do have pull-down or pull-up resistors on the inputs to the propeller, right?
    If you do have these resistors, which way? pulling up to the supply voltage, or pulling down to ground?
    what value are these resistors?

    with 3 inputs it's just a matter of patience as things will always settle out even with switch bounce.
    Like if you're at the A state, and then the dial is turned to B, well eventually the B input is just going to be asserted, no matter how much B bounces or is delayed from the transition of A.
  • jmgjmg Posts: 15,173
    edited 2013-01-27 01:52
    whicker wrote: »
    with 3 inputs it's just a matter of patience as things will always settle out even with switch bounce.

    That is true for the pins, but not quite so true for a (poorly written) state-engine following those pins.
  • ChrisGaddChrisGadd Posts: 310
    edited 2013-01-27 06:11
    I used a similar encoder for a recent project of mine, here's a snippet of the code dealing with the knob:
    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
      knob_offset = 8                                       ' The rotary encoder uses a modified rotary switch with 12 positions connected together as 3 groups of 4
    ' knob_offset+1 = 9                                     '  Requires 3 pins to sense direction of rotation, offset is set to the lsb
    ' knob_offset+2 = 10                                    '  Clockwise rotation produces the pattern 011, 110, 101 (with common on ground and pullups on pins)
    
    VAR
      byte  knob_pos
    
    PUB Main | counter
      knob_pos := knob_position                                                     ' Initialize knob position
      repeat
        if CW
          counter++
        if CCW
          counter--
      
    PRI Knob_position : pos
      pos := (ina >> knob_offset) & %111
    
    PRI CW | c_pos
      c_pos := Knob_position
      if (c_pos == %011 and knob_pos == %110) or (c_pos == %110 and knob_pos == %101) or (c_pos == %101 and knob_pos == %011)
        knob_pos := c_pos
        result := 1
    
    PRI CCW | c_pos
      c_pos := Knob_position
      if (c_pos == %011 and knob_pos == %101) or (c_pos == %101 and knob_pos == %110) or (c_pos == %110 and knob_pos == %011)
        knob_pos := c_pos
        result := 1
    
    It works great, though it requires three IO pins to read the knob. I later redesigned it to use only one pin with a RC circuit:
    {{ Determine the direction of a knob rotation using one pin and a RC circuit
                                                                                                                         
      Knob is constructed using a modified 12-position switch                                                          
                                                                                           
              100Ω      100Ω  100Ω          
         ──┳────────┳──┳─┳─ 1       
            │          │    ┣───┼─ 2       
           0.1µF    ┣────┼───┼─ 3─────┐   
            │          │    │   ┣─ 4      │
                      │    ┣───┼─ 5      
                       ┣────┼───┼─ 6       
                       │    │   ┣─ 7       
                       │    ┣───┼─ 8       
                       ┣────┼───┼─ 9       
                       │    │   └─10       
                       │    └─────11       
                       └──────────12       
    
       Set IO pin to output a high to charge the capacitor                             
       Wait for the capacitor to charge                                                
       Clear a counter                                                                 
       Set IO pin to an input                                                          
       Increment counter each clock tick that input remains above the logic threshold  
    
        This produces readings of ~1051, ~1674, and ~2304
                             
    }}                                                                                                                                                
    CON
    _clkmode = xtal1 + pll16x                                                      
    _xinfreq = 5_000_000
    
    Knob_pin = 16
    
    VAR
      byte  knob_pos
    
    OBJ
      FDS : "FullDuplexSerial"
    
    PUB Main | Counter
      FDS.start(31,30,0,115_200)
      waitcnt(clkfreq + cnt)
      ctra := 01000 << 26 | Knob_pin                                                ' Set counter A to increment each 12.5ns that RC_pin is high
      frqa := 1
      outa[Knob_pin]~~
      knob_pos := Read_knob  
      Counter := 0
      FDS.tx($00)
      FDS.dec(counter)
      repeat
        if CW
          Counter++
          FDS.dec(counter)
          FDS.tx($0D)
        if CCW
          Counter--
          FDS.dec(counter)
          FDS.tx($0D)
       
    PRI Read_knob
      dira[Knob_pin]~~                                                              ' Charge capacitor
      waitcnt(cnt + clkfreq / 1000)
      phsa := 0                                                                     
      dira[Knob_pin]~                                                               ' Begin discharging
      repeat until not ina[Knob_pin]                                                ' Wait for discharge  
      case phsa
        745..1375 : result := 1
        1376..2005 : result := 2
        2006..2635 : result := 3
    
    PRI Read_avg : Average                                                          ' Only used to find the values
      Average := 0
      repeat 1024
        dira[Knob_pin]~~
        waitcnt(cnt + 600)
        phsa := 0
        dira[Knob_pin]~             
        repeat until not ina[Knob_pin]
        Average += phsa
      Average /= 1024        
    
    PRI CCW | c_pos
      c_pos := Read_knob
      if (c_pos == 2 and knob_pos == 1) or (c_pos == 3 and knob_pos == 2) or (c_pos == 1 and knob_pos == 3)
        knob_pos := c_pos
        result := 1
        
    PRI CW | c_pos
      c_pos := Read_knob
      if (c_pos == 3 and knob_pos == 1) or (c_pos == 1 and knob_pos == 2) or (c_pos == 2 and knob_pos == 3)
        knob_pos := c_pos
        result := 1
    
  • whickerwhicker Posts: 749
    edited 2013-01-27 15:22
    jmg wrote: »
    That is true for the pins, but not quite so true for a (poorly written) state-engine following those pins.
    That's what I was going to go after next, is that state machine does need work. But I ask the simple questions first to establish a baseline.
  • ADZ_916ADZ_916 Posts: 30
    edited 2013-01-27 17:34
    Currently my knob is hooked into prop pins 0, 1, 2, with the com line attached to 3.3v with a 220 ohm resistor. Resistor value was just what i had and will later be replaced by 10k.
  • ADZ_916ADZ_916 Posts: 30
    edited 2013-01-27 18:16
    Thanks for the code Chris! Ill see if I can get it to work for my encoder. I wish I knew a little bit more about prop assembly and the counters as i feel that it would be fairly easy to write an assembly routine that uses one or more cog counters to read the knob. My starting goal for this project is simple set a counters frequency based on knob input. I may have to just give in and buy a standard 2-bit rotary encoder although my aim for this project was to use as many scavenged parts as possible. My ultimate goal is to build a solid state Tesla coil controller with the prop driving a set of gate driver IC's driving a gate drive transformer that in turn drives a fullbridge of 18n50 mosfets running on a variable dc bus from 0-~320v. The prop takes feedback from a set of current transformers for feedback and current limiting. First things first though gotta have some form of input. I would really like to get this encoder working but may have to just revert to a 3 small pushbutton up/down/select scheme. Appreciate the help and any thoughts\ideas for this project.

    Thanks again!
    -ADZ
  • whickerwhicker Posts: 749
    edited 2013-01-27 23:17
    I saw in the other thread that you added 3 pullup resistors to each input, instead of one on the other side of the encoder (which just wastes power).
    This is the correct answer and you should always do this for cases like this.

    When dealing with digital circuitry you have to drive in both directions. an input should either be at the level of the supply voltage (+5V, +3.3V, etc), or at ground level (0V). Never left floating. For hobbyist applications on microcontrollers, we just generally leave unused inputs floating, but in professional practice this is bad. It tends to waste power and amplify noise and send more noise into the adjacent circuits with all the almost-random toggling from 1 to 0 and back.

    I know industrial equipment (PLC I/O) acts differently, where you can connect one side of a mechanical switch to the voltage source, and the other side to the input. Works perfectly where if the switch is closed, the input is read as a 1 because CURRENT is flowing, otherwise 0 because no significant current is flowing. But not with digital circuitry. Digital circuitry uses VOLTAGE to determine the 1 or 0.
  • ADZ_916ADZ_916 Posts: 30
    edited 2013-01-28 01:08
    Got what i was looking for after adding a couple lines to the code that ChrisGadd posted. The attached schematic and spin file will read the encoder and display a value in serial terminal as well as reset the value to zero when the button is pressed, hopefully this will be helpfull to anyone else who runs into a similar encoder. Credit goes to ChrisGadd as most of the code is his.

    Capture5.jpg

    ABC_Encoder.spin
    I will continue to update as this project moves along.
    Thanks Again!
    -ADZ
    1024 x 550 - 57K
Sign In or Register to comment.