Configure SPI Slave para manejar los datos que llegan en el momento equivocado

Estoy experimentando con el protocolo SPI (velocidades rápidas en las que se transfieren 5 bytes de datos en poco menos de 500 us) en el STM32F410 configurado como esclavo dúplex completo SPI. Un dispositivo maestro envía un byte de comando al STM32F4 y el STM32F4 responde con 4 bytes de reconocimiento seguidos de una rutina que se ejecuta en aproximadamente 1,3 ms (medido a través del conteo de ciclos de reloj DWT). Se espera que esta transferencia de datos de 5 bytes ocurra entre 25 y 27 veces por segundo.

Me doy cuenta de que, a veces, el dispositivo maestro puede enviar exactamente los mismos datos de forma consecutiva en un lapso de 90 us (el esclavo STM32F4 debería esperar recibir datos alternos a través de SPI en un lapso de al menos 4 ms en el medio). He adjuntado capturas de pantalla del analizador lógico que muestran esta ocurrencia:

Se esperaba el primer byte 0x42 recibido, y el esclavo STM32F4 respondió con los bytes apropiados (tenga en cuenta que el segundo 0x42 posterior no debería estar allí):Primera parte de la ocurrencia inusual

El segundo byte 0x42 recibido no debería haber ocurrido (el dispositivo maestro no se puede configurar/programar en este escenario):Segunda parte de ocurrencia inusual

Después de lo anterior, el esclavo STM32F4 nunca más pudo responder correctamente al dispositivo maestro (los bytes de reconocimiento enviados al maestro siempre son 0x99, que es el byte configurado en el búfer de envío de interrupción SPI):Respuesta incorrecta devuelta al maestro todo el tiempo después del error

En esta situación, intenté eliminar la llamada de función __AppliSendBuff(), que ordena a un transceptor de RF que transmita datos, y cuando ocurre algo inusual, el esclavo STM32F4 aún puede responder correctamente al maestro.

Mi pregunta es : ¿Cómo podría ignorar los datos SPI provenientes del maestro en momentos inesperados? ¿Por qué una transacción SPI fallida hace que todas las demás transacciones SPI futuras fallen? ¿Qué precauciones debo tomar al escribir código para el esclavo STM32F4?

Lo que intenté : deshabilitar las interrupciones SPI_CS GPIO cada vez que se retira el chip CS para que, en medio de todo el proceso, la transacción SPI no ocurra hasta que vuelva a habilitar la interrupción SPI_CS GPIO. Antes de este cambio, en el segundo comando 0x42 recibido, el esclavo STM32F4 solo respondería 0x99 al 0x42 y 0xFF siguiendo al 0x42. A pesar de esto, todas las demás transacciones SPI siguen fallando y el esclavo respondería con 0x99.

Mi código STM32F4 :
El segmento de interrupción:

/*** 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 */
      }
   }
}

El bucle principal:

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

¿También tengo que configurar el esclavo SPI para transmitir 4 bytes ficticios (quizás para vaciar los registros de datos SPI) en caso de que reciba un byte de comando basura/no registrado?

¡Gracias!


Actualización: Omití deshabilitar la desactivación de interrupción de CS Line (que es una desactivación de interrupción de GPIO) y moví las acciones de restablecimiento de bandera para que fueran la primera línea en los bloques else if y se mostraron algunos cambios en el analizador lógico (la primera secuencia correcta fue Siempre lo mismo):

Segunda secuencia incorrecta:Segunda secuencia incorrecta

0x41 exactamente después de la segunda secuencia incorrecta anterior:Incorrecto 0x41

0x42 exactamente después del 0x41 anterior (bytes incorrectos devueltos al maestro):Incorrecto 0x42

Y todos los bytes intercambiados después son los siguientes (independientemente de 0x41 o 0x42):Secuencias incorrectas después

Olvidé incluir las prioridades de interrupción en la publicación original anterior:

