-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
Since release 2.7.0, the SPI library no longer properly drives the SSEL (NSS) pin when using hardware SPI. This affects at least the STM32F4xx variants, and possibly others.
When defining a new SPIClass instance with AF MOSI/MISO/SCLK/SSEL pins, or when calling SPI.setSSEL(AF_PIN), the SPI peripheral does not drive the SSEL pin as expected. This forces users to manually control the SSEL pin in software, which breaks compatibility with many libraries that rely on hardware-controlled chip select.
(see MPU9250_WE by Wolfgang Ewald as an example, but nearly all libs, that use SPI, resort to software driven SSEL pin).
This issue persists up to v2.11.0.
My setup consists on using SPI2 with alternate function pins:
1- Use SPI2 (or other SPI peripheral) with alternate function pin mapping such as:
const PinMap PinMap_SPI_MOSI[] = {
{PB_15, SPI2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_SPI2)},
{NC, NP, 0}
};
const PinMap PinMap_SPI_MISO[] = {
{PB_14, SPI2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_SPI2)},
{NC, NP, 0}
};
const PinMap PinMap_SPI_SCLK[] = {
{PB_13, SPI2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLDOWN, GPIO_AF5_SPI2)},
{NC, NP, 0}
};
const PinMap PinMap_SPI_SSEL[] = {
{PB_12, SPI2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_SPI2)},
{NC, NP, 0}
};2- Instantiate SPI with these pins:
SPIClass SPI_2(PB_15, PB_14, PB_13, PB_12);
SPI_2.begin();Attempt to communicate with a peripheral (e.g. MPU6500) using hardware-controlled SSEL.
SPI_2.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE3));
SPI_2.transfer(data);
SPI_2.endTransaction();Observe that the SSEL pin is not driven by hardware, causing SPI communication to fail unless SSEL is handled manually via GPIO.
Root Cause Analysis
The issue was introduced in 2.7.0, where SPIClass::beginTransaction() stopped calling spi_init() unconditionally.
The intention was to avoid reinitializing the SPI peripheral if settings hadn’t changed. However, at the end of each transaction the SPI peripheral is internally reset to default values (including NSS configuration), which the _spiSettings struct does not reflect.
As a result, subsequent transactions run with an uninitialized peripheral.
Proposed Fix
Always call spi_init() on beginTransaction().
In SPI.cpp, move the spi_init() call outside the if scope:
void SPIClass::beginTransaction(SPISettings settings)
{
if (_spiSettings != settings) {
_spiSettings = settings;
+++ }
spi_init(&_spi, _spiSettings.clockFreq,
_spiSettings.dataMode,
_spiSettings.bitOrder);
--- }
}This restores pre-2.7.0 behavior and fixes SSEL handling for all transactions.