cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F7 SPI Runtime OR Memory access problems

AWent
Associate III

Hi Community,

I have a STM32F769I as a SPI slave.

I want to receive up to 6kByte data in 244 Byte packets (consequently up to 25 SPI packets á 244 Byte).

I am using the HAL_SPI_TxRxCpltCallback function. This function will be called after every 244 Byte packet; and stores the received data in a 6 kByte data buffer. After all data has been received a flag will be set to activate a print function in the main.

For now, the 6 kByte (dummy) data frames will be send every 2 seconds by the SPI master.

However, the STM32 sometimes misses SPI packets and consequently mess up the data.

As soon as I exceed an amount of data (4392 Byte = 18 packets á 244 bytes) the STM misses packets in a transaction and thus the data will only be printed after every second transaction.

Furthermore, memory access problems occure every now and then ending up in the HardFault_handler.

I think, I am facing some timing problems due to interrupts as well as memory access problems.

Any suggestions?

1 ACCEPTED SOLUTION

Accepted Solutions
KnarfB
Principal III

Hmm. The following works reliably for me copy-free at 12 MBit/s on two Nucleo STM32F042K6 boards (transmit master + receive slave) with STM32CubeMX generated code skeleton:

Transmitter:

  while (1)
  {
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
	  HAL_GPIO_WritePin( LD3_GPIO_Port, LD3_Pin, GPIO_PIN_SET );
	  HAL_StatusTypeDef status = HAL_SPI_Transmit_DMA( &hspi1, buffer, len );
	  if( status != HAL_OK ) {
		  Error_Handler();
	  }
	  HAL_GPIO_WritePin( LD3_GPIO_Port, LD3_Pin, GPIO_PIN_RESET );
	  HAL_Delay(100);
  }

Receiver:

char buffer[512*8];
int i=1;
volatile int done = 0;
 
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
	HAL_StatusTypeDef status = HAL_SPI_Receive_DMA( &hspi1, buffer+i*512, 512 );
	i++;
	if(i==8) {
		i=0;
		done = 1;
	}
}

and in main():

HAL_StatusTypeDef status = HAL_SPI_Receive_DMA( &hspi1, buffer, 512 );  // very first DMA
  for(;;) {
	  if(done) {
		  HAL_UART_Transmit( &huart2, buffer, sizeof(buffer), HAL_MAX_DELAY );
		  done=0;
	  }
  }

Frank

View solution in original post

13 REPLIES 13
AWent
Associate III

Due to the SPI transfer.

Attached are 2 oscilloscope captures of a 5800 byte (netto) (5856 byte brutto to fill up the 24th packet) frame transfer.

So all data will be transfered every time. I checked that several times.

0690X000009YQ4NQAW.png0690X000009YQ4IQAW.png

AWent
Associate III

These are snippets of my SPI Receive log (print command).

I only print the first and the last 2% of the data, otherwise the printing would take too long.

Here I transfered 5800 byte netto (5856 byte brutto to fill up the 24th acket again).

First the transfer worked fine.

Then the MCU hang up while receiving the next data frame.

0690X000009YQ5fQAG.jpg

A 6 kByte data frame wouldn't even work at the first transaction. It hangs up immediately.0690X000009YQ5zQAG.jpg

By the way: The dummy data I transfer is just a counter counting up in every byte. Just the first byte is the net amount of data. And the last byte is a 0xFF to indicate the end of the dummy frame.

AWent
Associate III

And some code snippets:

#define SPI_BUFFERSIZE 244
#define FRAME_BUFFER_SIZE 6000
 
/* Data handling */
static uint8_t frame_buffer[FRAME_BUFFER_SIZE];
static uint16_t data_in_frame_buffer;
static uint16_t frame_size;
 
static volatile bool SPI_RX_FLAG = false;
static volatile bool PRINT_DATA_FLAG = false;

