help with avago 2022 for quadrature shield

hello all-

I am trying to build a high speed quadrature shield using 2 Avago 2022 ICs. I was under the impression that the 2022s communicated via i2c, however now looking at the spec sheet that looks not to be the case. I am sure I am being a bit dense here (it's been a while since school), but I'm totally unclear on how I am supposed to communicate with this thing. It has a clock input and inputs for the quadrature, I just can't figure out how I am supposed to get data out of this thing. the spec refers to an 8-bit tri-state buffer, but to be honest I am a bit clueless.

I was hoping someone could point me in the right direction so I can get this thing wired up properly and communicate using the proper protocol. Spec sheet is attached. Using an arduino mega for reference.

thanks!

AV02-0096EN.pdf (384 KB)

How about a link to the Avago 2022 and/or the data sheet for the chip?

http://datasheet.octopart.com/HCTL-2022-Avago-datasheet-500438.pdf

You pull the /EN line low, use the SEL1 and SEL2 pins (4 and 17) to select which byte of the 32-bit counter to read, read all four bytes eight bits at a time from the eight data pins D0-7, and set /EN high again.

sorry guys I thought I attached the datasheet to the post- apparently not!

@john Thanks so much- that makes a lot more sense than what I was conjuring in my head. does anyone have a sketch that does this or something similar? I've never done bitwise operations on the arduino- does it work the same as in C?

alright so I've got this 2022 wired up to the mega and I wrote a basic sketch to output data from the IC.

The connections are as follows (2022 -> Arduino):

Vdd -> +5v
Vss -> Ground
CLK -> SCL (D21)
OE -> D43
SEL1 -> D44
SEL2 -> D45

D0 -> D46
D1 -> D47
D2 -> D48
D3 -> D49
D4 -> D50
D5 -> D51
D6 -> D52
D7 -> D53

CHA -> Encoder A
CHB -> Encoder B

Open pins on 2022:
U/D
RST
TEST
INDEX (encoder is not indexed)

Here is the test program I wrote to see if I was getting any data:

int oePin = 43;
int sel1Pin = 44;
int sel2Pin = 45;
int daPin0 =  46;
int daPin1 =  47;
int daPin2 =  48;
int daPin3 =  49;
int daPin4 =  50;
int daPin5 =  51;
int daPin6 =  52;
int daPin7 =  53;

int dataValue;
byte MyByte;

void setup()   {
  Serial.begin(9600);
  Serial.println("online");
  pinMode(oePin, OUTPUT);
  pinMode(sel1Pin, OUTPUT);
  pinMode(sel2Pin, OUTPUT);
  pinMode(daPin0, INPUT);
  pinMode(daPin1, INPUT);
  pinMode(daPin2, INPUT);
  pinMode(daPin3, INPUT);
  pinMode(daPin4, INPUT);
  pinMode(daPin5, INPUT);
  pinMode(daPin6, INPUT);
  pinMode(daPin7, INPUT);


  ReadData();

}


void loop()
{
  ReadData();
  delay(250);
}
void ReadData() {
  digitalWrite(oePin, LOW);
  
  //first byte
  digitalWrite(sel1Pin, LOW);
  digitalWrite(sel1Pin, HIGH);
  delay(10);
  MyByte =
  digitalRead(daPin0)|
  (digitalRead(daPin1)<< 1) |
  (digitalRead(daPin2)<< 2) |
  (digitalRead(daPin3)<< 3) |
  (digitalRead(daPin4)<< 4) |
  (digitalRead(daPin5)<< 5) |
  (digitalRead(daPin6)<< 6) |
  (digitalRead(daPin7)<< 7);

   Serial.print(MyByte,BIN);
   
   //second byte
   digitalWrite(sel1Pin, HIGH);
  digitalWrite(sel1Pin, HIGH);
  delay(10);
  MyByte =
  digitalRead(daPin0)|
  (digitalRead(daPin1)<< 1) |
  (digitalRead(daPin2)<< 2) |
  (digitalRead(daPin3)<< 3) |
  (digitalRead(daPin4)<< 4) |
  (digitalRead(daPin5)<< 5) |
  (digitalRead(daPin6)<< 6) |
  (digitalRead(daPin7)<< 7);

   Serial.print(MyByte,BIN);
   
   //third byte
   digitalWrite(sel1Pin, LOW);
  digitalWrite(sel1Pin, LOW);
  delay(10);
  MyByte =
  digitalRead(daPin0)|
  (digitalRead(daPin1)<< 1) |
  (digitalRead(daPin2)<< 2) |
  (digitalRead(daPin3)<< 3) |
  (digitalRead(daPin4)<< 4) |
  (digitalRead(daPin5)<< 5) |
  (digitalRead(daPin6)<< 6) |
  (digitalRead(daPin7)<< 7);

   Serial.print(MyByte,BIN);
   
   //fourth byte
   digitalWrite(sel1Pin, HIGH);
  digitalWrite(sel1Pin, LOW);
  delay(10);
  MyByte =
  digitalRead(daPin0)|
  (digitalRead(daPin1)<< 1) |
  (digitalRead(daPin2)<< 2) |
  (digitalRead(daPin3)<< 3) |
  (digitalRead(daPin4)<< 4) |
  (digitalRead(daPin5)<< 5) |
  (digitalRead(daPin6)<< 6) |
  (digitalRead(daPin7)<< 7);

   Serial.print(MyByte,BIN);
   
   
   digitalWrite(oePin, HIGH);
}

Using this I am not getting any data- the output is always 0000. Anyone see where I fumbled this up?

Thanks as always for the help!

Could it be that the RST or INDEX lines have to be tied high or low?

The CLK pin isn't a serial clock for data transmission, it's a system clock for internal chip timing. The datasheet says you can provide a clock up to 33MHz. The pulses from the quadrature encoder must be at leas three cycles of the clock long. Since each edge of each pulse causes a count you can use (Max Count Rate / 2) * 3 as the minimum clock rate. If you want 10000 counts per second you will need a 15 kHz clock. More is better, up to the 33 MHz limit.

Yeah it seems like tying the RST and INDEX lines low should be done. However it seems like my biggest issue right now is the clock signal. I was a bit wary about using the serial clock signal and kind of figured that was going to be a bust.

I've been looking around the forums for the simplest way to get the fastest possible clock signal from the mega but to not much avail. From what I gather I need to use the pins attached to Timer0. Do I simply set the clock pin to output and the frequency divider to be one in the setup function? Or do I setup the clock in the setup to be output and then make successive calls to LOW and HIGH on that pin in the loop function? Or should I just use an external timer- the spec for the IC says up 10 33mhz, whereas the arduino can only output 16mhz?

thanks as always!

How fast will the quadrature inputs change? Multiply that "counts per seconds" by 1.5 to get the minimum clock rate you need.

Setting the register bit to output one of the timers on one of the output pins is the easiest way to get a good clock signal. Heck, you could probably just set a PWN output to 127 (50% duty cycle) and use it as your clock.

the encoder needs to run around 15,000 pulses per second. based on what I found in the forums it looks like I can use timer1 with a prescale divider of 1 to generate a clock signal around 31k which should be sufficient (though way lower than the max clock of 33mhz). I added the following code to make this happen- also connected the clock input on the IC to pin 11:

TCCR1B = (TCCR1B & 0xF8) | 1 ; 
analogWrite (11, 128) ;

Additionally, I tied the RST and INDEX to their own digital pins and wrote them low in the setup function.

However, I am still getting the same 0000 output. I am wondering two things now. First, am I simply converting the data wrong as it comes into the software? I am pretty sure they way it is suppose to work is that the 4 bytes that I am collecting should form a 32bit value that tells me the encoder count. Secondly, I have seen a few other examples where 8 bit parallel communication is used and in those examples there are small delays between random steps of getting the data from the buffer- do I also need to include these delays?

Thank you all for the help- it's really appreciated. esp @johnwasser!

Again just for reference here is the revised basic wiring and the code I am using:

Vdd -> +5v
Vss -> Ground
CLK -> SCL (D21)
OE -> D43
SEL1 -> D44
SEL2 -> D45
RST -> D42 (not used)

D0 -> D46
D1 -> D47
D2 -> D48
D3 -> D49
D4 -> D50
D5 -> D51
D6 -> D52
D7 -> D53

CHA -> Encoder A
CHB -> Encoder B
INDEX -> D41 (not used)

Open pins on 2022:
U/D
TEST

int oePin = 43;
int sel1Pin = 44;
int sel2Pin = 45;
int daPin0 =  46;
int daPin1 =  47;
int daPin2 =  48;
int daPin3 =  49;
int daPin4 =  50;
int daPin5 =  51;
int daPin6 =  52;
int daPin7 =  53;

int indexPin = 41;
int resetPin = 42;

int clockPin = 11;

int dataValue;
byte MyByte;

void setup()   {
  Serial.begin(9600);
  Serial.println("online");
  pinMode(oePin, OUTPUT);
  pinMode(sel1Pin, OUTPUT);
  pinMode(sel2Pin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(resetPin, OUTPUT);
  pinMode(indexPin, OUTPUT);
  pinMode(daPin0, INPUT);
  pinMode(daPin1, INPUT);
  pinMode(daPin2, INPUT);
  pinMode(daPin3, INPUT);
  pinMode(daPin4, INPUT);
  pinMode(daPin5, INPUT);
  pinMode(daPin6, INPUT);
  pinMode(daPin7, INPUT);
  
  //setup timer1 prescale divider to be 1
  TCCR1B = (TCCR1B & 0xF8) | 1 ; 
  //setup clock output
  analogWrite (clockPin, 128) ; 
  
  //write these pins LOW so they don't do anything
  digitalWrite(indexPin,LOW);
  digitalWrite(resetPin, LOW);

}


void loop()
{
  ReadData();
  delay(250);
}

void ReadData() {
  //set the OE pin low to read the tr-state buffer
  digitalWrite(oePin, LOW);
  
  //first byte
  digitalWrite(sel1Pin, LOW);
  digitalWrite(sel1Pin, HIGH);
  delay(10);
  MyByte =
  digitalRead(daPin0)|
  (digitalRead(daPin1)<< 1) |
  (digitalRead(daPin2)<< 2) |
  (digitalRead(daPin3)<< 3) |
  (digitalRead(daPin4)<< 4) |
  (digitalRead(daPin5)<< 5) |
  (digitalRead(daPin6)<< 6) |
  (digitalRead(daPin7)<< 7);

   Serial.print(MyByte,BIN);
   
   //second byte
   digitalWrite(sel1Pin, HIGH);
  digitalWrite(sel1Pin, HIGH);
  delay(10);
  MyByte =
  digitalRead(daPin0)|
  (digitalRead(daPin1)<< 1) |
  (digitalRead(daPin2)<< 2) |
  (digitalRead(daPin3)<< 3) |
  (digitalRead(daPin4)<< 4) |
  (digitalRead(daPin5)<< 5) |
  (digitalRead(daPin6)<< 6) |
  (digitalRead(daPin7)<< 7);

   Serial.print(MyByte,BIN);
   
   //third byte
   digitalWrite(sel1Pin, LOW);
  digitalWrite(sel1Pin, LOW);
  delay(10);
  MyByte =
  digitalRead(daPin0)|
  (digitalRead(daPin1)<< 1) |
  (digitalRead(daPin2)<< 2) |
  (digitalRead(daPin3)<< 3) |
  (digitalRead(daPin4)<< 4) |
  (digitalRead(daPin5)<< 5) |
  (digitalRead(daPin6)<< 6) |
  (digitalRead(daPin7)<< 7);

   Serial.print(MyByte,BIN);
   
   //fourth byte
   digitalWrite(sel1Pin, HIGH);
  digitalWrite(sel1Pin, LOW);
  delay(10);
  MyByte =
  digitalRead(daPin0)|
  (digitalRead(daPin1)<< 1) |
  (digitalRead(daPin2)<< 2) |
  (digitalRead(daPin3)<< 3) |
  (digitalRead(daPin4)<< 4) |
  (digitalRead(daPin5)<< 5) |
  (digitalRead(daPin6)<< 6) |
  (digitalRead(daPin7)<< 7);

   Serial.print(MyByte,BIN);
   
   //set OE pin high to fish tr-state reading
   digitalWrite(oePin, HIGH);
}

For each byte your are setting sel1Pin twice, not SEL1 and SEL2. Simple mistake.

SEL1 SEL2
LOW HIGH First (Most Significant) Byte
HIGH HIGH Second
LOW LOW Third
HIGH LOW Last (Least Significant) Byte

ah- dumb mistake! unfortunately that doesn't do anything for me. I think I am still having a fundamental problem somewhere that I am not seeing.

does anyone have any example code that might shed any light on either the clock/timing situation or 8 bit bus data retrieval that might clarify this for me?

The RST line is 'active low' (that's why it is shown with a bar over the name in the pin diagrams). You are connecting it to an output and setting that output low so the chip is going to be held in a reset state. Try setting that pin HIGH instead.

ok I tried this with no change in the result. by this same logic I would assume that the line over the OE pin also means active low- I tried reversing the logic on the GetData function to reflect this and again no change. I'm still thinking I have something else totally miffed- either with the bit conversion to 32 bit number or in the timing/clock.

My guess would be the clock or the quadrature inputs. Do the quadrature encoders have Open Collector outputs which require pull-ups?

Do you know anyone with an oscilloscope? :slight_smile:

I know that the quadrature inputs are working fine. I had them hooked directly to the arduino initially and was getting counts just fine. I was having trouble at high speeds with missed counts and also with noise on some encoders, so we decided to look for an IC that would filter the signals and count the quadrature.

At this rate I've got to believe that the problem is with the clock. If I am sending ~31k to the IC thats rated up to 33mhz that means I am giving it like .01% of the speed it can handle- that just seems wrong. Sadly, I've never used the arduino to drive a clock before. Isn't there a way simply to tap the 16mhz clock from the microcontroller and use that to drive the IC? I can't be the first person who wants to output the clock directly like that...

also- thanks so much john, I totally owe you a virtual beer or something!

To output a buffered copy of the system clock on the CLKO pin you set the CKOUT fuse. You can do that as part of burning a fresh bootloader. Check the processor datasheet for whatever AVR processor is in your Arduino. That will tell you which pin is the CLKO pin and which fuse to set.

alright so I believe I have an 8mhz clock running on pin 11 using this code:

// outputs via timer1
  pinMode(clockPin, OUTPUT);  // select Pin as ch-A
  TCCR1A = B01000011; // Fast PWM change at OCR1A
  TCCR1B = B11001;  // System clock
  OCR1A = 0; // 8 MHz

from this post:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1230286016/all

I also revised my printing routine to combine the 4 bytes into a float using a union. Sadly I am still getting nothing out of the IC. To compound the problem I don't have access to an oscilloscope (this is what happens when a programmer is given a hardware assignment- eeks!) to test anything. Is it possible I blew out the IC at some point?

Because I am totally lost and frustrated at this point I made a circuit diagram so I could post it and see if anyone spots my problem. Below are the diagram and the revised code- let me know if you spot anything!

Circuit Diagram:
http://d.pr/4i5k

int oePin = 43;
int sel1Pin = 44;
int sel2Pin = 45;
int daPin0 =  46;
int daPin1 =  47;
int daPin2 =  48;
int daPin3 =  49;
int daPin4 =  50;
int daPin5 =  51;
int daPin6 =  52;
int daPin7 =  53;

int indexPin = 41;
int resetPin = 42;

int clockPin = 11;

int dataValue;
byte MyByte;

void setup()   {
  Serial.begin(9600);
  Serial.println("online");
  pinMode(oePin, OUTPUT);
  pinMode(sel1Pin, OUTPUT);
  pinMode(sel2Pin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(resetPin, OUTPUT);
  pinMode(indexPin, OUTPUT);
  pinMode(daPin0, INPUT);
  pinMode(daPin1, INPUT);
  pinMode(daPin2, INPUT);
  pinMode(daPin3, INPUT);
  pinMode(daPin4, INPUT);
  pinMode(daPin5, INPUT);
  pinMode(daPin6, INPUT);
  pinMode(daPin7, INPUT);
  
  // outputs via timer1
  pinMode(clockPin, OUTPUT);  // select Pin as ch-A
  TCCR1A = B01000011; // Fast PWM change at OCR1A
  TCCR1B = B11001;  // System clock
  OCR1A = 0; // 8 MHz

  
  //write these pins LOW so they don't do anything
  digitalWrite(indexPin,LOW);
  digitalWrite(resetPin, HIGH);

}

union u_tag {
    byte b[4];
    float fval;
} u;


void loop()
{
  ReadData();
  delay(250);
}

void ReadData() {
  //set the OE pin low to read the tr-state buffer
  digitalWrite(oePin, LOW);
  
  //first byte
  digitalWrite(sel1Pin, LOW);
  digitalWrite(sel2Pin, HIGH);
  delay(10);
  MyByte =
  digitalRead(daPin0)|
  (digitalRead(daPin1)<< 1) |
  (digitalRead(daPin2)<< 2) |
  (digitalRead(daPin3)<< 3) |
  (digitalRead(daPin4)<< 4) |
  (digitalRead(daPin5)<< 5) |
  (digitalRead(daPin6)<< 6) |
  (digitalRead(daPin7)<< 7);
   u.b[0] = MyByte;
   
   //second byte
   digitalWrite(sel1Pin, HIGH);
  digitalWrite(sel2Pin, HIGH);
  delay(10);
  MyByte =
  digitalRead(daPin0)|
  (digitalRead(daPin1)<< 1) |
  (digitalRead(daPin2)<< 2) |
  (digitalRead(daPin3)<< 3) |
  (digitalRead(daPin4)<< 4) |
  (digitalRead(daPin5)<< 5) |
  (digitalRead(daPin6)<< 6) |
  (digitalRead(daPin7)<< 7);
   u.b[1] = MyByte;
   
   //third byte
   digitalWrite(sel1Pin, LOW);
  digitalWrite(sel2Pin, LOW);
  delay(10);
  MyByte =
  digitalRead(daPin0)|
  (digitalRead(daPin1)<< 1) |
  (digitalRead(daPin2)<< 2) |
  (digitalRead(daPin3)<< 3) |
  (digitalRead(daPin4)<< 4) |
  (digitalRead(daPin5)<< 5) |
  (digitalRead(daPin6)<< 6) |
  (digitalRead(daPin7)<< 7);  
   u.b[2] = MyByte;
   
   //fourth byte
   digitalWrite(sel1Pin, HIGH);
  digitalWrite(sel2Pin, LOW);
  delay(10);
  MyByte =
  digitalRead(daPin0)|
  (digitalRead(daPin1)<< 1) |
  (digitalRead(daPin2)<< 2) |
  (digitalRead(daPin3)<< 3) |
  (digitalRead(daPin4)<< 4) |
  (digitalRead(daPin5)<< 5) |
  (digitalRead(daPin6)<< 6) |
  (digitalRead(daPin7)<< 7);
   u.b[3] = MyByte;
   
   Serial.println(u.fval);
   
   //set OE pin high to fish tr-state reading
   digitalWrite(oePin, HIGH);
}

I beginning to think I should have utilized an IC that communicates via I2C- still might, just have to believe this can work somehow!

thanks all!

Did you ever get this to work? I am currently attempting to do exactly what you tried with the HCTL-2022 quadrature decoder IC. Did you get this to work, or did you end up using another IC/solution?

Thanks.

I'm a few years late to the game, but I'll post in case someone in the future is interested. I got the Avago 2022 (HCTL-2022) to work with an Arduino Mega.

I had the same problems as mentioned in this thread for a long time, just zeros as output.

Two things that solved it for me was:

  1. connecting the Arduino to external power supply via DC-adapter because with just USB connected, the HCTL-2022 was just getting ~4.3 V to its power supply pin which is to low. Data sheet says between 4.5 and 5.5 V.

  2. Also the /RST pin needs to be high, otherwise the counter is cleared every execution of the code, resulting in just zeros as output. You can set it to low once in the setup()-function to clear the counter between runs.

I also used a crystal oscillator of 32MHz as system clock.

This solved it for me. I'll attach the code, it's not perfect since using delay() etc is not optimal, but it is a good place to start.

// Hanna

HWdecoderNew.ino (4.16 KB)

Dear hannibalal,

I really appreciate that you posted your code as I have a 2022 that I am currently trying to interface so that I can build a custom servo system.

I've have studied the datasheet in depth and implemented your code and wiring, but I just get 0's

And ideas on what might have gone wrong?

I can post my wiring diagram once I have a chance to draw a schematic.

I am using an extra Nano board with some code I copied to try and generate a clock pulse, maybe this is the problem?

Can anyone help me get my pulses counted? I really need this to work for a prototype of a sustainable transport invention I am building...

I am using an Arduino Nano to try and act as an external clock signal, (the rest is hooked up to a Mega)

My main code is identical to hannibalal and I am using this for 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)
{
}