1. 串口简介
串行口是计算机一种常用的接口,具有连接线少,通讯简单,得到广泛的使用.常用的串口是 RS-232-C 接口(又称 EIA RS-232-C)它是在 1970 年由美国电子工业协会(EIA)联合贝尔系统、 调制解调器厂家及计算机终端生产厂家共同制定的用于串行通讯的标准.它的全名是"数据终端设备(DTE)和数据通讯设备(DCE)之间串行二进制数据交换接口技术标准"该标准规定采用一个 25 个脚的 DB25 连接器,对连接器的每个引脚的信号内容加以规定,还对各种信号的电平加以规定.传输距离在码元畸变小于 4% 的情况下,传输电缆长度应为 50 英尺.
Linux 操作系统从一开始就对串行口提供了很好的支持,本文就 Linux 下的串行口通讯编程进行简单的介绍,如果要非常深入了解,建议看看本文所参考的 《Serial Programming Guide for POSIX Operating Systems》
计算机串口的引脚说明
序号 | 信号名称 | 符号 | 流向 | 功能 |
2 | 发送数据 | TXD | DTE→DCE | DTE发送串行数据 |
3 | 接收数据 | RXD | DTE←DCE | DTE 接收串行数据 |
4 | 请求发送 | RTS | DTE→DCE | DTE 请求 DCE 将线路切换到发送方式 |
5 | 允许发送 | CTS | DTE←DCE | DCE 告诉 DTE 线路已接通可以发送数据 |
6 | 数据设备准备好 | DSR | DTE←DCE | DCE 准备好 |
7 | 信号地 | 信号公共地 | ||
8 | 载波检测 | DCD | DTE←DCE | 表示 DCE 接收到远程载波 |
20 | 数据终端准备好 | DTR | DTE→DCE | DTE 准备好 |
22 | 振铃指示 | RI | DTE←DCE | 表示 DCE 与线路接通,出现振铃 |
2. 串口操作
串口操作需要的头文件
1 #include <stdio.h> /*标准输入输出定义*/
2 #include <stdlib.h> /*标准函数库定义*/
3 #include <unistd.h> /*Unix 标准函数定义*/
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <fcntl.h> /*文件控制定义*/
7 #include <termios.h> /*PPSIX 终端控制定义*/
8 #include <errno.h> /*错误号定义*/
3. 打开串口
Linux系统上一般有一个或者多个串口,而这些串口设备文件名字比较奇怪,如比下面这样
操作系统 | 串口1 | 串口2 | USB/RS-232转换器 |
Windows | COM1 | COM2 | - |
Linux | /dev/ttyS0 | /dev/ttyS1 | /dev/ttyUSB0 |
因为串口和其他设备一样,在类Unix系统中都是以设备文件的形式存在的,所以,理所当然得你可以使用open(2)系统调用/函数来访问它.但Linux系统中却有一个稍微不方便的地方,那就是普通用户一般不能直接访问设备文件.你可以选择以下方式做一些调整,以便你编写的程序可以访问串口.
- 改变设备文件的访问权限设置 [#cd9bd1e0]
- 以root超级用户的身份运行程序 [#kdd0e577]
- 将你的程序编写位setuid程序,以串口设备所有者的身份运行程序 [#s7b703ff]
OK.假如你已经准备好了让串口设备文件可以被所有用户访问,你可以在Linux系统中实验一下下面这个程序,它可以打开计算机的串口1.
1 int fd;
2 /*以读写方式打开串口*/
3 fd = open( "/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
4 if (-1 == fd){
5 /* 不能打开串口一*/
6 perror(" 提示错误!");
7 }
打开串口连接的时候,程序在open函数中除了Read+Write模式以外还指定了两个选项;
标志O_NOCTTY可以告诉UNIX这个程序不会成为这个端口上的“控制终端”.如果不这样做的话,所有的输入,比如键盘上过来的Ctrl+C中止信号等等,会影响到你的进程.而有些程序比如getty(1M/8)则会在打开登录进程的时候使用这个特性,但是通常情况下,用户程序不会使用这个行为.
O_NDELAY标志则是告诉UNIX,这个程序并不关心DCD信号线的状态——也就是不关心端口另一端是否已经连接.如果不指定这个标志的话,除非DCD信号线上有space电压否则这个程序会一直睡眠.
4. 设置串口
最基本的设置串口包括波特率设置,效验位和停止位设置.
很多系统都支持POSIX终端(串口)接口.程序可以利用这个接口来改变终端的参数,比如,波特率,字符大小等等.要使用这个端口的话,你必须将<termios.h>头文件包含到你的程序中.这个头文件中定义了终端控制结构体和POSIX控制函数.
与串口操作相关的最重要的两个POSIX函数可能就是tcgetattr(3)和tcsetattr(3).顾名思义,这两个函数分别用来取得设设置终端的属性.调用这两个函数的时候,你需要提供一个包含着所有串口选项的termios结构体,串口的设置主要是设置struct termios结构体的各成员值.
成员 | 描述 |
c_cflag | 控制选项 |
c_lflag | 行选项 |
c_iflag | 输入选项 |
c_oflag | 输出选项 |
c_cc | 控制字符 |
c_ispeed | 输入波特率(NEW) |
c_ospeed | 输出波特率(NEW) |
通过termio结构体的c_cflag成员可以控制波特率,数据的比特数,parity,停止位和硬件流控制,下面这张表列出了所有可以使用的常数
常量 | 描述 |
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) |
在传统的POSIX编程中,当连接一个本地的(不通过调制解调器)或者远程的终端(通过调制解调器)时,这里有两个选项应当一直打开,一个是CLOCAL,另一个是CREAD.这两个选项可以保证你的程序不会变成端口的所有者,而端口所有者必须去处理发散性作业控制和挂断信号,同时还保证了串行接口驱动会读取过来的数据字节.
波特率常数(CBAUD,B9600等等)通常指用到那些不支持c_ispeed和c_ospeed成员的旧的接口上.后面文章将会提到如何使用其他POSIX函数来设置波特率.
千万不要直接用使用数字来初始化c_cflag(当然还有其他标志),最好的方法是使用位运算的与或非组合来设置或者清除这个标志.不同的操作系统版本会使用不同的位模式,使用常数定义和位运算组合来避免重复工作从而提高程序的可移植性.
波特率设置
不同的操作系统会将波特率存储在不同的位置.旧的编程接口将波特率存储在上表所示的c_cflag成员中,而新的接口实装则提供了c_ispeed和c_ospeed成员来保存实际波特率的值.
程序中可是使用cfsetospeed(3)和cfsetispeed(3)函数在termios结构体中设置波特率而不用去管底层操作系统接口.下面的代码是个非常典型的设置波特率的例子.
1 struct termios options;
2
3 /*
4 * Get the current options for the port...
5 */
6 tcgetattr(fd, &options);
7 /*
8 * Set the baud rates to 19200...
9 */
10 cfsetispeed(&options, B19200);
11 cfsetospeed(&options, B19200);
12
13 /*
14 * Enable the receiver and set local mode...
15 */
16 options.c_cflag |= (CLOCAL | CREAD);
17
18 /*
19 * Set the new options for the port...
20 */
21 tcsetattr(fd, TCSANOW, &options);
函数tcgetattr(3)会将当前串口配置回填到termio结构体option中.然后,程序设置了输入输出的波特率并且将本地模式(CLOCAL)和串行数据接收(CREAD)设置为有效,接着将新的配置作为参数传递给函数tcsetattr(3).常量TCSANOW标志所有改变必须立刻生效而不用等到数据传输结束.其他另一些常数可以保证等待数据结束或者刷新输入输出之后再生效.
常量 | 描述 |
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 |
不同的系统上可能支持不同的输入输出速度,所以,通过串口连接两台机器或者设备的时候,应该将波特率设置成两者中较小的那个,即MIN(speed1, speed2).
设置字符大小
设置字符大小的时候,这里却没有像设置波特率那么方便的函数.所以,程序中需要一些位掩码运算来把事情搞定.字符大小以比特为单位指定:
1 options.c_flag &= ~CSIZE; /* Mask the character size bits */
2 options.c_flag |= CS8; /* Select 8 data bits */
设置奇偶校验和停止位
与设置字符大小的方式差不多,这里仍然需要组合一些位掩码来将奇偶校验设为有效和奇偶校验的类型.UNIX串口驱动可以生成even,odd和no parity位码.设置space奇偶校验需要耍点小手段.
- No parity (8N1)
1 options.c_cflag &= ~PARENB
2 options.c_cflag &= ~CSTOPB
3 options.c_cflag &= ~CSIZE;
4 options.c_cflag |= CS8;
- Even parity (7E1)
1 options.c_cflag |= PARENB
2 options.c_cflag &= ~PARODD
3 options.c_cflag &= ~CSTOPB
4 options.c_cflag &= ~CSIZE;
5 options.c_cflag |= CS7;
- Odd parity (7O1)
1 options.c_cflag |= PARENB
2 options.c_cflag |= PARODD
3 options.c_cflag &= ~CSTOPB
4 options.c_cflag &= ~CSIZE;
5 options.c_cflag |= CS7;
- Space parity is setup the same as no parity (7S1)
1 options.c_cflag &= ~PARENB
2 options.c_cflag &= ~CSTOPB
3 options.c_cflag &= ~CSIZE;
4 options.c_cflag |= CS8;
设置效验的函数实例:
1 /**
2 *@brief 设置串口数据位,停止位和效验位
3 *@param fd 类型 int 打开的串口文件句柄
4 *@param databits 类型 int 数据位 取值 为 7 或者8
5 *@param stopbits 类型 int 停止位 取值为 1 或者2
6 *@param parity 类型 int 效验类型 取值为N,E,O,,S
7 */
8 int set_Parity(int fd,int databits,int stopbits,int parity)
9 {
10 struct termios options;
11 if ( tcgetattr( fd,&options) != 0) {
12 perror("SetupSerial 1");
13 return(FALSE);
14 }
15 options.c_cflag &= ~CSIZE;
16 switch (databits) /*设置数据位数*/
17 {
18 case 7:
19 options.c_cflag |= CS7;
20 break;
21 case 8:
22 options.c_cflag |= CS8;
23 break;
24 default:
25 fprintf(stderr,"Unsupported data size\n"); return (FALSE);
26 }
27 switch (parity)
28 {
29 case 'n':
30 case 'N':
31 options.c_cflag &= ~PARENB; /* Clear parity enable */
32 options.c_iflag &= ~INPCK; /* Enable parity checking */
33 break;
34 case 'o':
35 case 'O':
36 options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/
37 options.c_iflag |= INPCK; /* Disnable parity checking */
38 break;
39 case 'e':
40 case 'E':
41 options.c_cflag |= PARENB; /* Enable parity */
42 options.c_cflag &= ~PARODD; /* 转换为偶效验*/
43 options.c_iflag |= INPCK; /* Disnable parity checking */
44 break;
45 case 'S':
46 case 's': /*as no parity*/
47 options.c_cflag &= ~PARENB;
48 options.c_cflag &= ~CSTOPB;break;
49 default:
50 fprintf(stderr,"Unsupported parity\n");
51 return (FALSE);
52 }
53 /* 设置停止位*/
54 switch (stopbits)
55 {
56 case 1:
57 options.c_cflag &= ~CSTOPB;
58 break;
59 case 2:
60 options.c_cflag |= CSTOPB;
61 break;
62 default:
63 fprintf(stderr,"Unsupported stop bits\n");
64 return (FALSE);
65 }
66 /* Set input parity option */
67 if (parity != 'n')
68 options.c_iflag |= INPCK;
69 tcflush(fd,TCIFLUSH);
70 options.c_cc[VTIME] = 150; /* 设置超时15 seconds*/
71 options.c_cc[VMIN] = 0; /* Update the options and do it NOW */
72 if (tcsetattr(fd,TCSANOW,&options) != 0)
73 {
74 perror("SetupSerial 3");
75 return (FALSE);
76 }
77 return (TRUE);
78 }
需要注意的是:
如果不是开发终端之类的,只是串口传输数据,而不需要串口来处理,那么使用原始模式(Raw Mode)方式来通讯,设置方式如下:
1 options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/
2 options.c_oflag &= ~OPOST; /*Output*/
设置硬件流控制
某些版本的UNIX系统支持通过CTS(Clear To Send)和RTS(Request To Send)信号线来设置硬件流控制.如果系统上定义了CNEW_RTSCTS和CRTSCTS常量,那么很可能它会支持硬件流控制.使用下面的方法将硬件流控制设置成有效:
1 options.c_cflag |= CNEW_RTSCTS; /* Also called CRTSCTS
将它设置成为无效的方法与此类似:
1 options.c_cflag &= ~CNEW_RTSCTS;
本地设置
本地模式成员变量c_lflag可以控制串口驱动怎样控制输入字符.通常,你可能需要通过c_lflag成员来设置经典输入和原始输入模式。
ISIG | Enable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals |
ICANON | Enable canonical input (else raw) |
XCASE | Map uppercase \lowercase (obsolete) |
ECHO | Enable echoing of input characters |
ECHOE | Echo erase character as BS-SP-BS |
ECHOK | Echo NL after kill character |
ECHONL | Echo NL |
NOFLSH | Disable flushing of input buffers after interrupt or quit characters |
IEXTEN | Enable extended functions |
ECHOCTL | Echo control characters as ^char and delete as ~? |
ECHOPRT | Echo erased character as character erased |
ECHOKE | BS-SP-BS entire line on line kill |
FLUSHO | Output being flushed |
PENDIN | Retype pending input at next read or input char |
TOSTOP | Send SIGTTOU for background output |
成员变量c_lflag可以使用的常量
选择经典输入
经典输入是以面向行设计的.在经典输入模式中输入字符会被放入一个缓冲之中,这样可以以与用户交互的方式编辑缓冲的内容,直到收到CR(carriage return)或者LF(line feed)字符.
选择使用经典输入模式的时候,你通常需要选择ICANON,ECHO和ECHOE选项:
1 options.c_lflag |= (ICANON | ECHO | ECHOE);
选择原始输入
原始输入根本不会被处理.输入字符只是被原封不动的接收.一般情况中,如果要使用原始输入模式,程序中需要去掉ICANON,ECHO,ECHOE和ISIG选项:
1 options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
输入选项
可以通过输入模式成员c_iflag来控制从端口上收到的字符的输入过程.与c_cflag一样,c_iflag的最终值是想要使用的所有状态的位运算OR的组合.
常量 | 描述 |
INPCK | Enable parity check |
IGNPAR | Ignore parity errors |
PARMRK | Mark parity errors |
ISTRIP | Strip parity bits |
IXON | Enable software flow control (outgoing) |
IXOFF | Enable software flow control (incoming) |
IXANY | Allow any character to start flow again |
IGNBRK | Ignore break condition |
BRKINT | Send a SIGINT when a break condition is detected |
INLCR | Map NL to CR |
IGNCR | Ignore CR |
ICRNL | Map CR to NL |
IUCLC | Map uppercase to lowercase |
IMAXBEL | Echo BEL on input line too long |
c_iflag成员可以使用的常量
设置输入奇偶校验选项
当程序在c_cflag中设置了奇偶校验成员(PARENB)的时候,程序就需要将输入奇偶校验设置成为有效.与奇偶校验相关的常量有INPCK,IGNPAR,PARMRK和ISTRIP.一般情况下,你可能需要选择INPCK和ISTRIP将奇偶校验设置为有效同时从接收字串中脱去奇偶校验位:
1 options.c_iflag |= (INPCK | ISTRIP);
IGNPAR是一个比较危险选项,即便有错误发生时,它也会告诉串口驱动直接忽略奇偶校验错误给数据放行.这个选项在测试链接的通讯质量时比较有用而通常不会被用在实际程序中.
PARMRK会导致奇偶校验错误被标志成特殊字符加入到输入流之中.如果IGNPAR选项也是有效的,那么一个NUL(八进制000)字符会被加入到发生奇偶校验错误的字符前面.否则,DEL(八进制177)和NUL字符会和出错的字符一起送出.
设置软件流控制
软件流控制可以通过IXON,IXOFF和IXANY常量设置成有效:
1 options.c_iflag |= (IXON | IXOFF | IXANY);
将其设置为无效的时候,很简单,只需要对这些位取反:
1 options.c_iflag &= ~(IXON | IXOFF | IXANY);
XON(start data)和XOFF(stop data)字符却是在c_cc数组中定义的,下面会详细描述这个数组.
输出选项
成员变量c_oflag之中包括了输出过滤选项.和输入模式相似,程序可以选择使用经过加工的或者原始的数据输出.
常量 | 描述 |
OPOST | Postprocess output (not set = raw output) |
OLCUC | Map lowercase to uppercase |
ONLCR | Map NL to CR-NL |
OCRNL | Map CR to NL |
NOCR | No CR output at column 0 |
ONLRET | NL performs CR function |
OFILL | Use fill characters for delay |
OFDEL | Fill character is DEL |
NLDLY | Mask for delay time needed between lines |
NL0 | No delay for NLs |
NL1 | Delay further output after newline for 100 milliseconds |
CRDLY | Mask for delay time needed to return carriage to left column |
CR0 | No delay for CRs |
CR1 | Delay after CRs depending on current column position |
CR2 | Delay 100 milliseconds after sending CRs |
CR3 | Delay 150 milliseconds after sending CRs |
TABDLY | Mask for delay time needed after TABs |
TAB0 | No delay for TABs |
TAB1 | Delay after TABs depending on current column position |
TAB2 | Delay 100 milliseconds after sending TABs |
TAB3 | Expand TAB characters to spaces |
BSDLY | Mask for delay time needed after BSs |
BS0 | No delay for BSs |
BS1 | Delay 50 milliseconds after sending BSs |
VTDLY | Mask for delay time needed after VTs |
VT0 | No delay for VTs |
VT1 | Delay 2 seconds after sending VTs |
FFDLY | Mask for delay time needed after FFs |
FF0 | No delay for FFs |
FF1 | Delay 2 seconds after sending FFs |
c_oflag成员的常量
选择加工过的输出
通过在c_oflag成员变量中设置OPOST选项的方法程序可以选择加工过的输入.
1 options.c_oflag |= OPOST;
在所有选项当中,你可能只需要使用ONLCR选项来将行分隔符映射到CR-LF组合对上.其他选项主要是历史遗留,仅仅与行打印机和终端跟不上串行数据的年代有关.
选择原始输出
原始输出方式可以通过在c_oflag中重置OPOST选项来选择:
1 options.c_oflag &= ~OPOST;
如果OPOST选项被设置成无效的话,其他c_oflag中的选项都会失效.
控制字符
字符数组c_cc里面包括了控制字符的定义和超时参数.这个数组的每个元素都是以常量定义的.
常量 | 描述 | 键 |
VINTR | Interrupt | CTRL-C |
VQUIT | Quit | CTRL-Z |
VERASE | Erase | Backspace (BS) |
VKILL | Kill-line | CTRL-U |
VEOF | End-of-file | CTRL-D |
VEOL | End-of-line | Carriage return (CR) |
VEOL2 | Second end-of-line | Line feed (LF) |
VMIN | Minimum number of characters to read | - |
VSTART | Start flow | CTRL-Q (XON) |
VSTOP | Stop flow | CTRL-S (XOFF) |
VTIME | Time to wait for data (tenths of seconds) | - |
成员变量c_cc中的控制字符
设置软件流控制字符
用来做软件流控制的字符包含在数组c_cc的VSTART和VSTOP元素里面.通常情况下,它们应该被设置成DC1(八进制021)和DC3(八进制023),它们在ASCII标准中代表着XON和XOFF字符.
设置读取超时
UNIX串口驱动提供了设置字符和包超时的能力.数组c_cc中有两个元素可以用来设置超时:VMIN和VTIME.在经典输入模式或者通过open(2)和fcntl(2)函数传递NDELAY选项时,超时设置会被忽略.
VMIN可以指定读取的最小字符数.如果它被设置为0,那么VTIME值则会指定每个字符读取的等待时间.
如果VMIN不为零,VTIME会指定等待第一个字符读取操作的时间.如果在这个指定时间中可以开始读取某个字符,直到VMIN个数的所有字符全部被读取,其他读取操作将会被阻塞(等待).也就是说,一旦读取第一个字符,串口驱动的预期就是接收到整个字符包(一共VMIN字节).如果在允许的时间内没有字符被读取,那么read(2)调用就会返回0.通过这个方法可以确切得告诉串口驱动程序需要读取N个字节,而且read(2)调用只会返回N或者0.然而,超时设置只对第一个字符的读取操作有效,所以,如果因为某些原因驱动程序在N字节的包中丢失某个字符的话,read(2)调用将会一直等下去.
VTIME可以以十分之一秒为单位指定等待字符输入的时间.如果VTIME设置为0(默认情况),除非open(2)或者fcntl(2)函数设置了NDELAY选项,否则read(2)将会永久得阻塞(等待).
5. 读写串口
设置好串口之后,读写串口就很容易了,把串口当作文件读写就是.
- 发送数据
1 char buffer[1024];int Length;int nByte;nByte = write(fd, buffer ,Length)
和写入其他设备文件的方式相同,write函数也会返回发送数据的字节数或者在发生错误的时候返回-1.通常,发送数据最常见的错误就是EIO,当调制解调器或者数据链路将Data Carrier Detect(DCD)信号线弄掉了,就会发生这个错误.而且,直至关闭端口这个情况会一直持续.
- 读取串口数据
使用文件操作read函数读取,如果设置为原始数据模式(Raw Date Mode)传输数据,那么read函数返回的字符数是实际串口收到的字符数,也就是返回从串口输入缓冲区中实际得到的字符的个数.在不能得到数据的情况下,read(2)系统调用就会一直等着,只到有端口上新的字符可以读取或者发生超时或者错误的情况发生.
1 char buff[1024];int Len;int readByte = read(fd,buff,Len);
如果需要read(2)函数迅速返回的话,可以使用操作文件的函数来实现异步读取,如fcntl,或者select等来操作:
1 fcntl(fd, F_SETFL, FNDELAY);
标志FNDELAY可以保证read(2)函数在端口上读不到字符的时候返回0.需要回到正常(阻塞)模式的时候,需要再次在不带FNDELAY标志的情况下调用fcntl(2)函数:
1 fcntl(fd, F_SETFL, 0);
当然,如果你最初就是以O_NDELAY标志打开串口的,你也可在之后使用这个方法改变读取的行为方式.
6. 关闭串口
关闭串口就是关闭文件.
1 close(fd);
关闭串口会将DTR信号线设置成low,这会导致很多调制解调器挂起.
7. 例子
下面是一个简单的读取串口数据的例子,使用了上面定义的一些函数和头文件.
1 /**********************************************************************
2 代码说明:使用串口二测试的,发送的数据是字符,
3 但是没有发送字符串结束符号,所以接收到后,后面加上了结束符号。
4 我测试使用的是单片机发送数据到第二个串口,测试通过。
5 **********************************************************************/
6 #define FALSE -1
7 #define TRUE 0
8 /*********************************************************************/
9 int OpenDev(char *Dev)
10 {
11 int fd = open( Dev, O_RDWR ); //| O_NOCTTY | O_NDELAY
12 if (-1 == fd)
13 {
14 perror("Can't Open Serial Port");
15 return -1;
16 }
17 else
18 return fd;
19 }
20 int main(int argc, char **argv){
21 int fd;
22 int nread;
23 char buff[512];
24 char *dev = "/dev/ttyS1"; //串口二
25 fd = OpenDev(dev);
26 set_speed(fd,19200);
27 if (set_Parity(fd,8,1,'N') == FALSE) {
28 printf("Set Parity Error\n");
29 exit (0);
30 }
31 while (1) //循环读取数据
32 {
33 while((nread = read(fd, buff, 512))>0)
34 {
35 printf("\nLen %d\n",nread);
36 buff[nread+1] = '\0';
37 printf( "\n%s", buff);
38 }
39 }
40 //close(fd);
41 // exit (0);
42 }