Reading position data from absolute encoder in BiSS C

Hi,

I'm working on a project that involves using an absolute encoder to give position feedback from a stepper motor. The absolute encoder being used is as follows:
POSITAL KCD-BC33B-1617-F7SW-JAQ

I've tried using RS485 boards/RS422 to TTL boards, and some other options of directly trying to read the data off the encoder on the arduino but have had absolutely zero luck. Does anyone who has any experience with this have any advice where to go from here? The encoder sends in bits of: 16 multiturn bits, 17 singleturn bits, 1 error bit, 1 warning bit, and 6 crc bits. The closest I've gotten is being able to getsome form of data output from the encoder however no matter how I move the motor, the data doesn't actually change, as I'll show in a screenshot below. Any help here is appreciated!

// Define pins for CLK+/- and DATA+/- connections
const int CLK_PIN = 2;
const int CLK_NEG_PIN = 3;
const int DATA_PIN = 4;
const int DATA_NEG_PIN = 5;

// Variables for storing BiSS-C data
uint16_t multi_turn_value;
uint16_t single_turn_value;
bool error_flag;
bool warning_flag;
uint8_t crc_value;

// State variables
bool ack_received = false;
bool data_ready = false;
bool receiving_data = false;
bool data_timeout = false;

// Setup function
void setup() {
  // Initialize serial communication for debugging
  Serial.begin(115200);
  
  // Configure pins for input
  pinMode(CLK_PIN, INPUT);
  pinMode(CLK_NEG_PIN, INPUT);
  pinMode(DATA_PIN, INPUT);
  pinMode(DATA_NEG_PIN, INPUT);
}

// Main loop
void loop() {
  // Wait for start of BiSS-C frame (MA = 0, SLO = 1)
  waitForFrameStart();
  
  // Read BiSS-C data frame
  readBISSC();
  
  // Process the decoded data
  if (data_ready) {
    // Print decoded data for debugging
    Serial.print("Multi-turn value: ");
    Serial.println(multi_turn_value);
    Serial.print("Single-turn value: ");
    Serial.println(single_turn_value);
    Serial.print("Error flag: ");
    Serial.println(error_flag);
    Serial.print("Warning flag: ");
    Serial.println(warning_flag);
    Serial.print("CRC value: ");
    Serial.println(crc_value);
    
    // Reset flags for next frame
    ack_received = false;
    data_ready = false;
    receiving_data = false;
    data_timeout = false;
    
    // Delay or perform other operations as needed
    delay(1000);
  }
}

// Function to wait for the start of BiSS-C frame
void waitForFrameStart() {
  // Wait for MA = 0 and SLO = 1 (start of frame)
  while (digitalRead(CLK_PIN) == HIGH || digitalRead(DATA_PIN) == LOW) {
    // Wait until MA = 0 and SLO = 1
  }
}

// Function to read BiSS-C data frame
void readBISSC() {
  // Variables for data bits
  uint32_t data_bits = 0;
  
  // Read the BiSS-C data frame
  for (int i = 0; i < 41; ++i) { // 42 bits total (16 + 17 + 1 + 1 + 6)
    data_bits |= (digitalRead(DATA_PIN) << i);
    delayMicroseconds(50); // Adjust delay as per your encoder's timing
  }
  
  // Decode the received bits according to the BiSS-C protocol
  multi_turn_value = (data_bits >> 16) & 0xFFFF;
  single_turn_value = (data_bits >> 17) & 0xFFFF;
  error_flag = (data_bits >> 1) & 0x01;
  warning_flag = (data_bits >> 1) & 0x01;
  crc_value = data_bits & 0x3F;
  
  // Validate CRC if needed (CRC calculation not detailed here)
  
  // Set data_ready flag indicating data is ready for processing
  data_ready = true;
}

image

Did you not read the documentation that the data is synchronous using their clock signal?

