Add support register-based SPI slave - #3040
Conversation
By adding a method to read and write one byte at a time, the slave's code can implement a complete register-based SPI slave. The new method includes timeouts so that the slave does not get stuck in an infinite while loop if something goes wrong. An additional reset() method can be used to reset the spi interface This allows the slave to recover if it falls behind or detects other errors that cause it to fall out sync with the master. Example code showing a simple register-based slave is included in the SPI/examples/RegisterSlave folder along with a python script that runs on a RPi and acts as the master.
|
Hi @philrittenhouse In your example you unconditionally call reset. The read/write function seems very than the transfet one except the last step to wait EOT. About example, it is very specific, requires STM32FreeRTOS and RPi to run examples. |
There was a problem hiding this comment.
Pull request overview
Adds new SPI APIs intended to enable register-based SPI slave implementations by supporting single-byte full-duplex transfers with timeouts, plus a reset capability for recovery. This extends the existing STM32 Arduino SPI abstraction and provides a working end-to-end example (including a Raspberry Pi master script).
Changes:
- Add low-level
spi_read_write_byte()andspi_reset()utilities in the SPI C layer. - Expose new
SPIClass::read_write_byte()andSPIClass::reset()methods in the C++ SPI API. - Add a RegisterSlave example sketch and a Python master demo script.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| libraries/SPI/src/utility/spi_com.h | Declares new low-level reset and single-byte transfer APIs. |
| libraries/SPI/src/utility/spi_com.c | Implements SPI reset and 1-byte transfer-with-timeout logic. |
| libraries/SPI/src/SPI.h | Exposes new reset() and read_write_byte() methods on SPIClass. |
| libraries/SPI/src/SPI.cpp | Implements the new SPIClass methods calling into the C layer. |
| libraries/SPI/examples/RegisterSlave/SPI_slave_demo.ino | Adds a FreeRTOS-based register-style SPI slave example. |
| libraries/SPI/examples/RegisterSlave/spi_demo.py | Adds a Raspberry Pi spidev script to exercise the example. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| SPI_HandleTypeDef *handle = &(obj->handle); | ||
|
|
||
| #if defined SPI1_BASE | ||
| // Reset SPI | ||
| if (handle->Instance == SPI1) { | ||
| __HAL_RCC_SPI1_FORCE_RESET(); |
| spi_status_e spi_read_write_byte(spi_t *obj, uint8_t tx, uint8_t *rx) | ||
| { | ||
| spi_status_e ret = SPI_OK; | ||
| int8_t tmp; | ||
| uint32_t tickstart; | ||
| SPI_TypeDef *_SPI = obj->handle.Instance; | ||
|
|
||
| tickstart = HAL_GetTick(); |
| void SPIClass::reset(void) | ||
| { | ||
| _spi.handle.State = HAL_SPI_STATE_RESET; | ||
| spi_reset(&_spi); | ||
| } |
| /** | ||
| * @brief Helper to perform a single byte transaction (read and write). | ||
| * begin() or beginTransaction() must be called at least once before. | ||
| * @param | ||
| * @param | ||
| * @param tx: byte to send | ||
| * @param *rx: byte received. If NULL the received byte will be discarded. | ||
| * @return true on success. | ||
| */ |
| /** | ||
| * @brief This function is used to send/receive one byte on SPI | ||
| * @param | ||
| * @param obj : pointer to spi_t structure | ||
| * @param tx: byte to send | ||
| * @param rx: pointer to byte received. If NULL the received byte will be discarded | ||
| * @param | ||
| * @retval status. SPI_OK = 0 | ||
| */ |
| void SPI_ISR() { | ||
| BaseType_t xHigherPriorityTaskWoken = pdFALSE; | ||
|
|
||
| SPI.reset(); // Reset the SPI hardware interface to flush any stale data: | ||
|
|
||
| // At this point xTaskToNotify should not be NULL |
| * The following pins should be conneced to the corresponding pins on the master | ||
| * PA4 - NSS | ||
| * PA5 - SCK | ||
| * PA6 - MISO | ||
| * PA7 - MOSI | ||
| * GND - GND |
By adding a method to the SPI library to read and write one byte at a time, the slave's code can implement a complete register-based SPI slave. The new method includes timeouts so that the slave does not get stuck in an infinite while loop if something goes wrong.
An additional reset() method can be used to reset the spi interface This allows the slave to recover if it falls behind or detects other errors that cause it to fall out sync with the master.
Example code showing a simple register-based slave is included in the SPI/examples/RegisterSlave folder along with a python script that runs on a RPi and acts as the master.
Resolves: #3039
See Also: #2050
Pull Request template
Summary
This PR fixes/implements the following bugs/features
The current SPI slave support needs to expose additional functionality in order for the user code to implement a register-based SPI slave.
In a register-based SPI slave, the slave maintains a list of registers that the master can read or write. The registers can be read to return state (e.g. temperature or mode) or written to change state (e.g. set mode, control outputs).
By adding a method to read and write one byte at a time, the slave's code can implement a complete register-based SPI slave. The slave code works by waiting for an interrupt from the master on SS. It then read a byte containing the read/write bit and an address. It then either reads the data to be written to the given address, or returns the data read from the given address.
Using the spi_transfer() method doesn’t work for this use case because the slave does not know in advance if the master is starting a read or a write request and must determine that on the fly.
Validation
Code formatting
Closing issues
Fixes #3039
Closes: #3039