Closed chrissbarr closed 2 weeks ago
Hi @chrissbarr Feel free to submit a PR. Any contribution are welcome.
Hi @chrissbarr
looking at this issue I thought about the same issue with Wire
(#2196) then I've made a PR to fix it the same way, all fields init to 0.
Describe the bug
SPIClass
's internalspi_t
instance is not initialised with default values. This can cause problems in particular with the fields in theSPI_InitTypeDef
struct, which can currently take invalid values which cause the SPI peripheral to be misconfigured in ways that currently fail silently.I think this is only likely to be a problem if there are any dynamic memory allocations in the program before the SPIClass instance is constructed. In the case where SPIClass is constructed statically, the memory it points at should already be zeroed. So, this is probably not an issue in the majority of cases. However, it tripped me up for quite a while, and it seems easy to correct (or at least make the failure mode more obvious).
The failure mode in my case was the SPI initialisation all appeared to work, but
SPIClass::transfer
would hang indefinitely.To Reproduce This is a simple Arduino sketch that can be used to examine the behaviour:
Steps to reproduce the behavior:
mySPI->begin()
call, then the nested call tospi_com.c::spi_init()
.obj->handle->Init
fields are all uninitialised and therefore have the values of whatever previously resided in memory (in the example's case,0xAAAAAAAA
):spi_com.c::spi_init()
function configures some of the Init fields, and by the time it callsHAL_SPI_Init()
half of the fields have correct/reasonable values:However, the remaining incorrect values propagate through the SPI initialisation and can cause the SPI to operate incorrectly or fail to start.
Expected behavior As there is no explicit initialisation of the SPI_InitTypeDef struct, it looks like the code works correctly on the assumption that all fields should start as zero, and only fields that might change from zero need to be initialised explicitly, within
spi_com.c::spi_init()
. If this is the working assumption, it seems like it might be reasonable to explicitly zero-initialise the struct. User-code can do this as follows:It might be reasonable to do this in the
SPIClass
's constructor (I guess viaSPIClass::begin
is also possible, but then if the user has manually set any parameters viaSPIClass::getHandle()
they will be overwritten).I notice that there are two safeguards in the existing code to catch problems with the Init fields, which could potentially be improved.
The first is that
HAL_SPI_Init()
does do some sanity checks on the input parameters:I found this called out my problem once I enabled the
USE_FULL_ASSERT
macro. It doesn't cover every field, but seems like it catches some of the potential problems. Not sure if there is a way to improve this. Anyway, this is in the STM32 HAL, so not something to be improved here.The second, which I think can be improved, is that
HAL_SPI_Init()
is reasonably likely to fail to initialise the SPI peripheral and return an error code if the Init struct has invalid values. This function returnsHAL_ERROR
for many different failure conditions (a few of which I triggered with my invalid Init fields).However, at the moment,
spi_com.c::spi_init()
doesn't check the return code ofHAL_SPI_Init()
, and continues to try and use the SPI peripheral even if it has failed configuration. So currently it appears to pass initialisation, and only fails (or misbehaves etc.) when later code tries to use the SPI peripheral.Perhaps it would be possible to check the return code of
HAL_SPI_Init()
and invoke an error handler if it fails? That way it wouldn't be failing silently, and would be easier to track down where/when the problem is.Desktop (please complete the following information):
Og
,Optimize for debugging
, etcBoard (please complete the following information):
Happy to raise a PR for the default initialisation and/or HAL_ERROR check if you think it would be welcome.