How to Use 9-Bit Serial Communication in Linux

How to access my 9-data bit serial port on a PC?

The document at http://www.semiconductorstore.com/pdf/newsite/oxford/ox16c950b.pdf describes the differences between various UARTs.
While your StarTech board includes the 16C950, which is RS-485 (and 9-bit) capable, it uses it in RS-232 compatible (550) mode, similar to 16550/8250 from IBM-PC days, and supports max 8 bit data.

You need a board with the same chip (16C950) but that exposes the RS-485 compatible 950 mode that supports 9 bit data as per the spec. And any board claiming such support would have to come with custom drivers for Windows, since Microsoft's is 8 bit only.

There are several other chips that can do 9-bit RS-485 mentioned here but again finding Windows driver support will be tricky. And, of course, many boards use 16C950 but only in 8-bit and/or RS-232 only mode, and without appropriate drivers.

In answer to your related question on Superuser, sawdust suggested the Sealevel 7205e, which looks like a good choice, with Windows driver support. It is pricey but they specifically mention 9-bit, RS-485 support, and Windows drivers. It may well be your best option.

C linux: Serial communication lost some first bytes when receive data

I used to work with RS485 bus a lot. And one problem that sometimes appeared was very similar to your. Because RS485 is half duplex bus, there is mechanism that switching receive mode and transmit mode on RS485 bus driver. And this was exactly cause of my problems.

When master device sent some data, slave was ready to reply (and replied) before bus driver (on master side) was switched to receive mode. This behavior ended with data loss.

May I suggest you to check, using oscilloscope, that slave sent data correctly? If so, you probably don't have too much options to do as possible solutions are:

  • Slave have to wait some time before sending reply to master.
  • Change HW, some RS485 driver that will be faster in switching modes or use different BUS.

Linux UART communication, first byte drop

I am trying uart communication between PC as sender ( Ubuntu 20.04 ) and embedded device as receiver ...

Your Linux application programs have no direct access to the hardware (i.e. the UART), but instead access a serial terminal using the termios API. See Linux serial drivers
to grasp the layers of code involved.

The Sender Part:

...

The Receiver Part:

...

The code you have posted has numerous bugs which makes replication of the problem you report difficult.


The Receiver terminates on a valid read timeout

When executing your Receiver program from the shell, the read() request is likely to simply timeout after one second, (incorrectly) reports an error, and then terminates.

$ ./a.out
Error reading: Success$

Unless you have initiated transmission of the serial message within that 1 second of open() and read() time, that Receiver program is unlikely to acquire any characters at all. Are you relying on some two-handed synchronization tricks to get these two programs to pass data?

The simple solution to this bug is to stop treating a timeout with no data as an error. That is, a return code of 0 from read() is not an error, but is a valid return condition.

         num_bytes = read(serial_port, &read_buf, sizeof(read_buf));

- if (num_bytes <= 0) {
+ if (num_bytes < 0) {
printf("Error reading: %s", strerror(errno));
break;
}

read() does not return a string

The read() syscall simply returns an array of bytes, rather than a null-terminated string. However your printf() does assume the buffer contents are a sting.

There's a memset() to clear the buffer, but that happens only once before the while loop is entered.

The proper fix is the use num_bytes to append a null byte to properly terminate the text. Note that the read request count should be reduced by one to insure that there's a spare byte in the array post-read.

-        num_bytes = read(serial_port, &read_buf, sizeof(read_buf));
+ num_bytes = read(serial_port, &read_buf, sizeof(read_buf) - 1);

...
+ read_buf[num_bytes] = 0;
printf("Read %i bytes. Received message: %s\n", num_bytes, read_buf);


But if i send it with null termination like {'\0','A','B','C'} its all fine at the receiver part.

Starting a text message with a string terminator make no sense at all.

On a successful read by your Receiver program, I would see the bizarre display of:

...  
Read 6 bytes. Received message:
...

The string terminator at the beginning of the buffer will inhibit the printing of all the received characters!

Why not use a punctuation mark such as '?' instead of an unprintable or a control character?

...
Read 6 bytes. Received message: ?ABCDE
...

Noncanonical read will not reliably return complete "messages"

Whatever you think constitutes a serial message or packet or datagram, a noncanonical read() is never assured of returning a complete "message". The VMIN and VTIME termios parameters can be adjusted match the expected characteristics of incoming messages; see Linux Blocking vs. non Blocking Serial Read. But the receiving program must always be able to accommodate "short" or partial reads. See this answer for a buffering scheme.


Unable to reliably replicate the reported problem

After making the code changes described above plus making the printf()s more readable, I am unable to reliably replicate the reported problem.

Some sample results of the tweaked Receiver program:

Read 0 bytes. Received message: 
Read 6 bytes. Received message: ?ABCDE
Read 6 bytes. Received message: ?ABCDE
Read 6 bytes. Received message: ?ABCDE
Read 1 bytes. Received message: ?
Read 3 bytes. Received message: ABC
Read 2 bytes. Received message: DE
Read 1 bytes. Received message: ?
Read 5 bytes. Received message: ABCDE
Read 1 bytes. Received message: ?
Read 5 bytes. Received message: ABCDE
Read 6 bytes. Received message: ?ABCDE
Read 1 bytes. Received message: ?
Read 5 bytes. Received message: ABCDE
Read 6 bytes. Received message: ?ABCDE
Read 6 bytes. Received message: ?ABCDE
Read 6 bytes. Received message: ?ABCDE
Read 6 bytes. Received message: ?ABCDE
Read 1 bytes. Received message: ?
Read 5 bytes. Received message: ABCDE
Read 6 bytes. Received message: ?ABCDE
Read 1 bytes. Received message: ?
Read 5 bytes. Received message: ABCDE
Read 6 bytes. Received message: ?ABCDE
Read 1 bytes. Received message: ?
Read 5 bytes. Received message: ABCDE

read() does seem to have an occasional inclination to return a short read of just the first character of the message, but it is never lost or "dropped". And it can also occasionally return the complete message in one buffer.

There is no consistent "failure" as reported when using my setup.



Related Topics



Leave a reply



Submit