"Pulled High" SPI connection to electronic counter device

Hi folks,

I could use a little help trying to get an SPI interface set up to an "standard electronic counter" (SEC) device that was extracted from an old fruit machine.

You can find the product manual here: https://www.mikrocontroller.net/attachment/163201/Zaehlwerk_27-080_01__man.pdf

The interface pinout is described in Appendix 2 (p.27) as:

  1. SOUT
  2. SCLOCK
  3. SIN
  4. Reset (NC)
  5. +12v
  6. GND
    and I'm wiring it to the hardware SPI pins on an Arduino Nano as follows. (There is no slave select line described, so I'm assuming it is always pulled low and this is intended to create a direct SPI connection to the host, rather than a shared bus):

When supplied with 12V power, the device display comes on and it appears to go through some power-up test sequence, it displays a model number etc., then shows the current counter value, so all looks good.

The manual describes various commands that can be issued - some return values over the SPI interface, whereas some change the output on screen. I thought I'd try to get it working with a simple command to make the display cycle through all the counter values, and I'm using code as follows:

// INCLUDES
#include <SPI.h>

// GLOBALS
// Array to hold received responses
byte response[8];

void setup() {
  Serial.begin(115200);

  // Start the SPI interface
  SPI.begin();

  // According to "Communications Interface" (p.8)
  // Uses SPI Mode 2, MSB, Clock speed between 100Hz - 5kHz
  SPI.beginTransaction(SPISettings(2000, MSBFIRST, SPI_MODE2));

  // Send a command - see "Command Format From Host" (p.10) for general structure
  // This is "r. <54> Cycle Counter Display" (p.19)
  // Which should cause counter to flash through all counter values
  SPI.transfer(0x54); // CMD
  SPI.transfer(0x00); // ID starts at 00 and increments with each message sent
  SPI.transfer(0x00); // DCNT - number of databytes following (not including CHKSUM)
  SPI.transfer(0x54); // CHECKSUM is lower 8 bits of sum of all preceding bytes

  // Clear the response buffer
  memset(response, 0, sizeof(response));
  // Then fill it with 4 byte response
  for (int i=0; i<4; i++) {
    response[i] = SPI.transfer(0x00); // Dummy value just to receive SPI response
  }

  // Release SPI interface
  SPI.endTransaction();
  SPI.end();

  // Print the response
  printBuffer();
}

void printBuffer() {
  for(int i=0; i<8; i++) {
    Serial.print(response[i]);
    if(i<7) Serial.print(",");
  }
  Serial.println("");
}

void loop() {
}

Now, unfortunately, I don't have access to a scope at the moment so I'm having to debug this "blind" - but I'm getting no response to the above - the counter does not exhibit any behaviour, and the "response" buffer is just empty. I've tried other commands from the manual too, and I likewise don't get any response.

Reading through the manual again, I noticed one thing - in the Hardware Specification (p.9), it describes "Reset, Data In & Clock Inputs are pulled high internally. 5v Inputs can be driven directly via open collector drivers".
My understanding was that, while I2C used pull ups on the data lines that the communicating devices then pulled low, SPI did not normally have pull-ups, but rather a "push-pull" approach. So I was a little puzzled to see this, and I wasn't sure that the Arduino implementation of SPI met the criteria of "open collector driver". This might be a red herring (it's late here, and I'm starting to get blurred thoughts!), but if anyone could suggest where I might be going wrong, I'd be very grateful, thanks!

Ok, so I'm making some progress, and got my hands on a pocket scope. Here's the signal generated from the above:
image

The first mistake I realised is that, even after setting SPI MODE 2, that doesn't mean the clock idles HIGH. So I manually added a digitalWrite(13, HIGH) to pull it high before and after the SPI transaction. That made things look better, but still no response.

At the back of the manual, there's an appendix that shows the expected pattern for a "Request Market" command:

So, I tried to recreate that, and noticed there's something very odd about the timing of the single ID bit - it seems to arrive "late", whereas it should be set high for the previous falling clock edge:
image

