help interfacing an external quadrature encoder/counter chip - Avago HTCL-2022

I would really appreciate some help to get an Avago HCTL-2022 chip working so I can accurately read a 1000p/r optical encoder. I have considered faster boards and such but I think a dedicated IC is best as pulses could get very fast with such a high resolution encoder on a fast stepper motor. Some boards have hardware quadrature encoders included in their architecture but the threads regarding setting those up seem much more complicated.

I have read the datasheet: https://media.digikey.com/pdf/Data%20Sheets/Avago%20PDFs/HCTL-2032,2022.pdf

And read a forum post: https://forum.arduino.cc/index.php?topic=60828.msg3721442#msg3721442 ,
and have implemented some code that was claimed to work and I am using an Arduino Mega (clone):

/* 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Reading decoder HCTL-2022 to count pulses from
an encoder using Arduino Mega 2560
Written by Hanna Hellman, 2016-07-08
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Arduino Pin       HCTL-2022 function
------------------------------------
DIG 9             /RST (active low)
DIG 11            SEL1
DIG 12            SEL2
DIG 13            /OE (active low)
DIG 22->29        Data pins D0->D7
+5V               VDD
GND               VSS
from encoder      CHA
from encoder      CHB
not connected     INDEX
not connected     U/D
not connected     TEST 
External clock    CLK
*/

// Global variable declaration
volatile long countData = 0; // 32-bit int (signed?) the count represents absolute position


void setup() {
  Serial.begin(9600);

  pinMode(9, OUTPUT);
  digitalWrite(9, LOW); // /RST resets the internal counter between runs
  delay(10);
  digitalWrite(9, HIGH); // Stay high for rest of the run
  
  // Set all pins in PORTA (digital pins 22->29 on the Mega) as input pins
  for(int i = 22; i<30; i++) {
    pinMode(i, INPUT);
  }
  // Set pins 5,6,7 in PORT B (digital pins 11,12,13 on the Mega) as output pins
  for(int j = 11; j<14; j++) {
    pinMode(j, OUTPUT);
    digitalWrite(j, LOW);
    
  }

}
/*-------------------------------------------------
  -------------------Functions---------------------
  -------------------------------------------------*/
byte getMSB(){
/*Get stable data for the most significant byte of countData*/

  byte MSBold = PINA;       // read datapins D0->D7 and store in MSBold
  byte MSBnew = PINA;       // read again immediatly after to assure stable data
  if (MSBnew == MSBold){ 
    byte MSBresult = MSBnew;
    return MSBresult;  
  }
  else getMSB();
}

byte getSecond(){
/*Get stable data for the 2nd byte of countData*/
  byte secondOld = PINA;       // read datapins D0->D7 and store in secondOld
  byte secondNew = PINA;       // read again immediatly after to assure stable data
  if (secondNew == secondOld){ 
    byte secondResult = secondNew;
    return secondResult;  
  }
  else getSecond();
}

byte getThird(){
/*Get stable data for the 3rd byte of countData*/
  byte thirdOld = PINA;       // read datapins D0->D7 and store in thirdOld
  byte thirdNew = PINA;       // read again immediatly after to assure stable data
  if (thirdNew == thirdOld){ 
    byte thirdResult = thirdNew;
    return thirdResult;  
  }
  else getThird();
}

byte getLSB(){
/*Get stable data for the least significant byte of countData*/
  byte LSBold = PINA;       // read datapins D0->D7 and store in LSBold
  byte LSBnew = PINA;       // read again immediatly after to assure stable data
  if (LSBnew == LSBold){  
    byte LSBresult = LSBnew;
    return LSBresult;  
  }
  else getLSB();
}

long mergeFunc(byte MSBresult, byte secondResult, byte thirdResult, byte LSBresult){
/*Merges the 4 bytes returning one 32-bit variable called countData*/
  long tempVar = 0;
  tempVar |= ((long) MSBresult << 24) | ((long) secondResult << 16) | ((long) thirdResult << 8) | ((long) LSBresult << 0);
  countData = tempVar;
  return countData;
}



