AnsweredAssumed Answered

[Bug] STM32F091 HAL_SPI_TransmitReceive ?

Question asked by sterbik.marco on Feb 17, 2016
Latest reply on May 16, 2016 by zaleski.mikita
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

Outcomes