20 Detent Rotary Encoder - Unsingned Int question

Hello everyone. I'm an older programmer (punched card FORTRAN & PL/I ) who is learning C++ and the ArduinoI have what is probably a very simple solution. However I am having trouble with this one.

I have a 20 detent rotary encoder that I want to use to indicate an "absolute" position ( ie. position 0, position 1,...through 19). The thing is pretty noisy and I found a simple debounce routine in the Arduino Playground Arduino Playground - RotaryEncoders that provides an accurate indicator of state change and position for this encoder on my Uno and Mega2560. The code (below) uses unsigned integers for the encoder positions so it ranges from 0 - 65535 and rolls over.

Question is...what's the best way to modify this code to reflect a finite set of positions; from 0 through 19, and provides accurate rollover when turning the shaft left or right. maybe it's best if I start from scratch but I through I would at least ask since this code seems to produce accurate results for me.

Thanks in advance.

Art

Code -------------------------

/* interrupt routine for Rotary Encoders
   tested with Noble RE0124PVB 17.7FINB-24 http://www.nobleusa.com/pdf/xre.pdf - available at pollin.de
   and a few others, seems pretty universal

   The average rotary encoder has three pins, seen from front: A C B
   Clockwise rotation A(on)->B(on)->A(off)->B(off)
   CounterCW rotation B(on)->A(on)->B(off)->A(off)

   and may be a push switch with another two pins, pulled low at pin 8 in this case
   raf@synapps.de 20120107

*/

// usually the rotary encoders three pins have the ground pin in the middle
enum PinAssignments {
  encoderPinA = 2,   // rigth
  encoderPinB = 3,   // left
  clearButton = 8    // another two pins
};

volatile unsigned int encoderPos = 0;  // a counter for the dial
unsigned int lastReportedPos = 1;   // change management
static boolean rotating=false;      // debounce management

// interrupt service routine vars
boolean A_set = false;              
boolean B_set = false;


void setup() {

  pinMode(encoderPinA, INPUT); 
  pinMode(encoderPinB, INPUT); 
  pinMode(clearButton, INPUT);
 // turn on pullup resistors
  digitalWrite(encoderPinA, HIGH);
  digitalWrite(encoderPinB, HIGH);
  digitalWrite(clearButton, HIGH);

// encoder pin on interrupt 0 (pin 2)
  attachInterrupt(0, doEncoderA, CHANGE);
// encoder pin on interrupt 1 (pin 3)
  attachInterrupt(1, doEncoderB, CHANGE);

  Serial.begin(9600);  // output
}

// main loop, work is done by interrupt service routines, this one only prints stuff
void loop() { 
  rotating = true;  // reset the debouncer

  if (lastReportedPos != encoderPos) {
    Serial.print("Index:");
    Serial.println(encoderPos, DEC);
    lastReportedPos = encoderPos;
  }
  if (digitalRead(clearButton) == LOW )  {
    encoderPos = 0;
  }
}

// Interrupt on A changing state
void doEncoderA(){
  // debounce
  if ( rotating ) delay (1);  // wait a little until the bouncing is done

  // Test transition, did things really change? 
  if( digitalRead(encoderPinA) != A_set ) {  // debounce once more
    A_set = !A_set;

    // adjust counter + if A leads B
    if ( A_set && !B_set ) 
      encoderPos += 1;

    rotating = false;  // no more debouncing until loop() hits again
  }
}

// Interrupt on B changing state, same as A above
void doEncoderB(){
  if ( rotating ) delay (1);
  if( digitalRead(encoderPinB) != B_set ) {
    B_set = !B_set;
    //  adjust counter - 1 if B leads A
    if( B_set && !A_set ) 
      encoderPos -= 1;

    rotating = false;
  }
}

You have delay() in your ISRs. That absolutely will NOT work.

since this code seems to produce accurate results for me.

As in you can tell each time it is turned? What, exactly, does this mean? I would expect that if you were getting accurate results, you would not have an issue.

costigaj:
Hello everyone. I'm an older programmer (punched card FORTRAN & PL/I ) who is learning C++ and the ArduinoI have what is probably a very simple solution. However I am having trouble with this one.

I have a 20 detent rotary encoder that I want to use to indicate an "absolute" position ( ie. position 0, position 1,...through 19). The thing is pretty noisy and I found a simple debounce routine in the Arduino Playground Arduino Playground - HomePage that provides an accurate indicator of state change and position for this encoder on my Uno and Mega2560. The code (below) uses unsigned integers for the encoder positions so it ranges from 0 - 65535 and rolls over.

