Linux串口读写

Linux串口读写(出处)

串口简介串行口是计算机一种常用的接口,具有连接线少,通讯简单,得到广泛的使用。常用的串口是 RS-232-C 接口(又称 EIARS-232-C)它是在 1970 年由美国电子工业协会(EIA)联合贝尔系统、调制解调器厂家及计算机终端生产厂家共同制定的用于串行通讯的标准。它的全名是"数据终端设备(DTE)和数据通讯设备(DCE)之间串行二进制数据交换接口技术标准"该标准规定采用一个 25 个脚的 DB25连接器,对连接器的每个引脚的信号内容加以规定,还对各种信号的电平加以规定。传输距离在码元畸变小于 4% 的情况下,传输电缆长度应为50 英尺。Linux 操作系统从一开始就对串行口提供了很好的支持计算机串口的引脚说明序号 信号名称 符号 流向 功能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 与线路接通,出现振铃串口操作串口操作需要的头文件#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<termios.h>#include<errno.h>打开串口在 Linux 下串口文件是位于 /dev 下的串口一 为 /dev/ttyS0串口二 为 /dev/ttyS1打开串口是通过使用标准的文件打开函数操作:int fd;fd = open( "/dev/ttyS0", O_RDWR);if (-1 == fd){perror(" 提示错误!");}设置串口最基本的设置串口包括波特率设置,效验位和停止位设置。串口的设置主要是设置 struct termios 结构体的各成员值。struct termio{ unsigned short c_iflag; unsignedshortc_oflag; unsignedshortc_cflag; unsignedshortc_lflag; unsignedcharc_line; unsignedcharc_cc[NCC];};设置这个结构体很复杂,我这里就只说说常见的一些设置:波特率设置下面是修改波特率的代码:struct termios Opt;tcgetattr(fd, &Opt);cfsetispeed(&Opt,B19200);cfsetospeed(&Opt,B19200);tcsetattr(fd,TCANOW,&Opt);设置波特率的例子函数:int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200,B300,B38400, B19200, B9600, B4800, B2400, B1200, B300, };int name_arr[] = {38400, 19200, 9600, 4800, 2400, 1200, 300,38400,19200, 9600, 4800, 2400, 1200, 300, };void set_speed(int fd, int speed){int i;int status; structtermios Opt;tcgetattr(fd, &Opt); for ( i= 0;i < sizeof(speed_arr) / sizeof(int); i++) {if (speed == name_arr[i]) { tcflush(fd,TCIOFLUSH);//设置前flushcfsetispeed(&Opt, speed_arr[i]);cfsetospeed(&Opt,speed_arr[i]);//通过tcsetattr函数把新的属性设置到串口上。//tcsetattr(串口描述符,立即使用或者其他标示,指向termios的指针) status = tcsetattr(fd,TCSANOW, &Opt); if (status != 0){perror("tcsetattr fd1");return;}tcflush(fd,TCIOFLUSH); //设置后flush}}}效验位和停止位的设置:无效验 8位 Option.c_cflag &= ~PARENB;Option.c_cflag &= ~CSTOPB;Option.c_cflag &= ~CSIZE;Option.c_cflag |= ~CS8;奇效验(Odd) 7位 Option.c_cflag |= ~PARENB;Option.c_cflag &= ~PARODD;Option.c_cflag &= ~CSTOPB;Option.c_cflag &= ~CSIZE;Option.c_cflag |= ~CS7;偶效验(Even) 7位 Option.c_cflag &= ~PARENB;Option.c_cflag |= ~PARODD;Option.c_cflag &= ~CSTOPB;Option.c_cflag &= ~CSIZE;Option.c_cflag |= ~CS7;Space效验 7位 Option.c_cflag &= ~PARENB;Option.c_cflag &= ~CSTOPB;Option.c_cflag &= &~CSIZE;Option.c_cflag |= CS8;设置效验的函数:int set_Parity(int fd, int databits, int stopbits, intparity){ structtermios options; if(tcgetattr(fd, &options) != 0) {perror("SetupSerial 1");return (FALSE); }options.c_cflag &= ~CSIZE; switch(databits) { case7:options.c_cflag |= CS7;break; case8:options.c_cflag |= CS8;break;default:fprintf(stderr,"Unsupported data size\n");return (FALSE); } switch(parity) { case’n’: case’N’:options.c_cflag &= ~PARENB;options.c_iflag &= ~INPCK;break; case’o’: case’O’:options.c_cflag |= (PARODD | PARENB);options.c_iflag |= INPCK;break; case’e’: case’E’:options.c_cflag |= PARENB;options.c_cflag &= ~PARODD;options.c_iflag |= INPCK;break; case’S’: case’s’:options.c_cflag &= ~PARENB;options.c_cflag &= ~CSTOPB;break;default:fprintf(stderr,"Unsupported parity\n");return (FALSE); } switch(stopbits) { case1:options.c_cflag &= ~CSTOPB;break; case2:options.c_cflag |= CSTOPB;break;default:fprintf(stderr,"Unsupported stop bits\n");return (FALSE); } if (parity!= ‘n’)options.c_iflag |= INPCK; tcflush(fd,TCIFLUSH);options.c_cc[VTIME] = 150;options.c_cc[VMIN] = 0; if(tcsetattr(fd, TCSANOW, &options) != 0) {perror("SetupSerial 3");return (FALSE); } return(TRUE);}需要注意的是:如果不是开发终端之类的,只是串口传输数据,而不需要串口来处理,那么使用原始模式(RawMode)方式来通讯,设置方式如下:options.c_lflag &= ~(ICANON | ECHO | ECHOE |ISIG);options.c_oflag &=~OPOST;读写串口设置好串口之后,读写串口就很容易了,把串口当作文件读写就是。发送数据char buffer[1024];intLength;intnByte;nByte = write(fd, buffer ,Length)读取串口数据使用文件操作read函数读取,如果设置为原始模式(RawMode)传输数据,那么read函数返回的字符数是实际串口收到的字符数。可以使用操作文件的函数来实现异步读取,如fcntl,或者select等来操作。char buff[1024];intLen;int readByte = read(fd,buff,Len);关闭串口关闭串口就是关闭文件。close(fd);例子