void loop() {

  digitalWrite(13, HIGH); // Set OE to HIGH (disable)
  delay(25);  // need a better way
  
  digitalWrite(11, LOW);
  digitalWrite(12, HIGH); // SEL1 = 0 and SEL2 = 1
  
  digitalWrite(13, LOW); // Set OE to LOW (enable)
  byte MSBresult = getMSB();

  
  digitalWrite(11, HIGH);
  digitalWrite(12, HIGH); // SEL1 = 1 and SEL2 = 1
  byte secondResult = getSecond();
  
  digitalWrite(11, LOW);
  digitalWrite(12, LOW); // SEL1 = 0 and SEL2 = 0
  byte thirdResult = getThird();
  
  digitalWrite(11, HIGH);
  digitalWrite(12, LOW); // SEL1 = 1 and SEL2 = 0
  byte LSBresult = getLSB();

  digitalWrite(13, HIGH); // Set OE to HIGH (disable)
  delay(25);
  
  countData = mergeFunc(MSBresult, secondResult, thirdResult, LSBresult);
  Serial.println("Counter: ");
  Serial.println(countData);
  Serial.println("\n");


  }
  
  /*
  Algorithm:
 
  disable OE (high)
  wait 25 ms
  set sel1= 0 and sel2 = 1 to read MSB
  enable OE (low)

  getMSB()
  set sel1 =1 and sel2=1
  get2nd()
  set sel1 = 0 and sel2 = 0
  get3rd()
  set sel1 = 1 and sel2 = 0
  getLSB()

  disable OE (high)
  wait 25 ms
  */

I have hooked up the ‘external clock’ using an Arduino Nano running the below code which is claimed to produce a 1MHz clock signal.

//Use Timer/Counter1 to generate a 1MHz square wave on Arduino pin 9.
//J.Christensen 27Apr2012

//**********from https://forum.arduino.cc/index.php?topic=103370.0



void setup(void)
{
    DDRB = _BV(DDB1);                  //set OC1A/PB1 as output (Arduino pin D9, DIP pin 15)
    TCCR1A = _BV(COM1A0);              //toggle OC1A on compare match
    OCR1A = 7;                         //top value for counter
    TCCR1B = _BV(WGM12) | _BV(CS10);   //CTC mode, prescaler clock/1
}

void loop(void)
{
}

Now I am only getting an output of 0 regardless of turning the encoder or not. If the problem is in the coding it could be something to do with parsing the binary data which seems to be a concern in this post:

If the problem is in the circuit, perhaps I have not wired correctly of maybe some of the unused pins need to be grounded? Do I need any pull downs or pull ups?

Perhaps the Arduino Nano producing the clock signal is not working, could the jumpers and breadboard be attenuating or interfering with the 1MHz pulse?

Can anyone help me to debug this?

I have attached a schematic for my circuitry, and a photo of the breadboard.

I would be extremely helpful if someone can help me interface this chip or suggest how to debug this?

It is for a prototype I am making on a tight budget that will show an invention I have for zero emissions transport (currently I have not earned a penny from this). Final application if for position control of linear motion.

If no one can help and I can’t figure out how to debug it, are there many other options? Builtin quadrature decoders on the Due or Teensy 3.x seem much more complicated to set up that this IC, and I could waste too much time. My last resort might be getting a very fast dev. board and using a software method of reading the encoder using interrupts and hoping that it is fast enough (it worked with Arduino Nano but was way to slow and missed many counts when not turned smooth and slow, not something a stepper will be doing).

Anyway thanks for looking,
Yan


edit May-11-18

Now working! Original code seems to have no problems after initial testing with a fixed circuit. The problem was not tieing the open-collector encoder outputs high . Working circuit shown in later post on May-11-18

Hi,
Ops circuit.


What is your 5V supply?
Feeding 5V into the Vin pin will not power the Nano or Mega properly.
With 5V going into Vin, you are powering an internal 5V linear regulator, and it will not function properly with 5V input.

