开发者

Is it possible to use 9-bit serial communication in Linux?

RS-232 communication sometime开发者_C百科s 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?


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


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


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


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.

Is it possible to use 9-bit serial communication in Linux?


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.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