长颈鹿的脖子怎么画:posix 操作系统串口编程指南(译Serial Programming Guide fo...

来源:百度文库 编辑:中财网 时间:2024/05/08 02:58:56
posix 操作系统串口编程指南(译Serial Programming Guide for POSIX Operating Systems)
2009年04月13日 星期一 01:59
本文是学习linux串口编程必读文章,网上的linux串口程序大多参照此文写成。 原文地址是:http://digilander.libero.it/robang/rubrica/serial.htm 有兴趣的可以看看原文。
posix 操作系统串口指引(Serial Programming Guide for POSIX Operating Systems) 5th Edition Michael R. Sweet Copyright 1994-1999, All Rights Reserved.
简介
posix 操作系统串口编程指南将教你如何在您的UNIX ?工作站或PC机成功,高效, 便携的编写串口程序。每章提供的编程示例使用的POSIX (可移植的UNIX标准)终端控制功能,并只要经过极少数的修改就可以运行在IRIX ? , HP - UX, SunOS ? ,以及Solaris ? ,Digital UNIX ? , Linux? ,和大多数其他的类UNIX操作系统。操作系统的最大的不同是用于串口设备和锁定的文件的文件名。
这个指南有下面几章和附录组成
第一章 串口编程基础
第二章 配置串口
第三章 调制解调器通信
第四章 高级串口编程
附录A RS-232引脚
附录B ASCII控制流
----------------------------------------------------------------------
第一章 串口编程基础
本章介绍串口通信,RS232和用语大多数计算机的其他标准以及如何用c程序访问串行口。
什么是串口通信?
计算机可以每次传送1bit或者nbit信息(数据),串口指每次只传送1bit数据。串口通信包括多种网络设备:键盘,鼠标,猫,和终端。
当用串口传送一个字(即字符或字节)数据,你接收和发送时每次传送一个bit.每个bit是1或者0.
串口的数据传输速率大都是以bit/s(bps)或者波特率(baud)表示1和0在每秒内传输的个数。回到计算机开始的时代,300baud被认为是很快的,但现今电脑应用RS232速率可以达到43800baud(波特)。当波特率超过1000,你将经常看见速率为千boud,或者kbps(例如9.6k,19.2k等).当速度在1,000,000以上表示为megabaud或者Mbps(例如1.5Mbps)。
当谈到串口设备和端口,他们被称为数据通信设备(DCE)或者数据终端设备(DTE)。他们之间的区别很简单-每对传输信号和接收,是交换。当两个DTE或者两个DCE设备连接在一起,一个无猫串口电缆或者适配器被用作交换信号对。
什么是 RS-232?
RS-232是电气工业组织(EIA)制定的为串口通信的电气化接口标准。RS-232实际上分三个等级(A,B,C),他们每个有不同的高低电压标准。填充应用最广泛的是RS-232C,他定义逻辑1的电压在-3V到-12V,逻辑0的电压在+3V到+12V。RS-232规格上讲这些信号在25英尺后就无法使用。你通常可以通过降低波特率将数据传得更远。
除了电缆进出数据,还有其他时间,状态,握手:
表1 - RS-232 引脚 管脚 描述
1 地
2 TXD-传输数据
3 RXD-接收数据
4 RTS-要求发送
5 CTS-清除待发
6 DSR-数据设置完成
7 GND-逻辑地
8 DCD-数据载波检测
9 预留
10 预留
11 未分配
12 备用DCD
13 备用CTS
14 备用 TXD
15 传输时钟
16 备用 RXD
17 接收时钟
18 未分配
19 备用 RTS
20 DTR-数据终端准备
21 信号质量检查
22 环路检查
23 数据速率选择
24 发送时钟
25 未分配
有两种接口标准RS-422和RS-422。RS-422用更低的电压和不同信号允许线缆长度上限为1000英尺(300米)。RS-574定义9引脚的串口连接和电压。
信号定义
RS-232标准定义串口中18中不同的信号。在这些中仅有6个是在UNIX环境中可用。
GND - 逻辑地
逻辑地不是信号,办事没有它却没有办法操作信号。基本上,逻辑地是一个参考电压已让电子管知道那些事正的和负的。
TXD- 传输数据
TXD信号载波数据从你的工作站到另一端的计算机或者设备(如猫)。一个高电平表示1,一个空电平表示0.
RXD- 接收数据
RXD载波信号将电脑或设备传输到工作站。像TXD,高低电平分别表示1和0.
DCD-数据载波检测
DCD信号接收来自电脑或者其他串口电缆。一个低电平在信号线上表示设备和电脑正在连接。DCD通常没有用。
DTR- 数据终端准备
DTR信号是工作站产生兵告诉计算机或者另一端的设备你正在准备(空电平)或者没有准备(高电平)。DTR能够自动运行当你打开在工作站上的串口。
CTS- 清除发送
CTS信号从其他串口电缆接收数据。
空电平表示已经准备从工作站发送串口数据。CTS通常用来控制从工作站到终端的串行数据流。
RTS- 请求发送
TRS信号设置低电平被工作站用来表示更多的数据准备发送。
像CTS,RTS辅助控制从工作站到计算机和另一端的串口设备的数据流。大部分工作站始终设置这种信号为低电平。
异步通信
对于计算机要知道什么时候串行数据传送进来,他需要一些方法决定从那个字符开始从哪里结束和下一个起始点。本指南专门处理异步串行数据。
在异步模式下串口数据线保持高电平直到出现一个字符被传送。一个开始位之前每个字符和后边紧跟的位的字符性质,一个奇偶校验位,和一个或者多个停止位。开始位通常是一个空电平然后告诉电脑新的串行数据有用。数据然后被发送或者任何时间接收,因此称为异步。
Figure 1 - Asynchronous Data Transmission

奇偶校验位是这个数据位的和表示这些数据包含一个偶数或奇数个1。偶校验,奇偶校验位是0如果在字符里1的数目里是偶数0;奇校验,奇偶校验位是0如果数据中1的数目是1.你也许听过低电平校验,高电平校验,无奇偶校验。低校验是指奇偶校验位是全0,高校验指得是这些位全为1。无校验是指无校验位表示或者传输。 剩余的位被称为停止位。
在连个字符间可以为1,1.5,或者2停止bits并且他们的值为1.停止位以前被用来给计算机时间处理前边的字符,但是现在只是为异步解释计算机接收字符服务。
异步数据格式通常表示为"8N1", "7E1",和往前。这些分别代表“8位数据位,无奇偶校验,1bit停止位”和“7位数据位,偶校验,1bit停止位”。
什么是全双工和半单工?
全双工是指计算机能够同时接收和传输数据-有两个单独的数据信道(一进一出)。
半双工是指计算机不能同时接收和传输数据。通常这意味着只有单一的信道通信。但是并不是所有的RS232信号都不使用。然而,他通常指得是通信链接用其他标准而不是RS232,RS232并不支持全双工操作。
流控
当在俩个串口间传输数据控制数据流是必须的。这是因为限制中间串口通信连接,一个是串口,或者另外一些是存储媒体。两种方法是常用的异步通信数据。
第一个方法是通常被称为“软件”流控和应用专用字符开始(XON or DC1, 021 octal)或者停止(XOFF or DC3, 023 octal)数据流。这些字符定义在美国交换信息标准编码(ASCII).虽然这些代码传输文本信息很有用,但是他们在没有特殊程序转换的时候不能使用。
第二种方法被称为“硬件”控制流,应用RS-232CTS和RTS信号代替特殊字符。接受者当它准备接受更多数据的时候设置CTS为低电压,同样的,还没有准备传输别的数据的时候,传送者设置RTS为低电平。因为硬件流控用一组分离的信号,他比软流控更快,同样需要发送和接收多位信心做同样的事情。CTS/RTS流控并非被所有的硬件和操作系统支持。
什么是中断?
通常一个接收或者传输数据信号保持在高电平知道一个新的字符传输。如果信号下降到低电平很长时间,常常是1/4,1/2秒,然后可以说产生中断。
一个中断有时候用来重启一个通信线路或者改变操作系统的通信模式例如猫。第三章,调制解调器涵盖这些更加高级的应用。
同步通信
不像异步通信,同步通信表现为一个恒定的数据流。读取线上的数据,电脑必须提供或者接收一个同步时钟以至发送和接收同步。
甚至这样的同步,电脑必须用某种方法标记开始数据。最常见的做法是用一个数据协议包例如串行数据链路控制(SDLC)或者高速数据链路控制(HDLC).
每个协议定义确定的位序列来表示数据包的开始和结束。每个协议也定义一个bit序列,用在没有数据的时候;这些序列允许电脑开始查看开始的数据包。
因为同步协议没有用每个字符同步位,他们通常提供至少25%的改进比异步通信,并且更加适合远程网络和配置更多的串行口。
尽管同步通信的高速率优点,但是大多数RS-232硬件并不支持,因为还需要额外的硬件和软件。
接入串行口
像所有的设备一样,UNIX提供接入串口的设备文件。要接入串口只需有打开这些相应的设备文件。 串口文件 每个串口在UNIX系统上有一个或多个设备文件(文件在 /dev 目录)与它有关:
表 2 - 串口和设备文件
system                                     port1             prot2
IRIX?                                   /dev/ttyf1       /dev/ttyf2
HP-UX                                 /dev/tty1p0       /dev/tty2p0
Solaris?/SunOS?                /dev/ttya           /dev/ttyb
Linux?                                /dev/ttyS0          /dev/ttyS1
Digital UNIX?                        /dev/tty01           /dev/tty02
打开一个串口
因为一个串口是一个文件,所以open()函数被用来接入它。一个问题是UNIX设备文件普通用户通常不能接入。工作区包括接口的权限到文件的问题,运行你的程序作为超级用户(root),或者设置程序的用户名以让普通用户也能运行设备文件。
现在我们假设文件已经可以让所有用户使用。这个代码打开串口1工作区在IRIX操作系统上:
Listing 1 - 打开串口
#include /* 标准输入/输出定义 */
#include /* 串函数定义 */
#include /* UNIX 标准函数定义*/
#include /* 文件控制定义 */
#include /* 错误数定义 */
#include /* POSIX 终端控制定义 */
/* * 'open_port()' - 打开串口 1. * * 返回文件描述成功或者 -1表示错误。 */
int open_port(void) {
int fd;   /* 文件描述端口定义*/
fd = open("/dev/ttyf1", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1){
/* * 不能打开端口。 */
perror("open_port: Unable to open /dev/ttyf1 - ");
}
else
fcntl(fd, F_SETFL, 0);
return (fd);
}
其他的操作系统将需要相应的设备文件名,但是其他的代码是一样的。
开放操作
你将发现当你打开设备文件,我们通常使用两个标记除了read+write模式:
fd = open("/dev/ttyf1", O_RDWR | O_NOCTTY | O_NDELAY);
O_NOCTTY标志告诉UNIX这个程序不想成为“控制终端”控制的端口,如果你不能明确说明这个,那么任何输入(像键盘中断信号等)将会影响你的程序。程序例如getty(1M/8)应用这个特征当开始登陆程序,但是通常一个用户程序不希望这个问题。
O_NDELAY标志告诉UNIX这个程序不关心DCD信号线状态-是否其他的端口是否运行。如果不指定这个标号,你的程序将会在DCD信号线是低电平时停止。
写数据到端口
写入数据到端口很简单-只要应用write() 系统回叫发送数据给它:
n = write(fd, "ATZ\r", 4);
if (n < 0)
fputs("write() of 4 bytes failed!\n", stderr);
write函数返回位的数值或者当错误时为-1。通常这个错误只有当一个调制解调器或者数据连接丢数据载波检测行时,你将运行到EIO 。这个情况将会维持直到你释放端口。
读端口数据
读端口数据有点麻烦。当你操作这个端口在原始数据模式,每个read()系统回叫将返回串口输入缓存实际提供的无论多少字符。如果没有字符可用,回叫将会阻塞(等待)直到字符进入,一个间隔计时器失效,或者错误发送。read函数将会立即返回通过下面这样做:
fcntl(fd, F_SETFL, FNDELAY);
FNDELAY选项使read函数返回0,当没有字符在端口上。存储正常(阻塞)动作,没有FNDELAY选项则回叫fcntl():
fcntl(fd, F_SETFL, 0);
这个同样应用在操作 O_NDELAY选项打开串口。
关闭串口
关闭串口,使用close系统调用:
close(fd);
关闭一个串口将会也设置DTR信号低电平,这会把调制解调挂起。
---------------------------------------------------------------------------------------第一章完(待续)
第二章 配置串口
这章讨论如何配置串口从C用POSIX终端接口。
POSIX终端接口
大多数系统支持POSIX终端(串)接口改变参数例如波特率,字符大小,等。首先你需要做的是包含头文件 ; 这个文件定义终端控制结构和POSIX控制函数。
最重要的POSIX函数是tcgetattr(3) 和 tcsetattr(3),这两个分别表示获取和设置终端属性。你提供一个指针到包含可用的所有串口选项控制台结构:
表3-控制台结构成员
Member       Description
c_cflag        控制选项
c_lflag          现行选项
c_iflag          输入选项
c_oflag        输出选项
c_cc            控制字符
c_ispeed      输入波特率 (new interface)
c_ospeed 输出波特率(new interface)
控制选项
c_cflag成员控制波特率,一些数据位,奇偶校验,停止位和硬件控制流。有常数为所有的支持配置。
Table 4 - c_cflag 成员常数描述
常数         描述
CBAUD Bit mask for baud rate
B0            0 baud (drop DTR)
B50          50 baud
B75         75 baud
B110       110 baud
B134       134.5 baud
B150        150 baud
B200        200 baud
B300       300 baud
B600        600 baud
B1200    1200 baud
B1800     1800 baud
B2400     2400 baud
B4800     4800 baud
B9600      9600 baud
B19200 19200 baud
B38400     38400 baud
B57600    57,600 baud
B76800    76,800 baud
B115200 115,200 baud
EXTA        External rate clock
EXTB         External rate clock
CSIZE        Bit mask for data bits
CS5           5       data bits
CS6            6 data bits
CS7           7 data bits
CS8             8 data bits
CSTOPB     2 stop bits (1 otherwise)
CREAD         Enable receiver
PARENB       Enable parity bit
PARODD    Use odd parity instead of even
HUPCL        Hangup (drop DTR) on last close
CLOCAL      Local line - do not change "owner" of port
LOBLK        Block job control output
CNEW_ RTSCTS
CRTSCTS       Enable hardware flow control (not supported on all platforms)
c_cflag成员包含两个选项应该常常应用,CLOCAL和CREAD.这将确保你的程序不被其他端口控制和挂起信号干扰,同时串口接口驱动将读取进入的数据。
波特率常量 (CBAUD, B9600, etc.) 是用来为更老的接口,他们没有c_ispeed和c_ospeed 成员。参考下一节关于POSIX函数信息来设置波特率。
没有直接初始化 c_cflag (或者其他flag)成员;你应该常常使用逐位与,或,非操作来设置或者清空成员位。不同的操作系统版本(和甚至补丁)应用各不相同,因此用位操作将会防止你使用错误的位标志,这些位标志需要更新一个串口驱动。
设置波特率
不同的操作系统波特率保存在不同的地方。老的接口依靠表4中的波特率常数存储波特率c_cflag成员,然而更新的应用提供c_ispeed和c_ospeed 成员,他们包含实际的波特率数值。
cfsetospeed(3) 和cfsetispeed(3)函数不论在何种操作系统接口提供设置到控制台的波特率。典型的你最好应用下边的代码设置波特率:Listing 2 - 设置波特率
struct termios options;
/*
* 得到现在端口的选项。。。。。。
*/
tcgetattr(fd, &options);
/*
*设置波特率为19200。。。。
*/
cfsetispeed(&options, B19200);
cfsetospeed(&options, B19200);
/*
* 启动接收者和设置本地模式。。。。。。
*/
options.c_cflag |= (CLOCAL | CREAD);
/*
* 设置新的端口选项。。。。。。。
*/
tcsetattr(fd, TCSANOW, &options);
tcgetattr(3) 函数装满你提供的串口配置到控制台。然后我们设置波特率和让本地模式运行和串口数据接收,我们用tcsetattr(3).选择新的配置。TCSANOW 常量规定所以的改变应该立即执行而不等待数据传送和接收结束。有其他的常量来等待输入和输出完成或者刷新输入输出缓存。
大多数系统不支持不同的输入输出速率,所以要保证设置相同的值保证最大的可移植性。
Table 5 - Constants for tcsetattr
Constant                    Description
TCSANOW               Make changes now without waiting for data to complete
TCSADRAIN              Wait until everything has been transmitted
TCSAFLUSH             Flush input and output buffers and make the change
设置字符大小
不像波特率,没有方便的函数来设置字符大小。你必须做一个小的位掩码设置。字符的尺寸在位中指定:
options.c_cflag &= ~CSIZE;     /* 字符大小的位掩码*/
options.c_cflag |= CS8;             /* 选中8位数据位*/
设置奇偶校验
Like the character size you must manually set the parity enable and parity type bits. UNIX serial drivers support even, odd, and no parity bit generation. Space parity can be simulated with clever coding.
像字节大小一样你必须手动设置奇偶校验使能和校验类型。UNIX串口驱动支持奇数,偶数,和无校验位系列。空校验能用代码仿真。
无校验(8N1):
options.c_cflag &= ~PARENB
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
奇校验(7E1):
options.c_cflag |= PARENB
options.c_cflag &= ~PARODD
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7;
偶校验(7O1):
options.c_cflag |= PARENB
options.c_cflag |= PARODD
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7;
空校验设置和无校验相同(7S1):
options.c_cflag &= ~PARENB
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
设置硬件流控
某些版本的UNIX支持使用CTS和RTS信号线的硬件流控。如果 CNEW_RTSCTS or CRTSCTS 常数被系统定义然后然后硬件流控才能被支持。按照下边能让硬件流控:
options.c_cflag |= CNEW_RTSCTS;    /* Also called CRTSCTS */
同样, 停止硬件流控l:
options.c_cflag &= ~CNEW_RTSCTS;
本地选项
本地选项成员c——cflag控制串口驱动管理如何输入字符。通常你能配置c_lflag 成员对标准或者原始输入。