/* 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);

Respuestas (1)

Tengo algunos problemas para seguir lo que sucedió antes de que agregara la interrupción GPIO y después. Si entiendo correctamente, lo que estaba sucediendo antes de que lo agregara era:

  1. Después de la primera secuencia (correcta), sale de la ISR con xFlag_42 configurado, y eventualmente termina en __AppliSendBuff(&command42).

  2. A la mitad del __AppliSendBuff, la interrupción se reafirma debido a la secuencia incorrecta.

  3. El ISR configura el búfer para enviar 0x99 y regresa al punto donde se estaba ejecutando antes de recibir la interrupción.

  4. La siguiente línea desarma xFlag_42, y luego se envía 0x99.

Todo eso tiene sentido. Según su descripción, parece que las transacciones posteriores también recibirían la respuesta 0x99, lo que no tiene sentido para mí, ya que todo debería comenzar de nuevo en ese punto.

Las cosas se vuelven considerablemente más complicadas cuando agrega la interrupción para deshabilitar el GPIO. No menciona cuál es la prioridad de interrupción, pero en general tiene dos ISR jugando con el búfer SPI, uno tras otro, y probablemente eso no sea lo que desea.

Podría intentar mover este xFlag_42 = RESET; ser la primera línea del else if. Eso daría como resultado que todo se ejecute dos veces. Si ese resultado no es el deseado, puede agregar una bandera que diga que está procesando el primer tráfico y escribir su ISR para que regrese inmediatamente sin hacer nada si esa bandera está configurada.

¡Gracias por la respuesta! Sí, eso es lo que estaba pasando. En la segunda secuencia incorrecta, el esclavo responde con cinco 0x99 al maestro a pesar de que era un 0x42. Se debe esperar el primer 0x99 a la secuencia incorrecta, pero todavía no entiendo por qué los otros bytes respondieron con un 0x99 como si se estuviera HAL_SPI_TransmitReceive_IT()ejecutando varias veces porque el 0x99 solo se encuentra en esa función.
Tampoco entiendo por qué las transacciones SPI posteriores se respondieron con 0x99. No tiene sentido, como dijiste, porque el registro de datos SPI debería haber sido reemplazado con los nuevos búferes. Intenté deshabilitar la interrupción de la línea CS y mover las banderas para que fueran la primera línea del else if, pero aún había errores aunque hubo algunos cambios. Actualizaré la publicación con respecto a las prioridades de interrupción, la interrupción SPI2_CS GPIO es: HAL_NVIC_SetPriority(EXTI1_IRQn, 1, 0);mientras que la prioridad de interrupción SPI en sí es:HAL_NVIC_SetPriority(SPI2_IRQn, 1, 1);
@Cimory, estás borrando las interrupciones, ¿verdad?
Las interrupciones GPIO se borran automáticamente (creo) a través de la biblioteca HAL. La función __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);se ejecuta antes de entrar en mi función de devolución de llamada de interrupción GPIO. No estoy seguro de borrar las interrupciones SPI. __HAL_SPI_DISABLE_IT()¿La función interna no debería HAL_SPI_IRQHandler()borrar todos los indicadores de interrupción y deshabilitar las interrupciones SPI de forma permanente hasta que se vuelva a activar?
¿Hay indicadores que debo borrar antes de iniciar una nueva transacción SPI? También intenté desinicializar el periférico SPI cada vez que hay una transacción SPI fallida HAL_SPI_DeInit()e inicializar el periférico SPI nuevamente, pero eso no resultó bien.
Lo siento, no estoy seguro de borrar las interrupciones.
Suena bastante arriesgado apagar y encender el periférico.
Buscaré borrar la interrupción SPI; podría ser la razón por la que estoy enviando 0x99 después de que aparece cualquier tipo de error. Gracias por la sugerencia. ¿Hay algún otro paso/procedimiento que pueda faltar en términos de configuración del STM32 como esclavo?
Agregué la verificación de indicadores activos en el bloque de interrupción SPI_CS y evité HAL_SPI_TransmitReceive_IT()que se ejecute si alguno de los indicadores para el ciclo while es SET, y la comunicación SPI funciona bien. ¡Gracias por la sugerencia y por ayudarme a entender el problema!
¡Excelente! Me alegro de poder ayudar.