STM32F4 how to properly use SPI?

Thread Starter

zazas321

Joined Nov 29, 2015
936
Hello. I am struggling to understand how to properly use SPI interface.
As far as I understand, every SPI communication starts with an instruction byte which specifies whether we want to READ or WRITE and then followed by a register address. After that, the DATA that we want to write or read.
SPI slave device:
https://www.mouser.co.uk/datasheet/2/949/w25m02gv_revf_050918_unsecured-1489896.pdf

Something like that :

upload_2019-8-1_12-24-25.png

I have generated my STM32 project using STM32cubeMX and the generated SPI libraries dont make any sense to me at all.
C:
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
  uint32_t tickstart;
  HAL_StatusTypeDef errorcode = HAL_OK;
  uint16_t initial_TxXferCount;

  /* Check Direction parameter */
  assert_param(IS_SPI_DIRECTION_2LINES_OR_1LINE(hspi->Init.Direction));

  /* Process Locked */
  __HAL_LOCK(hspi);

  /* Init tickstart for timeout management*/
  tickstart = HAL_GetTick();
  initial_TxXferCount = Size;

  if (hspi->State != HAL_SPI_STATE_READY)
  {
    errorcode = HAL_BUSY;
    goto error;
  }

  if ((pData == NULL) || (Size == 0U))
  {
    errorcode = HAL_ERROR;
    goto error;
  }

  /* Set the transaction information */
  hspi->State       = HAL_SPI_STATE_BUSY_TX;
  hspi->ErrorCode   = HAL_SPI_ERROR_NONE;
  hspi->pTxBuffPtr  = (uint8_t *)pData;
  hspi->TxXferSize  = Size;
  hspi->TxXferCount = Size;

  /*Init field not used in handle to zero */
  hspi->pRxBuffPtr  = (uint8_t *)NULL;
  hspi->RxXferSize  = 0U;
  hspi->RxXferCount = 0U;
  hspi->TxISR       = NULL;
  hspi->RxISR       = NULL;

  /* Configure communication direction : 1Line */
  if (hspi->Init.Direction == SPI_DIRECTION_1LINE)
  {
    SPI_1LINE_TX(hspi);
  }

#if (USE_SPI_CRC != 0U)
  /* Reset CRC Calculation */
  if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
  {
    SPI_RESET_CRC(hspi);
  }
#endif /* USE_SPI_CRC */

  /* 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 data in 16 Bit mode */
  if (hspi->Init.DataSize == SPI_DATASIZE_16BIT)
  {
    if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (initial_TxXferCount == 0x01U))
    {
      hspi->Instance->DR = *((uint16_t *)hspi->pTxBuffPtr);
      hspi->pTxBuffPtr += sizeof(uint16_t);
      hspi->TxXferCount--;
    }
    /* Transmit data in 16 Bit mode */
    while (hspi->TxXferCount > 0U)
    {
      /* Wait until TXE flag is set to send data */
      if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE))
      {
        hspi->Instance->DR = *((uint16_t *)hspi->pTxBuffPtr);
        hspi->pTxBuffPtr += sizeof(uint16_t);
        hspi->TxXferCount--;
      }
      else
      {
        /* Timeout management */
        if ((((HAL_GetTick() - tickstart) >=  Timeout) && (Timeout != HAL_MAX_DELAY)) || (Timeout == 0U))
        {
          errorcode = HAL_TIMEOUT;
          goto error;
        }
      }
    }
  }
  /* Transmit data in 8 Bit mode */
  else
  {
    if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (initial_TxXferCount == 0x01U))
    {
      *((__IO uint8_t *)&hspi->Instance->DR) = (*hspi->pTxBuffPtr);
      hspi->pTxBuffPtr += sizeof(uint8_t);
      hspi->TxXferCount--;
    }
    while (hspi->TxXferCount > 0U)
    {
      /* Wait until TXE flag is set to send data */
      if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE))
      {
        *((__IO uint8_t *)&hspi->Instance->DR) = (*hspi->pTxBuffPtr);
        hspi->pTxBuffPtr += sizeof(uint8_t);
        hspi->TxXferCount--;
      }
      else
      {
        /* Timeout management */
        if ((((HAL_GetTick() - tickstart) >=  Timeout) && (Timeout != HAL_MAX_DELAY)) || (Timeout == 0U))
        {
          errorcode = HAL_TIMEOUT;
          goto error;
        }
      }
    }
  }

This is the generated code for the SPI transmit, however, I dont seem to understand how do I address the instruction byte and the register address?
Have anyone used stm32cubemx generated SPI library and could give me some guidance?
 

Attachments

Thread Starter

zazas321

Joined Nov 29, 2015
936
I am using crossworks studio c++. I can narrow down the question a little bit. From what I have been able to understand, the function :
HAL_SPI_Transmit(SPI_HandleTypeDef *hspi,uint8_t*pData,uint16_t Size,uint32_t Timeout) uses pData which is a buffer that contains operating code, address and the data in one single buffer.

However, what I am getting confused about is that in the STM SPI documentation, command code byte structure is very different to my SPI slave device.

upload_2019-8-1_14-18-29.png

As can be seen from the picture above, Operating code and the address is one single byte whereas in my SPI slave device documentation, Operating code itself is 1 byte :
upload_2019-8-1_14-20-4.png



I dont see how that makes any sense, because from the ST documentation, READ sequency is initiated by writing 01 and then the address whereas the Slave device documentation says that to initiate READ, the Operating code must be 03h which is 0000 0011 and then followed by 3 dummy bytes
 

MrChips

Joined Oct 2, 2009
30,707
You are looking at two different opcodes.
The first one from the ST reference manual is the hardware level device control.
SPI is basically a serial in/out shift register. The master writes to the HW to initiates a transfer. Either or both master and slave can read back the shifted data, MOSI and MISO shifted data.

The second opcodes refer to a particular SW protocol using packet transmission supplied by the SW libraries and have nothing to do with the HW opcodes.
 

Thread Starter

zazas321

Joined Nov 29, 2015
936
Thank you for your response. What do you refer to HW and SW?

In that case, how do I properly construct a SPI frame in order to write some data to my external flash ( SPI slave device).
 

MrChips

Joined Oct 2, 2009
30,707
HW = hardware
SW = software

The ST reference manual describes how you would control the SPI hardware. You don't need this.
What you want are examples on how to read and write frames based on the SW protocol. Look for examples from CrossWorks platform.
 

Thread Starter

zazas321

Joined Nov 29, 2015
936
Thank you both for replies. There are barely any examples from CrossWorks platform, I guess no one is using it for a good reason - its not very good.

Also, I have not yet implemented the Quad SPI interface on my external flash - for now I am just using basic SPI interface. Anyways, I sort of get that now. Lets say If i wanted to read some data:

C:
uint8_t SPI_buffer[100];
uint16_t le = 8;


buffer[0] = 0x03; // OP CODE
buffer[1] = 0x00; // DUMMY BYTE
buffer[2] = 0x00; // DUMMY BYTE
buffer[3] = 0x00; // DUMMY BYTE


HAL_SPI_Receive(&hspi1, buffer, len, 0xff);
Refering to the datasheet of the SPI slave device:
https://www.winbond.com/resource-files/w25m02gv revb 070115.pdf


However, I am unsure about write operation, as the device datasheet does not have a simple instruction to Write, instead it has Write enable which I assume I would have to use. I have found this article somewhere, I am not sure how relevant it is for me yet - it seems way overcomplicated for a simple Write command
upload_2019-8-2_9-29-38.png
 
Top