SPISlave block after some reply

Hi all

I need to have Arduino Giga working as SPI slave device. Using the following code I'm able to receive data from master:

void setup()
{
       mbed::SPISlave spi(SPI_MOSI1, SPI_MISO1, SPI_SCK1, SPI_SS1);
       spi.frequency(2000000);
       spi.format(8, 0);
       spi.reply(0x00);
}

void update()
{
       if(spi.receive())
       {
           const byte data = static_cast<byte>(spi.read());
           ....
           spi.reply(0xAA); // test value
       }
}

However this code block on reply() function after a few times the function is called. I'm also not able to receive the reply data when the call doesn't block. I always receive the first byte passed to reply after the initialization (that in this example is 0x00).
I really don't understand why I experiment this block since the code is really simple.
Someone experienced a similar issue and know how to get it working?
Thank you

Hi @falsinsoft . This could be related I2C Slave Regression · Issue #1051 · arduino/ArduinoCore-mbed · GitHub

Hi @steve9

I don't know but I also tried I2CSlave and work as expected so I don't think the two issues can be related. However it's true I don't know the current mbed version I'm using since I develop using VSC with Platformio extension. I'll check...

My mistake @falsinsoft , thought it was I2C you were having issues with - need to pay more attention

The data are sent only on the next transmission. So the return data should be set on SS start, before the next byte is received.

@DrDiettrich and this should justify the block of reply() call? By the way I tried all the possible combinations of read/reply but without success. After 8 or 9 times the function reply() is called it does not return and block all. Checking the mbed source I can see the reply() call refer to the function:

void spi_slave_write(spi_t *obj, int value)
{
    SPI_TypeDef *spi = SPI_INST(obj);
    struct spi_s *spiobj = SPI_S(obj);
    SPI_HandleTypeDef *handle = &(spiobj->handle);
    while (!ssp_writeable(obj));
    const int bitshift = datasize_to_transfer_bitshift(handle->Init.DataSize);
    MBED_ASSERT(bitshift >= 0);

    if (bitshift == 1) {
        LL_SPI_TransmitData16(spi, (uint16_t)value);
#ifdef HAS_32BIT_SPI_TRANSFERS
    } else if (bitshift == 2) {
        LL_SPI_TransmitData32(spi, (uint32_t)value);
#endif
    } else {
        LL_SPI_TransmitData8(spi, (uint8_t)value);
    }
}

that have inside an infinite loop generating the block. The loop refe to to the function:

static inline int ssp_writeable(spi_t *obj)
{
    int status;
    struct spi_s *spiobj = SPI_S(obj);
    SPI_HandleTypeDef *handle = &(spiobj->handle);

    // Check if data is transmitted
#if defined(SPI_IP_VERSION_V2)
    status = ((__HAL_SPI_GET_FLAG(handle, SPI_FLAG_TXP) != RESET) ? 1 : 0);
#else /* SPI_IP_VERSION_V2 */
    status = ((__HAL_SPI_GET_FLAG(handle, SPI_FLAG_TXE) != RESET) ? 1 : 0);
#endif /* SPI_IP_VERSION_V2 */

    return status;
}

that I don't know exactly what is waiting for and, above all, why never change status and return back...

I guess that's due to the internal reply buffer overflow. I already told you how to fix your problem.

Probably my knowledge about SPI is very poor but in my understanding when master move CS and start transaction by activating clock the buffers are switched and the in/out bytes are exchanged between master and slave. Always one in byte and one out byte so what is a buffer for?

So the return data should be set on SS start, before the next byte is received.

As code how should this be handled? SPISlave class export only receive(), read() and reply() methods. How to use these methods to manage the situation you described?
Thank you

It buffers at least the first reply byte, possibly more for each further call to spi.reply().

Try to explain which byte is transferred from slave to master on the first transmission.

Reported example in the documentation is the following:

int main()
{
    device.reply(0x00);              // Prime SPI with first reply
    while (1) {
        if (device.receive()) {
            int v = device.read();   // Read byte from master
            v = (v + 1) % 0x100;     // Add one to it, modulo 256
            device.reply(v);         // Make this the next reply
        }
    }
}

In my case I check if data is received inside the Arduino loop() function instead of have an infinite while loop. Excluding this point the code is the same but doesn't work as explained. Can you show me how to modify this code based to your suggestion?
Thank you

Updated example:

Add byte getNextByteToSend() as appropriate.

What is the diffrence of your change compared with my initial (blocking) example? It's basically the same I do in my test and, as reported, after around 8/9 calls the reply() function stop to return...

You are right, my code is not okay. Use these funtions instead:

bool replyWaiting;
byte replyData; //next reply to transmit

void checkReply() { //to be called in loop()
  if (digitalread(SPI_SS1) == 0 && replyWaiting)
    SPI.reply(replyData);
    replyWaiting = false;
  }
}
//call whenever you have new data to reply
void setReply(byte repData) {
  replyData = repData;
  replyWaiting = true;
}

At first I really thank you for your time. Unfortunately your solution doesn't work, same problem. The method reply() stop return after 8 calls blocking all. In add of this the replied bytes are not transmitted to the master. Opposite communication (from master to slave) work well. I start suspecting some bug in STD mbed management but since my lack in SPI knowledge I have some difficult to know where the problem is...

Please show your code.

Code is the following:

bool transactionDone = false;

void update()
{
      if(digitalRead(SPI_SS1) == LOW)
      {
            if(transactionDone == false)
            {
                  SPI.reply(generateRandonByte());
                  transactionDone = true;
            }
      }
      else
      {
            transactionDone = false;
      }
}

I tried to check also HIGH signal just in case of some HW difference in my board but nothing changed, same problem...

I think that it's time to define a specific protocol for your slave: This protocol is specific to every single slave device, that's why slave examples are given for specific slave devices only.

Your slave waits for an ongoing transmission (SS LOW), reads a byte from the master and generates a reply for the next transmission. After that second transmission the slave should be deselected.

The master selects a slave, sends a byte, then accepts a reply and deselects the slave. That's 2 transmissions. The slave should be selected during that transaction, deselected afterwards.