cancel
Showing results for 
Search instead for 
Did you mean: 

[Bug] STM32F091 HAL_SPI_TransmitReceive ?

marcosterbik9
Associate
Posted on February 17, 2016 at 07:54

Setup

STM32F091 connected to a SiliconLabs Si4432 Transceiver for SPI communication. Debugging MCU through a STLINK-v2. SPI Config on the controller:

SPI_HandleTypeDef *shandle;
SPI_InitTypeDef *sinit;
GPIO_InitTypeDef ginit;
uint8_t c;
// SPI AF pins
ginit.Mode = GPIO_MODE_AF_PP;
ginit.Pull = GPIO_PULLDOWN;
ginit.Speed = GPIO_SPEED_HIGH;
ginit.Alternate = GPIO_AF0_SPI2;
for
(c = 0; c < 3; c++) {
ginit.Pin = g_spi_pins[c].pin;
HAL_GPIO_Init(g_spi_pins[c].gpio, &ginit);
}
ginit.Pin = g_spi_nsel.pin;
ginit.Mode = GPIO_MODE_OUTPUT_PP;
ginit.Alternate = 0;
HAL_GPIO_Init(g_spi_nsel.gpio, &ginit);
hal_gpio_set(g_spi_nsel);
__HAL_RCC_SPI2_CLK_ENABLE();
shandle = &g_spi_extern;
shandle->Instance = SPI2;
sinit = &(shandle->Init);
sinit->Mode = SPI_MODE_MASTER;
sinit->Direction = SPI_DIRECTION_2LINES;
sinit->DataSize = SPI_DATASIZE_8BIT;
sinit->CLKPolarity = SPI_POLARITY_LOW;
sinit->CLKPhase = SPI_PHASE_1EDGE;
sinit->NSS = SPI_NSS_SOFT;
sinit->BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
sinit->FirstBit = SPI_FIRSTBIT_MSB;
sinit->TIMode = SPI_TIMODE_DISABLE;
sinit->CRCCalculation = SPI_CRCCALCULATION_DISABLE;
sinit->CRCPolynomial = 3;
sinit->CRCLength = SPI_CRC_LENGTH_8BIT;
sinit->NSSPMode = SPI_NSS_PULSE_DISABLE;
HAL_SPI_Init(shandle);

Bug description A bit complicated. When connected to the debugger I am able to read single register from the connected chip, but reading a bulk of them (i.e. fifo) results in a hard-fault in HAL_SPI_TransmitReceive when writing data to the target buffer (see code section below). When not connected to the debugger, there seems to be no hard-fault, but communication to the chip still isn't working correctly (I am not able to read out received packets).

/* Wait until RXNE flag is reset */
if
((hspi->RxXferCount > 0) && ((hspi->Instance->SR & SPI_FLAG_RXNE) == SPI_FLAG_RXNE))
{
if
(hspi->RxXferCount > 1)
{
*((uint16_t*)hspi->pRxBuffPtr) = hspi->Instance->DR; 
// ---- HARD FAULT HERE ----
hspi->pRxBuffPtr += 
sizeof
(uint16_t);
hspi->RxXferCount -= 2;
if
(hspi->RxXferCount <= 1)
{
/* set fiforxthresold before to switch on 8 bit data size */
SET_BIT(hspi->Instance->CR2, SPI_RXFIFO_THRESHOLD);
}
}
else
{
(*hspi->pRxBuffPtr++) = *(__IO uint8_t *)&hspi->Instance->DR;
hspi->RxXferCount--;
}
}
if
((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick()-tickstart) >= Timeout))
{
errorcode = HAL_TIMEOUT;
goto
error;
}
}
}

Solution / possible Bugfix After digging deeper through the HAL_SPI_TransmitReceive code I was a bit confused about the, let's call it 'hybrid-communication' when configured to SPI_DATASIZE_8BIT (data is written/read in 16bit mode, as long as there is more than 1 byte remaining). So I just threw this stuff out and see there - it works now! Below is my new HAL_SPI_TransmitReceive that's successfully working for me now.

