12

RS-232 communication sometimes uses 9-bit bytes. This can be used to communicate with multiple microcontrollers on a bus where 8 bits are data and the extra bit indicates an address byte (rather than data). Inactive controllers only generate an interrupt for address bytes.

Can a Linux program send and receive 9-bit bytes over a serial device? How?

Charles
  • 50,943
  • 13
  • 104
  • 142
joeforker
  • 40,459
  • 37
  • 151
  • 246
  • 2
    Are you sure the 9th bit isn't just a parity bit ? I've only ever seen 7 or 8 data bits + optional parity bit + 1, 1.5 or 2 stop bits. – Paul R Jun 30 '10 at 14:44
  • 2
    @Paul R - 9 bit settings are uncommon, but (at least used to be) supported. http://en.wikipedia.org/wiki/Serial_port#Data_bits – Tim Post Jun 30 '10 at 14:57
  • Can you indulge those who are probably curious as to what you have that wants a 9 bit setting? – Tim Post Jun 30 '10 at 14:58
  • 1
    @Tim Post: Just a guess, but some 8051 microcontrollers have an enhanced serial port that use the 9th bit as an address field marker to avoid generating interrupts on messages intended for a different node. – Amardeep AC9MF Jun 30 '10 at 15:05
  • @Amardeep - so the 8'th bit remains the stop bit? – Tim Post Jun 30 '10 at 17:11
  • @Tim Post: The frame is composed of a start bit followed by 9 data bits followed by a stop bit. The data bits are numbered d0 to d8. – Amardeep AC9MF Jun 30 '10 at 18:01
  • 2
    The OP almost certainly wants a true 9-data bits, plus stop bit(s) & parity. It sounds like a http://en.wikipedia.org/wiki/Multidrop_bus – Mawg says reinstate Monica Jan 09 '13 at 04:13

6 Answers6

5

The termios system does not directly support 9 bit operation but it can be emulated on some systems by playing tricks with the CMSPAR flag. It is undocumented and my not appear in all implementations.

Here is a link to a detailed write-up on how 9-bit emulation is done:

http://www.lothosoft.ch/thomas/libmip/markspaceparity.php

Amardeep AC9MF
  • 18,464
  • 5
  • 40
  • 50
  • [Use The PC's UART With 9-Bit Protocols](http://electronicdesign.com/article/embedded/use-the-pc-s-uart-with-9-bit-protocols6245.aspx) has a less-detailed writeup, and hints that *receiving* 9 bit data might be possible by inspecting and clearing the "parity error" bit. – David Cary Sep 01 '11 at 20:50
3

9-bit data is a standard part of RS-485 and used in multidrop applications. Hardware based on 16C950 devices may support 9-bits, but only if the UART is used in its 950 mode (rather than the more common 450/550 modes used for RS-232).

A description of the 16C950 may be found here.

This page summarizes Linux RS-485 support, which is baked into more recent kernels (>=3.2 rc3).

Peter Krnjevic
  • 1,070
  • 15
  • 20
3

9-bit data framing is possible even if a real world UARTs doesn't. Found one library that also does it under Windows and Linux. See http://adontec.com/9-bit-serial-communication.htm

2

basically what he wants is to output data from a linux box, then send it on let's say a 2 wire bus with a bunch of max232 ic's -> some microcontroller with uart or software rs232 implementation

one can leave the individual max232 level converter's away as long as there are no voltage potency issues between the individual microcontrollers (on the same pcb, for example, rather than in different buildings ;) up until the maximum output (ttl) load of the max232 (or clones, or a resistor and invertor/transistor) ic.

can't find linux termios settings for MARK or SPACE parity (Which i'm sure the hardware uarts actually do support, just not linux tty implementation), so we shall just hackzor the actual parity generation a bit.

8 data bits, 2 stop bits is the same length as 8 databits, 1 parity bit, 1 stop bit. (where the first stopbit is a logic 1, negative line voltage).

one would then use the 9th bit as an indicator that the other 8 bits are the address of the individual or group of microcontrollers, which then take the next bytes as some sort of command, or data, as well, they are 'addressed'.

this provides for an 8 bit transparant, although one way traffic, means to address 'a lot of things' (256 different (groups of) things, actually ;) on the same bus. it's one way, for when one would want to do 2 way, you'd need 2 wire pairs, or modulate at multiple frequencies, or implement colission detection and the whole lot of that.

PIC microcontrollers can do 9 bit serial communication with ehm 'some trickery' (the 9th bit is actually in another register ;)

now... considering the fact that on linux and the likes it is not -that- simple...

have you considered simply turning parity on for the 'address word' (the one in which you need 9 bits ;) and then either setting it to odd or even, calculate it so that the right one is chosen to make the 9th (parity) bit '1' with parity on and 8 bit 'data', then turn parity back off and turn 2 stop bits on. (which still keeps a 9 bit word length in as far as your microcontroller is concerned ;)... it's a long time ago but as far as i recall stop bits are just as long as data bits in the timing of things.

this should work on anything that can do 8 bit output, with parity, and with 2 stop bits. which includes pc hardware and linux. (and dos etc)

pc hardware also has options to just turn 'parity' on or off for all words (Without actually calculating it) if i recall correctly from 'back in the days'

furthermore, the 9th bit the pic datasheet speaks about, actually IS the parity bit as in RS-232 specifications. just that you're free to turn it off or on. (on PIC's anyway - in linux it's a bit more complicated than that)

(nothing a few termios settings on linux won't solve i think... just turn it on and off then... we've made that stuff do weirder things ;)

