串口初始化配置(备忘)
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清零,这造成下边的设置操作,修改了一些原来的设置。
根据程序修改VTIME,VMIN可推知这里要使用非规范方式,
根据APUE可知由VTIME,VMIN的设置共可以有四种选择
A:VTIME > 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;
}
修改完的初步测试中发现有些地方还有问题,
待下一步测试,找到原因所在。
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