Los esquemas de solicitud/respuesta tienden a ser ineficientes y aparecen rápidamente en el puerto serie. Si está interesado en el rendimiento, mire el protocolo de ventana, como el protocolo de envío de archivos kermit.
Ahora bien, si quiere apegarse a su protocolo y reducir la latencia, seleccionar, sondear, leer le dará aproximadamente la misma latencia, porque como indicó Andy Ross, la latencia real está en el manejo FIFO del hardware.
Si tiene suerte, puede modificar el comportamiento del controlador sin parchear, pero aún necesita mirar el código del controlador. Sin embargo, hacer que el ARM maneje una tasa de interrupción de 10 kHz ciertamente no será bueno para el rendimiento general del sistema...
Otra opción es rellenar su paquete para que alcance el umbral FIFO cada vez. También confirmará si es o no un problema de umbral FIFO.
10 ms @ 115200 es suficiente para transmitir 100 bytes (suponiendo 8N1), por lo que lo que está viendo probablemente se deba a que low_latency
la bandera no está configurada. Prueba
setserial /dev/<tty_name> low_latency
Establecerá el indicador low_latency, que utiliza el kernel cuando mueve datos hacia arriba en la capa tty:
void tty_flip_buffer_push(struct tty_struct *tty)
{
unsigned long flags;
spin_lock_irqsave(&tty->buf.lock, flags);
if (tty->buf.tail != NULL)
tty->buf.tail->commit = tty->buf.tail->used;
spin_unlock_irqrestore(&tty->buf.lock, flags);
if (tty->low_latency)
flush_to_ldisc(&tty->buf.work);
else
schedule_work(&tty->buf.work);
}
El schedule_work
call podría ser responsable de la latencia de 10 ms que observa.
Los puertos serie en Linux están "envueltos" en construcciones de terminal de estilo Unix, lo que lo golpea con un retraso de 1 tic, es decir, 10 ms. Prueba si stty -F /dev/ttySx raw low_latency
ayuda, aunque no hay garantías.
En una PC, puede volverse duro y hablar directamente con los puertos serie estándar, emita setserial /dev/ttySx uart none
para desvincular el controlador de Linux del puerto serie hw y controlar el puerto a través de inb/outb
a los registros portuarios. Lo he intentado, funciona muy bien.
La desventaja es que no recibe interrupciones cuando llegan los datos y tiene que sondear el registro. a menudo.
Debería poder hacer lo mismo en el lado del dispositivo del brazo, puede ser mucho más difícil en el hw exótico del puerto serie.
Después de hablar con algunos ingenieros más sobre el tema, llegué a la conclusión de que este problema no se puede resolver en el espacio del usuario. Dado que necesitamos cruzar el puente hacia kernel land, planeamos implementar un módulo de kernel que hable con nuestro protocolo y nos dé latencias <1 ms.
--- editar ---
Resulta que estaba completamente equivocado. Todo lo que era necesario era aumentar la tasa de ticks del núcleo. Los 100 tics predeterminados agregaron el retraso de 10 ms. 1000 Hz y un buen valor negativo para el proceso en serie me da el comportamiento de tiempo que quería alcanzar.
Esto es lo que setserial
hace para establecer una latencia baja en un descriptor de archivo de un puerto:
ioctl(fd, TIOCGSERIAL, &serial);
serial.flags |= ASYNC_LOW_LATENCY;
ioctl(fd, TIOCSSERIAL, &serial);