cancel
Showing results for 
Search instead for 
Did you mean: 

SPI Slave transmit has extra byte at times

MPatt.11
Associate II

0690X000006DCWkQAO.jpg0690X000006DCWfQAO.jpg0690X000006DCWaQAO.jpgI have two STM32F7 processors set up in SPI Master/Slave configuration. When the slave wants to transmit data to the master, it issues an interrupt and the master reads 4 bytes. The first two are the number of data bytes to read, the second two is a command code. The master waits for a go-ahead interrupt, then reads the rest of the bytes.

At times, I'm seeing the first byte of the 4-byte transfer duplicated in the data transfer - thus shifting the bytes by one, and adding one byte to the transfer. This may make more sense by looking at the captures from the logic analyzer. I am using HAL_SPI_Transmit_IT to the master, and HAL_SPI_Receive_IT on the master side to receive the bytes.

The transfer rate is 14 MHz. Slowing the clock down makes no difference.

Using SPI_NSS_PULSE_DISABLE or SPI_NSS_PULSE_ENABLE does not help

On the Master side, using SPI_NSS_HARD_OUTPUT or generating the NSS signal manually makes no difference.

The only thing that seems to help is enforcing a minimum transfer rate of 2ms. In other words, the slave needs to wait 2ms minimum between transfers.

Slave initialization:

/* SPI Port for communications with Master processor
 **/
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
  GPIO_InitTypeDef GPIO_InitStruct;
  if(hspi->Instance==SPI1)
  {
    /* Peripheral clock enable */
    __HAL_RCC_SPI1_CLK_ENABLE();
  
    /**SPI1 GPIO Configuration    
    PA4     ------> SPI1_NSS
    PA5     ------> SPI1_SCK
    PA6     ------> SPI1_MISO
    PA7     ------> SPI1_MOSI 
    */
    GPIO_InitStruct.Pin = SPI_CS_IN_Pin|SPI_CLK_IN_Pin|SPI_MISO_Pin|SPI_MOSI_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
    /* SPI1 interrupt Init */
    HAL_NVIC_SetPriority(SPI1_IRQn, 1, 0);   
    HAL_NVIC_EnableIRQ(SPI1_IRQn);
  }
}
 
 
 
/* SPI1 init function */
void MX_SPI1_Init(void)
{
  /* SPI1 parameter configuration*/
  SPI_SLAVE_1.Instance = SPI1;
  SPI_SLAVE_1.Init.Mode = SPI_MODE_SLAVE;
  SPI_SLAVE_1.Init.Direction = SPI_DIRECTION_2LINES;
  SPI_SLAVE_1.Init.DataSize = SPI_DATASIZE_8BIT;
  SPI_SLAVE_1.Init.CLKPolarity = SPI_POLARITY_LOW;
  SPI_SLAVE_1.Init.CLKPhase = SPI_PHASE_1EDGE;
  SPI_SLAVE_1.Init.NSS = SPI_NSS_HARD_INPUT;
  SPI_SLAVE_1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  SPI_SLAVE_1.Init.TIMode = SPI_TIMODE_DISABLE;
  SPI_SLAVE_1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  SPI_SLAVE_1.Init.CRCPolynomial = 7;
  SPI_SLAVE_1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  SPI_SLAVE_1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
  if (HAL_SPI_Init(&SPI_SLAVE_1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
}

Has anyone seen similar issues with the SPI Slave transfer?

Mark

11 REPLIES 11
S.Ma
Principal

Try to reset the SPI between the header and data block transfer (SYSCFG).

If this is improving the situation, look at the SPI Slave Transmit FIFO.

You did not show us the signal which is the "go-ahead interrupt". My crystal cube says, that the problem is in that in the second phase of communication the master starts to clock before the interrupt routine stores the first byte into SPI_DR (and the issue is marginal). You probably first send from slave to master the ""go-ahead interrupt"" signal, then call HAL_SPI_Receive_IT(), don't you.

JW

MPatt.11
Associate II

Thank you both for the excellent ideas.

0690X000006DCYqQAO.jpg

This is a failed transfer go-ahead interrupt showing. The idea behind that interrupt was to prevent the master from starting the transfer before the slave is ready. The go-ahead (rising edge) interrupt goes out, then /ACS goes active, then the clocks start.

This image has one more change - I reset the SPI between the first transfer and the second. Now instead of getting the extra 0x14, I get an extra 0x00. Resetting the SPI would make sure that there is nothing in the TX buffer. I also removed the SPI_NSS_PULSE_DISABLE from the Slave TX Side which is why all of the clock pulses are now together.

BTW: I forgot to mention in the original post that I am making sure that the TXE is set and the fifo is empty. I check this before starting the first transmit and before the second transmit. Now I check it before the second transmit and before the reset.

static inline void SPI_check_ready(void)
{
  /* Wait End Of Transmission: TXE set and Tx Fifo empty */
  while((LL_SPI_IsActiveFlag_TXE(SPI_SLAVE_1.Instance) != 1));	
  while (LL_SPI_GetTxFIFOLevel(SPI_SLAVE_1.Instance) != LL_SPI_TX_FIFO_EMPTY);	
}

It is still failing as you can see. I find it hard to believe this could be a HAL issue. I guess I could try using the LL_ transfer functions and see if that makes any difference.

My crystal cube says, that the problem is in that in the second phase of communication the master starts to clock before the interrupt routine stores the first byte into SPI_DR (and the issue is marginal). You probably first send from slave to master the ""go-ahead interrupt"" signal, then call HAL_SPI_Receive_IT(), don't you.

JW

S.Ma
Principal

A general point to remember:

As SPI Master, the end of transmission is ruled by RXNE, which tells all clocks have been sent and the incoming data is now captured in the DR. TXE will rise as soon as you write the first data byte because it will be pushed to the shift register and free DR to be written for the next data to transmit later without "pause" between bytes.

HABOT
Associate III

I struggled with this problem for some time and after analyzing the implied functions, I discovered that it is a problem in the ISR provided by HAL. It doesn't matter if you use IT/DMA calls, after a while the SPI stream stars to shift whole bytes. I've solved the problem by using the LL library and writing my own ISR handler.

MPatt.11
Associate II

Thank you Harold. That was the path I was just starting to go down. I'm glad to hear that this is likely the right one!

JWasi.1
Associate

I know it's been almost 2 years since this thread began but I ran into this as well and, based on JW's idea, was able to fix it by adding a slight delay between my request for data and pulling it out of the slave. In other words, I gave my STM32 time to execute the HAL_SPI_TransmitReceive_IT function before starting the SPI clock om my MASTER.

TDuon.2
Associate

Hi @HABOT​ , I have the same problem. Can you please explain how you solve the problem? Can you give example code of your ISR please.

Much appreciated your support.

Thank you so much.