Receiving SPI-like data that I'm unfamiliar with!

Hi,

I am currently attempting to read some packets of data from a proprietary motor controller. I am not familiar with the protocol it uses, it appears to be synchronous serial data with start and stop bits around each byte.

I know that context is useful, so the thread below contains everything I know about the device, but I will summarize it below!

https://openinverter.org/forum/viewtopic.php?f=14&t=205&p=2141#p2141

The protocol for this unit first appeared to be SPI over LVDS. The device has the following 3 inputs:

  • "HTM" = Data into the unit (assumed to be MOSI)
  • "MTH" = Data from the unit (assumed to be MISO)
  • CLK = This is a 500KHz clock. The data packets from MOSI and MISO don't generally exceed 100 bytes, and I've managed to get stable packets without overlap from as low as 250KHz. Data from MTH will output in time with this clock (on a rising edge)
  • REQ = this is the status request line from the unit. This is a 1ms high pulse followed by a 3ms space, basically a 250Hz square wave at 25% duty cycle. In reality, the unit outputs a packet of around 100 bytes whenever it detects the leading edge of a REQ pulse, so a CS or SS line can be used to trigger this. The pulse can be half high for the duration of the packet, or as long as you like. One pulse = one packet.

When talking about high and low here, I am referring to the differential signal. Since these are normally inverting, in reality the microcontroller likely receives a low pulse to trigger a packet output.

From testing on the bench with the above, a typical packet looks like below:

The packet does not follows the SPI convention. Each "byte" has a start bit, 8 data bits, and a stop bit, as below:

The packet is 100 bytes wide, with many bytes being 0. A typical packet looks like below:

00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,11111111,11111111,
00000000,00000000,00100100,01100000,00000000,00000000,10011000,00000000,00000000,00000000,
00000001,11001000,01000000,00000000,00000000,00000000,00000000,10000000,00000000,00000100,
00000000,00000001,10000011,10111111,11111111,11111111,11111111,01101011,01010000,00000000,
00000000,00011000,10011000,00000000,00000000,00000100,01110010,11110001,00001000,00000000,
00000000,00000000,00000001,01001000,01000000,00000000,00000000,00000000,00000000,00000000,
00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,
00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,
00000000,10000000,10100000,00000000,10000000,00000000,00011000,00000000,00011000,00000000,
00000000,11010111,10000000,00000000,00100000,11111111,00000000,01110011,00011011,10110000

I spent some time looking into communication to this unit. From what I've seen/read online about SPI, it seems that SPI does not use start and stop bits. Also, SPI only transmits clock pulses as data is being requested. This unit accepts a "constant" clock, and outputs a 100 x 10-bit packet each time the REQ line is triggered. The clock runs constantly throughout. I also noticed that if the clock stops momentarily, the data keeps flowing without a clock pulse.

Because of this, I cannot read this as a SPI signal. My attempts to use a microcontroller set to transmit/receive SPI result in the microcontroller outputting 8 clock pulses, then a brief pause, then another 8 clock pulses, etc. This results in data being received, but the data is corrupt - the microcontroller just receives what was being sent at the time it attempted to read from the unit. I've attached my code below.

Another interesting thing is that the unit board waits for 39 clock pulses after receiving a REQ pulse, and starts to output on the 40th pulse.

I'm now looking at how to receive (and later transmit!) this bit stream. I can generate the clock pulses and REQ pulses easily, but to receive data synced to the clock pulses? 'm not sure how. I'm currently using a Teensy 3.2, the digital input pins are too "slow" to receive this data. Since the clock is at 500KHz, I'm currently looking to check for a 0 or 1 at my "data in" pin every 2us. The unit sends data on a rising edge of the clock pulse.

I think that all I really need to do is be able to grab this entire packet in a 1000-long array or similar, I can likely remove the start and stop bits and bit-shift the remainder into an array of integer variables afterwards.

Of course, I also need to be able to do the same in reverse, to talk to this unit.

Attached is the following images (click for big)

  1. An overview of the data received.
  2. A close-up of the start of each packet
  3. A close-up of the middle of each packet, where things are more interesting than "00000000"

Note that since this is a differential signal, it is likely inverted, so the 1 11111111 0 you see in the first packet is actually "0 00000000 1". I am not using a differential transceiver right now, although I do have one on the way.

Below is the code used to attempt to read from this unit. The code does appear functional, although I'm not sure if this is the correct way to approach this! As mentioned above, the issue is:

  • The unit waits for 39 clock pulses before outputting data, following a REQ high signal.
  • The unit needs a constant clock, the code below produces 8 clock pulses, followed by a gap (presumably as the "for" loop runs)
  • The unit outputs start and stop bits, so each byte is 10 bits long, SPI expects 8, so everything gets shifted. This wouldn't worry me, I imagine this is fixable in code, but when running constantly, each 100-byte data packet is represented differently to the program - if it was consistent then things would be easier. I guess it's an issue with timing.
#include <SPI.h>

#define req_pin 10
// MOSI: pin 11
// MISO: pin 12
// SCK: pin 13

//definitions
#define inverter_msg_length 100

//global variables
int inverter_msg[inverter_msg_length]={0};

void setup() {
  Serial.begin(9600);
  SPI.begin();
  pinMode(cs_pin, OUTPUT);

  //SPI.setBitOrder(MSBFIRST);
  //SPI.setDataMode(SPI_MODE0);
  //SPI.setClockDivider(SPI_CLOCK_DIV32);
  delay(100);
}

void loop() {

  digitalWrite(req_pin, 1); //send request
  SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0));
  Serial.println("Start of transmission.");

  for (int i=0;i<inverter_msg_length;i++) inverter_msg[i]=SPI.transfer(0);
  
  SPI.endTransaction();
  digitalWrite (req_pin, 0);
  Serial.println("End of transmission.");

//print results
  Serial.println("\t0\t1\t2\t3\t4\t5\t6\t7\t8\t9");
  Serial.println("   ------------------------------------------------------------------------------");  
  for (int j=0;j<10;j++)  {                             //print 10 rows
    Serial.print(j*10);if(j==0)Serial.print("0");Serial.print(" |\t");              //row heading 
    for (int k=0;k<10;k++){Serial.print(inverter_msg[j*10+k]);Serial.print("\t");}       //print 10 columns of data
    Serial.print("\n");
  }

//delay, so the serial monitor is readable for now  
  delay(500);
}

One thing to note is that the "REQ" line is not a chip select line, it does not need to be held low (high for me for now) throughout the transmission, it needs to simply be a pulse. For this Toyota system, it is a 1ms pulse, repeated after 3ms. The data transfer takes around 2ms. you can see this in the pictures above.

Of course, any knowledge on this subject would be greatly appreciated! I'm somewhat of a novice at programming, and have barely touched SPI, hopefully I'll be able to build something useable to control this unit!

Thanks :slight_smile: