0
\$\begingroup\$

I am experimenting with the SPI protocol (fast speeds where 5 bytes of data is transferred in slightly less than 500us) on the STM32F410 configured as a SPI full duplex slave. A master device sends a command byte to the STM32F4 and the STM32F4 responds with 4 acknowledge bytes followed by a routine that executes at about 1.3ms (measured through the DWT clock cycle count). This 5 byte data transfer is expected to happen around 25 to 27 times a second.

I notice that sometimes, the master device can send the exact same data consecutively in a span of 90us (The STM32F4 slave should expect to receive alternating data through SPI in a span of at least 4ms in between). I have attached screenshots of the logic analyzer showing this occurrence:

The first 0x42 byte received is to be expected, and the STM32F4 slave responded with the appropriate bytes (note that the second 0x42 afterwards should not be there): First portion of the unusual occurrence

The second 0x42 byte received should not have occurred (Master device cannot be configured/programmed in this scenario): Second portion of unusual occurrence

After the occurrence above, the STM32F4 slave could never respond to the master device correctly again (the acknowledge bytes sent back to the master is always 0x99 - which is the byte configured in the SPI interrupt send buffer): Incorrect response sent back to the master all the time after error

In this situation, I tried removing the function call __AppliSendBuff(), which commands an RF transceiver to transmit data, and when the unusual occurrence happens, the STM32F4 slave is still able to correctly respond to the master.

My question is: How could I ignore the SPI data coming from the master at unexpected times? Why is it that one failed SPI transaction causes every other future SPI transactions to fail? What precautions should I take when writing code for the STM32F4 slave?

What I tried: Disabling the SPI_CS GPIO interrupts whenever the CS chip is pulled down so that in the middle of the whole process, the SPI transaction would not occur until I re-enable the SPI_CS GPIO interrupt. Before this change, in the second 0x42 command received, the STM32F4 slave would just respond 0x99 to the 0x42 and 0xFF's tailing the 0x42. Despite this, every other SPI transactions still fail and the slave would respond with 0x99's.

My STM32F4 code:
The interrupt segment:

/*** STM32F4 INTERRUPT HANDLERS ***/

/* GPIO Interrupt Handler */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
   if(GPIO_Pin == SPI2_CS_PIN) {
      /* Temporarily disable SPI2 CS interrupt */
      HAL_NVIC_DisableIRQ(EXTI1_IRQn);

      /* Generate interrupt flags when master device sends SPI data */
      /* dummybyte is a uint8_t array of size 1, with the element being 0x99 */
      HAL_SPI_TransmitReceive_IT(&hspi2, (uint8_t*)dummybyte, (uint8_t*)pSpi2RxBuff, cSpi2RxLen);
   }
   else if (...) {
      ...
   }
}

/* SPI Interrupt Handler */
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) {
   if(hspi->Instance == SPI2) {
      if (...) {
         ...
      }
      else if(pSpi2RxBuff[0] == 0x41) {
         /* Activate 0x41 flag for further processing in main loop if 0x41 was received */
         xFlag_41 = SET;
      }
      else if(pSpi2RxBuff[0] == 0x42) {
         /* Activate 0x42 flag for further processing in main loop if 0x42 was received */
         xFlag_42 = SET;
      }
      else {
         /* Discard data by doing nothing */
      }
   }
}

The main loop:

while (1) {
   if(xFlag_41 == SET) {
      /* Respond back to system with acknowledge bytes */
      HAL_SPI_TransmitReceive(&hspi2, (uint8_t*)pSend41Data, (uint8_t*)pTempRxBuff, cSpi2TxLen, SPI2_TIMEOUT);
      
      /* Commands RF Transceiver to transmit data respective to received command from master */
      __AppliSendBuff(&command41);

      xFlag_41 = RESET;

      /* Re-enable SPI2_CS interrupts. Note that priority was set in MX_GPIO_Init() */
      HAL_NVIC_EnableIRQ(EXTI1_IRQn);
   }
   else if(xFlag_42 == SET) {
      /* Respond back to system with acknowledge bytes */
      HAL_SPI_TransmitReceive(&hspi2, (uint8_t*)pSend42Data, (uint8_t*)pTempRxBuff, cSpi2TxLen, SPI2_TIMEOUT);
          
      /* Commands RF Transceiver to transmit data respective to received command from master */
      __AppliSendBuff(&command42);

      xFlag_42 = RESET;

      /* Re-enable SPI2_CS interrupts. Note that priority was set in MX_GPIO_Init() */
      HAL_NVIC_EnableIRQ(EXTI1_IRQn);
   }
}

