Electronic – SPI data arrives at every 2nd send and in the wrong sequence

armcommunicationmicrocontrollerspistm32

I am simulating a project where two STM32s communicate with each other through SPI (HAL drivers), where the SPI Master is an STM32F4 and the SPI Slave is an STM32F3. Basically, the master will send 5 bytes of data (1 command byte followed by 4 dummy bytes) and the slave will respond back with 4 bytes of data to the master depending on the first command byte received.

How the system should operate: Pressing the user button on the master will send SPI data (in non-interrupt mode) to the slave (and the slave will receive the data in interrupt mode). The slave then sends data back to the master depending on the first received byte. There is a blue LED indicator on the slave device whether an SPI data has been received, and a green LED indicator on the master that indicates whether it receives the correct sequence of data from the slave. Below is how the clocked data should be sent/received by the master:

Clocked SPI Data Exchange

Slave device issue: For the first few button presses, the slave successfully receives data from the master. After a few cycles, from observing the LEDs and setting breakpoints in Debug mode, I noticed that the slave starts to receive data after every 2 button presses on the master. In the slave device, the data received is correct. I also have to note that the interrupt service routine on the master executes at every button press, so SPI data should be sent at every button press.

Master device issue: The master never receives the correct sequence of data. If, for example, the slave sends the data: 0x01, 0x02, 0x03, 0x04 in that order, the master would receive the data in this order: 0x02, 0x03, 0x04, 0x01. Furthermore, this also happens every 2 button presses.

This is what the event looks like:

  1. n button press:
    • Slave: Data is correctly received by slave (and sends data back to the master)
    • Master: Garbage value received
  2. (n+1) button press:
    • Slave: Data is not received by slave (and does not send data back because slave sends data based on received data)
    • Master: Relevant values received but in incorrect order
  3. (n+2) button press (same as what happened on the n-th button press):
    • Slave: Data is correctly received by slave (and sends data back to the master)
    • Master: Garbage value received
      and so on.

Here is the SPI configuration code for the Master:

static void MX_SPI1_Init (void) {

   /* SPI Configuration for the STM32F4 (Master) */
   /* APB2 Clock set to 8MHz to match APB2 clock of Slave*/

   hspi1.Instance = SPI1;
   hspi1.Init.Mode = SPI_MODE_MASTER;
   hspi1.Init.Direction = SPI_DIRECTION_2LINES;
   hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
   hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
   hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
   hspi1.Init.NSS = SPI_NSS_SOFT;
   hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
   hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
   hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
   hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
   hspi1.Init.CRCPolynomial = 7;

   if (HAL_SPI_Init(&hspi1) != HAL_OK) {
      Error_Handler();
   }
}

The SPI configuration for the Slave:

static void MX_SPI1_Init (void) {

   /* SPI Configuration for the STM32F3 (Slave) */
   /* APB2 Clock is 8MHz */

   hspi1.Instance = SPI1;
   hspi1.Init.Mode = SPI_MODE_SLAVE;
   hspi1.Init.Direction = SPI_DIRECTION_2LINES;
   hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
   hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
   hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
   hspi1.Init.NSS = SPI_NSS_SOFT;
   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;

   if (HAL_SPI_Init(&hspi1) != HAL_OK) {
      Error_Handler();
   }
   
   /* Set interrupt priority and enable interrupts for SPI1 */
   HAL_NVIC_SetPriority(SPI1_IRQn, 1, 0);
   HAL_NVIC_EnableIRQ(SPI1_IRQn);
}

Here is the relevant code for the Master (the Push Button interrupt segment):

void HAL_GPIO_EXTI_Callback (uint16_t GPIO_Pin) {

   /* Clear wake-up power flag */
   __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);

   if (GPIO_Pin == PUSH_BUTTON_PIN) {
      /* Transmit one command byte located in pTxBuff array of length cTxLen */
      HAL_SPI_Transmit(&hspi1, (uint8_t*)pTxBuff, cTxLen, spi_timeout);

      /* Wait for end of SPI transmission */
      while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);

      /* Transmit dummy values located in pTempTxBuff and store retrieved relevant data in pRxBuff */
      HAL_SPI_TransmitReceive(&hspi1, (uint8_t*)pTempTxBuff, (uint8_t*)pRxBuff, cRxLen, spi_timeout);

      /* Flash LED if correct sequence stored in pRxBuff was received - cRxLen = 4 */
      if((pRxBuff[0] == DATA0) && (pRxBuff[1] == DATA1) && (pRxBuff[2] == DATA2) && (pRxBuff[3] == DATA3)) {
         /* Toggle dedicated green LED */
         ToggleLED();
      }

}

And the relevant code for the Slave:

void HAL_SPI_RxCpltCallback (SPI_HandleTypeDef *hspi) {
   /* Toggle blue LED when SPI receives data through interrupt */
   ToggleLED();

   /* Check the data received through SPI interrupt stored in pRxBuff[0] (array is of size 1) */
   if (pRxBuff[0] == COMMAND_DATA) {

      /**
       * pTxBuff := contains appropriate data to be sent to Master device based on this specific command
       * pTempRxBuff := array to store dummy variables, this array is of size 4
       */
      HAL_SPI_TransmitReceive(&hspi1, (uint8_t*)pTxBuff, (uint8_t*)pTempRxBuff, cTxLen, spi_timeout);
   }

   /* Set device in SPI Receive interrupt mode again and store received data in pRxBuff of size 1 */
   HAL_SPI_Receive_IT(&hspi1, (uint8_t*)pRxBuff, cRxCmdLen);
}

I do not understand where the error is here. If it is only the master sending the data to the slave device, everything works fine. I have also matched the SPI configuration and bus clock speeds. I also tried changing the direction line to SPI_DIRECTION_1LINE as suggested elsewhere but it did not work. I also attempted using HAL_SPI_Transmit() and HAL_SPI_Receive() separately instead of HAL_SPI_TransmitReceive(), but the same issues still appear. Also, I would like to use HAL_SPI_TransmitReceive() on both to verify on the Master and the Slave that the devices are communicating properly.Any help would be greatly appreciated.

Thank you!

Best Answer

In SPI, MASTER and Slave are actually connected in a loop to the FIFO buffers. As shown in the picture.
Try flushing these data buffers and shift registers before each data frame transfer. On both master and slave controllers.

Diagram of SPI communication

(Image source: Electronics Hub - Basics of Serial Peripheral Interface (SPI))