Read the position of a absolut encoder

I have a BLDC-Motor with a absolut encoder from Faulhaber.

BLDC-Motor

Encoder

Now I should write a program. I want to read the exact position of the motor and stop at this position.
I'm not sure how I should connect the poles of the enocoder and how to read the position.

Thanks for your help

The encoder document is all in German and this is the English language section. There is also a German language section of the Forum.

Considering how much Faulhaber charge for their products I suspect they have other documents on their website that give all the details you need.

...R

The encoder documentation says it is the BISS-C protocol, described here and here: http://www.biss-interface.com/about.php

It is similar to SPI, but you will probably have to create a "bit bang" interface. Look for "software spi" for example code.

andre777:
Here are the english data sheets

BLDC-Motor:
https://fmcc.faulhaber.com/resources/img/EN_0824_B_FMM.PDF

Absolute Encoder:
https://fmcc.faulhaber.com/resources/img/EN_AESM-4096_DFF.PDF

I read so many forum posts and different types of arduino codes, but nothing works on my Arduino Uno.

That's my code, but the Serial monitor shows only "0". Anyone know why?

const int PIN_CS = 12;

const int PIN_CLOCK = 13;
const int PIN_DATA = 11;

void setup() {
  Serial.begin(9600);
  pinMode(PIN_CS, OUTPUT);
  pinMode(PIN_CLOCK, OUTPUT);
  pinMode(PIN_DATA, INPUT);

digitalWrite(PIN_CLOCK, HIGH);
  digitalWrite(PIN_CS, LOW);
}

//byte stream[16];
void loop() {

digitalWrite(PIN_CS, HIGH);
  digitalWrite(PIN_CS, LOW);
  int pos = 0;
  for (int i=0; i<10; i++) {
    digitalWrite(PIN_CLOCK, LOW);
    digitalWrite(PIN_CLOCK, HIGH);
   
    byte b = digitalRead(PIN_DATA) == HIGH ? 1 : 0;
    pos += b * pow(2, 10-(i+1));
  }
  for (int i=0; i<6; i++) {
    digitalWrite(PIN_CLOCK, LOW);
    digitalWrite(PIN_CLOCK, HIGH);
  }
  digitalWrite(PIN_CLOCK, LOW);
  digitalWrite(PIN_CLOCK, HIGH);
  Serial.println(pos);
}






thanks

I have a BLDC-Motor with a absolut encoder from Faulhaber.

BLDC-Motor:

Absolute Encoder:

I read so many forum posts and different types of arduino codes, but nothing works on my Arduino Uno.

That's my code, but the Serial monitor shows only "1020". Anyone know why?

int udd=7;
const int PIN_CS = 12;
const int PIN_CLOCK = 13;
const int PIN_DATA = 11;

void setup() {
  Serial.begin(9600);
  pinMode(PIN_CS, OUTPUT);
  pinMode(PIN_CLOCK, OUTPUT);
  pinMode(PIN_DATA, INPUT);
pinMode(udd, OUTPUT);
digitalWrite(udd, HIGH);
  digitalWrite(PIN_CLOCK, HIGH);
  digitalWrite(PIN_CS, LOW);
}


void loop() {

  digitalWrite(PIN_CS, HIGH);
  digitalWrite(PIN_CS, LOW);
  int pos = 0;
  for (int i=0; i<10; i++) {
    digitalWrite(PIN_CLOCK, LOW);
    digitalWrite(PIN_CLOCK, HIGH);
    
    byte b = digitalRead(PIN_DATA) == HIGH ? 1 : 0;
    pos += b * pow(2, 10-(i+1));
  }
  for (int i=0; i<6; i++) {
    digitalWrite(PIN_CLOCK, LOW);
    digitalWrite(PIN_CLOCK, HIGH);
  }
  digitalWrite(PIN_CLOCK, LOW);
  digitalWrite(PIN_CLOCK, HIGH);
  Serial.println(pos);
}

thanks for your help

The following won't work, because there are a number of bits in the return value that are not data, like the start bit and the 6 bit CRC. It is also extremely ungainly, inefficient and slow.

pos += b * pow(2, 10-(i+1));

By my count, the encoder returns 22 bits of information, with a 12 bit value buried in the middle. This is the BISS-C protocol, which is well documented.

Use bit shift to read the encoder instead, something like:

   pos = pos | digitalRead(PIN_DATA); //initialize pos to 0 before the loop!
   pos = pos<<1;

Then fish out the bits that correspond to the actual data. You will need to use a long integer to store the entire returned result.

hi jremington

thanks for your quick answer. I'm an amateur in programming. I'm not sure if connect my encoder right.

CLOCK = Pin13
DATA = Pin11
N.C. = Pin12

I'm not sure if I understand exactly what you mean with "Then fish out the bits that correspond to the actual data."

Only 12 bits of the 22 bits (not counting the ACK) are actual data.

Please post a hand drawn wiring diagram of your setup, identifying the pin numbers of both the encoder and the Arduino, and also show the power and ground connections.

