linux 环境下读取串口, 接收不到0x11, 0x0d, 0x13等数据的解决办法
// add by dern
options.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
即可解决特殊字符丢失问题。
网上许多流行的linux串口编程的版本中都没对c_iflag(termios成员变量)这个变量进行有效的设置,这样传送ASCII码时没什么问题,但传送二进制数据时遇到0x0d,0x11和0x13却会被丢掉。不用说也知道,这几个肯定是特殊字符,被用作特殊控制了。关掉ICRNL和IXON选项即可解决。
c_iflag &= ~(ICRNL | IXON);
0x0d 回车符CR
0x11 ^Q VSTART字符
0x13 ^S VSTOP字符
ICRNL 将输入的CR转换为NL
IXON 使起动/停止输出控制流起作用
在《UNIX环境高级编程第二版》第18章第11小节看到把终端I/O设置为原始模式(串口通讯就是终端I/O的原始模式)时输入属性设置为
term.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
屏蔽了许多属性,怪不得有人说如果是使用串口通讯c_iflag和c_oflag都设置为0就行了!
以下是我的设置的一些重要的串口属性
term.c_cflag |= CLOCAL | CREAD;
term.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
term.c_oflag &= ~OPOST;
term.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
初始化串口:
int setup_com(int fd){
struct termios options;
tcgetattr(fd, &options);
/* Set the baud rates to 38400…*/
cfsetispeed(&options, B38400);
cfsetospeed(&options, B38400);
/* Enable the receiver and set local mode…*/
options.c_cflag |= (CLOCAL | CREAD);
/* Set c_cflag options.*/
options.c_cflag |= PARENB;
options.c_cflag &= ~PARODD;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
/* Set c_iflag input options */
options.c_iflag &=~(IXON | IXOFF | IXANY);
options.c_iflag &=~(INLCR | IGNCR | ICRNL);
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
/* Set c_oflag output options */
options.c_oflag &= ~OPOST;
/* Set the timeout options */
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 10;
tcsetattr(fd, TCSANOW, &options);
return 1;
}
其中:
c_cflag : 控制选项
c_lflag : 线选项
c_iflag : 输入选项
c_oflag :输出选项
c_cc :控制字符
c_ispeed :输入数据波特率
c_ospeed :输出数据波特率
c_cflag: CLOCAL 本地模式,不改变端口的所有者
CREAD 表示使能数据接收器
PARENB 表示偶校验
PARODD 表示奇校验
CSTOPB 使用两个停止位
CSIZE 对数据的bit使用掩码
CS8 数据宽度是8bit
c_lflag: ICANON 使能规范输入,否则使用原始数据(本文使用)
ECHO 回送(echo)输入数据
ECHOE 回送擦除字符
ISIG 使能SIGINTR,SIGSUSP, SIGDSUSP和 SIGQUIT 信号
c_iflag: IXON 使能输出软件控制
IXOFF 使能输入软件控制
IXANY 允许任何字符再次开启数据流
INLCR 把字符NL(0A)映射到CR(0D)
IGNCR 忽略字符CR(0D)
ICRNL 把CR(0D)映射成字符NR(0A)
c_oflag: OPOST 输出后处理,如果不设置表示原始数据(本文使用原始数据raw)
c_cc[VMIN]: 最少可读数据
c_cc[VTIME]: 等待数据时间(10秒的倍数)
初始化完后就可以直接对串口像普通文件一样使用文件操作函数read和write进行读写操作了,read和write函数返回的是成功收到(送出)的字节数,如果成功那么返回值大于零,很容易判断;
不过要注意的是:linux下对串口的read是半阻塞式的,不设置好VMIN和VTIME的话,会导致执行read时发生明显的阻塞现象,上面的VMIN0和VTIME10设置成只要串口收到数据read就立即返回数据,如果一直没收到数据,则10秒后read超时才返回0值;如果VMIN和VTIME不设置的话,缺省值好像是60秒后read才能超时返回,在60秒内如果串口没收到数据将一直阻塞,看上去程序像死掉一样;
如果需要read无论有没有收到数据都立即返回,可以初始化成不延迟方式:
fcntl(fd, F_SETFL, FNDELAY);
这样执行到read时如果收到数据则返回成功收到的字节数,如果没收到数据则立即返回0,不会产生阻塞现象。
//设置数据流控制
switch(flow_ctrl)
{
case 0 ://不使用流控制
// options.c_cflag &= ~CRTSCTS;
options.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
break;
case 1 ://使用硬件流控制
options.c_cflag |= CRTSCTS;
break;
case 2 ://使用软件流控制
options.c_cflag |= IXON | IXOFF | IXANY;
break;
}
http://blog.csdn.net/yangshishu/article/details/44957303
http://taiwanxia.blog.163.com/blog/static/483307302010212111949866/