Do I also have to configure the SPI slave to transmit 4 dummy bytes (maybe to flush the SPI data registers) in the event that I do receive a junk/unregistered command byte?

Thank you!


Update: I omitted disabling the CS Line interrupt disable (which is a GPIO interrupt disable) and moved the flag reset actions to be the first line in the else if blocks and there were some changes shown in the logic analyzer (the first correct sequence was still the same):

Second incorrect sequence: Second incorrect sequence

0x41 exactly after the second incorrect sequence above: Wrong 0x41

0x42 exactly after the 0x41 above (incorrect bytes sent back to master): Wrong 0x42

And all the bytes exchanged afterwards are as below (regardless 0x41 or 0x42): Wrong sequences afterwards

I forgot to include the Interrupt Priorities in the original post above:

/* SPI CS line has the highest interrupt priority */
HAL_NVIC_SetPriority(EXTI1_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(EXTI1_IRQn);
/* SPI Peripheral connected to the master device */
HAL_NVIC_SetPriority(SPI2_IRQn, 1, 1);
HAL_NVIC_EnableIRQ(SPI2_IRQn);
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I'm having a little trouble following what happened before you added the GPIO interrupt, and after. If I understand correctly, what was happening before you added it was:

  1. After the first (correct) sequence, you come out of the ISR with xFlag_42 set, and so eventually you end up in __AppliSendBuff(&command42).

  2. Midway through the __AppliSendBuff, the interrupt reasserts due to the incorrect sequence.

  3. The ISR sets the buffer to send 0x99, and returns to the point where it was executing before it got the interrupt.

  4. The next line unsets xFlag_42, and so 0x99 is sent after that.

That all makes sense. From your description, it sounds like subsequent transactions would also receive the 0x99 response, which doesn't make sense to me, since the whole thing should start over at that point.

Things become considerably more complicated when you add the interrupt to disable the GPIO. You don't mention what the interrupt priority is, but overall you have two ISRs messing about with the SPI buffer, one after the other, and that is probably not what you want.

You could try moving this xFlag_42 = RESET; to be the first line of the else if. That would result in the whole thing executing twice. If that result is undesirable, you could add a flag that says you're in the processing the first traffic, and write your ISR so it returns immediately without doing anything if that flag is set.

\$\endgroup\$
10
  • \$\begingroup\$ Thank you for the response! Yes, that is what was happening. At the second incorrect sequence, the slave responds with five 0x99's to the master even though it was a 0x42. The first 0x99 to the incorrect sequence should be expected, but I still don't understand why the other bytes were responded back with a 0x99 as if the HAL_SPI_TransmitReceive_IT() is running multiple times because the 0x99 is only located in that function. \$\endgroup\$
    – Cimory
    Aug 11, 2020 at 1:21
  • \$\begingroup\$ I also do not understand why the subsequent SPI transactions were responded with 0x99's. It does not make sense, as you said, because the SPI data register should have been replaced with the new buffers. I tried disabling the CS line interrupt and moving the flags to be the first line of the else if, but there were still errors although there were some changes. I will update the post regarding the interrupt priorities, the SPI2_CS GPIO interrupt is: HAL_NVIC_SetPriority(EXTI1_IRQn, 1, 0); while the SPI interrupt priority itself is: HAL_NVIC_SetPriority(SPI2_IRQn, 1, 1); \$\endgroup\$
    – Cimory
    Aug 11, 2020 at 1:25
  • \$\begingroup\$ @Cimory, you're clearing the interrupts, right? \$\endgroup\$
    – Annie
    Aug 11, 2020 at 1:32
  • \$\begingroup\$ The GPIO interrupts are cleared automatically (I believe) through the HAL library. The function __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin); gets executed before going into my GPIO interrupt callback function. I am unsure regarding clearing the SPI interrupts. Shouldn't the function __HAL_SPI_DISABLE_IT() inside HAL_SPI_IRQHandler() clear all interrupt flags and disabling the SPI interrupts permanently until activated again? \$\endgroup\$
    – Cimory
    Aug 11, 2020 at 1:41
  • \$\begingroup\$ Are there flags that I need to clear before initiating a new SPI transaction? I also tried de-initializing the SPI peripheral whenever there is a failed SPI transaction through HAL_SPI_DeInit() and initializing the SPI peripheral again, but that didn't turn out well.. \$\endgroup\$
    – Cimory
    Aug 11, 2020 at 1:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.