串口通信
串口在网络通信中经常用到,最近在学习串口通信的编程。所以要对串口操作,就要了解串口的原理等。串口是串行接口的简称,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口。串行接口 (Serial Interface) 是指数据一位一位地顺序传送,其特点是通信线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢。串口是计算机上一种非常通用的设备通信协议。大多数计算机(不包括笔记本电脑)包含两个基于RS-232的串口。串口同时也是仪器仪表设备通用的通信协议;很多GPIB兼容的设备也带有RS-232口。同时,串口通信协议也可以用于获取远程采集设备的数据
串口通信是指外设和计算机间,通过数据信号线 、地线、控制线等,按位进行传输数据的一种通讯方式。这种通信方式使用的数据线少,在远距离通信中可以节约通信成本,但其传输速度比并行传输低。
电脑DB9的线,分为公头和母头:对应有九个针脚
9针脚的功能为:
数据:
GND(pin 5):地线
TXD(pin 3):串口数据输出(Transmit Data)
RXD(pin 2):串口数据输入(Receive Data)
握手:
RTS(pin 7):发送数据请求(Request to Send)
CTS(pin 8):清除发送(Clear to Send)
DSR(pin 6):数据发送就绪(Data Send Ready)
DCD(pin 1):数据载波检测(Data Carrier Detect)
DTR(pin 4):数据终端就绪(Data Terminal Ready)
其它
RI(pin 9):铃声指示
进行数据通信最善需要三根线:TxD RxD GND
TTL电平和RS232电平:
(一)、TTL电平标准
输出 L:<0.8V ;H:>2.4V。
输入 L:<1.2V ;H:>2.0V
TTL器件输出低电平要小于0.8V,高电平要大于2.4V。输入,低于1.2V就认为是0,高于2.0就认为是1。
(三)、RS232标准
逻辑1的电平为-3~-15V,逻辑0的电平为+3~+15V,注意电平的定义反相了一次。
串口通信的分类:
1.按照数据传送方向,分为:
单工:数据传输只支持数据在一个方向上传输;
半双工:允许数据在两个方向上传输。但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;它不需要独立的接收端和发送端,两者可以合并一起使用一个端口。
全双工:允许数据同时在两个方向上传输。因此,全双工通信是两个单工通信方式的结合,需要独立的接收端和发送端
2、按照通信方式,分为:
同步通信:带时钟同步信号传输。比如:SPI,IIC通信接口。
异步通信:不带时钟同步信号。比如:UART(通用异步收发器),单总线。
Linux串口编程
Linux下所有的设备都是以文件形式存储的,串口也是。串口编程的流程图如下:
- 打开串口
打开串口就是对文件进行操作,用open()函数。 - 配置串口
串口的配置参数主要有波特率、奇偶校验、数据位和停止位等。串口设置主要使用termios.h头文件中定义的termios结构,如下:
struct termios
{
tcflag_t c_iflag; //输入模式标志
tcflag_t c_oflag; //输出模式标志
tcflag_t c_cflag; //控制模式标志
tcflag_t c_lflag; //本地模式标志
cc_t c_line; //line discipline
cc_t c_cc[NCC]; //control characters
}
注意:设置波特率时要是输入和输出的速率相同。
设置波特率用到的两个函数:
cfsetispeed(&newopt, B2400);
cfsetospeed(&newopt, B2400);
校验方式有:无校验位、奇校验、偶校验、空格校验。
数据位有:CS5/CS6/CS7/CS8。
停止位有:一位或者两位。
设置完成之后记得激活配置:
/* 激活配置使其生效*/
145 if((tcsetattr( fd, TCSANOW,&newopt))!=0)
146 {
147 perror("com set error");
148 return -1;
149 }
注意tcsetattr函数中使用的标志:
TCSANOW:立即执行而不等待数据发送或者接受完成。
TCSADRAIN:等待所有数据传递完成后执行。
TCSAFLUSH:Flush input and output buffers and make the change
3 读写串口
对串口的读写操作很简单,用read()和write()函数进行读写操作即可,无特别之处。
4. 关闭串口
使用close()关闭设备文件描述符fd即可。
以下为代码:
53 /*设置波特率,设置数据位、停止位和校验位*/
54 int port_set(int fd,int baudrate,int databit,int stopbit,char parity)
55 {
56 struct termios oldopt,newopt;/*定义指向结构体stermios的指针opt*/
57
58 /*保存原有串口配置*/
59 if( tcgetattr( fd,&oldopt )!=0 )
60 {
61 printf("set serial 1\n");
62 return 0;
63 }
64 memset(&newopt,0,sizeof(newopt));
65 newopt.c_cflag|=(CLOCAL|CREAD ); /* CREAD 开启串行数据接收,CLOCAL并打开本地连接模式*/
66 newopt.c_cflag&=~CSIZE;/*设置数据位*/
67 switch(databit)
68 {
69 case 5:
70 newopt.c_cflag|=CS5;
71 break;
72 case 6:
73 newopt.c_cflag|=CS6;
74 break;
75 case 7:
76 newopt.c_cflag|=CS7;
77 break;
78 case 8:
79 newopt.c_cflag|=CS8;
80 break;
81 default:
82 printf("Unknow databit!\n");
83 return -1;
84 }
85 /* 0 50 75 110 134 150 200 300 600 1200 1800 2400 4800 9600 19200 38400 57600 115200 230400 */
86 /* 设置波特率 */
87 switch(baudrate)
88 {
89 case 2400:
90 cfsetispeed(&newopt, B2400);
91 cfsetospeed(&newopt, B2400);
92 break;
93 case 4800:
94 cfsetispeed(&newopt, B4800);
95 cfsetospeed(&newopt, B4800);
96 break;
97 case 9600:
98 cfsetispeed(&newopt, B9600);
99 cfsetospeed(&newopt, B9600);
100 break;
101 case 115200:
102 cfsetispeed(&newopt, B115200);
103 cfsetospeed(&newopt, B115200);
104 break;
105 default:
106 cfsetispeed(&newopt, B9600);
107 cfsetospeed(&newopt, B9600);
108 break;
109 }
switch(parity)
55 {
56 case 'n':
57 case 'N':/* 无奇偶校验 */
58 newopt.c_cflag&= ~PARENB;
59 newopt.c_iflag&= ~INPCK;
60 break;
61 case 'o':
62 case 'O':/* 奇校验 */
63 newopt.c_cflag|= (PARODD | PARENB);
64 newopt.c_iflag|= INPCK;
65 break;
66 case 'e':
67 case 'E':/* 偶校验 */
68 newopt.c_cflag|= PARENB;
69 newopt.c_cflag&= ~PARODD;
70 newopt.c_iflag|= INPCK;
71 break;
72 case 's':
73 case 'S':/* 设置为空格 */
74 newopt.c_cflag&= ~PARENB;
75 newopt.c_cflag&= ~CSTOPB;
76 break;
77 default:
78 printf("Unsupported parity\n");
79 return -1;
80 }
newopt.c_cflag&=~CSIZE;/*设置数据位*/
67 switch(databit)
68 {
69 case 5:
70 newopt.c_cflag|=CS5;
71 break;
72 case 6:
73 newopt.c_cflag|=CS6;
74 break;
75 case 7:
76 newopt.c_cflag|=CS7;
77 break;
78 case 8:
79 newopt.c_cflag|=CS8;
80 break;
81 default:
82 printf("Unknow databit!\n");
83 return -1;
84 }
/* 设置停止位 */
130 if( stopbit==1 )/*若停止位为1,则清除CSTOPB,若停止位为2,则激活CSTOPB*/
131 {
132 newopt.c_cflag &=~CSTOPB;/* 默认为一位停止位 */
133 }
134 else if( stopbit==2 )
135 {
136 newopt.c_cflag |=CSTOPB;/*CSTOPB表示送两位停止位*/
137 }
/* 设置最少字符和等待时间,对于接收字符和等待时间没有特别的要求时*/
140 newopt.c_cc[VTIME] = 0;/* 非规范模式读取时的超时时间;*/
141 newopt.c_cc[VMIN] = 0; /* 非规范模式读取时的最小字符数*/
142 tcflush(fd ,TCIFLUSH);/* tcflush清空终端未完成的输入/输出请求及数据;TCIFLUSH表示清空正收到的数据,且不读取出来 */
143
144 /* 激活配置使其生效*/
145 if((tcsetattr( fd, TCSANOW,&newopt))!=0)
146 {
147 perror("com set error");
148 return -1;
149 }
150 }
将每个步骤写成一个函数,直接进行调用即可。也可以将串口的路径、波特率等参数封装成一个结构体,用结构体指针传参,每次使用只需调用即可。