a pic microcontroller actually does exactly the same, just that it's not presented like 'what it actually is' in the datasheet. they actually call it 'the 9th bit' and things like that. on pc's and therefore on linux it works pretty much the same way tho.

anyway if this thing should work 'both ways' then good luck wiring it with 2 pairs or figuring out some way to do collission detection, which is hell a lot more problematic than getting 9 bits out.

either way it's not much more than an overrated shift register. if the uart on the pc doesn't want to do it (which i doubt), just abuse the DTR pin to just shift out the data by hand, or abuse the printer port to do the same, or hook up a shift register to the printer port... but with the parity trick it should work fine anyway.

   #include<termios.h>
   #include<stdio.h>
   #include<sys/types.h>
   #include<sys/stat.h>
   #include<fcntl.h>
   #include<unistd.h>
   #include<stdint.h>
   #include<string.h>
   #include<stdlib.h>

   struct termios com1pr;
   int com1fd;

   void bit9oneven(int fd){
   cfmakeraw(&com1pr);
   com1pr.c_iflag=IGNPAR;
   com1pr.c_cflag=CS8|CREAD|CLOCAL|PARENB;
   cfsetispeed(&com1pr,B300);
   cfsetospeed(&com1pr,B300);
   tcsetattr(fd,TCSANOW,&com1pr);
   };//bit9even

   void bit9onodd(int fd){
   cfmakeraw(&com1pr);
   com1pr.c_iflag=IGNPAR;
   com1pr.c_cflag=CS8|CREAD|CLOCAL|PARENB|PARODD;
   cfsetispeed(&com1pr,B300);
   cfsetospeed(&com1pr,B300);
   tcsetattr(fd,TCSANOW,&com1pr);
   };//bit9odd

   void bit9off(int fd){
   cfmakeraw(&com1pr);
   com1pr.c_iflag=IGNPAR;
   com1pr.c_cflag=CS8|CREAD|CLOCAL|CSTOPB;
   cfsetispeed(&com1pr,B300);
   cfsetospeed(&com1pr,B300);
   tcsetattr(fd,TCSANOW,&com1pr);
   };//bit9off

   void initrs232(){
   com1fd=open("/dev/ttyUSB0",O_RDWR|O_SYNC|O_NOCTTY);
   if(com1fd>=0){
   tcflush(com1fd,TCIOFLUSH);
   }else{printf("FAILED TO INITIALIZE\n");exit(1);};
   };//initrs232

   void sendaddress(unsigned char x){
   unsigned char n;
   unsigned char t=0;
   for(n=0;n<8;n++)if(x&2^n)t++;
   if(t&1)bit9oneven(com1fd);
   if(!(t&1))bit9onodd(com1fd);
   write(com1fd,&x,1);
   };

   void main(){

   unsigned char datatosend=0x00; //bogus data byte to send
   initrs232();
   while(1){
   bit9oneven(com1fd);
   while(1)write(com1fd,&datatosend,1);
   //sendaddress(223); // address microcontroller at address 223;
   //write(com1fd,&datatosend,1); // send an a
   //sendaddress(128); // address microcontroller at address 128;
   //write(com1fd,&datatosend,1); //send an a
   }
   //close(com1fd);
   };

somewhat works.. maybe some things the wrong way around but it does send 9 bits. (CSTOPB sets 2 stopbits, meaning that on 8 bit transparant data the 9th bit = 1, in addressing mode the 9th bit = 0 ;)

also take note that the actual rs232 line voltage levels are the other way around from what your software 'reads' (which is the same as the 'inverted' 5v ttl levels your pic microcontroller gets from the transistor or inverter or max232 clone ic). (-19v or -10v (pc) for logic 1, +19/+10 for logic 0), stop bits are negative voltage, like a 1, and the same lenght.

bits go out 0-7 (and in this case: 8 ;)... so start bit -> 0 ,1,2,3,4,5,6,7,

it's a bit hacky but it seems to work on the scope. enter image description here

  • receiving... is somewhat... different. but duplex operation is gonna be a pain anyway...(hardware wise).. to also receive with linux you could limit yourself to mcu addresses which rs232 parity would NOT be a logic 1 as linux seems to have no way to read out the parity bit, only to indicate that it did not match the setting selected), and the first stopbit is also 1... but then you lose 128 of the possible addresses. (you keep the 8 bit data transparancy tho - and can still have both way communication with 128 mcu's on the bus... – HRH Sven Olaf of CyberBunker Mar 04 '16 at 06:29
1

Can a Linux program send and receive 9-bit bytes over a serial device?

The standard UART hardware (8251 etc.) doesn't support 9-bit-data modes.

ChrisW
  • 54,973
  • 13
  • 116
  • 224
  • Most UARTs only have 8 bit data (and status) registers so it's not obvious how you would even send/receive 9 bit data - maybe it would require two 8 bits reads/writes per 9 bit byte ? – Paul R Jun 30 '10 at 15:41
  • @Paul R - Amardeep may be suggesting you do it by manually toggling the stop-bit and/or parity-bit settings before transmitting each byte; but that wouldn't do for receiving 9-bit data. – ChrisW Jun 30 '10 at 16:09
  • 1
    An 8250/16450/16550 UART must be tricked into transmitting the 9th data bit by using mark and space parity. You can't manually toggle start and stop bits. Receive can be done using parity error detection. – Amardeep AC9MF Jun 30 '10 at 18:17
1

I also made complete demo for 9-bit UART emulation (based on even/odd parity). You can find it here.

All sources available on git.

You can easily adapt it for your device. Hope you like it.

Arnab Nandy
  • 6,472
  • 5
  • 44
  • 50