SPI library transfer()?

I have a ESP8266 , so since it has a SPI port I decided to use the class library in my sketch.
But the docs for SPI.transfer are very confusing. Arduino - SPITransfer

There are 3 overloads defined:
receivedVal = SPI.transfer(val)
receivedVal16 = SPI.transfer16(val16)
SPI.transfer(buffer, size)

I am reading 2 bytes from a device that does not receive any commands, so which of these prototypes is applicable to me? It's a very confusing API because why on earth would I want to clock 8 bits out onto the bus if the receiver is not going to even do anything with them. What caused this class to be designed with the assumption all devices wanted hard-coded to be equal size for send and receive packet lengths? Just to get things working I am sending a 0 out, but it's confusing to anyone reading the code later on surely? Yes I know my device is not really an SPI device, it's really behaving like a shift register, but I should not have to run code that does nothing.

Can we clarify in the docs what is going on?

Is your intent to turn your question into a tutorial?

2 Likes

SPI does the send/receive simultaneously so you are not wasting anything. While the clock signal is syncing the data you are sending/receiving on the two different pins. If you are reading two bytes, then you want the SPI.transfer16() function.

@zaphodikus

Your post was MOVED to its current location as it is more suitable.

Could you also take a few moments to Learn How To Use The Forum.

Other general help and troubleshooting advice can be found here.
It will help you get the best out of the forum in the future.

1 Like

I'm actually not sure what I do know Perry :slight_smile:

I wanted to use the overloaded 16bits function, but since the peripheral in my case is not a SPI peripheral but rather a shift register, and does not accept/require commands. I would then be sending 16 zeroes out first surely? Does that mean that I miss the data clocked by the peripheral while clocking out the command? Which I am kind doing now, but in batches of 8, which may explain the pain I'm experiencing because I was calling the 8-bit transfer function which I am suspecting means I am
sending 8 bits,
reading 8,
sending 8 bits,
reading 8
which explains the corruption/confusions I get since I am "missing" the data while we send. When i implemented the same code on a raspberry pi, the API was not making these assumptions about having to send commands, hence the question about the documentation not clarifying how to use the buffer overload of the command. I was hoping the buffer command would let me for example send 0 bytes and receive 2 bytes? But since I'm not very familiar with the ecosystem yet, every time I search for examples I'm finding missleading examples that either use a different, perhaps older library, or solve a completely different problem. From my missunderstanding of the Arduino library I have gotten the MAX6675 to work purely by luck it seems, would like to do it correctly/by intent (and not to use bitbanging).

This is the code that works kinda by luck

  const int slaveAPin = D8;  // chip select
  pinMode (slaveAPin, OUTPUT);
  digitalWrite (slaveAPin, LOW);
  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
  SPI.begin();  /* begin SPI */
...
float ReadMax6675() {
  // https://www.arduino.cc/en/Reference/SPI
  // MAX6675
  digitalWrite (slaveAPin, LOW);
  delay(1);
  rawValue = SPI.transfer(0);
  rawValue<<=8;
  rawValue |= SPI.transfer(0);
  digitalWrite (slaveAPin, HIGH);
  delay(1);
  Serial.print("SPI DATA[");
  Serial.print(rawValue, HEX);
  Serial.println("]");
  //
  if ((rawValue& 0x04) || (0 == rawValue))
  { 
    Serial.println("NAN/invalid");
    rawValue = SPI.transfer(0);
    Serial.print("EAT DATA[");
    Serial.print(rawValue, HEX);
    Serial.println("]");
    return(-1.0);
  }else {
    rawValue >>=3;  // discard lowest 3 bits (status)
    celcius = rawValue*0.25;
    Serial.print(celcius, 3);
    Serial.println(" celcius");
  
    return(celcius);
  }
}

Me?

I only moved your question to a more suitable place to keep the forum tidy and increase the chance of someone who can help you seeing it. I don't have a clue what the answer is, I leave that to the clever people.

Thanks anyway though Perry. Allowing myself time and room to write it all down has kinda made some parts of the error a bit clearer to me in the meantime.

No. You are not sending THEN receiving data. SPI has two data pins, one for output, one for input. From your arduino, you connect the input pin to the output pin of your shift register so you can read in your 16 bit value. You do not need to connect the output pin to anything since you are not sending anything to the shift register.

I think, this is where the documentation confused me Faraday.

SPI.transfer(0);

Which implies I am going to wait 8 bit-times while sending 8 "off" pulses, the value parameter means it will send a 0 octet, so you mean by passing 0 for the command, I don't have to waste clocks? But at least I know my coding of this is correct and that my comms should now be more reliable :slight_smile: This small bear is still learning :bear:

The "command" has to go out on the wire before the device responds as I understand it? Yeah, I get that I am reading the register data correctly now, but why does the api waste time send those bits when I don't need to? If I understand the code then the delay(1); I have in there is moot now since I'm waiting or 8 clocks anyway, and so the possibility of overflowing the receive register on my end becomes a conflation in my mind. Beginning to assume that by reading 8 bits at a time I'm really just reading the LO byte, then the HI byte out of the receiver on my end by doing 2 8-bit reads (transactions). Is that what is really happening in this code?

I want to understand this better because I have another peripheral that takes a 16bit command and then sends back up to 64 bits, and the docs don't really make clear how to accomplish that without separating the calls.

Output and Input happen simultaneously. No clocks are 'wasted'. For every byte you clock out, a byte is simultaneously clocked in. In many cases, the output bits or input bits are being ignored.

In some cases, the input bits are the answer to some previously sent command and the output bits are a new command. If the new command (sent while clocking in the response) elicits a response, the NEXT transaction will clock in the response. In those cases, there will be a 'command' value (often 0) that does not elicit a response. That is the 'command' to send when you want to clock in a response but need no further response.

1 Like

Ah, so the whole time one has this concept of the previous command getting responded to on the subsequent transaction only. I think that's the bit that was not obvious to me from the SPI tutorials I had gone through. I had still been in a mode of thinking that was obviously fantasy because I had not grasped this part of the protocol. Hence my question was sounding so crazy, because it really was a crazy question.