That was generated with the following code, which looks ok to me, but at least I've now got something a little more specific to investigate...

/*
See https://www.mikrocontroller.net/attachment/163201/Zaehlwerk_27-080_01__man.pdf
 */

// DEFINES
// Response code values
#define ACK 61
#define DATA 60
#define NAK 62

// INCLUDES
#include <SPI.h>

// GLOBALS
// Array to hold received responses
byte response[8];

void setup() {
  Serial.begin(115200);

  pinMode(11, OUTPUT);
  pinMode(13, OUTPUT);
 
  // Start the SPI interface
  SPI.begin();

/*
  // According to "Communications Interface" (p.8)
  // Uses SPI Mode 2, MSB, Clock speed between 100Hz - 5kHz
  SPI.beginTransaction(SPISettings(100, MSBFIRST, SPI_MODE2));

  // Send a command - see "Command Format From Host" (p.10) for general structure
  // This is "r. <54> Cycle Counter Display" (p.19)
  // Which should cause counter to flash through all counter values
  SPI.transfer(0x54); // CMD
  SPI.transfer(0x00); // ID starts at 00 and increments with each message sent
  SPI.transfer(0x00); // DCNT - number of databytes following (not including CHKSUM)
  SPI.transfer(0x54); // CHECKSUM is lower 8 bits of sum of all preceding bytes

  SPI.endTransaction();

  // Clear the response buffer
  memset(response, 0, sizeof(response));
  // Then fill it with 4 byte response
  for (int i=0; i<4; i++) {
    response[i] = SPI.transfer(0x00); // Dummy value just to receive SPI response
  }
*/  
}

void printBuffer() {
  for(int i=0; i<8; i++) {
    Serial.print(response[i]);
    if(i<7) Serial.print(",");
  }
  Serial.println("");
}

void loop() {
  // Set clock line to idle HIGH
  digitalWrite(13, HIGH);

  // According to "Communications Interface" (p.8)
  // Uses SPI Mode 2, MSB, Clock speed between 100Hz - 5kHz  
  SPI.beginTransaction(SPISettings(1000, MSBFIRST, SPI_MODE2));

  // Send a command - see "Command Format From Host" (p.10) for general structure
  // This is "r. <21> Request Market Type"
  // as shown in example on p.29
  SPI.transfer(0x21); // CMD
  SPI.transfer(0x01); // ID starts at 00 and increments with each message sent
  SPI.transfer(0x00); // DCNT - number of databytes following (not including CHKSUM)
  SPI.transfer(0x22); // CHECKSUM is lower 8 bits of sum of all preceding bytes

  // Clear the response buffer
  memset(response, 0, sizeof(response));
  // Then fill it with 4 byte response
  for (int i=0; i<4; i++) {
    response[i] = SPI.transfer(0x00); // Dummy value just to receive SPI response
  }

  // Not sure if this is strictly required - if we're only communicating with a single device,
  // can probably just open a single transaction and leave it open
  SPI.endTransaction();
  // Set clock HIGH again
  digitalWrite(13, HIGH);

  // Print the response
  printBuffer();

  delay(1000);  
}

Now getting those spurious readings at random positions - i.e. my scope display is flickering between:
image
and:
image

Notice the extra spike. Still wondering whether this could be a side-effect of the fact that the slave device is pulling the lines up (and still not clear why a slave device should be doing anything to control the MOSI or CLK lines at all, tbh..)

Beginning to wonder whether the "spike" was just due to poor scope probe attachment, as I now can't recreate it.

Tried ditching SPI.transfer() and using shiftOut() with delayMicroseconds(500) to get a 1kHz sample rate instead- though I had to modify the function to simulate SPI Mode 2 (the default clock edge sampling used by shiftOut seems to match SPI Mode 0)

Still no response, even though I'm able to match the example signal shown in the datasheet exactly. Wondering if I might just have a duff unit...