SPI

Beelsebob
Posts: 1
Joined: Thu Sep 25, 2025 1:29 pm

SPI

Postby Beelsebob » Thu Sep 25, 2025 5:50 pm

I'm having real trouble understanding the SPI APIs. This is likely in part because I'm missunderstanding fundamental concepts of how SPI works. My aim is to implement an SPI slave, but because the device doesn't exist yet, and it speaks a new protocol, I am also implementing a master.

I have a few questions:

My understanding of SPI as it stands, is that the master is responsible for bringing chip select low, pulsing the clock, and transmitting data for a certain period. In a full duplex scenario, it receives data in the same period and receives exactly as many bits as it transmits. In a half duplex scenario, it continues to pulse the clock after it has transmitted, first for a pause for the slave to compute its response, and then for a period of time for the slave to transmit a number of bits. Is this correct?

I'm trying to use half duplex communication, does this mean that the master is responsible for knowing how long the slave will take to compute its response, and how many bits the slave will transmit back to it? If this is not so, how is the master supposed to deal with the slave communicating this information to it?

When the master talls

Code: Select all

spi_device_transmit
, in half duplex mode, it appears that I need to tell the driver how long the receive buffer is in the transaction's

Code: Select all

rxlength
field. It also appears that when the post transaction callback is called,

Code: Select all

rxlength
remains the same - it doesn't change to the amount of data that the slave transmitted. This seems to reinforce my above understanding that the master is expected to know before hand how much data the slave will respond with. If this isn't correct, is it up to the slave to somehow transmit in the response how long the rest of the response is?

In the SPI Slave API it doesn't appear that there's any way to simply wait for the master to send something, and receive. Am I right in thinking that in order to receive you must "transmit" a transaction that happens to have a 0 byte transmission, but has a receive buffer? For an API that's meant to be implementing a protocol that's entirely driven by the master, it seems very odd that the slave API isn't something along the lines of a function to yeild the CPU until a data received interrupt comes in, and then have a callback called when a request comes in.

I've set up a very basic experiment (code attached), in which the slave tries to respond to all messages with "My Tallest", and the master sends out requests containing command 0x1, and address 0xdead. When I do this, the master only appears to receive "llest". I honestly, have no idea why that would be. Can anyone clarify what's going on here?
Attachments
main_Slave.cpp
(2.65 KiB) Downloaded 10 times
main_Master.cpp
(5.49 KiB) Downloaded 15 times

Sprite
Espressif staff
Espressif staff
Posts: 10612
Joined: Thu Nov 26, 2015 4:08 am

Re: SPI

Postby Sprite » Fri Sep 26, 2025 8:45 am

My understanding of SPI as it stands, is that the master is responsible for bringing chip select low, pulsing the clock, and transmitting data for a certain period. In a full duplex scenario, it receives data in the same period and receives exactly as many bits as it transmits. In a half duplex scenario, it continues to pulse the clock after it has transmitted, first for a pause for the slave to compute its response, and then for a period of time for the slave to transmit a number of bits. Is this correct?
Yes.
I'm trying to use half duplex communication, does this mean that the master is responsible for knowing how long the slave will take to compute its response, and how many bits the slave will transmit back to it? If this is not so, how is the master supposed to deal with the slave communicating this information to it?
Exactly. The amount of time to calculate the response usually is in the protocol in the form of 'dummy bytes' that are sent without any particular value in them, just giving the slave time (and in some cases a clock signal) to do internal stuff. The master needs to know up front how much data to read; either it reads a fixed amount corresponding to what it needs to know (e.g. reading a block from a flash chip) or this is communicated up front (e.g. one SPI transaction to read status and available data length, a second one to read the data).
When the master talls

Code: Select all

spi_device_transmit
, in half duplex mode, it appears that I need to tell the driver how long the receive buffer is in the transaction's

Code: Select all

rxlength
field. It also appears that when the post transaction callback is called,
Please don't use that callback for anything important; it's intended for doing simple stuff like twiddling a GPIO. Just wait in the main code for the transaction to complete (i.e. spi_device_transmit returns) to actually look at and act on the result.
In the SPI Slave API it doesn't appear that there's any way to simply wait for the master to send something, and receive. Am I right in
thinking that in order to receive you must "transmit" a transaction that happens to have a 0 byte transmission, but has a receive buffer?
Yep.
For an API that's meant to be implementing a protocol that's entirely driven by the master, it seems very odd that the slave API isn't something along the lines of a function to yeild the CPU until a data received interrupt comes in, and then have a callback called when a request comes in.
ESP-IDF is built on FreeRTOS, and as such it's expected that you have a task to handle the SPI slave stuff. In that sense, having a blocking call that waits on the master to do a transaction makes a lot of sense.
I've set up a very basic experiment (code attached), in which the slave tries to respond to all messages with "My Tallest", and the master sends out requests containing command 0x1, and address 0xdead. When I do this, the master only appears to receive "llest". I honestly, have no idea why that would be. Can anyone clarify what's going on here?
You have the transaction defined (in the master) as 1 byte command - 2 bytes address - master tx data (where you send 0 bytes) - slave tx data (where you receive 128 bytes(. On the other hand, the slave transaction is defined as master TX data - slave RX data

Your slave then starts up a transaction with 12 bytes ("My Tallest!" + terminating zero). The M is interpreted as command, the "y " is interpreted as the address. Not gonna lie, I'm not entirely sure where the remaining "Ta" went; if that's not a coding issue you could stick a LA on the bus to check if those disappear on the slave or on the master side.

Who is online

Users browsing this forum: Applebot, Baidu [Spider], Qwantbot, YisouSpider and 4 guests