linux串口通信编程

网友投稿 419 2022-11-13

linux串口通信编程

串口的基本原理

1 串口通讯

2 串口通讯的数据格式

3 通讯方式

全双工模式(Full Duplex)通信允许数据同时在两个方向上传输。因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。在全双工模式中,每一端都有发送器和接收器,有两条传输线,信息传输效率高。

显然,在其它参数都一样的情况下,全双工比半双工传输速度要快,效率要高。

4 偶校验与奇校验

在标准ASCII码中,其最高位(b7)用作奇偶校验位。所谓奇偶校验,是指在代码传送过程中用来检验是否出现错误的一种方法,一般分奇校验和偶校验两种。奇校验规定:正确的代码一个字节中1的个数必须是奇数,若非奇数,则在最高位b7添1;偶校验规定:正确的代码一个字节中1的个数必须是偶数,若非偶数,则在最高位b7添1。

5 停止位

6 波特率

波特率就是每秒钟传输的数据位数。

波特率的单位是每秒比特数(bps),常用的单位还有:每秒千比特数Kbps,每秒兆比特数Mbps。串口典型的传输波特率600bps,1200bps,2400bps,4800bps,9600bps,19200bps,38400bps。

7 典型的串口通讯标准

EIA RS485(通常简称“RS485”): 1983年由美国电子工业协会(EIA)制定。

8 RS232串口

RS232是计算机与通信工业应用中最广泛一种串行接口。它以全双工方式工作,需要地线、发送线和接收线三条线。RS232只能实现点对点的通信方式。

8.1 RS232串口缺点

●传输速率低,最高波特率19200bps。

●抗干扰能力较差。

●传输距离有限,一般在15m以内。

●只能实现点对点的通讯方式。

8.2 RS232串口接口定义

RXD:接收数据,TXD:发送数据,GND/SG:信号地。

8.3 电脑DB9针接口定义

电脑DB9针接口是常见的RS232串口,其引脚定义如下:

2号脚:RXD(接收数据)

3号脚:TXD(发送数据)

5号脚:SG或GND(信号地)

其它脚:我们不用

电脑RS232串口与仪表串口连接图:

9 RS485串口

9.1 RS485串口特点

●RS485采用平衡发送和差分接收,具有良好的抗干扰能力,信号能传输上千米。

●RS485有两线制和四线制两种接线。采用四线制时,只能实现点对多的通讯(即只能有一个主设备,其余为从设备)。四线制现在很少采用,现在多采用两线制接线方式。

●两线制RS485只能以半双式方式工作,收发不能同时进行。

●RS485在同一总线上最多可以接32个结点,可实现真正的多点通讯,但一般采用的是主从通信方式,即一个主机带多个从机。

●因RS485接口具有良好的抗干扰能力,长的传输距离和多站能力等优点使其成为首选的串行接口。

10 串口通讯硬件常见的注意事项

●通讯用的屏蔽电缆最好选用双层隔离型屏蔽电缆,其次选用单层屏蔽电缆,最好不要选用无屏蔽层的电缆,且电缆屏蔽层一定要能完全屏蔽,有些质量差的电缆,屏蔽层很松散,根本起不到屏蔽的作用。单层屏蔽的电缆屏蔽层应一端接地,双层屏蔽的电缆屏蔽层其外层(含铠装)应两端接地,内层屏蔽则应一端接地。

●仪表使用RS232通讯时,通讯电缆长度不得超过15米。

●一般RS485协议的接头没有固定的标准,可能根据厂家的不同引脚顺序和管脚功能可能不尽相同,用户可以查阅相关产品RS485的引脚图。

●RS485通讯电缆最好选用阻阬匹配、低衰减的RS485专用通讯电缆(双绞线),不要使用普通的双绞电缆或质量较差的通讯电缆。因为普通电缆或质量差的通讯电缆,可能阻抗不匹配、衰减大、绞合度不够、屏蔽层太松散,这样会导致干扰将非常大,会造成通讯不畅,甚至通讯不上。