Yes I did, though when I was scouring forums I saw the people who had the same problem of having an encoder that communicates in BiSS C, they used two RS485 boards for clock and data and used them to convert to SPI protocol, which I've had no luck with

Where did you find that code? It does not appear to agree with the Biss C protocol description in the encoder data sheet, and is unlikely to do anything useful. It does not appear to transmit a clock signal.

Capture

I suggest to spend some time carefully studying the protocol timing diagram. Note the ACK, which has variable timing, so those 50 us delays are useless.

(post deleted by author)

(post deleted by author)

Me and the OP are working together on this project. We have different codes just to see who can get the encoder working. My code shows that the encoder latches in the output but it has the same error on bit 24 and 26. I have not parsed the data yet since we still have issues getting reliable data. It should latch with a differential 01 then go into the data frame.

#include <digitalWriteFast.h>

#define dat_p 6
#define dat_n 7
#define clk_p 2
#define clk_n 3
#define baud 115200


int dat_pr;
int dat_nr;
int x = 0;
int pos[23];

void setup(){
  Serial.begin(baud);
  Serial1.begin(baud);
  pinMode(dat_p, INPUT);
  pinMode(dat_n, INPUT);
  pinMode(clk_p, OUTPUT);
  pinMode(clk_n, OUTPUT);
  digitalWriteFast(clk_p, HIGH);
  digitalWriteFast(clk_n, LOW);
  Serial.println("Manual Code.");
  delay(1500);
}

void loop(){
    digitalWriteFast(clk_p, LOW); // LOW
    digitalWriteFast(clk_n, HIGH);

    digitalWriteFast(clk_p, HIGH); // HIGH
    digitalWriteFast(clk_n, LOW);   

    digitalWriteFast(clk_p, LOW); // LOW
    digitalWriteFast(clk_n, HIGH);

    digitalWriteFast(clk_p, HIGH);// HIGH
    digitalWriteFast(clk_n, LOW);

    dat_pr = digitalReadFast(dat_p);
    dat_nr = digitalReadFast(dat_n);

    Serial.println("P: " + String(dat_pr) + " N: " + String(dat_nr));

    digitalWriteFast(clk_p, LOW); // LOW
    digitalWriteFast(clk_n, HIGH);

    digitalWriteFast(clk_p, HIGH);// HIGH
    digitalWriteFast(clk_n, LOW);

    dat_pr = digitalReadFast(dat_p);
    dat_nr = digitalReadFast(dat_n);

    Serial.println("P: " + String(dat_pr) + " N: " + String(dat_nr));


  for(int i = 0; i < sizeof(pos); i ++){
  
    digitalWriteFast(clk_p, LOW); // LOW
    digitalWriteFast(clk_n, HIGH);

    digitalWriteFast(clk_p, HIGH);// HIGH
    digitalWriteFast(clk_n, LOW);

    dat_pr = digitalReadFast(dat_p);
    dat_nr = digitalReadFast(dat_n);

    if(dat_pr == 1 & dat_nr ==0){
      pos[i] = 1;
    }
    else{
      pos[i] = 0;
    }
  }

  while(x < sizeof(pos)){
    Serial.print(String(x) + ": " + String(pos[x]) + "\n");
      x++;
  }
  x = 0;

  delay(5000);
}

So my partner @zjlaw and I are working together on this project. We have different codes just to see who can get the encoder working. His code shows that the encoder latches in the output but it has the same error on bit 24 and 26. He hasn't parsed the data yet since we still have issues getting reliable data. It should latch with a differential 01 then go into the data frame.

#include <digitalWriteFast.h>
 
#define dat_p 6
#define dat_n 7
#define clk_p 2
#define clk_n 3
#define baud 115200
 
 
int dat_pr;
int dat_nr;
int x = 0;
int pos[23];
 
