High Delay in Rs232 Communication on a Pxa270

High delay in RS232 communication on a PXA270

Thank you very much for your comments.

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 :-)

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 setserial:

#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);

Best Practise to Read Desired Amount of Data from Serial?

But AFAIK system calls are expensive, ...

True, a system call can consume many more CPU cycles than a local procedure/function call. The system call requires CPU-mode transitions between user-mode to (protected) kernel mode, and then back to user mode.

... thus, isn't that approach somehow crude, especially when the desired length is large?

The first question you have to ask yourself when reading from a serial terminal (e.g. a /dev/ttyXn device)(rather than a serial port) is "what kind of data is going to be received, that is, is the data lines of (ASCII) text terminated by some type of EOL (end of line) character, or does the data need to be simply treated as binary (or raw) data?"

Lines of text should be read from a serial terminal using canonical (aka cooked) mode. The OS will perform a lexical scan of the received data for your program, and delimit each line of text based on the EOL characters you specify.
The read() can then return a line assuming that blocking I/O is used, and the line of text is not longer than the buffer that is provided.

Binary data should be read from a serial terminal using noncanonical (aka raw) mode. The OS will ignore the values of the data, and (when blocking I/O is used) each read() will return an amount of data based on constraints of time and number of bytes.

See this answer for more details.

Note that the post your question refers to actually is about reading text, yet the OP is (mis)using non-canonical mode. If the OP had used the proper mode to match the input, then he might have never had a partial-read problem.



I am just curious that whether there is a better practise to avoid intensive system calls in this scenario?

Proper termios configuration is essential for efficient I/O with serial terminals.

Blocking I/O mode should be considered the preferred mode.

The OS can perform its scheduling for multitasking better when processes relinquish control more often.

The OS is more efficient in determining when data is available for return to a user.

Note also that the termios configuration is most effective when blocking mode is used, e.g. the VMIN and VTIME specifications in noncanonical mode.

For example using a select() or poll() and then a read() is one additional syscall more than when compared to just the (blocking) read(). And yet you can find many such code examples because there seems to be some popular misconception that the program can get the data faster from the "UART" that way.

But non-blocking and async modes are not necessarily faster (in a multitasking OS), and the read() merely fetches data from the termios buffer which is several layers removed from the actual hardware.

If your program uses non-blocking mode but does not perform useful work while waiting for data, and instead uses select() or poll() (or even worse calls sleep()), then your program is unnecessarily complex and ineffecient. See this answer.

A blocking-mode read() can do all that waiting for your program, make your program simpler and easier to write and maintain, and be more runtime efficient.

However for blocking non-canonical reads, you will have to accept some degree of inefficiency. The best you can do is trade-off latency versus the number of syscalls. One possible example is this answer which tries to fetch as much data per syscall, yet allow for an easy byte-by-byte lexical scan of the received binary data.


Note that a possible source of latency when reading a serial terminal is a poorly configured kernel, rather than the termios API and read() overhead.

For instance, setting the ASYNC_LOW_LATENCY flag via ioctl() (e.g. see High delay in RS232 communication on a PXA270) is one way to improve read() latency.

RS232 Communication - Can I use it to create a steady state signal?

You should be able to set something like DTR (Data Terminal Ready), pin 20, or DSR (Data Set Ready), pin 6, high and keep it there as your steady-state signal. This is how modems/terminals detect that there is a device on the other end that is ready to communicate. It all depends on what level of access you have to the hardware through your driver.

[EDIT] This doesn't involve sending data, although you could still do that using TX/RX, pins 2 & 3.

RS-232 Reference on wikipedia

What are reasons that read() on a POSIX serial port might be slow?

The solution is to set the low_latency flag on the serial port.

See High delay in RS232 communication on a PXA270
and
http://osdir.com/ml/serial/2003-11/msg00020.html

ptp4l High Values of Master offset, freq and path delay

This issue was solve with the following

  1. Turn off the NTP service in both systems
       timedatectl set-ntp false
sudo systemctl stop systemd-timedated.service
sudo systemctl stop systemd-timesyncd


  1. Synchronize the time in the NIC with the following command, eno1 is the interface. Be careful with this becasue the NIC can have a different time that the system clock and can create problems. Take in consideration that eno1 is the interface where are you making the synchro
        phc2sys -c eno1 -O 0 -w -m -s CLOCK_REALTIME

  1. Turn on the phc2sys so it synchronize the system clock to ptp clock of interface
         phc2sys -s eno1 -m -w

  1. Turn on the ptp4l in the master
          ptp4l -i eno1 -mq -2

  1. Turn on the ptp4l in the slave side
           ptp4l -S -i enp3s0f1 -mq -s -2

The option -2 (IEEE 802.3 or Ethernet) in the ptp4l, it can be change to -4 (UDP IPV4) or -6 (UDP IPV6)



Related Topics



Leave a reply



Submit