Tengo dos PIC18F4620 conectados a través de SPI + Slave Select + línea IRQ adicional. Ambos controladores son impulsados desde el mismo oscilador de cristal usando la misma configuración de reloj. El maestro envía un byte y luego espera hasta que el esclavo alterna esa línea IRQ adicional. La duración de la alternancia es de 4 ciclos de instrucción. Todos los bordes se ven bien en el osciloscopio y la comunicación SPI funciona correctamente, excepto por la detección de la alternancia ( while(!PORTBbits.RB1);
).
Este es mi código de envío SPI:
while (spi_out_msg_buffer.write_cursor > spi_out_msg_buffer.read_cursor)
{
DisableInterrupts;
SSPBUF = spi_out_msg_buffer.data[spi_out_msg_buffer.read_cursor];
LATBbits.LATB0 = 1;
while(!PORTBbits.RB1); // wait for toggle on IRQ line
LATBbits.LATB0 = 0;
EnableInterrupts;
spi_out_msg_buffer.read_cursor++;
}
El while(!PORTBbits.RB1);
se traduce en dos instrucciones:
BTFSS PORTB, 1, ACCESS
BRA 0x188
Inserté esa B0
línea con fines de depuración, puede verla en la parte inferior de este diagrama de tiempo:
Puede ver el cambio de la línea IRQ (segunda desde abajo) y cómo pasa desapercibida, porque la B0
línea de depuración permanece alta. Cuando detengo la ejecución a través de ICD, se cuelga dentro del archivo while
. Vale la pena mencionar que normalmente funciona durante unos pocos bytes y luego se detiene, como se puede ver aquí:
Medí que el pulso es en realidad 4 ciclos de instrucción (= 16 ciclos PLL = 4 ciclos de reloj) de largo:
Creo que eso debería ser suficiente para que se detecte el pulso. Incluso si el primer BTFSS lo pierde porque el puerto se muestrea al comienzo del ciclo de instrucción, entonces el segundo debería obtenerlo:
10 MHz -> PLL -> 40 MHz -> 10 M instrucciones por segundo -> 100 ns por instrucción.
¿No debería ser lo suficientemente largo para salir del while
?
Está utilizando un compilador, por lo que no tiene idea de cuántos ciclos toma ese ciclo de sondeo. En ensamblador, podría reducirlo a 3 ciclos, pero renunció al derecho de contar ciclos cuando escribió el código en un lenguaje de alto nivel.
Sin embargo, el problema real es el enfoque general. No es una buena idea pedirle al código que detecte un error breve. Incluso si pudiera garantizar que el tiempo de ciclo es menor que el tiempo de falla, ahora no puede activar las interrupciones durante la espera. Esto puede presentar problemas arquitectónicos más adelante.
Una idea mucho mejor es usar el hardware que ya tiene para detectar la falla y luego hacer que el firmware verifique ese hardware. Lo más simple sería conectar la falla a una de las líneas INTx y luego buscar el indicador INTxIF. Cambiar los pines de detección también funcionaría, pero tenga en cuenta que la bandera se establece en ambos bordes y debe eliminar la falta de coincidencia para eliminar la condición.
La última instrucción es una bifurcación incondicional, ¿verdad? Por lo general, las bifurcaciones y los accesos a la memoria (realizados en la primera instrucción, ¿verdad?) tardan entre 2 y 3 veces más en ejecutarse, por lo que en su caso, esas instrucciones podrían tardar unos 16 ciclos en ejecutarse, que es 400 ns (en el mejor de los casos). ).
while(!SSPSTATbits.BF);
.La razón real era que yo era un imbécil. Mi DisableInterrupts
macro lo hizo INTCONbits.GIE=1
.
Encontré esto alternando un pin durante la espera ocupada:
while(!PORTBbits.RB1)
{
LATBbits.LATB0 = 0;
LATBbits.LATB0 = 1;
}
Noté que la alternancia se detuvo en intervalos regulares y cuando el pulso de notificación cae en una de estas ventanas, pasa desapercibido.
Kortuk
Majenko