下面是一个简单的读取串口数据的例子,使用了上面定义的一些函数和头文件#define FALSE -1#define TRUE 0int OpenDev(char *Dev){ //Dev就是设备,设备就是文件,就是给出该设备文件的路径 int fd =open(Dev, O_RDWR ); //| O_NOCTTY | O_NDELAY if (-1 ==fd) {perror("Can’t Open Serial Port");return -1; } elsereturn fd;}

int main(int argc, char **argv){ intfd; intnread; charbuff[512]; char *dev ="/dev/ttyS1"; //串口二 fd =OpenDev(dev);set_speed(fd, 19200); if(set_Parity(fd, 8, 1, ‘N’) == FALSE) {printf("Set Parity Error\n");exit (0); }

while(1) //循环读取数据 {while ((nread = read(fd, buff, 512))>0){printf("\nLen %d\n", nread);buff[nread+1] = ‘\0’;printf("\n%s", buff);} }//close(fd); // exit(0);}

1、虚拟机下使用串口的方法使用vmwave,默认串口设备是没有添加的,通过vmwave将设备加入即可正常使用串口。虚拟机串口打开后,可能会占用windows下的串口。另外,虚拟机的串口收发比正常的速度的确要慢许多。

2、消除Linux串口收发的一些规则Linux 串口收发有许多模式,如:(1) 接收返回模式: 如果串口没有接收到数据,read()函数不返回。(2) 数据接收\n才返回接收的数据,否则read()函数返回0(3) 特殊字符解析问题,部分特殊字符接收/发送时,会被屏蔽或者转义。如发送0x0A 接收变为0x0A 0x0A,0x0D被屏蔽等。(4) 接收反馈:如串口接收到数据,立即将该数据发送出去。(上面是我遇到的一些问题,可能表述不很清楚,呵呵。如果用于收发txt文件,一般不大注意。)

3、解决问题的方法是,消除这些默认规则,关键是struct termios的参数影响。

struct termios {tcflag_tc_iflag;tcflag_tc_oflag;tcflag_tc_cflag;tcflag_tc_lflag;cc_tc_line;cc_tc_cc[NCCS];};

由于研究不深,如果要消除所有上面的规则,我是如下处理的struct termios options;

串口打开方式:open ("dev/ttyS0" , O_RDWR|O_NOCTTY| O_NDELAY );

消除收发模式规则:options.c_lflag= 0;options.c_oflag= 0;options.c_iflag= 0;消除字符屏蔽规则:

options.c_cc[VINTR]=0;options.c_cc[VQUIT]= 0;options.c_cc[VERASE]= 0;options.c_cc[VKILL]= 0;options.c_cc[VEOF]= 0;options.c_cc[VTIME]= 1;options.c_cc[VMIN]= 0;options.c_cc[VSWTC]= 0;options.c_cc[VSTART] =0;options.c_cc[VSTOP]= 0;options.c_cc[VSUSP]= 0;options.c_cc[VEOL]= 0;options.c_cc[VREPRINT] =0;options.c_cc[VDISCARD] =0;options.c_cc[VWERASE] =0;options.c_cc[VLNEXT] =0;options.c_cc[VEOL2]= 0;

以上设置,在其它参数串口设置前执行,如果你需要保留部分参数,请参阅http://blog.chinaunix.net/article.php?articleId=15964&blogId=60

在RedHat Feroda 4 下编译通过

= = = = = = = = = = = 非阻塞read= = = = = = = = = ==

Q:在调用串口read(fd,buff,len);时,如果串口没有数据,会停在read处,请问有没有办法让这个read动作中止?

A:使用非阻塞方式select函数(I/O多工机制)或者open的时候加O_NONBLOCK参数。

int select(int n,fd_set * readfds,fd_set *writefds,fd_set * exceptfds,struct timeval *timeout);关于这个函数的使用我会在下篇blog中整理。

= = = = = = = = = = = 串口收发源码= = = = = = = = = ==

一下代码已经经过我测试,没有问题。开发环境Redhat9,运行环境s3c2410

= = = = = = receive.c= = = = = =

#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<termios.h>#include<errno.h>#include<string.h>

#define TRUE 1

//初始化串口选项:

void setTermios(struct termios * pNewtio, intuBaudRate){bzero(pNewtio, sizeof(struct termios));

//8N1pNewtio->c_cflag = uBaudRate | CS8 | CREAD |CLOCAL;pNewtio->c_iflag = IGNPAR;pNewtio->c_oflag = 0;pNewtio->c_lflag = 0; //non ICANONpNewtio->c_cc[VINTR] = 0;pNewtio->c_cc[VQUIT] = 0;pNewtio->c_cc[VERASE] = 0;pNewtio->c_cc[VKILL] = 0;pNewtio->c_cc[VEOF] = 4;pNewtio->c_cc[VTIME] = 5;pNewtio->c_cc[VMIN] = 0;pNewtio->c_cc[VSWTC] = 0;pNewtio->c_cc[VSTART] = 0;pNewtio->c_cc[VSTOP] = 0;pNewtio->c_cc[VSUSP] = 0;pNewtio->c_cc[VEOL] = 0;pNewtio->c_cc[VREPRINT] = 0;pNewtio->c_cc[VDISCARD] = 0;pNewtio->c_cc[VWERASE] = 0;pNewtio->c_cc[VLNEXT] = 0;pNewtio->c_cc[VEOL2] = 0;}

#define BUFSIZE 512

int main(int argc, char **argv){ intfd; intnread; charbuff[BUFSIZE]; structtermios oldtio, newtio; structtimeval tv; char *dev="/dev/ttyS1"; fd_setrfds;

if ((fd= open(dev, O_RDWR | O_NOCTTY))<0) {printf("err: can’t open serial port!\n");return -1; }

tcgetattr(fd, &oldtio);setTermios(&newtio, B115200); tcflush(fd,TCIFLUSH);tcsetattr(fd, TCSANOW, &newtio);tv.tv_sec=30;tv.tv_usec=0; while(TRUE) {printf("wait…\n");FD_ZERO(&rfds);FD_SET(fd, &rfds);if (select(1+fd, &rfds, NULL, NULL,&tv)>0){if (FD_ISSET(fd, &rfds)){nread=read(fd, buff, BUFSIZE);printf("readlength=%d\n", nread);buff[nread]=’\0′;printf("%s\n", buff);}} }tcsetattr(fd, TCSANOW, &oldtio);close(fd);}

= = = = = send.c= = = = = =#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<termios.h>#include<errno.h>#include<string.h>

//初始化串口选项:void setTermios(struct termios * pNewtio, int uBaudRate){bzero(pNewtio, sizeof(struct termios));

//8N1pNewtio->c_cflag = uBaudRate | CS8 | CREAD |CLOCAL;pNewtio->c_iflag = IGNPAR;pNewtio->c_oflag = 0;pNewtio->c_lflag = 0; //non ICANONpNewtio->c_cc[VINTR] = 0;pNewtio->c_cc[VQUIT] = 0;pNewtio->c_cc[VERASE] = 0;pNewtio->c_cc[VKILL] = 0;pNewtio->c_cc[VEOF] = 4;pNewtio->c_cc[VTIME] = 5;pNewtio->c_cc[VMIN] = 0;pNewtio->c_cc[VSWTC] = 0;pNewtio->c_cc[VSTART] = 0;pNewtio->c_cc[VSTOP] = 0;pNewtio->c_cc[VSUSP] = 0;pNewtio->c_cc[VEOL] = 0;pNewtio->c_cc[VREPRINT] = 0;pNewtio->c_cc[VDISCARD] = 0;pNewtio->c_cc[VWERASE] = 0;pNewtio->c_cc[VLNEXT] = 0;pNewtio->c_cc[VEOL2] = 0;}

int main(int argc, char **argv){ intfd; int nCount,nTotal, i; structtermios oldtio, newtio; char *dev="/dev/ttyS1"; if((argc!=3) || (sscanf(argv[1], "%d", &nTotal) !=1)) {printf("err: need tow arg!\n");return -1; }

if ((fd= open(dev, O_RDWR | O_NOCTTY))<0) {printf("err: can’t open serial port!\n");return -1; }

tcgetattr(fd, &oldtio);setTermios(&newtio, B115200); tcflush(fd,TCIFLUSH);tcsetattr(fd, TCSANOW, &newtio);

for(i=0; i<nTotal; i++) {nCount=write(fd, argv[2], strlen(argv[2]));printf("send data\n");sleep(1); }tcsetattr(fd, TCSANOW, &oldtio);close(fd); return0;}

= = = = = =.makefile= = = = = =CC = /usr/local/arm/2.95.3/bin/arm-linux-gccall:receive sendreceive: receive.c $(CC)receive.c -o receive

send: send.c $(CC) send.c-o send

clean: -rm -rftestCOM receive send走自己的路,让别人说去吧

Linux串口读写

相关文章:

你感兴趣的文章:

标签云: