串口初始化配置(备忘)

2019-07-13 05:58发布

在基于AT91的嵌入式linux中接收串口数据时,发现对于接收的数据经常出现接收不完整的现象。一帧的数据可能会被当做两帧接收,导致对于一帧数据接收出现问题。虽然这种情况在一般情况下,并不是经常出现,但是只要数据量稍微大一些,情况就会出现。 于是仔细看了程序中关于串口配置这一块的程序, int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop) { struct termios oldtio; struct termios newtio; 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 ; break; case 'E': newtio.c_iflag |= INPCK ; 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; case 460800: cfsetispeed(&newtio, B460800); cfsetospeed(&newtio, B460800); 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] = 1; tcflush(fd,TCIFLUSH); if((tcsetattr(fd,TCSANOW,&newtio))!=0) { perror("com set error"); return -1; } tcflush(fd,TCIFLUSH);//The `tcflush' function is used to clear the input and/or output return 0; }

在分析完程序后发现可能导致出问题的地方:在使用oldtio读取串口配置后,却没有将其复制给newtio,并且将newtio清零,这造成下边的设置操作,修改了一些原来的设置。 根据程序修改VTIMEVMIN可推知这里要使用非规范方式, 根据APUE可知由VTIMEVMIN的设置共可以有四种选择 AVTIME > 0, VMIN > 0 B: VTIME = 0, VMIN > 0 C: VTIME > 0, VMIN = 0 D: VTIME = 0, VMIN = 0 由程序修改的值  newtio.c_cc[VTIME]  = 0;
 newtio.c_cc[VMIN]   = 1;
可知这里要设置为第二种方式:只有接收到MIN个字节数据,read才返回;否则,read将阻塞。 因为这里没有将oldtio复制给newtio所以这里的ICANON标识一定没有设置,所以是处于非规范模式下。这里的VTIME, VMIN对这里的设置也是有效的。 但是这样难免修改一些我们没有注意的选项,根据这里的设置,结合APUE中的示例程序,发现APUE中将终端设置为原始模式(raw modle)与这里的设置较为相似,于是想采用APUE中的部分参数设置,来修改此处的程序。 ==================APUE中 put terminal into a raw modle =========================== int tty_raw(int fd) /* put terminal into a raw mode */ { int err; struct termios buf; if (ttystate != RESET) { errno = EINVAL; return(-1); } if (tcgetattr(fd, &buf) < 0) return(-1); save_termios = buf; /* structure copy */ /* * Echo off, canonical mode off, extended input * processing off, signal chars off. */ buf.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); /* * No SIGINT on BREAK, CR-to-NL off, input parity * check off, don't strip 8th bit on input, output * flow control off. */ buf.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); /* * Clear size bits, parity checking off. */ buf.c_cflag &= ~(CSIZE | PARENB); /* * Set 8 bits/char. */ buf.c_cflag |= CS8; /* * Output processing off. */ buf.c_oflag &= ~(OPOST); /* * Case B: 1 byte at a time, no timer. */ buf.c_cc[VMIN] = 1; buf.c_cc[VTIME] = 0; if (tcsetattr(fd, TCSAFLUSH, &buf) < 0) return(-1); /* * Verify that the changes stuck. tcsetattr can return 0 on * partial success. */ if (tcgetattr(fd, &buf) < 0) { err = errno; tcsetattr(fd, TCSAFLUSH, &save_termios); errno = err; return(-1); } if ((buf.c_lflag & (ECHO | ICANON | IEXTEN | ISIG)) || (buf.c_iflag & (BRKINT | ICRNL | INPCK | ISTRIP | IXON)) || (buf.c_cflag & (CSIZE | PARENB | CS8)) != CS8 || (buf.c_oflag & OPOST) || buf.c_cc[VMIN] != 1 || buf.c_cc[VTIME] != 0) { /* * Only some of the changes were made. Restore the * original settings. */ tcsetattr(fd, TCSAFLUSH, &save_termios); errno = EINVAL; return(-1); } ttystate = RAW; ttysavefd = fd; return(0); }
从英文注释,可以了解到,各个参数的具体意义, 同时参考博客http://blog.csdn.net/awei_xu/article/details/3725329 中的红 {MOD}标记部分设置来补充, if((fd = open(dev,O_RDWR | O_NOCTTY | O_NDELAY)) == -1) /*---------------------- 重要----------------------*/ //保证本程序不会成为端口的所有者,从而妨碍控制工作和挂起信号. opt.c_cflag |= (CLOCAL | CREAD); //选择原始输入方式: 原始输入方式是不经处理的. opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //输出不经处理 opt.c_oflag &= ~OPOST; //取消软件流控制(不设置可能存在丢码) opt.c_iflag &= ~(IXON | IXOFF | IXANY); /*----------------------------------------------------*/
修改后的程序: int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop) { struct termios oldtio; struct termios newtio; if( tcgetattr(fd, &oldtio) != 0) { perror("SetupSerial 1"); return -1; } newtio = oldtio; /*************Debug*******************/ newtio.c_cflag |= CLOCAL | CREAD; newtio.c_cflag &= ~(CSIZE | PARENB); newtio.c_lflag &= ~(ICANON | ISIG | ECHO | ECHOE | IEXTEN); newtio.c_oflag &= ~OPOST; newtio.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL | BRKINT | INPCK | ISTRIP); /***************************************/ switch( nBits ) { case 7: newtio.c_cflag |= CS7; break; case 8: newtio.c_cflag |= CS8; break; } switch( nEvent ) { case 'O': /*odd parity*/ newtio.c_iflag |= INPCK ; newtio.c_cflag |= PARENB; newtio.c_cflag |= PARODD; break; case 'E': /*even parity*/ newtio.c_iflag |= INPCK ; newtio.c_cflag |= PARENB; newtio.c_cflag &= ~PARODD; break; case 'N': /*no parity*/ 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; case 460800: cfsetispeed(&newtio, B460800); cfsetospeed(&newtio, B460800); 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] = 1; tcflush(fd,TCIFLUSH); if((tcsetattr(fd,TCSANOW,&newtio))!=0) { perror("com set error"); return -1; } tcflush(fd,TCIFLUSH); return 0; }
修改完的初步测试中发现有些地方还有问题, 待下一步测试,找到原因所在。