SSI grey code reader

This is a very compact grey code reader for the Hengsler series of absolute encoders. It can however be easily modified for any other SSI reading situation.

Grey code doesn't suffer the reading glitches of ordinary binary as if you happen to be exactly on a transition you will only possibly get a 1 bit error.

Running speed doesn't have to be too precise but it needs to be fairly regular. That is why I disable interrupts for the actual read operation. The flag test are there to ensure that the encoder output has made at least one transition i.e. hasn't gone O/C or S/C.

/*

gray code reader for Hengsler SSI 24bit multi-turn absolute encoder

I/O should be via RS224 chip

Pins chosen for Arduino Mega

Target reading bit speed 120kHz

Min reading speed for Henglser 90kHz

*/

#define SSI_CLOCK 42
#define SSI_DATA 38
#define ENCODER_LENGTH 24
#define ENCODER_SPEED 4

byte encoder[ENCODER_LENGTH + 1];
unsigned long value;


void setup(){
  pinMode(SSI_CLOCK, OUTPUT);       //  port L - 7
  pinMode(SSI_DATA, INPUT);         //  port D - 7
  PORTL = 255;                      //  set SSI clock idle
}


byte transfer(unsigned long &number){
  int i;
  int flag;

  noInterrupts();  
  delayMicroseconds(50);                               //  ensure everything settled
  flag = !((PIND >> 6) & 2);
  if (flag){                                           //  test for start condition
    interrupts();
  }
  else{
    for(i = 0; i <= ENCODER_LENGTH; i++){              //  overcount to read status end bit
      PORTL = 0;                                       //  set the whole port for speed
      delayMicroseconds(ENCODER_SPEED);

      PORTL = 255;                                     //  output from pin 42
      delayMicroseconds(ENCODER_SPEED);
      encoder[i] = PIND;                               //  input to pin 38
    }
    interrupts();
    delayMicroseconds(15);                             // delay to ensure no resend
    flag = flag | (encoder[ENCODER_LENGTH] >> 7);      // test for status end bit (zero)
  
    encoder[0] = encoder[0] >> 7;
    for(i = 1; i < ENCODER_LENGTH; i++){
      encoder[i] = (encoder[i] >> 7) ^ encoder[i - 1]; // bit shift and convert to binary
    }
    number = 0;
    for (i = 0; i <ENCODER_LENGTH; i ++){
      number = (number<<1) | (encoder[i]);             // convert to unsigned long int
    }
  }
  return flag;
}


void loop(){
  bool error;

  // {code}
  error = transfer(value);
  if (error){
    if (error & 2){
      // {start fault}
    }
    else{
      // {stop fault}
    }
  }
  else{
    // {code}
  }
  // {code}
}

Hi,

I'm trying to write code to read an SSI binary encoder. Could you please explain what modifications need to be done to your code in order to have it work with Binary?

Thank you

Well that was some time ago!

All you really need to do is to remove the grey-binary conversion which is the four lines beginning with:

encoder[0] = encoder[0] >> 7;

You should check on the encoder you are using to see if there are any extra start and stop bits.

In my code I'm ignoring about 4 control bits at the end of the sequence. The short delay afterwards ensures that the Hengstler encoder will reset to the start. Other encoders may be different.

hey
I am nice to arduino programming and could really use your help.
what do these code lines do/mean:

flag = !((PIND >> 6) & 2);

flag = flag | (encoder[ENCODER_LENGTH] >> 7);

also what should i do to modify this code for Arduino Uno

Thanks

Hi again,
First of all I spotted a silly mistake in the original program - corrected now.

This bit of code was part of a much larger program I wrote, that needed all the I/O that the mega provides. The result is that pin numbers and port names are quite different to the ones on the Uno. To make things easy for anyone (and me too in the future!) I've re-written it for the Uno and the code is below. I've tested it as far as possible without an actual encoder to put on it!

/*

gray code reader for Hengsler SSI 24bit multi-turn absolute encoder

I/O should be via RS224 chip

Pins chosen for Arduino Uno

Target reading bit speed 120kHz

Min reading speed for Henglser 90kHz

*/

#define SSI_CLOCK 8
#define SSI_DATA 14
#define ENCODER_LENGTH 24
#define ENCODER_SPEED 4

byte encoder[ENCODER_LENGTH + 1];
unsigned long value;


void setup(){
  pinMode(SSI_CLOCK, OUTPUT);       //  port B - 0 digital pin 8
  pinMode(SSI_DATA, INPUT);         //  port C - 0 analog pin 0
  PORTB = 1;                        //  set SSI clock idle
}


byte transfer(unsigned long &number){
  int i;
  int flag;

  noInterrupts();  
  delayMicroseconds(50);                               //  ensure everything settled
  flag = !((PINC << 1) & 2);
  if (flag){                                           //  test for start condition
    interrupts();
  }
  else{
    for(i = 0; i <= ENCODER_LENGTH; i++){              //  overcount to read status end bit
      PORTB = 0;                                       //  set the whole port for speed
      delayMicroseconds(ENCODER_SPEED);

      PORTB = 1;                                       //  output from pin 8
      delayMicroseconds(ENCODER_SPEED);
      encoder[i] = PINC & 1;                           //  input to analog pin 0
    }
    interrupts();
    delayMicroseconds(15);                             // delay to ensure no resend
    flag = flag | (encoder[ENCODER_LENGTH] & 1);       // test for status end bit (zero)

    for(i = 1; i < ENCODER_LENGTH; i++){
      encoder[i] = encoder[i] ^ encoder[i - 1];        // convert to binary (remove these 3 lines if already binary)
    }

    number = 0;
    for (i = 0; i <ENCODER_LENGTH; i ++){
      number = (number<<1) | (encoder[i]);             // convert to unsigned long int
    }
  }
  return flag;
}


void loop(){
  bool error;

  // {code}
  error = transfer(value);
  if (error){
    if (error & 2){
      // {start fault}
    }
    else{
      // {stop fault}
    }
  }
  else{
    // {code}
  }
  // {code}
}

There are a few simplifications as there is no need to avoid certain I/O that I was using in the original project. This also makes the flag operation slightly different. but the principle remains the same.

The line:

flag = !((PINC << 1) & 2);

reads PINC (which is a very fast way to grab a block of inputs)
shifts the result one bit to the left so that bit zero (analog pin 0, now acting as digital pin 14) represents the value 2
this is then masked with 2 (the& sign) so all other pins will be ignored.
The '!' then inverts this, so that you get a '2' if the input line is not high.

The line:

flag = flag | (encoder[ENCODER_LENGTH] & 1);

looks at the last value that was put into the encoder buffer
the '& 1' is actually not necessary here as the value should only be 0 or 1, but I kept it just in case some stray values had got into the buffer. In this case we want to see if the value is 1 (input was high, which it shouldn't be)

The '|' symbol puts the two values together so that if everything is OK you end up with a value of 0. This has proved that the encoder has gone from high to low at least once so is presumably working correctly.

Hello Folderol,
On the off-chance that you are still monitoring this thread, I wanted to post my question here first since it's the closest I've found in my searches.

You indicated that this may work with other absolute encoders that use grey code. How might I modify it to work with a 10 Bit Absolute encoder? (specs attached).

I'm very new to Arduino, so the more detail the better. Thanks!

AbsoluteEncoderSpecs.pdf (295 KB)