Skip to main content
Explorer
July 8, 2024
Solved

We need the register-based low level SPI R/W drive for STM32C031C6

  • July 8, 2024
  • 3 replies
  • 13045 views

1. CubeMX only allows HAL or LL for SPI and other peripherals. STM32C031C6 itself provides the good SPI clock rate. 

   SPI LL API function only allows two-byte R/W.. The slave STPM34 Meter IC requires 4-Byte SPI R/W. So we have to choose HAL SPI API calls, such as HAL_SPI_TransmitReceive().   

    However HAL introduces a big delay (15 us or more) between two consecutive HAL SPI-API calls. We need 6 SPI-API calls within 128us for our application,  This big delay inserted by HAL makes it impossible. .

2. The only solution is to write the register low level SPI R/W functions. Is it possible to write it under the current CubeMx IDE compiler for STM32C031C6? Could you provide us the solutions?

 

Thanks,

 

Rong

    This topic has been closed for replies.
    Best answer by Saket_Om

    Hello @RongShengWang 

     

    I updated my code. Could you check with this please.

    void SPI_Transmit_Receive(const uint8_t *data_tx, uint8_t *data_rx, uint8_t size)
    {
    	uint8_t rx_size= size;
    	uint8_t tx_size = size;
    
    
     /* Set the Rx Fifo threshold */
     if (size > 1U)
     {
     /* Set fiforxthreshold according the reception data length: 16bit */
     CLEAR_BIT(SPI1->CR2, SPI_RXFIFO_THRESHOLD);
     }
     else
     {
     /* Set fiforxthreshold according the reception data length: 8bit */
     SET_BIT(SPI1->CR2, SPI_RXFIFO_THRESHOLD);
     }
    
     /* Enable SPI */
     SPI1->CR1 |= SPI_CR1_SPE;
    
    
     while (size > 0)
     {
     /* Wait until TXE (Transmit buffer empty) flag is set */
     while (!__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_TXE));
    
     /* Transmit data */
     if (tx_size > 1U)
     {
     SPI1->DR = *((uint16_t *)data_tx);
     data_tx += sizeof(uint16_t);
     tx_size -= 2U;
     }
     else
     {
     *(__IO uint8_t *)&SPI1->DR = (*data_tx);
     data_tx ++;
     tx_size --;
     }
    
     /* Wait until RXNE (Receive buffer not empty) flag is set */
     while (!__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_RXNE));
    
    
    		if (rx_size > 1U)
     {
     *((uint16_t *)data_rx) = (uint16_t)SPI1->DR;
     data_rx += sizeof(uint16_t);
     rx_size -= 2U;
     if (rx_size <= 1U)
     {
     /* Set RX Fifo threshold before to switch on 8 bit data size */
     SET_BIT(SPI1->CR2, SPI_RXFIFO_THRESHOLD);
     }
     }
     else
     {
     (*(uint8_t *)data_rx) = *(__IO uint8_t *)&SPI1->DR;
     data_rx++;
     rx_size --;
     }
     }
    
     /* Wait until the SPI is not busy */
     while (__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_BSY));
    
     /* Disable SPI */
     SPI1->CR1 &= ~SPI_CR1_SPE;
    }

    3 replies

    Super User
    July 8, 2024

    The auto-generated code just does the initialisation stuff - you can write your own register-level code to do the runtime sending thereafter.

    Explorer
    July 8, 2024

    Hi, Andrew Neil,

        "you can write your own register-level code to do the runtime" - Could you provide me with a reference code?

         Thanks,

     

    Rong

        

     

    Super User
    July 8, 2024

    Sorry, no.

    I suggest you look at the LL code, and adapt that...

    Technical Moderator
    July 8, 2024

    Hi @RongShengWang ,

    Here 2 examples with SPI LL APIs using STM32C031: https://github.com/STMicroelectronics/STM32CubeC0/tree/main/Projects/NUCLEO-C031C6/Examples_LL/SPI.

    You can start from the IOC file of each example and adapt it to yours needs. 

    -Amel

    Explorer
    July 8, 2024

     

    Hi, Almel,

       SPI LL API function only allows two-byte R/W each time. The slave side requires 4-Byte SPI R/W each time. We can't use LL for the Master SPI.

       HAL introduces a big delay (15 us -20 us) between two consecutive HAL SPI-API calls (such as two calls to HAL_SPI_TransmitReceive()).  We need 6 SPI-API calls within 128us for our application,  This big delay inserted by HAL between any two consecutive HAL SPI-API calls makes  it impossible. .

    Could you explain how your LL example can overcome the problem in the above?

     

    Thanks

     

    Rong

    Technical Moderator
    July 9, 2024

    Hi @RongShengWang ,

    I see that you'll be trying the direct access to registers. Let us know the results.

    In LL side, you can try 4 calls of LL_SPI_TransmitData8() or 2 calls of LL_SPI_TransmitData16().

    Based on what you said previously, you made tests with HAL_SPI_TransmitReceive() which is introducing delays. I suggest you make the trial with LL.

    -Amel

    Explorer
    July 16, 2024

         I need your help with correcting my the register based SPI code.

         The SPI Master is STM32C031C6 Nucleo Eva board.

         The SPI Slave is STPM34 (Meter IC) Eva Board. 

         Test Result:

    1. The HAL Function HAL_SPI_TransmitReceive(...) works, but introduces a long delay.

    2. I tried to write the register based SPI code to do the same as the above HAL function,                              but it does not work.

         Could you make some correction on the code below? Or provide me with some working code?  Thank you very much!

     

    void SPI_Transmit_Receive (const uint8_t *data_tx, uint8_t *data_rx, uint8_t size)

    {

    uint8_t size1 = size;

    SPI1->CR1 |= (1u << 6); //ENABLE SPI

    while (size)

    {

        if (size == size1)

        {

           if (__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_TXE))

         {

             SPI1->DR = *((uint16_t *)data_tx);

             data_tx += sizeof(uint16_t);

            size -= 2U;

         }

      }

      if (size1 > size)

      {

          if (__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_RXNE))

         {

    *       (uint16_t *)data_rx = (uint16_t)SPI1->DR;

           data_rx += sizeof(uint16_t);

           size1 -= 2U;

        }

      }

    }

         SPI1->CR1 &= ~(1u << 6); //DISABLE SPI

    }

    Technical Moderator
    July 16, 2024

    Hello @RongShengWang ,

     

    Is the data size set to greater than SPI_DATASIZE_8BIT? If yes, could you please try with this code?

     

    void SPI_Transmit_Receive(const uint8_t *data_tx, uint8_t *data_rx, uint8_t size)
    {
    	uint8_t rx_size= size;
    	uint8_t tx_size = size;
    	
    	/* Set the Rx Fifo threshold */
     if (size > 1U)
     {
     /* Set fiforxthreshold according the reception data length: 16bit */
     CLEAR_BIT(SPI1->CR2, SPI_RXFIFO_THRESHOLD);
    	}
    	
     /* Enable SPI */
     SPI1->CR1 |= SPI_CR1_SPE;
    
    
     while (size > 0)
     {
     /* Wait until TXE (Transmit buffer empty) flag is set */
     while (!__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_TXE));
    
     /* Transmit data */
     if (tx_size > 1U)
     {
     SPI1->DR = *((uint16_t *)data_tx);
     data_tx += sizeof(uint16_t);
     tx_size -= 2U;
     }
     else
     {
     *(__IO uint8_t *)&SPI1->DR = (*data_tx);
     data_tx ++;
     tx_size --;
     }
    
     /* Wait until RXNE (Receive buffer not empty) flag is set */
     while (!__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_RXNE));
    		
    		
    		if (rx_size > 1U)
     {
     *((uint16_t *)data_rx) = (uint16_t)SPI1->DR;
     data_rx += sizeof(uint16_t);
     rx_size -= 2U;
     if (rx_size <= 1U)
     {
     /* Set RX Fifo threshold before to switch on 8 bit data size */
     SET_BIT(SPI1->CR2, SPI_RXFIFO_THRESHOLD);
     }
     }
     else
     {
     (*(uint8_t *)data_rx) = *(__IO uint8_t *)&SPI1->DR;
     data_rx++;
     rx_size --;
     }
     }
    
     /* Wait until the SPI is not busy */
     while (__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_BSY));
    
     /* Disable SPI */
     SPI1->CR1 &= ~SPI_CR1_SPE;
    }

     

     

    Explorer
    July 16, 2024

    Hi, Omar,

         Thank you very much for your timely help. I found my setting is -

         hspi1.Init.DataSize = SPI_DATASIZE_8BIT; - Was it a big problem with my code?

         Should I change it to hspi1.Init.DataSize = SPI_DATASIZE_16BIT to run your code?

         Thank you very much!

    Rong