Need more details about your encoder. The sample code you found isn't for an absolute rotary encoder. Also, because you mention 'detent', it might be more of a rotary dip switch type encoder. How many pins does it have? Do you have a data sheet for it? I'm wondering if it is 5 bit BCD (which would require at minimum 6 pins).

I'd change this:

volatile unsigned int encoderPos = 0;  // a counter for the dial

Into this:

int encoderPos = 0;  // a counter for the dial

Then i would check, and in case of a roll over set it back to the last value you like it to have.
Something like:

if (endocerPos < 0) encoderPos = 0;
if (encoderPos > 19) encoderPos = 19;

These 2 lines would be right before the respective:

    rotating = false;

This allows you to turn the encoder up to your value 19 and keep turning but without increasing
And the same for turning down to 0 and stay there.
But i'm not an advanced programmer so i don't know if that is the best solution for you.
And i'm assuming you have what you told us you have and not some other switch.

Edit: typo

PaulS. Thanks for the reply.

PaulS:
You have delay() in your ISRs. That absolutely will NOT work.

When I saw this in the code, I thought the same thing but given the encoder I have has 20 positions that "click" it actually does work.

since this code seems to produce accurate results for me.

As in you can tell each time it is turned? What, exactly, does this mean? I would expect that if you were getting accurate results, you would not have an issue.

Correct to your first question. It records a state change with each click but it rolls over at 65535. So it's "accurate" with regards to changes in state but not the limits to which I want it to opeate (only 0-19)

Sembazuru

Thanks for the repoly

[/quote]

Need more details about your encoder. The sample code you found isn't for an absolute rotary encoder. Also, because you mention 'detent', it might be more of a rotary dip switch type encoder. How many pins does it have? Do you have a data sheet for it? I'm wondering if it is 5 bit BCD (which would require at minimum 6 pins).

[/quote]

Encoder is an inexpensive one, probably as you say, more of the rotary dip switch. It has 20 distinct "clicks" over 360 degrees. It has 5 pins, three for the encoder and two for the switch that is part of the shaft (which I am not using). No data sheet.

MAS3

Thanks for the reply.

MAS3:
I'd change this:

volatile unsigned int encoderPos = 0;  // a counter for the dial

Into this:

int encoderPos = 0;  // a counter for the dial

Then i would check, and in case of a roll over set it back to the last value you like it to have.
Something like:

if (endocerPos < 0) encoderPos = 0;

if (encoderPos > 19) encoderPos = 19;




These 2 lines would be right before the respective:


rotating = false;




This allows you to turn the encoder up to your value 19 and keep turning but without increasing 
And the same for turning down to 0 and stay there.
But i'm not an advanced programmer so i don't know if that is the best solution for you.
And i'm assuming you have what you told us you have and not some other switch.

Edit: typo

If you want it to roll over from 19 to 0 and vice versa, you'd have to put in:

if (endocerPos < 0) encoderPos = 19;
if (encoderPos > 19) encoderPos = 0;

Instead.

costigaj:
Sembazuru

Thanks for the repoly

Need more details about your encoder. The sample code you found isn't for an absolute rotary encoder. Also, because you mention 'detent', it might be more of a rotary dip switch type encoder. How many pins does it have? Do you have a data sheet for it? I'm wondering if it is 5 bit BCD (which would require at minimum 6 pins).

Encoder is an inexpensive one, probably as you say, more of the rotary dip switch. It has 20 distinct "clicks" over 360 degrees. It has 5 pins, three for the encoder and two for the switch that is part of the shaft (which I am not using). No data sheet.

Well, with that description (particularily the bit about 3 pins) is actually making me think this is actually a quadrature encoder which is what the code you found is for. Absolute encoders tell you with 1 reading what angle (well angle range) the shaft is currently at, and a second reading can tell you if it has moved (i.e. the reading gives a different number). Comparing the second reading's number to the known sequence can tell you what direction it moved and how far, and can be self-correcting if you miss reading steps.

Quadrature encoders (which are one type of incremental encoder) can't tell you what angle the shaft is, and require 2 readings to tell you that a rotation has occurred and in what direction. If you miss reading a step you can get lost, which is why so many code examples use interrupts to catch encoders moving. (And why in one of my projects, where I was using interrupts for something else, I ended up using a quadrature decoder IC (specifically in my case, an Avago HTCL-2022) to count the movement of a quadrature encoder. I couldn't find a similar chip with some sort of serial interface, so I just connected it to an I2C I/O expander to cut the pin-count at my UNO down.)