加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

Linux下SerialPort编程详解

(2015-12-03 10:10:16)
标签:

serialport

分类: linux
作者: Sam (甄峰) sam_code@hotmail.com

0. 基础知识:
0.1: Serial Communication(串行通讯):
串行通讯:每次只能传输1bit数据。keyboard,mouse,MODEMs和终端(Terminals)都是串行设备。
使用串行通讯发送或接收1byte数据时,每次只能传输1bit数据(0或1)。
串行传输的地速度通常用 bits-per-second(bps)或者baudot rate(baud)表示。含义是:每秒传输的bit(0或1)的数量。

0.2:RS-232:
RS-232是电气工业组织(EIA)制定的为串口通信的电气化接口标准,
RS-232实际上分三个等级(A,B,C),他们每个有不同的高低电压标准。填充应用最广泛的是RS-232C,他定义逻辑1的电压在-3V到-12V,逻辑0的电压在+3V到+12V。RS-232规格上讲这些信号在25英尺后就无法使用。你通常可以通过降低波特率将数据传得更远。

0.2.1:RS-232信号定义

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辅助控制从工作站到计算机和另一端的串口设备的数据流。大部分工作站始终设置这种信号为低电平。


0.3: 异步传输(Asynchronous Communications)

串行数据是1bit为单位发送的,那如何解析这些数据,哪里是某个character的头和尾。下面来看异步传输中如何解决这个问题。

在异步传输模式,在没有数据传输时,保持低电平。有数据传输时,会先产生一个高电平作为起始位(start bit).后面跟数据bit. 然后是可选的校验位(Parity bit). 最后是一个或多个停止位(Stop bits)

http://s3/mw690/001Ld2gwgy6XtTcB45Qf2&690

MARK=1   SPACE=0。


起始位:Start  Bit:一个高电平表示将要传输数据。

校验位,Parity Bit :单纯的相加,看合是奇数还是偶数。通常,校验位有以下几种选择:

A:Even Parity.偶校验。(0:表示数据位中有偶数个1)

B:Odd Parity. 奇校验。(0: 表示数据位中有奇数个1)

C:Space Parity:校验位总是0.

D: Mark Parity: 校验位总为1.

E: No Parity: 无校验位。


停止位,Stop Bits:在连个字符间可以为1,1.5,或者2停止bits并且他们的值为1(MARK).



0.4: 全双工,半双工:

全双工是指计算机能够同时接收和传输数据-有两个单独的数据信道(一进一出)。

半双工是指计算机不能同时接收和传输数据。通常这意味着只有单一的信道通信。RS232并不支持全双工。


0.5:同步传输:

同步通信表现为一个恒定的数据流。读取线上的数据,电脑必须提供或者接收一个同步时钟以至发送和接收同步。尽管同步通信的高速率优点,但是大多数RS-232硬件并不支持,因为还需要额外的硬件和软件。


0.6: Flow Control:

Flow Control(数据流控制)作用,数据在两个串口之间传输时,常常会出现丢失数据的现象,或者两台计算机的处理速度不同,如台式机与单片机之间的通讯,接收端数据缓冲区已满,则此时继续发送来的数据就会丢失。当接收端数据处理不过来时,就发出“不再接收”的信号,发送端就停止发送,直到收到“可以继续发送”的信号再发送数据。因此流控制可以控制数据传输的进程,防止数据的丢失。  PC机中常用的两种流控制是硬件流控制(包括RTS/CTS、DTR/CTS等)和软件流控制XON/XOFF(继续/停止)

A:硬件流控制
    硬件流控制常用的有RTS/CTS流控制和DTR/DSR(数据终端就绪/数据设置就绪)流控制。
    硬件流控制必须将相应的电缆线连上,用RTS/CTS(请求发送/清除发送)流控制时,应将通讯两端的RTS、CTS线对应相连,数据终端设备(如计算机)使用RTS来起始调制解调器或其它数据通讯设备的数据流,而数据通讯设备(如调制解调器)则用CTS来起动和暂停来自计算机的数据流。这种硬件握手方式的过程为:在编程时根据接收端缓冲区大小设置一个高位标志(可为缓冲区大小的75%)和一个低位标志(可为缓冲区大小的25%),当缓冲区内数据量达到高位时,在接收端将CTS线置低电平(送逻辑0),当发送端的程序检测到CTS为低后,就停止发送数据,直到接收端缓冲区的数据量低于低位而将CTS置高电平。RTS则用来标明接收设备有没有准备好接收数据。
    常用的流控制还有还有DTR/DSR(数据终端就绪/数据设置就绪)


B:软件流控制
    由于电缆线的限制,我们在普通的控制通讯中一般不用硬件流控制,而用软件流控制。一般通过XON/XOFF来实现软件流控制。常用方法是:当接收端的输入缓冲区内数据量超过设定的高位时,就向数据发送端发出XOFF字符(十进制的19或Control-S,设备编程说明书应该有详细阐述),发送端收到XOFF字符后就立即停止发送数据;当接收端的输入缓冲区内数据量低于设定的低位时,就向数据发送端发出XON字符(十进制的17或Control-Q),发送端收到XON字符后就立即开始发送数据。一般可以从设备配套源程序中找到发送的是什么字符。
    应该注意,若传输的是二进制数据,标志字符也有可能在数据流中出现而引起误操作,这是软件流控制的缺陷,而硬件流控制不会有这个问题。


1. 设备的打开:

Linux系统下,字符设备和块设备都是文件形式存在。所以打开串口设备,也像打开文件一样。

设备名通常为: /dev/ttyS0-/dev/ttySn.   

USB转RS232则为: /dev/ttyUSB0-/dev/ttyUSBn

fd = open( port, O_RDWR|O_NOCTTY|O_NDELAY);

if (fd == -1)

{

         perror("Can't Open Serial Port");

         return -1;

 }

     //恢复串口为阻塞状态                               

     if(fcntl(fd, F_SETFL, 0) < 0)

                {

                       printf("fcntl failed!\n");

                     return -1;

                }     

         else

                {

                  printf("fcntl=%d\n",fcntl(fd, F_SETFL,0));

 

                }

这个打开文件的方式很奇特,除了常用的O_RDWR, 还使用了 O_NOCTTY|O_NDELAY

O_NDELAY的通常使用方式是:将文件以非阻塞模式打开,即在检测不到数据时立刻返回。但后面却立刻变回到阻塞模式。什么理由呢?

O_NDELAY实际上是告知系统,程序不在乎DCD(数据载波检测),即端口的另一端是否插入设备并up并不关心。如果不设置,程序会sleep直到DCD信号变为SPACE(0)

O_NOCTTY: 告知系统,程序不希望成为"controlling terminal".  如果不设置,会有其它设备影响程序。


2. 设备的配置:

串口通讯中,隐藏最多细节,最容易出错的地方就是设备的配置。现在详细学习如下:

POSIX Terminal(Serial) Interface 支持用户修改参数。其中最重要的两个function是:

tcgetattr(3)和tcsetattr(3).用来得到和设置Terminal (Serial) Attriabute.

int tcgetattr(int fd, struct termios *termios_p);

int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);


其中关键是:struct termios,它至少要包含以下项目:

Member Description
c_cflag Control options
c_lflag Line options
c_iflag Input options
c_oflag Output options
c_cc Control characters
c_ispeed Input baud (new interface)
c_ospeed Output baud (new interface)


Control Modes,:

包含:baud rate, data bit个数, parity, stop bits, Hardware  Flow Control. 


Constant Description
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)

这其中,CLOCAL and CREAD是必须Enable的。这将确保程序不被其它信号干扰,同时,Driver也能将进入的书读读入。

绝对不要直接初始化和修改c_cflag等设置。二是应该使用AND,OR,NOT操作去设置和clear bit去修改。因为不同OS的成员并不相同,所以使用位操作可以避免错误。



2.1. Baud rate(波特率)的设置:

CBAUD, B9600等常量是用在老接口之上的,那时候缺少c_ispeed和c_ospeed成员。当前设置baud rate可以使用POSIX function.

int cfsetispeed(struct termios *termios_p, speed_t speed);

int cfsetospeed(struct termios *termios_p, speed_t speed);

典型的设置波特率的代码如下:


struct termios options;

// Get the current options for the port...

tcgetattr(fd, &options);


 

cfsetispeed(&options, B19200);

cfsetospeed(&options, B19200);


 


options.c_cflag |= (CLOCAL | CREAD);


 


tcsetattr(fd, TCSANOW, &options);


2.2: 设置Character Size, Parity, Stop Bit。

与设置Baud rate不同,这支数据位,校验位,停止位没有方便的函数操作,只能按位操作。

数据位:

options.c_cflag &= ~CSIZE;

options.c_cflag |= CS8;    


校验位:

无校验

options.c_cflag &= ~PARENB;


Even校验(偶校验)、

options.c_cflag &= ~PARENB;

options.c_cflag &= ~PARODD;


ODD校验:

options.c_cflag &= ~PARENB;

options.c_cflag &= PARODD;


停止位:

1停止位:

options.c_cflag &= ~CSTOPB;

2停止位:

options.c_cflag &= CSTOPB;


2.3: 数据流控制:

硬件数据流:

某些版本的UNIX支持使用CTS和RTS信号线的硬件流控。

options.c_cflag |= CNEW_RTSCTS;   

停止硬件流控l: 
options.c_cflag &= ~CNEW_RTSCTS;


软件数据流控制:

Enable XON,XOFF:

options.c_iflag |= (IXON | IXOFF | IXANY);


Disable:

options.c_iflag &= ~(IXON | IXOFF | IXANY);


2.4: Local Options:

Local modes c_lflag: 用来设置串口驱动管理如何输入字符。


2.5:Input Options:

设置如何处理从端口中读出的数据。

这里有几个项目要注意:INLCR, 和0x0D, 0x0A有关。




3. Read, Wirte。




4. 异常查找:

4.1: 在用write发送数据时没有键入回车,信息就发送不出去,这主要是因为我们在输入输出时是按照规范模式接收到回车或换行才发送,而更多情况下我们是不必键入回车或换行的。此时应转换到行方式输入,不经处理直接发送,设置如下:
Opt.c_lflag &= ~ (ICANON | ECHO | ECHOE | ISIG);


4.2:发送字符0X0d的时候,往往接收端得到的字符是0X0a,原因是因为在串口设置中c_iflag和c_oflag中存在从NL-CR和CR-NL的映射,即串口能把回车和换行当成同一个字符

// Disable 0x0D<-->0x0A

options.c_iflag &= ~(INLCR | ICRNL | IGNCR);

options.c_oflag &= ~(ONLCR | OCRNL);




资源文档:

http://digilander.libero.it/robang/rubrica/serial.htm#CONTENTS








0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有