Sunday, October 30, 2016

9-bit serial communication in Linux (Raspbian) 8M1, 8S1 modes (with sources)

9-bit serial communication in Linux (Raspbian) 8M1, 8S1 modes (with sources)

 Problem

As you know, there is a problem when you use 9-bit serial communication under Linux. Here you can find solution which will help you solve this issue.



As I described earlier (see my post 9 bit UART (8M1, 8S1 modes) in Linux (hack) ) in RS-485 networks 8M1 mode sometime used for address byte transfer till device selection.

RS-485 network example

Usually protocols looks like this:
<address>, <other_protocol_data>

Where <other_procol_data> can be payload, CRC, etc. Often, when device address transferred via RS-485 it will be transferred in MARK parity mode and other protocol data will be transferred with SPACE parity.

9 bit MARK when address byte transferred

But there is a problem - you can't use 8M1/8S1 modes in Linux.

Solution

General information

As I described in my previous post all that you need is 8M1/8S1 emulation using ODD/EVEN parity modes, supported by Linux.

I got questions about C++ sources which can demonstrate this solution. So, I decided to write full solution. I found device with this protocol:

<device_addres>, ~<device_addres>, <command_byte>, ~<command_byte>

Where:
  • <device_addres> - device address;
  • <command_byte> - byte encoded command;
  • ~ - not operation (reverted byte)
So, my device address is 0x06 and supported command is 0x54. Full command packet will be:
0x06, 0xF9, 0x54, 0xAB

And 0x06 must transferred with set 9-bit - MARK parity must be set for this byte. Other data must be transferred with SPACE parity.

You can download source here: https://github.com/JFF-Bohdan/ninebituart

Or you can download sources using git: https://github.com/JFF-Bohdan/ninebituart.git

This source is C++ using Qt SDK and uses C++11. You can use this source directly or adapt it to your library/SDK.

Let's start from main.cpp:

in this file port will be opened and configured. Pointer to the port object will be transferred to the processor - class which will help work with 9-bit. Using setMarkingEnabled() method you can enable/disable 9-bit for transferred bytes.

AHardwareNineBitSender class will implement 9-bit support using MARK/SPACE parity and ASoftwareNineBitSender class will emulate MARK/SPACE using ODD/EVEN parities (supported by Linux). This classes inherits same class ANineBitSenderBase and have same signatures, so you can dynamically select class for processor.

Let's look into uartcommunicator.cpp:

as you can see in ASoftwareNineBitSender::writeData() implementation before transferring each byte updateParityMode() will be called. This method calculates bits set to ONE and dynamically changes parity for port between EVEN/ODD to emulate MARK/SPACE parities.

You need to understand that this source is for specific device and implements specific protocol. Protocol packet will be compiled using testPacket() function (in main.cpp)

Warning! If you want to test this source with your device, you need:
  1. change testPacket() implementation (in main.cpp) - this function compiles command packet (from host-device to slave-device in RS-485;
  2. change processor->readData() call (in main.cpp) - you need set first parameter - needful bytes count and second parameter - wait time (in msecs) for response wait;
  3. change TEST_PORT_NAME definition - this is port name;
  4. connect your your device to the RS-485;
  5. connect your USB-RS485 to the RPi and USB-RS485 to RS-485 network*
If you from Ukraine, you can use USB-RS485 convertors from http://vkmodule.com.ua:
http://vkmodule.com.ua/Converter/ConverterUSB485.html I'm using this devices for long time and they are amazing.

Compiling and testing

First of all you need install Qt SDK and gcc compiler:

$ sudo apt-get install build-essential
$ sudo apt-get install qt5-default
$ sudo apt-get install libqt5serialport5-dev
$ sudo apt-get install libqt5serialport5

Installing git client:

$ sudo apt-get install git 

If you have problems with Qt5 SDK downloading, you may need update your system. You can find solution here.

Go home (or to any folder you prefer for tests):

$ cd ~

Download sources:

$ git clone https://github.com/JFF-Bohdan/ninebituart.git

If this operation succeeded you will got a ninebituart directory in working  directory. Let's compile source code, first of all we need go to the folder with sources:

$ cd ninebituart

Before next step, please be sure that you have correct date & time in your system. You can use command date for current date & time retrieve. Also, you can find solution complete solution for packages update by using system-update.sh. After checking date & time you can continue compiling:

$ qmake ./ninebituart.pro

$ make

When compile finish you will go executable in bin directory.

If you want you can run it by this command (you must be in bin folder):

$ cd bin

Now we can execute executable and test

$ ./ninebituart 

Example output (on my RPi when communicating with my device):
avail ports:
"ttyAMA0"
"ttyUSB0"

opening port:  /dev/ttyUSB0
updating parity
TX: "06"
updating parity
TX: "f954ab"
RX:  "067e40a2ea"
COMPLETE


As you can see, i have two UARTs: "ttyAMA0", "ttyUSB0". Then program opened UART "/dev/ttyUSB0" and sent device address "06" (with 9 bit set), after that was sent data "f954ab" And finally, received device response: "067e40a2ea". Everything works!

Warning! Example output correct just for my device.

Hope you like it.

Enjoy!

2 comments:

  1. Hi I tried your code over the 9 bit protocol for wafer RS232 to MDB, but i am getting random code in linux, rasbin rasbery pi, compared to same code in windows. is there any encoding changes needed ?

    ReplyDelete
    Replies
    1. Could you be so kind to explain your question more detailed? Thank you.

      Delete