Transmisión ATMEGA UART con control de flujo

Fondo

Estoy usando un ATmega328P para enviar datos a otro dispositivo a una alta velocidad de transmisión (230400). El dispositivo al que estoy enviando datos admite el control de flujo y aumenta la señal RTS cuando necesita mantener la transmisión. El problema es que el dispositivo espera que detenga la transmisión inmediatamente después de que se afirma RTS.

El ATmega328P tiene un búfer de transmisión (UDR) y un registro de desplazamiento desde el cual se envían datos a la línea TX. Dado que el ATmega328P no es compatible con el control de flujo en el hardware, lo estoy implementando en el software. Antes de enviar (antes de escribir en UDR) estoy revisando RTS y esperando hasta que baje, pero aparentemente esto no es suficiente porque los datos en el búfer de transmisión y el registro de desplazamiento todavía se envían y aún se puede enviar un byte completo después de que RTS es afirmó cuando el ATmega328P vacía sus búferes.

Aparentemente, deshabilitar el transmisor cuando se afirma RTS (configurar el TXEN en cero) no ayuda, ya que no se hará efectivo hasta que se completen las transmisiones en curso y pendientes, según la sección de documentación 19.6.5 .

Una solución alternativa podría ser esperar hasta que se complete la transmisión (TXC) antes de enviar cada byte, pero esto afectaría la tasa de datos ya que no estoy enviando el flujo de bytes de forma consecutiva incluso cuando RTS no está afirmado (estoy empezando a enviar un nuevo byte solo después de que el anterior haya sido enviado por completo).

Mi pregunta es:

Con el ATmega328P, ¿hay alguna forma de admitir el control de flujo RTS en el transmisor mientras se sigue disfrutando de la canalización proporcionada por el búfer de transmisión y el registro de desplazamiento?

¿Qué tipo de dispositivo espera que detengas la transmisión en medio de un cuadro?
No veo qué tiene de malo el enfoque de transmisión completa. Sí, puede causar un retraso de un solo bit, dependiendo de la velocidad del reloj de su controlador, pero debe verificar la condición RTS en cualquier caso. Incluso con un UART implementado por software (que no es tan difícil), tendría que hacer esta verificación. ¿O me pierdo algo? Supongo que sí porque la pregunta no parece que no hayas pensado en ella durante un tiempo. ¿El otro dispositivo eleva la línea RTS "instantáneamente" después de recibir el último bit?
@IgnacioVazquez-Abrams no está en medio de un cuadro (un byte), pero atmega vaciará el búfer de transmisión antes de detenerse, por lo que todavía se puede enviar exactamente un byte después de que se afirmó RTS.
Entonces, elija la interrupción RTS de modo que tenga prioridad sobre la interrupción TXE.
@Rev1.0 si el TXEN no vació el búfer de transmisión antes de que sea efectivo, eso probablemente resolvería el problema. O si el dispositivo pudiera aceptar otro byte único después de que se afirma RTS. ¿Crees que es un retraso de un solo bit? solo después de que se afirma TXC, comienzo a enviar otro byte. Dependiendo del escenario, esto probablemente podría llevar algún tiempo. Supongo que una solución podría ser UART implementado por SW, pero eso requeriría muchos más recursos de la MCU y no tiene sentido cuando HW es compatible con UART.
@IgnacioVazquez-Abrams y luego que? poner el atmega a dormir para detener el reloj UART hasta que RTS se caiga? El problema es que no puedo desactivar el transmisor hasta que vacíe sus búferes.
Cuando dices "vaciar", supongo que hablas de que el siguiente byte va al búfer de transmisión debido al doble búfer. ¿Por qué no simplemente poner un solo byte en la cola de transmisión (UDR = datos), esperar a que TX complete el ISR, verificar si RTS está configurado, si es 'sí', escribir el siguiente byte en UDR, si 'no' no hacer nada y verificar la condición RTS? en bucle principal. Con respecto a la velocidad de datos: si ejecuta a 16MHz/8MHz, tiene alrededor de 70/35 instrucciones por bit (@230k baudios). Esto debería ser suficiente para evaluar RTS y reaccionar.
Y luego le dices a la rutina de transmisión que no envíe el siguiente byte.
@IgnacioVazquez-Abrams Ya no estoy enviando el siguiente byte cuando se afirma RTS, pero es demasiado tarde porque un byte ya está almacenado en el búfer y se enviará de todos modos (y esto sucederá después de que se haya afirmado RTS)
@Rev1.0 Lo que sugiere suena como una opción válida y es más o menos la solución alternativa que presento en mi pregunta. Puede tener razón en que la multa no es alta, pero al menos no es así como ATMEL sugiere hacerlo en sus documentos. Presentan un fragmento de código para enviar datos en UART (aunque sin control de flujo). En el ejemplo de código en la sección 19.6.1 en sus documentos , sugieren sondear UDREny no TXCn.