The pins in the red boxes are the pins on the Arduino.

The data sheet gives no reason to connect Pin 12 on the Arduino to pin 7 (N.C.) on the encoder. N.C. means "not connected". What do you think it does and why?

You will have to rethink your code. Please study the "output signals" diagram in the encoder data sheet and the documentation for the BISS-C protocol.

You need to
(1) start sending clock signals, while reading the data line and
(2) wait for ACK (LOW) and then START (HIGH),
(3) read the CDS bit,
(4) read 12 bits of position data,
(5) read two RES bits (ignore),
(6) read the 6 bit CRC value.

Finally, what will you do with the data? Using digitalRead() and digitalWrite(), the data will be read out at a rate of somewhere between 5-10 microseconds/bit, or at best in the range of 4000-8000 position samples/second.

Isn't cross-posting just the most irritating thing?

Hi jremington

I study the output signals but nothing works when i try to evaluate the data. I only have one week to finish my project and I still have no idea how i can read the position of this encoder ...

... but thanks for trying to help me.

If you google "arduino ssi" you will find some posts that describe reading similar encoders, like this one: Timer, SSI-Interface - Troubleshooting - Arduino Forum

The code in reply #6 is said to work, but it is poorly written.

This looks better: Absolute rotary Encoder SSI SPI how ? - Networking, Protocols, and Devices - Arduino Forum

I already tried the second one and then my result is always 18.72

const int CLOCK_PIN = 13;
const int DATA_PIN = 11;
const int BIT_COUNT = 12; // this's the percision of rotary encoder. 
int udd=7;

void setup() {
  pinMode(DATA_PIN, INPUT);
  pinMode(CLOCK_PIN, OUTPUT);
  pinMode(udd, OUTPUT);

  digitalWrite(udd, HIGH);
  digitalWrite(CLOCK_PIN, HIGH);

  Serial.begin(9600);
}


//read the current angular position
float readPosition() {
  unsigned long sample1 = shiftIn(DATA_PIN, CLOCK_PIN, BIT_COUNT);
  delayMicroseconds(25);  // Clock must be high for 20 microseconds before a new sample can be taken

  return ((sample1 & 0x0FFF) * 360UL) / 4096.0; // ouptut value from 0 to 360 with two point percision
}

//read in a byte of data from the digital input of the board.
unsigned long shiftIn(const int data_pin, const int clock_pin, const int bit_count) {
  unsigned long data = 0;

  for (int i=0; i<bit_count; i++) {
    data <<= 1; // shift all read data left one bit.
    
    /*
    // speed up I/O In order to meet the communication speed of this encoder.
    The correct form is:
    PORTD &= ~(1 << n); // Pin n goes low
    PORTD |= (1 << n); // Pin n goes high
    So:
    PORTD &= ~(1 << PD0); // PD0 goes low
    PORTD |= (1 << PD0); // PD0 goes high 

    PORTD &= ~(1 << PD1); // PD1 goes low
    PORTD |= (1 << PD1); // PD1 goes high 
     */
     
    //digitalWrite(clock_pin,LOW);
    PORTD &= ~(1 << 5); // clock pin goes low
    delayMicroseconds(1);
    //digitalWrite(clock_pin,HIGH);
    PORTD |= (1 << 5); // lock pin goes high
    delayMicroseconds(1);

    data |= digitalRead(data_pin); // cat the new read bit to the whole read data.
  }
  return data;
}



void loop() {
  float reading = readPosition();
  Serial.println(reading,2);
  delay(2);
}

If you study the data sheet for YOUR sensor, and compare with what that program actually does, you will see why it can't work.

It forms an outline that you will need to modify appropriately. See reply #9.

As jremmington has pointed out the basic approach to this interface is to write a clock pin LOW, write a clock pin HIGH and read the data back after the HIGH. digitalWrite() is fine for the timing of the clock pin, and you don't need to get into direct port manipulation syntax.

The BISS-C protocol has the slave start responding on the second rising edge after the clock pulses begin following an inactive period where the clock is HIGH following a low timeout.

Send 1 clock pulse (LOW/HIGH) and with no reading back. Then send 22 more clock pulses and read the data into a byte array with 22 elements. Array elements 3 through 14 will contain your 12 bits of position data. I'm not sure, but you may need to send a 23rd pulse with no read back to generate the stop

digitalWrite(PIN_CLOCK, LOW);
digitalWrite(PIN_CLOCK, HIGH);

for (int i = 0; i < 22; i++) {
    digitalWrite(PIN_CLOCK, LOW);
    digitalWrite(PIN_CLOCK, HIGH);

    array[i] = digitalRead(PIN_DATA);
  }
//digitalWrite(PIN_CLOCK, LOW);//possible 23rd pulse to trigger stop
//digitalWrite(PIN_CLOCK, HIGH);

digitalWrite(PIN_CLOCK, LOW);
delay(timeout);
digitalWrite(PIN_CLOCK, HIGH);