/**
* @brief Transmit and Receive an amount of data in blocking mode.
* @param hspi: pointer to a SPI_HandleTypeDef structure that contains
* the configuration information for SPI module.
* @param pTxData: pointer to transmission data buffer
* @param pRxData: pointer to reception data buffer
* @param Size: amount of data to be sent and received
* @param Timeout: Timeout duration
* @retval HAL status
*/
HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout)
{
__IO uint16_t tmpreg;
uint32_t tickstart = HAL_GetTick();
HAL_StatusTypeDef errorcode = HAL_OK;
assert_param(IS_SPI_DIRECTION_2LINES(hspi->Init.Direction));
/* Process Locked */
__HAL_LOCK(hspi);
if
(hspi->State != HAL_SPI_STATE_READY)
{
errorcode = HAL_BUSY;
goto
error;
}
if
((pTxData == NULL) || (pRxData == NULL) || (Size == 0))
{
errorcode = HAL_ERROR;
goto
error;
}
hspi->State = HAL_SPI_STATE_BUSY_TX_RX;
hspi->ErrorCode = HAL_SPI_ERROR_NONE;
hspi->pRxBuffPtr = pRxData;
hspi->RxXferCount = Size;
hspi->RxXferSize = Size;
hspi->pTxBuffPtr = pTxData;
hspi->TxXferCount = Size;
hspi->TxXferSize = Size;
/* Reset CRC Calculation */
if
(hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
{
SPI_RESET_CRC(hspi);
}
/* Set the Rx Fido threshold */
if
(hspi->Init.DataSize > SPI_DATASIZE_8BIT)
{
/* set fiforxthreshold according the reception data length: 16bit */
CLEAR_BIT(hspi->Instance->CR2, SPI_RXFIFO_THRESHOLD);
}
else
{
/* set fiforxthreshold according the reception data length: 8bit */
SET_BIT(hspi->Instance->CR2, SPI_RXFIFO_THRESHOLD);
}
/* Check if the SPI is already enabled */
if
((hspi->Instance->CR1 &SPI_CR1_SPE) != SPI_CR1_SPE) {
/* Enable SPI peripheral */
__HAL_SPI_ENABLE(hspi);
}
/* Transmit and Receive data in 16 Bit mode */
if
(hspi->Init.DataSize > SPI_DATASIZE_8BIT)
{
while
((hspi->TxXferCount > 0 ) || (hspi->RxXferCount > 0))
{
/* Check TXE flag */
if
((hspi->TxXferCount > 0) && ((hspi->Instance->SR & SPI_FLAG_TXE) == SPI_FLAG_TXE))
{
hspi->Instance->DR = *((uint16_t *)hspi->pTxBuffPtr);
hspi->pTxBuffPtr += 
sizeof
(uint16_t);
hspi->TxXferCount--;
/* Enable CRC Transmission */
if
((hspi->TxXferCount == 0) && (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE))
{
hspi->Instance->CR1|= SPI_CR1_CRCNEXT;
}
}
/* Check RXNE flag */
if
((hspi->RxXferCount > 0) && ((hspi->Instance->SR & SPI_FLAG_RXNE) == SPI_FLAG_RXNE))
{
*((uint16_t *)hspi->pRxBuffPtr) = hspi->Instance->DR;
hspi->pRxBuffPtr += 
sizeof
(uint16_t);
hspi->RxXferCount--;
}
if
((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick()-tickstart) >= Timeout))
{
errorcode = HAL_TIMEOUT;
goto
error;
}
}
}
/* Transmit and Receive data in 8 Bit mode */
else
{
while
((hspi->TxXferCount > 0) || (hspi->RxXferCount > 0))
{
/* check TXE flag */
if
((hspi->TxXferCount > 0) && ((hspi->Instance->SR & SPI_FLAG_TXE) == SPI_FLAG_TXE))
{
*(__IO uint8_t *)&hspi->Instance->DR = (*hspi->pTxBuffPtr++);
hspi->TxXferCount--;
/* Enable CRC Transmission */
if
((hspi->TxXferCount == 0) && (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE))
{
hspi->Instance->CR1 |= SPI_CR1_CRCNEXT;
}
}
/* Wait until RXNE flag is reset */
if
((hspi->RxXferCount > 0) && ((hspi->Instance->SR & SPI_FLAG_RXNE) == SPI_FLAG_RXNE))
{
(*hspi->pRxBuffPtr++) = *(__IO uint8_t *)&hspi->Instance->DR;
hspi->RxXferCount--;
}
if
((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick()-tickstart) >= Timeout))
{
errorcode = HAL_TIMEOUT;
goto
error;
}
}
}
/* Read CRC from DR to close CRC calculation process */
if
(hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
{
/* Wait until TXE flag */
if
(SPI_WaitFlagStateUntilTimeout(hspi, SPI_FLAG_RXNE, SPI_FLAG_RXNE, Timeout) != HAL_OK)
{
/* Error on the CRC reception */
hspi->ErrorCode|= HAL_SPI_ERROR_CRC;
errorcode = HAL_TIMEOUT;
goto
error;
}
if
(hspi->Init.DataSize == SPI_DATASIZE_16BIT)
{
tmpreg = hspi->Instance->DR;
/* To avoid GCC warning */
UNUSED(tmpreg);
}
else
{
tmpreg = *(__IO uint8_t *)&hspi->Instance->DR;
/* To avoid GCC warning */
UNUSED(tmpreg);
if
(hspi->Init.CRCLength == SPI_CRC_LENGTH_16BIT)
{
if
(SPI_WaitFlagStateUntilTimeout(hspi, SPI_FLAG_RXNE, SPI_FLAG_RXNE, Timeout) != HAL_OK)
{
/* Error on the CRC reception */
hspi->ErrorCode|= HAL_SPI_ERROR_CRC;
errorcode = HAL_TIMEOUT;
goto
error;
}
tmpreg = *(__IO uint8_t *)&hspi->Instance->DR;
/* To avoid GCC warning */
UNUSED(tmpreg);
}
}
}
/* Check if CRC error occurred */
if
(__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_CRCERR) != RESET)
{
hspi->ErrorCode|= HAL_SPI_ERROR_CRC;
/* Clear CRC Flag */
__HAL_SPI_CLEAR_CRCERRFLAG(hspi);
errorcode = HAL_ERROR;
}
/* Check the end of the transaction */
if
(SPI_EndRxTxTransaction(hspi,Timeout) != HAL_OK)
{
hspi->ErrorCode = HAL_SPI_ERROR_FLAG;
}
if
(hspi->ErrorCode != HAL_SPI_ERROR_NONE)
{
errorcode = HAL_ERROR;
}
error :
hspi->State = HAL_SPI_STATE_READY;
__HAL_UNLOCK(hspi);
return
errorcode;
}

Hope this helps you. Best regards Marco #stm32f0-hal-spi
2 REPLIES 2
Amel NASRI
ST Employee
Posted on February 17, 2016 at 16:55

Hi sterbik.marco,

Thanks for these comments that really need investigation from your side and our one.

In RM0360, it is said: ''When the data frame size fits into one byte (less than or equal to 8 bits), data packing is used automatically when any read or write 16-bit access is performed on the SPIx_DR register. The double data frame pattern is handled in parallel in this case. At first, the SPI operates using the pattern stored in the LSB of the accessed word, then with the other half stored in the MSB.''

This explains the current implementation in the case of 8 bits mode.

But this doesn't explain why you got the hardfault and why it is working as expected with the modifications you made.

-Mayla-

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

zaleski
Associate
Posted on May 16, 2016 at 14:42

The problem is in:

hspi->Instance->DR = *((uint16_t*)hspi->pTxBuffPtr);

and

*((uint16_t *)hspi->pRxBuffPtr) = hspi->Instance->DR;

In 8-bit mode the address pTxBuffPtr (pRxBuffPtr) may be not word-aligned. The casting (uint16_t*)hspi->pTxBuffPtr is the cause of hard-fault.