strange rotary encoder

hi,
I have some of those cheap chinese rotary encoders with the push button from ebay. When I use them with the Encoder lib they work fine.

BUT:
for every step it counts always exactly 4/-4 and not just 1/-1. And the example code shows each of the count steps in the serial terminal. If it was just the bouncing of the pin, it would count in random steps. A delay in the main loop doesn't help. I then see each step delayed.......

/* Encoder Library - Basic Example
 * http://www.pjrc.com/teensy/td_libs_Encoder.html
 *
 * This example code is in the public domain.
 */

#include <Encoder.h>

// Change these two numbers to the pins connected to your encoder.
//   Best Performance: both pins have interrupt capability
//   Good Performance: only the first pin has interrupt capability
//   Low Performance:  neither pin has interrupt capability
Encoder myEnc(5, 6);
//   avoid using pins with LEDs attached

void setup() {
  Serial.begin(9600);
  Serial.println("Basic Encoder Test:");
}

long oldPosition  = -999;

void loop() {
  long newPosition = myEnc.read();
  if (newPosition != oldPosition) {
    oldPosition = newPosition;
    Serial.println(newPosition);
  }
}

I connected the common pin of the encoder to ground and the two signals directly to the input with the internal pullup.

thanks and regards
Robert

You need to study the documentation for the library more carefully. There is a strong hint here:

//   Best Performance: both pins have interrupt capability
//   Good Performance: only the first pin has interrupt capability
//   Low Performance:  neither pin has interrupt capability

Hi,
but that doesn't change the result. All pins I use have them.

What doesn't change the result?
Have you read the documentation?

Yes i have.
I used ENCODER_USE_INTERRUPTS and ENCODER_OPTIMIZE_INTERRUPTS separately and together. Nothing changed. Or what Do you mean?

From the page describing the library:

The Encoder library monitors the 2 pins and updates a count of the relative change in position. The library updates its count at each change, which is often called 4X counting, since 4 counts are available for each physical mark or hole in the encoder hardware.

Two pins = 4 interrupts on change.

bsunisol:
BUT:
for every step it counts always exactly 4/-4 and not just 1/-1. And the example code shows each of the count steps in the serial terminal. If it was just the bouncing of the pin, it would count in random steps. A delay in the main loop doesn't help. I then see each step delayed.......

Looks like your library is counting "quarter steps" while you want to count "full steps".

So either look in your library how you can configure the library to count "full steps" instead of "quarter steps".

Or use another library.

Ok thanks guys. I'll see what I can do.

Hello again,
now I got it with that code:

#define CW    0b10000111
#define CCW   0b01001011
byte rotation=0;

IntervalTimer t;
void setup( void ) {
  Serial.begin( 9600 );
  
  pinMode( 5, INPUT_PULLUP );
  pinMode( 6, INPUT_PULLUP );
  
  t.begin( encode_input, 250 );
}

void loop( void ) {
}

void encode_input( void ) {
  cli();
  byte pins = 0 | digitalRead(5) << 1 | digitalRead(6);
  
  // no change?
  if( (rotation&0x03) == pins ) {
    sei();
    return;
  }
  
  // new position? ready?
  switch( rotation ) {
    case CW:
      Serial.println( '+' );
      rotation = 0;
    break;
    case CCW:
      Serial.println( '-' );
      rotation = 0;
    break;
    default:
      rotation = (rotation<<2) | pins;
  }
  
  sei();
}

When I change the direction from + to - it does one step in the last direction.....

Am I missing something?

bsunisol:
Am I missing something?

You are doing no good with interrupt programming.

1st problem: Most likely the 'rotation' variable is to be used not only in the interrupt code but also in normal code. So the variable has to be declared volatile:

volatile byte rotation=0;

2nd problem: Within an interupt handler, interrupts are automatically disabled and will be automatically enabled at end. So the cli(); and sei(); statements within the interrupt handling code are wrong.

3rd problem: You better not use functions within interrupt handlers, that are not safe for interrupts. Such like "Serial.print". The Serial.print command itself is depending on interrupts, and if the Serial output buffer is full, this function will do "busy waiting" until the waiting characters can be sent. But while interrupts are disabled, not a single character can be sent. So if it should happen, that the outgoing characters of "Serial.println( '-' );" should not fit into the Serial output buffer, your program will run into a dead-lock condition: Your interrupt will last forever, while waiting for the Serial output buffer to send some characters from the output buffer.

Does the code work that you posted?
Does the code work without any glitches?
Up to which rotation speed does it work without any glitches?

Here is some code that should be working for one rotary encoder (using TIMER2 on Atmega controllers):

// rotary encoder demo by 'jurs' for Arduino Forum
#define BAUDRATE 115200L // serial baud rate

struct rotary_t {byte pinA; byte pinB; int count;};
rotary_t myEncoder={2,3}; // define rotary encoder A/B pins
// connect rotary encoder middle pin to GND

volatile int8_t count_ISR;

void startTimer2()  // start TIMER2 interrupts
{ // will use TIMER2 at 1000 interrupts per second
  noInterrupts();
  TCCR2B = (1<<WGM22) | (1<<CS22)  | (1<<CS20);
  TCCR2A = (1<<WGM21);
  OCR2A = 124;
  TIMSK2 = (1<<OCIE2A); // enable Timer 2 interrupts
  interrupts();
}

