I’m experiencing a long delay (1.5ms – 9.5ms) in a RS232 communication on a PXA270 RISC PC/104. I want to minimize the long delay but I’m a beginner with embedded devices and C++ so I think I’m missing something.
The mentioned delay is at the time when the PXA board receives a packet from the external device via RS232 (115200 baud) until it sends an ACK custom packet back to the external device. I measured the delay on the PXA board with an oscilloscope, one channel at the Tx (green) and the other on the Rx (blue).
The PXA board is running an Arcom Embedded Linux (AEL). I know, it’s not a real-time OS, but I still think, that an average delay of 4.5ms is way too high for extracting the received packet, verify it’s CRC16, construct an ACK packet (with CRC) and send it back the serial line. I also deliberately put the CPU under heavy load (some parallel gzip operations) but the delay time didn’t increase at all. The maximum size of a received packet is 30 bytes.
A C++ application (another former co-worker wrote it) is handling the reception of the packets and their acknowledgement. One thread is sending and the other is receiving the packets.
I thought that the RTC on the PXA board has a very bad resolution and the AEL can not align the timing to the internal RTC resolution. But the RTC has a frequency of 32.768 kHz. The resolution is sufficient, still don’t explain the high delay. Btw, I think the OS is using the internal PXA clock (which has also a sufficient resolution) instead of the RTC for the timing.
Therefore the problem must be in the C++ app or in a driver/OS setting of the RS232 interface.
The following control flags are used for the RS232 communication in the C++ application according to the Serial Programming Guide for POSIX Operating Systems:
// Open RS232 on COM1 mPhysicalComPort = open(aPort, O_RDWR | O_NOCTTY | O_NDELAY); // Force read call to block if no data available int f = fcntl(mPhysicalComPort, F_GETFL, 0); f &= ~O_NONBLOCK; fcntl(mPhysicalComPort, F_SETFL, f); // Get the current options for the port and set the desired values tcgetattr(mPhysicalComPort, &options); cfsetispeed(&options, baudRate); cfsetospeed(&options, baudRate); // no parity (8N1) options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB; options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; // disable hardware flow control options.c_cflag &= ~CRTSCTS; // raw input options.c_lflag = 0; // disable software flow control options.c_iflag = 0; // raw output options.c_oflag = 0; // Set byte times options.c_cc[VMIN] = 1; options.c_cc[VTIME] = 0; // Set the new options for the port tcsetattr(mPhysicalComPort, TCSAFLUSH, &options); // Flush to put settings to work tcflush(mPhysicalComPort, TCIOFLUSH);
I think I’m missing something very simple. I think, that if the process of the app is running under a higher priority, this will not solve the problem. There must be something, which instructs the RS232 driver to handle the requests with a higher priority to minimize the latency.
Does anyone have any ideas? Thank you very much in advance for your help.
I was able to reduce the delay to ~0.4ms. The command
setserial(8) was referenced in the AEL manual. And bingo, I found the
low_latency flag there with the following description:
Minimize the receive latency of the serial device at the cost of greater CPU utilization. (Normally there is an average of 5-10ms latency before characters are handed off to the line discpline to minimize overhead.) This is off by default, but certain real-time applications may find this useful.
I then executed
setserial /dev/ttyS1 low_latency and the delay was reduced to ~0.4ms *yay*
But I wanted to implement this behaviour in the C++ app, without setting this flag globally with
setserial (this command is by default not included in all distros).
I’ve added the following code lines, which had the same effect as the
low_latency flag from
#include <sys/ioctl.h> #include <linux/serial.h> // Open RS232 on COM1 mPhysicalComPort = open(aPort, O_RDWR | O_NOCTTY | O_NDELAY); struct serial_struct serial; ioctl(mPhysicalComPort, TIOCGSERIAL, &serial); serial.flags |= ASYNC_LOW_LATENCY; // (0x2000) ioctl(mPhysicalComPort, TIOCSSERIAL, &serial);
Have phun 🙂