Respuestas (1)

Parece que tiene dos problemas básicos, un dispositivo externo mal diseñado y que está tratando de hacer que un hardware inapropiado haga un trabajo para el que no está diseñado.

Primero, ¿realmente este otro dispositivo no requiere absolutamente más bytes después de que se afirma RTS? Eso sería muy inusual. Es muy común que los transmisores vacíen un pequeño búfer antes de obedecer a RTS, por lo que, a menos que el otro ingeniero no supiera lo que estaba haciendo, el dispositivo debería poder tolerar unos pocos bytes más. Tal vez no pueda manejar el caso "con todas las funciones" de 16 bytes más, pero no permitir 2-4 bytes más es simplemente un mal diseño.

En segundo lugar, dado que debe dejar de transmitir más rápido que el tamaño del búfer de salida, eligió el hardware incorrecto. No estoy familiarizado con esa línea de micros, pero seguramente esto está en la hoja de datos. Veo tres opciones:

  1. Obtenga un micro que tenga una entrada RTS y que se detenga en el siguiente límite de bytes cuando se afirma. La mayoría de los UART en micros no tienen esta función adicional, pero ciertamente hay algunos que sí. Sé que algunos PIC 33F pueden hacer esto porque los elegí en parte para esta característica. Mire alrededor de la línea de productos de Atmel. Es posible que también tengan algunos micros con esta característica.

  2. Use un UART externo que controle el flujo de hardware a nivel de byte.

  3. Use el UART existente con más intervención de software. No podrá usar el búfer interno y tendrá que esperar hasta que se envíe un carácter, verificar RTS y luego escribir el siguiente carácter en el UART. Esto se facilitará si el UART puede interrumpir cuando está vacío, no solo cuando hay espacio en el búfer de salida.

Ninguna de estas compensaciones puede ser lo que desea, pero ese es el costo de una mala arquitectura. Arréglalo en la siguiente revolución o vuelve a girar ahora si es realmente importante. El mundo no siempre tiene una respuesta mágica solo porque tu jefe la quiere ahora o porque te arrinconaste.

Lo que pasa es que los AVR no tienen un búfer de salida UART; solo tienen el byte único que están transmitiendo o que han recibido, por lo que no estoy exactamente seguro de dónde está el problema en primer lugar.
@Ignacio: ¿En serio? ¿El UART ni siquiera tiene doble búfer? Eso suena extraño a menos que se trate de un procesador optimizado en costo de muy bajo nivel. Espero que haya al menos un registro en el que pueda escribir el siguiente byte mientras se registra el byte anterior. Eso significa que el byte en este registro de espera se enviará tan pronto como se complete el byte anterior, sin que el software pueda verificar RTS y no enviar el byte. Incluso los PIC más baratos y tontos tienen este FIFO de 1 profundidad, y me resulta difícil creer que ninguna de las partes de Atmel tiene eso.
La recepción tiene doble búfer, la transmisión no.
@IgnacioVazquez-Abrams Esto no es correcto: puede pensar que los AVR tienen un "búfer de un byte": tienen un registro de datos de transmisión UDRn más un registro de desplazamiento de transmisión. El SW puede escribir en UDRnel siguiente valor a enviar mientras el anterior se envía desde el registro de desplazamiento al mismo tiempo. El problema aquí es el hecho de que la transmisión no se puede detener hasta que ambos UDRny el registro de desplazamiento de transmisión estén vacíos.
@OlinLathrop Me gusta la forma en que lo dices "El mundo no siempre tiene una respuesta mágica, etc.", y estoy de acuerdo contigo al respecto. Entonces, una respuesta válida a mi pregunta podría ser "No, no hay forma de hacerlo" a menos que reemplace HW, implemente UART en SW o renuncie a la canalización proporcionada por el búfer de transmisión y el registro de desplazamiento. Si esta es la respuesta correcta, en realidad me hace sentir mejor porque significa que hice una buena investigación antes de publicar esta pregunta. Envié la pregunta por la pequeña posibilidad de que me perdí algo y hay otra forma de hacerlo, de lo contrario, ya tengo una solución.
@AmirGonnen: Ah, eso explica por qué estamos hablando de propósitos cruzados. Está esperando hasta que UDREesté vacío, pero lo que debería suceder es que el siguiente byte no debería ponerse en cola hasta que se complete la transmisión. Esto permitirá el control byte por byte.