void setup(){
  Serial.begin(baud);
  Serial1.begin(baud);
  pinMode(dat_p, INPUT);
  pinMode(dat_n, INPUT);
  pinMode(clk_p, OUTPUT);
  pinMode(clk_n, OUTPUT);
  digitalWriteFast(clk_p, HIGH);
  digitalWriteFast(clk_n, LOW);
  Serial.println("Manual Code.");
  delay(1500);
}
 
void loop(){
    digitalWriteFast(clk_p, LOW); // LOW
    digitalWriteFast(clk_n, HIGH);
 
    digitalWriteFast(clk_p, HIGH); // HIGH
    digitalWriteFast(clk_n, LOW);  
 
    digitalWriteFast(clk_p, LOW); // LOW
    digitalWriteFast(clk_n, HIGH);
 
    digitalWriteFast(clk_p, HIGH);// HIGH
    digitalWriteFast(clk_n, LOW);
 
    dat_pr = digitalReadFast(dat_p);
    dat_nr = digitalReadFast(dat_n);
 
    Serial.println("P: " + String(dat_pr) + " N: " + String(dat_nr));
 
    digitalWriteFast(clk_p, LOW); // LOW
    digitalWriteFast(clk_n, HIGH);
 
    digitalWriteFast(clk_p, HIGH);// HIGH
    digitalWriteFast(clk_n, LOW);
 
    dat_pr = digitalReadFast(dat_p);
    dat_nr = digitalReadFast(dat_n);
 
    Serial.println("P: " + String(dat_pr) + " N: " + String(dat_nr));
 
 
  for(int i = 0; i < sizeof(pos); i ++){
 
    digitalWriteFast(clk_p, LOW); // LOW
    digitalWriteFast(clk_n, HIGH);
 
    digitalWriteFast(clk_p, HIGH);// HIGH
    digitalWriteFast(clk_n, LOW);
 
    dat_pr = digitalReadFast(dat_p);
    dat_nr = digitalReadFast(dat_n);
 
    if(dat_pr == 1 & dat_nr ==0){
      pos[i] = 1;
    }
    else{
      pos[i] = 0;
    }
  }
 
  while(x < sizeof(pos)){
    Serial.print(String(x) + ": " + String(pos[x]) + "\n");
      x++;
  }
  x = 0;
 
  delay(5000);
}

Looks like you two need to work together before posting.

One forum thread is not going to work to sort out all the errors in two completely different approaches.

BTW for debugging interface problems like this, nothing beats these cheap logic analyzers, available in many outlets, when used with free PulseView software:

Thanks' for the advice about the logic analyzers, we'll definitely give it a go and try it out! So to clear up the confusion caused, the code I had posted in my original post was more of a shot in the dark to try and just get some data output, my partners code is the one that we have found to give better results as shown that we are able to get the encoder to latch, however this approach involves having to build up the protocol from the ground up. The main struggle we are having is no matter what method, we've been unable to get any data that's being output to change when the motor shaft is moved, it's always consistently just staying the same, and we both are very lost on where to go from here. Do you suggest that logic analyzer could help us understand with figuring out how to get to that point? I haven't worked with those before.

Since that code appears to transmit a clock signal, it has a chance of working. But drop the use of Strings, as they cause memory problems and crashes on Arduinos with limited memory.

Not enough information has been given, like a complete wiring diagram (hand drawn is preferred), with pins, connections and parts clearly labeled. Post a link to the interface transmitter, if you are using one. If not, then all bets are off.

See the "How to get the best out of this forum" post for more instructions.

The official description of the Biss-C interface and protocol can be downloaded here:
https://biss-interface.com/download/biss-c-protocol-description-english/

I have not found a useful example of anyone implementing a complete Biss-C interface with Arduino. The companies making gadgets that use Biss-C apparently expect customers to buy their proprietary interfaces and control software.

This synchronous communications may be just like the synchronous of the 1970-1990 time period. The transmitter will send a bit when the clock signal goes high. But the receiver must wait one half clock period before making the decision if the received bit is high or low.