Apparently weird [but correct] side effects of SPI.transfer() with multi-byte signature [SOLVED]

I've spent some (considerable) time debugging similar code on a UNO using IDE 1.8.19.
It now appears that SPI.transfer() corrupts the data it is given which, in principle, should not happen.
Before I did deep in the library, has anyone, who has come across this before, got a quick explanation. This simple code demonstrates the problem.

#include <SPI.h>

const uint8_t latchPin_A = 10;


void setup() {
  
  Serial.begin(115200) ;
  
  uint8_t mute[2] = {0, 0xFF} ;

  SPI.beginTransaction(SPISettings( 2000000UL, LSBFIRST, SPI_MODE0)) ;
  digitalWrite( latchPin_A , LOW ) ;
  
  Serial.println( mute[ 1 ] , HEX ) ;  // mute[1] is 0xFF
  
  SPI.transfer( &mute[ 1 ] , sizeof( mute[ 1 ] ) ) ; 
  
  Serial.println( mute[ 1 ] , HEX ) ;  // mute[1] should still be 0xFF but isn't
  
  digitalWrite( latchPin_A , HIGH ) ;
  SPI.endTransaction() ;

  Serial.println() ;

}

void loop() {

}

I just tried your sketch on a Nano and mute[1] keeps its value of FF. What are you seeing ?

#include <SPI.h>

const uint8_t latchPin_A = 10;


void setup()
{

  pinMode(latchPin_A,OUTPUT);
  
  Serial.begin(115200) ;

  uint8_t mute[2] = {0, 0xFF} ;

  SPI.beginTransaction(SPISettings( 2000000UL, LSBFIRST, SPI_MODE0)) ;
  digitalWrite( latchPin_A , LOW ) ;

  Serial.println( mute[ 1 ] , HEX ) ;  // mute[1] is 0xFF

  SPI.transfer( mute, sizeof( mute[ 1 ] ) ) ;

  Serial.println( mute[ 1 ] , HEX ) ;  // mute[1] should still be 0xFF but isn't

  digitalWrite( latchPin_A , HIGH ) ;
  SPI.endTransaction() ;

  Serial.println() ;

}

void loop()
{

}

Thanks for looing at it. Oddly, that depends on whether I have just uploaded the code, just opened the serial monitor or just pressed reset on the Uno:

If you can't duplicate it I'll first see if my installation contains the correct version of the SPI library.

Are you running the same code you posted?

I just caught it setting the value to zero

  • upload the code with the monitor open FF/FF
  • close the monitor and open it again FF/FF
  • leave the monitor open and press reset FF/0

So, something is wrong somewhere

Thanks. I think I see it now. It is using the same buffer for the received data (of which there is none). I'm only sending data so I was surprised about the "corruption". But it is probably perfectly valid. It is probably a false alarm.

Easy to check, make it const

const uint8_t mute[2] = {0, 0xFF} ;

I already thought of that. You get a compile time warning. If you run it the data is still changed.
I believe it is simply that the buffer is used also for the returned data (which, in my case, doesn't exist). Anyway, thanks for looking at it.

indeed, the transfer method modifies passed array

It is clear now. I've just held the MISO pin (pin12) high and it returns 0xFF.
I didn't expect the result because I was only sending data but of course SPI is clocking in anything that may come back. User error !
Thanks everyone for looking at it. I'll close it now.

That will send the first byte, not the second.

To send the second byte and NOT have it overwritten by received data:
SPI.transfer(mute[1]) ;
When sending a single byte, the received value is returned by the function, not replacing the byte transmitted from buffer.

1 Like

1. What are you doing in the above code? The array name is mute and the name itself points to the first item of the array. So, the code should be:

SPI.transfer(mute, sizeof(mute));  //both bytes of the array will be transmitted one-after-another.

2. The following command does transfer single byte data.

byte x = SPI.transfer(mute[1]);   //x holds the data coming from SPDR Register of Slave

3. The avialable data transfer commands of SPi protocol are:

receivedVal = SPI.transfer(val)
receivedVal16 = SPI.transfer16(val16)
SPI.transfer(buffer, size)    

4. If you execute this command (for array transmission): SPI.transfer(mute, sizeof(mute)), the original data of the mute[] array will be replaced by the data coming from Slave as the DPDR Registers of Master and Slave form a circulating buffer sysetm (Fig-1). It is not the data corruption; but, a feature of the SPI Protocol.
spi328latest
Figure-1:

5. You can study the following two sketches and can easily figure out the reason for the source array data to get changed.
Master SPI Sketch:

#include <SPI.h>
byte myData[] = {0x56, 0x89};

void setup ()
{
  Serial.begin(115200);
  digitalWrite(SS, HIGH);  // ensure SS stays high for now
  SPI.begin ();
  delay(100);
  digitalWrite(SS, LOW);    // SS is pin 10
  SPI.setClockDivider(SPI_CLOCK_DIV16);  //1Mbits/sec

  Serial.print("Array value before transfer command: ");
  Serial.print(myData[0], HEX);
  Serial.println(myData[1], HEX);
  SPI.transfer(myData, sizeof(myData));
  Serial.print("Array value after transfer command: ");
  Serial.print(myData[0], HEX);
  Serial.println(myData[1], HEX);
}

void loop()
{

}

Slave SPI Sketch:

#include <SPI.h>
volatile byte myData[] = {0x12, 0x34};
volatile int i = 0;
volatile byte x;

void setup ()
{
  Serial.begin(115200);
  pinMode(SS, INPUT_PULLUP);  
  pinMode(MISO, OUTPUT);
  SPCR |= !(_BV(MSTR));
  SPCR |= _BV(SPE);
  SPI.attachInterrupt();
  SPDR = 0x99;
}

void loop()
{

}

ISR(SPI_STC_vect)
{
  SPDR = myData[i];
  i++;
  if(i == 2)
  {
    i = 0;
  }
}

Master Serial Monitor Output:

Array value before transfer command: 5689
Array value after transfer command: 3456

Transferring second byte, why, what is the problem?

To send 1-byte data, the said instruction is not a weird command?

It is a minimal example to demonstrate the problem, innit? You can be as weird as you want as long as it demonstrates the issue.

Was it an issue?

This instruction: SPI.transfer(buff, size) clearly says that the returned value (bytes coming from Slave) would be accepted by the same buffer keeping in mind that there is an one-byte offset due to very circulating nature in the connection of the SPDR Registers.

Irrelevant. Doesnt matter if it was a real issue or user mistake. The example demontrated it.

Yes. Thanks. I agree it was misunderstanding on my part. I was writing a byte stream to a (slave) chip via SPI which does not even return a result. The sample sketch is scaled down to demonstrate the effect. In fact the MISO pin is not even connected. It did not immediately occur to me that this would have the effect of overwriting the buffer I gave it. Yes. Special with that signature for SPI.transfer() and is made clear in the documentation that the buffer is shared. But it is all solved now and it is no longer an issue.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.