Linux串口通讯(mark, space校验方式的实现)
时间:2005-04-29 来源:fishjustgoon
前一阵子因为工作需要摸索的一些linux下得串口通信,总结下结果, 有点乱。。。主要针对linux串口校验方式mark, space的摸索。。。
参考文档:
文档一:Serial Programming Guide for POSIX Operating Systems
文档二:Serial Programming Howto;
文档三:Serial Howto;
说明:由于当前Linux串口通讯方面的文档比较少,网上相关文档大都以这三篇为参考,第一个文档对串口设置和异步通讯介绍的都比较全面,从中能了解串口设置的全面知识,及一些细节性问题;第二个文档有一个非阻塞接收端的例子,写得很好;第三个文档主要从硬件方面介绍串口。
串口通讯程序可能用到的:
1. 打开串口
fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
O_RDWR 读写方式打开
O_NOCTTY 不允许进程管理串口
O_NDELAY 非阻塞
2.写串口
n = write(fd, "ATZ ", 4);
n实际写入个数;
3.设置串口为非阻塞方式
fcntl(fd, F_SETFL, FNDELAY);
4.设置串口为阻塞方式:
fcntl(fd, F_SETFL, 0);
5.读串口:
res = read(fd,buf,len);
6.关闭串口
Close(fd);
串口设置部分:
波特率:
Tcgetat tr(fd, &options);
cfsetispeed(&options, B19200);
cfsetospeed(&options, B19200);
options.c_cflag |= (CLOCAL | CREAD);
tcsetattr(fd, TCSANOW, &options);
校验位等:
No parity (8N1):
options.c_cflag &= ~PARENB
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
Even parity (7E1):
options.c_cflag |= PARENB
options.c_cflag &= ~PARODD
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7;
Odd parity (7O1):
options.c_cflag |= PARENB
options.c_cflag |= PARODD
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7;
Space parity is setup the same as no parity (7S1):
options.c_cflag &= ~PARENB
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
硬件流控制:
options.c_cflag |= CNEW_RTSCTS;
options.c_cflag &= ~CNEW_RTSCTS;
原始通讯方式
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
输入控制:
options.c_iflag |= (INPCK | ISTRIP);
输出控制:
options.c_oflag |= OPOST;
通讯相关:
每次传送一个字节,顺序:起始电平—〉数据位—〉校验位—〉结束电平
起始电平1个Bit;
数据位一般7或8个bit
校验位1 个或者无
结束电平1,2个bit ;
通讯校验方式总结:
1. even 每个字节传送整个过程中bit为1的个数是偶数个(校验位调整个数)
2. odd 每个字节穿送整个过程中bit为1的个数是奇数个(校验位调整个数)
3. noparity没有校验位
4. space 校验位总为0
5. mark 校验位总为1;
经过上网再找相应文档,找到mark/space校验方式的具体实现,采用伪装的方式来实现;实现代码:
Mark parity is simulated by using 2 stop bits (7M1):
options.c_cflag &= ~PARENB;
options.c_cflag |= CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7;
Space parity is setup the same as no parity (7S1):
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
来自:Linux Serial Port Programming Mini-Howto上的介绍
此方法利用停止位的值总是为1,这一点,来达到mark校验的校验位总为1的要求;这个办法在传ASCII码的时候没有问题;
用这个方法单桢下发,仍然接收不到数据,找原因,原来是装置的数据位是8位然后外加校验位;就是说实际是8M1;而不是要上面的7M1;类似猜测地改动8M1的实现方法如下:
Mark parity is simulated by using 2 stop bits (8M1):
options.c_cflag &= ~PARENB;
options.c_cflag |= CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
需要8S1下发,而当前掌握的校验方式不能实现,只能到7S1,在找相应文档:
Using Linux in the real world
A talk for the Linux Users of Victoria, May 1997
这篇文档,要实现的方式与当前本公司系统要实现的系统功能相同;有一节介绍到:
MARK and SPACE parity settings, however the POSIX standard has no definition for MARK or SPACE, and there is no support for this in the standard Linux serial driver.
Fortunately, we have access to the source and only a couple of lines of code to the Linux serial driver (the diffs are below) are required to enable MARK and SPACE parity on a 80x86 system, allowing us to easily toggle MARK and SPACE.
linux/drivers/char/serial.c
1237,1248d1236
<
< /*
< * This provides support for mark & space parity
< * If it is set and PARODD is not set, then we have MARK parity
< * If it is set and PARODD is set, then we have SPACE parity
< *
< * NB: This is not supported by POSIX at all
< */
< if (cflag & CMSPAR) {
< cval |= UART_LCR_SPAR;
< }
<
linux/include/asm-i386/termbits.h
125d124
< #define CMSPAR 010000000000 /* this adds support for mark/space parity */
他给出了linux下实现mark, space校验的方法;但是需要重新编译linux下的串口驱动程序;考虑到这篇文档是1997年写的,看了下当前linux下的这两个文件已经添上相应代码;所以问题可以解决;只是不知道实现方法,最终以CMSPAR为关键字google终于找到相应介绍两份:
第一份:来自一个论坛,讨论了重编译内核的可行性;并且作者已经实验成功过,备用;
第二份:来自于Using Linux in the real world
的作者:里面说明了本方法的可行性,只要在自己的应用程序中添上#define CMSPAR 010000000000再通过相应的设置就可使用Mark,space校验了;原文如下:
Re: Linux serial sticky parity (was Linux Serial Question)
Theodore Y. Ts'o
Thu, 2 Mar 2000 18:41:07 -0800
Date: Thu, 2 Mar 2000 15:47:12 -0800
From: <[EMAIL PROTECTED]>
I don't do any serial programming but it looks like there is a problem
supporting mark parity and space parity. James M. wants to use space
parity. Stty doesn't support it but the termios stucture in the
serial driver might. Such parity is specified by a c_cflag named
CMSPAR in <asm/termbits.h>. The equvalent is in <linux/serial-reg.h>
as UART_LCR_SPAR 0x20. This is called "sticky parity". It looks like
someone put a ? in the comment in the serial-reg.h file shown above and
called it "stick" instead of "sticky". If this "sticky" bit is set,
then the parity bit is always odd (1) or even (0) depending on how the
parity flag-bit has been set.
No, it's called "stick" parity, not "sticky". Check the 8250 / 16550
UART documentation.
The reason why CMSPAR isn't in bits/termios.h is because it's not POSIX,
and so it was dropped during the glibc conversion by the glibc folks.
Traditionally, the way Unix handled mark and space parity was to set or
clear the eighth bit, and simply used 8 bits no parity as far as termios
was concerned. Actually, mark and space parity was very rare to begin
with, so it rarely came up.
The problem comes when you want to use 8 bits and also force mark or
space parity. There is no way to do that under POSIX termios. So, back
in 1977 I added CMSPAR for the one squeeky wheel that was complaining
that we didn't have the support. Later, we converted to glibc, and the
glibc folks didn't add the bit. It's indicative that no one has
complained that this feature was missing until now.
It would seem that the high-level way to go would be to set the sticky
parity bit in the termios structure, but will this do the job? To
change the termios structure one may use tcsetattr() and tcgetattr()
as shown in Vern's HOWTO. It seems that the flags normally used are
in <bits/termios.h> and CMSPAR is missing from this. <termios.h>
#includes this but <asm/termios.h> #includes the one you want that
contains the CMSPAR flags. Will this flag work OK if one changes the
#includes in their program to the </asm> directory? Why isn't it in
<bits/termios.h>?
So yes, if you set the CMSPAR bit in the termios, it will do the right
thing. #including asm/termios.h is a bad idea, though. In the new
header file religion, it's always a bad thing to include anything in the
asm/ directory. You should complain to the glibc folks and ask them to
include CMSPAR in bits/termsio.h.
In the meantime, a program who needs this bit badly can simply manually
define CMSPAR, and they should be warned that this is a Linux specific
feature that won't work on any other OS.
参考了一下串口的硬件驱动程序:
Stick even parity parity enable parity
- - 0 无校验
0 0 1 奇校验
0 1 1 偶校验
1 0 1 Mark
1 1 1 space
得到如下的校验设置方式:
#define CMSPAR 010000000000
本句使能了stick parity的校验可行性
Mark校验
options.c_cflag |= PARENB | CS8 | CMSPAR |PARODD;
Space校验
options.c_cflag |= PARENB | CS8 | CMSPAR;
实验结果可行;