¿Es mejor en dos microcontroladores estrechamente conectados enviar nibbles con un acuse de recibo o un byte con un retraso para un acuse de recibo?

Estoy tratando de encontrar el mejor enfoque para transferir datos de alta velocidad de una manera entre dos microcontroladores (con sus propios cristales de la misma velocidad) de la misma variedad (entre AT89C4051 y AT89S52) desde un punto de vista eléctrico.

Los tengo cableados de la siguiente manera:

AT89S52 P0 is connected to AT89C4051 P1
AT89S52 P1.1 is connected to AT89C4051 P3.7

El AT89C4051 tiene 6 bytes de datos que necesita el AT89S52, y el AT89S52 siempre inicia la descarga.

Mis opciones son las siguientes:

** OPCIÓN 1. Transferir todo el byte. **

El código en AT89C4051 (transmisor) será este:

mov R1,#DATALOCATION
jb P3.7,$  ;Wait for falling edge
mov P1,@R1 ;send out byte
inc R1     ;Increment pointer
jnb P3.7,$ ;Wait for rising edge
mov P1,@R1
inc R1        
;and this code (minus first line) repeats twice for remaining bytes

Y el AT89S52 tendrá este código:

mov R0,#DATASPACE
mov P0,#0FFh ;Make ports accept data
clr P1.1 ;lower line to get first byte
mov @R0,P1 ;Load next byte in
inc R0     ;increment pointer
nop        ;waste cycles to let AT89C4051
nop        ;be ready for next byte. Is this wait time enough under worst
           ;case scenarios???
setb P1.1  ;raise line to get next byte
mov @R0,P1 ;Load next byte in
inc R0     ;increment pointer
nop        ;waste cycles to let AT89C4051
nop        ;be ready for next byte
;and this code (minus first line) repeats twice for remaining bytes

Ese es mi enfoque de 8 bits que parece rápido, pero no tenía suficientes pines libres para usar uno para un pin de reconocimiento ya que el resto se usa.

** OPCIÓN 2. Transferir todo el nibble **

El código en AT89C4051 (transmisor) será este:

mov R1,#DATALOCATION ;pointer = start of data
jb P3.7,$   ;Wait for falling edge (but this means stalls which I don't like)
mov A,@R1   ;get byte
orl A,#0F0h ;and accept lower nibble. 
clr ACC.7   ;Make P1.7 our ack bit (ack=0)
mov P1,A    ;return data in lower nibble with P1.7=0 as ack
jnb P3.7,$  ;Wait for rising edge
mov A,@R1   ;get byte
orl A,#0Fh  ;and accept high nibble. (ack=1)
swap A      ;and put it in our low nibble slot
mov P1,A    ;return data in lower nibble with P1.7=1 as ack
inc R1
;and this code (minus first line) repeats 5x for remaining bytes

El código en AT89S52 (receptor) será así:

mov R0,#DATALOCATION ;pointer = start of data
mov A,@R0 ;Load byte
orl A,#0F0h ;set our nibble and make rest of lines high to receive ack
mov P1,A  ;and show it
clr P1.1  ;lower clock
jb P1.7,$ ;wait till remote is ready
mov A,@R0 ;Load byte again
orl A,#0Fh ;set our nibble and make rest of lines high to receive ack
swap A    ;swap nibbles so we get right nibble
mov P1,A  ;show data
setb P1.1 ;raise clock
jnb P1.7,$ ;wait till remote is ready
inc R0
;and this code (minus first line) repeats 5x for remaining bytes

¿cuál es el mejor?

Pero la parte que me preocupa es el tiempo y el hardware.

Los micros están conectados a no más de 10 cm de distancia entre sí y todas las trazas de PCB tienen 12 milésimas de pulgada de ancho con 12 milésimas de espacio libre. Cuando ejecuto cualquier circuito de microcontrolador, si mi mano toca ambos cables del cristal, entonces la velocidad de operación parece variar (¿probablemente porque la resistencia humana afecta la frecuencia del cristal?)

Teniendo en cuenta todos los entornos a los que pueden estar expuestos los micros (excepto el agua), ¿cuál de mis dos ideas es la mejor para asegurarme de obtener datos a la mayor velocidad posible? y solo tengo 9 líneas de E/S para jugar aquí.

Entonces, ¿recurro al método nibble y espero los reconocimientos incluso si la respuesta tarda un tiempo? ¿O estoy seguro de usar el método de byte?

Recuerde, tenemos que asumir los peores escenarios. dedos tocando el área de cristal (como prueba), baterías débiles, etc. porque lo último que quiero que suceda es la pérdida de datos.

Para aclarar, cada cable de cristal está conectado a capacitores cerámicos de 33pF que también están conectados a tierra. (Estoy usando la configuración de cristal del microcontrolador estándar). y mis planos de tierra son grandes.

ACTUALIZAR

Como se solicitó, incluí las conexiones importantes. Ambos cristales son 22.1184Mhz. El condensador y la resistencia conectados al pin de reinicio son 47nF y 100K. Todos los demás condensadores son 33pF.

conexiones

También incluí un diagrama de tiempo básico. Tuve que usar pintura para dibujar las líneas porque no tengo ningún programa en mi computadora para hacer diagramas profesionales.

momento

¿Con qué frecuencia sucede esto y qué tan alta velocidad debe ser?
Creo que esta pregunta es en realidad independiente del conjunto de instrucciones del procesador. ¿Puede proporcionar diagramas de tiempo en lugar de código? Un diagrama de bloques también sería mejor que su descripción escrita de las conexiones.
Creo que los diagramas de tiempo son más de lo que necesito en función de todos los entornos a los que está expuesto el micro. Pero los cristales en ambos son 22.1184Mhz y muchas instrucciones se ejecutan a 1/12 de una velocidad de reloj (equivale a 1 ciclo) (alrededor de 0.54uS), pero algunas necesitan 2 ciclos para ejecutarse. Pero estoy haciendo esta pregunta más en términos de hardware. Como por ejemplo, ¿la interferencia cercana afectaría el funcionamiento de mi código? Entiendo completamente el tiempo de ejecución de cada instrucción en sí misma.
Esto sucede con mucha frecuencia y este fragmento solo debería ejecutarse en menos de 50uS (ideal) pero no más de 100uS. Solo como un ejemplo de tiempo, quiero que esto se ejecute no más del tiempo requerido para transmitir un byte por la línea serial a 56 kbps con una configuración típica de uart 8N1. y Elliot, incluí imágenes para su referencia.
¿Puede ejecutar una salida de cristal a ambos chips? Preveo la desincronización del reloj usando relojes separados.
tristemente no. Entonces, ¿asumiré, según el último comentario, que debo usar un bit de reconocimiento de devolución (como en mi idea de 4 bits)?
Para los relojes asíncronos, incluso si están cerca de los mismos Hz, deberá escribir un código ensamblador elaborado que garantice un muestreo mínimo y fijo . Pero no necesariamente tiene que cambiar a un enfoque de datos de 4 bits de ancho. Podrías quedarte con tu 9 bits. Solo necesita pensar un poco en los enfoques de drenaje abierto para su luz estroboscópica.
@Mike "lamentablemente no". Por supuesto que puede. Hay toneladas de generadores de reloj, como MAX7389, LTC6930, 7WZ o PLL como MK5811C. Uno de estos puede reemplazar ambos cristales para una sincronización perfecta del reloj, sin mencionar los esquemas simplificados.
Pero ya hice mi PCB grande y conseguir uno de esos chips perderá tiempo ya que la tienda local no tiene ninguno.

Respuestas (3)

En primer lugar, debe colocar su proyecto en un recinto que evite que los dedos humanos (o cualquier otra cosa) se acerquen tanto al cristal que tenga un efecto importante en la frecuencia.

Una vez que tenga los relojes bajo control, el primer método debería estar bien, suponiendo que haya tenido en cuenta los retrasos en el peor de los casos a través de los sincronizadores de E/S en ambos chips. Tenga en cuenta que aunque los dos procesadores tienen la misma frecuencia nominal, sus frecuencias reales variarán ligeramente y la relación de fase entre ellos puede ser cualquier cosa.

Probablemente necesitará algunas nopinstrucciones más en el lado maestro (el que genera el reloj). Además, esas nopinstrucciones deben ir antes de la instrucción que lee los datos; esto le da al procesador esclavo el tiempo que necesita para reconocer la transición del reloj y colocar los datos en el bus de 8 bits.

Sí, estoy de acuerdo con tu primera afirmación. Eso por supuesto que lo haré. Ahora son dos personas diciéndome que las frecuencias de cada cristal pueden diferir ligeramente. Debido a las diferencias y mi mala suerte, creo que es más seguro usar el enfoque nibble con acks en lugar de reprogramar el chip cientos de veces con el mismo programa solo para agregar un nop cada vez que se programa.
También esperaré a ver si alguien más me ofrece respuestas aquí que me ayuden antes de aceptar una respuesta.
¿Por qué necesitaría reprogramar el chip "cientos de veces"? Debería poder calcular la cantidad requerida de nops de la documentación de una sola vez.
bueno, tal vez no cientos de veces, pero podría ser inexacto debido a la desviación del reloj que probablemente no aparecerá en la documentación.
El punto es que puede calcular fácilmente las condiciones del peor de los casos y permitir un margen más allá de eso. No hay necesidad de ajustar nada si las condiciones cambian, siempre y cuando no superen el peor de los casos originales.

Bien, veamos si lo entendí bien. ¿Está tratando de lograr una transferencia de datos de alta velocidad y está golpeando bits? Además, literalmente está contando los tics de la CPU para cada operación, lo que significa que su MCU se ejecuta al 100% de carga continuamente.

Aquí sólo hay dos resultados posibles. O se queda sin RAM para los datos entrantes, o tiene que detenerse en algún lugar y procesarlos. Asumiré que es lo último, y el procesamiento lleva mucho más tiempo que la comunicación. Digamos al menos 5 veces más.

Ahora, lo divertido de las tasas de baudios es que el volumen de datos transferidos a 1 Mbps 1/6 del tiempo es exactamente el mismo que el volumen transferido a 170 kbps de forma continua. Parece entender esto, como en su comentario "línea serie a 56 kbps con un 8N1 típico".

Además, mediante el uso de comunicación de hardware, puede realizar la transferencia y el procesamiento de datos simultáneamente. Entonces, ¿por qué no usa hardware UART disponible en ambos MCU y perfectamente capaz de velocidades más altas que las que cita?

Ahora, en caso de que necesite esos UART para algún otro uso, eche un vistazo a la nota de aplicación 3524 de Microchip. Es una implementación de SPI que se supone que debe lograr una transmisión de 2 Mbps (a un reloj de 32 Mhz).

Ya estoy usando el hardware UART para la transferencia de datos y tengo una interrupción específica para ello. Excepto que la transferencia y el procesamiento simultáneos son imposibles. Incluso todos mis emuladores 8051 están de acuerdo conmigo porque una vez que se recibe/envía el byte, mi programa que tiene que preparar los bytes correctos que necesita la rutina serial se interrumpe y, por lo tanto, se procesará la basura porque mi rutina de transmisión transmite el siguiente byte una vez que el último uno se transmite.
No veo el uso de hardware UART en sus ejemplos de código. Por supuesto, es posible la transferencia y el procesamiento simultáneos, así es como funciona el software integrado. Una vez que se envía el byte, todo lo que su programa tiene que hacer es mover el siguiente byte al búfer de transmisión, eso es solo algunos códigos OP. Luego puede continuar preparando datos mientras se realiza la transmisión. Si no tienes datos listos, no envías nada. En el lado de recepción, inserta el byte recibido en el búfer y continúa procesando. Si no hay datos en el búfer, simplemente espérelos. ¿Por qué se procesará algo de "basura"?

Otra opción que podría considerar es copiar la forma en que PonyLink hace su señalización de bajo nivel.

Este es un enlace en serie entre FPGA, pero puede implementarlo golpeando un microcontrolador. Funciona de una manera inteligente e inusual:

Teniendo en cuenta la tolerancia de los dos cristales de MCU, ¿cuál es el pulso más corto que el emisor puede generar que el receptor pueda detectar? Llamaremos a esto un bit. Si es un pulso alto, entonces diremos que es un solo bit 1, y si fue un pulso bajo, entonces diremos que fue un solo bit 0.

Ahora, ¿cuál es el siguiente pulso más largo que se garantiza que el receptor detectará como más largo que el pulso más corto? Consideraremos que esto son dos bits. Nuevamente, si es un pulso alto, entonces considérelo como dos bits 1 (11), y si fue un pulso bajo, entonces considérelo como dos bits 0 (00).

Y así procedemos. Pulsos cada vez más largos utilizados para representar cadenas de bits más largas.

PonyLink también utiliza la codificación 10b8b . De esta manera puede garantizar que nunca haya una cadena de más de 5 bits del mismo valor, por lo que el receptor puede ver con frecuencia un borde para resincronizar.

No necesita implementar toda la complejidad del protocolo PonyLink completo, pero la señalización de bajo nivel que describí es muy interesante y podría ayudarlo aquí. Lea la sección "Señalización de bajo nivel" del documento protocol.txt .

Incluso hay un script de Python que puedes usar. Dígale las dos velocidades de reloj y su tolerancia, y calculará las longitudes de pulso que necesita para que esto funcione.