-
Notifications
You must be signed in to change notification settings - Fork 1k
Description
Describe the bug
SPI transfer16() produces two 8 byte transfers rather than one 16 bit transfer.
To Reproduce
Here is a code that is used for testing SPI transfers. In this instance, the CS pin tells an ADC to convert its input to digital, then 730 nanoseconds later the data is ready for a 16 bit SPI transfer.
With an SPI clock of 50kHz, this should be able to run at close to 1MSPS.
for (int n = 0; n < NKNTS; n++ ) {
// For the ADC
DIGITALWRITE(CSPin,HIGH);
DELAYNANOSECONDS(ADC_WAIT_NANOSECONDS);
DIGITALWRITE(CSPin,LOW);
// For the oscilloscope
DIGITALWRITE(SPAREPIN,HIGH);
// 16 bit transfer
data[n] = SPI.transfer16(0xFFFF);
// For the oscilloscope
DIGITALWRITE(SPAREPIN,LOW);
}
`
And here is the oscilloscope trace. Notice that rather than one 16 bit transfer, we have two 8 bit transfers. And note that they are separated by 400 nsecs, but there is 1200 nsecs before and after.
Here is the implementation, for transfer16() it calls spi_transfer( .... length )
Arduino_Core_STM32/libraries/SPI/src/SPI.cpp
Lines 179 to 195 in 89a2951
uint16_t SPIClass::transfer16(uint16_t data, bool skipReceive) | |
{ | |
uint16_t tmp; | |
if (_spiSettings.bitOrder) { | |
tmp = ((data & 0xff00) >> 8) | ((data & 0xff) << 8); | |
data = tmp; | |
} | |
spi_transfer(&_spi, (uint8_t *)&data, (!skipReceive) ? (uint8_t *)&data : NULL, sizeof(uint16_t)); | |
if (_spiSettings.bitOrder) { | |
tmp = ((data & 0xff00) >> 8) | ((data & 0xff) << 8); | |
data = tmp; | |
} | |
return data; | |
} |
And, in utility/spi.c, we find that spi_transfer() simply loops over LL_SPI_TransmitData8( ).
Arduino_Core_STM32/libraries/SPI/src/utility/spi_com.c
Lines 482 to 550 in 89a2951
spi_status_e spi_transfer(spi_t *obj, const uint8_t *tx_buffer, uint8_t *rx_buffer, | |
uint16_t len) | |
{ | |
spi_status_e ret = SPI_OK; | |
uint32_t tickstart, size = len; | |
SPI_TypeDef *_SPI = obj->handle.Instance; | |
uint8_t *tx_buf = (uint8_t *)tx_buffer; | |
if (len == 0) { | |
ret = SPI_ERROR; | |
} else { | |
tickstart = HAL_GetTick(); | |
#if defined(SPI_CR2_TSIZE) | |
/* Start transfer */ | |
LL_SPI_SetTransferSize(_SPI, size); | |
LL_SPI_Enable(_SPI); | |
LL_SPI_StartMasterTransfer(_SPI); | |
#endif | |
while (size--) { | |
#if defined(SPI_SR_TXP) | |
while (!LL_SPI_IsActiveFlag_TXP(_SPI)); | |
#else | |
while (!LL_SPI_IsActiveFlag_TXE(_SPI)); | |
#endif | |
LL_SPI_TransmitData8(_SPI, tx_buf ? *tx_buf++ : 0XFF); | |
#if defined(SPI_SR_RXP) | |
while (!LL_SPI_IsActiveFlag_RXP(_SPI)); | |
#else | |
while (!LL_SPI_IsActiveFlag_RXNE(_SPI)); | |
#endif | |
if (rx_buffer) { | |
*rx_buffer++ = LL_SPI_ReceiveData8(_SPI); | |
} else { | |
LL_SPI_ReceiveData8(_SPI); | |
} | |
if ((SPI_TRANSFER_TIMEOUT != HAL_MAX_DELAY) && | |
(HAL_GetTick() - tickstart >= SPI_TRANSFER_TIMEOUT)) { | |
ret = SPI_TIMEOUT; | |
break; | |
} | |
} | |
#if defined(SPI_IFCR_EOTC) | |
// Add a delay before disabling SPI otherwise last-bit/last-clock may be truncated | |
// See https://github.com/stm32duino/Arduino_Core_STM32/issues/1294 | |
// Computed delay is half SPI clock | |
delayMicroseconds(obj->disable_delay); | |
/* Close transfer */ | |
/* Clear flags */ | |
LL_SPI_ClearFlag_EOT(_SPI); | |
LL_SPI_ClearFlag_TXTF(_SPI); | |
/* Disable SPI peripheral */ | |
LL_SPI_Disable(_SPI); | |
#else | |
/* Wait for end of transfer */ | |
while (LL_SPI_IsActiveFlag_BSY(_SPI)); | |
#endif | |
} | |
return ret; | |
} | |
#ifdef __cplusplus | |
} | |
#endif | |
You already have a LL_SPI_TransmitData16( ), under hardware/stm32/2.8.1/system/Drivers/
What is missing in this implementation is a spi_transfer16() and to change transfer16() to call it.
Thank you