You need to connect your 5V supply to the Nano and Mega 5V pin.

Are you using a 2020 or a 2022?

Tom... :slight_smile:

Avago HCTL-2020datasheet.pdf (167 KB)

Hi,
2020 pinout
2020pinout.jpg

2022 pinout
2022pinout.jpg

Tom.. :slight_smile:

2022pinout.jpg

2020pinout.jpg

Hi, thanks for posting the images inline, will do that in future.

I’m not sure that is the problem only because originally I was using the +5V pins and last minute switched to the Vin in case it changed everything. But now I know what the +5V is correct!

I was using a phone power bank for the 5V, but I’m not sure it was required because the Mega was hooked up to USB for the serial monitor.

Should I ditch the power-bank and just link [Nano 5v <----- Mega 5v] and keep common ground?

I have been studying the datasheet for hours, I don’t understand the timing stuff but at the end there is a code example so perhaps I might have an attempt at coding from scratch using that as a template? But first I’m going to try and debug what I have because I don’t have masses of time because of all the other stuff that needs doing.

I will receive an oscilloscope in this week to test I’m getting the clock signal.

Any other ideas? Should I try pulling down or pull up any of the unused pins? Will this need a resistor not to fry the chip?

I have a suspicion that the Index pin should not have been left floating.

Thanks
Yan

Oooops it is actually an Avago HCTL-2022*.* Corrected the original post and title

Hi, What is the encoder that your are using? A scope will help.

Tom... :)

OMRON-E6B2-CWZ6C 1000P/R (very affordable off eBay from China :) )

I have got it to work using interrupts on the Nano.

I think my first test should be to check all the output pins are displaying changing bits or not, so I should will try and read the least significant byte and see if it changes on rotating the encoder.

I think the method of reading entire 8-bit ports has confused me a little as I'm quite new at embedded programming. I don't see why I can't just read all 8 bits one by one, more code to write but speed is not an issue as the outputs are supposed to latch for reading.

I will update as soon as I have any results (might be the weekend).

Oscilloscope arrived :slight_smile:

The Nano is producing a good 1Mhz pulse of 5V. I am now using USB as the only power source and have joined GND and +5V of the two boards.

Does this look OK? There are some spikes but that's OK right?)

Unfortunately I still get nothing.

My next check will be to add a third Nano (thank god they're cheap from eBay) to begin from scratch and try and read the 8 output bits and directly print to the serial monitor.

I noticed the Nano pins like to jump out of the breadboard and mess up the signal, but now I am aware of that and I'm still getting no readings.

I was sitting and thinking about this circuit and it occurred to me I have probably used the resistors wrong and if you look at my circuit diagram, am I correct in thinking this will never work because i am dropping most of the 5V over the resistors i put on the encoder outputs?

I have now googled what an open collector output is and now things are much more clear. I have looked at the encoder datasheet and Avago datasheet again and it looks like I need to change the wiring.

I probably need the weekend to re-configure and report back but here are some snippets from the datasheets:

Is this thread going to go dead because I am taking some time to debug the circuit?

Hi,
What signals are you getting out of the encoder?

Tom… :slight_smile:

I put the scope across the encoder outputs and go a lot of noise with the impressions of some square(ish) waves when rotating. More importantly, the peak voltage was less than a volt, so that made me realise the circuit is wrong because I didn't understand the term open-collector.

I'll report back soon, really busy with work at the moment.

Full schematic and code will be posted when it finally works :)

Hi,
Yes you need those 2k7 resistors.


Open collector means you need those resistors to provide a current flow through the output transistors in the encoder.
Tom... :slight_smile:

It appears to work!!!

Speed test pending, but it should be fine with the 1Mhz clock and the IC keeping its own memory.

Reading a high resolution (an affordable) rotary encoders at useful speeds is going to be super useful!

The key was understanding the use of open-collector NPN outputs.

todo:

check speed and repeatability
add index functionality
double check the inputs aren't inverted either in code or circuit and causing any errors
produce a stable clock pulse from the Mega, so extra Nano is not needed

working circuit: