Skip to main content
Associate III
July 8, 2024
Solved

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

  • July 8, 2024
  • 44 replies
  • 13217 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;
}

44 replies

Andrew Neil
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.

A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.
Associate III
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

    

 

Andrew Neil
Super User
July 8, 2024

Sorry, no.

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

A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.
Amel NASRI
ST 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

To give better visibility on the answered topics, please click on "Best Answer" on the reply which solved your issue or answered your question.
Associate III
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

Amel NASRI
ST 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

To give better visibility on the answered topics, please click on "Best Answer" on the reply which solved your issue or answered your question.
Associate III
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

}

ST 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;
}

 

 

In order to give better visibility on the answered topics, please click on 'Best answer' on the reply which solved your issue or answered your question. Saket_Om
Associate III
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