●仪表使用RS485通讯时,每台仪表必须手牵手地串下去,不可以有星型连接或者分叉,如果有星型连接或者分叉,干扰将非常大,会造成通讯不畅,甚至通讯不上。

●485总线结构理论上传输距离达到1200米,一般是指通讯线材优质达标,波特率9600,只有一台485设备才能使得通讯距离达到1200米,而且能通讯并不代表每次通讯都正常,所以通常485总线实际的稳定通讯距离远远达不到1200米。负载485设备多,线材阻抗不同时,通讯距离更短。

11.1 有关通讯的一些基本概念

●主机与从机:在通讯系统中起主要作用、发布主要命令的称为主机,接受命令的称为从机。

●连续方式:指主机不需要发布命令,从机就能自动地向主机发送数据。

●指令方式:指主机向从机发布命令,从机根据指令执行动作,并将结果“应答”给主机的模式。

●输出数据类型:指在连续方式通讯时,从机输出给主机的数据类型。

●通讯协议:指主机与从机通讯时,按哪一种编码规则来通讯。

●波特率:主从机之间通讯的速度。

●数据位:每次传输数据时,数据由几位组成。

●校验位:数据传输错误检测,可以是奇校验、偶校验或无校验。

●地址:每一台从机的编号。

11.2 主从机之间通讯设置要点

●要点一:主/从RS232/485硬件有无设置正确,通讯线有无接对。有些通讯板卡是RS422与RS485共用的,依靠板上跳线来实现的,有些仪表RS232/485也需要通讯跳线来实现。

●要点二:主机上的通讯端口有无设置正确;超时(一般设置为2s)、通讯延时(一般设置为5~20ms)、ACK信号延时(一般设置为0ms)有无设置正确。

●要点三:主/从机通讯协议有无选择正确。

●要点四:主/从机波特率有无选择正确。

●要点五:主/从机数据位有无选择正确。数据位可以选择7位,8位。

●要点六:主/从机校验位有无选择正确。校验位一般可选择偶校验、奇校验、无校验。

●要点七:主/从机停止位有无选择正确。停止位可以选择1位、1.5位还是2位。

●要点八:从机地址有无选择正确。

●要点九:主/从机的通讯方式有无选择正确。

1》 把串口线接到不同的串口,用串口调试工具从一个串口发数据,另一个能正常收到说明串口线是OK的。

1、串口的操作

1.1打开:fd = open(“/dev/ttySAC1”, O_RDWR | O_NOCTTY | O_NDELAY);

O_RDWR 读写方式打开;

O_NOCTTY 不允许进程管理串口(不太理解,一般都选上);

O_NDELAY 非阻塞(默认为阻塞,打开后也可以使用fcntl()重新设置)

n实际写入字节数;

res 读取的字节数;

1.4设置:fcntl(fd, F_SETFL, FNDELAY); //非阻塞

fcntl(fd, F_SETFL, 0); // 阻塞

1.5关闭:close(fd);

2、串口配置

bzero(&options,sizeof(options));

options.c_cflag |= B115200 | CLOCAL | CREAD; // 设置波特率,本地连接,接收使能

options.c_cflag |= CS8; // 数据位为 8 ,CS7 for 7

options.c_cflag &= ~CSTOPB; // 一位停止位, 两位停止为 |= CSTOPB

options.c_cflag &= ~PARENB; // 无校验

//options.c_cflag |= PARENB; //有校验

//options.c_cflag &= ~PARODD // 偶校验

//options.c_cflag |= PARODD // 奇校验

options.c_cc[VTIME] = 0; // 等待时间,单位百毫秒 (读)。后有详细说明

options.c_cc[VMIN] = 0; // 最小字节数 (读)。后有详细说明

tcflush(fd, TCIOFLUSH); // TCIFLUSH刷清输入队列。

TCOFLUSH刷清输出队列。

TCIOFLUSH刷清输入、输出队列。

tcsetattr(fd, TCSANOW, &options); // TCSANOW立即生效;

TCSAFLUSH:Flush input and output buffers and make the change

3、VTIME 和 VMIN

VTIME 定义要求等待的零到几百毫秒的值(通常是一个8位的unsigned char变量)。

