Incremental encoder pulsating too fast? Voltage too weak? Is Santa real?

Hello guys. I hope I'm not beating a dead horse here, but it seems I've run out of options from what I could google. I'm having problems using an incremental encoder to derive the total angular displacement with its signal. My hypothesis is that the sampling frequency available in the Arduino is just too low - but that doesn't make a lot of sense to me since a lot of people use incremental encoders for this purpose. I will walk you through my problem and the data I have so far.

With this code, nicely made available by Dejan,

/*     Arduino Rotary Encoder Tutorial
 *      
 *  by Dejan Nedelkovski, www.HowToMechatronics.com
 *  
 */
 
 #define outputA 11
 #define outputB 13
 int counter = 0; 
 int aState;
 int aLastState; 

void setPwmFrequency(int pin, int divisor) {
  byte mode;
  if(pin == 5 || pin == 6 || pin == 9 || pin == 10) {
    switch(divisor) {
      case 1: mode = 0x01; break;
      case 8: mode = 0x02; break;
      case 64: mode = 0x03; break;
      case 256: mode = 0x04; break;
      case 1024: mode = 0x05; break;
      default: return;
    }
    if(pin == 5 || pin == 6) {
      TCCR0B = TCCR0B & 0b11111000 | mode;
    } else {
      TCCR1B = TCCR1B & 0b11111000 | mode;
    }
  } 
}


   
 void setup() { 
   pinMode (outputA,INPUT);
   pinMode (outputB,INPUT);

      // Maximizing sampling frequency for PWM signal inputs
   setPwmFrequency(11, 1);
   setPwmFrequency(13, 1);
   
   // Possible baudrates = 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, or 115200.;
   Serial.begin (600);
   // Reads the initial state of the outputA
   aLastState = digitalRead(outputA);   
 } 
 void loop() { 
   aState = digitalRead(outputA); // Reads the "current" state of the outputA
   // If the previous and the current state of the outputA are different, that means a Pulse has occured
   if (aState != aLastState){     
     // If the outputB state is different to the outputA state, that means the encoder is rotating clockwise
     if (digitalRead(outputB) != aState) { 
       counter ++;
     } else {
       counter --;
     }
     //Serial.print("Position: ");
     //Serial.println(counter);
     Serial.println(counter);
     Serial.print(" ");

   } 
   aLastState = aState; // Updates the previous state of the outputA with the current state
 }
  • along with some few modifications to visualize data in a different form - I was able to achieve these results by turning the incremental encoder axis:

Of course I didn't do that exact curve with my hand. In the first samples of this graph I was, in fact, turning the axis slower. In the second half, I was trying to turn the axis faster. The encoder I am using has 600p/r and is, as I said, an incremental encoder, exactly like this one. This is not the link I bought it from, though. I've tested this same algorithm using the PWM inputs of an Arduino Leonardo 5-6, 9-10 and 11-13 (although this last one was a very quick test), and the results were about the same: whenever I would turn faster the movement would simply not be sensed - turning slowly I would get some barely useful results, though rather noisy still.

Using this code by David Cuartielles:

/*
  Analog Input, but with Serial Plotter!
 Demonstrates analog input by reading an analog sensor on analog pin 0 and
 turning on and off a light emitting diode(LED)  connected to digital pin 13.
 The amount of time the LED will be on and off depends on
 the value obtained by analogRead().
 
 The circuit:
 * Potentiometer attached to analog input 0
 * center pin of the potentiometer to the analog pin
 * one side pin (either one) to ground
 * the other side pin to +5V
 * LED anode (long leg) attached to digital output 13
 * LED cathode (short leg) attached to ground
 
 * Note: because most Arduinos have a built-in LED attached
 to pin 13 on the board, the LED is optional.
 
 
 Created by David Cuartielles
 modified 30 Aug 2011
 By Tom Igoe
 
 This example code is in the public domain.
 
 http://www.arduino.cc/en/Tutorial/AnalogInput
 
 */
 
int sensorPin = A0;    // select the input pin for the potentiometer
int ledPin = 13;      // select the pin for the LED
int sensorValue = 0;  // variable to store the value coming from the sensor
 
void setup() {
  // declare the ledPin as an OUTPUT:
  pinMode(ledPin, OUTPUT);
  // begin the serial monitor @ 9600 baud
  Serial.begin(9600);
}
 
void loop() {
  // read the value from the sensor:
  sensorValue = analogRead(sensorPin);
 
  Serial.println(sensorValue);
  Serial.print(" ");
 
  //delay(20);
}

... I was able to get a sense of how exactly The A.Leonardo was receiving the signal through an analog port. The code above has been slightly changed relative to its original version for an easier data acquisition process. The results from the Monitor Serial dialogue were imported into MATLAB, so I could see better what was going on. Again I twisted the axis fast and then slowly (something between 15deg/sec and 1deg/sec respectively) - so I could see the results. At first, I had these results:

The horizontal axis is the sample number and the vertical axis is the 10bit digital sensed input. As you might have guessed, the first half of these samples were taken by turning the encoder relatively fast, while the second half took place in a slower turn speed. Zooming in around the region of the 31000th sample, you can see this:

Remember the end of the scale would be 1023, where I was supposed to have the "on" signal characteristic from an encoder signal. These things give me the feel that I am not working with due sampling frequency. (How) Can I solve that? I know these "time" plots were taken with the analog inputs and, of course, not the PWM ones - but I guess it's worth it to gather a feel of what's going on.

EDIT: I do know there are some forms with which you can change the PWM sampling frequency, but I didn't change these - so I assume I'm working with the maximum Fs already.

A little thing about the wiring: this encoder doesn't have a Z-phase - only the A and B wires. The red wire (Vcc) was plugged in the 5V supply from Arduino; the black wire (GND) was plugged into the GND port, right next to the 5V supply; the green and white wires were plugged in the ports I mentioned earlier, for testing. I'm not using any resistor or additional component whatsoever on this chain. The encoder is "straight through" from/to the Arduino.

While I write this I get the feeling that this might have something to do with the supply voltage as well. The original specification is that the encoder is meant to be used with voltages from 5V and up to 24V (or 12V, I don't recall exactly). Thus, maybe the supply is too weak to have a proper good signal at high speeds/frequencies?! This might be true since the signal at the last image is considerably continuous at these waveforms. There are around 50 to 100 samples at each of those waveforms when the signal is not at 0V - so maybe sampling is not the problem either.

At the same time, I worry that if I increase the voltage too much I might increase the current to dangerous levels being sent to the Leonardo. EDIT: Might as well place a resistor before the PWM inputs there then?

Anyway, sorry for this bible. Thanks for the help in advance.

I've made some edits on the original post. If something didn't seem clear before, try reading again. I highlighted some important changes in green too.

P,

The encoders have open collector outputs. Do you have pullup resistors on the Arduino inputs from the encoders? You could use the internal pullups.

Perhaps this will help: Encoder tear down and analysis

At least they drew a schematic.

Paul