Quadrature Decoder Counter Problem

Hey guys,

I am using a quadrature decoder chip to decode the position of a 600PPR open collector outputs quadrature encoder. The reason I am using a quadrature decoder was because I was having some error in the position counter when interfacing directly with the arduino so I was hoping simplifying things down to simple up/down pulses provided by the decoder chip would increase the reliability of things on the arduino side.

A problem that I have run into (and I think resolved, but i'm not sure how) is that the position counter seems to work okay but will randomly jump to a whacky number. It usually likes to jump to 255 for some reason. After pondering and reading I saw that I did not have a resistor in place that adjusts the duration of the pulses provided by the chip to the arduino when placed between two legs of the chip. With absolutely no resistor in place, an open circuit in fact, I think the pulses were either too fast to be recognized by the arduino (is that possible?) or I was exhibiting some kind of behavior similar to when you don't add a pull down resistor to a digital input, and it fires randomly.

Not only was the counter messing up when I didn't have the resistor in place, but the arduino would randomly just stop working.

Anyway, I added a 1Mohm resistor which should have corrected the circuit and lengthened the pulses to the arduino to ~2-3us which I imagine is long enough if not too long.

I realize I rambled here, but is it possible to have a pulse too short for the arduino to pick up? Should things have still been working normally without having the resistor mentioned? Maybe it was a problem with my code?

Here is the datasheet for the quadrature decoder:
link

Here is my code:

volatile int counter=0;
void setup() 
{
Serial.begin(9600);
pinMode(2, INPUT);
pinMode(3, INPUT);
attachInterrupt(digitalPinToInterrupt(2), countup, FALLING);
attachInterrupt(digitalPinToInterrupt(3), countdn, FALLING);
}

void loop() 
{
Serial.println(counter);
delay(100);
}

void countup()
{
  counter++;
}
void countdn()
{
  counter--;
}

Soronemus:
I am using a quadrature decoder chip to decode the position of a 600PPR open collector outputs quadrature encoder. The reason I am using a quadrature decoder was because I was having some error in the position counter when interfacing directly with the arduino so I was hoping simplifying things down to simple up/down pulses provided by the decoder chip would increase the reliability of things on the arduino side.

OK, so you do it like that:

I'm unable to control the hardware I have with an Arduino sketch.
Solution: We need MORE HARDWARE!

Good idea: If simple hardware does not work, use more complex hardware and it might work!

But in any case, whether controlling simple or more complex hardware: When using "interrupt handling routines", you MUST handle interrupts and variables correctly, or it will not work and you get strange results.

Strange results from strange programming.

One problem in your code might be the strange handling of the "volatile counter" variable within your loop code.

This is a 2-byte variable, and if you access this variable in the loop, the value of one of the bytes might change WHILE YOU ACCESS the variable.

So this is WRONG CODE to access a volatile variable, which might change within an interrupt handler:

void loop() 
{
Serial.println(counter);
delay(100);
}

You must do "atomic read" and avoid that an interrupt can change one of the bytes while accessing the counter. One very common possibility would be:

  • disable interrupts
  • copy the contents of the volatile variable to a non-volatile variable
  • enable interrupts
  • then deal with the non-volatile copied contents only

Such like:

void loop() 
{
  noInterrupts();  // disable interrupts
  int nvCounter=counter; // copy volatile multi-byte variable contents while interrupts are disabled
  interrupts();  // enable interrupts again as soon as possible (or your system might run into standstill)
  Serial.println(nvCounter); // deal with the non-volatile copy of the variable
  delay(100);
}

Good luck!

And perhaps: LEARN SOME BASICS ABOUT INTERRUPT PROGRAMMING!

This is what you seem to have learned already:

  • when accessing variables from normal code and from interrupt handling code ==> declare 'volatile'
  • keep interrupt handling code short and fast

This is what you have to learn:

  • accessing mult-byte 'volatile' variables from normal code requires disabling interrupts while accessing

Thank you for the reply jurs! One of the other reasons I decided to use a quadrature decoder chip is that in the future I would like to be able to read three quadrature encoders at a time with a single arduino due. I figured if I put the three quadrature decoder chips on a prototype shield and use that it will greatly increase the simplicity of my interrupt service routines. Just attach an interrupt for the down and up counter pins of each chip instead of doing a bunch of if statements to compare the signal wires of the encoder. Hopefully that will allow me to read three encoders at once without having multiple cores or something. My motors aren't exactly going to be turning very fast (~1rps max).

You mentioned a problem with using a changing variable both inside and outside the ISR. I thought that was what marking a variable 'volatile' was intended to make acceptable? So I have to take additional precautions on top of that such as the code you posted above. Does that pose the risk of missing an encoder count while interrupts are disabled? I suppose I can adjust the value of the resistor to make the up/down pulses longer, and the arduino due is really fast compared to other arduinos, but can you think of any other precautions if say I wasn't using a due, but rather a slower uno?

open collector outputs quadrature encoder

AND

With absolutely no resistor in place, an open circuit in fact,

AND

pinMode(2, INPUT);
pinMode(3, INPUT);

????

Soronemus:
Does that pose the risk of missing an encoder count while interrupts are disabled?

Normally not. But it actually depends on

  • interrupt rate and
  • for how long you disable interrupts

If there is just one interrupt occurring while interrupts are disabled, the interrupt will be set "waiting", and the interrupt handler gets executed as soon as you enable the interrupts.So disabling interrupts for a short time just delays the interrupt handling for a few clock cycles, but the interrupt handler is executed. Just some clock cycles later.

But in case that you disable the interrupts for a longer time and then two (or more) interrupts would occur during the time while interrupts are disabled, you will lose at least one interrupt.

Vaclav:
open collector outputs quadrature encoder

AND

With absolutely no resistor in place, an open circuit in fact,

AND

pinMode(2, INPUT);
pinMode(3, INPUT);

????

Haha... I always get flack when I refer to the open collector nature of my encoder signal wires... on the one hand they are sinking current so that makes me want to say open collector inputs, but if I add a pull up resistor they behave like a normally closed I/O if I attach them to a digital input (hence pinMode(2, INPUT)). What is the correct nomenclature here??? I have always just called them open collector outputs since it doesn't sound right to say an encoder has an input to me.

That statement makes no sense. If they are open collector then they must have a pull-up, or use INPUT_PULLUP mode. If they are not open collector then what are you trying to say?

MorganS:
That statement makes no sense. If they are open collector then they must have a pull-up, or use INPUT_PULLUP mode. If they are not open collector then what are you trying to say?

The encoders have two open collector...er..signal wires. Do I say the encoder has two open collector outputs or do i say the encoder has two open collector inputs?

The encoder has 2 open collector sinking outputs.

You need to change:

void loop()
{
Serial.println(counter);
delay(100);
}

to

void loop()
{
  noInterrupts () ;  // read volatile with interrupts disabled to prevent garbage
  int val = counter ;
  interrupts () ;      // only disable interrupts for the bare minimum of time.
  Serial.println(val);
  delay(100);
}

Any volatile that is more than one byte must be protected in this way since the value is read from
memory one byte at a time and the ISR can run inbetween those reads...

The reason I am using a quadrature decoder was because I was having some error in the position counter when interfacing directly with the arduino

What was the problem?

MarkT:
What was the problem?

Small error would accumulate in the position counter. Here is a video I made which shows it.