SPI-Rx-Callback-function

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
	BSP_LED_Toggle(LED2);
	SPI_RX_FLAG = true;
	
	/* Receive & Store Data */
	for(int i=0 ; i < hspi->RxXferSize ; i++)
	{
		frame_buffer[data_in_frame_buffer] = aRxBuffer[i];
		data_in_frame_buffer++;
	}
	
	/* Read frame_size from first Rx-Packet */
	if(data_in_frame_buffer <= SPI_BUFFERSIZE+10)	//if first SPI-segment received
	{
		frame_size = ( (frame_buffer[0] << 8) | frame_buffer[1] );
		LCD_UsrLog("%2x %2x", frame_buffer[0], frame_buffer[1]); 
	}
	
	/* Print when all data is received */
	if(data_in_frame_buffer >= frame_size)
		{
			PRINT_DATA_FLAG = true;
		}
 
	
	if(HAL_SPI_TransmitReceive_DMA(&SpiHandle, (uint8_t*)&aTxBuffer[0], (uint8_t *)&aRxBuffer[0], BUFFERSIZE+1) != HAL_OK) //Preparation of next SPI TransmitReceive
  {
    Error_Handler();
  }
}

Main-function:

while (1)
  {
		while (SPI_RX_FLAG == false) {;}
			
			
		if (PRINT_DATA_FLAG == true)
		{
			BSP_LED_Toggle(LED1);
			LCD_LOG_ClearTextZone();
			LCD_UsrLog("Data [brutto:%d] [netto:%d]: \n", data_in_frame_buffer, frame_size);
			for(int i=0 ; i < 0.02*frame_size ; i++)
				{
					LCD_UsrLog("%2x", frame_buffer[i]);
				}
			LCD_UsrLog("\n");
			for(int i=0.98*frame_size ; i < frame_size ; i++)
				{
					LCD_UsrLog("%2x", frame_buffer[i]);
				}
			LCD_UsrLog("\n");
			data_in_frame_buffer = 0;	// reset data pointer to receive new data
			frame_size = 0;
			PRINT_DATA_FLAG = false;
		} else
		{
			LCD_UsrLog("[brutto:%d] [netto:%d]\n", data_in_frame_buffer, frame_size);
		}
		
		SPI_RX_FLAG = false;
  }

KnarfB
Principal III

What is the speed of you SPI and CPU/Mem?

Why do you copy the aRxBuffer in the callback, wouldn't copy-free be more efficient?

Frank

Alex R
Senior

Use a GPIO pin to mark the beginning and the end of the callback function. This way you can measure how long that routine takes, and the relation of the execution of the callback routine with respect to the SPI transactions.

Most likely your SPI transactions are too fast and the data gets overwritten.

You might want to consider using DMA instead of interrupts, if the data packet size is constant (same number of bytes always)

@Frank Bauernoeppel:

I used the SPI_FullDuplex_ComDMA Example as a basis for the program. Therefore, I took those system configurations: Receiver-system clock 216MHz, Transmitter-SPI 4MHz (Mbit/s)

And as I read and found, the HAL_SPI_TxRxCpltCallback is a DMA Callback which will be called as soon as the linked SPI Rx-Register is full (aRxBuffer).

And since the SPI packets length is limited to 8 bit at some/most MCUs (255byte), while I want to transfer up to 6kByte. Thats why I decided on a Buffer-Register and a Main-Data-Register.

I couldn't figure out yet, how to shift the pointer on the SPI-Buffer-Register, so that I just could hang each received packet behind the last one.

Is there a chance to do that?

@Alex R:

I will measure the runtime of the Callbacks and let you know.

Do you really think the SPI transfer is too fast already?

I already added an additional 2ms delay between each SPI packet at the transmitter side. And I only send frames every 2 seconds for now. In the end that should be a lot faster with several frames per second???

I thought I am using SPI with DMA, don't I?

I thought the HAL_SPI_TxRxCpltCallback is a DMA Callback function?

Which has some good and some bad features... I standardized the packet size to 244 Bytes, yes. However, the frame size could vary from 3-6kByte depending on the applications data size later on.

Like the Oscilloscope capture shows, the Calback only takes about 35us.

Furthermore, I attached a short video which shows the SPI logging on the receiver board.

At that time, the board hang up after only 2 transmissions. So the last picture you see is a point where the system got stuck. Obviously, the data is mixed up/shifted as well for some reason which I cuoldn't identify yet.

0690X000009YWLoQAO.bmp

Can you hook up a (USB) logic analyzer and watch the data on the wire? What happens when you slow down the SPI clock speed by a factor of 10 or more?

Frank