boolean updateEncoder()
{ // read the 'volatile' ISR variables and copy them into normal variables
  boolean changeState=false;
  if (count_ISR!=0)
  {
    changeState=true;
    noInterrupts();
    myEncoder.count+= count_ISR;
    count_ISR=0;
    interrupts();
  }
  return changeState;
}

ISR(TIMER2_COMPA_vect)  // handling of TIMER2 interrupts
{
  static byte oldState;
  byte state=oldState;
  int8_t result=0;
  state= state<<2 | (byte)digitalRead(myEncoder.pinA)<<1 | (byte)digitalRead(myEncoder.pinB); 
  state= state & 0xF;   // keep only the lower 4 bits
  /* // next two lines would be code to read 'quarter steps'
  if (state==0b0001 || state==0b0111 || state==0b1110 || state==0b1000) result= -1;
  else if (state==0b0010 || state==0b1011 || state==0b1101 || state==0b0100) result= 1;
  */
  // next two lines is code to read 'full steps'
  if (state==0b0001) result= -1;
  else if (state==0b0010) result= 1;
  oldState= state;
  count_ISR+= result;
}

void setup() {
  Serial.begin(BAUDRATE);
  Serial.println();
  Serial.println("Good night and good luck!"); // print some Test-Message at beginning
  pinMode(myEncoder.pinA, INPUT_PULLUP);
  pinMode(myEncoder.pinB, INPUT_PULLUP);
  startTimer2();   // start the timer interrupt
  delay(3);        // initialize by running interrupt handler at least once
  updateEncoder(); // read once after interrupt has run
  myEncoder.count=0; // set initial 'count' to 0
}

void loop() {
  if (updateEncoder()) Serial.println(myEncoder.count);
}

sorry i missed to say that it is just the pre-stage to be a class. the global variables will be elimanted with a local one and is not for public use.

and the Serial.print is just for this stage to see what is happening. when it is ready there is just an addition or subtraction.

Where do I use Interrupts? I use the timer like you?

It is certainly a bad idea to use interrupts with these encoders as you want to use a debounce system.

The question is - what are you calling a "step"? Do you mean the detent on the encoder? That may indeed correspond to a whole four transitions. In which case the code is perfectly correctly counting transitions - as you would expect it to.

bsunisol:
Where do I use Interrupts? I use the timer like you?

Here you activate a callback function for some timer interrupt:

t.begin( encode_input, 250 );

Reading the documentation of the library that you are using might help in understanding.

Although you posted just a fragment of code, I'm guessing from the class name that you are using this library:

Or maybe some other library code that works similar.

At least you are assigning a callback function to an interrupt timer: The timer interrupt will call your assigned function "encode_input" when the timer interrupt fires. So your function actually is executing during an interrupt.

Paul__B:
It is certainly a bad idea to use interrupts with these encoders as you want to use a debounce system.

I fully disagree with that.

While is certainly a bad idea to use hardware interrupts with these encoders, it is surely a very good idea sampling the encoder with timer interrupts.

jurs:
Here you activate a callback function for some timer interrupt:

t.begin( encode_input, 250 );

Reading the documentation of the library that you are using might help in understanding.

Although you posted just a fragment of code, I'm guessing from the class name that you are using this library:
GitHub - loglow/IntervalTimer: Timer library for Teensy 3.0
Or maybe some other library code that works similar.

At least you are assigning a callback function to an interrupt timer: The timer interrupt will call your assigned function "encode_input" when the timer interrupt fires. So your function actually is executing during an interrupt.

No that is the complete code. I use the IntervalTimer that is presented on the teensy page. I don't understand the difference between your timer interrupt and mine.

bsunisol:
No that is the complete code. I use the IntervalTimer that is presented on the teensy page. I don't understand the difference between your timer interrupt and mine.

The code I posted in reply #9 is only for Atmega based Arduino boards, such like Atmega328, Atmega2560 and ATmega32u4 controllers.

Which type of board are you using?
Is it an Arduino board with Atmega controller?
Or a non-Arduino board with different controller?
Which board/controller do you want code for?

At the moment I use a teensy 3.0 because my board is not ready yet. This will be an atmega328p.

As soon as the code works like it is supposed, it will be put in a class and be used in my project.

bsunisol:
At the moment I use a teensy 3.0 because my board is not ready yet. This will be an atmega328p.

As soon as the code works like it is supposed, it will be put in a class and be used in my project.

As Teensy 3.0 is based on some 32 bit ARM processor (MK20DX128 32 bit ARM Cortex-M4, 48 MHz) and the other board is based on Atmega328, I think you prefer a code that can run on both boards.

So possibly using the Delay and Timing Functions library is not the worst idea, to get compatible code for MK20DX128 as well as for Atmega328 controllers.

I'll look it up.

P.S.: Just looked for the download of the "IntervalTimer" library, but I could not detect ist. Is this library only part of the "Teensyduino software add-on for the Arduino", is it only compatible to the MK20DX128 controller and not available for download, install and usage on Atmega328 controllers?

In that case, you cannot have the same source code for MK20DX128 and for Atmega328 controllers.

For code working on Atmega328 in "full step counting", see my reply #9.

P.P.S.: For questions related to MK20DX128 / Teensy 3.0 microcontrollers perhaps better ask in a forum where people might better know that type of microcontroller, like https://forum.pjrc.com/forum.php