VMIN 定义了要求等待的最小字节数, 这个字节数可能是0。

只有设置为阻塞时这两个参数才有效,仅针对于读操作。

说起来比较复杂,举个例子吧,设置为阻塞状态,写操作未进行实验,这里仅讨论读操作,

read(fd,&buf,8); // 读串口

3.1

options.c_cc[VTIME] = 0;

options.c_cc[VMIN] = 0;

VMIN = 0,当缓冲区字节数 》= 0 时进行读操作,实际上这时读串口操作并未被阻塞,因为条件始终被满足。

3.2

options.c_cc[VTIME] = 0;

options.c_cc[VMIN] = 1;

VMIN = 1,当缓冲区字节数 》= 1 时进行读操作,当没有数据时读串口操作被阻塞。

3.3

options.c_cc[VTIME] = 0;

options.c_cc[VMIN] = 4;

VMIN = 4,当缓冲区字节数 》= 4 时进行读操作,否则读串口操作被阻塞。每次读出的最大字节数由read函数中第三个参数决定。直到缓冲区剩下的数据《 read 第三个参数 并且《 4 (如果这时read第三参数为 1 则进行4次读操作直至读完缓冲区,如read第三参数为2,连续进行读操作,直至缓冲区空或还剩一个字符)。没有设置VTIME,剩下的字符没有确定的期限,直到下次满足读条件的时候才被读出。

----------------------------------考虑VTIME-----------------------------

3.4

options.c_cc[VTIME] = 10; //单位百毫秒

options.c_cc[VMIN] = 4;

同3.3的区别就是,没满足条件或读缓冲区中剩下的数据会在1秒(10百毫秒)后读出。另外特别注意的是当设置VTIME后,如果read第三个参数小于VMIN ,将会将VMIN 修改为read的第三个参数,即使用read(fd,&buf,2);,以上设置变为:

options.c_cc[VTIME] = 10;

options.c_cc[VMIN] = 2;

1》打开串口函数open_port()中要实现的函数:

(1)open(“/dev/ttys0”,O_RDWR | O_NOCTTY | O_NDELAY);/*打开串口0*/

(2)fcntl(fd,F_SETFL,0)/*恢复串口为阻塞状态*/

(3)isatty(STDIN_FILENO) /*测试是否为中断设备 非0即是中断设备*/

2》 配置串口参数函数set_opt()中要实现的函数:

(1)保存原先有串口配置

tcgetattr(fd,&oldtio);

(2)先将新串口配置清0

bzore(&newtio,sizeof(newito));

(3)激活选项CLOCAL和CREAD 并设置数据位大小

newtio.c_cflag |=CLOCAL | CREAD;

newtio.c_cflag &= ~CSIZE;

newtio.c_cflag |=CS8;

(4)设置奇偶校验

奇校验:

newtio.c_cflag |= PARENB;

newtio.c_cflag |= PARODD;

偶校验:

newtio.c_iflag |= (INPCK | ISTRIP);

newtio.c_cflag |= PAREND;

newtio.c_cflag &= ~PARODD;

无奇偶校验:

newtio.c_cflag &= ~PARENB;

(5) 设置停止位

newtio.c_cflag &= ~CSTOPB; /*停止位为1*/

newtio.c_cflag |= CSTOPB;/*停止位为0*/

(6)设置波特率:

cfsetispeed(&newtio,B115200);

cfsetospeed(&newtio,B115200);

(7)设置等待时间和最小接受字符:

newtio.c_cc[VTIME] = 0;

newtio.c_cc[VMIN] = 0;

(8)处理为接收字符:

tcflush(fd,TCIFLUSH);

(9)激活新配置:

tcsetattr(fd,TCSANOW,&newtio);

3.读写串口

write(fd,buff,8);

read(fd,buff,8);

三、串口编程实例:

[cpp] view plain copy#include 《stdio.h》

#include 《string.h》

#include 《sys/types.h》

#include 《errno.h》

#include 《sys/stat.h》

#include 《fcntl.h》

#include 《unistd.h》

#include 《termios.h》

