cancel
Showing results for 
Search instead for 
Did you mean: 

STM32G051 as SPI receive-only slave is not receiving data

StevenG
Associate III

I have a custom board with an STM32G051.  The STM32 is a SPI receive only device.  PA1 is SCLK, PA2 is MOSI, PB0 is nSS (which is hardware controlled).  The beginnings of the project where created with CubeMX so the clocks should all be set correctly.

The SPI transactions are simple 12-bit data with an active low chip select.  Note that the MISO signal is not connected on the board.  The master device does not read any data from the STM32G051.

Simulated_SPI_data.png

The SPI peripheral is setup as below:

   hspi1.Instance = SPI1;
   hspi1.Init.Mode = SPI_MODE_SLAVE;
   hspi1.Init.Direction = SPI_DIRECTION_2LINES_RXONLY;
   hspi1.Init.DataSize = SPI_DATASIZE_12BIT;
   hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
   hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
   hspi1.Init.NSS = SPI_NSS_HARD_INPUT;
   hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
   hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
   hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
   hspi1.Init.CRCPolynomial = 7;
   hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
   hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;

 and

void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{

   GPIO_InitTypeDef GPIO_InitStruct = {0};
   if(spiHandle->Instance==SPI1)
   {
      /* USER CODE BEGIN SPI1_MspInit 0 */

      /* USER CODE END SPI1_MspInit 0 */
      /* SPI1 clock enable */
      __HAL_RCC_SPI1_CLK_ENABLE();

      __HAL_RCC_GPIOA_CLK_ENABLE();
      __HAL_RCC_GPIOB_CLK_ENABLE();

      /**SPI1 GPIO Configuration
    PA1     ------> SPI1_SCK
    PA2     ------> SPI1_MOSI
       */
      GPIO_InitStruct.Pin = SCLK_Pin|MOSI_Pin;
      GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
      GPIO_InitStruct.Pull = GPIO_PULLDOWN;
      GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
      GPIO_InitStruct.Alternate = GPIO_AF0_SPI1;
      GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
      HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

      GPIO_InitStruct.Pin = nCS_Pin;
      GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
      GPIO_InitStruct.Pull = GPIO_PULLDOWN;
      GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
      GPIO_InitStruct.Alternate = GPIO_AF0_SPI1;
      GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
      HAL_GPIO_Init( nCS_GPIO_Port, &GPIO_InitStruct);

      /* SPI1 interrupt Init */
      HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
      HAL_NVIC_EnableIRQ(SPI1_IRQn);
      /* USER CODE BEGIN SPI1_MspInit 1 */

      /* USER CODE END SPI1_MspInit 1 */
   }
}

 

There is a lot of logic to handle all the many different ways SPI can be configured in the HAL_SPI_Receive() routine so I made a copy of it and stripped out sections that are not relevant to this project.  Note that I did this step as part of debugging when I couldn't get the SPI peripheral to receive anything.  My thought was to reduce the amount of code to review/debug.

//
// based on HAL_SPI_Receive() with a lot of changes that are specific to this project
//
HAL_StatusTypeDef nonHAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
   uint32_t 	tickstart;
   uint32_t	statusReg;


   register HAL_StatusTypeDef errorcode = HAL_OK;

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

   /* Process Locked */
   __HAL_LOCK(hspi);

   HAL_SPI_Abort( hspi );

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

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

   __HAL_SPI_DISABLE(hspi);

   /* Set the transaction information */
   hspi->State       = HAL_SPI_STATE_BUSY_RX;
   hspi->ErrorCode   = HAL_SPI_ERROR_NONE;
   hspi->pRxBuffPtr  = (uint8_t *)pData;
   hspi->RxXferSize  = Size;
   hspi->RxXferCount = Size;

   /*Init field not used in handle to zero */
   hspi->pTxBuffPtr  = (uint8_t *)NULL;
   hspi->TxXferSize  = 0U;
   hspi->TxXferCount = 0U;
   hspi->RxISR       = NULL;
   hspi->TxISR       = NULL;


   // Set FIFO for 8-bit length since it will never get all 16 bits
   // with a 12-bit transaction
   SET_BIT(hspi->Instance->CR2, SPI_RXFIFO_THRESHOLD);

   /* Enable SPI peripheral */
   __HAL_SPI_ENABLE(hspi);

   /* Receive 1 piece of data in 12 Bit mode */

   statusReg = hspi->Instance->SR & SPI_FLAG_RXNE;
   while( statusReg == 0 )
   {
      hspi->Instance->DR = 0x7E;

      // wait for SPI_FLAG_RXNE to be set
      if( hspi->Instance->SR & SPI_FLAG_FRLVL )
      {
	 // Wow, actually might have gotten something
	 asm("nop");
      }

      /* Timeout management */
      if ((((HAL_GetTick() - tickstart) >=  Timeout) && (Timeout != HAL_MAX_DELAY)) || (Timeout == 0U))
      {
	 errorcode = HAL_TIMEOUT;
	 hspi->State = HAL_SPI_STATE_READY;
	 break;
      }

      statusReg = hspi->Instance->SR & SPI_FLAG_RXNE;
   }

   // To get here, Receiver Not Empty must be true or the transaction timed out
   if( errorcode != HAL_TIMEOUT )
   {
      *((uint16_t *)hspi->pRxBuffPtr) = (uint16_t)hspi->Instance->DR;
      hspi->pRxBuffPtr += sizeof(uint16_t);
      hspi->RxXferCount--;
   }

   hspi->State = HAL_SPI_STATE_READY;


   __HAL_UNLOCK(hspi);
   return errorcode;
}

 

This is really a minimal project as the pin connections below show:

Chip.png

 

If anyone has suggestions (other than switching to a bit-bang solution) I would be happy to try it.  I have tried using the SSM (software slave management) and the EXTI0 falling edge interrupt but that didn't work either.

 

I checked the errata sheet but didn't see anything listed about SPI1 not working in receive-only mode.

 

As a debug attempt I did, at one point, use the debugger to write a value to the DR register.  At that point the TXE status bit went active and then was cleared by the hardware.  That seems to indicate that the nSS signal is being seen but I don't remember if that was with the EXTI0 based SSM mode or with the hardware mode.

 

I did try to use the example projects of SPI_FullDuplex_ComPolling_Slave and SPI_FullDuplex_ComPolling_Master.  I was able to get the Master version to run but the Slave version also did not receive any data; however, I did have to make a number of changes to get both versions to compile and run.

1 REPLY 1
TDK
Super User

You haven't just pared down the HAL code, you've materially changed it by adding additional functions and changing how it does things. For example, the call to HAL_SPI_Abort and the write to DR.

 

I was able to load the SPI_FullDuplex_ComPolling_Master example from STM32CubeMX and compile it without any changes. I'd suggest starting from there and from using the unmodified HAL functions.

TDK_0-1765839029088.png

 

You mention the debugger. Reading DR in the debugger will clear the RXNE flag which can cause side effects in the code.

 

> The master device does not read any data from the STM32G051.

How exactly was this determined? Did code time out and return HAL_TIMEOUT? Or was the data not as expected? Were the CLK or NSS signals active?

 

RXONLY mode is generally not great. Clocks continue to be sent until the peripheral is disabled. Standard two-line mode is recommended. Leave the MOSI pin uninitialized if it's not needed. You can use the same buffer to send and receive.

If you feel a post has answered your question, please click "Accept as Solution".