串口初始化配置(备忘)

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

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

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

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值