这个话题,你们可能再熟悉不过了,网上资料不少,由于这是linux下编程比较重要的一个方面,懂这方面的人不少;这里我只是想给初学者简单的介绍下这方面的知识: 串口编程其实说白了, 是拿根串口线把电脑和所要控制的机器链接起来,而后在经过编程的方式对下位机进行发送指定的数据或进行控制,或进行传输,而后在接受下位机反馈回来的信息提示是否已经正确。是否是好俗! 串口是计算机上一种很是通用设备通讯的协议,经常使用PC机上包含的是RS232规格的串口,固然,除了RS232 ,还有RS485和RS422两种规格,用于不一样的设备通讯;这里主要是介绍RS232串口编程。 在串口编程中,比较重要的是串口的设置,咱们要设置的部分包括波特率,数据位,中止位,奇偶校验位;要注意的是,每台机器的串口默认设置多是不一样的,若是你没设置这些,仅仅按照默认设置进行发送数据,极可能出现n 多异想不到而又查不出来的状况;因此,在真正通信前,咱们必须设置这些: 下面就开始介绍这些基本设置的函数,(其实都是些固定框架,程序中稍微改改就行)~o~ 1.设置波特率 注意每台机器都有输出和输入接受信息的速度 ,因此用cfsetispeed 和cfsetospeed来分别设置;注意到struct termios 这样一个结构,它包括了串口端全部的设置,下面还要用到。它在termios.h中被定义。。还有一个地方比较难以理解,为何设置了speed_arr 和name_arr两个数组,这是由于在linuxe下,系统为波特率专门准备了一张表用B38400,B19200......代替,而咱们实际上传进去的只能是38400,19200这些值,因此咱们拿咱们传进去的和name_arr进行比较,若是相等则从系统对照表中取出相应值进行设置,若是不等证实传的值在系统对照表中没有,则不进行设置。 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; struct termios Opt; //定义了这样一个结构 tcgetattr(fd, &Opt); //用来获得机器原端口的默认设置 for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++) { if (speed == name_arr) //判断传进来是否相等 { tcflush(fd, TCIOFLUSH); //刷新输入输出缓冲 cfsetispeed(&Opt, speed_arr); //这里分别设置 cfsetospeed(&Opt, speed_arr); status = tcsetattr(fd, TCSANOW, &Opt); //这是马上把bote rates设置真正写到串口中去 if (status != 0) perror("tcsetattr fd1"); //设置错误 return; } tcflush(fd,TCIOFLUSH); //同上 } } 2。设置奇偶校验,数据,中止位 这三个参数一般放在一块儿设置,databits是数据位,stopbits是中止位,parity是校验位。 串口的这些设置是很复杂很复杂的,Termios成员中共定义c_cflag 控制项 c_lflag 线路项 c_iflag 输入项 c_oflag 输出项 c_cc 控制字符 c_ispeed 输入波特 c_ospeed 输出波特 那么多项,对于每一项都有不少的设置,这里咱们不讲的那么复杂,就一个通用的串口框架进行解释,主要进行奇偶校验,数据,中止位的设置。而其余的一些控制项,在程序中用到时穿插讲解: int set_Parity(int fd,int databits,int stopbits,int parity) { struct termios options; //定义一个结构 if ( tcgetattr( fd,&options) != 0) //首先读取系统默认设置options中,必须 { perror("SetupSerial 1"); return(FALSE); } options.c_cflag &= ~CSIZE; //这是设置c_cflag选项不按位数据位掩码 switch (databits) /*设置数据位数*/ { case 7: options.c_cflag |= CS7; //设置c_cflag选项数据位为7位 break; case 8: options.c_cflag |= CS8; //设置c_cflag选项数据位为8位 break; default: fprintf(stderr,"Unsupported data size\n"); //其余的都不支持 return (FALSE); } switch (parity) //设置奇偶校验,c_cflag和c_iflag有效 { case 'n': case 'N': //无校验 固然都不选 options.c_cflag &= ~PARENB; /* Clear parity enable */ options.c_iflag &= ~INPCK; /* Enable parity checking */ break; case 'o': //奇校验 其中PARENB校验位有效;PARODD奇校验 INPCK检查校验 case 'O': options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/ options.c_iflag |= INPCK; /* Disnable parity checking */ break; case 'e': case 'E': //偶校验,奇校验不选就是偶校验了 options.c_cflag |= PARENB; /* Enable parity */ options.c_cflag &= ~PARODD; /* 转换为偶效验*/ options.c_iflag |= INPCK; /* Disnable parity checking */ break; default: fprintf(stderr,"Unsupported parity\n"); return (FALSE); } /* 设置中止位*/ switch (stopbits) //这是设置中止位数,影响的标志是c_cflag { case 1: options.c_cflag &= ~CSTOPB; //不指明表示一位中止位 break; case 2: options.c_cflag |= CSTOPB; //指明CSTOPB表示两位,只有两种可能 break; default: fprintf(stderr,"Unsupported stop bits\n"); return (FALSE); } /* Set input parity option */ if (parity != 'n') //这是设置输入是否进行校验 options.c_iflag |= INPCK; //这个地方是用来设置控制字符和超时参数的,通常默认便可。稍微要注意的是c_cc数组的VSTART 和 VSTOP 元素被设定成DC1 和 DC3,表明ASCII 标准的XON和XOFF字符。因此若是在传输这两个字符的时候就传不过去,这时须要把软件流控制屏蔽 options.c_iflag &= ~(IXON | IXOFF | IXANY); options.c_cc[VTIME] = 150; // 15 seconds options.c_cc[VMIN] = 0; tcflush(fd,TCIFLUSH); /* Update the options and do it NOW */ //刷新和马上写进去 if (tcsetattr(fd,TCSANOW,&options) != 0) { perror("SetupSerial 3"); return (FALSE); } return (TRUE); } //串口设置框架到这里就大概结束了,对于设置了数据位校验位中止位和波特率的端口已经能够传输大多数信息。在实际中的状况每每是不少特例,好比, 在用write发送数据时没有键入回车,信息就将发送不出去的状况,这主要是由于咱们在输出输入时是按照 规范模式接受到回车或者换行才发送,而不少状况咱们是不须要回车和换行的,这时,应当切换到行方式输入,设置options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);不经处理直接发送。 又好比 在咱们发送字符0x0d的时候,每每接受端获得的字符是0x0a 这是怎么回事,缘由是在串口设置中c_iflag和c_oflag中存在从NL-CR 和CR-NL的映射,也就是说,串口能够把回车和换行当作一个字符,因此,此时咱们应该屏蔽掉这些,用options.c_oflag &=~(INLCR|IGNCR|ICRNL|);和options.c_oflag &=~(ONLCR|OCRNL); 进行设置。 总之,串口的设置是很复杂也很麻烦的东西,具体状况要具体分析,找到相应的办法,若是发现数据不能传送,不妨耐点心在串口设置上找答案吧 言归正传,后面的东西就很简单了,接下来是打开串口: int OpenDev(char *Dev) { int fd = open( Dev, O_RDWR ); //| O_NOCTTY | O_NDELAY这种方式看open函数 if (-1 == fd) { /*设置数据位数*/ perror("Can't Open Serial Port"); return -1; } else return fd; } 而后是数据的接受和发送,把通用的主函数贴下来,很容易的。 int main(int argc, char **argv) { int fd; int nread; char buff[512]; char *dev ="/dev/ttyS0"; //linux下的端口就是经过打开设备文件操做的 fd = OpenDev(dev); //打开 if (fd>0) set_speed(fd,19200); //打开后设置波特率19200 else { printf("Can't Open Serial Port!\n"); exit(0); } if (set_Parity(fd,8,1,'N')== FALSE) //设置8,1,n 注意,这里和上面要和下位机相符才可能通讯 { printf("Set Parity Error\n"); exit(1); } //通常读的时候通常都用read ,写的时候通常都用write,read要注意阻塞后程序中止不动,因此要用select 进行控制,注意tv每次循环都要设置;write 不用考虑阻塞,但要用循环写方式保证必定写完,其实读最好也用循环读方式保证必定能读到全部东西而且能拼接在一块儿,而后在进行其余操做。最后while (1) 是串口通信中经常使用的循环 就是一直执行,直到碰到break;这些东西挺烦琐,不过其实也没什么。这里就不详细说了,下面是个最最简单的。。 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); } 完了,是否是不难,其实除了串口设置是新知识,,事实上linux都是文件,串口是设备文件,设置好后,其余的东西就当成文件进行操做吧。 ios |