[基础|理论|实验]linux下的RS232串口通讯
1.Linux把所有的外部终端设备都当做文件进行操作,通常分为块设备、字符设备、网络设备。串口是属于字符设备,是计算机一种常用的接口,具有连接线少,通讯简单,得到广泛的使用。常用的串口是 RS-232-C 接口,传输距离在码元畸变小于 4% 的情况下传输电缆长度应为 50 英尺。通常的操作步骤为"打开串口文件"->"设置串口参数"->"读写串口操作"->"关闭串口文件"。
2.详解:
需要头文件信息:
1
2 #include <stdio_ext.h>
3 #include <stdio.h> /*标准输入输出定义*/
4 #include <stdlib.h> /*标准函数库定义*/
5 #include <unistd.h> /*Unix 标准函数定义*/
6 #include <sys/types.h> /*系统类型定义*/
7 #include <sys/stat.h> /*系统状态定义*/
8 #include <fcntl.h> /*文件控制定义*/
9 #include <termios.h> /*PPSIX 终端控制定*/
10 #include <errno.h> /*错误号定义/
2 #include <stdio_ext.h>
3 #include <stdio.h> /*标准输入输出定义*/
4 #include <stdlib.h> /*标准函数库定义*/
5 #include <unistd.h> /*Unix 标准函数定义*/
6 #include <sys/types.h> /*系统类型定义*/
7 #include <sys/stat.h> /*系统状态定义*/
8 #include <fcntl.h> /*文件控制定义*/
9 #include <termios.h> /*PPSIX 终端控制定*/
10 #include <errno.h> /*错误号定义/
打开串口:
linux下的串口文件通常是位于/dev目录下,串口1为/dev/ttyS0,串口2为/dev/ttyS1,以此类推。其它终端还有usb0,py0等。
1 fd = open("/dev/ttyS0",O_RDWR| O_NOCTTY | O_NDELAY);
2 if(-1 == fd)
3 {
4 perror("can not open serial port");
5
6 }
2 if(-1 == fd)
3 {
4 perror("can not open serial port");
5
6 }
设置串口:
最基本的设置串口包括波特率设置,效验位和停止位设置。主要是设置termios结构体的成员函数。
1 struct termios
2 {
3 tcflag_t c_iflag; /* input mode flags */
4 tcflag_t c_oflag; /* output mode flags */
5 tcflag_t c_cflag; /* control mode flags */
6 tcflag_t c_lflag; /* local mode flags */
7 cc_t c_line; /* line discipline */
8 cc_t c_cc[NCCS]; /* control characters */
9 speed_t c_ispeed; /* input speed */
10 speed_t c_ospeed; /* output speed */
11 #define _HAVE_STRUCT_TERMIOS_C_ISPEED 1
12 #define _HAVE_STRUCT_TERMIOS_C_OSPEED 1
13 };
2 {
3 tcflag_t c_iflag; /* input mode flags */
4 tcflag_t c_oflag; /* output mode flags */
5 tcflag_t c_cflag; /* control mode flags */
6 tcflag_t c_lflag; /* local mode flags */
7 cc_t c_line; /* line discipline */
8 cc_t c_cc[NCCS]; /* control characters */
9 speed_t c_ispeed; /* input speed */
10 speed_t c_ospeed; /* output speed */
11 #define _HAVE_STRUCT_TERMIOS_C_ISPEED 1
12 #define _HAVE_STRUCT_TERMIOS_C_OSPEED 1
13 };
波特率设置
1 struct termios Opt;
2 tcgetattr(fd, &Opt); /*获得文件句柄的termios结构*/
3 cfsetispeed(&Opt,B2400); /*设置为2400Bps,通常串口IO是一致的波特率*/
4 cfsetospeed(&Opt,B2400);
5 tcsetattr(fd,TCANOW,&Opt); /*设置文件句柄的termios结构,TCANOW为设置立即生效*/
2 tcgetattr(fd, &Opt); /*获得文件句柄的termios结构*/
3 cfsetispeed(&Opt,B2400); /*设置为2400Bps,通常串口IO是一致的波特率*/
4 cfsetospeed(&Opt,B2400);
5 tcsetattr(fd,TCANOW,&Opt); /*设置文件句柄的termios结构,TCANOW为设置立即生效*/
设置数据位
1 options.c_cflag &= ~CSIZE;
2 option.c_cflag |= CS8; /*设置为8位数据位,其它如CS5->CS7*/
2 option.c_cflag |= CS8; /*设置为8位数据位,其它如CS5->CS7*/
设置校验位
1 option.c_cflag |= PARENB; /*设置校验位使能*/
2 option.c_cflag |= PARODD; /*设置为奇校验*/
3 //option.c_cflag &= ~PARODD; /*设置为偶校验*/
4
5 //以下设置仅限于linux系统
6 option.c_cflag |= CMSPAR;
7 /*设置space校验,奇偶校验位恒为0*/
8 //option.c_cflag &= ~PARODD;或者是直接没有设置
9 /*设置mark校验,奇偶校验位恒为1*/
10 //option.c_cflag |= PARODD;
2 option.c_cflag |= PARODD; /*设置为奇校验*/
3 //option.c_cflag &= ~PARODD; /*设置为偶校验*/
4
5 //以下设置仅限于linux系统
6 option.c_cflag |= CMSPAR;
7 /*设置space校验,奇偶校验位恒为0*/
8 //option.c_cflag &= ~PARODD;或者是直接没有设置
9 /*设置mark校验,奇偶校验位恒为1*/
10 //option.c_cflag |= PARODD;
设置停止位
1 option.c_cflag &= ~CSTOPB; //一位停止位
2 //option.c_cflag |= CSTOPB; //两位停止位
2 //option.c_cflag |= CSTOPB; //两位停止位
关闭文件
1 close(fd);
//增加关于RAW原始通信(非规范模式)详解
在使用非规范模式的时候,需要关闭c_lflag字段的ICANON标志就会使得终端出于非规范模式,在这个模式下,接受到的数据不是成行的。故这里区别与上面说明,需要使用c_cc数组的两个变量:MIN和TIME,数组元素名下标为VMIN和VTIME。
VTIME定义要求等待的时间(百毫米,通常是unsigned char变量),而VMIN定义了要求等待的最小字节数(相比之下,read函数的第三个参数指定了要求读的最大字节数)。
如果VTIME=0,VMIN=要求等待读取的最小字节数,read必须在读取了VMIN个字节的数据或者收到一个信号才会返回。
如果VTIME=时间量,VMIN=0,不管能否读取到数据,read也要等待VTIME的时间量。
如果VTIME=时间量,VMIN=要求等待读取的最小字节数,那么将从read读取第一个字节的数据时开始计时,并会在读取到VMIN个字节或者VTIME时间后返回。
如果VTIME=0,VMIN=0,不管能否读取到数据,read都会立即返回。
如果VTIME=时间量,VMIN=0,不管能否读取到数据,read也要等待VTIME的时间量。
如果VTIME=时间量,VMIN=要求等待读取的最小字节数,那么将从read读取第一个字节的数据时开始计时,并会在读取到VMIN个字节或者VTIME时间后返回。
如果VTIME=0,VMIN=0,不管能否读取到数据,read都会立即返回。
另外要想以上设置生效,还需要在打开文件的时候,不要指定O_NDELAY或者是O_NONBLOCK参数。
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> /*错误号定义*/
9
10 int main(void)
11 {
12
13
14 int fd;
15 struct termios option;
16 int status = 0;
17 char buff[512];//这样定义buff的内容是随机数
18 int num = 0;
19
20 fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY );
21 if (-1 == fd)
22 {
23 perror("can not open serial port");
24
25 }
26 tcgetattr(fd, &option);
27 option.c_cflag |= CLOCAL; //保证程序不会成为端的占有者
28 option.c_cflag |= CREAD; //使端口能读取输入的数据
29 cfsetispeed(&option, B2400);
30 cfsetospeed(&option, B2400);
31
32 option.c_cflag &= ~CSIZE;
33 option.c_cflag |= CS8;
34 option.c_cflag |= PARENB;
35 //option.c_cflag &= ~PARODD;
36
37 option.c_cflag |= PARODD; //奇校验
38 option.c_cflag |= CMSPAR; //标记校验位
39 option.c_cflag &= ~CSTOPB; //一位停止位
40 option.c_iflag |= INPCK; /* Disnable parity checking */
41
42 // 一下是添加测试
43 ///////////////////////////////////////////////////////////////////////////////////
44
45 option.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
46
47 option.c_oflag &= ~OPOST;
48 option.c_oflag &= ~(ONLCR | OCRNL); //添加的
49
50 option.c_iflag &= ~(ICRNL | INLCR);
51 option.c_iflag &= ~(IXON | IXOFF | IXANY); //添加的
52 ///////////////////////////////////////////////////////////////////////////////////////
53
54 option.c_cc[VTIME] = 0; //设置超时
55 option.c_cc[VMIN] = 1; //设置最小字节数
56 tcflush(fd, TCIFLUSH);
57
58
59 status = tcsetattr(fd, TCSANOW, &option);
60 if (-1 == status)
61 {
62 perror("tcsetattr false");
63 }
64
65 write(fd, "\x02", sizeof("\x02"));
66 num = read(fd, buff, 512);
67 printf("%d\n", num);
68 printf("%d\n", errno);
69 printf("%x", buff[0]);//注意这里,如果读取不成功,将是随机值。
70 }
71
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> /*错误号定义*/
9
10 int main(void)
11 {
12
13
14 int fd;
15 struct termios option;
16 int status = 0;
17 char buff[512];//这样定义buff的内容是随机数
18 int num = 0;
19
20 fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY );
21 if (-1 == fd)
22 {
23 perror("can not open serial port");
24
25 }
26 tcgetattr(fd, &option);
27 option.c_cflag |= CLOCAL; //保证程序不会成为端的占有者
28 option.c_cflag |= CREAD; //使端口能读取输入的数据
29 cfsetispeed(&option, B2400);
30 cfsetospeed(&option, B2400);
31
32 option.c_cflag &= ~CSIZE;
33 option.c_cflag |= CS8;
34 option.c_cflag |= PARENB;
35 //option.c_cflag &= ~PARODD;
36
37 option.c_cflag |= PARODD; //奇校验
38 option.c_cflag |= CMSPAR; //标记校验位
39 option.c_cflag &= ~CSTOPB; //一位停止位
40 option.c_iflag |= INPCK; /* Disnable parity checking */
41
42 // 一下是添加测试
43 ///////////////////////////////////////////////////////////////////////////////////
44
45 option.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
46
47 option.c_oflag &= ~OPOST;
48 option.c_oflag &= ~(ONLCR | OCRNL); //添加的
49
50 option.c_iflag &= ~(ICRNL | INLCR);
51 option.c_iflag &= ~(IXON | IXOFF | IXANY); //添加的
52 ///////////////////////////////////////////////////////////////////////////////////////
53
54 option.c_cc[VTIME] = 0; //设置超时
55 option.c_cc[VMIN] = 1; //设置最小字节数
56 tcflush(fd, TCIFLUSH);
57
58
59 status = tcsetattr(fd, TCSANOW, &option);
60 if (-1 == status)
61 {
62 perror("tcsetattr false");
63 }
64
65 write(fd, "\x02", sizeof("\x02"));
66 num = read(fd, buff, 512);
67 printf("%d\n", num);
68 printf("%d\n", errno);
69 printf("%x", buff[0]);//注意这里,如果读取不成功,将是随机值。
70 }
71