#include 《stdlib.h》

int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)

{

/* 五个参量 fd打开文件 speed设置波特率 bit数据位设置 neent奇偶校验位 stop停止位 */

struct termios newtio,oldtio;

if ( tcgetattr( fd,&oldtio) != 0) {

perror(“SetupSerial 1”);

return -1;

}

bzero( &newtio, sizeof( newtio ) );

newtio.c_cflag |= CLOCAL | CREAD;

newtio.c_cflag &= ~CSIZE;

switch( nBits )

{

case 7:

newtio.c_cflag |= CS7;

break;

case 8:

newtio.c_cflag |= CS8;

break;

}

switch( nEvent )

{

case ‘O’:

newtio.c_cflag |= PARENB;

newtio.c_cflag |= PARODD;

newtio.c_iflag |= (INPCK | ISTRIP);

break;

case ‘E’:

newtio.c_iflag |= (INPCK | ISTRIP);

newtio.c_cflag |= PARENB;

newtio.c_cflag &= ~PARODD;

break;

case ‘N’:

newtio.c_cflag &= ~PARENB;

break;

}

switch( nSpeed )

{

case 2400:

cfsetispeed(&newtio, B2400);

cfsetospeed(&newtio, B2400);

break;

case 4800:

cfsetispeed(&newtio, B4800);

cfsetospeed(&newtio, B4800);

break;

case 9600:

cfsetispeed(&newtio, B9600);

cfsetospeed(&newtio, B9600);

break;

case 115200:

cfsetispeed(&newtio, B115200);

cfsetospeed(&newtio, B115200);

break;

default:

cfsetispeed(&newtio, B9600);

cfsetospeed(&newtio, B9600);

break;

}

if( nStop == 1 )

newtio.c_cflag &= ~CSTOPB;

else if ( nStop == 2 )

newtio.c_cflag |= CSTOPB;

newtio.c_cc[VTIME] = 0;

newtio.c_cc[VMIN] = 0;

tcflush(fd,TCIFLUSH);

if((tcsetattr(fd,TCSANOW,&newtio))!=0)

{

perror(“com set error”);

return -1;

}

printf(“set done!”);

return 0;

}

int open_port(int fd,int comport)

{

/* fd 打开串口 comport表示第几个串口 */

char *dev[]={“/dev/ttyS0”,“/dev/ttyS1”,“/dev/ttyS2”};

long vdisable;

if (comport==1)

{ fd = open( “/dev/ttyS0”, O_RDWR|O_NOCTTY|O_NDELAY);

if (-1 == fd){

return(-1);

}

else

printf(“open ttyS0 。。.。。”);

}

else if(comport==2)

{ fd = open( “/dev/ttyS1”, O_RDWR|O_NOCTTY|O_NDELAY);

if (-1 == fd){

perror(“Can’t Open Serial Port”);

return(-1);

}

else

printf(“open ttyS1 。。.。。”);

}

else if (comport==3)

{

fd = open( “/dev/ttyS2”, O_RDWR|O_NOCTTY|O_NDELAY);

if (-1 == fd){

perror(“Can‘t Open Serial Port”);

return(-1);

}

else

printf(“open ttyS2 。。.。。”);

}

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

else

printf(“fcntl=%d”,fcntl(fd, F_SETFL,0));

if(isatty(STDIN_FILENO)==0)

printf(“standard input is not a terminal device”);

else

printf(“isatty success!”);

printf(“fd-open=%d”,fd);

return fd;

}

int main(void)

{

int fd;

int nread,i;

char buff[]=“Hello”;

if((fd=open_port(fd,1))《0){

perror(“open_port error”);

return;

}

if((i=set_opt(fd,115200,8,’N‘,1))《0){

perror(“set_opt error”);

return;

}

printf(“fd=%d”,fd);

// fd=3;

nread=read(fd,buff,8);

printf(“nread=%d,%s”,nread,buff);

close(fd);

return;

}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Azure技术01-Azure介绍
下一篇:java二叉树的数据插入算法介绍
相关文章

 发表评论

暂时没有评论,来抢沙发吧~