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í):
El segundo byte 0x42 recibido no debería haber ocurrido (el dispositivo maestro no se puede configurar/programar en este escenario):
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):
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):
0x41 exactamente después de la segunda secuencia incorrecta anterior:
0x42 exactamente después del 0x41 anterior (bytes incorrectos devueltos al maestro):
Y todos los bytes intercambiados después son los siguientes (independientemente de 0x41 o 0x42):
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);
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:
Después de la primera secuencia (correcta), sale de la ISR con xFlag_42 configurado, y eventualmente termina en __AppliSendBuff(&command42).
A la mitad del __AppliSendBuff, la interrupción se reafirma debido a la secuencia incorrecta.
El ISR configura el búfer para enviar 0x99 y regresa al punto donde se estaba ejecutando antes de recibir la interrupción.
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.
Cimory
HAL_SPI_TransmitReceive_IT()
ejecutando varias veces porque el 0x99 solo se encuentra en esa función.Cimory
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);
annie
Cimory
__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íaHAL_SPI_IRQHandler()
borrar todos los indicadores de interrupción y deshabilitar las interrupciones SPI de forma permanente hasta que se vuelva a activar?Cimory
HAL_SPI_DeInit()
e inicializar el periférico SPI nuevamente, pero eso no resultó bien.annie
annie
Cimory
Cimory
HAL_SPI_TransmitReceive_IT()
que se ejecute si alguno de los indicadores para el ciclo while esSET
, y la comunicación SPI funciona bien. ¡Gracias por la sugerencia y por ayudarme a entender el problema!annie