串口初始化配置(备忘)

来源:互联网 发布:安神助眠的药 知乎 编辑:程序博客网 时间:2024/06/06 03:25

在基于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 ===========================

inttty_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 中的红色标记部分设置来补充,

 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;}

修改完的初步测试中发现有些地方还有问题,

待下一步测试,找到原因所在。